diff --git a/_p_check.ts b/_p_check.ts deleted file mode 100644 index d4943ff..0000000 --- a/_p_check.ts +++ /dev/null @@ -1,16 +0,0 @@ -import fs from 'node:fs'; -import { Font } from './vendor/fonteditor-core/lib/ttf/font.js'; -const buf = fs.readFileSync('/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf'); -const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; -const cps = Array.from(',。!?、;:“”‘’'); -const font = Font.create(ab, { type: 'ttf', subset: cps as any, kerning: true }); -const opt = (font as any).optimize(); -const ttf = opt.get(); -console.log('GPOS present:', !!ttf.GPOS, 'len:', ttf.GPOS && ttf.GPOS.length); -console.log('GSUB present:', !!ttf.GSUB); -console.log('numGlyphs:', ttf.glyf.length); -const ttfBuf = opt.write({ type: 'ttf', hinting: false } as any); -console.log('ttfBuf type:', ttfBuf.constructor.name, 'len:', ttfBuf.length); -// 完整 woff2 via font.write -const woff2 = opt.write({ type: 'woff2', hinting: false } as any); -console.log('woff2 (font.write):', woff2.length, 'type:', woff2.constructor.name); diff --git a/_p_chxia.ts b/_p_chxia.ts deleted file mode 100644 index 04b1464..0000000 --- a/_p_chxia.ts +++ /dev/null @@ -1,61 +0,0 @@ -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(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); diff --git a/_p_chxia_break.cjs b/_p_chxia_break.cjs deleted file mode 100644 index 6cea5c4..0000000 --- a/_p_chxia_break.cjs +++ /dev/null @@ -1,9 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const ttf2woff2 = require('./vendor/fonteditor-core/lib/ttf2woff2.js'); - -const fontBuf = fs.readFileSync('./backend/font_util/标点测试字体/初夏明朝.ttf'); -const text = ',。!?、:;'; - -// 复刻 fontSubset 的关键阶段 -const { fontSubset } = require('./backend-ts-compiled.cjs'); diff --git a/_p_e2e.ts b/_p_e2e.ts deleted file mode 100644 index 7988380..0000000 --- a/_p_e2e.ts +++ /dev/null @@ -1,24 +0,0 @@ -import fs from 'node:fs'; -import { fontSubset } from './backend/font_util/font.js'; - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } -function bench(label: string, fn: () => void, iters = 15) { - for (let i = 0; i < 5; i++) fn(); - 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)})`); -} - -for (const [name, p, text] of [ - ['初夏纯标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', ',。!?、;:“”‘’'], - ['初夏汉字标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', '你好,世界!今天天气不错。'], - ['思源8字', './font/思源黑体.ttf', '天地玄黄宇宙洪荒'], - ['令东千字', './font/令东齐伋复刻体.ttf', '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈'], -] as const) { - const buf = fs.readFileSync(p); - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; - console.log(`\n=== ${name} ===`); - let outLen = 0; - bench(' fontSubset(woff2)', () => { const o = fontSubset(ab, text, { sourceType: 'ttf' as const, outType: 'woff2' as const }); outLen = o.length; }); - console.log(` woff2 out: ${outLen}B`); -} diff --git a/_p_gposread.ts b/_p_gposread.ts deleted file mode 100644 index 3b43d8a..0000000 --- a/_p_gposread.ts +++ /dev/null @@ -1,31 +0,0 @@ -import fs from 'node:fs'; -const buf = fs.readFileSync('/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf'); -const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; -const view = new DataView(ab); -const numTables = view.getUint16(4, false); -let gposOff=0,gposLen=0; -for (let i=0;ix-y)[Math.floor(a.length/2)];} - -// 方式A: readBytes 的 slice 方式 -const tA:number[]=[]; -for(let i=0;i<30;i++){const t0=performance.now();const bytes=new Uint8Array(ab,gposOff,gposLen).slice();tA.push(performance.now()-t0);} -console.log(`slice(${gposLen}): ${median(tA).toFixed(4)}ms`); - -// 方式B: new Uint8Array + set -const tB:number[]=[]; -for(let i=0;i<30;i++){const t0=performance.now();const b=new Uint8Array(gposLen);b.set(new Uint8Array(ab,gposOff,gposLen));tB.push(performance.now()-t0);} -console.log(`new+set: ${median(tB).toFixed(4)}ms`); - -// 方式C: subarray (零拷贝,但要确认后续不改原 buffer) -const tC:number[]=[]; -for(let i=0;i<30;i++){const t0=performance.now();const b=new Uint8Array(ab,gposOff,gposLen);tC.push(performance.now()-t0);} -console.log(`subarray(view): ${median(tC).toFixed(4)}ms`); - -// 方式D: copyBytesTo (Node 较新) -const tD:number[]=[]; -const src=new Uint8Array(ab); -for(let i=0;i<30;i++){const b=new Uint8Array(gposLen);const t0=performance.now();src.copyBytesTo?src.copyBytesTo(b,0,gposOff,gposOff+gposLen):null;tD.push(performance.now()-t0);} -console.log(`copyBytesTo: ${median(tD).toFixed(4)}ms`); diff --git a/_p_gsub_cmp.ts b/_p_gsub_cmp.ts deleted file mode 100644 index bc335ab..0000000 --- a/_p_gsub_cmp.ts +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'node:fs'; -import { Font } from './vendor/fonteditor-core/lib/ttf/font.js'; -import { subsetGSUB } from './backend/font_util/gsub-subset.js'; -import crypto from 'node:crypto'; - -const cases: [string, string, string][] = [ - ['初夏纯标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', ',。!?、;:“”‘’'], - ['初夏汉字', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', '你好,世界!今天天气不错。'], - ['思源8字', './font/思源黑体.ttf', '天地玄黄宇宙洪荒'], - ['令东千字', './font/令东齐伋复刻体.ttf', '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈'], - ['FiraCode', '/mnt/d/字体资源/FiraCode/FiraCode-Medium.ttf', '=> !== >= <= ==='], -]; -for (const [name, p, text] of cases) { - if (!fs.existsSync(p)) { console.log(`${name}: 跳过(文件不存在)`); continue; } - const buf = fs.readFileSync(p); - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; - const cps = Array.from(text).map(c => c.codePointAt(0)!); - const font = Font.create(ab, { type: 'ttf', subset: cps as any, kerning: true }); - const ttf = font.get(); - const subsetGids: number[] = ttf.subsetGids ?? []; - const origToNew = new Map(); - for (let i = 0; i < subsetGids.length; i++) origToNew.set(subsetGids[i], i); - const gsub = ttf.GSUB; - if (!gsub) { console.log(`${name}: 无 GSUB`); continue; } - const b = gsub instanceof Uint8Array ? gsub : new Uint8Array(gsub); - const out = subsetGSUB(b, origToNew); - const hash = crypto.createHash('sha256').update(out).digest('hex').slice(0,16); - console.log(`${name}: gsubOut=${out.length}B hash=${hash}`); -} diff --git a/_p_gsub_struct.ts b/_p_gsub_struct.ts deleted file mode 100644 index 503e958..0000000 --- a/_p_gsub_struct.ts +++ /dev/null @@ -1,23 +0,0 @@ -import fs from 'node:fs'; -const buf = fs.readFileSync('/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf'); -const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; -const view = new DataView(ab); -// 找 GSUB -const numTables = view.getUint16(4, false); -let off=0; -for (let i=0;i>>16), '.', (v&0xffff)); -console.log('lookupCount:', lookupCount); -for (let i=0;i c.codePointAt(0)!); - -// 先拿到 reader + tables 目录 -const r0 = new (TTFReader as any)({ subset: cps, kerning: true }); -// 手动复刻 readBuffer 前半,拿 reader + tables -const Reader = (await import('./vendor/fonteditor-core/lib/ttf/reader.js')).default.default ?? (await import('./vendor/fonteditor-core/lib/ttf/reader.js')).default; -const Directory = (await import('./vendor/fonteditor-core/lib/ttf/directory.js')).default.default ?? (await import('./vendor/fonteditor-core/lib/ttf/directory.js')).default; - -function makeReader() { - const reader = new Reader(ab, 0, ab.byteLength, false); - reader.offset = 12; - return reader; -} - -// 各表独立计时 -function timeTable(tableName: string, iters = 15) { - const TableClass = support[tableName]; - if (!TableClass) { console.log(` ${tableName}: 无 support 类`); return; } - // warmup + 计时 - const ts: number[] = []; - for (let i = 0; i < 5; i++) { - const reader = makeReader(); - const dir = new Directory(12).read(reader, {}); - if (!dir[tableName]) return; - const tc = new TableClass(dir[tableName].offset); - tc.read(reader, { tables: dir, readOptions: { subset: cps, kerning: true } } as any); - } - for (let i = 0; i < iters; i++) { - const reader = makeReader(); - const dir = new Directory(12).read(reader, {}); - const tc = new TableClass(dir[tableName].offset); - const t0 = performance.now(); - tc.read(reader, { tables: dir, readOptions: { subset: cps, kerning: true } } as any); - ts.push(performance.now() - t0); - } - ts.sort((a,b)=>a-b); - console.log(` ${tableName}: ${ts[Math.floor(ts.length/2)].toFixed(3)}ms (min ${ts[0].toFixed(3)})`); -} - -console.log('=== 初夏标点 各表 read 耗时 ==='); -for (const t of ['head','hhea','maxp','OS/2','name','cmap','hmtx','vmtx','GPOS','GSUB','post']) { - timeTable(t); -} diff --git a/_p_perTable2.ts b/_p_perTable2.ts deleted file mode 100644 index 84f6801..0000000 --- a/_p_perTable2.ts +++ /dev/null @@ -1,37 +0,0 @@ -import fs from 'node:fs'; -import * as TTFReaderNS from './vendor/fonteditor-core/lib/ttf/ttfreader.js'; -const TTFReader: any = TTFReaderNS.default.default ?? TTFReaderNS.default; -const supportNS: any = await import('./vendor/fonteditor-core/lib/ttf/table/support.js'); -const support: any = supportNS.default.default ?? supportNS.default; - -const buf = fs.readFileSync('/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf'); -const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; -const cps = Array.from(',。!?、;:“”‘’').map(c => c.codePointAt(0)!); - -// monkey-patch 每个表类的 read,累加耗时 -const stats: Record = {}; -for (const tn of Object.keys(support)) { - const TableClass: any = support[tn]; - if (!TableClass || !TableClass.prototype || !TableClass.prototype.read) continue; - const orig = TableClass.prototype.read; - TableClass.prototype.read = function(reader: any, ttf: any) { - const t0 = performance.now(); - const r = orig.call(this, reader, ttf); - const dt = performance.now() - t0; - (stats[tn] ??= []).push(dt); - return r; - }; -} - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } - -// 多次跑 readBuffer 累加统计 -for (let i = 0; i < 25; i++) { - const r = new (TTFReader as any)({ subset: cps, kerning: true }); - r.readBuffer(ab); -} - -console.log('=== 初夏标点 各表 read 耗时(25次中位数)==='); -const rows = Object.entries(stats).map(([n, a]) => ({ n, med: median(a), min: Math.min(...a) })); -rows.sort((a,b)=>b.med-a.med); -for (const r of rows) console.log(` ${r.n}: ${r.med.toFixed(3)}ms (min ${r.min.toFixed(3)}, ${stats[r.n].length}次)`); diff --git a/_p_read.ts b/_p_read.ts deleted file mode 100644 index 867c364..0000000 --- a/_p_read.ts +++ /dev/null @@ -1,30 +0,0 @@ -import fs from 'node:fs'; -import * as TTFReaderNS from './vendor/fonteditor-core/lib/ttf/ttfreader.js'; -const TTFReader: any = (TTFReaderNS as any).default.default ?? (TTFReaderNS as any).default ?? TTFReaderNS; - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } -function bench(label: string, mk: () => () => void, iters = 9) { - const fn = mk(); - for (let i = 0; i < 3; i++) fn(); - const ts: number[] = []; - for (let i = 0; i < iters; i++) { const f = mk(); const t0 = performance.now(); f(); ts.push(performance.now() - t0); } - console.log(`${label}: ${median(ts).toFixed(3)}ms (min ${Math.min(...ts).toFixed(3)})`); -} - -for (const [name, p, text] of [ - ['初夏标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', ',。!?、;:“”‘’'], - ['思源8字', './font/思源黑体.ttf', '天地玄黄宇宙洪荒'], -] as const) { - const buf = fs.readFileSync(p); - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; - const cps = Array.from(text).map(c => c.codePointAt(0)!); - console.log(`\n=== ${name} ===`); - - bench(' readBuffer', () => () => { const r = new (TTFReader as any)({ subset: cps, kerning: true }); r.readBuffer(ab); }); - bench(' resolveGlyf', () => () => { - const r = new (TTFReader as any)({ subset: cps, kerning: true }); - const ttf = r.readBuffer(ab); - r.resolveGlyf(ttf); - }); - bench(' full read', () => () => { const r = new (TTFReader as any)({ subset: cps, kerning: true }); r.read(ab); }); -} diff --git a/_p_sgpos.ts b/_p_sgpos.ts deleted file mode 100644 index 64fca86..0000000 --- a/_p_sgpos.ts +++ /dev/null @@ -1,32 +0,0 @@ -import fs from 'node:fs'; -import { Font } from './vendor/fonteditor-core/lib/ttf/font.js'; -import { subsetGPOS } from './backend/font_util/gpos-subset.js'; -import { subsetGSUB } from './backend/font_util/gsub-subset.js'; - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } - -const p = '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf'; -const text = ',。!?、;:“”‘’'; -const buf = fs.readFileSync(p); -const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; -const codePoints = Array.from(text).map(c => c.codePointAt(0)!); - -// 一次性拿到 subsetGids + GPOS 字节 + origToNew -const font = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true }); -const preOpt = font.get(); -const subsetGids: number[] = preOpt.subsetGids ?? []; -const origToNew = new Map(); -for (let i = 0; i < subsetGids.length; i++) origToNew.set(subsetGids[i], i); -const gposBytes = preOpt.GPOS instanceof Uint8Array ? preOpt.GPOS : new Uint8Array(preOpt.GPOS); -const gsubBytes = preOpt.GSUB instanceof Uint8Array ? preOpt.GSUB : new Uint8Array(preOpt.GSUB); -console.log('subsetGids:', subsetGids.length, 'GPOS:', gposBytes.length, 'GSUB:', gsubBytes.length); - -function bench(label: string, fn: () => void, iters = 25) { - for (let i = 0; i < 8; i++) fn(); - 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)})`); -} - -bench('subsetGPOS', () => subsetGPOS(gposBytes, origToNew)); -bench('subsetGSUB', () => subsetGSUB(gsubBytes, origToNew)); diff --git a/_p_stages.ts b/_p_stages.ts deleted file mode 100644 index 47da29d..0000000 --- a/_p_stages.ts +++ /dev/null @@ -1,85 +0,0 @@ -import fs from 'node:fs'; -import { Font } from './vendor/fonteditor-core/lib/ttf/font.js'; -import { probeGsubAndCmap } from './backend/font_util/gsub-probe.js'; -import { collectReachableGsubTargets } from './backend/font_util/gsub-reachable.js'; -import { subsetGPOS } from './backend/font_util/gpos-subset.js'; -import { subsetGSUB } from './backend/font_util/gsub-subset.js'; - -function rewriteLayoutTablesForSubset(opt: any, subsetGids: number[]) { - const ttf = opt.get(); - const origToNew = new Map(); - for (let i = 0; i < subsetGids.length; i++) origToNew.set(subsetGids[i], i); - const og = ttf.GPOS; - if (og) { const b = og instanceof Uint8Array ? og : new Uint8Array(og); if (b.byteLength > 0) { const r = subsetGPOS(b, origToNew); if (r) ttf.GPOS = r; } } - const os = ttf.GSUB; - if (os) { const b = os instanceof Uint8Array ? os : new Uint8Array(os); if (b.byteLength > 0) { ttf.GSUB = subsetGSUB(b, origToNew); } } -} - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } - -const p = '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf'; -const text = ',。!?、;:“”‘’'; -const buf = fs.readFileSync(p); -const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; -const codePoints = Array.from(text).map(c => c.codePointAt(0)!); - -// 准备共享对象 -function build() { - const probe = probeGsubAndCmap(ab, codePoints, 'ttf'); - let extraSubsetGids: number[] | undefined; - if (probe.ok) { - const seed = new Set([0]); - for (const cp of codePoints) { const g = probe.lookup.get(cp); if (g !== undefined) seed.add(g); } - const r = collectReachableGsubTargets(probe.gsubBytes!, seed); - if (r.size > 0) extraSubsetGids = [...r]; - } - return { extraSubsetGids }; -} - -const { extraSubsetGids } = build(); - -function timePhase(label: string, fn: () => void, iters = 13) { - for (let i = 0; i < 4; i++) fn(); - 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`); -} - -console.log('=== 初夏纯标点 各阶段(独立 best-of)==='); -timePhase('1.probe', () => probeGsubAndCmap(ab, codePoints, 'ttf')); - -let fontRef: any; -timePhase('2.Font.create', () => { fontRef = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true, extraSubsetGids }); }); - -const font = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true, extraSubsetGids }); -const preOpt = font.get(); -const subsetGids: number[] = preOpt.subsetGids ?? []; -console.log(` subsetGids count: ${subsetGids.length}`); - -timePhase('3.optimize', () => { const f = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true, extraSubsetGids }); f.optimize(); }); - -const opt = font.optimize(); -// rewrite 内部拆解 -timePhase('4a.subsetGPOS', () => { - const f = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true, extraSubsetGids }); - const o = f.optimize(); - // 复刻 rewriteLayoutTablesForSubset 的 GPOS 部分 - const ttf = o.get(); - const origToNew = new Map(); - for (let i = 0; i < subsetGids.length; i++) origToNew.set(subsetGids[i], i); - const g = ttf.GPOS; - if (g) { subsetGPOS(g instanceof Uint8Array ? g : new Uint8Array(g), origToNew); } -}); - -timePhase('4.rewriteLayoutTables', () => { - const f = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true, extraSubsetGids }); - const o = f.optimize(); - rewriteLayoutTablesForSubset(o, subsetGids); -}); - -timePhase('5.write(woff2)', () => { - const f = Font.create(ab, { type: 'ttf', subset: codePoints as any, kerning: true, extraSubsetGids }); - const o = f.optimize(); - rewriteLayoutTablesForSubset(o, subsetGids); - o.write({ type: 'woff2', hinting: false } as any); -}); diff --git a/_p_tables.ts b/_p_tables.ts deleted file mode 100644 index 50a49a2..0000000 --- a/_p_tables.ts +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'node:fs'; -import * as TTFReaderNS from './vendor/fonteditor-core/lib/ttf/ttfreader.js'; -const TTFReader: any = TTFReaderNS.default.default ?? TTFReaderNS.default; - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } - -for (const [name, p, text] of [ - ['初夏标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', ',。!?、;:“”‘’'], -] as const) { - const buf = fs.readFileSync(p); - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; - const cps = Array.from(text).map(c => c.codePointAt(0)!); - console.log(`\n=== ${name} (字体 ${ (buf.length/1024/1024).toFixed(1)}MB) ===`); - - // 读 directory 拿各表大小 - const view = new DataView(ab); - const numTables = view.getUint16(4, false); - const tables: {name:string; offset:number; length:number}[] = []; - for (let i = 0; i < numTables; i++) { - const rec = 12 + i*16; - const tag = String.fromCharCode(view.getUint8(rec), view.getUint8(rec+1), view.getUint8(rec+2), view.getUint8(rec+3)); - const off = view.getUint32(rec+8, false); - const len = view.getUint32(rec+12, false); - tables.push({name: tag, offset: off, length: len}); - } - tables.sort((a,b)=>b.length-a.length); - console.log('表大小 TOP:'); - for (const t of tables.slice(0,12)) console.log(` ${t.name}: ${(t.length/1024).toFixed(0)}KB`); -} diff --git a/_p_woff2enc.ts b/_p_woff2enc.ts deleted file mode 100644 index 07a7a18..0000000 --- a/_p_woff2enc.ts +++ /dev/null @@ -1,30 +0,0 @@ -import fs from 'node:fs'; -import { Font } from './vendor/fonteditor-core/lib/ttf/font.js'; -const woff2enc: any = await import('./vendor/fonteditor-core/woff2/woff2-encode.js'); - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } -function bench(label: string, fn: () => void, iters = 15) { - for (let i = 0; i < 5; i++) fn(); - 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)})`); -} - -for (const [name, p, text] of [ - ['初夏标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', ',。!?、;:“”‘’'], - ['思源8字', './font/思源黑体.ttf', '天地玄黄宇宙洪荒'], - ['令东千字', './font/令东齐伋复刻体.ttf', '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈'], -] as const) { - const buf = fs.readFileSync(p); - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; - const cps = Array.from(text); - const font = Font.create(ab, { type: 'ttf', subset: cps as any, kerning: true }); - const opt = (font as any).optimize(); - const ttfBuf = opt.write({ type: 'ttf', hinting: false } as any); - console.log(`\n=== ${name} (ttf ${(ttfBuf.length/1024).toFixed(1)}KB) ===`); - bench(' TTFWriter.write(ttf)', () => opt.write({ type: 'ttf', hinting: false } as any)); - bench(' encodeTTFToWOFF2(全部)', () => woff2enc.encodeTTFToWOFF2(ttfBuf)); - // 单独 transformGlyfAndLoca?需拆解,先量总 encode - const w2 = woff2enc.encodeTTFToWOFF2(ttfBuf); - console.log(` woff2 out: ${(w2.length)}B`); -} diff --git a/_p_write.ts b/_p_write.ts deleted file mode 100644 index 31b2855..0000000 --- a/_p_write.ts +++ /dev/null @@ -1,28 +0,0 @@ -import fs from 'node:fs'; -import { Font } from './vendor/fonteditor-core/lib/ttf/font.js'; - -function median(a: number[]) { return a.slice().sort((x,y)=>x-y)[Math.floor(a.length/2)]; } -function bench(label: string, fn: () => void, iters = 9) { - for (let i = 0; i < 3; i++) fn(); - 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)})`); -} - -for (const [name, p, text] of [ - ['初夏标点', '/mnt/d/字体资源/初夏明朝/初夏明朝-Regular.ttf', ',。!?、;:“”‘’'], - ['思源8字', './font/思源黑体.ttf', '天地玄黄宇宙洪荒'], - ['令东千字', './font/令东齐伋复刻体.ttf', '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈'], -] as const) { - const buf = fs.readFileSync(p); - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset+buf.byteLength) as ArrayBuffer; - const cps = Array.from(text); - console.log(`\n=== ${name} (${text.length}字) ===`); - - bench(' create', () => Font.create(ab, { type: 'ttf', subset: cps as any, kerning: true })); - const font = Font.create(ab, { type: 'ttf', subset: cps as any, kerning: true }); - const opt = (font as any).optimize(); - bench(' write-woff2', () => opt.write({ type: 'woff2', hinting: false } as any)); - // write-ttf 对比 - bench(' write-ttf', () => opt.write({ type: 'ttf', hinting: false } as any)); -}