Engine Substitusi OpenType GSUB

Permukaan kapabilitas substitusi glyph (v2.119.43 - v2.119.50)

 

Arabic Shaping  CFF / OpenType Subsetting

Engine OpenType GSUB (Glyph SUBstitution) di dalam HotPDF memungkinkan pemanggil menanyakan, menggerakkan, dan menyematkan semua jenis substitusi glyph yang dideklarasikan font OpenType - ligature, stylistic alternates, varian kontekstual, bentuk shaping Arabic / Indic, bentuk alternatif CJK, dan seterusnya. Setiap OpenType GSUB LookupType 1 hingga 8 diimplementasikan dan diekspos sebagai permukaan query yang hanya berisi kapabilitas; pemanggil menggerakkan emisi teks dan memutuskan glyph pengganti mana yang akan ditulis ke content stream halaman

 

API publik

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);

 

Deskripsi

Engine aktif setelah RegisterUnicodeTTF mem-parsing font dan menyimpan cache tabel GSUB / GDEF / cmap-nya. Setiap query substitusi menelusuri rantai ScriptList / LangSysList / FeatureList / LookupList dari font dan meneruskannya ke handler LookupType yang sesuai. Dua belas metode di atas adalah permukaan publik yang lengkap; semuanya yang lain (penelusuran cmap, parsing ScriptList, lookup tabel Coverage, resolusi ClassDef, penghormatan LookupFlag, pembukaan wrapper Extension, dispatch bertingkat SequenceLookupRecord) berada di balik permukaan itu

 

Kontrak defensif berlaku di seluruh bagian: font tanpa tabel GSUB, feature tag yang bukan 4 byte, fitur yang tidak diiklankan script / language terpilih, GID yang tidak dicakup subtabel mana pun, dan glyph masukan yang diabaikan LookupFlag semuanya mengembalikan no-op aman (False / OutGID = InputGID / empty OutGIDs / ConsumedCount = 1) sehingga pemanggil tidak pernah melihat exception untuk kasus rutin "no substitution applies"

 

Matriks LookupType

LookupType 1 (Single Substitution) - satu glyph dipetakan ke satu substitute. Fitur kanonis: salt, ss01-ss20, smcp, onum, liga saat LookupType 1 terhubung, plus bentuk posisional Arabic init / medi / fina / isol pada font yang menggerakkannya lewat GSUB. Gunakan GetSingleSubstituteGlyph

LookupType 2 (Multiple Substitution) - satu glyph dipecah menjadi rangkaian substitute glyph. Penggunaan kanonis: ccmp Glyph Composition / Decomposition (huruf Latin beraksen yang sudah diprecompose dipecah menjadi base + combining mark untuk penempatan mark berikutnya). Gunakan GetMultipleSubstituteGlyphs

LookupType 3 (Alternate Substitution) - satu glyph dipetakan ke salah satu dari N alternates. Fitur kanonis: aalt (Access All Alternates), salt saat dipasang sebagai Type 3, titl (Titling Alternates), ss01-ss20 stylistic set ketika perancang font menawarkan lebih dari satu alternate per slot. Gunakan GetAlternateGlyphCount + GetAlternateGlyph

LookupType 4 (Ligature Substitution) - N glyph masukan dilipat menjadi satu ligature. Fitur kanonis: liga (Standard Ligatures: fi / fl / ffi / ffl), clig (Contextual Ligatures), dlig (Discretionary Ligatures), hlig (Historical Ligatures), rlig (Required Ligatures - Arabic LAM-ALEF dan sejenisnya), ligatur skrip Indic (akhn, pres, blws, psts). Gunakan ApplyLigatureSubstitution

LookupType 5 (Contextual Substitution) + LookupType 6 (Chained Contextual Substitution) - mencocokkan rangkaian glyph masukan dan men-dispatch lookup bersarang pada posisi tertentu di dalam kecocokan. Ketiga varian Format (1 sequence literal, 2 sequence ClassDef, 3 sequence Coverage) diimplementasikan; dispatcher SequenceLookupRecord masuk lagi ke LookupList dan menangani nested lookup Single / Multiple / Alternate (pertama) / Ligature dengan pelacakan MatchPositions aktif. Fitur kanonis: rclt (Required Contextual Alternates - Arabic init/medi/fina/isol saat digerakkan GSUB), clig, calt, shaping Indic pres / blws / psts / half / pstf / cjct. Gunakan ApplyContextualSubst (satu titik masuk mencakup LookupType 5 dan 6)

LookupType 7 (Extension Substitution) - lapisan indirection murni yang didefinisikan spesifikasi OpenType untuk font yang subtabel substitusinya berada di luar jangkauan 16-bit LookupList. Setiap API publik secara transparan mengikuti indirection Offset32 32-bit menuju subtabel LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8 yang sebenarnya. Membuka jalan untuk font CJK / Indic yang berat (Noto Sans CJK, Noto Sans Devanagari) dengan GSUB yang melebihi 64 KB. Tidak ada API terpisah - unwrap berlangsung otomatis

LookupType 8 (Reverse Chained Contextual Single Substitution) - substitusi 1:1 yang peka konteks dengan ciri pembeda bahwa pemanggil harus menerapkannya dalam urutan pemindaian REVERSE pada run multi-glyph (akhir -> awal) karena setiap substitute dapat bergantung pada konteks lookahead MASA DEPAN yang belum boleh tersubstitusi. Penggunaan kanonis: alternates kontekstual Arabic / Syriac / N'Ko / Indic yang bentuk finalnya bergantung pada glyph berikutnya. Gunakan ApplyReverseChainedContextualSubst; pemanggil mengendalikan loop reverse scan

 

Script / LangSys selection

Secara default engine lebih memilih skrip DFLT (atau skrip pertama yang dideklarasikan font) dan LangSys default. Panggil SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.) dan SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., trailing-space padded to 4 bytes) untuk mengunci query ke pasangan script / language tertentu. String kosong mengembalikan baseline jalur default. Pilihan ini bertahan lintas query dan dihapus pada RegisterUnicodeTTF('', nil)

 

Semantik strict-vs-fallback: ScriptTag yang tidak dikenal membuat query berikutnya mengembalikan hasil no-op kosong (agar pemanggil bisa mendeteksi bahwa skrip pilihannya tidak tersedia); LangTag yang tidak dikenal mundur ke LangSys default skrip sesuai konvensi OpenType. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures menghitung apa yang benar-benar diiklankan font yang dimuat

 

LookupFlag honor and GDEF

Setiap query membaca LookupFlag milik setiap tabel Lookup (dan trailing markFilteringSet uint16 opsional saat useMarkFilteringSet diaktifkan) lalu melewati glyph masukan yang ditandai untuk diabaikan. Bit yang didefinisikan spesifikasi dihormati: ignoreBaseGlyphs (0x0002, lewati GDEF class 1), ignoreLigatures (0x0004, lewati class 2), ignoreMarks (0x0008, lewati class 3), useMarkFilteringSet (0x0010), dan byte tinggi markAttachmentType. ClassDef Format 1 + 2 sama-sama di-parse; header GDEF v1.0 / v1.1 / v1.2 semuanya diterima. Font tanpa tabel GDEF kembali ke "no glyph is ignored" sehingga output tetap byte-identical untuk pemanggil yang memakai font tanpa GDEF

 

TTF subsetter closure (MarkUnicodeGlyphUsed)

TTF subsetter v2.84.0 milik HotPDF menurunkan set glyph terpakai dari FUnicodeUsedCps melalui cmap. Glyph substitusi GSUB (stylistic alternates, ligature, varian kontekstual - semua yang dikembalikan 7 API query di atas) biasanya tidak memiliki codepoint yang menjangkaunya lewat cmap, jadi sebelumnya glyph-glyph itu tidak terlihat oleh subsetter dan pembaca konsumen merender .notdef sebagai gantinya

 

Setelah memancarkan GID apa pun yang dikembalikan GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst ke dalam aliran teks PDF, panggil MarkUnicodeGlyphUsed(GID) sekali per GID yang dipancarkan untuk menariknya ke embedded subset. Helper ini idempoten, defensif (GID di luar rentang dibuang diam-diam), dan terintegrasi dengan composite-glyph closure pass v2.84.0: pemanggil hanya perlu menandai GID substitute tingkat atas - komponen komposit ditarik otomatis

 

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];  // post-cmap GIDs

if PDF.ApplyLigatureSubstitution(Run, 0, 'rlig', LigGID, ConsumedCount) then

begin

  // emit LigGID + advance by ConsumedCount

  PDF.MarkUnicodeGlyphUsed(LigGID);

end;

 

Cakupan dan batasan

Engine ini adalah permukaan query yang hanya berisi kapabilitas: ia menjawab "what would GSUB do here", tetapi tidak menjalankan pipeline shaping otomatis (layout kelas Harfbuzz, reordering peka cluster untuk Indic, resolusi BiDi, positioning GPOS, attachment mark). Pemanggil bertanggung jawab mengendalikan scan loop, memilih substitute / alternate mana yang akan dipancarkan, memanggil MarkUnicodeGlyphUsed untuk setiap GID substitute yang dipancarkan, dan menerapkan positioning GPOS / mark apa pun yang dibutuhkan font

 

Shaping Arabic / Persian / Urdu sisi producer (ligatur wajib LAM-ALEF + Arabic Presentation Forms-A) diimplementasikan sebagai pipeline bawaan terpisah yang berjalan otomatis selama text emission - lihat 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 + penghormatan LookupFlag (Phase 4). v2.119.47 Contextual + Chained Contextual + dispatcher SequenceLookupRecord (Phase 5). v2.119.48 Reverse Chained Contextual - matriks LookupType 1-8 tertutup (Phase 6). v2.119.49 API pemilihan Script / LangSys (Phase 7). v2.119.50 closure TTF subsetter lewat MarkUnicodeGlyphUsed (Phase 9; Phase 8 adalah spike integrasi shaping sisi producer dan dipecah menjadi 8a-8f untuk revisi mendatang)

 

Lihat juga: Arabic / Persian / Urdu Shaping, CFF / OpenType Font Subsetting Functions, THotPDF.EnableFontSubsetting