Basic PDF Creation Using iTextSharp - Part III

Part III - Wrap up. Using what we learned to create a PDF invoice from an XML file

This is the third part of the article series describing PDF creation using iTextSharp. In the first and second part we looked at how to create a PDF, how to assign meta information and how we write some text into the documents. We also looked a bit at how to place text using exact positioning using the content byte and some simple image and graphics handling.

Let's end this series with a small example creating a simple invoice from an xml file using all of the knowledge gathered from these three parts. You'll be surprised at how easy it is! Just keep the focus on how you place stuff on the document, that's the tricky part!

Okay, there are thousands of ways to present an invoice. I've chosen to do a head, row and total kind of layout. So, there will be a table holding the header information with the addresses and order references, a rows table with items, prices and quantity's and a grand total table holding the totals, the fees and the VAT information. There is also a table with the payee information, such as office details, contact information and such. I guess it is a kind of classic invoice layout.  

We need some data for the example, so I've created a data class reading an xml file with four tables: invoice_header, invoice_rows, invoice_total and invoice_payeeinfo

I've wrapped it in a separate class to make it is easy to create overloaded methods supplying the data from other sources than the xml file, for example a database. The class has a file path parameter in its constructor pointing to the file you want to use. The class exposes four methods:

  1. public DataTable GetInvoiceHeader()  
  2. public DataTable GetInvoiceRows()  
  3. public DataTable GetInvoiceTotal()  
  4. public DataTable GetInvoicePayeeInfo()  

 

To create an instance of the class, just supply the file path to an XML file having the four tables.

  1. // Read the sample XML file using the contructor, giving the file path  
  2. Invoice invoice = new Invoice(Server.MapPath("invoices") + "\\invoice_123456.xml");  
  3. // Create references for each of the on-row tables to make it easier to access the values in the table   
  4. // using syntax like this: drHead["invoiceId"].ToString()  
  5. DataRow drHead = invoice.GetInvoiceHeader().Rows[0];  
  6. DataRow drTotal = invoice.GetInvoiceTotal().Rows[0];  
  7. DataRow drPayee = invoice.GetInvoicePayeeInfo().Rows[0];  

 

The complete procedure for creating the invoice is in the downloadable solution but I will point out some highlights that need an explanation.

Simplify writing to the content byte

When writing a lot of text, create a method for doing this; that will decrease the amount of code you have to write. I created a simple method taking some parameters, but you can extend it to use more.
  1. // This is the method writing text to the content byte  
  2. private void writeText(PdfContentByte cb, string Text, int X, int Y, BaseFont font, int Size)  
  3. {  
  4.    cb.SetFontAndSize(font, Size);  
  5.    cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, Text, X, Y, 0);  
  6. }  
I use this method in code simply by calling it like this:
  1. writeText(cb, "Invoice line totals", left_margin, top_margin, f_cb, 10);  
Templates

You can create templates of text or images that you can reuse when creating PDFs. I created a simple footer in the example solution which makes it easy to add page footer to each and every page created. Use the method AddTemplate like this:

  1. // Add a footer template to the document  
  2. cb.AddTemplate(PdfFooter(cb, drPayee), 30, 1);  
The PdfFooter method does the work for us:
  1. // Create the template and assign height  
  2. PdfTemplate tmpFooter = cb.CreateTemplate(580, 70);  
This creates a template reference to which we can add text and other stuff to, just like you add text to the document itself.
  1. // Begin writing the footer  
  2. tmpFooter.BeginText();  
  3. // Set the font and size  
  4. tmpFooter.SetFontAndSize(f_cn, 8);  
  5. // Write out details from the payee tabletmpFooter.ShowTextAligned(PdfContentByte.ALIGN_LEFT, "Text!!!", 0, 53, 0);  
  6. tmpFooter.EndText();

pdf_example_footer.jpg

Here is a sample piece of the invoice footer created in the sample solution.

Page breaks

You need to control the page breaks yourself when writing text using the content byte, but it is a simple straight forward process so, don't worry. The easiest way of doing it is assigning a local variable and set it to the last writable position on the page (NOTE: The page coordinates goes from the bottom to the top) and then check the current writing position against that variable, deciding if we need to do a page break or not. Let's look at how we do it while looping thru the invoice lines in the example solution.
  1. foreach (DataRow drItem in invoice.GetInvoiceRows().Rows)  
  2. {  
  3.    writeText(cb, "blah", left_margin, top_margin, f_cn, 10);  
  4.    top_margin -= 12;  
  5.    // Simpleage break function, check position  
  6.    if(top_margin <= lastwriteposition)  
  7.    {  
  8.       // Okay, we need to switch page, end writing text  
  9.       cb.EndText();  
  10.       // Make the page break  
  11.       document.NewPage();  
  12.       // Start writing again on the new page  
  13.       cb.BeginText();  
  14.       // Assign the new write location on page two!  
  15.       // Here you might want to implement a new row header creation  
  16.       top_margin = 780;  
  17.    }  
  18. }  
Okay, that's it. Run the sample code in the downloadable solution, change it so it suits your needs and enjoy PDF creation with iTextSharp!

pdf_example_invoice.jpg

Part I, Part II