如何将 PDF 页面的内容旋转到任意角度?
How do I rotate the contents of a PDF page to an arbitrary angle?
我需要将 PDF 页面的内容旋转任意角度,而 PDPage.setRotation(int) 命令被限制为 90 度的倍数。
页面的内容是矢量图和文字,我需要稍后能够放大内容,这意味着我无法将页面转换为图像,因为分辨率下降。
请阅读 ISO-32000-1(这是 PDF 的 ISO 标准),更多 specifically Table 30 ("Entries in a page object")。它像这样定义 Rotate
条目(文字 copy/paste):
The number of degrees by which the page shall be rotated clockwise
when displayed or printed. The value shall be a multiple of 90.
Default value: 0.
只要 ISO 标准使用 shall 一词,您就会遇到规范性规则(与标准使用 should 在这种情况下,您将面临推荐)。
简而言之:您要求的是 PDF 规范明确禁止的内容。 PDF 不可能满足您的要求。您的页面可以有 0、90、180 或 270 度的方向。您将不得不旋转页面上的内容,而不是旋转页面。
在评论中已经指出绘制一些内容,例如现有的常规纵向或横向页面,以任意角度到新的常规纵向或横向页面上,可以使用提供的机制 in this answer.
正如此处提供的代码
- 需要PDFBox开发2.0.0-SNAPSHOT版本和
- 使用在当前上下文中没有必要的形式 xobjects,
尽管如此,这里有一个适用于当前常规版本 1.8.8 的快速但不完善的解决方案,无需引入表单 xobjects。
这个方法
void transformPage(PDDocument document, PDPage page, AffineTransform at) throws IOException, COSVisitorException
{
PDRectangle cropBox = page.findCropBox();
float xOffset = (cropBox.getUpperRightX() + cropBox.getLowerLeftX()) / 2f;
float yOffset = (cropBox.getUpperRightY() + cropBox.getLowerLeftY()) / 2f;
AffineTransform transform = AffineTransform.getTranslateInstance(xOffset, yOffset);
transform.concatenate(at);
transform.concatenate(AffineTransform.getTranslateInstance(-xOffset, -yOffset));
PDPageContentStream stream = new PDPageContentStream(document, page, true, false);
stream.concatenate2CTM(transform);
stream.close();
COSBase contents = page.getCOSDictionary().getDictionaryObject(COSName.CONTENTS);
if (contents instanceof COSStreamArray)
{
COSStreamArray contentsArray = (COSStreamArray) contents;
COSArray newArray = new COSArray();
newArray.add(contentsArray.get(contentsArray.getStreamCount() - 1));
for (int i = 0; i < contentsArray.getStreamCount() - 1; i++)
{
newArray.add(contentsArray.get(i));
}
COSStreamArray newStreamArray = new COSStreamArray(newArray);
page.getCOSDictionary().setItem(COSName.CONTENTS, newStreamArray);
}
}
将给定的转换应用到给定的页面。为了使手头的用例(旋转 PDF 页面的内容)更容易,转换包含在转换中,将坐标系的原点移动到转换页面的中心。
方法可以这样用
try ( InputStream sourceStream = getClass().getResourceAsStream("13.pdf") )
{
final PDDocument document = PDDocument.load(sourceStream);
final AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 4);
List<PDPage> pages = document.getDocumentCatalog().getAllPages();
for (PDPage page: pages)
{
transformPage(document, page, transform);
}
document.save("13-transformedPages.pdf");
}
将文档的页面逆时针旋转 45°(PI/4,数学上的正旋转方向)。
我需要将 PDF 页面的内容旋转任意角度,而 PDPage.setRotation(int) 命令被限制为 90 度的倍数。 页面的内容是矢量图和文字,我需要稍后能够放大内容,这意味着我无法将页面转换为图像,因为分辨率下降。
请阅读 ISO-32000-1(这是 PDF 的 ISO 标准),更多 specifically Table 30 ("Entries in a page object")。它像这样定义 Rotate
条目(文字 copy/paste):
The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. Default value: 0.
只要 ISO 标准使用 shall 一词,您就会遇到规范性规则(与标准使用 should 在这种情况下,您将面临推荐)。
简而言之:您要求的是 PDF 规范明确禁止的内容。 PDF 不可能满足您的要求。您的页面可以有 0、90、180 或 270 度的方向。您将不得不旋转页面上的内容,而不是旋转页面。
在评论中已经指出绘制一些内容,例如现有的常规纵向或横向页面,以任意角度到新的常规纵向或横向页面上,可以使用提供的机制 in this answer.
正如此处提供的代码
- 需要PDFBox开发2.0.0-SNAPSHOT版本和
- 使用在当前上下文中没有必要的形式 xobjects,
尽管如此,这里有一个适用于当前常规版本 1.8.8 的快速但不完善的解决方案,无需引入表单 xobjects。
这个方法
void transformPage(PDDocument document, PDPage page, AffineTransform at) throws IOException, COSVisitorException
{
PDRectangle cropBox = page.findCropBox();
float xOffset = (cropBox.getUpperRightX() + cropBox.getLowerLeftX()) / 2f;
float yOffset = (cropBox.getUpperRightY() + cropBox.getLowerLeftY()) / 2f;
AffineTransform transform = AffineTransform.getTranslateInstance(xOffset, yOffset);
transform.concatenate(at);
transform.concatenate(AffineTransform.getTranslateInstance(-xOffset, -yOffset));
PDPageContentStream stream = new PDPageContentStream(document, page, true, false);
stream.concatenate2CTM(transform);
stream.close();
COSBase contents = page.getCOSDictionary().getDictionaryObject(COSName.CONTENTS);
if (contents instanceof COSStreamArray)
{
COSStreamArray contentsArray = (COSStreamArray) contents;
COSArray newArray = new COSArray();
newArray.add(contentsArray.get(contentsArray.getStreamCount() - 1));
for (int i = 0; i < contentsArray.getStreamCount() - 1; i++)
{
newArray.add(contentsArray.get(i));
}
COSStreamArray newStreamArray = new COSStreamArray(newArray);
page.getCOSDictionary().setItem(COSName.CONTENTS, newStreamArray);
}
}
将给定的转换应用到给定的页面。为了使手头的用例(旋转 PDF 页面的内容)更容易,转换包含在转换中,将坐标系的原点移动到转换页面的中心。
方法可以这样用
try ( InputStream sourceStream = getClass().getResourceAsStream("13.pdf") )
{
final PDDocument document = PDDocument.load(sourceStream);
final AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 4);
List<PDPage> pages = document.getDocumentCatalog().getAllPages();
for (PDPage page: pages)
{
transformPage(document, page, transform);
}
document.save("13-transformedPages.pdf");
}
将文档的页面逆时针旋转 45°(PI/4,数学上的正旋转方向)。