当前位置: 首页 > 知识库问答 >
问题:

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

艾晋
2023-03-14

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

我要做的事情。最初,我有3个数字签名的模板PDF。在某些情况下,我只需要2个签名,所以在这种情况下,我需要从模板中删除第三个签名。看起来我不能用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();

共有1个答案

卫沈义
2023-03-14

正如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.");
    }
}

说明:RemveField帮助器方法RemveFieldRemveWidget

可以将其应用于以下文档和字段:

PDDocument document = PDDocument.load(SOURCE_PDF);

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

document.save(TARGET_PDF);        
document.close();

说明:测试代码

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

PPS:您可以在RemveWidget帮助器方法中找到一个TODO。如果该方法输出"小部件注释没有关联页面;无法删除小部件",则必须添加丢失的代码。

 类似资料:
  • 我有一个带有很多acroforms的pdf,我对它进行了一些操作,结果得到了一个新的pdf。所以我有PDF-1(这是原始版本)和PDF-2(只是PDF-1的副本),现在我想合并它们。两个PDF都有一些缩略形式,例如:字段a、字段2。。。 在合并它们之前,我会将PDF-1展平,因为我只想从PDF-2中获取acrofields。当我检查新合并的PDF时,我可以看到PDF-1页面上没有可见字段,PDF-

  • 我需要比较PDF文档,这些文档是用iText创建的。我实际上设法比较了文件,但我发现了一个微小的差异。 当在像Notepad++这样的编辑器中打开PDF文件时,我可以看到文件末尾有这样的东西:

  • > 当同一主机上的两个进程使用网络套接字通信时,它们的套接字必须使用不同的端口吗? 我想是的,因为套接字由IP地址和端口号标识。如果进程的套接字使用相同的端口,则无法区分它们的套接字,对吗? 但是一个主机可以有多个网络接口,所以有多个IP地址。同一个主机上的多个网络接口是否可以共享一个端口,从而使两个套接字共享同一个端口? 例如,SSH本地端口转发由以下选项指定: 然后本地主机(SSH客户端运行的

  • 问题内容: 我在这里的研究中得到了不同的答案。 有人可以验证Redis服务器只能存储任何数值的表示吗? 例如,如果我在lpush中使用具有双重类型的Java客户端(Jedis),在发送给Redis之前是否需要将其转换为等效于字符串类型的内容? 还是有一种方法可以发送诸如double之类的实际数字类型?如果是这样,是否有任何示例代码说明了如何实现此目的? 谢谢 问题答案: Redis将所有内容存储在

  • 问题内容: 我有一个包含文本和HTML的字符串。我想删除或以其他方式禁用某些HTML标记,例如,同时允许其他HTML标记,以便我可以安全地将其呈现在网页上。我有一个允许标签的列表,如何处理字符串以删除任何其他标签? 问题答案: 这是一个使用BeautifulSoup的简单解决方案: 如果您也要删除无效标签的内容,请替换为。 您可能还会考虑使用lxml和Tidy。

  • 我在mongo DB集合中存储了以下文档,我将动态接收要删除的url,例如,我需要删除订阅者的urlhttp://localhost.8080/FNOL/subscriber1对于文件中的“名称”:“FNOL”,“国家”:“美国”,“lob”:“财产”。 如何使用mongo编写remove命令? 我需要重新定义我的文档结构吗? 提前感谢。 移除后: