perf(post): subset 模式跳过 glyph name 解析降级 format 3,CJK 子集化提速 45%~50%

subset 模式下 post glyph name 对 web 字体渲染无用(浏览器靠 cmap→gid→glyf 渲染,
glyph name 仅用于 PDF 嵌入/编辑器)。原实现 subset 模式保存 pascalString 区
readBytes(思源 278KB)+ view 引用,resolveGlyf 再按需读 nameIndex + 算 name,
实测 post 段占 Font.create 的 69%(思源 56 glyph 子集 1.19ms)。

优化:subset 模式直接降级 post.format=3,跳过 nameIndex 解析、pascalString
readBytes、resolveGlyf 按需 name 读取。resolveGlyf 的 `2 === format` 条件不成立
整段跳过;write/size 走 format 3(32 字节,无 nameIndex/names)。

收益(CJK Font.create 是 fontSubset 主体):
- 思源千字文中段 3.057→1.673ms(-45%)
- 思源标点 2.705→1.361ms(-50%)
- FiraCode 22.2→21.0ms(-5.5%,瓶颈在 GSUB 不在此)
体积:FiraCode 8724→8704B(post 无 name 更紧凑)

验证:基准测试 29 用例 SSIM 全部无回归(千字文 0.9986、思源标点 0.9977、
初夏纯标点 0.9920、FiraCode 0.9923、otf-思源 0.9310 均不变)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-23 18:16:56 +08:00
parent 2c56d2cbb2
commit 6f96e2b1dd

View File

@ -41,23 +41,22 @@ var _default = exports.default = _table.default.create('post', [], {
if (format === 2) {
var numberOfGlyphs = reader.readUint16();
/* 优化60: 直接 view 批量读取 glyphNameIndex */
var view = reader.view;
var vOffset = view.byteOffset + reader.offset;
var pascalStringOffset = reader.offset + numberOfGlyphs * 2;
var pascalStringLength = ttf.tables.post.length - (pascalStringOffset - this.offset);
var pascalStringBytes = reader.readBytes(pascalStringOffset, pascalStringLength);
/* 优化87: subset 模式下只读取子集字形的 nameIndex跳过其余 */
/* post name subset glyph name web
* cmapgidglyf 渲染不依赖 post glyph nameglyph name 仅用于 PDF 嵌入/编辑器
* 强制降级 format 3 nameIndex/names跳过 nameIndex 解析pascalString
* readBytes思源 278KBresolveGlyf 的按需 name 读取实测占 Font.create 69%
* resolveGlyf 148 `2 === format` 不成立整段跳过write/size format 332B */
if (ttf.readOptions && ttf.readOptions.subset) {
tbl._pascalStringBytes = pascalStringBytes;
tbl._pascalStringOffsets = null;
tbl.format = 3;
tbl.nameIndex = null;
tbl.names = null;
/* 保存 view 引用和偏移量,供后续按需读取 nameIndex */
tbl._nameIndexViewOffset = vOffset;
tbl._nameIndexView = view;
} else {
/* 优化60: 直接 view 批量读取 glyphNameIndex */
var view = reader.view;
var vOffset = view.byteOffset + reader.offset;
var pascalStringOffset = reader.offset + numberOfGlyphs * 2;
var pascalStringLength = ttf.tables.post.length - (pascalStringOffset - this.offset);
var pascalStringBytes = reader.readBytes(pascalStringOffset, pascalStringLength);
var glyphNameIndex = new Array(numberOfGlyphs);
for (var i = 0; i < numberOfGlyphs; i++) {
glyphNameIndex[i] = view.getUint16(vOffset, false);