Fast Rust library for PDF inspection, classification, and text extraction. Intelligently detects scanned vs text-based PDFs to enable smart routing decisions.
pdf-inspector
一款用于 PDF 分类与文本提取的高性能 Rust 库。默认情况下,它会判断 PDF 是文本型还是扫描型,并以位置感知方式提取文本,将其转换为干净的 Markdown,而无需 OCR。原生 Rust 和 CLI 使用者可选择启用选择性 OCR。提供 Python、Node.js 以及浏览器 WebAssembly 绑定。
由 Firecrawl 打造,可在本地于 200ms 内处理文本型 PDF,并为约 54% 无需 OCR 的 PDF 跳过昂贵的 OCR 服务。
功能特性
- 智能分类 — 通过采样内容流,在约 10-50ms 内检测 TextBased、Scanned、ImageBased 或 Mixed PDF。返回置信度分数(0.0-1.0)和逐页 OCR 路由。
- 文本提取 — 位置感知提取,包含字体信息、X/Y 坐标,并自动恢复多栏阅读顺序。旋转的文本行(页边印章、图表轴标题)保留真实的轴对齐包围盒,并报告其
rotation角度,而不是将宽度折叠为零。 - Markdown 转换 — 标题(H1-H4,基于字号比例)、项目符号/编号/字母列表、代码块(等宽字体检测)、表格(基于矩形和启发式规则)、粗体/斜体格式、URL 链接以及分页。
- 表格检测 — 双模式:基于 PDF 绘图指令的矩形检测,以及基于文本对齐的启发式检测。可处理财务表格、脚注以及跨页的连续表格。
- CID 字体支持 — 支持 Type0/Identity-H 字体的 ToUnicode CMap 解码,支持 UTF-16BE、UTF-8 和 Latin-1 编码。
- 多栏布局 — 自动检测报纸式栏位、顺序阅读顺序,并支持 RTL 文本。
- 编码问题检测 — 自动标记损坏的字体编码,以便调用方回退到 OCR。
- 选择性 OCR — Rust、CLI、Python 和 Node 可仅渲染需要 OCR 的页面,在本地运行 PP-OCRv6 Small,并保留逐页来源信息以及托管回退建议。
- 单次文档加载 — 文档仅解析一次,并在检测和提取之间共享,避免冗余 I/O。
- 浏览器 WebAssembly — 在浏览器和 Web Workers 中本地运行相同的 Rust 解析器,内置 CMaps,无需服务器往返。
- 默认轻量 — 默认 Rust 和浏览器构建保持纯提取。原生 Python 和 Node 软件包包含 OCR 集成,但 PDFium、ONNX Runtime 和模型文件仍保持外部依赖状态,仅在页面被路由到 OCR 时才会被加载。
基准测试
在 opendataloader-bench 语料库(200 个 PDF)上评估。仅展示不使用基于模型的 PDF 解析的本地引擎;已禁用 OCR。分数为 0-1,越高越好。
| 引擎 | 总体 | 阅读顺序 (NID) | 表格 (TEDS) | 标题 (MHS) | 速度 (200 份文档) |
|---|---|---|---|---|---|
| pdf-inspector | 0.875 | 0.915 | 0.814 | 0.788 | 0.470s |
| liteparse | 0.873 | 0.913 | 0.693 | 0.811 | 0.750s |
| opendataloader | 0.831 | 0.902 | 0.489 | 0.739 | 2.569s |
| pymupdf4llm | 0.735 | 0.886 | 0.401 | 0.424 | 17.117s |
| markitdown | 0.589 | 0.844 | 0.273 | 0.000 | 16.165s |
结果已于 2026 年 7 月 31 日在 Apple M4 Pro 上更新。引擎版本分别为 pdf-inspector 0.2.6、LiteParse 2.10.1、OpenDataLoader 2.2.1、PyMuPDF4LLM 0.2.0 和 MarkItDown 0.1.5。速度为排除一次预热运行后,五次交替或轮换完整语料库运行的中位数;每个解析器都在单个进程中按顺序处理文档。
完整的解析器配置、逐文档预测、评估器输出以及生成的图表,可在可复现结果分支中获取。
最佳适用场景: 对速度、阅读顺序和表格结构要求较高的原生文本 PDF。在本次对比中,pdf-inspector 取得了更高的总体、阅读顺序和表格得分,并拥有最快的完整运行耗时。这使它成为报告、研究论文、财务文件、发票和法律 PDF 的理想本地默认方案,这些文件需要干净、结构化的 Markdown,且无需引入额外的 OCR 延迟或基础设施。
使用配对基准测试框架,在完全相同的语料库和评估器版本下比较两个本地构建版本。
快速开始
Python
pip install pdf-inspector
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)
完整 API 参考:docs/python.md
Node.js
npm install @firecrawl/pdf-inspector
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);
完整 API 参考:napi/README.md
浏览器 WebAssembly
npm install @firecrawl/pdf-inspector-wasm
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 安装:
cargo add pdf-inspector
或者手动添加:
[dependencies]
pdf-inspector = "1"
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
# 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 (coordinates relative to the visible page box): axis-aligned box, rotation, font, 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 与 CLI 使用者可在构建时选择启用 OCR:
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
带版本号的 OCR JSON 封装会报告已路由页面、每页来源与置信度、警告信息,以及建议交由托管文档流水线处理的页面。原生 Python 和 Node 包可暴露相同流水线,无需源码构建特性。所有原生入口点仍然只在 OCR 被路由时,才需要单独安装的 PDFium 和 ONNX Runtime 库。如需了解固定版本下载、平台支持、模型缓存行为以及托管回退集成,请参阅 OCR 运行时配置指南。如需更低层级的控制,请参阅 Rust API 指南。
在源码检出环境中,请改用 cargo run --bin pdf2md -- document.pdf 或 cargo run --bin detect-pdf -- document.pdf。
架构
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 仅加载一次,并在检测与提取阶段之间共享,因此不存在重复解析。
项目结构
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)
分类机制
- 解析 xref 表和页树(不加载完整对象)
- 根据
ScanStrategy选择页面(默认:扫描全部页面并提前退出) - 在内容流中查找
Tj/TJ(文本操作符)和Do(图像操作符) - 根据采样页面中是否存在文本操作符进行分类
该机制可在毫秒内识别 300 页以上的 PDF。结果包含 pages_needing_ocr——缺少文本的具体页码列表,从而支持按页进行 OCR 路由,而不是全有或全无。
扫描策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
EarlyExit(默认) |
扫描所有页面,遇到第一个非文本页面时停止 | 适用于将 TextBased PDF 路由到快速提取的流水线 |
Full |
扫描所有页面,不提前退出 | 准确区分 Mixed 与 Scanned |
Sample(n) |
采样 n 个均匀分布的页面(首页、末页、中间页) |
适用于速度优先于精确度的超大 PDF |
Pages(vec) |
仅扫描指定的从 1 开始的页码 | 适用于调用方已知需要检查哪些页面的场景 |
Markdown 输出
转换器支持处理:
| 元素 | 检测方式 |
|---|---|
| 标题(H1-H4) | 相对于正文的字号分层,并采用 0.5pt 聚类 |
| 粗体/斜体 | 字体名称模式(Bold、Italic、Oblique) |
| 无序列表 | •、-、*、○、●、◦ 前缀 |
| 有序列表 | 1.、1)、(1) 模式 |
| 字母列表 | a.、a)、(a) 模式 |
| 代码块 | 等宽字体(Courier、Consolas、Monaco、Menlo、Fira Code、JetBrains Mono)及关键字检测 |
| 表格 | 基于 PDF 绘制操作中的矩形检测,加上基于文本对齐的启发式检测 |
| 财务表格 | 对合并的数值进行拆分 |
| 图注/表注 | 检测 "Figure"、"Table"、"Source:" 前缀 |
| 上标/下标 | 相对于基线的字号和 Y 轴偏移 |
| 网址 | 转换为 Markdown 链接 |
| 连字符断行 | 将跨行拆分的单词重新连接 |
| 页码 | 从输出中过滤掉 |
| 首字下沉 | 将大号首字母与后续文本合并 |
| 点线引导符 | 目录样式的点线被折叠为 " ... " |
应用场景:智能 PDF 路由
pdf-inspector 专为大规模处理 PDF 的流水线而打造。无需将每一份 PDF 都进行 OCR 处理:
PDF arrives
→ pdf-inspector classifies it (~20ms)
→ TextBased + high confidence?
YES → extract locally (~150ms), done
NO → send to OCR service (2-10s)
这有助于降低大多数本身即为文本型 PDF 文件(如报告、论文、发票和法律文档)的处理成本与延迟。
调试
有关 RUST_LOG 环境变量的用法,请参阅 docs/debugging.md。
许可证
Introduction
用于 PDF 检查、分类和文本提取的快速 Rust 库。智能检测扫描型与文本型 PDF,助力实现智能路由决策。【此简介由AI生成】