PDF Security Blog

PDF Generator Fingerprints: What Software Made This File (And Where It Lies)

HTPBE Team··14 min read
PDF Generator Fingerprints: What Software Made This File (And Where It Lies)

This article is a snapshot — content was accurate as of August 2026 (code examples tested against the API as of June 2026). The product evolves actively; specific counts, examples, and detection rules may have changed since publication — see the changelog for the current state.

Two bank statements land in your underwriting queue. Both look like they came from the same bank. Both open cleanly. Both show the account holder you expect. One was generated by the bank’s statement engine. The other was rebuilt in a desktop editor, with the closing balance quietly raised by a few thousand. From the outside, they are indistinguishable. From the inside, they were made by entirely different software — and that software left its name behind.

Every PDF carries a fingerprint of the tool that produced it. Not a watermark you can see, but a set of structural habits: how the file lays out its objects, how it embeds fonts, what it writes into its own metadata, how it joins pages together. A risk team that learns to read these fingerprints gains a powerful, content-independent question to ask of any document: does the software that claims to have made this file actually behave like that software?

This article walks through the major server-side PDF generation libraries, explains what the Producer and Creator fields really tell you (and where they lie), and shows how a structural analysis reads all of it automatically.

The two fields everyone looks at first

Open any PDF’s properties and you will find two metadata fields that name software:

  • Creator — the application a human used to author the document. Microsoft Word, Adobe InDesign, LaTeX, a bank’s internal reporting tool.
  • Producer — the library that wrote the final PDF bytes. Adobe PDF Library, iText, ReportLab, the print-to-PDF subsystem of an operating system.

In a clean pipeline these tell a coherent story. A document authored in Word and saved to PDF reports Creator: Microsoft Word and Producer: Microsoft® Word. A LaTeX paper reports Creator: TeX and Producer: pdfTeX-1.40.26. The two fields together describe a real, plausible toolchain.

The problem: both fields are just strings inside the file. Nothing in the PDF format forces them to be true. Anyone with a hex editor or a one-line script can set Producer to whatever they like. A forger who knows their target bank uses a particular reporting engine can simply type that engine’s name into the field. So the naive check — “does the Producer string match what this institution uses?” — catches the lazy forger and misses the competent one.

That is exactly why the string is the beginning of the analysis, not the end. The far more interesting question is whether the rest of the file is consistent with the tool it names.

Why generators are distinguishable in the first place

A PDF is not a picture of a page. It is a small program: a tree of objects that describe fonts, text-positioning operators, images, and page structure, wrapped in a cross-reference table (the xref) that tells a reader where each object lives. Turning a page into that object tree involves a thousand small decisions, and every generation library makes those decisions differently and consistently.

Think of it like handwriting. Two people can both write the word “balance,” but the way they form their letters, space their words, and dot their i’s is habitual and hard to fake on purpose. PDF libraries are the same. The shape of the object tree, the way fonts are subset and embedded, the structure of the content stream, the defaults written into metadata — these are the library’s built-in habits. They are not settings the operator thinks about; they are byproducts of how that specific codebase was written.

So when the Producer field claims one library but the file is built with the structural habits of another, that contradiction is the signal. The claimed identity says “institutional reporting engine.” The construction says “desktop editor that re-saved this file.” They cannot both be true.

A field guide to the major generators

Here is how the most common server-side and desktop pipelines behave, and what their presence tells a fraud team. This is descriptive — it is the same knowledge any developer who has shipped PDF features already has — not a recipe for evasion.

iText (Java / .NET)

iText is a programmatic library: developers build the document object by object in code. It is heavily used inside enterprise systems — invoicing platforms, government portals, contract tools — precisely because it produces clean, predictable, single-pass files. A genuine iText document tends to be born in one operation, with no editing history layered on top. When you see iText behaving like iText, you are usually looking at machine-generated output from an institutional pipeline. The catch: iText is also freely available, so a forger can use it to build a fake document from scratch. That is a different problem — fabrication, not modification — and we are honest about it below.

Apache PDFBox (Java)

PDFBox both reads and writes PDFs, which makes it a common engine inside document-processing services and, unfortunately, inside re-save tools. A file that was loaded, altered, and written back out through PDFBox carries the structural signature of a rebuild — the original construction is gone, replaced by PDFBox’s own layout. When an institution’s documents normally come from a specific reporting engine and one arrives bearing the habits of a generic read-write library, that mismatch is worth a closer look.

ReportLab (Python)

ReportLab is the workhorse behind a huge share of Python-generated PDFs: invoices, reports, tickets, and — because it is so easy to script — a huge share of fabricated documents too. Like iText, a clean ReportLab file is single-pass and tidy. The forensic value is comparative: if you know the genuine issuer uses an enterprise reporting suite and the document in front of you is built the ReportLab way, the document did not come from where it claims.

Apache FOP

FOP renders XSL-FO into PDF and is common in formal publishing pipelines — regulated statements, structured financial documents, legal filings. Its output is distinctive and rigid. FOP is rarely what a casual forger reaches for, so its presence is more often a sign of a genuine formatting pipeline than of tampering — but, as always, the question is whether the rest of the file agrees.

wkhtmltopdf and Puppeteer / headless Chrome

These render HTML to PDF. They power an enormous range of legitimate systems — payroll portals, HR platforms, SaaS invoice generators — and an equally enormous range of forgery operations, because building a fake payslip as an HTML template and rendering it is easy. This is the single most important honesty point in PDF forensics: an HTML-rendered PDF is structurally identical whether the data inside it is real or fabricated. There is no byte-level signal that separates a genuine payroll PDF from a forged one when both came out of the same renderer. We return these as inconclusive for exactly this reason — more on that below.

LibreOffice and Microsoft Office

These are consumer software. They exist so that anyone can author any document. A bank statement that reports Creator: Microsoft Word is not evidence of tampering — but it is evidence that there is no meaningful “original” to verify against, because the whole document could have been typed from nothing. Real banks do not export your statement from Word. So an Office origin on a document that should have come from an institutional engine is itself a routing signal.

Ghostscript

Ghostscript distills and rewrites PDFs. It is a legitimate part of many print and archival pipelines, but it is also a classic laundering step: load a forged or modified file, run it through Ghostscript, and the output is a clean, single-xref file that no longer shows the incremental edits underneath. The rewrite itself leaves Ghostscript’s own habits in the file — which is why a re-distilled document claiming an institutional origin is a recognizable pattern rather than an invisible one.

Born institutional vs. rebuilt by a consumer editor

Step back from individual tools and the landscape splits cleanly in two.

An institutional document is born from an automated pipeline that does the same thing for every customer, every day. The bank’s statement engine, the payroll system, the government portal’s certificate generator. These produce files with a consistent, single-pass construction and a Producer that matches an enterprise toolchain. There is a genuine “original” — the file the institution actually emitted — and the question “was this modified after that institution made it?” has a real answer.

A consumer software document is one anyone can make: Word, an online editor, a desktop PDF tool. There is no canonical original because the whole thing could have been authored from scratch. Asking “was this modified?” is the wrong question — there was never an authoritative version to deviate from.

The fraud-relevant move is almost always a consumer tool impersonating an institutional one. The forger takes a real statement, opens it in a desktop editor, changes a number, and saves. Or builds a lookalike from a template and renders it. The output claims an institutional Producer, but it was built by consumer tools — and consumer tools have different habits than the engine they are pretending to be.

Want to see this on a real file right now? Drop any PDF into the free PDF check on the homepage and read back its origin classification — institutional, consumer software, online editor, or scanned — along with any modification markers. No signup needed for a single check.

What a structural analysis actually reads

This is where HTPBE? comes in. Instead of trusting the Producer string, the analysis cross-examines it against the file’s construction and against the file’s own internal records. Several independent checks contribute. In outcome terms, HTPBE? flags things like:

  • HTPBE_TOOL_VS_STRUCTURE_MISMATCH — the declared authoring software does not match how the file is actually built. The string says one thing; the bytes say another.
  • HTPBE_FONT_VS_TOOL_MISMATCH — font handling that is inconsistent with the tool the document claims to come from. Generators have characteristic ways of embedding type; a mismatch here is hard to fake.
  • HTPBE_PRODUCER_IDENTITY_FORGED — evidence that a declared institutional generator was spoofed by a third-party tool to disguise the file’s real origin.
  • HTPBE_RESIDUAL_PRIOR_GENERATOR — residual identity left behind by a tool that tried to claim a different origin. The file remembers who really made it, even when the visible field was overwritten.
  • HTPBE_EDITING_TOOL_FINGERPRINT — fingerprints of a known PDF editing tool on a document that should never have passed through one.
  • HTPBE_ONLINE_EDITOR_ORIGIN — the document carries the identity of an online editing or conversion service, which strips and rewrites original provenance.

Underneath these, the timestamp and revision-history layers add more: HTPBE_DATES_DISAGREE when the modification date postdates the declared creation date, HTPBE_MULTIPLE_REVISION_LAYERS when the file was saved repeatedly after it claims to have been freshly generated, and the signature checks HTPBE_POST_SIGNATURE_EDIT and HTPBE_SIGNATURE_REMOVED when a digital signature was edited around or stripped.

The important point for a risk team: you do not need to know what any of these mechanisms are internally. You read the named markers in the response and route on them. The detection logic stays on our side; your side stays a simple decision tree.

The verdict you get back

HTPBE? returns one of three statuses, and the third is the one most people misread.

  • intact — no modification detected and the origin looks institutional. The file behaves like what it claims to be.
  • modified — forensic evidence of post-creation change. One or more markers fired. This takes priority over everything else: a modified Word document is still modified.
  • inconclusive — the document came from consumer software, an online editor, an HTML renderer, or a scanner. This is not a failure and not a pass. It is a routing signal that says: PDF structure alone cannot answer the integrity question here, because this kind of file can be created from scratch by anyone.

That last one is the honest core of generator forensics. If you expected a bank statement straight from a banking system and you got back inconclusive with a consumer-software or HTML-renderer origin, the document was not generated the way a real statement is generated — so verify it directly with the issuer or reject it. The inconclusive verdict is doing real work: it tells you the tooling is wrong for the claim, even when no tampering marker fired.

There is no numeric risk score in the response. You get a status, a modification_confidence (certain, high, or none), and an ordered modification_markers array. That is deliberate — a named marker you can branch on is more useful than an opaque 0–100 number nobody can audit.

Reading the fingerprint over the API

Submitting a file is a two-step flow: post a URL, get a check ID, then fetch the result. Here is the whole thing in Python.

import os
import requests

API = "https://api.htpbe.tech/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['HTPBE_API_KEY']}"}

# Step 1 — submit the PDF URL for analysis
submit = requests.post(
    f"{API}/analyze",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={"url": "https://your-bucket.example.com/statements/applicant-42.pdf"},
)
check_id = submit.json()["id"]

# Step 2 — retrieve the verdict
result = requests.get(f"{API}/result/{check_id}", headers=HEADERS).json()

status = result["status"]            # "intact" | "modified" | "inconclusive"
markers = result["modification_markers"]
producer = result["producer"]
creator = result["creator"]
origin = result["origin"]["type"]    # institutional | consumer_software | online_editor | scanned | unknown

print(f"status={status} origin={origin} producer={producer!r} creator={creator!r}")
print("markers:", markers)

And the routing logic a fraud team actually cares about:

if status == "modified":
    # Generator identity contradicts construction, dates disagree,
    # signature stripped, etc. The named markers say which.
    if "HTPBE_PRODUCER_IDENTITY_FORGED" in markers:
        reason = "Declared institutional generator was spoofed"
    elif "HTPBE_TOOL_VS_STRUCTURE_MISMATCH" in markers:
        reason = "Claimed authoring tool does not match file construction"
    else:
        reason = markers[0]  # strongest marker first
    route_to_manual_review(check_id, reason)

elif status == "inconclusive":
    # Consumer/online-editor/HTML-renderer/scanned origin.
    # If you expected an institutional document, this is itself a red flag.
    if origin in ("consumer_software", "online_editor"):
        request_issuer_confirmation(check_id)
    else:
        flag_for_human_eyes(check_id)

else:  # intact
    accept(check_id)

Notice that the consumer-vs-institutional split drives the workflow. A modified verdict routes to review. An inconclusive verdict on a document that should have been institutional triggers an issuer callback rather than a silent pass. That is the generator fingerprint doing its job: not catching every forgery on its own, but telling you when the toolchain is wrong for the claim.

What this approach cannot do — and we will not pretend otherwise

Generator fingerprinting is structural. It reads how the file was built, not what the file says. That boundary matters:

  • It cannot validate the data. If a forger builds a fake payslip in an HTML renderer that real payroll systems also use, the structure is genuine even though the numbers are invented. That is why those files come back inconclusive, not intact. PDF forensics is the wrong layer to catch a born-fake document; content verification with the issuer is the right one.
  • It cannot read cryptographic signature validity. It detects whether a signature is present, was edited around, or was removed — not whether the certificate chains to a trusted authority. Pair it with a signature-validation library if you need that.
  • It cannot OCR or compare visual content. Two files that look identical but differ by a single pixel-painted digit are out of scope for structural analysis unless that edit also disturbed the file’s construction.

Being clear about these limits is the point. A tool that claims to catch everything catches your trust first and your fraud never. Generator fingerprinting catches a specific, common, and expensive class of fraud — consumer tools impersonating institutional pipelines — and is honest about the rest.

Who should be reading these signals

If you run risk or fraud operations at a lender, an insurer, an HR or payroll platform, or a legal-tech company, your queue is full of documents claiming institutional origins. A meaningful share of the fraudulent ones were not made by the institution they name — they were rebuilt in a consumer editor and re-stamped. The generator fingerprint is the cheapest, fastest way to surface that mismatch before a human ever opens the file. For the lending-specific version of this problem, see how this plays out in bank statement fraud in lending, or the dedicated fake bank statement detection workflow.

The structural layer reads every one of these signals automatically and returns a verdict you can branch on in a few lines of code. Wire it into the HTPBE? API, route on the named markers, and let the documents that claim to be institutional prove that the software which made them actually was.

Share This Article

Found this article helpful? Share it with others to spread knowledge about PDF security and fraud detection.

https://htpbe.tech/blog/pdf-generator-tool-fingerprints

Secure your workflow

Create your account — API key on signup, free test environment on every plan.
From $15/mo. No sales call. Cancel any time.