Skip to content

v0.4.2 — the same file drew a different picture on different runs

Choose a tag to compare

@tannevaled tannevaled released this 27 Aug 12:53
· 25 commits to main since this release
62002d5

The same file drew a different picture on different runs

An inline image dictionary may say the same thing twice and disagree with itself: /W 20 beside /Width 10, /CS /RGB beside /ColorSpace /3chanRGB, /D beside /Decode with the components reversed. Expanded() walked the dictionary in one pass, so when both spellings were present the winner was whichever Go's map iteration happened to yield last — and that order is deliberately randomised per range.

Eight renders of safedocs' Inline_Image_Abbreviations fixture, same binary, same file:

before 79b9edef 79b9edef b2594aef 6bf89747 6bf89747 544c8e6b 79b9edef 43030bf3
after 87f31291 × 8

Five different answers out of eight, against one.

The scope is measured, not guessed

Rendering the first page of 3 668 corpus files three times over — the 1 633 real forms, 1 400 arXiv papers and all 635 vendor fixtures — turns up exactly one file that disagrees with itself, and it is that fixture, which safedocs built for this case: its seven images each pair an abbreviation against the written-out name, with comments marking the line to remove "to see the effect".

So the corpus measurements already taken are not contaminated. One file is, and it is the one designed to be.

Which spelling wins

The specification permits both in an inline image and says nothing about precedence, and the public implementations disagree: pdf.js asks for the abbreviation and falls back to the written-out name; MuPDF asks the other way round.

The abbreviation wins here, for two reasons: it is the spelling Table 93 gives for an inline image, the written-out name being the tolerated alias; and safedocs marks the abbreviation as the line whose removal changes what a viewer shows, which is a statement about the reference viewer.

Two passes settle it — the written-out names first, then the abbreviations over them. No order within either pass can matter, because the names in each are distinct, so the answer no longer depends on the run whichever way it is read.

The test expands the same dictionary two hundred times and requires every answer to match the first. Against the parent commit it fails on the first comparison, reporting Height = 40 where the first gave 10.

100% statement coverage, go vet and -race clean, nine cross-compile targets, no dependencies outside the standard library.