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

如何使用iText7获取PDF主/子图层名称及图层内元素坐标属性

iText7提取PDF图层及图层内元素属性实现方案

1 递归获取所有图层(含子图层)名称

你原有代码只能拿到顶层图层的原因是getLayers()方法仅返回根层级的图层列表,子图层存储在父图层的getChildren()属性中,需要递归遍历才能获取全量图层:

import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.layer.PdfLayer;
import java.io.IOException;
import java.util.List;

public class LayerExtractor {
    public static void main(String[] args) throws IOException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader("你的PDF文件路径"));
        List<PdfLayer> rootLayers = pdfDoc.getCatalog().getOCProperties(true).getLayers();
        // 递归遍历所有图层
        for (PdfLayer rootLayer : rootLayers) {
            parseLayer(rootLayer, 0);
        }
        pdfDoc.close();
    }

    private static void parseLayer(PdfLayer layer, int depth) {
        if (!layer.isOnPanel()) {
            return;
        }
        // 打印当前图层名称,depth用来标识层级
        String layerName = layer.getPdfObject().get(PdfName.Name).toString();
        System.out.printf("%s%s%n", "  ".repeat(depth), layerName);
        // 递归遍历子图层
        List<PdfLayer> children = layer.getChildren();
        if (children != null && !children.isEmpty()) {
            for (PdfLayer child : children) {
                parseLayer(child, depth + 1);
            }
        }
    }
}

2 提取图层内元素的坐标、尺寸属性

你需要通过iText7的画布事件监听机制,遍历页面内容时捕获文本、路径(包含线条、曲线、矩形等)、图片等元素,同时判断元素所属的OCG(可选内容组,对应PDF图层),匹配后提取对应属性:

2.1 实现思路

  • 自定义IEventListener实现类,处理TextRenderInfo(文本元素)、PathRenderInfo(路径类元素)、ImageRenderInfo(图片元素)事件
  • 每个渲染信息对象可关联到对应的OCG,即可判断所属图层
  • 从渲染信息中提取坐标、尺寸属性,PDF默认坐标原点为页面左下角,可根据需求自行转换为左上角原点

2.2 代码示例

import com.itextpdf.kernel.canvas.PdfCanvasProcessor;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.canvas.parser.EventType;
import com.itextpdf.kernel.pdf.canvas.parser.data.IEventData;
import com.itextpdf.kernel.pdf.canvas.parser.data.PathRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.data.TextRenderInfo;
import com.itextpdf.kernel.pdf.canvas.parser.listener.IEventListener;
import com.itextpdf.kernel.pdf.layer.PdfLayer;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class LayerElementExtractor implements IEventListener {
    // 存储图层ID和图层名称的映射
    private final Map<Integer, String> layerIdNameMap = new HashMap<>();

    public LayerElementExtractor(PdfDocument pdfDoc) {
        // 预加载所有图层的ID和名称映射
        loadLayerMap(pdfDoc.getCatalog().getOCProperties(true).getLayers());
    }

    private void loadLayerMap(List<PdfLayer> layers) {
        for (PdfLayer layer : layers) {
            if (layer.isOnPanel()) {
                layerIdNameMap.put(layer.getPdfObject().getIndirectReference().getObjNumber(), layer.getPdfObject().get(PdfName.Name).toString());
                if (layer.getChildren() != null) {
                    loadLayerMap(layer.getChildren());
                }
            }
        }
    }

    @Override
    public void eventOccurred(IEventData data, EventType type) {
        Integer layerId = null;
        // 获取当前元素所属的图层ID
        if (data instanceof TextRenderInfo textInfo) {
            layerId = textInfo.getMcid();
        } else if (data instanceof PathRenderInfo pathInfo) {
            layerId = pathInfo.getMcid();
        }
        if (layerId == null || !layerIdNameMap.containsKey(layerId)) {
            return;
        }
        String layerName = layerIdNameMap.get(layerId);
        
        // 提取对应元素属性
        if (type == EventType.RENDER_TEXT) {
            TextRenderInfo textInfo = (TextRenderInfo) data;
            float x = textInfo.getBaseline().getStartPoint().get(0);
            float y = textInfo.getBaseline().getStartPoint().get(1);
            float width = textInfo.getBaseline().getEndPoint().get(0) - x;
            float height = textInfo.getAscentLine().getStartPoint().get(1) - textInfo.getDescentLine().getStartPoint().get(1);
            String content = textInfo.getText();
            System.out.printf("图层[%s] 文本元素:内容=%s, x=%.2f, y=%.2f, 宽=%.2f, 高=%.2f%n", layerName, content, x, y, width, height);
        } else if (type == EventType.RENDER_PATH) {
            PathRenderInfo pathInfo = (PathRenderInfo) data;
            Rectangle bbox = pathInfo.getPath().getBounds();
            float x = bbox.getX();
            float y = bbox.getY();
            float width = bbox.getWidth();
            float height = bbox.getHeight();
            System.out.printf("图层[%s] 路径元素:x=%.2f, y=%.2f, 宽=%.2f, 高=%.2f%n", layerName, x, y, width, height);
        }
    }

    @Override
    public Set<EventType> getSupportedEvents() {
        return Set.of(EventType.RENDER_TEXT, EventType.RENDER_PATH, EventType.RENDER_IMAGE);
    }

    public static void main(String[] args) throws IOException {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader("你的PDF文件路径"));
        LayerElementExtractor extractor = new LayerElementExtractor(pdfDoc);
        // 遍历所有页面提取元素
        for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {
            PdfPage page = pdfDoc.getPage(i);
            PdfCanvasProcessor processor = new PdfCanvasProcessor(extractor);
            processor.processPageContent(page);
        }
        pdfDoc.close();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 23:45:07