THotPDF.AssignSyntheticCodepointForGID / GetSyntheticCodepointForGID

THotPDF PUA synthetic codepoint allocator (v2.119.68)

 

GSUB Engine  Auto Shaping Pipeline  Arabic Shaping

จัดสรรและสืบค้นรหัสพอยต์สังเคราะห์ในช่วงพื้นที่ใช้งานส่วนบุคคล (Private Use Area - U+E000 - U+F8FF) สำหรับ GID สำรองของ OpenType GSUB ที่ไม่มีรหัสพอยต์ Unicode ธรรมชาติที่เข้าถึงได้ผ่าน cmap ของฟอนต์ ปิดช่องว่างการส่งออกระดับ GID ฝั่งผู้ผลิตที่เหลือจาก GSUB query และ refinement API ของ v2.119.43-66

 

Delphi syntax:

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

function GetSyntheticCodepointForGID(GID: Word): Word;

 

Why the API exists

pipeline automatic shaping ฝั่ง producer ใน v2.119.32-67 (Arabic / Latin / Devanagari) ต้องให้ substitute GIDs ที่ GSUB engine คืนมาเข้าถึงได้ผ่าน Unicode codepoint - pipeline ข้อความ hex-encoded เดิม emit codepoints ไม่ใช่ GIDs และ consumer reader resolve codepoint กลับเป็น GID ผ่าน /CIDToGIDMap ที่ฝังในเอกสาร สำหรับ substitute GIDs ที่มี Unicode codepoint ตามธรรมชาติผ่าน cmap ของ font (Arabic Presentation Forms, Latin Standard Ligatures FB00-FB06) pipeline เดิมทำงานได้ดี

 

แต่ substitutes เฉพาะ font ที่ไปอยู่บน GIDs ภายใน font - เช่น Devanagari cluster shapes ส่วนใหญ่, stylistic alternates ที่ผู้ออกแบบ font ส่งมาเฉพาะเป็น GIDs แบบมีเลข, CJK ideographic variation sequences (IVS), discretionary ligatures ที่ไม่มี Presentation Form ตรงกัน - ไม่มี codepoint ใน cmap ของ font เลย ก่อน v2.119.68 GIDs เหล่านี้เข้าถึงผ่าน producer-side hex pipeline ไม่ได้; v2.119.68 ปิดช่องว่างนี้ด้วยการให้ callers จัดสรร synthetic codepoint ใน Private Use Area สำหรับ GID ใดก็ได้

 

AssignSyntheticCodepointForGID semantics

จัดสรร PUA codepoint ถัดไปที่ว่าง (เริ่มที่ U+E000) สำหรับ GID ที่ส่งเข้ามา และ mirror assignment ลงใน cache ทุกตัวที่ producer-side hex pipeline เดิม + consumer-reader resolution chain ต้องพึ่งพา:

 

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 - เพื่อให้ word-wrap calculator ของ v2.65 พบ hmtx advance ที่ถูกต้องสำหรับ synthetic codepoint เมื่อปรากฏในเนื้อหา AcroForm text-field

3. FUnicodeSyntheticCpForGID[GID] := SyntheticCP - table reverse-lookup ราย GID ที่ GetSyntheticCodepointForGID ใช้เพื่อทำให้ call ซ้ำของ AssignSyntheticCodepointForGID เป็น idempotent (call ที่สองด้วย GID เดิมจะคืน SyntheticCP ที่จัดสรรไว้แล้ว)

 

คืน True เมื่อสำเร็จพร้อมตั้ง SyntheticCP เป็น codepoint ที่จัดสรร คืน False (และปล่อย SyntheticCP เป็น 0) ในเงื่อนไขป้องกันใด ๆ ต่อไปนี้: ยังไม่มี font registered (ไม่เคยเรียก RegisterUnicodeTTF หรือเรียกด้วย arguments ว่างเพื่อ reset state), GID ไม่ถูกต้อง (เป็นศูนย์หรือเกิน glyph count ของ cmap), ช่วง PUA หมด (จัดสรรครบทุก 6400 slots U+E000 - U+F8FF), cache ยังไม่ initialised ตอนเข้า method

 

GetSyntheticCodepointForGID semantics

query แบบ pure-functional ของ assignment ที่มีอยู่ คืน synthetic codepoint ที่จัดสรรให้ GID หากเคยเรียก AssignSyntheticCodepointForGID(GID, ...) มาก่อน มิฉะนั้นคืน 0 (ซึ่งไม่ใช่ PUA codepoint ที่ valid จึงใช้เป็น sentinel "ไม่มี assignment" ได้ด้วย) ไม่จัดสรรค่าใหม่ เรียกได้อย่างปลอดภัยก่อนมี AssignSyntheticCodepointForGID ใดทำงาน

 

Allocator state lifecycle

FUnicodeSyntheticCpForGID และ cursor next-available-PUA (FUnicodeNextSyntheticCp) ถูก lazy-allocate เมื่อเรียก AssignSyntheticCodepointForGID ครั้งแรก cursor เริ่มที่ 0 (ยังไม่ initialised) และขยับเป็น $E000 ในการจัดสรรครั้งแรก; การจัดสรรถัดไปเดินผ่าน $E001, $E002, ..., $F8FF ทั้งสอง fields ถูก reset เป็น empty / 0 ในทุกครั้งที่ RegisterUnicodeTTF('', nil) ถูกเรียกพร้อมกับ state subset ราย font ที่เหลือ ดังนั้น callers ที่ reuse instance THotPDF ข้ามหลายเอกสารจะเริ่มแต่ละเอกสารด้วย allocator ใหม่

 

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

  // จัดสรร synthetic codepoint ที่ฝั่ง producer

  // 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

 

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

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

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

CP4 := PDF.GetSyntheticCodepointForGID(150);  // CP4 = $E000

CP5 := PDF.GetSyntheticCodepointForGID(999);  // CP5 = 0 (no assignment)

 

Consumer-reader behavior

consumer reader เห็น PUA codepoint ใน text-showing operator และ resolve ผ่าน /CIDToGIDMap ที่ฝังในเอกสารไปยัง GID เป้าหมาย จากนั้น render GID นั้นด้วย embedded font program จากมุมมองของ reader ไม่มีความแตกต่างระหว่าง Unicode codepoint "ตามธรรมชาติ" ที่ cmap route ไปยัง GID กับ PUA synthetic codepoint ที่ /CIDToGIDMap route ไปยัง GID - ทั้งสองให้ glyph ที่ render เหมือนกัน

 

พฤติกรรม copy / paste: PUA codepoints round-trip เป็นตัวมันเองผ่าน copy / paste เมื่อ ToUnicode CMap ประกาศเป็น identity mappings callers ที่ต้องการให้ source Unicode characters (input run ที่สร้าง substitute) round-trip แทน สามารถ register reverse mapping ด้วย RegisterToUnicodeReverseMapping หรือเขียน sequence properties ของ marked-content แบบ ActualText ผ่าน BeginTaggedContent และ emit synthetic codepoints ภายใน bracketed content HotPDF ใช้ pattern internal-CID เดียวกันโดยอัตโนมัติสำหรับ AcroForm appearance streams ที่ backed by RegisterUnicodeTTF และมี supplementary-plane Unicode characters

 

Phase 8 roadmap closure

v2.119.68 / Phase 8c.6 ปิด roadmap ของ Phase 8 GSUB engine: query API LookupType 1-8 ทั้งหมด (Phase 1-6), Script / LangSys selection API (Phase 7), entry point ของ TTF subsetter closure (Phase 9), static post-pass ligature folding (v2.119.32 / 58 / 60 / 62), opt-in automatic pipeline (v2.119.59), การ emit อัตโนมัติ Arabic rlig + Latin liga / clig + rclt (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) และตอนนี้ PUA synthetic codepoint GID-level emit (v2.119.68) ผสานเป็น producer-side shaping surface เดียวที่รองรับ substitute glyph ทุกแบบที่ OpenType font สามารถสร้างได้

 

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