mirror of
https://github.com/2234839/web-font.git
synced 2026-09-17 06:35:49 +08:00
GPOS/GSUB 表类 read 原用 reader.readBytes (= new Uint8Array(view.buffer, off, len).slice()), .slice() 拷贝整个表字节(初夏 GPOS 68KB、GSUB 31KB)。V8 中 68KB slice 耗 ~40μs, 而 subarray 零拷贝仅 ~0.3μs(130× 差距)。 GPOS/GSUB 是原始字节透传:subsetGPOS/subsetGSUB 只读 OTReader + 写全新 OTWriter, write 只 writeBytes 只读,optimize 不碰,整个 read→optimize→subset→write 生命周期 从不原地修改底层字节,故可安全返回共享 ArrayBuffer 的 subarray 视图。 初夏 full read 0.124→0.063ms (-49%),readBuffer 0.165→0.098ms。 全基准 SSIM 与输出字节完全不变。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
import fs from 'node:fs';
|
||
import { Font } from './vendor/fonteditor-core/lib/ttf/font.js';
|
||
import { probeGsubAndCmap, collectReachableGsubTargets } from './backend/font_util/gsub-probe.js';
|
||
|
||
const fontBuf = fs.readFileSync('/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf');
|
||
const ab = fontBuf.buffer.slice(fontBuf.byteOffset, fontBuf.byteOffset + fontBuf.byteLength) as ArrayBuffer;
|
||
const text = ',。!?、;:“”‘’';
|
||
const codePoints = Array.from(text);
|
||
const option = { sourceType: 'ttf' as const, outType: 'woff2' as const };
|
||
|
||
function timeOnce<T>(label: string, fn: () => T, runs = 1): T {
|
||
// warmup
|
||
for (let i = 0; i < 3; i++) fn();
|
||
const t0 = performance.now();
|
||
let res: T;
|
||
for (let i = 0; i < runs; i++) res = fn();
|
||
const t1 = performance.now();
|
||
console.log(`${label}: ${((t1 - t0) / runs).toFixed(2)}ms`);
|
||
return res!;
|
||
}
|
||
|
||
// 阶段拆解(best of 5)
|
||
function median(arr: number[]) { return arr.slice().sort((a,b)=>a-b)[Math.floor(arr.length/2)]; }
|
||
|
||
function bench(label: string, fn: () => void, iters = 7) {
|
||
for (let i = 0; i < 3; i++) fn(); // warmup
|
||
const ts: number[] = [];
|
||
for (let i = 0; i < iters; i++) {
|
||
const t0 = performance.now();
|
||
fn();
|
||
ts.push(performance.now() - t0);
|
||
}
|
||
console.log(`${label}: ${median(ts).toFixed(3)}ms (min ${Math.min(...ts).toFixed(3)})`);
|
||
}
|
||
|
||
// 1. probe
|
||
bench('1.probe', () => probeGsubAndCmap(ab, codePoints, option.sourceType));
|
||
|
||
// 2. Font.create
|
||
bench('2.Font.create', () => Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true }));
|
||
|
||
// 完整 Font.create 拿到对象
|
||
const font = Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true });
|
||
|
||
// 3. optimize
|
||
bench('3.optimize', () => (font as any).optimize());
|
||
|
||
// 4. write (woff2)
|
||
const optimized = (font as any).optimize();
|
||
bench('5.write(woff2)', () => {
|
||
const f = Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true });
|
||
const o = (f as any).optimize();
|
||
o.write({ type: 'woff2', hinting: false } as any);
|
||
});
|
||
|
||
// 端到端对比
|
||
bench('E2E', () => {
|
||
const f = Font.create(ab, { type: option.sourceType, subset: codePoints as any, kerning: true });
|
||
const o = (f as any).optimize();
|
||
o.write({ type: 'woff2', hinting: false } as any);
|
||
}, 5);
|