唯杰地图 logo唯杰地图
VJMAP
EN

font-text

vjmapext/references/entity/font-text.md 4.5 KB84 行
原文

本文回答:字体加载 —— opentype 字体加载、默认字体、按需加载。

一、核心 API(按 vjmapext.d.ts 核对)

// 加载内置默认字体(MapCadLayer 构造时已自动调用过一次,结果存 _fontReady)
await vjmapext.initDefaultFont();                    // Promise<boolean>

// 加载自定义 ttf/otf
await vjmapext.loadOpentypeFont('/fonts/NotoSansSC-Regular.otf', 'NotoSansSC');
// 或 layer 方法(同上,另会 forceFullRender;但已渲染过的文字有字形缓存,见坑 1)
await mapcadLayer.loadFont('/fonts/arial.ttf', 'Arial');

// 查已加载
vjmapext.getLoadedFontNames();                       // string[]

完成后即可在 TextEnt.font / MTextEnt.font 中引用字体名(属性名是 font,不是 fontName)。

二、默认字体与回退

  • 未指定 font → 用"当前默认字体":最近一次成功加载的字体(初始为内置 simkai;每次 loadOpentypeFont/loadFont 成功都会把它设为默认)
  • 指定但未加载 → 同样回退到当前默认字体(字体名查找不区分大小写)

判一个字体是否已就绪——⚠️ getLoadedFontNames() 返回的是小写化后的注册名:

const ready = vjmapext.getLoadedFontNames().includes('cad');   // 注册时传 'CAD',这里也要查 'cad'
if (!ready) await vjmapext.loadOpentypeFont('/fonts/cad-full.ttf', 'CAD');

三、按需加载

字体文件通常大(10+ MB),建议分批:

// 初始:默认字体够用
await vjmapext.initDefaultFont();

// 用到 CAD 文字时再加载大字库
async function ensureCadFont() {
  if (!vjmapext.getLoadedFontNames().includes('cad')) {   // 名字已小写化
    await vjmapext.loadOpentypeFont('/fonts/cad-full.ttf', 'CAD');
  }
}

四、子集化

超大字库推荐子集(TTF → 只保留用到的字形):

  • 打包阶段用 fonttools pyftsubset / subfont 生成子集
  • 运行时用 loadOpentypeFont 加载子集,体积可从 15 MB 压到 500 KB

五、Web 字体 vs opentype

方式 场景
浏览器 @font-face DOM 元素(Marker / Popup / HTML 面板)
loadOpentypeFont / mapcadLayer.loadFont CAD TextEnt / MTextEnt(WebGL 绘制)

二者互不相干:DOM 文字不需要 loadOpentypeFont;CAD 文字只认它。

六、辅助函数

包内还有 measureTextWidth / estimateTextWidth / getTextWidth / getCharWidth / truncateTextToWidth 等排版测量导出(签名见 d.ts),自定义实体排版文字时可用。

语义(v1.0.x 源码):

  • measureTextWidth(text, size, fontName?) = 纯字形宽:取字体 getAdvanceWidth 或逐 glyph advanceWidth 累加;fontName 未加载/不存在时回退当前默认字体测量,只有一个字体都没加载(内置 simkai 尚未就绪)时才返回 0。
  • getTextWidth / getCharWidth = measureTextWidth 结果 >0 用之,否则回退 estimateTextWidth 估算。
  • estimateTextWidth = 规则估算(非测量):空格 0.35×size、CJK 全角 0.95×size、其他字符 0.58×size——拉丁宽窄字(W vs i)在估算与 simkai 下都等宽,别指望它区分。
  • truncateTextToWidth(text, maxWidth, size, fontName?) 逐字累加 getCharWidth,超宽截断返回前缀。
  • initDefaultFont() 幂等(内部 Promise 缓存),getLoadedFontNames() 返回已注册名数组(默认字体 simkai)。
  • loadOpentypeFont(url, name) 失败返回 false 不抛错(坏 URL/非字体内容皆然),且 Pn 全局默认字体指针会被成功加载的字体覆盖——加载顺序影响后续未指定字体的渲染。

七、常见坑

  1. loadFont 不 await 直接 addEntity:首次绘制时字体未就绪,用回退字体渲染。TextEnt/MTextEnt 把字形缓存在实体上(_tessCache),只有 ent.changed() 才清;loadFont 的 forceFullRender 只清渲染缓存——后加载的字体不会自动替换已用回退字体渲染过的文字,须对这些实体逐个 ent.changed()。最好先 await 字体再 addEntity。
  2. 中文路径/空格:浏览器对 URL 编码敏感;路径用 ASCII 并 URL-encode。
  3. 跨域 CORS:CDN 字体需服务端设置 Access-Control-Allow-Origin。
  4. woff2 子集浏览器兼容:vjmapext 对 ttf/otf 支持最稳;woff/woff2 依赖浏览器。
  5. 字体内存占用:大字库加载后驻留;不需要的字体就别加载(1.0.1 没有 unloadFont,控制加载列表即可)。