Exporting The Razor WebGrid To PDF Using iTextSharp

This article looks at how you can provide your users with the ability to export the contents of a Razor Web Pages WebGrid to a PDF file using the popular free PDF library, iTextSharp.

First, you need to get hold of the iTextSharp library. You can do this within WebMatrix via the built-in Nuget feature. Click the Nuget button on the main menu and type "iTextSharp" into the searhc box. There are a couple of versions of iTextSharp available - the most recent one is version 5, which is released under the GNU Affero General Public Licence. This is not as permissive as the licence (LGPL/MPL) under which the previous version was released. Not only that, but the differences in functionality between 4 and 5 are so small that I chose to use version 4.1.6, which is also the one I based my popular series of iTextSharp articles on.

Once you have decided which version you want to use, click the Install button and agree to the licence condiitons. The dll file will be installed in your bin folder.

Now you need a grid:

@{
    Page.Title = "Export To PDF";
    var db = Database.Open("Northwind");
    var query = "SELECT CustomerID, CompanyName, ContactName, Address, City, Country, Phone FROM Customers";
    var data = db.Query(query);
    var grid = new WebGrid(data, ajaxUpdateContainerId: "grid");
}
<h1>Export to PDF</h1>
<div id="gridContainer">
    <div id="grid">
        @grid.GetHtml(    
            tableStyle : "table",
            alternatingRowStyle : "alternate",
            headerStyle : "header",
            columns: grid.Columns(
                grid.Column("CustomerID", "ID"),
                grid.Column("CompanyName", "Company Name"),
                grid.Column("ContactName", "Contact Name"),
                grid.Column("Address"),
                grid.Column("City"),
                grid.Column("Country"),
                grid.Column("Phone")
            )
        )
        <img src="/images/pdf-icon.png" id="pdf" alt="Export to PDF" title="Export to PDF" />
    </div>
</div>

When the user clicks on the image, it should result in the data being downloaded as a PDF file. At the moment, it is just an image and it appears below the grid. Here's a little bit of jQuery to move the image to the footer area of the grid and to change the cursor when the user hovers over it:

<script type="text/javascript">
    $(function () {
        $('#pdf').appendTo($('tfoot tr td')).on('hover', function () {
            $(this).css('cursor', 'pointer');
        });
        $('#pdf').on('click', function () {
            $('<iframe src="/GeneratePdf"></iframe>').appendTo('body').hide();
        });
    });
</script>

The jQuery code also adds a handler to the click event of the image. It creates an iframe which it then adds to the body element, and then it makes the display property equal 'none' using the jQuery hide command. The src for the iframe is a file called GeneratePdf.cshtml, which is responsible for creating the PDF file. The hidden iframe technique is a clean way to manage downloads via AJAX without leaving the current page. Adding an iframe dynamically like this effectively "sucks" the HTTP response from the src URL through to the current page.

Here's the code for GeneratePdf.cshtml:

@using iTextSharp.text;
@using iTextSharp.text.pdf;
@{
    Layout = null;
    Response.AddHeader("Content-disposition", "attachment; filename=report.pdf");
    Response.ContentType = "application/octet-stream";
    var db = Database.Open("Northwind");
    var sql = "SELECT CustomerID, CompanyName, ContactName, Address, City, Country, Phone FROM Customers";
    var data = db.Query(sql);
    var columns = data.First().Columns;

    var doc = new Document();
    PdfWriter.GetInstance(doc, Response.OutputStream);
    doc.SetPageSize(PageSize.A4.Rotate());
    var arial = FontFactory.GetFont("Arial", 8, Color.BLACK);
    var arialBold = FontFactory.GetFont("Arial", 10, Font.BOLD, Color.BLACK);
    doc.Open();
    var table = new PdfPTable(columns.Count) {
        TotalWidth = 700f, 
        LockedWidth = true
    };

    foreach(var column in columns){
        table.AddCell(new Phrase(column, arialBold));
    }
    foreach(var row in data){
        foreach(var column in columns){
            table.AddCell(new Phrase(row[column] != null ? row[column].ToString() : string.Empty, arial));
        }
    }
    doc.Add(table);
    doc.Close();
}

As with any file that is intended to deliver a non-html response, the Layout is set to null to prevent stray HTML being included in the output. The Content-Disposition value is set to attachment, and the Content-type is set to application/octet-stream. This combination results in the browser offering a choice to the user - save or open, rather than attempting to display the response. The same query is executed against the database and the column names are extracted from the first record in the query result. Then a PDF file is created, and the PdfWrite.GetInstance method is used to ensure that the resulting document is written to the Response. The document is rotated to Landscape, and a couple of fonts are created for styling - one 8pt Arial black, and the other is 10pt and bold. A table is created and the column names are added using the bold font, followed by the data.

This article shows how easy it is to use the Database helper to generate data to be displayed in a report, and how to use iTextSharp to generate the PDF file. If you want to know more about using iTextSharp, you can read these articles.

The source code for the sample site that accompanies this article is available as a GitHub repo.

 

Date Posted:
Last Updated:
Posted by:
Total Views to date: 27843

9 Comments

- bob

A good report should have:
- page numbers
- header and footer
- header row (1st row) of the table should be repeated on all of the produced pages.
...

- Mike

@bob

Yep - you can do all that using iTextSharp. I might even show how to do that in a separate article some time....

- Mridul Raj

Hi mike,

Is there a way to add tool tip text to the textbox in a pdf file created using itextsharp?

- Mike

@Mridul

I don't know.

- Massimo

Hi, Thank You for your articles.
I got an error for:
var columns = data.First().Columns;

'System.Collections.ObjectModel.ReadOnlyCollection' does not contain a definition for 'First'
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Collections.ObjectModel.ReadOnlyCollection' does not contain a definition for 'First'

Source Error:


Line 10: "@CustomerID = " + CustomerID.ToString();
Line 11: var data = db.Query(sql);
Line 12: var columns = data.First().Columns;
Line 13:
Line 14: var doc = new Document();

- prins patrick

Great tutorial! Helped me a lot! Thanks!

- satinder singht

Hi mike,
Nice article,
Am also waiting for Header/Footer separate article....

- Satyabrata Mohapatra

As always,extremely helpful.Used this technique successfully for an web application,in which around 80,000 students downloaded their admit card in PDF for a Joint Entrance Examination.

It is a habit now, for me to visit your website regularly with a cup of coffee every morning.

Thanks a lot sir.

- Sithelo

Great tutorial. I was wondering how I can use a value from my query say CompanyName and add it to a cell something like AddCell(new Phrase(data.CompanyName, arial). How do you insert your query value in cell when the query result is a single record?

Recent Comments

sandeep 8/28/2015 7:03 AM
In response to 7 C# 6.0 Features That Every ASP.NET Developer Should Know About
very good article, i like it........keep writing such quality article in future. thx Mike....

Hassan, MVC Learner 8/28/2015 6:37 AM
In response to Get The Drop On ASP.NET MVC DropDownLists
Great Help, simple, great and patiently explained article !...

Anvesh 8/28/2015 12:39 AM
In response to ASP.NET MVC DropDownLists - Multiple Selection and Enum Support
what if we are taking postback values from FormCollections instead of an array int[] category. How...

Ben 8/27/2015 10:50 PM
In response to Simple File Download Protection with ASP.NET
Is it possible to setup your project to publish files to outside of your root directory? I would to...

Fred 8/26/2015 12:50 AM
In response to WebMatrix Opens Wrong Version Of Visual Studio
I enjoyed many of your tutorials but the problem is none of the tutorials are combined like most be....

Muhammad Ashikuzzaman 8/25/2015 2:48 PM
In response to Managing Checkboxes And Radios In ASP.NET Razor Web Pages
That's a very good tips for razor...

Sergey 8/25/2015 8:32 AM
In response to More Flexible Routing For ASP.NET Web Pages
Hi. How I can set up my site to get urldata from link for default page? site.com/default/1-...

Tony Gray 8/25/2015 6:27 AM
In response to Adding Validation
Hi Mike, Really helpful article and series. Thanks. Small typo in 4th paragraph you have so...

amanda n 8/25/2015 12:38 AM
In response to Solving the Operation Must Use An Updateable Query error
Thank you very much. I'm a uni student and while solving coding problems is usually enjoyable and me...

salman 8/23/2015 9:25 AM
In response to How To Send Email In ASP.NET MVC
thanks ......


© 2006 - 2015 Mike Brind