กลับไปหน้า Tools

GetNotes Tools

firecrawl/pdf-inspector

Tool นี้คืออะไร

pdf-inspector เป็นไลบรารี Rust ที่รวดเร็วสำหรับการจำแนกประเภทและดึงข้อความจากไฟล์ PDF เหมาะสำหรับนักพัฒนาที่ต้องการประมวลผล PDF ที่เป็นข้อความ (text-based) ได้อย่างรวดเร็วและแม่นยำ โดยไม่ต้องพึ่ง OCR ช่วยลดค่าใช้จ่ายและเวลาในการประมวลผล

ข้อมูลโปรเจกต์

ดาว

9.2K

Forks

614

License

MIT

อัปเดต GitHub ล่าสุด

4 ส.ค. 2569

เพิ่มใน GetNotes

17 ส.ค. 2569

Repository

firecrawl/pdf-inspector

รูปแบบ

Library / SDK

เหมาะกับงาน

เอกสารและความรู้

เหมาะกับอาชีพ

Ecosystem

Node.js · Rust

แปลและเรียบเรียงโดย AI

เนื้อหาฉบับภาษาไทย

ใช้อ่านเพื่อทำความเข้าใจเบื้องต้น โปรดตรวจสอบรายละเอียดสำคัญกับเอกสารต้นฉบับด้านล่าง

pdf-inspector

Crates.io npm PyPI License: MIT

ไลบรารี Rust ที่รวดเร็วสำหรับการจำแนกประเภท PDF และการดึงข้อความ ตรวจจับว่า PDF เป็นแบบข้อความ (text-based) หรือแบบสแกน (scanned), ดึงข้อความพร้อมข้อมูลตำแหน่ง และแปลงเป็น Markdown ที่สะอาด — ทั้งหมดนี้โดยไม่ต้องใช้ OCR มีการผูก (bindings) สำหรับ Python, Node.js และ browser WebAssembly

สร้างโดย Firecrawl เพื่อจัดการกับ PDF ที่เป็นข้อความในเครื่องภายในเวลาไม่ถึง 200ms โดยข้ามบริการ OCR ที่มีราคาแพงสำหรับ PDF ประมาณ 54% ที่ไม่จำเป็นต้องใช้

คุณสมบัติ

  • การจำแนกประเภทอัจฉริยะ — ตรวจจับ PDF ประเภท TextBased, Scanned, ImageBased หรือ Mixed ในเวลาประมาณ 10-50ms โดยการสุ่มตัวอย่าง content streams คืนค่าคะแนนความเชื่อมั่น (0.0-1.0) และการกำหนดเส้นทาง OCR แบบต่อหน้า
  • การดึงข้อความ — การดึงข้อความที่รับรู้ตำแหน่งพร้อมข้อมูลฟอนต์, พิกัด X/Y และลำดับการอ่านแบบหลายคอลัมน์อัตโนมัติ
  • การแปลงเป็น Markdown — หัวข้อ (H1-H4 ผ่านอัตราส่วนขนาดฟอนต์), รายการแบบจุด/ตัวเลข/ตัวอักษร, บล็อกโค้ด (การตรวจจับฟอนต์ monospace), ตาราง (อิงตามสี่เหลี่ยมและฮิวริสติก), การจัดรูปแบบตัวหนา/ตัวเอียง, การเชื่อมโยง URL และการแบ่งหน้า
  • การตรวจจับตาราง — โหมดคู่: การตรวจจับตารางอิงตามสี่เหลี่ยมจาก PDF drawing ops และการตรวจจับแบบฮิวริสติกจากการจัดแนวข้อความ จัดการกับตารางทางการเงิน, เชิงอรรถ และตารางต่อเนื่องข้ามหน้า
  • การรองรับฟอนต์ CID — การถอดรหัส ToUnicode CMap สำหรับฟอนต์ Type0/Identity-H, การเข้ารหัส UTF-16BE, UTF-8 และ Latin-1
  • เค้าโครงหลายคอลัมน์ — การตรวจจับคอลัมน์สไตล์หนังสือพิมพ์อัตโนมัติ, ลำดับการอ่านแบบต่อเนื่อง และการรองรับข้อความ RTL
  • การตรวจจับปัญหาการเข้ารหัส — ตั้งค่าสถานะการเข้ารหัสฟอนต์ที่เสียหายโดยอัตโนมัติ เพื่อให้ผู้เรียกสามารถกลับไปใช้ OCR ได้
  • การโหลดเอกสารครั้งเดียว — เอกสารจะถูกแยกวิเคราะห์เพียงครั้งเดียวและใช้ร่วมกันระหว่างการตรวจจับและการดึงข้อความ หลีกเลี่ยงการ I/O ที่ซ้ำซ้อน
  • Browser WebAssembly — รันตัวแยกวิเคราะห์ Rust เดียวกันในเครื่องในเบราว์เซอร์และ Web Workers พร้อม CMaps แบบฝัง และไม่มีการเดินทางไปกลับเซิร์ฟเวอร์
  • น้ำหนักเบา — Pure Rust, ไม่มีโมเดล ML, ไม่มีบริการภายนอก พึ่งพา lopdf เพียงตัวเดียวสำหรับการแยกวิเคราะห์ PDF

การวัดประสิทธิภาพ

ประเมินจากคลังข้อมูล opendataloader-bench (200 PDF) แสดงเฉพาะเอนจินในเครื่องที่ไม่มีการแยกวิเคราะห์ PDF แบบโมเดลเท่านั้น OCR ถูกปิดใช้งาน คะแนนอยู่ระหว่าง 0-1 ยิ่งสูงยิ่งดี

EngineOverallReading Order (NID)Tables (TEDS)Headings (MHS)Speed (200 docs)
pdf-inspector0.8750.9150.8140.7880.470s
liteparse0.8730.9130.6930.8110.750s
opendataloader0.8310.9020.4890.7392.569s
pymupdf4llm0.7350.8860.4010.42417.117s
markitdown0.5890.8440.2730.00016.165s

ผลลัพธ์ได้รับการอัปเดตเมื่อวันที่ 31 กรกฎาคม 2026 บน Apple M4 Pro เวอร์ชันของเอนจินคือ pdf-inspector 0.2.6, LiteParse 2.10.1, OpenDataLoader 2.2.1, PyMuPDF4LLM 0.2.0 และ MarkItDown 0.1.5 ความเร็วคือค่ามัธยฐานของการรันคลังข้อมูลทั้งหมดห้าครั้งแบบสลับหรือหมุนเวียน หลังจากรันวอร์มอัพที่ถูกยกเว้น โดยแต่ละตัวแยกวิเคราะห์จะประมวลผลเอกสารตามลำดับในกระบวนการเดียว

การกำหนดค่าตัวแยกวิเคราะห์ที่สมบูรณ์, การคาดการณ์ต่อเอกสาร, เอาต์พุตของตัวประเมิน และแผนภูมิที่สร้างขึ้นมีอยู่ใน reproducible results branch

เหมาะสมที่สุด: PDF ที่เป็นข้อความดั้งเดิมที่ความเร็ว, ลำดับการอ่าน และโครงสร้างตารางมีความสำคัญ ในการเปรียบเทียบนี้ pdf-inspector ให้คะแนนโดยรวม, ลำดับการอ่าน และตารางที่สูงกว่า พร้อมกับการรันที่สมบูรณ์ที่เร็วที่สุด ทำให้เป็นค่าเริ่มต้นในเครื่องที่แข็งแกร่งสำหรับรายงาน, เอกสารวิจัย, เอกสารทางการเงิน, ใบแจ้งหนี้ และ PDF ทางกฎหมายที่ต้องการ Markdown ที่สะอาดและมีโครงสร้างโดยไม่ต้องเพิ่มความล่าช้าหรือโครงสร้างพื้นฐานของ OCR

ใช้ paired benchmark harness เพื่อเปรียบเทียบสองบิลด์ในเครื่องกับคลังข้อมูลและเวอร์ชันตัวประเมินที่เหมือนกัน

เริ่มต้นอย่างรวดเร็ว

Python

bash
pip install maturin
maturin develop --release
python
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

การอ้างอิง API ฉบับเต็ม: docs/python.md

Node.js

bash
npm install @firecrawl/pdf-inspector
javascript
import { readFileSync } from 'fs';
import { processPdf, classifyPdf } from '@firecrawl/pdf-inspector';

const result = processPdf(readFileSync('document.pdf'));
console.log(result.pdfType);   // "TextBased", "Scanned", "ImageBased", "Mixed"
console.log(result.markdown);  // Markdown string or null

การอ้างอิง API ฉบับเต็ม: napi/README.md

Browser WebAssembly

bash
npm install @firecrawl/pdf-inspector-wasm
javascript
import init, { processPdf } from '@firecrawl/pdf-inspector-wasm';

await init();
const response = await fetch('/document.pdf');
const pdf = new Uint8Array(await response.arrayBuffer());
const result = processPdf(pdf);

console.log(result.pdfType);
console.log(result.markdown);

การอ้างอิง API ฉบับเต็ม: wasm/README.md

Rust

ติดตั้งจาก crates.io:

bash
cargo add pdf-inspector

หรือเพิ่มด้วยตนเอง:

toml
[dependencies]
pdf-inspector = "1"
rust
use pdf_inspector::process_pdf;

let result = process_pdf("document.pdf")?;
println!("Type: {:?}", result.pdf_type);
if let Some(markdown) = &result.markdown {
    println!("{}", markdown);
}

การอ้างอิง API ฉบับเต็ม: docs/rust-api.md

CLI

bash
# Install the CLI tools
cargo install pdf-inspector

# Convert PDF to Markdown
pdf2md document.pdf

# JSON output (for piping)
pdf2md document.pdf --json

# Positioned TextItem JSON, including is_underline metadata
pdf2md document.pdf --items-json

# Raw markdown only (no headers)
pdf2md document.pdf --raw

# Token-efficient output (collapses long dot leaders and similar source padding)
pdf2md document.pdf --compact

# Insert page break markers (<!-- Page N -->)
pdf2md document.pdf --pages

# Process only specific pages
pdf2md document.pdf --select-pages 1,3,5-10

# Detection only (no extraction)
detect-pdf document.pdf
detect-pdf document.pdf --json

# Detection + layout analysis (tables, columns)
detect-pdf document.pdf --analyze --json

จากการตรวจสอบซอร์สโค้ด ให้ใช้ cargo run --bin pdf2md -- document.pdf หรือ cargo run --bin detect-pdf -- document.pdf แทน

สถาปัตยกรรม

code
PDF bytes
  │
  ├─► detector         → PdfType (TextBased / Scanned / ImageBased / Mixed)
  │
  └─► extractor
        ├─ fonts        → font widths, encodings
        ├─ content_stream → walk PDF operators → TextItems + PdfRects
        ├─ xobjects     → Form XObject text, image placeholders
        ├─ links        → hyperlinks, AcroForm fields
        └─ layout       → column detection → line grouping → reading order
              │
              ├─► tables
              │     ├─ detect_rects      → rectangle-based tables (union-find)
              │     ├─ detect_heuristic  → alignment-based tables
              │     ├─ grid              → column/row assignment → cells
              │     └─ format            → cells → Markdown table
              │
              └─► markdown
                    ├─ analysis     → font stats, heading tiers
                    ├─ preprocess   → merge headings, drop caps
                    ├─ convert      → line loop + table/image insertion
                    ├─ classify     → captions, lists, code
                    └─ postprocess  → cleanup → final Markdown

เอกสารจะถูกโหลด ครั้งเดียว ผ่าน load_document_from_path / load_document_from_mem และใช้ร่วมกันระหว่างขั้นตอนการตรวจจับและการดึงข้อความ ดังนั้นจึงไม่มีการแยกวิเคราะห์ที่ซ้ำซ้อน

โครงสร้างโปรเจกต์

code
src/
  lib.rs                — Public API, PdfOptions builder, convenience functions
  python.rs             — PyO3 Python bindings
  types.rs              — Shared types: TextItem, TextLine, PdfRect, ItemType
  text_utils.rs         — Character/text helpers (CJK, RTL, ligatures, bold/italic)
  process_mode.rs       — ProcessMode enum (DetectOnly, Analyze, Full)
  detector.rs           — Fast PDF type detection without full document load
  glyph_names.rs        — Adobe Glyph List → Unicode mapping
  tounicode.rs          — ToUnicode CMap parsing for CID-encoded text
  extractor/            — Text extraction pipeline
  tables/               — Table detection and formatting
  markdown/             — Markdown conversion and structure detection
  bin/                  — CLI tools (pdf2md, detect_pdf)
napi/                   — Node.js/Bun bindings (napi-rs)
wasm/                   — Browser bindings (wasm-bindgen)

## การทำงานของการจำแนกประเภท

1.  แยกวิเคราะห์ตาราง xref และโครงสร้างหน้า (ไม่มีการโหลดอ็อบเจกต์ทั้งหมด)
2.  เลือกหน้าตาม `ScanStrategy` (ค่าเริ่มต้น: ทุกหน้าพร้อมการออกก่อนกำหนด)
3.  มองหา `Tj`/`TJ` (ตัวดำเนินการข้อความ) และ `Do` (ตัวดำเนินการรูปภาพ) ในสตรีมเนื้อหา
4.  จำแนกประเภทตามการมีอยู่ของตัวดำเนินการข้อความในหน้าตัวอย่าง

สิ่งนี้สามารถตรวจจับ PDF ที่มีมากกว่า 300 หน้าได้ในหน่วยมิลลิวินาที ผลลัพธ์ประกอบด้วย `pages_needing_ocr` — รายการหมายเลขหน้าเฉพาะที่ไม่มีข้อความ ทำให้สามารถกำหนดเส้นทาง OCR แบบต่อหน้าได้ แทนที่จะเป็นแบบทั้งหมดหรือไม่มีเลย

### กลยุทธ์การสแกน

| กลยุทธ์ | พฤติกรรม | เหมาะที่สุดสำหรับ |
|---|---|---|
| `EarlyExit` (ค่าเริ่มต้น) | สแกนทุกหน้า หยุดเมื่อเจอหน้าแรกที่ไม่ใช่ข้อความ | ไปป์ไลน์ที่กำหนดเส้นทาง PDF ที่เป็นข้อความไปยังการแยกข้อมูลที่รวดเร็ว |
| `Full` | สแกนทุกหน้า ไม่มีการออกก่อนกำหนด | การจำแนกประเภทแบบผสมเทียบกับแบบสแกนที่แม่นยำ |
| `Sample(n)` | สุ่มตัวอย่าง `n` หน้าที่กระจายอย่างสม่ำเสมอ (หน้าแรก, หน้าสุดท้าย, กลาง) | PDF ขนาดใหญ่มากที่ความเร็วสำคัญกว่าความแม่นยำ |
| `Pages(vec)` | สแกนเฉพาะหมายเลขหน้าแบบ 1-indexed ที่ระบุเท่านั้น | เมื่อผู้เรียกทราบว่าต้องตรวจสอบหน้าใดบ้าง |

## เอาต์พุต Markdown

ตัวแปลงจัดการ:

| องค์ประกอบ | ตรวจจับได้อย่างไร |
|---|---|
| หัวข้อ (H1-H4) | ระดับขนาดตัวอักษรสัมพันธ์กับข้อความเนื้อหา โดยมีการจัดกลุ่ม 0.5pt |
| ตัวหนา/ตัวเอียง | รูปแบบชื่อฟอนต์ (Bold, Italic, Oblique) |
| รายการสัญลักษณ์แสดงหัวข้อย่อย | คำนำหน้า `•`, `-`, `*`, `○`, `●`, `◦` |
| รายการลำดับเลข | รูปแบบ `1.`, `1)`, `(1)` |
| รายการตัวอักษร | รูปแบบ `a.`, `a)`, `(a)` |
| บล็อกโค้ด | ฟอนต์ Monospace (Courier, Consolas, Monaco, Menlo, Fira Code, JetBrains Mono) และการตรวจจับคีย์เวิร์ด |
| ตาราง | การตรวจจับตามสี่เหลี่ยมจาก PDF drawing ops + การตรวจจับแบบฮิวริสติกจากการจัดแนวข้อความ |
| ตารางการเงิน | การแยกโทเค็นสำหรับค่าตัวเลขที่รวมกัน |
| คำบรรยายภาพ | การตรวจจับคำนำหน้า "Figure", "Table", "Source:" |
| ตัวห้อย/ตัวยก | ขนาดตัวอักษรและ Y-offset สัมพันธ์กับเส้นฐาน |
| URL | แปลงเป็นลิงก์ Markdown |
| การแบ่งคำ | รวมคำที่ถูกแบ่งข้ามบรรทัดเข้าด้วยกัน |
| หมายเลขหน้า | กรองออกจากเอาต์พุต |
| อักษรตัวใหญ่ขึ้นต้น | ตัวอักษรเริ่มต้นขนาดใหญ่รวมกับข้อความที่ตามมา |
| จุดนำ | จุดสไตล์ TOC ยุบเป็น " ... " |

## กรณีการใช้งาน: การกำหนดเส้นทาง PDF อัจฉริยะ

pdf-inspector ถูกสร้างขึ้นสำหรับไปป์ไลน์ที่ประมวลผล PDF ในปริมาณมาก แทนที่จะส่ง PDF ทุกไฟล์ผ่าน OCR:

PDF มาถึง → pdf-inspector จำแนกประเภท (~20ms) → TextBased + ความมั่นใจสูง? ใช่ → แยกข้อมูลในเครื่อง (~150ms), เสร็จสิ้น ไม่ → ส่งไปยังบริการ OCR (2-10s)

code

สิ่งนี้ช่วยประหยัดค่าใช้จ่ายและลดเวลาแฝงสำหรับ PDF ส่วนใหญ่ที่เป็นข้อความอยู่แล้ว (รายงาน, เอกสารวิชาการ, ใบแจ้งหนี้, เอกสารทางกฎหมาย)

## การดีบัก

ดู [docs/debugging.md](https://github.com/firecrawl/pdf-inspector/blob/main/docs/debugging.md) สำหรับการใช้งานตัวแปรสภาพแวดล้อม `RUST_LOG`

## ใบอนุญาต

[MIT](https://github.com/firecrawl/pdf-inspector/blob/main/LICENSE)

เอกสารโปรเจกต์

อ่านเอกสารต้นฉบับ

README วิธีติดตั้ง วิธีใช้งาน และข้อกำหนดจาก repository ต้นฉบับ

ดูไฟล์บน GitHub

pdf-inspector

Crates.io npm PyPI License: MIT

Fast Rust library for PDF classification and text extraction. By default it detects whether a PDF is text-based or scanned, extracts text with position awareness, and converts to clean Markdown without OCR. Native Rust and CLI consumers can opt into selective OCR. Includes bindings for Python, Node.js, and browser WebAssembly.

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 classification — Detect TextBased, Scanned, ImageBased, or Mixed PDFs in ~10-50ms by sampling content streams. Returns a confidence score (0.0-1.0) and per-page OCR routing.
  • Text extraction — Position-aware extraction with font info, X/Y coordinates, and automatic multi-column reading order.
  • Markdown conversion — Headings (H1-H4 via font size ratios), bullet/numbered/letter lists, code blocks (monospace font detection), tables (rectangle-based and heuristic), bold/italic formatting, URL linking, and page breaks.
  • Table detection — Dual-mode: rectangle-based detection from PDF drawing ops, plus heuristic detection from text alignment. Handles financial tables, footnotes, and continuation tables across pages.
  • CID font support — ToUnicode CMap decoding for Type0/Identity-H fonts, UTF-16BE, UTF-8, and Latin-1 encodings.
  • Multi-column layout — Automatic detection of newspaper-style columns, sequential reading order, and RTL text support.
  • Encoding issue detection — Automatically flags broken font encodings so callers can fall back to OCR.
  • Selective OCR — Rust, CLI, Python, and Node can render only pages that need OCR, run PP-OCRv6 Small locally, and preserve per-page provenance and hosted-fallback recommendations.
  • Single document load — The document is parsed once and shared between detection and extraction, avoiding redundant I/O.
  • Browser WebAssembly — Run the same Rust parser locally in browsers and Web Workers, with embedded CMaps and no server round trip.
  • Lightweight by default — The default Rust and browser builds remain pure extraction. Native Python and Node packages include the OCR integration, but PDFium, ONNX Runtime, and model files remain external and are touched only when a page is routed to OCR.

Benchmark

Evaluated on the opendataloader-bench corpus (200 PDFs). Only local engines without model-based PDF parsing are shown; OCR was disabled. Scores are 0-1, higher is better.

EngineOverallReading Order (NID)Tables (TEDS)Headings (MHS)Speed (200 docs)
pdf-inspector0.8750.9150.8140.7880.470s
liteparse0.8730.9130.6930.8110.750s
opendataloader0.8310.9020.4890.7392.569s
pymupdf4llm0.7350.8860.4010.42417.117s
markitdown0.5890.8440.2730.00016.165s

Results were refreshed on July 31, 2026, on an Apple M4 Pro. Engine versions were pdf-inspector 0.2.6, LiteParse 2.10.1, OpenDataLoader 2.2.1, PyMuPDF4LLM 0.2.0, and MarkItDown 0.1.5. Speed is the median of five alternating or rotating complete corpus runs after an excluded warm-up run, with each parser processing documents sequentially in a single process.

The complete parser configuration, per-document predictions, evaluator output, and generated charts are available in the reproducible results branch.

Best fit: Native-text PDFs where speed, reading order, and table structure matter. In this comparison, pdf-inspector delivered the higher overall, reading-order, and table scores, along with the fastest complete run. That makes it a strong local default for reports, research papers, financial documents, invoices, and legal PDFs that need clean, structured Markdown without adding OCR latency or infrastructure.

Use the paired benchmark harness to compare two local builds against the exact same corpus and evaluator revision.

Quick start

Python

bash
pip install pdf-inspector
python
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

# Selective OCR; clean text PDFs do not load the external OCR runtime.
ocr = pdf_inspector.process_pdf_with_ocr("document.pdf")
print(ocr.pages_routed_to_ocr)

Full API reference: docs/python.md

Node.js

bash
npm install @firecrawl/pdf-inspector
javascript
import { readFileSync } from 'fs';
import { processPdf, processPdfWithOcr } from '@firecrawl/pdf-inspector';

const pdf = readFileSync('document.pdf');
const result = processPdf(pdf);
console.log(result.pdfType);   // "TextBased", "Scanned", "ImageBased", "Mixed"
console.log(result.markdown);  // Markdown string or null

const ocr = await processPdfWithOcr(pdf); // selective OCR, off the event loop
console.log(ocr.pagesRoutedToOcr);

Full API reference: napi/README.md

Browser WebAssembly

bash
npm install @firecrawl/pdf-inspector-wasm
javascript
import init, { processPdf } from '@firecrawl/pdf-inspector-wasm';

await init();
const response = await fetch('/document.pdf');
const pdf = new Uint8Array(await response.arrayBuffer());
const result = processPdf(pdf);

console.log(result.pdfType);
console.log(result.markdown);

Full API reference: wasm/README.md

Rust

Install from crates.io:

bash
cargo add pdf-inspector

Or add it manually:

toml
[dependencies]
pdf-inspector = "1"
rust
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 API reference: docs/rust-api.md

CLI

bash
# Install the CLI tools
cargo install pdf-inspector

# Convert PDF to Markdown
pdf2md document.pdf

# JSON output (for piping)
pdf2md document.pdf --json

# Positioned TextItem JSON, including is_underline metadata
pdf2md document.pdf --items-json

# Raw markdown only (no headers)
pdf2md document.pdf --raw

# Token-efficient output (collapses long dot leaders and similar source padding)
pdf2md document.pdf --compact

# Insert page break markers (<!-- Page N -->)
pdf2md document.pdf --pages

# Process only specific pages
pdf2md document.pdf --select-pages 1,3,5-10

# Detection only (no extraction)
detect-pdf document.pdf
detect-pdf document.pdf --json

# Detection + layout analysis (tables, columns)
detect-pdf document.pdf --analyze --json

Rust and CLI consumers opt into OCR at build time:

bash
cargo install pdf-inspector --features ocr --bin pdf2md
PDFIUM_LIB_PATH=/path/to/libpdfium ORT_DYLIB_PATH=/path/to/libonnxruntime \
  pdf2md scan.pdf --ocr auto --json

The OCR JSON envelope is versioned and reports routed pages, per-page source and confidence, warnings, and pages recommended for the hosted document pipeline. Native Python and Node packages expose the same pipeline without a source-build feature. All native entry points still require separately installed PDFium and ONNX Runtime libraries only when OCR is routed. See the OCR runtime setup guide for pinned downloads, platform support, model-cache behavior, and hosted-fallback integration. See the Rust API guide for lower-level controls.

From a source checkout, use cargo run --bin pdf2md -- document.pdf or cargo run --bin detect-pdf -- document.pdf instead.

Architecture

code
PDF bytes
  │
  ├─► detector         → PdfType (TextBased / Scanned / ImageBased / Mixed)
  │
  └─► extractor
        ├─ fonts        → font widths, encodings
        ├─ content_stream → walk PDF operators → TextItems + PdfRects
        ├─ xobjects     → Form XObject text, image placeholders
        ├─ links        → hyperlinks, AcroForm fields
        └─ layout       → column detection → line grouping → reading order
              │
              ├─► tables
              │     ├─ detect_rects      → rectangle-based tables (union-find)
              │     ├─ detect_heuristic  → alignment-based tables
              │     ├─ grid              → column/row assignment → cells
              │     └─ format            → cells → Markdown table
              │
              └─► markdown
                    ├─ analysis     → font stats, heading tiers
                    ├─ preprocess   → merge headings, drop caps
                    ├─ convert      → line loop + table/image insertion
                    ├─ classify     → captions, lists, code
                    └─ postprocess  → cleanup → final Markdown

The document is loaded once via load_document_from_path / load_document_from_mem and shared between the detection and extraction stages, so there's no redundant parsing.

Project structure

code
src/
  lib.rs                — Public API, PdfOptions builder, convenience functions
  python.rs             — PyO3 Python bindings
  types.rs              — Shared types: TextItem, TextLine, PdfRect, ItemType
  text_utils.rs         — Character/text helpers (CJK, RTL, ligatures, bold/italic)
  process_mode.rs       — ProcessMode enum (DetectOnly, Analyze, Full)
  detector.rs           — Fast PDF type detection without full document load
  glyph_names.rs        — Adobe Glyph List → Unicode mapping
  tounicode.rs          — ToUnicode CMap parsing for CID-encoded text
  extractor/            — Text extraction pipeline
  tables/               — Table detection and formatting
  markdown/             — Markdown conversion and structure detection
  bin/                  — CLI tools (pdf2md, detect_pdf)
napi/                   — Node.js/Bun bindings (napi-rs)
wasm/                   — Browser bindings (wasm-bindgen)

How classification works

  1. 1Parse the xref table and page tree (no full object load)
  2. 2Select pages based on ScanStrategy (default: all pages with early exit)
  3. 3Look for Tj/TJ (text operators) and Do (image operators) in content streams
  4. 4Classify based on text operator presence across sampled pages

This detects 300+ page PDFs in milliseconds. The result includes pages_needing_ocr — a list of specific page numbers that lack text, enabling per-page OCR routing instead of all-or-nothing.

Scan strategies

StrategyBehaviorBest for
EarlyExit (default)Scan all pages, stop on first non-text pagePipelines routing TextBased PDFs to fast extraction
FullScan all pages, no early exitAccurate Mixed vs Scanned classification
Sample(n)Sample n evenly distributed pages (first, last, middle)Very large PDFs where speed matters more than precision
Pages(vec)Only scan specific 1-indexed page numbersWhen the caller knows which pages to check

Markdown output

The converter handles:

ElementHow it's detected
Headings (H1-H4)Font size tiers relative to body text, with 0.5pt clustering
Bold/italicFont name patterns (Bold, Italic, Oblique)
Bullet lists, -, *, , , prefixes
Numbered lists1., 1), (1) patterns
Letter listsa., a), (a) patterns
Code blocksMonospace fonts (Courier, Consolas, Monaco, Menlo, Fira Code, JetBrains Mono) and keyword detection
TablesRectangle-based detection from PDF drawing ops + heuristic detection from text alignment
Financial tablesToken splitting for consolidated numeric values
Captions"Figure", "Table", "Source:" prefix detection
Sub/superscriptFont size and Y-offset relative to baseline
URLsConverted to Markdown links
HyphenationRejoins words broken across lines
Page numbersFiltered from output
Drop capsLarge initial letters merged with following text
Dot leadersTOC-style dots collapsed to " ... "

Use case: smart PDF routing

pdf-inspector was built for pipelines that process PDFs at scale. Instead of sending every PDF through OCR:

code
PDF arrives
  → pdf-inspector classifies it (~20ms)
  → TextBased + high confidence?
      YES → extract locally (~150ms), done
      NO  → send to OCR service (2-10s)

This saves cost and latency for the majority of PDFs that are already text-based (reports, papers, invoices, legal docs).

Debugging

See docs/debugging.md for RUST_LOG environment variable usage.

License

MIT

#markdown#nodejs#ocr-routing#pdf#pdf-classification#pdf-extraction#pdf-parser#python#rust#text-extraction