Rendering PDF views in ASP MVC using iTextSharp

Generating PDF files can be done by calling iTextSharp classes and methods directly, but consistency with the rest of MVC is preferable. In this post I’m going to explain how you can use iTextSharp to create PDF views the same way HTML views are created. What we need is to write our PDF files under the Views directory using a markup language. iTextSharp supports a special XML format that can be used in our views instead of HTML. One thing to note about aspx is that your tags don’t have to be HTML tags. You can type anything you want; of course Visual Studio will only recognize HTML but the ASP engine will spit that data out to the browser anyway. So, we are going to use regular views but we are going to write iTextSharp XML tags in them instead of HTML. Then we’ll need a way to intercept the result and convert it into PDF before it’s sent to the browser.

We are going to expand the Controller class with functionalities needed for handling PDF views. Our new class will be called MyController and it will inherit from MVC’s Controller. Any controller that needs to support PDF views will need to inherit from MyController instead of Controller. As you know, the Controller class has a method called ‘View’ which performs all that’s needed to be done for an HTML view to be rendered to the browser. Following a similar approach, we are going to create a new method called ‘ViewPdf’ that will send out a PDF file instead of an HTML file. But before we write that method, we’ll need to be able to render our views into a string instead of the browser. To do this we need to create a fake HttpResponse and give it a memory stream to write to. Then we’ll need to create a fake HttpContext to use that fake response. Then we can call ExecuteResult on our ActionResult object giving it the fake context. This will cause the view to be rendered to our memory stream. These steps will be included in a new method called RenderActionResultToString. This method will later on be used by ViewPdf to generate the PDF data.

protected string RenderActionResultToString(ActionResult result)
{
    // Create memory writer.
    var sb = new StringBuilder();
    var memWriter = new StringWriter(sb);

    // Create fake http context to render the view.
    var fakeResponse = new HttpResponse(memWriter);
    var fakeContext = new HttpContext(System.Web.HttpContext.Current.Request,
        fakeResponse);
    var fakeControllerContext = new ControllerContext(
        new HttpContextWrapper(fakeContext),
        this.ControllerContext.RouteData,
        this.ControllerContext.Controller);
    var oldContext = System.Web.HttpContext.Current;
    System.Web.HttpContext.Current = fakeContext;

    // Render the view.
    result.ExecuteResult(fakeControllerContext);

    // Restore old context.
    System.Web.HttpContext.Current = oldContext;

    // Flush memory and return output.
    memWriter.Flush();
    return sb.ToString();
}

Now that we can intercept our rendered views as strings, we can now convert them into PDF files. This will be done inside the ViewPdf method, which returns an ActionResult object, so it can be used in the controller the same way the View method is used. ViewPdf creates a memory stream to which the generated PDF will be written, then it gets the buffer from that memory stream and uses it to initialize the BinaryContentResult object that will be returned to the caller.

        protected ActionResult ViewPdf(object model)
        {
            // Create the iTextSharp document.
            Document doc = new Document();
            // Set the document to write to memory.
            MemoryStream memStream = new MemoryStream();
            PdfWriter writer = PdfWriter.GetInstance(doc, memStream);
            writer.CloseStream = false;
            doc.Open();

            // Render the view xml to a string, then parse that string into an XML dom.
            string xmltext = this.RenderActionResultToString(this.View(model));
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.InnerXml = xmltext.Trim();

            // Parse the XML into the iTextSharp document.
            ITextHandler textHandler = new ITextHandler(doc);
            textHandler.Parse(xmldoc);

            // Close and get the resulted binary data.
            doc.Close();
            byte[] buf = new byte[memStream.Position];
            memStream.Position = 0;
            memStream.Read(buf, 0, buf.Length);

            // Send the binary data to the browser.
            return new BinaryContentResult(buf, “application/pdf”);
        }

That’s all that’s needed in our MyController class, but what’s the BinaryContentResult class used inside the ViewPdf method? It’s a class inherited from ActionResult that is used to return arbitrary content, much like the ContentResult class, but returns binary data instead.

    public class BinaryContentResult : ActionResult
    {
        private string ContentType;
        private byte[] ContentBytes;

        public BinaryContentResult(byte[] contentBytes, string contentType)
        {
            this.ContentBytes = contentBytes;
            this.ContentType = contentType;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            var response = context.HttpContext.Response;
            response.Clear();
            response.Cache.SetCacheability(HttpCacheability.NoCache);
            response.ContentType = this.ContentType;

            var stream = new MemoryStream(this.ContentBytes);
            stream.WriteTo(response.OutputStream);
            stream.Dispose();
        }
    }

We are now ready to start writing PDF views. The following example shows a simple PDF view that renders the list of orders for a specific customer.

<%@ Page Language=”C#” Inherits=”System.Web.Mvc.ViewPage<Sample1.Models.Customer>” %>
<%@ Import Namespace=”Sample1.Models” %>

<?xml version=”1.0″ encoding=”UTF-8″ ?>
<itext creationdate=”2/4/2003 5:49:07 PM” producer=”iTextSharpXML”>
 <paragraph leading=”18.0″ font=”unknown” size=”16.0″ align=”Default”>
     <chunk>Orders in PDF</chunk>
 </paragraph>
 <paragraph leading=”18.0″ font=”unknown” size=”10.0″ align=”Default”>
  <chunk>Customer Name: <%= this.Model.Name %></chunk><newline />
  <chunk>Address: <%= this.Model.Address %></chunk><newline />
 </paragraph>
 <paragraph leading=”18.0″ font=”unknown” size=”10.0″ align=”Default”>
 <chunk font=”unknown” size=”12.0″>Orders:</chunk><newline />
 <% foreach (Order o in this.Model.Order)
       { %>
        <chunk font=”unknown” size=”10.0″><%= o.OrderId %>, <%= o.Description %>, <%= o.Amount %>, <%= o.Date %></chunk><newline />
 <% } %>
 </paragraph>
</itext>

We’ll name the view OrdersInPdf and our controller will then look like this:

    public class HomeController : MyController
    {
        private Sample1Entities entities = new Sample1Entities();

        public ActionResult OrdersInHtml()
        {
            Customer c = (
                from cust in entities.Customer
                where cust.CustomerId == 1
                select cust).First();
            c.Order.Load();
            return View(c);
        }

        public ActionResult OrdersInPdf()
        {
            Customer c = (
                from cust in entities.Customer
                where cust.CustomerId == 1
                select cust).First();
            c.Order.Load();
            // To render a PDF instead of an HTML, all we need to do is call ViewPdf
            // instead of View. This requires the controller to be inherited from
            // MyController instead of MVC’s Controller.
            return ViewPdf(c);
        }
    }

As the code implies, OrdersInHtml will render an HTML view which is the default behaviour of MVC, while OrdersInPdf will render a PDF instead. Notice that the only difference between the two is the return statement, OrdersInPdf calls ViewPdf instead of View. The programmer needs to make sure when he calls ViewPdf that the view contains iTextSharp XML data rather than HTML data. The other thing to notice in the controller is the first line which shows that the controller inherits from MyController instead of Controller.

iTextSharp also has the ability to convert HTML into PDF so we can modify the ViewPdf (or create a new method) to make it parse HTML instead of XML. However, it’s usually hard to get the expected formatting when you convert from HTML.

Download Sample

License
This article and the included code is licensed under The Code Project Open License (CPOL).

Share

11 thoughts on “Rendering PDF views in ASP MVC using iTextSharp”

  1. Hi – Great article & this looks like exactly what I am after, however I cannot reference ITextHandler no matter what I try (I can access all the rest of the required iTextSharp methods etc. I am using MVC 2.0 with .Net FX 3.5 – have you any idea why I cannot use the ITextHandler? Any help you can offer would be much appreciated.

  2. Hello Adam, thanks and I hope this is useful to you. I haven’t tried MVC 2.0, but this shouldn’t be different from MVC 1.0. Did you copy your itextsharp.dll file to some place accessible by your project (the bin folder for example)? If so, did you add a reference to it in your project’s references? Under Solution Explorer, right click on References then select Add Reference. From there you select the Browse tab and select the itextsharp.dll file.
    Also, you need to have this using statement:
    using iTextSharp.text.xml;
    at the top of your file, coz ITextHandler is defined under iTextSharp.text.xml namespace. If that still didn’t work then you might be using a different version of iTextSharp.

  3. I’m having the same problem as Adam. I’ve added the reference to iTextSharp and I’ve got the using statement at the top of the file but it still says that its an unknown reference.

  4. Hi,

    I am having the same problem as the other Adam. I have the itextsharp .dll referenced and can see all the other functions in it but there is no ITextHandler. I have even downloaded the iTextSharp source code, opened it in visual studio and done a search for the string “ITextHandler” in the entire solution – no matches. Any ideas? Have the removed it?

  5. iTextSharp 4.1.6 (if i recall correctly) has ITextHandler class. If your iTextSharp is 5.0.2 (latest version), there is no ITextHandler in there. A lot has been changed in latest iTextSharp version and I am also looking for code samples.

    Currently i use HTMLWorker class in the latest version. HTMLWorker’s Parse( ) method can do the same function.

  6. I’ve progressed on top coupled with added http://code-cruising.net/rendering-pdf-views-in-asp-mvc-using-itextsharp upon Stumbleupon.net for that reason my friends look at the application significantly. I in basic terms consumed Rendering PDF views in ASP MVC using iTextSharp Code Cruising when the discover position inside Digg.net bookmark your items, when i determined if at all sufficiently good for every man to company name your website report where it, the chances are you are going to find it saved exactly the same

  7. Hello! Someone in my Facebook group shared this site with us so
    I came to give it a look. I’m definitely enjoying the information. I’m bookmarking and will be tweeting this to my followers! Superb blog and terrific design.

  8. This is a good info but you are not using a “using” statement… my concern is that a memorystream should be handled inside a using statement… but how can it be done without the “Cannot access a closed Stream.” error…

  9. Not sure if ‘using’ is needed with memory streams, as those shouldn’t be holding any system handles and should be fine to wait until they are collected by the garbage collector. However, I don’t see a problem in adding the ‘using’ statement either.

Leave a Reply

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