diff --git a/Rotativa.Demo/Rotativa.Demo.csproj b/Rotativa.Demo/Rotativa.Demo.csproj index a4f16b7..b009393 100644 --- a/Rotativa.Demo/Rotativa.Demo.csproj +++ b/Rotativa.Demo/Rotativa.Demo.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -17,6 +18,15 @@ true ..\ true + + + + + 4.0 + + + + true @@ -70,6 +80,13 @@ + + TestWebForms.aspx + ASPXCodeBehind + + + TestWebForms.aspx + @@ -116,6 +133,7 @@ + Designer @@ -172,8 +190,13 @@ + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + - + + + \ No newline at end of file diff --git a/Rotativa.sln b/Rotativa.sln index 7680568..07ab80e 100644 --- a/Rotativa.sln +++ b/Rotativa.sln @@ -1,10 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa", "Rotativa\Rotativa.csproj", "{D93FAA11-31F0-4629-B53C-AA8680283529}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa.Demo", "Rotativa.Demo\Rotativa.Demo.csproj", "{8EC08BFB-6ABA-4D71-8405-77AFF5859F3D}" -EndProject +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C58DBD7F-8582-4005-AD32-E94AB45A338F}" ProjectSection(SolutionItems) = preProject .nuget\NuGet.Config = .nuget\NuGet.Config @@ -12,10 +8,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C58DBD .nuget\NuGet.targets = .nuget\NuGet.targets EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa", "Rotativa\Rotativa.csproj", "{D93FAA11-31F0-4629-B53C-AA8680283529}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa.Demo", "Rotativa.Demo\Rotativa.Demo.csproj", "{8EC08BFB-6ABA-4D71-8405-77AFF5859F3D}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa.Tests", "Rotativa.Tests\Rotativa.Tests.csproj", "{DE9EB72A-2AE7-42E8-9A44-AA525B463688}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa.UnitTests", "Rotativa.UnitTests\Rotativa.UnitTests.csproj", "{26A2783C-662D-4885-BD2B-A9AFB55C6BF9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rotativa.MVC4", "Rotativa.MVC4\Rotativa.MVC4.csproj", "{733E5EA3-068F-439D-B0F9-35D06C668657}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,6 +40,10 @@ Global {26A2783C-662D-4885-BD2B-A9AFB55C6BF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {26A2783C-662D-4885-BD2B-A9AFB55C6BF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {26A2783C-662D-4885-BD2B-A9AFB55C6BF9}.Release|Any CPU.Build.0 = Release|Any CPU + {733E5EA3-068F-439D-B0F9-35D06C668657}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {733E5EA3-068F-439D-B0F9-35D06C668657}.Debug|Any CPU.Build.0 = Debug|Any CPU + {733E5EA3-068F-439D-B0F9-35D06C668657}.Release|Any CPU.ActiveCfg = Release|Any CPU + {733E5EA3-068F-439D-B0F9-35D06C668657}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Rotativa/AsPdfResultBase.cs b/Rotativa/AsPdfResultBase.cs index 744253e..db24bf3 100644 --- a/Rotativa/AsPdfResultBase.cs +++ b/Rotativa/AsPdfResultBase.cs @@ -15,6 +15,9 @@ namespace Rotativa { public abstract class AsPdfResultBase : ActionResult { + protected Regex _urlRootReplacement = new Regex(@"( href=[""']| src=[""']|[ :]url\()/", RegexOptions.IgnoreCase | RegexOptions.Multiline); + protected Regex _urlRelativeReplacement = new Regex(@"( href=[""']| src=[""']|[ :]url\()(?!(?:http|https|ftp):)", RegexOptions.IgnoreCase | RegexOptions.Multiline); + private const string ContentType = "application/pdf"; /// diff --git a/Rotativa/HtmlAsPdf.cs b/Rotativa/HtmlAsPdf.cs new file mode 100644 index 0000000..2c7ee28 --- /dev/null +++ b/Rotativa/HtmlAsPdf.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.Web; +using System.Web.Mvc; + +namespace Rotativa +{ + /// + /// Allow a developer to render HTML as a PDF + /// + public class HtmlAsPdf : AsPdfResultBase + { + /// + /// Constructor + /// + public HtmlAsPdf() + { + WkhtmltopdfPath = String.Empty; + } + + /// + /// Returns an empty string, as Wkhtmltopdf will receive our HTML directly. + /// + /// An empty string. + protected override string GetUrl(ControllerContext context) + { + return String.Empty; + } + + /// + /// Render a PDF representation of the passed markup. + /// + /// The markup to render as PDF. + /// A binary byte-array containing the rendered PDF. + protected byte[] CallTheDriver(String html) + { + // replace href and src attributes with full URLs + string baseUrl = string.Format("{0}://{1}/", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority); + string relativeUrl = string.Format("{0}://{1}{2}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority, HttpContext.Current.Request.Url.AbsolutePath); + + if (!relativeUrl.EndsWith("/")) relativeUrl = relativeUrl + "/"; + + string buffer = _urlRootReplacement.Replace(html, String.Format("$1{0}", baseUrl)); + buffer = _urlRelativeReplacement.Replace(buffer, String.Format("$1{0}", relativeUrl)); + + var fileContent = WkhtmltopdfDriver.ConvertHtml(WkhtmltopdfPath, GetConvertOptions(), buffer); + return fileContent; + } + + [Obsolete("Not Used By HtmlAsPdf", true)] + protected override byte[] CallTheDriver(ControllerContext context) { throw new NotImplementedException("This class only used to render WebForms"); } + + /// + /// Render a PDF representation of the passed markup. + /// Will handle default filepaths, and saving copies to server. + /// + /// The markup to render as PDF. + /// A binary byte-array containing the rendered PDF. + public byte[] BuildPdf(String html) + { + if (html == null) + throw new ArgumentNullException("html"); + + if (WkhtmltopdfPath == string.Empty) + WkhtmltopdfPath = HttpContext.Current.Server.MapPath("~/Rotativa"); + + var fileContent = CallTheDriver(html); + + if (string.IsNullOrEmpty(SaveOnServerPath) == false) + { + File.WriteAllBytes(SaveOnServerPath, fileContent); + } + + return fileContent; + } + + /// + /// Render a PDF representation of the passed markup. + /// + /// The markup to render as PDF. + public void ExecuteResult(String html) + { + var context = new HttpContextWrapper(HttpContext.Current); + + var fileContent = this.BuildPdf(html); + + var response = this.PrepareResponse(context.Response); + + response.OutputStream.Write(fileContent, 0, fileContent.Length); + } + } +} diff --git a/Rotativa/PageAsPdf.cs b/Rotativa/PageAsPdf.cs new file mode 100644 index 0000000..c34c2d7 --- /dev/null +++ b/Rotativa/PageAsPdf.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; +using System.Web; +using System.Web.UI; + +namespace Rotativa +{ + public class PageAsPdf : System.Web.UI.Page + { + protected HtmlAsPdf PDF = new HtmlAsPdf(); + + protected override void Render(System.Web.UI.HtmlTextWriter writer) + { + // Check that we're not already rendering a PDF + if (HttpContext.Current.Items["Rotativa.RenderingPDF"] as Boolean? != true) + { + // Flag the context so that we don't go into stack overflow + HttpContext.Current.Items["Rotativa.RenderingPDF"] = true; + + StringWriter sw = new StringWriter(); + HtmlTextWriter hw = new HtmlTextWriter(sw); + // Render the page to our StringWriter + base.Render(hw); + + // Render the PDF + this.PDF.ExecuteResult(sw.ToString()); + } + else + // Render the page normally + base.Render(writer); + } + } +} diff --git a/Rotativa/Rotativa.csproj b/Rotativa/Rotativa.csproj index e5145ea..7767284 100644 --- a/Rotativa/Rotativa.csproj +++ b/Rotativa/Rotativa.csproj @@ -45,6 +45,10 @@ + + ASPXCodeBehind + + diff --git a/Rotativa/ViewAsPdf.cs b/Rotativa/ViewAsPdf.cs index e191bcf..08dfeea 100644 --- a/Rotativa/ViewAsPdf.cs +++ b/Rotativa/ViewAsPdf.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Text; +using System.Text.RegularExpressions; using System.Web; using System.Web.Mvc; @@ -101,11 +102,15 @@ protected override byte[] CallTheDriver(ControllerContext context) StringBuilder html = sw.GetStringBuilder(); // replace href and src attributes with full URLs - string baseUrl = string.Format("{0}://{1}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority); - html.Replace(" href=\"/", string.Format(" href=\"{0}/", baseUrl)); - html.Replace(" src=\"/", string.Format(" src=\"{0}/", baseUrl)); + string baseUrl = string.Format("{0}://{1}/", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority); + string relativeUrl = string.Format("{0}://{1}{2}", HttpContext.Current.Request.Url.Scheme, HttpContext.Current.Request.Url.Authority, HttpContext.Current.Request.Url.AbsolutePath); - var fileContent = WkhtmltopdfDriver.ConvertHtml(WkhtmltopdfPath, GetConvertOptions(), html.ToString()); + if (!relativeUrl.EndsWith("/")) relativeUrl = relativeUrl + "/"; + + string buffer = _urlRootReplacement.Replace(html.ToString(), String.Format("$1{0}", baseUrl)); + buffer = _urlRelativeReplacement.Replace(buffer, String.Format("$1{0}", relativeUrl)); + + var fileContent = WkhtmltopdfDriver.ConvertHtml(WkhtmltopdfPath, GetConvertOptions(), buffer); return fileContent; } } diff --git a/Rotativa/WkhtmltopdfDriver.cs b/Rotativa/WkhtmltopdfDriver.cs index e307f59..c427a13 100644 --- a/Rotativa/WkhtmltopdfDriver.cs +++ b/Rotativa/WkhtmltopdfDriver.cs @@ -53,19 +53,19 @@ private static byte[] Convert(string wkhtmltopdfPath, string switches, string ht } var proc = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = Path.Combine(wkhtmltopdfPath, "wkhtmltopdf.exe"), - Arguments = switches, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - WorkingDirectory = wkhtmltopdfPath, - CreateNoWindow = true - } - }; + { + StartInfo = new ProcessStartInfo + { + FileName = Path.Combine(wkhtmltopdfPath, "wkhtmltopdf.exe"), + Arguments = switches, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + RedirectStandardInput = true, + WorkingDirectory = wkhtmltopdfPath, + CreateNoWindow = true + } + }; proc.Start(); // generate PDF from given HTML string, not from URL