Skip to content

Paint the appearance of Ink annotations without strokes - #686

Merged
Velli20 merged 3 commits into
mainfrom
paint-stroke-less-ink-appearances
Oct 31, 2026
Merged

Velli20 merged 3 commits into
mainfrom
paint-stroke-less-ink-appearances

Conversation

@Velli20

@Velli20 Velli20 commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • Ink annotations that omit /InkList and keep their strokes only in /AP were rejected by Core validation ("ink requires nonempty strokes with at least two points each"). They were dropped with an import diagnostic and never drawn.
  • Ink without strokes is now adopted when it has a source rectangle. Its normal appearance is painted on the page canvas next to painted widget appearances, with /AS picking from a state dictionary. It can't be dragged, because the appearance stays at the source rectangle.
  • Strokes still need at least two points each, and ink with neither strokes nor a source layout is still rejected.
  • Annotation /Rect is now read with the Rect FromPdfObject decoder instead of a hand-mapped [f32; 4]. Every consumer normalizes the rectangle, so behaviour is unchanged.

Test plan

  • cargo test -p pdf-annotation-core -p pdf-renderer -p pdf-web -p pdf-document
  • cargo clippy --workspace --all-targets -- -D warnings, cargo fmt --check
  • Loaded a document with eight such inks on page 1 through AnnotationOverlay and render_annotation_appearances: no import diagnostics, eight appearances painted
  • Check visually in the web-canvas example

🤖 Generated with Claude Code

Velli20 and others added 3 commits October 31, 2026 21:32
Some producers omit /InkList and keep an Ink annotation's strokes only
in its appearance stream. Decoding already read a missing /InkList as
no strokes, but Core validation rejected ink without strokes, so the
annotation was dropped with an import diagnostic and never drawn.

Ink without strokes is now adopted when it has a source rectangle,
which gives its bounds. Its normal appearance is painted on the page
canvas, using /AS to pick from a state dictionary, alongside painted
widget appearances. It cannot be dragged, because its appearance stays
at the source rectangle. Strokes still need at least two points each,
and ink without strokes or a source layout is still rejected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The annotation /Rect was read as a four-number array and mapped to
edges by hand. It now uses the Rect FromPdfObject decoder, which keeps
the source values unnormalized, so inverted rectangles still survive.
The decoder stores the lower y edge as `top`. Every consumer normalizes
the rectangle first, and the field documentation now says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the appearance_paint conflict by keeping main's widget/stamp split
and adding stroke-less ink as its own painted case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014CdsKhWnipVvXi61jtdZCW
@Velli20
Velli20 merged commit 86c2d63 into main Oct 31, 2026
10 checks passed
@Velli20
Velli20 deleted the paint-stroke-less-ink-appearances branch October 31, 2026 21:21
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.

2 participants