All posts
Tutorial6 min read

Extract embedded files and attachments from a PDF

How to extract embedded files and attachments from a PDF with Acrobat, pdfdetach, qpdf, and Python. Works for Excel sheets, source data, and supporting documents.

By · Updated

To extract embedded files from a PDF, first check Acrobat's Attachments pane. For batch work, use pdfdetach -saveall from Poppler. The attachments are stored inside the PDF file itself, so copying visible text or printing to PDF will not recover them.

PDFs have a feature most readers hide: embedded file attachments. A research paper can ship with the dataset attached, an annual report with the source spreadsheet, an invoice with a packing slip. The attachments are in the file but invisible until you go looking.

Acrobat / Adobe Reader

Open the PDF, click the paperclip icon in the left rail (View → Show/Hide → Navigation Panes → Attachments if it's hidden). Each attachment shows its name and size. Right-click → Save Attachment.

Preview on macOS

Preview doesn't show embedded attachments. The file is still there — you just can't see it. Either open in Acrobat, or extract via the command-line tools below.

Command line: pdfdetach

Comes with poppler-utils (brew install poppler on Mac, apt install poppler-utils on Linux):

pdfdetach -list report.pdf # list attachments pdfdetach -saveall -o out/ report.pdf # extract every attachment to out/

This is the right tool for batch — process a folder of PDFs and dump every attachment in one command.

qpdf, when pdfdetach isn't installed

qpdf ships on most systems that already do PDF plumbing, and it handles attachments too:

qpdf --list-attachments report.pdf qpdf --show-attachment=dataset.csv report.pdf > dataset.csv

--show-attachment writes the raw bytes to stdout, so redirect to a file. The attachment keys come from --list-attachments. qpdf is also the fix for encrypted input: qpdf --decrypt locked.pdf unlocked.pdf, then extract from the unlocked copy.

Document-level vs page-level attachments

Attachments live in two different places. Document-level files sit in the PDF's EmbeddedFiles name tree and appear in Acrobat's Attachments pane — this is what pdfdetach and pikepdf extract. Page-level files are FileAttachment annotations pinned to a specific page, visible as paperclip icons on the page itself. If -list shows nothing but you can see a paperclip on the page, you have the annotation kind: extract those via the annotation API (pypdf's page["/Annots"], or Acrobat's comment tools).

Python: pypdf or pikepdf

pikepdf is the cleaner API for attachments:

import pikepdf with pikepdf.open("report.pdf") as pdf: for name, attachment in pdf.attachments.items(): with open(name, "wb") as f: f.write(attachment.read_bytes())

The same thing in pypdf — note that reader.attachments values are lists, because one name can map to multiple payload versions:

from pypdf import PdfReader reader = PdfReader("report.pdf") for name, payloads in reader.attachments.items(): with open(name, "wb") as f: f.write(payloads[0])

Batch: a folder of PDFs

for f in *.pdf; do out="attachments/${f%.pdf}" mkdir -p "$out" pdfdetach -saveall -o "$out" "$f" done

One subfolder per source PDF matters: attachments with the same filename in different PDFs overwrite each other if everything lands in one directory.

The reverse: adding an attachment

pdfattach (same poppler package) puts files in: pdfattach report.pdf dataset.csv with-data.pdf. Useful for round-trip testing — attach, extract, compare checksums — and for packaging supporting files when you generate PDFs programmatically.

Common problems and fixes

  • pdfdetach refuses an encrypted PDF — decrypt first with qpdf --decrypt, then extract from the decrypted copy.
  • The saved file won't open — check what it actually is with file attachment.bin, and trust the original filename from -list over content sniffing.
  • Nothing listed, but the file is far bigger than the visible pages warrant — look for page-level FileAttachment annotations or embedded images; neither shows up in -list.
  • You need this in a script, not a shell — use the pikepdf/pypdf route; parsing pdfdetach output is brittle compared to a real API.

Verify what you extracted

Before piping the output anywhere, sanity-check it: file out/* to confirm the types match what you expected, and compare wc -c sizes against the sizes pdfdetach -list reported to catch truncation. For a migration or audit, record sha256sum of each attachment — the checksum is your proof that the bytes match the source.

Don't confuse attachments with these

  • Embedded images — image objects rendered on the page, not separate files. See the post on extracting images from a PDF.
  • Embedded fonts — for rendering, not for extraction. Stripped or copied via specific font tools, not pdfdetach.
  • Form attachments inside PDF forms — sometimes accessible via the Attachments panel, sometimes only via the form's submit-data interface.

When the data you want is in the PDF body, not the attachment

If the PDF doesn't have attachments and the data you need is in the visible content (tables, forms, text), the attachment route is a dead end. ExtractFox's PDF data extractor handles the visible content — pair it with pdfdetach if you also need the bundled files.

Tool
Extract the data inside the PDF, not the files attached to it
Describe the tables or fields you need from the pages themselves and get them back as Excel, CSV, or JSON. It does not open embedded attachments — pdfdetach is still the tool for those.

Related reading

Stop reading, start extracting

Drop a PDF or image into ExtractFox and get structured data back in seconds.

Try a free extraction →