如何使用PdfBox生成分层PDF文件?

3
我在使用PdfBox生成分层pdf页面时遇到了问题。我看到这里有几篇帖子讨论了这个问题,但它们都集中在从另一个pdf导入页面到目标文档上。
我的情况有点不同(至少我认为是这样的 :))。我创建了一个名为MapImage的类,其中包含纸张大小(以像素为单位)和我想要添加到单个pdf页面的BufferedImages列表。
以下是我尝试的代码:
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();

很遗憾,生成的PDF文件已经损坏。 我设法创建了一个只有一张图片(没有图层)的页面,但不幸的是我不知道如何做到这一点。

是否有人遇到过这样的问题?


你想使用可选内容组(也称为图层)的原因是什么? - mkl
是的。我应该准备一个包含地图数据的PDF文件。该地图由许多不同的图层组成。有时候,接收PDF文件的人需要能够关闭某些图层以查看它们下面的内容。 - Marcin Roguski
啊,好的,这个在原问题中不是很清楚。 - mkl
2个回答

6

好的,我解决了。 这是我想要的方法。也许对某些人有用 :)

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

"MC"+index代表什么? - V K
说实话 - 我不知道。我是通过在网上找到的代码片段来想出这个解决方案的。我想这可能是一种被pdf阅读器识别的资源命名约定。我不是pdf专家,不幸的是我没有时间深入研究它。但我也想听听你的问题的答案。 - Marcin Roguski
MC 可能代表着标记内容。 - user2560528

1

对于PDFBox 2.0:

public static PDOptionalContentGroup appendImageAsLayer(PDDocument doc, PDPage page,
                                                        PDImageXObject pdImage, AffineTransform transform,
                                                        String layerName) throws IOException {
    PDDocumentCatalog catalog = doc.getDocumentCatalog();
    PDOptionalContentProperties ocprops = catalog.getOCProperties();
    if (ocprops == null) {
        ocprops = new PDOptionalContentProperties();
        catalog.setOCProperties(ocprops);
    }
    PDOptionalContentGroup layer = ocprops.getGroup(layerName);
    if (layer == null) {
        layer = new PDOptionalContentGroup(layerName);
        ocprops.addGroup(layer);
    }

    try (PDPageContentStream contentStream = new PDPageContentStream(
            doc, page, PDPageContentStream.AppendMode.APPEND, false)) {
        contentStream.beginMarkedContent(COSName.OC, layer);
        contentStream.saveGraphicsState();
        contentStream.transform(new Matrix(transform));
        contentStream.drawImage(pdImage, 0, 0, pdImage.getWidth(), pdImage.getHeight());
        contentStream.restoreGraphicsState();
        contentStream.endMarkedContent();
    }

    return layer;
}

可以使用以下方式创建PDImageXObject:

PDImageXObject pdImage = PDImageXObject.createFromFile(imagePath, doc);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接