为什么我的表单在没有调用 flattenFields 方法的情况下被展平?

Why is my form being flattened without calling the flattenFields method?

我正在用这个表格测试我的方法https://help.adobe.com/en_US/Acrobat/9.0/Samples/interactiveform_enabled.pdf

它是这样调用的:

Pdf.editForm("./src/main/resources/pdfs/interactiveform_enabled.pdf", "./src/main/resources/pdfs/FILLEDOUT.pdf"));

其中 Pdf 只是一个工人 class 而 editForm 是一个静态方法。

editForm 方法如下所示:

public static int editForm(String inputPath, String outputPath) {
    try {
        PdfDocument pdf = new PdfDocument(new PdfReader(inputPath), new PdfWriter(outputPath));
        PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
        Map<String, PdfFormField> m = form.getFormFields();
        for (String s : m.keySet()) {
            if (s.equals("Name_First")) {
                m.get(s).setValue("Tristan");
            }
            if (s.equals("BACHELORS DEGREE")) {
                m.get(s).setValue("Off"); // On or Off
            }
            if (s.equals("Sex")) {
                m.get(s).setValue("FEMALE");
            }
            System.out.println(s);
        }
        pdf.close();
        logger.info("Completed");
    } catch (IOException e) {
        logger.error("Unable to fill form " + outputPath + "\n\t" + e);
        return 1;
    }

    return 0;
}

不幸的是,FILLEDOUT.pdf 文件在调用此方法后不再是表单。我做错了什么吗?

我正在使用 this 资源作为指导。请注意我没有调用 form.flattenFields()。但是,如果我确实调用该方法,则会收到 java.lang.IllegalArgumentException.

错误

感谢您的宝贵时间。

填写的PDF仍然是AcroForm,否则下面的例子会生成两次相同的PDF。

public class Main {
public static final String SRC = "src/main/resources/interactiveform_enabled.pdf";
public static final String DEST = "results/filled_form.pdf";
public static final String DEST2 = "results/filled_form_second_time.pdf";

public static void main(String[] args) throws Exception {
    File file = new File(DEST);
    file.getParentFile().mkdirs();
    Main main = new Main();

    Map<String, String> data1 = new HashMap<>();
    data1.put("Name_First", "Tristan");
    data1.put("BACHELORS DEGREE", "Off");
    main.fillPdf(SRC, DEST, data1, false);

    Map<String, String> data2 = new HashMap<>();
    data2.put("Sex", "FEMALE");
    main.fillPdf(DEST, DEST2, data2, false);
}

private void fillPdf(String src, String dest, Map<String, String> data, boolean flatten) {
    try {
        PdfDocument pdf = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
        PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);

        //Delete print field from acroform because it is defined in the contentstream not in the formfields
        form.removeField("Print");

        Map<String, PdfFormField> m = form.getFormFields();

        for (String d : data.keySet()) {
            for (String s : m.keySet()) {
                if(s.equals(d)){
                    m.get(s).setValue(data.get(d));
                }
            }
        }

        if(flatten){
            form.flattenFields();
        }

        pdf.close();
        System.out.println("Completed");
    } catch (IOException e) {
        System.out.println("Unable to fill form " + dest + "\n\t" + e);
    }
}
}

您遇到的问题与 'reader enabled forms' 有关。 归结为最初提供给您的程序的 PDF 文件已启用 reader。因此,您可以在 Adob​​e Reader 中打开 PDF 并填写表格。这允许 Acrobat 用户扩展 Adob​​e Reader 的行为。 使用 iText 填写并关闭 PDF 后,它会将 PDF 保存为 'not reader-extended'。 这使得 AcroForm 仍然可以使用 iText 填充,但是当您使用 Adob​​e Reader 打开 PDF 时,您在原始 PDF 中看到的扩展功能消失了。但这并不意味着表格被压扁了。

iText 无法启用表单 reader,事实上,创建启用 reader 的表单的唯一方法是使用 Acrobat Professional。这就是 Acrobat 和 Adob​​e Reader 交互的方式,这不是 iText 可以模仿或解决的。您可以在 link.

上找到更多信息和可能的解决方案

调用 form.flattenFields() 方法时出现的 IllegalArgumentException 是由于 PDF 文档的构造方式所致。 "Print form" 按钮应该在 AcroForm 中定义,但它在 PDF 的内容流中定义,这意味着 AcroForm 中的按钮具有空文本值,这就是导致异常的原因。

您可以通过在展平之前从 AcroForm 中删除打印字段来解决此问题。

您的表单已 Reader 启用,即它包含由 Adob​​e 颁发的密钥和证书的使用权数字签名,以向普通 Adob​​e Reader 表明它将激活许多在那个 PDF 上操作时的附加功能。

如果您按照原始代码标记文件,现有的 PDF 对象将重新排列并略有变化。这破坏了使用权签名,并且 Adob​​e Reader 认识到这一点,否认 "The document has been changed since it was created and use of extended features is no longer available."

如果您在 附加模式 中标记文件,但是,更改将作为增量更新附加到 PDF。因此,签名仍然正确地签署了它的原始字节范围并且 Adob​​e Reader 没有抱怨。

要激活 附加模式,请在创建 PdfDocument:

时使用 StampingProperties
PdfDocument pdf = new PdfDocument(new PdfReader(inputPath), new PdfWriter(outputPath), new StampingProperties().useAppendMode());

(使用 iText 7.1.1-SNAPSHOT 和 Adob​​e Acrobat Reader DC 版本 2018.009.20050 测试)


顺便说一下,Adobe Reader 不仅会检查签名,它还会尝试确定增量更新中的更改是否不超出使用权限激活的附加功能的范围签名.

否则,您可以简单地获取启用 Reader 的小型 PDF,并在追加模式下将所有现有页面替换为您自己选择的内容。这当然不符合 Adob​​e 的利益...