Compare two PDFs to verify nothing changed

When you hold the original, verification stops being guesswork. What each comparison tier proves, how to run it by hand, and the API version.

Every guide to spotting an edited PDF — including ours — ends with the same admission: the most reliable check is comparing the file against the original. Banks hold the statements they issued. Employers hold the payslips. Lenders keep the copy from the first application. Yet almost everyone does the comparison by eyeballing two open windows. This page is about doing it properly: what byte-level comparison can prove, in decreasing order of strength, and where it honestly runs out.

Direction matters. Everything below compares a candidate — the file being verified — against an original you already trust: one you generated, received first-hand, or hold on record. Comparing two files you trust equally answers a different, weaker question.

Tier 1: hash them — the only proof

Two commands, one answer

$ shasum -a 256 statement-on-record.pdf statement-resubmitted.pdf
8f2c…a91b  statement-on-record.pdf
8f2c…a91b  statement-resubmitted.pdf

Equal hashes end the investigation: every byte matches, so content, metadata and structure are all unchanged. This is the only comparison result in this entire field that is a proof rather than a signal, which is exactly why it is worth building workflows where you keep originals. Unequal hashes, though, prove almost nothing by themselves — a re-download through a different pipeline, a re-save, an added signature all change bytes without touching a single visible character. The next two tiers are about reading what kind of difference it is.

Tier 2: the original inside the candidate

PDF has a property no other common document format shares: edits made in place are appended. An incremental update adds new objects, a new cross-reference section and a new %%EOF to the end of the file — and leaves every original byte exactly where it was. Which means a file edited in place literally contains the document it was edited from, verbatim, as a prefix ending at a revision boundary.

Is the original a byte prefix of the candidate?

$ cmp -n "$(stat -f%z statement-on-record.pdf)" \
      statement-on-record.pdf statement-resubmitted.pdf \
  && echo "candidate = original + appended bytes"

When this holds — and the join lands on a %%EOF the candidate's own revision chain recognises — you know something remarkably strong: the candidate is your original, plus a recorded edit. Not "similar to". Not "probably derived from". The unmodified original is sitting inside the file being questioned, and the appended bytes are the complete, inspectable edit. The reverse direction is just as informative: a candidate that is a prefix of your original is an earlier revision — someone submitted the document as it looked before a later change, such as before it was signed.

What the appended bytes touched

"Appended revision" is not yet a finding — signing a document appends a revision, and so does filling a form field. The question that decides severity is which objects the appended revision overwrites. An update that adds a signature object touches nothing that was visible. An update that re-writes an object number which already existed in your original — and that object carries page, content-stream or image data — changed what the document shows after you issued it. That distinction is readable from the cross-reference sections, mechanically: this is the strongest form of the incremental-updates signal, because with the original in hand there is no ambiguity about what the earlier revision looked like.

Tier 3: when both files were rewritten

Ancestry breaks the moment either file is re-saved whole — a print-to-PDF, an export, most online tools. The bytes then share no prefix, and the honest comparison degrades to parsed structure, field by field:

  • Metadata, both stores. Info dictionary and XMP, compared field by field — producer, creator, title, both date pairs. See PDF metadata forensics for what each divergence means.
  • The /ID pair. The first entry is assigned at creation and required to survive every save; two files whose permanent IDs match claim descent from the same original. Self-declared, so supporting evidence — never proof.
  • Page count and page geometry. Re-exports preserve how many pages there are and their physical size; a changed MediaBox on page 2 is a fact demanding an explanation.
  • The font sets. A font present in one file and absent in the other means text was re-set by a different tool — or new text arrived in a new face, the same mechanism as the font-anomalies signal.

Every one of these is a fact about difference, not a verdict about intent. Multi-stage document pipelines produce structural divergence legitimately and constantly.

What no byte comparison can prove

That two rewritten files look identical. Proving rendered appearance requires actually rendering both documents and comparing pixels — which means running a full PDF renderer over untrusted input, with the false positives of anti-aliasing and font substitution on top. Acrobat's Compare Files feature lives in that tier, and for a visual side-by-side of two specific documents it is the right tool. A byte-level comparison stops one honest step earlier: it tells you whether and where the files differ structurally, and says plainly that structural cleanliness is not visual identity.

Automating it: one API call

Tamperlens runs all three tiers as a single request: POST /api/v1/compare with the file on record and the file being verified. The report leads with the relationship — identical, candidate-extends-original, candidate-truncates-original or rewritten — then the appended-revision classification, the metadata diff and the structural diff, each as evidence-carrying findings.

Verify a resubmitted statement against the one on record

curl -s https://tamperlens.com/api/v1/compare \
  -H "Authorization: Bearer $TAMPERLENS_KEY" \
  -F [email protected] \
  -F [email protected]

Both files are parsed in memory and never stored; a comparison is metered as two documents against the monthly quota. Details, the full report schema and error codes are in the API quickstart.

Hold originals? Verify against them.

An API key with 25 documents a month — twelve comparisons — is free: create an account or start with the API quickstart. No card required.

Tamperlens reports risk signals and byte-level facts, not authenticity verdicts. Only byte identity is a proof; everything else needs your judgment.

Related reading