|
OpenType GSUB Substitution Engine Glyph substitution capability surface (v2.119.43 - v2.119.50)
|
Arabic Shaping CFF / OpenType Subsetting |
|
HotPDF 内部的 OpenType GSUB(Glyph SUBstitution)engine 允许调用方查询、驱动并嵌入 OpenType 字体声明的各类 glyph substitution,包括 ligatures、stylistic alternates、contextual variants、Arabic / Indic shaping forms、CJK alternate forms 等。OpenType GSUB LookupType 1 到 8 均已实现,并作为 capability-only 查询表面公开;调用方负责驱动文本输出,并决定向页面内容流写入哪个 substitute glyph
Public API type TGSUBStringArray = array of AnsiString;
// LookupType 1 - Single Substitution (one glyph -> one glyph) function GetSingleSubstituteGlyph(InputGID: Word; const FeatureTag: AnsiString): Word;
// LookupType 2 - Multiple Substitution (one glyph -> sequence of glyphs) function GetMultipleSubstituteGlyphs(InputGID: Word; const FeatureTag: AnsiString; var OutGIDs: array of Word): Boolean;
// LookupType 3 - Alternate Substitution (one glyph -> one of N alternates) function GetAlternateGlyphCount(InputGID: Word; const FeatureTag: AnsiString): Integer; function GetAlternateGlyph(InputGID: Word; const FeatureTag: AnsiString; AlternateIndex: Integer): Word;
// LookupType 4 - Ligature Substitution (N glyphs -> one ligature) function ApplyLigatureSubstitution(const InputGIDs: array of Word; StartIndex: Integer; const FeatureTag: AnsiString; out OutGID: Word; out ConsumedCount: Integer): Boolean;
// LookupType 5 + 6 - Contextual / Chained Contextual Substitution function ApplyContextualSubst(const InputGIDs: array of Word; StartIndex: Integer; const FeatureTag: AnsiString; var OutGIDs: array of Word; out ConsumedLen: Integer): Boolean;
// LookupType 8 - Reverse Chained Contextual Single Substitution function ApplyReverseChainedContextualSubst(const InputGIDs: array of Word; StartIndex: Integer; const FeatureTag: AnsiString; out OutGID: Word): Boolean;
// Script / LangSys selection (Phase 7) procedure SetGSUBScript(const ScriptTag: AnsiString); procedure SetGSUBLanguage(const LangTag: AnsiString); function GetGSUBScripts: TGSUBStringArray; function GetGSUBLanguages(const ScriptTag: AnsiString): TGSUBStringArray; function GetGSUBFeatures(const ScriptTag, LangTag: AnsiString): TGSUBStringArray;
// TTF subsetter closure (Phase 9) procedure MarkUnicodeGlyphUsed(GID: Word);
说明 经
整体防御性契约:没有 GSUB table 的字体、非 4-byte feature tags、所选 script / language 未公开的 feature、没有 subtable 覆盖的 GID、以及被 LookupFlag 忽略的输入 glyph,都会返回安全 no-op(False / OutGID = InputGID / empty OutGIDs / ConsumedCount = 1),因此调用方不会因为常规“没有适用替换”的情况看到异常
LookupType matrix LookupType 1(Single Substitution) - 一个 glyph 映射到一个 substitute。典型 feature 包括 LookupType 2(Multiple Substitution) - 一个 glyph 拆分为一串 substitute glyphs。典型使用者是 LookupType 3(Alternate Substitution)——一个字形映射到 N 个备选字形之一。典型特性: LookupType 4(Ligature Substitution) - N 个输入 glyph 合并为一个连字。典型 feature 包括 LookupType 5(Contextual Substitution) + LookupType 6(Chained Contextual Substitution) - 匹配输入 glyph 序列,并在匹配内部的特定位置分派 nested lookups。三种 Format 变体(1 字面序列、2 ClassDef 序列、3 Coverage 序列)均已实现;SequenceLookupRecord dispatcher 会重新进入 LookupList,并通过实时 MatchPositions 跟踪处理 Single / Multiple / Alternate(first)/ Ligature nested lookups。典型 feature 包括 LookupType 7(Extension Substitution) - OpenType 规范为 substitution subtable 位于 LookupList 16-bit 范围之外的字体定义的纯间接层。每个公共 API 都会透明跟随 32-bit Offset32 间接引用到真实 LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8 subtable。该机制解锁 GSUB 超过 64 KB 的重型 CJK / Indic 字体,例如 Noto Sans CJK 和 Noto Sans Devanagari。不需要单独 API,unwrap 自动完成。 LookupType 8(Reverse Chained Contextual Single Substitution) - 上下文感知的 1:1 替换,其特殊之处在于调用方必须对多 glyph run 以反向扫描顺序应用(end -> start),因为每个替换可能依赖尚不能被替换的“未来” lookahead 上下文。典型用途:Arabic / Syriac / N'Ko / Indic 上下文 alternates,其 final form 取决于后续 glyph。使用
Script / LangSys selection 默认情况下,engine 优先使用
strict 与 fallback 语义:未知
LookupFlag honor and GDEF 每次查询都会读取每个 Lookup table 的 LookupFlag,以及
TTF subsetter closure (MarkUnicodeGlyphUsed) HotPDF v2.84.0 TTF subsetter 通过 cmap 从
把
典型工作流 (Latin small caps)
PDF.RegisterUnicodeTTF('myFont', 'C:\\Windows\\Fonts\\arial.ttf'); PDF.SetGSUBScript('latn'); PDF.SetGSUBLanguage(''); // default LangSys SmallCapGID := PDF.GetSingleSubstituteGlyph(InputGID, 'smcp'); if SmallCapGID <> InputGID then begin // emit SmallCapGID into the page content stream... PDF.MarkUnicodeGlyphUsed(SmallCapGID); // pull into subset end;
典型工作流 (Arabic LAM-ALEF ligature)
PDF.SetGSUBScript('arab'); Run := [LamGID, FathaGID, AlefGID]; // post-cmap GIDs if PDF.ApplyLigatureSubstitution(Run, 0, 'rlig', LigGID, ConsumedCount) then begin // emit LigGID + advance by ConsumedCount PDF.MarkUnicodeGlyphUsed(LigGID); end;
Scope and limitations 该 engine 是 capability-only 查询表面:它回答“GSUB 在这里会做什么”,但不会运行自动 shaping pipeline(Harfbuzz 级布局、Indic 的 cluster-aware reordering、BiDi resolution、GPOS positioning、mark attachment)。调用方负责驱动扫描循环,选择要输出的 substitute / alternate,为每个输出的 substitute GID 调用
生产端阿拉伯文 / 波斯文 / 乌尔都语塑形(LAM-ALEF 强制连字 + Arabic Presentation Forms-A)作为独立内置流水线实现,在文本输出期间自动运行,详见 阿拉伯文 / 波斯文 / 乌尔都语塑形.
Version trace v2.119.43 Single Substitution + Phase 1。v2.119.44 Multiple + Alternate(Phase 2)。v2.119.45 Ligature(Phase 3)。v2.119.46 Extension + GDEF + LookupFlag honor(Phase 4)。v2.119.47 Contextual + Chained Contextual + SequenceLookupRecord dispatcher(Phase 5)。v2.119.48 Reverse Chained Contextual,LookupType 1-8 矩阵闭合(Phase 6)。v2.119.49 Script / LangSys selection API(Phase 7)。v2.119.50 通过
另请参阅: Arabic / Persian / Urdu Shaping, CFF / OpenType Font Subsetting Functions, THotPDF.EnableFontSubsetting |