| name | pdfium-core-bindings-setup |
| description | Use when setting up the link between a Rust program and the PDFium library: choosing a binding strategy (dynamic, system, or static), wiring Cargo feature flags, sourcing prebuilt PDFium binaries, or fixing a binding that will not load. Prevents the most common pdfium-render startup failure, a PdfiumError::LoadLibraryError caused by a missing, misnamed, wrong-architecture, or API-version-mismatched library. Covers Pdfium::bind_to_library, bind_to_system_library, bind_to_statically_linked_library, Pdfium::default, the pdfium_* and image_* feature flags, per-platform library file names, and the PDFIUM_STATIC_LIB_PATH / PDFIUM_DYNAMIC_LIB_PATH build variables across pdfium-render 0.8.x and 0.9.x. Keywords: pdfium-render bindings, bind_to_library, bind_to_system_library, bind_to_statically_linked_library, Pdfium::default, LoadLibraryError, libpdfium.so, pdfium.dll, libpdfium.dylib, pdfium_latest feature flag, PDFIUM_STATIC_LIB_PATH, static linking, library not found, pdfium fails to load, missing symbols, how do I set up pdfium-render, why does pdfium crash on startup, where do I get PDFium binaries.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires pdfium-render 0.8,0.9. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
pdfium-core-bindings-setup
What this skill covers
pdfium-render does NOT build or bundle PDFium. It is a Rust wrapper that binds
to a separately compiled PDFium library at run time. Binding is the first
operation in every pdfium-render program: it produces a
Box<dyn PdfiumLibraryBindings>, which Pdfium::new wraps into the Pdfium
root handle. If binding fails, nothing else in the crate works.
This skill covers choosing a binding strategy, wiring the Cargo feature flags,
sourcing prebuilt PDFium binaries, placing the library file correctly per
platform, and the 0.8.x to 0.9.x setup traps. For the ownership tree and the
late-binding rationale see pdfium-core-architecture. For diagnosing a binding
that fails at run time see pdfium-errors-binding.
Quick reference
Three binding functions exist on the Pdfium struct. ALL are associated
functions (no self), and ALL return
Result<Box<dyn PdfiumLibraryBindings>, PdfiumError>:
| Function | Binds to | Requires |
|---|
Pdfium::bind_to_library(path) | a dynamic library at an explicit path | the file on disk |
Pdfium::bind_to_system_library() | a PDFium installed on the OS | PDFium on the system search path |
Pdfium::bind_to_statically_linked_library() | PDFium compiled into the executable | the static crate feature |
Two path helpers build platform-correct names:
Pdfium::pdfium_platform_library_name() -> OsString returns the bare file
name for the current platform (libpdfium.so, pdfium.dll, libpdfium.dylib).
Pdfium::pdfium_platform_library_name_at_path(path) -> PathBuf joins that
name onto a directory.
Pdfium::new(bindings) wraps a Box<dyn PdfiumLibraryBindings> into a Pdfium.
Decision tree: which binding strategy
Need WASM / browser target?
YES -> dynamic binding; bundle a separate WASM PDFium module
(use the paulocoutinhox build). See pdfium-impl-wasm.
NO -> Need a single self-contained executable, no sidecar files?
YES -> static linking (`static` feature + PDFIUM_STATIC_LIB_PATH)
NO -> Is PDFium already installed system-wide on every target?
YES -> bind_to_system_library()
NO -> dynamic binding with fallback (DEFAULT, simplest)
ALWAYS default to dynamic binding with fallback unless a requirement above
forces another path. It is the pattern every official example uses.
Minimal Cargo.toml
[dependencies]
pdfium-render = "0.9"
image = "0.25"
The default features are pdfium_latest, image_latest, and thread_safe.
pdfium_latest resolves to a specific PDFium API version (pdfium_7763 as of
0.9.1). See "The critical pinning rule" below.
Pattern: dynamic binding with fallback (default choice)
This is the canonical pattern from the pdfium-render README and
examples/export.rs. It tries a library next to the executable first, then
falls back to a system-installed PDFium:
use pdfium_render::prelude::*;
let bindings = Pdfium::bind_to_library(
Pdfium::pdfium_platform_library_name_at_path("./"),
)
.or_else(|_| Pdfium::bind_to_system_library())?;
let pdfium = Pdfium::new(bindings);
Pdfium::default() implements exactly this fallback (extended in 0.8.12 to also
try the current working directory). For the common case, write:
let pdfium = Pdfium::default();
ALWAYS bind PDFium once and reuse the Pdfium instance. NEVER re-bind per
request: re-loading the library on every call is slow (anti-pattern, issue #59).
For long-lived servers, store the bound Pdfium in a once_cell / OnceLock;
see pdfium-impl-performance.
The or_else fallback is the ONE sanctioned fallback in this skill, because each
arm is a distinct, documented binding strategy and the failure is reported
explicitly as a PdfiumError. Do NOT wrap binding in a silent match that
swallows the error: a failed bind must surface.
Pattern: system library only
When PDFium is guaranteed installed system-wide (a controlled server image, a
Linux distro package), bind directly:
use pdfium_render::prelude::*;
let pdfium = Pdfium::new(Pdfium::bind_to_system_library()?);
bind_to_system_library() searches the OS dynamic-library path. On Linux that
is LD_LIBRARY_PATH and the standard ld.so directories; on Windows the PATH
and the executable directory; on macOS the DYLD_* paths.
Pattern: static linking
Static linking produces a single self-contained executable with no sidecar
.so / .dll / .dylib. It requires the static crate feature:
pdfium-render = { version = "0.9", features = ["static"] }
use pdfium_render::prelude::*;
let pdfium = Pdfium::new(Pdfium::bind_to_statically_linked_library()?);
At build time, set PDFIUM_STATIC_LIB_PATH to the directory containing
libpdfium.a (pdfium.lib on Windows). The crate build.rs then emits
cargo:rustc-link-lib=static=pdfium and
cargo:rustc-link-search=native=$PDFIUM_STATIC_LIB_PATH.
Static builds also need a C++ standard library and, on macOS, CoreGraphics:
- Linux / GNU toolchain: add the
libstdc++ feature.
- LLVM toolchain: add the
libc++ feature.
- macOS: add the
core_graphics feature to resolve _CGBitmap / _CGContext.
NEVER omit the C++ runtime feature on a static build: the link step fails with a
wall of undefined references (issue #51). See references/anti-patterns.md.
Feature flags
| Group | Flags |
|---|
| Default | pdfium_latest, image_latest, thread_safe |
| PDFium API version | pdfium_latest, pdfium_future, explicit pins pdfium_7763 ... pdfium_5961 |
image crate version | image_latest, image_025, image_024, image_023 |
| Linking | static, libstdc++, libc++, core_graphics |
| Raw FFI bindings | bindings (regenerate the crate's raw Rust FFI bindings) |
| WASM | console_log (route logging to the browser console, added 0.9.1) |
| Compile-time PDFium feature gates | pdfium_use_skia, pdfium_use_win32, pdfium_enable_xfa, pdfium_enable_v8 |
The PDFium API-version pins and the compile-time gates were introduced in
0.8.24 and 0.8.25; the image_* selectors in 0.8.26. The full pin list and
introduction versions are in references/methods.md.
The critical pinning rule
The pdfium_* feature selects which PDFium API version the crate calls.
ALWAYS make this feature match the Chromium build number of the actual PDFium
binary you bind to. A mismatch produces missing-symbol errors or run-time
crashes, NOT a compile error.
To pin explicitly and disable thread safety for single-threaded throughput:
pdfium-render = { version = "0.9", default-features = false, features = [
"pdfium_7763", "image_025", "image",
] }
bblanchon/pdfium-binaries releases are tagged chromium/<number> (for example
chromium/7763). Match the number to the pdfium_<number> feature.
Per-platform library file names
The dynamic loader expects EXACTLY these names. A wrong name produces
PdfiumError::LoadLibraryError:
| Platform | Dynamic library | Static archive |
|---|
| Linux | libpdfium.so | libpdfium.a |
| macOS | libpdfium.dylib | libpdfium.a |
| Windows | pdfium.dll | pdfium.lib |
For dynamic binding, the simplest deployment places the platform library in the
same directory as the compiled executable and binds with
pdfium_platform_library_name_at_path("./"). ALWAYS use the path helpers rather
than hardcoding a file name: they pick the correct name per platform.
Build environment variables
| Variable | Purpose | Emits |
|---|
PDFIUM_STATIC_LIB_PATH | directory holding libpdfium.a | rustc-link-lib=static=pdfium + rustc-link-search |
PDFIUM_DYNAMIC_LIB_PATH | directory holding the dynamic library | rustc-link-lib=dylib=pdfium + rustc-link-search |
PDFIUM_STATIC_LIB_PATH_<triple> | per-target override (0.9.0+) | same, for one target triple |
ALWAYS point these variables at the directory, NEVER at the file itself.
For the per-target form, replace the hyphens in the target triple with
underscores, for example PDFIUM_STATIC_LIB_PATH_aarch64_apple_darwin.
Obtaining PDFium binaries
pdfium-render never builds PDFium. Source a prebuilt binary:
https://github.com/bblanchon/pdfium-binaries/releases: native builds for
Android, iOS, Linux (glibc and musl), macOS, Windows, plus an experimental
WASM build. Archives are named pdfium-<platform>-<arch>.tgz (for example
pdfium-linux-x64.tgz). Releases are tagged chromium/<number>.
https://github.com/paulocoutinhox/pdfium-lib/releases: Android, iOS, macOS,
and WASM builds. ALWAYS use this source for WASM: its WASM build uses a
growable heap, while the bblanchon WASM build has a non-growable heap and runs
out of memory on multi-page documents.
Version traps: 0.8.x to 0.9.x
| Item | Trap |
|---|
bind_to_library signature | Took / returned strings before 0.8.9; changed to AsRef<Path> / PathBuf in 0.8.9. Code written for 0.8.8 or earlier fails to compile against 0.9.x. |
Pdfium::default() | Before 0.8.12 it did not search the current working directory; on 0.8.11 and earlier, binding via default() may miss a library placed in the cwd. |
Pdfium::get_bindings() | Removed in 0.9.0. Accessing the raw bindings is covered by pdfium-core-raw-ffi. |
| Default API surface | This skill defaults to the 0.9.x API. Treat removed 0.8.x names as upgrade traps. |
The 0.9.0 release also marks all raw FPDF_* functions unsafe and implements
Send / Sync; those affect pdfium-core-raw-ffi and pdfium-core-memory,
not binding setup itself.
Common failures (quick triage)
| Symptom | Likely cause | Fix |
|---|
LoadLibraryError | library not next to executable and not on system path | place the correctly named file beside the binary, or install it system-wide |
LoadLibraryError | wrong file name for the platform | rename to libpdfium.so / pdfium.dll / libpdfium.dylib |
| crash or missing symbols at run time | pdfium_* feature does not match the binary's Chromium build | pin pdfium_<number> to the binary's chromium/<number> tag |
| undefined references at link time (static) | C++ runtime not linked | add libstdc++ or libc++ |
undefined _CGBitmap / _CGContext (static, macOS) | CoreGraphics not linked | add core_graphics |
| segfault loading any document | x64 binary on arm64 host, or glibc binary on musl | use the matching architecture and libc archive |
Full root-cause analysis lives in pdfium-errors-binding.
Reference files
references/methods.md: complete signatures for the binding functions, path
helpers, Pdfium::new / new_with_config / default, and the full feature
flag list with introduction versions.
references/examples.md: working, verified Rust setup code for dynamic,
system, and static binding, plus a OnceLock reuse example.
references/anti-patterns.md: real binding failures from the GitHub issue
tracker, why each fails, and the fix.
Companion skills
pdfium-core-architecture: the ownership tree and why late binding exists.
pdfium-errors-binding: diagnosing a binding that fails at run time.
pdfium-impl-wasm: binding and running PDFium in the browser.
pdfium-core-raw-ffi: reaching the raw PdfiumLibraryBindings trait.
pdfium-impl-performance: the bind-once OnceLock pattern for servers.