quinta-feira, 10 de fevereiro de 2011

ASP.NET - Dynamically generate .pdf


Generating .pdf document on the fly is a very frequently asked feature in any of web or windows application. In this article, I shall show how to generate .pdf document using an Open Source .dll iTextSharp.
My sample page looks like below

Prerequisite

To generate the .pdf document using this article, you will need to download the Opern source .dll from http://sourceforge.net/projects/itextsharp/ and refer in your project. To refer it in your project, right click it and select "Add Reference ....". Select the .dll from hard disk and clickk OK.


Video of this article

 You can watch the video of this article at http://www.dotnetfunda.com/tutorials/videos/x72-pdf-generator-in-net--dynamically-generate-pdf-in-aspnet-.aspx

Lets start with designing the .pdf generator page in .NET

Here, I have shown
  1. How to generate .pdf from GridView dynamically on the fly
  2. Generate .pdf and download
  3. Download the .pdf with custom text
  4. Generate .pdf with image
To show this example, I have taken 4 asp:Button controls and a asp:GridView control that will bind the data from the database. My .aspx page looks like below.
<form id="form1" runat="server">
<div id="divGridView">
<asp:gridview id="GridView1" runat="server" enableviewstate="False" width="100%" BorderWidth="1"
cellpadding="4" forecolor="#333333" gridlines="None" autogeneratecolumns="false"
datakeynames="AutoId" autogeneratedeletebutton="false" EmptyDataText="No records found" >
<HeaderStyle BackColor="#507CD1" HorizontalAlign="Left" Font-Bold="True" ForeColor="White" />
<Columns>
<asp:BoundField DataField="AutoId" HeaderText="AutoId" />
<asp:TemplateField HeaderText="Edit">
<ItemTemplate>
<a href="javascript:void(0)" onclick="ShowEditBox(<%# Eval("AutoId") %>)" title="Edit">Edit</a>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<span id="PName<%# Eval("AutoId") %>"><%# Eval("Name") %></span>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="Address" DataField="Address" />
<asp:BoundField HeaderText="City" DataField="City" />
<asp:BoundField HeaderText="Phone" DataField="Phone" />
<asp:TemplateField HeaderText="Delete">
<ItemTemplate>
<span onclick="return confirm('Are you sure?')">
<a href="javascript:DeleteRecord(<%# Eval("AutoId") %>)" title="Delete"><font color="red">Delete?</font></a>
</span>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:gridview>
<asp:Label ID="lblMessage" runat="server" EnableViewState="false" />
<asp:Button ID="btnGen" runat="server" Text="Generate PDF Doc" OnClick="GenerateOnlyPDF" />
<asp:Button ID="Button1" runat="server" Text="Download PDF" OnClick="GeneratePDFAndDownload" />
<asp:Button ID="Button2" runat="server" Text="Download PDF with custom text" OnClick="GeneratePDFWithText" />
<asp:Button ID="Button3" runat="server" Text="Generate PDF with Image" OnClick="GeneratePDFwithImage" />
</div>
</form>

Do not worry about the hyperlink control have placed under GridView, here our moto is to explain the GridView data into .pdf document.

Namespaces to be used

You will have to use at least following namespaces to work for the sample provided with this article.
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.html.simpleparser;
In order to show the .NET .pdf generation, I have created a common function called GeneratePDF. Its code looks like below

A common GeneratePDF method

This method takes path, fileName, download and text parameters and act acordingly.
/// <summary>
/// generate the .pdf
/// </summary>
/// <param name="path">path of the document</param>
/// <param name="fileName">name of the .pdf documen</param>
/// <param name="download">is this downloadable</param>
/// <param name="text">text to place in the .pdf</param>
private void GeneratePDF(string path, string fileName, bool download, string text)
{
var document = new Document();
try
{
if (download)
{
PdfWriter.GetInstance(document, Response.OutputStream);
}
else
{
PdfWriter.GetInstance(document, new FileStream(path + fileName, FileMode.Create));
}
// generates the grid first
StringBuilder strB = new StringBuilder();
document.Open();
if (text.Length.Equals(0)) // export the text
{
BindMyGrid();
using (StringWriter sWriter = new StringWriter(strB))
{
using (HtmlTextWriter htWriter = new HtmlTextWriter(sWriter))
{
GridView1.RenderControl(htWriter);
}
}
}
else // export the grid
{
strB.Append(text);
}
// now read the Grid html one by one and add into the document object
using (TextReader sReader = new StringReader(strB.ToString()))
{
List<IElement> list = HTMLWorker.ParseToList(sReader, new StyleSheet());
foreach (IElement elm in list)
{
document.Add(elm);
}
}
}
catch (Exception ee)
{
lblMessage.Text = ee.ToString();
}
finally
{
document.Close();
}
}
Above function, first creates a new instance of the Document object (found in the iTextSharp.dll) and based on whether we want the .pdf document to be downloable or not it fires PDFWriter.GetInstance method withResponse.OutputStream or the name of the file with FileMode.Create. Later, if we do not pass any text into this method, then it bind the GridView and use GridView.RenderControl method to get the GridView html and append into the StringBuilder else append the text into the StringBuilder.
Next, it reads the string int the TextReader and then loop through each element find into the string using HTMLworker.ParseToList method and add them to the document.
NOTE: In the earlier version of iTextSharp HTMLParser.Parse method used to do the work that we are doing here using TextReader and looping through all the element but unfortunately in theiTextSharp 5.0.2.0 version I couldn't find this method, so I have written this work around. If anyone finds any better way parse the HTML in iTextSharp 5.0.2.0, kindly let me know by responding this article.
At last if any error occurs write into the Label control else close the Document object in the finally block. I will be using above function to generate the document for all 3 buttons shown in the 1st picture above.

An override method that is needed to export the GridView

Below method need to keep on the page where you are writing the Export functionality in case you are trying to expor the GridView into .pdf. This is to do with the GridView.RenderControl method. Nothing much to do in this, just ensure that this method is there in your code behind page when you are rendering the GridView programmatically.
/// <summary>
/// This is needed to avoid error while exporting that GridView must be placed under Form control with runat=server
/// </summary>
/// <param name="control"></param>
public override void VerifyRenderingInServerForm(Control control)
{
}
In case you are getting "RegisterForEventValidation can only be called during Render();" error, make sure that you have kept EnableEventValidation="false" in the Page directives of the .aspx page.
Lets start seeing each method for the button placed on the page (displayed in the 1st image above) one by one.

Generating .pdf document from GridView on the fly in .NET and save to the hard disk

In order to generate the document on the fly and save into the hard disck, I have created a path and fileName string variable that stores the path and .pdf document name to be used while generating the .pdf document.
/// <summary>
/// Generate .pdf
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void GenerateOnlyPDF(object sender, EventArgs e)
{
string path = Server.MapPath("~/");
string fileName = "pdfDocument" + DateTime.Now.Ticks + ".pdf";
GeneratePDF(path, fileName, false, "");
}
Make sure that your page has VerifyRenderingInServerForm method explained above in your code behind page.

Generate .pdf document from GridView dynamically and download

In this method, I have passed only fileName to the GeneratePDF method and rest parameters are either default or empty as I just need to generate the .pdf and download. The GeneratePDF method generates the .pdf document and last 5 lines of code for this method force it to be downlodable.
/// <summary>
/// Generate .pdf
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void GeneratePDFAndDownload(object sender, EventArgs e)
{
string fileName = "pdfDocument" + DateTime.Now.Ticks + ".pdf";
GeneratePDF("", fileName, true, "");
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.Flush();
Response.End();
}
Make sure that your page has VerifyRenderingInServerForm method explained above in your code behind page.
My generated .pdf from GridView looks like below

 

Generate .pdf dynamically in .NET with custom text and download

In this method, I have passed fileName and custom text to write in .pdf. Notice that I have passed 3rd parameter of the GeneratePDF method as true (so that this .pdf file will be forced to download). In this case, theStringBuilder of the GeneratePDF method will not have the GridView html content but the custom text we are passing, so the custom text would appear in the .pdf document.
/// <summary>
/// generate .pdf with some custom text
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void GeneratePDFWithText(object sender, EventArgs e)
{
string fileName = "pdfDocument" + DateTime.Now.Ticks + ".pdf";
string text = "<b>DotNetFunda.Com</b> is a <i>great</i> <u>resource for .NET.</u>";
GeneratePDF("", fileName, true, text);
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.Flush();
Response.End();
}

 

My generated .pdf document with custom text look like below


 

Generate .pdf in .NET with image

In this method, we create an instance of Document object (from iTextSharp.dll) and then create the instance of the PdfWriter with the document object and .pdf file to be written with FileMode.Create.
First, I have added ITFunda.com as the the text in the document and then to add the image inside the .pdf, we have created the iTextSharp.text.Image object by giving the path of the image (from our hard disk) and added to the document object. This method will create a .pdf document on the hard disk at root of the application.
/// <summary>
/// generate image with some text
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void GeneratePDFwithImage(object sender, EventArgs e)
{
string path = Server.MapPath("~/");
string fileName = "pdfDocument" + DateTime.Now.Ticks + ".pdf";
Document doc = new Document();
try
{
PdfWriter.GetInstance(doc, new FileStream(path + fileName, FileMode.Create));
doc.Open();
// Add some text
doc.Add(new Paragraph("ITFunda.Com"));
// Add the image now
iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(Server.MapPath("~/itfunda.gif"));
doc.Add(gif);
}
catch (Exception ex)
{
lblMessage.Text = ex.ToString();
}
finally
{
doc.Close();
}
}
My generated .pdf document with custom text and image looks like below

A very good article on working with images with iTextSharp is written by Mikesdotnetting, you can read it at http://www.mikesdotnetting.com/Article/87/iTextSharp-Working-with-images

Conclusion

Generating .pdf in .NET has become easier now, thanks to the team behind iTextSharp.
Hope you liked this article. Thanks for reading. Keep reading and sharing your knoweldge.

Sem comentários:

Enviar um comentário