ZugferdInfoExt.vb
''
'' This code is part of Document Solutions for PDF demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Linq
Imports System.Reflection
Imports System.Collections.Generic
Imports System.Globalization
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports s2industries.ZUGFeRD
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing

'' This sample demonstrates how to retrieve invoice data from a ZUGFeRD compliant XML attachment.
'' All ZUGFeRD data is printed to the PDF generated by this sample.
'' See ZugferdInfo for a similar sample that prints selected portions of the ZUGFeRD data.

'' The sample PDF invoice containing ZUGFeRD data which this sample uses as input
'' was generated by ZugferdInvoice.
''
'' ZUGFeRD is a German e-invoicing standard based around PDF and XML file formats.
'' Its poised to change the way invoices are handled and can be used by any sort of business.
'' It will make invoice processing more efficient for senders and customers.
'' For details please see What is ZUGFeRD?.
''
'' This sample uses the ZUGFeRD-csharp package
'' to parse the ZUGFeRD-compatible XML that is attached to the invoice.
Public Class ZugferdInfoExt
    Function CreatePDF(ByVal stream As Stream) As Integer
        '' The sample invoice with ZUGFeRD data:
        Dim invoicePdf = Path.Combine("Resources", "PDFs", "zugferd-invoice.pdf")

        '' Output document:
        Dim doc = New GcPdfDocument()
        Using fs = File.OpenRead(invoicePdf)
            '' Load the ZUGFeRD compliant invoice PDF:
            Dim invoice = New GcPdfDocument()
            invoice.Load(fs)

            '' Get the ZUGFeRD attachment from the invoice by the ZUGFeRD 1.x standard file name:
            Dim attachment = invoice.EmbeddedFiles.Values.FirstOrDefault(Function(it) it.File.FileName = "ZUGFeRD-invoice.xml")
            If attachment IsNot Nothing Then
                Using xmlstream = attachment.GetStream()
                    '' Load the invoice descriptor:
                    Dim descriptor = InvoiceDescriptor.Load(xmlstream)

                    Dim tl = New TextLayout(72)
                    tl.MaxWidth = doc.PageSize.Width
                    tl.MaxHeight = doc.PageSize.Height
                    tl.MarginAll = tl.Resolution
                    tl.DefaultTabStops = 24

                    '' Recursively render all InvoiceDescriptor properties to text layout:
                    tl.AppendLine($"{NameOf(InvoiceDescriptor)}:", _tfStrong)
                    RenderProperties(descriptor, tl, 1)

                    '' Write text layout to output PDF
                    tl.PerformLayout(True)
                    Dim topt = New TextSplitOptions(tl)
                    While (True)
                        Dim rest As TextLayout = Nothing
                        Dim splitResult = tl.Split(topt, rest)
                        doc.Pages.Add().Graphics.DrawTextLayout(tl, PointF.Empty)
                        If splitResult <> SplitResult.Split Then
                            Exit While
                        End If
                        tl = rest
                    End While
                    '' Done:
                    doc.Save(stream)
                    Return doc.Pages.Count
                End Using
            Else
                Return 0
            End If
        End Using
    End Function

    '' Recursively print all source's properties to text layout:
    Shared Sub RenderProperties(ByVal source As Object, ByVal tl As TextLayout, ByVal level As Integer)
        If source Is Nothing Then
            Return
        End If
        Dim props = source.GetType().GetProperties()
        For Each prop In props
            RenderProperty(prop, source, tl, level)
        Next
    End Sub

    '' Text formats for output:
    Private Shared ReadOnly _tfData = New TextFormat() With {
        .Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "segoeui.ttf")),
        .FontSize = 12
    }
    Private Shared ReadOnly _tfStrong = New TextFormat(_tfData) With {
        .Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "segoeuib.ttf"))
    }
    Private Shared ReadOnly _tfLabels = New TextFormat(_tfData) With {
        .Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "segoeuii.ttf")),
        .FontSize = 11
    }

    '' Print a property:
    Shared Sub RenderProperty(ByVal prop As PropertyInfo, ByVal parent As Object, ByVal tl As TextLayout, ByVal level As Integer)
        Dim space = String.Empty
        For i = 0 To level - 1
            space += vbTab
        Next
        Dim name = prop.Name
        Dim value = prop.GetValue(parent, Nothing)
        If value Is Nothing Then
            tl.Append(space, _tfLabels)
            tl.AppendLine($"{name}:", _tfLabels)
        ElseIf (value.GetType().IsValueType OrElse value.GetType() Is GetType(String)) Then
            tl.Append(space, _tfLabels)
            tl.Append($"{name}: ", _tfLabels)
            tl.AppendLine(String.Format(CultureInfo.GetCultureInfo("en-US"), "{0}", value), _tfData)
        Else
            If TypeOf value Is IEnumerable(Of Object) Then
                Dim collection As IEnumerable(Of Object) = CType(value, IEnumerable(Of Object))
                Dim index = 0
                For Each item In collection
                    tl.Append(space, _tfLabels)
                    tl.AppendLine($"Collection item {name} [{index}]:", _tfStrong)
                    index += 1
                    RenderProperties(item, tl, level + 1)
                Next
            Else
                tl.Append(space, _tfLabels)
                tl.AppendLine($"Container {name}:", _tfStrong)
                RenderProperties(value, tl, level + 1)
            End If
        End If
    End Sub
End Class