使用PDFBox在Java中围绕中心旋转PDF

5
PDDocument document = PDDocument.load(new File(input));
PDPage page = document.getDocumentCatalog().getPages().get(0);
PDPageContentStream cs = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.PREPEND, false, false); 
cs.transform(Matrix.getRotateInstance(Math.toRadians(45), 0, 0));

我正在使用上述代码旋转PDF。

enter image description here

对于上面的图片,我得到了以下输出

enter image description here

从那段代码来看,页面的内容已经移出了框架,并且旋转并不是围绕其中心发生的。但我希望得到以下输出:

enter image description here

请给我一些建议。提前致谢。


看起来你希望页面最终会以倾斜的矩形显示。但实际上不会这样。PDF页面总是有垂直或水平的边缘。这样页面的内容可以任意定向。为了让旋转后的内容在可见的矩形内(具有水平/垂直边缘),您可以使用Matrix.getRotateInstance调用的额外两个参数将其移动到那里(您将它们设置为0,0),或者通过相应地设置页面的mediabox和cropbox来移动可见的矩形。 - mkl
getRotateInstance 的第二个和第三个参数是在旋转之前和之后所做的 x 和 y 转换,以便您有效地围绕不同于原点的点进行旋转。尝试将页面宽度的一半作为 x,将页面高度的一半作为 y。如果不起作用,请在两者前面加上减号 - 我总是忘记应该将其指定为朝向原点的平移(在这种情况下,它们应该是负数)还是作为旋转点(在这种情况下,它们应该是正数)。 - Erwin Bolwidt
@mkl - 我尝试设置媒体和裁剪框,如下所示: page.setCropBox(new PDRectangle(0f,0f,595f,841f)); page.setMediaBox(new PDRectangle(0f,0f,595f,841f)); 但是结果仍然相同。 - sagar
我尝试通过设置媒体和裁剪框来解决问题,如page.setCropBox(new PDRectangle(0f,0f,595f,841f)); page.setMediaBox(new PDRectangle(0f,0f,595f,841f)); 但结果仍然相同。显然,您将页面向左旋转,因此其内容现在部分具有负x坐标值。为了使其位于框内,框必须以负的x坐标开始,而不是您选择的0。 - mkl
@mkl - 特别是在围绕中心旋转后 - 目前的旋转是通过将左下角视为原点来进行的。如果可以通过以页面中心为原点来进行旋转,那将非常有帮助。旋转后,我想隐藏超出页面范围的区域。 - sagar
显示剩余2条评论
1个回答

25

旋转页面内容的两种主要方式,使其在查看器中呈现为围绕可见页面中心发生旋转:一种是通过将旋转与平移连接起来,真正地围绕中心旋转;另一种方法是移动裁剪框,使页面区域中心跟随旋转。

真正围绕中心旋转

为此,我们需要用两个平移将旋转包装起来,第一个将坐标系原点移动到页面中心,第二个将其移回来。

PDDocument document = PDDocument.load(resource);
PDPage page = document.getDocumentCatalog().getPages().get(0);
PDPageContentStream cs = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.PREPEND, false, false); 
PDRectangle cropBox = page.getCropBox();
float tx = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2;
float ty = (cropBox.getLowerLeftY() + cropBox.getUpperRightY()) / 2;
cs.transform(Matrix.getTranslateInstance(tx, ty));
cs.transform(Matrix.getRotateInstance(Math.toRadians(45), 0, 0));
cs.transform(Matrix.getTranslateInstance(-tx, -ty));
cs.close();

(RotatePageContent测试testRotateCenter)

显然,您可以将矩阵相乘,并仅向PDF添加单个转换。

拉动裁剪框

为此,我们计算页面中心的移动,并相应地移动框。

PDDocument document = PDDocument.load(resource);
PDPage page = document.getDocumentCatalog().getPages().get(0);
PDPageContentStream cs = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.PREPEND, false, false);
Matrix matrix = Matrix.getRotateInstance(Math.toRadians(45), 0, 0);
cs.transform(matrix);
cs.close();

PDRectangle cropBox = page.getCropBox();
float cx = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2;
float cy = (cropBox.getLowerLeftY() + cropBox.getUpperRightY()) / 2;
Point2D.Float newC = matrix.transformPoint(cx, cy);
float tx = (float)newC.getX() - cx;
float ty = (float)newC.getY() - cy;
page.setCropBox(new PDRectangle(cropBox.getLowerLeftX() + tx, cropBox.getLowerLeftY() + ty, cropBox.getWidth(), cropBox.getHeight()));
PDRectangle mediaBox = page.getMediaBox();
page.setMediaBox(new PDRectangle(mediaBox.getLowerLeftX() + tx, mediaBox.getLowerLeftY() + ty, mediaBox.getWidth(), mediaBox.getHeight()));

(旋转页面内容 测试 testRotateMoveBox)

旋转后缩小内容以适应页面

如果想要缩小旋转后的内容以使其全部适合页面,可以将其作为第一种变体的简单扩展来完成:

PDDocument document = PDDocument.load(resource);
PDPage page = document.getDocumentCatalog().getPages().get(0);
PDPageContentStream cs = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.PREPEND, false, false);

Matrix matrix = Matrix.getRotateInstance(Math.toRadians(45), 0, 0);
PDRectangle cropBox = page.getCropBox();
float tx = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2;
float ty = (cropBox.getLowerLeftY() + cropBox.getUpperRightY()) / 2;

Rectangle rectangle = cropBox.transform(matrix).getBounds();
float scale = Math.min(cropBox.getWidth() / (float)rectangle.getWidth(), cropBox.getHeight() / (float)rectangle.getHeight());

cs.transform(Matrix.getTranslateInstance(tx, ty));
cs.transform(matrix);
cs.transform(Matrix.getScaleInstance(scale, scale));
cs.transform(Matrix.getTranslateInstance(-tx, -ty));
cs.close();

(RotatePageContent测试testRotateCenterScale)

更改裁剪框以使所有先前的页面区域仍然可见

如果想要改变裁剪框而不进行缩放,以使所有内容适合于页面上,则可以轻松扩展第二种方案:

PDDocument document = PDDocument.load(resource);
PDPage page = document.getDocumentCatalog().getPages().get(0);
PDPageContentStream cs = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.PREPEND, false, false);
Matrix matrix = Matrix.getRotateInstance(Math.toRadians(45), 0, 0);
cs.transform(matrix);
cs.close();

PDRectangle cropBox = page.getCropBox();
Rectangle rectangle = cropBox.transform(matrix).getBounds();
PDRectangle newBox = new PDRectangle((float)rectangle.getX(), (float)rectangle.getY(), (float)rectangle.getWidth(), (float)rectangle.getHeight());
page.setCropBox(newBox);
page.setMediaBox(newBox);

(RotatePageContent测试testRotateExpandBox)

样例结果

以下图像显示了上述每种方法的输出:

截图

  1. 实际围绕中心旋转
  2. 旋转后缩小内容以适应页面
  3. 沿着裁剪框移动
  4. 更改裁剪框以使所有之前的页面区域保持可见

第四幅图与其他图不在同一比例尺下,应该显示得更大。


1
document.save() 命令在 RotatePageContent 测试示例中,但不在实际答案中。 - ponder275
实际上,这里的代码侧重于实例化所需的对象并使用它们来创建所需的效果。无论之后文档是否保存在某个地方或以其他方式使用,例如逐页复制到其他文档中,都不是我们关心的。 - mkl
1
@mkl,非常感谢您详细的答复。我发表评论只是为了帮助一些像我一样不了解pdfBox的人节省一些时间,而不是批评您的答案。 - ponder275
Matrix.getTranslateInstance(tx, ty)和Matrix.getTranslateInstance(-tx, -ty)的目的是什么? - nishantbhardwaj2002
2
矩阵旋转始终围绕点[0,0]进行,要使图像围绕其中心旋转,您必须先将图像移动到其中心位于[0,0]的位置,然后再将其平移回原来的位置。@nishantbhardwaj2002 - jnovacho
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接