Skip to content

Render: a generated widget is clipped to its box (#222) - #225

Merged
timzifer merged 2 commits into
mainfrom
claude/issue-222
Oct 14, 2026
Merged

timzifer merged 2 commits into
mainfrom
claude/issue-222

Conversation

@timzifer

@timzifer timzifer commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Fixes #222.

Cause

An annotation's appearance stream is drawn with its /BBox mapped onto /Rect and clipped to it. The appearance cera generates for a widget (render with RenderOptions.Form) was not clipped, and flatLook gave the flattened form a box one point larger for the same reason. The border of a field lies on the edge of the box: with /Rect off the pixels the clip and the border's edge are both antialiased, and the edge pixels of the stream SetFields wrote came out lighter than those of the generated appearance (644 pixels, up to 88 levels in the issue's case).

Change

The generated appearance of a widget is clipped to the widget's box exactly (interp.annotation, ClipRect with the widget matrix), and flatLook writes the flattened form with /BBox [0 0 W H]. The render with a state, the saved file and the flattened page are now drawn alike at any scale; at a box off the pixels the outer edge of the border is lighter in all three, as for the appearance streams of documents.

Discarded first attempt (first commit)

The first commit rounded the clip of every annotation appearance outward to whole device pixels (as PDFium, MuPDF, Poppler and Ghostscript clip rectangles). The corpus API run then reported 41 files of pdf.js whose flattened first page differs from the render, by up to 4.9 % of the pixels: content flattened into a page cannot be clipped to the pixels of a render. The second commit replaces it; the diff of the PR is only the second approach plus the test.

Tests

  • overlayFile (formoverlay_test.go) held its fields to pixels because of this bug; three of its four fields are now off the pixels. TestFormOverlayOrder fails without the fix (up to 1471 pixels, by 88) and passes with it.
  • go test ./... green locally.
  • Corpus API run over pdf.js (3 pages per file): report identical to main.
  • Accuracy against PDFium at 150 dpi (full corpus): numbers identical to main.

The /BBox of an annotation's appearance stream is mapped onto its /Rect,
and the border of a field lies on the edge of that box. With the box off
the pixels, the exact clip and the border's edge, both antialiased,
lightened the edge pixels twice: a field Editor.SetFields wrote was drawn
with other edges than the same value shown from a FormState, which is not
clipped. The clip of an annotation's appearance is now rounded outward to
whole device pixels when it stays on the axes, as PDFium, MuPDF, Poppler
and Ghostscript clip rectangles. The accuracy report against PDFium is
unchanged.

overlayFile puts its fields off the pixels, which the test held them to.

Fixes #222.
The first fix of #222 rounded the clip of every annotation's appearance
outward to whole pixels. That made the render of an appearance stream
differ from the page it is flattened to, whose content clips exactly: the
corpus API run reported 41 files of pdf.js whose flattened first page
differed, by up to 4.9 % of its pixels.

The other way round: the appearance cera generates for a widget is
clipped to the widget's box exactly, as the stream Editor.SetFields
writes for it and the form Editor.Flatten draws are. The render with a
FormState, the saved file and the flattened page are drawn alike at any
scale; the border of a field off the pixels is lighter on its outer edge
in all three, as the appearance streams of documents are.

The corpus API report and the accuracy report against PDFium are the same
as on main.
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.

A field SetFields wrote is drawn with other edges than the value of a state

1 participant