mkl-public / testarea-pdfbox2

Test area for public PDFBox v2 issues on stackoverflow etc
Apache License 2.0
82 stars 44 forks source link

How to add image to pdf #1

Closed shavo007 closed 6 years ago

shavo007 commented 6 years ago

Hi,

I seen your test case for creating a button and adding an image appearance.

Im looking to use an existing button (as a placeholder) on the pdf to draw an image. Is this the correct approach?

If so does this code look correct? (This is based of the test you defined)

  try (InputStream resource = getClass().getClassLoader().getResourceAsStream("2x2colored.png")) {
            BufferedImage bufferedImage = ImageIO.read(resource);
            PDImageXObject pdImageXObject = LosslessFactory.createFromImage(acrobatDocument, bufferedImage);
            float width = 10 * pdImageXObject.getWidth();
            float height = 10 * pdImageXObject.getHeight();

            PDAppearanceStream pdAppearanceStream = new PDAppearanceStream(acrobatDocument);
            pdAppearanceStream.setResources(new PDResources());
            try (PDPageContentStream pdPageContentStream = new PDPageContentStream(acrobatDocument, pdAppearanceStream)) {
                pdPageContentStream.drawImage(pdImageXObject, 0, 0, width, height);
            }
            pdAppearanceStream.setBBox(new PDRectangle(width, height));

            //get button you want to replace
            PDButton button  = (PDPushButton)acrobatAcroForm.getField("PushButton");

            List<PDAnnotationWidget> widgets = button.getWidgets();
            for (PDAnnotationWidget pdAnnotationWidget : widgets) {

                PDAppearanceDictionary pdAppearanceDictionary = pdAnnotationWidget.getAppearance();
                if (pdAppearanceDictionary == null) {
                    pdAppearanceDictionary = new PDAppearanceDictionary();
                    pdAnnotationWidget.setAppearance(pdAppearanceDictionary);
                }

                //add appearance to button
                pdAppearanceDictionary.setNormalAppearance(pdAppearanceStream);
            }
 button.setReadOnly(true);
            acrobatAcroForm.getFields().add(button);

            acrobatDocument.save(new File("build", "imageWithButton.pdf"));
        }

Thanks, Shane.

mkl-public commented 6 years ago

I added another test method to the CreateImageButton test class: testUpdateSimpleImageButton. It is based on your code with one relevant change: I removed the line

acrobatAcroForm.getFields().add(button);

The reason: The button form field already is in that list, after all you retrieved it using acrobatAcroForm.getField("PushButton"). Adding it again is not appropriate as it generates a duplicate entry.

And another caveat: The code as is only works well if the image has approximately the same aspect ratio as the button. If that cannot be guaranteed, you should replace the argument in

pdAppearanceStream.setBBox(new PDRectangle(width, height));

by a larger rectangle, by the smallest larger rectangle that has the same aspect ratio as the button.

shavo007 commented 6 years ago

Awesome work @mkl-public . Thanks for your prompt response.

The aspect ratio is definitely something to consider.

shavo007 commented 6 years ago

I forgot to ask is this the best approach to add images to a template?

mkl-public commented 6 years ago

"The best approach"? Well, it depends.

First of all, using some annotation (e.g. a button like you do) to mark the area for an image is a good idea.

There are two major approaches for filling in, though: Either, as you do, one sets the button appearance to a XObject filled with the image; or, alternatively, one retrieves the coordinates of the field, then drops the field and adds the image at the given coordinates to the page content stream.

The advantage of the latter approach is that there still are some very incomplete PDF viewers which have issues with annotations in general; such viewers would probably not display your image correctly. (There used to be many such viewers but their relevance has been dropping more and more, so nowadays this advantage is merely slim.)

Furthermore, there are two differences between the approaches which are not generally an advantage of one or the other approach but depending on the use case may favor one or the other:

shavo007 commented 6 years ago

@mkl-public thanks for detailed explanation