PDF 检查员
为Node.js/Bun快速进行PDF分类并基于区域的文本提取. 原始 Rust 表演通过 美洲狮。 。 。 .
由 纵火 对于混合式OCR管线——尽可能从PDF结构中取出文本,只有在需要时才返回OCR.
特征
- 智能分类 ——以文字为主/扫描/以图像为主/混合为~10-50ms,并有置信分和每页OCR路由.
- 基于区域的提取 ——从有每个区域质量检查的边框中取出文字(
needsOcr) (中文(简体) ). - 布局意识 ——多列读取顺序,每个文本项的位置和字体信息,RTL支持.
- 强大的文本解码 ——CID/Type0字体通过ToUnicode CMaps,外加被破解的编码被自动标出,因此呼叫者可以倒回OCR.
- 选择性 OCR —— 说
Auto路径仅被本地提取和返回源/模式出处拒绝的页面加上主机回落建议. - 外部文物 – 本地包没有嵌入 OCR 模型、 PDFium 或 ONNX 运行时; 干净
Auto请求绝不加载或下载.
基准
打开数据装入器- bench 平面 (200 PDF), 本地引擎没有基于模型的 PDF 解析; OCR 已禁用 。 分数0-1,高点更好:
| 发动机 | 总体情况 | 阅读顺序 | 表格(TEDS) | 标题 | 速度 |
|---|---|---|---|---|---|
| pdf 检查员 | 0.875 (中文(简体) ) | 0.915 (韩语) | 0.814 (中文(简体) ) | 0.788 (简体中文) | 0.470s (单位:千美元) |
| 点数 | 0.873 (中文(简体) ) | 0.913 (简体中文) | 0.693 (单位:千美元) | 0.811 (中文(简体) ) | 0.750兹 |
| 打开数据载入器 | 0.831 (韩语) | 0.902 (中文(简体) ) | 0.489 (韩语) | 0.739 (中文(简体) ) | 2.569s 电话 |
| pymupdf4llm (英语) | 0.735 (简体中文) | 0.886 (中文(简体) ) | 0.401 (简体中文) | 0.424 (中文(简体) ) | 17.117s (韩语) |
| 缩放 | 0.589 (中文(简体) ) | 0.844 (中文(简体) ) | 0.273 (简体中文) | 0.0万个 | 16.165s (中文(简体) ) |
于2026年7月31日在Apple M4 Pro上刷新;速度为被排除出热身后5个完整实体运行的中位数. 整个方法和版本载于 重新读取(原始内容存档于2019-03-21) (中文(简体) ). ) 结果分支。 。 。 .
安装
npm install @firecrawl/pdf-inspector
# or
bun add @firecrawl/pdf-inspector
预建二进制 Linux x64/ARM64 软件 (滑翔和肌肉/高山), 马科斯ARM64,以及 视窗 x64 npm只安装一个匹配您的平台. 不需要拉链.
OCR称路由工作需要兼容的 PDFium 和 ONNX 运行时共享
图书馆。 设定 PDFIUM_LIB_PATH 财务报告和审定财务报表 ORT_DYLIB_PATH 当他们不在的时候
平台库搜索路径。 被绑定的OCR模型集被下载
第一个路由页面上的校验和验证; 使用 offline: true 带着温暖
缓存或 modelDirectory 禁止网络访问。 见
OCR 运行时间设置指南
用于被绑定的下载,支持平台,以及主机倒置行为.
API 密码
processPdfWithOcr(buffer: Buffer, options?: OcrOptions): Promise<OcrPdfResult>
首先运行本地提取, 只运行质量选择的页面
信号 默认模式是 Auto· ; Off 返回相同的详细结果
形状没有外部运行时间工作,以及 Force OCR 每一个选定的页面.
作品运行在libuv线程池上,从未屏蔽出节点事件回路.
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)
离线部署,通过 modelDirectory 财务报告和审定财务报表 offline: true其他项目
控制包括: dpi, (中文(简体) ), minimumConfidence, (中文(简体) ),
hostedRecommendationConfidence,以及 password。 。 。 .
classifyPdf(buffer: Buffer): PdfClassification
将 PDF 分类为基于文本、扫描、混合或基于图像(~10-50ms)。 返回需要OCR的页面.
import { 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.875
extractTextWithPositions(buffer: Buffer, pages?: number[], options?: FrameOptions): TextItem[]
一个同源变体, extractTextWithPositionsAsync,采取同样的论据
对同一项目的决议(见a/ac.96/12和corr.1) Async 变体) (中文(简体) ).
每个文本项目(加图像占位符、链接和窗体字段)及其字体
和立场. x编号 :y 相对于页面的 PDF 点 可见页面
选项框 (单位:千美元)CropBox ∩ MediaBox,否则就是 MediaBox),来源于框左下角
角为 y 向上增长. extractTextInRegions 读取其区域
相对同一框,但从左上角 y 增长
向下,随箱高翻: boxHeight - y文本项目 y 实值
基准和基准 height 字体大小,所以
[x, boxHeight - y - height, x + width, boxHeight - y] 覆盖甘油带
基线以上(底线低于基线);用于图像、链接和窗体-场
项目 y 是矩形底部,那个框是准确的。 CropBox 等值的页面
媒体框 (0, 0) 不受影响.
默认的页面 /Rotate 未应用,文本为
以旋转为主,使文本读取从左到右(这是
"sheet" 边框; extractTextWithPositionsAndRotations 报告页数
(被转而). 过 { frame: "display" } 以获取所交付的每件物品
页面的框取而代之——可见的页面框由页面的顺时针转出
可继承 /Rotate,左下方来源, y 向上,旋转的转弯
页面已撤销——如此 x编号 :y编号 :width编号 :height 财务报告和审定财务报表 rotation 将该项目描述为
造型师画的 页面为 /Rotate 0 其文本并非主要内容
旋转在两个框中都是相同的.
fontWeight 是该字体在 CSS 共享的 100.900 尺度上的重量级
font-weight 和OS/2号文件 usWeightClass 字段(400个正版,700个粗体),改为
来自嵌入式字体程序的 OS/2 表格,否则字体描述器
/FontWeight,否则在字体名("光","中","-Md","-Md")中就有一个重字,
"黑色","W6";当没有人说时被省略. isBold 不变
并且独立于它, 所以一个中等的面相报告 fontWeight: 500 与
isBold: false。通过 { boldFromWeight: true } 改为: isBold 从a
重量级为600(SemiBold)或以上——或 boldWeightThreshold,任意
- 并合并相邻的运行,所以
在较轻的一款内运行的更重的一款保留其自己的物品,同时运行其
权重不同,但同意像往常一样大胆地合并.
boldSource 说在哪里 isBold 来自.. "FontName" (粗体词或
字体名称中的样式缩写, "FontFlags" (字体描述器)
ForceBold旗或嵌入式程序粗体选择),, "WeightClass"
(重量级,与 boldFromWeight)或(或) "Painted" (正文已补正
被打得看起来更重
说大胆,所以一个面孔 他的名字说大胆过一个 fontWeight 四百个
是从体重课上说的 当 isBold 实值
false。 。 。 .
fixedPitch 实值 true 当字体描述器的固定 Pitch 旗或
嵌入式程序的 post 表格中表示字体是单行的,否则会测量
从字体的宽度表 : true 当它的十二个或更多的格言分享时
一个垫款, false 当两个不同。 当字体声明时省略
没有两笔进步,只有不到十几分 许多
生产者写入 /Flags 4
不管脸色如何,所以国旗只读作是.
fillColor 财务报告和审定财务报表 strokeColor 显示运行所用的颜色, 如 sRGB
[red, green, blue] 0.255的数组:它的格子被第一个填入
将模式 0、2、4和6与模式1、2、5和
6. 设备RGB读取为sRGB,设备Gray读取为三个等元件
设备CMYK将PDF规格转换为设备RGB的方式; 2;
基于ICC的空格按其组件数读取,并通过
他们的调色板。 任何其它颜色空间都省略了颜色(分离,
设备N,图案, CalRGB, Lab,. renderMode 文本渲染模式
(单位:千美元)Tr)运行显示为,0.7:运行在模式3(隐形,OCR模式)
文本层)和模式 7(只剪接) 在页面上不放格言,所以一个呼叫者
可以从无形文本中分辨出可见文本。 颜色和模式是
图形状态——它们横跨文本对象, q编号 :Q 你应当拯救他们,你应当使他们复活,
和表格 X 对象从它所引用的油漆开始 ——和报告
他们没有改变任何关于什么运行被提取。 三者皆为省略
图像、链接和窗体-字段项目,一个合并的项目保持其第一次运行.
legacySymbolRewrite: true 标记其已解码文本包含字符的项目
由遗产符号 清理而改变。 合并项目保留了上述证据
源,并保守地分割项目继承它。 当
清理并没有改变一个特性; 缺勤并非一般保证
解码精度. 消费者纠正其他文字可以使用标记来避免
将重写符号视为权威的Unicode值.
import { extractTextWithPositions } from '@firecrawl/pdf-inspector'
for (const item of extractTextWithPositions(pdf, [1])) { // pages are 1-indexed
console.log(item.page, item.text, item.x, item.y, item.fontSize)
}
// Boxes as a renderer draws the page (`/Rotate` applied)
const rendered = extractTextWithPositions(pdf, undefined, { frame: 'display' })
// Bold also from the weight class, SemiBold (600) and heavier
const weighted = extractTextWithPositions(pdf, undefined, { boldFromWeight: true })
// ... or from Bold (700) and heavier
const heavier = extractTextWithPositions(pdf, undefined, { boldFromWeight: true, boldWeightThreshold: 700 })
extractTextWithPositionsAndRotations(buffer: Buffer, pages?: number[], options?: FrameOptions): PositionedText
extractTextWithPositions 加号 pageRotations,一个 { page, rotation: 'ccw' | 'cw' }
每一页的条目,其文本主要旋转,因此在
"sheet" 边框。 与 { frame: "display" } 物品在提供
页面框架和条目仅报告翻出哪些页.
extractTextInRegions(buffer: Buffer, pageRegions: PageRegions[], options?: FrameOptions): PageRegionTexts[]
从 PDF 从边框区域中提取文本。 为混合式OCR管道所设计,其中布局模型在渲染的页面图像中检测出区域,而此函数从PDF结构中提取出文本用于基于文本的页面——跳过GPU OCR.
区域boxs为 [x1, y1, x2, y2] 在带有左上方源的 PDF 点中,
相对可见的页面框。 默认情况下,将在 "sheet"
框架: 内容流中的框, /Rotate 未应用
边框 extractTextWithPositions 报告被翻转到左上方的项目
源代码。 工作表框架只匹配已渲染的页面图像, 两者都持有 :
页面有 /Rotate 0,其文本并非主要旋转。 页面
其文本主要旋转的文本在工作表框中交接,这样文本
读取从左到右 (extractTextWithPositionsAndRotations 报告
页面被翻转),因此其工作表框 bbox 与已渲染的图像不符
即使是与 /Rotate 0。通过 { frame: "display" } 以在
已打开的页面( 可见的框因页面可继承而顺时针旋转)
/Rotate; 以旋转为主的页面的页面级转折被取消
首先,个人运行 保持自己的 rotation),作为布局模型
在页面图像上工作 报告它们,无论页面的 /Rotate 或文本
向上转. extractTablesInRegions 选择相同, boldFromWeight
列入(见 extractTextWithPositions) (中文(简体) ).
每个区域的成果包括: needsOcr 表示不可靠提取的旗帜(空文本、GID编码字体、垃圾文本、编码问题)。 当病因是疑似被粘住的文字层时, ocrReason 设置为 "suspected_garbled_text"。 。 。 .
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],
]
}
])
// The same call with bboxes taken from a rendered page image
const onRendered = extractTextInRegions(
pdf,
[{ page: 0, regions: [[0, 0, 300, 400]] }],
{ frame: 'display' },
)
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
}
}
Async 变体
processPdf, (中文(简体) ), classifyPdf, (中文(简体) ), extractPagesMarkdown,以及 extractTextWithPositions 在调用线条上是同步的和解析的——在节点中,这就是事件回路. 对于脚本中的一次性调用来说是好的,但是在服务器中,一个大文档可以把回路维持了上到上百毫秒,一个不寻常的回路可以持续更长.
processPdfAsync, (中文(简体) ), classifyPdfAsync, (中文(简体) ), extractPagesMarkdownAsync,以及 extractTextWithPositionsAsync 接受同样的参数并产生相同的结果,但运行libuv线程池上的剖析并返回一个承诺,使事件循环自由. 拨打时仍丢出无效的选项 。 输入缓冲器在调用回话前被复制,所以立即再用或变异是安全的:
import { classifyPdfAsync, extractPagesMarkdownAsync, extractTextWithPositionsAsync } from '@firecrawl/pdf-inspector'
const classification = await classifyPdfAsync(pdf)
if (classification.pdfType === 'TextBased') {
const { pages } = await extractPagesMarkdownAsync(pdf)
// ...
}
const items = await extractTextWithPositionsAsync(pdf, [1, 2], { frame: 'display' })
类型
interface PdfClassification {
pdfType: string // "TextBased" | "Scanned" | "Mixed" | "ImageBased"
pageCount: number
pagesNeedingOcr: number[] // 0-indexed page numbers
confidence: number // 0.0 - 1.0
}
interface PdfResult { // processPdf / detectPdf (excerpt)
pdfType: string
markdown?: string // omitted by detectPdf
pageCount: number
// The document information dictionary's entries, decoded as PDF text
// strings (UTF-16 or UTF-8 after a byte order mark, PDFDocEncoding
// otherwise); each omitted when missing or not a string.
title?: string
author?: string
subject?: string
keywords?: string
creator?: string // the application the document was authored in
producer?: string // the application that wrote the PDF
creationDate?: string // as written, e.g. "D:20240115103000+01'00'"
modDate?: string
// ...
}
interface PageRegions {
page: number // 0-indexed
regions: number[][] // [[x1, y1, x2, y2], ...] in PDF points, top-left origin of the visible page box
// (sheet frame by default; the rendered page with { frame: "display" })
}
interface FrameOptions {
frame?: "sheet" | "display" // coordinate frame of items and region bboxes; "sheet" by default
boldFromWeight?: boolean // also read isBold from fontWeight >= boldWeightThreshold and merge runs by that verdict; false by default
boldWeightThreshold?: number // the weight class boldFromWeight reads bold from, 100..900; 600 by default
}
interface PositionedText {
items: TextItem[] // as returned by extractTextWithPositions
pageRotations: PageRotation[] // one entry per page whose text was predominantly rotated
}
interface PageRotation {
page: number // 1-indexed, matching TextItem.page
rotation: string // "ccw" | "cw": how the page was turned in the "sheet" frame
}
interface PageRegionTexts {
page: number
regions: RegionText[]
}
interface RegionText {
text: string
needsOcr: boolean // true when text is unreliable
ocrReason?: string // "suspected_garbled_text" when known
}
interface OcrPdfResult {
markdown: string
pages: OcrPageResult[] // 1-indexed pages + provenance
pageCount: number
pagesRecommendedForOcr: number[]
pagesRoutedToOcr: number[]
pagesRecommendingHosted: number[]
ocrReasonsByPage: PageOcrReasons[]
pagesWithTables: number[]
pagesWithColumns: number[]
isComplex: boolean
processingTimeMs: number
renderTimeMs: number
ocrTimeMs: number
}
平台
预建二进制船作为月台专用包通过自动安装 optionalDependencies数字 :
| 平台 | 建筑 | 软件包 |
|---|---|---|
| 链接 | x64(滑动) | @firecrawl/pdf-inspector-linux-x64-gnu |
| 链接 | x64(母马/高山) | @firecrawl/pdf-inspector-linux-x64-musl |
| 链接 | ARM64(滑翔机) | @firecrawl/pdf-inspector-linux-arm64-gnu |
| 链接 | ARM64(母马/高山) | @firecrawl/pdf-inspector-linux-arm64-musl |
| 马科斯 | 阿尔马-64 | @firecrawl/pdf-inspector-darwin-arm64 |
| 窗口 | x64 (韩语) | @firecrawl/pdf-inspector-win32-x64-msvc |
许可证
麻省理工学院