如何使用 iText 和 Java 检查具有相同变量名的 PDF 文件中的复选框
How to check a checkbox in PDF file with the same variable name with iText and Java
我一直在使用 Java
的 iText
库来自动填充 PDF 文档。我做的第一件事是映射每个字段。一旦我映射了每个字段,我将变量名称保存到 Strings
中以便于访问。
到目前为止,还不错。问题是我有一组 6 个具有相同变量名的复选框。例如,它们被命名为 topmostSubform[0].Page2[0].p2_cb01[0]
.
通过一些测试,我发现如果我选中第一个复选框,那么 topmostSubform[0].Page2[0].p2_cb01[0] = 1
如果我选中第二个(自动取消选中第一个),那么 topmostSubform[0].Page2[0].p2_cb01[0] = 2
然后依次topmostSubform[0].Page2[0].p2_cb01[0] = 3
直到得到最后一个数6
。
我正在使用 form.setField("topmostSubform[0].Page2[0].p2_cb01[0]", "1");
填写字段。当我填写值 1
时,第一个复选框被选中,但是当我填写应该选中第二个复选框的数字 2
时,它不起作用。如果我选择 2, 3, 4, 5 or 6
没关系,它只是不起作用,复选框保持空白,我无法选中它们。
这里有一段代码:
String _5_1 = "topmostSubform[0].Page2[0].p2_cb01[0]";
AcroFields form = stamper.getAcroFields();
form.setField(_5_1, "3");
拜托,我需要建议。
请允许我引用 ISO-32000-1 第 12.7.3.2 节 "Field names":
It is possible for different field dictionaries to have the same fully
qualified field name if they are descendants of a common ancestor with
that name and have no partial field names (T entries) of their own.
Such field dictionaries are different representations of the same
underlying field; they should differ only in properties that specify
their visual appearance. In particular, field dictionaries with the
same fully qualified field name shall have the same field type (FT),
value (V), and default value (DV).
如果我们将此应用于您的问题:不同的字段词典可能具有相同的名称 topmostSubform[0].Page2[0].p2_cb01[0]
。这样的字段字典是相同字段的不同表示,它们应具有相同的值。
有两种选择:
- 如果您的 PDF 中的字段字典名称 (
topmostSubform[0].Page2[0].p2_cb01[0]
) 具有不同的值,则您没有有效的 PDF 文件:它违反了 ISO-32000-1,是官方的PDF规范。
- 也许您认为您有具有相同字段名称和不同值的复选框,但也许这些复选框实际上是具有不同单选按钮的单选字段。也许您没有使用正确的值。也许还有其他事情在起作用。要让 SO reader 能够帮助您,他需要查看 PDF 文件。
如果选项 1 适用,请放弃所有希望:您的 PDF 质量很差。修理它或扔掉它。如果选项 2 适用,请分享 PDF。
检查PDF文件后更新:
选项 2 适用。您有一个混合表单,这意味着该表单在 PDF 中描述了两次,一次使用 AcroForm 技术,一次使用 XFA。请先阅读我对以下问题的回答:
当您在 Adobe Reader 中打开 PDF 时,您会注意到这些字段就像单选按钮一样。当你点击一个时,它是selected,但当你点击另一个时,它是selected,但第一个不再是selected。
你看到的,就是XFA中描述的表格,XFA表格和AcroForm描述有一些重要的区别。这不是错误。它是混合形式所固有的。
当您使用以下方式填写表格时:
form.setField("topmostSubform[0].Page2[0].p2_cb01[0]", "1");
iText 正确填写了 AcroForm,但未能填写 XFA 表单,因为 iText 对应该在 XFA 流(实际上是用 XML 表示)。有关详细信息:这在 iText in Action - Second Edition.
的第 8 章中进行了解释
在这种情况下我通常做的正是那个问他是否可以安全地扔掉 XFA 部分的人所做的:我删除了 XFA 部分:
AcroFields form = stamper.getAcroFields();
form.removeXfa();
这大大简化了事情,但还没有解决您的问题。为了解决你的问题,我们需要查看PDF里面的内容:
正如您在屏幕截图中看到的(取自 iText RUPS),表单有两种不同的描述:您有一个 /Fields
数组(AcroForm 描述)和一个/XFA
部分由不同的流组成,如果你加入它们,就会形成一个大的 XML 文件。
我们还看到,您认为只有一个字段 topmostSubform[0].Page2[0].p2_cb01[0]
,但实际上有 6 个字段:
topmostSubform[0].Page2[0].p2_cb01[0]
topmostSubform[0].Page2[0].p2_cb01[1]
topmostSubform[0].Page2[0].p2_cb01[2]
topmostSubform[0].Page2[0].p2_cb01[3]
topmostSubform[0].Page2[0].p2_cb01[4]
topmostSubform[0].Page2[0].p2_cb01[5]
现在让我们看一下这些字段。
这是字段 topmostSubform[0].Page2[0].p2_cb01[0]
:
这是字段 topmostSubform[0].Page2[0].p2_cb01[0]
:
这些是 AcroForm 复选框,但有一条针对人类的说明说:select 只有一个。这条指令只能被人理解,机器和软件都不能理解。
我第一次尝试编写 FillHybridForm 示例失败,因为我犯了与您类似的错误。我没有仔细观察不同的外观状态。我以为topmostSubform[0].Page2[0].p2_cb01[0]
的On值为0
,topmostSubform[0].Page2[0].p2_cb01[1]
的值为1
,依此类推。它不是... topmostSubform[0].Page2[0].p2_cb01[0]
的 On 值为 1
,topmostSubform[0].Page2[0].p2_cb01[1]
的值为 2
,依此类推。
这是填写所有复选框的方法:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
form.removeXfa();
form.setField("topmostSubform[0].Page2[0].p2_cb01[0]", "1");
form.setField("topmostSubform[0].Page2[0].p2_cb01[1]", "2");
form.setField("topmostSubform[0].Page2[0].p2_cb01[2]", "3");
form.setField("topmostSubform[0].Page2[0].p2_cb01[3]", "4");
form.setField("topmostSubform[0].Page2[0].p2_cb01[4]", "5");
form.setField("topmostSubform[0].Page2[0].p2_cb01[5]", "6");
stamper.close();
reader.close();
}
现在所有复选框都已选中。见 f8966_filled.pdf:
当然:作为人类,我们知道我们不应该这样做,因为我们应该将字段视为单选按钮,但没有技术原因在 AcroForm 描述中我们为什么不能。阻止我们这样做的逻辑只存在于 XFA 描述中。
如果可以丢弃 XFA 部分,这将解决您的问题。如果可以将表格展平,它也将解决您的问题,在这种情况下您应该添加:
stamper.setFormFlattening(true);
如果您不接受上述选项,您不应该丢弃 XFA 部分,而是按照上述方法填写 AcroForm 部分 和 使用 iText 提取XML 数据集(参见第一个屏幕截图中的 datasets
),按照美国政府希望您更新它的方式对其进行更新,并使用 iText 将更新数据集放回 datasets
对象中.
呸...这是我在 Whosebug 上写过的最长的答案之一。
我一直在使用 Java
的 iText
库来自动填充 PDF 文档。我做的第一件事是映射每个字段。一旦我映射了每个字段,我将变量名称保存到 Strings
中以便于访问。
到目前为止,还不错。问题是我有一组 6 个具有相同变量名的复选框。例如,它们被命名为 topmostSubform[0].Page2[0].p2_cb01[0]
.
通过一些测试,我发现如果我选中第一个复选框,那么 topmostSubform[0].Page2[0].p2_cb01[0] = 1
如果我选中第二个(自动取消选中第一个),那么 topmostSubform[0].Page2[0].p2_cb01[0] = 2
然后依次topmostSubform[0].Page2[0].p2_cb01[0] = 3
直到得到最后一个数6
。
我正在使用 form.setField("topmostSubform[0].Page2[0].p2_cb01[0]", "1");
填写字段。当我填写值 1
时,第一个复选框被选中,但是当我填写应该选中第二个复选框的数字 2
时,它不起作用。如果我选择 2, 3, 4, 5 or 6
没关系,它只是不起作用,复选框保持空白,我无法选中它们。
这里有一段代码:
String _5_1 = "topmostSubform[0].Page2[0].p2_cb01[0]";
AcroFields form = stamper.getAcroFields();
form.setField(_5_1, "3");
拜托,我需要建议。
请允许我引用 ISO-32000-1 第 12.7.3.2 节 "Field names":
It is possible for different field dictionaries to have the same fully qualified field name if they are descendants of a common ancestor with that name and have no partial field names (T entries) of their own. Such field dictionaries are different representations of the same underlying field; they should differ only in properties that specify their visual appearance. In particular, field dictionaries with the same fully qualified field name shall have the same field type (FT), value (V), and default value (DV).
如果我们将此应用于您的问题:不同的字段词典可能具有相同的名称 topmostSubform[0].Page2[0].p2_cb01[0]
。这样的字段字典是相同字段的不同表示,它们应具有相同的值。
有两种选择:
- 如果您的 PDF 中的字段字典名称 (
topmostSubform[0].Page2[0].p2_cb01[0]
) 具有不同的值,则您没有有效的 PDF 文件:它违反了 ISO-32000-1,是官方的PDF规范。 - 也许您认为您有具有相同字段名称和不同值的复选框,但也许这些复选框实际上是具有不同单选按钮的单选字段。也许您没有使用正确的值。也许还有其他事情在起作用。要让 SO reader 能够帮助您,他需要查看 PDF 文件。
如果选项 1 适用,请放弃所有希望:您的 PDF 质量很差。修理它或扔掉它。如果选项 2 适用,请分享 PDF。
检查PDF文件后更新:
选项 2 适用。您有一个混合表单,这意味着该表单在 PDF 中描述了两次,一次使用 AcroForm 技术,一次使用 XFA。请先阅读我对以下问题的回答:
当您在 Adobe Reader 中打开 PDF 时,您会注意到这些字段就像单选按钮一样。当你点击一个时,它是selected,但当你点击另一个时,它是selected,但第一个不再是selected。
你看到的,就是XFA中描述的表格,XFA表格和AcroForm描述有一些重要的区别。这不是错误。它是混合形式所固有的。
当您使用以下方式填写表格时:
form.setField("topmostSubform[0].Page2[0].p2_cb01[0]", "1");
iText 正确填写了 AcroForm,但未能填写 XFA 表单,因为 iText 对应该在 XFA 流(实际上是用 XML 表示)。有关详细信息:这在 iText in Action - Second Edition.
的第 8 章中进行了解释在这种情况下我通常做的正是那个问他是否可以安全地扔掉 XFA 部分的人所做的:我删除了 XFA 部分:
AcroFields form = stamper.getAcroFields();
form.removeXfa();
这大大简化了事情,但还没有解决您的问题。为了解决你的问题,我们需要查看PDF里面的内容:
正如您在屏幕截图中看到的(取自 iText RUPS),表单有两种不同的描述:您有一个 /Fields
数组(AcroForm 描述)和一个/XFA
部分由不同的流组成,如果你加入它们,就会形成一个大的 XML 文件。
我们还看到,您认为只有一个字段 topmostSubform[0].Page2[0].p2_cb01[0]
,但实际上有 6 个字段:
topmostSubform[0].Page2[0].p2_cb01[0]
topmostSubform[0].Page2[0].p2_cb01[1]
topmostSubform[0].Page2[0].p2_cb01[2]
topmostSubform[0].Page2[0].p2_cb01[3]
topmostSubform[0].Page2[0].p2_cb01[4]
topmostSubform[0].Page2[0].p2_cb01[5]
现在让我们看一下这些字段。
这是字段 topmostSubform[0].Page2[0].p2_cb01[0]
:
这是字段 topmostSubform[0].Page2[0].p2_cb01[0]
:
这些是 AcroForm 复选框,但有一条针对人类的说明说:select 只有一个。这条指令只能被人理解,机器和软件都不能理解。
我第一次尝试编写 FillHybridForm 示例失败,因为我犯了与您类似的错误。我没有仔细观察不同的外观状态。我以为topmostSubform[0].Page2[0].p2_cb01[0]
的On值为0
,topmostSubform[0].Page2[0].p2_cb01[1]
的值为1
,依此类推。它不是... topmostSubform[0].Page2[0].p2_cb01[0]
的 On 值为 1
,topmostSubform[0].Page2[0].p2_cb01[1]
的值为 2
,依此类推。
这是填写所有复选框的方法:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
form.removeXfa();
form.setField("topmostSubform[0].Page2[0].p2_cb01[0]", "1");
form.setField("topmostSubform[0].Page2[0].p2_cb01[1]", "2");
form.setField("topmostSubform[0].Page2[0].p2_cb01[2]", "3");
form.setField("topmostSubform[0].Page2[0].p2_cb01[3]", "4");
form.setField("topmostSubform[0].Page2[0].p2_cb01[4]", "5");
form.setField("topmostSubform[0].Page2[0].p2_cb01[5]", "6");
stamper.close();
reader.close();
}
现在所有复选框都已选中。见 f8966_filled.pdf:
当然:作为人类,我们知道我们不应该这样做,因为我们应该将字段视为单选按钮,但没有技术原因在 AcroForm 描述中我们为什么不能。阻止我们这样做的逻辑只存在于 XFA 描述中。
如果可以丢弃 XFA 部分,这将解决您的问题。如果可以将表格展平,它也将解决您的问题,在这种情况下您应该添加:
stamper.setFormFlattening(true);
如果您不接受上述选项,您不应该丢弃 XFA 部分,而是按照上述方法填写 AcroForm 部分 和 使用 iText 提取XML 数据集(参见第一个屏幕截图中的 datasets
),按照美国政府希望您更新它的方式对其进行更新,并使用 iText 将更新数据集放回 datasets
对象中.
呸...这是我在 Whosebug 上写过的最长的答案之一。