Concept · Embedded files
Embedded Files in PDF:
PDF/A-3 Attachments
A PDF can carry other files sealed inside its binary structure: an XML invoice,
a source spreadsheet, a dataset, a machine-readable twin of the visible page.
Each attachment is listed in the catalog, typed with a MIME subtype, and
linked to the document with an AFRelationship that says exactly
how the two relate. PDF/A-3 is the archival level that makes this practical:
it is the only PDF/A variant that allows arbitrary file types as attachments.
The analogy: a binder with appendices physically bound in
In a physical binder, appendices are bound into the back cover. They cannot fall out, be misplaced, or arrive separately. When you hand someone the binder, the appendices come with it. A PDF with embedded files works the same way: the attachment is part of the binary file, not a loose document attached in an email or referenced by a URL that might break.
If you send the PDF, you send its attachments. If you archive it, you archive everything inside. The reader does not need to track down a separate XML file or remember which spreadsheet corresponds to which report: it is all in one place, one file, permanently associated.
AFRelationship: how an attachment relates to the document
Every /Filespec dictionary in a PDF can carry an
/AFRelationship key that declares the semantic role of the
attachment. This lets software and validators understand not just that a file
is embedded, but what it is for.
| Relationship | What it declares | Typical use |
|---|---|---|
| Source | The attached file is the source material from which this document was derived. | The original spreadsheet or data file that produced a generated report. |
| Data | The attached file is a dataset associated with the document. | Raw measurement data behind a scientific paper or analysis. |
| Alternative | The attached file is a machine-readable alternative of the entire document. | The XML invoice embedded in a ZUGFeRD / Factur-X e-invoice. See ZUGFeRD. |
| Supplement | The attached file is supplementary material that extends or accompanies the document. | An appendix, reference table, or supporting document bound into a report. |
| Unspecified | No specific relationship is declared. | General-purpose attachments where the relationship is not categorized. |
rust-pdf exposes these as the AFRelationship enum:
SOURCE, DATA, ALTERNATIVE,
SUPPLEMENT, UNSPECIFIED in Python
(camel-case in C#, Go uses Rel prefix, Node matches C#).
Why PDF/A-3? The only archival level for arbitrary attachments
PDF/A is the ISO archival family. Its levels differ in what they permit inside the file, and the difference that matters for attachments is significant.
PDF/A-1 and PDF/A-2
Embedded files are allowed only if they are themselves PDF/A-compliant. You cannot attach an XML invoice, a CSV dataset, or a spreadsheet: the format mismatch makes the outer document non-conformant.
PDF/A-3
Any file type can be attached. The outer document still meets full archival conformance. This is what makes ZUGFeRD / Factur-X, scientific data packages, and source-bundled reports possible in a single, verifiable file.
The rest of the PDF/A-3 requirements (embedded fonts, color profiles, XMP metadata, no JavaScript, no encryption) remain exactly the same as PDF/A-2. The only change is the attachment rule. See What is PDF/A? for a full comparison of the levels.
Where embedded files are used
Any situation where a document needs to travel with its source data, its machine-readable twin, or its supporting material in a single self-contained file.
E-invoices (ZUGFeRD / Factur-X)
The invoice XML is embedded as factur-x.xml with AFRelationship.Alternative. The same PDF is human-readable and machine-importable. Learn more.
Source spreadsheets behind reports
Attach the .xlsx or .csv that generated a financial or business report as AFRelationship.Source. Readers get the raw numbers, not just the rendered totals.
Datasets behind scientific papers
Embed the raw measurement data as AFRelationship.Data alongside the paper PDF. Reviewers and replicators access the data directly from the published document.
Machine-readable twin of a human document
Any regulatory filing, contract summary, or structured form can carry its XML or JSON twin inside the PDF so automated systems read it without OCR or re-keying.
Long-term archival records
A PDF/A-3 archive is self-contained by design. Attach the source files, metadata exports, or reference documents that must remain accessible alongside the primary record for decades.
Supplementary material
Legal exhibits, technical appendices, reference tables, or translation glossaries attached as AFRelationship.Supplement travel with the primary document, never separately.
How to embed a file in a PDF with rust-pdf
One call embeds a file as a typed attachment with its relationship, on a PDF/A-3 base.
# pip install rustpdf
import rustpdf
data = open("source.xlsx", "rb").read()
with rustpdf.Document() as doc:
doc.pdfa(rustpdf.PdfaLevel.A3B) # A-3 allows arbitrary attachments
f = doc.add_font_file("Roboto-Regular.ttf")
doc.add_page()
doc.show_text(f, 22, 72, 760, "Quarterly report")
doc.attach_file(
"data.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
data, rustpdf.AFRelationship.SUPPLEMENT, "Source spreadsheet")
doc.save("report_with_data.pdf") # PDF/A-3b, veraPDF-valid
// dotnet add package RustPdf
using RustPdf;
byte[] data = File.ReadAllBytes("source.xlsx");
using var doc = new Document();
doc.Pdfa(PdfaLevel.A3b);
int f = doc.AddFontFile("Roboto-Regular.ttf");
doc.AddPage();
doc.ShowText(f, 22, 72, 760, "Quarterly report");
doc.AttachFile("data.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
data, AFRelationship.Supplement, "Source spreadsheet");
byte[] bytes = doc.ToBytes();
// go get github.com/rustpdf/rustpdf-go@latest
data, _ := os.ReadFile("source.xlsx")
doc, _ := rustpdf.New()
defer doc.Close()
doc.PdfaLevel(rustpdf.A3b)
f, _ := doc.AddFontFile("Roboto-Regular.ttf")
doc.AddPage()
doc.ShowText(f, 22, 72, 760, "Quarterly report", 0)
doc.AttachFile("data.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
data, rustpdf.RelSupplement, "Source spreadsheet")
out, _ := doc.ToBytes()
// npm install rustpdf
const { Document, PdfaLevel, AFRelationship } = require("rustpdf");
const fs = require("fs");
const data = fs.readFileSync("source.xlsx");
const doc = new Document();
doc.pdfa(PdfaLevel.A3b);
const f = doc.addFontFile("Roboto-Regular.ttf");
doc.addPage();
doc.showText(f, 22, 72, 760, "Quarterly report");
doc.attachFile("data.xlsx",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
data, AFRelationship.Supplement, "Source spreadsheet");
const bytes = doc.toBytes();
rust-pdf emits the /EmbeddedFile stream with the correct MIME
/Subtype and /Params (size, modification date),
wraps it in a /Filespec with your /AFRelationship,
and registers it in both /Names /EmbeddedFiles and
/AF in the catalog. Full details in the documentation.
Available in Python, C#, Go, Node.js, Java, PHP, Ruby, Delphi, and Swift.
Embedded files in PDF: FAQ
Can a PDF contain other files?
Yes. A PDF can carry other files sealed inside its binary structure. Each attachment is an EmbeddedFile stream with a MIME subtype, size, and modification date, wrapped in a Filespec dictionary and listed in the catalog under /Names /EmbeddedFiles and /AF. The embedded files travel with the PDF: if you send or archive the document, everything inside it goes too.
What is AFRelationship?
AFRelationship is a key on a PDF Filespec dictionary that declares how the embedded file relates to the visible document. The defined values are Source (the attachment is the source material from which this document was derived), Data (a dataset associated with the document), Alternative (a machine-readable alternative of the whole document, used in e-invoices), Supplement (supplementary material), and Unspecified (no specific relationship declared).
Why does this need PDF/A-3?
PDF/A-1 and PDF/A-2 restrict embedded files: they only allow embedding other PDF/A-compliant files. PDF/A-3 explicitly lifts that restriction, permitting any file type as an attachment while the outer document still meets archival requirements. So a PDF/A-3 can legally carry an XML invoice, a CSV dataset, a spreadsheet, or any other format.
What is the difference between PDF/A-2 and A-3 for attachments?
PDF/A-2 only allows embedding files that are themselves PDF/A-compliant. PDF/A-3 removes that constraint: any file type can be attached. The rest of the conformance requirements are the same between the two levels. This single change is what makes PDF/A-3 the right base for ZUGFeRD / Factur-X invoices, scientific data packages, and other use cases that need to bundle non-PDF source files.
How do I embed a file in code?
With rust-pdf, call doc.attach_file(name, mime, data, relationship, description) on a Document that has pdfa(PdfaLevel.A3b) set. The library emits the EmbeddedFile stream, wraps it in a Filespec with the correct AFRelationship, and registers it in /Names/EmbeddedFiles and /AF. The output is veraPDF-validated PDF/A-3b or 3a. See the code examples above.
Embed files in PDF in your language
One Rust core, nine language bindings. Prototype for free, license the corporate features when you ship.