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.