簡體   English   中英

如何使用 java PDFBox 以編程方式將圖像插入到 AcroForm 字段中?

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

我創建了帶有 3 個標簽的簡單 PDF 文檔:名字、姓氏和照片。 然后我使用 Adob​​e Acrobat PRO DC 添加了帶有 2 個“文本字段”和一個“圖像字段”的 AcroForm 層。

在此處輸入圖片說明

因此,如果我想填寫表格,我可以在常規 Acrobat Reader 中打開此 PDF 文件,並通過鍵入名字、姓氏來填寫,為了插入照片,我單擊圖像占位符並在打開的對話框窗口中選擇照片。

在此處輸入圖片說明

但是我怎樣才能以編程方式做同樣的事情呢? 創建了簡單的 Java 應用程序,它使用 Apache PDFBox 庫(2.0.7 版)來查找表單字段和插入值。

我可以輕松填充文本編輯字段,但無法弄清楚如何插入圖像:

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!");
    }
}

我區分了一件奇怪的事情 - 在 Acrobat Pro DC 中,它說我添加了圖像字段,但我通過生成的名稱獲得的唯一字段:'photo_af_image' 是按鈕類型 - PDPushButton (這就是為什么我檢查(字段 instanceof PDPushButton) ),但與 Image 無關。

如何將圖像插入 AcroForm 的“photo_af_image”字段,使其適合 Acrobat Pro DC 創建的框的大小?

我終於找到並建立了很好的解決方案。 此解決方案的目標是:

  1. 使用簡單的工具創建帶有文本和圖像占位符的表單層,這可以由非程序員完成,不需要操作低級 PDF 結構;
  2. 使用簡單的工具使插入圖像的大小由表單創建者驅動; 尺寸由高度驅動,寬度由比例調整;

下面通過 acroForm 占位符插入圖像的解決方案的主要思想是:

  1. 您必須迭代 acroForm 層並找到具有相應占位符名稱的按鈕;
  2. 如果找到的字段是 PDPushButton 類型,則獲取其第一個小部件;
  3. 從圖像文件創建 PDImageXObject;
  4. 使用 PDImageXObject 創建 PDAppearanceStream 並設置相同的 x & y 位置並調整高度和寬度以匹配占位符的高度;
  5. 將此 PDAppearanceStream 設置為小部件;
  6. 您可以選擇展平文檔以將 acroform 布局合並到主文檔

這是代碼:

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);
    }
}

請讓我知道是否有更好的解決方案或您可以改進的代碼。

Renat Gatin 的回答對於讓我開始做這件事非常寶貴。 謝謝你。 但是,我發現我可以用更少的復雜性完成相同的結果。 最初的答案似乎主要是使用 PDPushButton 字段來確定字段的大小和位置。 該字段本身與插入圖像幾乎沒有關系。 代碼直接寫入文檔流,並沒有真正填充字段。

我在表單中創建了一個文本字段,它覆蓋了我想要圖像的整個區域,在我的例子中是一個二維碼。 然后使用發現的字段坐標,我可以在文檔中寫入圖像。 這是使用 PDFBox 2.0.11 完成的。

免責聲明:

  • 我的領域完全是方形的,以適應始終為方形的二維碼
  • 我的圖像是黑白的。 我沒有嘗試插入彩色圖像
  • 已知我的模板只有一頁,因此“document.getPage(0)”
  • 我沒有在用於定位圖像的字段中插入任​​何文本

這是我作為示例提供的部分代碼,而不是完整或通用的解決方案:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM