Sunday, January 26, 2014

How to generate the invoice form as PDF file using iTextSharp

We often generate the invoice form as PDF format and send the customer as reference. Microsoft Crystal Report and Telerik Report are handy but expensive for small business. There are other API. Among them, I prefer iTextSharp since it is widely used and lot of examples online. You can download here. I show you how to used iTextSharp for generating PDF as below :
Invoice PDF
Our interface has textboxs and gridview for accepting and displaying Data. It has two options (Download PDF and Send PDF with email). If select the first radio and press "Create PDF" button, it downloads the PDF. Otherwise, it gets the byte array of invoice PDF in code-behind. Web interface should be as below :

User Input Data
First, we download the "itextsharp.dll" and add reference at web site as below:
We need the following namespace in web page as below:
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Data;
Copy the following code into form tag for interface :
<form id="form1" runat="server">
<div>
    <asp:Label ID="Label1" runat="server" Text="Order No :" Width="120px"></asp:Label>
    <asp:TextBox ID="txtOrderNo" runat="server"></asp:TextBox><br/>
    <asp:Label ID="Label2" runat="server" Text="Customer Name :" Width="120px"></asp:Label>
    <asp:TextBox ID="txtCustomerName" runat="server">John Willion</asp:TextBox><br />
    <asp:Label ID="Label3" runat="server" Text="Address :" Width="120px"></asp:Label>
    <asp:TextBox ID="txtAddress" runat="server" Height="74px" TextMode="MultiLine" Width="249px">No. 123, New Panasin Street, Ramkhamkeang 24/1, Bangkapi, Bangkok, 10200.</asp:TextBox>
</div>
<div>
    <asp:RadioButton ID="rdoDownload" runat="server" Checked="True" GroupName="pdf" Text="Download PDF" />
    <asp:RadioButton ID="rdoSendEmail" runat="server" GroupName="pdf" Text="Send PDF with email" />
</div>
<div>
    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" Font-Names="Arial"
        Font-Size="11pt" AlternatingRowStyle-BackColor="#C2D69B" HeaderStyle-BackColor="green"
        AllowPaging="true" PageSize="10" OnPageIndexChanging="GridView1_PageIndexChanging">
        <Columns>
            <asp:BoundField ItemStyle-Width="150px" DataField="NO" HeaderText="NO" />
            <asp:BoundField ItemStyle-Width="150px" DataField="ITEM" HeaderText="ITEM" />
            <asp:BoundField ItemStyle-Width="150px" DataField="QUANTITY" HeaderText="QUANTITY" />
            <asp:BoundField ItemStyle-Width="150px" DataField="AMOUNT" HeaderText="AMOUNT(IDR)" />
        </Columns>
    </asp:GridView>
</div>
<div>
    <asp:Button ID="Button1" runat="server" Text="Create PDF" OnClick="Button1_Click" />
</div>
</form>

Copy the following code to code-behind for class variables and data binding for gridview :
//Class Variables
string orderNo = DateTime.Now.Ticks.ToString().Substring(0, 6);
string orderDate = DateTime.Now.ToString("dd MMM yyyy");
decimal totalAmtStr;
string accountNo = "0123456789012";
string accountName = "John Willion";
string branch = "Phahon Yothin Branch";
string bank = "Kasikorn Bank";

// for Gridview
DataTable dt = new DataTable();

protected void Page_Load(object sender, EventArgs e)
{
    //This sample used iTextShart.dll - 4.1.6.0
    // DataTable binding
    txtOrderNo.Text = orderNo;
    dt.Columns.Add("NO", Type.GetType("System.String"));
    dt.Columns.Add("ITEM", Type.GetType("System.String"));
    dt.Columns.Add("QUANTITY", Type.GetType("System.String"));
    dt.Columns.Add("AMOUNT", Type.GetType("System.String"));

    for (int i = 0; i < 10; ++i)
    {
        dt.Rows.Add();
        dt.Rows[i]["NO"] = (i + 1).ToString();
        dt.Rows[i]["ITEM"] = "Item " + i.ToString();
        dt.Rows[i]["QUANTITY"] = (i + 1).ToString();
        dt.Rows[i]["AMOUNT"] = (i + 1).ToString();
        totalAmtStr += (i + 1);
    }

    //GridView 
    GridView1.DataSource = dt;
    GridView1.DataBind();
}
Let's create "CreatePDF()" function in code-behind as below:
protected MemoryStream CreatePDF()
{
    // Create a Document object
    Document document = new Document(PageSize.A4, 70, 70, 70, 70);

    //MemoryStream
    MemoryStream PDFData = new MemoryStream();
    PdfWriter writer = PdfWriter.GetInstance(document, PDFData);

    // First, create our fonts
    var titleFont = FontFactory.GetFont("Arial", 14, Font.BOLD);
    var boldTableFont = FontFactory.GetFont("Arial", 10, Font.BOLD);
    var bodyFont = FontFactory.GetFont("Arial", 10, Font.NORMAL);
    Rectangle pageSize = writer.PageSize;

    // Open the Document for writing
    document.Open();
    //Add elements to the document here

    #region Top table
    // Create the header table 
    PdfPTable headertable = new PdfPTable(3);
    headertable.HorizontalAlignment = 0;
    headertable.WidthPercentage = 100;
    headertable.SetWidths(new float[] { 4, 2, 4 });  // then set the column's __relative__ widths
    headertable.DefaultCell.Border = Rectangle.NO_BORDER;
    //headertable.DefaultCell.Border = Rectangle.BOX; //for testing
    headertable.SpacingAfter = 30;
    PdfPTable nested = new PdfPTable(1);
    nested.DefaultCell.Border = Rectangle.BOX;
    PdfPCell nextPostCell1 = new PdfPCell(new Phrase("ABC Co.,Ltd", bodyFont));
    nextPostCell1.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
    nested.AddCell(nextPostCell1);
    PdfPCell nextPostCell2 = new PdfPCell(new Phrase("111/206 Moo 9, Ramkhamheang Road,", bodyFont));
    nextPostCell2.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
    nested.AddCell(nextPostCell2);
    PdfPCell nextPostCell3 = new PdfPCell(new Phrase("Nonthaburi 11120", bodyFont));
    nextPostCell3.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
    nested.AddCell(nextPostCell3);
    PdfPCell nesthousing = new PdfPCell(nested);
    nesthousing.Rowspan = 4;
    nesthousing.Padding = 0f;
    headertable.AddCell(nesthousing);

    headertable.AddCell("");
    PdfPCell invoiceCell = new PdfPCell(new Phrase("INVOICE", titleFont));
    invoiceCell.HorizontalAlignment = 2;
    invoiceCell.Border = Rectangle.NO_BORDER;
    headertable.AddCell(invoiceCell);
    PdfPCell noCell = new PdfPCell(new Phrase("No :", bodyFont));
    noCell.HorizontalAlignment = 2;
    noCell.Border = Rectangle.NO_BORDER;
    headertable.AddCell(noCell);
    headertable.AddCell(new Phrase(orderNo, bodyFont));
    PdfPCell dateCell = new PdfPCell(new Phrase("Date :", bodyFont));
    dateCell.HorizontalAlignment = 2;
    dateCell.Border = Rectangle.NO_BORDER;
    headertable.AddCell(dateCell);
    headertable.AddCell(new Phrase(orderDate, bodyFont));
    PdfPCell billCell = new PdfPCell(new Phrase("Bill To :", bodyFont));
    billCell.HorizontalAlignment = 2;
    billCell.Border = Rectangle.NO_BORDER;
    headertable.AddCell(billCell);
    headertable.AddCell(new Phrase(txtCustomerName.Text + "\n" + txtAddress.Text, bodyFont));
    document.Add(headertable);
    #endregion

    #region Items Table
    //Create body table
    PdfPTable itemTable = new PdfPTable(4);
    itemTable.HorizontalAlignment = 0;
    itemTable.WidthPercentage = 100;
    itemTable.SetWidths(new float[] { 10, 40, 20, 30 });  // then set the column's __relative__ widths
    itemTable.SpacingAfter = 40;
    itemTable.DefaultCell.Border = Rectangle.BOX;
    PdfPCell cell1 = new PdfPCell(new Phrase("NO", boldTableFont));
    cell1.HorizontalAlignment = 1;
    itemTable.AddCell(cell1);
    PdfPCell cell2 = new PdfPCell(new Phrase("ITEM", boldTableFont));
    cell2.HorizontalAlignment = 1;
    itemTable.AddCell(cell2);
    PdfPCell cell3 = new PdfPCell(new Phrase("QUANTITY", boldTableFont));
    cell3.HorizontalAlignment = 1;
    itemTable.AddCell(cell3);
    PdfPCell cell4 = new PdfPCell(new Phrase("AMOUNT(USD)", boldTableFont));
    cell4.HorizontalAlignment = 1;
    itemTable.AddCell(cell4);

    foreach (DataRow row in dt.Rows)
    {
        PdfPCell numberCell = new PdfPCell(new Phrase(row["NO"].ToString(), bodyFont));
        numberCell.HorizontalAlignment = 0;
        numberCell.PaddingLeft = 10f;
        numberCell.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
        itemTable.AddCell(numberCell);

        PdfPCell descCell = new PdfPCell(new Phrase(row["ITEM"].ToString(), bodyFont));
        descCell.HorizontalAlignment = 0;
        descCell.PaddingLeft = 10f;
        descCell.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
        itemTable.AddCell(descCell);

        PdfPCell qtyCell = new PdfPCell(new Phrase(row["QUANTITY"].ToString(), bodyFont));
        qtyCell.HorizontalAlignment = 0;
        qtyCell.PaddingLeft = 10f;
        qtyCell.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
        itemTable.AddCell(qtyCell);

        PdfPCell amtCell = new PdfPCell(new Phrase(row["AMOUNT"].ToString(), bodyFont));
        amtCell.HorizontalAlignment = 1;
        amtCell.Border = Rectangle.LEFT_BORDER | Rectangle.RIGHT_BORDER;
        itemTable.AddCell(amtCell);

    }
    // Table footer
    PdfPCell totalAmtCell1 = new PdfPCell(new Phrase(""));
    totalAmtCell1.Border = Rectangle.LEFT_BORDER | Rectangle.TOP_BORDER;
    itemTable.AddCell(totalAmtCell1);
    PdfPCell totalAmtCell2 = new PdfPCell(new Phrase(""));
    totalAmtCell2.Border = Rectangle.TOP_BORDER; //Rectangle.NO_BORDER; //Rectangle.TOP_BORDER;
    itemTable.AddCell(totalAmtCell2);
    PdfPCell totalAmtStrCell = new PdfPCell(new Phrase("Total Amount", boldTableFont));
    totalAmtStrCell.Border = Rectangle.TOP_BORDER;   //Rectangle.NO_BORDER; //Rectangle.TOP_BORDER;
    totalAmtStrCell.HorizontalAlignment = 1;
    itemTable.AddCell(totalAmtStrCell);
    PdfPCell totalAmtCell = new PdfPCell(new Phrase(totalAmtStr.ToString("#,###.00"), boldTableFont));
    totalAmtCell.HorizontalAlignment = 1;
    itemTable.AddCell(totalAmtCell);

    PdfPCell cell = new PdfPCell(new Phrase("*** Please note that ABC Co., Ltd’s bank account is USD Bank Account ***", bodyFont));
    cell.Colspan = 4;
    cell.HorizontalAlignment = 1;
    itemTable.AddCell(cell);
    document.Add(itemTable);
    #endregion

    Chunk transferBank = new Chunk("Your Bank Account:", boldTableFont);
    transferBank.SetUnderline(0.1f, -2f); //0.1 thick, -2 y-location
    document.Add(transferBank);
    document.Add(Chunk.NEWLINE);

    // Bank Account Info
    PdfPTable bottomTable = new PdfPTable(3);
    bottomTable.HorizontalAlignment = 0;
    bottomTable.TotalWidth = 300f;
    bottomTable.SetWidths(new int[] { 90, 10, 200 });
    bottomTable.LockedWidth = true;
    bottomTable.SpacingBefore = 20;
    bottomTable.DefaultCell.Border = Rectangle.NO_BORDER;
    bottomTable.AddCell(new Phrase("Account No", bodyFont));
    bottomTable.AddCell(":");
    bottomTable.AddCell(new Phrase(accountNo, bodyFont));
    bottomTable.AddCell(new Phrase("Account Name", bodyFont));
    bottomTable.AddCell(":");
    bottomTable.AddCell(new Phrase(accountName, bodyFont));
    bottomTable.AddCell(new Phrase("Branch", bodyFont));
    bottomTable.AddCell(":");
    bottomTable.AddCell(new Phrase(branch, bodyFont));
    bottomTable.AddCell(new Phrase("Bank", bodyFont));
    bottomTable.AddCell(":");
    bottomTable.AddCell(new Phrase(bank, bodyFont));
    document.Add(bottomTable);

    //Approved by
    PdfContentByte cb = new PdfContentByte(writer);
    BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, true);
    cb = writer.DirectContent;
    cb.BeginText();
    cb.SetFontAndSize(bf, 10);
    cb.SetTextMatrix(pageSize.GetLeft(300), 200);
    cb.ShowText("Approved by,");
    cb.EndText();
    //Image Singature
    iTextSharp.text.Image logo = iTextSharp.text.Image.GetInstance(Server.MapPath("~/Images/Bill_Gates2.png"));
    logo.SetAbsolutePosition(pageSize.GetLeft(300), 140);
    document.Add(logo);

    cb = new PdfContentByte(writer);
    bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, true);
    cb = writer.DirectContent;
    cb.BeginText();
    cb.SetFontAndSize(bf, 10);
    cb.SetTextMatrix(pageSize.GetLeft(70), 100);
    cb.ShowText("Thank you for your business! If you have any questions about your order, please contact us at 800-555-NORTH.");
    cb.EndText();

    writer.CloseStream = false; //set the closestream property
    // Close the Document without closing the underlying stream
    document.Close();
    return PDFData;
}
Copy the following function to code-behind for downloading the Invoice PDF:
protected void DownloadPDF(System.IO.MemoryStream PDFData)
{
    // Clear response content & headers
    Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.ContentType = "application/pdf";
    Response.Charset = string.Empty;
    Response.Cache.SetCacheability(System.Web.HttpCacheability.Public);
    Response.AddHeader("Content-Disposition", string.Format("attachment;filename=Receipt-{0}.pdf", orderNo));
    Response.OutputStream.Write(PDFData.GetBuffer(), 0, PDFData.GetBuffer().Length);
    Response.OutputStream.Flush();
    Response.OutputStream.Close();
    Response.End();
}
Finally, copy the following code into "Button1_Click" :
protected void Button1_Click(object sender, EventArgs e)
{
    if (rdoDownload.Checked)
    {
        DownloadPDF(CreatePDF());
    }
    else
    {
        MemoryStream ms = CreatePDF();
        ms.Position = 0; //Set pointer to the beginning of the stream
        byte[] PDFData = new byte[ms.Length];
        ms.Read(PDFData, 0, (int)ms.Length); // get byte arrary for PDF 
       // Attach byte array to email here
    }
}

Hope this helps,
SI THU

Reference :
http://www.4guysfromrolla.com/articles/030911-1.aspx
http://www.mikesdotnetting.com/Article/86/iTextSharp-Introducing-Tables

11 comments:

  1. Yes, this would be a great solution for all invoicing issues. Try http://www.fetchflow.com/Signup/PayPal It offers a robust free level with unlimited invoicing and customers. It allows you to accept Paypal.

    ReplyDelete
  2. thanks for this tutorial. Please help! I am trying to load data from my dataset into my invoice but it comes out blank every time

    ReplyDelete
    Replies
    1. Sorry, I reply late. Pls send me your project. I can look your code.

      Delete
    2. thanks a lot its extremely good to help............

      Delete
  3. thanks a lot its extremely good to help.
    http://www.odix.in/

    ReplyDelete
  4. Beginner C#, jQuery, SQL Server, ASP.NET MVC, LINQ and much more..

    Beginner Programming Tutorials

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Nice stuff

    Thank you SI Thu

    Rao Tran

    ReplyDelete
  7. Thanks for sharing the info, keep up the good work going.... I really enjoyed exploring your site. good resource.. best timesheet software

    ReplyDelete
  8. what is Gridview1, it seems not to be resolvable

    ReplyDelete