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

使用PDFBox v2清理PDF指定文本遇阻:无法识别非链接冗余文本

PDFBox v2 清理PDF冗余文本问题排查

我尝试使用PDFBox v2清理若干PDF文档中的超链接及非原始冗余文本,编写了如下processText函数用于清理PDF中的超链接及指定冗余文本:

private int processText(PDPage page) throws IOException {
        int counter = 0;
        boolean found = false;
        List<Object> filteredTokens = new ArrayList<>();

        PDFStreamParser parser = new PDFStreamParser(page);
        Object token = parser.parseNextToken();
        while (token != null) {
            boolean filtered = true;
            if (token instanceof Operator) {
                String name = ((Operator) token).getName();
                if ("Tj".equals(name)) {
                    COSString stringToken = (COSString) filteredTokens.get(filteredTokens.size() - 1);

                    if (stringToken.getString().contains("http://") || stringToken.getString().contains("https://") || stringToken.getString().contains("Dr-Bios.com")
                            || stringToken.getString().contains("www.kythuatvitinh.com")) {
                        print(stringToken.getString());
                        filteredTokens.remove(filteredTokens.size() - 1);
                        filtered = false;
                        found = true;
                        counter++;
                    }
                }
            }
            if (filtered)
                filteredTokens.add(token);
            token = parser.parseNextToken();
        }

        if (found) {
            PDStream newContents = new PDStream(_document);
            try (OutputStream out = newContents.createOutputStream()) {
                ContentStreamWriter writer = new ContentStreamWriter(out);
                writer.writeTokens(filteredTokens);
            }
            page.setContents(newContents);
        }

        return counter;
    }

该函数可成功清除含http://、https://的超链接文本,但无法识别并清除如"Dr-Bios.com"这类非链接形式的冗余文本,此类文本每页都存在,请求协助排查解决问题。(示例截图显示PDF页面底部存在"Dr-Bios.com"冗余文本)


问题原因分析

你的代码只处理了PDF内容流中的Tj文本绘制运算符,但这类冗余文本很可能是通过TJ运算符绘制的——TJ用于处理包含字符间距的多行/分段文本,文本会被拆分成多个COSString片段和间距数值组成的数组。另外,即使是Tj操作,目标文本也可能被拆分成多个独立的文本指令,单个COSString中不包含完整的目标字符串,导致匹配失败。

修复后的代码

private int processText(PDPage page) throws IOException {
    int counter = 0;
    boolean found = false;
    List<Object> filteredTokens = new ArrayList<>();

    PDFStreamParser parser = new PDFStreamParser(page);
    Object token = parser.parseNextToken();
    while (token != null) {
        boolean filtered = true;
        if (token instanceof Operator) {
            String name = ((Operator) token).getName();
            // 处理单文本绘制指令Tj
            if ("Tj".equals(name)) {
                COSString stringToken = (COSString) filteredTokens.get(filteredTokens.size() - 1);
                String text = stringToken.getString();
                if (text.contains("http://") || text.contains("https://") || text.contains("Dr-Bios.com")
                        || text.contains("www.kythuatvitinh.com")) {
                    filteredTokens.remove(filteredTokens.size() - 1);
                    filtered = false;
                    found = true;
                    counter++;
                }
            } 
            // 处理分段文本绘制指令TJ
            else if ("TJ".equals(name)) {
                COSArray arrayToken = (COSArray) filteredTokens.get(filteredTokens.size() - 1);
                List<Object> cleanedArray = new ArrayList<>();
                boolean arrayHasTarget = false;

                for (Object element : arrayToken) {
                    if (element instanceof COSString) {
                        COSString str = (COSString) element;
                        String text = str.getString();
                        if (text.contains("http://") || text.contains("https://") || text.contains("Dr-Bios.com")
                                || text.contains("www.kythuatvitinh.com")) {
                            arrayHasTarget = true;
                            counter++;
                            continue; // 跳过目标文本片段
                        }
                    }
                    cleanedArray.add(element);
                }

                if (arrayHasTarget) {
                    found = true;
                    filteredTokens.remove(filteredTokens.size() - 1);
                    if (!cleanedArray.isEmpty()) {
                        filteredTokens.add(new COSArray(cleanedArray));
                    } else {
                        filtered = false; // 数组为空时,移除TJ运算符
                    }
                }
            }
        }
        if (filtered) {
            filteredTokens.add(token);
        }
        token = parser.parseNextToken();
    }

    if (found) {
        PDStream newContents = new PDStream(_document);
        try (OutputStream out = newContents.createOutputStream()) {
            ContentStreamWriter writer = new ContentStreamWriter(out);
            writer.writeTokens(filteredTokens);
        }
        page.setContents(newContents);
    }

    return counter;
}

关键修改说明

  1. 新增TJ运算符处理:遍历TJ对应的数组,逐个检查文本片段是否包含目标冗余内容,匹配到则跳过该片段。
  2. 处理空数组场景:如果TJ数组中的目标文本被全部移除,会连带去掉TJ运算符,避免生成无效的绘图指令。
  3. 保留原Tj逻辑:同时覆盖了单文本和分段文本的清理场景,解决文本拆分导致的匹配失败问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:24:52