Compact Java PDF engine on top of Java 25 FFM.
- Open PDF from file path or byte array
- Probe and diagnose unreadable, corrupt, or password protected files
- Render pages to RGBA buffers at custom DPI
- Render bounded previews and thumbnails with memory caps
- Extract plain text and per character bounds
- Read page size, page count, labels, rotation, permissions, encryption state
- Read and write metadata from Info dictionary
- Extract raw XMP and parse structured XMP metadata
- Generate KOReader-compatible partial MD5 checksums for lightweight file identity
- Read bookmarks and links
- Read page annotations
- Delete pages, insert blank pages, import pages, save documents
- Save to path, byte array, or OutputStream
- No JNI bridge
- Direct FFM calls with explicit native resource ownership
- Deterministic close semantics for document and page handles
- Thread confinement for document and page operations
- Strict and recover policy modes
- Hard limits for document bytes, render pixels, and render worker count
- Typed exceptions with error code mapping
import java.nio.file.Path;
import org.grimmory.pdfium4j.PdfDocument;
import org.grimmory.pdfium4j.PdfPage;
import org.grimmory.pdfium4j.model.PdfProcessingPolicy;
import org.grimmory.pdfium4j.model.RenderResult;
PdfProcessingPolicy policy = PdfProcessingPolicy.defaultPolicy()
.withMaxDocumentBytes(512L * 1024 * 1024)
.withMaxRenderPixels(40_000_000L)
.withMaxParallelRenderThreads(8);
try (PdfDocument doc = PdfDocument.open(Path.of("book.pdf"), null, policy)) {
try (PdfPage page = doc.page(0)) {
RenderResult cover = page.renderThumbnail(512);
String text = page.extractText();
}
var diagnostics = PdfDocument.diagnose(Path.of("book.pdf"));
var checksum = PdfDocument.koReaderPartialMd5(Path.of("book.pdf"));
var xmp = doc.xmpMetadataString();
doc.save(Path.of("output.pdf"));
}The library ships as a core API JAR plus per-platform native classifier JARs. Add the core dependency and the classifier matching your target platform.
dependencies {
implementation("org.grimmory:pdfium4j:0.1.0")
runtimeOnly("org.grimmory:pdfium4j:0.1.0:natives-linux-x64")
}val osName = System.getProperty("os.name").lowercase()
val osArch = System.getProperty("os.arch")
val pdfiumNatives = when {
"linux" in osName && osArch == "amd64" -> "natives-linux-x64"
"linux" in osName && osArch == "aarch64" -> "natives-linux-arm64"
"mac" in osName && osArch == "aarch64" -> "natives-darwin-arm64"
"mac" in osName && osArch == "x86_64" -> "natives-darwin-x64"
"windows" in osName && osArch == "amd64" -> "natives-windows-x64"
else -> error("Unsupported platform: $osName/$osArch")
}
dependencies {
implementation("org.grimmory:pdfium4j:0.1.0")
runtimeOnly("org.grimmory:pdfium4j:0.1.0:$pdfiumNatives")
}dependencies {
implementation 'org.grimmory:pdfium4j:0.1.0'
runtimeOnly 'org.grimmory:pdfium4j:0.1.0:natives-linux-x64'
}<dependency>
<groupId>org.grimmory</groupId>
<artifactId>pdfium4j</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.grimmory</groupId>
<artifactId>pdfium4j</artifactId>
<version>0.1.0</version>
<classifier>natives-linux-x64</classifier>
</dependency>| Platform | Classifier |
|---|---|
| Linux x86_64 | natives-linux-x64 |
| Linux aarch64 | natives-linux-arm64 |
| macOS aarch64 | natives-darwin-arm64 |
| macOS x86_64 | natives-darwin-x64 |
| Windows x86_64 | natives-windows-x64 |
- Java 25+
- JVM flags:
--enable-preview --enable-native-access=ALL-UNNAMED
PDFium4j ships prebuilt PDFium binaries as classified JAR artifacts. When the correct native classifier JAR is on the classpath, the library extracts the shared library to a temp directory at startup and loads it automatically.
If classpath loading is unavailable, PDFium4j falls back to System.loadLibrary("pdfium").
| Platform | Classifier |
|---|---|
| Linux x86_64 | natives-linux-x64 |
| Linux aarch64 | natives-linux-arm64 |
| macOS aarch64 | natives-darwin-arm64 |
| macOS x86_64 | natives-darwin-x64 |
| Windows x86_64 | natives-windows-x64 |
pdfium4j/ Root (Java module)
src/main/java/ FFM bindings and public API
src/test/java/ Tests
build/generated-natives/ Downloaded PDFium binaries (build-time)
./gradlew buildUse Java 25 and run the same verification path as CI:
./gradlew clean check --warning-mode allThis runs formatting and static analysis gates (Spotless, Checkstyle, PMD, SpotBugs), plus tests.
For local iteration:
./gradlew spotlessApply
./gradlew pmdMain pmdTest spotbugsMain spotbugsTestApache License 2.0. See LICENSE.