PDF reporting using ASP.NET MVC

Posted by on January 28, 2012 in ASP.NET MVC | 13 comments

PDF reporting using ASP.NET MVC

Almost every web application, needs a reporting option. Many times the client wants to generate a PDF report that corresponds with the web page he or she is viewing. Using iTextSharp, a free C# PDF library this is possible. This article describes and includes a solution for creating reports using iTextSharp and ASP.NET MVC3.

Background

iTextSharp is a free C# PDF library that is ported from the Java-PDF Library iText. iText was launched in 2000 and is a popular open source Java library for programmatic creation and manipulation of PDF. As with many successful open-source Java libraries it was ported to C# under the name iTextSharp. iTextSharp has been under development since 2008 and is distributed under the GNU Affero General Public License version 3.

At its core iTextSharp is an api to manipulate PDF creation. For a recent project I was looking for a solution that would enable me to create PDF documents from existing HTML documents or Views used in an ASP.NET MVC3 project. The ideal solution for me would be to throw an existing ASP.NET MVC3 view at it and let it generate a PDF that is exactly the same as the HTML rendered by a browser.

The solution described here uses the HTMLWorker class from iTextSharp to generate a PDF from an HTML view. HTMLWorker is a iTextSharp class that is able to parse an HTML document and generate a PDF using the what is called the SimpleParser of iTextSharp. Notice the name SimpleParser which indicates that not all HTML elements and css styles are supported. However, I was able to get good results using this solution.

To add reporting to your ASP.NET MVC3 project, first add the PDFReportGenerator project to your solution and add the iTextSharp.dll binary to your solution. PDFReportGenerator needs a reference to the binary. By adding the binary to the solution you are able to build the project directly from source control. Check the demo src for an example.

Creating a report.

Follow the following steps to generate an actual report from your web application.

  1. Create a controller that derives from PdfViewController.
  2. Create a view that generates the HTML which should be translated to a PDF report.
  3. Create an action on a controller which calls the ViewPDF method on the PdfViewController.
  4. Create a link to trigger the action on the controller.

The following paragraph describes these steps in more detail.

Create a controller that derives from PdfViewController

Create a new or reuse an existing Controller and let it derive from PdfViewController from the PdfReportGenerator project.

This enables your controller to call the ViewPDF method of the PDFViewController which generates the real PDF. In the demo project this is the HomeController.

Create a view that generates the HTML

Create a View that should be translated to a report. This could be an existing view or a new view specially for reporting. I usually create a new view as it lets me control the HTML markup for the report. As stated earlier the report generator does not support all the HTML markup. In the demo project this is the PrintDemo view.

Below the PrintDemo view from the demo project is shown. This is just a simple ASP.NET Razor view with a table and some rows. It uses a strongly typed model but that it not necessary. A tip when trying to design your report is to add borders to your table or div. Using these borders when looking at the generated PDF you can clearly see the start and end of the areas of your report.

@using MvcReportGeneratorDemo.Models
@model CustomerList
<br />
<table cellpadding="3" cellspacing="3">
    <tr border="1" bgcolor="#777777" color="#ffffff">
        <td>Name</td>
        <td>Address</td>
        <td>Place</td>
    </tr>
    @foreach (Customer customer in Model)
    {
        <tr border="1">
            <td>@customer.Name</td>
            <td>@customer.Address</td>
            <td>@customer.Place</td>
        </tr>
    }
</table>

Create an action which calls the ViewPDF method

The PdfViewController class from which your controller derives, contains a ViewPDF method. This method has the following signature.

protected ActionResult ViewPdf(string pageTitle,
            string viewName, object model)

Parameters

pageTitle
Type: System.StringThe title of the report which appears on the header of the page.
viewName
Type: System.StringThe name of the view which should be converterd to a report.
model
Type: System.Object

The model that is rendered by the view.

This methods generates the HTML view and converts into a PDF report and sends this PDF as a binary stream back to the client. This means that when the client has a PDF plugin installed, the PDF is shown inside the browser.

From an action inside your controller this method should be called to generate the report and send it to the cliënt. The following action from the Demo application generates the PDF. “Customer report” is the title of the report, “PrintDemo is the name of the view and the model is returned by the CreateCustomerList() which as its name implies generates a dummy list with customers.

public ActionResult PrintCustomers()
{
   return this.ViewPdf("Customer report",
        "PrintDemo", CreateCustomerList());
}

The last step is to create a link on a page that calls this action to actually print the report.

Trigger the action on the controller

A simple method to create a link to trigger the action on the controller is by using an ActionLink. This link calls the action that we defined on the controller.

@Html.ActionLink("Print customers",
      "PrintCustomers", null,
      new { target = "_blank" })

These are the steps you need to create PDF reports from your ASP.NET MVC3 projects.

Read the next part of the article if you are interested how into the details of how the PdfReportGenerator actually converts the ASP.NET MVC3 view into a report.

Detailed Reporting Project Overview

The PdfReportGenerator project consist of 6 classes which can be seen in the image below. The PdfReportGenerator assembly works by rendering the ASP.NET MVC3 view into a string and converting this string with HTML using the iTextSharp into a PDF report.

Class model used for PDF reporting

Rendering an ASP.NET MVC3 view into a string

The class HtmlViewRenderer is responsible for rendering the ASP.NET view into a string. The method RenderViewToString has the following signature.

public string RenderViewToString(Controller controller,
      string viewName, object viewData)

The arguments are viewName which is the name of the view that should get rendered to a string including the model viewData that is needed by the view. The controller is necessary

to be able to use to render the view using the view engine of ASP.NET MVC.

Convert the html string into a PDF byte array

Once we have the html in a string the StandardPdfRenderer converts the html string into a PDF byte array. The class StandardPdfRenderer has a method Render with the following signature.

public byte[] Render(string htmlText, string reportTitle)

The htmlText is the rendered view as a string that was produced by the HtmlViewRender, the pageTitle is as the name suggests the title of the report.

Send the byte array back to the client as a stream

The last step is to convert the byte array into an instance of the BinaryContentResult class. The class BinaryContentResult derives from the ASP.NET MVC ActionResult. It overrides the ExecuteResult and returns the content as a binary stream.

The class PdfViewController is the class that combines these classes. The method ViewPdf uses all the three previously mentioned classes to generate the PDF as shown in the code below

protected ActionResult ViewPdf(string pageTitle,
                    string viewName, object model)
{
 // Render the view html to a string.
 string htmlText =
       this.htmlViewRenderer.RenderViewToString(this,
           viewName, model);
 // Let the html be rendered into a PDF document
 // through iTextSharp.
 byte[] buffer = standardPdfRenderer.Render(htmlText,
           pageTitle);
 // Return the PDF as a binary stream to the client.
 return new BinaryContentResult(buffer,
           "application/pdf");
}

Points of Interest

Colors
iTextsharp supports color out of the box, in the demo application the background colors of the rows are alternated using different colors. These colors are visible in the report.

New page support
One thing that I needed with my project that was not supported by the HTML conversion in iTextSharp was functionality to force a page break.Most of the time with reporting you need to be able to force a page break, for example if you want a graph to start always on a new page. The way I solved this was to add support for it to iTextSharp.

As it is open-source you are able to add new features to it. I added support for a non existing HTML tag called <np /> which forces iTextSharp to create a new page. I created a patch so that it could be committed to the iTextSharp project. I do not know if it will be included in the main trunk of the project as it feels somewhat strange to invent new html tags just to support a page break. But if you need it, you can use the patch to compile iTextSharp with new page support.

Images
It is possible to add images to the report by using an <img src="" /> tag. I had no success with dynamically generated images. So therefore, I generated the images that I needed before the action conversion process took place.

ASP.Net Support
It should be possible to use the same kind of solution using older versions of ASP.NET MVC. However, I have not tried it.

Other Options
After I wrote this post several people mentioned other alternatives for generating PDF reports using ASP.NET MVC. Michael Johnson wrote a blog post about using WkHtmlToPDF and someone mentioned using Flying Saucer for generating PDF’s.

The source code of the solution can be found on CodeProject

13 Comments

  1. Very nice article. This type of article is required for every mvc developer . I will apply this into my next project. thanks Patrick.

  2. Please could you specify more or provide an example about how to solve the problem with the images? Thanks

    • Store a path to your image in a filed of your viewmodel that is filling the report and it will work.

      • I’m facing this problem too. When you said, “Store a path to your image in a field”. Were you talking about a full path to the image?
        Is there a way to specify a relative path?

        Thanks in advance

  3. can you help me, i need to find way, after create a report in reportviwe converting to pdf and show the pdf in new tab of explorer or firefox or…

    • Modify the protected ActionResult ViewPdf

      If you want to make a download link add this line:
      Response.AddHeader(“Content-Disposition”, String.Format(“attachment; filename={0}.pdf”, pageTitle));

      and if you want to display it in another tab add this line:

      Response.AddHeader(“Content-Disposition”, String.Format(“inline; filename={0}.pdf”, pageTitle));

      and then the last line should

      return File(buffer, “application/pdf”);

      Regards

  4. First,
    to say that this is an amazing contribution to iTextSharp MVC community.

    I have a problem displaying Cyrillic letters č,ć as they are completely missing from the generated *.pdf file.

    I tried modifying BaseFont using Times from System fonts but it’s not working.

    string pathToTimes = Environment.GetEnvironmentVariable(“SystemRoot”) + “\\fonts\\times.ttf”;
    baseFont = BaseFont.CreateFont(pathToTimes, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
    Font myFont = new Font(baseFont, 9f, Font.NORMAL);

    The title however displays Cyrillic letters and that is strange.

    Can you help?

  5. When i derivate MyController from the PdfViewController, its generates an exception cause Controller must be derivated too… Wha i have to do, sorry for my bad english..

    Thankou.

  6. First of all, thanks for the code, excellent work !

    My question is: How do you change the page layout from portrait to landscape ?

    Tjanks in advance

    Tobias

  7. This is awesome!.. I have manipulated the MVC part of it a bit and used for MVC 2. Thank you You saved me a lot of development time.

  8. I have been Looking for solution to render PDF for HTML Content and i used your approach in mvc2. I am facing an error “Could not load file or assembly ‘itextsharp, Version=10.0.0.0, Culture=neutral, PublicKeyToken=8354ae6d2174ddca’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0×80131040)”. I have added assemply in reference and also in web.config add assembly. But this error is persisting. Any help?

  9. When i am tryng to add image than it compelling me to have a image folder from c drive. Cant i give path from my project ??

  10. Its a very awesome article i had ever seen… I liked its simplicity

Leave a Comment

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>