Carry links and bookmarks over, and add sanitize and flatten - #4
Merged
Conversation
This package rebuilds a file rather than editing it, which quietly cost two things and quietly kept a third it should not have. Links were copied as they stood. A link pointing at page three of the source still pointed at the source's own page object — which the copy then followed, dragging that file's whole page tree into a file that was meant to hold one page. Destinations are now remapped: a link or a bookmark is pointed at the page it became here, in whatever shape the file wrote it — an array, a dictionary, a name in either of the two places a name tree may live. That needed pages to be numbered before they are written, since what goes on one may name another. Bookmarks were dropped altogether, so a merge lost every one. They are now carried over from each source document in turn, and a bookmark whose target was removed goes with it — but one that led nowhere in the source stays, without a destination. That distinction matters: 7% of the corpus's bookmarks are already broken where they are, and dropping them would have been reported as this package losing them. Sanitize removes what runs rather than shows: page and annotation actions, JavaScript, launching, form submission, and the annotation types that exist to play or embed something. It also drops /AF, which is how a page carries a file with it in PDF 2.0 — and how, in two corpus files, an embedded file survived a catalogue this package had rebuilt from nothing. A link to the web is not executable and stays. Flatten draws each annotation's own appearance where it sits, honouring the state it says it is in and the matrix it says it has, and then drops the annotations. Measured on the corpus: 84 929 of 84 935 annotations and all 19 386 bookmarks survive an identity write, no destination points outside the document it was written into, and a sanitised file holds nothing executable. The four earlier whole-corpus checks still hold. One thing the campaign found and this does not fix: 355 files come out more than three times their input, because a large dictionary has nowhere to be compressed. Object streams in the writer are what that needs, and they are the next thing. Exact 100% statement coverage over the package and the command, go vet clean, nine cross-compilation targets. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
This package rebuilds a file rather than editing it, which quietly cost two
things and quietly kept a third it should not have.
Links were copied as they stood. A link pointing at page three of the source
still pointed at the source's own page object — which the copy then followed,
dragging that file's whole page tree into a file that was meant to hold one
page. Destinations are now remapped: a link or a bookmark is pointed at the
page it became here, in whatever shape the file wrote it — an array, a
dictionary, a name in either of the two places a name tree may live. That
needed pages to be numbered before they are written, since what goes on one may
name another.
Bookmarks were dropped altogether, so a merge lost every one. They are now
carried over from each source document in turn, and a bookmark whose target was
removed goes with it — but one that led nowhere in the source stays, without a
destination. That distinction matters: 7% of the corpus's bookmarks are already
broken where they are, and dropping them would have been reported as this
package losing them.
Sanitize removes what runs rather than shows: page and annotation actions,
JavaScript, launching, form submission, and the annotation types that exist to
play or embed something. It also drops /AF, which is how a page carries a file
with it in PDF 2.0 — and how, in two corpus files, an embedded file survived a
catalogue this package had rebuilt from nothing. A link to the web is not
executable and stays. Flatten draws each annotation's own appearance where it
sits, honouring the state it says it is in and the matrix it says it has, and
then drops the annotations.
Measured on the corpus: 84 929 of 84 935 annotations and all 19 386 bookmarks
survive an identity write, no destination points outside the document it was
written into, and a sanitised file holds nothing executable. The four earlier
whole-corpus checks still hold.
One thing the campaign found and this does not fix: 355 files come out more
than three times their input, because a large dictionary has nowhere to be
compressed. Object streams in the writer are what that needs, and they are the
next thing.
Exact 100% statement coverage over the package and the command, go vet clean,
nine cross-compilation targets.