如何使用 iText 从 PDF 表单中提取图像
How to Extract Images from a PDF Form with iText
这篇文章 (How to extract images from a PDF with iText in the correct order?) 解释了如何从普通 PDF 文件中提取图像。我需要提取用户在 PDF 表单域.
中输入的图像
我使用 iText 7。我可以使用如下代码访问 iText 中的表单字段:
PdfReader reader = new PdfReader(new FileInputStream(new ClassPathResource("myFile.pdf").getFile()));
PdfDocument document = new PdfDocument(reader);
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(document, false);
Map<String, PdfFormField> fields = acroForm.getFormFields();
PdfButtonFormField imageField = null;
PdfDictionary dictionary = null;
for (String fldName : fields.keySet()) {
PdfFormField field = fields.get(fldName);
if ("Image1_af_image".equals(fldName)) {
imageField = (PdfButtonFormField)fields.get("Image1_af_image");
dictionary = imageField.getPdfObject();
}
}
其中 Image1_af_imgage
是表单中图像字段的默认名称。是否可以从 PdfButtonFormField
或其关联的字典对象中提取图像流?
感谢您非常有帮助的回复。我已将您的代码合并如下:
public void iTextTest3() throws IOException {
PdfReader reader = new PdfReader(new FileInputStream(new ClassPathResource("templates/TestForm.pdf").getFile()));
PdfDocument document = new PdfDocument(reader);
String fieldname = "Image1_af_image";
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(document, false);
PdfFormField imagefield = acroForm.getField(fieldname);
// get the appearance dictionary
PdfDictionary apDic = imagefield.getWidgets().get(0).getNormalAppearanceObject();
// get the xobject resources
PdfDictionary xObjDic = apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
for (PdfName key : xObjDic.keySet()) {
System.out.println(key);
PdfStream s = xObjDic.getAsStream(key);
// only process images
if (PdfName.Image.equals(s.getAsName(PdfName.Subtype))) { //*** code fails here ***
PdfImageXObject pixo = new PdfImageXObject(s);
byte[] imgbytes = pixo.getImageBytes();
String ext = pixo.identifyImageFileExtension();
// write the image to file
String fileName = null;
FileOutputStream fos = new FileOutputStream(fileName = key.toString().substring(1) + "." + ext);
System.out.println(("image fileName: " + fileName));
fos.write(imgbytes);
fos.close();
}
}
document.close();
}
代码失败,因为 s.getAsName(PdfName.Subtype)
returns 值 "Form"
。我猜想我需要做的是按照您在 post 中的建议递归到 XObject 树中,但我不确定该怎么做。我试过 xObjDic.getAsDictionary()
但不确定 PdfName
应该作为参数传入什么。
PDF 中按钮的视觉外观可以完全自定义,包括文本、图形和图像。因此,图像数据可以以略微不同的方式存储在不同的 PDF 文档中。但一般来说,表单字段的 小部件注释 将有一个 外观流 ,它将图像数据作为 XObject 在其 Resources 字典中。
创建带有按钮的 PDF 以供测试:
String fieldname = "Image1_af_image";
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
PdfButtonFormField imagefield = PdfFormField.createButton(pdfDoc, new Rectangle(100, 100, 50, 50),
PdfButtonFormField.FF_PUSH_BUTTON);
imagefield.setImage("button.png").setFieldName(fieldname);
form.addField(imagefield);
从按钮获取图像数据:
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDoc, false);
PdfFormField imagefield = acroForm.getField(fieldname);
// get the appearance dictionary
PdfDictionary apDic = imagefield.getWidgets().get(0).getNormalAppearanceObject();
// get the xobject resources
PdfDictionary xObjDic = apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
for (PdfName key : xObjDic.keySet()) {
System.out.println(key);
PdfStream s = xObjDic.getAsStream(key);
// only process images
if (PdfName.Image.equals(s.getAsName(PdfName.Subtype))) {
PdfImageXObject pixo = new PdfImageXObject(s);
byte[] imgbytes = pixo.getImageBytes();
String ext = pixo.identifyImageFileExtension();
// write the image to file
FileOutputStream fos = new FileOutputStream(key.toString().substring(1) + "." + ext);
fos.write(imgbytes);
fos.close();
}
}
您可以使用 PDF 对象查看器,例如 iText RUPS 或 Adobe Acrobat 的内置“浏览内部 PDF 结构”,检查 PDF 文档的确切结构并找出图像数据所在的位置已存储。
编辑:
一种更通用的提取图像数据的方法,以防它位于嵌套的 Form XObjects 中:
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDoc, false);
PdfFormField imagefield = acroForm.getField(fieldname);
// get the appearance dictionary
PdfDictionary apDic = imagefield.getWidgets().get(0).getNormalAppearanceObject();
// get the xobject resources
PdfDictionary xObjDic = apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
extractImagesFromXObj(xObjDic);
public void extractImagesFromXObj(PdfDictionary xObjDic) throws IOException {
for (PdfName key : xObjDic.keySet()) {
System.out.println(key);
PdfStream s = xObjDic.getAsStream(key);
PdfName subType = s.getAsName(PdfName.Subtype);
// only process images
if (PdfName.Image.equals(subType)) {
PdfImageXObject pixo = new PdfImageXObject(s);
byte[] imgbytes = pixo.getImageBytes();
String ext = pixo.identifyImageFileExtension();
// write the image to file
FileOutputStream fos = new FileOutputStream(key.toString().substring(1) + "." + ext);
fos.write(imgbytes);
fos.close();
}
// process nested XObject dictionaries recursively
else if (PdfName.Form.equals(subType)) {
PdfDictionary nestedXObjDic = s.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
extractImagesFromXObj(nestedXObjDic);
}
}
}
这篇文章 (How to extract images from a PDF with iText in the correct order?) 解释了如何从普通 PDF 文件中提取图像。我需要提取用户在 PDF 表单域.
中输入的图像我使用 iText 7。我可以使用如下代码访问 iText 中的表单字段:
PdfReader reader = new PdfReader(new FileInputStream(new ClassPathResource("myFile.pdf").getFile()));
PdfDocument document = new PdfDocument(reader);
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(document, false);
Map<String, PdfFormField> fields = acroForm.getFormFields();
PdfButtonFormField imageField = null;
PdfDictionary dictionary = null;
for (String fldName : fields.keySet()) {
PdfFormField field = fields.get(fldName);
if ("Image1_af_image".equals(fldName)) {
imageField = (PdfButtonFormField)fields.get("Image1_af_image");
dictionary = imageField.getPdfObject();
}
}
其中 Image1_af_imgage
是表单中图像字段的默认名称。是否可以从 PdfButtonFormField
或其关联的字典对象中提取图像流?
感谢您非常有帮助的回复。我已将您的代码合并如下:
public void iTextTest3() throws IOException {
PdfReader reader = new PdfReader(new FileInputStream(new ClassPathResource("templates/TestForm.pdf").getFile()));
PdfDocument document = new PdfDocument(reader);
String fieldname = "Image1_af_image";
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(document, false);
PdfFormField imagefield = acroForm.getField(fieldname);
// get the appearance dictionary
PdfDictionary apDic = imagefield.getWidgets().get(0).getNormalAppearanceObject();
// get the xobject resources
PdfDictionary xObjDic = apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
for (PdfName key : xObjDic.keySet()) {
System.out.println(key);
PdfStream s = xObjDic.getAsStream(key);
// only process images
if (PdfName.Image.equals(s.getAsName(PdfName.Subtype))) { //*** code fails here ***
PdfImageXObject pixo = new PdfImageXObject(s);
byte[] imgbytes = pixo.getImageBytes();
String ext = pixo.identifyImageFileExtension();
// write the image to file
String fileName = null;
FileOutputStream fos = new FileOutputStream(fileName = key.toString().substring(1) + "." + ext);
System.out.println(("image fileName: " + fileName));
fos.write(imgbytes);
fos.close();
}
}
document.close();
}
代码失败,因为 s.getAsName(PdfName.Subtype)
returns 值 "Form"
。我猜想我需要做的是按照您在 post 中的建议递归到 XObject 树中,但我不确定该怎么做。我试过 xObjDic.getAsDictionary()
但不确定 PdfName
应该作为参数传入什么。
PDF 中按钮的视觉外观可以完全自定义,包括文本、图形和图像。因此,图像数据可以以略微不同的方式存储在不同的 PDF 文档中。但一般来说,表单字段的 小部件注释 将有一个 外观流 ,它将图像数据作为 XObject 在其 Resources 字典中。
创建带有按钮的 PDF 以供测试:
String fieldname = "Image1_af_image";
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
PdfButtonFormField imagefield = PdfFormField.createButton(pdfDoc, new Rectangle(100, 100, 50, 50),
PdfButtonFormField.FF_PUSH_BUTTON);
imagefield.setImage("button.png").setFieldName(fieldname);
form.addField(imagefield);
从按钮获取图像数据:
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDoc, false);
PdfFormField imagefield = acroForm.getField(fieldname);
// get the appearance dictionary
PdfDictionary apDic = imagefield.getWidgets().get(0).getNormalAppearanceObject();
// get the xobject resources
PdfDictionary xObjDic = apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
for (PdfName key : xObjDic.keySet()) {
System.out.println(key);
PdfStream s = xObjDic.getAsStream(key);
// only process images
if (PdfName.Image.equals(s.getAsName(PdfName.Subtype))) {
PdfImageXObject pixo = new PdfImageXObject(s);
byte[] imgbytes = pixo.getImageBytes();
String ext = pixo.identifyImageFileExtension();
// write the image to file
FileOutputStream fos = new FileOutputStream(key.toString().substring(1) + "." + ext);
fos.write(imgbytes);
fos.close();
}
}
您可以使用 PDF 对象查看器,例如 iText RUPS 或 Adobe Acrobat 的内置“浏览内部 PDF 结构”,检查 PDF 文档的确切结构并找出图像数据所在的位置已存储。
编辑:
一种更通用的提取图像数据的方法,以防它位于嵌套的 Form XObjects 中:
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(pdfDoc, false);
PdfFormField imagefield = acroForm.getField(fieldname);
// get the appearance dictionary
PdfDictionary apDic = imagefield.getWidgets().get(0).getNormalAppearanceObject();
// get the xobject resources
PdfDictionary xObjDic = apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
extractImagesFromXObj(xObjDic);
public void extractImagesFromXObj(PdfDictionary xObjDic) throws IOException {
for (PdfName key : xObjDic.keySet()) {
System.out.println(key);
PdfStream s = xObjDic.getAsStream(key);
PdfName subType = s.getAsName(PdfName.Subtype);
// only process images
if (PdfName.Image.equals(subType)) {
PdfImageXObject pixo = new PdfImageXObject(s);
byte[] imgbytes = pixo.getImageBytes();
String ext = pixo.identifyImageFileExtension();
// write the image to file
FileOutputStream fos = new FileOutputStream(key.toString().substring(1) + "." + ext);
fos.write(imgbytes);
fos.close();
}
// process nested XObject dictionaries recursively
else if (PdfName.Form.equals(subType)) {
PdfDictionary nestedXObjDic = s.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
extractImagesFromXObj(nestedXObjDic);
}
}
}