A vendor sent contract v7 and swore only one date had changed since v6. Diffing the two files took me an afternoon the first time I tried it naively, because "compare two PDFs" sounds like one operation and is actually three, stacked — and each layer lies to you in its own way if you ask it the wrong question.
Layer one: the text diff (fast, and noisier than you expect)
Extract both texts and diff them:
pdftotext -layout v6.pdf - | head
pdftotext v7.pdf v7.txt && diff v6.txt v7.txt
You will find the changed date. You will also find forty other "changes": a ligature that extracted as one character in v6 and two in v7, a table whose reading order flipped between exports, hyphenation that moved when a font was substituted, page headers that appear mid-stream. PDF stores text as positioned glyphs, not as a reading order; two exports of the same visual document can legitimately extract differently. A text diff answers "did the extracted character stream change," which is a proxy for the question you actually have, and the proxy is noisy in proportion to how layout-heavy the document is.
Taming it: normalize before diffing (collapse whitespace, strip running headers/footers, sort nothing — order matters), and diff with -w -B as a first pass to see whether the signal survives the noise floor. If the diff is still all weather and no climate, the change you care about is not at this layer.
Layer two: the render diff (what a human would notice)
Render both PDFs to images at the same DPI and compare page by page:
pdftoppm -png -r 100 v7.pdf page
Pixel-diffing catches what text extraction cannot in principle see: the moved logo, the table that reflowed, the signature that appears in one and not the other. Its blind spot is the mirror image of layer one's — it will flag a document whose text changed invisibly (a corrected metadata date, a re-encoded but identical-looking page) as "no change," and it drowns in false positives if the two files were rendered by different engines at slightly different sizes. Align page counts first; if v7 has one more page than v6, every subsequent page "changed," and your diff needs page matching, not page pairing.
For one-off checks I use a PDF compare tool, which does the side-by-side render comparison without the shell pipeline; in CI, the pdftoppm route plus an image-diff threshold is perfectly serviceable.
Layer three: the structural diff (what actually changed in the file)
Sometimes neither text nor pixels changed and the file still did: fonts re-embedded, metadata rewritten, a form field's value set, an annotation added. qpdf --json or a pikepdf walk will show object-level differences. This layer matters when the stakes are evidentiary (did the signed file change?) rather than editorial.
Which layer is your question?
- "Did the wording change?" → text diff, normalized, noise tolerated.
- "Would a reader notice a difference?" → render diff.
- "Is this bit-for-bit the same document in disguise?" → structural diff.
The vendor's date had indeed changed — along with a payment term on page nine that the text diff surfaced only after normalization stripped the headers out of the way. Three layers, one afternoon the first time, ten minutes ever since. The trick was never the tools. It was knowing which question I was asking.
Top comments (0)