Monday, April 15, 2013

How to Create Asp.Net MVC 3 Report ?


What is iTextSharp ?
  • 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
  • iTextSharp has been under development since 2008

What does iTextSharp do ?
  • Generate documents and Reports based on data from an XML file or a database
  • Create maps and books, exploiting numerous interactive features available in PDF
  • Add bookmarks, page numbers, watermarks, and other features to existing PDF documents
  • Split or concatenate pages from existing PDF files
  • Fill out interactive forms
  • Serve dynamically generated or manipulated PDF documents to a web browser

How to Create a Report ?

Step 1 : Open Visual Studio 2010

Step 2 : File ==> New Project as MvcPDFReport


New Project as MvcPDFReport


Step 3 : Select "Internet Application" Project Template

Select "Internet Application" Project Template


After above Steps :

Complete project


Step 4 : After that File ==> Add ==> New Project


New Project

Step 5 : Visual C# ==> Class Library as iTextSharpReportGenerator

Class Library as iTextSharpReportGenerator


After All above Steps :
Complete project


How to Create  iTextSharp Report API ?

Step 1 : Download iTextSharp DLL

Download iTextSharp DLL

Step 2 : Extract above Zip File is as below

Extract above Zip File is as below

Below Steps for "iTextSharpReportGenerator" Class Library Project

Step 1 : Add References for the itextsharp.dll , System.Web and System.Web.Mvc
             
Add References

Step 2 : Add ==> New Class as PdfViewController

PdfViewController.cs 

using System.Web.Mvc;

namespace iTextSharpReportGenerator
{
 /// <summary>
 /// Extends the controller with functionality for rendering PDF views
 /// </summary>
 public class PdfViewController : Controller
 {
   private readonly HtmlViewRenderer _htmlViewRenderer;
   private readonly StandardPdfRenderer _standardPdfRenderer;

   public PdfViewController()
   {
      this._htmlViewRenderer = new HtmlViewRenderer();
      this._standardPdfRenderer = new StandardPdfRenderer();
   }

  protected ActionResult ViewPdf(string pageTitle,string viewName,object model)
  {
   // Render the view html to a string
   var 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");
  }
 }
}


Step 3 : Add ==> New Class as BinaryContentResult

BinaryContentResult.cs

using System.IO;
using System.Web;
using System.Web.Mvc;

namespace iTextSharpReportGenerator
{
    /// <summary>
    /// An ActionResult used to send binary data to the browser
    /// </summary>
    public class BinaryContentResult : ActionResult
    {
        private readonly string _contentType;
        private readonly 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.Public);
            response.ContentType = this._contentType;

            using (var stream = new MemoryStream(this._contentBytes))
            {
                stream.WriteTo(response.OutputStream);
                stream.Flush();
            }
        }
    }
}

Step 4 : Add ==> New Class as StandardPdfRenderer

StandardPdfRenderer.cs

using System.IO;
using iTextSharp.text;
using iTextSharp.text.html.simpleparser;
using iTextSharp.text.pdf;

namespace iTextSharpReportGenerator
{
  /// <summary>
  /// This class is responsible for rendering a html text string to a PDF 
  /// document using the html renderer of iTextSharp
  /// </summary>
 public class StandardPdfRenderer
 {
   private const int HorizontalMargin = 40;
   private const int VerticalMargin = 40;

   public byte[] Render(string htmlText, string pageTitle)
   {
     byte[] renderedBuffer;

     using (var outputMemoryStream = new MemoryStream())
      {
        using (var pdfDocument = new Document(PageSize.A4, HorizontalMargin, HorizontalMargin, VerticalMargin, VerticalMargin))
       {
        PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDocument, outputMemoryStream);
        pdfWriter.CloseStream = false;
        pdfWriter.PageEvent = new PrintHeaderFooter { Title = pageTitle };
        pdfDocument.Open();
        using (var htmlViewReader = new StringReader(htmlText))
         {
           using (var htmlWorker = new HTMLWorker(pdfDocument))
            {
               htmlWorker.Parse(htmlViewReader);
            }
         }
       }

        renderedBuffer = new byte[outputMemoryStream.Position];
        outputMemoryStream.Position = 0;
        outputMemoryStream.Read(renderedBuffer, 0, renderedBuffer.Length);
     }

     return renderedBuffer;
   }
  }
  }


Step 5 : Add ==> New Class as FakeView

FakeView.cs

using System;
using System.IO;
using System.Web.Mvc;

namespace iTextSharpReportGenerator
{
    /// <summary>
    /// Defines the FakeView type
    /// </summary>
    public class FakeView : IView
    {
        public void Render(ViewContext viewContext, TextWriter writer)
        {
            throw new NotImplementedException();
        }

    }
}


Step 6 : Add ==> New Class as PrintHeaderFooter

PrintHeaderFooter.cs

using System;
using System.Globalization;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace iTextSharpReportGenerator
{
    /// <summary>
    /// This class represents the standard header and footer for a PDF print
    /// application by Extending the PdfPageEventHelper (iTextSharp Class)
    /// </summary>
    public class PrintHeaderFooter : PdfPageEventHelper
    {
      private PdfContentByte _pdfContent;
      private PdfTemplate _pageNumberTemplate;
      private BaseFont _baseFont;
      private DateTime _printTime;

      public string Title { get; set; }

      public override void OnOpenDocument(PdfWriter writer, Document document)
      {
        _printTime = DateTime.Now;
        _baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
        _pdfContent = writer.DirectContent;
        _pageNumberTemplate = _pdfContent.CreateTemplate(50, 50);
      }

      public override void OnStartPage(PdfWriter writer, Document document)
       {
         base.OnStartPage(writer, document);
         Rectangle pageSize = document.PageSize;

         if (Title != string.Empty)
          {
            _pdfContent.BeginText();
            _pdfContent.SetFontAndSize(_baseFont, 11);
            _pdfContent.SetRGBColorFill(0, 0, 0);
            _pdfContent.SetTextMatrix(pageSize.GetLeft(40), pageSize.GetTop(40));
            _pdfContent.ShowText(Title);
            _pdfContent.EndText();
          }
        }

      public override void OnEndPage(PdfWriter writer, Document document)
      {
         base.OnEndPage(writer, document);
         int pageN = writer.PageNumber;
         string text = pageN + " - ";
         float len = _baseFont.GetWidthPoint(text, 8);

         Rectangle pageSize = document.PageSize;
         _pdfContent = writer.DirectContent;
         _pdfContent.SetRGBColorFill(100, 100, 100);

         _pdfContent.BeginText();
         _pdfContent.SetFontAndSize(_baseFont, 8);
         _pdfContent.SetTextMatrix(pageSize.Width / 2, pageSize.GetBottom(30));
         _pdfContent.ShowText(text);
         _pdfContent.EndText();

         _pdfContent.AddTemplate(_pageNumberTemplate, (pageSize.Width / 2) + len, pageSize.GetBottom(30));

          _pdfContent.BeginText();
          _pdfContent.SetFontAndSize(_baseFont, 8);
          _pdfContent.ShowTextAligned(PdfContentByte.ALIGN_RIGHT, _printTime.ToString(CultureInfo.InvariantCulture), pageSize.GetRight(40), pageSize.GetBottom(30), 0);
          _pdfContent.EndText();
       }

       public override void OnCloseDocument(PdfWriter writer, Document document)
       {
          base.OnCloseDocument(writer, document);

          _pageNumberTemplate.BeginText();
          _pageNumberTemplate.SetFontAndSize(_baseFont, 8);
          _pageNumberTemplate.SetTextMatrix(0, 0);
          _pageNumberTemplate.ShowText(string.Empty + (writer.PageNumber - 1));
          _pageNumberTemplate.EndText();
       }
    }
}


Step 7 : Add ==> New Class as HtmlViewRenderer 

HtmlViewRenderer.cs

using System.IO;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace iTextSharpReportGenerator
{
    /// <summary>
    /// This class is responsible for rendering a HTML view into a string
    /// </summary>
    public class HtmlViewRenderer
    {
      public string RenderViewToString(Controller controller, string viewName, object viewData)
       {
          var renderedView = new StringBuilder();
          using (var responseWriter = new StringWriter(renderedView))
          {
            var fakeResponse = new HttpResponse(responseWriter);
            var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
            var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeContext), controller.ControllerContext.RouteData, controller.ControllerContext.Controller);

             var oldContext = HttpContext.Current;
             HttpContext.Current = fakeContext;

             using (var viewPage = new ViewPage())
              {
               var html = new HtmlHelper(CreateViewContext(responseWriter, fakeControllerContext), viewPage);
               html.RenderPartial(viewName, viewData);
               HttpContext.Current = oldContext;
              }
          }

        return renderedView.ToString();
      }

      private static ViewContext CreateViewContext(TextWriter responseWriter, ControllerContext fakeControllerContext)
       {
         return new ViewContext(fakeControllerContext, new FakeView(), new ViewDataDictionary(), new TempDataDictionary(), responseWriter);
       }
   }
}

After above Steps Class Library Project is as below


Complete project

Class Diagram for above Report API


Class Diagram for above Report API


How to use Above Report API in MVC 3 Project (i.e. MvcPDFReport)?

Step 1 : Add ==> New Class as Provider inside the Model Folder

Provider.cs

namespace MvcPDFReport.Models
{
    /// <summary>
    /// Provider model
    /// </summary>
    public class Provider
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string Place { get; set; }
    }
}

  
Step 2 : Add ==> New Class as ProviderList inside the Model Folder

ProviderList.cs

using System.Collections.Generic;

namespace MvcPDFReport.Models
{
    /// <summary>
    /// ProviderList model which extends Provider collection
    /// </summary>
    public class ProviderList : List<Provider>
    {
        public string ImageUrl { get; set; }
    }
}


Step 3 : Extends the PdfViewController functionality on HomeController

HomeController.cs

using System.Web.Mvc;
using MvcPDFReport.Models;
using iTextSharpReportGenerator;

namespace MvcPDFReport.Controllers
{
 /// <summary>
 /// Extends the PdfViewController with functionality for rendering PDF views
 /// </summary>
 public class HomeController : PdfViewController
  {
   public ActionResult PrintProviders()
   {
     var providerList = CreateCustomerList();
     FillImageUrl(providerList, "iTextSharp.png");
     return this.ViewPdf("Software Providers Report", "PdfReport", providerList);
   }

   #region Private Methods

   private void FillImageUrl(ProviderList providerList, string imageName)
   {
     if (Request.Url == null) return;
     var url = string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"));
     providerList.ImageUrl = url + "Content/" + imageName;
   }

   private ProviderList CreateCustomerList()
   {
   return new ProviderList()
    {
    new Provider {Id=1,Name="Inexis Consulting",Address="Colombo 4",Place="Sri Lanka"},
    new Provider {Id=2,Name="Microsoft",Address ="Washington",Place="USA"},
    new Provider {Id=3,Name="IBM",Address="Armonk, New York",Place="USA" },
    new Provider {Id=4,Name="HP",Address="California",Place="USA"},
    new Provider {Id=5,Name="Novell",Address="Provo,Utah",Place="USA"},
    new Provider {Id=6,Name="Google",Address="California",Place="USA"},
    new Provider {Id=7,Name="Oracle",Address="Redwood City",Place="USA"},
    new Provider {Id=8,Name="Apple",Address="California",Place="USA"},
    new Provider {Id=9,Name="SAP",Address="Walldorf",Place="Germany"},
   };
  }
  #endregion
 }
}

Step 4 : Add ==> View as PdfReport inside the Home Folder  

PdfReport.cshtml

@using MvcPDFReport.Models
@model ProviderList
<br />
<img src="@Model.ImageUrl" width="64" height="64" />
<br />
<table cellpadding="3" cellspacing="3">
    <tr border="1" bgcolor="#777777" color="#ffffff">
        <td width="20%" align="center">
            Provider Name
        </td>
        <td width="50%" align="center">
            Provider Address
        </td>
        <td width="30%" align="center">
            Provider Place
        </td>
    </tr>
    @{
        var oddColor = "#FACC6D";
        var evenColor = "#99CC33";
        var odd = oddColor;
    }
    @foreach (var p in Model)
    {
        <tr border="1" bgcolor="@odd">
            <td>@p.Name
            </td>
            <td>@p.Address
            </td>
            <td>@p.Place
            </td>
        </tr>
       
        odd = odd == oddColor ? evenColor : oddColor;
    }
</table>

Step 5 : Add Hyper Link for the Index View

Index.cshtml

@{
    ViewBag.Title = "Home Page";
 }

<h2>Press the link to print a Software Provider List to PDF</h2>

@Html.ActionLink("Print Software Providers", "PrintProviders", null, new { target = "_blank" })


After above Steps MVC project is as below


Complete project


What is the Final Look of the Report

Screen 1 :

Final Look of the Report
   

Screen 2 :

Final Look of the Report


How to do Print,Zoom,Page Navigation,Save etc.. ?

On Chrome - When You Move the Mouse Right Bottom side of the Report

On Chrome

On Firefox - Top of the Report

On Firefox


On Internet Explorer - When You Move the Mouse Bottom of the Report

On Internet Explorer


Do You Need to Know More about iTextSharp ?

Do You Need a Book ?



Do You Need to Download this Sample Project ?

Download


Conclusion
  • You can see that it's very easy to create MVC Reports by using iTextSharp
  • It's having lot of inbuilt features to make your job very easy
  • Such as iTextsharp supports colors out of the box as you can see on above report
  • It is possible to add images to the report by using an <img src="" /> tag

I hope this helps to You.Comments and feedback greatly appreciated.

If you feel it was a good article,Give me a +1.Thank You.




Happy Coding.


You Might Also Like

How to Improve ASP.Net MVC 3 View Performance ?

32 comments:

  1. Replies
    1. Hi Priyanka,Anjan,

      Glad to hear that it helped!
      Keep in Touch.

      Delete
  2. @Sampath : Its really very nice......
    Thanx keep it up

    ReplyDelete
    Replies
    1. Hi Jogendra,

      Thanks, I’m glad you enjoyed it!
      Keep in Touch.

      Delete
  3. Thanks sampath for sharing it with me, Nice explanation, From my side i don't think any improvements needed. well done. I personally use iText Sharp for grid reporting.
    Thanks again and please keep sharing nice articles like this.

    -Razin Memon-

    ReplyDelete
  4. Thanks for sharing, this seems to be very interesting...

    -Safeer Hussain-

    ReplyDelete
    Replies
    1. Hi Safeer,

      Thanks,I’m glad you found it useful!
      Keep in Touch.

      Delete
  5. great descriptive article.. keep it up ...

    ReplyDelete
    Replies
    1. Hi Shalin,

      Thanks, I’m glad you enjoyed it!
      Keep in Touch.

      Delete
  6. Well done!
    The only comment: use NuGet for installing the library. Launch Package Management Console in Visual Studio and type command: PM>Install-Package iTextSharp
    This command will install the library and all dependencies.

    -Gediminas Bukauskas-

    ReplyDelete
    Replies
    1. Hi Gediminas,

      Thanks for your feedback and additional information.
      But the only problem NuGet having is it's having older version than current.As a example Nuget is having iTextSharp V 5.3.3 But the latest from sourcefrog is 5.4.1.As such.

      Delete
  7. Hi Sampath,
    Great Article...
    Can we create complex reports like bar charts,line charts etc.

    ReplyDelete
    Replies
    1. Hi Theja,

      Thanks for your feedback.

      About Your Question:
      Yes.You can.
      On my future blog post I will explain how to do that.Just for the time being you can check below mentioned link for more details(this is a java post.But easily you can convert it to C#) .

      http://jtoee.blogspot.com/2008/04/writing-pdf-charts-with-itext-in-less.html

      Delete
  8. Great work as usual man :) keep it up

    ReplyDelete
    Replies
    1. Hi Dinindu,

      You're warmly welcome.
      Keep in Touch.

      Delete
  9. Sorry but i got trouble with the Picture ( dont know what to put it at the beginning).

    and second, if i display a List province by Entity framework, so how can i deal with the class ProvinceList

    ReplyDelete
    Replies
    1. it mean where is the 1st place to but the Image in, it keep alert that cant not find picture.
      and about entity framwork, it mean such at ( db is variable of entity)
      db.Province.tolist()
      so how can i change it to type ProvinceList.

      thanks

      Delete
    2. Hi Nguyen,

      First of all download the above source code and then play with that source code.I have mentioned the place where image should reside on above article. Plz check that the "After above Steps MVC project is as below" section's image for additional details.
      With EF If you need to get the List of objects you have to use ToList(); method.So If you need more help plz let me know.

      Delete
    3. yo, thanks. i have successful in create pdf file.
      But one more trouble is the font in display pdf, u know anyway to inport font like Arial or timenew roman, something like that..

      Delete
    4. in the table, i mean, the page layout for pdf

      Delete
    5. Hi Nguyen,

      My Pleasure :)

      You can change the font details as you wish by using below code snippet that is on above article.

      Check the Step 6 with below code snippet:

      public override void OnOpenDocument(PdfWriter writer, Document document)
      {
      _baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
      }

      Delete
    6. hi Sampath Lokuge
      i did as u told but it only change the title. The content in font
      PdfReport.cshtml didnot work. i use font Arial @ standardpdf class cs to add new paragrap> > on pdf ok
      but @ pdfreport.cshtml, it cant not display the right font. i dont know if i lack any library.

      and 1 more thing, is it possible if i want to display a name of user who r loggin in? i try use viewbag at controller PrintPDF and a viewbag in html page but there is an error happen.

      THank ^^

      Delete
    7. @Nguyen Duy.
      I know how to display unicode in itextsharp but i can not change the font. Do u have any solution?
      To display unicode, u can put following code into StandardPdfRenderer below htmlWorker.Parse(htmlViewReader);
      var styleSheet = new iTextSharp.text.html.simpleparser.StyleSheet();
      styleSheet.LoadTagStyle(HtmlTags.BODY, HtmlTags.FACE, "Arial Unicode MS");
      styleSheet.LoadTagStyle(HtmlTags.BODY, HtmlTags.ENCODING, BaseFont.IDENTITY_H);
      htmlWorker.SetStyleSheet(styleSheet);

      Delete
  10. I do like your tutorial. Everything is fine. But when i try to attach PDF file to email. I can't do it. Do you have any answer? Thanks!

    ReplyDelete
    Replies
    1. Hi Wai,

      Thanks for your feedback. :)

      How did you try to add it ? Can I have more info about it ?

      Delete
  11. It's not free. Read the licensing if you create a commercial app.

    ReplyDelete
    Replies
    1. Hi,

      If you download it from the SourceForge (http://sourceforge.net/projects/itextsharp/) then it's free.It's having Affero GNU Public License (AGPL - http://www.gnu.org/licenses/agpl-3.0.html).I hope this will help to you. :)

      Delete
  12. i read this article , but my problem is still not resolve . my problem is how to use class in table rather incline class for getting attractive ui in pdf .

    ReplyDelete

Thanks for your Feedback.