How to Generate Invoices with PDF SDK - ByteScout
  • Home
  • /
  • Blog
  • /
  • How to Generate Invoices with PDF SDK

How to Generate Invoices with PDF SDK

The PDF format is one of the most commonly used formats in the enterprise documents exchanges. Each company has its software for invoices, maybe from a specific vendor or developed by its own development team. Many dedicated frameworks exist in the market to manipulate PDF files, they all aim to make the developer’s work easy.

In this tutorial, we will show how to create your printable invoices with PDF SDK. Bytescout’s PDF SDK contains many features to deal with PDF documents. You can create PDF files from scratch, on the fly, or modify an existing one, even exporting them to another file format like Microsoft Office word document without any format and data loss. You can find more details about that on the main website of Bytescout at https://bytescout.com

The rest of this tutorial is focusing on invoices PDF SDK. We’ll see three ways to generate an invoice:

.

All of the three methods are done on the server-side so they can be plugged into an automated invoicing software for future use. Each method is independent of the others and you can choose the most suitable one.

We will use the .NET framework 4.5, thus we have to use the net4.5 version of Bytescout.PDF.dll

It is located at C:\Program Files\Bytescout PDF SDK\net4.5\ folder

invoice_src0

Section 1: Building an invoice using the Bytescout’s pdf SDK framework

We will build the whole invoice by full use of Bytescout’s pdf SDK. The framework contains a lot of features that can be used to write and format texts, draw lines, arcs, tables, images, canvas, and many others. Those objects are then positioned on the page by using the (left, top) system coordinates where (0,0) is at the top left corner of the page.

We start with the default Visual Studio C# console application, name the project InvoiceSample01, add a reference to Bytescout.PDF.dll and System.Drawing.dll

invoice_src1

In the file Program.cs we add the code below

Program.cs

using Bytescout.PDF;

namespace InvoiceSample01
{
    class Program
    {
        static void Main(string[] args)
        {
            //Create the pdf document
            var invoicePdfDocument = new Document();

            //Define the type of Page format
            var invoicePage = new Page(PaperFormat.A4);

            //Add page to the pdf document
            invoicePdfDocument.Pages.Add(invoicePage);

            //Define two types of font

            //Simple Arial font, size of ten em
            var font = new Font("Arial", 10);
            //Arial font of 12 em and bold
            var fontBold = new Font("Arial", 12, true, false, false, false);

            //Define a brush
            var brush = new SolidBrush();

            /*
             Build each part (text, vertical/horizontal lines...) and place them in the page 
             using their coordinates (left,top)
             */

          
                        
            invoicePage.Canvas.DrawString("Computer accessories Ltd", fontBold, brush, 20, 20);
            invoicePage.Canvas.DrawString("INVOICE", fontBold, brush, 500, 20);

            //Seller address
            invoicePage.Canvas.DrawString("27 Street WorldLand", font, brush, 20, 40);
            invoicePage.Canvas.DrawString("City Lake", font, brush, 20, 60);
            invoicePage.Canvas.DrawString("+00.00.00.00.54", font, brush, 20, 80);
            invoicePage.Canvas.DrawString("+00.00.00.00.55", font, brush, 20, 100);

            //Invoice header labels
            invoicePage.Canvas.DrawString("Date", font, brush, 400, 40);
            invoicePage.Canvas.DrawString("Invoice #", font, brush, 400, 60);
            invoicePage.Canvas.DrawString("Customer #", font, brush, 400, 80);

            //Invoice header values
            invoicePage.Canvas.DrawString("2016-30-11", font, brush, 500, 40);
            invoicePage.Canvas.DrawString("1234567", font, brush, 500, 60);
            invoicePage.Canvas.DrawString("CT-111-52", font, brush, 500, 80);

            //Buyer information

            invoicePage.Canvas.DrawString("Bill To", fontBold, new SolidBrush(new ColorRGB(0, 0, 255)), 20, 120);
            invoicePage.Canvas.DrawString("Ship Sea Land", font, brush, 20, 140);
            invoicePage.Canvas.DrawString("Albert Best Buyer", font, brush, 20, 160);
            invoicePage.Canvas.DrawString("42 Dolphin Blv", font, brush, 20, 180);
            invoicePage.Canvas.DrawString("River City", font, brush, 20, 200);
            invoicePage.Canvas.DrawString("+44.00.00.12.55", font, brush, 20, 220);

            //Add a grey background color in the grid header                        
            SolidBrush brushBackground = new SolidBrush(new ColorRGB(192, 192, 192));
            invoicePage.Canvas.DrawRectangle(brushBackground, 20, 250, 560, 20);

            //Draw the grid lines

	     //The type of pen 
            SolidPen pen = new SolidPen();
            
            //Horizontal lines
            invoicePage.Canvas.DrawLine(pen, 20, 250, 580, 250);

            invoicePage.Canvas.DrawLine(pen, 20, 270, 580, 270);

            invoicePage.Canvas.DrawLine(pen, 20, 370, 580, 370);

            //Vertical lines
            invoicePage.Canvas.DrawLine(pen, 20, 250, 20, 370);

            invoicePage.Canvas.DrawLine(pen, 370, 250, 370, 370);

            invoicePage.Canvas.DrawLine(pen, 440, 250, 440, 370);

            invoicePage.Canvas.DrawLine(pen, 510, 250, 510, 370);

            invoicePage.Canvas.DrawLine(pen, 580, 250, 580, 370);
            

            //Labels
            invoicePage.Canvas.DrawString("Description", fontBold, brush, 160, 255);
            {
                //Items under Description
                invoicePage.Canvas.DrawString("RAM SDRAM PC 1333 Mhz", font, brush, 25, 275);
                invoicePage.Canvas.DrawString("RJ45 LAN connector", font, brush, 25, 295);
                invoicePage.Canvas.DrawString("C19 power supply cable 16A ", font, brush, 25, 315);
                invoicePage.Canvas.DrawString("Headphones CZBluetooth 4", font, brush, 25, 335);
            }
            invoicePage.Canvas.DrawString("Unit price", fontBold, brush, 380, 255);
            {
                //Items under Unit price
                invoicePage.Canvas.DrawString("20,45", font, brush, 390, 275);
                invoicePage.Canvas.DrawString("2,50", font, brush, 390, 295);
                invoicePage.Canvas.DrawString("10,70", font, brush, 390, 315);
                invoicePage.Canvas.DrawString("30,50", font, brush, 390, 335);
            }
            invoicePage.Canvas.DrawString("Quantity", fontBold, brush, 450, 255);
            {
                //Items under Quantity
                invoicePage.Canvas.DrawString("3", font, brush, 470, 275);
                invoicePage.Canvas.DrawString("4", font, brush, 470, 295);
                invoicePage.Canvas.DrawString("5", font, brush, 470, 315);
                invoicePage.Canvas.DrawString("1", font, brush, 470, 335);
            }
            invoicePage.Canvas.DrawString("Amount", fontBold, brush, 520, 255);
            {
                //Items under Amount
                invoicePage.Canvas.DrawString("61,35", font, brush, 540, 275);
                invoicePage.Canvas.DrawString("10,00", font, brush, 540, 295);
                invoicePage.Canvas.DrawString("53,50", font, brush, 540, 315);
                invoicePage.Canvas.DrawString("30,50", font, brush, 540, 335);
            }

            invoicePage.Canvas.DrawString("Sub Total", font, brush, 450, 380);
            invoicePage.Canvas.DrawString("Taxable", font, brush, 450, 400);
            invoicePage.Canvas.DrawString("Tax Rate", font, brush, 450, 420);
            invoicePage.Canvas.DrawString("Tax Due", font, brush, 450, 440);
            invoicePage.Canvas.DrawString("Total", fontBold, brush, 450, 460);

            {
                //amount value
                invoicePage.Canvas.DrawString("0,001", font, brush, 540, 380);
                invoicePage.Canvas.DrawString("0,002", font, brush, 540, 400);
                invoicePage.Canvas.DrawString("0,003", font, brush, 540, 420);
                invoicePage.Canvas.DrawString("0,004", font, brush, 540, 440);
                invoicePage.Canvas.DrawString("0,005", font, brush, 540, 460);
            }

            //Save the pdf as invoice01.pdf
            invoicePdfDocument.Save("invoice01.pdf");

            invoicePdfDocument.Dispose();
        }

    }
}

After running the program, the invoice01.pdf is created in the bin/Debug/ folder and looks like the figure below

invoice_src2

As we’ve seen along with the Program.cs code, each piece of the final invoice has been inserted into the pdf document’s page object. The major part of the task is about positioning the elements at the right place on the page. We’ve used the Canvas property of the pdf document object to draw texts and graphics. You can picture a Canvas as a container where you can draw something. Although this method is very powerful – remember you have the full control of each element on the page, building the pdf file in this way may become tedious at the earlier stage of the development since you’ll be playing with a lot of coordinates. The logic and the presentation layer are also in the same place so it may be hard to develop and to maintain.

The next two sections will show you how to use a template file to separate the invoice design and the invoice data. We’ll use an HTML and a Microsoft Office document (.docx) template files.

Tips: When two elements have the same coordinate on the same page, the last added one is placed on the top of the other. In the above method, the Document.Save(string fileName) method writes the pdf file into the file system.

Section 2: Generate the invoice form an HTML template

We start with the default C# console application in visual studio 2015 Community. We choose framework 4.5 and name the solution “InvoiceSample02”. We also add references to Bytescout.PDF.dll and Bytescout.PDF.Converters.dll. Those files can be found in the folder C:\Program Files\Bytescout PDK SDK\net4.5\

The figure below shows the snapshot of the solution at this stage

invoice_src3

We’ve added the InvoiceTemplates folder where we’ll put the invoice template. The Model folder contains the definition of classes used to map with the template.

You can picture a template file as a generic file that is used as a base layout to generate different invoices. All generated invoices differ only by specific fields like the customer name, the invoice details, the total amounts, etc. The layout doesn’t change and remains the same in terms of design.

After creating the file template000.html in the InvoiceTemplates folder, copy and paste the following code into it.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cstyle%3E%0A%20%20%20%20%20%20%20%20body%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20Arial%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%200.8em%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20800px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoicePageContainer%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceHeader%2C%20%23invoiceDetail%2C%20%23invoiceFooter%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20block%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceHeader%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20min-height%3A%20100px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23buyerInfoContainer%2C%20%23invoiceReferenceContainer%2C%20%23sellerInfoContainer%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20block%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23buyerInfoContainer%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20min-height%3A%20100px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20top%3A%20100px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23sellerInfoContainer%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20min-height%3A%20100px%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceDetail%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20top%3A%20200px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceTable%20%7B%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20border-left%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-right%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-top%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-bottom%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-spacing%3A%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20950px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceTableDetail%20tr%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-bottom%3A%20none%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceTableSubTotal%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-top%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%0A%20%20%20%20%20%20%20%20%23buyerInfoContainer%2C%20%23sellerInfoContainer%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20left%3A%2010px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceReferenceContainer%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20left%3A%20700px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceReferenceContainer_Labels%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20inline%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20min-width%3A%20200px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceReferenceContainer_Entry%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20inline%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%2064px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20left%3A%20100px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoice%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%201.5em%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20bold%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20block%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-align%3A%20right%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.amount%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-align%3A%20right%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.unitprice%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-align%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-left%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.quantity%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-align%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-left%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-right%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.total%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20bold%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23Tr2%20td%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20300px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20vertical-align%3A%20text-top%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-bottom%3A%201px%20solid%20black%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23invoiceTableTotal%20.amount%2C%20%23invoiceTable%20thead%2C%20%23invoiceBillTo%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20background-color%3A%20%23032b64%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20white%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23companyName%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20bolder%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%201.2em%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.padding-10%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%2010px%2010px%200px%200px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C%2Fstyle%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;style&gt;" title="&lt;style&gt;" />

</head>
<body>


<div id="invoicePageContainer">

<div id="invoiceHeader">


<div id="sellerInfoContainer">

<div id="companyName">Computer accessories Ltd</div>


<div id="companyAddress">27 Street WorldLand </div>


<div id="companyCity">City Lake </div>


<div id="companyPhone">+00.00.00.00.54</div>


<div id="companyFax">+00.00.00.00.55</div>

            </div>



<div id="invoiceReferenceContainer">

<div id="invoice">INVOICE</div>


<div id="invoiceReferenceContainer_Labels">

<div id="">Date</div>


<div id="Div1">Invoice #</div>


<div id="Div2">Customer #</div>

                </div>


<div id="invoiceReferenceContainer_Entry">

<div id="invoiceDate">{{INVOICE_DATE}}</div>


<div id="invoiceId">{{INVOICE_ID}}</div>


<div id="invoiceCustomerId">{{INVOICE_CUSTOMERID}}</div>

                </div>

            </div>


<div id="buyerInfoContainer">

<div id="invoiceBillTo">Bill To :</div>


<div id="invoiceHeader_billTo">{{INVOICE_CUSTOMER_TO}}</div>


<div id="customerName">{{INVOICE_CUSTOMER_NAME}}</div>


<div id="customerStreetAddress">{{INVOICE_CUSTOMER_STREET_ADDRESS}}</div>


<div id="customerCity">{{INVOICE_CUSTOMER_CITY}}</div>


<div id="customerPhone">{{INVOICE_CUSTOMER_PHONE_NUMBER}}</div>

            </div>

        </div>


<div id="invoiceDetail">

<table id="invoiceTable">

<thead>

<tr>

<th style="width:700px">Description</th>


<th style="width:100px">Unit price</th>


<th style="width:50px;">Quantity</th>


<th style="width:50px;">Amount</th>

                    </tr>

                </thead>


<tbody>
                    {{INVOCE_DETAILS}}
                                      

<tr id="invoiceTableSubTotal">

<td></td>


<td></td>


<td class="padding-10">SubTotal</td>


<td class="amount padding-10">{{INVOICE_SUBTOTAL}}</td>

                    </tr>


<tr id="invoiceTableTaxable">

<td></td>


<td></td>


<td class="padding-10">Taxable</td>


<td class="amount padding-10">{{INVOICE_TAXABLE}}</td>

                    </tr>


<tr id="invoiceTableTaxRate">

<td></td>


<td></td>


<td class="padding-10">Tax rate</td>


<td class="amount padding-10">{{INVOICE_TAXRATE}}</td>

                    </tr>


<tr id="invoiceTableTaxDue">

<td></td>


<td></td>


<td class="padding-10">Tax due</td>


<td class="amount padding-10">{{INVOICE_TAXDUE}}</td>

                    </tr>


<tr id="invoiceTableTotal">

<td></td>


<td></td>


<td class="total padding-10">Total</td>


<td class="amount padding-10">{{INVOICE_TOTAL}}</td>

                    </tr>

                </tbody>


<tfoot>
                </tfoot>

            </table>

        </div>


<div id="invoiceFooter">
        </div>

    </div>


</body>
</html>

The figure below shows the HTML template file opened in the browser.

invoice_src4

Static parts have been surrounded in blue. This is the case of the seller information which never changes across multiple invoices. We’ve used the double curly brackets to identity all dynamic fields. As you have noticed, those fields will be replaced by the real values during the mapping phase.

Tips: We’ve used the double curly brackets because Visual Studio highlights them in the editor.

invoice_src5

The next major step is to create our model class. A model class is just an object where each property is associated to one field in the template model.

The figure below shows our five models.

invoice_src6

We split the whole invoice model into five distinct parts. Each part corresponds to an independent set of data like the Buyer information, the item detail section, and so on.

Template000_Buyer.cs

using System.Text;
namespace InvoiceSample02.Model
{
    public class Template000_Buyer
    {
        // Map to {{INVOICE_CUSTOMER_TO}}
        public string InvoiceCustomerTo { get; set; }

        // Map to {{INVOICE_CUSTOMER_NAME}}
        public string InvoiceCustomerName { get; set; }
        
        // Map to {{INVOICE_CUSTOMER_STREET_ADDRESS}}
        public string InvoiceCustomerStreetAddress { get; set; }
        
        // Map to {{INVOICE_CUSTOMER_CITY}}
        public string InvoiceCustomerCity { get; set; }
        
        // Map to {{INVOICE_CUSTOMER_PHONE_NUMBER}}
        public string InvoiceCustomerPhoneNumber { get; set; }

        //This method takes a stringbuilder input then looks and replaces a specific pattern
        //by the final value
        public void MapAndGenerate(StringBuilder input)
        {
            input = input.Replace("{{INVOICE_CUSTOMER_TO}}", InvoiceCustomerTo);
            input = input.Replace("{{INVOICE_CUSTOMER_NAME}}", InvoiceCustomerName);
            input = input.Replace("{{INVOICE_CUSTOMER_STREET_ADDRESS}}", InvoiceCustomerStreetAddress);
            input = input.Replace("{{INVOICE_CUSTOMER_CITY}}", InvoiceCustomerCity);
            input = input.Replace("{{INVOICE_CUSTOMER_PHONE_NUMBER}}", InvoiceCustomerPhoneNumber);           
        }
    }
}

Template000_Header.cs

using System.Text;

namespace InvoiceSample02.Model
{    
    public class Template000_Header
    {
        // Map to {{INVOICE_DATE}}
        public string InvoiceDate { get; set; }

        // Map to {{INVOICE_ID}}
        public string InvoiceId { get; set; }

        // Map to {{INVOICE_CUSTOMERID}}
        public string InvoiceCustomerId { get; set; }

        //This method takes a stringbuilder input then looks and replaces a specific pattern
        //by the final value
        public void MapAndGenerate(StringBuilder input)
        {
            input = input.Replace("{{INVOICE_DATE}}", InvoiceDate);
            input = input.Replace("{{INVOICE_ID}}", InvoiceId);
            input = input.Replace("{{INVOICE_CUSTOMERID}}", InvoiceCustomerId);          
        }
    }
}

Template000_Total.cs

using System.Text;
namespace InvoiceSample02.Model
{
    public class Template000_Total 
    {
        // Map to {{INVOICE_SUBTOTAL}}
        public string InvoiceSubTotal { get; set; }

        // Map to {{INVOICE_TAXABLE}}
        public string InvoiceTaxable { get; set; }

        // Map to {{INVOICE_TAXRATE}}
        public string InvoiceTaxRate { get; set; }

        // Map to {{INVOICE_TAXDUE}} 
        public string InvoiceTaxDue { get; set; }

        // Map to {{INVOICE_TOTAL}}
        public string InvoiceTotal { get; set; }

        //This method takes a stringbuilder input then looks and replaces a specific pattern
        //by the final value
        public void MapAndGenerate(StringBuilder input)
        {
            input = input.Replace("{{INVOICE_SUBTOTAL}}", InvoiceSubTotal);
            input = input.Replace("{{INVOICE_TAXABLE}}", InvoiceTaxable);
            input = input.Replace("{{INVOICE_TAXRATE}}", InvoiceTaxRate);
            input = input.Replace("{{INVOICE_TAXDUE}}", InvoiceTaxDue);
            input = input.Replace("{{INVOICE_TOTAL}}", InvoiceTotal);           
        }
    }
}

Template000_ItemDetail.cs

namespace InvoiceSample02.Model
{
    public class Template000_ItemDetail
    {
        //The template of an item in the invoice detail section.
        public const string ItemHtml = @"
<tr class='padding-10'>

<td>{{INVOICEDETAILITEM_LIB}}</td>


<td class='unitprice'>{{INVOICEDETAILITEM_UNITPRICE}}</td>


<td class='quantity'>{{INVOICEDETAILITEM_QUANTITY}}</td>


<td class='amount'>{{INVOICEDETAILITEM_AMOUNT}}</td>

                        </tr>

";

        // Map to {{INVOICEDETAILITEM_LIB}}
        public string InvoiceDetailItemLib { get; set; }

        // Map to {{INVOICEDETAILITEM_UNITPRICE}}
        public string InvoiceDetailItemUnitPrice { get; set; }

        // Map to {{INVOICEDETAILITEM_QUANTITY}}
        public string InvoiceDetailItemQuantity { get; set; }

        // Map to {{INVOICEDETAILITEM_AMOUNT}}
        public string InvoiceDetailItemAmount { get; set; }

        //ToString function is now returning the item detail in html format
        public override string ToString()
        {
            string html = ItemHtml.Replace("{{INVOICEDETAILITEM_LIB}}", InvoiceDetailItemLib);
            html = html.Replace("{{INVOICEDETAILITEM_UNITPRICE}}", InvoiceDetailItemUnitPrice);
            html = html.Replace("{{INVOICEDETAILITEM_QUANTITY}}", InvoiceDetailItemQuantity);
            html = html.Replace("{{INVOICEDETAILITEM_AMOUNT}}", InvoiceDetailItemAmount);
            return html;
        }
    }

}

Template000_Model.cs

using System.Collections.Generic;
using System.IO;
using System.Text;
namespace InvoiceSample02.Model
{
    // Template000_Model is the whole invoice, it contains the header information, the buyer and total and
    // also a list of items
    public class Template000_Model
    {
        //Reads and sets the template file
        private StringBuilder template000Html = new StringBuilder().Append( File.ReadAllText("../../InvoiceTemplates/template000.html"));

        public Template000_Model()
        {
            //Initialize all properties
            Header = new Template000_Header();
            Buyer = new Template000_Buyer();
            Details = new List&amp;amp;amp;lt;Template000_ItemDetail&amp;amp;amp;gt;();
            Total = new Template000_Total();
        }


        public Template000_Header Header { get; set; }
        public Template000_Buyer Buyer { get; set; }
        public List&amp;amp;amp;lt;Template000_ItemDetail&amp;amp;amp;gt; Details { get; set; }
        public Template000_Total Total { get; set; }

        
        //This method adds an item to the invoice model        
        public void AddItem(Template000_ItemDetail item)
        {
            Details.Add(item);
        }
        //Genarete the invoice detail section
        private void GenerateDetailsHtml()
        {
            StringBuilder detailHtml = new StringBuilder();
            foreach (var item in Details)
            {
                detailHtml.Append(item.ToString());
            }
            template000Html = template000Html.Replace("{{INVOCE_DETAILS}}", detailHtml.ToString());
        }
        //Process all mapping tasks and generate the final content in html string
        public StringBuilder MapAndGenerateHtml()
        {
            Header.MapAndGenerate(template000Html);
            Buyer.MapAndGenerate(template000Html);
            Total.MapAndGenerate(template000Html);
            GenerateDetailsHtml();
            return template000Html;
        }
    }
}

The Template000_Model class is where we reassemble all parts into one model. It contains the reference to the template file in the template000Html variable.

The main program below is divided into two parts, the first one is where we create the invoice object and the second is where we call the PDF SDK to generate the invoice pdf file. The final document is actually generated in memory by calling the method HtmlToPdfConverter.ConvertHtmlToPdf of Bytescout.PDF.Converters namespace and created in the file system by the MemoryStream.WriteTo method.

Main Program.cs

using Bytescout.PDF.Converters;
using InvoiceSample02.Model;
using System.IO;
using System.Text;

namespace InvoiceSample02
{
    class Program
    {
        static void Main(string[] args)
        {
            //Declare the invoice object
            Template000_Model model = new Template000_Model();

            //Set value of each field in the invoice's header section
            model.Header.InvoiceDate = "2016-30-11";
            model.Header.InvoiceId = "1234567";
            model.Header.InvoiceCustomerId = "CT-111-52";

            //Fill invoice's buyer section
            model.Buyer.InvoiceCustomerTo = "Ship Sea Land";
            model.Buyer.InvoiceCustomerName = "Albert Best Buyer";
            model.Buyer.InvoiceCustomerStreetAddress = "42 Dolphin Blv";
            model.Buyer.InvoiceCustomerCity = "River City";
            model.Buyer.InvoiceCustomerPhoneNumber = "+44.00.00.12.55";

            //Add items to invoice
            model.AddItem(new Template000_ItemDetail() { InvoiceDetailItemLib = "RAM SDRAM PC 1333 Mhz", InvoiceDetailItemUnitPrice = "20,45", InvoiceDetailItemQuantity = "3", InvoiceDetailItemAmount = "61,35" });
            model.AddItem(new Template000_ItemDetail() { InvoiceDetailItemLib = "RJ45 LAN connector", InvoiceDetailItemUnitPrice = "2,50", InvoiceDetailItemQuantity = "4", InvoiceDetailItemAmount = "10,00" });
            model.AddItem(new Template000_ItemDetail() { InvoiceDetailItemLib = "C19 power supply cable 16A", InvoiceDetailItemUnitPrice = "10,70", InvoiceDetailItemQuantity = "5", InvoiceDetailItemAmount = "53,50" });
            model.AddItem(new Template000_ItemDetail() { InvoiceDetailItemLib = "Headphones CZ Bluetooth 4", InvoiceDetailItemUnitPrice = "30,50", InvoiceDetailItemQuantity = "1", InvoiceDetailItemAmount = "30,50" });
            model.AddItem(new Template000_ItemDetail() { InvoiceDetailItemLib = "Optical Mouse black edition", InvoiceDetailItemUnitPrice = "19,99", InvoiceDetailItemQuantity = "1", InvoiceDetailItemAmount = "19,99" });


            //Fill the Total section
            model.Total.InvoiceSubTotal = "45,05";
            model.Total.InvoiceTaxable = "45,05";
            model.Total.InvoiceTaxRate = "20%";
            model.Total.InvoiceTaxDue = "10,05";
            model.Total.InvoiceTotal = "55,10";

            //Generate the final string            
            StringBuilder invoiceHtml = model.MapAndGenerateHtml();

            // Generate the pdf with Bytescout's PDF SDK helper
            using (HtmlToPdfConverter converter = new HtmlToPdfConverter())
            {                
                // Create an in memory stream from the initial string
                var htmlSourceStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(invoiceHtml.ToString()));

                // Create an in memory stream to hold the generated pdf file 
                var destinationStream = new MemoryStream();

                // Generates the pdf in memory
                converter.ConvertHtmlToPdf(htmlSourceStream, destinationStream);

                // And writes it to disk
                FileStream file = new FileStream("invoice.pdf", FileMode.Create, FileAccess.Write);
                destinationStream.WriteTo(file);

                // Finaly close of opened resources
                file.Close();
                htmlSourceStream.Close();
                destinationStream.Close();
            }
        }
    }
}

 

After running the program, the invoice.pdf file is created at the same folder as the executable file, in the bin/Debug/ directory in the actual case.

invoice_src7

Section summary:

In this section, we’ve seen how to generate the invoice pdf file from the HTML template. The invoice layout was entirely made with HTML and CSS, independently of the invoice model object, and separated to the PDF process rendering. HTML and CSS are very popular and commonly used today so this method may be the most suitable one if you’re using them in your work.

Section 3: Generate an invoice file from a DOCX template file

 

Microsoft Office Word documents are largely used in the business for years. Primary versions had the extension .doc and the latest formats are in the .docx extension. They are based on the OpenXML standard. The template file will have the extension .docx thus we will use Microsoft’s OpenXML SDK to process it. To make the printable invoices PDF SDK tools will be added to our solution.

We start with a basic C# console program named “InvoiceSample03”.

Then we add the Open XML SDK.

Right-click the References items > Manage NuGet Packages …

invoice_src8

In the browse field, enter OpenXML SDK and choose the Microsoft’s OpenXML SDK 2.5 in the search result then click the Install button

invoice_src9

Add the reference to the Bytescout.PDF.Converters.dll (located under the folder C:\Program Files\Bytescout PDF SDK\net4.5\)

Add a reference to the assembly WindowsBase

We also create the Templates folder and copy the provided “InvoiceTemplate-000.docx” file inside it.

The solution should be similar to the figure below:

invoice_src10

The invoicetemplate-000 is a normal word document apart from template fields which are identified by double curly brackets.

invoice_src11

Copy the following content to the main Program.cs

Program.cs


using Bytescout.PDF.Converters;
using DocumentFormat.OpenXml.Packaging;
using System;
using System.IO;
namespace InvoiceSample03
{
    class Program
    {
        static void Main(string[] args)
        {
            //Path to the original template file
            string invoiceTemplate = "../../Templates/InvoiceTemplate-000.docx";

            //The name of the template working copy file
            string invoicePrint = "Invoice001.docx";

            //Create the working invoice from the template, overwrites if exist
            File.Copy(invoiceTemplate, invoicePrint,true);

            //Process the work document
            using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(invoicePrint, true))
            {    
                //this variable will contains the original content of the template file
                string docText = null;
                //Get the original stream from the orignal content
                using (StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream()))
                {
                    docText = sr.ReadToEnd();
                }

                docText = docText.Replace("{{DATE}}","");
                docText = docText.Replace("{{INVOICE}}","");
                docText = docText.Replace("{{CUSTOMER}}", "");
                
                docText = docText.Replace("{{SELLERNAME}}","");
                docText = docText.Replace("{{SELLERADDRESS}}", "");
                docText = docText.Replace("{{SELLERSTREET}}", "");
                docText = docText.Replace("{{SELLERCITY}}", "");
                docText = docText.Replace("{{SELLERLINE}}", "");

                { //Description column
                    var descriptionColumnStr = String.Empty;
                    descriptionColumnStr = "RAM SDRAM PC 1333 Mhz " + Environment.NewLine;
                    descriptionColumnStr += "RJ45 LAN connector" + Environment.NewLine;
                    descriptionColumnStr += "C19 power supply cable 16A" + Environment.NewLine;
                    descriptionColumnStr += "Headphones CZBluetooth 4" + Environment.NewLine;
                    descriptionColumnStr += "Optical Mouse black edition" + Environment.NewLine;
                    docText = docText.Replace("{{ITEM_DESCRIPTION}}", descriptionColumnStr);
                }

                { //Unit price column
                    var unitPriceColumnStr = String.Empty;
                    unitPriceColumnStr = "20,45" + Environment.NewLine;
                    unitPriceColumnStr += "2,50" + Environment.NewLine;
                    unitPriceColumnStr += "10,70" + Environment.NewLine;
                    unitPriceColumnStr += "30,50" + Environment.NewLine;
                    unitPriceColumnStr += "19,99" + Environment.NewLine;
                    docText = docText.Replace("{{UP}}", unitPriceColumnStr);
                }

                { //Quantity column
                    var quantityColumnStr = String.Empty;
                    quantityColumnStr = "3" + Environment.NewLine;
                    quantityColumnStr += "4" + Environment.NewLine;
                    quantityColumnStr += "5" + Environment.NewLine;
                    quantityColumnStr += "1" + Environment.NewLine;
                    quantityColumnStr += "1" + Environment.NewLine;
                    docText = docText.Replace("{{QTY}}", quantityColumnStr);
                }

                { //Amount column
                    var amountColumnStr = String.Empty;
                    amountColumnStr = "61,35" + Environment.NewLine;
                    amountColumnStr += "10,00" + Environment.NewLine;
                    amountColumnStr += "53,50" + Environment.NewLine;
                    amountColumnStr += "30,50" + Environment.NewLine;
                    amountColumnStr += "19,99" + Environment.NewLine;
                    docText = docText.Replace("{{PRICE}}", amountColumnStr);
                }

                {//Sub total part
                    docText = docText.Replace("{{ST}}", "45,05");
                    docText = docText.Replace("{{TX}}", "45,05");
                    docText = docText.Replace("{{TR}}", "20%");
                    docText = docText.Replace("{{TD}}", "10,05");
                    docText = docText.Replace("{{TOTAL}}", "55,10");
                }         

                //Write to new content to the template file working copy 
                using (StreamWriter sw = new StreamWriter(wordDoc.MainDocumentPart.GetStream(FileMode.Create)))                
                {
                    sw.Write(docText);
                }              
            }

            // Create the object to do the DOCX to PDF file
            using (DocxToPdfConverter converter = new DocxToPdfConverter())
            {
                // Perform conversion and save the pdf in the file "Invoice001.pdf"
                converter.ConvertDocxToPdf(invoicePrint, "Invoice001.pdf");
            }
            
            Console.ReadLine();
        }
    }
}

 

You can run the program to the generated pdf file below.

invoice_src12

The program can be divided into three major parts:

1 – Create a working copy of the invoice template .docx file and read it’s content.

2- Replace all template’s field identifiers to their actual values.

3- Call the PDF SDK helpers to generate the pdf version.

Step 1 is done by using the OpenXml SDK. You can use this library for almost all your OpenXML developments. It includes Microsoft Office Word, Excel, PowerPoint, and other applications using the Microsoft OpenXml standard format. We only use the WordprocessingDocument object to read the template file’s content.

Step 2 is where we’ve replaced the patterns to values and created the final .docx file.

Step 3 is where we call the PDF SDK DocxToPdfConverter utility to generate the invoice pdf from the .docx file created in step 2.

In terms of software design and architecture, your solution should keep the minimum of dependencies between layers. We advise you to always use the template file when it is possible. It avoids hard-coding and is very flexible since the template file is not embedded into any assembly. You can even place them in a shared folder in the network and update them in the fly without recompiling your program or restarting your application pool.

Performance indicators are also part of the game. We will do some metrics to show you how bad or well each method performs :

Average processing time / PDF SDK method (in ms) – base on the same pdf content.

*Workstation with 2 x Intel Xeon e5620 – 8Cores/16 Threads – 2.4Ghz, 24GB Memory

Hard-coding – section 1

Method: Document.Save

15

Exporting HTML to PDF – Section 2

Method: HtmlToPdfConverter.ConvertHtmlToPdf

369

Exporting DOCX to PDF – Section 3

Method: DocxToPdfConverter.ConvertDocxToPdf

4550

As shown in the above table, the method shown in the first section performs very fast with only an average of 15ms to generate an invoice file while the processing time increases by 24 times for creating the invoice from an HTML template. The .docx template is 296 times slower than method 1.

Those metrics can guide you in the way you’ll use your invoice processing. If your business requires very fast processing, you may choose the method shown in section 1 (actually there’s a linear evolution between the number of generated invoices and the processing time). The hard-coding way can generate 1000 invoices in 16 seconds, 2000 invoices in 32 seconds and so on

If the time factor is not critical, the second method is a good alternative for an average processing time of ~370ms. This scenario offers a good mix of performance and maintenance.

The third case is very useful if your business requires a DOCX template, however, the latency time makes it very partial for synchronous processing.

Conclusion

We’ve seen along with this tutorial three ways to make printable invoices with PDF SDK. You can use the one which suits well your skills or the business requirements. The PDF SDK can be plugged into your actual invoice processing software. This can be done with only a couple of line codes and independently of any existing business logic rules, thus minimizing the risk of unexpected bugs. It can be also called from your invoice automation software as batch programs.

   

About the Author

ByteScout Team ByteScout Team of Writers ByteScout has a team of professional writers proficient in different technical topics. We select the best writers to cover interesting and trending topics for our readers. We love developers and we hope our articles help you learn about programming and programmers.  
prev
next