如何使用Java/NodeJS提取带OCR文本层PDF的文本数据
带OCR隐形文本层PDF的文本提取方案
核心失败原因
- OCR生成的文本层默认是不可见渲染状态:通常是字号1px、透明度0、垫在扫描位图下方的文本,绝大多数PDF解析库的默认配置会主动过滤这类「不显示在页面上」的文本
- 部分OCR工具会把文本层放在独立的可选内容组(也就是PDF图层)里,默认解析逻辑会跳过标记为隐藏的图层内容
- Apache Tika的默认PDF解析配置开启了重叠文本过滤、隐藏内容跳过规则,刚好命中OCR文本层的特征,所以提取不到内容是配置问题,不是文件没有文本层
Java 实现方案
不要用默认配置的Tika,直接基于PDFBox做解析,配置自由度更高,稳定版选2.0.x分支即可,不要用3.0的beta版。
先引入Maven依赖:
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.32</version> </dependency>
提取代码:
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; import java.io.File; import java.io.IOException; public class OcrPdfTextExtractor { public static String extractText(String pdfPath) throws IOException { try (PDDocument document = PDDocument.load(new File(pdfPath))) { PDFTextStripper stripper = new PDFTextStripper(); // 以下三个配置是提取OCR层的核心 stripper.setSortByPosition(true); // 按页面排版顺序还原文本 stripper.setSuppressDuplicateOverlappingText(false); // 关闭重叠文本过滤,不要把OCR层当重复内容删掉 stripper.setAddMoreFormatting(true); return stripper.getText(document); } } public static void main(String[] args) throws IOException { String text = extractText("target_file.pdf"); System.out.println(text); } }
如果必须用Tika,需要手动覆写PDFParser的配置,把上述三个参数传入PDFTextStripper,不要用AutoDetectParser的默认参数。
NodeJS 实现方案
用pdfjs-dist做解析,不要用封装过度的第三方pdf提取包,这类包基本都默认开了不可见文本过滤。依赖选3.x稳定版即可:
npm install pdfjs-dist@3.11.174
提取代码:
const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js'); const fs = require('fs'); async function extractOcrPdf(filePath) { const fileBuffer = fs.readFileSync(filePath); const pdfDocument = await pdfjsLib.getDocument({ data: new Uint8Array(fileBuffer), disableRange: true, disableStream: true }).promise; let fullText = ''; for (let pageIdx = 1; pageIdx <= <= pdfDocument.numPages; pageIdx++) { const page = await pdfDocument.getPage(pageIdx); const textContent = await page.getTextContent({ includeMarkedContent: true, // 关键配置:提取标记内容层的文本,不要跳过隐藏内容 disableCombineTextItems: false }); // 按坐标排序还原正常阅读顺序,避免文本顺序错乱 const sortedItems = textContent.items.sort((a, b) => { const yDiff = b.transform[5] - a.transform[5]; return Math.abs(yDiff) < 2 ? a.transform[4] - b.transform[4] : yDiff; }); const pageText = sortedItems.map(item => item.str).join(' '); fullText += `\n=== 第${pageIdx}页 ===\n${pageText}`; } return fullText; } // 调用示例 extractOcrPdf('target_file.pdf').then(res => console.log(res));
排错校验
- 写代码前先做基础校验:用本地PDF阅读器打开文件,尝试拖动鼠标选中文本,如果能选中复制出内容,说明文件确实带OCR层,上述代码可以正常提取;如果选不中,说明文件是纯扫描位图,没有嵌入文本层,需要自己跑OCR识别
- 如果提取出乱码:说明OCR层用了自定义嵌入字体且没有附带ToUnicode映射表,这种情况需要单独提取嵌入字体做编码映射,常规解析方案无法处理
- 不要用iText 5.x版本做提取,该版本对隐藏内容的过滤逻辑写死在源码里,改配置的成本远高于换PDFBox
内容的提问来源于stack exchange,提问作者TryBlock
相关产品推荐
相关产品推荐

