PdfResult a custom ActionResult in ASP.NET MVC#

I have been very bad about not blogging and not posting code from my recent talks.  For that I apologize.  Let me say just one thing on that.  4 kids, ADD, and shiny new toys. :)

Ok, so several people have asked about generating pdf’s from html and css.  At work, we needed this for receipts for registration among other things.  We found a great tool from Winnovative Software that works great with css and html and spitting out an almost perfect pdf. 

We are using mvc in our solution, so we decided to come up with a custom ActionResult so that it would make it very easy to return a pdf from an action call.

Here is what the action looks like:

public PdfResult PdfForm()
        {
            string html = this.CaptureActionHtml(this, c => (ViewResult)c.PrintForm());

            return new PdfResult(html, "PdfForm", true);
        }

        public ActionResult PrintForm()
        {
            var db = new NorthwindDataContext();
            
            List<Customer> customers = db.Customers.ToList<Customer>();

            ViewData["customers"] = customers;
            return View("PrintForm");
        }

 

Notice the CaptureActionHtml.  I found this code on this blog post as I was trying to figure out a way to use asp.net mvc as the template engine to save time for generating the html which the html to pdf tool needed.

Here is the CaptureActionHtml class:

using System;
using System.IO;
using System.Web;
using System.Web.Mvc;

namespace HtmlHelperExamples
{
    public static class ControllerExtensions
    {
        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The controller</param>
        /// <param name="masterPageName">The master page to use for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this TController controller,
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(controller, masterPageName, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController, 
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            return controller.CaptureActionHtml(targetController, null, action);
        }

        /// <summary>
        /// Captures the HTML output by a controller action that returns a ViewResult
        /// </summary>
        /// <typeparam name="TController">The type of controller to execute the action on</typeparam>
        /// <param name="controller">The current controller</param>
        /// <param name="targetController">The controller which has the action to execute</param>
        /// <param name="masterPageName">The name of the master page for the view</param>
        /// <param name="action">The action to execute</param>
        /// <returns>The HTML output from the view</returns>
        public static string CaptureActionHtml<TController>(
            this Controller controller,
            TController targetController, 
            string masterPageName,
            Func<TController, ViewResult> action)
            where TController : Controller
        {
            if (controller == null)
            {
                throw new ArgumentNullException("controller");
            }
            if (targetController == null)
            {
                throw new ArgumentNullException("targetController");
            }
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            // pass the current controller context to orderController
            var controllerContext = controller.ControllerContext;
            targetController.ControllerContext = controllerContext;

            // replace the current context with a new context that writes to a string writer
            var existingContext = System.Web.HttpContext.Current;
            var writer = new StringWriter();
            var response = new HttpResponse(writer);
            var context = new HttpContext(existingContext.Request, response) {User = existingContext.User};
            System.Web.HttpContext.Current = context;

            // execute the action
            var viewResult = action(targetController);

            // change the master page name
            if (masterPageName != null)
            {
                viewResult.MasterName = masterPageName;
            }

            // we have to set the controller route value to the name of the controller we want to execute
            // because the ViewLocator class uses this to find the correct view
            var oldController = controllerContext.RouteData.Values["controller"];
            controllerContext.RouteData.Values["controller"] = typeof(TController).Name.Replace("Controller", "");

            // execute the result
            viewResult.ExecuteResult(controllerContext);

            // restore the old route data
            controllerContext.RouteData.Values["controller"] = oldController;

            // restore the old context
            System.Web.HttpContext.Current = existingContext;

            return writer.ToString();
        }
    }
}

Here is the PdfResult class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Winnovative.WnvHtmlConvert;
using System.Text.RegularExpressions;

namespace System.Web.Mvc
{
    public class PdfResult : ViewResult
    {
        public string ContentType { get; set; }
        public string Content { get; set; }
        public string OutputFileName { get; set; }
        public bool ReturnAsAttachment { get; set; }

        public PdfResult(string html, string outputFileName, bool returnAsAttachment)
        {
            // ViewResult vr = ar as ViewResult;
            
            this.ContentType = "application/pdf";
            this.Content = html;
            this.OutputFileName = outputFileName;
            this.ReturnAsAttachment = returnAsAttachment;
        }
        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = ContentType;
            
            string baseURL = "";
            string htmlString = this.Content;

            bool selectablePDF = true;

            // Create the PDF converter. Optionally you can specify the virtual browser width as parameter.
            //1024 pixels is default, 0 means autodetect
            PdfConverter pdfConverter = new PdfConverter();

            // set the license key
            pdfConverter.LicenseKey = "yourkeyhere";

            // set the converter options
            pdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
            pdfConverter.PdfDocumentOptions.PdfCompressionLevel = PdfCompressionLevel.Normal;
            pdfConverter.PdfDocumentOptions.PdfPageOrientation = PDFPageOrientation.Portrait;
            pdfConverter.PdfDocumentOptions.ShowHeader = false;
            pdfConverter.PdfDocumentOptions.ShowFooter = false;

            // set to generate selectable pdf or a pdf with embedded image
            pdfConverter.PdfDocumentOptions.GenerateSelectablePdf = selectablePDF;

            // Performs the conversion and get the pdf document bytes that you can further
            // save to a file or send as a browser response
            // The baseURL parameterhelps the converter to get the CSS files and images
            // referenced by a relative URL in the HTML string. This option has efect only
            // if the HTML string contains a valid HEAD tag.
            // The converter will automatically inserts a <BASE HREF="baseURL"> tag.
            byte[] pdfBytes = null;

            if (baseURL.Length > 0)
            {
                pdfBytes = pdfConverter.GetPdfBytesFromHtmlString(htmlString, baseURL);
            }
            else
            {
                pdfBytes = pdfConverter.GetPdfBytesFromHtmlString(htmlString);
            }

            // send the PDF document as a response to the browser for download
            // System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
            context.HttpContext.Response.Clear();

            // this.OutputFileName = Regex.Replace(this.OutputFileName, " ", "_", RegexOptions.IgnoreCase);
            // this.OutputFileName = StringHelper.StripNonAlphaNumeric(this.OutputFileName);

            context.HttpContext.Response.AddHeader("Content-Type", "application/pdf");

            if (this.ReturnAsAttachment)
            {
                context.HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename=" + this.OutputFileName + ".pdf; size=" + pdfBytes.Length.ToString());
            }

            context.HttpContext.Response.Flush();
            context.HttpContext.Response.BinaryWrite(pdfBytes);
            context.HttpContext.Response.Flush();
            context.HttpContext.Response.End();

        }
    }

}

 

Hopefully this will help those of you who are wanting to generate the pdf’s using html and css along with using the default mvc view engine as your template engine. Enjoy!

Tuesday, October 06, 2009 8:20:29 AM UTC #     |  Trackback Tracked by:
"去口臭" (去口臭) [Trackback]

 

 

All content © 2014, Jim Zimmerman
Book
New Book
Links to me
On this page
Sponsors
Calendar
<October 2009>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
Archives
Tags
Blogroll OPML
Technorati
Favorite Links
Disclaimer

Powered by: newtelligence dasBlog 1.9.6264.0

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Send mail to the author(s) E-mail