Idiomatic Rust bindings for Pdfium

pdfium-render provides an idiomatic high-level Rust interface to Pdfium, the C++ PDF library used by the Google Chromium project. With this library, you can 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.

```rust use pdfium_render::prelude::*;

fn export_pdf_to_jpegs(path: &str, password: Option<&str>) -> Result<(), PdfiumError> {
    // Renders each page in the given test PDF file to a separate JPEG file.

    // Bind to a Pdfium library in the same directory as our Rust executable;
    // failing that, fall back to using a Pdfium library provided by the operating system.

    let pdfium = Pdfium::new(
        Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
            .or_else(|_| Pdfium::bind_to_system_library())?,
    );

    // Load the document...

    let document = pdfium.load_pdf_from_file(path, password)?;

    // ... set rendering options that will be applied to all pages...

    let render_config = PdfRenderConfig::new()
        .set_target_width(2000)
        .set_maximum_height(2000)
        .rotate_if_landscape(PdfBitmapRotation::Degrees90, true);

    // ... then render each page to a bitmap image, saving each image to a JPEG file.

    for (index, page) in document.pages().iter().enumerate() {
        page.render_with_config(&render_config)?
            .as_image() // Renders this page to an image::DynamicImage...
            .as_rgba8() // ... then converts it to an image::Image...
            .ok_or(PdfiumError::ImageError)?
            .save_with_format(
                format!("test-page-{}.jpg", index), 
                image::ImageFormat::Jpeg
            ) // ... and saves it to a file.
            .map_err(|_| PdfiumError::ImageError)?;
    }

    Ok(())
}

```

pdfium-render binds to a Pdfium library at run-time, allowing for flexible selection of system-provided or bundled Pdfium libraries and providing idiomatic Rust error handling in situations where a Pdfium library is not available. A key advantage of binding to Pdfium at run-time rather than compile-time is that a Rust application using pdfium-render can be compiled to WASM for running in a browser alongside a WASM-packaged build of Pdfium.

pdfium-render aims to eventually provide bindings to all non-interactive functionality provided by Pdfium. This is a work in progress that will be completed by version 1.0 of this crate.

Examples

Short, commented examples that demonstrate all the major Pdfium document handling features are available at https://github.com/ajrcarey/pdfium-render/tree/master/examples. These examples demonstrate:

What's new

Version 0.7.16 adds additional convenience functions for quickly creating stand-alone cubic Bézier path page objects and adding them to a mutable collection of page objects.

Version 0.7.15 adds additional functions for working with page annotations, including retrieval of annotation names, author names, comments, creation and modification timestamps, and text and character ranges linked to annotations.

Version 0.7.14 adds compatibility with web workers to the WASM implementation of pdfium-render, thanks to an excellent contribution from https://github.com/NyxCode.

Version 0.7.13 adds transformation functions to PdfRenderConfig, allowing a page to be optionally moved, scaled, rotated, and skewed during rendering, and adds clipping support to PdfRenderConfig, allowing for rendering of only a portion of a page.

Binding to Pdfium

pdfium-render does not include Pdfium itself. You have several options:

When compiling to WASM, packaging an external build of Pdfium as a separate WASM module is essential.

Dynamic linking

Binding to a dynamically-built Pdfium library is the simplest option. On Android, a system-provided libpdfium.so is packaged as part of the operating system (although recent versions of Android no longer permit user applications to access it); alternatively, you can package a pre-built dynamic library appropriate for your operating system alongside your Rust executable.

If you are compiling a native (i.e. non-WASM) build, and you place an appropriate Pdfium library in the same folder as your compiled application, then binding to it dynamically at runtime is as simple as:

```rust use pdfium_render::prelude::*;

let pdfium = Pdfium::new(
    Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./")).unwrap()
);

```

A common pattern used in the examples at https://github.com/ajrcarey/pdfium-render/tree/master/examples is to first attempt to bind to a Pdfium library in the same folder as the compiled example, and attempt to fall back to a system-provided library if that fails:

```rust use pdfium_render::prelude::*;

let pdfium = Pdfium::new(
    Pdfium::bind_to_library(Pdfium::pdfium_platform_library_name_at_path("./"))
        .or_else(|_| Pdfium::bind_to_system_library())
        .unwrap() // Or use the ? unwrapping operator to pass any error up to the caller
);

```

At the time of writing, the WASM builds at https://github.com/bblanchon/pdfium-binaries/releases are compiled with a non-growable WASM heap memory allocator. This means that attempting to open a PDF document longer than just a few pages will result in an unrecoverable out of memory error. The WASM builds at https://github.com/paulocoutinhox/pdfium-lib/releases are recommended as they do not have this problem.

Static linking

If you prefer to link Pdfium directly into your executable at compile time, use the optional static crate feature. This enables the Pdfium::bind_to_statically_linked_library() function which binds directly to the Pdfium functions included in your executable:

```rust use pdfium_render::prelude::*;

let pdfium = Pdfium::new(Pdfium::bind_to_statically_linked_library().unwrap());

```

As a convenience, pdfium-render can instruct cargo to link a statically-built Pdfium library for you. Set the path to the directory containing your pre-built library using the PDFIUM_STATIC_LIB_PATH environment variable when you run cargo build, like so:

rust PDFIUM_STATIC_LIB_PATH="/path/containing/your/static/pdfium/library" cargo build

pdfium-render will pass the following flags to cargo:

rust cargo:rustc-link-lib=static=pdfium cargo:rustc-link-search=native=$PDFIUM_STATIC_LIB_PATH

This saves you writing a custom build.rs yourself. If you have your own build pipeline that links Pdfium statically into your executable, simply leave the PDFIUM_STATIC_LIB_PATH environment variable unset.

Note that the path you set in PDFIUM_STATIC_LIB_PATH should not include the filename of the library itself; it should just be the path of the containing directory. You must make sure your statically-built library is named in the appropriate way for your target platform (libpdfium.a on Linux and macOS, for example) in order for the Rust compiler to locate it.

pdfium-render will not build Pdfium for you; you must build Pdfium yourself, or source a pre-built static archive from elsewhere.

Compiling to WASM

See https://github.com/ajrcarey/pdfium-render/tree/master/examples for a full example that shows how to bundle a Rust application using pdfium-render alongside a pre-built Pdfium WASM module for inspection and rendering of PDF files in a web browser.

The Pdfium::load_pdf_from_file() and Pdfium::load_pdf_from_reader() functions are not available when running in the browser. The Pdfium::load_pdf_from_bytes() function is available, and the following additional functions are provided:

The PdfDocument::save_to_file() function is not available when running in the browser. The PdfDocument::save_to_bytes() and PdfDocument::save_to_writer() functions are available, and the following additional function is provided:

The following additional functions are provided during rendering:

The PdfFont::load_type1_from_file() and PdfFont::load_true_type_from_file() functions are not available when running in the browser. The following additional functions are provided:

Multithreading

Pdfium makes no guarantees about thread safety and should be assumed not to be thread safe. The Pdfium authors specifically recommend that parallel processing, not multi-threading, be used to process multiple documents simultaneously.

pdfium-render achieves thread safety by locking access to Pdfium behind a mutex; each thread must acquire exclusive access to this mutex in order to make any call to Pdfium. This has the effect of sequencing all calls to Pdfium as if they were single-threaded, even when using pdfium-render from multiple threads. This approach offers no performance benefit, but it ensures that Pdfium will not crash when running as part of a multi-threaded application.

An example of safely using pdfium-render as part of a multithreaded parallel iterator is available at https://github.com/ajrcarey/pdfium-render/tree/master/examples.

Optional features

This crate provides the following optional features:

The thread_safe feature is enabled by default. All other features are disabled by default.

Porting existing Pdfium code from other languages

The high-level idiomatic Rust interface provided by pdfium-render is built on top of raw FFI bindings defined in the PdfiumLibraryBindings trait. It is completely feasible to use these raw FFI bindings directly if you wish, making porting existing code that calls FPDF_* functions trivial while still gaining the benefits of late binding and WASM compatibility. For instance, the following code snippet (taken from a C++ sample):

```cpp string test_doc = "test.pdf";

FPDF_InitLibrary();
FPDF_DOCUMENT doc = FPDF_LoadDocument(test_doc, NULL);
// ... do something with doc
FPDF_CloseDocument(doc);
FPDF_DestroyLibrary();

```

would translate to the following Rust code:

```rust let bindings = Pdfium::bindtosystem_library().unwrap();

let test_doc = "test.pdf";

bindings.FPDF_InitLibrary();
let doc = bindings.FPDF_LoadDocument(test_doc, None);
// ... do something with doc
bindings.FPDF_CloseDocument(doc);
bindings.FPDF_DestroyLibrary();

```

Pdfium's API uses three different string types: classic C-style null-terminated char arrays, UTF-8 byte arrays, and a UTF-16LE byte array type named FPDF_WIDESTRING. For functions that take a C-style string or a UTF-8 byte array, pdfium-render's binding will take the standard Rust &str type. For functions that take an FPDF_WIDESTRING, pdfium-render exposes two functions: the vanilla FPDF_*() function that takes an FPDF_WIDESTRING, and an additional FPDF_*_str() helper function that takes a standard Rust &str and converts it internally to an FPDF_WIDESTRING before calling Pdfium. Examples of functions with additional _str() helpers include FPDFBookmark_Find(), FPDFAnnot_SetStringValue(), and FPDFText_SetText().

The PdfiumLibraryBindings::get_pdfium_utf16le_bytes_from_str() and PdfiumLibraryBindings::get_string_from_pdfium_utf16le_bytes() utility functions are provided for converting to and from FPDF_WIDESTRING in your own code.

Development status

The initial focus of this crate was on rendering pages in a PDF file; consequently, FPDF_* functions related to page rendering were prioritised. By 1.0, the functionality of all FPDF_* functions exported by all Pdfium modules will be available, with the exception of certain functions specific to interactive scripting, user interaction, and printing.

By version 0.8.0, pdfium-render should provide useful coverage for the vast majority of common use cases, whether rendering existing documents or creating new ones.

There are 368 FPDF_* functions in the Pdfium API. As of version 0.7.16, 270 (73%) have bindings available in pdfium-render, with the functionality of roughly three-quarters of these available via the pdfium-render high-level interface.

Some functions and type definitions in the high-level interface have been renamed or revised since their initial implementation. The initial implementations are still available but are marked as deprecated. These deprecated items will be removed in release 0.9.0.

If you need a binding to a Pdfium function that is not currently available, just raise an issue.

Version history