【问题标题】:How to use itext to fill out (dynamic XFA) PDF from data in a text file如何使用itext从文本文件中的数据填写(动态XFA)PDF
【发布时间】:2016-01-10 09:13:52
【问题描述】:

我有一个本地 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();
    }
}

【问题讨论】:

  • 编写代码,获取您的文本文件并使其成为像 data.xml 这样的 XML 文件
  • 我对这种方法有些犹豫,因为我之前操作过一个 .xml 文件......它可以很好地导入我的 PDF,但是当某家公司试图将它上传到他们的系统时行不通。让我相信我以某种方式“破坏”了 XML 结构,可能是使用了不合适的字符或其他东西。我希望 itext 允许我完全遵守原始的 xml 结构,同时仍然在 PDF 中正确填充数据字段。任何关于如何继续的反馈或来源都很棒,谢谢。
  • 从命令中可以看出,它需要一个参数。 xml。您可以确保它是有效的 XML
  • 谢谢凯文。那么我的问题是,如何确保它是有效的 XML?正如我之前的评论所述,我手动操作了一个 XML 文件并成功将它导入到 PDF 表单中。然而,当这家公司将其上传到他们的系统时,它被认为是损坏的。尽管它已成功手动导入 PDF,但我有没有办法验证它是否“损坏”或错误的 xml 结构?也许通过一行代码或在线服务?谢谢

标签: xml forms pdf itext xfa


【解决方案1】:

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

一个简单的例子: 假设您有一个只有 1 个文本字段的 XFA 表单。此文本字段与任何带有标签名称“名称”的 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 specification

【讨论】:

  • 感谢 rhens 的详细反馈。听起来我可以使用的一种策略是完全手动填写 XFA 表格。然后我可以将其导出为 .xml 并在文本编辑器中阅读以了解数据描述/绑定。我对如何将 .txt 文件中的值放入 .xml 文件感到困惑,这样我就不会“破坏”预定义的数据结构。最近发生了这种情况,其中一家与我的工作相关的公司无法上传 pdf,因为我手动更改了 xml,并且可能出现了轻微的语法错误。我希望 itext 能让我完全遵守预定义的 xml 数据结构?
  • 确保 XML 有效与 XFA 或 iText 无关。您可以使用标准 XML 工具来操作 XML 结构。查看JAXP 了解Java XML 处理。 DOM 可能是最容易理解的。 NodereplaceChild()setNodeValue() 可用于向 XML 文档添加内容。
猜你喜欢
  • 1970-01-01
  • 2014-08-21
  • 1970-01-01
  • 2022-08-02
  • 1970-01-01
  • 2019-05-11
  • 2018-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多