BASALT · JOURNAL

How to see everything a PDF is actually carrying

2026-08-06 · what is inside a pdf file

A PDF viewer shows you the pages. The pages are one part of the file, and often not the part that causes trouble. If you want to know what is inside a PDF file, you have to look at the object graph underneath the rendering, because the format is designed to carry material that no page displays.

The file is a graph, not a stack of pages

At the end of every PDF is a trailer pointing to a cross-reference table, and that table points to the document catalog. The catalog is the root object, and everything else hangs off it by reference.

From the catalog you reach the page tree, where visible content lives. You also reach several siblings of the page tree that have nothing to do with rendering: the names dictionary, /OCProperties for optional content, /Outlines for bookmarks, /AcroForm for form fields, /Metadata for the XMP packet, and /OpenAction for anything the document runs when it opens.

A page-by-page review walks the page tree and nothing else. That is the structural reason careful reviewers still ship documents that disclose more than they meant to.

Metadata, in two stores that can disagree

The document information dictionary holds /Author, /Title, /Subject, /Keywords, /Creator, and /Producer. /Author is usually an account name, and /Title is frequently the original filename, which carries matter names and client names more often than people expect.

Separately, /Metadata on the catalog points to an XMP packet: an XML block with its own vocabulary, typically recording a document identifier, an instance identifier linking the file to every copy derived from it, and a history of the applications that touched it.

The two stores are independent, and extraction tools read both. Clearing one is a common half-measure, and blanking a field differs from removing it.

Embedded files

The names dictionary can contain an /EmbeddedFiles name tree, and a file attachment annotation can attach a file at a point on a page. Either way, the PDF carries a complete second file inside itself.

That file is commonly the spreadsheet a chart was built from, a source document, or an email. It is not paginated, so page review never encounters it, and it is not page text, so extracting the document body will not surface it. Nothing on any page has to indicate it is there. Embedded files, the attachment inside your PDF nobody looks at covers both mechanisms.

Optional content groups

Optional content groups are the layers feature. Membership is controlled by the configuration dictionaries under /OCProperties, and a group that is off draws nothing on screen and nothing on paper.

Off does not mean absent. The operators remain in the content stream and the text extracts normally. Files from templates, CAD exports, and design tools routinely carry layers holding draft wording, internal notes, or a superseded figure, and nothing in a reading session says the document has layers at all. Hidden layers in a PDF is the longer treatment.

OCR text, annotations, and accessibility strings

A scan made searchable carries a text layer drawn in rendering mode 3: real glyphs, invisible, aligned with the picture of the words. That layer is a second, independent copy of the page, so destroying pixels without editing glyphs leaves the words fully extractable, and the reverse leaves the picture legible.

Annotation objects carry a /Contents string beside their appearance stream, and form field values live in the field dictionary rather than in the widget you see, so a field can look blank and still hold its value. /ActualText and /Alt store plain text equivalents for accessibility, which any extractor reads.

JavaScript and actions

The names dictionary can hold a /JavaScript name tree of document level scripts, /OpenAction on the catalog can fire when the file opens, and annotations and form fields can carry their own action dictionaries.

Most of this is form validation and number formatting. Occasionally it is code written against an internal system, containing hostnames or logic that describes how a firm works. Either way, you should know whether a document you are sending executes anything.

Prior revisions and stale thumbnails

PDF supports incremental updates. An edit can append new objects and a fresh cross-reference section while leaving every original byte in place, so a file edited several times can hold several earlier states of itself, reconstructable by walking backward through the cross-reference chain. Counting %%EOF markers is a quick proxy, and how to check whether a PDF was really redacted explains what to do with the count.

Pages can also carry embedded thumbnails. One generated before an edit and never regenerated is a small picture of the earlier page, usually good enough to read a name.

Doing the inspection

The command line covers most of this if you run several tools. exiftool -a -u -g1 reports both metadata stores separately. pdfdetach -list enumerates embedded files. qpdf --qdf --object-streams=disable expands the file so the object graph is readable, which matters because object streams compress many objects together and a grep of a compressed PDF finds nothing while the strings are plainly present.

The gap is that none of these produce a single inventory, and a clean result from one says nothing about the others. What you want is one report covering the whole catalog, then a decision about what to remove, rather than a guess about what might be there.

Frequently asked questions

What is actually inside a PDF file besides the pages?

A PDF holds a document catalog pointing to the page tree plus several parallel structures: a document information dictionary and an XMP packet, a names dictionary that can carry embedded files and JavaScript, optional content group definitions, a bookmark outline with named destinations, form fields, annotations, and earlier revisions retained by incremental updates. None of that appears during a page-by-page read.

How can I see hidden content in a PDF on a Mac?

Expand the file with qpdf --qdf --object-streams=disable and read the object graph directly, then use exiftool -a -u -g1 for both metadata stores, pdfdetach -list for attachments, and a count of %%EOF markers for prior revisions. Each tool covers a different structure, so a clean result from one says nothing about the others.

Does a PDF contain earlier versions of the document?

It can. PDF's incremental update mechanism appends a new revision to the end of the file while leaving the original bytes in place, so a document edited several times may hold several earlier states of each page. Those states are reconstructable by walking backward through the cross-reference chain, which is why an edit saved as an increment does not remove what it appears to remove.

Can a PDF have files hidden inside it?

Yes. The names dictionary can hold an /EmbeddedFiles tree and file attachment annotations can attach a file to a page position, so a PDF can carry a complete spreadsheet, document, or email inside itself. Nothing on any page necessarily indicates the attachment exists, and text extraction of the document body will not reveal it.

Why does text extract from a PDF that looks blank?

Because visibility and presence are separate things in PDF. Text can be drawn in rendering mode 3, assigned to an optional content group that is switched off, covered by an opaque graphic drawn afterward, or stored in /ActualText for accessibility. In each case an extractor reads the characters normally, because nothing removed them from the content stream.

Does a PDF run code when I open it?

It can. The catalog's /OpenAction entry can trigger an action on open, and the names dictionary can hold a /JavaScript tree of document level scripts, with further actions attached to annotations and form fields. Most such scripts are ordinary form validation, but they are worth enumerating before you send a file.

Is inspecting a PDF the same as cleaning it?

No. Inspection tells you what the file carries and changes nothing. Cleaning is a separate set of operations, each targeting a different structure: stripping both metadata stores, deleting embedded files, resolving or removing optional content groups, flattening form values, and writing the result as a single generation so prior revisions cannot be recovered.

Should I upload a confidential PDF to a website to check what is in it?

No, because uploading it discloses the exact document whose disclosure risk you were assessing. The file, its metadata, and any attachments inside it land on infrastructure you do not control, subject to whatever retention that service applies. The risk of cloud PDF tools covers this. Inspection is local work and should stay local.

Doing it in Basalt

Basalt has an inspect tool that reports what a document carries beyond its pages: metadata and XMP, embedded files, hidden layers, annotations, OCR text layers, JavaScript, and saved generations, in one report rather than five command line tools. Its clean up tool then removes optional layers and embedded files and edits or removes bookmarks, writing a cleaned copy so the original is untouched.

Parsing happens in a sealed engine process with no network entitlement, enforced by macOS at the code signature level, verifiable on your own copy with codesign -d --entitlements :- /Applications/Basalt.app/Contents/XPCServices/BasaltEngine.xpc. No account, no telemetry, nothing leaves the Mac. It is $29 once for up to three Macs, with a 24 hour free trial.

Redaction that proves itself

Basalt destroys the content you mark, then re-opens the file it wrote and proves the content is gone before it saves anything. Your documents never leave your Mac.

DOWNLOAD BASALT 2.3.0 BUY $29 FREE FOR 24 HOURS · MACOS 13+