万级 PDF、百万页扫描文档,如何一键变成可搜索的双层 PDF?
扫描 PDF 只是“图片堆”,图文双层 PDF 才是真正可用的数字资产。
在很多单位里,我们常常见到这样的场景:
📁 几十个文件夹、数百个子目录 📄 上万份历史合同、票据、卷宗 🖼️ 每一份 PDF 都由成千上万张扫描图片组成
结果就是:
❌ 搜不到关键字❌ 复制只能手打❌ 二次利用成本极高
今天,我们就分享一套可在本机运行的批量 OCR 方案,支持 万级 PDF、百万级内页图片,最终输出 标准图文双层 PDF,WPS、Acrobat 全部兼容。
一、什么是「图文双层 PDF」?
一句话解释:
人眼看到的是扫描图,系统读到的是文字层。
这对档案数字化、合同管理、知识库建设来说,是关键一步。
二、技术难点在哪里?
要真正可用,至少要解决这几点:
✅ 支持嵌套文件夹✅ 万级 PDF 不崩溃✅ 百万页图片可稳定运行✅ 中 / 英 / 中英混排准确✅ 本机离线、无需上传
传统小工具,基本扛不住这个规模。
三、我们的解决方案
✅ 技术选型
✅ 全开源✅ 全离线✅ 单机上即可长期运行
四、核心处理逻辑
整体流程非常简单清晰:
扫描文件夹 → 找到 PDF ↓逐页渲染成图片 ↓OCR 识别文字与位置 ↓在原 PDF 上叠加透明文字层 ↓输出双层 PDF五、关键代码示例(精简可落地)
1. 递归扫描所有 PDF(支持嵌套)
from pathlib import Pathdefscan_pdfs(root):return [p for p in Path(root).rglob("*.pdf")]无论目录多深,都能完整覆盖。
2. 单页 OCR + 双层 PDF 合成(核心)
import fitzfrom paddleocr import PaddleOCRocr = PaddleOCR(use_angle_cls=True, lang="ch")defprocess_page(page, img_np): result = ocr.ocr(img_np, cls=True)for line in result:for box, (text, conf) in line:if conf < 0.6:continue x0, y0 = box[0] x1, y1 = box[2] rect = fitz.Rect(x0, y0, x1, y1) page.insert_textbox( rect, text, fontsize=1, color=(0, 0, 0), render_mode=3# 不可见但可选中 )
render_mode=3是实现「视觉不变、文字可选」的关键。
3. 批量处理入口(双击即可运行)
from concurrent.futures import ThreadPoolExecutorimport fitzdefprocess_pdf(src, dst): doc = fitz.open(src)for page in doc: pix = page.get_pixmap(dpi=300) img = pix.tobytes("png") process_page(page, img) doc.save(dst)with ThreadPoolExecutor() as pool:for pdf in scan_pdfs("input"): out = Path("output") / pdf.relative_to("input") out.parent.mkdir(exist_ok=True, parents=True) pool.submit(process_pdf, pdf, out)✅ 自动遍历✅ 自动建目录✅ 多线程加速
六、真实性能表现
在普通办公 PC(16G 内存)下的实测数据:
即便没有 GPU,也能稳稳跑完全量任务。
七、适合哪些场景?
✅ 法院卷宗、政务档案✅ 医疗病历、检验报告✅ 金融合同、票据凭证✅ 企业制度、工程图纸✅ 高校论文、历史资料
一句话:只要你有“死 PDF”,它就适用。
八、写在最后
扫描,只是数字化的起点;可被检索、可被复用,才叫真正的数字化。
让百万页历史资料,从“存下来”变成“用起来”。
如果你正被海量扫描文档困扰,希望这篇文章能给你一个明确、可落地的方向。
夜雨聆风