OpenType GSUB Substitution Engine

Glyph substitution capability surface (v2.119.43 - v2.119.50)

 

Arabic Shaping  CFF / OpenType Subsetting

HotPDF 내부의 OpenType GSUB(Glyph SUBstitution) 엔진을 사용하면 OpenType 글꼴이 선언한 모든 종류의 글리프 치환을 조회하고, 구동하고, 임베드할 수 있습니다. 합자, 스타일 대체 글리프, 문맥 변형, Arabic / Indic 셰이핑 형태, CJK 대체 글리프 등이 여기에 해당합니다. OpenType GSUB LookupType 1부터 8까지 모두 구현되어 있으며 조회 전용 인터페이스로 노출됩니다. 텍스트 출력은 호출자가 구동하고, 페이지 콘텐츠 스트림에 어떤 대체 글리프를 쓸지도 호출자가 결정합니다

 

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

 

Description

이 엔진은 RegisterUnicodeTTF가 글꼴을 파싱하고 GSUB / GDEF / cmap 테이블을 캐시한 뒤에 활성화됩니다. 치환 조회는 각각 글꼴의 ScriptList / LangSysList / FeatureList / LookupList 체인을 따라가며 알맞은 LookupType 핸들러로 dispatch합니다. 위의 12개 메서드가 전체 공개 인터페이스이며, 그 밖의 모든 것(cmap 탐색, ScriptList 파싱, Coverage 테이블 조회, ClassDef 해석, LookupFlag 적용, Extension 래퍼 해제, SequenceLookupRecord 중첩 dispatch)은 이 인터페이스 뒤에 있습니다

 

전반적으로 방어적인 계약을 따릅니다. GSUB 테이블이 없는 글꼴, 4바이트가 아닌 feature 태그, 선택된 스크립트 / 언어가 제공하지 않는 기능, 어떤 서브테이블도 다루지 않는 GID, LookupFlag로 무시되는 입력 글리프는 모두 안전한 no-op(False / OutGID = InputGID / 빈 OutGIDs / ConsumedCount = 1)을 반환하므로, "적용할 치환이 없는" 일상적인 경우에 호출자가 예외를 볼 일은 없습니다

 

LookupType matrix

LookupType 1 (Single Substitution) - 글리프 하나가 대체 글리프 하나로 매핑됩니다. 대표 기능은 salt, ss01-ss20, smcp, onum, LookupType 1로 연결된 경우의 liga, 그리고 GSUB로 구동하는 글꼴의 Arabic 위치별 형태 init / medi / fina / isol입니다. GetSingleSubstituteGlyph를 사용하십시오

LookupType 2 (Multiple Substitution) - 글리프 하나가 대체 글리프 시퀀스로 분리됩니다. 대표 사용처는 ccmp Glyph Composition / Decomposition입니다(미리 조합된 악센트 Latin 문자를 base + 결합 부호로 나누어 이후의 mark 위치 지정에 활용). GetMultipleSubstituteGlyphs를 사용하십시오

LookupType 3 (Alternate Substitution) - 글리프 하나가 N개의 대체 글리프 중 하나로 매핑됩니다. 대표 기능은 aalt(Access All Alternates), Type 3으로 연결된 경우의 salt, titl(Titling Alternates), 그리고 글꼴 디자이너가 슬롯마다 둘 이상의 대체 글리프를 제공할 때의 ss01-ss20 스타일 세트입니다. GetAlternateGlyphCount + GetAlternateGlyph를 사용하십시오

LookupType 4 (Ligature Substitution) - N input glyphs fold into one ligature. Canonical features: liga (Standard Ligatures: fi / fl / ffi / ffl), clig (Contextual Ligatures), dlig (Discretionary Ligatures), hlig (Historical Ligatures), rlig (Required Ligatures - Arabic LAM-ALEF and similar), Indic script ligatures (akhn, pres, blws, psts). Use ApplyLigatureSubstitution.

LookupType 5 (Contextual Substitution) + LookupType 6 (Chained Contextual Substitution) - 입력 글리프 시퀀스와 일치하는 부분을 찾고, 일치한 범위 안의 특정 위치에서 중첩된 lookup을 dispatch합니다. Format 변형 세 가지(1 literal 시퀀스, 2 ClassDef 시퀀스, 3 Coverage 시퀀스)가 모두 구현되어 있으며, SequenceLookupRecord dispatcher는 LookupList에 다시 진입하여 Single / Multiple / Alternate(첫 번째) / Ligature 중첩 lookup을 실시간 MatchPositions 추적과 함께 처리합니다. 대표 기능은 rclt(Required Contextual Alternates - GSUB로 구동하는 경우의 Arabic init/medi/fina/isol), clig, calt, Indic 셰이핑 pres / blws / psts / half / pstf / cjct입니다. ApplyContextualSubst를 사용하십시오(진입점 하나로 LookupType 5와 6을 모두 처리합니다)

LookupType 7 (Extension Substitution) - 치환 서브테이블이 LookupList의 16비트 범위를 넘는 위치에 있는 글꼴을 위해 OpenType 규격이 정의한 순수 간접 참조 계층입니다. 모든 공개 API는 32비트 Offset32 간접 참조를 따라 실제 LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8 서브테이블까지 투명하게 이동합니다. GSUB가 64 KB를 넘는 대형 CJK / Indic 글꼴(Noto Sans CJK, Noto Sans Devanagari)을 쓸 수 있게 해 줍니다. 별도의 API는 없으며 해제는 자동으로 이루어집니다

LookupType 8 (Reverse Chained Contextual Single Substitution) - 문맥을 인식하는 1:1 치환으로, 각 대체 글리프가 아직 치환되지 않아야 하는 뒤쪽(lookahead) 문맥에 의존할 수 있으므로, 호출자가 여러 글리프로 이루어진 런을 반드시 REVERSE 스캔 순서(끝 -> 시작)로 적용해야 한다는 점이 특징입니다. 대표 사용처는 어말형이 뒤따르는 글리프에 좌우되는 Arabic / Syriac / N'Ko / Indic 문맥 대체 글리프입니다. ApplyReverseChainedContextualSubst를 사용하며, 역방향 스캔 루프는 호출자가 구동합니다

 

Script / LangSys selection

기본적으로 엔진은 DFLT 스크립트(또는 글꼴이 선언한 첫 번째 스크립트)와 기본 LangSys를 우선 선택합니다. 조회를 특정 스크립트 / 언어 쌍에 고정하려면 SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.)와 SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., trailing-space padded to 4 bytes)를 호출하십시오. 빈 문자열을 지정하면 기본 경로 기준선으로 돌아갑니다. 선택은 조회 사이에 유지되며 RegisterUnicodeTTF('', nil)을 호출하면 초기화됩니다

 

Strict 대 fallback 동작은 다음과 같습니다. 알 수 없는 ScriptTag를 지정하면 이후 조회는 빈 no-op 결과를 반환하므로(호출자가 선택한 스크립트를 쓸 수 없다는 것을 감지할 수 있습니다), 알 수 없는 LangTag는 OpenType 관례에 따라 해당 스크립트의 기본 LangSys로 fallback합니다. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures는 로드된 글꼴이 실제로 제공하는 항목을 열거합니다

 

LookupFlag honor and GDEF

모든 조회는 각 Lookup 테이블의 LookupFlag(그리고 useMarkFilteringSet이 설정된 경우 뒤따르는 선택적 markFilteringSet uint16)를 읽고, 무시하도록 표시된 입력 글리프를 건너뜁니다. 규격에 정의된 비트는 모두 지원합니다. ignoreBaseGlyphs(0x0002, GDEF class 1 건너뜀), ignoreLigatures(0x0004, class 2 건너뜀), ignoreMarks(0x0008, class 3 건너뜀), useMarkFilteringSet(0x0010), 그리고 상위 바이트 markAttachmentType입니다. ClassDef Format 1과 2를 모두 파싱하며, GDEF v1.0 / v1.1 / v1.2 헤더를 모두 받아들입니다. GDEF 테이블이 없는 글꼴은 "무시되는 글리프 없음"으로 fallback하므로, GDEF가 없는 글꼴을 쓰는 호출자의 출력은 바이트 단위로 동일하게 유지됩니다

 

TTF subsetter closure (MarkUnicodeGlyphUsed)

HotPDF의 v2.84.0 TTF 서브셋터는 사용된 글리프 집합을 cmap을 거쳐 FUnicodeUsedCps에서 도출합니다. GSUB 대체 글리프(스타일 대체 글리프, 합자, 문맥 변형 등 위의 조회 API 7개가 반환하는 모든 것)는 일반적으로 cmap으로 도달하는 코드 포인트가 없으므로, 이전에는 서브셋터에 보이지 않았고 소비하는 리더가 그 자리에 .notdef를 렌더링했습니다

 

GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst가 반환한 GID를 PDF 텍스트 스트림에 출력한 뒤에는, 출력한 GID마다 한 번씩 MarkUnicodeGlyphUsed(GID)를 호출하여 임베드 서브셋에 포함시키십시오. 이 헬퍼는 멱등이고, 방어적이며(범위를 벗어난 GID는 조용히 무시됩니다), v2.84.0 composite-glyph closure 패스와 통합되어 있습니다. 호출자는 최상위 대체 GID만 표시하면 되고, 합성 구성 요소는 자동으로 포함됩니다

 

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;

 

Scope and limitations

이 엔진은 조회 전용 인터페이스입니다. "여기서 GSUB가 무엇을 할 것인가"에는 답하지만, 자동 셰이핑 파이프라인(Harfbuzz급 레이아웃, Indic의 클러스터 인식 재정렬, BiDi 해석, GPOS 위치 지정, mark 부착)은 실행하지 않습니다. 스캔 루프를 구동하고, 어떤 대체 / 대안 글리프를 출력할지 선택하고, 출력한 각 대체 GID에 대해 MarkUnicodeGlyphUsed를 호출하고, 글꼴이 요구하는 GPOS / mark 위치 지정을 적용하는 것은 호출자의 몫입니다

 

Producer 측 Arabic / Persian / Urdu 셰이핑(LAM-ALEF 필수 합자 + Arabic Presentation Forms-A)은 텍스트 출력 중에 자동으로 실행되는 별도의 내장 파이프라인으로 구현되어 있습니다. 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 적용(Phase 4). v2.119.47 Contextual + Chained Contextual + SequenceLookupRecord dispatcher(Phase 5). v2.119.48 Reverse Chained Contextual - LookupType 1-8 매트릭스 완료(Phase 6). v2.119.49 Script / LangSys 선택 API(Phase 7). v2.119.50 MarkUnicodeGlyphUsed를 통한 TTF 서브셋터 closure(Phase 9. Phase 8은 producer 측 셰이핑 통합 spike였으며 이후 개정을 위해 8a-8f로 분할됨)

 

See also: Arabic / Persian / Urdu Shaping, CFF / OpenType Font Subsetting Functions, THotPDF.EnableFontSubsetting