使用PDFBox如何区分PDF页面中图片上层文字与被图片遮挡的底层文字
PDFBox 区分图片上下层文字的实现方案
核心逻辑是利用PDF页面内容流的绘制顺序规则:PDF 中所有元素的层级由它在内容流里的出现顺序决定,先写入的元素层级更低,后写入的元素层级更高。因此只要你拿到图片和所有文本元素的绘制顺序,就能直接判断遮挡关系。
具体实现步骤如下:
- 第一步:自定义
PDFStreamEngine子类,重写对应的操作符处理方法,同时记录所有元素的绘制顺序索引
你需要同时监听图片绘制和文本绘制的操作符,每处理一个元素就给它分配一个自增的序号,代表它的绘制先后顺序 - 第二步:收集所有图片的
BoundingBox以及对应的绘制序号,同时收集所有文本块的BoundingBox和对应的绘制序号 - 第三步:对每个落在图片 bbox 范围内的文本块,直接对比两者的绘制序号:
- 如果文本的序号 < 图片的序号:文本绘制在图片之前,属于底层被遮挡的文字
- 如果文本的序号 > 图片的序号:文本绘制在图片之后,属于叠在图片上层的文字
注意:如果页面有内容组(Form XObject)嵌套的情况,需要递归处理 XObject 内部的内容流,同样按内部的绘制顺序记录序号,避免层级判断错误。
核心代码参考片段:
// 自定义内容流处理器 public class LayerAwareContentExtractor extends PDFStreamEngine { private int drawOrder = 0; private List<ElementInfo> elements = new ArrayList<>(); // 处理图片绘制操作符 @Override protected void processOperator(Operator operator, List<COSBase> operands) throws IOException { if ("Do".equals(operator.getName())) { COSName name = (COSName) operands.get(0); PDXObject xobject = getResources().getXObject(name); if (xobject instanceof PDImageXObject) { // 记录图片的bbox和绘制序号 Matrix ctm = getGraphicsState().getCurrentTransformationMatrix(); Rectangle2D bbox = calculateImageBBox(ctm, (PDImageXObject)xobject); elements.add(new ElementInfo("image", bbox, drawOrder++)); } else if (xobject instanceof PDFormXObject) { // 递归处理表单XObject内部内容 processChildStream((PDFormXObject)xobject, getCurrentPage(), getGraphicsState().getCurrentTransformationMatrix()); } } super.processOperator(operator, operands); } // 处理文本块,重写writeString方法 @Override protected void writeString(String string, List<TextPosition> textPositions) throws IOException { // 计算当前文本块的bbox Rectangle2D textBbox = calculateTextBBox(textPositions); elements.add(new ElementInfo("text", textBbox, drawOrder++)); super.writeString(string, textPositions); } }
你只需要遍历最终得到的 elements 列表,匹配 bbox 重叠的图片和文本,对比两者的 drawOrder 值就能直接得到层级关系。
内容的提问来源于stack exchange,提问作者Orit
相关产品推荐
相关产品推荐

