feat(pdf): annotate a pdf from json - #848
Merged
Merged
Conversation
`PdfFile::annotate` takes the payload our browser-side annotator will produce — highlight, underline, strike-out, squiggly and ink, in pdf user space — and writes the annotated file. It hangs off the file rather than off `Document`, which pdf does not have, and addresses annotations by page index and geometry rather than by element id. A `version` that is not 1 is refused rather than half-understood, as is an unknown type, a page that is not there, a quad that is not eight coordinates, and a missing required member. Annotations landing on one page share a single page rewrite. `FileTypeCapabilities` gains `annotate`, and the conformance test holds it to the same rule as the rest: a type nothing can open cannot claim it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018e3PEzyU2oAFSzsEoWsSmz
`PdfFile::annotate` promises `std::invalid_argument` for a malformed payload, but nlohmann reports a member of the wrong type — `"page": "0"`, a colour of strings, a string `version` — as a `json::type_error`, which escaped the public entry point unconverted. One catch now covers the whole read, so the parse error and the type error arrive the same way, and three cases pin it. Keys the per-page bundle by page index rather than by `Page *`, so the page rewrites run in page order rather than in allocation order, and puts the new includes in the order the file already had. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147n68S7LNAv9KynLaGgXN4
andiwand
force-pushed
the
feat/pdf-annotate-api
branch
from
September 6, 2026 15:45
28a9001 to
16a4509
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
Phase 4 of
docs/design/pdf-annotation.md. Stacked on #847. Second of four; the browser layer and bindings follow.Takes the JSON payload the browser-side annotator will produce and writes the annotated file. Per decision 1, it hangs off
PdfFile, notDocument— pdf has none — and addresses annotations by page index and user-space geometry, never by element id. That is what keeps this feature independent of the deferredediting.mdwork.{"version": 1, "annotations": [ {"page": 0, "type": "highlight", "quads": [[72, 700, 300, 700, 72, 688, 300, 688]], "color": [1, 0.9, 0.2], "opacity": 1, "author": "…", "contents": "…"}, {"page": 1, "type": "ink", "strokes": [[100, 500, 130, 540]], "width": 2, "color": [0.9, 0.1, 0.1]} ]}Refused rather than half-understood
Fail-fast, per the repo convention — each with its own test:
versionthat is not 1 (the drift guard against a newer frontend)typewe do not writepagethat is not therequadthat is not eight coordinatescolor,quads,type,page)An empty payload is legal and writes a no-op update, which falls out of the writer rather than being special-cased.
Annotations landing on the same page share a single page rewrite rather than one per annotation.
Capability
FileTypeCapabilitiesgainsannotate, set on the pdf row. The existingedit/savebits are documented againstDocument, which pdf has none of, so reusing either would have been a lie — and the conformance test'sedit ⇒ saveinvariant would not hold.annotategets the same treatment the others get: a type nothing can open cannot claim it.Bindings for it come with the rest in the bindings PR.
Verified
Seven tests over
style-various-1.pdfthrough the real public entry point: a highlight lands with its appearance resolved, all five types land on the right pages,author/contents/opacityreach the file (with a(incontentssurviving escaping), the empty payload, every refusal, and the encrypted file.448 tests pass across the pdf, writer, odr, file and html suites;
-Werrorclean. clang-tidy on the touched file reports only two pre-existing findings (file_meta's exception-escape at an untouched line, and the knowndecryptshadowing false positive).Changelog entry added.