Cómo insertar una PDPage dentro de otra PDPage con pdfbox

Utilizo diferentes herramientas como el procesamiento para crear gráficos vectoriales. Estas plots están escritas como archivos PDF de una o varias páginas. Me gustaría incluir estas plots en un único informe tipo pdf usando pdfbox.

Mi flujo de trabajo actual incluye estos pdfs como imágenes con el siguiente pseudo código

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

Mientras esto funciona, pierde los beneficios de los formatos de archivo vectorial, especialmente el tamaño de archivo frente a la calidad de impresión.

¿Es posible usar pdfbox para incluir otras páginas de PDF como objetos incrustados dentro de una página (no se agrega como una página separada)? ¿Podría, por ejemplo, usar un PDStream? Preferiría una solución como pdflatex es capaz de incrustar figuras en pdf en un nuevo documento pdf.

¿Qué otras bibliotecas de Java puede recomendar para esa tarea?

¿Es posible usar pdfbox para incluir otras páginas de PDF como objetos incrustados dentro de una página?

Debería ser posible. El formato PDF permite el uso de los denominados objetos xobjects para servir como tales objetos incrustados. No veo una implementación explícita para eso, sin embargo, pero el procedimiento es lo suficientemente similar a lo que hace PageExtractor o PDFMergerUtility .

Una prueba de concepto derivada de PageExtractor utiliza el SNAPSHOT actual de la versión de desarrollo de PDFBox 2.0.0:

 PDDocument source = PDDocument.loadNonSeq(SOURCE, null); List 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(); 

Este código importa la primera página de un documento fuente a un documento objective como XObject y lo coloca dos veces en una página con diferentes transformaciones de escala y rotación, por ejemplo, para esta fuente

Fuente PDF, página 1

crea esto

PDF de destino

El método de ayuda importAsXObject realmente hace la importación se define así:

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

Como se mencionó anteriormente, esto es solo una prueba de concepto, los casos de esquina aún no se han tenido en cuenta.

Para actualizar esta pregunta:

Ya hay una clase de ayuda en org.apache.pdfbox.multipdf.LayerUtility para hacer la importación.

Ejemplo para mostrar la superposición de una página PDF en otro PDF: SuperimposePage .

Esta clase es parte de los ejemplos de Apache PDFBox y se le agregaron transformaciones de muestra como se muestra en @mkl.

Como mkl sugirió apropiadamente, PDFClown se encuentra entre las bibliotecas de Java que proporcionan soporte explícito para la incrustación de páginas (los denominados Form XObjects (ver PDF Referencia 1.7, § 4.9)).

Para que pueda conocer el funcionamiento de PDFClown, el siguiente código representa el equivalente de la solución PDFKox de mkl (NOTA: como mkl más tarde se dijo, su muestra de código no estaba de ninguna manera optimizada, por lo que esta comparación puede no corresponderse con la real estado de PDFBox – los comentarios son bienvenidos para aclarar esto):

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

Comparando este código con el equivalente de PDFBox, puede notar algunas diferencias relevantes que muestran el estilo más ordenado de PDFClown (sería bueno que algún experto en PDFBox pudiera validar mis afirmaciones):

  • Conversión de página a formulario XObject : PDFClown admite de forma nativa un método dedicado (Page.toXObject ()), por lo que no es necesario realizar tareas pesadas adicionales, como el método de ayuda importAsXObject ();
  • Gestión de recursos : PDFClown asigna de forma automática (y transparente) los recursos de la página , por lo que no es necesario realizar llamadas explícitas como page.getResources (). AddXObject (xobject, “X”);
  • Dibujo XObject : PDFClown admite métodos de alto nivel (escala explícita, anclajes de traducción y rotación) y de bajo nivel (transformaciones afines) para colocar su FormXObject en la página, por lo que no es necesario tratar necesariamente con transformaciones afines.

El punto es que PDFClown presenta una rica architecture compuesta por múltiples capas de abstracción : de acuerdo con sus requisitos, puede elegir el estilo de encoding más apropiado (ya sea para profundizar en las estructuras básicas de bajo nivel de PDF o para aprovechar su práctica y elegante alta modelo de nivel). PDFClown te permite modificar cada byte y resolver tareas complejas con una llamada a un método ridículamente simple, a tu antojo.

DIVULGACIÓN: soy el desarrollador principal de PDFClown.