【问题标题】:Add page as layer from separate pdf(different page size) using pdfbox使用 pdfbox 从单独的 pdf(不同的页面大小)添加页面作为图层
【发布时间】:2015-04-02 11:14:18
【问题描述】:

如果页面大小不同,如何将页面从外部 pdf 文档添加到目标 pdf? 这是我想要完成的事情:

我尝试使用 LayerUtility(如本例中的 PDFBox LayerUtility - Importing layers into existing 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();

我做错了什么?

【问题讨论】:

  • 如果您的主要关注点是代码中的 hanging process,而不是 如果页面不同,则将页面从外部 pdf doc 添加到目标 pdf尺寸 一般来说,需要更多信息:我运行了您的代码,并且对于我在这里的示例输入文档,它可以完美运行。因此,导致挂起的问题取决于一些额外的因素。可能您的源文件很特殊,应该检查一下。
  • 实际上进程并没有挂起,只是根本没有创建文件。如果我取消外部页面导入,并将此页面添加到目标文档,一切正常:文件已创建,外部页面添加为最后一页。
  • 这里是如果页面只是作为下一页添加的代码:d.pr/n/11Dv1 - 如果作为表单导入,则不会创建 tmp 文件,没有错误,什么都没有......
  • 看来您的文档有些特别之处。你能分享它们吗?此外,请注明您使用的 PDFBox 版本。我正在使用 1.8.8 进行测试。

标签: java pdf pdfbox


【解决方案1】:

PDFBox 依赖项

主要问题是 PDFBox 具有三个核心组件和一个必需的依赖项。缺少一个核心组件。

在 cmets 中,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 ...

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

PDFBox 版本 1.8.x 依赖项描述为here。尤其是pdfboxfontboxjempbox这三个核心组件都应该在同一个版本中,并且有需要依赖commons-logging

添加缺失的组件后,示例就可以正常工作了。

定位导入页面

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

对于原始示例文件,此 PDF 页面

已添加到此页面上

这导致了这个页面

OP然后想知道

如何定位导入的图层

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

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

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

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

PDFBox LayerUtility 的期望

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

layerUtility.appendFormAsLayer 使用此代码添加额外的内容流:

PDPageContentStream contentStream = new PDPageContentStream(
        targetDoc, targetPage, true, !DEBUG);

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

如果改为

PDPageContentStream contentStream = new PDPageContentStream(
        targetDoc, targetPage, true, !DEBUG, true);

已被使用,图形状态将被重置为其默认状态,因此是已知的。

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

不过,幸运的是,LayerUtility 也有一个方法 wrapInSaveRestore(PDPage) 来克服这个弱点,方法是操纵给定页面的内容,使其最后具有默认图形状态。

因此,应该替换

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

通过

PDPage destPage = destinationPages.get(0);
layerUtility.wrapInSaveRestore(destPage);
layerUtility.appendFormAsLayer(destPage, firstForm, affineTransform, "external page");

【讨论】:

  • @mkl 请为此在 JIRA 中打开一个问题。我通常很乐意为你做这件事,但这次我不确定我是否完全理解了这个问题,所以最好用你自己的话来做。 (我认为您想要重置图形上下文的选项,但我想知道当前情况是否是功能,而不是故障)。
  • @TilmanHausherr 查看它的代码,我发现LayerUtility 包含一个方法wrapInSaveRestore,如果是未知文档,您总是应该在importPageAsForm 之前调用。根据其 JavaDocs LayerUtility 的说法,只有“大多数文档确实将其内容包装为保存/恢复状态”支持的期望。哦,好吧,我会在这里编辑我的答案。
  • @TilmanHausherr 不幸的是,wrapInSaveRestore 方法本身就是错误的,参见。 PDFBOX-2666。哦,好吧。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-15
  • 2015-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多