Open型 GSUB 置換エンジン

グリフ置換機能サーフェス (v2.119.43 - v2.119.50)

 

アラビア文字シェーピング  CFF / Open型 サブセット化

HotPDF内のOpen型 GSUB(グリフ置換)エンジンを使用すると、呼び出し元は、Open型フォントが宣言するすべての種類のグリフ置換(合字、文体による代替、コンテキストのバリアント、アラビア語/インド系文字のシェーピングフォーム、CJKの代替フォームなど)を照会、駆動、および埋め込むことができます。Open型 GSUB Lookup型 1 から 8 のすべてが実装され、機能のみのクエリサーフェスとして公開されています。呼び出し元はテキストの出力を駆動し、どの置換グリフをページコンテンツストリームに書き込むかを決定します。

 

パブリックAPI

type

  TGSUBStringArray = array of AnsiString;

 

// Lookup型 1 - 単一置換 (1グリフ -> 1グリフ)

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

 

// Lookup型 2 - 複数置換 (1グリフ -> グリフのシーケンス)

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

  var OutGIDs: array of Word): Boolean;

 

// Lookup型 3 - 代替置換 (1グリフ -> N個の代替のいずれか)

function GetAlternateGlyphCount(InputGID: Word; const FeatureTag: AnsiString): Integer;

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

  AlternateIndex: Integer): Word;

 

// Lookup型 4 - 合字置換 (Nグリフ -> 1合字)

function ApplyLigatureSubstitution(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  out OutGID: Word; out ConsumedCount: Integer): Boolean;

 

// Lookup型 5 + 6 - コンテキスト / 連鎖コンテキスト置換

function ApplyContextualSubst(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  var OutGIDs: array of Word;

  out ConsumedLen: Integer): Boolean;

 

// Lookup型 8 - 逆連鎖コンテキスト単一置換

function ApplyReverseChainedContextualSubst(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  out OutGID: Word): Boolean;

 

// スクリプト / 言語システムの選択 (フェーズ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 サブセッタークロージャ (フェーズ9)

procedure MarkUnicodeGlyphUsed(GID: Word);

 

説明

エンジンは、RegisterUnicodeTTFがフォントを解析し、GSUB / GDEF / cmap テーブルをキャッシュした後にアクティブになります。各置換クエリはフォントの ScriptList / LangSysList / FeatureList / LookupList チェーンをたどり、適切な Lookup型 ハンドラーにディスパッチします。上記の12のメソッドは完全なパブリックサーフェスです。それ以外(cmapの歩行、ScriptListの解析、Coverageテーブルのルックアップ、ClassDefの解決、LookupFlagの考慮、Extensionラッパーのアンラップ、SequenceLookupレコードのネストされたディスパッチ)はその背後に存在します。

 

全体を通して防御的な契約です。GSUBテーブルのないフォント、4バイトではない機能タグ、選択したスクリプト/言語が通知しない機能、サブテーブルがカバーしないGID、およびLookupFlagによって無視される入力グリフはすべて、安全なno-op (False / OutGID = InputGID / 空のOutGIDs / ConsumedCount = 1) を返すため、呼び出し元は「置換が適用されない」日常的なケースで例外を見ることがありません。

 

Lookup型 マトリックス

Lookup型 1 (単一置換) - 1つのグリフが1つの置換にマッピングされます。一般的な機能:saltss01-ss20smcponum、Lookup型 1が配線されている場合のliga、さらにGSUBを通じて駆動するフォントのinit / medi / fina / isol アラビア語の位置形式。GetSingleSubstituteGlyphを使用します。

Lookup型 2 (複数置換) - 1つのグリフが置換グリフのシーケンスに分割されます。一般的なユーザー:ccmp グリフの合成 / 分解 (下流のマークの配置のために、事前に合成されたアクセント付きラテン文字がベース + 結合マークに分割されます)。GetMultipleSubstituteGlyphsを使用します。

Lookup型 3 (代替置換) - 1つのグリフがN個の代替のいずれかにマッピングされます。一般的な機能:aalt (すべての代替にアクセス)、型 3として配線されている場合のsalttitl (タイトル代替)、フォントデザイナーが1つのスロットにつき複数の代替を提供する場合のss01-ss20の文体セット。GetAlternateGlyphCount + GetAlternateGlyphを使用します。

Lookup型 4 (合字置換) - N個の入力グリフが1つの合字に折りたたまれます。一般的な機能:liga (標準合字:fi / fl / ffi / ffl)、clig (コンテキスト合字)、dlig (任意の合字)、hlig (歴史的な合字)、rlig (必須合字 - アラビア語のLAM-ALEFなど)、インド系文字の合字 (akhn, pres, blws, psts)。ApplyLigatureSubstitutionを使用します。

Lookup型 5 (コンテキスト置換) + Lookup型 6 (連鎖コンテキスト置換) - 入力グリフシーケンスと一致し、一致の内部の特定の位置でネストされたルックアップをディスパッチします。3つのFormatバリアント (1 リテラルシーケンス、2 ClassDefシーケンス、3 Coverageシーケンス) はすべて実装されています。SequenceLookupレコードディスパッチャはLookupListに再入し、ライブのMatchPositionsトラッキングを使用して、単一 / 複数 / 代替 (最初) / 合字のネストされたルックアップを処理します。一般的な機能:rclt (必須のコンテキスト代替 - GSUB駆動時のアラビア語 init/medi/fina/isol)、cligcalt、インド系のシェーピング pres / blws / psts / half / pstf / cjctApplyContextualSubstを使用します (1つのエントリポイントがLookup型 5と6の両方をカバーします)。

Lookup型 7 (拡張置換) - Open型仕様が定義する純粋な間接レイヤーであり、置換サブテーブルがLookupListの16ビットの到達範囲を超えるフォント向けです。すべてのパブリックAPIは透過的に、実際のLookup型 1 / 2 / 3 / 4 / 5 / 6 / 8 サブテーブルへの32ビットOffset32の間接性をたどります。GSUBが64 KBを超える重いCJK / インド系フォント (Noto Sans CJK、Noto Sans Devanagari) のブロックを解除します。個別のAPIはありません。アンラップは自動的に行われます。

Lookup型 8 (逆連鎖コンテキスト単一置換) - コンテキストを認識する1:1置換であり、各置換はまだ置換されていない将来の先読みコンテキストに依存する可能性があるため、呼び出し元が複数のグリフのランにわたって「逆」のスキャン順序 (末尾 -> 先頭) で適用する必要があるという特徴があります。一般的な使用法:最終形式が次のグリフに依存するアラビア語 / シリア語 / ンコ語 / インド系のコンテキスト代替。ApplyReverseChainedContextualSubstを使用します。呼び出し元が逆スキャンループを駆動します。

 

スクリプト / LangSys の選択

デフォルトでは、エンジンはDFLTスクリプト(またはフォントが宣言する最初のスクリプト)とデフォルトのLangSysを優先します。SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / など) および SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / など、4バイトになるように末尾のスペースでパディング) を呼び出して、クエリを特定のスクリプト/言語のペアに固定します。空の文字列はデフォルトパスのベースラインを復元します。選択はクエリ間で保持され、RegisterUnicodeTTF('', nil) でクリアされます。

 

厳密とフォールバックのセマンティクス:不明なScriptTagを使用すると、後続のクエリは空のno-op結果を返します(そのため、呼び出し元は選択したスクリプトが利用できないことを検出できます)。不明なLangTagは、Open型の規則に従ってスクリプトのデフォルトのLangSysにフォールバックします。GetGSUBScripts / GetGSUBLanguages / GetGSUBFeaturesは、読み込まれたフォントが実際に通知するものを列挙します。

 

LookupFlag の考慮と GDEF

すべてのクエリは各LookupテーブルのLookupFlag(およびuseMarkFilteringSetが設定されている場合はオプションの末尾のmarkFilteringSet uint16)を読み取り、無視するようにフラグが立てられている入力グリフをスキップします。仕様で定義されているビットが考慮されます:ignoreBaseGlyphs (0x0002、GDEFクラス1をスキップ)、ignoreLigatures (0x0004、クラス2をスキップ)、ignoreMarks (0x0008、クラス3をスキップ)、useMarkFilteringSet (0x0010)、および高位バイトのmarkAttachment型。ClassDef Format 1 + 2 は両方とも解析されます。GDEF v1.0 / v1.1 / v1.2 ヘッダーはすべて受け入れられます。GDEFテーブルがないフォントは「無視されるグリフはない」にフォールバックするため、GDEFのないフォントを使用する呼び出し元では出力はバイト単位で同一のままになります。

 

TTF サブセッタークロージャ (MarkUnicodeGlyphUsed)

HotPDFのv2.84.0 TTFサブセッターは、cmapを通じてFUnicodeUsedCpsから使用されるグリフのセットを取得します。GSUB置換グリフ(文体による代替、合字、コンテキストバリアント - 上記の7つのクエリAPIが返すものすべて)は、通常、cmap経由で到達するコードポイントを持たないため、以前はサブセッターから見えず、コンシューマーリーダーは代わりに.notdefをレンダリングしていました。

 

GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst によって返された GID を PDF テキストストリームに出力した後、出力された GID ごとに MarkUnicodeGlyphUsed(GID) を1回呼び出して、埋め込みサブセットに引き込みます。このヘルパーはべき等であり、防御的であり (範囲外の GID は暗黙的にドロップされます)、v2.84.0 の合成グリフクロージャパスと統合されます:呼び出し元は最上位の置換 GID をマークするだけでよく、合成コンポーネントは自動的にプルされます。

 

一般的なワークフロー (ラテン文字のスモールキャップス)

 

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;

 

一般的なワークフロー (アラビア語LAM-ALEFの合字)

 

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;

 

範囲と制限事項

このエンジンは機能のみのクエリサーフェスです。これは「GSUBはここで何をするか」に答えますが、自動シェーピングパイプライン (Harfbuzzクラスのレイアウト、インド系向けのクラスター対応の並べ替え、BiDi解決、GPOSの配置、マークの添付) は実行しません。呼び出し元はスキャンループを駆動し、出力する置換/代替を選択し、出力される置換GIDごとに MarkUnicodeGlyphUsed を呼び出し、フォントが要求する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 アラビア語 / ペルシア語 / ウルドゥー語 シェーピング.

 

バージョン履歴

v2.119.43 単一置換 + フェーズ 1。v2.119.44 複数 + 代替 (フェーズ 2)。v2.119.45 合字 (フェーズ 3)。v2.119.46 拡張 + GDEF + LookupFlag の考慮 (フェーズ 4)。v2.119.47 コンテキスト + 連鎖コンテキスト + SequenceLookupレコード ディスパッチャ (フェーズ 5)。v2.119.48 逆連鎖コンテキスト - Lookup型 1-8 マトリックスの完了 (フェーズ 6)。v2.119.49 スクリプト/LangSys 選択API (フェーズ 7)。v2.119.50 MarkUnicodeGlyphUsed による TTF サブセッターのクロージャ (フェーズ 9; フェーズ 8 はプロデューサー側のシェーピング統合スパイクであり、将来の改訂のために8a-8fに分割されました)。

 

See also: アラビア語 / ペルシア語 / ウルドゥー語 シェーピング, CFF / Open型 フォント サブセット化関数, THotPDF.EnableFontSubsetting