BASALT · JOURNAL
Embedded files, the attachment inside your PDF nobody looks at
A PDF can carry a complete file inside it. Not a link to a file and not a picture of one: the actual bytes of a spreadsheet, a document, or an email, stored as a stream in the same container as the pages. Nothing on any page has to indicate it is there.
This is the disclosure route that survives careful review, because reviewing a document means reading its pages, and an embedded file is not on a page.
Two mechanisms, one result
The specification provides two ways to put a file inside a PDF, and they live in different places in the object graph.
The first is the /EmbeddedFiles name tree in the catalog's names dictionary. Each entry maps a name to a file specification dictionary pointing at an embedded file stream, usually with a /Params dictionary recording the original size and modification date. This is document level: the attachment belongs to the file as a whole and is tied to no page.
The second is the file attachment annotation, subtype /FileAttachment, which sits in a page's /Annots array at a specific position and renders as a small pushpin or paperclip icon, unless its appearance stream says otherwise.
Portfolios are a presentation of the first mechanism. A collection PDF is an ordinary document whose catalog carries a /Collection dictionary telling the viewer to show the embedded files as a browsable list. Remove /Collection and the attachments are still there; the viewer just stops advertising them.
Why this is worse than most hidden data
Most of what a PDF hides is fragmentary. A stale thumbnail is a low resolution picture of one page. An /ActualText entry is a phrase. A metadata field is a name.
An embedded file is the whole source. The classic case is a report where a chart was built in a spreadsheet and the workbook was attached, deliberately or by an export setting, so a reader could check the numbers. The chart shows an aggregate. The workbook inside the file shows every row, every hidden sheet, every formula, and often a tab nobody meant to circulate.
The same happens with email exports, where the original message and its full header trail rides along inside a summary, and with design proofs carrying their working file.
Text extraction of the document body will not find any of this. Neither will search, printing, or a rendering-based comparison of two versions. The attachment is not text on a page; it is a stream beside the page tree. What a PDF still carries after you think you cleaned it covers the wider inventory this sits inside.
Finding what a file is carrying
pdfdetach -list from the poppler tools enumerates embedded files and reports their names. It is the fastest single check and belongs in any routine for documents about to leave your control.
It is not complete on its own. A file attachment annotation with an unusually structured file specification can be missed. Expanding the document with qpdf --qdf --object-streams=disable and searching for /EmbeddedFile, /Filespec, and /FileAttachment shows the objects directly. The object stream flag matters: without it the strings are compressed, and a grep of the raw file returns nothing while the objects are plainly present.
Nested containers are the case people miss. An attachment can itself be a PDF carrying its own attachments, so enumerating one level is not enumerating the document.
Removing them properly
Deleting an attachment means removing the embedded file stream, the file specification dictionary referencing it, and the entry in the /EmbeddedFiles name tree or the page's /Annots array. Remove only the reference and the stream is orphaned but still in the file, trivially recovered by anything that walks objects rather than following references from the catalog.
Rewriting the document so unreferenced objects are dropped is what actually removes the bytes. That is a full rewrite, not an incremental save.
This is what undoes most attempted cleanups. Incremental updates append changes and leave the original bytes in place, so a removal saved as an increment can leave the attachment sitting in the previous revision of the same file. Counting %%EOF markers in the result tells you whether that happened, and how to check whether a PDF was really redacted walks through the check.
Where attachments come from without anyone deciding
People rarely attach a file on purpose and then forget. The ones that cause problems arrive automatically.
Export presets in office suites and design tools can embed the source document. Some archival workflows embed source files as policy. Email to PDF conversion frequently attaches the original message with its own attachments. Assembly tools carry forward the attachments of every input, so a bundle inherits everything any of its inputs was carrying.
That last one compounds. Combine forty exhibits into one production and you have combined forty sets of embedded files from forty sources, none of which appeared in the page review of any individual exhibit. The pre-filing checklist puts enumeration in sequence with the rest of the cleanup.
Frequently asked questions
How do I remove attachments from a PDF?
Removing an attachment requires deleting the embedded file stream, the file specification dictionary referencing it, and the entry in either the /EmbeddedFiles name tree or the page's annotation array, then rewriting the document so unreferenced objects are dropped. Removing only the reference orphans the stream and leaves the bytes recoverable. The rewrite must be a single generation, because an incremental save leaves the attachment in the previous revision.
How do I know if a PDF has files attached to it?
Run pdfdetach -list on the file, which enumerates document level embedded files and reports their names. For a fuller view, expand the document with qpdf --qdf --object-streams=disable and look for /EmbeddedFile, /Filespec, and /FileAttachment objects, because file attachment annotations live on pages rather than in the names tree. Object streams compress these strings, so a grep of the raw file proves nothing.
Can a PDF contain a spreadsheet?
Yes. A PDF can embed a file of any type, including the workbook a chart on the page was built from, and that workbook arrives intact with its formulas, hidden sheets, and every underlying row. This happens through deliberate attachment and through export presets that embed the source automatically. Nothing on the page necessarily indicates the workbook is inside the file.
Does flattening a PDF remove attachments?
No. Flattening merges annotation and form field appearances into the page content, which is an operation on what renders. Document level embedded files are not part of any page, so flattening leaves them untouched, and a file attachment annotation may lose its icon while its file specification and embedded stream remain. Attachment removal is a separate operation on a different structure.
Will printing a PDF to a new PDF get rid of attachments?
Generally yes for the attachments, because printing regenerates the pages and carries forward only what draws. It is a costly way to do it: you lose searchable text, bookmarks, tagging, and form structure across the whole document, and the new file typically picks up fresh metadata identifying your machine. Removing the attachments directly keeps the rest intact.
Do merged PDFs inherit attachments from the source files?
Usually, yes. Assembly tools carry forward document level embedded files and page annotations from each input, so a combined bundle holds the attachments of every document that went into it. This is how one production file ends up carrying source material from dozens of origins that were never reviewed together. Enumerate the bundle after merging, not just the inputs before.
Are portfolio PDFs different from attachments?
Not underneath. A portfolio is an ordinary PDF whose catalog carries a /Collection dictionary instructing the viewer to present its embedded files as a browsable list, but the files are stored exactly like any other document level attachment. Removing /Collection only stops the viewer advertising them; the embedded streams remain until they are deleted and the file is rewritten.
Can an attachment inside a PDF have its own attachments?
Yes. An embedded file can itself be a PDF carrying its own /EmbeddedFiles tree and file attachment annotations, so enumerating one level does not tell you what the document ultimately contains. Nested containers are common in email exports and in bundles assembled from other bundles, so any enumeration you trust has to descend into embedded PDFs.
Doing it in Basalt
Basalt reports embedded files as part of its inspect pass, alongside metadata, hidden layers, annotations, OCR layers, JavaScript, and saved generations, so attachments turn up in the same look that finds everything else. The clean up tool removes them and writes a cleaned copy, and every tool in the app writes a new file, so your original is never modified.
Parsing happens in a sealed engine process with no network entitlement, enforced by macOS at the code signature level, which you can check with codesign -d --entitlements :- /Applications/Basalt.app/Contents/XPCServices/BasaltEngine.xpc. Documents never leave the Mac. It is $29 once for up to three Macs, free for 24 hours.
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.