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

如何使用PDFBOX移除PDF文档中的文本?

使用PDFBOX移除PDF中的指定文本

PDFBOX的PDPageContentStream确实没有直接的get/remove文本方法,因为PDF的内容是由一系列绘图指令组成的,而非结构化的文本块。要移除特定文本,核心思路是解析页面内容流,过滤掉目标文本对应的绘制指令,再重新生成内容流。以下是具体实现步骤和注意事项:

核心步骤

  • 解析内容流:提取页面所有的操作指令(操作符+操作数)
  • 过滤目标指令:识别绘制目标文本的Tj/TJ指令,将其从指令列表中移除
  • 重建内容流:将过滤后的指令重新写入页面,替换原有内容流

示例代码

import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.common.PDString;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.contentstream.PDFStreamParser;
import org.apache.pdfbox.contentstream.ContentStreamWriter;

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

public class RemoveTextFromPDF {
    public static void main(String[] args) throws Exception {
        String targetText = "要移除的目标文本";
        PDDocument document = PDDocument.load(new File("input.pdf"));

        for (PDPage page : document.getPages()) {
            // 解析页面内容流,获取所有指令
            PDFStreamParser parser = new PDFStreamParser(page);
            parser.parse();
            List<Object> tokens = parser.getTokens();
            List<Object> filteredTokens = new ArrayList<>();

            for (int i = 0; i < tokens.size(); i++) {
                Object token = tokens.get(i);
                if (token instanceof Operator) {
                    Operator op = (Operator) token;
                    String opName = op.getName();

                    // 处理单行文本指令 Tj
                    if ("Tj".equals(opName)) {
                        PDString textStr = (PDString) tokens.get(i - 1);
                        String content = textStr.getString();
                        // 仅保留非目标文本的指令
                        if (!content.equals(targetText)) {
                            filteredTokens.add(tokens.get(i - 1));
                            filteredTokens.add(op);
                        }
                        i++; // 跳过已处理的操作数
                    }
                    // 处理多行/带间距的文本指令 TJ
                    else if ("TJ".equals(opName)) {
                        COSArray textArray = (COSArray) tokens.get(i - 1);
                        COSArray filteredArray = new COSArray();
                        boolean hasValidContent = false;

                        for (Object obj : textArray) {
                            if (obj instanceof PDString) {
                                PDString str = (PDString) obj;
                                String content = str.getString();
                                if (!content.equals(targetText)) {
                                    filteredArray.add(obj);
                                    hasValidContent = true;
                                }
                            } else {
                                // 保留文本间距调整参数
                                filteredArray.add(obj);
                                hasValidContent = true;
                            }
                        }

                        if (hasValidContent) {
                            filteredTokens.add(filteredArray);
                            filteredTokens.add(op);
                        }
                        i++; // 跳过已处理的操作数
                    }
                    // 其他指令直接保留
                    else {
                        filteredTokens.add(op);
                    }
                } else {
                    // 非操作符的操作数,直接保留(除非前面是被过滤的文本指令)
                    filteredTokens.add(token);
                }
            }

            // 生成新的内容流并替换原页面内容
            PDStream newStream = new PDStream(document);
            try (OutputStream out = newStream.createOutputStream(COSName.FLATE_DECODE)) {
                ContentStreamWriter writer = new ContentStreamWriter(out);
                writer.writeTokens(filteredTokens);
            }
            page.setContents(newStream);
        }

        document.save(new File("output.pdf"));
        document.close();
    }
}

关键注意事项

  • 文本编码问题:如果PDF使用自定义字体编码,PDString.getString()可能无法正确解析文本。此时需要跟踪当前文本状态的字体(通过Tf指令获取),使用font.decode(string.getBytes())进行解码。
  • 文本拆分问题:目标文本可能被拆分为多个Tj/TJ指令,需要拼接连续的文本内容进行匹配,而非单独判断每个指令的文本。
  • 特殊元素处理:如果文本位于表单域、注释或水印中,需要单独处理这些元素(比如遍历页面的PDAnnotation或PDField进行移除),而非仅处理页面内容流。
  • 内容流完整性:过滤指令时要确保保留文本块的边界指令(BT/ET),避免破坏PDF的语法结构。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:48:40