# pdfium-render and the Rust path to a C++ PDF engine

> pdfium-render wraps the Pdfium library that Chromium uses for PDF rendering in an idiomatic Rust API, with runtime library binding so the same crate can target desktop, Android and WASM. The catch is that Pdfium is a C++ library you must supply, and the crate cannot rescue you from a bad PDF without help.

**ajrcarey/pdfium-render** — A high-level idiomatic Rust wrapper around Pdfium, the C++ PDF library used by the Google Chromium project.

- Repository: https://github.com/ajrcarey/pdfium-render
- Website: https://crates.io/crates/pdfium-render
- Stars: 716 · Forks: 134
- Language: Rust
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/ajrcarey-pdfium-render

## Pdfium does the work, the crate only wraps it

pdfium-render is described as an idiomatic high-level Rust interface to Pdfium, the C++ PDF library used by the Google Chromium project. What Pdfium itself can do is listed plainly: render pages in PDF files to bitmaps, load, edit and extract text and images from existing PDF files, and create new PDF files from scratch.

That list is the reason to use this crate rather than a pure-Rust PDF library. It is the same engine that renders PDFs in the browser, so a document that looks right in Chrome looks right in your service.

The wrapper sits on top rather than reimplementing. The Rust API handles errors in the idiomatic way, with Result types and a PdfiumError, and the underlying library is bound at run-time rather than compile-time. That single design decision shapes almost everything else about the crate, including the WASM story and the fact that you have to provide the library yourself.

The examples directory in the repository is the best catalogue of the surface area: separate files for annotations, attachments, concatenation, form fields, image extraction, links, paths, fonts, objects, export and export with clip and crop, plus one named custom_font_provider.rs, one for creating documents, and one that shows an axum integration with once_cell.

## Runtime binding is why the same crate reaches three platforms

pdfium-render binds to a Pdfium library at run-time, and the README names the advantage: a Rust application using pdfium-render can be compiled to WASM for running in a browser alongside a WASM-packaged build of Pdfium.

Compile-time binding would have meant a compiled-in native library, which is the wrong shape for a browser deployment. With run-time binding, the Rust code and the Pdfium binary are separate artefacts, and a WASM build of Pdfium can sit alongside.

The cost is that binding can fail. The crate provides idiomatic Rust error handling in situations where a Pdfium library is not available, which means your code has to handle the case where the library is simply missing on the machine.

The library itself is not included. The README lists three options: bind to a dynamically-built Pdfium library provided by the operating system, bind to a dynamically-built Pdfium library packaged alongside your Rust executable, or bind to a statically-built Pdfium library linked to your executable at compile time. For WASM, packaging an external build of Pdfium as a separate WASM module is described as essential rather than optional.

The dynamic linking section adds a platform wrinkle: on Android a pre-built libpdfium.so is packaged as part of the operating system, but recent versions of Android no longer permit user applications to access it.

## A first render, end to end

The README opens with a function that renders every page of a PDF to a separate JPEG file, and it reads as a good first use because it touches the main ideas: binding to a library, loading a document, configuring the render, and writing images out.

The example opens with the crate prelude and a function that takes a path and an optional password:

```rust
    use pdfium_render::prelude::*;
    fn export_pdf_to_jpegs(path: &impl AsRef<Path>, password: Option<&str>) -> Result<(), PdfiumError> {
```

Inside, a Pdfium::default() instance binds to the library and loads the document from the given path with the optional password. Rendering options are collected in a PdfRenderConfig, which is built fluently and applies to every page:

```rust
        let render_config = PdfRenderConfig::new()
            .set_target_width(2000)
            .set_maximum_height(2000)
```

Then each page is rendered, converted to an image, stripped of its alpha channel for JPEG compatibility, and saved with the image format enum:

```rust
        for (index, page) in document.pages().iter().enumerate() {
            page.render_with_config(&render_config)?
                .as_image()? // Renders this page to an image::DynamicImage...
                .into_rgb8() // ... removes the alpha channel for compatibility with the Jpeg format...
                .save_with_format(
                    format!("page-{}.jpg", index), 
                    image::ImageFormat::Jpeg
                ) // ... and saves the image to a file.
                .map_err(|_| PdfiumError::ImageError)?;
        }
```

Two details are worth noticing. The page index starts at zero, so the first file is page-0.jpg, and the image conversion is a chain of fallible steps that each get mapped into PdfiumError, with the save error collapsed to PdfiumError::ImageError.

## What the examples directory says about the feature list

The README enumerates what the examples demonstrate, and the list is a good map of the crate's real capabilities rather than a marketing summary.

There is rendering, both of whole pages and of portions of pages, and multi-page tiled rendering, plus watermarking. There is text and image extraction, and page object introspection, annotation introspection and link introspection. There is form field introspection and filling, and document signature introspection, plus document attachment creation and introspection.

There is also creation in the other direction: new documents and new pages, and page objects for text, paths and bitmaps, with page object transformation and custom font management. Document concatenation is there as well, which is the operation you want when you need to merge files without a round trip through an external tool.

Two entries in the list are about engineering properties rather than PDF features: thread safety, and compiling to WASM. The thread safety one is a crate feature, thread_safe, and release 0.9.4 explicitly improved memory safety when using it.

Watermarking and multi-page tiled rendering are also listed as examples, and the export_clip_crop.rs file name suggests the partial page rendering support is real rather than aspirational, which matches the set_origin function added in release 0.9.3 for partial page rendering.

## The crate tracks Pdfium release numbers through a feature flag

pdfium-render does not vendor Pdfium, so it has to be told which Pdfium it is talking to. That is done with a feature called pdfium_latest, and the release notes show it being moved forward each time upstream ships.

Release 0.9.1 increments pdfium_latest to pdfium_7763, matching the new Pdfium release 7763 published at the pdfium-binaries repository. Release 0.9.3 does the same thing, incrementing the feature to pdfium_7881 for a newer upstream release.

The practical consequence is that the crate version and the Pdfium version are two separate numbers that have to be kept in step. A deployment that upgrades the crate without matching the feature to the Pdfium build it ships can end up with a mismatch, and the mismatch is a compile-time or link-time problem rather than something the type system catches.

Other release notes give a sense of the level of care. Release 0.9.4 fixes two double-free errors, one in font handling and one in page object handling, and fixes a string handling problem with custom font path configurations, and it handles an edge case in the WASM bindings where memory page growth triggered by non-Rust code in the browser could affect memory handling. Release 0.9.3 fixes a build failure on targets where c_char is unsigned, which is the kind of portability bug that only shows up on unusual targets.

## Licensing, versions and the FFI boundary

The Cargo.toml declares the licence as MIT OR Apache-2.0, a dual licence, though the repository listing shows GitHub as unable to classify it, so the LICENSE.md file in the repository root is the text that governs. No legal advice is offered here beyond naming the identifier.

The package version is 0.9.4 and the crate is on version 0.x, so the API is not promised to be stable. The Rust edition is 2021 and the minimum supported Rust version is 1.61, which is a low floor and suggests the crate is usable on older toolchains.

The build setup is worth noting because it explains the include directory and build.rs in the repository root. The crate is built as a library, a staticlib and a cdylib, so it can be consumed as a normal Rust dependency or linked into a native binary. bindgen appears as an optional build dependency, and libloading is a dependency on non-wasm targets, which is the mechanism behind the run-time binding.

Image handling is deliberately version-flexible. Rather than a single image dependency, the Cargo.toml lists image_025, image_024 and image_023, three separate optional dependency entries for versions 0.25, 0.24 and 0.23 of the image crate, each with jpeg enabled and default features off. That lets a project pick whichever version of the image crate it already depends on, which is a detail that will save you a dependency conflict.

## Conclusion

pdfium-render is a good fit for a Rust service that must render, extract from or manipulate PDFs and wants the same engine Chromium uses rather than a pure-Rust reimplementation, since the crate covers rendering, text and image extraction, form fields, annotations, signatures and document creation. It is a poor fit for a project that cannot ship a native Pdfium build alongside its binary, and for a WASM deployment that has not planned how Pdfium itself will be packaged as a separate module. Before adopting, confirm which Pdfium build you will bind to, since the crate deliberately does not include Pdfium, and check the pdfium_latest feature against the Pdfium release you ship, because the crate tracks upstream release numbers through that feature.

## FAQ

### Does pdfium-render include Pdfium itself?

No. The README states that pdfium-render does not include Pdfium, and you can bind to a dynamically-built library provided by the operating system, one packaged alongside your Rust executable, or a statically-built library linked at compile time.

### How do I choose which Pdfium version pdfium-render uses?

Through the pdfium_latest crate feature, which is incremented to match a specific upstream Pdfium release. Release 0.9.1 set it to pdfium_7763 and release 0.9.3 set it to pdfium_7881.

### What can pdfium-render do with a PDF document?

Render pages and portions of pages to bitmaps, extract text and images, introspect and fill form fields, introspect document signatures, create and introspect document attachments, concatenate documents, introspect page objects, annotations and links, and create new documents, pages and page objects for text, paths and bitmaps.

### Can pdfium-render be used in a browser?

Yes. Because it binds to Pdfium at run-time rather than compile-time, a Rust application can be compiled to WASM and run alongside a WASM-packaged build of Pdfium, though packaging an external Pdfium build as a separate WASM module is described as essential.

## Sources

- [ajrcarey/pdfium-render on GitHub](https://github.com/ajrcarey/pdfium-render)
- [Issues](https://github.com/ajrcarey/pdfium-render/issues)
- [Project website](https://crates.io/crates/pdfium-render)
- [README](https://github.com/ajrcarey/pdfium-render/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ajrcarey-pdfium-render
