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

筅森魡賤 提交于 2019-11-30 18:36:08

问题


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 Acrobat PRO DC.

So if I want to fill up the form I can open this PDF file in regular Acrobat Reader and fill up by typing First Name, Last Name and in order to insert Photo I click on image placeholder and select photo in opened Dialog Window.

But how can I do same thing programmatically? Created simple Java Application that uses Apache PDFBox library (version 2.0.7) to find form fields and insert values.

I can easily populate Text Edit field, but can not figure out how can I insert image:

public class AcroFormPopulator {

    public static void main(String[] args) {

        AcroFormPopulator abd = new AcroFormPopulator();
        try {
            abd.populateAndCopy("test.pdf", "generated.pdf");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void populateAndCopy(String originalPdf, String targetPdf) throws IOException {
        File file = new File(originalPdf);

        PDDocument document = PDDocument.load(file);
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

        Map<String, String> data = new HashMap<>();
        data.put("firstName", "Mike");
        data.put("lastName", "Taylor");
        data.put("photo_af_image", "photo.jpeg");

        for (Map.Entry<String, String> item : data.entrySet()) {
            PDField field = acroForm.getField(item.getKey());
            if (field != null) {

                if (field instanceof PDTextField) {
                    field.setValue(item.getValue());

                } else if (field instanceof PDPushButton) {
                    File imageFile = new File(item.getValue());

                    PDPushButton pdPushButton = (PDPushButton) field;
                    // do not see way to isert image

                } else {
                    System.err.println("No field found with name:" + item.getKey());
                }
            } else {
                System.err.println("No field found with name:" + item.getKey());
            }
        }

        document.save(targetPdf);
        document.close();
        System.out.println("Populated!");
    }
}

I have distinguished a weird thing - in Acrobat Pro DC it says that I add Image Field, but the only field I get by generated name: 'photo_af_image' is of type button - PDPushButton (that is why I check if (field instanceof PDPushButton)), but is nothing to do with Image.

How can I insert image to AcroForm 'photo_af_image' field, so that it will fit the size of a box created af Acrobat Pro DC?


回答1:


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.



来源:https://stackoverflow.com/questions/46799087/how-to-insert-image-programmatically-in-to-acroform-field-using-java-pdfbox

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!