# How to Detect Specific Pages Needing OCR in Mixed PDFs with pdf-inspector

> Quickly find pages needing OCR in mixed PDFs with pdf-inspector. This tool precisely identifies and lists pages requiring OCR, streamlining your data extraction process.

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: how-to-guide
- Published: 2026-08-11

---

**`pdf-inspector` classifies PDFs into four types and returns the exact page numbers that require OCR processing, enabling targeted extraction of mixed documents.**

Detecting specific pages needing OCR in mixed PDFs is essential for efficient document processing workflows. The `firecrawl/pdf-inspector` Rust library provides a precise detection pipeline that identifies which pages contain extractable text versus those requiring optical character recognition. This article walks through the technical implementation and practical usage of its OCR page detection capabilities.

## PDF Classification and OCR Detection Overview

The library categorizes documents into **TextBased**, **Scanned**, **ImageBased**, and **Mixed** types. For every document processed, it produces a complete list of pages needing OCR and the corresponding reasons for each flag.

The detection pipeline centers on two core functions in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs): `detect_pdf_type` for file-based detection and `detect_pdf_type_mem` for in-memory buffers. Both return a `PdfTypeResult` struct containing the critical `pages_needing_ocr` field—a `Vec<usize>` of 1-indexed page numbers.

## Core Detection Pipeline in detector.rs

### PDF Type Detection with Sampling

The entry point `detect_pdf_type_with_config` in [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) initiates the analysis with configurable sampling strategies. By default, it uses `Sample(8)` to analyze eight representative pages before making document-level decisions.

The `detect_from_document` function (lines 78–89) constructs the `PdfTypeResult` with two essential OCR-related fields:

- `pages_needing_ocr`: The complete list of pages requiring OCR processing
- `ocr_reasons_by_page`: A HashMap mapping each page number to its detection reason(s)

### Per-Page Content Analysis

Each sampled page undergoes detailed analysis via `analyze_page_content` (lines 334–345), which returns a `PageAnalysis` struct describing:

- Text operator count
- Image presence and characteristics
- Vector-outlined text detection
- Font usage patterns

This granular analysis feeds into the OCR decision logic that determines whether individual pages need character recognition.

## OCR Decision Rules for Mixed PDFs

For **Mixed** PDFs—documents containing both text-based and image-based pages—the algorithm performs a second pass over every page in the document, not just the sampled set. The `pages_needing_ocr` list is built according to conditions defined in [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) (lines 779–814):

### Template Image Detection

A page is flagged when `has_template_image && looks_like_scan` evaluates to true. This identifies pages containing scan-like background images that typically overlay or replace actual text content.

### Vector-Outlined Text

Pages containing `has_vector_text` are marked for OCR. Vector text appears as graphical paths rather than encoded character data, making it invisible to standard text extraction while visually readable to humans.

### Low Text Operator Count with Images

When `text_operator_count < min_text_ops_per_page && has_images`, the page lacks sufficient encoded text despite containing visual content. This heuristic catches image-heavy pages with minimal or no extractable text.

### Undecodable Font Detection

Even pages with detectable text operators may need OCR when fonts cannot be mapped to Unicode. The library checks for:

- **Identity-H/V fonts** without ToUnicode CMap tables
- **Type 3 fonts** with custom character encodings

These checks operate on *used fonts only* (post-P1/P2 fixes) and are implemented in [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) (lines 820–828). When undecodable fonts are detected, the page joins `pages_needing_ocr` regardless of its text operator count.

## OCR Reason Strings

Every flagged page receives a human-readable explanation through the `ocr_reasons_by_page` map. The reason constants are defined in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) (lines 107–119):

```rust
pub const OCR_REASON_GARBLED: &str = "suspected_garbled_text";
pub const OCR_REASON_VECTOR_TEXT: &str = "vector_text";
pub const OCR_REASON_SCANNED: &str = "scanned";
pub const OCR_REASON_NO_TEXT: &str = "no_text";

```

The helper `page_ocr_reasons` in [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) (lines 552–571) translates low-level analysis results into these standardized strings, enabling downstream systems to categorize and handle pages appropriately.

## Basic Usage: Detect OCR Pages from File

```rust
use pdf_inspector::{detect_pdf_type, PdfTypeResult};

fn main() -> Result<(), pdf_inspector::PdfError> {
    // Detect the PDF type and pages that need OCR
    let result: PdfTypeResult = detect_pdf_type("sample.pdf")?;

    println!("PDF type: {:?}", result.pdf_type);
    println!("Pages needing OCR: {:?}", result.pages_needing_ocr);
    
    // Optional: see per-page reasons
    for (page, reasons) in result.ocr_reasons_by_page {
        println!("Page {page}: {:?}", reasons);
    }
    Ok(())
}

```

## Custom Scan Strategy: Target Specific Pages

For controlled analysis of known problematic pages, use `DetectionConfig` with a custom `ScanStrategy`:

```rust
use pdf_inspector::{detect_pdf_type_with_config, DetectionConfig, ScanStrategy};

let config = DetectionConfig {
    strategy: ScanStrategy::Pages(vec![1, 5, 10]),
    ..Default::default()
};

let result = detect_pdf_type_with_config("mixed.pdf", config)?;
println!("OCR pages: {:?}", result.pages_needing_ocr);

```

## Memory Buffer API: In-Memory PDF Processing

When PDFs are already loaded in memory—common in serverless or streaming architectures—use the `_mem` variant:

```rust
let pdf_bytes = std::fs::read("mixed.pdf")?;
let result = pdf_inspector::detect_pdf_type_mem(&pdf_bytes)?;
println!("Pages needing OCR: {:?}", result.pages_needing_ocr);

```

## Integration with OCR Engines

The `pages_needing_ocr` list enables precise integration with external OCR systems:

```rust
for page in result.pages_needing_ocr {
    let image = pdf_inspector::extract_page_image("mixed.pdf", page)?;
    let ocr_text = my_ocr_engine::run(&image)?;
    // merge `ocr_text` into the final Markdown output
}

```

This targeted approach avoids redundant OCR processing on text-based pages while ensuring no content is missed from scanned or image-based sections.

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) | Core PDF-type detection, OCR page selection, and reason generation |
| [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) | Public API (`detect_pdf_type`, `PdfTypeResult`) and OCR-reason constants |
| `src/extractor/*.rs` | Content-stream parsing, font collection, and image analysis |
| [`src/text_quality.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/text_quality.rs) | Helper for building `ocr_reasons_by_page` mappings |

## Summary

- **`pdf-inspector` returns exact page numbers** requiring OCR through the `pages_needing_ocr` field in `PdfTypeResult`
- **Mixed PDFs trigger per-page analysis** beyond initial sampling, checking template images, vector text, text operator density, and font decodability
- **Four OCR reasons** categorize detection decisions: garbled text, vector text, scanned appearance, and insufficient text
- **Both file and memory APIs** support diverse deployment scenarios
- **Integration helpers** like `extract_page_image` streamline OCR pipeline construction

## Frequently Asked Questions

### What does the pages_needing_ocr field contain?

The `pages_needing_ocr` field contains a `Vec<usize>` of 1-indexed page numbers that require OCR processing to extract readable text. For **Scanned** or **ImageBased** PDFs, this includes all pages. For **Mixed** PDFs, it contains only the specific pages failing the text extractability criteria.

### How does pdf-inspector handle fonts that appear to have text but cannot be extracted?

The library performs undecodable font detection on used fonts only, checking for Identity-H/V fonts lacking ToUnicode CMaps and Type 3 fonts with custom encodings. These pages are added to `pages_needing_ocr` even when text operators are present, ensuring no content is silently lost during extraction.

### Can I customize which pages are analyzed during detection?

Yes. Pass a `DetectionConfig` with `ScanStrategy::Pages(vec![...])` to `detect_pdf_type_with_config` for targeted analysis. However, note that the OCR decision phase still evaluates all pages in the document for Mixed PDFs, not just the sampled subset.

### What is the difference between suspected_garbled_text and no_text reasons?

The `"suspected_garbled_text"` reason indicates a page with undecodable fonts that may display correctly but extract as meaningless characters, while `"no_text"` applies to pages with insufficient text operators relative to image content. Both conditions require OCR for reliable text recovery.