Java PDFBox是否允许从AcroForm中删除一个字段?

Java PDFBox是否允许从AcroForm中删除一个字段?,java,pdfbox,Java,Pdfbox,我正在使用Apache并试图删除一个字段。但是找不到这样做的方法,就像我可以用iText做的那样:PdfStamper.getAcroFields().removeField(“signature3”) 我要做的事。最初我有3个数字签名的PDF模板。在某些情况下,我只需要2个签名,因此在这种情况下,我需要从模板中删除第3个签名。似乎我不能用PDFBox实现这一点,我发现最接近的事情是将这个字段展平,但问题是如果展平一个特定的PDField(不是整个表单,但只有一个字段)-所有其他签名都失去了功能

我正在使用Apache并试图删除一个字段。但是找不到这样做的方法,就像我可以用iText做的那样:
PdfStamper.getAcroFields().removeField(“signature3”)

我要做的事。最初我有3个数字签名的PDF模板。在某些情况下,我只需要2个签名,因此在这种情况下,我需要从模板中删除第3个签名。似乎我不能用PDFBox实现这一点,我发现最接近的事情是将这个字段展平,但问题是如果展平一个特定的PDField(不是整个表单,但只有一个字段)-所有其他签名都失去了功能,看起来它们也变平了。 下面是执行此操作的代码:

PDDocument document = PDDocument.load(file);
PDDocumentCatalog documentCatalog = document.getDocumentCatalog();
PDAcroForm acroForm = documentCatalog.getAcroForm();

List<PDField> flattenList = new ArrayList<>();
for (PDField field : acroForm.getFieldTree()) {
    if (field instanceof PDSignatureField && "signature3".equals(field.getFullyQualifiedName())) {
        flattenList.add(field);
    }
}

acroForm.flatten(flattenList, true);
document.save(dest);        
document.close();
PDDocument document=PDDocument.load(文件);
PDDocumentCatalog documentCatalog=document.getDocumentCatalog();
PDAcroForm acroForm=documentCatalog.getAcroForm();
List FlattList=新建ArrayList();
for(PDField字段:acroForm.getFieldTree()){
if(PDSignatureField&“signature3.equals”的字段实例等于(field.getFullyQualifiedName()){
列表。添加(字段);
}
}
acroForm.flatten(flattelist,true);
文件保存(dest);
document.close();

正如Tilman在评论中提到的,PDFBox没有从字段树中删除字段的方法。尽管如此,它还是有操纵底层PDF结构的方法,因此可以自己编写这样的方法,例如:

PDField removeField(PDDocument document, String fullFieldName) throws IOException {
    PDDocumentCatalog documentCatalog = document.getDocumentCatalog();
    PDAcroForm acroForm = documentCatalog.getAcroForm();

    if (acroForm == null) {
        System.out.println("No form defined.");
        return null;
    }

    PDField targetField = null;

    for (PDField field : acroForm.getFieldTree()) {
        if (fullFieldName.equals(field.getFullyQualifiedName())) {
            targetField = field;
            break;
        }
    }
    if (targetField == null) {
        System.out.println("Form does not contain field with given name.");
        return null;
    }

    PDNonTerminalField parentField = targetField.getParent();
    if (parentField != null) {
        List<PDField> childFields = parentField.getChildren();
        boolean removed = false;
        for (PDField field : childFields)
        {
            if (field.getCOSObject().equals(targetField.getCOSObject())) {
                removed = childFields.remove(field);
                parentField.setChildren(childFields);
                break;
            }
        }
        if (!removed)
            System.out.println("Inconsistent form definition: Parent field does not reference the target field.");
    } else {
        List<PDField> rootFields = acroForm.getFields();
        boolean removed = false;
        for (PDField field : rootFields)
        {
            if (field.getCOSObject().equals(targetField.getCOSObject())) {
                removed = rootFields.remove(field);
                break;
            }
        }
        if (!removed)
            System.out.println("Inconsistent form definition: Root fields do not include the target field.");
    }

    removeWidgets(targetField);

    return targetField;
}

void removeWidgets(PDField targetField) throws IOException {
    if (targetField instanceof PDTerminalField) {
        List<PDAnnotationWidget> widgets = ((PDTerminalField)targetField).getWidgets();
        for (PDAnnotationWidget widget : widgets) {
            PDPage page = widget.getPage();
            if (page != null) {
                List<PDAnnotation> annotations = page.getAnnotations();
                boolean removed = false;
                for (PDAnnotation annotation : annotations) {
                    if (annotation.getCOSObject().equals(widget.getCOSObject()))
                    {
                        removed = annotations.remove(annotation);
                        break;
                    }
                }
                if (!removed)
                    System.out.println("Inconsistent annotation definition: Page annotations do not include the target widget.");
            } else {
                System.out.println("Widget annotation does not have an associated page; cannot remove widget.");
                // TODO: In this case iterate all pages and try to find and remove widget in all of them
            }
        }
    } else if (targetField instanceof PDNonTerminalField) {
        List<PDField> childFields = ((PDNonTerminalField)targetField).getChildren();
        for (PDField field : childFields)
            removeWidgets(field);
    } else {
        System.out.println("Target field is neither terminal nor non-terminal; cannot remove widgets.");
    }
}
PDDocument document = PDDocument.load(SOURCE_PDF);

PDField field = removeField(document, "Signature1");
Assert.assertNotNull("Field not found", field);

document.save(TARGET_PDF);        
document.close();
(测试
testRemoveInvisibleSignature


PS:我不确定PDFBox在某处实际缓存了多少表单相关信息。因此,我建议不要在同一个文档操作会话中进一步操作表单信息,至少不需要测试


PPS:您可以在
removeWidgets
helper方法中找到TODO。如果该方法输出“Widget annotation没有关联的页面;无法删除Widget”,则必须添加缺少的代码。

正如Tilman在评论中提到的,PDFBox没有从字段树中删除字段的方法。尽管如此,它还是有操纵底层PDF结构的方法,因此可以自己编写这样的方法,例如:

PDField removeField(PDDocument document, String fullFieldName) throws IOException {
    PDDocumentCatalog documentCatalog = document.getDocumentCatalog();
    PDAcroForm acroForm = documentCatalog.getAcroForm();

    if (acroForm == null) {
        System.out.println("No form defined.");
        return null;
    }

    PDField targetField = null;

    for (PDField field : acroForm.getFieldTree()) {
        if (fullFieldName.equals(field.getFullyQualifiedName())) {
            targetField = field;
            break;
        }
    }
    if (targetField == null) {
        System.out.println("Form does not contain field with given name.");
        return null;
    }

    PDNonTerminalField parentField = targetField.getParent();
    if (parentField != null) {
        List<PDField> childFields = parentField.getChildren();
        boolean removed = false;
        for (PDField field : childFields)
        {
            if (field.getCOSObject().equals(targetField.getCOSObject())) {
                removed = childFields.remove(field);
                parentField.setChildren(childFields);
                break;
            }
        }
        if (!removed)
            System.out.println("Inconsistent form definition: Parent field does not reference the target field.");
    } else {
        List<PDField> rootFields = acroForm.getFields();
        boolean removed = false;
        for (PDField field : rootFields)
        {
            if (field.getCOSObject().equals(targetField.getCOSObject())) {
                removed = rootFields.remove(field);
                break;
            }
        }
        if (!removed)
            System.out.println("Inconsistent form definition: Root fields do not include the target field.");
    }

    removeWidgets(targetField);

    return targetField;
}

void removeWidgets(PDField targetField) throws IOException {
    if (targetField instanceof PDTerminalField) {
        List<PDAnnotationWidget> widgets = ((PDTerminalField)targetField).getWidgets();
        for (PDAnnotationWidget widget : widgets) {
            PDPage page = widget.getPage();
            if (page != null) {
                List<PDAnnotation> annotations = page.getAnnotations();
                boolean removed = false;
                for (PDAnnotation annotation : annotations) {
                    if (annotation.getCOSObject().equals(widget.getCOSObject()))
                    {
                        removed = annotations.remove(annotation);
                        break;
                    }
                }
                if (!removed)
                    System.out.println("Inconsistent annotation definition: Page annotations do not include the target widget.");
            } else {
                System.out.println("Widget annotation does not have an associated page; cannot remove widget.");
                // TODO: In this case iterate all pages and try to find and remove widget in all of them
            }
        }
    } else if (targetField instanceof PDNonTerminalField) {
        List<PDField> childFields = ((PDNonTerminalField)targetField).getChildren();
        for (PDField field : childFields)
            removeWidgets(field);
    } else {
        System.out.println("Target field is neither terminal nor non-terminal; cannot remove widgets.");
    }
}
PDDocument document = PDDocument.load(SOURCE_PDF);

PDField field = removeField(document, "Signature1");
Assert.assertNotNull("Field not found", field);

document.save(TARGET_PDF);        
document.close();
(测试
testRemoveInvisibleSignature


PS:我不确定PDFBox在某处实际缓存了多少表单相关信息。因此,我建议不要在同一个文档操作会话中进一步操作表单信息,至少不需要测试


PPS:您可以在
removeWidgets
helper方法中找到TODO。如果方法输出“Widget annotation没有关联的页面;无法删除Widget”,则必须添加缺少的代码。

。。。你问的是PdfBox问题,但你把它标记为iText问题?你能解释你为什么这么做吗?如果你不想解释,人们会对你的动机做出假设。这些假设不会是积极的。澄清一下:我假设你反对自由软件,你只想要免费的软件,因为你不重视开发人员和他们的工作。你不知道,因为你是一个。这个假设可能是错误的,但既然你不想解释,那就是我的猜测。如果一个问题被错误地标记了,那么删除错误的标记比写3条评论来抱怨它所花费的精力要少。你是说签名字段是空的吗?你能分享模板PDF吗?PDFBox没有从字段树中删除字段的方法,但如果树结构简单,则可能仍然可以删除字段。因此。。。你问的是PdfBox问题,但你把它标记为iText问题?你能解释你为什么这么做吗?如果你不想解释,人们会对你的动机做出假设。这些假设不会是积极的。澄清一下:我假设你反对自由软件,你只想要免费的软件,因为你不重视开发人员和他们的工作。你不知道,因为你是一个。这个假设可能是错误的,但既然你不想解释,那就是我的猜测。如果一个问题被错误地标记了,那么删除错误的标记比写3条评论来抱怨它所花费的精力要少。你是说签名字段是空的吗?你能分享模板PDF吗?PDFBox没有从字段树中删除字段的方法,但是如果树结构简单,它可能还是可以的。非常感谢你,mkl!这是一个让孩子们复位的好主意!很好的实现。非常感谢您,mkl!这是一个让孩子们复位的好主意!很好的执行。