Fill Forms, Validate E-Invoices, and Read PDF Metadata

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.

PDF forms that actually fill.

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.

rust
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(())
}

XFA forms work again. Without Adobe.

Migrate XFA-based PDF forms to modern standards with PDFluent. Flatten, convert, and extract data from XFA 3.3 forms without Adobe dependencies.

rust
// 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(())
}

E-invoicing that actually validates.

Generate and validate ZUGFeRD and Factur-X e-invoices with PDFluent. Full PDF/A-3 compliance for EU e-invoicing mandates.

rust
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")?;

Read and write PDF metadata.

Access XMP metadata, document information dictionary, and custom properties. Batch update metadata across thousands of files.

rust
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(())
}

Make scanned PDFs searchable.

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.

rust
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(())
}