使用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; }
关键修改说明
- 新增
TJ运算符处理:遍历TJ对应的数组,逐个检查文本片段是否包含目标冗余内容,匹配到则跳过该片段。 - 处理空数组场景:如果
TJ数组中的目标文本被全部移除,会连带去掉TJ运算符,避免生成无效的绘图指令。 - 保留原
Tj逻辑:同时覆盖了单文本和分段文本的清理场景,解决文本拆分导致的匹配失败问题。
内容的提问来源于stack exchange,提问作者Arunas Jaspelkis
相关产品推荐
相关产品推荐

