perf(gsub): ScriptList/FeatureList 整块拷贝快路径(同 GPOS)

subsetGSUB 复用 scriptListSpan/featureListSpan,span 自洽且不越过相邻 list
header offset 时整块 subarray 拷贝。交错判定用 SL span≤featureListOff、
FL span≤lookupListOff(LookupList subtable 散落,header offset 不能代表其
字节范围,故不用「下一更大 offset」作上界)。霞鹜文楷 GSUB 物理交错
(LL<SL<FL 且 ScriptTable 跨越 FL) 自动降级 serialize,字节与优化前完全一致。

收益:初夏 GSUB 11474→8474B(-26%)、FiraCode 15504→14878B(-4%),
保留 fontTools FeatureTable 去重。霞鹜降级路径 cmp 逐字节一致。全基准 SSIM 不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
崮生(子虚) 2026-07-24 11:23:06 +08:00
parent ccc2e995c8
commit 3103dd5db1

View File

@ -21,7 +21,7 @@
* @reference https://learn.microsoft.com/en-us/typography/opentype/spec/gsub
*/
import { OTWriter as Writer, OTReader as Reader, serializeScriptList, serializeFeatureList } from "./ot-bytes.js";
import { OTWriter as Writer, OTReader as Reader, serializeScriptList, serializeFeatureList, scriptListSpan, featureListSpan } from "./ot-bytes.js";
/** GSUB lookup 类型常量 */
const LT_SINGLE = 1;
@ -1252,8 +1252,28 @@ export function subsetGSUB(
* - gid
* - type5 gid coverage
*/
const scriptListBytes = serializeScriptList(r, scriptListOff);
const featureListBytes = serializeFeatureList(r, featureListOff);
/** ScriptList / FeatureList subsetGPOS
* glyphId list
* subarray fontTools
*
* span list header offsetSL featureListOff
* FL lookupListOff GSUB LookupList(10) < ScriptList(38)
* < FeatureList(76)ScriptTable FeatureList LookupList
* FeatureList lookupListOff(10) < featureListOff(76)FL span
* > lookupListOff SL span(350) fl(76)
* serialize使 offset LookupList subtable
* header offset */
const slSpan = scriptListSpan(r, scriptListOff);
const slContiguous = slSpan >= 0 && scriptListOff + slSpan <= featureListOff;
const scriptListBytes = slContiguous
? gsubBytes.subarray(scriptListOff, scriptListOff + slSpan)
: serializeScriptList(r, scriptListOff);
const flSpan = featureListSpan(r, featureListOff);
const flContiguous = flSpan >= 0 && featureListOff + flSpan <= lookupListOff;
const featureListBytes = flContiguous
? gsubBytes.subarray(featureListOff, featureListOff + flSpan)
: serializeFeatureList(r, featureListOff);
r.clearError();
/** ScriptList/FeatureList 解析失败(异常表)则整体保留原始 GSUB 字节(安全降级) */
if (!scriptListBytes || !featureListBytes) return gsubBytes;