XFA forms are embedded XML applications inside a PDF. PDFluent can parse the XFA data packet, read field values, and write new values back to the document.
use pdfluent::{PdfDocument, XfaForm};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("dynamic-form.pdf")?;
let mut form = doc.xfa_form_mut()?;
form.set_field("EmployeeName", "Ada Lovelace")?;
form.set_field("EmployeeId", "EMP-1815")?;
form.set_field("Department", "Engineering")?;
doc.save("filled-form.pdf")?;
Ok(())
}XFA support is in the xfa feature flag. It includes the XML parser and the XFA data packet writer.
# Cargo.toml
[dependencies]
pdfluent = { version = "0.9", features = ["xfa"] }Not every PDF contains an XFA form. Use has_xfa() to check before calling xfa_form().
use pdfluent::PdfDocument;
let doc = PdfDocument::open("dynamic-form.pdf")?;
if doc.has_xfa() {
println!("XFA form detected");
} else {
println!("No XFA form found in this document");
}Call xfa_form() to get a read-only view. Iterate fields() to discover field names and their current values.
let form = doc.xfa_form()?;
for field in form.fields() {
println!(
"name={:?} type={:?} value={:?}",
field.name(),
field.field_type(),
field.value()
);
}Use xfa_form_mut() to get a mutable handle. set_field() accepts any value that implements Into<String>.
let mut form = doc.xfa_form_mut()?;
form.set_field("EmployeeName", "Ada Lovelace")?;
form.set_field("StartDate", "2024-03-01")?;
form.set_field("FullTime", "true")?; // checkboxSave writes the updated XFA data packet back into the PDF structure. The output is a valid PDF with the new field values baked in.
doc.save("filled-form.pdf")?;
println!("Saved filled-form.pdf");No JVM, no runtime, no DLL dependencies. Ships as a single native binary or WASM module.
Rust's ownership model prevents buffer overflows and use-after-free. No segfaults in PDF parsing.
Same code runs server-side, in Docker, on AWS Lambda, on Cloudflare Workers, or in the browser via WASM.