How to Detect Specific Pages Needing OCR in Mixed PDFs with pdf-inspector
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: 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 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 processingocr_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 (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 (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 (lines 107–119):
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 (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
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:
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:
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:
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 |
Core PDF-type detection, OCR page selection, and reason generation |
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 |
Helper for building ocr_reasons_by_page mappings |
Summary
pdf-inspectorreturns exact page numbers requiring OCR through thepages_needing_ocrfield inPdfTypeResult- 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_imagestreamline 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →