Skip to content

Settle the page before the shot, and render a mismatch again - #19

Merged
andiwand merged 1 commit into
mainfrom
fix/settle-the-page-before-the-shot
Sep 20, 2026
Merged

andiwand merged 1 commit into
mainfrom
fix/settle-the-page-before-the-shot

Conversation

@andiwand

Copy link
Copy Markdown
Member

An unchanged pair could compare as different from one run to the next. Two things allow that.

screenshot waited for readyState only

That does not cover a web font. The load event fires while the face is still arriving, and the text is laid out again once it lands, so the shot could catch the page mid-render. The poppler sleep the TODO apologised for was the symptom:

# TODO for pdf2htmlex the second screenshot sometimes fades in from white... not sure why, but a sleep solves it
if "poppler" in url:
    loaded_page_settling_time = 0.3

settle() now waits for document.fonts.ready and then for two requestAnimationFrame ticks, which say a paint has happened rather than merely been asked for. settling_time stays available for a caller that still wants a sleep, and a driver without async scripts falls back to it.

compare_html reported the first render it disliked

diff, (image_a, image_b) = html_render_diff(a, b, browser=browser)
result = diff.getbbox() is None

One pixel anywhere fails the file, on one render, with no second look. It now renders a mismatch again before reporting it — once by default, --retries N to change it, --retries 0 for the old behaviour.

This does not weaken the comparison. A real difference is in the markup and comes back every time; only one that does not reproduce is dropped. The matching files, which are almost all of them, are still rendered once, so the cost lands only where a mismatch was already going to be reported.

Why

Reported downstream: OpenDocument.core runs this over 420 pdf views, and the test (macos-26) job failed on five pull requests in a row, naming a different set of files each time:

PR changed reported different
907 pdf css style-various-1 page0, page1
908 sheet javascript 16 pages of 978-3-030-65771-0.pdf-single
909 document javascript
910 pdf css style-various-1 document, page0
911 viewport meta

908 and 909 changed only spreadsheet and document javascript. Neither can alter a pdf view by a single pixel. So the difference was in the rendering, not in the files — and a stable cause, a missing font say, would have named the same files every run.

Checked

pytest: 8 existing tests still pass, plus 5 new ones over compare_html's handling of a mismatch, driven through a stub renderer because what is under test is the control flow rather than how a page paints. Against main's compare_html four of the five fail.

What I could not do: reproduce the runner's non-determinism here. Eight shots of each named page are byte-identical in both firefox and chrome on an idle machine. So the retry is what carries the fix, and the settle is what removes the reason to need it.

Two things made an unchanged pair compare as different from one run to the
next.

`screenshot` waited for `readyState` only. That does not cover a web font: the
load event fires while the face is still arriving, and the text is laid out
again once it lands, so the shot could catch the page mid-render. It now waits
for `document.fonts.ready` and then for two frames, which say a paint has
happened rather than merely been asked for. The `poppler` sleep the TODO
apologised for is what this replaces; `settling_time` is there for a caller
that still wants one.

`compare_html` reported the first render it disliked. It now renders a
mismatch again before reporting it, once by default. A real difference is in
the markup and comes back every time, so nothing is hidden, and the matching
files - almost all of them - are rendered once as before. `--retries 0` asks
for the old behaviour.

Reported downstream: opendocument-app/OpenDocument.core ran this over 420 pdf
views on a macos-26 runner and the job failed on five pull requests in a row,
naming a different set of files each time. Three of those five changed only
spreadsheet or document javascript, which cannot alter a pdf view, so the
difference was in the rendering rather than in the files.

I could not reproduce the runner's non-determinism here - eight shots of each
named page are byte-identical in both firefox and chrome on an idle machine -
so the retry is what carries the fix, and the settle is what removes the
reason to need it.
@andiwand
andiwand merged commit 8b72c1d into main Sep 20, 2026
6 checks passed
@andiwand
andiwand deleted the fix/settle-the-page-before-the-shot branch September 20, 2026 10:57
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