|
OpenType GSUB Substitution Engine Glyph substitution capability surface (v2.119.43 - v2.119.50)
|
Arabic Shaping CFF / OpenType Subsetting |
|
GSUB-движок OpenType (Glyph SUBstitution) внутри HotPDF позволяет вызывающим запрашивать, вести и встраивать любые подстановки глифов, какие объявляет шрифт OpenType — лигатуры, стилистические альтернативы, контекстные варианты, формы арабского / Indic шейпинга, альтернативные формы CJK и так далее. Реализован и открыт каждый OpenType GSUB LookupType с 1-го по 8-й как поверхность «только запрос возможностей»; вывод текста ведёт вызывающий и сам решает, какой substitute-глиф писать в контентный поток страницы.
Публичный API type TGSUBStringArray = array of AnsiString;
// LookupType 1 — Single Substitution (один глиф -> один глиф) function GetSingleSubstituteGlyph(InputGID: Word; const FeatureTag: AnsiString): Word;
// LookupType 2 — Multiple Substitution (один глиф -> последовательность глифов) function GetMultipleSubstituteGlyphs(InputGID: Word; const FeatureTag: AnsiString; var OutGIDs: array of Word): Boolean;
// LookupType 3 — Alternate Substitution (один глиф -> один из N альтернативных) function GetAlternateGlyphCount(InputGID: Word; const FeatureTag: AnsiString): Integer; function GetAlternateGlyph(InputGID: Word; const FeatureTag: AnsiString; AlternateIndex: Integer): Word;
// LookupType 4 — Ligature Substitution (N глифов -> одна лигатура) 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 (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-сабсеттера (Phase 9) procedure MarkUnicodeGlyphUsed(GID: Word);
Описание Движок активируется после того, как
Защитный контракт везде: шрифты без таблицы GSUB, теги фич не из 4 байтов, фичи, которых не объявляет выбранный скрипт / язык, GID вне покрытия сабтаблиц и игнорируемые по LookupFlag входные глифы — всё возвращает безопасный no-op (False / OutGID = InputGID / пустые OutGIDs / ConsumedCount = 1), так что вызывающие никогда не видят исключений в рутинных случаях «подстановка не применилась».
Матрица LookupType LookupType 1 (Single Substitution) — один глиф отображается в один substitute. Канонические фичи: LookupType 2 (Multiple Substitution) — один глиф разбивается на последовательность substitute-глифов. Канонический потребитель: LookupType 3 (Alternate Substitution) — один глиф отображается в один из N альтернативных. Канонические фичи: LookupType 4 (Ligature Substitution) — N входных глифов сворачиваются в одну лигатуру. Канонические фичи: LookupType 5 (Contextual Substitution) + LookupType 6 (Chained Contextual Substitution) — сопоставляет последовательность входных глифов и диспетчеризует вложенные lookups в конкретных позициях внутри совпадения. Реализованы все три варианта Format (1 литеральная последовательность, 2 последовательность ClassDef, 3 последовательность Coverage); диспетчер SequenceLookupRecord повторно входит в LookupList и обрабатывает вложенные lookups Single / Multiple / Alternate (первый) / Ligature с живым отслеживанием MatchPositions. Канонические фичи: LookupType 7 (Extension Substitution) — чистый слой косвенности, который спецификация OpenType определяет для шрифтов, чья сабтаблица подстановок лежит за 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) — контекстно-зависимая подстановка 1:1, чья особенность в том, что применять её нужно в ОБРАТНОМ порядке сканирования по run-у из нескольких глифов (конец -> начало), потому что каждая подстановка может зависеть от ПОСЛЕДУЮЩЕГО lookahead-контекста, который ещё не должен быть подставлен. Каноническое применение: контекстные альтернативы арабского / сирийского / N'Ko / Indic, чья финальная форма зависит от следующего глифа. Используйте
Выбор Script / LangSys По умолчанию движок предпочитает скрипт
Семантика строгости-против-фолбэка: неизвестный
Почтение LookupFlag и GDEF Каждый запрос читает LookupFlag каждой таблицы Lookup (и опциональный замыкающий uint16 markFilteringSet, когда
Замыкание TTF-сабсеттера (MarkUnicodeGlyphUsed) TTF-сабсеттер HotPDF v2.84.0 выводит набор использованных глифов из
После вывода любого GID, возвращённого
Типичный рабочий процесс (латинские малые заглавные)
PDF.RegisterUnicodeTTF('myFont', 'C:\\Windows\\Fonts\\arial.ttf'); PDF.SetGSUBScript('latn'); PDF.SetGSUBLanguage('');// LangSys по умолчанию SmallCapGID := PDF.GetSingleSubstituteGlyph(InputGID, 'smcp'); if SmallCapGID <> InputGID then begin // вывести SmallCapGID в контентный поток страницы... PDF.MarkUnicodeGlyphUsed(SmallCapGID);// втянуть в сабсет end;
Типичный рабочий процесс (арабская лигатура LAM-ALEF)
PDF.SetGSUBScript('arab'); Run := [LamGID, FathaGID, AlefGID];// GID после cmap if PDF.ApplyLigatureSubstitution(Run, 0, 'rlig', LigGID, ConsumedCount) then begin // вывести LigGID + продвинуться на ConsumedCount PDF.MarkUnicodeGlyphUsed(LigGID); end;
Область применения и ограничения Движок — поверхность «только запрос возможностей»: он отвечает на вопрос «что здесь сделал бы GSUB», но не гоняет автоматический шейпинг-конвейер (вёрстка уровня HarfBuzz, кластерно-осведомлённая перестановка для Indic, разрешение BiDi, позиционирование GPOS, привязка знаков). За ведение цикла сканирования, выбор выводимого substitute / alternate, вызовы
Producer-side шейпинг арабского / персидского / урду (обязательная лигатура LAM-ALEF + Arabic Presentation Forms-A) реализован отдельным встроенным конвейером, выполняющимся автоматически при выводе текста — см. Arabic / Persian / Urdu Shaping.
Трассировка версий 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 (Phase 4). v2.119.47 — Contextual + Chained Contextual + диспетчер SequenceLookupRecord (Phase 5). v2.119.48 — Reverse Chained Contextual, матрица LookupType 1-8 закрыта (Phase 6). v2.119.49 — API выбора Script / LangSys (Phase 7). v2.119.50 — замыкание TTF-сабсеттера через
См. также: Arabic / Persian / Urdu Shaping, CFF / OpenType Font Subsetting Functions, THotPDF.EnableFontSubsetting |