Combine multiple PDF files into one document. PDFluent preserves bookmarks across all input files.
use pdfluent::prelude::*;
fn main() -> Result<()> {
let merged = PdfMerger::new()
.add(PdfDocument::open("part1.pdf")?)
.add(PdfDocument::open("part2.pdf")?)
.add(PdfDocument::open("part3.pdf")?)
.build()?;
merged.save("combined.pdf")?;
println!("Merged {} pages into combined.pdf", merged.page_count());
Ok(())
}Add the pdfluent crate to your Cargo.toml. No system libraries are required.
[dependencies]
pdfluent = "1"Open every source PDF with PdfDocument::open. The merger takes full PdfDocument values by move, so consume each input once.
use pdfluent::prelude::*;
let a = PdfDocument::open("invoice_jan.pdf")?;
let b = PdfDocument::open("invoice_feb.pdf")?;
let c = PdfDocument::open("invoice_mar.pdf")?;Create a PdfMerger, chain .add() for each document. Inputs are appended in the order they are added.
let merger = PdfMerger::new()
.add(a)
.add(b)
.add(c);Choose a BookmarkMergeStrategy. Concat (the default) groups each source's bookmarks under a top-level entry; FlattenAll sequences them; Discard drops all bookmarks. In 1.0 Concat has dedicated treatment; FlattenAll and Discard fall back to the underlying concatenation.
let merger = merger
.with_bookmarks(BookmarkMergeStrategy::Concat)
.with_page_labels(true); // 1.0: accepted but currently a no-opCall .build() to produce a merged PdfDocument, then save or serialise it. build() is the terminating step and consumes the merger.
let merged = merger.build()?;
merged.save("annual_report.pdf")?;
println!("Total pages: {}", merged.page_count());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.