Friday, February 24, 2012

Rotativa, how to print PDF in Asp.Net MVC

Creating PDF docs in Asp.Net MVC is a fairly common functionality requested by LOB applications. Usually either for reporting or, more often, for having printable friendly documents (invoices, receipts etc).
I used to design a Crystal report document and then evaluate it and convert it to PDF in the web application. The desing part was ok but what really bothered me was the problems that often came up with deploying it. Version mismatches could really turn it into a nightmare.

Lately I came up using the excellent wkhtmltopdf tool to convert html content to PDF. It uses the WebKit engine (used by Chrome and Safari) to render html. The nice thing about it is that I can leverage my knowledge of html and css to obtain a good looking PDF and also it's quite fast.

It's a exe file not a dll library (a dll actually exists but it's not managed code and it has some problems related to usage in a multi-threaded app). It has to be executed from the Asp.net app spawning a process, so it requires some unusual coding from a web development perspective. Another downside is that it requires some work to set it up: copying exe and dll files in the web app solution, make sure they get copied over when building and publishing and some config stuff needed to make it access authenticated actions.

It seemed to me a perfect candidate to become a Nuget package. It was fairly easy to build it, except some quirks when trying to modify package properties.

I've named it Rotativa /rota'tiva/, which is Italian for rotary printing press.

With Rotativa all it takes to print a PDF is:
1. Install using the Nuget package manager (search for Rotativa) or, more conveniently, with the package manager console, typing:

Install-Package Rotativa
2. Writing a controller action to return a view representing the desired PDF output. This means just coding as usual to serve the data as regular MVC action. Such as:
public ActionResult Invoice(int invoiceId)
{
    var invoiceViewModel;
    // code to retrieve data from a database
    return View(invoiceViewModel);
}
Nothing special here, just an action. Perhaps the view used will have a different master page (or layout page) then the other views of the web app (or you’ll define a separete css for the print media, see http://www.w3.org/TR/CSS2/media.html). You can test and “preview” the results just using the browser, since we are working with a regular action, returning a html result.

3. When the html is ok you can just setup a special action returning a custom ActionResult: ActionAsPdf. The code will look like this:
public ActionResult PrintInvoice(int invoiceId)
{
  return new ActionAsPdf(
                 "Invoice", 
                 new { invoiceId= invoiceId }) 
                 { FileName = "Invoice.pdf" };
}
ActionAsPdf requires a string parameter with the name of the action to be converted to PDF. It can accept a parameter with other route data, in this case we are defining the invoiceId parameter. You can specify the name of the file being returned using the FIleName property.

To link to the PDF file in other views you just have to insert a regular link to the PrintInvoice action. Something like

@Html.ActionLink(“PrintInvoice”)
if you’re working with razor views.
And voila, that’s all and it just works, even if the printed action is protected with forms authentication.

Source code is on GitHub https://github.com/webgio/Rotativa.