Java 如何使用pdfbox在另一个PDPage中插入PDPage

Java 如何使用pdfbox在另一个PDPage中插入PDPage,java,pdf,pdfbox,Java,Pdf,Pdfbox,我使用不同的工具,如处理来创建矢量图。这些绘图以单页或多页PDF格式编写。我想使用pdfbox将这些图包含在一个单独的报告中,如pdf 我当前的工作流程将这些PDF作为图像包含在下面的伪代码中 PDDocument inFile = PDDocument.load(file); PDPage firstPage = (PDPage) inFile.getDocumentCatalog().getAllPages().get(0); BufferedImage image = firstPage.

我使用不同的工具,如处理来创建矢量图。这些绘图以单页或多页PDF格式编写。我想使用pdfbox将这些图包含在一个单独的报告中,如pdf

我当前的工作流程将这些PDF作为图像包含在下面的伪代码中

PDDocument inFile = PDDocument.load(file);
PDPage firstPage = (PDPage) inFile.getDocumentCatalog().getAllPages().get(0);
BufferedImage image = firstPage.convertToImage(BufferedImage.TYPE_INT_RGB, 300);
PDXObjectImage ximage = new PDPixelMap(document, image);

PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.drawXObject(ximage, 0, 0, ximage.getWidth(), ximage.getHeight());
contentStream.close();
虽然这样做,但它失去了矢量文件格式的优势,特别是文件/大小与打印质量的对比

是否可以使用pdfbox将其他pdf页面作为嵌入对象包含在页面中(不作为单独页面添加)?例如,我可以使用PDStream吗?我更喜欢pdflatex这样的解决方案,它能够将pdf图形嵌入到新的pdf文档中

对于该任务,您可以推荐哪些其他Java库

是否可以使用pdfbox将其他pdf页面作为嵌入对象包含在页面中

这应该是可能的。PDF格式允许使用所谓的表单XObject作为此类嵌入对象。虽然我没有看到一个明确的实现,但是这个过程与
PageExtractor
PDFMergerUtility
所做的非常相似

使用PDFBox 2.0.0开发版本的当前快照从
PageExtractor
派生出的概念证明:

PDDocument source = PDDocument.loadNonSeq(SOURCE, null);
List<PDPage> pages = source.getDocumentCatalog().getAllPages();

PDDocument target = new PDDocument();
PDPage page = new PDPage();
PDRectangle cropBox = page.findCropBox();
page.setResources(new PDResources());
target.addPage(page);

PDFormXObject xobject = importAsXObject(target, pages.get(0));
page.getResources().addXObject(xobject, "X");

PDPageContentStream content = new PDPageContentStream(target, page);
AffineTransform transform = new AffineTransform(0, 0.5, -0.5, 0, cropBox.getWidth(), 0);
content.drawXObject(xobject, transform);
transform = new AffineTransform(0.5, 0.5, -0.5, 0.5, 0.5 * cropBox.getWidth(), 0.2 * cropBox.getHeight());
content.drawXObject(xobject, transform);
content.close();

target.save(TARGET);
target.close();
source.close();

如上所述,这只是一个概念证明,尚未考虑角落案例。

正如mkl适当建议的那样,是为页面嵌入提供明确支持的Java库之一(所谓的表单XObject(参见PDF参考1.7,§4.9))

为了让您了解PDFClown的工作方式,以下代码代表了mkl的PDFBox解决方案(注意:正如mkl后来所说,他的代码示例并没有经过优化,因此此比较可能与PDFBox的实际状态不符——欢迎评论以澄清这一点):

将此代码与PDFBox的等效代码进行比较,您可以注意到一些相关的差异,这些差异显示了PDFClown的整洁风格(如果一些PDFBox专家能够验证我的断言,那就太好了):

  • 页面到表单对象的转换:PDFClown本机支持专用方法(Page.toXObject()),因此不需要额外的繁重工作,例如帮助器方法importAsXObject()
  • 资源管理:PDFClown自动(透明)分配页面资源,因此不需要显式调用,例如page.getResources().addXObject(xobject,“X”)
  • XObject绘图:PDFClown支持高级(显式缩放、平移和旋转定位)和低级(仿射变换)方法将FormXObject放入页面,因此无需处理仿射变换
关键是PDFClown具有由多个抽象层组成的丰富体系结构:根据您的需求,您可以选择最合适的编码风格(深入研究PDF的低级基本结构或利用其方便而优雅的高级模型)。PDFClown允许您随意调整每个字节,并通过一个极其简单的方法调用来解决复杂任务


披露:我是PDFClown的首席开发人员。

更新此问题:

org.apache.pdfbox.multipdf.layeruptity
中已经有一个助手类来执行导入

将PDF页面叠加到另一个PDF上的示例:


此类是示例的一部分,并向其添加了@mkl所示的转换。

是否可以使用pdfbox将其他pdf页面作为嵌入对象包含在页面中?应该可以。PDF格式允许使用所谓的表单XObject作为此类嵌入对象。虽然我没有看到一个明确的实现,但是这个过程与
PDFMergerUtility
的功能非常相似。对于该任务,您可以推荐哪些其他Java库任何通用库都应该允许您这样做,iText和PdfClown都明确支持类似的功能。@mkl对,这应该是一项相当常见的任务。查看
PDFMergerUtility
,它将PDF添加为单独的页面。iText不是一个选项,因为其最新版本的许可证已更改。不过我会看看PdfClown。谢谢你指点它。它很有魅力。要添加的是,它需要存储库中的2.0.0快照。但仍无法编辑您的答案。谢谢。我添加了一个关于2.0.0使用的提示。您可能希望将您的解决方案添加到pdfbox文档中。无论如何,这已经有了一个很好的解释。这甚至可能触发它的实现……关于比较:我不是PDFBox专家。因此,比较中的一些缺点可能是由于我的编码,而不是由于PDFBox的缺陷。例如,关于资源管理,我认为我明确添加资源并非绝对必要。@mkl感谢您的注意:我更新了我的答案,以确保读者不会将其理解为客观比较。@Stefanochizzolin感谢您添加此演示。它确实有一个很好的高级API。
PDFormXObject importAsXObject(PDDocument target, PDPage page) throws IOException
{
    final PDStream src = page.getContents();
    if (src != null)
    {
        final PDFormXObject xobject = new PDFormXObject(target);

        OutputStream os = xobject.getPDStream().createOutputStream();
        InputStream is = src.createInputStream();
        try
        {
            IOUtils.copy(is, os);
        }
        finally
        {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        }

        xobject.setResources(page.findResources());
        xobject.setBBox(page.findCropBox());

        return xobject;
    }
    return null;
}
Document source = new File(SOURCE).getDocument();
Pages sourcePages = source.getPages();

Document target = new File().getDocument();
Page targetPage = new Page(target);
target.getPages().add(targetPage);

XObject xobject = sourcePages.get(0).toXObject(target);

PrimitiveComposer composer = new PrimitiveComposer(targetPage);
Dimension2D targetSize = targetPage.getSize();
Dimension2D sourceSize = xobject.getSize();
composer.showXObject(xobject, new Point2D.Double(targetSize.getWidth() * .5, targetSize.getHeight() * .35), new Dimension(sourceSize.getWidth() * .6, sourceSize.getHeight() * .6), XAlignmentEnum.Center, YAlignmentEnum.Middle, 45);
composer.showXObject(xobject, new Point2D.Double(targetSize.getWidth() * .35, targetSize.getHeight()), new Dimension(sourceSize.getWidth() * .4, sourceSize.getHeight() * .4), XAlignmentEnum.Left, YAlignmentEnum.Top, 90);
composer.flush();

target.getFile().save(TARGET, SerializationModeEnum.Standard);
source.getFile().close();