Apache PdfBox读取PDF文本错乱,寻求正确读取解决方案
解决Apache PDFBox读取PDF时日期文本错乱的问题
我使用以下代码读取PDF文件:
PDDocument document = PDDocument.load(url.openStream()); PDFTextStripperByArea stripper = new PDFTextStripperByArea(); stripper.setSortByPosition(true); EarningsData earningsData = new EarningsData(); PDFTextStripper tStripper = new PDFTextStripper(); String pdfText = tStripper.getText(document); document.close();
读取目标PDF时,原PDF中的行:
31.03.2023 31.12.2022 31.03.2022 31.03.2023 31.03.2022
被读取为:
31.03.2023 3 1.12.2022 31.03.2022 g 1.03.2 023 | 3 1.03.2 022
直接复制粘贴该行也会出现同样问题,说明问题出在PDF本身的文本存储结构,而非PDFBox的Bug,以下是可行的解决方法:
原因分析
这种文本错乱是因为PDF底层存储时,字符可能被拆分成独立文本块、编码映射异常,或者文本块的排列顺序与视觉顺序不一致,导致提取工具(包括PDFBox和系统复制功能)无法按视觉顺序拼接文本。
解决方法
方法1:基于字符位置重新排序拼接
通过PDFBox获取每个字符的位置坐标,按水平位置排序后重新拼接文本,还原视觉上的正确顺序:
PDDocument document = PDDocument.load(url.openStream()); List<List<TextPosition>> allPageCharacters = new ArrayList<>(); // 遍历每页,收集所有字符的位置信息 for (int pageIndex = 0; pageIndex < document.getNumberOfPages(); pageIndex++) { PDFTextStripper positionStripper = new PDFTextStripper() { @Override protected void writeString(String text, List<TextPosition> textPositions) throws IOException { allPageCharacters.add(textPositions); } }; positionStripper.setStartPage(pageIndex + 1); positionStripper.setEndPage(pageIndex + 1); positionStripper.getText(document); } // 定位目标行的字符集合(通过包含的特征文本筛选) List<TextPosition> targetLineChars = null; for (List<TextPosition> pageChars : allPageCharacters) { StringBuilder lineContent = new StringBuilder(); for (TextPosition tp : pageChars) { lineContent.append(tp.getUnicode()); } if (lineContent.toString().contains("31.03.2023") && lineContent.toString().contains("31.12.2022")) { targetLineChars = pageChars; break; } } // 按水平坐标排序并拼接字符 if (targetLineChars != null) { targetLineChars.sort(Comparator.comparingDouble(TextPosition::getXDirAdj)); StringBuilder correctedText = new StringBuilder(); for (TextPosition tp : targetLineChars) { correctedText.append(tp.getUnicode()); } // 进一步清洗多余空格或错误字符 correctedText = new StringBuilder(correctedText.toString().replaceAll("\\s+", " ").replace("g", "3").replace("|", "")); System.out.println("修正后文本:" + correctedText); } document.close();
方法2:使用区域提取+文本清洗
利用PDFTextStripperByArea精准定位目标行所在区域,提取后对错误字符进行清洗:
PDDocument document = PDDocument.load(url.openStream()); PDFTextStripperByArea areaStripper = new PDFTextStripperByArea(); areaStripper.setSortByPosition(true); // 需先通过PDF编辑器(如Adobe Acrobat)获取目标行的坐标范围(x, y, 宽度, 高度) // 示例坐标需根据实际PDF调整 areaStripper.addRegion("dateRegion", new Rectangle2D.Double(150, 480, 700, 30)); // 提取第一页的目标区域文本(假设目标行在第一页) PDPage targetPage = document.getPage(0); areaStripper.extractRegions(targetPage); String rawDateText = areaStripper.getTextForRegion("dateRegion"); // 清洗错误字符和多余分隔符 String cleanedText = rawDateText.replace("g", "3") .replace(" | ", " ") .replaceAll("\\s+", " ") .trim(); System.out.println("清洗后文本:" + cleanedText); document.close();
方法3:OCR识别(备选方案)
如果上述方法仍无法解决,可将PDF页面渲染为图像,再用OCR工具(如Tesseract)识别文本,适合文本结构严重异常的PDF:
PDDocument document = PDDocument.load(url.openStream()); PDFRenderer renderer = new PDFRenderer(document); BufferedImage pageImage = renderer.renderImageWithDPI(0, 300); // 渲染第一页为300DPI图像 // 使用Tesseract OCR识别图像中的文本 Tesseract tesseract = new Tesseract(); tesseract.setDatapath("tessdata路径"); tesseract.setLanguage("eng"); String ocrText = tesseract.doOCR(pageImage); // 从OCR结果中提取目标行文本(可通过字符串匹配筛选) document.close();
内容的提问来源于stack exchange,提问作者Kannan J
相关产品推荐
相关产品推荐

