How to Perform OCR on Specific Pages When pdf‑inspector Detects Them as Scanned
pdf‑inspector analyzes PDF content streams in src/detector.rs and returns a pages_needing_ocr array containing exact 1‑based page numbers via the PdfProcessResult struct, allowing you to run external OCR engines like Tesseract only on pages that actually contain scanned images rather than selectable text.
The firecrawl/pdf‑inspector repository provides a Rust‑based pipeline for classifying PDF documents as TextBased, Scanned, ImageBased, or Mixed. When processing documents classified as Scanned or Mixed, the library identifies exactly which pages require optical character recognition (OCR) through specific metadata fields, eliminating redundant processing of text‑based pages and optimizing your document digitization workflow.
How pdf‑inspector Classifies and Flags Scanned Pages
Detection Logic in src/detector.rs
In src/detector.rs, the detect_from_document function scans each page’s content streams for the presence of text operators, image operators, and OCR‑layer markers (lines 250‑300). Pages that contain only raster images or have an invisible OCR text layer are marked with the reason OCR_REASON_SCANNED (string value "scanned"). This granular detection populates the final page list used downstream.
The PdfProcessResult Structure
After detection completes, the core pipeline in src/lib.rs (lines 3912‑3930) constructs a PdfProcessResult struct that surfaces two critical fields:
pages_needing_ocr: AVec<u32>of 1‑based page numbers requiring OCRocr_reasons_by_page: A HashMap explaining why each specific page was flagged
When the PDF type is Scanned or ImageBased, the extractor short‑circuits and returns only this OCR metadata without Markdown content (lines 3912‑3916), signaling that external processing is required.
Retrieving the List of Pages Requiring OCR
Rust API Implementation
Use process_pdf_with_options to obtain the result struct containing the flagged pages:
use pdf_inspector::{process_pdf_with_options, PdfOptions, PdfProcessResult};
let opts = PdfOptions::default(); // Optionally set page_filter here
let result: PdfProcessResult = process_pdf_with_options("document.pdf", opts).unwrap();
println!("PDF type: {:?}", result.pdf_type);
println!("Pages needing OCR: {:?}", result.pages_needing_ocr);
// Output example: [2, 5, 7]
CLI with JSON Output
The pdf2md binary exposes the OCR metadata when you pass the --json flag (implementation in src/bin/pdf2md.rs, lines 426‑433):
pdf2md --json input.pdf > report.json
# Extract the page list using jq
jq '.pages_needing_ocr' report.json
# Returns: [2, 5, 7]
Python Bindings
The Python module exposes the same functionality through process_pdf, defined in src/python.rs (lines 318‑340):
import pdf_inspector
result = pdf_inspector.process_pdf("document.pdf")
print(result.pdf_type) # "Scanned" or "Mixed"
print(result.pages_needing_ocr) # [2, 5, 7]
Performing OCR on Specific Pages Only
Once you have the exact page numbers, render only those pages to images and process them through your preferred OCR engine.
Rendering Target Pages to Images
Use pdf2image in Python or pdfium-render in Rust to convert specific pages rather than the entire document:
from pdf2image import convert_from_path
for page_num in result.pages_needing_ocr:
# Render only the specific page
images = convert_from_path(
"document.pdf",
first_page=page_num,
last_page=page_num
)
# images[0] contains the PIL Image for processing
Running External OCR Engines
Feed the rendered images to Tesseract, PaddleOCR, or cloud vision APIs:
import pytesseract
ocr_results = {}
for page_num in result.pages_needing_ocr:
image = convert_from_path(
"document.pdf",
first_page=page_num,
last_page=page_num
)[0]
text = pytesseract.image_to_string(image)
ocr_results[page_num] = text
Integrating Results Back
Because pdf2md returns empty Markdown for Scanned or ImageBased documents, you must merge the OCR text back into your final output manually. Create a page‑mapping dictionary that combines the original page numbers with the recognized text, then concatenate or insert the content according to your document structure requirements.
Summary
- Detection happens in
src/detector.rs(lines 250‑300), where content streams are analyzed for text operators and image markers - Metadata is stored in
PdfProcessResult.pages_needing_ocras defined insrc/lib.rs(lines 3918‑3930) - Access the list via the Rust API (
process_pdf_with_options), CLI (pdf2md --json), or Python bindings (process_pdf) - Optimization is achieved by rendering only the specified pages (1‑based indexing) to images using libraries like
pdf2image - Completion requires running external OCR engines (Tesseract, etc.) on the flagged pages and merging results back into your document pipeline
Frequently Asked Questions
Does pdf‑inspector perform the actual OCR or just detect which pages need it?
pdf‑inspector only detects and reports which pages require OCR via the pages_needing_ocr field. You must use external engines like Tesseract, PaddleOCR, or Google Vision API to perform the actual text recognition on rendered page images, as the library’s extraction pipeline short‑circuits for scanned content.
What page numbering system does pdf‑inspector use for the OCR list?
The pages_needing_ocr vector uses 1‑based indexing, meaning the first page of the document is page 1. This convention aligns with standard document referencing and matches the input parameters of most PDF rendering libraries like pdf2image and poppler.
Why does pdf‑inspector return empty Markdown for Scanned PDFs?
According to lines 3912‑3916 in src/lib.rs, when the detector classifies a document as Scanned or ImageBased, the extraction pipeline intentionally returns only metadata and no Markdown content. This design prevents attempts to extract non‑existent text layers and explicitly signals that you must run external OCR on the pages_needing_ocr list to obtain the actual document content.
Can I limit processing to specific page ranges before detection?
Yes. When using the Rust API, set the page_filter field in PdfOptions before calling process_pdf_with_options. This restricts both the detection phase in src/detector.rs and subsequent extraction to your specified range, significantly improving performance on large documents by avoiding analysis of pages you already know are text‑based.
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 →