Rust · Python · Node · CLI

Classify PDFs. Extract Markdown. No OCR.

A fast Rust library that tells text-based PDFs from scanned ones, then extracts position-aware text and clean Markdown — locally, in milliseconds. Skip the OCR bill for the ~54% of PDFs that never needed it.

01

Built for routing, not just reading

01

Smart classification

TextBased, Scanned, ImageBased, or Mixed in ~10–50ms by sampling content streams. Returns a confidence score and per-page OCR routing.

02

Position-aware text

Extraction with font info, X/Y coordinates, and automatic multi-column reading order.

03

Markdown conversion

Headings, bullet/numbered lists, code blocks, tables, bold/italic, URL linking, and page breaks.

04

Table detection

Rectangle-based detection from drawing ops plus heuristic alignment detection. Financial tables, footnotes, and cross-page continuations.

05

CID font support

ToUnicode CMap decoding for Type0/Identity-H fonts, with UTF-16BE, UTF-8, and Latin-1 encodings.

06

Multi-column layout

Newspaper-style column detection, sequential reading order, and right-to-left text support.

07

Encoding checks

Flags broken font encodings automatically so callers can fall back to OCR only when it's actually needed.

08

Lightweight

Pure Rust. No ML models, no external services. A single parse shared between detection and extraction.

02

Fastest of the direct-text engines

Evaluated on the opendataloader-bench corpus (200 PDFs). Direct text-extraction engines only — no OCR, no ML. Higher is better.

EngineOverallReading orderTablesHeadings200 docs
pdf-inspector0.830.880.660.744s
opendataloader0.840.910.490.7411s
pymupdf4llm0.730.890.400.4118s
markitdown0.580.880.000.008s
OCR/ML engines (docling, marker, mineru) score 0.83–0.88 overall — but take 2–180 minutes on the same corpus.
Where we win

Fastest engine measured, the best table detection of any engine here, and heading quality now on par with opendataloader — at ~2.5× its speed.

Where we're working

Reading order still trails opendataloader slightly, and tables that need the visual structure only an OCR engine can see.

03

Three lines to Markdown

use pdf_inspector::process_pdf;

let result = process_pdf("document.pdf")?;
println!("Type: {:?}", result.pdf_type);
if let Some(markdown) = &result.markdown {
    println!("{}", markdown);
}
// full reference → docs/rust-api.md
import pdf_inspector

result = pdf_inspector.process_pdf("document.pdf")
print(result.pdf_type)   # "text_based" | "scanned" | "image_based" | "mixed"
print(result.markdown)   # Markdown string or None
# full reference → docs/python.md
import { readFileSync } from 'fs';
import { processPdf } from '@firecrawl/pdf-inspector';

const result = processPdf(readFileSync('document.pdf'));
console.log(result.pdfType);   // "TextBased" | "Scanned" | ...
console.log(result.markdown);  // Markdown string or null
// full reference → napi/README.md
# install the CLI tools
cargo install pdf-inspector

# convert a PDF to Markdown
pdf2md document.pdf

# classify only — is it scanned?
detect-pdf document.pdf --analyze --json

# structured output for pipelines
pdf2md document.pdf --json
04

Two ways to parse

Run the classifier locally for text-based PDFs; hand the scanned, OCR, and at-scale work to Firecrawl.

Open source · runs local

Use pdf-inspector yourself

Pure-Rust library and CLI. Classify and extract text-based PDFs on your own machine in milliseconds — no external calls, no OCR bill, MIT licensed.

Firecrawl
Firecrawl Parse · hosted API

Or let Firecrawl handle the hard ones

Scanned documents, OCR, DOCX / XLSX / HTML, and parsing at scale — clean, LLM-ready Markdown from one API call. The downstream route for everything local parsing can't reach.