如何使用 iTextSharp 正确填写 XFA 表单数据以允许在 Acrobat XI 中编辑和保存结果
Posted
技术标签:
【中文标题】如何使用 iTextSharp 正确填写 XFA 表单数据以允许在 Acrobat XI 中编辑和保存结果【英文标题】:How to correctly fill in XFA form data using iTextSharp to allow editing and saving result in 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,然后将其保存到文件中。
当您在 Adobe 中打开文件时,您会看到正确填写的表单数据。但是,如果您随后从 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*/
【问题讨论】:
【参考方案1】:不,你没有正确使用PdfStamper
。
阅读器启用是使用数字签名实现的(它需要来自 Adobe 的私钥)。当您使用“标准方式”填写表格时,您会破坏该签名。您需要在追加模式下填写表格。
我在我的书的第 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
标记之后,并且 Adobe 应用的数字签名不会被破坏。
【讨论】:
【参考方案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
【讨论】:
以上是关于如何使用 iTextSharp 正确填写 XFA 表单数据以允许在 Acrobat XI 中编辑和保存结果的主要内容,如果未能解决你的问题,请参考以下文章
如何在 iTextSharp 中填写 PDF 表单并支持多种语言?
不正确的字符串值:第 1 行的列 'VARIABLE_VALUE' 的 '\xD6\xD0\xB9\xFA\xB1\xEA...' [重复]