【问题标题】:How to insert an PDPage within another PDPage with pdfbox如何使用 pdfbox 在另一个 PDPage 中插入 PDPage
【发布时间】:2014-12-03 02:13:12
【问题描述】:

我使用不同的工具(例如处理)来创建矢量图。这些图以单页或多页 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 格式允许使用所谓的 form xobjects 来充当此类嵌入对象。不过,我没有看到明确的实现,但该过程与PDFMergerUtility 所做的足够相似。 您可以为该任务推荐哪些其他 Java 库? - 任何通用库都应该使您能够做到这一点,iText 和 PdfClown 都明确支持类似的东西。
  • @mkl 对,这应该是一个相当普遍的任务。查看 PDFMergerUtility 并将 pdf 添加为单独的页面。 iText 不是一个选项,因为它的最新版本的许可证更改。不过会看看 PdfClown。感谢您指出它。

标签: java pdf pdfbox


【解决方案1】:

是否可以使用 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 导入到目标文档中,并将其两次放到具有不同缩放和旋转变换的页面上,例如对于这个来源

它创造了这个

实际执行导入的辅助方法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;
}

如上所述,这只是一个概念证明,尚未考虑极端情况。

【讨论】:

  • 像魅力一样工作。想补充一点,它需要来自存储库的 2.0.0 快照。虽然还不能编辑你的答案。谢谢。
  • 我添加了对 2.0.0 使用的提示。
  • 您可能希望将您的解决方案添加到 pdfbox 文档中。无论如何,有一个postponed request。这甚至可能触发它的实现......
【解决方案2】:

更新这个问题:

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

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

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

【讨论】:

    【解决方案3】:

    正如 mkl 适当建议的那样,PDFClown 是为页面嵌入提供显式支持的 Java 库之一(所谓的 Form XObjects(参见 PDF 参考 1.7,§ 4.9))。

    为了让你体验一下PDFClown的工作原理,下面的代码相当于mkl的PDFBox解决方案(注意:正如mkl后面所说,他的代码示例绝不是优化的,所以这个比较可能不对应到 PDFBox 的实际状态——欢迎 cmets 澄清这一点):

    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 的首席开发人员。

    【讨论】:

    • 关于比较:我不是 PDFBox 专家。因此,比较中的一些缺点可能是由于我的编码,而不是由于 PDFBox 的缺陷。例如。关于资源管理,我认为我明确添加资源并不是绝对必要的。
    • @mkl 感谢您的注意:我更新了我的答案,以确保读者不会将其解释为客观比较。
    • @StefanoChizzolini 感谢您添加此演示。它确实有一个很好的高级 API。
    猜你喜欢
    • 2017-07-06
    • 1970-01-01
    • 2014-01-09
    • 1970-01-01
    • 1970-01-01
    • 2019-08-29
    • 2019-08-30
    相关资源
    最近更新 更多