perf(gpos-subset): 循环内 r.u16 改 dv.getUint16

延续 gsub-reachable/gsub-subset 的已验证模式:subsetGPOS 主函数、
readCoverageGids、readClassDefMap、serializePairPosFormat1 的循环内
连续 u16 读取从 r.u16(方法调用+边界检查)改为 dv.getUint16(off,false)。
函数级 header offset 与 errorFlag 安全网保留 r.u16/scriptListSpan。

12 字体 woff2 输出 sha 逐字节一致,SSIM 全不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-24 19:36:31 +08:00
parent 17145e05fd
commit bd68995fde

View File

@ -124,20 +124,20 @@ export function subsetGPOS(
const r = new Reader(dv);
/** ---- GPOS Header ---- */
const major = r.u16(0);
const minor = r.u16(2);
const major = dv.getUint16(0, false);
const minor = dv.getUint16(2, false);
/** 仅支持 v1.0 / v1.1v1.1 的 FeatureVariations 本 MVP 不处理,存在时降级 */
if (major !== 1 || minor > 1) return null;
const scriptListOff = r.u16(4);
const featureListOff = r.u16(6);
const lookupListOff = r.u16(8);
const scriptListOff = dv.getUint16(4, false);
const featureListOff = dv.getUint16(6, false);
const lookupListOff = dv.getUint16(8, false);
if (minor === 1 && r.u32(10) !== 0) return null;
/** ---- 解析 LookupList记录每个 lookup 的类型与 subtable 绝对偏移 ---- */
const lookupCount = r.u16(lookupListOff);
const lookupCount = dv.getUint16(lookupListOff, false);
const lookupRelOffs: number[] = [];
for (let i = 0; i < lookupCount; i++) {
lookupRelOffs.push(r.u16(lookupListOff + 2 + i * 2));
lookupRelOffs.push(dv.getUint16(lookupListOff + 2 + i * 2, false));
}
const lookups: Array<{
supported: boolean;
@ -148,19 +148,19 @@ export function subsetGPOS(
for (let i = 0; i < lookupCount; i++) {
const lOff = lookupListOff + lookupRelOffs[i];
const lookupType = r.u16(lOff);
const subTableCount = r.u16(lOff + 4);
const lookupType = dv.getUint16(lOff, false);
const subTableCount = dv.getUint16(lOff + 4, false);
const subtableAbsOffs: number[] = [];
let effectiveType = lookupType;
for (let j = 0; j < subTableCount; j++) {
const subOff = lOff + r.u16(lOff + 6 + j * 2);
const subOff = lOff + dv.getUint16(lOff + 6 + j * 2, false);
if (lookupType === LT_EXTENSION) {
if (r.u16(subOff) !== 1) {
if (dv.getUint16(subOff, false) !== 1) {
/** 无法解包的 extension标记为不支持 */
effectiveType = -1;
continue;
}
effectiveType = r.u16(subOff + 2);
effectiveType = dv.getUint16(subOff + 2, false);
subtableAbsOffs.push(subOff + r.u32(subOff + 4));
} else {
subtableAbsOffs.push(subOff);
@ -455,19 +455,21 @@ function vrCount(valueFormat: number): number {
/** 读取 coverage 表的原始 gid 列表(按 coverage index 顺序) */
function readCoverageGids(r: Reader, coverageAbs: number): number[] | null {
const fmt = r.u16(coverageAbs);
const dv = r.dv;
const fmt = dv.getUint16(coverageAbs, false);
const out: number[] = [];
if (fmt === 1) {
const count = r.u16(coverageAbs + 2);
for (let i = 0; i < count; i++) out.push(r.u16(coverageAbs + 4 + i * 2));
const count = dv.getUint16(coverageAbs + 2, false);
const base = coverageAbs + 4;
for (let i = 0; i < count; i++) out.push(dv.getUint16(base + i * 2, false));
return out;
}
if (fmt === 2) {
const rangeCount = r.u16(coverageAbs + 2);
const rangeCount = dv.getUint16(coverageAbs + 2, false);
for (let i = 0; i < rangeCount; i++) {
const recOff = coverageAbs + 4 + i * 6;
const start = r.u16(recOff);
const end = r.u16(recOff + 2);
const start = dv.getUint16(recOff, false);
const end = dv.getUint16(recOff + 2, false);
for (let g = start; g <= end; g++) out.push(g);
}
return out;
@ -581,22 +583,23 @@ function readClassDefMap(
origToNew: Map<number, number>,
): Map<number, number> | null {
const out = new Map<number, number>();
const fmt = r.u16(classDefAbs);
const dv = r.dv;
const fmt = dv.getUint16(classDefAbs, false);
if (fmt === 1) {
const startGlyph = r.u16(classDefAbs + 2);
const glyphCount = r.u16(classDefAbs + 4);
const startGlyph = dv.getUint16(classDefAbs + 2, false);
const glyphCount = dv.getUint16(classDefAbs + 4, false);
for (let i = 0; i < glyphCount; i++) {
const ng = remapGid(startGlyph + i, origToNew);
if (ng < 0) continue;
out.set(ng, r.u16(classDefAbs + 6 + i * 2));
out.set(ng, dv.getUint16(classDefAbs + 6 + i * 2, false));
}
} else if (fmt === 2) {
const rangeCount = r.u16(classDefAbs + 2);
const rangeCount = dv.getUint16(classDefAbs + 2, false);
for (let i = 0; i < rangeCount; i++) {
const recOff = classDefAbs + 4 + i * 6;
const start = r.u16(recOff);
const end = r.u16(recOff + 2);
const cls = r.u16(recOff + 4);
const start = dv.getUint16(recOff, false);
const end = dv.getUint16(recOff + 2, false);
const cls = dv.getUint16(recOff + 4, false);
for (let g = start; g <= end; g++) {
const ng = remapGid(g, origToNew);
if (ng >= 0) out.set(ng, cls);
@ -705,7 +708,8 @@ function serializePairPosFormat1(
): boolean {
const firstGids = readCoverageGids(r, subAbs + coverageOff);
if (!firstGids) return false;
const pairSetCount = r.u16(subAbs + 8);
const dv = r.dv;
const pairSetCount = dv.getUint16(subAbs + 8, false);
const vr1 = vrCount(valueFormat1);
const vr2 = vrCount(valueFormat2);
@ -714,13 +718,13 @@ function serializePairPosFormat1(
for (let idx = 0; idx < pairSetCount; idx++) {
const newFirstGid = remapGid(firstGids[idx], origToNew);
if (newFirstGid < 0) continue;
const pairSetOff = subAbs + r.u16(subAbs + 10 + idx * 2);
const pairValueCount = r.u16(pairSetOff);
const pairSetOff = subAbs + dv.getUint16(subAbs + 10 + idx * 2, false);
const pairValueCount = dv.getUint16(pairSetOff, false);
/** 收集 secondGlyph 也在子集中的对 */
const keptSeconds: Array<{ newSecondGid: number; recAbs: number }> = [];
for (let p = 0; p < pairValueCount; p++) {
const recAbs = pairSetOff + 2 + p * (2 + vr1 * 2 + vr2 * 2);
const newSecondGid = remapGid(r.u16(recAbs), origToNew);
const newSecondGid = remapGid(dv.getUint16(recAbs, false), origToNew);
if (newSecondGid < 0) continue;
keptSeconds.push({ newSecondGid, recAbs });
}