
pdf-inspector Node.js/Bun 原生绑定实战基于 napi-rs 的 PDF 分类与选择性 OCR API 完全指南【免费下载链接】pdf-inspectorFast Rust library for PDF inspection, classification, and text extraction. Intelligently detects scanned vs text-based PDFs to enable smart routing decisions.项目地址: https://gitcode.com/GitHub_Trending/pdf/pdf-inspectornapi/README.md是 pdf-inspector 项目面向 Node.js/Bun 生态的官方 API 指南本文以该文档为主体结合仓库中 napi/src/lib.rs 的 napi-rs 绑定源码与 napi/test.mjs 的端到端测试用例完整讲解如何在 Node.js/Bun 中调用firecrawl/pdf-inspector从智能分类、区域化文本提取、坐标体系解析到选择性 OCR 的路由、离线部署与异步线程池模型。读完本文你将能够在一个真实的 JS 服务中直接接入文本 PDF 本地快速解析、扫描 PDF 按页路由 OCR的混合管线。一、包是什么用 Rust 原生性能为 JS 提供 PDF 智能管线firecrawl/pdf-inspector是 pdf-inspector一个用 Rust 编写的高速 PDF 检测、分类与文本提取库的 Node.js/Bun 绑定通过 napi-rs其依赖napi { version 3.0.0, features [serde-json] }与napi-derive 3.0.0编译为原生.node动态库。它的设计目标非常聚焦为混合 OCR 管线服务——能从 PDF 结构提取文本的地方就提取只有必要时才回退到 OCR。包的能力清单源自文档 Features 一节逐一与源码对应能力说明源码佐证智能分类text-based / scanned / image-based / mixed约 10–50ms带置信度分数与逐页 OCR 路由napi/src/lib.rs 中PdfType枚举TextBased/Scanned/ImageBased/Mixed与classify_pdf函数区域化提取从包围盒中提取文本每个区域带needsOcr质量检查extract_text_in_regions、PageRegions/RegionText结构布局感知多栏阅读顺序、每项文本的位置与字体信息、RTL 支持TextItem的x/y/rotation/font字段及extract_pages_markdown的pagesWithColumns稳健文本解码CID/Type0 字体经 ToUnicode CMap 解码自动标记损坏编码供调用方回退 OCRlegacy_symbol_rewrite字段与hasEncodingIssues标志选择性 OCRAuto模式只路由被原生提取拒绝的页面返回来源/模型溯源与托管回退建议OcrMode枚举Off/Auto/Force、process_pdf_with_ocr及OcrPdfResult.pagesRecommendingHosted外部工件原生包不内嵌 OCR 模型、PDFium 或 ONNX Runtime干净的Auto请求从不加载或下载它们docs/ocr-runtime.md 明确模型在运行时下载不内嵌于任何 pdf-inspector 包二、性能基准在 opendataloader-bench 上的定位文档给出了作者方在 opendataloader-bench 的 Benchmark 一节补充了各引擎版本pdf-inspector 0.2.6、LiteParse 2.10.1、OpenDataLoader 2.2.1、PyMuPDF4LLM 0.2.0、MarkItDown 0.1.5引擎OverallReading orderTables (TEDS)HeadingsSpeed200 文档pdf-inspector0.8750.9150.8140.7880.470sliteparse0.8730.9130.6930.8110.750sopendataloader0.8310.9020.4890.7392.569spymupdf4llm0.7350.8860.4010.42417.117smarkitdown0.5890.8440.2730.00016.165s需要说明的是这是一份仓库文档声明的对比结果具体到你的硬件与语料请使用 docs/benchmarking.md 介绍的配对基准工具在相同语料与评估器修订版上自行复现验证而非直接外推。三、安装与平台支持3.1 安装命令npm install firecrawl/pdf-inspector # 或 bun add firecrawl/pdf-inspector当前仓库中 napi/package.json 记录的版本为1.19.0包同时暴露index.js与index.d.tsTypeScript 类型声明并带有一个pdf-inspector的 CLI bin。安装时不需要 Rust 工具链——二进制以平台专属包形式随optionalDependencies自动安装npm 只会安装与当前平台匹配的那一个平台架构对应 npm 平台包Linuxx64glibcfirecrawl/pdf-inspector-linux-x64-gnuLinuxx64musl/Alpinefirecrawl/pdf-inspector-linux-x64-muslLinuxARM64glibcfirecrawl/pdf-inspector-linux-arm64-gnuLinuxARM64musl/Alpinefirecrawl/pdf-inspector-linux-arm64-muslmacOSARM64firecrawl/pdf-inspector-darwin-arm64Windowsx64firecrawl/pdf-inspector-win32-x64-msvc这些目标与 napi/Cargo.toml 中声明的 napi 构建目标一一对应也印证在 napi/package.json 的napi.targets数组x86_64-unknown-linux-gnu、x86_64-unknown-linux-musl、aarch64-unknown-linux-gnu、aarch64-unknown-linux-musl、aarch64-apple-darwin、x86_64-pc-windows-msvc中。源码构建则使用napi build --platform --release。3.2 OCR 运行时前置条件关键事实干净的Auto请求不会加载任何 OCR 依赖。只有当原生提取判定至少一个页面需要 OCR即路由发生时进程才需要以下三样外部工件详见 docs/ocr-runtime.mdPDFium验证版本为 Firecrawl PDFiumnative-v7988含 PDFium153.0.7988.0ONNX Runtime验证版本1.27.0PP-OCRv6 Small 模型集固定工件修订oar-ocr-v0.7.0来自 GreatV/oar-ocrApache-2.0 许可约 31MB含检测模型、识别模型与字符字典三件套。当这些共享库不在平台库搜索路径上时通过环境变量指定# Linux export PDFIUM_LIB_PATH/absolute/path/to/libpdfium.so export ORT_DYLIB_PATH/absolute/path/to/libonnxruntime.somacOS 上文件名以.dylib结尾Windows 上用 PowerShell 指向pdfium.dll与onnxruntime.dll$env:PDFIUM_LIB_PATH C:\absolute\path\to\pdfium.dll $env:ORT_DYLIB_PATH C:\absolute\path\to\onnxruntime.dll模型在首个被路由页面触发下载并做 SHA-256 校验缓存位置由PDF_INSPECTOR_MODEL_CACHE指定管理根目录。对于封闭/离线部署预先填充模型目录并使用offline: true配合modelDirectoryNode.js 侧写法CLI 对应--ocr-offline --ocr-model-dirRust 对应ModelDownloadPolicy::OfflinePython 对应offlineTrue, model_directory...即可完全禁止网络访问。还有一个重要的托管回退边界docs/ocr-runtime.md 的 Hosted fallback boundary 一节pagesRecommendingHosted只有在本地管线成功完成后才出现它标记OCR 结果为空、低置信或仍不完整的页面——这是页面质量层面的判断而缺库、模型获取失败、OCR 执行报错属于部署/执行失败会作为错误抛出由下游集成捕获后把整个文档路由给托管解析服务。这两种情况被刻意区分开避免把部署问题误判为页面质量问题。四、核心 APIprocessPdfWithOcr选择性 OCR 一站式入口4.1 签名与模式processPdfWithOcr(buffer: Buffer, options?: OcrOptions): PromiseOcrPdfResult先运行原生提取只对质量信号选中的页面执行 OCR。默认模式是AutoAuto仅对被原生质量信号拒绝的页面运行 OCROff不触碰任何外部运行时直接返回与 OCR 模式相同形状的详细结果即只走原生提取但复用 OCR 结果结构Force对每个被选中的页面强制 OCR。该调用运行在libuv 线程池上源码中通过AsyncTaskProcessPdfWithOcrTask实现见 napi/src/lib.rs 的process_pdf_with_ocr不会阻塞 Node 的事件循环。三种模式在 Rust 侧由OcrMode枚举体现并经由to_core_ocr_options映射到核心库的pdf_inspector::vision::OcrPdfOptions。4.2 基础用法import { OcrMode, processPdfWithOcr } from firecrawl/pdf-inspector const result await processPdfWithOcr(pdf, { mode: OcrMode.Auto, pageNumbers: [1, 3], // 1-indexed }) for (const page of result.pages) { console.log(page.pageNumber, page.provenance.source) } console.log(result.pagesRoutedToOcr) console.log(result.pagesRecommendingHosted)4.3 OcrOptions 完整参数源码级说明对照 napi/src/lib.rs 中OcrOptions结构体的定义与文档说明各选项如下选项类型默认值说明modeOcrModeAutoOCR 路由行为pageNumbersnumber[]全部可选的1-indexed页面选择passwordstring无加密 PDF 的密码dpinumber150页面栅格化分辨率minimumConfidencenumber由核心库决定低于此0–1 阈值的 OCR 文本 span 会被丢弃hostedRecommendationConfidencenumber由核心库决定页面置信度低于此0–1 阈值时推荐走托管解析modelDirectorystring平台缓存离线 OCR 模型集所在目录offlinebooleanfalse禁止模型下载要求已有模型目录或热缓存在源码映射中offline: true会把下载策略设为ModelDownloadPolicy::Offlineresult.ocr.model_downloads ...Offline而dpi写入result.render.dpiminimumConfidence写入result.ocr.minimum_confidencehostedRecommendationConfidence写入result.hosted_recommendation_confidence清晰展示了 JS 层参数与核心管线渲染/OCR/回退三阶段的一一对应关系。4.4 OcrPdfResult 返回结构interface OcrPdfResult { markdown: string pages: OcrPageResult[] // 1-indexed 页面 provenance pageCount: number pagesRecommendedForOcr: number[] pagesRoutedToOcr: number[] pagesRecommendingHosted: number[] ocrReasonsByPage: PageOcrReasons[] pagesWithTables: number[] pagesWithColumns: number[] isComplex: boolean processingTimeMs: number renderTimeMs: number ocrTimeMs: number }其中每个OcrPageResult的provenanceOcrPageProvenance携带sourceNative/Ocr/Fused三选一见PageContentSource枚举、ocrModel模型名 修订号OcrModelIdentity、renderDpi、ocrConfidence、timingsrenderMs/ocrMs/assemblyMs与warnings——这正是来源/模型溯源的实现载体。napi/test.mjs 验证了Off模式下每个页面的provenance.source Native且ocrModel undefined而干净文本 PDF 在Auto下pagesRoutedToOcr为空、renderTimeMs与ocrTimeMs均为 0——即轻量路径被完整保留。五、轻量分类classifyPdfclassifyPdf(buffer: Buffer): PdfClassification将 PDF 分类为 TextBased / Scanned / Mixed / ImageBased约 10–50ms并返回哪些页面需要 OCRimport { classifyPdf } from firecrawl/pdf-inspector import { readFileSync } from fs const pdf readFileSync(document.pdf) const result classifyPdf(pdf) console.log(result.pdfType) // TextBased | Scanned | Mixed | ImageBased console.log(result.pageCount) // 42 console.log(result.pagesNeedingOcr) // [5, 12, 15] (0-indexed) console.log(result.confidence) // 0.875interface PdfClassification { pdfType: string // TextBased | Scanned | Mixed | ImageBased pageCount: number pagesNeedingOcr: number[] // 0-indexed page numbers confidence: number // 0.0 - 1.0 }注意一个索引约定差异classifyPdf的pagesNeedingOcr是0-indexed源码注释明确说明而processPdfWithOcr的pageNumbers、pagesRoutedToOcr等是1-indexedextractTextInRegions的PageRegions.page又是 0-indexed。跨 API 混用时务必小心。源码中classify_pdf直接调用核心库的classify_pdf_mem比detectPdf更轻——它跳过了构建完整PdfResult的过程只返回类型、页数与 OCR 页列表。测试 napi/test.mjs 也验证了detectPdf的markdown为undefined仅检测不做提取。六、坐标体系extractTextWithPositions 与可见页盒Visible Page Box6.1 坐标基准extractTextWithPositions(buffer: Buffer, pages?: number[]): TextItem[]每个文本项外加图片占位符、链接与表单字段都带字体与位置信息。文档与 napi/src/lib.rs 的TextItem注释共同定义了坐标基准x/y是PDF 点相对于页面的可见页盒CropBox ∩ MediaBox否则用MediaBox不与 MediaBox 相交的 CropBox 被忽略无 MediaBox 的页面按 US Letter 度量原点位于页盒左下角y向上增长而extractTextInRegions的 region 坐标相对于同一个盒子的左上角y向下增长因此转换公式为boxHeight - y页面的/Rotate不被应用。对文本项y是基线、height是字号所以字形带glyph band基线上方部分的包围盒是[x, boxHeight - y - height, x width, boxHeight - y]下伸部会落在该盒之下对图片、链接和表单字段项y是矩形底边上述盒子是精确的。CropBox 与 MediaBox 重合在(0, 0)的页面不受影响。test.mjs 用cropbox_offset_origin.pdf夹具精确验证了这一体系MediaBox[0 0 400 500]、CropBox[50 60 350 460]在原始坐标(120, 300)写下的字形在可见页盒左下角坐标系中应位于(70, 240)测试断言两者误差均在0.01以内随后用boxHeight - y转换出 region 框extractTextInRegions恰好只提取到该行文字。6.2 TextItem 的完整字段除文档强调的page/text/x/y/fontSize外napi/src/lib.rs 暴露了更丰富的字段部分仅在特定 PDF 中出现width/height轴线对齐盒尺寸rotation基线逆时针旋转角度0普通横排、90自下而上如旋转页边章、270自上而下、180倒置旋转的文本运行保持真实的高瘦盒子而非塌缩为零宽——测试用rotated_margin_stamp.pdf验证了 arXiv 页边章的rotation ≈ 90且height 10 * widthadvanceKnown运行的前进量是否来自字体度量false表示字体无宽度信息时按每绘制字形半个 em 估计font/fontTag/fontSizelegacySymbolRewrite仅当遗留私有区符号清理确实改变了字符时才存在——这是解码溯源标记而非 OCR 请求合并项保留两侧证据、分裂项保守继承未发生改写时字段被省略缺失不等于解码准确。消费方在修正其他文本时应避免把被重写的符号当作权威 Unicode 值isBold/isItalic/isUnderline/isStrikeout后两者由几何检测基线下的规则/细矩形判下划线贯穿字形中部判删除线baselineShift上下标运行相对所依附正文基线的带符号基线偏移正上标负下标普通文本为 0紧邻单词的数字型上标已融合为 Unicode 上下标字符如word²并携带 0itemTypeText/Image/Link/FormFieldlinkUrl仅链接项存在mcid内容流中 BDC/BMC 标记内容 ID可与extractStructureElements返回的(page, mcid)对做 JOIN把 tagged PDF 的结构树角色H1–H6、P、Table、TD 等挂接到文本上非 tagged PDF 为undefined。6.3 旋转页面的坐标系翻转test.mjs 还验证了extractTextWithPositionsAndRotations当页面文本以旋转为主时提取器会翻转坐标系使文本左→右阅读pageRotations报告被翻转的页面及其方向ccw/cw1-indexed 页码直立页面不出现在该数组中。例如tnagriculture_06_12.pdf被断言为[{ page: 1, rotation: ccw }]。需要逐页感知坐标帧的场景应使用这个 API 而非extractTextWithPositions。七、区域提取extractTextInRegions混合 OCR 管线的关键拼图7.1 设计与用法extractTextInRegions(buffer: Buffer, pageRegions: PageRegions[]): PageRegionTexts[]这个 API 专为混合 OCR 管线设计布局模型在渲染出的页面图像上检测区域本函数则直接从 PDF 结构提取这些区域内的文本——对文本型页面跳过 GPU OCR。import { extractTextInRegions } from firecrawl/pdf-inspector const result extractTextInRegions(pdf, [ { page: 0, // 0-indexed regions: [ [0, 0, 300, 400], // [x1, y1, x2, y2] in PDF points, top-left origin of the visible page box (CropBox) [300, 0, 612, 400], ] } ]) for (const region of result[0].regions) { if (region.needsOcr) { // Unreliable text — send this region to OCR instead } else { console.log(region.text) // Extracted text in reading order } }每个区域结果带一个needsOcr标志标记提取不可靠的情形空文本、GID 编码字体、乱码文本、编码问题。当怀疑是乱码文本层所致时ocrReason被设为suspected_garbled_text。interface PageRegions { page: number // 0-indexed regions: number[][] // [[x1, y1, x2, y2], ...] in PDF points, top-left origin of the visible page box } interface PageRegionTexts { page: number regions: RegionText[] } interface RegionText { text: string needsOcr: boolean // true when text is unreliable ocrReason?: string // suspected_garbled_text when known }7.2 区域表格提取家族源码扩展围绕区域概念napi/src/lib.rs 还提供了一组表格相关 API文档虽未逐一展开但同属该设计extractTablesInRegions与extractTextInRegions同参数但对区域内条目跑表格检测并返回 Markdown 管道表检测到表格时text为管道表且needsOcr为false未发现表格时text为空、needsOcr为true供调用方回退 GPU OCRdetectVectorGridInRegion检测区域内矢量规则线/矩形网格返回 TSR 兼容的结构 token 与 crop 像素单元盒pageIdx0-indexedrenderDpi是消费返回单元盒的 crop 图像 DPInapi/probe-indent.mjs 展示了它的探针用法extractTablesWithStructure/extractTablesWithStructureCells/extractTablesWithStructureAuto由外部表格结构识别模型如 PaddleOCR 上的 SLANet在渲染 crop 上产出structureTokens与cellBboxespdf-inspector 用该结构布局单元格、从原生 PDF 拉取单元格文本——全程无 OCR。Auto变体会检查已知的 SLANet 检测病态幻影空行phantom_empty_row、单格多行multi_row_in_cell_expanded必要时原地展开多行单元格或回退到启发式extractTablesInRegionsfallbackReason携带诊断标签。这些 API 与extractTextInRegions共用同一坐标约定可见页盒、左上原点、PDF 点构成版面模型给区域 → 原生结构提文本 → 不可靠再 OCR的完整闭环。八、异步变体与事件循环友好设计文档明确指出processPdf、classifyPdf、extractPagesMarkdown是同步的解析发生在调用线程——在 Node 中即事件循环。脚本里一次性调用没问题但服务器场景下大文档可能阻塞事件循环数十到数百毫秒。对应地processPdfAsync, classifyPdfAsync, extractPagesMarkdownAsync接收相同参数、产生相同结果但解析运行在libuv 线程池上并返回 Promise事件循环保持空闲。输入 Buffer 在调用返回前就被复制因此可以立即复用或修改它import { classifyPdfAsync, extractPagesMarkdownAsync } from firecrawl/pdf-inspector const classification await classifyPdfAsync(pdf) if (classification.pdfType TextBased) { const { pages } await extractPagesMarkdownAsync(pdf) // ... }napi/src/lib.rs 的实现注释解释了这一设计的深层原因napi Buffer 跨线程读取是 napi-rs 已知的健全性soundness隐患——JS 单线程保证了拷贝期间无并发修改但如果零拷贝让 worker 直接读 Buffer调用方在 Promise 落定前修改缓冲区就会与 worker 读操作竞争这是未定义行为而非可恢复错误。因此每个*TaskProcessPdfTask、ClassifyPdfTask、ExtractPagesMarkdownTask都在 JS 线程完成buffer.to_vec()的一次性 memcpy其代价相比被释放的解析开销可忽略。所有compute还包裹了catch_panic把 Rust panic 转为 NAPI 错误而非中止进程。test.mjs 的验证点包括异步结果与同步结果逐字段相等processPdfAsync(scratch)后立刻scratch.fill(0)结果不受影响输入拷贝验证三个异步调用Promise.all并发全部正常落定非法输入not a pdf、空 Buffer分别以/process_pdf/、/classify_pdf/、/extract_pages_markdown/模式拒绝。九、同步 API 速查含文档之外的低级入口文档正文覆盖了processPdfWithOcr、classifyPdf、extractTextWithPositions、extractTextInRegions与三个*Async变体。作为扩展napi/src/lib.rs 还暴露了以下同步入口README 顶层 Quick start 亦有呼应processPdf(buffer, pages?)一次调用完成检测类型 提取文本 转 Markdown返回PdfResult含pdfType、markdown、pageCount、pagesNeedingOcr、ocrReasonsByPage、title、confidence、isComplexLayout、pagesWithTables、pagesWithColumns、hasEncodingIssuesdetectPdf(buffer)仅快速检测不提取不生成 markdownextractText(buffer)返回纯文本字符串extractTextWithPositionsAndRotations(buffer)定位文本 被翻转页面的坐标帧信息extractStructureElements(buffer, pages?)从 tagged PDF 解析结构树返回(page, mcid, role)引用列表与TextItem.mcid按(page, mcid)连接即可恢复 H1–H6 等语义角色文本未 tagged 的 PDF 返回空数组测试断言firecrawl_docs_tagged.pdf能表面出 H1 角色并成功 JOIN 出标题文本extractPagesMarkdown(buffer, pages?)逐页 Markdown 提取 布局分类元数据省略pages按文档序返回所有页传入数组则保持调用方顺序测试验证[2, 0]返回顺序为页 2、页 0注意这里页码是 0-indexed而extractTextWithPositions的 pages 参数是 1-indexedextractStructureElements也是 1-indexed 与TextItem.page一致——两个索引约定并存调用前务必确认。十、工程细节与常见陷阱小结综合文档与源码以下实践要点值得记住索引约定三套并存classifyPdf.pagesNeedingOcr0-indexed、processPdfWithOcr.pageNumbers1-indexed、extractTextInRegions/extractTablesInRegions/detectVectorGridInRegion.pageIdx0-indexed、extractTextWithPositions/extractStructureElements的 pages 参数1-indexed、extractPagesMarkdown的 pages 参数0-indexed 且保序。混用是最高发错误test.mjs 甚至断言了pageNumbers: [0]会因page 0被拒绝坐标体系以可见页盒为唯一基准CropBox 偏移的页面务必用boxHeight - y在左下原点向上与左上原点向下两套 API 之间换算服务器上优先用*Async变体且可放心复用输入 BufferOCR 是按需外部依赖干净的文本 PDF 在Auto下不加载 PDFium/ONNX Runtime/模型只有路由发生才需要配置PDFIUM_LIB_PATH/ORT_DYLIB_PATH离线场景用offline: truemodelDirectory部署失败与页面质量失败要分开处理前者抛错捕获后整体回退托管后者体现在pagesRecommendingHosted完成后再判断解码溯源字段是提示而非承诺legacySymbolRewrite缺失不代表解码绝对准确消费方只应据此避免把被重写的符号当作权威 Unicode 值。十一、许可证与进一步阅读许可证MIT见 napi/package.json 的license: MIT与仓库 LICENSENode.js 绑定完整实现napi/src/lib.rs、构建配置 napi/package.json、端到端测试 napi/test.mjsOCR 运行时PDFium/ONNX Runtime 固定版本、模型缓存、离线模式、托管回退边界docs/ocr-runtime.md顶层项目概览与多语言入口Python / WebAssembly / Rust / CLIREADME.md其中 Python 绑定见 docs/python.md、浏览器 WebAssembly 见 wasm/README.md、Rust API 见 docs/rust-api.md测试夹具位于 tests/fixtures含 rotated_margin_stamp.pdf、cropbox_offset_origin.pdf、firecrawl_docs_tagged.pdf、tnagriculture_06_12.pdf 等本文提及用例的对应 PDF。一句话收束firecrawl/pdf-inspector的核心价值在于分类先行、按页路由、外部依赖零侵入——用classifyPdf做毫秒级分流用extractTextInRegions/extractTablesWithStructure*承接布局模型给出的区域用processPdfWithOcr在Auto下只为必要页面引入 OCR同时通过*Async变体把这一切挡在事件循环之外。【免费下载链接】pdf-inspectorFast Rust library for PDF inspection, classification, and text extraction. Intelligently detects scanned vs text-based PDFs to enable smart routing decisions.项目地址: https://gitcode.com/GitHub_Trending/pdf/pdf-inspector创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考