使用pdfbox从单独的pdf(不同页面大小)添加页面作为图层

Jan*_*iec 5 java pdf pdfbox

如果页面大小不同,如何将外部pdf文档中的页面添加到目标pdf?这是我想要完成的事情: 在此输入图像描述

我尝试使用LayerUtility(就像在这个例子中PDFBox LayerUtility - 将图层导入到现有PDF中),但是一旦我从外部pdf导入页面,该过程就会挂起:

PDDocument destinationPdfDoc = PDDocument.load(fileInputStream);
PDDocument externalPdf = PDDocument.load(EXTERNAL PDF);

List<PDPage> destinationPages = destinationPdfDoc.getDocumentCatalog().getAllPages();

LayerUtility layerUtility = new LayerUtility(destinationPdfDoc);

// process hangs here
PDXObjectForm firstForm = layerUtility.importPageAsForm(externalPdf, 0);

AffineTransform affineTransform = new AffineTransform();
layerUtility.appendFormAsLayer(destinationPages.get(0), firstForm, affineTransform, "external page");


destinationPdfDoc.save(resultTempFile);

destinationPdfDoc.close();
externalPdf.close();
Run Code Online (Sandbox Code Playgroud)

我做错了什么?

mkl*_*mkl 6

PDFBox依赖项

主要问题是PDFBox有三个核心组件,一个需要依赖.缺少一个核心组件.

OP在评论中澄清了这一点

实际上进程没有挂起,文件根本就没有创建.

由于这听起来可能有异常或错误,因此在聊天中提出了尝试将代码封装为try { ... } catch (Throwable t) { t.printStackTrace(); }块的方法.事实上,

java.lang.NoClassDefFoundError: org/apache/fontbox/util/BoundingBox 
    at org.apache.pdfbox.util.LayerUtility.importPageAsForm(LayerUtility.java:203) 
    at org.apache.pdfbox.util.LayerUtility.importPageAsForm(LayerUtility.java:135) 
    at ...
Run Code Online (Sandbox Code Playgroud)

事实证明,OP的设置中缺少fontbox.jar.

此处描述 PDFBox版本1.8.x依赖项.特别是有三个核心组件pdfbox,fontboxjempbox,所有这些组件都应存在于同一版本中,并且存在所需的依赖性commons-logging.

一旦添加了缺失的组件,样本就能正常工作.

定位导入的页面

可以通过AffineTransform参数中的转换将导入的页面定位在目标页面上.此参数还允许其他变换,例如缩放,旋转,镜像,倾斜,......*

对于原始样本文件,此PDF页面

来自test-pdf.pdf的源页面

被添加到此页面上

在此输入图像描述

这导致了这个页面

OP原始代码的结果

OP然后想知道

如何定位导入的图层

layerUtility.appendFormAsLayer呼叫中的参数是AffineTransform affineTransform.new AffineTransform()这里使用的OP 创建了一个单位矩阵,这又使得源页面被添加到坐标系的原点,在这种情况下是在底部.

通过使用翻译而不是身份,例如

PDRectangle destCrop = destinationPages.get(0).findCropBox();
PDRectangle sourceBox = firstForm.getBBox();
AffineTransform affineTransform = AffineTransform.getTranslateInstance(0, destCrop.getUpperRightY() - sourceBox.getHeight());
Run Code Online (Sandbox Code Playgroud)

可以将源页面放在其他位置,例如在顶部:

结果使用上面的翻译

PDFBox LayerUtility的期望

不幸的是,事实证明,layerUtility.appendFormAsLayer将表单附加到页面而不重置图形上下文.

layerUtility.appendFormAsLayer 使用此代码添加其他内容流:

PDPageContentStream contentStream = new PDPageContentStream(
        targetDoc, targetPage, true, !DEBUG);
Run Code Online (Sandbox Code Playgroud)

遗憾的是,此构造函数生成的内容流将继承目标页面现有内容末尾的图形状态.这尤其意味着用户空间坐标系可能不再处于其默认状态.一些软件例如镜像坐标系以使y坐标向下增加.

如果相反

PDPageContentStream contentStream = new PDPageContentStream(
        targetDoc, targetPage, true, !DEBUG, true);
Run Code Online (Sandbox Code Playgroud)

如果已经使用过,图形状态将被重置为默认状态,因此可以知道.

因此,该方法本身不能以受控方式用于任意输入.

幸运的是,LayerUtility还有一种方法wrapInSaveRestore(PDPage)可以通过操纵给定页面的内容来最终获得默认图形状态来克服这个弱点.

因此,人们应该取而代之

layerUtility.appendFormAsLayer(destinationPages.get(0), firstForm, affineTransform, "external page");
Run Code Online (Sandbox Code Playgroud)

通过

PDPage destPage = destinationPages.get(0);
layerUtility.wrapInSaveRestore(destPage);
layerUtility.appendFormAsLayer(destPage, firstForm, affineTransform, "external page");
Run Code Online (Sandbox Code Playgroud)