使用PDFBox 2.0.8如何检测PDF文档中的文本重叠?
嘿,这个问题我熟!用PDFBox 2.0.8检测文本重叠,核心就是抓每个字符的边界框(Bounding Box),然后对比它们的区域是否相交。我给你拆解下具体怎么做:
步骤1:收集每个字符的边界框信息
PDFBox的PDFTextStripper类是解析文本的核心,但默认只会输出文本内容。我们需要继承它,重写processTextPosition方法,把每个字符的位置信息(也就是TextPosition对象)收集起来。每个TextPosition包含了字符的x/y坐标、宽度、高度,这些正是判断重叠需要的关键数据。
步骤2:判断两个字符的边界框是否重叠
拿到所有字符的位置后,我们需要两两对比它们的矩形区域。这里要注意PDF的坐标系:y轴是从页面底部往上递增的,所以字符的下边界是y坐标 - 高度。
判断重叠的逻辑很简单:两个矩形不重叠的情况只有四种——A的右边界≤B的左边界,A的左边界≥B的右边界,A的下边界≥B的上边界,A的上边界≤B的下边界。反过来,如果这四个条件都不满足,就说明两个字符的区域有重叠。
另外,你可以加个阈值过滤误判:比如只有重叠区域超过字符面积的10%,才判定为真正的重叠(避免字体 kerning 导致的微小间距重叠)。
代码实现示例
下面是一个完整的可运行示例,包含了收集字符位置和检测重叠的逻辑:
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.TextPosition; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class TextOverlapDetector extends PDFTextStripper { private final List<TextPosition> allCharacters = new ArrayList<>(); public TextOverlapDetector() throws IOException { super(); } @Override protected void processTextPosition(TextPosition textPosition) { allCharacters.add(textPosition); super.processTextPosition(textPosition); } // 查找所有重叠的字符对 public List<OverlappingPair> findOverlappingCharacters() { List<OverlappingPair> overlappingPairs = new ArrayList<>(); // 遍历所有字符对,避免重复对比(i < j) for (int i = 0; i < allCharacters.size(); i++) { TextPosition charA = allCharacters.get(i); for (int j = i + 1; j < allCharacters.size(); j++) { TextPosition charB = allCharacters.get(j); if (isOverlapping(charA, charB)) { overlappingPairs.add(new OverlappingPair(charA, charB)); } } } return overlappingPairs; } // 判断两个字符是否重叠 private boolean isOverlapping(TextPosition tpA, TextPosition tpB) { // 计算字符A的边界框:左、右、上、下 float aLeft = tpA.getX(); float aRight = tpA.getX() + tpA.getWidth(); float aTop = tpA.getY(); float aBottom = tpA.getY() - tpA.getHeight(); // 计算字符B的边界框 float bLeft = tpB.getX(); float bRight = tpB.getX() + tpB.getWidth(); float bTop = tpB.getY(); float bBottom = tpB.getY() - tpB.getHeight(); // 检查是否不重叠,取反就是重叠 boolean noOverlap = (aRight <= bLeft) || (aLeft >= bRight) || (aBottom >= bTop) || (aTop <= bBottom); return !noOverlap; } // 辅助类:存储重叠的字符对 public static class OverlappingPair { private final TextPosition char1; private final TextPosition char2; public OverlappingPair(TextPosition char1, TextPosition char2) { this.char1 = char1; this.char2 = char2; } // 打印重叠信息的方法 public void printInfo() { System.out.printf("发现重叠字符:'%s' (位置: X=%.2f, Y=%.2f) 和 '%s' (位置: X=%.2f, Y=%.2f)%n", char1.getUnicode(), char1.getX(), char1.getY(), char2.getUnicode(), char2.getX(), char2.getY()); } } // 测试用例 public static void main(String[] args) { try (PDDocument doc = PDDocument.load(new File("your-document.pdf"))) { TextOverlapDetector detector = new TextOverlapDetector(); detector.getText(doc); // 触发文本解析,收集字符位置 List<OverlappingPair> overlaps = detector.findOverlappingCharacters(); System.out.println("检测到的重叠字符对数量:" + overlaps.size()); for (OverlappingPair pair : overlaps) { pair.printInfo(); } } catch (IOException e) { e.printStackTrace(); } } }
优化小技巧
- 按行分组对比:不同行的字符几乎不会重叠,你可以在
processTextPosition里按y坐标分组(同一行的y坐标差异极小,比如设置±1的阈值),只对比同一行内的字符,能大幅减少循环次数,提升效率。 - 重叠比例过滤:如果不想把字体间距的微小重叠算进去,可以计算两个矩形的交集面积,除以较小的矩形面积,只有当比例超过你设定的阈值(比如10%)时,才判定为有效重叠。
- 忽略空白字符:可以在收集字符时跳过空格、制表符等空白字符,减少不必要的对比。
内容的提问来源于stack exchange,提问作者Susnigdha Chatterjee
相关产品推荐
相关产品推荐

