【问题标题】:How to correctly fill in XFA form data using iTextSharp to allow editing and saving result in Acrobat XI如何使用 iTextSharp 正确填写 XFA 表单数据以允许在 Acrobat XI 中编辑和保存结果
【发布时间】:2014-02-21 21:55:41
【问题描述】:

我有一个应用程序,我正在使用 iTextSharp 填充 pdf 表单。

    /// <summary>
    /// Imports XFA Data into a new PDF file.
    /// </summary>
    /// <param name="pdfTemplate">A PDF File with an unpopulated form.</param>
    /// <param name="xmlFormData">XFA form data in XML format.</param>
    /// <returns>a memorystream containing the new PDF file.</returns>
    public static void XFAImport(System.IO.Stream pdfTemplate, System.IO.Stream xmlFormData, System.IO.Stream outputStream)
    {
        using (iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(pdfTemplate))
        {
            using (iTextSharp.text.pdf.PdfStamper stamper = new iTextSharp.text.pdf.PdfStamper(reader, outputStream))
            {
                stamper.Writer.CloseStream = false;
                stamper.AcroFields.Xfa.FillXfaForm(xmlFormData);
            }
        }
    }

上述代码采用未填写的pdf表格和xml数据写入outputStream,然后将其保存到文件中。

当您在 Adob​​e 中打开文件时,您会看到正确填写的表单数据。但是,如果您随后从 Acrobat XI 保存该文件,然后重新打开它,您导入的数据将不再可见。

我不认为问题出在我正在导入的 XML 上,因为如果我不使用 iTextShart,而是使用 Acrobat XI 的“工具/表单/更多表单选项/导入数据”。生成的文件能够正确保存和重新打开。

我的问题是:

我是否正确使用了上面的 PdfStamper?

我可以采取任何步骤来正确保存生成的文件吗?

PS。我注意到,在使用 Acrobat XI 重新保存输出 pdf 文件后,生成的文件与原始文件基本相同,但在末尾插入了额外的 11k 数据。

输出pdf文件结束:

trailer
<</Size 51/Root 14 0 R/Info 3 0 R/ID [<56549fdaf0c5ab4e9321d77f406e6455><5b60738018e0cdac94c6d1b924fc8bed>]>>
%iText-5.4.4
startxref
529008
%%EOF

在 Acrobat XI 中保存后添加了更多数据:

trailer
<</Size 51/Root 14 0 R/Info 3 0 R/ID [<56549fdaf0c5ab4e9321d77f406e6455>         <5b60738018e0cdac94c6d1b924fc8bed>]>>
%iText-5.4.4
startxref
529008
%%EOF
3 0 obj
<</CreationDate(D:20100120124725-05'00')/Creator(Adobe LiveCycle Designer ES 8.2)/ModDate(D:20140221145558-06'00')/Producer(Adobe LiveCycle Designer ES 8.2; modified using iTextSharp’ 5.4.4 ©2000-2013 1T3XT BVBA \(AGPL-version\))>>
endobj
4 0 obj
<</Length 3261/Subtype/XML/Type/Metadata>>stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>

/*more data excluded*/

【问题讨论】:

    标签: c# pdf itextsharp pdf-form xfa


    【解决方案1】:

    不,你没有正确使用PdfStamper

    阅读器启用是使用数字签名实现的(它需要来自 Adob​​e 的私钥)。当您使用“标准方式”填写表格时,您会破坏该签名。您需要在追加模式下填写表格。

    我在我的书的第 8.7.2 节中对此进行了解释,题为“使用 iText 填写支持阅读器的表单”(这让我感到震惊,为什么没有人在提出问题之前阅读文档;有人想知道为什么有人甚至费心写作一本书)。您可以在此处找到本节附带的示例:ReaderEnabledForm

    C#版本可以在对应章节on SourceForge中找到:

    底线:你需要更换

    new iTextSharp.text.pdf.PdfStamper(reader, outputStream)
    

    new iTextSharp.text.pdf.PdfStamper(reader, outputStream, '\0', true)
    

    在这种情况下,您的更改将附加在%%EOF 标记之后,并且 Adob​​e 应用的数字签名不会被破坏。

    【讨论】:

      【解决方案2】:

      感谢您的提示。 这就是我们最终要做的事情(VB.NET):

      Public Shared Sub XFAImport(pdfTemplate As System.IO.Stream, xmlFormData As System.IO.Stream, outputStream As System.IO.Stream)
          ' Imports XFA Data into a new PDF file.
          ' pdfTemplate is PDF File with an unpopulated form
          ' xmlFormData is an XFA form data in XML format (the data we wish to enter)
          ' We get a memorystream containing the new PDF file
      
          Dim reader As New pdf.PdfReader(pdfTemplate)
          PdfReader.unethicalreading = True ' Allow reading a PDF file that is protected by a password
      
          Using reader
              Using stamper As New iTextSharp.text.pdf.PdfStamper(reader, outputStream, "\0", True)
                  stamper.Writer.CloseStream = False
                  stamper.AcroFields.Xfa.FillXfaForm(xmlFormData)
              End Using
          End Using
      End Sub
      
      
      Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
          Dim strErr As String = ""
      
          Dim afto22pdf As String = Server.MapPath("../AFTO22/afto22_protected.pdf")
          Dim newXml As String = Server.MapPath("../AFTO22/newxml1.xml")
          Dim newAfto22pdf As String = Server.MapPath("../AFTO22/newAfto22_protected.pdf")
      
      
          Dim pdfTemplate As New FileStream(afto22pdf, FileMode.Open, FileAccess.Read)
          Dim xmlFormData As New FileStream(newXml, FileMode.Open, FileAccess.Read)
          Dim outputStream As New FileStream(newAfto22pdf, FileMode.Create, FileAccess.Write)
          Try
              XFAImport(pdfTemplate, xmlFormData, outputStream)
          Catch ex As Exception
              strErr = "Error detected: " & ex.Message
          End Try
      
          Label1.Text = strErr.ToString
      
          outputStream.Close()
          pdfTemplate.Close()
          xmlFormData.Close()
          outputStream = Nothing
          pdfTemplate = Nothing
          xmlFormData = Nothing
      End Sub
      

      【讨论】:

        【解决方案3】:

        实际上,上面的代码在我们以编程方式填充其中一部分之后,需要手动在表单中输入数据的人造成了一些问题。我们的 XFA 表单有大约 10 个步骤,我们只填充前 2 个步骤。尝试对后续步骤进行数字签名的人看到一条错误消息,指出“dataModel 没有方法‘克隆’。” 无论如何,我们最终直接填充了表单字段,而无需使用外部 XML。这解决了我们的问题。

        Try
           Dim filename As String = Server.MapPath("../AFTO22/Afto22_populated.pdf")
           Dim pdfReader As New PdfReader(Server.MapPath("~/AFTO22/afto22.pdf"))
           pdfReader.unethicalreading = True
        
           Using stream As New FileStream(filename, FileMode.Create)
              Dim pdfStamper As New PdfStamper(pdfReader, stream, "\0", True)
              Dim formFields As AcroFields = pdfStamper.AcroFields
              formFields.SetField("FIELD1", "My Name")
              formFields.SetField("FIELD5", "My Rank")
        
              pdfStamper.FormFlattening = False
              pdfStamper.Close()
           End Using
        Catch ex As Exception
           Label1.Text = ex.Message
        End Try

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-01-19
          • 1970-01-01
          • 2015-03-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多