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

如何借助PDFBox准确转换PDF图片与文本坐标以移除图片上的文字

解决PDFBox中图片坐标与文本坐标不匹配的问题

你的核心问题是图片坐标转换逻辑错误,导致和TextPosition返回的文本坐标无法匹配。以下是修正方案:

一、错误原因分析

原代码的图片坐标转换存在三个关键问题:

  1. 直接使用图片原始像素尺寸(imageWidth/imageHeight),忽略了CTM(当前变换矩阵)的缩放、旋转、切变效果,实际显示尺寸由CTM决定而非原始像素。
  2. 错误手动引入切变参数(shearX/shearY),CTM已经包含所有变换,无需额外加减。
  3. Y轴坐标系转换逻辑颠倒,PDF默认以左下角为原点(Y向上),如果你的文本坐标使用左上角为原点(Y向下),转换方式错误。

二、修正后的图片坐标获取代码

通过CTM变换图片的四个顶点,计算出图片在用户空间的准确包围盒,再根据需求转换到文本坐标系:

@Override
protected void processOperator(Operator operator, List<COSBase> operands) throws IOException {
    String operation = operator.getName();
    if ("Do".equals(operation)) {
        COSName objectName = (COSName) operands.get(0);
        PDXObject xobject = getResources().getXObject(objectName);
        if (xobject instanceof PDImageXObject) {
            PDImageXObject image = (PDImageXObject) xobject;
            float imgWidth = image.getWidth();
            float imgHeight = image.getHeight();
            
            Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
            
            // 变换图片四个顶点到用户空间坐标(PDF默认左下角为原点)
            Point2D.Float p0 = ctm.transformPoint(0, 0);
            Point2D.Float p1 = ctm.transformPoint(imgWidth, 0);
            Point2D.Float p2 = ctm.transformPoint(imgWidth, imgHeight);
            Point2D.Float p3 = ctm.transformPoint(0, imgHeight);
            
            // 计算图片的最小/最大包围盒坐标
            float minX = Math.min(Math.min(p0.x, p1.x), Math.min(p2.x, p3.x));
            float maxX = Math.max(Math.max(p0.x, p1.x), Math.max(p2.x, p3.x));
            float minY = Math.min(Math.min(p0.y, p1.y), Math.min(p2.y, p3.y));
            float maxY = Math.max(Math.max(p0.y, p1.y), Math.max(p2.y, p3.y));
            
            // 转换到文本坐标系(左上角为原点,Y轴向下)
            PDRectangle mediaBox = getCurrentPage().getMediaBox();
            float pageHeight = mediaBox.getHeight();
            float textMinY = pageHeight - maxY;
            float textMaxY = pageHeight - minY;
            
            // 存入坐标数组(根据你的Coordinate类结构调整参数)
            Coordinate coordinate = new Coordinate(
                (int) minX, 
                (int) textMinY, 
                (int) maxX, 
                (int) textMaxY, 
                pageNum
            );
            coordinates.add(coordinate);
        } else if (xobject instanceof PDFormXObject) {
            PDFormXObject form = (PDFormXObject) xobject;
            showForm(form);
        }
    }
}

三、修正文本坐标的匹配逻辑

TextPosition返回的x1/y1是字符基线的坐标,height是从基线到字符顶部的距离,需要确保和图片坐标使用同一坐标系,同时改用重叠检测(而非完全包含),避免遗漏部分覆盖的字符:

@Override
protected void processTextPosition(TextPosition text) {
    PDPage pdpage = getCurrentPage();
    int pageno = getCurrentPageNo();
    super.processTextPosition(text);

    // ... 其他字符属性获取代码保持不变 ...
    
    float x1 = text.getX();
    float y1 = text.getY();
    float height = text.getHeight();
    float width = text.getUnicode().trim().isEmpty() ? text.getWidthOfSpace() : text.getWidth();
    
    // 转换字符坐标到文本坐标系(和图片坐标统一)
    float pageHeight = pdpage.getMediaBox().getHeight();
    float charTextY1 = pageHeight - (y1 + height);
    float charTextY2 = pageHeight - y1;
    float charTextX2 = x1 + width;

    PCharacter pchar = new PCharacter(pageno, text.getDir(), x1, charTextY1, charTextX2, charTextY2, ...);

    // 判断字符是否与图片重叠
    boolean isOnImage = PSVGReader.coordinates.stream().anyMatch(coordinate -> 
        pageno == coordinate.getPageNo()
        && x1 < coordinate.getX2() // 字符左边界小于图片右边界
        && charTextX2 > coordinate.getX1() // 字符右边界大于图片左边界
        && charTextY1 < coordinate.getY2() // 字符上边界小于图片下边界
        && charTextY2 > coordinate.getY1() // 字符下边界大于图片上边界
    );

    // 只保留不在图片上的字符
    if (!isOnImage) {
        page.rawCharacters().add(pchar);
    }

    pages().put(pageno, page);
}

关键注意事项

  1. 坐标系统一:必须确保图片坐标和文本坐标使用同一基准(要么都是PDF原生的左下角原点,要么都是左上角原点),否则必然匹配失败。
  2. CTM变换的重要性:所有图形元素的位置都受CTM影响,直接手动计算缩放、旋转会忽略复杂变换场景(比如斜切、旋转的图片)。
  3. 重叠检测更实用:实际场景中字符可能只部分覆盖图片,用重叠判断比完全包含更准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:44:58