4486 Views // 0 Comments // Not Rated

Opening ASP.NET 2.0 Web Page Exports In Client Apps Instead Of Browser Plug-Ins (Part 2 - PDF)

In a recent post I discussed how to open a web page in Excel...that is, in the actual Excel Office application, not in a crap-like Excel browser window. Well, the users loved it and inevitably wanted more. Whenever you see Excel exportation in your requirements, rest assured that PDF is soon to follow...

Now, since you really can't do much with Adobe Reader other than print PDFs and download what seems like hourly updates for it, the browser version of this beloved "Petrified" Document Format isn't that bad. However, for consistency's sake, we are going to click a button on a web page, and open that page's content in the user's local client Adobe as a PDF file.

The first thing to do is create a new page, and add the following two lines in the markup directly after the page directive:

Code Listing 1

  1. <% this.Response.ContentType = "application/pdf"; %>
  2. <% this.Response.AppendHeader("content-disposition", "attachment; filename=anything you want.pdf"); %>

This will, as I described in the aforementioned post, force the page to ooze it's response into Excel on the client, instead of redirect it to a browser. And that's all we need for Excel, since, as I said in that post, "Thank God Excel can speak HTML." Well, God's divine benevolence ends at Excel; Adobe cannot open HTML files.

So we're going to have to write some code. You'll need to acquire a library that does PDF processing. At my client, we're using WebSupergoo's (best name ever) ADCpdf library to do our dirty work. They have a method that will convert raw HTML into a PDF doc that can be saved to a stream, which is perfect for what we want to do.

So grab your PDF tool, and do whatever you need to do to get HTML into a stream. The HTML will probably be the result of a call to RenderControl on a user control on your page that is responsible for rendering the content that you want to export, not the entire page itself. The stream is a standard System.IO.Stream descendant. Here's what to do:

  1. In the page load, invoke your PDF tool, and get a steam object from your HTML (again, however you need to do this is specific to your situation).
  2. Do a Response.Clear() to ensure that we are only going to be serving up exactly the HTML we want, and not the whole buffered page.
  3. Write to Response.OutputStream.  ABCpdf's Save method allows you to pass a stream directly to it.  You can also call Write on that steam and write a raw byte array to it.

That's it! This will cause IE to pump the response directly to Adobe. So basically, the meat and potatoes of this trick is to force the content into a client app by hard coding the response type directly in the page's markup. If this app can read HTML, you're done. If not, you'll have to manually write your output to the stream.

One issue with this procedure is that after our page launches the client app, it remains alive, and sits there on the desktop with absolutely no content it in. I've actually noticed that behavior in Microsoft's web apps around the Internet, for example on MSDN downloads and Outlook's web access when opening attachments. We need to close that window!

In my app, I have a lot of modality going on with my windows, so perhaps my solution to this problem is a bit more convoluted than it has to be. For example, I first tried to open these windows from JavaScript with window.open to ensure that the current window is not lost, like so:

Code Listing 2

  1. function DoPopup()
  2. {
  3. var popup;
  4. popup = window.open('PDFExport.aspx', popup, 'status=0, toolbar=0, menubar=0, directories=0, resizable=0, scrollbars=0, top=100, left=100, width=100, height=100');
  5. setTimeout('popup.close();', 10000);
  6. }

This works...pretty well. A few problems are, first, if the code behind of the page we're popping up takes longer than setTimeout's timeout time, the window will disappear and its response will be lost; no client app will be invoked. Secondly, and simply, it's a kludge. If the timeout's not an issue, (a metric I have is that a timeout of 5000 is sufficient for a 1.8 MB Excel document) it'll always work, but the window might not close.

So I found a better way. If you do a Response.Redirect instead of a ScriptManager.RegisterStartupScript (which is how I called window.open from the server), the page will postback with the button click, and kick off our export page. When it's done, the client app will open. Despite the Response.Redirect, since we're not (officially) going to a webpage, the current page will stay put! No need to mess around with popups!

So this gives us a similar experience to any file download from a webpage, just with the added benefit that the file will be opened in the appropriate client app, not a plug-in of a browser!

No Tags

No Files

No Thoughts

Your Thoughts?

You need to login with Twitter to share a Thought on this post.