This post originated from an RSS feed registered with .NET Buzz
by Brendan Tompkins.
Original Post: WYSIWYG Web Printing with PDF & Response Filters
Feed Title: Brendan Tompkins
Feed URL: /error.htm?aspxerrorpath=/blogs/brendan.tompkins/Rss.aspx
Feed Description: Blog First. Ask Questions Later.
By far the #1 complaint I hear from users of various web applications is that printing a page is not WYSIWYG. Itâs really a simple problem that the industry has sort of swept under the rug for most of the history of the Internet. Browser printing in the current versions of IE and Firefox is simply horrible, and while server technologies like ASP.NET and PHP have gotten better and better at screen presentation with every release, printing simply not kept up pace. As web developers we spend our time figuring out all the intricacies of browser page rendering, and often are asked to do the same thing for printing. Itâs frankly a frustrating, difficult problem to solve. IE7 should finally fix many of these problems when itâs released, but what are we supposed to do until then? And even when itâs released, how many years will we be designing to support the current crop of web browsers?
Adobe PDF Printing Rocks
Adobeâs PDF format does have itâs issues, but it also has huge advantages that make it a great solution for creating printable website documents. 1) Nearly all your users already have the free reader software and 2) Printing is nearly 100% WYSIWYG. So whatâs the solution to your website printing problems at least until you can guarantee that the majority of your users will be using a print-friendly browser? Simple, when a user makes a print request on your site, dynamically create PDF documents on the fly making sure to include all of your users page state, and output that PDF document to the browser. Simple right? Well, itâs actually easier said than done. You have two big issues when attempting to solve this problem: Finding a decent PDF writing component to deploy on your webservers, and being able to accurately render a html page to this component while at the same time preserving your userâs page state.
Finding a Decent PDF Component
In order to create a PDF document on the fly, I wanted a PDF component that would take a URL and create a document from the resulting page. There are plenty of resources out there for you to use that will help you find the right PDF component, so I wonât list them here. I settled on webSuperGooâs ABCPdf component. I liked itâs simplicity â you can grab any page and turn it into a PDF, including JavaScript rendered elements using the following code:
You can then take the resulting document and stream it back to the browser. Hereâs what the PDF output looks like rendered via the Acrobat reader in IE
Preserving Page State
A problem youâll find with this method of generating PDF documents is that while it works well for static HTML pages and other content, it quickly breaks down for anything but the simplest web applications. The reason? This PDF component wonât carry the userâs state information such as login status, session and viewstate cache, through the request, therefore, the resulting PDF output may not show things like secure content, grid sorting, or other page content that is dependent on Page State. Youâll often bump up against this limitation when youâre trying to stream the content of a web response to some other output location, such as a file, database, cache, email or something else entirely. The solution to this problem is to create a custom ASP.NET Response.Filter. This will allow you to intercept the response that would normally be sent to the userâs browser, and do all kinds of neat things with it, like turn it into a PDF document. Hereâs a great article from a great site that talks all about these Response.Filter thingeys.
Putting it all Together
In order to get this all to work properly, youâve got to wire it all up. Iâve found that the following method works best:
First, create a linkbutton on your master page to allow the user to create a âPrintable PDF Viewâ Handle the button click event, and hand off the response to your custom PDF response filter. Hereâs the code Snippet which switches the filter:
In your custom filter, write the output stream temporarially to the cache (or wherever you want to temporarially store it). It has to be stored somewhere that any web request can access it. Good candidates are the file system, cache, or the database. NOTE: This is what is called âSecurity by Obscurityâ and is generally considered not secure enough for any highly sensitive data, such as credit card information. The issue is that while it may be extremely difficult to guess the GUID and youâd have to make the page request during the milliseconds or so that passes while this rendered HTML waits to be picked up, it is possible that it could be intercepted. So, donât do this if you work for a bank or the government . I donât and find it secure enough for most applications I work with.
publicoverridevoid Write(byte[] buffer, int offset, int count)
The PDF component has be issued a one-time ticket (GUID) to pickup the resulting HTML (which may contain secure information) and the Http response is re-directed to an ASHX handler that will create the PDF document. This handler creates a SuperGoo pdf document, and requests the stored html, passing in the ticket (GUID).
// Delete this so no one can pick up this page by guessing an ID later on.
context.Cache.Remove(context.Request["DocId"]);
}
else
{
context.Response.Write("<HTML><BODY><H1>THIS PAGE HAS EXPIRED.</H1></BODY></HTML>");
}
context.Response.End();
}
This all allows the exact HTML that would normally be sent back to the browser, to be routed through the PDF component and sent back to the user in PDF resulting in a true WYSIWYG printable page.