Creating a Purchase Order PDF - Code

Index.razor

@page "/"
@using BlazorPurchaseOrders.Data
@using Microsoft.Extensions.Configuration
@using System.IO;

@inject IPOHeaderService POHeaderService
@inject IPOLineService POLineService
@inject NavigationManager NavigationManager
@inject IJSRuntime IJS
@inject IEmailService EmailService
@inject IEmailSender EmailSender
@inject IConfiguration _config
@inject IControlTableService ControlTableService
@inject CreatePurchaseOrderPdf exportService

@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]

@inject AuthenticationStateProvider AuthenticationStateProvider

<head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
</head>

<div class="col-sm-12">
    <AuthorizeView>
        <NotAuthorized>
            <h5>Please Log in</h5>
            <h6>or</h6>
            <h5>Register to use Blazor Purchase Orders</h5>
        </NotAuthorized>
        <Authorized>
            <h3>Purchase Orders</h3>
            <br />
            <SfGrid DataSource="@poheader"
                    Toolbar="Toolbaritems">
                <GridEvents RowSelected="RowSelectHandler" OnToolbarClick="ToolbarClickHandler" TValue="POHeader"></GridEvents>
                <GridColumns>
                    <GridColumn Field="@nameof(POHeader.POHeaderOrderNumber)"
                                HeaderText="No"
                                TextAlign="@TextAlign.Left"
                                Width="10">
                    </GridColumn>
                    <GridColumn Field="@nameof(POHeader.POHeaderOrderDate)"
                                HeaderText="Date"
                                Format="d"
                                Type="ColumnType.Date"
                                TextAlign="@TextAlign.Center"
                                Width="15">
                    </GridColumn>
                    <GridColumn Field="@nameof(POHeader.SupplierName)"
                                HeaderText="Supplier"
                                TextAlign="@TextAlign.Left"
                                Width="40">
                    </GridColumn>
                    <GridColumn Field="@nameof(POHeader.TotalOrderValue)"
                                HeaderText="Value"
                                TextAlign="@TextAlign.Right"
                                Format="C2"
                                Width="20">
                    </GridColumn>
                    <GridColumn Field="@nameof(POHeader.POHeaderRequestedBy)"
                                HeaderText="Requested by"
                                TextAlign="@TextAlign.Left"
                                Width="40">
                    </GridColumn>
                </GridColumns>
            </SfGrid>

            <WarningPage @ref="Warning" WarningHeaderMessage="@WarningHeaderMessage" WarningContentMessage="@WarningContentMessage" />

            <ConfirmPage @ref="ConfirmOrderDelete" ConfirmHeaderMessage="@ConfirmHeaderMessage" ConfirmContentMessage="@ConfirmContentMessage" ConfirmationChanged="ConfirmOrderArchive" />

        </Authorized>
    </AuthorizeView>
</div>

<div class="col-lg-12 control-section toast-default-section">
    <SfToast @ref="ToastObj" Timeout="4000">
        <ToastPosition X="Right" Y="Top"></ToastPosition>
    </SfToast>
</div>

<style>
    @@font-face {
        font-family: 'Toast_icons';
        src: url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype');
        font-weight: normal;
        font-style: normal;
    }

    #toast_types button {
        margin: 5px;
        min-width: 160px;
        max-width: 160px;
    }

    .toast-icons {
        font-family: 'Toast_icons' !important;
        speak: none;
        font-size: 55px;
        font-style: normal;
        font-weight: normal;
        font-variant: normal;
        text-transform: none;
        line-height: 1;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    #toast_type .e-toast-icon.e-icons {
        height: auto;
        font-size: 30px;
    }

    .toast-icons.e-success::before {
        content: "\e701";
    }

    .toast-icons.e-error::before {
        content: "\e700";
    }

    .toast-icons.e-info::before {
        content: "\e704";
    }

    .toast-icons.e-warning::before {
        content: "\e703";
    }

    #toast_types {
        text-align: center;
    }
</style>

@code {

    // Create an empty list, named poheader, of empty POHeader objects.
    IEnumerable<POHeader> poheader;
    POHeader orderHeader = new POHeader();

    IEnumerable<POLine> orderLinesByPOHeader;
    public List<POLine> orderLines = new List<POLine>();

    private List<ItemModel> Toolbaritems = new List<ItemModel>();

    //int POHeaderID = 0;

    private int selectedPOHeaderID { get; set; } = 0;
    private Guid selectedPOHeaderGuid { get; set; }
    private string selectedPOHeaderSupplierEmail { get; set; } = "";
    private int selectedPOHeaderOrderNumber { get; set; } = 0;

    WarningPage Warning;
    string WarningHeaderMessage = "";
    string WarningContentMessage = "";

    ConfirmPage ConfirmOrderDelete;
    string ConfirmHeaderMessage = "";
    string ConfirmContentMessage = "";
    public bool ConfirmationChanged { get; set; } = false;

    private string emailbody = "";

    [CascadingParameter]
    private Task<AuthenticationState> authenticationStateTask { get; set; }

    SfToast ToastObj;

    string ServerLocation = "";

    ControlTable controltable = new();                                              //NEW
    private string SmtpHost { get; set; } = "";                                     //NEW
    private int SmtpPort { get; set; } = 0;                                         //NEW
    private string SmtpUserFriendlyName { get; set; } = "";                         //NEW
    private string SmtpUserEmailAddress { get; set; } = "";                         //NEW
    private string SmtpPass { get; set; } = "";                                     //NEW
    private string SENDGRID_APIKEY { get; set; } = "";                              //NEW
    private string SenderEmail { get; set; } = "";                                  //NEW
    private string SenderEmailFriendlyName { get; set; } = "";                      //NEW
    private string EmailSendMethod { get; set; } = "";                              //NEW

    private List<ToastModel> Toast = new List<ToastModel>
{
    new ToastModel{ Title = "Warning!", Content="There was a problem with your network connection.", CssClass="e-toast-warning", Icon="e-warning toast-icons" },
    new ToastModel{ Title = "Success!", Content="Your message has been sent successfully.", CssClass="e-toast-success", Icon="e-success toast-icons" },
    new ToastModel{ Title = "Error!", Content="A problem has been occurred while submitting your email", CssClass="e-toast-danger", Icon="e-error toast-icons" },
    new ToastModel{ Title = "Information!", Content="Please read the comments carefully.", CssClass="e-toast-info", Icon="e-info toast-icons" }
    };

    protected override async Task OnInitializedAsync()
    {
        //Populate the list of orders objects from the Purchase Order Header table.
        await GetOrderList();

        Toolbaritems.Add(new ItemModel() { Text = "Add", TooltipText = "Add a new order", PrefixIcon = "e-add" });
        Toolbaritems.Add(new ItemModel() { Text = "Edit", TooltipText = "Edit selected order", PrefixIcon = "e-edit" });
        Toolbaritems.Add(new ItemModel() { Text = "Delete", TooltipText = "Delete selected order", PrefixIcon = "e-delete" });
        Toolbaritems.Add(new ItemModel() { Text = "Preview", TooltipText = "Preview selected order", PrefixIcon = "e-print" });
        Toolbaritems.Add(new ItemModel() { Text = "Email", TooltipText = "Email selected order", PrefixIcon = "bi-envelope" });
        //Toolbaritems.Add(new ItemModel() { Text = "PDF", TooltipText = "PDF Test", PrefixIcon = "e-print" });

        ServerLocation = _config.GetValue<string>("ApplicationSettings:PreviewOrderLinkRoute");

        controltable = await ControlTableService.ControlTable_GetOne();                 //NEW
        SmtpHost = controltable.SmtpHost;                                               //NEW
        SmtpPort = controltable.SmtpPort;                                               //NEW
        SmtpUserFriendlyName = controltable.SmtpUserFriendlyName;                       //NEW
        SmtpUserEmailAddress = controltable.SmtpUserEmailAddress;                       //NEW
        SmtpPass = controltable.SmtpPass;                                               //NEW
        SENDGRID_APIKEY = controltable.SENDGRID_APIKEY;                                 //NEW
        SenderEmail = controltable.SenderEmail;                                         //NEW
        SenderEmailFriendlyName = controltable.SenderEmailFriendlyName;                 //NEW
        EmailSendMethod = controltable.EmailSendMethod;                                 //NEW
    }


    public async Task ToolbarClickHandler(Syncfusion.Blazor.Navigations.ClickEventArgs args)
    {
        if (args.Item.Text == "Add")
        {
            //Code for adding goes here
            //POHeaderID = 0;
            NavigationManager.NavigateTo($"/purchaseorder/{Guid.Empty}");
        }

        if (args.Item.Text == "Edit")
        {
            //Code for editing - Check that an Order has been selected from the grid
            if (selectedPOHeaderID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                NavigationManager.NavigateTo($"/purchaseorder/{selectedPOHeaderGuid}");
            }
        }

        if (args.Item.Text == "Delete")
        {
            //Code for deleting
            if (selectedPOHeaderID == 0)    //Check that an order has been selected
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                //Populate orderHeader using selectedPOHeaderID
                orderHeader = await POHeaderService.POHeader_GetOne(selectedPOHeaderID);

                ConfirmHeaderMessage = "Confirm Deletion";
                ConfirmContentMessage = "Please confirm that this order should be deleted.";
                ConfirmOrderDelete.OpenDialog();
            }
        }

        if (args.Item.Text == "Preview")
        {

            //Code for editing - Check that an Order has been selected from the grid
            if (selectedPOHeaderID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
            }
            else
            {
                await IJS.InvokeAsync<object>("open", new object[] { "/previeworder/" + selectedPOHeaderGuid + "", "_blank" });
            }
        }

        if (args.Item.Text == "Email")
        {
            //Code for editing - Check that an Order has been selected from the grid
            if (selectedPOHeaderID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
                return;
            }

            if (selectedPOHeaderSupplierEmail == null || selectedPOHeaderSupplierEmail == "")
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Supplier Email Address is missing.";
                Warning.OpenDialog();
                return;
            }

            //Populate orderHeader using selectedPOHeaderID
            orderHeader = await POHeaderService.POHeader_GetOne(selectedPOHeaderID);

            orderLinesByPOHeader = await POLineService.POLine_GetByPOHeader(selectedPOHeaderID);
            orderLines = orderLinesByPOHeader.ToList(); //Convert from IEnumerable to List

            string filename = "";

            using (MemoryStream pdfStream = exportService.CreatePdf(orderHeader, orderLines))
            {
                filename = @$"{Environment.CurrentDirectory}/wwwroot/Files/PO-" + selectedPOHeaderOrderNumber + ".pdf";

                FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);

                byte[] pdfData = pdfStream.ToArray();

                fs.Write(pdfData, 0, pdfData.Length);
                fs.Close();
            }

            //Create body of email using html
            emailbody = "<p>Dear Supplier</p>";
            emailbody = emailbody + "<p>Please find attached our order number " + selectedPOHeaderOrderNumber;
            emailbody = emailbody + ". Alternatively click one of the following links (or copy and paste the link into your browser)";
            emailbody = emailbody + "</p>";

            //without the anchor tag
            emailbody = emailbody + "<p>" + ServerLocation + "/previeworder/"
                + selectedPOHeaderGuid + "</p>";

            //with the anchor tag
            emailbody = emailbody + "<p><a href= "" + ServerLocation + "/previeworder/"
                + selectedPOHeaderGuid + "">" + ServerLocation + "/previeworder/"
                + selectedPOHeaderGuid + "<a></p>";

            emailbody = emailbody + "<p>Kind regards</p>";
            emailbody = emailbody + "<p>BlazorCode</p>";

            bool emailSent = false;
            string attachmentName = "PurchaseOrder-" + selectedPOHeaderOrderNumber + ".pdf";

            switch (EmailSendMethod)
            {
                case "SMTP":
                    emailSent = EmailService.Send(SmtpHost,
                    SmtpPort,
                    SmtpUserFriendlyName,
                    SmtpUserEmailAddress,
                    SmtpPass,
                    selectedPOHeaderSupplierEmail,
                    "Purchase Order:" + selectedPOHeaderOrderNumber + ".",
                    emailbody,
                    filename,
                    attachmentName);
                    break;

                case "SendGrid":
                    emailSent = await EmailSender.SendEmail(SENDGRID_APIKEY,
                    SenderEmailFriendlyName,
                    SenderEmail,
                    selectedPOHeaderSupplierEmail,
                    "Purchase Order:" + selectedPOHeaderOrderNumber + ".",
                    emailbody,
                    filename,
                    attachmentName);
                    break;

                default:
                    WarningHeaderMessage = "Warning!";
                    WarningContentMessage = "No Email Method has been setup in Control Table.";
                    Warning.OpenDialog();
                    break;
            }

            //Always delete purchase order pdf
            File.Delete(filename);

            if (emailSent)
            {
                await this.ToastObj.Show(Toast[1]);
            }
            else
            {
                await this.ToastObj.Show(Toast[2]);
            }
        }



        if (args.Item.Text == "PDF")
        {
            //Code for editing - Check that an Order has been selected from the grid
            if (selectedPOHeaderID == 0)
            {
                WarningHeaderMessage = "Warning!";
                WarningContentMessage = "Please select an Order from the grid.";
                Warning.OpenDialog();
                return;
            }

            //Populate orderHeader using selectedPOHeaderID
            orderHeader = await POHeaderService.POHeader_GetOne(selectedPOHeaderID);

            //Populate orderLines using selectedPOHeaderID
            orderLinesByPOHeader = await POLineService.POLine_GetByPOHeader(selectedPOHeaderID);
            orderLines = orderLinesByPOHeader.ToList(); //Convert from IEnumerable to List

            //Pass orderHeader and orderLines to exportservice.CreatePdf and get back a MemoryStream of the pdf document
            using (MemoryStream pdfStream = exportService.CreatePdf(orderHeader, orderLines))
            {
                //Get current directory and concatenate with 'Files' folder and selected Order Number to create pdf file name
                string filename = @$"{Environment.CurrentDirectory}/wwwroot/Files/PO-" + selectedPOHeaderOrderNumber + ".pdf";

                //Define a FileStream object
                FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);

                //Convert MemoryStream to a byte array
                byte[] pdfData = pdfStream.ToArray();

                //Write the byte array to the FileStream (Disc) and close the Filestream
                fs.Write(pdfData, 0, pdfData.Length);
                fs.Close();
            }
        }
    }

    public void RowSelectHandler(RowSelectEventArgs<POHeader> args)
    {
        //{args.Data} returns the current selected records.
        selectedPOHeaderID = args.Data.POHeaderID;
        selectedPOHeaderGuid = args.Data.POHeaderGuid;
        selectedPOHeaderSupplierEmail = args.Data.POHeaderSupplierEmail;
        selectedPOHeaderOrderNumber = args.Data.POHeaderOrderNumber;
    }

    protected async Task ConfirmOrderArchive(bool archiveConfirmed)
    {
        if (archiveConfirmed)
        {
            orderHeader.POHeaderIsArchived = true;
            bool Success = await POHeaderService.POHeaderUpdate(orderHeader);
            await GetOrderList();
            StateHasChanged();
        }
    }

    protected async Task GetOrderList()
    {
        var user = (await authenticationStateTask).User;


        if (user.IsInRole("Admin") || user.IsInRole("Manager"))
        {
            poheader = await POHeaderService.POHeaderList(null);   //leave user name blank
        }
        else
        {
            poheader = await POHeaderService.POHeaderList(user.Identity.Name); //pass user name
        }

    }
}

CreatePurchaseOrderPdf.cs

using System;
using System.Collections.Generic;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Grid;
using Syncfusion.Drawing;
using System.IO;

namespace BlazorPurchaseOrders.Data
{    public class CreatePurchaseOrderPdf
    {
        public MemoryStream CreatePdf(POHeader orderHeader, List<POLine> orderLines)
        {
            //Create a new PDF document
            using (PdfDocument pdfDocument = new())
            {
                int paragraphAfterSpacing = 8;
                int cellMargin = 4;

                //Add page to the PDF document
                PdfPage page = pdfDocument.Pages.Add();

                //Set the page size
                pdfDocument.PageSettings.Size = PdfPageSize.A4;
                pdfDocument.PageSettings.Margins.Left = 28;
                pdfDocument.PageSettings.Margins.Right = 28;
                pdfDocument.PageSettings.Margins.Top = 8;
                pdfDocument.PageSettings.Margins.Bottom = 14;

                #region Header

                //Define 'bounds' for header
                RectangleF boundsHeader = new (0, 0, pdfDocument.Pages[0].GetClientSize().Width, 200);
                PdfPageTemplateElement header = new(boundsHeader);
                pdfDocument.Template.Top = header;

                //Load the image from the disc                
                string logoFilename = @$"{Environment.CurrentDirectory}/wwwroot/Media/blazorcode_logo_small.png";    
                FileStream logoStream = new(logoFilename, FileMode.Open, FileAccess.Read);
                PdfBitmap logo = new(logoStream);

                //Draw the image -- SizeF scales the image to specified size
                header.Graphics.DrawImage(logo, new PointF(0, 0), new SizeF(183, 35));               

                //Create a new font
                PdfStandardFont companyAddressFont = new(PdfFontFamily.Helvetica, 10, PdfFontStyle.Bold);

                //Set the format for string
                PdfStringFormat stringFormat = new();

                //Set the text alignment
                stringFormat.Alignment = PdfTextAlignment.Right;

                string adddressText = "blazorcode.uk";
                header.Graphics.DrawString(adddressText, companyAddressFont, PdfBrushes.Black, new RectangleF(0, 0, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                adddressText = "5 High Street";
                header.Graphics.DrawString(adddressText, companyAddressFont, PdfBrushes.Black, new RectangleF(0, 14, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                adddressText = "Trumpington";
                header.Graphics.DrawString(adddressText, companyAddressFont, PdfBrushes.Black, new RectangleF(0, 28, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                adddressText = "TR4A IRS";
                header.Graphics.DrawString(adddressText, companyAddressFont, PdfBrushes.Black, new RectangleF(0, 42, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                //Concatenate Supplier Name & Address, omitting blank address lines
                string supplierAddress = orderHeader.SupplierName + Environment.NewLine;

                if (!String.IsNullOrEmpty(orderHeader.POHeaderSupplierAddress1))
                {
                    supplierAddress += orderHeader.POHeaderSupplierAddress1 + Environment.NewLine;
                }
                if (!String.IsNullOrEmpty(orderHeader.POHeaderSupplierAddress2))
                {
                    supplierAddress += orderHeader.POHeaderSupplierAddress2 + Environment.NewLine;
                }
                if (!String.IsNullOrEmpty(orderHeader.POHeaderSupplierAddress3))
                {
                    supplierAddress += orderHeader.POHeaderSupplierAddress3 + Environment.NewLine;
                }
                supplierAddress += orderHeader.POHeaderSupplierPostCode;

                //Set the text alignment and style
                stringFormat.Alignment = PdfTextAlignment.Left;                
                PdfStandardFont headerFont = new(PdfFontFamily.Helvetica, 9, PdfFontStyle.Regular);

                //Print Supplier Name & Address
                header.Graphics.DrawString(supplierAddress, headerFont, PdfBrushes.Black, new RectangleF(0, 90, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                //Order No etc
                header.Graphics.DrawString("Order No", headerFont, PdfBrushes.Black, new RectangleF((page.GetClientSize().Width) / 2, 90, page.GetClientSize().Width / 2, 18), stringFormat);
                header.Graphics.DrawString("Order Date", headerFont, PdfBrushes.Black, new RectangleF((page.GetClientSize().Width) / 2, 108, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);
                header.Graphics.DrawString("Contact", headerFont, PdfBrushes.Black, new RectangleF((page.GetClientSize().Width) / 2, 126, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                header.Graphics.DrawString(": " + orderHeader.POHeaderOrderNumber.ToString(), headerFont, PdfBrushes.Black, new RectangleF(((page.GetClientSize().Width) / 2) + 50, 90, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);
                header.Graphics.DrawString(": " + orderHeader.POHeaderOrderDate.ToShortDateString(), headerFont, PdfBrushes.Black, new RectangleF(((page.GetClientSize().Width) / 2) + 50, 108, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);
                header.Graphics.DrawString(": " + orderHeader.POHeaderRequestedBy, headerFont, PdfBrushes.Black, new RectangleF(((page.GetClientSize().Width) / 2) + 50, 126, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                //Purchase Order Title
                stringFormat.Alignment = PdfTextAlignment.Center;
                PdfStandardFont docTypeFont = new(PdfFontFamily.Helvetica, 14, PdfFontStyle.Bold);
                header.Graphics.DrawString("PURCHASE ORDER", docTypeFont, PdfBrushes.Black, new RectangleF(0, 170, page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                #endregion

                #region Body

                PdfStandardFont contentFont = new(PdfFontFamily.Helvetica, 8);

                decimal runningNetPrice = 0;
                decimal runningTaxAmount = 0;
                decimal runningGrossPrice = 0;
                float bottomOfGrid = 0;
                int linesPerPage = 40;

                //Calculate the number of pages                
                int pageCount = ((orderLines.Count + (linesPerPage - 1)) / linesPerPage);

                //Initialise pageNo and the line number
                int pageNo = 1;
                int i = 0;          //Effectively line number

                // Deal with full pages first - (if only one page is needed, see next section)
                while (pageNo < pageCount)
                {
                    //Create a PdfGrid
                    PdfGrid pdfGrid = new();
                    pdfGrid.Style.CellPadding.Left = cellMargin;
                    pdfGrid.Style.CellPadding.Right = cellMargin;

                    //Sets up columns, column alignment and header text
                    CreatePdfGridLayout(pdfGrid);

                    while (i < (pageNo * linesPerPage))                        
                    {
                        string truncatedProductCode = orderLines[i].POLineProductCode;
                        string truncatedProductDescription = orderLines[i].POLineProductDescription;
                        SizeF sizeProductCode = contentFont.MeasureString(orderLines[i].POLineProductCode);
                        SizeF sizeProductDescription = contentFont.MeasureString(orderLines[i].POLineProductDescription);

                        //Product Code Column Width (minus cellMargin) = 70 - (2*4) = 62
                        if (sizeProductCode.Width > 62)
                        {
                            truncatedProductCode = GetFittedStringToPrint(truncatedProductCode, 62, contentFont);
                        }

                        //Product Description Column Width (minus cellMargin) = 130 - (2*4) = 122
                        if (sizeProductDescription.Width > 122)
                        {
                            truncatedProductDescription = GetFittedStringToPrint(truncatedProductDescription, 122, contentFont);
                        }

                        //Add rows
                        PdfGridRow pdfGridRow = pdfGrid.Rows.Add();
                        pdfGridRow.Cells[0].Value = truncatedProductCode;
                        pdfGridRow.Cells[1].Value = truncatedProductDescription;
                        pdfGridRow.Cells[2].Value = String.Format("{0:N0}", orderLines[i].POLineProductQuantity);
                        pdfGridRow.Cells[3].Value = String.Format("{0:C2}", orderLines[i].POLineProductUnitPrice);
                        pdfGridRow.Cells[4].Value = String.Format("{0:C2}", orderLines[i].POLineNetPrice);
                        pdfGridRow.Cells[5].Value = String.Format("{0:P2}", orderLines[i].POLineTaxRate);
                        pdfGridRow.Cells[6].Value = String.Format("{0:C2}", orderLines[i].POLineTaxAmount);
                        pdfGridRow.Cells[7].Value = String.Format("{0:C2}", orderLines[i].POLineGrossPrice);

                        runningNetPrice = (decimal)(runningNetPrice + orderLines[i].POLineNetPrice);
                        runningTaxAmount += orderLines[i].POLineTaxAmount;
                        runningGrossPrice += orderLines[i].POLineGrossPrice;

                        i++;
                    }

                    //Draw PDF grid into the PDF page
                    PdfLayoutResult result = pdfGrid.Draw(page, new PointF(0, paragraphAfterSpacing));

                    if (pageNo < pageCount)
                    {                   
                        page = pdfDocument.Pages.Add();
                    }

                    //Increment page no
                    pageNo++;
                }

                //Now deal with last records, those that will not fill a whole page                
                PdfGrid pdfGridLastPage = new();
                pdfGridLastPage.Style.CellPadding.Left = cellMargin;
                pdfGridLastPage.Style.CellPadding.Right = cellMargin;

                //Sets up columns, column alignment and header text
                CreatePdfGridLayout(pdfGridLastPage);

                PdfGraphics graphicsLastPage = page.Graphics;

                while (i < orderLines.Count)
                {                    
                    string truncatedProductCode = orderLines[i].POLineProductCode;
                    string truncatedProductDescription = orderLines[i].POLineProductDescription;
                    SizeF sizeProductCode = contentFont.MeasureString(orderLines[i].POLineProductCode);
                    SizeF sizeProductDescription = contentFont.MeasureString(orderLines[i].POLineProductDescription);

                    //Product Code Column Width (minus cellMargin) = 70 - (2*4) = 62
                    if (sizeProductCode.Width > 62)
                    {
                        truncatedProductCode = GetFittedStringToPrint(truncatedProductCode, 62, contentFont);
                    }

                    //Product Description Column Width (minus cellMargin) = 130 - (2*4) = 122
                    if (sizeProductDescription.Width > 122)
                    {
                        truncatedProductDescription = GetFittedStringToPrint(truncatedProductDescription, 122, contentFont);
                    }

                    //Add rows
                    PdfGridRow pdfGridRow = pdfGridLastPage.Rows.Add();
                    pdfGridRow.Cells[0].Value = truncatedProductCode;
                    pdfGridRow.Cells[1].Value = truncatedProductDescription;
                    pdfGridRow.Cells[2].Value = String.Format("{0:N0}", orderLines[i].POLineProductQuantity);
                    pdfGridRow.Cells[3].Value = String.Format("{0:C2}", orderLines[i].POLineProductUnitPrice);
                    pdfGridRow.Cells[4].Value = String.Format("{0:C2}", orderLines[i].POLineNetPrice);
                    pdfGridRow.Cells[5].Value = String.Format("{0:P2}", orderLines[i].POLineTaxRate);
                    pdfGridRow.Cells[6].Value = String.Format("{0:C2}", orderLines[i].POLineTaxAmount);
                    pdfGridRow.Cells[7].Value = String.Format("{0:C2}", orderLines[i].POLineGrossPrice);

                    runningNetPrice = (decimal)(runningNetPrice + orderLines[i].POLineNetPrice);
                    runningTaxAmount += orderLines[i].POLineTaxAmount;
                    runningGrossPrice += orderLines[i].POLineGrossPrice;

                    //Increment order line no
                    i++;
                }

                //Draw PDF grid into the PDF page
                PdfLayoutResult resultLastPage = pdfGridLastPage.Draw(page, new PointF(0, paragraphAfterSpacing));

                bottomOfGrid = resultLastPage.Bounds.Bottom;

                //Section for showing totals
                string totalNetPrice = String.Format("{0:C2}", runningNetPrice);
                string totalTaxAmount = String.Format("{0:C2}", runningTaxAmount);
                string totalGrossPrice = String.Format("{0:C2}", runningGrossPrice);

                stringFormat.Alignment = PdfTextAlignment.Right;
                PdfStandardFont totalsFont = new(PdfFontFamily.Helvetica, 8, PdfFontStyle.Bold);

                graphicsLastPage.DrawString(totalNetPrice, totalsFont, PdfBrushes.Black, new RectangleF(295 - cellMargin, bottomOfGrid + 10, 60, page.GetClientSize().Height), stringFormat);
                graphicsLastPage.DrawString(totalTaxAmount, totalsFont, PdfBrushes.Black, new RectangleF(395 - cellMargin, bottomOfGrid + 10, 60, page.GetClientSize().Height), stringFormat);
                graphicsLastPage.DrawString(totalGrossPrice, totalsFont, PdfBrushes.Black, new RectangleF(455 - cellMargin, bottomOfGrid + 10, 60, page.GetClientSize().Height), stringFormat);

                stringFormat.Alignment = PdfTextAlignment.Left;
                graphicsLastPage.DrawString("Order Total", totalsFont, PdfBrushes.Black, new RectangleF(0 + cellMargin, bottomOfGrid + 10, 60, page.GetClientSize().Height), stringFormat);

                //Create new PDF pen
                PdfPen pen = new(Color.Black, 1);

                //Draw line on the page below the Order Totals line
                graphicsLastPage.DrawLine(pen, 0, bottomOfGrid + 20, 515, bottomOfGrid + 20);

                #endregion

                #region Footer

                //Define 'bounds' for footer
                RectangleF boundsFooter = new(0, pdfDocument.Pages[0].GetClientSize().Height, 
                    pdfDocument.Pages[0].GetClientSize().Width, 10);
                PdfPageTemplateElement footer = new (boundsFooter);
                pdfDocument.Template.Bottom = footer;

                PdfStandardFont footerFont = new (PdfFontFamily.Helvetica, 7);
                PdfBrush brush = new PdfSolidBrush(Color.Black);               

                //Page x of y
                //string footerPageNumber = $"Page {pageNo} of {pageCount}";
                //stringFormat.Alignment = PdfTextAlignment.Center;
                //footer.Graphics.DrawString(footerPageNumber, footerFont, brush, new RectangleF(0, 0, 
                //    page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                //Create date printed field
                string datePrinted = "Printed on: " + DateTime.Now.ToString("dd/MM/yyyy");
                stringFormat.Alignment = PdfTextAlignment.Right;
                footer.Graphics.DrawString(datePrinted, footerFont, brush, new RectangleF(0, 0, 
                    page.GetClientSize().Width, page.GetClientSize().Height), stringFormat);

                #endregion

                //Alternative version for page numbers using PdfCompositeField
                ///Create page number and count fields
                PdfPageNumberField pageNumber = new();
                PdfPageCountField count = new();
                //Create composite field.
                //Add the fields in composite fields
                PdfCompositeField compositeField = new(footerFont, brush, "Page {0} of {1}", pageNumber, count);

                //Draw the composite fields in footer.  Note they are both on the same 'line'.
                compositeField.Draw(footer.Graphics, new PointF(240, 0));

                using (MemoryStream stream = new ())
                {
                    //Saving the PDF document into the stream
                    pdfDocument.Save(stream);
                    //Closing the PDF document
                    pdfDocument.Close(true);
                    return stream;
                }

                static string GetFittedStringToPrint(string input, float maxWidth, PdfStandardFont contentFont)
                {
                    var output = input;
                    var stringMeasurement = contentFont.MeasureString(input); 
                    if (stringMeasurement.Width > maxWidth)
                    {
                        var inputMinusOneChar = input;
                        do
                        {
                            inputMinusOneChar = inputMinusOneChar.Substring(0, inputMinusOneChar.Length - 1).Trim();
                            stringMeasurement = contentFont.MeasureString(inputMinusOneChar + "...");
                        } while (stringMeasurement.Width > maxWidth);
                        output = inputMinusOneChar + "...";
                    }
                    return output;
                }

                static void CreatePdfGridLayout(PdfGrid pdfGrid)
                {
                    //Create PDF grid column.   NOTE: A4 Page width between standard margins = 515 points
                    PdfGridColumn column1 = new(pdfGrid);
                    column1.Width = 70;
                    PdfGridColumn column2 = new(pdfGrid);
                    column2.Width = 130;
                    PdfGridColumn column3 = new(pdfGrid);
                    column3.Width = 45;
                    PdfGridColumn column4 = new(pdfGrid);
                    column4.Width = 50;
                    PdfGridColumn column5 = new(pdfGrid);
                    column5.Width = 60;
                    PdfGridColumn column6 = new(pdfGrid);
                    column6.Width = 50;
                    PdfGridColumn column7 = new(pdfGrid);
                    column7.Width = 50;
                    PdfGridColumn column8 = new(pdfGrid);
                    column8.Width = 60;

                    //Add eight columns.
                    pdfGrid.Columns.Add(column1);
                    pdfGrid.Columns.Add(column2);
                    pdfGrid.Columns.Add(column3);
                    pdfGrid.Columns.Add(column4);
                    pdfGrid.Columns.Add(column5);
                    pdfGrid.Columns.Add(column6);
                    pdfGrid.Columns.Add(column7);
                    pdfGrid.Columns.Add(column8);

                    //Set alignment of columns 3 to 8
                    column3.Format.Alignment = PdfTextAlignment.Right;
                    column4.Format.Alignment = PdfTextAlignment.Right;
                    column5.Format.Alignment = PdfTextAlignment.Right;
                    column6.Format.Alignment = PdfTextAlignment.Right;
                    column7.Format.Alignment = PdfTextAlignment.Right;
                    column8.Format.Alignment = PdfTextAlignment.Right;

                    //Add header.
                    pdfGrid.Headers.Add(1);
                    PdfGridRow pdfGridHeader = pdfGrid.Headers[0];
                    pdfGridHeader.Cells[0].Value = "Code";
                    pdfGridHeader.Cells[1].Value = "Description";
                    pdfGridHeader.Cells[2].Value = "Quantity";
                    pdfGridHeader.Cells[3].Value = "Unit Price";
                    pdfGridHeader.Cells[4].Value = "Net Price";
                    pdfGridHeader.Cells[5].Value = "Tax Rate";
                    pdfGridHeader.Cells[6].Value = "Tax";
                    pdfGridHeader.Cells[7].Value = "Total";

                    //Applying built-in style to the PDF grid
                    pdfGrid.ApplyBuiltinStyle(PdfGridBuiltinStyle.GridTable4Accent1);

                    //We want the grid header to be in bold.  To do this:
                    //Create an instance of PdfGridRowStyle 
                    PdfGridRowStyle pdfGridRowStyle = new ();
                    pdfGridRowStyle.Font = new PdfStandardFont(PdfFontFamily.Helvetica, 8, PdfFontStyle.Bold);
                    //Set style for the header 
                    pdfGrid.Headers.ApplyStyle(pdfGridRowStyle);
                }
            }
        }
    }
}

EmailSenderMailKit.cs

using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using MimeKit.Text;
using System;
using System.Collections.Generic;
using System.IO;

namespace BlazorPurchaseOrders.Data
{
    public interface IEmailService
    {
        bool Send(string SmtpHost,
            int SmtpPort,
            string SmtpUserFriendlyName,
            string SmtpUserEmailAddress,
            string SmtpPass,
            string to,
            string subject,
            string body,
            string filename,
            string attachmentName);
    }

    public class EmailService : IEmailService
    {
        public bool Send(string SmtpHost, 
            int SmtpPort, 
            string SmtpUserFriendlyName, 
            string SmtpUserEmailAddress,
            string SmtpPass,
            string to, 
            string subject, 
            string body,
            string filename,
            string attachmentName)
        {

            // create message
            var email = new MimeMessage();
            email.From.Add(new MailboxAddress(SmtpUserFriendlyName, SmtpUserEmailAddress));
            email.To.Add(new MailboxAddress(to, to));
            email.Subject = subject;
            var msgbody = new BodyBuilder
            {
                HtmlBody = string.Format(body),
                TextBody = body
            };            
            msgbody.Attachments.Add(filename);
            foreach (var file in msgbody.Attachments)
            {
                file.ContentDisposition.FileName = attachmentName;
            }
            email.Body = msgbody.ToMessageBody();

            try
            {
                // send email
                using var smtp = new SmtpClient();
                smtp.Connect(SmtpHost, SmtpPort, SecureSocketOptions.StartTls);
                smtp.Authenticate(SmtpUserEmailAddress, SmtpPass);
                smtp.Send(email);
                smtp.Disconnect(true);

                return true;
            }
            catch
            {
                return false;
            }

        }
    }
}

EmailSenderSendGrid.cs

using System;
using System.IO;
using System.Threading.Tasks;
using SendGrid;
using SendGrid.Helpers.Mail;

namespace BlazorPurchaseOrders.Data
{
    public interface IEmailSender
    {
        Task<bool> SendEmail(string apiKey,
            string FromFriendlyName,
            string FromEmailAddress,
            string toEmail,
            string subject,
            string body,
            string filename,
            string attachmentName);
    }

    public class EmailSender : IEmailSender
    {
        public async Task<bool> SendEmail(string apiKey, 
            string FromFriendlyName,
            string FromEmailAddress,
            string toEmail, 
            string subject, 
            string body,
            string filename,
            string attachmentName)
        {
            var client = new SendGridClient(apiKey);
            var from = new EmailAddress(FromEmailAddress, FromFriendlyName);            
            var to = new EmailAddress(toEmail, toEmail);
            var plainTextContent = body;
            var htmlContent = body;            
            var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
            var bytes = File.ReadAllBytes(filename);
            var file = Convert.ToBase64String(bytes);
            msg.AddAttachment(attachmentName, file);
            var response = await client.SendEmailAsync(msg).ConfigureAwait(false);
            return response.IsSuccessStatusCode;           
        }
    }
}

Startup.cs

Add the following line below the other 'services' lines:

services.AddSingleton<CreatePurchaseOrderPdf>();