1
votes

I'm using BIRT to generate a simple report with PDF output. This document has some gaps (filled with a placeholder image with an specific color, which is inserted as embedded image in BIRT) that I would like to replace with a user provided image.

BIRT uses iText under the covers, so I decided to go with iText too, in its version 5. The document looks like this:

enter image description here

Now, I have written this code in order to fill only the first gap:

private void replaceStream(PRStream orig, PdfStream stream) throws IOException {
    orig.clear();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    stream.writeContent(baos);
    orig.setData(baos.toByteArray(), false);
    for (PdfName name : stream.getKeys()) {
        orig.put(name, stream.get(name));
    }
}

private void placeSignature(File source, File target, File signature)
        throws IOException, DocumentException {
    PdfReader reader = new PdfReader(source.getPath());
    PdfObject obj;
    for (int i = 1; i <= reader.getXrefSize(); i++) {
        obj = reader.getPdfObject(i);
        if (obj != null && obj.isStream()) {
            PRStream stream = (PRStream) obj;

            byte[] b;
            try {
                b = PdfReader.getStreamBytes(stream);
            } catch (UnsupportedPdfException e) {
                b = PdfReader.getStreamBytesRaw(stream);
            }
            BufferedImage img = ImageIO.read(new ByteArrayInputStream(b));

            if (img != null) {
                boolean signaturePlaceholder = true;
                for (int x = 0; x < img.getWidth(); x++) {
                    for (int y = 0; y < img.getHeight(); y++) {
                        // Check if image is a placeholder, matches a colour
                        if (img.getRGB(x, y) != -65) {
                            signaturePlaceholder = false;
                        }
                    }
                }
                if (signaturePlaceholder) {
                    Image img2 = Image.getInstance(signature.getPath());
                    PdfImage newImg = new PdfImage(img2, "", null);
                    replaceStream(stream, newImg);
                    System.out.println("Replaced!");
                    break;
                }
            }
        }
    }
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(target));
    stamper.close();
    reader.close();
}

@Test
public void testReplace() throws IOException, DocumentException {
    placeSignature(new File("src/test/resources/signature_test2.pdf"),
            new File("target/signature_test2.pdf"),
            new File("src/test/resources/signature.jpg"));
}

However, when running the test, that's the result I have:

enter image description here

It seems that the PDF stream gets shared across all the image tags. However, I would like to change only the content for the first. If I use another image to fill any of the gaps (with different size, for example), it doesn't get replaced.

Question is, is there some workaround for this issue when parsing the PDF or do I need to use different images for each of the placeholders while creating the report in BIRT.

This is the link to the sample PDF file.

1

1 Answers

2
votes

The page in your PDF contains only a single image XObject resource which is used in a single form XObject resource which is used three times in the page content stream.

Thus, after you replaced the only image resource, all of its (indirect) usages on the page show the replacement image.

If you wanted to change this using iText, you'd have to edit the content stream and replace the instruction for the form XObject use by the use of the new image. But you'd first have to identify which of those XObject usages there is the one you want to replace.

Non-trivial, in particular if the Birt template is meant to be somewhat flexible.

I would recommend you instead use different images (using different marker colors) for the different locations in your report template. This of course becomes more and more difficult the more such placeholder images there are, in particular if the number is dynamic and can become arbitrarily large, e.g. one per dataset entry in a dataset with an unknown but potentially large number of entries.