Limitations and guarantees
What you can rely on PDiffy for, and what it doesn't do.
Guarantees
- The same document gives the same snapshot on Windows and Linux. Every change is tested on both in CI.
- Encoding-only differences don't change a snapshot: object numbers, compression, object order, cross-reference
streams, incremental updates, font subset prefixes, zip order and relationship IDs. Equivalence tests write each
document both ways and compare the snapshots. A weekly check also rewrites every readable, unencrypted PDF/UA file in
the veraPDF corpus with qpdf (uncompressed, with object streams, and
linearized) and expects the same snapshot. The exception is an update saved to a signed file, which leaves the end
of the file unsigned and shows as
covers-file=noon its signatures. - A small meaningful change does change a snapshot: an edited word, removed alt text, a moved element, a changed color. Mutation tests change one thing at a time and expect the snapshot to change; see How detection is tested.
- A damaged part is marked, not dropped. A PDF page, attachment or outline that can't be read prints as
unreadable, as does a Word part. An unreadable PDF page or Word part also prints a hash of how the file stores it, so a change to it still fails the test, though the snapshot can't say what changed. In a Word document, content PDiffy doesn't read yet prints asunsupported <element> hash=..., whether it sits in a paragraph or between paragraphs, tables, rows and cells, so a change to it still fails the test. Formatting it doesn't read prints as a hash,other=, on the line it belongs to. Document settings it doesn't read aren't recorded. - A missing object reads as nothing. A reference to an object the PDF doesn't have, or can't find, reads as the
null object, as the PDF standard says, so what it pointed to is left out (a bookmark, say). The
documentline counts such objects (unreadable-objects=2), so one going missing still changes the snapshot. An image that can't be decoded is hashed as stored. A PDF too damaged to open at all throwsInvalidDataException. - A file built to overflow the stack or exhaust memory doesn't crash PDiffy's own reading. Structure elements and
bookmarks are read 256 levels deep; form fields, name and number trees and marked content are walked without
recursion; XML with a DTD or nested more than 256 deep is refused; a page whose image dictionaries hold references
that loop back on themselves, or chain more than 256 deep, is unreadable, since PdfPig would follow them until the
stack overflows, and so is a page with an image dictionary whose arrays name the same objects so often that PdfPig
would build more than 10,000 tokens reading it; and a Word part over 256 MB isn't loaded. What's past a limit is
marked, as damage is:
unread-children=N,unreadable. Tests build such files and check that they load. - A change to what a snapshot prints is announced. See Format changes: a new version number when nearly every snapshot changes, and a changelog entry either way.
What PDiffy doesn't do
- It isn't a PDF/UA or WCAG validator. It records what a document says and shows what changed. To check conformance, use veraPDF or PAC. The accessibility flags mark changes that can affect assistive technology; they don't certify a document.
- It compares content, not appearance. Layout records positions, sizes, fonts and colors, not pixels. It marks content that a clipping path, or something opaque drawn over it, hides entirely (
clipped=yes,covered=yes), but a font swapped for one with the same name and character widths that draws different shapes, or content hidden in other ways, doesn't change the snapshot. See the known gaps. - A re-encoded JPEG or Word image changes the snapshot, even when its pixels don't. PDiffy doesn't decode JPEG or JPEG 2000 images, or any image in a Word document, so it hashes them as stored. Re-encoding a JPEG almost always changes its pixels anyway, but any re-encoding that keeps the pixels, such as one that only strips metadata, still changes the hash.
- It can't read text that isn't text. Text drawn as vector paths or embedded in an image has no characters to extract; it shows only as a path or an image in the layout.
- Some limits are PdfPig's, the library PDiffy reads PDFs with. PDiffy refuses to decompress a Flate stream past 256
MB: a page whose content stream is refused comes back unreadable, an image is hashed as stored, and XMP prints
xmp=unreadable. The cap is per stream, so a page whose content is split across many large streams can still add up. Streams in other encodings are decoded whole, and a page tree nested many thousands deep can still overflow the stack while the file opens. Run untrusted PDFs in a process with a memory limit. - It reads what the file declares. When a font's character codes don't map to Unicode, the extracted text may be wrong; the
fontssection lists such codes asunmappedso you can see it. - Word documents are read from the file, not laid out. There are no page numbers or positions for DOCX, and fields such as PAGE record the result Word last computed, which
DocxDetail.FieldResultscan leave out.
Versions
PDiffy follows semantic versioning:
- The public API doesn't change incompatibly within 1.x. A type or member can be added in a minor release, but not removed or renamed until 2.0.
- What a snapshot prints changes only in a minor release, never a patch release, and every change is listed in Format changes. A newer PdfPig can change what a snapshot prints, so PDiffy moves to one only in a minor release and lists the upgrade there too. A test project that gets a newer PdfPig through another package can still see such changes without upgrading PDiffy.
- A pinned rule set, such as
PdfCheckSet.Version1_0orDocxCheckSet.Version1_0, keeps the rules its release shipped.PdfCheckSet.LatestandDocxCheckSet.Latestgain new rules in minor releases. A fix to how a rule reads a document applies in every set, so it can change what a pinned set finds; such a fix is listed in Format changes.
Compatibility
- .NET: .NET 10.
- Operating systems: Windows and Linux, tested in CI. PDiffy is fully managed, with no native binaries.
- Formats: PDF (any version PdfPig reads, tagged or not) and DOCX (Word 2007 and later). A PDF 2.0 file's structure namespaces, namespace role maps and associated files are recorded, but PDiffy's built-in checks follow PDF/UA-1, not PDF/UA-2.
- Snapshot formats:
pdfua-snapshot v1anddocx-snapshot v1. - Test frameworks: any. A failure is a
SnapshotException.