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 1-pixel shifts and most anti-aliasing noise; 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, 100 megapixels rendered per PDF per comparison (100 DPI is about 0.94 MP per Letter page, so about 105 pages; the error tells you which DPI fits). That limit is measured: 103 A4 pages that all differ, at 100 DPI with the report and PNG exports, take about 80 s, inside the deadline, pages larger than 40 megapixels are rendered at a lower DPI (noted in the result), pages with an unusable size (under 1 pt or over 200,000 pt) are refused, 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 5.14.0 (PDFium 8076, Chrome 156) 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. When page counts differ, unchanged pages anchor the pairing, so only the edited stretches are aligned by similarity. Each thumbnail used for scanned pages is rendered once. 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.1

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.1
File Size Uploaded Provenance
localtools_pdf_diff-1.0.1.tar.gz 140.9 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 286.0 kB

Release files / localtools_pdf_diff-1.0.1.tar.gz

Download URL localtools_pdf_diff-1.0.1.tar.gz
Size 140.9 kB
Tags Source
SHA-256 checksum
How to use checksums
32007a8c76a3c76be2de86c4778de4dc82108a0cb246608a8aa44fa57b6c3150
BLAKE2b-256 checksum
How to use checksums
d74396aac84d0c1357767ab6ee4ba573216c8adfcb245adeb09502e4b6a2dae3
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.1-py3-none-any.whl

Download URL localtools_pdf_diff-1.0.1-py3-none-any.whl
Size 145.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
11d834d768b28268b7ca9d276a0a6193b38968a7e819be9657e29e1b5aa4ed49
BLAKE2b-256 checksum
How to use checksums
75bb697d45568787ffa87e018a9279b77e3b2b8871e67b2bbed8ca75388bcd31
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.1

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