Skip to content

feat(content): recover malformed AutoCAD CID fonts (leaked-CID + broken subset) - #15

Open
lFLouSel wants to merge 1 commit into
Xero-Team:mainfrom
lFLouSel:feat/recover-malformed-cid-fonts
Open

feat(content): recover malformed AutoCAD CID fonts (leaked-CID + broken subset)#15
lFLouSel wants to merge 1 commit into
Xero-Team:mainfrom
lFLouSel:feat/recover-malformed-cid-fonts

Conversation

@lFLouSel

@lFLouSel lFLouSel commented Aug 5, 2026

Copy link
Copy Markdown

Two related defects seen in CAD (AutoCAD / nanoCAD) PDFs make composite-font text render as tofu or vanish. Both are diagnosed by a single cheap page pre-scan and repaired per show operator, so they are grouped in one PR -- they share the outline memoization and the per-operator override/restore plumbing.

1. Leaked CID font (show-before-Tf)

A drawing emits a text show before the block's Tf, so the show inherits ("leaks") the previous block's font. The bytes are 2-byte CIDs typeset for a composite Type0 font, but the leaked font is a simple 1-byte font: 00 13 decodes as two codes, the 0x00 high byte hits .notdef, and the glyph renders as tofu.

The reverse also occurs: after a composite Tf, later simple 1-byte text shows without re-issuing Tf and is mis-read as 2-byte CIDs (an odd-length string element proves the run is really simple text).

Repair: arm_leaked_cid_fix pre-scans the page once, recording the CIDs each explicitly-selected Type0 font legitimately shows. A leaked show is rerouted to the Type0 font whose set best covers its codes (persisted like an inserted Tf). Forward leaks are rerouted per-operator to the sibling simple font of the same BaseFont.

2. Corrupted Identity-H subset

A Type0 subset's embedded program is missing the very glyphs it shows (empty loca entries) while a sibling Type0 font carries them at the same CIDs. Repair: arm_broken_subset_fix reroutes such a font, whole, to a sibling that outlines a majority of its missing CIDs -- guarded so a healthy font is never touched and an unrelated font cannot hijack the run.

Safety

Both repairs are inert unless the page exhibits the defect -- no display-list change otherwise. Outline lookups are memoized per (font, CID).

Tests

Five tests build synthetic pages (including minimal 8-glyph TrueType fixtures with present vs empty glyphs) and assert: leaked shows reroute to the covering Type0 font and persist; forward-leaked simple text routes to the simple sibling while genuine CID shows stay Type0; a broken subset reroutes to a recovering sibling; and no reroute happens when no sibling can recover the glyphs.

…en subset)

Two related defects seen in CAD (AutoCAD/nanoCAD) PDFs make composite-font text
render as tofu or vanish. Both are diagnosed by a single cheap page pre-scan and
repaired per show operator, so they are grouped here (they share the outline
memoization and the per-operator override/restore plumbing).

1. Leaked CID font (show-before-Tf). A drawing emits a text show *before* the
   block's `Tf`, so the show inherits ("leaks") the previous block's font. The
   bytes are 2-byte CIDs typeset for a composite Type0 font, but the leaked font
   is simple 1-byte: `00 13` decodes as two codes, the `0x00` high byte hits
   .notdef, and the glyph renders as tofu. The reverse also occurs: after a
   composite `Tf`, later simple 1-byte text shows without re-issuing `Tf` and is
   mis-read as 2-byte CIDs (an odd-length element proves it is really simple).
   Repair: `arm_leaked_cid_fix` pre-scans the page, recording the CIDs each
   explicitly-selected Type0 font legitimately shows; a leaked show is rerouted
   to the Type0 font whose set best covers its codes (persisted like an inserted
   `Tf`). Forward leaks are rerouted per-operator to the sibling simple font of
   the same BaseFont.

2. Corrupted Identity-H subset. A Type0 subset's embedded program is missing the
   glyphs it shows (empty `loca`) while a sibling Type0 font carries them at the
   same CIDs. Repair: `arm_broken_subset_fix` reroutes a font, whole, to a
   sibling that outlines a majority of its missing CIDs — guarded so a healthy
   font is never touched and an unrelated font cannot hijack the run.

Both repairs are inert unless the page exhibits the defect (no page changes
otherwise). Tests build synthetic pages (including minimal TrueType fixtures
with present vs empty glyphs) and assert reroute happens exactly when warranted
and is skipped when no sibling can recover the glyphs.
@lFLouSel
lFLouSel requested a review from YUZHEthefool as a code owner August 5, 2026 13:21
@github-actions github-actions Bot added area: content Content, display lists, fonts, images, and color size: L 500-999 changed lines, excluding Cargo.lock enhancement New feature or request labels Aug 5, 2026

@YUZHEthefool YUZHEthefool left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on the subset-font recovery path. I cannot merge this version yet because the two pre-scans parse the whole content stream before normal interpretation and retain operands in an unbounded pending vector. That bypasses the existing operand/operator/time limits and reopens an operand-only DoS path. Please make the scan share the interpreter budgets and safely disable recovery on exhaustion.

There are also correctness blockers: the scan forces two-byte CID codes and treats odd-length strings as simple-font leakage, while normal rendering uses the CMap decoder and valid Type0 CMaps can have variable-length codes. Finally, outline presence alone does not prove two sibling CID fonts are semantically equivalent; redirecting every run without validating mappings/metrics can silently substitute unrelated text. Please reuse the actual CMap decoding, narrow the recovery condition substantially, and add regressions for these cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: content Content, display lists, fonts, images, and color enhancement New feature or request size: L 500-999 changed lines, excluding Cargo.lock

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants