Skip to content

fix(font): preserve Type1 charstring widths and control flow - #1060

Merged
andiwand merged 3 commits into
mainfrom
review/42-type1-charstrings
Oct 5, 2026
Merged

andiwand merged 3 commits into
mainfrom
review/42-type1-charstrings

Conversation

@andiwand

@andiwand andiwand commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Type1 subroutine return did not stop execution, widths were truncated, and sbw's vertical side bearing was discarded. Honor returns, retain fractional widths, apply both side bearings to the first move, and emit pending width when synthesizing endchar. Return the converted bytes directly instead of unused width metadata.

Reject invalid fixed-point values, division, subroutine indices, argument counts and stacks; enforce Type1's ten-level nesting limit. A charstring that fails these checks becomes an empty glyph in the CFF, so one broken glyph does not refuse the font. Drop stem hints along with the other unsupported hints: Type1 permits sequences that cannot be copied directly into Type2's hint ordering.

Validation: 48 targeted font tests and all 50 PDF corpus cases pass; warning counts/messages match the preceding layer. Extend the existing subroutine regression and add two tests for widths/bearings and invalid arithmetic/control flow.

@andiwand
andiwand force-pushed the review/41-type1-decoding branch from 8bf12c4 to d25dc82 Compare October 5, 2026 12:17
Base automatically changed from review/41-type1-decoding to main October 5, 2026 12:43
andiwand and others added 2 commits October 5, 2026 14:43
to_type2 now throws on a division by zero, an invalid subroutine index or
an overflowing stack, and to_cff let the error refuse the whole font. A
PDF then falls back to a substitute font and shows the wrong glyphs.
to_cff now turns a charstring that does not translate into an empty
glyph, and the other glyphs stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gb1fLafqqzehpqPuU6uBfn
@andiwand
andiwand force-pushed the review/42-type1-charstrings branch from 8db1ac8 to 87186bf Compare October 5, 2026 12:48
Type1 and CFF each checked by hand that a double is a finite whole value
in the range of an integer type. util::number::to_integer<T> now does
this, and the three call sites throw their own errors on an empty result.
The bound is 2^digits, which is exact as a double, so the check is also
correct for 64-bit types, where max() rounds up to 2^63.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gb1fLafqqzehpqPuU6uBfn
@andiwand
andiwand merged commit af4c24a into main Oct 5, 2026
23 checks passed
@andiwand
andiwand deleted the review/42-type1-charstrings branch October 5, 2026 12:59
andiwand added a commit that referenced this pull request Oct 5, 2026
The PDF outputs changed with #1060, #1061 and #1068: Type1 and CFF font
data, and embedded PNGs split into 64 KiB IDAT chunks. Only data URIs
differ.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gb1fLafqqzehpqPuU6uBfn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant