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:
public DataTable GetInvoiceHeader()
public DataTable GetInvoiceRows()
public DataTable GetInvoiceTotal()
public DataTable GetInvoicePayeeInfo()To create an instance of the class, just supply the file path to an XML file having the four tables.
// Read the sample XML file using the contructor, giving the file path
Invoice invoice = new Invoice(Server.MapPath("invoices") + "\\invoice_123456.xml");
// Create references for each of the on-row tables to make it easier to access the values in the table
// using syntax like this: drHead["invoiceId"].ToString()
DataRow drHead = invoice.GetInvoiceHeader().Rows[0];
DataRow drTotal = invoice.GetInvoiceTotal().Rows[0];
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.
// This is the method writing text to the content byte
private void writeText(PdfContentByte cb, string Text, int X, int Y, BaseFont font, int Size)
{
cb.SetFontAndSize(font, Size);
cb.ShowTextAligned(PdfContentByte.ALIGN_LEFT, Text, X, Y, 0);
}I use this method in code simply by calling it like this:
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:
// Add a footer template to the document
cb.AddTemplate(PdfFooter(cb, drPayee), 30, 1);The PdfFooter method does the work for us:
// Create the template and assign height
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.
// Begin writing the footer
tmpFooter.BeginText();
// Set the font and size
tmpFooter.SetFontAndSize(f_cn, 8);
// Write out details from the payee tabletmpFooter.ShowTextAligned(PdfContentByte.ALIGN_LEFT, "Text!!!", 0, 53, 0);
tmpFooter.EndText();
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.
foreach (DataRow drItem in invoice.GetInvoiceRows().Rows)
{
writeText(cb, "blah", left_margin, top_margin, f_cn, 10);
top_margin -= 12;
// Simpleage break function, check position
if(top_margin <= lastwriteposition)
{
// Okay, we need to switch page, end writing text
cb.EndText();
// Make the page break
document.NewPage();
// Start writing again on the new page
cb.BeginText();
// Assign the new write location on page two!
// Here you might want to implement a new row header creation
top_margin = 780;
}
}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!

Alternative Approaches for Future Projects
While iTextSharp provides granular control over PDF creation as we've seen throughout this series, there are alternative libraries worth considering for future invoice generation projects, e.g. IronPDF takes a different approach that can significantly reduce development time.
Rather than manually positioning elements using coordinates and content bytes, this library allows you to work with data sources and templates directly. You can bind your XML data (invoice_header, invoice_rows, invoice_total) to templates while the library handles positioning and page breaks automatically. It also supports HTML to PDF conversion using a Chrome rendering engine, meaning you could design invoice layouts using HTML/CSS and populate them with your XML or database data.
This template-based approach eliminates the need to calculate positions manually or handle page breaks in code - particularly useful for invoices where data amounts vary between documents. Both approaches have their place: choose based on whether you prioritize pixel-perfect control or faster development.
Please just leave a note if you want me to create the same article series for som PDF creation with IronPDF!
Ciao!
/Micke

Nikolai KimPosted Jul 18, 2022, 9:14 AM
Hi Micke, do you have experience using itextHsharp creating multiple table in PDF?
Alisa PervenenokPosted Apr 24, 2019, 1:06 AM
Micke, do you have any experience in using iText to export Excel-document to PDF?
Sushant KPosted Sep 4, 2017, 1:10 AM
.EmpXBODY{ FONT-SIZE: 14px;FONT-FAMILY: Calibri; padding:0px; margin:0px; }
Sushant KPosted Sep 4, 2017, 1:09 AM
Protected void btnPdf_Click(object sender, EventArgs e) { DataTable dtHead; tblButtonsHome.Visible = false; tblHeader.Visible = false; tblExitStatus.Visible = false; GridView gvtemp = new GridView(); gvtemp = (GridView)Page.FindControl("gvCompleteExitStatus"); dtHead = clsemp.getEmployeeDetails(EmployeeCode, true); if (dtHead.Rows.Count > 0) { gvHeader.DataSource = dtHead; gvHeader.DataBind(); } dt = (DataTable)Session["CompleteExitStatus"]; if (dt.Columns.Contains("Emp. Status")) { //dt.Columns.Remove("Employee Remarks"); dt.Columns.Remove("Emp. Status"); dt.Columns.Remove("Asset Remarks"); dt.Columns.Remove("Asset Date"); dt.Columns.Remove("FLM Recovery"); dt.Columns.Remove("Dept Recovery"); } gvtemp.DataSource = dt; gvtemp.DataBind(); gvtemp.GridLines = GridLines.Both; gvtemp.HeaderStyle.BackColor = System.Drawing.ColorTranslator.FromHtml("#5D7B9D"); gvtemp.HeaderStyle.ForeColor = System.Drawing.Color.Black; gvtemp.PagerStyle.BackColor = System.Drawing.ColorTranslator.FromHtml("#284775"); gvtemp.RowStyle.BackColor = System.Drawing.ColorTranslator.FromHtml("#F7F6F3"); Response.ContentType = "application/pdf"; Response.AddHeader("content-disposition", "attachment;filename=" + EmployeeName + ".pdf"); Response.Cache.SetCacheability(HttpCacheability.NoCache); StringWriter stringWriter = new StringWriter(); HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter); MainTable.RenderControl(htmlTextWriter); StringReader stringReader = new StringReader(stringWriter.ToString()); //Document Doc = new Document(PageSize.A4.Rotate(), 10f, 5f, 5f, 5f); Document Doc = new Document(PageSize.A4.Rotate(), 5, 5, 5,1); HTMLWorker htmlparser = new HTMLWorker(Doc); PdfWriter.GetInstance(Doc, Response.OutputStream); Doc.Open(); htmlparser.Parse(stringReader); Doc.Close(); Response.Write(Doc); Response.End(); //} //catch (Exception ex) //{ // Response.Redirect("~/RedirectPage.aspx?errorCode=" + 10001 + "&errorDesc=" + ex.Message.ToString().Replace("\n\r", "")); //} }
Sushant KPosted Sep 4, 2017, 1:02 AM
Hi Micke i have Used your Code to generate pdf but i am getting one blank page(page1) and then my data is load from page 2 to page 6.. actually i don't want that blank page at all i really don't know from where it is coming..... kindly guide me ...Please ASAP. i am pasting my html as well code behind. ............................................................................................................................................................. <%@ Page Language="C#" AutoEventWireup="true" Inherits="frmCompleteExitStatus" CodeBehind="frmCompleteExitStatus.aspx.cs" EnableEventValidation="false" %> <%@ Register TagName="wucheader" TagPrefix="uc1" Src="~/wucHeader.ascx" %> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Employee Exit Form</title> <link rel="stylesheet" type="text/css" href="CommonStyleSheet.css" /> </head> <body class="EmpXBODY"> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <uc1:wucheader runat="server" ID="wucHeader" Visible="false" /> <table id="tblButtonsHome" runat="server" cellpadding="0" cellspacing="0" style="width: 100%;"> <tr> <td colspan="2" style="text-align: left"> <asp:Button ID="ExportToExcel" runat="server" Text="Export To Excel" class="button_example" OnClick="ExportToExcel_Click" /> <asp:Button ID="btnPdf" runat="server" Text="PDF" class="button_example" OnClick="btnPdf_Click" /> </td> <td colspan="2" style="text-align: right"> <asp:HyperLink ID="hplkHome" runat="server"> <asp:Image ID="Image3" runat="server" ImageUrl="~/images/home.png" /></asp:HyperLink> </td> </tr> </table> <table id="tblHeader" runat="server" style="width: 100%;"> <tr> <td> <asp:Label ID="lblEmpID" runat="server" Text="Employee Code :" CssClass="LABEL"></asp:Label> <asp:Label ID="txtEmpID" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblEmpName" runat="server" Text="Name : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtEmpName" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblDept" runat="server" Text="Dept : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtDept" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblLocation" runat="server" Text="Location : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtLocation" runat="server" CssClass="LABEL"></asp:Label> </td> </tr> <tr> <td> <asp:Label ID="lblRole" runat="server" Text="Cadre :" CssClass="LABEL"></asp:Label> <asp:Label ID="txtRole" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblDesignation" runat="server" Text="Designation :" CssClass="LABEL"></asp:Label> <asp:Label ID="txtDesignation" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblFunction" runat="server" Text="Function : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtFunction" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblFLM" runat="server" Text="FLM Name : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtFLMName" runat="server" CssClass="LABEL"></asp:Label> </td> </tr> <tr> <td> <asp:Label ID="lblFunctionHead" runat="server" Text=" Function Head Name : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtFunctionHead" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblDOJ" runat="server" Text="Date of Joining: " CssClass="LABEL"></asp:Label> <asp:Label ID="txtDOJ" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblLastWorkingDate" runat="server" Text="Date of Relieving : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtLastWorkgDate" runat="server" CssClass="LABEL"></asp:Label> </td> <td> </td> </tr> <tr> <td> <asp:Label ID="lblEmpUpdatedOn" runat="server" Text="Employee Update Status : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtEmpUpdatedOn" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblFLMUpdatedOn" runat="server" Text="FLM Update Status : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtFLMUpdatedOn" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblDeptUpdatedOn" runat="server" Text="Department Update Status : " CssClass="LABEL"></asp:Label> <asp:Label ID="txtDeptUpdatedOn" runat="server" CssClass="LABEL"></asp:Label> </td> <td> <asp:Label ID="lblRecovery" runat="server" BackColor="#FFFF99" Font-Bold="True" Font-Size="24px" ForeColor="#006600"></asp:Label> </td> </tr> <tr> <td colspan="4"> <table cellpadding="0" cellspacing="1" border="1" width="99%" runat="server" id="tblExitStatus" style="background-color: #003f6b"> <tr> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsFLMApproved" runat="server" Text="FLM Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsAdminDeptApproved" runat="server" Text="Admin Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsITDeptApproved" runat="server" Text="IT Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsComplianceDeptApproved" runat="server" Text="Compliance Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsHRDeptApproved" runat="server" Text="HR Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsFinanceDeptApproved" runat="server" Text=" Finance Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> <td style="color: #ffffff; padding: 1px 0; background-color: #00497c; text-align: center"> <asp:Label ID="lblIsPayRollDeptApproved" runat="server" Text=" Payroll Approved" CssClass="StatusTEXTBOX"></asp:Label> </td> </tr> <tr> <td id="tdIsFLMApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsFLMApproved" Width="130px" runat="server" Style="margin-left: 0px" CssClass="StatusTEXTBOX"></asp:Label> </td> <td id="tdIsAdminApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsAdminDeptApproved" Width="130px" runat="server" Style="margin-left: 0px;" CssClass="StatusTEXTBOX"></asp:Label> </td> <td id="tdIsITApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsITDeptApproved" Width="130px" runat="server" Style="margin-left: 0px" CssClass="StatusTEXTBOX"></asp:Label> </td> <td id="tdIsComplianceApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsComplianceDeptApproved" Width="130px" runat="server" Style="margin-left: 0px" CssClass="StatusTEXTBOX"></asp:Label> </td> <td id="tdIsHRApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsHRDeptApproved" Width="130px" runat="server" Style="margin-left: 0px" CssClass="StatusTEXTBOX"></asp:Label> </td> <td id="tdIsFinanceApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsFinanceDeptApproved" Width="130px" runat="server" Style="margin-left: 0px" CssClass="StatusTEXTBOX"></asp:Label> </td> <td id="tdIsPayRollApproved" runat="server" style="color: #003f6b; background-color: #ffffff; text-align: center;"> <asp:Label ID="txtIsPayRollApproved" Width="130px" runat="server" Style="margin-left: 0px" CssClass="StatusTEXTBOX"></asp:Label> </td> </tr> </table> </td> </tr> </table> <table id="MainTable" runat="server" cellspacing="0" border="0" style="width: 100%;"> <tr> <td> <table cellpadding="0" cellspacing="0" style="width: 100%; text-align: left; text-indent: 10px;"> <tr> <td style="text-align: left; text-indent: 10px;"> <asp:GridView ID="gvHeader" runat="server" CellPadding="4" ForeColor="#333333" ShowHeader="false" AutoGenerateColumns="true" GridLines="None" RowStyle-Wrap="false"> <EditRowStyle BackColor="#999999" /> <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" /> <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" /> <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" /> <RowStyle BackColor="#F7F6F3" ForeColor="#333333" VerticalAlign="Top" /> <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" /> <SortedAscendingCellStyle BackColor="#E9E7E2" /> <SortedAscendingHeaderStyle BackColor="#506C8C" /> <SortedDescendingCellStyle BackColor="#FFFDF8" /> <SortedDescendingHeaderStyle BackColor="#6F8DAE" /> </asp:GridView> <asp:GridView ID="gvCompleteExitStatus" runat="server" CellPadding="4" ForeColor="#333333" AutoGenerateColumns="true" GridLines="None" OnRowDataBound="gvCompleteExitStatus_RowDataBound" RowStyle-Wrap="false"> <EditRowStyle BackColor="#999999" /> <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" /> <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" /> <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" /> <RowStyle BackColor="#F7F6F3" ForeColor="#333333" /> <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" /> <SortedAscendingCellStyle BackColor="#E9E7E2" /> <SortedAscendingHeaderStyle BackColor="#506C8C" /> <SortedDescendingCellStyle BackColor="#FFFDF8" /> <SortedDescendingHeaderStyle BackColor="#6F8DAE" /> </asp:GridView> </td> </tr> </table> </td> </tr> </table> </form> </body> </html>............................................................... ............ my css code for .EmpXBODY { FONT-SIZE: 14px;FONT-FAMILY: Calibri; padding:0px; margin:0px; } ........................................................................................... . ........ protected void btnPdf_Click(object sender, EventArgs e) { DataTable dtHead; tblButtonsHome.Visible = false; tblHeader.Visible = false; tblExitStatus.Visible = false; GridView gvtemp = new GridView(); gvtemp = (GridView)Page.FindControl("gvCompleteExitStatus"); dtHead = clsemp.getEmployeeDetails(EmployeeCode, true); if (dtHead.Rows.Count > 0) { gvHeader.DataSource = dtHead; gvHeader.DataBind(); } dt = (DataTable)Session["CompleteExitStatus"]; if (dt.Columns.Contains("Emp. Status")) { //dt.Columns.Remove("Employee Remarks"); dt.Columns.Remove("Emp. Status"); dt.Columns.Remove("Asset Remarks"); dt.Columns.Remove("Asset Date"); dt.Columns.Remove("FLM Recovery"); dt.Columns.Remove("Dept Recovery"); } gvtemp.DataSource = dt; gvtemp.DataBind(); gvtemp.GridLines = GridLines.Both; gvtemp.HeaderStyle.BackColor = System.Drawing.ColorTranslator.FromHtml("#5D7B9D"); gvtemp.HeaderStyle.ForeColor = System.Drawing.Color.Black; gvtemp.PagerStyle.BackColor = System.Drawing.ColorTranslator.FromHtml("#284775"); gvtemp.RowStyle.BackColor = System.Drawing.ColorTranslator.FromHtml("#F7F6F3"); Response.ContentType = "application/pdf"; Response.AddHeader("content-disposition", "attachment;filename=" + EmployeeName + ".pdf"); Response.Cache.SetCacheability(HttpCacheability.NoCache); StringWriter stringWriter = new StringWriter(); HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter); MainTable.RenderControl(htmlTextWriter); StringReader stringReader = new StringReader(stringWriter.ToString()); //Document Doc = new Document(PageSize.A4.Rotate(), 10f, 5f, 5f, 5f); Document Doc = new Document(PageSize.A4.Rotate(), 5, 5, 5,1); HTMLWorker htmlparser = new HTMLWorker(Doc); PdfWriter.GetInstance(Doc, Response.OutputStream); Doc.Open(); htmlparser.Parse(stringReader); Doc.Close(); Response.Write(Doc); Response.End(); //} //catch (Exception ex) //{ // Response.Redirect("~/RedirectPage.aspx?errorCode=" + 10001 + "&errorDesc=" + ex.Message.ToString().Replace("\n\r", "")); //} }
Sonu ChaudharyPosted Jun 8, 2016, 2:56 PM
good one ..
abosnicPosted Jan 19, 2016, 9:59 AM
thanks
Pamela PaulosPosted Sep 26, 2014, 7:03 AM
hi can anyone help on how i can generate an invoice based on data from a database? I'm relatively new to this
andre toddPosted Nov 4, 2013, 10:37 AM
This is great. Just what I needed
sadun ssssssPosted Apr 12, 2013, 10:54 PM
Thank a lot for this tutorial part.....
Tati OkoPosted Dec 17, 2011, 6:11 PM
This article is very useful for me as I am newbie to iTextSharp for PDF creation. I have successfully accommodated the Footer DataTable (GetInvoicePayeeInfo) in my project. However, when there are many pages because of large data, How do you include the Footer on every Odd pages ? Thanks and best regards!
Dang TrangPosted Nov 16, 2011, 6:11 AM
Thank you very much for this nice article. It helps me alot.
isg bglPosted Mar 12, 2011, 7:24 AM
Thanks for your article. Can you help me, how to make PDF of .aspx page. Regards