Why More Developers Choose Apache PDFBox for PDF Processing

This article provides a comprehensive guide to Apache PDFBox, covering its licensing advantages, architecture, installation, core operations such as creating, editing, extracting, merging, encrypting PDFs, migration tips from 2.x to 3.x, performance considerations, and recommended use cases for Java backend development.

SpringMeng
SpringMeng
SpringMeng
Why More Developers Choose Apache PDFBox for PDF Processing

What is PDFBox?

Apache PDFBox is an open‑source Java library for processing PDF documents. It can create new PDFs, edit existing PDFs, extract text, images and metadata, and merge, split, encrypt, decrypt and sign PDFs.

Environment setup (≈5 minutes)

Add the dependency

PDFBox 3.x requires Java 8+. Maven:

<dependency>
  <groupId>org.apache.pdfbox</groupId>
  <artifactId>pdfbox</artifactId>
  <version>3.0.8</version>
</dependency>

Gradle:

implementation 'org.apache.pdfbox:pdfbox:3.0.8'

First program – extract text

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import java.io.File;
import java.io.IOException;

public class ExtractText {
    public static void main(String[] args) throws IOException {
        File file = new File("sample.pdf");
        try (PDDocument document = Loader.loadPDF(file)) {
            PDFTextStripper stripper = new PDFTextStripper();
            String text = stripper.getText(document);
            System.out.println(text);
        }
    }
}

Note: PDFBox 3.0 removed PDDocument.load(); use Loader.loadPDF() instead.

Extract a page range

try (PDDocument document = Loader.loadPDF(new File("sample.pdf"))) {
    PDFTextStripper stripper = new PDFTextStripper();
    stripper.setStartPage(2); // start from page 2
    stripper.setEndPage(4);   // end at page 4
    String text = stripper.getText(document);
    System.out.println(text);
}

Core operations

Create a PDF document

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

public class CreatePDF {
    public static void main(String[] args) {
        try (PDDocument document = new PDDocument()) {
            PDPage page = new PDPage();
            document.addPage(page);
            PDPageContentStream cs = new PDPageContentStream(document, page);
            cs.setFont(PDType1Font.HELVETICA_BOLD, 12);
            cs.beginText();
            cs.newLineAtOffset(100, 700); // X=100, Y=700 from lower‑left corner
            cs.showText("Hello, PDFBox!");
            cs.endText();
            cs.close();
            document.save("HelloPDFBox.pdf");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The coordinate origin is the lower‑left corner; X increases to the right, Y upward.

Multi‑line text handling:

contentStream.setLeading(14.5f); // line spacing
contentStream.showText("First line");
contentStream.newLine();
contentStream.showText("Second line");

Extract images

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.cos.COSName;
import java.io.File;
import java.io.IOException;

public class ExtractImages {
    public static void main(String[] args) throws IOException {
        try (PDDocument document = Loader.loadPDF(new File("sample.pdf"))) {
            for (int i = 0; i < document.getNumberOfPages(); i++) {
                PDPage page = document.getPage(i);
                PDResources res = page.getResources();
                for (COSName name : res.getXObjectNames()) {
                    if (res.isImageXObject(name)) {
                        PDImageXObject img = (PDImageXObject) res.getXObject(name);
                        System.out.println("Found image: " + name.getName());
                        // img.getImage() returns a BufferedImage you can save
                    }
                }
            }
        }
    }
}

Merge PDFs

import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import java.io.File;
import java.io.IOException;

public class MergePDFs {
    public static void main(String[] args) throws IOException {
        PDFMergerUtility merger = new PDFMergerUtility();
        merger.addSource(new File("file1.pdf"));
        merger.addSource(new File("file2.pdf"));
        merger.addSource(new File("file3.pdf"));
        merger.setDestinationFileName("merged.pdf");
        merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
    }
}

Encrypt PDFs

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import java.io.File;
import java.io.IOException;

public class EncryptPDF {
    public static void main(String[] args) throws IOException {
        try (PDDocument document = Loader.loadPDF(new File("sample.pdf"))) {
            AccessPermission ap = new AccessPermission();
            ap.setCanPrint(true);   // allow printing
            ap.setCanModify(false); // disallow modification
            StandardProtectionPolicy spp = new StandardProtectionPolicy(
                "user_password",   // user password
                "owner_password", // owner password
                ap);
            spp.setEncryptionKeyLength(128);
            document.protect(spp);
            document.save("encrypted.pdf");
        }
    }
}

Fill PDF forms

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import java.io.File;
import java.io.IOException;

public class FillForm {
    public static void main(String[] args) throws IOException {
        try (PDDocument document = Loader.loadPDF(new File("form.pdf"))) {
            PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
            PDTextField nameField = (PDTextField) acroForm.getField("name");
            if (nameField != null) {
                nameField.setValue("Zhang San");
            }
            PDTextField emailField = (PDTextField) acroForm.getField("email");
            if (emailField != null) {
                emailField.setValue("[email protected]");
            }
            document.save("filled_form.pdf");
        }
    }
}

Underlying architecture

How does PDFBox turn a binary PDF file into readable text and images?

Physical structure of a PDF

Header – version information (e.g. %PDF-1.7)

Body – objects such as pages, images, fonts, annotations

Cross‑reference table – enables fast object location

Trailer – points to the cross‑reference table and the document root

PDF structure diagram
PDF structure diagram

PDFBox two‑layer architecture

PDFBox separates low‑level PDF objects (COS layer) from high‑level APIs (PD layer).

COS layer maps directly to PDF objects such as COSDictionary, COSArray, COSStream. The PD layer provides developer‑friendly classes like PDDocument, PDPage, PDPageContentStream. This design lets you start with the PD layer for common tasks and drop down to the COS layer when full control is needed.

Three processing layers:

Base layer ( PDFStreamEngine) – parses content streams into tokens.

Graphics abstraction layer ( PDFGraphicsStreamEngine) – defines drawing operations.

Implementation layer ( PageDrawer, PDFTextStripper) – concrete rendering and text extraction.

For text extraction, PDFTextStripper overrides showGlyph() to collect character positions.

The architecture is highly extensible; you can subclass PDFStreamEngine to implement custom analysis or rendering.

PDFBox architecture diagram
PDFBox architecture diagram

Common issues and pitfalls

Migration from 2.x to 3.x

PDDocument.load()

removed – use Loader.loadPDF().

IO module refactored – new Loader class introduced.

Chinese text support

Default PDType1Font does not support Chinese. Load a TrueType font manually:

PDType0Font font = PDType0Font.load(document, new File("simsun.ttf"));
contentStream.setFont(font, 12);

Large‑file performance

Use Loader.loadPDF() with a MemoryUsageSetting to control memory consumption.

Process very large files page‑by‑page.

Employ try‑with‑resources to ensure timely release of resources.

Pros and cons

Advantages

Apache 2.0 license – commercial‑friendly.

All‑in‑one feature set (create, edit, extract, sign, merge, etc.).

Active Apache top‑level project with frequent releases.

Intuitive API – easy for newcomers.

Command‑line tools for quick tasks.

Pure Java implementation – runs anywhere Java runs.

Disadvantages

Performance on very large or complex PDFs can be slower than commercial libraries.

Table extraction is not built‑in; external tools like Tabula‑java are needed.

Chinese characters require manual font loading.

Version 3.0 introduces breaking API changes.

Recommended scenarios

Document management systems – strong recommendation (create, edit, extract).

Automated report generation – strong recommendation (generate complex PDFs with charts and tables).

Invoice/contract parsing – strong recommendation (extract structured data under a commercial‑friendly license).

E‑book readers – recommended (text search and content extraction).

Digital signatures – recommended (PDF‑standard signing).

PDF merge/split – recommended (use PDFMergerUtility).

Final thoughts

Apache PDFBox, as an Apache top‑level project under the Apache 2.0 license, gives developers maximum freedom to use, modify and ship the library even in closed‑source commercial products. It covers creation, editing, extraction, encryption, signing and merging with a straightforward API.

Performance on massive files and native table extraction are its main limitations, but for most PDF processing needs PDFBox is more than sufficient.

Source code repository: https://github.com/apache/pdfbox

Official documentation: https://pdfbox.apache.org

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaopen-sourceapachepdf-manipulationpdfboxpdf-processingpdfbox-3.0
SpringMeng
Written by

SpringMeng

Focused on software development, sharing source code and tutorials for various systems.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.