Sunday, May 5, 2013

Create a PDF document based on user input dynamically

A PDF (Portable Document Format) document is one of the most versatile ways of delivering a document to the customer.  It is supported on every major OS from PCs, laptops to mobile. 

The most formidable framework available (IMHO) is iTextSharp available from SourceForge or you could simply Google it.  You will need to download the binaries (assemblies) in order to proceed with this example.  The good part of iTextSharp is that it is open source, which means it falls under the GNU License.  You are free to download and distribute it with your site as long as you provide it in an unmodified form.  So lets get started -

Open up VS2010 (or VS2012) and lets create a project to create our PDF.  Of course when we create the Default ASP.Net Website Microsoft gives us the template site that contains all the garbage. 

Basically, we need some fields to input data into so that we can then import that into our newly created PDF and then present it to the user.  So lets get rid of all that Microsoft template garbage and then put in some fields.  We also need to add an event, in our case it will be a button and the event will be the Click.  Finally, it is always good practice to add a status label so that you can let the user know if something goes wrong and give a message what happened -

<h1>My PDF Example</h1>
<
asp:Table ID="tblInfo" runat="server" BorderWidth="0px">
<
asp:TableRow>
<
asp:TableCell ColumnSpan="2">
Personal Information
</asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>Name: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtName" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>Street Address: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtStreet" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>City: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtCity" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>State: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtState" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>Zip: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtZip" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
</
asp:Table>
<
asp:Button ID="btnCreatePDF" runat="server" Text="Create PDF" />
<
asp:Label ID="lblStatus" runat="server" />


So our entire default.aspx HTML will look like -



<%@ Page Title="Home Page" Language="VB" MasterPageFile="~/Site.Master" 
AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<asp:Content ID="HeaderContent" runat="server"
ContentPlaceHolderID="HeadContent">
</
asp:Content>
<
asp:Content ID="BodyContent" runat="server"
ContentPlaceHolderID="MainContent">
<
h1>My PDF Example</h1>
<
asp:Table ID="tblInfo" runat="server" BorderWidth="0px">
<
asp:TableRow>
<
asp:TableCell ColumnSpan="2">
Personal Information
</asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>Name: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtName" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>Street Address: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtStreet" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>City: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtCity" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>State: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtState" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
<
asp:TableRow>
<
asp:TableCell>Zip: </asp:TableCell>
<
asp:TableCell>
<
asp:TextBox ID="txtZip" runat="server" />
</
asp:TableCell>
</
asp:TableRow>
</
asp:Table>
<
asp:Button ID="btnCreatePDF" runat="server" Text="Create PDF" />
<
asp:Label ID="lblStatus" runat="server" />
</
asp:Content>




This consists of the entire default.aspx page.  Now we need to move to the code behind.  This is a little more labor intensive, but we’ll break it down into sections so that we can figure out what is going on. 
First things first, we need to reference the iTextSharp assembly that we downloaded from the website.  Obviously, we need the binary (the dll file) – This article assumes you know how to reference assemblies inside VS2010



Lets open the default.aspx.vb (or code behind) file and start coding the actual creation of our PDF.  To start off, we need to declare our global imports.  This will allow us access to the methods and properties inside the assemblies.  We’ll use a system namespace so that we can buffer the document and then present it to the user.  Also, we’ll use the iTextSharp namespace so that it can assist us in creating our PDF -



Imports System.IO
Imports iTextSharp.text
Imports iTextSharp.text.pdf




Nothing too magical going on here.  Simple imports.  To get the ball rolling, we need to create a method that will actually create the document.  This method will need to have a trigger in which we will use the Click event of the button we put on the form.  So to get the method rolling, I created one called CreateForm() -



Public Sub CreateForm()
End Sub

Lets start creating the PDF.  In iTextSharp it is called a Document.  This represents the canvas that the PDF is created on.  You can specify the layout and margins in the instantiation call -




Dim doc As New Document(PageSize.LETTER)




You can specify the margins by passing in the properties like so -



Dim doc As New Document(PageSize.LETTER, 50, 50, 25, 25)




The ordinal sequence is Left, Right, Top then Bottom (after the page orientation property).  Lets go ahead and create our memorystream so that we can post the document to the user -



Dim streamer As New MemoryStream()




Now we need to actually start writing the PDF.  How do we do this?  Well with the PdfWriter of course -



Dim writer As PdfWriter = PdfWriter.GetInstance(doc, streamer)




Like any other connection in the framework, we need to call the Open() method to the object we are trying to connect to -



doc.Open()




So we are instantiating the variable with the GetInstance method.  We are passing in the Document and the Stream so it knows what instance we are trying to use (You can create more than one PDF at a time, obviously).  Now we need to do some setup.  We need to plan out our Pdf so that we can anticipate what we’ll need throughout the document.  For instance, Fonts -



Dim titleFont As Font = FontFactory.GetFont("Tahoma", 18, Font.BOLD)
Dim tableHeader As Font = FontFactory.GetFont("Tahoma", 14, Font.BOLD)
Dim boldBodyFont As Font = FontFactory.GetFont("Tahoma", 12, Font.BOLD)
Dim normalBodyFont As Font = FontFactory.GetFont("Tahoma", 12, Font.NORMAL)




Ok, so here we have 4 fonts.  It is pretty intuitive so I won’t insult anyone by going through a big description.  Instead of creating variables, you could simply pass in the font directly into the methods, but this leads to a lot of repetitive typing and for readability it is just simpler to create variables.



Now that we have our fonts setup, we can put some text on the document -



doc.Add(New Paragraph("Welcome to my PDF Example", titleFont))




You will notice that for the new paragraph, we pass in some text and then name our font variable.  Notice that we have defined the font variable to be Tahoma, 18pt and Bold.  This will give us the appearance of a header.  Now because we have a title, we need to put some space below it.  When you create stuff on the document, it does not automatically put in line spacing, you can either define it on the document or insert NEWLINE -



doc.Add(Chunk.NEWLINE)
doc.Add(Chunk.NEWLINE)




As you can see here, we are adding two new lines.

Now we need to build a table to put our dynamic data into.  This works much like building any other table in code behind.  We need to declare a table and then tell it how many columns we want -




Dim personalInfoTable As PdfPTable = New PdfPTable(3)

Here you can see that we are creating our table with 3 columns.  Let’s create our first column.  This column will span 2 columns as the table header.  You will see why we span only 2 in a little further down the article -




Dim cell As PdfPCell = New PdfPCell(New Phrase("Your Personal Information"))
cell.Colspan = 2
cell.HorizontalAlignment = 1




Here you can see we are creating a cell that we want to span 2 columns.  The horizontal alignment works like this 0 = Left, 1 = Centered, 2 = Right.

Now we’ll create another column and rotate it 90 degrees so that it will write text vertically instead of from left-right.  Due to the fact that we are collecting names and addresses, we’ll make it say CONFIDENTIAL! -




Dim warningcell As PdfPCell = New PdfPCell(New Phrase("Confidential!"))
warningcell.Rotation = 90
warningcell.Rowspan = 6




Like before we created the cell, but then we rotated it 90 degrees.  Finally, we want it to span the entire table and since we know how many rows there will be, we specify.



personalInfoTable.SetWidths(New Integer() {1, 1, 4})




Here we are setting the widths of the columns, notice I have three numbers in the array.  Now we need to go ahead and create the rest of our table -



personalInfoTable.AddCell(warningcell)
personalInfoTable.AddCell(cell)
personalInfoTable.AddCell(New Phrase("Name:", boldBodyFont))
personalInfoTable.AddCell(txtName.Text)
personalInfoTable.AddCell(New Phrase("Street Address:", boldBodyFont))
personalInfoTable.AddCell(txtStreet.Text)
personalInfoTable.AddCell(New Phrase("City:", boldBodyFont))
personalInfoTable.AddCell(txtCity.Text)
personalInfoTable.AddCell(New Phrase("State:", boldBodyFont))
personalInfoTable.AddCell(txtState.Text)
personalInfoTable.AddCell(New Phrase("Zip:", boldBodyFont))
personalInfoTable.AddCell(txtZip.Text)




So now we have all our cells added to the table.  Finally, we need to add the table to the document -



doc.Add(personalInfoTable)




Close the document for editing -



doc.Close()




Now we can provide the user the completed document through the memorystream -



Response.ContentType = "application/pdf"
Response.AddHeader("Content-Disposition", _
String.Format("attachment;filename=Receipt-{0}.pdf", "Sample"))
Response.BinaryWrite(streamer.ToArray())




Now we need our trigger, which is the button’s click event -



Protected Sub btnCreatePDF_Click(sender As Object, _
e As System.EventArgs) Handles btnCreatePDF.Click
CreateForm()
End Sub




That completes our code behind.  Here is the entire code behind as I have it with error checking and notification -



Imports System.IO
Imports iTextSharp.text
Imports iTextSharp.text.pdf

Partial Class _Default
Inherits System.Web.UI.Page

Public Sub CreateForm()
Dim doc As New Document(PageSize.LETTER)
Dim streamer As New MemoryStream()
Dim writer As PdfWriter = PdfWriter.GetInstance(doc, streamer)
Try
doc.Open()
'Setup Fonts
Dim titleFont As Font = FontFactory.GetFont("Tahoma", 18, Font.BOLD)
Dim tableHeader As Font = FontFactory.GetFont("Tahoma", 14, Font.BOLD)
Dim boldBodyFont As Font = FontFactory.GetFont("Tahoma", 12, Font.BOLD)
Dim normalBodyFont As Font = FontFactory.GetFont("Tahoma", 12, Font.NORMAL)

doc.Add(New Paragraph("Welcome to my PDF Example", titleFont))
doc.Add(Chunk.NEWLINE)
doc.Add(Chunk.NEWLINE)

Dim personalInfoTable As PdfPTable = New PdfPTable(3)
Dim cell As PdfPCell = New PdfPCell(New Phrase("Your Personal Information"))
cell.Colspan = 2
cell.HorizontalAlignment = 1

Dim warningcell As PdfPCell = New PdfPCell(New Phrase("Confidential!"))
warningcell.Rotation = 90
warningcell.Rowspan = 6

personalInfoTable.SetWidths(New Integer() {1, 1, 4})
personalInfoTable.AddCell(warningcell)
personalInfoTable.AddCell(cell)
personalInfoTable.AddCell(New Phrase("Name:", boldBodyFont))
personalInfoTable.AddCell(txtName.Text)
personalInfoTable.AddCell(New Phrase("Street Address:", boldBodyFont))
personalInfoTable.AddCell(txtStreet.Text)
personalInfoTable.AddCell(New Phrase("City:", boldBodyFont))
personalInfoTable.AddCell(txtCity.Text)
personalInfoTable.AddCell(New Phrase("State:", boldBodyFont))
personalInfoTable.AddCell(txtState.Text)
personalInfoTable.AddCell(New Phrase("Zip:", boldBodyFont))
personalInfoTable.AddCell(txtZip.Text)
doc.Add(personalInfoTable)

doc.Close()

Response.ContentType = "application/pdf"
Response.AddHeader("Content-Disposition", _
String.Format("attachment;filename=Receipt-{0}.pdf", "Sample"))
Response.BinaryWrite(streamer.ToArray())
Catch ex As Exception
lblStatus.Text = ex.Message.ToString()
End Try
End Sub


Protected Sub
btnCreatePDF_Click(sender As Object, _
e As System.EventArgs) Handles btnCreatePDF.Click
CreateForm()
End Sub
End Class




This should give you something like the following result -

Our WebPage -


image


Our PDF -


image



That completes the article.  Simple example of how to create a PDF from data in ASP.Net



Håþþ¥ .ñꆆïñg…

No comments:

Post a Comment