A PDF can forbid copying its text. `pdfcraft-cli text` honours that and refuses, naming the owner
password as the way through. pdftotext ignores the flag and extracts anyway, so the oracle was
comparing our refusal against its text and scoring 0.000 -- measuring obedience, not quality.
bug1815476.pdf is the case: a one-page Spanish form we render perfectly, whose text we decline to
extract and pdftotext hands over regardless. It sat at the bottom of the worst list looking like a
font bug, which is what sent me looking at it.
Those files are now skipped and counted, not scored, and the count is printed rather than passed
over in silence, on the same rule the oracle crate exists for: a comparison that did not happen
must not look like one that succeeded. Over the first 250 corpus files that moves the mean from
0.811 to 0.854 and drops one file from the sample, with the median unchanged at 0.991.
Permissions come from `Document::permissions`, so this is not a match on our own error text. A
file that cannot be opened at all counts as permitted, because that is a different problem and
dropping it quietly would hide it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every test that wanted an external oracle probed for it by hand and chose for itself what to do
when it was missing. The common choice -- print a line and return -- reports as a pass, so a suite
with no oracles installed looked exactly like one in which every oracle agreed.
That was not hypothetical. No CI runner installed qpdf, so `qpdf_agrees` had never executed on a
runner, while README.md said saved output was "independently checked with qpdf". For R2-R4 that
test is the only check of owner-password authentication (Algorithm 7) that does not go through our
own code.
`pdfcraft-oracle` holds the probes and one rule: a missing oracle is a declared skip or a failure,
never a silent pass. `PDFCRAFT_ORACLES_REQUIRED` picks which, on the same convention as
`CRAFT_FONTS_REQUIRED`. CI installs qpdf and sets it, so a skip notice there is now a red build.
The crate has no panicking path of its own; `or_skip!` expands its assert into the caller, which
is test code.
Verified by running the qpdf tests against qpdf 12.4.2 for the first time -- they pass, so the
README claim is now both enforced and true. Both gate directions were watched: absent and
unrequired skips with a notice, absent and required fails with the install command in the message.
Only qpdf is installed in CI, because it is the only tool the test job uses. The Poppler pixel
comparison in crates/organize stays ignored until a change turns it on and verifies it there.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adopt the shared Crafting Apps licensing standard. LICENSE-MIT names "ArtCraft Team and the
PrintCraft contributors"; LICENSE-APACHE matches the standard byte for byte; NOTICE gets the
standard header and brand paragraph above the existing third-party list. docs/brand/LICENSE-brand.txt
now holds the ArtCraft Team brand terms: the marks may be used only unmodified within PrintCraft,
and forks and modified versions must remove them. The brand entries in ATTRIBUTION.toml, AGENTS.md
§1.2 and the README follow suit, and the README's Licence section becomes the standard
"License and credits" block. xtask inherits the workspace licence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>