Monday, 15 November 2010

Creating PDF documents dynamically using Umbraco and XSL-FO part 2

Since my last post I have made a few modifications to the PDF generation, the main one being that the files are now dynamically renamed so that they reflect the name of the case study instead of all being called PDF.PDF which was not a very helpful filename, I just wanted to get something live last week, so decided that something was better than nothing :)

The issue with the filenames comes down to the way that the PDF's are being generated by using an alternative template in Umbraco, this means that all you need to do is add " /pdf " to the end of a case study URL and it will create a PDF version of the case study. The down side is that your browser will merrily download the file and save it as PDF.PDF because that is the name of the last part of the URL.

What you need to do is set the content-disposition header to be equal to the name you would like the file use, Darren Ferguson mentioned this on the Change the name of the PDF forum post.

We have used the same technique for downloading dynamically generated excel files, so I thought it would be useful to create a small macro to set both this header and also to set the caching headers to prevent any caching issues, I think in the past we have experienced all possible issues, including various issues where IE behaves differently to other browsers when you are using SSL and so the below code should work in all situations!

The template for the PDF alternative template is very simple:

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server">
   <umbraco:Macro Alias="PDFHeaders" runat="server"></umbraco:Macro>
   <umbraco:Macro xsl="FO-CaseStudy.xslt" Alias="PDFXSLFO" runat="server"></umbraco:Macro>
</asp:Content>

The following code snippet is the XSLT macro that simply creates our file name and then passes the file name into the helper function:

<xsl:template match="/">
        <xsl:variable name="fileName">
            <xsl:text>Vizioz_</xsl:text>
            <xsl:value-of select="$currentPage/@nodeName" />
            <xsl:text>_case_study.pdf</xsl:text>
        </xsl:variable>
        <xsl:value-of select="Vizioz.Helper:AddDocumentDownloadHeaders('application/pdf', $fileName)"/>
    </xsl:template>

And the following code is the helper function that clears the current response and adds all the appropriate headers:

public static void AddDocumentDownloadHeaders(string contentType, string fileName)
{
 HttpResponse response = HttpContext.Current.Response;
 HttpRequest request = HttpContext.Current.Request;

 response.Clear();
 response.ClearHeaders();

 if (request.IsSecureConnection & request.Browser.Browser == "IE")
  {
   // Don't use the caching headers if the browser is IE and it's a secure connection
   // see: http://support.microsoft.com/kb/323308
  }
 else
  {
   // force not using the cache
   response.AppendHeader("Cache-Control", "no-cache");
   response.AppendHeader("Cache-Control", "private");
   response.AppendHeader("Cache-Control", "no-store");
   response.AppendHeader("Cache-Control", "must-revalidate");
   response.AppendHeader("Cache-Control", "max-stale=0");
   response.AppendHeader("Cache-Control", "post-check=0");
   response.AppendHeader("Cache-Control", "pre-check=0");
   response.AppendHeader("Pragma", "no-cache");
   response.Cache.SetCacheability(HttpCacheability.NoCache);
   response.Cache.SetNoStore();
   response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
  }

 response.AppendHeader("Expires", DateTime.Now.AddMinutes(-1).ToLongDateString());
 response.AppendHeader("Keep-Alive", "timeout=3, max=993");
 response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + "\"");
 response.ContentType = contentType;
}

I will write another blog soon with some more details about XSL-FO and how to create the PDF's dynamically.

Please do re-tweet if you find this interest :)

Top 5 most popular posts