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: 27353

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

Justin Kusuma 7/24/2015 3:38 AM
In response to Posting Data With jQuery AJAX In ASP.NET Razor Web Pages
Hi Mike, thanks much for sharing such an article :) Really help me a lot... further, I'd like to...

Michael Easterbrook 7/22/2015 5:35 PM
In response to Inline Razor Syntax Overview
I removed the @ symbols and I am still getting the same error. It only occurs when I have an "if" a...

Sujay 7/22/2015 1:36 PM
In response to ASP.NET MVC, Entity Framework, One-to-Many and Many-to-Many INSERTS
can you explain how to link two tables so that it forms many to many relationship?(Article and...

Max G 7/21/2015 9:29 PM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Hi, I've opted for this solution in one of my applications but i've found that the apppool is and I...

Michael Easterbrook 7/20/2015 4:31 PM
In response to Inline Razor Syntax Overview
When I have the following code: @foreach (var procRow in procRowDecade) { if (@procRow[3] +...

Shanice 7/18/2015 10:58 PM
In response to A Better Way To Export Gridviews To Excel
Hi. I'm working with mvc. I need to add the above code in the business logic layer, however the...

Matt 7/18/2015 6:29 PM
In response to Nested Layout Pages with Razor
Cheers sir, nice explanation :)...

Keshavan 7/17/2015 9:06 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Hi Mike, I have followed exactly as illustrated in blog, I get error "StdSchedulerFactory.cs" not...

Paul Thiel 7/16/2015 5:17 PM
In response to ASP.NET 5 By Numbers
Comments Below: "The new version of ASP.NET is called ASP.NET 5. It is a framework for developing...

saket singh 7/16/2015 8:42 AM
In response to Scheduled Tasks In ASP.NET With Quartz.Net
hi Mike, great tutorial on Quartz.net , but i have One Problem , Everything is working fine as as...


© 2006 - 2015 Mike Brind