This guide shows developers how to use PDFluent to handle interactive forms, process e-invoices, and manage document metadata. It is for Rust programmers working with PDFs.
Fill and flatten AcroForm PDF forms. Passes the PDF 1.7 conformance test suite (Adobe test corpus, 304 tests). Import data from FDF, XFDF, or JSON.
use pdfluent::PdfDocument;
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("form.pdf")?;
println!("{} form field(s)", doc.form_fields()?.len());
doc.form_mut()
.set_text("name", "Jane Smith")?
.set_checkbox("subscribe", true)?;
doc.save("filled.pdf")?;
Ok(())
}Migrate XFA-based PDF forms to modern standards with PDFluent. Flatten, convert, and extract data from XFA 3.3 forms without Adobe dependencies.
// Planned 1.1 surface — see note above.
// 1.0-compatible variant: fill AcroForm fields + save.
use pdfluent::prelude::*;
fn main() -> Result<()> {
let mut doc = PdfDocument::open("belastingaangifte_2024.pdf")?;
{
let mut form = doc.form_mut();
form.set_text("bsn", "123456782")?
.set_text("name", "Test User")?;
}
doc.save("belastingaangifte_2024_filled.pdf")?;
Ok(())
}Generate and validate ZUGFeRD and Factur-X e-invoices with PDFluent. Full PDF/A-3 compliance for EU e-invoicing mandates.
use pdfluent::{Sdk, invoice::{InvoiceBuilder, ZugferdProfile, ValidationLevel}};
let sdk = Sdk::init_with_license("license.json")?;
// Build the invoice data structure
let invoice = InvoiceBuilder::new()
.profile(ZugferdProfile::En16931)
.seller("Acme GmbH", "DE123456789")
.buyer("BundesMinisterium", "DE987654321")
.invoice_number("2024-001234")
.issue_date("2024-03-15")
.line_item("Consulting services", 10.0, 150.00, "H") // 19% VAT
.build()?;
// Validate against EN 16931 — 344 business rules
let validation = invoice.validate(ValidationLevel::En16931)?;
if !validation.is_valid() {
for rule in validation.violations() {
eprintln!(" {} — {}", rule.id(), rule.message());
}
return Err("Invoice failed EN 16931 validation".into());
}
// Embed XML into PDF/A-3b hybrid (ZUGFeRD)
let pdf = sdk.open("invoice_template.pdf")?;
let hybrid = pdf.attach_zugferd_invoice(&invoice)?;
hybrid.save("invoice_2024-001234.pdf")?;Access XMP metadata, document information dictionary, and custom properties. Batch update metadata across thousands of files.
use pdfluent::PdfDocument;
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("input.pdf")?;
// Read existing metadata
let info = doc.metadata();
println!("Title: {:?}", info.title);
println!("Author: {:?}", info.author);
// Write new metadata
doc.metadata_mut()
.set_title("Q1 2026 Financial Report")
.set_author("Finance Team")
.set_subject("Quarterly earnings")
.set_keywords(&["earnings", "Q1", "2026"])
.commit()?;
doc.save("output.pdf")?;
Ok(())
}Built-in Rust OCR engine or connect to Google Cloud Vision, AWS Textract, or Azure AI — your choice. PDFluent handles the PDF side either way.
use pdfluent::{Sdk, ocr::{OcrLayerOptions, HocrResult}};
use std::process::Command;
fn main() -> pdfluent::Result<()> {
let sdk = Sdk::new()?;
let doc = sdk.open("scanned_contract.pdf")?;
// Detect which pages are image-only (no text layer)
let scanned_pages: Vec<u32> = doc.pages()
.filter(|p| p.is_image_only())
.map(|p| p.index())
.collect();
println!("{} of {} pages are scanned", scanned_pages.len(), doc.page_count());
let mut builder = doc.add_ocr_layer();
for page_index in &scanned_pages {
// Extract the page as a 300 DPI PNG for OCR
let img = doc.render_page(*page_index, Default::default())?;
img.save(format!("/tmp/page_{}.png", page_index))?;
// Run Tesseract and get hOCR output
Command::new("tesseract")
.args([
&format!("/tmp/page_{}.png", page_index),
&format!("/tmp/page_{}", page_index),
"-l", "eng", "hocr",
])
.status()?;
let hocr = std::fs::read_to_string(
format!("/tmp/page_{}.hocr", page_index)
)?;
// Write invisible text overlay back into the page
let result = HocrResult::parse(&hocr)?;
builder.add_page(*page_index, result);
}
let opts = OcrLayerOptions::builder()
.text_rendering_mode(pdfluent::ocr::TextRenderingMode::Invisible)
.conform_to_pdfa2b(true)
.build();
let searchable = builder.finish(opts)?;
searchable.save("scanned_contract_searchable.pdf")?;
println!("Saved searchable PDF with {} OCR pages", scanned_pages.len());
Ok(())
}