LSTANCZYK
3/21/2017 - 5:02 PM

Filter for an MVC4 application to render all views as PDF if AcceptTypes contains "application/pdf" or querystring contains asPDF=1. Uses w

Filter for an MVC4 application to render all views as PDF if AcceptTypes contains "application/pdf" or querystring contains asPDF=1. Uses wkthml2pdf via Pechkin.

Currently requires https://github.com/gmanny/Pechkin/pull/42

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using System.IO;
using System.Web.UI;
using Pechkin;
using System.Text.RegularExpressions;

namespace Utils.PDF
{

    /*
    protected void Application_Start()
    {
        ...
        GlobalFilters.Filters.Add(new PdfFilterAttribute());
        RegisterGlobalFilters(GlobalFilters.Filters);
        ...
    }
    */
    public class PdfFilterAttribute:ActionFilterAttribute
    {
        // Regular expression for an HTML title
        static string regex = @"(?<=<title.*>)([\s\S]*)(?=</title>)";

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            bool acceptPdf = (filterContext.RequestContext.HttpContext.Request.AcceptTypes != null &&
                    filterContext.RequestContext.HttpContext.Request.AcceptTypes.Contains("application/pdf"));

            bool requestPdf = (filterContext.RequestContext.HttpContext.Request.QueryString.AllKeys.Contains("asPDF"));

            if(acceptPdf || requestPdf)
            {
                var sb = new StringBuilder();
                var sw = new StringWriter(sb);
                var tw = new HtmlTextWriter(sw);
                var output = (HttpWriter)filterContext.RequestContext.HttpContext.Response.Output;
                filterContext.RequestContext.HttpContext.Response.Output = tw;
                filterContext.HttpContext.Items["__private_value__sb"] = sb;
                filterContext.HttpContext.Items["__private_value__output"] = output;
            }
            else
            {
                base.OnActionExecuting(filterContext);
            }
        }



        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            if (filterContext.HttpContext.Items.Contains("__private_value__sb"))
            {
                var sb = (StringBuilder)filterContext.HttpContext.Items["__private_value__sb"];
                var output = (HttpWriter)filterContext.HttpContext.Items["__private_value__output"];
                string html = sb.ToString();
                //response processing
                
                //get the document path
                var Request = filterContext.RequestContext.HttpContext.Request;
                var uri = Request.Url.AbsoluteUri;

                //get the title
                Regex ex = new Regex(regex, RegexOptions.IgnoreCase);
                string title = ex.Match(html).Value.Trim();

                //make sure we have absolute paths to css
                var urlHelper = new UrlHelper(filterContext.RequestContext);                
                var baseUri = string.Format("{0}://{1}", Request.Url.Scheme, Request.Url.Authority);
                html = html.Replace("link href=\"/", "link href=\"" + baseUri + "/");

                //configure pechkin
                var pechkin = Factory.Create(new GlobalConfig()
                    .SetPaperSize(System.Drawing.Printing.PaperKind.A4)
                    .SetCopyCount(1)
                    .SetOutlineGeneration(true)
                  );
                
                //generate pdf
                var pdf = pechkin.Convert(new ObjectConfig()
                    .SetAllowLocalContent(true)
                    .SetRenderDelay(1000)
                    .SetIntelligentShrinking(false)
                    .SetRunJavascript(true)
                    .SetLoadImages(true) //.SetZoomFactor(1.5)
                    .SetPrintBackground(true)
                    .SetScreenMediaType(false) //use print instead
                    .SetCreateExternalLinks(false)
                    .SetPageUri(uri), html);

                //prepare the response
                var Response = filterContext.HttpContext.Response;
                Response.Clear();
                Response.ClearContent();
                Response.ClearHeaders();
                Response.ContentType = "application/pdf";
                Response.AddHeader("Content-Disposition", string.Format("attachment;filename={1}.pdf; size={0}", pdf.Length, title));
                output.WriteBytes(pdf, 0, pdf.Length);
            }
            else
            {
                base.OnResultExecuted(filterContext);
            }
        }
    }
}