Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Rendering PDF views in ASP MVC using iTextSharp

, 22 Mar 2010
Rate this:
Please Sign up or sign in to vote.
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. A downloadable sa

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.

License

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

Share

About the Author

sarmadka
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

 
QuestionError when use cshtml page instead of OrdersInPdf.aspx Pinmemberdipurajp27-May-14 23:24 
QuestionUseful code, but why the seemingly unnecessary code? PinmemberMember 101646004-Aug-13 13:38 
QuestionThe underlying provider failed on Open. Pinmemberartemoniks9-Jul-13 4:37 
GeneralMy vote of 1 Pinmemberdave_dv31-May-13 10:10 
GeneralMy vote of 1 Pinmemberdave_dv31-May-13 10:10 
QuestionNice article but like a few others have said PinmvpSacha Barber20-Jan-13 22:02 
Just use FileActionResult
 
Also I find this code easier to get View as string
 
protected string RenderViewToString<T>(string viewPath, T model)
        {
            ViewData.Model = model;
            using (var writer = new StringWriter())
            {
                var view = new WebFormView(viewPath);
                var data = new ViewDataDictionary<T>(model);
                var context = new ViewContext(ControllerContext, view, data, new TempDataDictionary(), writer);
                context.View.Render(context, writer);
                return writer.ToString();
            }
        }
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralHow can i add image in pdf file. Pinmembersonam_m11-Oct-12 0:10 
QuestionRender tables Pinmemberjselesan10-Oct-12 1:56 
GeneralMy vote of 5 PinmemberDudi Bedner9-Apr-12 23:24 
QuestionBinaryContentResult class advantage over the MVC's FileContentResult PinmemberDudi Bedner9-Apr-12 23:19 
AnswerRe: BinaryContentResult class advantage over the MVC's FileContentResult Pinmembersarmadka10-Apr-12 7:48 
Questionhtml to pdf Pinmemberuser01011213-Feb-12 2:57 
QuestionHow do I add an image into the pdf generated? PinmemberDr. Song Li8-Sep-11 10:41 
GeneralMy vote of 5 PinmemberPatrick Kalkman19-Jul-11 23:01 
QuestionITextHandler is not in IText 5.1.1.0 Pinmemberhsmcc22-Jun-11 3:51 
QuestionRe: ITextHandler is not in IText 5.1.1.0 PinmemberMartin Irigaray6-Jan-12 15:21 
AnswerRe: ITextHandler is not in IText 5.1.1.0 PinmemberMember 368900318-Jul-12 14:10 
Generalimages in xml Pinmemberjimibt8-Mar-11 5:48 
GeneralRe: images in xml Pinmembersarmadka8-Mar-11 6:51 
GeneralRe: images in xml Pinmemberjimibt9-Mar-11 1:02 
GeneralRe: images in xml PinmemberMiguelCouto12-May-11 13:17 
GeneralRe: images in xml Pinmemberjimibt12-May-11 14:13 
GeneralBinaryContentResult not necessary PinmemberCraig Howard17-Feb-11 5:23 
GeneralRe: BinaryContentResult not necessary PinmemberDudi Bedner10-Apr-12 0:14 
GeneralTable Sample PinmemberEng. Jalal1-Apr-10 2:56 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140921.1 | Last Updated 22 Mar 2010
Article Copyright 2010 by sarmadka
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid