Как правильно заполнить данные формы XFA с помощью iTextSharp, чтобы разрешить редактирование и сохранение результата в Acrobat XI

У меня есть приложение, которое я использую для заполнения формы PDF с помощью 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);
            }
        }
    }

Приведенный выше код принимает незаполненную форму pdf и данные xml и записывает в outputStream, который затем сохраняется в файл.

Когда вы открываете файл в Adobe, вы видите, что данные формы заполнены правильно. Однако если вы затем сохраните этот файл из Acrobat XI, а затем снова откроете его, импортированные данные больше не будут видны.

Я не думаю, что проблема связана с XML, который я импортирую, потому что, если вместо использования iTextShart я вместо этого использую Acrobat XI «инструменты/форма/дополнительные параметры формы/импорт данных». Полученный файл можно сохранить и снова открыть корректно.

Мой вопрос:

Правильно ли я использую PdfStamper выше?

Есть ли какие-либо шаги, которые я могу предпринять, чтобы правильно сохранить полученный файл?

PS. Я заметил, что после повторного сохранения выходного pdf-файла с помощью Acrobat XI результирующий файл в основном идентичен оригиналу, однако в конце вставлены дополнительные 11 КБ данных.

конец выходного 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*/

person eoldre    schedule 21.02.2014    source источник


Ответы (3)


Нет, вы неправильно используете PdfStamper.

Включение считывателя осуществляется с помощью цифровой подписи (для этого требуется закрытый ключ от Adobe). Когда вы заполняете форму «стандартным способом», вы ломаете эту подпись. Вам необходимо заполнить форму в режиме добавления.

Я объяснил это в разделе 8.7.2 моей книги, озаглавленной «Заполнение форм с поддержкой Reader с помощью iText» (поражаюсь, почему никто никогда не читает документацию перед тем, как задать вопрос; интересно, почему кто-то вообще утруждает себя написанием книги) . Вы можете найти пример, прилагаемый к этому разделу, здесь: ReaderEnabledForm

Вы можете найти версию C# в соответствующей главе на SourceForge:

Итог: нужно заменить

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

с участием

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

В этом случае ваши изменения будут добавлены после маркера %%EOF, а цифровая подпись, примененная Adobe, не будет нарушена.

person Bruno Lowagie    schedule 22.02.2014

Спасибо за советы. Вот что мы в итоге сделали (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
person Alex Calder    schedule 01.08.2017

На самом деле, приведенный выше код вызывал некоторые проблемы у людей, которым нужно было вводить данные в форму вручную ПОСЛЕ того, как мы заполнили ее часть программно. Наша форма XFA содержит около 10 шагов, из которых мы заполняем только первые 2 шага. Люди, пытающиеся поставить цифровую подпись на более поздних этапах, видели сообщение об ошибке, в котором говорилось, что «в модели данных нет метода« клонировать »». Так или иначе, мы закончили тем, что заполнили поля формы напрямую, пропустив необходимость использования внешнего 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

person Alex Calder    schedule 24.08.2017