How to Detect if a PDF Is Text-Based or Scanned Using pdf-inspector

Use pdf-inspector's detector module to classify PDFs as TextBased, Scanned, Mixed, or ImageBased by sampling pages, analyzing image counts, and computing text-to-alphanumeric ratios in approximately 10 milliseconds.

The firecrawl/pdf-inspector Rust library provides a fast, metadata-only detection system that distinguishes text-based PDFs from scanned documents without rendering full page layouts. This classification powers downstream decisions about whether OCR processing is necessary.


Understanding the PdfType Classification

The detector returns a PdfType enum defined in src/types.rs with four distinct variants:

Variant Description
TextBased Searchable Unicode text with minimal raster imagery
Scanned Image-based pages from scanners with minimal extractable text
Mixed Combination of genuine text and scanned pages
ImageBased Dominated by graphics/photos; treated similarly to scanned

These classifications enable routing decisions: text-based PDFs proceed directly to extraction pipelines, while scanned documents trigger OCR workflows.


Core Detection Heuristics in src/detector.rs

The detection algorithm in src/detector.rs employs multiple complementary strategies through functions detect_pdf_type and detect_pdf_type_with_config.

Page Sampling for Speed

Detection uses a configurable page sample (default ~5 pages) rather than full document analysis. This keeps typical detection under 10 milliseconds for 100-page PDFs while maintaining accuracy.

Image Count Threshold

Each sampled page yields a raster image count. Totals exceeding approximately 1 image push classification toward Scanned or ImageBased.

Text-to-Alphanumeric Ratio

Text extraction runs on sampled pages. The ratio of alphanumeric characters to total extracted characters determines text quality:

  • Ratio ≥ ~50%: Indicates genuine text content
  • Ratio < ~50%: Signals scanned image with garbled or minimal text

This heuristic lives in src/text_quality.rs.

Tiled-Scan Detection

Scanned PDFs sometimes use JBIG2 strip tiling—many tiny images forming a page. The detect_tiled_scan function aggregates tile areas; cumulative area exceeding ~2 million pixels upgrades classification to Scanned.

Heuristic Overrides

Two additional signals influence final classification:

  • Tagged PDFs (structure tree present): Trusted as text-based unless contradicted by other signals
  • PageOcrReasons: OCR necessity indicators force Scanned classification

Using the Detection API

CLI Detection

The detect-pdf binary compiled from src/bin/detect_pdf.rs provides immediate command-line classification:


# Human-readable output

detect-pdf mydoc.pdf

# JSON for scripting

detect-pdf mydoc.pdf --json

# Full diagnostic details

detect-pdf mydoc.pdf --analyze --json

Rust API

Fast in-process detection through src/lib.rs:

use pdf_inspector::{detect_pdf, PdfProcessResult};

fn main() -> Result<(), pdf_inspector::PdfError> {
    // Metadata-only detection
    let result: PdfProcessResult = pdf_inspector::detect_pdf("report.pdf")?;
    println!("PDF type: {:?}", result.pdf_type);
    Ok(())
}

Access raw diagnostics via src/detector.rs:

use pdf_inspector::detector::{detect_pdf_type, PdfTypeResult};

let info: PdfTypeResult = detect_pdf_type("report.pdf")?;
println!("Pages sampled: {}", info.pages_sampled);
println!("Image count: {}", info.image_count);
println!("Text ratio: {}", info.text_ratio);

Python Binding

The Python interface through src/python.rs provides one-line classification:

import pdf_inspector

info = pdf_inspector.detect_pdf("report.pdf")
print(info.pdf_type)  # "TextBased", "Scanned", "Mixed", or "ImageBased"

WebAssembly in Browsers

Browser-based detection via wasm/src/lib.rs:

import init, { classifyPdf } from "@firecrawl/pdf-inspector-wasm";

await init();
const pdfBytes = await fetch("report.pdf").then(r => r.arrayBuffer());

const classification = classifyPdf(new Uint8Array(pdfBytes));
console.log(classification.pdf_type);

Configuration and Customization

The detect_pdf_type_with_config function accepts detection parameters:

Parameter Default Purpose
pages_to_sample ~5 Pages analyzed for classification
image_threshold ~1 Image count triggering scanned suspicion
text_ratio_min ~0.5 Minimum alphanumeric ratio for text-based

Adjust these when processing document categories with unusual characteristics—heavily illustrated textbooks, for example, may need higher image thresholds.


Summary

  • Detection speed: ~10ms per 100-page PDF via metadata-only analysis
  • Four classifications: TextBased, Scanned, Mixed, ImageBased
  • Core files: src/detector.rs (logic), src/types.rs (enum), src/lib.rs (public API)
  • Multi-platform: CLI, Rust, Python, and WebAssembly interfaces
  • Key metrics: Image count, text-to-alphanumeric ratio, tiled scan area

The detector's speed makes it suitable for high-throughput pipelines where PDFs must be routed to appropriate processing paths before full content extraction.


Frequently Asked Questions

How accurate is pdf-inspector's scanned PDF detection?

Accuracy is high for typical office documents due to multiple overlapping heuristics. The combination of image counting, text ratio analysis, and tiled-scan detection catches most scanner-generated PDFs, though unusual layouts (presentations with embedded full-page images) may require configuration tuning.

Can I use pdf-inspector detection without installing Rust?

Yes. Pre-built CLI binaries work standalone. Python users install via pip with precompiled wheels. Browser applications use the WebAssembly package from npm at @firecrawl/pdf-inspector-wasm.

Does detection require loading the entire PDF into memory?

No. The detector streams and samples only configured pages (default 5). The detect_pdf_mem function accepts byte slices for cases where files already reside in memory, but standard detect_pdf operates on paths with minimal memory footprint.

What distinguishes Scanned from ImageBased classifications?

Scanned specifically indicates document pages from scanning equipment—typically full-page images with minimal text. ImageBased applies to graphics-heavy documents like photo albums or illustrations where images dominate the content purpose rather than document capture origin.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →