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

> Quickly detect if a PDF is text-based or scanned using pdf-inspector's detector module. Analyze PDFs in milliseconds for accurate classification.

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

---

**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`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/bin/detect_pdf.rs) provides immediate command-line classification:

```bash

# 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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs):

```rust
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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs):

```rust
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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/python.rs) provides one-line classification:

```python
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`](https://github.com/firecrawl/pdf-inspector/blob/main/wasm/src/lib.rs):

```javascript
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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) (logic), [`src/types.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/types.rs) (enum), [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/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.