问题描述
我创建了带有3个标签的简单PDF文档:名字,姓氏和照片。然后我使用Adobe Acrobat PRO DC添加了带有2个文本字段的AcroForm图层和一个图像字段。
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.
因此,如果我想填写表格,我可以在常规Acrobat Reader中打开此PDF文件,并输入名字,姓氏填写并插入照片我点击图片占位符并在打开的对话窗口中选择照片。
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.
但是我怎么能以编程方式做同样的事情呢?
创建简单的Java应用程序,它使用Apache PDFBox库(版本2.0.7)来查找表单字段和插入值。
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!");
}
}
我区分了一个奇怪的东西 - 在Acrobat Pro DC中它说我添加了Image Field,但是我得到的唯一字段是生成的名称:'photo_af_image'是按钮类型 - PDPushButton (这就是为什么我检查是否(字段instanceof PDPushButton)),但是与Image无关。
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.
如何将图像插入AcroForm'photo_af_image'字段,以便它符合Acrobat Pro创建的框的大小DC?
推荐答案
我终于找到并建立了很好的解决方案。此解决方案的目标是:
I finally have found and built up nice solution. The goals of this solution is:
- 使用简单的
工具创建包含文本和图像占位符的表单层,这可以完成非程序员并且不需要
操纵低级PDF结构; - 使用简单工具由表单创建者驱动插入图像的大小;尺寸由高度驱动,但宽度将按比例调整;
以下解决方案的主要思想是通过acroForm占位符插入图像是:
The main idea of solution below for inserting images by acroForm placeholders is:
- 你必须迭代acroForm图层并找到带有
对应占位符名称的按钮; - 如果找到的字段是PDPushButton类型的第一个小部件;
- 从图像文件创建PDImageXObject;
- 使用PDImageXObject创建PDAppearanceStream并设置相同x& $ b $按位置调整高度和宽度以匹配
占位符的高度; - 将此PDAppearanceStream设置为小部件;
- 您可以选择展平文档以合并acroform lay to
main one
- you have to iterate acroForm layer and find button withcorresponding placeholder name;
- if found field is of type PDPushButton get its first widget;
- create PDImageXObject from image file;
- create PDAppearanceStream using PDImageXObject and setting same x &y position and adjust the height and width to match the height ofplaceholder;
- set this PDAppearanceStream to a widget;
- you can optionally flatten the document to merge acroform lay tomain one
以下是代码:
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.
这篇关于如何使用java PDFBox以编程方式将图像插入AcroForm字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!