# Pdfium Impl Wasm

> Use when compiling a Rust application that uses pdfium-render to WebAssembly to run PDF rendering or inspection in a browser, when loading a PDF without a filesystem, or when diagnosing a WASM-only failure such as an out-of-memory crash, "Unable to locate wasmTable", or corrupted bitmap output. Prevents shipping the non-growable bblanchon WASM heap, calling filesystem loaders that do not exist in the browser, and using the memory-unsafe raw WASM bitmap buffer accessor. Covers the two-WASM-module architecture, bind_to_system_library in the browser, load_pdf_from_fetch and load_pdf_from_blob, the console_log feature, and safe bitmap buffer reads across pdfium-render 0.8.x and 0.9.x. Keywords: pdfium-render, WASM, WebAssembly, browser, wasm-pack, wasm-bindgen, load_pdf_from_fetch, load_pdf_from_blob, console_log, bind_to_system_library, paulocoutinhox, bblanchon, non-growable heap, out of memory, OOM, Unable to locate wasmTable, Module._malloc not defined, FPDFBitmap_GetBuffer, corrupted image, blank canvas in b

- Skill: `impertio-studio/pdfium-impl-wasm` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add impertio-studio/pdfium-impl-wasm`
- Raw SKILL.md: https://api.skillmd.com/api/skills/impertio-studio/pdfium-impl-wasm/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: MIT
- Author: Impertio-Studio (https://skillmd.com/u/impertio-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/impertio-studio/pdfium-impl-wasm

---


# pdfium-render: WebAssembly

Running pdfium-render in a browser. WASM is a first-class but delicate target:
PDFium is C++ and ships as its own WASM module, the browser has no filesystem, and
two of the most common pdfium-render failures only ever appear on WASM. This skill
owns the WASM target. General binding is `pdfium-core-bindings-setup`; general
document loading is `pdfium-syntax-document-loading`.

All API names below are verified against `docs.rs/pdfium-render` 0.9.1 and the
pdfium-render README. The default target is the 0.9.x API; 0.8.x differences are
flagged inline.

## The Two-Module Architecture

A pdfium-render WASM application is ALWAYS two separate WASM modules:

1. Your Rust application compiled to WASM (via `wasm-pack` / `wasm-bindgen`).
2. A prebuilt PDFium WASM module, packaged separately, NOT linked into module 1.

The PDFium WASM module must be loaded by the browser FIRST so its exported
functions exist. Your Rust module then binds to those exports at run time. This is
exactly why pdfium-render uses run-time binding: a compiled-in static PDFium is not
possible for the browser.

NEVER expect to bundle PDFium into your Rust `.wasm`. ALWAYS ship the PDFium WASM
module as a separate asset and load it before your application module.

## Quick Reference

| Goal | API / artifact | Notes |
|------|----------------|-------|
| Bind in the browser | `Pdfium::bind_to_system_library()` | binds to the separate PDFium WASM module |
| Load a PDF by URL | `Pdfium::load_pdf_from_fetch(url, password)` | `async`, browser only |
| Load a PDF from a Blob | `Pdfium::load_pdf_from_blob(blob, password)` | `async`, browser only |
| Route logs to the console | `console_log` crate feature | added 0.9.1 |
| Read a bitmap buffer (raw FFI) | `FPDFBitmap_GetBuffer_as_vec` / `_as_array` | NEVER raw `FPDFBitmap_GetBuffer` |
| PDFium WASM build | paulocoutinhox/pdfium-lib | growable heap |

Full signatures: see `references/methods.md`.

## Decision Tree

```
Which prebuilt PDFium WASM build?
  -> ALWAYS paulocoutinhox/pdfium-lib (growable heap)
  -> NEVER bblanchon WASM build (non-growable heap, OOMs past a few pages)

How does the user supply the PDF?
  -> from a URL          -> load_pdf_from_fetch(url, password).await
  -> from a <input file> -> get a Blob, load_pdf_from_blob(blob, password).await
  -> NEVER load_pdf_from_file: no filesystem in the browser

Need PDFium log output?
  -> enable the `console_log` feature

Dropping to raw FFI for bitmap bytes?
  -> FPDFBitmap_GetBuffer_as_vec / _as_array
  -> NEVER the raw FPDFBitmap_GetBuffer (memory-unsafe on WASM, issue #174)
```

## Pattern: Cargo.toml for a WASM Build

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

[dependencies]
pdfium-render = { version = "0.9", features = ["console_log"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = ["Blob"] }
```

- `crate-type = ["cdylib"]` is required for a `wasm-bindgen` target.
- `console_log` is the only pdfium-render feature specific to WASM; the default
  `pdfium_latest`, `image_latest`, and `thread_safe` features still apply.
- Build with the standard `wasm-bindgen` toolchain, for example
  `wasm-pack build --target web`. The canonical, end-to-end setup including the
  HTML harness is the `examples/` directory of the pdfium-render repository.

## Pattern: Bind in the Browser

In the browser `Pdfium::bind_to_system_library()` is the binding entry point. It
binds to the PDFium functions the separately loaded PDFium WASM module exported.

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

#[wasm_bindgen]
pub async fn render_first_page(url: String) -> Result<(), JsValue> {
    // The PDFium WASM module must already be loaded by the page before this runs.
    let bindings = Pdfium::bind_to_system_library()
        .map_err(|e| JsValue::from_str(&e.to_string()))?;
    let pdfium = Pdfium::new(bindings);

    let document = pdfium.load_pdf_from_fetch(url, None).await
        .map_err(|e| JsValue::from_str(&e.to_string()))?;

    let _ = document.pages().len();
    Ok(())
}
```

NEVER use `bind_to_library(path)` on WASM: there is no filesystem path to a `.so` /
`.dll` in the browser. NEVER use `bind_to_statically_linked_library()`: PDFium
cannot be statically linked into a browser WASM module.

## Pattern: Load a PDF by URL

`load_pdf_from_fetch` is the browser replacement for `load_pdf_from_file`. It is an
`async` function and fetches the PDF over HTTP:

```rust
pub async fn load_pdf_from_fetch<'a>(
    &'a self,
    url: impl ToString,
    password: Option<&str>,
) -> Result<PdfDocument<'a>, PdfiumError>
```

```rust
let document = pdfium.load_pdf_from_fetch("/files/report.pdf", None).await?;
```

`password` is the same uniform `Option<&str>` parameter as every other loader (see
`pdfium-syntax-document-loading`): pass `Some("secret")` for an encrypted PDF.

## Pattern: Load a PDF from a Blob

`load_pdf_from_blob` loads a PDF the user picked through a `<input type="file">`
element or produced in JavaScript. The blob is a `web_sys::Blob`:

```rust
pub async fn load_pdf_from_blob<'a>(
    &'a self,
    blob: Blob,
    password: Option<&str>,
) -> Result<PdfDocument<'a>, PdfiumError>
```

```rust
#[wasm_bindgen]
pub async fn open_uploaded(blob: web_sys::Blob) -> Result<(), JsValue> {
    let pdfium = Pdfium::new(
        Pdfium::bind_to_system_library()
            .map_err(|e| JsValue::from_str(&e.to_string()))?,
    );
    let document = pdfium.load_pdf_from_blob(blob, None).await
        .map_err(|e| JsValue::from_str(&e.to_string()))?;
    let _ = document.pages().len();
    Ok(())
}
```

## The console_log Feature

The `console_log` crate feature (added in 0.9.1) makes pdfium-render initialize
`console_log`-based logging to the browser developer console. Enable it when you
have not set up `console_log` yourself. It is purely a WASM diagnostics aid and has
no effect on native builds.

ALWAYS enable `console_log` while bringing up a WASM build: without it, binding and
load failures surface as opaque `JsValue` errors with no context in the console.

## Reading Bitmap Buffers Safely

Most code reads rendered pixels through the high-level `PdfBitmap` accessors
(`as_image()`, `as_rgba_bytes()`, `as_raw_bytes()`; see `pdfium-syntax-rendering`).
Those are safe on WASM. Only raw-FFI code touches the bitmap buffer directly.

On WASM the raw `FPDFBitmap_GetBuffer` accessor was memory-unsafe: it could
overwrite image data (issue #174). It is `unsafe` and superseded.

ALWAYS use the safe provided methods on `PdfiumLibraryBindings` when you must read a
bitmap buffer in raw FFI: `FPDFBitmap_GetBuffer_as_vec`,
`FPDFBitmap_GetBuffer_as_array`, or `FPDFBitmap_GetBuffer_as_slice`. NEVER call the
raw `FPDFBitmap_GetBuffer` on WASM. Raw FFI in general is `pdfium-core-raw-ffi`.

## Version Traps

| Item | 0.8.x | 0.9.x | Action |
|------|-------|-------|--------|
| `console_log` feature | absent | added 0.9.1 | enable for WASM diagnostics on 0.9.1+ |
| raw WASM `FPDFBitmap_GetBuffer` | memory-unsafe | superseded, `unsafe` | use `_as_vec` / `_as_array` |
| `load_pdf_from_bytes` | deprecated 0.7.26 | REMOVED 0.9.0 | use `load_pdf_from_byte_slice` / `_vec` |

## Anti-Patterns (summary)

- Shipping the bblanchon WASM PDFium build: non-growable heap, OOM crash past a few
  pages (issue #13). Use the paulocoutinhox build.
- Calling `load_pdf_from_file` on WASM: there is no browser filesystem.
- Version-mismatching your Rust module and the PDFium WASM build: produces
  "Unable to locate wasmTable" / "Module._malloc() not defined" (issues #134, #128,
  #95).
- Using the raw `FPDFBitmap_GetBuffer` on WASM (issue #174).
- Binding before the PDFium WASM module has loaded.

Each anti-pattern with the failure detail and fix: see `references/anti-patterns.md`.

## Cross-References

- `pdfium-core-bindings-setup`: the three bind functions, prebuilt-binary sources,
  feature flags. WASM uses `bind_to_system_library()`.
- `pdfium-syntax-document-loading`: the full loader family and the uniform
  `password: Option<&str>` parameter.
- `pdfium-syntax-rendering`: `PdfBitmap` and its safe pixel accessors.
- `pdfium-core-raw-ffi`: the `PdfiumLibraryBindings` trait and raw `FPDF_*` calls.
- `pdfium-errors-binding`: diagnosing a failed bind.
- `pdfium-impl-performance`: memory cost of rendering, relevant to the WASM heap.

## Reference Files

- `references/methods.md`: complete API signatures and feature flags.
- `references/examples.md`: verified Rust + WASM code.
- `references/anti-patterns.md`: real WASM failures, why they fail, the fix.

## Source

Verified 2026-05-20 against `https://docs.rs/pdfium-render/latest/pdfium_render/`
(crate root, `Pdfium` prelude page, `PdfiumLibraryBindings` trait page) and the
pdfium-render README WASM section. WASM anti-patterns (issues #13, #174, #134,
#128, #95) are recorded in vooronderzoek-pdfium.md sections 8 and 9.

