Skip to main content

PDF Diff

Compare two PDFs locally, three ways:

  • Visual: both PDFs are rendered with PDFium at the DPI you choose, pages are paired (by position when the page counts match, otherwise by content so inserted/removed pages line up), and pixels are compared. Changed areas are highlighted (red = only in A, green = only in B) with a per-page change score. A tolerance option ignores anti-aliasing and 1-pixel shifts; a threshold ignores small colour differences. Views: side by side, diff overlay, slider.
  • Text: words are extracted with their positions and diffed word by word in reading order; deleted and inserted words are highlighted on the page images. Reflowed lines and line-end hyphenation don't count as changes; ligatures are normalised.
  • Structure: page count, size, rotation and labels; document info and XMP; fonts (embedded or not); annotations; form fields and values; bookmarks; attachments (size + SHA-256).

Encrypted PDFs work if you enter the password. Exports: a diff report PDF (highlighted pages), a ZIP of PNGs of the changed pages, and a JSON summary. Runs offline.

Install (no administrator)

irm https://raw.githubusercontent.com/chayprabs/localtools/main/pdf-diff/install.ps1 | iex
curl -fsSL https://raw.githubusercontent.com/chayprabs/localtools/main/pdf-diff/install.sh | bash
# Or, with Python 3.12+ on any OS:
pipx install localtools-pdf-diff

Portable ZIPs: Windows, macOS and Linux releases.

Use

Run localtools-pdf-diff and the UI opens at http://127.0.0.1:47103. Choose PDF A (original) and PDF B (revised), pick a mode, click Compare, then open any page pair from the list.

localtools-pdf-diff a.pdf b.pdf                          # visual diff, summary on stdout
localtools-pdf-diff a.pdf b.pdf --mode text --json       # word diff as JSON
localtools-pdf-diff a.pdf b.pdf --report diff.pdf --png-zip pages.zip --dpi 150
localtools-pdf-diff a.pdf b.pdf --mode structure
localtools-pdf-diff locked.pdf b.pdf --password-a secret

Exit code: 0 = no differences, 1 = different, 2 = error (one line on stderr). Options: --mode visual|text|structure, --dpi 36-300 (default 100), --threshold 0-254 (default 24), --no-aa, --password-a, --password-b, --json, --report FILE.pdf, --png-zip FILE.zip. Exports are written to a temporary file and then renamed.

API: POST /api/diff (multipart: files a and b; fields mode, dpi, threshold, aa = 1/0, password_a, password_b) returns the JSON summary with a job id. GET /api/file?job=<id>&name=<file> serves that job's page images (a-N.png, b-N.png, d-N.png), report.pdf, pages.zip and summary.json; the last three finished jobs stay available while the tool runs.

curl -F a=@a.pdf -F b=@b.pdf -F mode=text http://127.0.0.1:47103/api/diff

Limits

64 MiB and 2,000 pages per PDF, 400,000 words per PDF, 300 megapixels rendered per PDF per comparison (100 DPI is about 0.94 MP per Letter page, so about 320 pages; the error tells you which DPI fits), pages larger than 40 megapixels are rendered at a lower DPI (noted in the result), at most 16 form parts per request, 120 seconds per comparison, two comparisons at a time (more get HTTP 503). Each comparison runs in a separate worker process that is killed on timeout, when the browser cancels, or when the tool exits, because PDFium can hang on malicious files. Workers get a 2 GiB memory limit on Windows (job object) and Linux (address space). macOS does not allow that limit, so there the pixel and page caps are the memory bound. Temporary folders are deleted after each job; leftovers from a crash are swept at the next start.

Engine

PDFium (the PDF engine in Chrome) through pypdfium2 4.30.0 (PDFium 6462) for opening, decryption, rendering, text with character boxes and structure, all under Apache-2.0/BSD licenses. It renders pages the way Chrome does and repairs broken cross-reference tables. Pillow does the pixel diff in C. Python's difflib does the word diff. The newer pypdfium2 5.x wheels need macOS 11/13 platform tags that the portable build does not target, and 4.30.1 was withdrawn because of a text-extraction regression, so 4.30.0 is pinned. Tests compare the word diffs against PyMuPDF as an independent reference (PyMuPDF is used only in tests, never shipped).

Not included

  • XFA-only forms (no AcroForm fallback): this PDFium build has no XFA, so those pages show PDFium's placeholder.
  • OCR: scanned (image-only) PDFs can be compared visually, but text mode finds no words in them.
  • XMP is read from the last uncompressed metadata packet. Compressed XMP streams are not decoded.
  • Page ranges: to compare part of a very large PDF, split it first.
  • Moved-text detection: text that moved shows up as a deletion plus an insertion.

Update / uninstall

localtools-pdf-diff update / localtools-pdf-diff uninstall. If you installed with pipx: pipx upgrade localtools-pdf-diff / pipx uninstall localtools-pdf-diff.

MIT license. Bundled components: see THIRD_PARTY_NOTICES.md.

Metadata

Release files for localtools-pdf-diff 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for localtools-pdf-diff 1.0.0
File Size Uploaded Provenance
localtools_pdf_diff-1.0.0.tar.gz 128.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for localtools-pdf-diff 1.0.0
File Interpreter ABI Platform
localtools_pdf_diff-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 261.6 kB

Release files / localtools_pdf_diff-1.0.0.tar.gz

Download URL localtools_pdf_diff-1.0.0.tar.gz
Size 128.9 kB
Tags Source
SHA-256 checksum
How to use checksums
88d1f170ae41b523f6991036d9e06b5c3f31895f14cd24f27cea48cb91487f72
BLAKE2b-256 checksum
How to use checksums
7554f1de32bc8015c71dbdcdcdac40e56190fe44b6d23de39aea92c186d06017
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / localtools_pdf_diff-1.0.0-py3-none-any.whl

Download URL localtools_pdf_diff-1.0.0-py3-none-any.whl
Size 132.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fd594875e79aa175640be963e6324da89a093792bb5233c1e7646ebd530d0a07
BLAKE2b-256 checksum
How to use checksums
ce1eb62258910f39b79754b7c0b9a577ab0fc863ff1dbf592486668c36368b2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Security for localtools-pdf-diff 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page