diff --git a/backend/font_util/gpos-subset.ts b/backend/font_util/gpos-subset.ts index 3038f1d..f7d52f9 100644 --- a/backend/font_util/gpos-subset.ts +++ b/backend/font_util/gpos-subset.ts @@ -222,7 +222,10 @@ export function subsetGPOS( /** ScriptList/FeatureList 解析失败(异常表)则整体降级返回 null(调用方保留原始 GPOS 字节) */ if (!scriptListBytes || !featureListBytes) return null; - const w = new Writer(); + /** 主 Writer 按原 GPOS 表大小预分配容量:子集输出通常 ≤ 原表(剔除字形), + * 默认 2048 对大 GPOS(初夏 lookup[10] 283 subtable 累积 ~16KB)触发 4+ 次 grow 全拷贝。 + * 取原表大小作为初始容量(足够且不至于对小表浪费太多)。 */ + const w = new Writer(gposBytes.byteLength); /** Header(偏移量最后回填) */ w.writeUint16(1); diff --git a/backend/font_util/gsub-subset.ts b/backend/font_util/gsub-subset.ts index 42a81f6..ad8787f 100644 --- a/backend/font_util/gsub-subset.ts +++ b/backend/font_util/gsub-subset.ts @@ -1398,7 +1398,8 @@ export function subsetGSUB( /** ScriptList/FeatureList 解析失败(异常表)则整体保留原始 GSUB 字节(安全降级) */ if (!scriptListBytes || !featureListBytes) return gsubBytes; - const w = new Writer(); + /** 主 Writer 按原 GSUB 表大小预分配容量,避免大 GSUB(令东千字文 GSUB 数十 KB)多次 grow 全拷贝 */ + const w = new Writer(gsubBytes.byteLength); /** Header */ w.writeUint16(1); diff --git a/backend/font_util/ot-bytes.ts b/backend/font_util/ot-bytes.ts index af3b9db..9a33695 100644 --- a/backend/font_util/ot-bytes.ts +++ b/backend/font_util/ot-bytes.ts @@ -33,10 +33,16 @@ export class OTWriter { * (思源 subsetGPOS 207 次/call 占 ~58%),原实现每次调 private ensure()——V8 对 class * private method 内联不充分,per-call 函数调用开销在百次累计下显著。改为内联容量判断 * (够用直接写,不够才调 grow),消除热路径上的函数调用。 */ - private buf: Uint8Array = new Uint8Array(2048); + /** 初始容量:默认 2048;subsetGPOS/subsetGSUB 主 Writer 按原表大小预分配避免多次 grow 全拷贝 */ + private buf: Uint8Array; private size: number = 0; private patches: Array<{ pos: number; base: number; targetGetter: () => number }> = []; + /** 预估输出容量的 Writer(省去从默认容量多次 2× 扩容的全拷贝)。initialCapacity 为 0/省略时用默认 2048。 */ + constructor(initialCapacity: number = 2048) { + this.buf = new Uint8Array(initialCapacity > 0 ? initialCapacity : 2048); + } + get length(): number { return this.size; }