THotPDF.AssignSyntheticCodepointForGID / GetSyntheticCodepointForGID

THotPDF PUA synthetic codepoint allocator (v2.119.68)

 

GSUB Engine  Auto Shaping Pipeline  Arabic Shaping

為沒有自然 Unicode codepoint 可透過字型 cmap 抵達的 OpenType GSUB substitute GIDs,設定並查詢 Private Use Area(U+E000 - U+F8FF)synthetic codepoints。這補上 v2.119.43-66 GSUB query 與 refinement APIs 留下的 producer-side GID-level emission gap

 

Delphi 語法:

function AssignSyntheticCodepointForGID(GID: Word; out SyntheticCP: Word): Boolean;

function GetSyntheticCodepointForGID(GID: Word): Word;

 

Why the API exists

v2.119.32-67 producer-side automatic shaping pipeline(Arabic / Latin / Devanagari)要求 GSUB engine 回傳的 substitute GIDs 可透過 Unicode codepoint 抵達;既有 hex-encoded text pipeline 輸出的是 codepoints 而非 GIDs,閱讀端會透過 embedded /CIDToGIDMap 將 codepoint 解析回 GID。對於可透過字型 cmap 取得自然 Unicode codepoint 的 substitute GIDs(Arabic Presentation Forms、Latin Standard Ligatures FB00-FB06),既有 pipeline 可正常運作/CIDToGIDMap. For substitute GIDs that have a natural Unicode codepoint via the font's cmap (Arabic Presentation Forms, Latin Standard Ligatures FB00-FB06), the existing pipeline works fine.

 

但落在 font-internal GIDs 上的 font-specific substitutes,例如多數 Devanagari cluster shapes、字型設計者只以 numbered GIDs 提供的 stylistic alternates、CJK ideographic variation sequences(IVS)、沒有對應 Presentation Form 的 discretionary ligatures,在字型 cmap 中完全沒有 codepoint。v2.119.68 之前,這些 GIDs 無法透過 producer-side hex pipeline 抵達;v2.119.68 允許呼叫端為任意 GID 在 Private Use Area 設定 synthetic codepoint,以補上此缺口

 

AssignSyntheticCodepointForGID semantics

為提供的 GID 設定下一個可用 PUA codepoint(從 U+E000 開始),並將設定鏡射到既有 producer-side hex pipeline + consumer-reader resolution chain 所依賴的每個 cache:

 

1. FUnicodeCpToGid[SyntheticCP] := GID - so the producer-side hex pipeline emits SyntheticCP into the text-showing operator and the consumer reader resolves SyntheticCP back to GID through /CIDToGIDMap at render time.

2. FAcroFormUnicodeAdvances[SyntheticCP] := em-fraction - so the v2.65 word-wrap calculator finds the correct hmtx advance for the synthetic codepoint when it appears in AcroForm text-field content.

3. FUnicodeSyntheticCpForGID[GID] := SyntheticCP - the per-GID reverse-lookup table used by GetSyntheticCodepointForGID to make repeat AssignSyntheticCodepointForGID calls idempotent (the second call with the same GID returns the already-allocated SyntheticCP).

 

Returns True on success with SyntheticCP set to the allocated codepoint. Returns False (and leaves SyntheticCP at 0) under any of these defensive conditions: no font registered (RegisterUnicodeTTF never called or called with empty arguments to reset state), invalid GID (zero or beyond the cmap's glyph count), PUA range exhausted (all 6400 slots U+E000 - U+F8FF allocated), cache uninitialised on entry.

 

GetSyntheticCodepointForGID semantics

Pure-functional query of any existing assignment. Returns the synthetic codepoint allocated for GID if AssignSyntheticCodepointForGID(GID, ...) has been called previously; otherwise returns 0 (which is not a valid PUA codepoint, so it doubles as a "no assignment" sentinel). Does not allocate. Safe to call before any AssignSyntheticCodepointForGID has run.

 

Allocator state lifecycle

FUnicodeSyntheticCpForGID and the next-available-PUA cursor (FUnicodeNextSyntheticCp) are lazy-allocated on first AssignSyntheticCodepointForGID call. The cursor starts at 0 (uninitialised) and bumps to $E000 on first allocation; subsequent allocations move it through $E001, $E002, ..., FUnicodeSyntheticCpForGID[GID] := SyntheticCP - the per-GID reverse-lookup table used by GetSyntheticCodepointForGID to make repeat AssignSyntheticCodepointForGID calls idempotent (the second call with the same GID returns the already-allocated SyntheticCP).

 

成功時回傳 True,並將 SyntheticCP 設為設定的 codepoint。遇到下列任一防禦條件時回傳 False(且讓 SyntheticCP 保持 0):未註冊字型(從未呼叫 RegisterUnicodeTTF,或以空參數呼叫以重設狀態)、無效 GID(零或超出 cmap glyph count)、PUA range 耗盡(U+E000 - U+F8FF 的 6400 個 slots 全部已設定)、進入時 cache 尚未初始化

 

GetSyntheticCodepointForGID semantics

對任何既有 assignment 的 pure-functional query。若先前已呼叫 AssignSyntheticCodepointForGID(GID, ...),則回傳為 GID 設定的 synthetic codepoint;否則回傳 0(不是有效 PUA codepoint,因此也作為「no assignment」sentinel)。不會設定。可在任何 AssignSyntheticCodepointForGID 執行前安全呼叫

 

Allocator state lifecycle

FUnicodeSyntheticCpForGID 與 next-available-PUA cursor(FUnicodeNextSyntheticCp)會在首次 AssignSyntheticCodepointForGID 呼叫時 lazy-allocate。cursor 起始為 0(uninitialised),第一次設定時跳到 $E000;後續設定會依序移動到 $E001、$E002、...、$F8FF。每次 RegisterUnicodeTTF('', nil) 都會將這兩個 fields 與其餘 per-font subset state 一起重設為 empty / 0,因此跨多份檔案重用 THotPDF instance 的呼叫端,每份檔案都會從新的 PUA cursor 開始

 

Typical workflow (Devanagari cluster shape)

 

PDF.RegisterUnicodeTTF('NotoDeva', 'NotoSansDevanagari-Regular.ttf');

PDF.ShapingFeatures := [sfIndicShaping];

PDF.SetGSUBScript('deva');

 

// Get a font-internal cluster GID through the GSUB engine

ClusterGID := PDF.GetSingleSubstituteGlyph(BaseGID, 'nukt');

if ClusterGID <> BaseGID then

begin

  // Check if cmap reaches the substitute - usually no for Indic

  // clusters, since cluster GIDs are font-internal

  // Allocate a synthetic codepoint that the producer-side

  // hex pipeline can emit

  if PDF.AssignSyntheticCodepointForGID(ClusterGID, SyntheticCP) then

  begin

    // SyntheticCP is now in the U+E000-F8FF range; emit it

    // through UnicodeTextOut just like a normal codepoint

    PDF.CurrentPage.UnicodeTextOut(X, Y, 0, UnicodeChar(SyntheticCP));

    PDF.MarkUnicodeGlyphUsed(ClusterGID);

  end;

end;

 

Idempotency example

 /CIDToGIDMap to the target GID, then renders that GID using the embedded font program. From the reader's perspective there is no difference between a "natural" Unicode codepoint that the cmap routes to GID and a PUA synthetic codepoint that /CIDToGIDMap routes to GID - both produce the same rendered glyph.

 

Copy / paste behavior: PUA codepoints round-trip as themselves through copy / paste when the ToUnicode CMap declares them as identity mappings. Callers that want the source Unicode characters (the input run that produced the substitute) to round-trip instead can register a reverse mapping with RegisterToUnicodeReverseMapping or author ActualText marked-content sequence properties through BeginTaggedContent and emit the synthetic codepoints inside the bracketed content. HotPDF uses the same internal-CID pattern automatically for RegisterUnicodeTTF-backed AcroForm appearance streams that contain supplementary-plane Unicode characters.

PDF.AssignSyntheticCodepointForGID(150, CP1);  // CP1 = $E000

PDF.AssignSyntheticCodepointForGID(151, CP2);  // CP2 = $E001

PDF.AssignSyntheticCodepointForGID(150, CP3);  // CP3 = $E000 (idempotent)rlig + Latin liga / clig + rclt automatic emission (Phase 8b / 8c.2 / 8b / GSUB 'rclt'), ToUnicode reverse-mapping (v2.119.61 / 62 / 65), advance query (v2.119.64), Devanagari Indic reorder pre-pass (v2.119.67), and now PUA synthetic codepoint GID-level emit (v2.119.68) all integrate into a single producer-side shaping surface that handles every kind of substitute glyph an OpenType font can produce.

 

請參閱: OpenType GSUB Substitution Engine, Automatic Shaping Pipeline (Phase 8), Arabic / Persian / Urdu Shaping Support, Syriac / Mongolian / Devanagari Shaping, THotPDF.BeginTaggedContent