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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| localtools_pdf_diff-1.0.0.tar.gz | 128.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.