Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ Install with: `npm install @cantoo/pdf-lib`
- [Create Form](#create-form)
- [Fill Form](#fill-form)
- [Flatten Form](#flatten-form)
- [Work with XFA Forms](#work-with-xfa-forms)
- [Extract XFA JavaScript](#extract-xfa-javascript)
- [Modify XFA JavaScript](#modify-xfa-javascript)
- [Copy Pages](#copy-pages)
- [Embed PNG and JPEG Images](#embed-png-and-jpeg-images)
- [Embed PDF Pages](#embed-pdf-pages)
Expand Down Expand Up @@ -102,6 +105,9 @@ Install with: `npm install @cantoo/pdf-lib`
- Create forms
- Fill forms
- Flatten forms
- Preserve XFA forms
- Extract XFA JavaScript
- Modify XFA JavaScript
- Add Pages
- Insert Pages
- Remove Pages
Expand Down Expand Up @@ -480,6 +486,130 @@ const pdfBytes = await pdfDoc.save()
// • Rendered in an <iframe>
```

### Work with XFA Forms

XFA (XML Forms Architecture) forms are complex, dynamic PDF forms commonly used for government forms, tax documents, and enterprise applications. Unlike standard AcroForms, XFA forms embed their structure and JavaScript in XML format.

**Important:** To preserve XFA forms when loading and saving PDFs, use the `preserveXFA` option:

<!-- prettier-ignore -->
```js
import { PDFDocument } from 'pdf-lib'

const xfaPdfBytes = ... // Load your XFA PDF

// Load with XFA preservation
const pdfDoc = await PDFDocument.load(xfaPdfBytes, {
preserveXFA: true
})

// Make modifications...

// Save with XFA preservation
const pdfBytes = await pdfDoc.save({
preserveXFA: true
})
```

**Note:** Without `preserveXFA: true`, XFA data will be removed when saving, which may cause the form to lose functionality.

### Extract XFA JavaScript

XFA forms often contain JavaScript for validation, calculations, and data import/export. You can extract all JavaScript from an XFA form:

<!-- prettier-ignore -->
```js
import { PDFDocument } from 'pdf-lib'

const xfaPdfBytes = ... // Load your XFA PDF

// Load the PDF with XFA preservation
const pdfDoc = await PDFDocument.load(xfaPdfBytes, {
preserveXFA: true
})

// Extract all XFA JavaScript
const scripts = pdfDoc.getXFAJavaScripts()

// Each script contains:
// - field: The field name (e.g., 'Button1', 'TextField2')
// - event: The event name (e.g., 'event__click', 'event__change')
// - script: The JavaScript code

console.log(`Found ${scripts.length} scripts`)

scripts.forEach((script) => {
console.log(`Field: ${script.field}`)
console.log(`Event: ${script.event}`)
console.log(`Code: ${script.script}`)
})

// Find specific scripts
const clickHandlers = scripts.filter(s =>
s.event.includes('click')
)

const validationScripts = scripts.filter(s =>
s.script.includes('validate') || s.script.includes('Validate')
)
```

### Modify XFA JavaScript

You can modify JavaScript in XFA forms to customize behavior, add logging, or fix issues:

<!-- prettier-ignore -->
```js
import { PDFDocument } from 'pdf-lib'

const xfaPdfBytes = ... // Load your XFA PDF

// Load the PDF with XFA preservation
const pdfDoc = await PDFDocument.load(xfaPdfBytes, {
preserveXFA: true
})

// Extract scripts to find what you want to modify
const scripts = pdfDoc.getXFAJavaScripts()
const importButton = scripts.find(s => s.field === 'ImportButton')

if (importButton) {
// Modify the import button's click handler
const newScript = `
// Custom import handler
try {
console.println("Starting import...");
${importButton.script}
console.println("Import completed!");
} catch(e) {
xfa.host.messageBox("Error: " + e.message);
}
`

const success = pdfDoc.setXFAJavaScript(
'ImportButton', // field name
importButton.event, // event name (e.g., 'event__click')
newScript // new JavaScript code
)

console.log(`Modification ${success ? 'succeeded' : 'failed'}`)
}

// Save with XFA preservation
const pdfBytes = await pdfDoc.save({
preserveXFA: true
})

// The modified PDF will have the updated JavaScript
```

**Use Cases:**
- Add error handling to existing scripts
- Modify validation rules
- Add logging for debugging
- Customize import/export behavior
- Fix compatibility issues

### Copy Pages

_This example produces [this PDF](assets/pdfs/examples/copy_pages.pdf)_ (when [this PDF](assets/pdfs/with_update_sections.pdf) is used for the `firstDonorPdfBytes` variable and [this PDF](assets/pdfs/with_large_page_count.pdf) is used for the `secondDonorPdfBytes` variable).
Expand Down
Loading