openpdf-renderer: render annotation appearance streams (/AP /N) - #1606
Open
andreasrosdalw wants to merge 3 commits into
Open
openpdf-renderer: render annotation appearance streams (/AP /N)#1606andreasrosdalw wants to merge 3 commits into
andreasrosdalw wants to merge 3 commits into
Conversation
OpenPdfCorePageRenderer only ever walked the page content stream, so annotations -- stamps, highlights, square/circle markup, free text callouts, filled-in form field appearances -- never rendered at all, regardless of what the PDF producer baked into their /AP appearance streams. That's a real, common gap: any PDF with form fields or markup annotations rendered as if they were blank. After the page content stream finishes, render each /Annots entry's normal appearance (/AP /N -- directly a stream, or a sub-dictionary of states selected by /AS, e.g. a checkbox's /Yes vs /Off) at its /Rect, per the PDF §12.5.5 placement algorithm: the appearance's /BBox is mapped through its own /Matrix, reduced to an axis-aligned bounding box, then fitted to /Rect by independent X/Y scale + translate. Reuses the existing renderForm() for the actual Matrix/BBox-clip/content-stream handling, since an appearance stream is structurally a Form XObject. Skips Hidden/NoView-flagged and Popup annotations, and annotations with no usable appearance stream -- deliberately does not synthesize a default appearance (e.g. a Link's usually-invisible border) when the PDF doesn't provide one. Also resets the page transform/clip to their initial (page-to-device) state before rendering annotations, since annotation /Rect coordinates are defined in page space and a content stream with unbalanced q/Q (or a top-level cm) could otherwise leave stale state behind.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 9 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
- Move the ANNOTATION_FLAG_HIDDEN / ANNOTATION_FLAG_NOVIEW constants next to the other constant fields at the top of the class instead of declaring them inline next to their first use. - Test file: use the already-imported PdfWriter/Rectangle instead of fully-qualified names, and extract the shared annotation/appearance setup between the two new tests into buildAnnotatedPdf(boolean) to remove the duplication between them.
andreasrosdalw
added a commit
to andreasrosdalw/OpenPDF
that referenced
this pull request
Jul 28, 2026
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1604, LibrePDF#1605, and LibrePDF#1606, so these four independent PRs (all based on master) don't each carry their own overlapping README.md diff and risk merge conflicts with each other depending on merge order.
andreasrosdalw
added a commit
to andreasrosdalw/OpenPDF
that referenced
this pull request
Jul 28, 2026
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1603, LibrePDF#1605, and LibrePDF#1606, so these four independent PRs (all based on master) don't each carry their own overlapping README.md diff and risk merge conflicts with each other depending on merge order.
andreasrosdalw
added a commit
to andreasrosdalw/OpenPDF
that referenced
this pull request
Jul 28, 2026
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1603, LibrePDF#1604, and LibrePDF#1606, so these four independent PRs (all based on master) don't each carry their own overlapping README.md diff and risk merge conflicts with each other depending on merge order.
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1603, LibrePDF#1604, and LibrePDF#1605, so these four independent PRs (all based on master) don't each carry their own overlapping README.md diff and risk merge conflicts with each other depending on merge order.
This was referenced Jul 28, 2026
|
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.



Summary
OpenPdfCorePageRendereronly ever walked the page content stream, so annotations — stamps, highlights, square/circle markup, free-text callouts, filled-in form field appearances — never rendered at all, regardless of what the PDF producer baked into their/APappearance streams. Any PDF using form fields or markup annotations rendered as if it had none./Annotsentry's normal appearance (/AP /N— directly a stream, or a sub-dictionary of states selected by/AS, e.g. a checkbox's/Yesvs/Off) is now rendered at its/Rect, per the PDF §12.5.5 placement algorithm: the appearance's/BBoxis mapped through its own/Matrix, reduced to an axis-aligned bounding box, then fitted to/Rectby independent X/Y scale + translate (no rotation/shear beyond what/Matrixitself already encodes).renderForm()for the actual/Matrix//BBox-clip/content-stream handling, since an appearance stream is structurally a Form XObject — added a small sharedformMatrixOf()helper so both call sites parse/Matrixthe same way./Fbits 2/6) and Popup annotations, and skips annotations with no usable appearance stream. Deliberately does not synthesize a default appearance (e.g. a Link's usually-invisible border, or a Square/Circle's/IC//C//BS-derived look) when the PDF doesn't provide one — that's a separate, larger feature./Rectcoordinates are defined in page space and a content stream with unbalancedq/Q(or a top-levelcm) could otherwise leave stale transform/clip state behind from content-stream processing.openpdf-renderer/README.md(XObject/annotation coverage prose + the supported-features table) and the class-level Javadoc.