diff --git a/README.md b/README.md index baae08b..49a1fee 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ TuesPechkin is a .NET Wrapper for the [wkhtmltopdf](https://github.com/wkhtmltop - [Azure Websites does not currently support the use of wkhtmltopdf.](http://social.msdn.microsoft.com/Forums/windowsazure/en-US/eb48e701-8c0b-4be3-b694-2e11cc6ff2e1/wkhtmltopdf-in-windows-azure?forum=windowsazurewebsitespreview) - It is not tested with any operating systems besides Windows. - [It is available as a *NuGet package* for your convenience.](https://www.nuget.org/packages/TuesPechkin/) -- It is built and tested around wkhtmltopdf 0.11.0, 0.12.0, and 0.12.1. +- It is built and tested around wkhtmltopdf 0.12.2. +- Even if you use the IIS-compatible method documented below, you may only use one converter/toolset instance per application pool/process. A workaround is being researched for a future version. ### wkhtmltox.dll The wkhtmltox.dll file and any dependencies it might have (for older versions, 0.11.0-) are not included in the TuesPechkin NuGet package; however, you can bring your own copy of the library or download one of the following NuGet packages that contain the library: @@ -29,7 +30,7 @@ For 2.0.0 I am wanting to use the 'git flow' style of branching/merging/releasin ### 1. Choose a deployment -TuesPechkin exposes an 'IDeployment' interface to represent the folder where wkhtmltox.dll resides. There exists a `StaticDeployment` implementation that accepts a string path, and there exists an abstract `EmbeddedDeployment` class that can be implemented to automatically deploy the wkhtmltopdf dll(s) wherever you need them. +TuesPechkin exposes an 'IDeployment' interface to represent the folder where wkhtmltox.dll resides. There exists a `StaticDeployment` implementation that accepts a string path, a `TempFolderDeployment` implementation that generates an application-scoped folder path under the `%Temp%` folder, and an abstract `EmbeddedDeployment` class that can be implemented to automatically deploy the wkhtmltopdf dll(s) wherever you need them. ### 2. Choose a toolset @@ -99,7 +100,8 @@ var document = new HtmlToPdfDocument IConverter converter = new StandardConverter( new PdfToolset( - new StaticDeployment(DLL_FOLDER_PATH))); + new Win32EmbeddedDeployment( + new TempFolderDeployment()))); byte[] result = converter.convert(document); ``` @@ -109,7 +111,8 @@ byte[] result = converter.convert(document); IConverter converter = new ThreadSafeConverter( new PdfToolset( - new StaticDeployment(DLL_FOLDER_PATH))); + new Win32EmbeddedDeployment( + new TempFolderDeployment()))); // Keep the converter somewhere static, or as a singleton instance! @@ -121,20 +124,21 @@ byte[] result = converter.convert(document); IConverter converter = new ThreadSafeConverter( new RemotingToolset( - new StaticDeployment(DLL_FOLDER_PATH))); + new Win32EmbeddedDeployment( + new TempFolderDeployment()))); // Keep the converter somewhere static, or as a singleton instance! byte[] result = converter.convert(document); ``` -### Use the embedded library from the TuesPechkin.Wkhtmltox.Win32 NuGet package. +### Use the embedded library from the TuesPechkin.Wkhtmltox.Win64 NuGet package instead. ```csharp IConverter converter = - new StandardConverter( - new PdfToolset( - new Win32EmbeddedDeployment( - new StaticDeployment(DLL_FOLDER_PATH)))); + new StandardConverter( + new PdfToolset( + new Win64EmbeddedDeployment( + new TempFolderDeployment()))); byte[] result = converter.convert(document); ``` diff --git a/TuesPechkin.TestWebApp/Controllers/HomeController.cs b/TuesPechkin.TestWebApp/Controllers/HomeController.cs index 12d6962..c6743b8 100644 --- a/TuesPechkin.TestWebApp/Controllers/HomeController.cs +++ b/TuesPechkin.TestWebApp/Controllers/HomeController.cs @@ -7,18 +7,41 @@ namespace TuesPechkin.TestWebApp.Controllers { public class HomeController : Controller { + private static IDeployment specificPath = + new StaticDeployment( + Path.Combine( + AppDomain.CurrentDomain.BaseDirectory, + "../TuesPechkin.Tests/wk-ver/0.12.2")); + + private static string randomPath = Path.Combine( + Path.GetTempPath(), + Guid.NewGuid().ToString(), + "wkhtmltox.dll"); + private static IConverter converter = new ThreadSafeConverter( new RemotingToolset( new Win32EmbeddedDeployment( - new StaticDeployment( - Path.Combine( - Path.GetTempPath(), - Guid.NewGuid().ToString(), - "wkhtmltox.dll"))))); + new TempFolderDeployment()))); + + private static IConverter anotherConverter = + new ThreadSafeConverter( + new RemotingToolset( + specificPath)); + + private static IConverter imageConverter = + new ThreadSafeConverter( + new RemotingToolset( + new Win32EmbeddedDeployment( + new TempFolderDeployment()))); // GET: /Home/ public ActionResult Index() + { + return View(); + } + + public ActionResult PdfTest() { var doc = new HtmlToPdfDocument(); doc.Objects.Add(new ObjectSettings { PageUrl = "www.google.com " }); @@ -34,25 +57,47 @@ public ActionResult Index() return this.View(); } - /*[HttpGet] + [HttpGet] public FileResult ScratchPad() { - var doc = new HtmlDocument(); + var doc = new HtmlToPdfDocument(); var obj = new ObjectSettings(); obj.PageUrl = Url.Action("PostAnything", "Home", routeValues: null, protocol: Request.Url.Scheme); obj.LoadSettings.CustomHeaders.Add("X-MY-HEADER", "my value"); obj.LoadSettings.Cookies.Add("my_awesome_cookie", "cookie value"); + obj.LoadSettings.PostItems.Add(new PostItem + { + Name = "my_special_value", + Value = "is an amazing value" + }); - var converter = Factory.Create(); - var result = converter.Convert(obj); + doc.Objects.Add(obj); + + var result = anotherConverter.Convert(doc); return File(result, "application/pdf"); - }*/ + } public ActionResult PostAnything() { return View(); } + + [HttpGet] + public FileResult ImageTest() + { + var doc = new HtmlToImageDocument() + { + In = "www.google.com", + Format = "jpg", + ScreenWidth = 500, + ScreenHeight = 500 + }; + + var result = imageConverter.Convert(doc); + + return File(result, "image/jpeg"); + } } } \ No newline at end of file diff --git a/TuesPechkin.TestWebApp/TuesPechkin.TestWebApp.csproj b/TuesPechkin.TestWebApp/TuesPechkin.TestWebApp.csproj index b4ef627..29b6d78 100644 --- a/TuesPechkin.TestWebApp/TuesPechkin.TestWebApp.csproj +++ b/TuesPechkin.TestWebApp/TuesPechkin.TestWebApp.csproj @@ -15,8 +15,7 @@ TuesPechkin.TestWebApp v4.0 false - false - 443 + true enabled disabled false @@ -262,7 +261,7 @@ True 50249 / - http://localhost/TuesPechkin.TestWebApp + http://localhost:65432 False False diff --git a/TuesPechkin.TestWebApp/Views/Home/PostAnything.cshtml b/TuesPechkin.TestWebApp/Views/Home/PostAnything.cshtml index 75c2079..742363b 100644 --- a/TuesPechkin.TestWebApp/Views/Home/PostAnything.cshtml +++ b/TuesPechkin.TestWebApp/Views/Home/PostAnything.cshtml @@ -10,7 +10,7 @@ Name Value -@foreach (HttpCookie cookie in Request.Cookies) +@foreach (HttpCookie cookie in Request.Cookies.AllKeys.Select(k => Request.Cookies[k])) { @cookie.Name @@ -25,11 +25,26 @@ Name Value -@foreach (string key in Request.Headers) -{ + @foreach (string key in Request.Headers) + { + + @key + @Request.Headers[key] + + } + + +

Post values

+ - - + + -} + @foreach (string key in Request.Form) + { + + + + + }
@key@Request.Headers[key]NameValue
@key@Request.Form[key]
\ No newline at end of file diff --git a/TuesPechkin.Tests/EventSubscriptionTests.cs b/TuesPechkin.Tests/EventSubscriptionTests.cs index 60287a8..c643223 100644 --- a/TuesPechkin.Tests/EventSubscriptionTests.cs +++ b/TuesPechkin.Tests/EventSubscriptionTests.cs @@ -26,8 +26,7 @@ public void BeginDoesNotBlockConversion() Assert.IsTrue(count > 0); } - // Not fully implemented yet; haven't figured out how to force Error - [TestMethod, Ignore] + [TestMethod] public void ErrorDoesNotBlockConversion() { var count = 0; @@ -76,8 +75,7 @@ public void ProgressChangeDoesNotBlockConversion() Assert.IsTrue(count > 0); } - // Not fully implemented yet; haven't figured out how to force Error - [TestMethod, Ignore] + [TestMethod] public void WarningDoesNotBlockConversion() { var count = 0; diff --git a/TuesPechkin.Tests/GeneralTests.cs b/TuesPechkin.Tests/GeneralTests.cs index 60a05d5..4acf90b 100644 --- a/TuesPechkin.Tests/GeneralTests.cs +++ b/TuesPechkin.Tests/GeneralTests.cs @@ -109,7 +109,7 @@ public void TwoSequentialConversionsFromString() Assert.IsNotNull(result); } - [TestMethod, Ignore] + [TestMethod] public void MultipleObjectConversionFromString() { // See: https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1790 diff --git a/TuesPechkin.Tests/TuesPechkin.Tests.csproj b/TuesPechkin.Tests/TuesPechkin.Tests.csproj index 6d5ed59..96964a6 100644 --- a/TuesPechkin.Tests/TuesPechkin.Tests.csproj +++ b/TuesPechkin.Tests/TuesPechkin.Tests.csproj @@ -89,6 +89,9 @@ PreserveNewest + + PreserveNewest + diff --git a/TuesPechkin.Tests/TuesPechkinTests.cs b/TuesPechkin.Tests/TuesPechkinTests.cs index b704125..a396ea5 100644 --- a/TuesPechkin.Tests/TuesPechkinTests.cs +++ b/TuesPechkin.Tests/TuesPechkinTests.cs @@ -9,7 +9,7 @@ namespace TuesPechkin.Tests [TestClass] public abstract class TuesPechkinTests { - protected const string TEST_WK_VER = "0.12.1"; + protected const string TEST_WK_VER = "0.12.2"; protected const string TEST_URL = "www.google.com"; // Simulates 1.x.x diff --git a/TuesPechkin.Tests/wk-ver/0.12.2/wkhtmltox.dll b/TuesPechkin.Tests/wk-ver/0.12.2/wkhtmltox.dll new file mode 100644 index 0000000..141fa6d Binary files /dev/null and b/TuesPechkin.Tests/wk-ver/0.12.2/wkhtmltox.dll differ diff --git a/TuesPechkin.Wkhtmltox.Win32/Properties/AssemblyInfo.cs b/TuesPechkin.Wkhtmltox.Win32/Properties/AssemblyInfo.cs index a9a9181..263e6da 100644 --- a/TuesPechkin.Wkhtmltox.Win32/Properties/AssemblyInfo.cs +++ b/TuesPechkin.Wkhtmltox.Win32/Properties/AssemblyInfo.cs @@ -32,4 +32,4 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.1.0")] +[assembly: AssemblyVersion("0.12.2.1")] diff --git a/TuesPechkin.Wkhtmltox.Win32/TuesPechkin.Wkhtmltox.Win32.nuspec b/TuesPechkin.Wkhtmltox.Win32/TuesPechkin.Wkhtmltox.Win32.nuspec new file mode 100644 index 0000000..292841f --- /dev/null +++ b/TuesPechkin.Wkhtmltox.Win32/TuesPechkin.Wkhtmltox.Win32.nuspec @@ -0,0 +1,15 @@ + + + + TuesPechkin.Wkhtmltox.Win32 + $version$ + TuesPechkin.Wkhtmltox.Win32 + tuespetre + tuespetre + https://github.com/tuespetre/TuesPechkin + false + A bundled copy of wkhtmltox.dll for use with TuesPechkin. + Copyright 2015 + wkhtmltopdf html pdf + + \ No newline at end of file diff --git a/TuesPechkin.Wkhtmltox.Win32/WinEmbeddedDeployment.cs b/TuesPechkin.Wkhtmltox.Win32/WinEmbeddedDeployment.cs index 04816f5..f7d23aa 100644 --- a/TuesPechkin.Wkhtmltox.Win32/WinEmbeddedDeployment.cs +++ b/TuesPechkin.Wkhtmltox.Win32/WinEmbeddedDeployment.cs @@ -14,15 +14,25 @@ public class Win32EmbeddedDeployment : EmbeddedDeployment { public Win32EmbeddedDeployment(IDeployment physical) : base(physical) { } - protected override IEnumerable> GetContents() + public override string Path { - var raw = Resources.wkhtmltox_32_dll; + get + { + return System.IO.Path.Combine( + base.Path, + GetType().Assembly.GetName().Version.ToString()); + } + } + protected override IEnumerable> GetContents() + { return new [] { new KeyValuePair( key: WkhtmltoxBindings.DLLNAME, - value: new GZipStream(new MemoryStream(raw), CompressionMode.Decompress)) + value: new GZipStream( + new MemoryStream(Resources.wkhtmltox_32_dll), + CompressionMode.Decompress)) }; } } diff --git a/TuesPechkin.Wkhtmltox.Win32/wkhtmltox_32.dll.gz b/TuesPechkin.Wkhtmltox.Win32/wkhtmltox_32.dll.gz index 5270533..984dad7 100644 Binary files a/TuesPechkin.Wkhtmltox.Win32/wkhtmltox_32.dll.gz and b/TuesPechkin.Wkhtmltox.Win32/wkhtmltox_32.dll.gz differ diff --git a/TuesPechkin.Wkhtmltox.Win64/Properties/AssemblyInfo.cs b/TuesPechkin.Wkhtmltox.Win64/Properties/AssemblyInfo.cs index 9ec80b9..e722b8f 100644 --- a/TuesPechkin.Wkhtmltox.Win64/Properties/AssemblyInfo.cs +++ b/TuesPechkin.Wkhtmltox.Win64/Properties/AssemblyInfo.cs @@ -32,4 +32,4 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.1")] +[assembly: AssemblyVersion("0.12.2.1")] diff --git a/TuesPechkin.Wkhtmltox.Win64/TuesPechkin.Wkhtmltox.Win64.nuspec b/TuesPechkin.Wkhtmltox.Win64/TuesPechkin.Wkhtmltox.Win64.nuspec new file mode 100644 index 0000000..59dbeef --- /dev/null +++ b/TuesPechkin.Wkhtmltox.Win64/TuesPechkin.Wkhtmltox.Win64.nuspec @@ -0,0 +1,15 @@ + + + + TuesPechkin.Wkhtmltox.Win64 + $version$ + TuesPechkin.Wkhtmltox.Win64 + tuespetre + tuespetre + https://github.com/tuespetre/TuesPechkin + false + A bundled copy of wkhtmltox.dll for use with TuesPechkin. + Copyright 2015 + wkhtmltopdf html pdf + + \ No newline at end of file diff --git a/TuesPechkin.Wkhtmltox.Win64/WinEmbeddedDeployment.cs b/TuesPechkin.Wkhtmltox.Win64/WinEmbeddedDeployment.cs index 02d2008..8058a97 100644 --- a/TuesPechkin.Wkhtmltox.Win64/WinEmbeddedDeployment.cs +++ b/TuesPechkin.Wkhtmltox.Win64/WinEmbeddedDeployment.cs @@ -14,15 +14,25 @@ public class Win64EmbeddedDeployment : EmbeddedDeployment { public Win64EmbeddedDeployment(IDeployment physical) : base(physical) { } - protected override IEnumerable> GetContents() + public override string Path { - var raw = Resources.wkhtmltox_64_dll; + get + { + return System.IO.Path.Combine( + base.Path, + GetType().Assembly.GetName().Version.ToString()); + } + } - return new [] + protected override IEnumerable> GetContents() + { + return new[] { new KeyValuePair( key: WkhtmltoxBindings.DLLNAME, - value: new GZipStream(new MemoryStream(raw), CompressionMode.Decompress)) + value: new GZipStream( + new MemoryStream(Resources.wkhtmltox_64_dll), + CompressionMode.Decompress)) }; } } diff --git a/TuesPechkin.Wkhtmltox.Win64/wkhtmltox_64.dll.gz b/TuesPechkin.Wkhtmltox.Win64/wkhtmltox_64.dll.gz index e8ec8fc..a08b002 100644 Binary files a/TuesPechkin.Wkhtmltox.Win64/wkhtmltox_64.dll.gz and b/TuesPechkin.Wkhtmltox.Win64/wkhtmltox_64.dll.gz differ diff --git a/TuesPechkin.sln b/TuesPechkin.sln index bb2bc57..f43e44f 100644 --- a/TuesPechkin.sln +++ b/TuesPechkin.sln @@ -35,12 +35,15 @@ Global {023DF833-B252-48B3-B6AF-DBBB13E39B13}.Release|Any CPU.ActiveCfg = Release|Any CPU {023DF833-B252-48B3-B6AF-DBBB13E39B13}.Release|Any CPU.Build.0 = Release|Any CPU {35E72E2C-DE58-4ED7-9B47-5EF13860AF42}.Debug|Any CPU.ActiveCfg = Debug|x64 + {35E72E2C-DE58-4ED7-9B47-5EF13860AF42}.Debug|Any CPU.Build.0 = Debug|x64 {35E72E2C-DE58-4ED7-9B47-5EF13860AF42}.Release|Any CPU.ActiveCfg = Release|x64 {35E72E2C-DE58-4ED7-9B47-5EF13860AF42}.Release|Any CPU.Build.0 = Release|x64 {D58769FA-E008-4016-A81C-A85C606B3691}.Debug|Any CPU.ActiveCfg = Debug|x86 + {D58769FA-E008-4016-A81C-A85C606B3691}.Debug|Any CPU.Build.0 = Debug|x86 {D58769FA-E008-4016-A81C-A85C606B3691}.Release|Any CPU.ActiveCfg = Release|x86 {D58769FA-E008-4016-A81C-A85C606B3691}.Release|Any CPU.Build.0 = Release|x86 {B12D40DF-C865-4116-9C42-0EEDCF9FB19A}.Debug|Any CPU.ActiveCfg = Debug|x86 + {B12D40DF-C865-4116-9C42-0EEDCF9FB19A}.Debug|Any CPU.Build.0 = Debug|x86 {B12D40DF-C865-4116-9C42-0EEDCF9FB19A}.Release|Any CPU.ActiveCfg = Release|x86 {B12D40DF-C865-4116-9C42-0EEDCF9FB19A}.Release|Any CPU.Build.0 = Release|x86 {1BCD70EF-32EF-4339-9CCF-EA07415A39A9}.Debug|Any CPU.ActiveCfg = Debug|x86 diff --git a/TuesPechkin/Document/CropSettings.cs b/TuesPechkin/Document/CropSettings.cs new file mode 100644 index 0000000..c64d7e2 --- /dev/null +++ b/TuesPechkin/Document/CropSettings.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TuesPechkin +{ + public class CropSettings : ISettings + { + [WkhtmltoxSetting("crop.top")] + public double? Top { get; set; } + + [WkhtmltoxSetting("crop.bottom")] + public double? Bottom { get; set; } + + [WkhtmltoxSetting("crop.width")] + public double? Width { get; set; } + + [WkhtmltoxSetting("crop.height")] + public double? Height { get; set; } + } +} diff --git a/TuesPechkin/Document/HtmlToImageDocument.cs b/TuesPechkin/Document/HtmlToImageDocument.cs new file mode 100644 index 0000000..cc6299f --- /dev/null +++ b/TuesPechkin/Document/HtmlToImageDocument.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TuesPechkin +{ + public class HtmlToImageDocument : IDocument + { + [WkhtmltoxSetting("screenHeight")] + public double ScreenHeight { get; set; } + + [WkhtmltoxSetting("screenWidth")] + public double? ScreenWidth { get; set; } + + [WkhtmltoxSetting("quality")] + public double? Quality { get; set; } + + [WkhtmltoxSetting("fmt")] + public string Format { get; set; } + + [WkhtmltoxSetting("out")] + public string Out { get; set; } + + [WkhtmltoxSetting("in")] + public string In { get; set; } + + [WkhtmltoxSetting("transparent")] + public bool? Transparent { get; set; } + + public CropSettings CropSettings + { + get + { + return this.crop; + } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + this.crop = value; + } + } + + public LoadSettings LoadSettings + { + get + { + return this.load; + } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + this.load = value; + } + } + + public WebSettings WebSettings + { + get + { + return this.web; + } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + this.web = value; + } + } + + public IEnumerable GetObjects() + { + return new IObject[0]; + } + + private CropSettings crop = new CropSettings(); + + private LoadSettings load = new LoadSettings(); + + private WebSettings web = new WebSettings(); + } +} diff --git a/TuesPechkin/Document/LoadSettings.cs b/TuesPechkin/Document/LoadSettings.cs index c6b7bb3..555a5d6 100644 --- a/TuesPechkin/Document/LoadSettings.cs +++ b/TuesPechkin/Document/LoadSettings.cs @@ -10,8 +10,9 @@ public class LoadSettings : ISettings { public LoadSettings() { - //this.Cookies = new Dictionary(); - //this.CustomHeaders = new Dictionary(); + this.Cookies = new Dictionary(); + this.CustomHeaders = new Dictionary(); + this.PostItems = new List(); } public enum ContentErrorHandling @@ -24,11 +25,11 @@ public enum ContentErrorHandling [WkhtmltoxSetting("load.blockLocalFileAccess")] public bool? BlockLocalFileAccess { get; set; } - /*[WkhtmltoxSetting("load.cookies")] - public Dictionary Cookies { get; private set; }*/ + [WkhtmltoxSetting("load.cookies")] + public Dictionary Cookies { get; private set; } - /*[WkhtmltoxSetting("load.customHeaders")] - public Dictionary CustomHeaders { get; private set; }*/ + [WkhtmltoxSetting("load.customHeaders")] + public Dictionary CustomHeaders { get; private set; } [WkhtmltoxSetting("load.debugJavascript")] public bool? DebugJavascript { get; set; } @@ -39,6 +40,9 @@ public enum ContentErrorHandling [WkhtmltoxSetting("load.password")] public string Password { get; set; } + [WkhtmltoxSetting("load.post")] + public IList PostItems { get; private set; } + [WkhtmltoxSetting("load.proxy")] public string Proxy { get; set; } diff --git a/TuesPechkin/Document/PostItem.cs b/TuesPechkin/Document/PostItem.cs new file mode 100644 index 0000000..312c208 --- /dev/null +++ b/TuesPechkin/Document/PostItem.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TuesPechkin +{ + public sealed class PostItem + { + public string Name { get; set; } + + public string Value { get; set; } + + public bool IsFile { get; set; } + } +} diff --git a/TuesPechkin/EmbeddedDeployment.cs b/TuesPechkin/EmbeddedDeployment.cs index 67cc11f..4ae55ce 100644 --- a/TuesPechkin/EmbeddedDeployment.cs +++ b/TuesPechkin/EmbeddedDeployment.cs @@ -14,17 +14,19 @@ public virtual string Path { if (!deployed) { - if (!Directory.Exists(physical.Path)) - { - Directory.CreateDirectory(physical.Path); - } - foreach (var nameAndContents in GetContents()) { var filename = System.IO.Path.Combine( physical.Path, nameAndContents.Key); + var path = System.IO.Path.GetDirectoryName(filename); + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + if (!File.Exists(filename)) { WriteStreamToFile(filename, nameAndContents.Value); diff --git a/TuesPechkin/ImageToolset.cs b/TuesPechkin/ImageToolset.cs new file mode 100644 index 0000000..c865a99 --- /dev/null +++ b/TuesPechkin/ImageToolset.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Printing; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace TuesPechkin +{ + public sealed class ImageToolset : MarshalByRefObject, IToolset + { + public event EventHandler Unloaded; + + public IDeployment Deployment { get; private set; } + + public bool Loaded { get; private set; } + + public ImageToolset() + { + } + + public ImageToolset(IDeployment deployment) + { + if (deployment == null) + { + throw new ArgumentNullException("deployment"); + } + + Deployment = deployment; + } + + public void Load(IDeployment deployment = null) + { + if (Loaded) + { + return; + } + + if (deployment != null) + { + Deployment = deployment; + } + + WinApiHelper.SetDllDirectory(Deployment.Path); + WkhtmltoxBindings.wkhtmltoimage_init(0); + + Loaded = true; + } + + public void Unload() + { + if (Loaded) + { + WkhtmltoxBindings.wkhtmltoimage_deinit(); + + if (Unloaded != null) + { + Unloaded(this, EventArgs.Empty); + } + } + } + + public override object InitializeLifetimeService() + { + return null; + } + + #region Rest of IToolset stuff + public IntPtr CreateGlobalSettings() + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Creating global settings (wkhtmltoimage_create_global_settings)"); + + return WkhtmltoxBindings.wkhtmltoimage_create_global_settings(); + } + + public IntPtr CreateObjectSettings() + { + throw new NotSupportedException(); + } + + public int SetGlobalSetting(IntPtr setting, string name, string value) + { + Tracer.Trace( + String.Format( + "T:{0} Setting global setting '{1}' to '{2}' for config {3}", + Thread.CurrentThread.Name, + name, + value, + setting)); + + var success = WkhtmltoxBindings.wkhtmltoimage_set_global_setting(setting, name, value); + + Tracer.Trace(String.Format("...setting was {0}", success == 1 ? "successful" : "not successful")); + + return success; + } + + public unsafe string GetGlobalSetting(IntPtr setting, string name) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Getting global setting (wkhtmltoimage_get_global_setting)"); + + byte[] buf = new byte[2048]; + + fixed (byte* p = buf) + { + WkhtmltoxBindings.wkhtmltoimage_get_global_setting(setting, name, p, buf.Length); + } + + int walk = 0; + + while (walk < buf.Length && buf[walk] != 0) + { + walk++; + } + + return Encoding.UTF8.GetString(buf, 0, walk); + } + + public int SetObjectSetting(IntPtr setting, string name, string value) + { + throw new NotSupportedException(); + } + + public unsafe string GetObjectSetting(IntPtr setting, string name) + { + throw new NotSupportedException(); + } + + public IntPtr CreateConverter(IntPtr globalSettings) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Creating converter (wkhtmltoimage_create_converter)"); + + return WkhtmltoxBindings.wkhtmltoimage_create_converter(globalSettings, null); + } + + public void DestroyConverter(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Destroying converter (wkhtmltoimage_destroy_converter)"); + + WkhtmltoxBindings.wkhtmltoimage_destroy_converter(converter); + + pinnedCallbacks.Unregister(converter); + } + + public void SetWarningCallback(IntPtr converter, StringCallback callback) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Setting warning callback (wkhtmltoimage_set_warning_callback)"); + + WkhtmltoxBindings.wkhtmltoimage_set_warning_callback(converter, callback); + + pinnedCallbacks.Register(converter, callback); + } + + public void SetErrorCallback(IntPtr converter, StringCallback callback) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Setting error callback (wkhtmltoimage_set_error_callback)"); + + WkhtmltoxBindings.wkhtmltoimage_set_error_callback(converter, callback); + + pinnedCallbacks.Register(converter, callback); + } + + public void SetFinishedCallback(IntPtr converter, IntCallback callback) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Setting finished callback (wkhtmltoimage_set_finished_callback)"); + + WkhtmltoxBindings.wkhtmltoimage_set_finished_callback(converter, callback); + + pinnedCallbacks.Register(converter, callback); + } + + public void SetPhaseChangedCallback(IntPtr converter, VoidCallback callback) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Setting phase change callback (wkhtmltoimage_set_phase_changed_callback)"); + + WkhtmltoxBindings.wkhtmltoimage_set_phase_changed_callback(converter, callback); + + pinnedCallbacks.Register(converter, callback); + } + + public void SetProgressChangedCallback(IntPtr converter, IntCallback callback) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Setting progress change callback (wkhtmltoimage_set_progress_changed_callback)"); + + WkhtmltoxBindings.wkhtmltoimage_set_progress_changed_callback(converter, callback); + + pinnedCallbacks.Register(converter, callback); + } + + public bool PerformConversion(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Starting conversion (wkhtmltoimage_convert)"); + + return WkhtmltoxBindings.wkhtmltoimage_convert(converter) != 0; + } + + public void AddObject(IntPtr converter, IntPtr objectConfig, string html) + { + throw new NotSupportedException(); + } + + public void AddObject(IntPtr converter, IntPtr objectConfig, byte[] html) + { + throw new NotSupportedException(); + } + + public int GetPhaseNumber(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Requesting current phase (wkhtmltoimage_current_phase)"); + + return WkhtmltoxBindings.wkhtmltoimage_current_phase(converter); + } + + public int GetPhaseCount(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Requesting phase count (wkhtmltoimage_phase_count)"); + + return WkhtmltoxBindings.wkhtmltoimage_phase_count(converter); + } + + public string GetPhaseDescription(IntPtr converter, int phase) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Requesting phase description (wkhtmltoimage_phase_description)"); + + return Marshal.PtrToStringAnsi(WkhtmltoxBindings.wkhtmltoimage_phase_description(converter, phase)); + } + + public string GetProgressDescription(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Requesting progress string (wkhtmltoimage_progress_string)"); + + return Marshal.PtrToStringAnsi(WkhtmltoxBindings.wkhtmltoimage_progress_string(converter)); + } + + public int GetHttpErrorCode(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Requesting http error code (wkhtmltoimage_http_error_code)"); + + return WkhtmltoxBindings.wkhtmltoimage_http_error_code(converter); + } + + public byte[] GetConverterResult(IntPtr converter) + { + Tracer.Trace("T:" + Thread.CurrentThread.Name + " Requesting converter result (wkhtmltoimage_get_output)"); + + IntPtr tmp; + var len = WkhtmltoxBindings.wkhtmltoimage_get_output(converter, out tmp); + var output = new byte[len]; + Marshal.Copy(tmp, output, 0, output.Length); + return output; + } + #endregion + + private DelegateRegistry pinnedCallbacks = new DelegateRegistry(); + } +} diff --git a/TuesPechkin/Properties/AssemblyInfo.cs b/TuesPechkin/Properties/AssemblyInfo.cs index fd3ad6c..ae2a9c4 100644 --- a/TuesPechkin/Properties/AssemblyInfo.cs +++ b/TuesPechkin/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ [assembly: AssemblyDescription(".NET wrapper for wkhtmltopdf; supports 32-bit, 64-bit, multi-threaded and IIS environments.")] [assembly: AssemblyProduct("TuesPechkin")] [assembly: AssemblyCopyright("Copyright 2014 Derek Gray")] -[assembly: AssemblyVersion("2.0.1")] +[assembly: AssemblyVersion("2.1.0")] [assembly: ComVisible(false)] [assembly: Guid("11f63696-7105-436d-9ec6-2fee54c40b11")] diff --git a/TuesPechkin/StandardConverter.cs b/TuesPechkin/StandardConverter.cs index 981a540..cfd0bcf 100644 --- a/TuesPechkin/StandardConverter.cs +++ b/TuesPechkin/StandardConverter.cs @@ -186,7 +186,7 @@ private void OnProgressChanged(IntPtr converter, int progress) { Document = ProcessingDocument, Progress = progress, - ProgressDescription = Toolset.GetProgressDescription(converter) + ProgressDescription = progressDescription }; if (ProgressChange != null) @@ -288,17 +288,18 @@ private void Apply(IntPtr config, string name, object value, bool isGlobal) ? (FuncShim)((k, v) => Toolset.SetGlobalSetting(config, k, v)) : (FuncShim)((k, v) => Toolset.SetObjectSetting(config, k, v)); - if (type == typeof(double?)) + if (type == typeof(double)) { - apply(name, ((double?)value).Value.ToString("0.##", CultureInfo.InvariantCulture)); + apply(name, ((double)value).ToString("0.##", CultureInfo.InvariantCulture)); } - else if (type == typeof(bool?)) + else if (type == typeof(bool)) { - apply(name, ((bool?)value).Value ? "true" : "false"); + apply(name, ((bool)value) ? "true" : "false"); } else if (typeof(IEnumerable>).IsAssignableFrom(type)) { var dictionary = (IEnumerable>)value; + var counter = 0; foreach (var entry in dictionary) { @@ -308,7 +309,29 @@ private void Apply(IntPtr config, string name, object value, bool isGlobal) } apply(name + ".append", null); - apply(string.Format("{0}[0]", name), entry.Key + "," + entry.Value); + apply(string.Format("{0}[{1}]", name, counter), entry.Key + "\n" + entry.Value); + + counter++; + } + } + else if (typeof(IEnumerable).IsAssignableFrom(type)) + { + var list = (IEnumerable)value; + var counter = 0; + + foreach (var item in list) + { + if (string.IsNullOrEmpty(item.Name) || string.IsNullOrEmpty(item.Value)) + { + continue; + } + + apply(name + ".append", null); + apply(string.Format("{0}[{1}].name", name, counter), item.Name); + apply(string.Format("{0}[{1}].value", name, counter), item.Value); + apply(string.Format("{0}[{1}].file", name, counter), item.IsFile ? "true" : "false"); + + counter++; } } else diff --git a/TuesPechkin/TempFolderDeployment.cs b/TuesPechkin/TempFolderDeployment.cs new file mode 100644 index 0000000..523711f --- /dev/null +++ b/TuesPechkin/TempFolderDeployment.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace TuesPechkin +{ + [Serializable] + public sealed class TempFolderDeployment : IDeployment + { + public string Path { get; private set; } + + public TempFolderDeployment() + { + // Scope it to the application + Path = System.IO.Path.Combine( + System.IO.Path.GetTempPath(), + AppDomain.CurrentDomain.BaseDirectory.GetHashCode().ToString()); + + // Scope it by bitness, too + Path = System.IO.Path.Combine( + Path, + IntPtr.Size.ToString()); + } + } +} diff --git a/TuesPechkin/TuesPechkin.csproj b/TuesPechkin/TuesPechkin.csproj index 9f4fefb..7ef2449 100644 --- a/TuesPechkin/TuesPechkin.csproj +++ b/TuesPechkin/TuesPechkin.csproj @@ -41,6 +41,11 @@ + + + + + diff --git a/TuesPechkin/TuesPechkin.nuspec b/TuesPechkin/TuesPechkin.nuspec index c2aa977..17edbe3 100644 --- a/TuesPechkin/TuesPechkin.nuspec +++ b/TuesPechkin/TuesPechkin.nuspec @@ -2,7 +2,7 @@ TuesPechkin - 2.0.1 + 2.1.0 TuesPechkin tuespetre tuespetre diff --git a/TuesPechkin/WkhtmltoxBindings.cs b/TuesPechkin/WkhtmltoxBindings.cs index a357030..3fbbaf5 100644 --- a/TuesPechkin/WkhtmltoxBindings.cs +++ b/TuesPechkin/WkhtmltoxBindings.cs @@ -12,6 +12,7 @@ public unsafe static class WkhtmltoxBindings public const string DLLNAME = "wkhtmltox.dll"; private const CharSet CHARSET = CharSet.Unicode; + #region pdf bindings [DllImport(DLLNAME, CharSet = CHARSET)] public static extern void wkhtmltopdf_add_object(IntPtr converter, IntPtr objectSettings, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] @@ -107,5 +108,80 @@ public static extern void wkhtmltopdf_set_progress_changed_callback(IntPtr conve [DllImport(DLLNAME, CharSet = CHARSET)] public static extern void wkhtmltopdf_set_warning_callback(IntPtr converter, [MarshalAs(UnmanagedType.FunctionPtr)] StringCallback callback); + #endregion + + #region image bindings + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_convert(IntPtr converter); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern IntPtr wkhtmltoimage_create_converter(IntPtr globalSettings, byte[] data); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern IntPtr wkhtmltoimage_create_global_settings(); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_current_phase(IntPtr converter); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_deinit(); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern void wkhtmltoimage_destroy_converter(IntPtr converter); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_extended_qt(); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static unsafe extern int wkhtmltoimage_get_global_setting(IntPtr settings, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] + String name, + byte* value, int valueSize); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_get_output(IntPtr converter, out IntPtr data); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_http_error_code(IntPtr converter); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_init(int useGraphics); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_phase_count(IntPtr converter); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern IntPtr wkhtmltoimage_phase_description(IntPtr converter, int phase); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern IntPtr wkhtmltoimage_progress_string(IntPtr converter); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern void wkhtmltoimage_set_error_callback(IntPtr converter, [MarshalAs(UnmanagedType.FunctionPtr)] + StringCallback callback); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern void wkhtmltoimage_set_finished_callback(IntPtr converter, [MarshalAs(UnmanagedType.FunctionPtr)] + IntCallback callback); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern int wkhtmltoimage_set_global_setting(IntPtr settings, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] + String name, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] + String value); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern void wkhtmltoimage_set_phase_changed_callback(IntPtr converter, [MarshalAs(UnmanagedType.FunctionPtr)] + VoidCallback callback); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern void wkhtmltoimage_set_progress_changed_callback(IntPtr converter, [MarshalAs(UnmanagedType.FunctionPtr)] + IntCallback callback); + + [DllImport(DLLNAME, CharSet = CHARSET)] + public static extern void wkhtmltoimage_set_warning_callback(IntPtr converter, [MarshalAs(UnmanagedType.FunctionPtr)] + StringCallback callback); + #endregion } } \ No newline at end of file