Replies: 1 comment 1 reply
|
Thank you for sharing this thoughtful and detailed research. I’ll definitely keep it in mind and revisit it when we’re ready to refactor our text-rendering code.
A refactoring of our text-shaping code is overdue, and I’ll definitely take your analysis into consideration when we undertake it. |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
I am sharing an Enhanced Unicode PDF architecture developed for Typsastra. This is not a request for fpdf2 to adopt it, but a working reference that may be useful for future Unicode PDF encoding work, particularly as an alternative to the current per-glyph
/ToUnicodemapping.The implementation was motivated by Khmer support but is script-independent and has been tested with multilingual complex-script content.
Implementations
Typst adapter
Converts already-shaped text into source-ordered logical units:
https://github.com/Sovichea/typst/tree/75202cf09a26a5ef5dfd0f26ab7a4fe007e1be39
https://github.com/Sovichea/typst/blob/75202cf09a26a5ef5dfd0f26ab7a4fe007e1be39/crates/typst-pdf/src/text/logical.rs
Krilla PDF backend
Implements logical CID allocation, synthetic TrueType glyphs, exact
/ToUnicode, font sharding, and logical-orderTJoutput:https://github.com/Sovichea/krilla/tree/d05158cf3ebead248745f846d0397e84dfb9f2d0
https://github.com/Sovichea/krilla/blob/d05158cf3ebead248745f846d0397e84dfb9f2d0/PDF_LOGICAL_UNITS.md
Architecture
The boundary between shaping and PDF generation is a source-ordered logical unit:
The contract is:
flowchart LR A[Logical Unicode] B[Existing shaping engine] C[Positioned glyphs] D[Logical units] E[Semantic and visual key] F[Unique logical CID] G[Synthetic glyph] H[Exact ToUnicode] I[Logical-order TJ] J[PDF] A --> B B --> C C --> D D --> E E --> F F --> G F --> H G --> I H --> I I --> JA CID is reused only when both the logical text and visual construction are identical:
Each synthetic glyph contains the unit's shaped source glyphs as positioned TrueType components. Logical CIDs are then emitted in source order, with
TJadjustments reproducing visual placement.Unsupported units fall back to ordinary glyph output rather than failing the document.
Possible relevance to fpdf2
fpdf2's pipeline in
fpdf/fonts.pyalready contains most of the surrounding machinery: HarfBuzz shaping with cluster tracking,Glyphrecords carrying source codepoint tuples,SubsetMapallocating per-glyph character codes, positionedTJoutput, and fontTools subsetting with synthetic glyph construction (TTGlyphPen, already used for the.notdeffallback).The smallest integration boundary would be to change the unit of identity from "glyph" to "logical unit" at the shaping-to-font boundary:
This would not require replacing fpdf2's shaping engine or redesigning its layout. The main new code would be:
ក្សas three codepoints, not a partial fragment);SubsetMap;/ToUnicodeemitted from the unit's full source text.fpdf2's existing per-glyph
unicodetuple andSubsetMapalready point in this direction, so the gap is smaller than in most PDF generators.Test binary
A prebuilt Typst-compatible binary is available for testing:
https://github.com/Sovichea/typsastra/releases/tag/enhanced-unicode-v0.3.1
It supports Windows, Linux, and macOS. After downloading the appropriate archive:
Relevant fixture:
https://github.com/Sovichea/typsastra/blob/enhanced-unicode-v0.3.1/tests/fixtures/enhanced-unicode/unicode-selection.typ
There is no expectation that fpdf2 adopt this implementation. I am sharing it as a tested architecture that may provide useful ideas for exact Unicode
/ToUnicodeencoding without depending on/ActualText.All reactions