Bộ máy thay thế OpenType GSUB

Bề mặt năng lực thay thế glyph (v2.119.43 - v2.119.50)

 

Arabic Shaping  CFF / OpenType Subsetting

Bộ máy OpenType GSUB (Glyph SUBstitution) bên trong HotPDF cho phép bên gọi truy vấn, điều khiển và nhúng mọi kiểu thay thế glyph mà một phông OpenType khai báo - ligature, biến thể kiểu chữ, biến thể theo ngữ cảnh, các dạng tạo hình Arabic / Indic, các dạng thay thế CJK, và nhiều hơn nữa. Mọi OpenType GSUB LookupType từ 1 đến 8 đều được cài đặt và phơi ra như một bề mặt chỉ để truy vấn năng lực; bên gọi điều khiển việc phát văn bản và quyết định glyph thay thế nào sẽ được ghi vào luồng nội dung trang

 

API công khai

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

 

Mô tả

Bộ máy này hoạt động sau khi RegisterUnicodeTTF đã phân tích một phông và cache các bảng GSUB / GDEF / cmap của nó. Mỗi truy vấn thay thế sẽ đi dọc chuỗi ScriptList / LangSysList / FeatureList / LookupList của phông và chuyển tới handler LookupType tương ứng. 12 phương thức phía trên là toàn bộ bề mặt công khai; mọi thứ khác (duyệt cmap, phân tích ScriptList, tra bảng Coverage, phân giải ClassDef, tôn trọng LookupFlag, bóc lớp wrapper Extension, dispatch lồng qua SequenceLookupRecord) đều nằm sau bề mặt đó

 

Hợp đồng phòng thủ xuyên suốt: phông không có bảng GSUB, feature tag không đủ 4 byte, feature mà script / language đã chọn không công bố, các GID không subtable nào bao phủ, và các glyph đầu vào bị LookupFlag bỏ qua đều trả về no-op an toàn (False / OutGID = InputGID / OutGIDs rỗng / ConsumedCount = 1) để bên gọi không bao giờ thấy ngoại lệ cho các trường hợp thường nhật "không có phép thay thế nào áp dụng"

 

LookupType matrix

LookupType 1 (Single Substitution) - một glyph ánh xạ sang một substitute. Feature điển hình: salt, ss01-ss20, smcp, onum, liga khi LookupType 1 được nối dây, cộng với các dạng vị trí Arabic init / medi / fina / isol trong những phông điều khiển chúng qua GSUB. Dùng GetSingleSubstituteGlyph

LookupType 2 (Multiple Substitution) - một glyph tách thành một dãy glyph thay thế. Trường hợp điển hình: ccmp Glyph Composition / Decomposition (các chữ Latin có dấu được tách từ dạng ghép sẵn thành base + combining marks để phục vụ định vị mark về sau). Dùng GetMultipleSubstituteGlyphs

LookupType 3 (Alternate Substitution) - một glyph ánh xạ sang một trong N biến thể. Feature điển hình: aalt (Access All Alternates), salt khi được nối dây như Type 3, titl (Titling Alternates), các stylistic set ss01-ss20 khi nhà thiết kế phông cung cấp hơn một biến thể cho mỗi ô. Dùng GetAlternateGlyphCount + GetAlternateGlyph

LookupType 4 (Ligature Substitution) - N glyph đầu vào gộp thành một ligature. Feature điển hình: liga (Standard Ligatures: fi / fl / ffi / ffl), clig (Contextual Ligatures), dlig (Discretionary Ligatures), hlig (Historical Ligatures), rlig (Required Ligatures - Arabic LAM-ALEF và các dạng tương tự), các ligature của script Indic (akhn, pres, blws, psts). Dùng ApplyLigatureSubstitution

LookupType 5 (Contextual Substitution) + LookupType 6 (Chained Contextual Substitution) - khớp một dãy glyph đầu vào và chuyển vào các lookup lồng nhau tại những vị trí cụ thể bên trong phần khớp. Cả ba biến thể Format (1 literal sequence, 2 ClassDef sequence, 3 Coverage sequence) đều đã được cài; bộ dispatch của SequenceLookupRecord quay lại LookupList và xử lý các lookup lồng Single / Multiple / Alternate (lần đầu) / Ligature với việc theo dõi MatchPositions trực tiếp. Feature điển hình: rclt (Required Contextual Alternates - Arabic init/medi/fina/isol khi do GSUB điều khiển), clig, calt, tạo hình Indic pres / blws / psts / half / pstf / cjct. Dùng ApplyContextualSubst (một điểm vào xử lý cả LookupType 5 và 6)

LookupType 7 (Extension Substitution) - lớp gián tiếp thuần túy mà đặc tả OpenType định nghĩa cho những phông có subtable thay thế nằm ngoài tầm với 16-bit của LookupList. Mọi API công khai đều lần theo lớp gián tiếp Offset32 32-bit để tới subtable thật thuộc LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8. Gỡ nút thắt cho các phông CJK / Indic nặng (Noto Sans CJK, Noto Sans Devanagari) có GSUB vượt quá 64 KB. Không có API riêng - việc bóc lớp là tự động

LookupType 8 (Reverse Chained Contextual Single Substitution) - phép thay thế 1:1 có nhận biết ngữ cảnh, với điểm khác biệt là bên gọi phải áp dụng nó theo thứ tự quét NGƯỢC trên một dải nhiều glyph (cuối -> đầu) vì mỗi substitute có thể phụ thuộc vào ngữ cảnh nhìn trước ở TƯƠNG LAI chưa được thay thế. Cách dùng điển hình: các alternate ngữ cảnh của Arabic / Syriac / N'Ko / Indic mà dạng cuối phụ thuộc vào glyph theo sau. Dùng ApplyReverseChainedContextualSubst; bên gọi tự điều khiển vòng quét ngược

 

Chọn Script / LangSys

Mặc định bộ máy ưu tiên script DFLT (hoặc script đầu tiên mà phông khai báo) và LangSys mặc định. Gọi SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.) và SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., trailing-space padded to 4 bytes) để ghim truy vấn vào một cặp script / language cụ thể. Chuỗi rỗng sẽ khôi phục baseline đường mặc định. Lựa chọn này tồn tại qua nhiều truy vấn và bị xóa khi gọi RegisterUnicodeTTF('', nil)

 

Ngữ nghĩa strict-vs-fallback: một ScriptTag không xác định sẽ khiến các truy vấn sau trả về kết quả no-op rỗng (để bên gọi nhận ra script đã chọn không khả dụng); một LangTag không xác định sẽ rơi về LangSys mặc định của script theo quy ước OpenType. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures liệt kê đúng những gì phông đã nạp thực sự công bố

 

Tôn trọng LookupFlag và GDEF

Mỗi truy vấn đều đọc LookupFlag của từng bảng Lookup (và markFilteringSet uint16 tùy chọn ở cuối khi useMarkFilteringSet được bật) rồi bỏ qua các glyph đầu vào bị đánh dấu là cần ignore. Các bit do đặc tả định nghĩa đều được tôn trọng: ignoreBaseGlyphs (0x0002, bỏ qua class 1 của GDEF), ignoreLigatures (0x0004, bỏ qua class 2), ignoreMarks (0x0008, bỏ qua class 3), useMarkFilteringSet (0x0010), và byte cao markAttachmentType. Cả ClassDef Format 1 lẫn 2 đều được phân tích; các header GDEF v1.0 / v1.1 / v1.2 đều được chấp nhận. Phông không có bảng GDEF sẽ rơi về trạng thái "không glyph nào bị bỏ qua" để đầu ra vẫn giống hệt theo byte với các bên gọi dùng phông không có GDEF

 

Khép lại TTF subsetter (MarkUnicodeGlyphUsed)

Bộ chia nhỏ TTF v2.84.0 của HotPDF lấy tập glyph đã dùng từ FUnicodeUsedCps thông qua cmap. Các glyph thay thế của GSUB (biến thể kiểu chữ, ligature, biến thể ngữ cảnh - tất cả những gì 7 API truy vấn ở trên trả về) thường không có codepoint nào đi tới chúng qua cmap, nên trước đây chúng vô hình đối với bộ chia nhỏ và trình đọc phía consumer hiển thị .notdef thay thế

 

Sau khi phát bất kỳ GID nào do GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst trả về vào một luồng văn bản PDF, hãy gọi MarkUnicodeGlyphUsed(GID) một lần cho mỗi GID đã phát để kéo nó vào subset nhúng. Helper này idempotent, có phòng thủ (GID ngoài phạm vi sẽ bị bỏ qua im lặng) và tích hợp với lượt closure glyph tổng hợp của v2.84.0: bên gọi chỉ cần đánh dấu GID substitute cấp cao nhất - các thành phần tổng hợp sẽ được tự động kéo theo

 

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;

 

Phạm vi và giới hạn

Bộ máy này chỉ là một bề mặt truy vấn năng lực: nó trả lời câu hỏi "GSUB sẽ làm gì ở đây", nhưng không chạy một pipeline tạo hình tự động (bố cục cỡ Harfbuzz, reorder có nhận biết cụm cho Indic, phân giải BiDi, định vị GPOS, gắn mark). Bên gọi tự chịu trách nhiệm điều khiển vòng quét, chọn substitute / alternate nào sẽ phát, gọi MarkUnicodeGlyphUsed cho mỗi GID substitute đã phát, và áp dụng mọi định vị GPOS / mark mà phông yêu cầu

 

Tạo hình Arabic / Persian / Urdu phía producer (ligature bắt buộc LAM-ALEF + Arabic Presentation Forms-A) được cài như một pipeline dựng sẵn riêng và chạy tự động trong lúc phát văn bản - xem Hỗ trợ tạo hình Arabic / Persian / Urdu

 

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 - matrix LookupType 1-8 được khép kín (Phase 6). v2.119.49 API chọn Script / LangSys (Phase 7). v2.119.50 đóng vòng bộ chia nhỏ TTF qua MarkUnicodeGlyphUsed (Phase 9; Phase 8 là một đợt spike tích hợp tạo hình phía producer và được tách thành 8a-8f cho các bản sửa đổi sau)

 

Xem thêm: Arabic / Persian / Urdu Shaping, CFF / OpenType Font Subsetting Functions, THotPDF.EnableFontSubsetting