Mahendra Mavani's # Corner

My experiments with software development
posts - 35, comments - 26, trackbacks - 7

Generating PDF Reports from Html Page

Some level of Reporting is very much integral to any web application now a days. Traditionally we have been writing special html page to achieve this result. This page is specially formatted to fit reporting requirements (like printer friendly view) which is achieved by mean of CSS magic.  There is nothing wrong with the approach except the fact that it violates DRY [Hey, isn’t same content available in some other html page as well which is not suitable for direct printing??]

The violation of DRY comes with it’s own technical debt. Now every time you have some change to the page, you need to ensure same changes must be applied to the corresponding reporting page as well. Other issue I have with this kind of reporting pages is, they are neither very good fit as saving soft copy for later use nor they are mean to be shared easily with others. 

We, at Headspring, are married to idea of least possible technical debt. Having separate print page just for the sake of formatting is something that clearly goes against our principle. For the very same reason, we opted for “HTML to PDF Conversation” approach. Hence we are not duplicating same content in two html page rather uses same html fragment on browser view as well as PDF view. Looking at available option we found “Prince” is very good fit for our requirement and hence we made Buy decision over Build and trailed Prince library for our need which in this case is ASP.NET MVC web application.

In very simple terms, Prince works as filter on response stream, intercepting browser response just before it is about to be rendered. Once html response is handed over to prince, rest magic happens inside prince which understands CSS 3 and formats your html into nice looking PDF document. Prince has very rich formatting support which includes repeating header, footer on each page and repeating caption row for table when table content span across multiple pages.

Here is solution for putting prince to work for you.

Create and HttpModule like this

   1:  public class PrinceModule : IHttpModule
   2:      {
   3:          public void Init(HttpApplication context)
   4:          {
   5:              context.BeginRequest += context_BeginRequest;
   6:              context.EndRequest += context_EndRequest;
   7:          }
   8:   
   9:          public void Dispose() { }
  10:   
  11:          private void context_EndRequest(object sender, EventArgs e)
  12:          {
  13:              var httpApplication = (HttpApplication)sender;
  14:              
  15:              if (IgnoreRequestForPrinceFilter(httpApplication.Request))
  16:                  return;
  17:   
  18:              httpApplication.Response.AddHeader("Content-Type", "binary/octet-stream");
  19:              httpApplication.Response.AddHeader("Content-Disposition", "attachment; filename=Report.pdf;");
  20:          }
  21:   
  22:          private void context_BeginRequest(object sender, EventArgs e)
  23:          {
  24:              var httpApplication = (HttpApplication)sender;
  25:   
  26:              if (IgnoreRequestForPrinceFilter(httpApplication.Request))
  27:                  return;
  28:   
  29:              httpApplication.Response.Clear();
  30:   
  31:              AttachPrinceFilter(httpApplication);
  32:          }
  33:   
  34:          private static bool IgnoreRequestForPrinceFilter(HttpRequest req)
  35:          {
  36:              return (req.Params["ViewAsPDF"] == null);
  37:          }
  38:   
  39:          private static void AttachPrinceFilter(HttpApplication httpApplication)
  40:          {
  41:              var configuration = ObjectFactory.GetInstance<IConfiguration>();
  42:              var path = configuration.GetPrincePath();
  43:              var prince = new Prince(path);
  44:              prince.SetBaseURL(httpApplication.Request.Url.AbsoluteUri);
  45:              prince.SetLog("prince.log");
  46:              prince.SetInsecure(true);
  47:   
  48:              httpApplication.Response.Filter = new PrinceFilter(prince, httpApplication.Response.Filter);
  49:          }
  50:      }

Next step is to register it with your web.config, under httpModule section

<httpModules>        
      <add type="Cuc.Jcms.UI.ViewPage.PrinceModule,Cuc.Jcms" name="PrinceModule" />
</httpModules>

 

Let’s look bit closely at the code snippet above. Notice two important points here

1) Module has real work to do only when request was made with parameter namely “ViewAsPdf”.

2) AttachPriceFilter method is reading prince path from configuration (which in this case is hidden behind Interface and I am using IoC to get concrete instance of it).

 

So once this module is hooked up with your website, all you have to do is add ViewAsPdf=1 at the end of url and see any page as PDF document. [Prince however has requirement that the source html page should be valid xHtml page. Since we are building website that is ADA, Section 508 compliance, every single page in my app is xHtml  compliance. And yes we do have mechanism in place to validate this automatically. ]

Approach, I have highlighted above comes with a side benefit of supporting easy UI testing in automatic fashion. We uses WatiN/Gallio/MBUnit combination for automatic UI testing which now also includes not only generating PDF report pages but also asserting on it’s content through native WatiN library support, just like how you would do it for any other html page. After all these PDF report pages are html page only and we can get html response when we request original URL after removing “ViewAsPdf=1” .

Till next time, Code smartly…

 

Print | posted on Monday, February 08, 2010 3:25 PM | Filed Under [ ASP.NET ]

Feedback

Gravatar

# re: Generating PDF Reports from Html Page

Very interesting topic. Thanks a million.
7/2/2010 3:54 AM | Emilee
Gravatar

# re: Generating PDF Reports from Html Page

i borrowed a book from a friend which had how to generate a pdf with php. it wasnt exactly as big as yours but anyway. i never tried it because im not good enough at php to do hardly anything.joomla also generates a pdf of the articles and content. i spose u could also go through the code of the pages and work out how to do it from that.
7/21/2010 4:59 AM | online casino

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 1 and 2 and type the answer here:

Powered by: