ZugferdInfoExt.cs
//
// This code is part of GrapeCity Documents for PDF samples.
// Copyright (c) GrapeCity, Inc. All rights reserved.
//
using System;
using System.IO;
using System.Drawing;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using s2industries.ZUGFeRD;

namespace GcPdfWeb.Samples
{
    // 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
    {
        public void CreatePDF(Stream stream)
        {
            // The sample invoice with ZUGFeRD data:
            var invoicePdf = Path.Combine("Resources", "PDFs", "zugferd-invoice.pdf");

            // Output document:
            var doc = new GcPdfDocument();
            using (FileStream fs = File.OpenRead(invoicePdf))
            {
                // Load the ZUGFeRD compliant invoice PDF:
                var invoice = new GcPdfDocument();
                invoice.Load(fs);

                // Get the ZUGFeRD attachment from the invoice by the ZUGFeRD 1.x standard file name:
                var attachment = invoice.EmbeddedFiles.Values.FirstOrDefault(it => it.File.FileName == "ZUGFeRD-invoice.xml");
                if (attachment != null)
                {
                    using (var xmlstream = attachment.GetStream())
                    {
                        // Load the invoice descriptor:
                        var descriptor = InvoiceDescriptor.Load(xmlstream);

                        var 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);
                        TextSplitOptions to = new TextSplitOptions(tl);
                        while (true)
                        {
                            var splitResult = tl.Split(to, out TextLayout rest);
                            doc.Pages.Add().Graphics.DrawTextLayout(tl, PointF.Empty);
                            if (splitResult != SplitResult.Split)
                                break;
                            tl = rest;
                        }
                        // Done:
                        doc.Save(stream);
                    }
                }
            }
        }

        // Recursively print all source's properties to text layout:
        private static void RenderProperties(object source, TextLayout tl, int level)
        {
            if (source == null)
                return;

            var props = source.GetType().GetProperties();
            foreach (var prop in props)
                RenderProperty(prop, source, tl, level);
        }

        // Text formats for output:
        private static readonly TextFormat _tfData = new TextFormat() {
            Font = Font.FromFile(Path.Combine("Resources", "Fonts", "segoeui.ttf")),
            FontSize = 12
        };
        private static readonly TextFormat _tfStrong = new TextFormat(_tfData) {
            Font = Font.FromFile(Path.Combine("Resources", "Fonts", "segoeuib.ttf"))
        };
        private static readonly TextFormat _tfLabels = new TextFormat(_tfData) {
            Font = Font.FromFile(Path.Combine("Resources", "Fonts", "segoeuii.ttf")),
            FontSize = 11
        };

        // Print a property:
        private static void RenderProperty(PropertyInfo property, object parent, TextLayout tl, int level)
        {
            var space = string.Empty;
            for (int i = 0; i < level; ++i)
                space += "\t";
            var name = property.Name;
            var value = property.GetValue(parent, null);
            if (value == null)
            {
                tl.Append(space, _tfLabels);
                tl.AppendLine($"{name}:", _tfLabels);
            }
            else if (value.GetType().IsValueType || value is string)
            {
                tl.Append(space, _tfLabels);
                tl.Append($"{name}: ", _tfLabels);
                tl.AppendLine($"{value}", _tfData);
            }
            else
            {
                if (value is IEnumerable<object> collection)
                {
                    var index = 0;
                    foreach (var item in collection)
                    {
                        tl.Append(space, _tfLabels);
                        tl.AppendLine($"Collection item {name} [{index++}]:", _tfStrong);
                        RenderProperties(item, tl, level + 1);
                    }
                }
                else
                {
                    tl.Append(space, _tfLabels);
                    tl.AppendLine($"Container {name}:", _tfStrong);
                    RenderProperties(value, tl, level + 1);
                }
            }
        }
    }
}