Cookbook

PDFluent cookbook

10 recipes for the Rust API. Each one is a block in crates/pdfluent/examples/site_snippets.rs, which is built by CI, so a recipe that stops compiling breaks that build instead of your first attempt.

The examples load no licence key. The SDK is open; only signing refuses without one. Paths in the code are placeholders.

Reading

Extract the text of a document

Open a document and print its text, first as one string in reading order and then page by page.

use pdfluent::PdfDocument;

let doc = PdfDocument::open("invoice.pdf")?;

// The full text, in reading order.
let text = doc.extract_text()?;
println!("{text}");

// Or per page.
for n in 0..doc.page_count() {
    println!("--- page {} ---", n + 1);
    println!("{}", doc.page(n)?.text()?);
}

// site:open-and-extract in crates/pdfluent/examples/site_snippets.rs

Render every page to PNG

Rasterise each page to a PNG at a chosen resolution; to_images writes the files and returns their paths.

use pdfluent::{ImageFormat, PdfDocument, ToImagesOptions};

let doc = PdfDocument::open("document.pdf")?;

// The page number goes in as `{page}`. Without that marker `to_images`
// appends `_N` before the extension instead, so a pattern is never wrong
// in a way the compiler can see.
//
// ToImagesOptions is #[non_exhaustive]: it takes the builder, not a struct
// literal, so that a new option is not a breaking change.
let report = doc.to_images(
    "page_{page}.png",
    ToImagesOptions::new()
        .with_dpi(150)
        .with_format(ImageFormat::Png),
)?;
println!("{} pages rendered", report.paths.len());

// site:render-pages-to-png in crates/pdfluent/examples/site_snippets.rs

Pages and output

Read text with its position, then merge two documents

Walk each text block with its bounding box, then combine two documents into a single output.

use pdfluent::{PdfDocument, PdfMerger};

let doc = PdfDocument::open("report.pdf")?;

// Text with its bounding box, block by block.
for block in doc.text_with_layout()? {
    let [x, y, _, _] = block.bbox;
    println!("p{} [{x:.0},{y:.0}] {}", block.page, block.text);
}

// Merge multiple PDFs.
let merged = PdfMerger::new()
    .add(PdfDocument::open("part1.pdf")?)
    .add(PdfDocument::open("part2.pdf")?)
    .build()?;
merged.save("combined.pdf")?;

// site:text-and-merge in crates/pdfluent/examples/site_snippets.rs

Work on a PDF that headless Chrome produced

Watermark, compress and archive an exported document; PDFluent does not convert HTML itself.

use pdfluent::{CompressOptions, PdfAProfile, PdfDocument, WatermarkOptions};

// Chrome wrote the PDF; everything after that is PDFluent.
let mut doc = PdfDocument::open("out.pdf")?;

doc.add_watermark("DRAFT", WatermarkOptions::centered())?;
let report = doc.compress(CompressOptions::default())?;
println!("{} streams compressed", report.streams_compressed);

// convert_to_pdfa returns a new document rather than changing this one.
let archived = doc.convert_to_pdfa(PdfAProfile::A2b)?;
archived.save("invoice-archived.pdf")?;

// site:after-chrome in crates/pdfluent/examples/site_snippets.rs

Forms

Read an XFA form and write one field

List the logical fields of an XFA form and set one of them by its SOM path.

use pdfluent::PdfDocument;

let mut doc = PdfDocument::open("tax_return.pdf")?;

// The form model: one row per logical field.
let model = doc.xfa_form_model()?;
for field in &model.fields {
    println!("{} = {:?}", field.name, field.value);
}

// Fill by name.
doc.set_xfa_field_value("form1.name", pdfluent::xfa::XfaFieldValue::Text("Smith"))?;

// site:fill-xfa in crates/pdfluent/examples/site_snippets.rs

Fill an XFA form and save it

Set a field, confirm it reached the datasets packet, and save the filled document.

use pdfluent::{PdfDocument, XfaFieldValue};

let mut doc = PdfDocument::open("tax_return.pdf")?;

// The form model: every logical field, with its value and its type.
let model = doc.xfa_form_model()?;
for f in &model.fields {
    println!("{} = {:?} ({:?})", f.name, f.value, f.field_type);
}

// Fill by name. The value is written back into the datasets packet, so
// saving keeps it.
let value = XfaFieldValue::Text("Alice Smith");
let outcome = doc.set_xfa_field_value("form1.applicant.name", value)?;
println!("persisted: {}", outcome.persisted_to_datasets);
doc.save("filed_return.pdf")?;

// site:xfa-fill-and-flatten in crates/pdfluent/examples/site_snippets.rs

PDF/A

Check a document against PDF/A-2b

Run the PDF/A-2b check and print every violation it reports.

use pdfluent::{PdfAProfile, PdfDocument};

let doc = PdfDocument::open("legacy.pdf")?;

let report = doc.validate_pdfa(PdfAProfile::A2b)?;
if report.is_compliant() {
    println!("conforms to PDF/A-2b");
} else {
    for violation in &report.violations {
        println!("{violation:?}");
    }
}

// site:validate-pdfa in crates/pdfluent/examples/site_snippets.rs

Check first, then convert to PDF/A

Read the findings before converting; convert_to_pdfa hands back a new document instead of changing this one.

use pdfluent::{PdfAProfile, PdfDocument};

let doc = PdfDocument::open("legacy.pdf")?;

// Validate, then read the findings.
let report = doc.validate_pdfa(PdfAProfile::A2b)?;
if !report.is_compliant() {
    for v in &report.violations {
        eprintln!("[{}] {} ({:?})", v.rule, v.message, v.severity);
    }
}

// convert_to_pdfa returns a new document rather than changing this one.
let archived = doc.convert_to_pdfa(PdfAProfile::A2b)?;
archived.save("archived.pdf")?;

// site:pdfa-validate-and-convert in crates/pdfluent/examples/site_snippets.rs

Signatures

Sign with a PKCS#12 certificate

Sign a document from a .p12 file, choosing the PAdES profile this build can honour.

use pdfluent::signer::{PadesProfile, Pkcs12Signer, SignOptions};
use pdfluent::PdfDocument;

let mut doc = PdfDocument::open("contract.pdf")?;
let signer = Pkcs12Signer::from_pfx_file("cert.p12", "password")?;

// SignOptions::new() asks for PAdES B-LT, which needs a document security
// store this build cannot write. Choose the profile you can honour.
let opts = SignOptions::new()
    .profile(PadesProfile::BasicSignature)
    .reason("Approved");

doc.sign(&signer, opts)?;
doc.save("contract-signed.pdf")?;

// site:sign-pkcs12 in crates/pdfluent/examples/site_snippets.rs

Verify the signatures on a document

Walk the validations of a signed document and handle each status, including ones this build does not know.

use pdfluent::signer::SignatureStatus;
use pdfluent::PdfDocument;

let doc = PdfDocument::open("signed.pdf")?;
let report = doc.verify_signatures()?;
for validation in report.validations() {
    match &validation.status {
        SignatureStatus::Valid => println!("Signature is valid"),
        SignatureStatus::Invalid { reason } => println!("Signature is invalid: {reason}"),
        SignatureStatus::Unknown { reason } => {
            println!("Signature status is unknown: {reason}")
        }
        // SignatureStatus is #[non_exhaustive]: new outcomes can be added
        // without a breaking change, so a match on it needs a catch-all.
        _ => println!("Signature status not recognised by this build"),
    }
}

// site:verify-signatures in crates/pdfluent/examples/site_snippets.rs

Where to go next