FormSubmitXml.vb
''
'' This code is part of GrapeCity Documents for PDF samples.
'' Copyright (c) GrapeCity, Inc. All rights reserved.
''
Imports System.Drawing
Imports System.IO
Imports System.Linq
Imports System.Xml
Imports System.Text
Imports System.Collections.Generic
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Pdf.AcroForms
Imports GrapeCity.Documents.Pdf.Actions
Imports GrapeCity.Documents.Pdf.Annotations
Imports GrapeCity.Documents.Text

'' This sample creates an AcroForm PDF that can be submitted to the server.
'' It relies on the server to put the submitted data into an XML,
'' import that XML into a PDF containing a similar form,
'' And send the form with loaded data back to the client.
'' Note that the produced PDF with filled form fields
'' Is shown in the client browser's default PDF viewer.
'' The code Is similar to FormSubmit.
Public Class FormSubmitXml
    Function CreatePDF(ByVal stream As Stream) As Integer
        Dim doc = New GcPdfDocument()
        Dim page = doc.NewPage()

        Dim rc = Util.AddNote(
                "Fill the fields in the form and click 'Submit' to send it back to the server. " +
                "The sample server will put the submitted data into an XML, feed that XML " +
                "to a PDF with a different but compatible form, and send the resulting form " +
                "filled with the submitted data back to your browser. " +
                "Note that the form with the submitted data is opened in the browser's default PDF viewer, " +
                "and does not have the 'Submit' and 'Reset' buttons.",
                page)

        Dim g = page.Graphics
        Dim tf = New TextFormat() With {.Font = StandardFonts.Times, .FontSize = 14}
        Dim ip = New PointF(72, rc.Bottom + 36)
        Dim fldOffset = 72 * 2 + 46
        Dim fldHeight = tf.FontSize * 1.2F
        Dim dY = 32

        '' Text field
        g.DrawString("First name:", tf, ip)
        Dim fldFirstName = New TextField() With {.Name = "FirstName", .Value = "John"}
        fldFirstName.Widget.Page = page
        fldFirstName.Widget.Rect = New RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight)
        fldFirstName.Widget.TextFormat.Font = tf.Font
        fldFirstName.Widget.TextFormat.FontSize = tf.FontSize
        doc.AcroForm.Fields.Add(fldFirstName)
        ip.Y += dY

        '' Text field
        g.DrawString("Last name:", tf, ip)
        Dim fldLastName = New TextField() With {.Name = "LastName", .Value = "Smith"}
        fldLastName.Widget.Page = page
        fldLastName.Widget.Rect = New RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight)
        fldLastName.Widget.TextFormat.Font = tf.Font
        fldLastName.Widget.TextFormat.FontSize = tf.FontSize
        doc.AcroForm.Fields.Add(fldLastName)
        ip.Y += dY

        '' Checkbox
        g.DrawString("Subscribe to Mailing List:", tf, ip)
        Dim fldCheckbox = New CheckBoxField() With {.Name = "Subscribe", .Value = True}
        fldCheckbox.Widget.Page = page
        fldCheckbox.Widget.Rect = New RectangleF(ip.X + fldOffset, ip.Y, fldHeight, fldHeight)
        doc.AcroForm.Fields.Add(fldCheckbox)
        ip.Y += dY

        '' Multiline TextBox
        g.DrawString("Additional information:", tf, ip)
        Dim fldAdditionalInfo = New TextField() With {.Name = "AdditionalInfo", .Multiline = True}
        fldAdditionalInfo.Widget.Page = page
        fldAdditionalInfo.Widget.Rect = New RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight * 2)
        fldAdditionalInfo.Widget.TextFormat.Font = tf.Font
        fldAdditionalInfo.Widget.TextFormat.FontSize = tf.FontSize
        doc.AcroForm.Fields.Add(fldAdditionalInfo)
        ip.Y += dY * 2

        '' Submit form button:
        Dim btnSubmit = New PushButtonField()
        btnSubmit.Widget.Rect = New RectangleF(ip.X + fldOffset, ip.Y, 72, fldHeight)
        btnSubmit.Widget.ButtonAppearance.Caption = "Submit"
        btnSubmit.Widget.Highlighting = HighlightingMode.Invert
        btnSubmit.Widget.Page = page

        '' The URL for the submission
        btnSubmit.Widget.Events.Activate = New ActionSubmitForm("/Samples/HandleFormSubmitXml")
        doc.AcroForm.Fields.Add(btnSubmit)

        '' Reset form button:
        Dim btnReset = New PushButtonField()
        btnReset.Widget.Rect = New RectangleF(ip.X + fldOffset + 72 * 1.5F, ip.Y, 72, fldHeight)
        btnReset.Widget.ButtonAppearance.Caption = "Reset"
        btnReset.Widget.Highlighting = HighlightingMode.Invert
        btnReset.Widget.Page = page
        btnReset.Widget.Events.Activate = New ActionResetForm()
        doc.AcroForm.Fields.Add(btnReset)
        ip.Y += dY

        '' Done
        doc.Save(stream)
        Return doc.Pages.Count
    End Function

    ''
    '' NOTE: the code below Is used by the web sample browser controller When the form
    '' prepared by this sample Is submitted, it Is Not directly called by the CreatePDF() method.
    ''

    '' Creates a GcPdfDocument, loads an AcroForm PDF into it, And fills it with data
    '' using the GcPdfDocument.ImportFormDataFromXML() method.
    ''
    '' This method Is called by the samples controller when the form prepared by this sample
    '' Is submitted by the user. The samples controller parses the client response And builds
    '' the 'values' collection filling it with the submitted field values, then calls
    '' this method to prepare the XML, imports it into a newly created PDF, And returns
    '' the resulting PDF to the controller, which sends it back to the client.
    Public Shared Function ImportFormData(ByVal values As List(Of FieldExportEntry)) As Stream
        Dim pdf = New GcPdfDocument()
        Using fs = New FileStream(Path.Combine("Resources", "PDFs", "ImportFormXML.pdf"), FileMode.Open, FileAccess.Read)
            pdf.Load(fs)
            Using ms = New MemoryStream()
                SaveFieldsToXML(values, ms)
                ms.Seek(0, SeekOrigin.Begin)
                pdf.ImportFormDataFromXML(ms)
            End Using
            Dim outMs = New MemoryStream()
            pdf.Save(outMs)
            outMs.Seek(0, SeekOrigin.Begin)
            Return outMs
        End Using
    End Function

    '' Represents a form field And its value(s) for export to XML.
    Public Class FieldExportEntry
        Public Property Name As String
        Public Property Values As List(Of String)
        '' Note: this sample does Not support child fields:
        '' public List<FieldTreeNode> Children { get set }
    End Class

    '' Saves the fields And their values to a stream.
    ''
    '' This method Is similar to GcPdfDocument.ExportFormDataToXML(), with the following
    '' imporant limitations:
    '' - it does Not support child fields (field.Children collection)
    '' - it does Not handle fields with names that are Not valid XML names (xfdf:original).
    Public Shared Sub SaveFieldsToXML(ByVal values As List(Of FieldExportEntry), ByVal stream As Stream)
        Dim xws = New XmlWriterSettings() With
            {
                .Indent = True,
                .CloseOutput = False,
                .Encoding = Encoding.UTF8
            }
        Using xw = XmlWriter.Create(stream, xws)
            xw.WriteStartElement("fields")
            xw.WriteAttributeString("xmlns", "xfdf", Nothing, "http:''ns.adobe.com/xfdf-transition/")
            For Each ftn In values
                xw.WriteStartElement(ftn.Name)
                For Each v In ftn.Values
                    xw.WriteStartElement("value")
                    '' NOTE: the values In the array are formed by the client PDF viewer,
                    '' And it represents 'on' checkbox values as 'true', while ImportFormDataFromXML
                    '' expects 'on' values to be represented as "Yes" (that's how ExportFormDataToXML
                    '' works, similar to Acrobat). This here Is a quick And dirty hack just for the
                    '' sake of this sample:
                    If v = "true" Then
                        xw.WriteString("Yes")
                    Else
                        xw.WriteString(v)
                    End If
                    xw.WriteEndElement()
                Next
                xw.WriteEndElement()
            Next
            xw.WriteEndElement()
        End Using
    End Sub
End Class