Search code examples
javapdflayerpdfbox

How to generate layered pdf file using PdfBox?


I have a problem with generating a layered pdf page using PdfBox. I have seen several posts here on the subject, but they focus on importing pages from another pdf to a target document.

My case is a liitle bit different (at least I think so :) ). I created a class MapImage that contains the paper size (in pixels) and a list of BufferedImages' that I want to add to a single pdf page.

Here is the code that I tried:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
PDDocument document = new PDDocument();
for (MapImage image : images) {
    PDPage page = new PDPage(new PDRectangle(image.getPaperWidth(), image.getPaperHeight()));
    page.setResources(new PDResources(new COSDictionary()));
    document.addPage(page);

    LayerUtility layerUtility = new LayerUtility(document);
    int i=1;
    for(BufferedImage layer : image.getLayers()) {
        PDJpeg img = new PDJpeg(document, layer);                        
        layerUtility.appendFormAsLayer(page, new PDXObjectForm(img.getCOSStream()), new AffineTransform(), "Layer " + i++);
    }
}
document.save(baos);
document.close();

Unfortunately the resulting PDF is corrupted. I manged to create a page with only one image (with no layers) but unfortunately I have no idea how to do this.

Did anyone have come accross such problem ?


Solution

  • OK, I solved it. This is the method that does what I wanted. Maybe it will be useful for someone :)

    public static PDOptionalContentGroup appendImageAsLayer(PDDocument document, PDPage targetPage, BufferedImage image, String layerName) throws IOException {
        PDDocumentCatalog catalog = document.getDocumentCatalog();
        PDOptionalContentProperties ocprops = catalog.getOCProperties();
        if (ocprops == null) {
            ocprops = new PDOptionalContentProperties();
            catalog.setOCProperties(ocprops);
        }
        if (ocprops.hasGroup(layerName)) {
            throw new IllegalArgumentException("Optional group (layer) already exists: " + layerName);
        }
    
        PDOptionalContentGroup layer = new PDOptionalContentGroup(layerName);
        ocprops.addGroup(layer);
    
        PDResources resources = targetPage.findResources();
        if(resources == null ) {
            resources = new PDResources(new COSDictionary());
            targetPage.setResources(resources);
        }
        PDPropertyList props = resources.getProperties();
        if (props == null) {
            props = new PDPropertyList();
            resources.setProperties(props);
        }
    
        // Find first free resource name with the pattern "MC<index>"
        int index = 0;
        PDOptionalContentGroup ocg;
        COSName resourceName;
        do {
            resourceName = COSName.getPDFName("MC" + index);
            ocg = props.getOptionalContentGroup(resourceName);
            index++;
        } while (ocg != null);
        // Put mapping for our new layer/OCG
        props.putMapping(resourceName, layer);
        PDJpeg img = new PDJpeg(document, image);
    
        PDPageContentStream contentStream = new PDPageContentStream(document, targetPage, true, false);
        contentStream.beginMarkedContentSequence(COSName.OC, resourceName);
        contentStream.drawImage(img, 0, 0);
        contentStream.endMarkedContentSequence();
        contentStream.close();
    
        return layer;
    }