OpenType GSUB-vervangingsengine

Oppervlak voor glyphsubstitutiemogelijkheden (v2.119.43 - v2.119.50)

 

Arabische vorming  CFF / OpenType-subsetting

De OpenType GSUB-engine (Glyph SUBstitution) in HotPDF laat aanroepers elk type glyphsubstitutie opvragen, aansturen en inbedden dat een OpenType-lettertype declareert - ligaturen, stilistische alternatieven, contextuele varianten, Arabische / Indic-vormingsvormen, CJK-alternatieve vormen en meer. Elke OpenType GSUB LookupType 1 t/m 8 is geïmplementeerd en beschikbaar als een query-oppervlak voor alleen mogelijkheden; de aanroeper stuurt de tekstuitvoer en beslist welke vervangende glyph naar de inhoudsstroom van de pagina wordt geschreven

 

Openbare API

type

  TGSUBStringArray = array of AnsiString;

 

// LookupType 1 - Enkele substitutie (one glyph -> one glyph)

function GetSingleSubstituteGlyph(InputGID: Word; const FeatureTag: AnsiString): Word;

 

// LookupType 2 - Meervoudige substitutie (one glyph -> sequence of glyphs)

function GetMultipleSubstituteGlyphs(InputGID: Word; const FeatureTag: AnsiString;

  var OutGIDs: array of Word): Boolean;

 

// LookupType 3 - Alternatieve substitutie (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 - Ligatuur-substitutie (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 - Contextuele / keten-contextuele substitutie

function ApplyContextualSubst(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  var OutGIDs: array of Word;

  out ConsumedLen: Integer): Boolean;

 

// LookupType 8 - Omgekeerde keten-contextuele enkele substitutie

function ApplyReverseChainedContextualSubst(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  out OutGID: Word): Boolean;

 

// Script / LangSys-selectie (fase 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-subsetterafsluiting (fase 9)

procedure MarkUnicodeGlyphUsed(GID: Word);

 

Beschrijving

De engine activeert nadat RegisterUnicodeTTF een lettertype heeft geparseerd en de GSUB / GDEF / cmap-tabellen heeft gecachet. Elke substitutiequery loopt de ScriptList / LangSysList / FeatureList / LookupList-keten af en stuurt door naar de juiste LookupType-handler. De 12 methoden hierboven vormen het volledige openbare oppervlak; alles daarachter (cmap-loop, ScriptList-parsing, Coverage-table lookup, ClassDef-resolutie, LookupFlag-honor, het uitpakken van Extension-wrapper en SequenceLookupRecord-geneste dispatch) leeft achter dat oppervlak

 

Overal geldt een defensief contract: lettertypen zonder GSUB-tabel, featuretags die geen 4 bytes lang zijn, features die het geselecteerde script / de geselecteerde taal niet adverteren, GID's die geen subtable dekt en invoer-glyphs die door LookupFlag worden genegeerd geven allemaal een veilige no-op terug (False / OutGID = InputGID / lege OutGIDs / ConsumedCount = 1), zodat aanroepers nooit uitzonderingen zien voor de gewone gevallen waarin "geen substitutie van toepassing is"

 

LookupType-matrix

LookupType 1 (Enkele substitutie) - een glyph wordt één vervangende glyph. Canonieke features: salt, ss01-ss20, smcp, onum, liga wanneer LookupType 1 is gekoppeld, plus init / medi / fina / isol Arabische positionele vormen in lettertypen die ze via GSUB aansturen. Gebruik GetSingleSubstituteGlyph

LookupType 2 (Meervoudige substitutie) - een glyph splitst in een reeks vervangende glyphs. Canonieke toepassing: ccmp Glyph-samenstelling / ontleding (vooraf samengestelde Latijnse letters met accenten worden gesplitst in basis + combinatietekens voor latere mark-positionering). Gebruik GetMultipleSubstituteGlyphs

LookupType 3 (Alternatieve substitutie) - een glyph wordt één van N alternatieven. Canonieke features: aalt (Toegang tot alle alternatieven), salt wanneer gekoppeld als Type 3, titl (Titelalternatieven), ss01-ss20 stilistische sets wanneer de fontontwerper meer dan één alternatief per slot biedt. Gebruik GetAlternateGlyphCount + GetAlternateGlyph

LookupType 4 (Ligatuur-substitutie) - N invoerglyphs smelten samen tot één ligatuur. Canonieke features: liga (standaardligaturen: fi / fl / ffi / ffl), clig (contextuele ligaturen), dlig (discretionaire ligaturen), hlig (historische ligaturen), rlig (vereiste ligaturen - Arabic LAM-ALEF en vergelijkbare), Indic-scriptligaturen (akhn, pres, blws, psts). Gebruik ApplyLigatureSubstitution

LookupType 5 (Contextuele substitutie) + LookupType 6 (Geketende contextuele substitutie) - matcht een invoerglyphreeks en stuurt geneste lookups aan op specifieke posities binnen de match. Alle drie Format-varianten (1 letterlijke reeks, 2 ClassDef-reeks, 3 Coverage-reeks) zijn geïmplementeerd; de SequenceLookupRecord-dispatcher gaat opnieuw de LookupList in en behandelt geneste lookups voor Enkele / Meervoudige / Alternatieve (eerste) / Ligatuur-substitutie met live bijhouden van MatchPositions. Canonieke features: rclt (Vereiste contextuele alternatieven - Arabische init/medi/fina/isol wanneer GSUB-gestuurd), clig, calt, Indic-vorming pres / blws / psts / half / pstf / cjct. Gebruik ApplyContextualSubst (één ingang dekt zowel LookupType 5 als 6)

LookupType 7 (Extensiesubstitutie) - een zuivere indirectielaag die de OpenType-specificatie voorschrijft voor lettertypen waarvan de substitutiesubtabel buiten het 16-bits bereik van de LookupList ligt. Elke publieke API volgt transparant de 32-bits Offset32-doorverwijzing naar de echte LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8-subtabel. Ontgrendelt omvangrijke CJK / Indic-lettertypen (Noto Sans CJK, Noto Sans Devanagari) waarvan GSUB groter is dan 64 KB. Geen aparte API - het uitpakken gebeurt automatisch

LookupType 8 (Omgekeerde keten-contextuele enkele substitutie) - contextbewuste 1:1-substitutie waarvan het onderscheidende kenmerk is dat aanroepers deze in OMGEKEERDE scanvolgorde over een multi-glyph-run moeten toepassen (einde -> begin), omdat elke substitutie kan afhangen van TOEKOMSTIGE lookahead-context die nog niet mag zijn gesubstitueerd. Canoniek gebruik: contextuele alternatieven voor Arabisch / Syrisch / N'Ko / Indic waarvan de finale vorm afhangt van de volgende glyph. Gebruik ApplyReverseChainedContextualSubst; de aanroeper stuurt de reverse-scanlus aan

 

Script / LangSys-selectie

Standaard geeft de engine de voorkeur aan het DFLT-script (of het eerste script dat het lettertype declareert) en de default LangSys. Roep SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.) en SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., met spatie tot 4 bytes aangevuld) aan om queries vast te zetten op een specifiek script / taalpaar. Een lege string herstelt de standaardpad-basislijn. Selecties blijven over queries heen bestaan en worden gewist bij RegisterUnicodeTTF('', nil)

 

Strikte versus fallback-semantiek: een onbekende ScriptTag laat volgende queries lege no-op-resultaten teruggeven (zodat aanroepers kunnen zien dat hun gekozen script niet beschikbaar is); een onbekende LangTag valt terug op de default LangSys van het script volgens de OpenType-conventie. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures geven weer wat het geladen lettertype werkelijk adverteert

 

LookupFlag-honor en GDEF

Elke query leest de LookupFlag van elke Lookup-tabel (en de optionele afsluitende markFilteringSet uint16 wanneer useMarkFilteringSet is ingesteld) en slaat invoer-glyphs over die zijn gemarkeerd om te negeren. De bits uit de specificatie worden gerespecteerd: ignoreBaseGlyphs (0x0002, sla GDEF klasse 1 over), ignoreLigatures (0x0004, sla klasse 2 over), ignoreMarks (0x0008, sla klasse 3 over), useMarkFilteringSet (0x0010) en de hoge byte markAttachmentType. ClassDef Format 1 en 2 worden beide geparsed; GDEF v1.0 / v1.1 / v1.2-koppen worden allemaal geaccepteerd. Lettertypen zonder GDEF-tabel vallen terug op "geen glyph wordt genegeerd", zodat de uitvoer byte-identiek blijft voor aanroepers die GDEF-loze lettertypen gebruiken

 

TTF-subsetterafsluiting (MarkUnicodeGlyphUsed)

De TTF-subsetter van HotPDF v2.84.0 leidt zijn gebruikte glyph-set af uit FUnicodeUsedCps via de cmap. GSUB-vervangende glyphs (stilistische alternatieven, ligaturen, contextuele varianten - alles wat de 7 query-API's hierboven teruggeven) hebben meestal geen codepoint dat via de cmap bij hen uitkomt, waardoor ze eerder onzichtbaar waren voor de subsetter en de lezer .notdef in hun plaats tekende

 

Nadat je een GID die is teruggegeven door GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst naar een PDF-tekststream hebt geschreven, roep je MarkUnicodeGlyphUsed(GID) één keer per uitgegeven GID aan om die in de ingebedde subset te trekken. De helper is idempotent, defensief (GID's buiten bereik worden stilzwijgend genegeerd) en integreert met de composite-glyph-afsluitpass van v2.84.0: aanroepers hoeven alleen de top-level vervangende GID te markeren - composite componenten worden automatisch meegenomen

 

Typische workflow (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

  // schrijf SmallCapGID naar de paginainhoudsstroom

  PDF.MarkUnicodeGlyphUsed(SmallCapGID);  // opnemen in subset

end;

 

Typische workflow (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;

 

Bereik en beperkingen

De engine is een query-oppervlak voor alleen mogelijkheden: hij antwoordt "wat zou GSUB hier doen", maar draait geen automatische vormingspipeline (Harfbuzz-klasse lay-out, clusterbewuste herordening voor Indic, BiDi-resolutie, GPOS-positionering, markbevestiging). Aanroepers zijn verantwoordelijk voor het sturen van de scanlus, het kiezen welke vervangende glyph of alternatieve glyph moet worden uitgegeven, het aanroepen van MarkUnicodeGlyphUsed voor elke uitgegeven vervangende GID en het toepassen van elke GPOS / mark-positionering die een lettertype vereist

 

De vorming aan de producer-kant voor Arabisch / Perzisch / Urdu (LAM-ALEF-verplichte ligatuur + Arabic Presentation Forms-A) is geïmplementeerd als een aparte ingebouwde pipeline die automatisch draait tijdens tekstuitvoer - zie Arabische / Perzische / Urdu-vorming

 

Versietrace

v2.119.43 Enkele substitutie + fase 1. v2.119.44 Meervoudige + alternatieve substitutie (fase 2). v2.119.45 Ligatuur (fase 3). v2.119.46 Extensie + GDEF + LookupFlag-honor (fase 4). v2.119.47 Contextuele + geketende contextuele substitutie + SequenceLookupRecord-dispatcher (fase 5). v2.119.48 Omgekeerde geketende contextuele substitutie - LookupType 1-8-matrix afgesloten (fase 6). v2.119.49 Script / LangSys-selectie-API (fase 7). v2.119.50 TTF-subsetterafsluiting via MarkUnicodeGlyphUsed (fase 9; fase 8 was een integratiespike voor vorming aan de producer-kant en is opgesplitst in 8a-8f voor toekomstige revisies)

 

Zie ook: Arabische / Perzische / Urdu-vorming, CFF / OpenType-lettertypesubsettingfuncties, THotPDF.EnableFontSubsetting