Why Apache PDFBox Is Becoming the Go-To Java PDF Library

This article provides a comprehensive guide to Apache PDFBox, covering its licensing advantages, latest version, installation steps, core APIs for creating, editing, extracting, merging, encrypting and signing PDFs, underlying architecture, migration tips, performance considerations, and real‑world use cases.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Why Apache PDFBox Is Becoming the Go-To Java PDF Library

Introduction

Apache PDFBox is an open‑source Java library for processing PDF documents. It is maintained by the Apache Software Foundation and released under the Apache 2.0 license.

As of July 2026 the latest version is 3.0.8 .

What PDFBox Is

PDFBox provides a one‑stop solution for PDF handling, including:

Create new PDF files

Edit existing PDFs

Extract text, images and metadata

Merge, split, encrypt, decrypt and sign PDFs

Why Choose PDFBox?

License: Apache 2.0 permits free use, modification and distribution in closed‑source commercial products, unlike iText’s AGPL.

Active Apache top‑level project: continuous releases (3.0.6, 3.0.7, 3.0.8 in 2026).

Feature completeness: covers text extraction, digital signatures, merging, encryption, etc.

Environment Setup (≈5 minutes)

Adding the Dependency

PDFBox 3.x requires Java 8+.

<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);
        }
    }
}

Key note: PDDocument.load() was removed in 3.0; use Loader.loadPDF() instead.

Extract a specific page range:

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

Core Operations – From Basics to Advanced

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
            cs.showText("Hello, PDFBox!");
            cs.endText();
            cs.close();
            document.save("HelloPDFBox.pdf");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Coordinate system: origin is at the lower‑left corner; X increases to the right, Y upwards.

Handling multiple lines:

cs.setLeading(14.5f);
cs.showText("First line");
cs.newLine();
cs.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 doc = Loader.loadPDF(new File("sample.pdf"))) {
            for (int i = 0; i < doc.getNumberOfPages(); i++) {
                PDPage page = doc.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());
                    }
                }
            }
        }
    }
}

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 a PDF

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 doc = Loader.loadPDF(new File("sample.pdf"))) {
            AccessPermission ap = new AccessPermission();
            ap.setCanPrint(true);
            ap.setCanModify(false);
            StandardProtectionPolicy spp = new StandardProtectionPolicy(
                "user_password", "owner_password", ap);
            spp.setEncryptionKeyLength(128);
            doc.protect(spp);
            doc.save("encrypted.pdf");
        }
    }
}

Fill an AcroForm

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 doc = Loader.loadPDF(new File("form.pdf"))) {
            PDAcroForm form = doc.getDocumentCatalog().getAcroForm();
            PDTextField name = (PDTextField) form.getField("name");
            if (name != null) name.setValue("张三");
            PDTextField email = (PDTextField) form.getField("email");
            if (email != null) email.setValue("[email protected]");
            doc.save("filled_form.pdf");
        }
    }
}

Underlying Principles

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

Understanding the low‑level mechanics clarifies capability limits and performance bottlenecks.

Physical Structure of a PDF

File header – version marker (e.g., %PDF-1.7)

File body – stores all objects (pages, images, fonts, annotations)

Cross‑reference table – enables fast object lookup

File trailer – points to the cross‑reference table and the document root

PDF physical structure
PDF physical structure

PDFBox’s Two‑Layer Architecture

COS layer (low‑level): Direct mapping of PDF primitives such as COSDictionary, COSArray, COSStream. Provides full access to the file’s internals.

PD layer (high‑level): Developer‑friendly APIs – PDDocument (whole document), PDPage (a page), PDPageContentStream (content stream). Enables common tasks while allowing a drop‑down to the COS layer for fine‑grained control.

Three‑Tier Content Processing Pipeline

First tier – PDFStreamEngine (basic processing): Parses content streams into token sequences, dispatches operations, maintains graphic state and resources.

Second tier – PDFGraphicsStreamEngine (graphics abstraction): Defines abstract methods for path construction, filling, stroking and image drawing – describes *what* to draw, not *how*.

Third tier – Implementation layer: Concrete classes such as PageDrawer (renders to Graphics2D) and PDFTextStripper (extracts text). PDFTextStripper overrides showGlyph() to collect glyph positions.

This design gives PDFBox excellent extensibility; developers can subclass PDFStreamEngine for custom analysis or rendering.

Common Issues and Pitfalls

Migration from 2.x to 3.x

PDDocument.load() removed: replace with Loader.loadPDF().

IO module refactor: new Loader class and MemoryUsageSetting for memory control.

// PDFBox 2.x
PDDocument doc = PDDocument.load(new File("sample.pdf"));
// PDFBox 3.x
PDDocument doc = Loader.loadPDF(new File("sample.pdf"));

Chinese Font Support

The default PDType1Font does not render Chinese characters. Load a TrueType font manually:

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

Large‑File Performance

When using Loader.loadPDF(), pass a MemoryUsageSetting to limit RAM consumption.

Process the document page‑by‑page for very large files.

Use try‑with‑resources to ensure streams are closed promptly.

Advantages

Apache 2.0 license – free for commercial closed‑source use.

Comprehensive feature set (text extraction, digital signatures, merging, encryption, etc.).

Active Apache top‑level project with frequent releases.

Intuitive API – easy for developers without prior PDF experience.

Command‑line tools for common tasks.

Pure Java implementation – runs on any Java‑compatible platform.

Disadvantages

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

Table extraction is not built‑in; external tools such as Tabula‑java are required.

Chinese text requires manual font loading.

Version 3.0 introduces breaking API changes that need careful migration.

Recommended Scenarios

Document management systems – create, edit and extract PDF content.

Automated report generation – produce complex PDFs with charts and tables.

Contract/invoice parsing – extract structured data under a commercial‑friendly license.

E‑book readers – support text search and content extraction.

Digital signatures – standards‑compliant signing.

PDF encryption/decryption – dual‑password protection.

PDF merging/splitting – use PDFMergerUtility.

References

GitHub 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.

JavaPDF extractionOpen-source librariesApache PDFBoxPDF processingPDF mergingPDF encryption
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

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.