Migrate from PDFBox, Poppler, C++, Java, pdf-rs, and PyMuPDF

This guide provides specific instructions for developers to migrate their existing PDF projects from other libraries to the PDFluent SDK in Rust.

Migrate from Apache PDFBox to PDFluent

A step-by-step guide for replacing Apache PDFBox with PDFluent. Covers dependency setup, document loading, text extraction, form filling, and saving.
  1. Step 1

    Replace the dependency

    Remove PDFBox from pom.xml or build.gradle and add pdfluent to Cargo.toml.

    Before
    rust
    <!-- pom.xml -->
    <dependency>
        <groupId>org.apache.pdfbox</groupId>
        <artifactId>pdfbox</artifactId>
        <version>3.0.1</version>
    </dependency>
    After (PDFluent)
    rust
    # Cargo.toml
    [dependencies]
    pdfluent = "1.1.0"
  2. Step 2

    Open a document

    PDFBox uses PDDocument.load() with a File or byte array. PDFluent uses Document::open which returns a Result.

    Before
    rust
    import org.apache.pdfbox.pdmodel.PDDocument;
    import java.io.File;
    
    PDDocument doc = PDDocument.load(new File("contract.pdf"));
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("contract.pdf")?;
  3. Step 3

    Extract text

    PDFBox requires a PDFTextStripper instance and produces a single string for the whole document. PDFluent extracts per-page.

    Before
    rust
    import org.apache.pdfbox.text.PDFTextStripper;
    
    PDFTextStripper stripper = new PDFTextStripper();
    stripper.setStartPage(1);
    stripper.setEndPage(1);
    String text = stripper.getText(doc);
    After (PDFluent)
    rust
    let text = doc.page(1)?.text()?;
    
    // All pages
    for i in 0..doc.page_count() {
        let text = doc.page(i)?.text()?;
        println!("{}", text);
    }
  4. Step 4

    Fill AcroForm fields

    PDFBox accesses fields through PDDocumentCatalog and PDAcroForm. PDFluent hands out a mutable form handle with doc.form_mut(); flattening is a document-level call.

    Before
    rust
    import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
    
    PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
    acroForm.getField("first_name").setValue("Jane");
    acroForm.getField("last_name").setValue("Smith");
    acroForm.flatten();
    After (PDFluent)
    rust
    let mut form = doc.form_mut();
    form.set_text("first_name", "Jane")?;
    form.set_text("last_name", "Smith")?;
    doc.flatten_forms()?;
  5. Step 5

    Save and close

    PDFBox requires explicit close(). PDFluent drops the document when it goes out of scope; call save() to write.

    Before
    rust
    doc.save("output.pdf");
    doc.close(); // must call close() to release file handles
    After (PDFluent)
    rust
    doc.save("output.pdf")?;
    // doc drops automatically at end of scope
  • PDFBox page numbers are 1-indexed in PDFTextStripper but 0-indexed in PDPageTree. PDFluent always uses 0-indexed.
  • PDFBox does not support XFA forms. If your PDFs use XFA, PDFluent handles them natively.
  • PDFBox PDDocument must be closed explicitly or resource leaks occur. PDFluent documents drop cleanly with Rust ownership.
Can PDFluent read PDFs created by PDFBox?
Yes. PDFluent reads standard PDF files regardless of which library produced them.
PDFBox is free (Apache 2.0). Does switching to PDFluent cost money?
PDFluent is free and complete under the AGPL, so cost only becomes a question if you cannot publish your own source. If budget is the primary constraint and you are on the JVM, PDFBox remains a viable option.
How does PDFBox PDF/A support compare?
PDFBox has partial PDF/A support via the preflight module. PDFluent validates PDF/A-1, PDF/A-2, and PDF/A-3 and can convert documents to those standards.
Can I call PDFluent from an existing Java service?
PDFluent exposes a C API that can be called from Java via JNI or JNA. This allows incremental migration without rewriting everything at once.

From Poppler to PDFluent

Poppler is a GPL-licensed C++ library. PDFluent is a Rust crate under the AGPL, with a commercial licence that lifts the copyleft when you need it — no C++ compilation step, and a higher-level API.
  1. Step 1

    Remove the C++ Poppler dependency

    Poppler is a system library installed via apt, brew, or built from source. It requires C++ headers and a linker step in your build. PDFluent is a Rust crate — add it to Cargo.toml and Cargo handles everything. No system package required, no pkg-config, no build.rs linking.

    Before
    rust
    # Install system dependency
    apt-get install -y libpoppler-cpp-dev
    
    # CMakeLists.txt
    find_package(PkgConfig REQUIRED)
    pkg_check_modules(POPPLER REQUIRED poppler-cpp)
    target_link_libraries(myapp ${POPPLER_LIBRARIES})
    After (PDFluent)
    rust
    # Cargo.toml — no system package needed
    [dependencies]
    pdfluent = "1.1.0"
  2. Step 2

    Replace Poppler document loading with Document::open

    Poppler's C++ API returns raw pointers from load_from_file. You must check for null and remember to delete the document when done. PDFluent returns a Result. The ? operator handles errors, and the document is freed when it goes out of scope.

    Before
    rust
    #include <poppler/cpp/poppler-document.h>
    #include <poppler/cpp/poppler-page.h>
    
    poppler::document* doc =
        poppler::document::load_from_file("input.pdf");
    if (!doc) {
        std::cerr << "Failed to open" << std::endl;
        return 1;
    }
    
    poppler::page* p = doc->create_page(0);
    std::vector<poppler::text_box> boxes = p->text_list();
    
    delete p;
    delete doc;
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    fn process() -> pdfluent::Result<()> {
        let doc = PdfDocument::open("input.pdf")?;
        let text = doc.page(1)?.text()?;
        println!("{}", text);
        Ok(())
    }
  3. Step 3

    Verify your GPL licensing obligations are resolved

    Poppler is licensed under GPL-2.0. Any software that links against it as a library must also be GPL-licensed or obtain a separate commercial exception. PDFluent offers both routes from one publisher: the AGPL if copyleft is no obstacle, or a yearly commercial licence, from €999 for one organisation, that lifts it.

    Before
    rust
    # Poppler GPL-2.0:
    # Your application must be GPL-licensed if it links
    # against Poppler directly as a library.
    # Commercial use requires legal review or a separate
    # arrangement with each downstream user.
    After (PDFluent)
    rust
    # PDFluent — two routes, one publisher:
    # AGPLv3 if copyleft is no obstacle, or a
    # yearly commercial licence that lifts it.
    # Published price, bought with a card.
  • Poppler focuses on rendering and text extraction. If your primary use case is pixel-accurate PDF rendering, review PDFluent's current rendering capabilities before migrating.
  • Poppler does not support creating or editing PDFs — it is read-only. If you use additional tools (like libqpdf or Cairo) alongside Poppler for writing, PDFluent handles both reading and writing in a single crate.
  • Poppler uses 0-based page indexing, matching PDFluent's 0-based indexing. Page number references do not need adjustment.
  • Poppler has no form-filling API. AcroForm and XFA support require a separate library when using Poppler.
Does PDFluent require any system libraries like Poppler does?
No. PDFluent is a pure Rust crate with no system library dependencies. It compiles to a static binary on Linux, macOS, and Windows without requiring any pre-installed packages.
Can PDFluent fill AcroForms, which Poppler cannot?
Yes. PDFluent supports AcroForm field filling, XFA forms, and form data extraction. Poppler has no form manipulation API — this is a capability gain when migrating.
Is the GPL licensing concern real, or just theoretical?
It is a practical concern for any commercial product that ships executables linking against Poppler. Legal interpretation varies by jurisdiction and deployment model, but many legal teams treat it as a real constraint. PDFluent eliminates this concern entirely.
Does PDFluent work on the same platforms as Poppler?
PDFluent supports Linux, macOS, Windows, and WebAssembly. Poppler targets desktop platforms but does not compile to WASM. If browser-side PDF processing is relevant to your roadmap, PDFluent adds that capability.

From C++ PDF to Rust

A guide for C++ developers moving from libpoppler, MuPDF, or custom PDF code to PDFluent.
  1. Step 1

    Replace manual memory management with ownership

    C++ PDF code allocates and frees objects manually. Poppler hands you raw pointers; you decide when to delete them. In Rust, the compiler tracks ownership at compile time. Values are freed when they go out of scope. There are no delete calls.

    Before
    rust
    // libpoppler-cpp
    #include <poppler/cpp/poppler-document.h>
    #include <poppler/cpp/poppler-page.h>
    
    poppler::document* doc =
        poppler::document::load_from_file("report.pdf");
    if (!doc) {
        std::cerr << "Failed to open PDF" << std::endl;
        return 1;
    }
    
    poppler::page* p = doc->create_page(0);
    // must manually delete both
    delete p;
    delete doc;
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    fn process() -> pdfluent::Result<()> {
        let doc = PdfDocument::open("report.pdf")?;
        // doc and page freed automatically when they go out of scope
        Ok(())
    }
  2. Step 2

    Replace raw pointer APIs with safe method calls

    C++ PDF libraries pass raw pointers for page objects, text content, and annotations. You must check for null pointers at every step. PDFluent methods return Option or Result — the compiler forces you to handle the failure case, eliminating null pointer crashes.

    Before
    rust
    poppler::page* page = doc->create_page(0);
    if (!page) return 1;
    
    std::vector<poppler::text_box> boxes = page->text_list();
    for (const auto& box : boxes) {
        std::string text = box.text().to_latin1();
        std::cout << text << std::endl;
    }
    delete page;
    After (PDFluent)
    rust
    let page = doc.page(1)?;
    let text = page.text()?;
    println!("{}", text);
  3. Step 3

    Migrate build configuration from CMake to Cargo

    C++ projects link against poppler or MuPDF through CMake or pkg-config. Cargo handles all dependency resolution automatically. Remove the C++ library linkage from your build scripts and add PDFluent to Cargo.toml.

    Before
    rust
    # CMakeLists.txt
    find_package(PkgConfig REQUIRED)
    pkg_check_modules(POPPLER REQUIRED poppler-cpp)
    
    target_include_directories(myapp PRIVATE
        ${POPPLER_INCLUDE_DIRS})
    target_link_libraries(myapp PRIVATE
        ${POPPLER_LIBRARIES})
    After (PDFluent)
    rust
    # Cargo.toml
    [dependencies]
    pdfluent = "1.1.0"
    
    # cargo build --release
    # No pkg-config, no system library required
  • Poppler is GPL-licensed. If your C++ code links against it, your application must also be GPL-licensed. PDFluent is AGPL by default and sells a commercial licence that lifts the copyleft — check your licensing obligations before and after migration.
  • MuPDF has a custom Affero GPL license for open-source use and a commercial license for proprietary use. PDFluent publishes its price: per organisation or per product, and never per seat.
  • C++ page indexing varies by library. PDFluent uses 0-based page indexing consistently.
  • If you have C++ code that calls Poppler through a wrapper, you can introduce PDFluent incrementally via its C API before fully migrating.
Will I lose performance switching from C++ to Rust?
No. Rust compiles to native machine code with no garbage collector and no runtime overhead.
Can PDFluent replace MuPDF for PDF rendering?
PDFluent supports text extraction, form operations, and PDF manipulation. High-fidelity pixel rendering (the primary use of MuPDF) is on the roadmap. If your workload is primarily rendering to images, evaluate the current rendering capabilities before committing to a migration.
How does PDFluent handle thread safety compared to Poppler?
Rust's type system enforces thread safety at compile time. PDFluent types implement Send and Sync where appropriate, and the compiler prevents data races. Poppler requires careful manual synchronization for multi-threaded use.
Can I call PDFluent from my existing C++ code during a gradual migration?
Yes. PDFluent exposes a C API that can be called from C++ with a thin header. This allows you to migrate one operation at a time without rewriting the entire codebase at once.

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.
  1. Step 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.

    Before
    rust
    // 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>
    After (PDFluent)
    rust
    # Cargo.toml
    [dependencies]
    pdfluent = "1.1.0"
  2. Step 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.

    Before
    rust
    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();
    }
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    fn process_report() -> pdfluent::Result<()> {
        let doc = PdfDocument::open("report.pdf")?;
        let text = doc.page(1)?.text()?;
        println!("{}", text);
        Ok(())
        // doc is freed automatically when it goes out of scope
    }
  3. Step 3

    Replace JVM deployment with a static binary

    A Java PDF service needs a JVM in the image. A Rust binary compiled with PDFluent has no runtime dependency. Build with cargo build --release and copy the single binary to your target.

    Before
    rust
    # Dockerfile (Java / iText)
    FROM eclipse-temurin:21-jre
    WORKDIR /app
    COPY target/myapp.jar .
    # JRE layer in the image
    ENTRYPOINT ["java", "-jar", "myapp.jar"]
    After (PDFluent)
    rust
    # Dockerfile (Rust / PDFluent)
    FROM debian:bookworm-slim
    WORKDIR /app
    COPY target/release/myapp .
    # single binary, no runtime
    ENTRYPOINT ["./myapp"]
  • 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 PdfDocument::from_bytes() is the equivalent.
Do I need to rewrite all my PDF code at once?
No. You can expose PDFluent through its C API or Node/Python bindings and call it from a Java service, migrating one operation at a time while keeping the rest of your Java codebase intact.
How do I handle the Java checked exception pattern in Rust?
Replace try/catch blocks with the ? operator on Result-returning functions. Define a top-level error type (or use anyhow) and let the compiler guide you. Resource cleanup is handled by Rust's drop semantics — no finally blocks needed.
Will PDFluent read PDFs produced by iText or PDFBox?
Yes. PDFluent reads any standards-compliant PDF regardless of which library produced it. Documents generated by iText, PDFBox, or other Java libraries open without issues.
Does PDFluent support XFA forms that iText handles?
Yes. PDFluent supports static and dynamic XFA forms including FormCalc execution, which iText handles through a separate pdfXFA add-on.

Migrate from pdf-rs to PDFluent

Both libraries are written in Rust. pdf-rs is read-only. If you need to write, fill forms, sign, or validate PDFs, switch to PDFluent.
  1. Step 1

    Replace the dependency in Cargo.toml

    Remove pdf from Cargo.toml and add pdfluent. Both are pure Rust, so there are no native library changes.

    Before
    rust
    # Cargo.toml
    [dependencies]
    pdf = "0.9"
    After (PDFluent)
    rust
    # Cargo.toml
    [dependencies]
    pdfluent = "1.1.0"
  2. Step 2

    Open a PDF for reading

    pdf-rs uses FileOptions to open a file. PDFluent uses Document::open. The error handling model is the same — both return Results.

    Before
    rust
    use pdf::file::FileOptions;
    
    let file = FileOptions::cached()
        .open("document.pdf")
        .unwrap();
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("document.pdf")?;
  3. Step 3

    Extract text from pages

    pdf-rs requires traversing content streams manually. PDFluent provides a high-level text() method per page.

    Before
    rust
    use pdf::file::FileOptions;
    use pdf::object::*;
    
    let file = FileOptions::cached().open("document.pdf").unwrap();
    
    for page in file.pages() {
        let page = page.unwrap();
        // Content stream must be parsed manually
        // No high-level text extraction API
        if let Some(content) = page.contents.as_ref() {
            for op in content.operations.iter() {
                // match on operator codes to extract text
                println!("{:?}", op);
            }
        }
    }
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("document.pdf")?;
    
    for i in 0..doc.page_count() {
        let text = doc.page(i)?.text()?;
        println!("{}", text);
    }
  4. Step 4

    Write and modify PDFs

    pdf-rs has no write API. PDFluent supports modifying existing documents, filling forms, adding annotations, and saving. This step has no pdf-rs equivalent.

    Before
    rust
    // pdf-rs: no write API available
    // Cannot modify documents, fill forms, or save changes
    After (PDFluent)
    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("application.pdf")?;
    
    // Fill AcroForm
    let mut form = doc.form_mut();
    form.set_text("applicant_name", "Jane Smith")?;
    form.set_text("date", "2024-04-14")?;
    
    // Validate PDF/A before saving
    let report = doc.validate_pdfa(pdfluent::PdfAProfile::A1b)?;
    println!("Compliant: {}", report.is_compliant());
    
    doc.save("application_filled.pdf")?;
  • pdf-rs uses cached file access by default. PDFluent loads the document into memory. For very large files, use Document::open_streaming().
  • pdf-rs has incomplete support for some encrypted PDFs and complex content streams. PDFluent handles these cases.
Is pdf-rs actively maintained?
pdf-rs receives occasional updates but does not have a commercial support model. PDFluent is actively developed with a published roadmap.
Can PDFluent read PDFs that pdf-rs fails on?
In many cases yes. PDFluent handles a broader range of PDF versions, encryption modes, and content stream encodings.
pdf-rs is free. Does PDFluent cost money?
PDFluent is free and complete under the AGPL, with no feature restrictions. Commercial use without publishing your own source is a yearly licence, from €999 for one organisation.
My codebase only reads PDFs. Is it worth migrating?
If you only need read access and pdf-rs meets your needs, migration may not be worthwhile. PDFluent adds value when you need text extraction with layout information, form reading, annotation access, or any write operation.

From PyMuPDF to PDFluent

PyMuPDF (the fitz module) is a Python binding over MuPDF, dual-licensed AGPL-3.0 and commercial by its publisher, Artifex. PDFluent 1.1.0 ships a compat layer that follows part of the PyMuPDF API; coverage is partial, behaviour can differ in places, and it is an independent implementation, not PyMuPDF. Verify every example below against the shipped 1.1.0 release before relying on it.
  1. Step 1

    Swap the install

    Remove PyMuPDF and install the PDFluent Python package. The package name on PyPI is pdfluent; the compat layer that follows part of the PyMuPDF API described below ships in release 1.1.0.

    Before
    bash
    pip install pymupdf
    After (PDFluent)
    bash
    pip install pdfluent==1.1.0
  2. Step 2

    Open a document and read the page count

    This shows PDFluent's stable Python API, for comparison. It is PDFluent's own naming (page_count(), not len(doc)), not a claim about what the compat layer that follows part of the PyMuPDF API exposes.

    Before
    python
    import fitz  # PyMuPDF
    
    doc = fitz.open("invoice.pdf")
    print(f"{len(doc)} pages")
    doc.close()
    After (PDFluent)
    python
    import pdfluent
    
    doc = pdfluent.open("invoice.pdf")
    print(f"{doc.page_count()} pages")
  3. Step 3

    Coordinates: top-left, matching PyMuPDF

    PDFluent's native Rust and C APIs report PDF geometry using the PDF specification's own bottom-left origin. The Python compat layer that follows part of the PyMuPDF API returns coordinates with a top-left origin instead. That is an explicit SDK design decision made on 2026-10-08 so page and rectangle coordinates read the same way for code arriving from PyMuPDF.

  • This section covers the compat layer that follows part of the PyMuPDF API, which ships in release 1.1.0. Check every code example here against the shipped 1.1.0 release before relying on it.
  • Exact Python method-name parity with PyMuPDF — whether PDFluent's compatible surface will expose the same method names as fitz, one for one — is pending legal review as of 2026-10-08 and may still be unresolved. The SDK's own docs and changelog are the source of truth for which method names are actually implemented and cleared; this guide intentionally does not assume that parity.
  • PyMuPDF is dual-licensed: AGPL-3.0 for open-source use, or a commercial licence from its publisher, Artifex, for closed-source distribution. PDFluent follows the same shape — AGPLv3, or a yearly commercial licence from €999 that lifts the copyleft obligation.
  • PyMuPDF and PDFluent are built on different PDF engines (MuPDF vs. PDFluent's own Rust engine). Expect some behavioural differences on edge cases — malformed PDFs, uncommon encodings, specific rendering fidelity — even once the Python method names line up.
Is the compat layer that follows part of the PyMuPDF API method-for-method identical to fitz?
Not confirmed. Exact Python method-name parity with PyMuPDF is pending legal review as of 2026-10-08. Check the SDK's own changelog and docs for the current, shipped method names before writing code against it; this guide deliberately avoids asserting one-to-one naming.
Which PDFluent release includes the compat layer that follows part of the PyMuPDF API?
Release 1.1.0, available on all channels; the release notes are at https://github.com/pdfluent/pdfluent-sdk/releases/tag/v1.1.0.
Will I need to change any code once I switch?
Expect to. PDFluent describes the surface as a compat layer that follows part of the PyMuPDF API, and coverage is partial, so behaviour can differ in places. Check the SDK's changelog for specifics, and expect some call sites to need adjustment.
Why do coordinates use a top-left origin instead of PDFluent's usual bottom-left?
It's an explicit design decision (2026-10-08) for the compat layer that follows part of the PyMuPDF API specifically: code ported from PyMuPDF that reasons about rectangles and point positions should not also have to flip its y-axis math.
PyMuPDF is AGPL. Does PDFluent have the same licensing shape?
Yes. PDFluent ships the complete SDK under the AGPLv3, with a yearly commercial licence from €999 per organisation for teams that cannot publish their own source — the same open-source-or-commercial choice PyMuPDF offers through Artifex.

PyMuPDF and MuPDF are trademarks of Artifex Software, Inc. PDFluent is an independent product of Innovation Trigger B.V. and is not affiliated with or endorsed by Artifex.