# PDF Documents

> Create PDF and PDF/A-3b with pdf/create: properties, passwords, access permissions, digital signatures and attachments; read PDF metadata and embedded files.

- **Product:** DS Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** DS Server > Document Processing > PDF Documents
- **4205 words**
- **Web URL:** https://docs.textcontrol.com/ds-server/document-processing/pdf/
- **LLMs URL:** https://docs.textcontrol.com/ds-server/document-processing/pdf/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/ds-server/document-processing/pdf/llms-full.txt

---

Converting and merging already return PDF when you ask for it. For a PDF that has to meet more requirements, such as PDF/A for an archive, a title and keywords for a document management system, passwords and permissions, a digital signature or attached files, the DocumentProcessing Web API has the pdf/create endpoint. Two more endpoints read an existing PDF: its XMP metadata and the files embedded in it. The sections below show all three with curl and with the .NET package.

### How Creating a PDF Works

The endpoint is [POST /documentprocessing/pdf/create](https://docs.textcontrol.com/ds-server/reference/web-api/documentprocessing/pdf-create/post/llms-full.txt):

| Part of the request | Content |
|---|---|
| Body | A JSON object, the `PDFCreationSettings`, with the content type `application/json`. Its property `documentData` holds the source document as a Base64 string and is required; all other properties are optional |
| `returnFormat` | `PDF` or `PDFA`. The default is `PDF` |
| Response | The PDF document as a Base64 string in a JSON string |

The source can be any document that DS Server loads, for example a DOCX file or a document in the TX format that the merge endpoint created. When DS Server runs on Linux, the Core build supports DOCX, HTML, PDF and XLSX only, as [Limits on Linux](https://docs.textcontrol.com/ds-server/overview/core-classic/#limits-on-linux) lists.

Every request needs an access token, as [Authentication](https://docs.textcontrol.com/ds-server/document-processing/authentication/llms-full.txt) describes.

### PDF or PDF/A

PDF/A is the PDF format for long-term archiving. DS Server creates one PDF/A conformance level: `PDFA` creates PDF/A-3b, as the .NET API reference describes the value `PDFA` of the `PDFFormat` enumeration. There is no parameter for another level.

| You need | Use |
|---|---|
| A PDF or PDF/A with default settings | The `returnFormat` `PDF` or `PDFA` of [Converting Documents](https://docs.textcontrol.com/ds-server/document-processing/converting/llms-full.txt), [Merging Templates](https://docs.textcontrol.com/ds-server/document-processing/mail-merge/llms-full.txt) or [Appending Documents](https://docs.textcontrol.com/ds-server/document-processing/appending/llms-full.txt) |
| Document properties, passwords, permissions, a signature, attached files or JavaScript | pdf/create |
| A merged document with any of these | Merge with `returnFormat=TX`, then send the result to pdf/create |

The last row is the workflow the Text Control blog describes for a digitally signed PDF in [DS Server 1.1 Released: PDF and Barcodes](https://www.textcontrol.com/blog/2021/06/07/ds-server-1-1-released-pdf-and-barcodes/): the merge endpoint returns the document in the TX Text Control format, and pdf/create turns it into the final PDF with all settings. On Linux, the Core build doesn't support the TX format; merge with `returnFormat=DOCX` there instead.

### Create a PDF/A Document With curl

The following commands create a PDF/A document with a title, an author and keywords from *document.docx*:

```bash
DS=http://localhost:5000
TOKEN="<access_token>"

# Build the PDFCreationSettings object around the Base64 string of the document
base64 -w 0 document.docx | jq -Rs '{
  documentData: .,
  documentTitle: "Invoice 4711",
  author: "Accounting",
  documentKeywords: ["invoice", "2026"]
}' > pdf-settings.json

curl --request POST "$DS/documentprocessing/pdf/create?returnFormat=PDFA" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data-binary @pdf-settings.json \
  --output pdf.json

# Decode the Base64 string of the response
jq -r . pdf.json | base64 -d > invoice.pdf
```

The commands work as follows:

- `jq -Rs` reads the Base64 text as one string, `.`, and builds the JSON object around it. Writing the body to a file keeps large documents out of the command line.
- `returnFormat=PDFA` creates PDF/A-3b. Leave it out, or set `PDF`, for a plain PDF.
- The response is a JSON string. `jq -r .` removes its quotes, and `base64 -d` decodes it into the PDF file.

### Create a PDF/A Document With .NET

The package `TXTextControl.DocumentServices.DocumentProcessing` has the PDF methods in its `PDF` property. The settings are a `PDFSettings` object, in the namespace `TXTextControl.DocumentServices.DocumentProcessing.PDF`:

```csharp
using TXTextControl.DocumentServices.DocumentProcessing;
using TXTextControl.DocumentServices.DocumentProcessing.OAuth;
using TXTextControl.DocumentServices.DocumentProcessing.PDF;

var oauthSettings = new OAuthSettings("<client ID>", "<client secret>");
var documentProcessing = new DocumentProcessing("http://localhost:5000", oauthSettings);

byte[] document = File.ReadAllBytes("document.docx");

var settings = new PDFSettings
{
    DocumentTitle = "Invoice 4711",
    Author = "Accounting",
    DocumentKeywords = new[] { "invoice", "2026" }
};

byte[] pdfA = await documentProcessing.PDF.Create(document, settings, PDFFormat.PDFA);
File.WriteAllBytes("invoice.pdf", pdfA);
```

The code works as follows:

- The `DocumentProcessing` constructor takes the service URL and the credentials of a security profile, and requests an access token.
- `PDFSettings` holds the settings of the PDF. It inherits the document properties `DocumentTitle`, `DocumentSubject`, `Author`, `CreatorApplication`, `CreationDate`, `LastModificationDate` and `UserPassword` from `DocumentSettings`.
- `PDF.Create` takes the document as a byte array, the settings and a `PDFFormat` value, `PDF` or `PDFA`, and returns the PDF as a byte array.

The classes are listed in the [DocumentProcessing namespace](https://docs.textcontrol.com/ds-server/reference/dotnet-api/txtextcontrol-documentservices-documentprocessing/llms-full.txt) of the .NET API.

### Set the PDF Options

The properties of the JSON body and of `PDFSettings` correspond:

| JSON property | `PDFSettings` | Content |
|---|---|---|
| `documentTitle`, `documentSubject` | `DocumentTitle`, `DocumentSubject` | The title and the subject, each up to 2,000 characters in a PDF |
| `documentKeywords` | `DocumentKeywords` | The keywords, as an array of strings |
| `author`, `creatorApplication` | `Author`, `CreatorApplication` | The author and the application that created the document |
| `creationDate`, `lastModificationDate` | `CreationDate`, `LastModificationDate` | The dates saved in the document, in JSON as date-time strings such as `"2026-09-29T10:00:00Z"` |
| `userPassword` | `UserPassword` | The password a reader needs to open the document |
| `masterPassword` | `MasterPassword` | The password that protects the access permissions |
| `documentAccessPermissions` | `DocumentAccessPermissions` | What a reader may do with the document, see [Protect a PDF With Passwords](#protect-a-pdf-with-passwords) |
| `imageCompressionQuality` | `ImageCompressionQuality` | The compression quality of images, from 1 to 100 |
| `documentLevelJavaScriptActions` | `DocumentLevelJavaScriptActions` | JavaScript actions embedded in the PDF, as an array of strings |
| `embeddedFiles` | `EmbeddedFiles` | Files attached to the PDF, see [Read the Embedded Files](#read-the-embedded-files) for their properties |
| `digitalSignature` | `DigitalSignature` | The certificate to sign the PDF with, see [Sign the PDF](#sign-the-pdf) |

The attached files go in `embeddedFiles`, an array, and their dates are date-time strings. DS Server ignores a property it doesn't know without an error, so a misspelled name such as `embeddedFile` leaves the PDF without attachments.

### Protect a PDF With Passwords

A reader needs the user password to open the document. The master password protects the access permissions, which only have an effect when a master password is set. The permissions and their values are:

| Value | Number | Allows |
|---|---|---|
| `None` | 0 | None of the permissions |
| `AllowAuthoring` | 1 | Adding comments and filling in form fields, including signature fields |
| `AllowAuthoringFields` | 2 | Filling in existing form fields, including signature fields |
| `AllowContentAccessibility` | 4 | Extracting content for the visually impaired only |
| `AllowDocumentAssembly` | 8 | Inserting, rotating and deleting pages and creating bookmarks and thumbnails |
| `AllowExtractContents` | 16 | Copying or otherwise extracting text and graphics |
| `AllowGeneralEditing` | 32 | Changing the content of the document |
| `AllowHighLevelPrinting` | 64 | Printing, fully allowed |
| `AllowLowLevelPrinting` | 128 | Printing, low-level |
| `AllowAll` | 255 | All of the permissions |

In JSON, `documentAccessPermissions` is a number: add the numbers of the permissions you allow. A name such as `"AllowAll"` is refused with `400` and the error "The JSON value could not be converted". The following body creates a PDF that everyone can open and print, but not change:

```bash
base64 -w 0 document.docx | jq -Rs '{
  documentData: .,
  masterPassword: "<owner password>",
  documentAccessPermissions: 68
}' > pdf-settings.json
```

68 is `AllowHighLevelPrinting` (64) plus `AllowContentAccessibility` (4). Send the file to pdf/create as in [Create a PDF/A Document With curl](#create-a-pdf-a-document-with-curl). In C#, combine the values of the `DocumentAccessPermissions` enumeration, which is in the `PDF` namespace, with the `|` operator:

```csharp
var settings = new PDFSettings
{
    MasterPassword = "<owner password>",
    DocumentAccessPermissions = DocumentAccessPermissions.AllowHighLevelPrinting |
                                DocumentAccessPermissions.AllowContentAccessibility
};

byte[] pdf = await documentProcessing.PDF.Create(document, settings, PDFFormat.PDF);
```

Add a `userPassword` or `UserPassword` to require a password for opening the document as well.

### Sign the PDF

pdf/create signs the PDF with an X.509 certificate that you send with the request:

- **JSON.** `digitalSignature` is an object with `x509Certificate2`, the certificate as a string, `password`, the password of the certificate, and the optional `timeServerURL`, the URL of a time server.
- **.NET.** `PDFSettings.DigitalSignature` takes a `DigitalSignature` object with the properties `Certificate`, an `X509Certificate2`, `Password` and `TimeServerURL`.

The certificate and its password travel to DS Server with every request, so send them over HTTPS only, as [HTTPS](https://docs.textcontrol.com/ds-server/installation/https/llms-full.txt) sets it up.

### Read the Metadata of a PDF

[POST /documentprocessing/pdf/metadata](https://docs.textcontrol.com/ds-server/reference/web-api/documentprocessing/pdf-metadata/post/llms-full.txt) returns the metadata of a PDF document as a JSON string. In the example of the endpoint reference, the string is an XMP packet, the XML format of PDF metadata:

```bash
base64 -w 0 invoice.pdf | jq -Rs . > invoice.json

curl --request POST "$DS/documentprocessing/pdf/metadata" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data-binary @invoice.json \
  | jq -r . > metadata.xml
```

The body is the PDF as a Base64 string in a JSON string. `jq -r .` turns the JSON string into the XML text. A PDF without metadata is answered with `400 Bad Request`. In C#, `PDF.GetMetaData` returns the same string:

```csharp
string metadata = await documentProcessing.PDF.GetMetaData(File.ReadAllBytes("invoice.pdf"));
```

### Read the Embedded Files

[POST /documentprocessing/pdf/embeddedfiles](https://docs.textcontrol.com/ds-server/reference/web-api/documentprocessing/pdf-embeddedfiles/post/llms-full.txt) returns the files attached to a PDF, such as the XML invoice data of an electronic invoice. The body is the PDF as a Base64 string, and the response an array of `EmbeddedFile` objects:

| Property | Content |
|---|---|
| `fileName` | The file name of the attachment |
| `data` | The content of the file. In the example of the endpoint reference, it is a Base64 string and `dataType` is `BASE64BINARY` |
| `dataType` | The type of `data` |
| `mimeType` | The MIME type, such as `application/pdf` |
| `description` | The description of the attachment |
| `relationship` | The relationship of the file to the PDF |
| `creationDate`, `lastModificationDate` | The dates of the file |
| `metadata` | The metadata of the file |

The following commands save each attachment of *invoice.pdf* under its file name:

```bash
curl --request POST "$DS/documentprocessing/pdf/embeddedfiles" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data-binary @invoice.json \
  --output attachments.json

count=$(jq length attachments.json)
for i in $(seq 0 $((count - 1))); do
  name=$(jq -r ".[$i].fileName" attachments.json)
  jq -r ".[$i].data" attachments.json | base64 -d > "$(basename "$name")"
done
```

In C#, `PDF.GetEmbeddedFiles` returns an array of `EmbeddedFile` objects, in the `PDF` namespace:

```csharp
byte[] pdf = File.ReadAllBytes("invoice.pdf");
EmbeddedFile[] files = await documentProcessing.PDF.GetEmbeddedFiles(pdf);

foreach (EmbeddedFile file in files)
{
    Console.WriteLine($"{file.FileName} ({file.MIMEType})");
    File.WriteAllBytes(Path.GetFileName(file.FileName), Convert.FromBase64String(file.Data));
}
```

`Path.GetFileName` and `basename` keep a file name from the PDF from writing outside the current folder. A PDF whose attachments your application doesn't know is outside input; check the names and types before you use the files.

### Next Steps

- [PDF Form Fields](https://docs.textcontrol.com/ds-server/document-processing/pdf-forms/llms-full.txt): read the AcroForm fields of a PDF and flatten form fields.
- [Converting Documents](https://docs.textcontrol.com/ds-server/document-processing/converting/llms-full.txt): create PDF and PDF/A with default settings.
- [Merging Templates](https://docs.textcontrol.com/ds-server/document-processing/mail-merge/llms-full.txt): create the document that pdf/create turns into the final PDF.
- [Errors and Status Codes](https://docs.textcontrol.com/ds-server/document-processing/errors/llms-full.txt): what a refused request means.

---

## Nearby

- **Previous:** [Thumbnails](https://docs.textcontrol.com/ds-server/document-processing/thumbnails/llms-full.txt)
- **Next:** [PDF Form Fields](https://docs.textcontrol.com/ds-server/document-processing/pdf-forms/llms-full.txt)
