简体   繁体   English

如何使用 java PDFBox 以编程方式将图像插入到 AcroForm 字段中?

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

I have created simple PDF document with 3 labels: First Name, Last Name and Photo.我创建了带有 3 个标签的简单 PDF 文档:名字、姓氏和照片。 Then I added AcroForm layer with 2 'Text Fields' and one 'Image Field' using Adobe Acrobat PRO DC.然后我使用 Adob​​e Acrobat PRO DC 添加了带有 2 个“文本字段”和一个“图像字段”的 AcroForm 层。

在此处输入图片说明

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.因此,如果我想填写表格,我可以在常规 Acrobat Reader 中打开此 PDF 文件,并通过键入名字、姓氏来填写,为了插入照片,我单击图像占位符并在打开的对话框窗口中选择照片。

在此处输入图片说明

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.创建了简单的 Java 应用程序,它使用 Apache PDFBox 库(2.0.7 版)来查找表单字段和插入值。

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.我区分了一件奇怪的事情 - 在 Acrobat Pro DC 中,它说我添加了图像字段,但我通过生成的名称获得的唯一字段:'photo_af_image' 是按钮类型 - PDPushButton (这就是为什么我检查(字段 instanceof PDPushButton) ),但与 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?如何将图像插入 AcroForm 的“photo_af_image”字段,使其适合 Acrobat Pro DC 创建的框的大小?

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;使用简单的工具创建带有文本和图像占位符的表单层,这可以由非程序员完成,不需要操作低级 PDF 结构;
  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:下面通过 acroForm 占位符插入图像的解决方案的主要思想是:

  1. you have to iterate acroForm layer and find button with corresponding placeholder name;您必须迭代 acroForm 层并找到具有相应占位符名称的按钮;
  2. if found field is of type PDPushButton get its first widget;如果找到的字段是 PDPushButton 类型,则获取其第一个小部件;
  3. create PDImageXObject from image file;从图像文件创建 PDImageXObject;
  4. create PDAppearanceStream using PDImageXObject and setting same x & y position and adjust the height and width to match the height of placeholder;使用 PDImageXObject 创建 PDAppearanceStream 并设置相同的 x & y 位置并调整高度和宽度以匹配占位符的高度;
  5. set this PDAppearanceStream to a widget;将此 PDAppearanceStream 设置为小部件;
  6. you can optionally flatten the document to merge acroform lay to main one您可以选择展平文档以将 acroform 布局合并到主文档

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.请让我知道是否有更好的解决方案或您可以改进的代码。

The answer by Renat Gatin was invaluable for getting me started on this. Renat Gatin 的回答对于让我开始做这件事非常宝贵。 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.最初的答案似乎主要是使用 PDPushButton 字段来确定字段的大小和位置。 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.这是使用 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)"已知我的模板只有一页,因此“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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 PDFBox API:如何更改字体以处理 AcroForm 字段中的 Cyrillic 值 - PDFBox API: How to change font to handle Cyrillic values in an AcroForm field 使用Java和IText,如何从PDF中提取AcroForm字段名称? - Using Java and IText, how to extract AcroForm field names from a PDF? PDFBox是否允许从AcroForm删除一个字段? - Does PDFBox allow to remove one field from AcroForm? 使用pdfbox调用acroform.getFields()时获取null - getting null when call acroform.getFields() using pdfbox 无法使用pdfbox和iText从PDF读取表格(Acroform) - Unable to read form (acroform) from PDF using pdfbox and iText 如何在 PDFBox AcroForm 中使用 TTF 字体,然后展平文档? - How to use TTF font with PDFBox AcroForm and then flatten document? 是否可以使用iText以编程方式向“ Acroform”字段添加“打开Web链接操作”操作? - Is it possible to add 'Open a web link action' action to acroform field programmatically using iText? 如何使用JAVA中的PDFBox从PDF创建图像 - How to create image from PDF using PDFBox in JAVA 如何使用 java 中的 pdfbox 从 pdf 获取图像坐标 - How to get image cordinates from pdf using pdfbox in java 使用 Java pdfbox 仅将图像的 1/4 显示在 PDF 文档中的 PDF 文档中 - Insert image a jpg file into a PDF document using Java pdfbox only 1/4 of image show up in the PDF document
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM