Skip to content

Hide page content in optional content groups that are turned off - #583

Merged
Velli20 merged 1 commit into
mainfrom
hide-optional-content-off-layers
Oct 18, 2026
Merged

Velli20 merged 1 commit into
mainfrom
hide-optional-content-off-layers

Conversation

@Velli20

@Velli20 Velli20 commented Oct 18, 2026

Copy link
Copy Markdown
Owner

Summary

Content inside /OC marked-content sequences was painted no matter what the document's layer settings said. Content in a group listed under the default configuration's /OFF still rendered, which differed from PDFium. Page content now respects optional content.

  • Content streams: BDC keeps its property-list name. Resources reads the optional content entries of /Properties. A new OptionalContentFilter in pdf-canvas skips painting and text recording inside sequences whose group or membership is off. It is only created when the document turns some group off, so other documents pay nothing for marked content.
  • Renderers: PdfRenderer passes the document's default visibility. The web page renderer passes the annotation overlay's state, so page content and annotations share it.
  • New pdf-optional-content crate: the optional content model moves out of pdf-annotation-core because rendering now depends on it. All its decoders are FromPdfObject impls. A single /OCG reference is stored as a one-group /AnyOn membership, which merges the former AnnotationOptionalContent enum into OptionalContentMembership.
  • Reader APIs: DictionaryContext::lenient_entries (decode the entries that fit a type, skip the rest) and DictionaryContext::optional_list (one value or an array, nulls skipped).

Notes

  • The generated annotation_contract.ts changes: AnnotationOptionalContent is gone, and an annotation's optional_content is now an OptionalContentMembership.
  • Not covered yet: an /OC entry on a form or image XObject itself. On the web, page content picks up a SetOCGState change the next time the page is rendered.

Testing

  • cargo check --workspace --all-targets, cargo clippy --all --workspace and cargo fmt --check are clean.
  • Tests pass for the changed crates. The full cargo test wasn't run locally because the Skia build script downloads prebuilt binaries.
  • cargo xtask annotation-types --check passes and cargo xtask web-canvas --profile debug builds.
  • cargo conformance render on TCPDF example 024 now matches PDFium: the line in the off layer is hidden, and the line in the on layer and the one in the other layer still show.

🤖 Generated with Claude Code

Marked-content sequences tagged /OC were painted regardless of the
document's layer settings, so content in a group listed under the
default configuration's /OFF still rendered. Page content now honours
optional content:

- BDC keeps its property-list name, and Resources reads the optional
  content entries of /Properties.
- The canvas gets an OptionalContentFilter that hides painting and text
  recording inside sequences whose group or membership is off. It only
  exists when the document turns some group off, so other documents pay
  nothing for marked content.
- PdfRenderer and the web page renderer pass the visibility state, the
  web renderer sharing the annotation overlay's state.

The optional content model moves out of pdf-annotation-core into a new
pdf-optional-content crate, since rendering now depends on it. Its
decoders are all FromPdfObject impls. A single /OCG reference is
represented as a one-group /AnyOn membership, which collapses the
former AnnotationOptionalContent enum into OptionalContentMembership.
pdf-object-reader gains DictionaryContext::lenient_entries and
optional_list for mixed namespaces and single-or-array entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Conformance reads

Documents that Safe-PDF reads differently from the base commit, compared with PDFium. A read
regresses when Safe-PDF fails to open a document it opened before, crashes or times out, or
counts a different number of pages than PDFium.

pdf.js corpus

1008 documents compared with 6c723d8: 1 read worse, 0 read better.

Regressions

Document Base This pull request
issue19517 read, 1 page timeout: time limit exceeded (stage safe-read)

840 documents of the PDFium corpus read as on 6c723d8.

Details are in the job summaries of the workflow run. Rerun a case locally with
cargo conformance run --corpus <corpus> --read-only --case <id>.

@Velli20
Velli20 merged commit 4565f4c into main Oct 18, 2026
10 checks passed
@Velli20
Velli20 deleted the hide-optional-content-off-layers branch October 18, 2026 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant