使用 iText 更改现有 PDF 中的 AcroFields 顺序?

Change AcroFields order in existing PDF with iText?

我有一个带有文本表单字段的 pdf,其中一个位于另一个之上。当我通过 iText 填写字段并展平表单时,我在另一个表单字段顶部创建的表单字段现在位于底部。

例如,我有一个名为 "number_field" 的文本字段,它位于标题为 "name_field" 的第二个文本字段下方。当我通过 iText 为这些字段设置值时(number_field 为 10,name_field 为 'John'),number_field 现在位于 name_field 之上.

如何使用 iText 更改这些字段在页面上的顺序?可能吗?

Link 示例 PDF:https://freecompany.sharefile.com/d-s84f6d63e7d04fe79

我在 iText Group 的问题跟踪器中制作了以下票证:

A problem is caused by the fact that iText reads the field items into a HashMap, hence there is no way to predict in which order they will be flattened. This usually isn't a problem. I don't think this problem occurs in case you don't flatten the PDF, because in that case, the appearance is stored in the widget annotations and it's up to the PDF viewer to decide which field covers another one in case of overlapping fields.

However, if form fields overlap, then you can't predict which field will cover which when flattening.

Suppose that we'd use a TreeMap instead of a HashMap, would this solve the problem? Not really, because which Comparator would we use? Sometimes a Tab-order is defined, but not always. If it's not defined, should we order the fields in the order in which they appear in the /Fields array? Or does it make more sense to order them based on the order of the widget annotations in the /Annots array? Another option is to order them based on their position on the page. In short: this is not a decision iText should make.

However, if somebody would like to solve this problem, we could create a Comparator member variable for PdfStamperImp. If such a Comparator is provided (we could even provide some implementations), then the flattening process would be executed in the order defined by the Comparator.

这张工单的优先级很低(我假设您不是 iText Software 公司之一的客户),但在写这张工单时,我有了另一个想法。

我已经在评论中提到了 。在这种情况下,您将获得所有字段位置(使用 getFieldPositions() 方法)并使用 ColumnText 以正确的顺序绘制所有内容。这种方法有几个缺点:为了使字体、字体大小、字体颜色正确,您必须检查字段。这需要一些编程。

我现在将此作为答案发布,因为我有更好的选择:分两次填写表格! FillFormFieldOrder 示例中显示了这一点。我们填写表格 src,得到扁平化的表格 dest,如下所示:

public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
    go2(go1(src), dest);
}

可以看到,我们先执行go1()方法:

public byte[] go1(String src) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PdfStamper stamper = new PdfStamper(reader, baos);
    AcroFields form = stamper.getAcroFields();
    form.setField("sunday_1", "1");
    form.setField("sunday_2", "2");
    form.setField("sunday_3", "3");
    form.setField("sunday_4", "4");
    form.setField("sunday_5", "5");
    form.setField("sunday_6", "6");
    stamper.setFormFlattening(true);
    stamper.partialFormFlattening("sunday_1");
    stamper.partialFormFlattening("sunday_2");
    stamper.partialFormFlattening("sunday_3");
    stamper.partialFormFlattening("sunday_4");
    stamper.partialFormFlattening("sunday_5");
    stamper.partialFormFlattening("sunday_6");
    stamper.close();
    reader.close();
    return baos.toByteArray();
}

这会填写所有 sunday_x 字段,并使用部分表单扁平化来扁平化 仅那些字段 go1() 方法将 src 作为参数,returns 和 byte[] 将部分扁平化形式。

byte[] 将用作 go2() 方法的参数,该方法将 dest 作为其第二个参数。现在我们要填写 sunday_x_notes 字段:

public void go2(byte[] src, String dest) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
    AcroFields form = stamper.getAcroFields();
    form.setField("sunday_1_notes", "It's Sunday today, let's go to the sea");
    form.setField("sunday_2_notes", "It's Sunday today, let's go to the park");
    form.setField("sunday_3_notes", "It's Sunday today, let's go to the beach");
    form.setField("sunday_4_notes", "It's Sunday today, let's go to the woods");
    form.setField("sunday_5_notes", "It's Sunday today, let's go to the lake");
    form.setField("sunday_6_notes", "It's Sunday today, let's go to the river");
    stamper.setFormFlattening(true);
    stamper.close();
    reader.close();
}

如您所见,我们现在展平了所有字段。结果看起来像 this:

现在,您再也不用担心字段的顺序了,/Fields 数组中没有,/Annots 数组中也没有。这些字段按照您想要的确切顺序填写。注释现在涵盖日期,而不是相反。