From Java PDF to Rust

A guide for Java developers moving any Java PDF library — iText, PDFBox, or similar — to PDFluent in Rust. Covers the ownership model, error handling, and deployment changes.

Migrating from Java PDF to PDFluent. Install with cargo add pdfluent

Migration steps

1

Map your Java PDF operations to PDFluent equivalents

Java PDF libraries use object graphs you mutate through method calls. PDFluent exposes the same operations — open, read, fill, save — through a Rust API. Start by listing every PDF operation your code performs and find the PDFluent equivalent in the docs.

Java PDF (before)
// pom.xml
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>7.2.5</version>
    <type>pom</type>
</dependency>

// Or for PDFBox:
// <groupId>org.apache.pdfbox</groupId>
// <artifactId>pdfbox</artifactId>
// <version>3.0.0</version>
PDFluent (after)
# Cargo.toml
[dependencies]
pdfluent = "0.9"
2

Handle errors with Result instead of checked exceptions

Java PDF libraries throw checked exceptions — you surround every call with try/catch and manage cleanup in finally blocks. Rust uses the Result type. The ? operator propagates errors up the call stack automatically, and ownership ensures resources are freed when values go out of scope. There is no finally block because there is no need for one.

Java PDF (before)
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import java.io.IOException;

PdfDocument pdf = null;
try {
    pdf = new PdfDocument(new PdfReader("report.pdf"));
    String text = PdfTextExtractor.getTextFromPage(
        pdf.getPage(1),
        new LocationTextExtractionStrategy()
    );
    System.out.println(text);
} catch (IOException e) {
    System.err.println("Failed to open PDF: " + e.getMessage());
} finally {
    if (pdf != null) pdf.close();
}
PDFluent (after)
use pdfluent::Document;

fn process_report() -> pdfluent::Result<()> {
    let doc = Document::open("report.pdf")?;
    let text = doc.page(0)?.extract_text()?;
    println!("{}", text);
    Ok(())
    // doc is freed automatically when it goes out of scope
}
3

Replace JVM deployment with a static binary

A Java PDF service requires a JVM at the deployment target, adds 200-400 MB to your Docker image, and incurs a 1-3 second cold start. A Rust binary compiled with PDFluent has no runtime dependency and cold-starts in milliseconds. Build with cargo build --release and copy the single binary to your target.

Java PDF (before)
# Dockerfile (Java / iText)
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/myapp.jar .
# ~200 MB JRE layer, 1-3s cold start
ENTRYPOINT ["java", "-jar", "myapp.jar"]
PDFluent (after)
# Dockerfile (Rust / PDFluent)
FROM debian:bookworm-slim
WORKDIR /app
COPY target/release/myapp .
# ~20 MB total image, <50ms cold start
ENTRYPOINT ["./myapp"]

Things to watch out for

  • !Java libraries use 1-based page indexing. PDFluent uses 0-based indexing — page(0) is the first page.
  • !iText 7 is AGPL-licensed. If you are migrating partly to escape licensing constraints, verify your current obligations before removing iText.
  • !PDFBox's PDDocument.load() accepts byte arrays in addition to files. PDFluent's Document::from_bytes() is the equivalent.
  • !The Rust ownership model prevents use-after-free bugs that are possible in Java when document references are held after close() is called.

Frequently asked questions