|
OpenType GSUB Substitution Engine Glyph substitution capability surface (v2.119.43 - v2.119.50)
|
Arabic Shaping CFF / OpenType Subsetting |
|
Двигателят OpenType GSUB (Glyph SUBstitution) в HotPDF позволява на ползвателите да питат, управляват и вграждат всеки вид замяна на глифове, който един OpenType шрифт декларира - лигатури, стилистични алтернативи, контекстни варианти, арабски / Indic форми за шейпинг, CJK алтернативни форми и т.н. Всякакъв OpenType GSUB LookupType от 1 до 8 е имплементиран и изложен като повърхност за запитване само за възможности; ползвателят управлява извеждането на текста и решава кой заместващ глиф да запише в потока за PDF съдържание
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 таблица, feature tags, които не са 4-байтови, функции, които избраната писменост / език не обявява, GID-ове, които никоя подтаблица не покрива, и входни glyph-ове, игнорирани от LookupFlag, всички връщат безопасен no-op (False / OutGID = InputGID / празни OutGIDs / ConsumedCount = 1), така че извикващите никога да не виждат изключения за обичайни случаи на "no substitution applies"
LookupType matrix LookupType 1 (Single Substitution) - един glyph се съпоставя с един заместител. Канонични функции: salt, ss01-ss20, smcp, onum, liga, когато LookupType 1 е включен, плюс init / medi / fina / isol Arabic positional forms в шрифтове, които ги управляват през GSUB. Използвайте GetSingleSubstituteGlyph LookupType 2 (Multiple Substitution) - един glyph се разделя на поредица от substitute glyphs. Canonical потребител: ccmp Glyph Composition / Decomposition (предварително съставени акцентувани латински букви се разделят на базов + combining mark за downstream позициониране на марки). Използвайте GetMultipleSubstituteGlyphs LookupType 3 (Alternate Substitution) - един glyph се съпоставя с един от N алтернативи. Канонични функции: aalt (Access All Alternates), salt, когато е включен като Type 3, titl (Titling Alternates), ss01-ss20 stylistic sets, когато дизайнерът на шрифта предлага повече от една алтернатива за слот. Използвайте GetAlternateGlyphCount + GetAlternateGlyph LookupType 4 (Ligature Substitution) - N input glyphs fold into one ligature. Canonical features: LookupType 5 (Contextual Substitution) + LookupType 6 (Chained Contextual Substitution) - съвпада с входна glyph последователност и изпраща вложени lookups на конкретни позиции вътре в съвпадението. И трите Format варианта (1 literal sequence, 2 ClassDef sequence, 3 Coverage sequence) са реализирани; dispatcher-ът SequenceLookupRecord влиза повторно в LookupList и обработва вложени lookups от тип Single / Multiple / Alternate (first) / Ligature с активно проследяване на MatchPositions. Канонични функции: rclt (Required Contextual Alternates - Arabic init/medi/fina/isol, когато са GSUB-driven), clig, calt, Indic shaping pres / blws / psts / half / pstf / cjct. Използвайте ApplyContextualSubst (една входна точка покрива LookupType 5 и 6) LookupType 7 (Extension Substitution) - чист слой на индирекция, който OpenType спецификацията дефинира за шрифтове, чиято substitution subtable е извън 16-битовия обсег на LookupList. Всеки публичен API прозрачно следва 32-битовата Offset32 индирекция към реалната LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8 подтаблица. Отключва тежки CJK / Indic шрифтове (Noto Sans CJK, Noto Sans Devanagari), чийто GSUB надхвърля 64 KB. Няма отделен API - разопаковането е автоматично LookupType 8 (Reverse Chained Contextual Single Substitution) - context-aware 1:1 substitution whose distinguishing feature is that callers must apply it in REVERSE scan order over a multi-glyph run (end -> start) because each substitute may depend on FUTURE lookahead context that must not have been substituted yet. Canonical use: Arabic / Syriac / N'Ko / Indic contextual alternates whose final form depends on the following glyph. Use
Script / LangSys selection По подразбиране двигателят предпочита скрипта
Strict-vs-fallback семантика: непознат ScriptTag кара последващите заявки да връщат празни no-op резултати (така извикващите могат да засекат, че избраната писменост не е налична); непознат LangTag се връща към default LangSys на писмеността според OpenType конвенцията. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures изброяват това, което зареденият шрифт наистина обявява
LookupFlag honor and GDEF Всяка заявка чете LookupFlag на всяка Lookup таблица (и незадължителния trailing markFilteringSet uint16, когато е зададен useMarkFilteringSet) и пропуска входните glyph-ове, отбелязани за игнориране. Специфицираните от стандарта битове се спазват: ignoreBaseGlyphs (0x0002, пропуска GDEF class 1), ignoreLigatures (0x0004, пропуска class 2), ignoreMarks (0x0008, пропуска class 3), useMarkFilteringSet (0x0010) и горния байт markAttachmentType. И двата ClassDef формата 1 + 2 се парсват; GDEF v1.0 / v1.1 / v1.2 headers се приемат. Шрифтове без GDEF таблица падат обратно на "no glyph is ignored", така че изходът остава byte-identical за извикващи, които използват GDEF-less шрифтове
TTF subsetter closure (MarkUnicodeGlyphUsed) TTF subsetter-ът на HotPDF v2.84.0 извежда своя used-glyph набор от FUnicodeUsedCps през cmap. GSUB заместителните glyph-ове (stylistic alternates, ligatures, contextual variants - всичко, което горните 7 query API-а връщат) обикновено нямат codepoint, който да достига до тях през cmap, така че преди бяха невидими за subsetter-а и потребителският reader ги рендираше като .notdef
След като изведете в PDF текстов поток всеки GID, върнат от GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst, извикайте MarkUnicodeGlyphUsed(GID) по веднъж за всеки изведен GID, за да го включите във вградения subset. Помощният метод е idempotent, защитен (GID-ове извън обхват се пропускат безшумно) и се интегрира с v2.84.0 composite-glyph closure pass: извикващите трябва да маркират само top-level заместителния GID - composite components се извличат автоматично
Typical 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 // emit SmallCapGID into the page content stream... PDF.MarkUnicodeGlyphUsed(SmallCapGID); // pull into subset end;
Typical workflow (Arabic LAM-ALEF ligature)
PDF.SetGSUBScript('arab'); Пример: Run := [LamGID, FathaGID, AlefGID]; // GID след cmap if PDF.ApplyLigatureSubstitution(Run, 0, 'rlig', LigGID, ConsumedCount) then begin // emit LigGID + advance by ConsumedCount PDF.MarkUnicodeGlyphUsed(LigGID); end;
Обхват и ограничения Двигателят е повърхност за запитване само за възможности: той отговаря "какво би направил GSUB тук", но не изпълнява автоматичен конвейер за шейпинг (layout от клас Harfbuzz, пренареждане с оглед на клъстери за Indic, разрешаване на BiDi, GPOS позициониране, прикрепване на марки). Ползвателите носят отговорност да управляват цикъла на сканиране, да избират кой заместител / алтернатива да изведат, да извикват
Producer-side Arabic / Persian / Urdu shaping (LAM-ALEF mandatory ligature + Arabic Presentation Forms-A) is implemented as a separate built-in pipeline that runs automatically during text emission - see Arabic / Persian / Urdu Shaping.
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 matrix е затворен (Phase 6). v2.119.49 Script / LangSys selection API (Phase 7). v2.119.50 TTF subsetter closure чрез MarkUnicodeGlyphUsed (Phase 9; Phase 8 беше producer-side shaping integration spike и е разделен в 8a-8f за бъдещи ревизии)
See also: Arabic / Persian / Urdu Shaping, CFF / OpenType Font Subsetting Functions, THotPDF.EnableFontSubsetting |