Java 如何使用iText从PDF表单中提取图像

Java 如何使用iText从PDF表单中提取图像,java,forms,pdf,itext,itext7,Java,Forms,Pdf,Itext,Itext7,本文()解释了如何从普通PDF文件中提取图像。我需要提取一个用户输入到PDF表单字段中的图像 我使用iText7。我可以使用如下代码访问iText中的表单字段: PdfReader reader = new PdfReader(new FileInputStream(new ClassPathResource("myFile.pdf").getFile())); PdfDocument document = new PdfDocument(reader); PdfAcroFo

本文()解释了如何从普通PDF文件中提取图像。我需要提取一个用户输入到PDF表单字段中的图像

我使用iText7。我可以使用如下代码访问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();
       }
}

代码失败,因为
s.getAsName(PdfName.Subtype)
返回值
“Form”
。我猜我需要做的是按照你在帖子中的建议递归到XObject树中,但我不知道该怎么做。我尝试了
xObjDic.getAsDictionary()
,但不确定作为参数传入的是什么
PdfName

PDF中按钮的视觉外观可以完全定制,包括文本、图形和图像。因此,图像数据可以以稍微不同的方式存储在不同的PDF文档中。但一般来说,表单字段的小部件注释将有一个外观流,它将图像数据作为XObject保存在其资源字典中

创建带有用于测试的图像按钮的PDF:

String fieldname=“Image1\u af\u image”;
PdfAcroForm form=PdfAcroForm.getAcroForm(pdfDoc,true);
PdfButtonFormField imagefield=PdfFormField.createButton(pdfDoc,新矩形(100,100,50,50),
PdfButtonFormField.FF_按钮);
imagefield.setImage(“button.png”).setFieldName(fieldname);
form.addField(imagefield);
从按钮获取图像数据:

pdfacroformacroform=PdfAcroForm.getAcroForm(pdfDoc,false);
PdfFormField imagefield=acroForm.getField(fieldname);
//获取外观字典
PdfDictionary apDic=imagefield.getWidgets().get(0.getNormalAppearanceObject();
//获取xobject资源
PdfDictionary xObjDic=apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
用于(PdfName键:xObjDic.keySet()){
系统输出打印项次(键);
PdfStream s=xObjDic.getAsStream(键);
//仅处理图像
if(PdfName.Image.equals(s.getAsName(PdfName.Subtype))){
PdfImageXObject pixo=新的PdfImageXObject;
字节[]imgbytes=pixo.getImageBytes();
字符串ext=pixo.identifimagefileextension();
//将图像写入文件
FileOutputStream fos=新的FileOutputStream(key.toString().substring(1)+“+ext);
fos.写入(单位为千兆字节);
fos.close();
}
}
您可以使用PDF对象查看器(如Adobe Acrobat或Adobe Acrobat的内置“浏览内部PDF结构”)检查PDF文档的确切结构,并找出图像数据的存储位置

编辑:

如果图像数据是嵌套形式的XObject,则提取图像数据的更通用方法是:

pdfacroformacroform=PdfAcroForm.getAcroForm(pdfDoc,false);
PdfFormField imagefield=acroForm.getField(fieldname);
//获取外观字典
PdfDictionary apDic=imagefield.getWidgets().get(0.getNormalAppearanceObject();
//获取xobject资源
PdfDictionary xObjDic=apDic.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
提取图像fromxobj(xObjDic);
public void extractImagesFromXObj(PdfDictionary xObjDic)引发IOException异常{
用于(PdfName键:xObjDic.keySet()){
系统输出打印项次(键);
PdfStream s=xObjDic.getAsStream(键);
PdfName subType=s.getAsName(PdfName.subType);
//仅处理图像
if(PdfName.Image.equals(子类型)){
PdfImageXObject pixo=新的PdfImageXObject;
字节[]imgbytes=pixo.getImageBytes();
字符串ext=pixo.identifimagefileextension();
//将图像写入文件
FileOutputStream fos=新的FileOutputStream(key.toString().substring(1)+“+ext);
fos.写入(单位为千兆字节);
fos.close();
}
//递归处理嵌套的XObject字典
else if(PdfName.Form.equals(子类型)){
PdfDictionary nestedXObjDic=s.getAsDictionary(PdfName.Resources).getAsDictionary(PdfName.XObject);
提取图像fromxobj(nestedXObjDic);
}
}
}

您提到iText时没有说明版本。根据您的代码,我假设它是iText 7版本?对不起,是的iText7。@StephenSchultz我根据您的问题编辑修改了我的答案。根据PDF创建软件的不同,图像的确切结构不同,图像XObject可能不是正常外观流的直接资源,而是嵌套形式的XObject。因此,一般来说,还必须递归到外观流的XObject形式中,并在那里查找图像XObject。我不想用更一般的方法把我最初的答案复杂化。但是根据您的评论和对问题的编辑,我添加了一些示例代码来遍历嵌套字典。这太棒了--完全符合您的描述,也正是我所需要的--谢谢!(我发现我缺少的是迭代xObjDic.keyset()。学习新技巧总是很好!)@StephenSchultz,如果这解决了你的问题,请考虑接受这个答案,当我试图用在我的帖子中的初始答案中提供的代码来编程的时候,我得到了这个例外:COM.ITExtPDF.Keln.PDFExcExt:没有关联的PDFIGER用于制作间接。在com.itextpdf.kernel.pdf.PdfObject.makeIndirect(PdfObject.java:229)。我将代码中的“button.png”解释为一个资源flle名称。我尝试了一个相对于应用程序路径的名称,并提供了一个绝对路径。
    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();
    }