Generate, merge, split, shrink, and print PDFs

A practical guide for developers using the PDFluent SDK to build document automation and processing pipelines in Rust.

Automate PDF generation and processing at scale.

Generate thousands of invoices, process incoming PDFs, fill forms, add watermarks, and run PDF pipelines in CI/CD — all from a single Rust binary with no external dependencies.

rust
use pdfluent::{PdfDocument, WatermarkOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("template.pdf")?;

    doc.form_mut()
        .set_text("customer_name", "ACME Corp")?
        .set_text("date", "2026-01-15")?;

    doc.add_watermark("PROCESSED", WatermarkOptions::centered().opacity(0.15))?;
    doc.save("filled.pdf")?;
    Ok(())
}

Every PDF operation your pipeline needs.

Automate PDF document processing at scale with PDFluent: extract text, split/merge pages, apply redactions, and convert to PDF/A — all in pure Rust.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("invoice.pdf")?;

    // Positioned text blocks: text + bounding box + page
    for block in doc.text_with_layout()? {
        println!("p{} {:?} {}", block.page, block.bbox, block.text);
    }
    Ok(())
}

Generate PDFs programmatically in Rust.

Create invoices, receipts, reports, and templated documents from data. Pure Rust, no Java, no Python, no native dependencies.

rust
use pdfluent::{PdfDocument, Page, Font, Color};

fn generate_invoice(order: &Order) -> Vec<u8> {
    let mut doc = Document::new();
    let mut page = Page::a4();

    // Title
    page.add_text(
        &format!("Invoice #{}", order.id),
        Font::helvetica_bold(24),
        Position::new(50.0, 780.0),
    );

    // Line items table
    let mut y = 700.0;
    for item in &order.items {
        page.add_text(
            &item.description,
            Font::helvetica(11),
            Position::new(50.0, y),
        );
        page.add_text(
            &format!("€ {:.2}", item.price),
            Font::helvetica(11),
            Position::new(480.0, y),
        );
        y -= 20.0;
    }

    // Footer
    page.add_text(
        &format!("Total: € {:.2}", order.total),
        Font::helvetica_bold(13),
        Position::new(50.0, y - 20.0),
    );

    doc.add_page(page);
    doc.save_bytes()
}

Merge and split. Correctly.

Lossless PDF merge and split in Rust — bookmarks, page labels, and named destinations preserved.

rust
use pdfluent::{PdfDocument, PdfMerger, BookmarkMergeStrategy};

fn main() -> pdfluent::Result<()> {
    let merged = PdfMerger::new()
        .add(PdfDocument::open("chapter1.pdf")?)
        .add(PdfDocument::open("chapter2.pdf")?)
        .add(PdfDocument::open("chapter3.pdf")?)
        .with_bookmarks(BookmarkMergeStrategy::Concat)
        .with_page_labels(true)
        .build()?;

    merged.save("book_complete.pdf")?;
    println!("Pages: {}", merged.page_count());
    Ok(())
}

Split PDFs by page, bookmark, or pattern.

Extract pages, split at bookmarks, or divide by content pattern. Batch split thousands of documents with bookmark and label preservation.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("report.pdf")?;

    // Split into single-page documents
    for (i, page) in doc.split_pages()?.into_iter().enumerate() {
        page.save(format!("page-{}.pdf", i + 1))?;
    }

    // Or extract a contiguous range (0-based, end-exclusive)
    doc.extract_pages(0..5)?.save("first-five.pdf")?;
    Ok(())
}

Shrink PDFs without quality loss.

Compress images, remove duplicate streams, and optimize cross-reference tables. Reduce file size by 40-80% on typical documents.

rust
use pdfluent::{PdfDocument, CompressOptions};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("large.pdf")?;

    let report = doc.compress(CompressOptions::strict())?;
    println!("streams compressed: {}", report.streams_compressed);
    println!("streams deduplicated: {}", report.streams_deduplicated);

    doc.save("compressed.pdf")?;
    Ok(())
}

Print PDFs programmatically.

Send PDFs to system printers or generate print-ready output for PostScript and PCL workflows. No GUI needed.

rust
use pdfluent::{Sdk, print::{PrintOptions, DuplexMode, PrintRange}};

fn main() -> pdfluent::Result<()> {
    let sdk = Sdk::new()?;
    let doc = sdk.open("invoice.pdf")?;

    let opts = PrintOptions::builder()
        .printer_name("HP_LaserJet_M404")
        .copies(1)
        .duplex(DuplexMode::LongEdge)
        .page_range(PrintRange::All)
        .collate(true)
        .build();

    let job = doc.print(opts)?;

    println!("Print job ID: {}", job.id());
    println!("Status: {:?}", job.wait_for_completion()?);

    Ok(())
}

Build and extract PDF portfolios.

Create PDF portfolios containing embedded files. Extract attachments from existing documents. Handle PDF/A-3 with embedded XML invoices.

rust
use pdfluent::{Sdk, portfolio::{PortfolioOptions, EmbeddedFile}};

fn main() -> pdfluent::Result<()> {
    let sdk = Sdk::new()?;
    let mut doc = sdk.open("invoice.pdf")?;

    // Attach a ZUGFeRD XML file for PDF/A-3 e-invoicing
    let xml_bytes = std::fs::read("zugferd_invoice.xml")?;

    let attachment = EmbeddedFile::builder()
        .name("ZUGFeRD-invoice.xml")
        .mime_type("application/xml")
        .description("ZUGFeRD 2.1 invoice data")
        .relationship(pdfluent::portfolio::Relationship::Alternative)
        .data(xml_bytes)
        .build();

    let opts = PortfolioOptions::builder()
        .add(PdfDocument::open(attachment)?)
        .conform_to_pdfa3b(true)
        .build();

    let output = doc.embed_files(opts)?;
    output.save("invoice_pdfa3.pdf")?;

    println!("Attachments: {}", output.attachment_count());
    Ok(())
}

PDF processing that deploys anywhere.

No native dependencies. No JVM. No DLLs. PDFluent runs on Lambda, Cloudflare Workers, and Fly.io straight from a single Rust binary.

rust
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use pdfluent::PdfDocument;
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct Request {
    /// Base64-encoded PDF bytes
    pdf_b64: String,
}

#[derive(Serialize)]
struct Response {
    page_count: usize,
}

async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> {
    let bytes = base64::decode(&event.payload.pdf_b64)?;
    let doc = Document::open_bytes(&bytes)?;
    Ok(Response { page_count: doc.page_count() })
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    run(service_fn(handler)).await
}