Skip to main content

Fast PDF inspection, classification, and text extraction with smart scanned vs text-based detection

Project description

pdf-inspector

Fast PDF classification and text extraction. Detects whether a PDF is text-based or scanned, extracts text with position awareness, and converts to clean Markdown — all without OCR. Python bindings via PyO3 for the pdf-inspector Rust library.

Built by Firecrawl to handle text-based PDFs locally in under 200ms, skipping expensive OCR services for the ~54% of PDFs that don't need them.

Features

  • Smart classificationtext_based / scanned / image_based / mixed in ~10–50ms, with a confidence score and per-page OCR routing.
  • Markdown conversion — headings, lists, code blocks, bold/italic, URL linking, and dual-mode table detection (PDF drawing ops + text-alignment heuristics).
  • Layout-aware extraction — multi-column reading order, position and font info per text item, RTL support.
  • Robust text decoding — CID/Type0 fonts via ToUnicode CMaps, plus automatic flagging of broken encodings so callers can fall back to OCR.
  • Lightweight — native Rust core, no ML models, no external services; ships type stubs.

Benchmark

opendataloader-bench corpus (200 PDFs), direct-extraction engines only — no OCR, no ML. Scores 0–1, higher is better:

Engine Overall Reading order Tables (TEDS) Headings Speed
pdf-inspector 0.83 0.88 0.66 0.74 4s
opendataloader 0.84 0.91 0.49 0.74 11s
pymupdf4llm 0.73 0.89 0.40 0.41 18s

OCR/ML engines (docling, marker, mineru) score 0.83–0.88 overall but take 2–180 minutes on the same corpus. Full numbers in the repo README.

Install

pip install pdf-inspector

Prebuilt wheels cover CPython ≥3.8 on Linux (x86_64, aarch64), macOS (Intel, Apple Silicon), and Windows (x64). Other platforms build from source, which requires a Rust toolchain. For local development in a repo checkout:

pip install maturin
maturin develop --release

Usage

import pdf_inspector

# Full processing: detect + extract + convert to Markdown
result = pdf_inspector.process_pdf("document.pdf")
print(result.pdf_type)      # "text_based", "scanned", "image_based", "mixed"
print(result.confidence)     # 0.0 - 1.0
print(result.page_count)     # number of pages
print(result.markdown)       # Markdown string or None

# Process specific pages only
result = pdf_inspector.process_pdf("document.pdf", pages=[1, 3, 5])

# Process from bytes (no filesystem needed)
with open("document.pdf", "rb") as f:
    result = pdf_inspector.process_pdf_bytes(f.read())

# Fast detection only (no text extraction)
result = pdf_inspector.detect_pdf("document.pdf")
if result.pdf_type == "text_based":
    print("Can extract locally!")
else:
    print(f"Pages needing OCR: {result.pages_needing_ocr}")

# Plain text extraction
text = pdf_inspector.extract_text("document.pdf")

# Positioned text items with font info
items = pdf_inspector.extract_text_with_positions("document.pdf")
for item in items[:5]:
    print(f"'{item.text}' at ({item.x:.0f}, {item.y:.0f}) size={item.font_size}")

# Per-page markdown (one Markdown string per page, plus layout metadata)
result = pdf_inspector.extract_pages_markdown("document.pdf")
for page in result.pages:
    print(f"Page {page.page}: {len(page.markdown)} chars, needs_ocr={page.needs_ocr}")

# Restrict to specific 0-indexed pages (preserves caller order)
result = pdf_inspector.extract_pages_markdown("document.pdf", pages=[0, 2])

API reference

Function Description
process_pdf(path, pages=None) Full processing (detect + extract + markdown)
process_pdf_bytes(data, pages=None) Full processing from bytes
detect_pdf(path) Fast detection only (returns PdfResult)
detect_pdf_bytes(data) Fast detection from bytes
classify_pdf(path) Lightweight classification (returns PdfClassification)
classify_pdf_bytes(data) Lightweight classification from bytes
extract_text(path) Plain text extraction
extract_text_bytes(data) Plain text extraction from bytes
extract_text_with_positions(path, pages=None) Text with X/Y coords and font info
extract_text_with_positions_bytes(data, pages=None) Text with positions from bytes
extract_text_in_regions(path, page_regions) Extract text in bounding-box regions
extract_text_in_regions_bytes(data, page_regions) Region extraction from bytes
extract_pages_markdown(path, pages=None) Per-page Markdown + layout metadata (all pages by default)
extract_pages_markdown_bytes(data, pages=None) Per-page Markdown from bytes

Types

Type stubs (pdf_inspector.pyi) ship with the package. Result types at a glance:

class PdfResult:                     # process_pdf / detect_pdf
    pdf_type: str                    # "text_based" | "scanned" | "image_based" | "mixed"
    markdown: str | None             # extracted Markdown (None for detect_pdf)
    page_count: int
    processing_time_ms: int
    pages_needing_ocr: list[int]
    title: str | None
    confidence: float                # 0.0 - 1.0
    is_complex_layout: bool
    pages_with_tables: list[int]
    pages_with_columns: list[int]
    has_encoding_issues: bool        # broken font encodings — consider OCR fallback

class PdfClassification:             # classify_pdf
    pdf_type: str
    page_count: int
    pages_needing_ocr: list[int]     # 0-indexed
    confidence: float

class TextItem:                      # extract_text_with_positions
    text: str
    x: float
    y: float
    width: float
    height: float
    font: str
    font_size: float
    page: int
    is_bold: bool
    is_italic: bool
    is_underline: bool
    is_strikeout: bool
    item_type: str

class PageRegionTexts:               # extract_text_in_regions
    page: int                        # 0-indexed
    regions: list[RegionText]        # RegionText: text: str, needs_ocr: bool

class PagesExtractionResult:         # extract_pages_markdown
    pages: list[PageMarkdown]        # PageMarkdown: page (0-indexed), markdown, needs_ocr
    pages_with_tables: list[int]     # 1-indexed
    pages_with_columns: list[int]    # 1-indexed
    pages_needing_ocr: list[int]     # 1-indexed
    is_complex: bool                 # any page has tables or multi-column layout

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pdf_inspector-0.2.5.tar.gz (1.4 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pdf_inspector-0.2.5-cp38-abi3-win_amd64.whl (2.4 MB view details)

Uploaded CPython 3.8+Windows x86-64

pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.7 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.7 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

pdf_inspector-0.2.5-cp38-abi3-macosx_11_0_arm64.whl (2.5 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

pdf_inspector-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file pdf_inspector-0.2.5.tar.gz.

File metadata

  • Download URL: pdf_inspector-0.2.5.tar.gz
  • Upload date:
  • Size: 1.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pdf_inspector-0.2.5.tar.gz
Algorithm Hash digest
SHA256 5388cebd5067d1cf87cc9d68e831941e5f1145805e80be7a7cdbcc245b6d2a81
MD5 23b605e675859c7b05696ee2a5771226
BLAKE2b-256 29a3e443e9a2beb6f5336075ec1fc32df978a89dc64edd74374518ce9a38ad50

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdf_inspector-0.2.5.tar.gz:

Publisher: publish-pypi.yml on firecrawl/pdf-inspector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdf_inspector-0.2.5-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for pdf_inspector-0.2.5-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 6601eae42965998cd34dd30ba2928b754e29c3ccfd994e5df8e2fd2b0e3ee52e
MD5 66f284ee460be9e587ea47e31acb3a7f
BLAKE2b-256 c7b933409d70adfc9960416cc517e3ae4e24cd0705dbe867768bf08dc73743cd

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdf_inspector-0.2.5-cp38-abi3-win_amd64.whl:

Publisher: publish-pypi.yml on firecrawl/pdf-inspector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 46cee576aed80cb2b11492a8960842f78e68c28bb5e548961e238bf8bd9c1263
MD5 3ba954de08d4d41752516f599445dccf
BLAKE2b-256 3c8c2f2b65b3ab7cc60a67448b443e29e1d1156a07426bc6cd5969b75269f82f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish-pypi.yml on firecrawl/pdf-inspector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 5e040ed2cf51688a85f20d065c459c93a34af60cb174a8bb0a771edfdd30074e
MD5 f255326fc36f2434392314c2e894b7ba
BLAKE2b-256 79b08a77719e9423e755aa6101a56ca2cb702ebd35383ffa6987a471a8889096

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdf_inspector-0.2.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish-pypi.yml on firecrawl/pdf-inspector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdf_inspector-0.2.5-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pdf_inspector-0.2.5-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d750b77dc21526d3790c836fc8f523ff7592356b915283c9620b68e780b42692
MD5 55dd315f75303038c98b59fc1e7f26fc
BLAKE2b-256 1b02eb45a2d575d2351051b925b73d321aa845dc0ed7326efc7a52f9b6955ad0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdf_inspector-0.2.5-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: publish-pypi.yml on firecrawl/pdf-inspector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pdf_inspector-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pdf_inspector-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 93c4a7ea1270e89ff041bc2aa87f4e2dc0d0f575295fe6ee33c78628bbe916b8
MD5 e752626ba69c7e01301eaaf29a69c1bd
BLAKE2b-256 4a14a319a4c14497291d5b59a99e3d8849d16f2e3b66d173d2c45a46147de938

See more details on using hashes here.

Provenance

The following attestation bundles were made for pdf_inspector-0.2.5-cp38-abi3-macosx_10_12_x86_64.whl:

Publisher: publish-pypi.yml on firecrawl/pdf-inspector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page