fix(pdf): word spacing, page rotation and glyph names - #702
Merged
Conversation
Four rendering fixes found while reading six new pdf test files. Word spacing: `Tw` reached only the substitute-font path. A run painted with an embedded font carries PUA glyphs, whose space is not a U+0020 for CSS `word-spacing` to move, so the property was skipped and every justified line rendered short — the shortfall pooling into gaps mid-word. Byte 32 now stays a literal space and the emitted font maps U+0020 onto the same glyph, so the painted shape is unchanged. Page rotation: `/Rotate` was parsed and read by nothing. It now composes onto the page-box transform and swaps the reported dimensions on a quarter turn. Glyph names: a simple font's `/Encoding` names now select the glyph through the font program's own charset (9.6.6.2) before the glyph-list detour. Subset producers name glyphs `gidNNNNN`, which no glyph list translates, so those files fell through to code-as-GID and painted unrelated glyphs. It also picks the exact named variant a producer asks for, e.g. `hyphen.case` over `hyphen`. Flate streams: a producer that omits the ADLER32 trailer no longer fails to decode, and bytes trailing a stream are no longer appended to what it decodes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014VsAxi7RXU1TNQVz8EM7VJ
andiwand
force-pushed
the
fix/pdf-word-spacing-rotation-glyph-names
branch
from
August 18, 2026 18:09
f03169c to
b7426e2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
Four rendering fixes, found by reading six new pdf test files into the private corpus.
Word spacing (
Tw)html/pdf_file.cppemitted CSSword-spacingonly on the substitute-fontpath. A run painted with an embedded font carries PUA-re-encoded glyphs, whose
space is not a real U+0020, so the property was skipped outright — and every
justified line rendered ~9% short, the shortfall pooling into gaps mid-word:
Byte 32 now keeps a literal U+0020 in the visual layer and
write_font_facemaps U+0020 onto the very glyph the font puts at that code, so the painted
shape is unchanged and the browser can space it. The visual run is escaped with
escape_markuprather thanescape_text, whose substitution is adifferent character that
word-spacingdoes not move./Rotatepdf_document_parser.cppresolved and normalizedpage.rotate, and nothingever read it. It now composes onto
begin_page's box transform, and a quarterturn swaps the reported page dimensions.
svg_background_with_page_rotation_issue402.pdf— the file added for issue 402 — renders landscape for the first time.
Glyph names through the font's charset
abstract::Fontgainsglyph_for_name, implemented onCffFontagainst thecharset;
Font::glyph_for_codeconsults it before the glyph-list detour, as9.6.6.2 prescribes for simple Type1/CFF fonts. Subset producers name glyphs
gidNNNNN, which no glyph list translates, so those files fell all the waythrough to the code-as-GID last resort and painted unrelated glyphs
(
Gutenbergstr, 21→6cm87G8utemun▯▯2). It also picks the exact namedvariant a producer asks for — one file had been drawing a comma where the pdf
asked for
hyphen.case.Flate streams without an ADLER32 trailer
A producer that ends its streams at the final deflate block and omits the
4-byte checksum made the whole file fail to decode.
zlib_inflatedrops thecheck (ghostscript does the same, loudly) and clears the input queue, which
also stops stray bytes trailing a stream from being appended to what it
decodes.
Test
data pins advanced.
CffFontTest.SelectsGlyphByNameand
PdfFont.simple_font_glyph_for_code_via_font_charset.file.
Known gap left for follow-ups: JBIG2 image masks and filled form-field
appearance streams.