# How PDFium Is Integrated into the pdf-inspector OCR Rendering Pipeline

> Discover how pdf-inspector integrates PDFium as its rasterization engine using a Rust wrapper to convert PDF pages into images for OCR backends. Learn about the OCR rendering pipeline.

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: internals
- Published: 2026-09-02

---

**pdf-inspector integrates Google PDFium as its rasterisation engine, converting PDF pages into images via a Rust wrapper before feeding them to OCR backends.**

The [firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector) repository relies on PDFium—a lightweight, open-source PDF rendering library—to bridge the gap between PDF documents and optical character recognition. This integration enables headless, high-throughput processing of both native and scanned PDFs without external viewer dependencies.

## PDFium's Role in the OCR Pipeline

PDFium serves as the **rendering backbone** in a four-stage pipeline. Unlike PDF parsers that extract embedded text directly, pdf-inspector renders each page to a bitmap when OCR is required. This ensures consistent handling of scanned documents, mixed-content PDFs, and files with corrupted text layers.

The rasterisation step produces ARGB bitmaps at configurable DPI, which the OCR engine then processes. PDFium's direct access to PDF byte streams eliminates the need for temporary files or external processes, making the pipeline suitable for serverless and CI/CD environments.

## Core Integration Architecture in `src/vision/`

The PDFium integration spans five tightly-coupled modules under `src/vision/`:

| Component | Responsibility | Primary Source File |
|-----------|---------------|---------------------|
| **PDFium wrapper** | Safe Rust interface to PDFium's C API; exposes `render_page()` returning `ImageBuffer` | [`src/vision/pdfium.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pdfium.rs) |
| **Render orchestrator** | Normalises PDFium output (pixel format, DPI) into `RenderedPage` structs | [`src/vision/render.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/render.rs) |
| **Pipeline coordinator** | Chains download → rendering → OCR into `PdfDocument → PdfPage → RenderedPage → OcrResult` | [`src/vision/pipeline.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pipeline.rs) |
| **Contracts** | Defines `RenderRequest`, `RenderResponse`, `OcrRequest`, `OcrResponse` for internal/external APIs | [`src/vision/contracts.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/contracts.rs) |
| **Downloader** | Fetches remote or embedded PDFs as local byte slices for PDFium consumption | [`src/vision/download.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/download.rs) |

## PDFium Rendering Flow

The pipeline executes in four sequential stages:

1. **Download** – `download::download_pdf()` retrieves PDF bytes from URLs or streams
2. **PDFium rendering** – `process_pdf()` instantiates `PdfiumRenderer` and calls `render_page(page_idx, dpi)` for each page
3. **OCR execution** – Rendered images pass to `OcrEngine::recognize()` (Tesseract or equivalent)
4. **Merge and output** – OCR results combine with native text extraction for final markdown/JSON

In [`src/vision/pdfium.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pdfium.rs), the wrapper handles **one-time library loading** and document lifecycle management. This design amortises PDFium initialisation cost across multiple pages and concurrent requests.

## Key Source Files Deep-Dive

### [`src/vision/pdfium.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pdfium.rs) – The PDFium Rust Wrapper

This file implements `PdfiumRenderer`, a safe abstraction over `pdfium::Pdfium::render_page`. Key responsibilities:

- Dynamic library loading and symbol resolution
- Document opening from byte slices (no file I/O required)
- Page rasterisation with DPI control
- Bitmap conversion from PDFium's native format to `image::DynamicImage`

The wrapper returns ARGB buffers that downstream modules consume without PDFium-specific handling.

### [`src/vision/render.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/render.rs) – Normalisation Layer

Converts raw PDFium output into the crate's standard `RenderedPage` type. Handles:

- Pixel format standardisation (RGBA → RGB or grayscale as needed)
- DPI validation and clamping
- Metadata attachment (page index, dimensions, render timestamp)

### [`src/vision/pipeline.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pipeline.rs) – Orchestration Core

Implements `process_pdf()`, the primary entry point. This function:

1. Creates a `PdfiumRenderer` instance
2. Iterates pages, invoking `render_page()` for each
3. Routes `RenderedPage` structs to the OCR subsystem in [`src/vision/oar.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/oar.rs)
4. Aggregates `OcrResult` objects into the final response

Error handling covers PDFium load failures, render timeouts, and malformed page structures.

## Practical Code Examples

### Full Pipeline: URL to OCR Results

```rust
use pdf_inspector::vision::{
    download::download_pdf,
    pipeline::process_pdf,
    contracts::{RenderRequest, RenderResponse},
};

fn ocr_from_url(url: &str) -> Result<RenderResponse, anyhow::Error> {
    // Fetch PDF bytes
    let pdf_bytes = download_pdf(url)?;

    // Configure render request (default 150 DPI balances speed and accuracy)
    let request = RenderRequest {
        data: pdf_bytes,
        dpi: 150,
        ..Default::default()
    };

    // Execute: download → PDFium render → OCR
    let response = process_pdf(request)?;

    // response.pages contains RenderedPage + OcrResult per page
    Ok(response)
}

```

### Direct PDFium Wrapper Usage

```rust
use pdf_inspector::vision::pdfium::PdfiumRenderer;

fn render_page_debug(pdf_data: &[u8], page_idx: u16) -> anyhow::Result<()> {
    // Initialise once—loads PDFium dynamic library
    let renderer = PdfiumRenderer::new(pdf_data)?;

    // Render at 200 DPI for higher OCR accuracy
    let img = renderer.render_page(page_idx, 200)?;

    // Persist for inspection
    img.save(format!("debug_page_{}.png", page_idx))?;
    Ok(())
}

```

## Why PDFium for Headless OCR

PDFium was selected over alternatives (Poppler, MuPDF) for several architectural reasons:

- **Minimal dependencies**: Single dynamic library, no X11/graphics stack requirements
- **Memory efficiency**: Stream-based processing suits large PDFs without full document materialisation
- **Google maintenance**: Chromium-proven codebase with regular security updates
- **License compatibility**: BSD-3-Clause permits commercial and derivative use

The headless capability is essential for pdf-inspector's benchmark workflows and server deployments where GUI frameworks are unavailable or prohibited.

## Summary

- **PDFium integration** resides exclusively in [`src/vision/pdfium.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pdfium.rs) with a safe Rust wrapper over the C API
- **Rendering pipeline** flows through [`download.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/download.rs) → [`pipeline.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/pipeline.rs) → [`pdfium.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/pdfium.rs) → [`oar.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/oar.rs)
- **Key entry points**: `PdfiumRenderer::new()`, `render_page()`, and `process_pdf()`
- **Configurable DPI** controls the accuracy/speed tradeoff at render time
- **Zero external viewer dependencies** enable containerised and CI-native operation

## Frequently Asked Questions

### What version of PDFium does pdf-inspector require?

The repository dynamically links against PDFium at runtime. The `PdfiumRenderer::new()` constructor in [`src/vision/pdfium.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pdfium.rs) loads the system PDFium library or a bundled copy, verifying symbol compatibility during initialisation. Specific version requirements are documented in the crate's build configuration.

### Can I use pdf-inspector without PDFium installed?

No. PDFium is mandatory for the OCR rendering path. The pipeline will fail at `PdfiumRenderer` construction with a clear error if the library cannot be loaded. Native text extraction without OCR may function for text-based PDFs using alternative parsers, but scanned content requires PDFium rasterisation.

### How does pdf-inspector handle PDFium rendering errors?

In [`src/vision/pipeline.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/pipeline.rs), each `render_page()` call wraps PDFium operations in `anyhow::Result`. Corrupted pages, password-protected documents, or invalid page indices propagate as structured errors without terminating the entire pipeline. The `RenderResponse` includes per-page error states for partial success scenarios.

### What DPI settings are recommended for OCR accuracy?

The default 150 DPI in `RenderRequest` suits most documents. For fine print or complex layouts, 200-300 DPI improves OCR recall at ~2-4× render cost. The `render_page()` parameter accepts any `u16` DPI value; [`src/vision/render.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/vision/render.rs) validates and clamps extreme values to prevent memory exhaustion.