1

我有一个本地 PDF 表单,它有一个永远不会改变的特定模板。我已将该表单标识为 XFA (xml) 动态表单,因为没有返回任何键集。我正在尝试使用 itext 使用 .txt 文件中包含的数据填写表单。据我了解,我需要以某种方式从文本文件中获取数据并将其正确放入 .xml 文件中,以便 itext 可以使用给定的 xml 操作原始 PDF。

该表单具有以下布局示例:

例子

我在 Eclipse 中使用的示例代码编译/运行成功,但它需要文件data.xml中的数据才能用字段数据填充空表单并输出填充的版本。问题是,对于我的实际项目,我没有用于正确填充表单的 data.xml 文件。原始字段数据位于 .txt 文件中,每行包含 PDF 中不同字段的数据。

示例:参考上面的图片,我的 .txt 文件看起来像这样,直到并包括标记为“FOUR”的字段:

  • 约翰
  • 15
  • 黑色的
  • 本田
  • 丰田
  • 福特
  • 宝马

我对两件事感到困惑:

1.如何提取原始 PDF 的 xml 结构,以便在使用 .txt 文件中的数据填充它时知道要遵循的格式?

2.如何从文本文件中获取值并将它们正确插入到 .xml 结构中?

以下代码有效,但需要data.xml填写“incomplete.pdf”。它使用代码xfa.fillXfaForm(new FileInputStream(XML));输入数据,但我一直坚持如何识别“XML”的结构以及如何首先填写它。

任何帮助表示赞赏,非常感谢。

代码:

package sandbox;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


import java.io.PrintStream;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.XfaForm;


public class FillXFA {

    public static final String SRC = "C:/Workspace/PDF/incomplete.pdf";
    public static final String XML = "C:/Workspace/PDF/data.xml";
    public static final String DEST = "C:/Workspace/PDF/completed.pdf";

    public static void main(String[] args) throws IOException, DocumentException {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new FillXFA().manipulatePdf(SRC, DEST);
    }

    public void readXfa(String src, String dest)
            throws IOException, ParserConfigurationException, SAXException,
                TransformerFactoryConfigurationError, TransformerException {
            FileOutputStream os = new FileOutputStream(dest);
            PdfReader reader = new PdfReader(src);
            XfaForm xfa = new XfaForm(reader);
            Document doc = xfa.getDomDocument();
            Transformer tf = TransformerFactory.newInstance().newTransformer();
            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            tf.setOutputProperty(OutputKeys.INDENT, "yes");
            tf.transform(new DOMSource(doc), new StreamResult(os));
            reader.close();
        }

    public void manipulatePdf(String src, String dest)
        throws IOException, DocumentException {
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader,
                new FileOutputStream(dest));
        AcroFields form = stamper.getAcroFields();
        XfaForm xfa = form.getXfa();
        xfa.fillXfaForm(new FileInputStream(XML));
        stamper.close();
        reader.close();
    }
}
4

1 回答 1

1

在 XFA 中,表单字段和表单数据之间的链接是使用称为数据绑定的概念建立的。字段可以有一个类似 XPath 的表达式来从 XML 数据结构中选择它们的值。这意味着需要对 XML 数据进行适当的结构化以适用于特定的 XFA 表单,但这种结构不一定是唯一的。

一个简单的例子:假设您有一个只有 1 个文本字段的 XFA 表单。此文本字段具有与标记名称为 "Name" 的任何 XML 元素的数据绑定。在这种情况下,您的 data.xml 可以简单地是:

<Name>Hurmle</Name>

但这以及无数种不同的 XML 结构也将起作用:

<StackOverflow>
    <accounts>
        <account>
            <Name>Hurmle</Name>
        </account>
    </accounts>
</StackOverflow>

您的代码示例中的readXfa方法将用于从 XFA 表单中提取完整的 XML 流。它由不同的部分组成。最相关的是:

  • 模板:描述逻辑表单结构,包括所有字段及其数据绑定。
  • xfa:datasets:保存有关数据的信息。由 2 个部分组成。
    • dataDescription:表单数据的架构,可选。数据描述语法在 XFA 规范中定义。
    • xfa:data:表单数据。

确定哪种 XML 结构有效的一种方法是查看所有字段的数据绑定(参见模板)。因此,您将知道字段期望从何处获取数据。对于非平凡的形式,这可能很复杂和/或需要大量工作。

如果在 XFA 表单中可用,您可以使用dataDescription。它将为您提供数据和信息的结构,例如元素的最小和最大出现次数。

最后,您可以查看表单中已经存在的数据(参见xfa:data)。请记住,此 XML 结构不一定是完整的:可以省略空元素。例如,如果表单有 2 个字段,则可以将值指定为:

<SomeRoot>
    <Field1>Value1</Field1>
    <Field2></Field2>
</SomeRoot>

但是也:

<SomeRoot>
    <Field1>Value1</Field1>
</SomeRoot>

第一种情况会让您更容易找出所需的结构。如果xfa:data丢失或不完整,您可以尝试使用支持 XFA 的 PDF 查看器手动填写所有表单字段。保存时,查看器将根据数据描述和数据绑定填充xfa:data 。

供参考:XFA 规范

于 2015-10-13T08:35:41.147 回答