Substituční engine OpenType GSUB

Rozhraní schopností substituce glyphů (v2.119.43 - v2.119.50)

 

Arabic Shaping  CFF / OpenType Subsetting

Engine OpenType GSUB (Glyph SUBstitution) uvnitř HotPDF umožňuje volajícím dotazovat, řídit a vkládat každý druh substituce glyphů, který deklaruje font OpenType - ligatury, stylistické alternativy, kontextové varianty, arabské / indické tvary, alternativní tvary CJK a další. Každý OpenType GSUB LookupType 1 až 8 je implementovaný a zpřístupněný jako čisté dotazovací rozhraní schopností; volající řídí emisi textu a rozhoduje, který náhradní glyph zapíše do obsahového streamu stránky

 

Veřejné 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);

 

Popis

Engine se aktivuje poté, co RegisterUnicodeTTF naparsuje font a uloží jeho tabulky GSUB / GDEF / cmap do cache. Každý substituční dotaz projde řetězec ScriptList / LangSysList / FeatureList / LookupList fontu a předá řízení odpovídajícímu handleru LookupType. Dvanáct výše uvedených metod je úplné veřejné rozhraní; vše ostatní (průchod cmap, parsování ScriptList, vyhledání v Coverage table, řešení ClassDef, respektování LookupFlag, rozbalení Extension wrapperu, vnořený dispatch SequenceLookupRecord) žije za tímto rozhraním

 

Defenzivní kontrakt v celém enginu: fonty bez tabulky GSUB, feature tagy jiné než 4 bajty, funkce neinzerované vybraným script / language, GID nepokryté žádnou subtable a vstupní glyfy ignorované přes LookupFlag vracejí bezpečný no-op (False / OutGID = InputGID / prázdné OutGIDs / ConsumedCount = 1), takže volající nikdy neuvidí výjimky pro běžné případy "žádná substituce se nepoužije"

 

Matice LookupType

LookupType 1 (Single Substitution) - jeden glyph se mapuje na jednu náhradu. Kanonické funkce: salt, ss01-ss20, smcp, onum, liga při zapojení LookupType 1, plus arabské poziční tvary init / medi / fina / isol ve fontech, které je řídí přes GSUB. Použijte GetSingleSubstituteGlyph

LookupType 2 (Multiple Substitution) - jeden glyph se rozdělí na sekvenci náhradních glyphů. Kanonický uživatel: ccmp Glyph Composition / Decomposition (předkomponovaná latinská písmena s diakritikou se rozdělí na základ + kombinační značky pro downstream mark positioning). Použijte GetMultipleSubstituteGlyphs

LookupType 3 (Alternate Substitution) - jeden glyph se mapuje na jednu z N alternativ. Kanonické funkce: aalt (Access All Alternates), salt při zapojení jako Type 3, titl (Titling Alternates), stylistické sady ss01-ss20, když návrhář fontu nabízí více než jednu alternativu na slot. Použijte GetAlternateGlyphCount + GetAlternateGlyph

LookupType 4 (Ligature Substitution) - N vstupních glyphů se složí do jedné ligatury. Kanonické funkce: liga (Standard Ligatures: fi / fl / ffi / ffl), clig (Contextual Ligatures), dlig (Discretionary Ligatures), hlig (Historical Ligatures), rlig (Required Ligatures - arabské LAM-ALEF a podobné), ligatury indických písem (akhn, pres, blws, psts). Použijte ApplyLigatureSubstitution

LookupType 5 (Contextual Substitution) + LookupType 6 (Chained Contextual Substitution) - odpovídá vstupní sekvenci glyphů a dispatchuje vnořené lookupy na konkrétních pozicích uvnitř shody. Implementované jsou všechny tři varianty Format (1 doslovná sekvence, 2 sekvence ClassDef, 3 sekvence Coverage); dispatcher SequenceLookupRecord znovu vstupuje do LookupList a zpracovává vnořené lookupy Single / Multiple / Alternate (první) / Ligature se živým sledováním MatchPositions. Kanonické funkce: rclt (Required Contextual Alternates - arabské init/medi/fina/isol při řízení přes GSUB), clig, calt, indické tvarování pres / blws / psts / half / pstf / cjct. Použijte ApplyContextualSubst (jeden vstupní bod pokrývá LookupType 5 i 6)

LookupType 7 (Extension Substitution) - čistá indirection vrstva, kterou specifikace OpenType definuje pro fonty, jejichž substituční subtable leží mimo 16bitový dosah LookupList. Každé veřejné API transparentně sleduje 32bitovou indirection Offset32 ke skutečné subtable LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8. Odblokuje velké fonty CJK / Indic (Noto Sans CJK, Noto Sans Devanagari), jejichž GSUB přesahuje 64 KB. Samostatné API není potřeba - rozbalení je automatické

LookupType 8 (Reverse Chained Contextual Single Substitution) - kontextová substituce 1:1, jejímž rozlišovacím znakem je, že ji volající musí aplikovat v OPAČNÉM pořadí skenování nad víceglyphovým během (konec -> začátek), protože každá náhrada může záviset na BUDOUCÍM lookahead kontextu, který ještě nesmí být substituován. Kanonické použití: kontextové alternativy Arabic / Syriac / N'Ko / Indic, jejichž finální tvar závisí na následujícím glyphu. Použijte ApplyReverseChainedContextualSubst; volající řídí reverzní smyčku skenování

 

Výběr Script / LangSys

Ve výchozím nastavení engine preferuje script DFLT (nebo první script deklarovaný fontem) a výchozí LangSys. Zavolejte SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.) a SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., s koncovými mezerami doplněnými na 4 bajty), abyste dotazy připnuli ke konkrétnímu páru script / language. Prázdný řetězec obnoví výchozí baseline cesty. Volby přetrvávají napříč dotazy a mažou se při RegisterUnicodeTTF('', nil)

 

Sémantika strict-vs-fallback: neznámý ScriptTag způsobí, že následné dotazy vracejí prázdné no-op výsledky (aby volající poznali, že zvolený script není dostupný); neznámý LangTag spadne zpět na výchozí LangSys scriptu podle konvence OpenType. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures vyjmenují, co načtený font skutečně inzeruje

 

Respektování LookupFlag a GDEF

Každý dotaz čte LookupFlag každé Lookup table (a volitelný koncový markFilteringSet uint16, když je nastaveno useMarkFilteringSet) a přeskočí vstupní glyfy označené k ignorování. Respektované jsou bity definované specifikací: ignoreBaseGlyphs (0x0002, přeskočit GDEF class 1), ignoreLigatures (0x0004, přeskočit class 2), ignoreMarks (0x0008, přeskočit class 3), useMarkFilteringSet (0x0010) a horní bajt markAttachmentType. Parsují se ClassDef Format 1 i 2; přijímají se všechny hlavičky GDEF v1.0 / v1.1 / v1.2. Fonty bez tabulky GDEF spadnou na "žádný glyph není ignorován", takže výstup zůstává bajtově identický pro volající používající fonty bez GDEF

 

Uzavření TTF subsetteru (MarkUnicodeGlyphUsed)

TTF subsetter HotPDF z v2.84.0 odvozuje sadu použitých glyphů z FUnicodeUsedCps přes cmap. Náhradní glyfy GSUB (stylistické alternativy, ligatury, kontextové varianty - vše, co vrací 7 výše uvedených dotazovacích API) typicky nemají codepoint, který by na ně přes cmap dosáhl, takže dříve byly pro subsetter neviditelné a čtečka dokumentů místo nich vykreslila .notdef

 

Po emitování libovolného GID vráceného metodami GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst do textového streamu PDF zavolejte MarkUnicodeGlyphUsed(GID) jednou pro každé emitované GID, aby se vtáhlo do vloženého subsetu. Pomocná funkce je idempotentní, defenzivní (GID mimo rozsah se tiše zahodí) a integruje se s průchodem uzavření kompozitních glyphů z v2.84.0: volající musí označit jen top-level náhradní GID - kompozitní komponenty se dotáhnou automaticky

 

Typický pracovní postup (latinské 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;

 

Typický pracovní postup (arabská ligatura LAM-ALEF)

 

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;

 

Rozsah a omezení

Engine je čisté dotazovací rozhraní schopností: odpovídá na otázku "co by zde GSUB udělal", ale nespouští automatickou pipeline tvarování (layout třídy Harfbuzz, reorder se znalostí clusterů pro Indic, řešení BiDi, polohování GPOS, mark attachment). Volající odpovídají za řízení skenovací smyčky, výběr náhrady / alternativy k emisi, volání MarkUnicodeGlyphUsed pro každé emitované náhradní GID a aplikaci libovolného GPOS / mark positioning, které font vyžaduje

 

Tvarování Arabic / Persian / Urdu na straně producenta (povinná ligatura LAM-ALEF + Arabic Presentation Forms-A) je implementováno jako samostatná vestavěná pipeline, která běží automaticky během emise textu - viz Arabic / Persian / Urdu Shaping

 

Stopa verzí

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 + respektování LookupFlag (Phase 4). v2.119.47 Contextual + Chained Contextual + dispatcher SequenceLookupRecord (Phase 5). v2.119.48 Reverse Chained Contextual - matice LookupType 1-8 uzavřena (Phase 6). v2.119.49 API výběru Script / LangSys (Phase 7). v2.119.50 uzavření TTF subsetteru přes MarkUnicodeGlyphUsed (Phase 9; Phase 8 byla integrační větev tvarování na straně producenta a pro budoucí revize se rozdělila na 8a-8f)

 

Viz také: Arabic / Persian / Urdu Shaping, Funkce subsetování fontů CFF / OpenType, THotPDF.EnableFontSubsetting