Click here to Skip to main content
15,867,330 members
Articles / Web Development / HTML

Rendering PDF Views in ASP MVC using iTextSharp

Rate me:
Please Sign up or sign in to vote.
4.75/5 (16 votes)
22 Mar 2010CPOL3 min read 241.4K   35   32
This post talks about how you can add a new type of views to your ASP.NET MVC application to support PDF files. The technique allows you to define your views inside the Views folder using a markup language in a way similar to the way the regular views are constructed and displayed.

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 in which 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.

C#
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.

C#
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.

C#
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.

ASP.NET
<%@ 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:

C#
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.

This article was originally posted at http://sarmad-khalid.info/blog?p=13

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Canada Canada
Sarmad Khalid is a senior software engineer specialized in .NET, LAMP, and C++ groups of technologies. He has long experience developing web and desktop applications for several clients across North America and the Middle East.

Comments and Discussions

 
QuestionStack empty error in MVC 4 Pin
thirupathiCst16-Mar-17 2:50
thirupathiCst16-Mar-17 2:50 
QuestionAdd bold font Pin
Pedro Cassettari20-Mar-15 7:42
Pedro Cassettari20-Mar-15 7:42 
GeneralMy vote of 2 Pin
Belkhiri92-Mar-15 6:56
Belkhiri92-Mar-15 6:56 
QuestionError when use cshtml page instead of OrdersInPdf.aspx Pin
dipurajp27-May-14 23:24
dipurajp27-May-14 23:24 
QuestionThe underlying provider failed on Open. Pin
artemoniks9-Jul-13 4:37
artemoniks9-Jul-13 4:37 
GeneralMy vote of 1 Pin
dave_dv31-May-13 10:10
dave_dv31-May-13 10:10 
GeneralMy vote of 1 Pin
dave_dv31-May-13 10:10
dave_dv31-May-13 10:10 
QuestionNice article but like a few others have said Pin
Sacha Barber20-Jan-13 22:02
Sacha Barber20-Jan-13 22:02 
GeneralHow can i add image in pdf file. Pin
sonam_m11-Oct-12 0:10
sonam_m11-Oct-12 0:10 
QuestionRender tables Pin
jselesan10-Oct-12 1:56
jselesan10-Oct-12 1:56 
GeneralMy vote of 5 Pin
Dudi Bedner9-Apr-12 23:24
Dudi Bedner9-Apr-12 23:24 
QuestionBinaryContentResult class advantage over the MVC's FileContentResult Pin
Dudi Bedner9-Apr-12 23:19
Dudi Bedner9-Apr-12 23:19 
AnswerRe: BinaryContentResult class advantage over the MVC's FileContentResult Pin
sarmadka10-Apr-12 7:48
sarmadka10-Apr-12 7:48 
QuestionHow do I add an image into the pdf generated? Pin
Dr. Song Li8-Sep-11 10:41
Dr. Song Li8-Sep-11 10:41 
GeneralMy vote of 5 Pin
Patrick Kalkman19-Jul-11 23:01
Patrick Kalkman19-Jul-11 23:01 
QuestionITextHandler is not in IText 5.1.1.0 Pin
hsmcc22-Jun-11 3:51
hsmcc22-Jun-11 3:51 
You used ITextSharp 4.1.6.0 in your sample. How can you make your sample work in 5.1.1.0?

Thanks
QuestionRe: ITextHandler is not in IText 5.1.1.0 Pin
Martin Irigaray6-Jan-12 15:21
Martin Irigaray6-Jan-12 15:21 
AnswerRe: ITextHandler is not in IText 5.1.1.0 Pin
Flavia.Obreja18-Jul-12 14:10
Flavia.Obreja18-Jul-12 14:10 
Generalimages in xml Pin
jimibt8-Mar-11 5:48
jimibt8-Mar-11 5:48 
GeneralRe: images in xml Pin
sarmadka8-Mar-11 6:51
sarmadka8-Mar-11 6:51 
GeneralRe: images in xml Pin
jimibt9-Mar-11 1:02
jimibt9-Mar-11 1:02 
GeneralRe: images in xml Pin
MiguelCouto12-May-11 13:17
MiguelCouto12-May-11 13:17 
GeneralRe: images in xml Pin
jimibt12-May-11 14:13
jimibt12-May-11 14:13 
GeneralBinaryContentResult not necessary Pin
Craig Howard17-Feb-11 5:23
Craig Howard17-Feb-11 5:23 
GeneralRe: BinaryContentResult not necessary Pin
Dudi Bedner10-Apr-12 0:14
Dudi Bedner10-Apr-12 0:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.