# Attachments

> Embed and extract files in PDF and PDF/A-3 with EmbeddedFile: MIME types and relationships, e-invoice XML, Attachments.GetFromFile and attachments on re-save.

- **Product:** TX Text Control .NET Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** TX Text Control .NET Server > PDF Processing > Attachments
- **5083 words**
- **Web URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/attachments/
- **LLMs URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/attachments/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/attachments/llms-full.txt

---

A PDF file can carry other files inside it: the XML data of an electronic invoice next to its printable pages, the JSON data a report was merged from or a spreadsheet with the details of a contract. TX Text Control embeds such attachments when it saves a document as PDF or PDF/A and reads them from the PDF documents you receive. You attach files with a MIME type and a relationship, create PDF/A-3 invoices with embedded XML, extract attachments from uploads and control which attachments a PDF keeps when you save it again.

The basic loop that reads the attachments of a loaded PDF is part of [Load and Modify a PDF](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/pdf/load-modify/#read-metadata-and-attachments) in Getting Started. Attaching files from JavaScript in the Document Editor is described in [Document Properties](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-editor/properties/#embed-files-in-pdf-documents).

### How Attachments Work

An attachment is an [EmbeddedFile](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/llms-full.txt) object: a file name, the file's data and optional properties. Embedded files are supported for PDF and PDF/A only, so they're written when a document is saved with `StreamType.AdobePDF` or `StreamType.AdobePDFA` and ignored in other formats.

| Property | Contains |
|---|---|
| [FileName](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/filename-property/llms-full.txt) | The name of the file, which can include a relative or absolute path; read only |
| [Data](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/data-property/llms-full.txt) | The content of the file, a string or a byte array; read only |
| [MIMEType](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/mimetype-property/llms-full.txt) | The MIME type, such as `application/xml`; without one, `application/octet-stream` is used |
| [Relationship](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/relationship-property/llms-full.txt) | How the file relates to the document; written to PDF/A only |
| [Description](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/description-property/llms-full.txt) | A description of the file |
| [CreationDate](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/creationdate-property/llms-full.txt), [LastModificationDate](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/lastmodificationdate-property/llms-full.txt) | The dates of the file; when they're `null`, the dates of the PDF document are used |

The [EmbeddedFile constructor](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/embeddedfile/constructor/llms-full.txt) takes the file name, the data as a byte array or a string and XMP metadata, which can be `null`. The metadata consists of one or more `rdf:Description` elements that are added to the metadata of the PDF document, as electronic invoices require. A third constructor takes a `SafeFileHandle` instead of the data and reads the file when the document is saved.

Three places handle attachments:

- **[SaveSettings.EmbeddedFiles](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/loadsavesettingsbase/embeddedfiles-property/llms-full.txt)** sets the files that `Save` embeds.
- **[LoadSettings.EmbeddedFiles](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/loadsavesettingsbase/embeddedfiles-property/llms-full.txt)** returns the files of a loaded PDF, or `null` if it has none. They're loaded when [PDFImportSettings](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/pdfimportsettings/enumeration/llms-full.txt) contains `LoadEmbeddedFiles`, which is part of the default.
- **[DocumentSettings.EmbeddedFiles](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/documentsettings/embeddedfiles-property/llms-full.txt)** holds the files of the current document. It's filled when a document is loaded, and its files are embedded automatically when the document is saved, unless `SaveSettings.EmbeddedFiles` is set.

### Choose the Relationship and MIME Type

PDF/A-3 requires a MIME type and a relationship for every embedded file, and validators such as veraPDF mark a document without proper MIME typing as non-compliant. `Relationship` is a string with one of these predefined values:

| Relationship | Use it for |
|---|---|
| `Source` | The original document the PDF was created from, such as a DOCX file |
| `Data` | Machine-readable data, such as XML, JSON or CSV |
| `Alternative` | Another representation of the document's content; the value used for the XML of ZUGFeRD, Factur-X and XRechnung invoices |
| `Supplement` | Additional information, such as appendices |
| `Unspecified` | Anything else |

Common MIME types are `application/xml` for XML, `application/json` for JSON, `text/csv` for CSV and `image/png` or `image/jpeg` for images. The blog article [Why Defining MIME Types for PDF/A Attachments Is Essential](https://www.textcontrol.com/blog/2025/12/10/why-defining-mime-types-for-pdfa-attachments-is-essential/) explains the requirements in detail.

### Attach a File to a PDF

The following program loads an invoice document, attaches the JSON data the invoice was created from and saves the result as PDF/A:

```csharp
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    // PDF/A requires fonts that can be embedded
    tx.FontSettings.EmbeddableFontsOnly = true;

    tx.Load("Documents/invoice.docx", StreamType.WordprocessingML);

    EmbeddedFile data = new EmbeddedFile("data.json", File.ReadAllBytes("Documents/data.json"), null)
    {
        Description = "Invoice data",
        MIMEType = "application/json",
        Relationship = "Data",
        CreationDate = DateTime.Now
    };

    SaveSettings saveSettings = new SaveSettings()
    {
        EmbeddedFiles = new EmbeddedFile[] { data }
    };

    tx.Save("invoice.pdf", StreamType.AdobePDFA, saveSettings);
}
```

`EmbeddedFiles` takes an array, so one `Save` call embeds any number of files. PDF/A-3 allows files of any type, and PDF readers such as Adobe Acrobat Reader list them in their attachments panel. Instead of `SaveSettings`, you can also assign the array to `tx.DocumentSettings.EmbeddedFiles` before saving. [FontSettings.EmbeddableFontsOnly](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol/fontsettings/embeddablefontsonly-property/llms-full.txt) has to be `true` before a document is saved as PDF/A, as [PDF/A](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/pdf-a/#the-rules-of-pdf-a) explains.

Adobe Acrobat lists the embedded files of a PDF in its **Attachments** panel. The following panel shows the embedded XML of an electronic invoice, created as in the next section, with its description and relationship:

![Attachments panel of Adobe Acrobat listing factur-x.xml with the description factur-x, its modification date, size and the relationship Alternative](https://docs.textcontrol.com/assets/img/tx-text-control-dotnet-server/acrobat-attachments-facturx.4783f0bc.webp)

### Create an Electronic Invoice

ZUGFeRD and its French counterpart Factur-X are hybrid invoices: a PDF/A-3 document that people read, with the invoice as XML attached for software to process. Both follow the European standard EN 16931. XRechnung invoices consist of XML only, and since ZUGFeRD 2.1, XRechnung XML can be embedded in a PDF the same way. Since January 1, 2025, domestic B2B invoices in Germany must use such a structured format.

TX Text Control creates the readable invoice and embeds the XML. The XML itself comes from your ERP system or a library such as the open-source ZUGFeRD-csharp. The following program merges the invoice data into a template, embeds *factur-x.xml* with the XMP metadata that declares the standard and saves a PDF/A-3 document:

```csharp
using System.Text;
using TXTextControl;
using TXTextControl.DocumentServer;

LoadSettings loadSettings = new LoadSettings()
{
    ApplicationFieldFormat = ApplicationFieldFormat.MSWord,
    LoadSubTextParts = true
};

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    // PDF/A requires fonts that can be embedded
    tx.FontSettings.EmbeddableFontsOnly = true;

    tx.Load("Documents/template.docx", StreamType.WordprocessingML, loadSettings);

    // The readable invoice
    using (MailMerge mailMerge = new MailMerge { TextComponent = tx })
    {
        mailMerge.MergeJsonData(File.ReadAllText("Documents/data.json"));
    }

    // The XML invoice, created by your ERP system or an e-invoice library
    byte[] invoiceXml = Encoding.UTF8.GetBytes(File.ReadAllText("Documents/factur-x.xml"));

    // rdf:Description elements that declare the invoice standard
    string metadata = File.ReadAllText("Documents/metadata.xml");

    EmbeddedFile invoice = new EmbeddedFile("factur-x.xml", invoiceXml, metadata)
    {
        Description = "factur-x",
        MIMEType = "application/xml",
        Relationship = "Alternative",
        LastModificationDate = DateTime.Now
    };

    SaveSettings saveSettings = new SaveSettings()
    {
        EmbeddedFiles = new EmbeddedFile[] { invoice }
    };

    tx.Save("invoice.pdf", StreamType.AdobePDFA, saveSettings);
}
```

The code works as follows:

- [MailMerge.MergeJsonData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver/mailmerge/mergejsondata-method/llms-full.txt) fills the template with the invoice data, as [Simple Mail Merge](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/mail-merge/simple/llms-full.txt) describes.
- *metadata.xml* contains the XMP extension schema of ZUGFeRD with the conformance level, the document type, the version and the name of the embedded file. The blog article [Creating ZUGFeRD 2.3 (XRechnung, Factur-X) Documents with .NET C#](https://www.textcontrol.com/blog/2025/01/02/creating-zugferd-2-3-xrechnung-factur-x-documents-with-net-c-sharp/) shows its complete content and creates the XML with ZUGFeRD-csharp.
- `StreamType.AdobePDFA` writes a PDF/A-3 document, the format hybrid invoices require. [PDF/A](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/pdf-a/llms-full.txt) describes its rules.

Check the result with a validation service for ZUGFeRD and Factur-X. To make later changes to the invoice and its XML detectable, sign the PDF when you save it, as the blog article [Why Structured E-Invoices Still Need Tamper Protection using C# and .NET](https://www.textcontrol.com/blog/2026/03/24/why-structured-e-invoices-still-need-tamper-protection-using-csharp-and-dotnet/) recommends. Signing is described in [Digital Signatures](https://docs.textcontrol.com/tx-text-control-dotnet-server/digital-signatures/llms-full.txt).

### Extract Attachments From a PDF

To read the attachments of a PDF without working with its content, use the static class [Attachments](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/attachments/llms-full.txt) of the `TXTextControl.DocumentServer.PDF` namespace. The following program writes every attachment of *Documents/invoice.pdf* to the current folder:

```csharp
using TXTextControl.DocumentServer.PDF;

EmbeddedFile[] files = Attachments.GetFromFile(Path.GetFullPath("Documents/invoice.pdf"))
    ?? Array.Empty<EmbeddedFile>();

Console.WriteLine($"{files.Length} attachments");

foreach (EmbeddedFile file in files)
{
    Console.WriteLine($"{file.FileName} ({file.MimeType}, {file.Relationship}): {file.Description}");

    // The file name can contain a path
    string fileName = Path.GetFileName(file.FileName);

    if (file.HasBinaryData)
    {
        File.WriteAllBytes(fileName, file.BinaryData);
    }
    else if (file.HasStringData)
    {
        File.WriteAllText(fileName, file.StringData);
    }
}
```

The code works as follows:

- [GetFromFile](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/attachments/getfromfile-method/llms-full.txt) takes the complete path of a PDF file, and [GetFromData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/attachments/getfromdata-method/llms-full.txt) takes the PDF as a byte array. Both return an array of [DocumentServer.PDF.EmbeddedFile](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/embeddedfile/llms-full.txt) objects.
- This `EmbeddedFile` class is a different class from `TXTextControl.EmbeddedFile`, with read-only properties. Its MIME type is named [MimeType](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/embeddedfile/mimetype-property/llms-full.txt).
- [HasBinaryData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/embeddedfile/hasbinarydata-property/llms-full.txt) and [HasStringData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/embeddedfile/hasstringdata-property/llms-full.txt) tell whether the content is in [BinaryData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/embeddedfile/binarydata-property/llms-full.txt) or in [StringData](https://docs.textcontrol.com/tx-text-control-dotnet-server/reference/txtextcontrol-documentserver-pdf/embeddedfile/stringdata-property/llms-full.txt).

The program uses only the `TXTextControl.DocumentServer.PDF` namespace, because both namespaces contain a class named `EmbeddedFile`.

#### Extract the XML of an Uploaded Invoice

An ASP.NET Core application that receives e-invoices can return their XML directly. The following action reads the attachments of an uploaded PDF and returns the file with the relationship `Alternative` and an XML MIME type:

```csharp
using Microsoft.AspNetCore.Mvc;
using TXTextControl.DocumentServer.PDF;

public class InvoiceController : Controller
{
    [HttpPost]
    public IActionResult ExtractInvoiceXml(IFormFile document)
    {
        // Copy the upload into a byte array
        byte[] data;

        using (MemoryStream memoryStream = new MemoryStream())
        {
            document.CopyTo(memoryStream);
            data = memoryStream.ToArray();
        }

        EmbeddedFile? invoice = (Attachments.GetFromData(data) ?? Array.Empty<EmbeddedFile>())
            .FirstOrDefault(file => file.Relationship == "Alternative" && file.HasBinaryData &&
                file.MimeType != null && file.MimeType.EndsWith("xml"));

        if (invoice == null)
        {
            return NotFound("The PDF has no XML invoice.");
        }

        return File(invoice.BinaryData, invoice.MimeType, Path.GetFileName(invoice.FileName));
    }
}
```

The MIME type is compared by its ending because older invoices use `text/xml` and newer ones `application/xml`. How uploads reach a controller is described in [Loading Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/document-processing/loading/#load-uploaded-documents-in-asp-net-core).

### Keep, Add and Replace Attachments

When `ServerTextControl` loads a PDF, it stores the attachments in `DocumentSettings.EmbeddedFiles`, and `Save` writes them again. A PDF that you load, change and save as PDF therefore keeps its attachments without any code. `SaveSettings.EmbeddedFiles` replaces them: the array you set is what the saved PDF contains. To add a file and keep the existing ones, pass both:

```csharp
using TXTextControl;

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    // PDF/A requires fonts that can be embedded
    tx.FontSettings.EmbeddableFontsOnly = true;

    LoadSettings loadSettings = new LoadSettings();
    tx.Load("Documents/invoice.pdf", StreamType.AdobePDF, loadSettings);

    // The attachments of the loaded PDF, or none
    List<EmbeddedFile> files = new List<EmbeddedFile>(loadSettings.EmbeddedFiles ?? Array.Empty<EmbeddedFile>());

    files.Add(new EmbeddedFile("line-items.csv", File.ReadAllBytes("Documents/line-items.csv"), null)
    {
        Description = "Line items",
        MIMEType = "text/csv",
        Relationship = "Supplement"
    });

    SaveSettings saveSettings = new SaveSettings()
    {
        EmbeddedFiles = files.ToArray()
    };

    tx.Save("invoice-with-line-items.pdf", StreamType.AdobePDFA, saveSettings);

    Console.WriteLine($"{files.Count} attachments saved");
}
```

A file read from a PDF contains its data as a byte array, and its name, description, MIME type, relationship and dates are kept when it's embedded again. Keep in mind that loading a PDF rebuilds its pages as an editable document, as [Load and Modify a PDF](https://docs.textcontrol.com/tx-text-control-dotnet-server/getting-started/pdf/load-modify/#what-happens-when-you-load-a-pdf) explains, so the saved PDF is a new document with the same attachments.

### Next Steps

- [PDF/A](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/pdf-a/llms-full.txt): create archivable PDF/A-3 documents.
- [Document Metadata](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/metadata/llms-full.txt): write and read the title, author and keywords.
- [Signing PDF Documents](https://docs.textcontrol.com/tx-text-control-dotnet-server/digital-signatures/signing/llms-full.txt): protect a PDF and its attachments with a signature.
- [PDF Validation](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/validation/llms-full.txt): check the PDF documents you create.

---

## Nearby

- **Previous:** [Flatten Forms](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/forms/flatten/llms-full.txt)
- **Next:** [Text Extraction](https://docs.textcontrol.com/tx-text-control-dotnet-server/pdf/text-extraction/llms-full.txt)
