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

PDFBox获取PDF方向不符合预期 旋转PDF签名坐标定位偏差排查

问题背景

我尝试获取PDF页面方向以计算签名图片的放置坐标,现有两份宽高参数完全一致但页面方向不同的PDF,最初用于获取页面宽高的代码如下:

PDPage page = doc.getPage(0);
PDRectangle pageRect = page.getCropBox();
System.out.println("Width page " + pageRect.getWidth());
System.out.println("Height page " + pageRect.getHeight());

测试用两份样例分别为0度方向PDF、180度方向PDF。
基于0度PDF定义的签名坐标计算逻辑如下:

float lx = (float) 300;
float ly = (float) 278;
float rx = (float) 442;
float ry = (float) 349;
Rectangle2D humanRect = new Rectangle2D.Float(lx, ry, rx - lx, ry - ly);

float x = (float) humanRect.getX();


float width = (float) humanRect.getWidth();
float height = (float) humanRect.getHeight();

PDPage page = doc.getPage(0);

PDRectangle pageRect = page.getCropBox();
LogSystem.info("Width page " + pageRect.getWidth());
LogSystem.info("Height page " + pageRect.getHeight());

float pageWidth = (float) pageRect.getWidth();
float pageHeigth = (float) pageRect.getHeight();

PDRectangle rect = new PDRectangle();

float y = pageRect.getHeight() - (float)humanRect.getY();

int orientation = page.getRotation();

// signing should be at the same position regardless of page rotation.

switch (orientation)
{
        case 90:
            rect.setLowerLeftY(x);
            rect.setUpperRightY(x + width);
            rect.setLowerLeftX(y);
            rect.setUpperRightX(y + height);
            break;
        case 180:
            rect.setUpperRightX(pageWidth - x);
            rect.setLowerLeftX(pageWidth - x - width);
            rect.setLowerLeftY(y);
            rect.setUpperRightY(y + height);
            break;
        case 270:
            rect.setLowerLeftY(pageHeigth - x - width);
            rect.setUpperRightY(pageHeigth - x);
            rect.setLowerLeftX(pageWidth - y - height);
            rect.setUpperRightX(pageWidth - y);
            break;
        case 0:
        default:
            rect.setLowerLeftX(x);
            rect.setUpperRightX(x + width);
            rect.setLowerLeftY(pageHeigth - y - height);
            rect.setUpperRightY(pageHeigth - y);
            break;
    }
    LogSystem.info("Rect"+rect);

    return rect;

上述switch分支的坐标转换逻辑参考自CreateVisibleSignature2.java文件中的createSignatureRectacle方法。实际测试中,0度PDF上的签名位置完全符合预期,但180度PDF上的签名位置存在偏差、定位不精准。
核心疑问:

  • 为何调用getRotation()获取的页面方向结果不符合预期?
  • 该如何正确处理该问题?
  • 是否支持忽略页面方向,始终默认文档方向为0度来指定签名坐标?
问题原因

180度页面签名偏移和getRotation()取值本身无关,是转换逻辑存在两个明确错误:

  • 你在switch分支外提前计算了float y = pageRect.getHeight() - (float)humanRect.getY();,这个操作是0度场景下专门用来把「视觉左上角为原点的坐标」翻转成PDF原生坐标系(左下角为原点)的逻辑,你把这个预翻转的y值直接传入180度分支,相当于做了两次坐标翻转,必然出现位置偏移。
  • 直接取getCropBox()作为页面尺寸边界的写法不可靠,部分PDF的CropBox存在偏移值,正确的页面边界应该取MediaBox和CropBox的交集。
正确处理方案

方案1:修正手写坐标转换逻辑

删除提前计算y值的代码,所有坐标转换逻辑放到对应旋转分支内统一处理,同时修正页面边界的获取方式:

PDPage page = doc.getPage(0);
// 取正确的页面边界:MediaBox和CropBox的交集
PDRectangle pageRect = page.getMediaBox();
if (page.getCropBox() != null) {
    pageRect = pageRect.intersection(page.getCropBox());
}
float pageWidth = pageRect.getWidth();
float pageHeight = pageRect.getHeight();

// 这里的x/y是你基于视觉页面左上角为原点定义的签名坐标
float x = 300f;
float y = 278f;
float width = 142f;
float height = 71f;

PDRectangle rect = new PDRectangle();
int rotation = page.getRotation();
switch (rotation) {
    case 90:
        rect.setLowerLeftX(y);
        rect.setLowerLeftY(pageWidth - x - width);
        rect.setUpperRightX(y + height);
        rect.setUpperRightY(pageWidth - x);
        break;
    case 180:
        rect.setLowerLeftX(pageWidth - x - width);
        rect.setLowerLeftY(y);
        rect.setUpperRightX(pageWidth - x);
        rect.setUpperRightY(y + height);
        break;
    case 270:
        rect.setLowerLeftX(pageHeight - y - height);
        rect.setLowerLeftY(x);
        rect.setUpperRightX(pageHeight - y);
        rect.setUpperRightY(x + width);
        break;
    case 0:
    default:
        rect.setLowerLeftX(x);
        rect.setLowerLeftY(pageHeight - y - height);
        rect.setUpperRightX(x + width);
        rect.setUpperRightY(pageHeight - y);
        break;
}
return rect;

方案2:用PDFBox内置矩阵转换,彻底避免手动算错

不需要自己手写旋转分支,直接用PDFBox自带的Matrix做坐标逆变换,不管页面旋转多少度,你只需要传入基于0度视觉页面左上角的坐标即可:

PDPage page = doc.getPage(0);
PDRectangle pageRect = page.getMediaBox();
if (page.getCropBox() != null) {
    pageRect = pageRect.intersection(page.getCropBox());
}
// 签名在视觉页面的位置(左上角原点)
float x = 300f, y = 278f, width = 142f, height = 71f;
// 获取页面旋转对应的变换矩阵,做逆变换映射到PDF原生坐标系
Matrix matrix = page.getMatrix();
Matrix inverse = matrix.createInverse();
// 转换签名矩形的两个对角点
Point2D.Float lowerLeft = inverse.transformPoint(x + pageRect.getLowerLeftX(), pageRect.getUpperRightY() - y - height);
Point2D.Float upperRight = inverse.transformPoint(x + width + pageRect.getLowerLeftX(), pageRect.getUpperRightY() - y);
PDRectangle rect = new PDRectangle(lowerLeft.x, lowerLeft.y, upperRight.x - lowerLeft.x, upperRight.y - lowerLeft.y);
return rect;
忽略页面旋转固定按0度算坐标的实现方式

完全支持,两种实现路径可选:

  • 不需要修改原PDF的场景:直接用上述方案2的矩阵转换逻辑,不管页面旋转值是多少,你都可以统一按0度视觉页面的坐标规则传值,转换逻辑会自动适配。
  • 可以修改PDF页面属性的场景:在计算坐标前,先把页面所有内容做对应旋转角度的反向变换,再调用page.setRotation(0)把页面旋转值强制设为0,后续所有坐标都直接按0度规则计算即可,不需要做任何转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:31:11