fix(pdf): read a literal string with nested parentheses - #711
Merged
Conversation
ISO 32000-1 7.3.4.2 lets a balanced pair of parentheses stand unescaped inside a literal string, so the reader now tracks nesting depth and ends the string only on the `)` closing the outermost pair. `cairo` and `pdfTeX` write their `/Producer` and `/Creator` that way; those files lost their document metadata to the best-effort catch in `pdf_file.cpp`, and a catalog reached through such a string made the whole render fail. Also give `ViewStream` the seeking it was documented not to have: `FileParser::read_object_stream` addresses each member by absolute position, and a silently failing `seekg` left it reading them in order — correct only while the members are laid out as the header lists them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDs5aK3ZGSZsEvqUUwBBXU
andiwand
force-pushed
the
pdf-nested-parentheses-string
branch
from
August 20, 2026 08:27
acf8f36 to
39f2a0d
Compare
andiwand
enabled auto-merge (squash)
August 20, 2026 08:28
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
A public pdf printed from Firefox (https://opendocument.app/, cairo 1.18.4)
failed to render at all —
map::at: key not found, thrown whereparse_document_implreaches for a/Rootthat recovery never found.The cause is one line of ISO 32000-1 7.3.4.2: balanced pairs of parentheses
within a string require no special treatment.
read_stringended the string atthe first
), so(cairo 1.18.4 (https://cairographics.org))stopped mid-valueand the rest of the dictionary parsed as garbage. That dictionary is the
/Infoof an object stream whose members include the document catalog — so the parse of
the catalog's own object stream threw, the xref recovery scan hit the same wall
looking for
/Type /Catalog, and the retry ran into a trailer with no/Root.read_stringnow tracks nesting depth and returns only on the)closing theoutermost pair. An escaped parenthesis goes through the escape branch and never
touches the depth.
Also here:
ViewStreamgets the seeking its comment said it did not have.FileParser::read_object_streamseeks to each member's absolute position, andstd::streambuf's defaultseekofffails, so the seek was silently a no-op andthe members were read in order instead. That happens to be right for every file
we have, and wrong for any object stream whose members are not laid out as its
header lists them — nothing in the spec requires them to be.
What changes for a reader
Beyond the new file rendering, three pdfs in the private set get their document
metadata back. It was all-or-nothing (
pdf/AGENTS.md), and a nested-parenthesisproducer string had been throwing the whole thing away:
basic_text.pdf,test_fail.pdf—pdfTeX-1.40.14, whose/PTEX.Fullbannernests parentheses
text_visibility.pdf—cairo 1.13.1 (http://cairographics.org)Tests
PdfObjectParser.standard_string_balanced_parenthesesandViewStream.seek/
ViewStream.seek_out_of_range— new unit testspdf/opendocument-app-website.pdfadded to the public input set, with itsreference output; the three private
meta.jsonfiles regenerated