OCR 如何读扫描 PDF,以及本工具为何返回 Markdown
扫描 PDF 本质是文字的照片。本 OCR 端点返回 Markdown,不是带隐藏文本层的 PDF;Auto 复用已有文本,Force 重读每一页。

扫描 PDF 把每一页存成栅格图像,是文字的照片,不是可选中的字符。光学字符识别(Optical Character Recognition,简称 OCR)去看这些像素,猜测哪些形状是字符,再输出真正的文本。在 PDF123 上,它既是免费的浏览器工具,也是一个接口,POST /api/v1/misc/ocr-pdf;返回的是 Markdown,不是重新写过文本层的 PDF。
扫描件仍然是一张图
OCR 不清洗、不锐化,也不替换底层的位图。一张扫描清晰却被 OCR 认错的页面,看起来还是清晰的。图像从来不是质量瓶颈,识别才是:同一批扫描件换一个识别引擎,准确率可以差出一大截,像素本身没有变化。
传统 PDF OCR 的做法,是在图像下面另写一层不可见文本,并与图像对齐,让选择和搜索都落到正确的词上。示意图画的就是这种做法,不少桌面工具至今仍在用这条路。
本端点实际返回什么
OCR 页面调用的是 /api/v1/misc/ocr-pdf,服务端跑的是一个独立的 Rust crate,pdf-inspector(启用了它的 ocr feature)。处理方式不是把整份 PDF 丢给一个识别模型:pdf-inspector 先逐页判断哪些页已经能直接抽出原生文字,哪些页只有图像。能抽文字的页复用原生文本,不碰识别模型;只有图像的页才会转给识别管线,先用 PDFium(Chrome 内部渲染 PDF 用的同一套开源库)把页面转成位图,再交给 ONNX Runtime 跑 PP-OCRv6 Small 模型做推理。两部分结果按页序拼接、融合成一份 Markdown,这就是响应体是 text/markdown、下载文件名以 .md 结尾的由来。
这份契约本身是重写后才有的。仓库里原来的 Java/Spring Boot 后端调用的是 OCRmyPDF,走的正是“图像加隐藏文本层”那条老路,输出仍然是 PDF;Rust 重写把这条路径整体换成了 pdf-inspector 的选择性 OCR,输出也跟着从 PDF 换成了 Markdown。旧的 Java 后端已经从仓库里删除,不是一个还能切换回去的选项。
如果你要的是可搜索 PDF(图像加文本层),这套接口给不出来。它给的是 Agent 或数据流水线能直接吃进去的文本。
Auto 与 Force 的区别
表单里有一个 ocrType 字段,取值决定走哪条路径:
- Normal(除
force-ocr外的任意值)走 Auto:文件本来就有文本的页面沿用原文本,只对纯图像页跑 OCR。遇到干净的电子版 PDF,Auto 判断每一页都能抽出原生文字,整份文件都不会碰 PDFium 或 ONNX Runtime。 - Force OCR(
ocrType=force-ocr)对每一页重新识别,包括已经带文本层的页。在真正的纯图像页上,多付出的只是时间,不是精度;换来的这一趟识别,会绕开一份残缺或损坏的旧文本层。
门户表单把 ocrType 的默认值设成 Force OCR。这里没有语言选项:旧 Java 路径遗留的语种参数会被服务端忽略。
Auto 和 Force 的分野只发生在请求内部,门户和 API 的能力探测接口不替你把关:GET /api/v1/settings/get-endpoints-status 对 ocr-pdf 永远回答“已启用”,并不实际检查 PDFium、ONNX Runtime 或 PP-OCRv6 模型是否已经装好。真正的检查在请求触发识别的那一刻才发生,任何一项缺失,接口直接返回 503,不会退化成一份只含原生文字的半成品 Markdown。PP-OCRv6 Small 模型本身约 31 MB,部署时若没有预置到本地缓存,会在第一次真正需要识别的页面上联网下载;一台离线又没预置模型的机器,第一次 Force 请求大概率会卡在这一步,而不是慢慢跑完。
识别是概率性的
识别引擎做的是把像素模式匹配到字母与词形的统计模型上。它们在干净、高对比度、标准字体的印刷件上表现好,遇到下面几种情况会明显变差:
- 分辨率或对比度低的扫描件,比如传真件对比 300 DPI 原件
- 手写体、装饰字体、异形版式,比如多栏表格、旋转文字、密集表单
- 页面倾斜带来的字形形变,足以让匹配出错
不管走 Auto 还是 Force,一张模糊的传真件也不会被识别得像清晰扫描件那样准。这是模型本身的能力边界,调参数解决不了。
OCR 不做什么
OCR 只给你文本,不会重建段落、标题、表格,也不会生成可回流编辑的 Word 文档。把识别结果整理成可编辑版式,是识别之上再做一层转换的问题,和读扫描件不是同一份工作。
OCR PDF 在服务端运行,不需要账号,下载结果是 Markdown。文件自带的文本层看起来不对,就用 Force OCR,让 Auto 不用去信任那层文本;想确认某台机器到底有没有装好 PDFium 和模型,直接跑一次比看探测接口的状态更可靠,后者只报告端点存在,不报告运行时是否就绪。自动化用的 API Key 与 OpenAPI 文档见开发者。