[英]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.
然后我使用 Adobe 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:
此解决方案的目标是:
The main idea of solution below for inserting images by acroForm placeholders is:下面通过 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:免责声明:
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.