You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 03:10:37