Boost PDF-to-Image Conversion in Kotlin with JNI and Rust: A Step-by-Step Guide

This tutorial explains how a Kotlin backend can efficiently convert PDF pages to high-resolution PNG images by integrating the native PDFium library through a Rust-based JNI adapter, covering performance benefits, workflow steps, and complete code examples for building and invoking the native module.

Architecture Development Notes
Architecture Development Notes
Architecture Development Notes
Boost PDF-to-Image Conversion in Kotlin with JNI and Rust: A Step-by-Step Guide

As a backend developer I have been using Kotlin for years, appreciating its concise and elegant syntax for clean code.

However, the JVM ecosystem sometimes lacks specific libraries or low-level memory control, which becomes an issue when converting each page of a PDF to high‑resolution PNG images.

Apache PDFBox works for simple cases but often yields inaccurate results, runs out of memory on large files, and takes about 15 seconds to convert 30 high‑resolution pages.

PDFium, the open‑source library behind Chrome’s PDF viewer, provides much more accurate results and can process the same 30 pages in roughly 2 seconds, but it is a native C++ library.

Using Java Native Interface (JNI) together with a small Rust adapter, we can integrate PDFium into a Kotlin (or Java) application.

This article shows how to extend your Kotlin/Java app with a native library like PDFium.

Why not just run a subprocess?

Running a subprocess is simple and safe because it isolates crashes and memory leaks, but JNI allows the native library to run inside the JVM process, offering higher performance, better memory utilization, simpler data exchange, callback support, and the fun of combining Kotlin with Rust.

Workflow

The integration involves the following steps:

Create a Kotlin “library” class that declares native methods.

Obtain PDFium binaries for the target platform, either by downloading pre‑compiled .so/.dylib/.dll files or building from source.

Write a Rust adapter that wraps PDFium calls using the pdfium-render crate and the jni crate.

Compile and link the Rust crate as a dynamic library ( .so, .dylib or .dll) while ensuring the PDFium binary is linked at runtime.

Load the native library from Kotlin with System.loadLibrary.

Call the native function from Kotlin code to extract PDF pages to PNG files.

1. Create Kotlin class

Define a file PdfiumLibrary.kt with an external fun extractPages(...) declaration.

package fluffy.tigerrr

class PdfiumLibrary {
    /**
     * Extract each page of a PDF to PNG images.
     * Returns the number of pages extracted, or -1 on error.
     */
    external fun extractPages(inputFile: String, outputDir: String, targetWidth: Int): Int
}

2. Obtain PDFium binary

Download the appropriate binary for your OS or build it from source following the official PDFium documentation.

3. Write Rust adapter

Add a Cargo.toml with dependencies:

[package]
name = "my-pdfium-bindings"
version = "0.1.0"
edition = "2021"

[dependencies]
pdfium-render = { version = "0.8.27", features = ["image", "thread_safe"] }
image = "^0"
jni = "0.21.1"

[lib]
crate-type = ["cdylib"]

Configure build.rs to link the PDFium library:

fn main() {
    println!("cargo:rustc-link-search=native=PATH_TO_PDFIUM_DYLIB_FILE");
    println!("cargo:rustc-link-lib=dylib=pdfium");
}

4. Compile Rust library

Run cargo build --release to produce a dynamic library ( .so, .dylib or .dll) and place it alongside the PDFium binary.

5. Load library in Kotlin

package fluffy.tigerrr

class PdfiumLibrary {
    external fun extractPages(inputFile: String, outputDir: String, targetWidth: Int): Int

    companion object {
        init {
            System.loadLibrary("my_pdfium_bindings")
        }
    }
}

6. Call native function

package fluffy.tigerrr

fun main() {
    val lib = PdfiumLibrary()
    for (i in 1..3) {
        val t1 = System.currentTimeMillis()
        val result = lib.extractPages("./test.pdf", "./result", 3000)
        val t2 = System.currentTimeMillis()
        println("Run #$i: extracted $result pages in ${(t2 - t1) / 1000.0} seconds")
    }
}

Running the program prints the number of pages extracted and the time taken for each run.

Conclusion

This guide demonstrates how to integrate PDFium into a Kotlin application via a Rust‑based JNI adapter, a technique applicable to any JVM language and other C‑compatible languages.

Even if this approach is not commonly used in production, understanding the options and mastering multiple tools is always valuable.

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.

RustKotlinNative integrationJNIPDF to PNGPDFium
Architecture Development Notes
Written by

Architecture Development Notes

Focused on architecture design, technology trend analysis, and practical development experience sharing.

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.