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

时间:2014-10-08 12:09:06

标签: java pdf pdfbox

我使用不同的工具,如处理来创建矢量图。这些图表是单页或多页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库?

3 个答案:

答案 0 :(得分:4)

  

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

应该可以。 PDF格式允许使用所谓的表单xobjects作为这样的嵌入对象。但是,我没有看到明确的实现,但该过程与PageExtractorPDFMergerUtility的过程类似。

使用PDFBox 2.0.0开发版本的当前SNAPSHOT从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();

此代码将源文档的第一页作为XObject导入目标文档,并将其两次放到具有不同缩放和旋转变换的页面上,例如:对于这个来源

Source PDF, page 1

它创建了这个

Target PDF

实际执行导入的辅助方法importAsXObject定义如下:

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;
}

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

答案 1 :(得分:3)

更新此问题:

  

org.apache.pdfbox.multipdf.LayerUtility中已有一个辅助类来进行导入。

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

此类是Apache PDFBox Examples的一部分,并且已添加@mkl所示的示例转换。

答案 2 :(得分:2)

正如mkl所说, PDFClown 是提供显式支持页面嵌入的Java库(所谓的Form XObjects)(参见PDF参考资料1.7) ,§4.9))。

为了让您体验PDFClown的工作方式,下面的代码代表mkl的PDFBox解决方案(注意:正如mkl后面所述,他的代码示例并未进行优化,因此此比较可能不对应到PDFBox的实际状态 - 欢迎评论澄清这一点):

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();

将此代码与PDFBox的等效代码进行比较,您可以注意到一些显示PDFClown整洁风格的相关差异(如果某些PDFBox专家可以验证我的断言,那将会很不错):

  • Page-to-FormXObject转换 PDFClown本身支持专用方法(Page.toXObject()),因此不需要额外的繁重工作,例如辅助方法importAsXObject();
  • 资源管理 PDFClown自动(并透明地)分配页面资源,因此不需要显式调用,例如page.getResources()。addXObject(xobject,“ X“);
  • XObject绘图 PDFClown支持高级(显式缩放,平移和旋转锚点)和低级(仿射变换)方法将FormXObject放入页面,所以没有必要处理仿射变换。

重点是 PDFClown具有由多个抽象层组成的丰富架构:根据您的要求,您可以选择最合适的编码风格(深入研究PDF的低级基础)结构或利用其方便和优雅的高级模型)。 PDFClown让您可以随意调整每个字节,并通过一个非常简单的方法调用来解决复杂的任务。

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