From 3103dd5db130e3a8962301268088313dbfd8b02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Fri, 24 Jul 2026 11:23:06 +0800 Subject: [PATCH] =?UTF-8?q?perf(gsub):=20ScriptList/FeatureList=20?= =?UTF-8?q?=E6=95=B4=E5=9D=97=E6=8B=B7=E8=B4=9D=E5=BF=AB=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=EF=BC=88=E5=90=8C=20GPOS=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit subsetGSUB 复用 scriptListSpan/featureListSpan,span 自洽且不越过相邻 list header offset 时整块 subarray 拷贝。交错判定用 SL span≤featureListOff、 FL span≤lookupListOff(LookupList subtable 散落,header offset 不能代表其 字节范围,故不用「下一更大 offset」作上界)。霞鹜文楷 GSUB 物理交错 (LL --- backend/font_util/gsub-subset.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/backend/font_util/gsub-subset.ts b/backend/font_util/gsub-subset.ts index 31c3df3..ed5f763 100644 --- a/backend/font_util/gsub-subset.ts +++ b/backend/font_util/gsub-subset.ts @@ -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 offset」:SL 上界为 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;