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
相关产品推荐
相关产品推荐

