问题
I have an application that I'm using the populate a pdf form using iTextSharp.
/// <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);
}
}
}
The above code takes a pdf form that is not filled out and xml data and writes to the outputStream which is then saved to a file.
When you open the file in Adobe you see the form data filled out correctly. However if you then save that file from Acrobat XI, and then reopen it, the data that you imported is no longer visible.
I don't believe the problem is with the XML that I am importing, because if instead of using iTextShart, I instead use Acrobat XI's "tools/form/more form options/import data". The resulting file is able to be saved and reopened correctly.
My question is:
Am I using the PdfStamper correctly above?
Are there any steps I can take to make the resulting file save correctly?
PS. I've noticed that after resaving the output pdf file using Acrobat XI that the resulting file is mostly identical to the original, however it has an additional 11k of data inserted at the end.
end of the output pdf file:
trailer
<</Size 51/Root 14 0 R/Info 3 0 R/ID [<56549fdaf0c5ab4e9321d77f406e6455><5b60738018e0cdac94c6d1b924fc8bed>]>>
%iText-5.4.4
startxref
529008
%%EOF
After saving in Acrobat XI is has more data added to it:
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:
No, you are not using PdfStamper
correctly.
Reader enabling is achieved using a digital signature (it requires a private key from Adobe). When you fill out the form using the "standard way", you break that signature. You need to fill out the form in append mode.
I've explained this in section 8.7.2 of my book, entitled "Filling out Reader-enabled forms using iText" (it beats me why nobody ever reads the documentation before asking a question; one wonders why one even bothers writing a book). You can find the example that comes with this section here: ReaderEnabledForm
You can find the C# version in of the corresponding chapter on SourceForge:
Bottom line: you need to replace
new iTextSharp.text.pdf.PdfStamper(reader, outputStream)
with
new iTextSharp.text.pdf.PdfStamper(reader, outputStream, '\0', true)
In this case, your changes will be appended after the %%EOF
marker and the digital signature applied by Adobe won't be broken.
回答2:
Thanks for the tips. This is what we ended up doing (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:
Actually, the code above caused some problems for people who needed to enter data in the form manually AFTER we populated part of it programmatically. Our XFA form has some 10 steps, where we populate only the first 2 steps. People trying to digitally sign later steps saw an error message, stating "dataModel does not have a method 'clone'." Anyhow, we ended up populating the form fields directly, skipping the need to use an external XML. This solved our problems.
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
来源:https://stackoverflow.com/questions/21945972/how-to-correctly-fill-in-xfa-form-data-using-itextsharp-to-allow-editing-and-sav