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

使用PDFBox检测未保存新PDF每页指定字符串并添加缺失项目名

问题分析与解决方案

核心原因

你遇到的问题根源在于PDFTextStripper依赖完整的PDF序列化结构提取文本——未保存的PDDocument还处于内存构建状态,新增的页面内容尚未完成完整的PDF格式序列化,所以直接调用getText()会返回空字符串。而保存后重新加载的文档已经完成序列化,结构完整,能正常提取文本。

可行解决方案

方案1:内存流临时序列化(无需写入磁盘)

不用先保存到磁盘,而是将未完成的文档写入内存流,再从内存流加载临时文档来提取文本,既避免磁盘IO,又能完成文本检查:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;

// 假设document是你正在构建的未保存PDDocument实例
ByteArrayOutputStream baos = new ByteArrayOutputStream();
document.save(baos);
// 从内存流加载临时文档用于文本提取
PDDocument tempDoc = PDDocument.load(new ByteArrayInputStream(baos.toByteArray()));

// 遍历每页检查文本(注意PDFTextStripper页码从1开始,PDDocument页面索引从0开始)
for (int pageNum = 1; pageNum <= tempDoc.getNumberOfPages(); pageNum++) {
    PDFTextStripper textStripper = new PDFTextStripper();
    textStripper.setStartPage(pageNum);
    textStripper.setEndPage(pageNum);
    String pageText = textStripper.getText(tempDoc);
    
    // 检查是否缺少项目名,操作原document添加内容
    if (!pageText.contains("目标项目名")) {
        PDPage targetPage = document.getPage(pageNum - 1);
        // 追加内容到页面
        try (PDPageContentStream contentStream = new PDPageContentStream(
                document, targetPage, PDPageContentStream.AppendMode.APPEND, true, true)) {
            contentStream.beginText();
            contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);
            contentStream.newLineAtOffset(50, 50); // 调整到你需要的位置
            contentStream.showText("目标项目名");
            contentStream.endText();
        }
    }
}

// 关闭临时文档,保存原文档
tempDoc.close();
document.save(new FileOutputStream(new File(pathToFile)));
document.close();

方案2:构建时直接标记(更高效可靠)

如果PDF是你自己生成的,在构建页面时直接记录每页是否已添加项目名,完全避免文本提取的依赖——这种方法不仅效率更高,还能避免字体、排版导致的文本提取误差:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

PDDocument document = new PDDocument();
List<Boolean> pageHasProjectName = new ArrayList<>();

// 生成页面时同步标记
PDPage page1 = new PDPage();
document.addPage(page1);
// 添加页面内容...(未添加项目名则标记为false)
pageHasProjectName.add(false);

PDPage page2 = new PDPage();
document.addPage(page2);
// 添加页面内容...(已添加项目名则标记为true)
pageHasProjectName.add(true);

// 后续统一检查并补充
for (int i = 0; i < document.getNumberOfPages(); i++) {
    if (!pageHasProjectName.get(i)) {
        PDPage targetPage = document.getPage(i);
        try (PDPageContentStream contentStream = new PDPageContentStream(
                document, targetPage, PDPageContentStream.AppendMode.APPEND, true, true)) {
            contentStream.beginText();
            contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);
            contentStream.newLineAtOffset(50, 50);
            contentStream.showText("目标项目名");
            contentStream.endText();
        }
        // 更新标记
        pageHasProjectName.set(i, true);
    }
}

document.save(new FileOutputStream(new File(pathToFile)));
document.close();

注意事项

  • 方案1中,临时文档仅用于文本提取,所有修改操作都要针对原document实例。
  • PDFBox的PDDocument页面索引从0开始,但PDFTextStripper的页码参数从1开始,注意索引转换。
  • 方案2更适合自定义生成PDF的场景,避免了文本提取可能出现的异常(比如加密、非标准字体导致的提取失败)。

内容的提问来源于stack exchange,提问作者Владимир Гичев

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:40:29