diff --git a/README b/README index eded0c4..0d94b0c 100644 --- a/README +++ b/README @@ -1,4 +1,6 @@ -Extremely easy way to create Pdf files from Asp.net MVC. +Modified from original to support multiple cookies + +Extremely easy way to create Pdf files from Asp.net MVC. Usage: diff --git a/README.markdown b/README.markdown index a623478..27ed64d 100644 --- a/README.markdown +++ b/README.markdown @@ -1,4 +1,6 @@ -Extremely easy way to create Pdf files from Asp.net MVC. +Modified from original to support multiple cookies + +Extremely easy way to create Pdf files from Asp.net MVC. Usage: ```csharp diff --git a/Rotativa/AsPdfResultBase.cs b/Rotativa/AsPdfResultBase.cs index 744253e..3266cb2 100644 --- a/Rotativa/AsPdfResultBase.cs +++ b/Rotativa/AsPdfResultBase.cs @@ -1,282 +1,271 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using System.Web.Mvc; -using System.Web.Security; -using Rotativa.Options; - -namespace Rotativa -{ - public abstract class AsPdfResultBase : ActionResult - { - private const string ContentType = "application/pdf"; - - /// - /// This will be send to the browser as a name of the generated PDF file. - /// - public string FileName { get; set; } - - /// - /// Path to wkhtmltopdf binary. - /// - public string WkhtmltopdfPath { get; set; } - - /// - /// Custom name of authentication cookie used by forms authentication. - /// - [Obsolete("Use FormsAuthenticationCookieName instead of CookieName.")] - public string CookieName - { - get { return FormsAuthenticationCookieName; } - set { FormsAuthenticationCookieName = value; } - } - - /// - /// Custom name of authentication cookie used by forms authentication. - /// - public string FormsAuthenticationCookieName { get; set; } - - /// - /// Sets the page margins. - /// - public Margins PageMargins { get; set; } - - /// - /// Sets the page size. - /// - [OptionFlag("-s")] - public Size? PageSize { get; set; } - - /// - /// Sets the page width in mm. - /// - /// Has priority over but has to be also specified. - [OptionFlag("--page-width")] - public double? PageWidth { get; set; } - - /// - /// Sets the page height in mm. - /// - /// Has priority over but has to be also specified. - [OptionFlag("--page-height")] - public double? PageHeight { get; set; } - - /// - /// Sets the page orientation. - /// - [OptionFlag("-O")] - public Orientation? PageOrientation { get; set; } - - /// - /// Sets cookies. - /// - [OptionFlag("--cookie")] - public Dictionary Cookies { get; set; } - - /// - /// Sets post values. - /// - [OptionFlag("--post")] - public Dictionary Post { get; set; } - - /// - /// Indicates whether the page can run JavaScript. - /// - [OptionFlag("-n")] - public bool IsJavaScriptDisabled { get; set; } - - /// - /// Indicates whether the PDF should be generated in lower quality. - /// - [OptionFlag("-l")] - public bool IsLowQuality { get; set; } - - /// - /// Indicates whether the page background should be disabled. - /// - [OptionFlag("--no-background")] - public bool IsBackgroundDisabled { get; set; } - - /// - /// Minimum font size. - /// - [OptionFlag("--minimum-font-size")] - public int? MinimumFontSize { get; set; } - - /// - /// Number of copies to print into the PDF file. - /// - [OptionFlag("--copies")] - public int? Copies { get; set; } - - /// - /// Indicates whether the PDF should be generated in grayscale. - /// - [OptionFlag("-g")] - public bool IsGrayScale { get; set; } - - /// - /// Sets proxy server. - /// - [OptionFlag("-p")] - public string Proxy { get; set; } - - /// - /// HTTP Authentication username. - /// - [OptionFlag("--username")] - public string UserName { get; set; } - - /// - /// HTTP Authentication password. - /// - [OptionFlag("--password")] - public string Password { get; set; } - - /// - /// Use this if you need another switches that are not currently supported by Rotativa. - /// - [OptionFlag("")] - public string CustomSwitches { get; set; } - - [Obsolete(@"Use BuildPdf(this.ControllerContext) method instead and use the resulting binary data to do what needed.")] - public string SaveOnServerPath { get; set; } - - protected AsPdfResultBase() - { - WkhtmltopdfPath = string.Empty; - FormsAuthenticationCookieName = ".ASPXAUTH"; - PageMargins = new Margins(); - } - - protected abstract string GetUrl(ControllerContext context); - - /// - /// Returns properties with OptionFlag attribute as one line that can be passed to wkhtmltopdf binary. - /// - /// Command line parameter that can be directly passed to wkhtmltopdf binary. - protected string GetConvertOptions() - { - var result = new StringBuilder(); - - if (PageMargins != null) - result.Append(PageMargins.ToString()); - - var fields = GetType().GetProperties(); - foreach (var fi in fields) - { - var of = fi.GetCustomAttributes(typeof(OptionFlag), true).FirstOrDefault() as OptionFlag; - if (of == null) - continue; - - object value = fi.GetValue(this, null); - if (value == null) - continue; - - if (fi.PropertyType == typeof(Dictionary)) - { - var dictionary = (Dictionary)value; - foreach (var d in dictionary) - { - result.AppendFormat(" {0} {1} {2}", of.Name, d.Key, d.Value); - } - } - else if (fi.PropertyType == typeof(bool)) - { - if ((bool)value) - result.AppendFormat(CultureInfo.InvariantCulture, " {0}", of.Name); - } - else - { - result.AppendFormat(CultureInfo.InvariantCulture, " {0} {1}", of.Name, value); - } - } - - return result.ToString().Trim(); - } - - private string GetWkParams(ControllerContext context) - { - var switches = string.Empty; - - HttpCookie authenticationCookie = null; - if (context.HttpContext.Request.Cookies != null && context.HttpContext.Request.Cookies.AllKeys.Contains(FormsAuthentication.FormsCookieName)) - { - authenticationCookie = context.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName]; - } - if (authenticationCookie != null) - { - var authCookieValue = authenticationCookie.Value; - switches += " --cookie " + FormsAuthenticationCookieName + " " + authCookieValue; - } - - switches += " " + GetConvertOptions(); - - var url = GetUrl(context); - switches += " " + url; - - return switches; - } - - protected virtual byte[] CallTheDriver(ControllerContext context) - { - var switches = GetWkParams(context); - var fileContent = WkhtmltopdfDriver.Convert(WkhtmltopdfPath, switches); - return fileContent; - } - - public byte[] BuildPdf(ControllerContext context) - { - if (context == null) - throw new ArgumentNullException("context"); - - if (WkhtmltopdfPath == string.Empty) - WkhtmltopdfPath = HttpContext.Current.Server.MapPath("~/Rotativa"); - - var fileContent = CallTheDriver(context); - - if (string.IsNullOrEmpty(SaveOnServerPath) == false) - { - File.WriteAllBytes(SaveOnServerPath, fileContent); - } - - return fileContent; - } - - public override void ExecuteResult(ControllerContext context) - { - var fileContent = BuildPdf(context); - - var response = PrepareResponse(context.HttpContext.Response); - - response.OutputStream.Write(fileContent, 0, fileContent.Length); - } - - private static string SanitizeFileName(string name) - { - string invalidChars = Regex.Escape(new string(Path.GetInvalidPathChars()) + new string(Path.GetInvalidFileNameChars())); - string invalidCharsPattern = string.Format(@"[{0}]+", invalidChars); - - string result = Regex.Replace(name, invalidCharsPattern, "_"); - return result; - } - - protected HttpResponseBase PrepareResponse(HttpResponseBase response) - { - response.ContentType = ContentType; - - if (!String.IsNullOrEmpty(FileName)) - response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", SanitizeFileName(FileName))); - - response.AddHeader("Content-Type", ContentType); - - return response; - } - } +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; +using System.Web.Mvc; +using System.Web.Security; +using Rotativa.Options; + +namespace Rotativa +{ + public abstract class AsPdfResultBase : ActionResult + { + private const string ContentType = "application/pdf"; + + /// + /// This will be send to the browser as a name of the generated PDF file. + /// + public string FileName { get; set; } + + /// + /// Path to wkhtmltopdf binary. + /// + public string WkhtmltopdfPath { get; set; } + + /// + /// Name of all the cookies + /// + public IList CookieNames { get; set; } + + /// + /// Sets the page margins. + /// + public Margins PageMargins { get; set; } + + /// + /// Sets the page size. + /// + [OptionFlag("-s")] + public Size? PageSize { get; set; } + + /// + /// Sets the page width in mm. + /// + /// Has priority over but has to be also specified. + [OptionFlag("--page-width")] + public double? PageWidth { get; set; } + + /// + /// Sets the page height in mm. + /// + /// Has priority over but has to be also specified. + [OptionFlag("--page-height")] + public double? PageHeight { get; set; } + + /// + /// Sets the page orientation. + /// + [OptionFlag("-O")] + public Orientation? PageOrientation { get; set; } + + /// + /// Sets cookies. + /// + [OptionFlag("--cookie")] + public Dictionary Cookies { get; set; } + + /// + /// Sets post values. + /// + [OptionFlag("--post")] + public Dictionary Post { get; set; } + + /// + /// Indicates whether the page can run JavaScript. + /// + [OptionFlag("-n")] + public bool IsJavaScriptDisabled { get; set; } + + /// + /// Indicates whether the PDF should be generated in lower quality. + /// + [OptionFlag("-l")] + public bool IsLowQuality { get; set; } + + /// + /// Indicates whether the page background should be disabled. + /// + [OptionFlag("--no-background")] + public bool IsBackgroundDisabled { get; set; } + + /// + /// Minimum font size. + /// + [OptionFlag("--minimum-font-size")] + public int? MinimumFontSize { get; set; } + + /// + /// Number of copies to print into the PDF file. + /// + [OptionFlag("--copies")] + public int? Copies { get; set; } + + /// + /// Indicates whether the PDF should be generated in grayscale. + /// + [OptionFlag("-g")] + public bool IsGrayScale { get; set; } + + /// + /// Sets proxy server. + /// + [OptionFlag("--proxy")] + public string Proxy { get; set; } + + /// + /// HTTP Authentication username. + /// + [OptionFlag("--username")] + public string UserName { get; set; } + + /// + /// HTTP Authentication password. + /// + [OptionFlag("--password")] + public string Password { get; set; } + + /// + /// Use this if you need another switches that are not currently supported by Rotativa. + /// + [OptionFlag("")] + public string CustomSwitches { get; set; } + + [Obsolete(@"Use BuildPdf(this.ControllerContext) method instead and use the resulting binary data to do what needed.")] + public string SaveOnServerPath { get; set; } + + protected AsPdfResultBase() + { + WkhtmltopdfPath = string.Empty; + CookieNames = new List() { ".ASPXAUTH" }; + PageMargins = new Margins(); + } + + protected abstract string GetUrl(ControllerContext context); + + /// + /// Returns properties with OptionFlag attribute as one line that can be passed to wkhtmltopdf binary. + /// + /// Command line parameter that can be directly passed to wkhtmltopdf binary. + protected string GetConvertOptions() + { + var result = new StringBuilder(); + + if (PageMargins != null) + result.Append(PageMargins.ToString()); + + var fields = GetType().GetProperties(); + foreach (var fi in fields) + { + var of = fi.GetCustomAttributes(typeof(OptionFlag), true).FirstOrDefault() as OptionFlag; + if (of == null) + continue; + + object value = fi.GetValue(this, null); + if (value == null) + continue; + + if (fi.PropertyType == typeof(Dictionary)) + { + var dictionary = (Dictionary)value; + foreach (var d in dictionary) + { + result.AppendFormat(" {0} {1} {2}", of.Name, d.Key, d.Value); + } + } + else if (fi.PropertyType == typeof(bool)) + { + if ((bool)value) + result.AppendFormat(CultureInfo.InvariantCulture, " {0}", of.Name); + } + else + { + result.AppendFormat(CultureInfo.InvariantCulture, " {0} {1}", of.Name, value); + } + } + + return result.ToString().Trim(); + } + + private string GetWkParams(ControllerContext context) + { + var switches = string.Empty; + + foreach (var cookieName in CookieNames) + { + var cookie = HttpContext.Current.Request.Cookies[cookieName]; + if (cookie != null) + { + switches += string.Format(" --cookie {0} {1}", cookie.Name, cookie.Value); + } + } + + switches += " " + GetConvertOptions(); + + var url = GetUrl(context); + switches += " " + url; + + return switches; + } + + protected virtual byte[] CallTheDriver(ControllerContext context) + { + var switches = GetWkParams(context); + var fileContent = WkhtmltopdfDriver.Convert(WkhtmltopdfPath, switches); + + return fileContent; + } + + public byte[] BuildPdf(ControllerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + + if (WkhtmltopdfPath == string.Empty) + WkhtmltopdfPath = HttpContext.Current.Server.MapPath("~/Rotativa"); + + var fileContent = CallTheDriver(context); + + if (string.IsNullOrEmpty(SaveOnServerPath) == false) + { + File.WriteAllBytes(SaveOnServerPath, fileContent); + } + + return fileContent; + } + + public override void ExecuteResult(ControllerContext context) + { + var fileContent = BuildPdf(context); + + var response = PrepareResponse(context.HttpContext.Response); + + response.OutputStream.Write(fileContent, 0, fileContent.Length); + } + + private static string SanitizeFileName(string name) + { + string invalidChars = Regex.Escape(new string(Path.GetInvalidPathChars()) + new string(Path.GetInvalidFileNameChars())); + string invalidCharsPattern = string.Format(@"[{0}]+", invalidChars); + + string result = Regex.Replace(name, invalidCharsPattern, "_"); + return result; + } + + protected HttpResponseBase PrepareResponse(HttpResponseBase response) + { + response.ContentType = ContentType; + + if (!String.IsNullOrEmpty(FileName)) + response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", SanitizeFileName(FileName))); + + response.AddHeader("Content-Type", ContentType); + + return response; + } + } } \ No newline at end of file diff --git a/Rotativa/Rotativa.csproj b/Rotativa/Rotativa.csproj index e5145ea..e03b4e6 100644 --- a/Rotativa/Rotativa.csproj +++ b/Rotativa/Rotativa.csproj @@ -35,7 +35,7 @@ - + diff --git a/Rotativa/WkhtmltopdfDriver.cs b/Rotativa/WkhtmltopdfDriver.cs index e307f59..f2cf686 100644 --- a/Rotativa/WkhtmltopdfDriver.cs +++ b/Rotativa/WkhtmltopdfDriver.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Text; namespace Rotativa @@ -43,7 +44,9 @@ private static byte[] Convert(string wkhtmltopdfPath, string switches, string ht // "-q" - silent output, only errors - no progress messages // " -" - switch output to stdout // "- -" - switch input to stdin and output to stdout - switches = "-q " + switches + " -"; + + var tempFile = Path.GetTempFileName(); + switches = " " + switches + " \"" + tempFile + "\""; // generate PDF from given HTML string, not from URL if (!string.IsNullOrEmpty(html)) @@ -59,9 +62,9 @@ private static byte[] Convert(string wkhtmltopdfPath, string switches, string ht FileName = Path.Combine(wkhtmltopdfPath, "wkhtmltopdf.exe"), Arguments = switches, UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, + //RedirectStandardOutput = true, + //RedirectStandardError = true, + //RedirectStandardInput = true, WorkingDirectory = wkhtmltopdfPath, CreateNoWindow = true } @@ -69,36 +72,40 @@ private static byte[] Convert(string wkhtmltopdfPath, string switches, string ht proc.Start(); // generate PDF from given HTML string, not from URL - if (!string.IsNullOrEmpty(html)) - { - using (var sIn = proc.StandardInput) - { - sIn.WriteLine(html); - } - } + //if (!string.IsNullOrEmpty(html)) + //{ + // using (var sIn = proc.StandardInput) + // { + // sIn.WriteLine(html); + // } + //} - var ms = new MemoryStream(); - using (var sOut = proc.StandardOutput.BaseStream) - { - byte[] buffer = new byte[4096]; - int read; + //var ms = new MemoryStream(); + //using (var sOut = proc.StandardOutput.BaseStream) + //{ + // byte[] buffer = new byte[4096]; + // int read; - while ((read = sOut.Read(buffer, 0, buffer.Length)) > 0) - { - ms.Write(buffer, 0, read); - } - } + // while ((read = sOut.Read(buffer, 0, buffer.Length)) > 0) + // { + // ms.Write(buffer, 0, read); + // } + //} - string error = proc.StandardError.ReadToEnd(); + //string error = proc.StandardError.ReadToEnd(); - if (ms.Length == 0) - { - throw new Exception(error); - } + //if (ms.Length == 0) + //{ + // throw new Exception(error); + //} proc.WaitForExit(); - return ms.ToArray(); + //return null; + //return ms.ToArray(); + var data = File.ReadAllBytes(tempFile); + File.Delete(tempFile); + return data; } ///