feat(content): recover malformed AutoCAD CID fonts (leaked-CID + broken subset) - #15
feat(content): recover malformed AutoCAD CID fonts (leaked-CID + broken subset)#15lFLouSel wants to merge 1 commit into
Conversation
…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.
YUZHEthefool
left a comment
There was a problem hiding this comment.
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.
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 13decodes as two codes, the0x00high 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-issuingTfand is mis-read as 2-byte CIDs (an odd-length string element proves the run is really simple text).Repair:
arm_leaked_cid_fixpre-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 insertedTf). 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
locaentries) while a sibling Type0 font carries them at the same CIDs. Repair:arm_broken_subset_fixreroutes 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.