How to insert image programmatically in to AcroForm field using java PDFBox?

后端 未结 2 2079
情深已故
情深已故 2021-01-06 02:19

I have created simple PDF document with 3 labels: First Name, Last Name and Photo. Then I added AcroForm layer with 2 \'Text Fields\' and one \'Image Field\' using Adobe Acr

相关标签:
2条回答
  • 2021-01-06 02:29

    I finally have found and built up nice solution. The goals of this solution is:

    1. to create form layer with text and image placeholders using simple tools, which can be done by non-programmer and does not require to manipulate low level PDF structure;
    2. make size of inserted image be driven by form creator using simple tools; size to be driven by height, but width will be adjusted by ratio;

    The main idea of solution below for inserting images by acroForm placeholders is:

    1. you have to iterate acroForm layer and find button with corresponding placeholder name;
    2. if found field is of type PDPushButton get its first widget;
    3. create PDImageXObject from image file;
    4. create PDAppearanceStream using PDImageXObject and setting same x & y position and adjust the height and width to match the height of placeholder;
    5. set this PDAppearanceStream to a widget;
    6. you can optionally flatten the document to merge acroform lay to main one

    Here is code:

    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.imageio.ImageIO;
    
    import org.apache.pdfbox.cos.COSArray;
    import org.apache.pdfbox.cos.COSDictionary;
    import org.apache.pdfbox.cos.COSName;
    import org.apache.pdfbox.pdmodel.PDDocument;
    import org.apache.pdfbox.pdmodel.PDPageContentStream;
    import org.apache.pdfbox.pdmodel.PDResources;
    import org.apache.pdfbox.pdmodel.common.PDRectangle;
    import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
    import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
    import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
    import org.apache.pdfbox.pdmodel.interactive.action.PDActionHide;
    import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
    import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
    import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
    import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
    import org.apache.pdfbox.pdmodel.interactive.form.PDField;
    import org.apache.pdfbox.pdmodel.interactive.form.PDPushButton;
    import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
    
    public class AcroFormPopulator {
    
        public static void main(String[] args) {
            AcroFormPopulator abd = new AcroFormPopulator();
            try {
                Map<String, String> data = new HashMap<>();
                data.put("firstName", "Mike");
                data.put("lastName", "Taylor");
                data.put("dateTime", (new Date()).toString());
                data.put("photo_af_image", "photo1.jpg");
                data.put("photo2_af_image", "photo2.jpg");
                data.put("photo3_af_image", "photo3.jpg");
    
                abd.populateAndCopy("test.pdf", "generated.pdf", data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void populateAndCopy(String originalPdf, String targetPdf, Map<String, String> data) throws IOException {
            File file = new File(originalPdf);
            PDDocument document = PDDocument.load(file);
            PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
    
            for (Map.Entry<String, String> item : data.entrySet()) {
                String key = item.getKey();
                PDField field = acroForm.getField(key);
                if (field != null) {
                    System.out.print("Form field with placeholder name: '" + key + "' found");
    
                    if (field instanceof PDTextField) {
                        System.out.println("(type: " + field.getClass().getSimpleName() + ")");
                        field.setValue(item.getValue());
                        System.out.println("value is set to: '" + item.getValue() + "'");
    
                    } else if (field instanceof PDPushButton) {
                        System.out.println("(type: " + field.getClass().getSimpleName() + ")");
                        PDPushButton pdPushButton = (PDPushButton) field;
    
                        List<PDAnnotationWidget> widgets = pdPushButton.getWidgets();
                        if (widgets != null && widgets.size() > 0) {
                            PDAnnotationWidget annotationWidget = widgets.get(0); // just need one widget
    
                            String filePath = item.getValue();
                            File imageFile = new File(filePath);
    
                            if (imageFile.exists()) {
                                /*
                                 * BufferedImage bufferedImage = ImageIO.read(imageFile); 
                                 * PDImageXObject pdImageXObject = LosslessFactory.createFromImage(document, bufferedImage);
                                 */
                                PDImageXObject pdImageXObject = PDImageXObject.createFromFile(filePath, document);
                                float imageScaleRatio = (float) pdImageXObject.getHeight() / (float) pdImageXObject.getWidth();
    
                                PDRectangle buttonPosition = getFieldArea(pdPushButton);
                                float height = buttonPosition.getHeight();
                                float width = height / imageScaleRatio;
                                float x = buttonPosition.getLowerLeftX();
                                float y = buttonPosition.getLowerLeftY();
    
                                PDAppearanceStream pdAppearanceStream = new PDAppearanceStream(document);
                                pdAppearanceStream.setResources(new PDResources());
                                try (PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdAppearanceStream)) {
                                    pdPageContentStream.drawImage(pdImageXObject, x, y, width, height);
                                }
                                pdAppearanceStream.setBBox(new PDRectangle(x, y, width, height));
    
                                PDAppearanceDictionary pdAppearanceDictionary = annotationWidget.getAppearance();
                                if (pdAppearanceDictionary == null) {
                                    pdAppearanceDictionary = new PDAppearanceDictionary();
                                    annotationWidget.setAppearance(pdAppearanceDictionary);
                                }
    
                                pdAppearanceDictionary.setNormalAppearance(pdAppearanceStream);
                                System.out.println("Image '" + filePath + "' inserted");
    
                            } else {
                                System.err.println("File " + filePath + " not found");
                            }
                        } else {
                            System.err.println("Missconfiguration of placeholder: '" + key + "' - no widgets(actions) found");
                        }
                    } else {
                        System.err.print("Unexpected form field type found with placeholder name: '" + key + "'");
                    }
                } else {
                    System.err.println("No field found with name:" + key);
                }
            }
    
            // you can optionally flatten the document to merge acroform lay to main one
            acroForm.flatten();
    
            document.save(targetPdf);
            document.close();
            System.out.println("Done");
        }
    
        private PDRectangle getFieldArea(PDField field) {
            COSDictionary fieldDict = field.getCOSObject();
            COSArray fieldAreaArray = (COSArray) fieldDict.getDictionaryObject(COSName.RECT);
            return new PDRectangle(fieldAreaArray);
        }
    }
    

    Please let me know if there is better solution or something this code you can improve.

    0 讨论(0)
  • 2021-01-06 02:42

    The answer by Renat Gatin was invaluable for getting me started on this. Thank you for that. However, I found I could accomplish the same result with less complexity. The original answer seems to be using the PDPushButton field primarily to determine the field's size and location. The field itself has little to do with inserting the image. The code is writing directly to the document stream and not really populating the field.

    I created a text field in my form which covers the entire area where I want the image, in my case a QR code. Then using the discovered coordinates of the field, I can write the image in the document. This was done using PDFBox 2.0.11.

    Disclaimers:

    • My field was made exactly square to fit QR codes which are always square
    • My images are black and white. I did not attempt to insert a color image
    • My template is known to have only one page, hence "document.getPage(0)"
    • I did not insert any text in the field used to position the image

    Here is my partial code provided as an example, not a complete or generic solution:

    public void setField(PDDocument document, String name, PDImageXObject image) 
        throws IOException {
    
      PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
      PDField field = acroForm.getField(name);
      if (field != null) {
        PDRectangle rectangle = getFieldArea(field);
        float size = rectangle.getHeight();
        float x = rectangle.getLowerLeftX();
        float y = rectangle.getLowerLeftY();
    
        try (PDPageContentStream contentStream = new PDPageContentStream(document, 
            document.getPage(0), PDPageContentStream.AppendMode.APPEND, true)) {
          contentStream.drawImage(image, x, y, size, size);
        }
      }
    }
    
    private PDRectangle getFieldArea(PDField field) {
      COSDictionary fieldDict = field.getCOSObject();
      COSArray fieldAreaArray = (COSArray) fieldDict.getDictionaryObject(COSName.RECT);
      return new PDRectangle(fieldAreaArray);
    }
    
    0 讨论(0)
提交回复
热议问题