如何使用PDFbox 1.8.11查找PDF中的哪个图像字段已插入图像,哪个未附加图像?

时间:2019-07-09 20:01:49

标签: itext pdfbox

我有一个PDF,其中包含图像字段。我没有使用带有JavaScript的PDPushButton来附加图片,因为如果这样做,按钮的顶层将替换为我所附加的图片,这不是我想要的。因此,我明确地使用了Adobe LiveCycle Designer中可用的ImageField。我可以使用PDFBox提取附加在其上的文件,但是我无法找到任何方式查看哪些图像字段具有附加文件,而哪些没有。例如,如果我在这里有以下代码:

ImageField [1],ImageField [2],ImageField [3]

我想看类似的东西 ImageField [1]:空, ImageField [2]:是的, ImageField [3]:正确enter code here

等,假设ImageField [2]和ImageField [3]都附有图像。

下面是我正在处理的代码:

我有一个常数:

然后我遍历整个图像字段名称,看看哪个字段是PDXObjectImage的实例,然后如果它是PDXObjectImage,则我检查那个object.getRGBImage()。getHeight()> 0假设仅上载的文件高度> 1,表示已附加文件。

私有静态String [] IMAGE_FIELD_ROW = {“ ImageField1 [0]”,“ ImageField2 [0]”,....} => 100行字符串值,例如“ ImageField3 [0]”,“ ImageField4 [0] ]”,...等。

    for(int i = 0; i<IMAGE_FIELD_ROW.length; i++)
    {
        if(field.getPartialName().equals(IMAGE_FIELD_ROW[i]))
        {
            Map<String, PDAppearanceStream> stateAppearances = field.getWidget().getAppearance().getNormalAppearance();
            for (Map.Entry<String, PDAppearanceStream> entry: stateAppearances.entrySet())
            {
                PDAppearanceStream appearance = entry.getValue();
                PDResources resources = appearance.getResources();
                 if (resources == null)
                     return;
                 Map<String, PDXObject> xObjects = resources.getXObjects();
                 if (xObjects == null)
                     return;

                 for (Map.Entry<String, PDXObject> entryNew : xObjects.entrySet())
                 {
                     PDXObject xObject = entryNew.getValue();
                     System.out.println("printing out the xobject name: "+ entryNew.getKey());


                     if (xObject instanceof PDXObjectForm)
                     {

                         PDXObjectForm form = (PDXObjectForm)xObject;
                         PDResources resources2 = form.getResources();
                         if (resources2 == null)
                             return;
                         Map<String, PDXObject> xObjects2 = resources2.getXObjects();
                         if (xObjects2 == null)
                         {
                             return;
                         }
                         for (Map.Entry<String, PDXObject> entry2 : xObjects2.entrySet())
                         {

                             PDXObject xObject2 = entry2.getValue();

                             if (xObject2 instanceof PDXObjectForm)
                             {
                                 continue;
                             }
                             else if (xObject2 instanceof PDXObjectImage)
                             {
                                 PDXObjectImage ig = (PDXObjectImage)xObject2;
                                 if(ig.getRGBImage().getHeight() >  0)
                                 {
                                     images.put(field.getPartialName(), "true");
                                 }
                                 else
                                 {
                                     images.put(field.getPartialName(), null);
                                 }

                                 //imageIds.add(imageId);
                             }
                             else
                             {
                                continue;
                             }

                     }

                 }
            }

        }
        }
    }

Images是一个地图变量:Mapimages。

我的代码文件也很大,所以我不想通过粘贴整个文件使任何人不知所措。以下是我正在使用的示例PDF文件的保管箱链接:

https://www.dropbox.com/s/g2wqm8ipsp8t8l5/GSA%20500%20PDF_v4.pdf?dl=0

1 个答案:

答案 0 :(得分:0)

您的PDF是AcroForm / XFA的混合文档; XFA部件使用带有imageEdit用户界面的字段,而AcroForm部件使用按钮字段。

因此,它提供了两种方法来检查是否设置了图像字段:查看AcroForm按钮并检查其外观以查看图像,或者检索XFA XML并进行检查。

检查XFA XML

最初,我确实忽略了问题标题中的PDFBox版本,并针对PDFBox 2.0.x实施了该版本。事实证明,尽管相同的代码可以用于PDFBox 1.8.11,但是可能会抛出一些其他异常,因此必须予以考虑。

检查XFA XML的后一种方法实际上对于手头的文档来说要容易一些。只需在XML中搜索具有相关名称的元素,然后检查其内容即可。作为一项额外的健全性检查,您可以验证元素的内容类型属性:

boolean isFieldFilledXfa(Document xfaDom, String fieldName) {
    NodeList fieldElements = xfaDom.getElementsByTagName(fieldName);
    for (int i = 0; i < fieldElements.getLength(); i++) {
        Node node = fieldElements.item(i);
        if (node instanceof Element) {
            Element element = (Element) node;
            if (element.getAttribute("xfa:contentType").startsWith("image/")) {
                return element.getTextContent().length() > 0;
            }
        }
    }
    return false;
}

CheckImageFieldFilled辅助方法)

有了它,您可以检查您的文档:

PDDocument document = PDDocument.load(SOURCE);
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
Document xfaDom = acroForm.getXFA().getDocument();

System.out.println("Filled image fields from ImageField1..ImageField105:");
for (int i=1; i < 106; i++) {
    if (isFieldFilledXfa(xfaDom, "ImageField" + i)) {
        System.out.printf("* ImageField%d\n", i);
    }
}

CheckImageFieldFilled测试方法testCheckXfaGsa500Pdf_v4

输出:

Filled image fields from ImageField1..ImageField105:
* ImageField1
* ImageField3
* ImageField6

检查AcroForm外观

此处的实现仅适用于PDFBox2.0.x。内容流解析器类的结构在2.0.0中已进行了全面改进,使该代码的反向移植到1.8.x有点乏味。

要检查按钮外观是否真正显示 图像(并且不仅在资源中包含图像 ),可以使用简单的PDFGraphicsStreamEngine这样的子类:

public class WidgetImageChecker extends PDFGraphicsStreamEngine
{
    public WidgetImageChecker(PDAnnotationWidget widget) {
        super(widget.getPage());
        this.widget = widget;
    }

    public boolean hasImages() throws IOException {
        count = 0;
        PDAppearanceStream normalAppearance = widget.getNormalAppearanceStream();
        processChildStream(normalAppearance, widget.getPage());
        return count != 0;
    }

    @Override
    public void drawImage(PDImage pdImage) throws IOException {
        count++;
    }

    @Override
    public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException { }

    @Override
    public void clip(int windingRule) throws IOException { }

    @Override
    public void moveTo(float x, float y) throws IOException {  }

    @Override
    public void lineTo(float x, float y) throws IOException { }

    @Override
    public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException {  }

    @Override
    public Point2D getCurrentPoint() throws IOException { return null; }

    @Override
    public void closePath() throws IOException { }

    @Override
    public void endPath() throws IOException { }

    @Override
    public void strokePath() throws IOException { }

    @Override
    public void fillPath(int windingRule) throws IOException { }

    @Override
    public void fillAndStrokePath(int windingRule) throws IOException { }

    @Override
    public void shadingFill(COSName shadingName) throws IOException { }

    final PDAnnotationWidget widget;
    int count = 0;
} 

CheckImageFieldFilled助手类)

使用它可以创建如下检查方法:

boolean isFieldFilledAcroForm(PDAcroForm acroForm, String fieldName) throws IOException {
    for (PDField field : acroForm.getFieldTree()) {
        if (field instanceof PDPushButton && fieldName.equals(field.getPartialName())) {
            for (final PDAnnotationWidget widget : field.getWidgets()) {
                WidgetImageChecker checker = new WidgetImageChecker(widget);
                if (checker.hasImages())
                    return true;
            }
        }
    }
    return false;
}

CheckImageFieldFilled辅助方法)

并像这样使用它:

PDDocument document = PDDocument.load(SOURCE);
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

System.out.println("Filled image fields (AcroForm) from ImageField1..ImageField105:");
for (int i=1; i < 106; i++) {
    if (isFieldFilledAcroForm(acroForm, "ImageField" + i + "[0]")) {
        System.out.printf("* ImageField%d\n", i);
    }
}

CheckImageFieldFilled测试testCheckAcroFormGsa500Pdf_v4

输出,就像上面一样:

Filled image fields (AcroForm) from ImageField1..ImageField105:
* ImageField1
* ImageField3
* ImageField6