diff --git a/build/NuSpecs/ImageProcessor.nuspec b/build/NuSpecs/ImageProcessor.nuspec index 557d85150..f12daba8f 100644 --- a/build/NuSpecs/ImageProcessor.nuspec +++ b/build/NuSpecs/ImageProcessor.nuspec @@ -25,8 +25,8 @@ Feedback is always welcome. - - + + \ No newline at end of file diff --git a/build/content/ImageProcessor.Web/web.config.transform b/build/content/ImageProcessor.Web/web.config.transform index 7d02ee462..5c3edb967 100644 --- a/build/content/ImageProcessor.Web/web.config.transform +++ b/build/content/ImageProcessor.Web/web.config.transform @@ -3,6 +3,7 @@ + @@ -13,6 +14,7 @@ + \ No newline at end of file diff --git a/src/ImageProcessor.UnitTests/ImageProcessor.UnitTests.csproj b/src/ImageProcessor.UnitTests/ImageProcessor.UnitTests.csproj index 5b6bc12d1..f3b6404f2 100644 --- a/src/ImageProcessor.UnitTests/ImageProcessor.UnitTests.csproj +++ b/src/ImageProcessor.UnitTests/ImageProcessor.UnitTests.csproj @@ -133,4 +133,5 @@ + \ No newline at end of file diff --git a/src/ImageProcessor.Web.UnitTests/Extensions/StringExtensionsUnitTests.cs b/src/ImageProcessor.Web.UnitTests/Extensions/StringExtensionsUnitTests.cs new file mode 100644 index 000000000..3e9053aed --- /dev/null +++ b/src/ImageProcessor.Web.UnitTests/Extensions/StringExtensionsUnitTests.cs @@ -0,0 +1,160 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Test harness for the string extensions +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.UnitTests.Extensions +{ + using System; + using System.Collections.Generic; + + using ImageProcessor.Web.Extensions; + using NUnit.Framework; + + /// + /// Test harness for the string extensions + /// + [TestFixture] + public class StringExtensionsUnitTests + { + /// + /// Tests the passing to an integer array + /// + [Test] + public void TestToIntegerArray() + { + Dictionary data = new Dictionary + { + { + "123-456,78-90", + new[] { 123, 456, 78, 90 } + }, + { + "87390174,741897498,74816,748297,57355", + new[] + { + 87390174, 741897498, 74816, + 748297, 57355 + } + }, + { "1-2-3", new[] { 1, 2, 3 } } + }; + + foreach (KeyValuePair item in data) + { + int[] result = item.Key.ToPositiveIntegerArray(); + Assert.AreEqual(item.Value, result); + } + } + + /// + /// Tests the passing to an float array + /// + [Test] + public void TestToFloatArray() + { + Dictionary data = new Dictionary + { + { + "12.3-4.56,78-9.0", + new[] { 12.3F, 4.56F, 78, 9 } + }, + { + "87390.174,7.41897498,748.16,748297,5.7355", + new[] + { + 87390.174F, 7.41897498F, + 748.16F, 748297, 5.7355F + } + }, + { "1-2-3", new float[] { 1, 2, 3 } } + }; + + foreach (KeyValuePair item in data) + { + float[] result = item.Key.ToPositiveFloatArray(); + Assert.AreEqual(item.Value, result); + } + } + + /// + /// Tests the MD5 fingerprint + /// + /// The input value + /// The expected output of the hash + [Test] + [TestCase("test input", "2e7f7a62eabf0993239ca17c78c464d9")] + [TestCase("lorem ipsum dolor", "96ee002fee25e8b675a477c9750fa360")] + [TestCase("LoReM IpSuM DoLoR", "41e201da794c7fbdb8ce5526a71c8c83")] + [TestCase("1234567890", "e15e31c3d8898c92ab172a4311be9e84")] + public void TestToMd5Fingerprint(string input, string expected) + { + string result = input.ToMD5Fingerprint(); + bool comparison = result.Equals(expected, StringComparison.InvariantCultureIgnoreCase); + Assert.True(comparison); + } + + /// + /// Tests the SHA-1 fingerprint + /// + /// The input value + /// The expected output of the hash + [Test] + [TestCase("test input", "49883b34e5a0f48224dd6230f471e9dc1bdbeaf5")] + [TestCase("lorem ipsum dolor", "75899ad8827a32493928903aecd6e931bf36f967")] + [TestCase("LoReM IpSuM DoLoR", "2f44519afae72fc0837b72c6b53cb11338a1f916")] + [TestCase("1234567890", "01b307acba4f54f55aafc33bb06bbbf6ca803e9a")] + public void TestToSHA1Fingerprint(string input, string expected) + { + string result = input.ToSHA1Fingerprint(); + bool comparison = result.Equals(expected, StringComparison.InvariantCultureIgnoreCase); + Assert.True(comparison); + } + + /// + /// Tests if the value is a valid URI path name. I.E the path part of a uri. + /// + /// The value to test + /// Whether the value is correct + /// + /// The full RFC3986 does not seem to pass the test with the square brackets + /// ':' is failing for some reason in VS but not elsewhere. Could be a build issue. + /// + [Test] + [TestCase("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", true)] + [TestCase("-", true)] + [TestCase(".", true)] + [TestCase("_", true)] + [TestCase("~", true)] + [TestCase(":", true)] + [TestCase("/", true)] + [TestCase("?", true)] + [TestCase("#", false)] + [TestCase("[", false)] + [TestCase("]", false)] + [TestCase("@", true)] + [TestCase("!", true)] + [TestCase("$", true)] + [TestCase("&", true)] + [TestCase("'", true)] + [TestCase("(", true)] + [TestCase(")", true)] + [TestCase("*", true)] + [TestCase("+", true)] + [TestCase(",", true)] + [TestCase(";", true)] + [TestCase("=", true)] + [TestCase("lorem ipsum", false)] + [TestCase("é", false)] + public void TestIsValidUriPathName(string input, bool expected) + { + bool result = input.IsValidVirtualPathName(); + Assert.AreEqual(expected, result); + } + } +} diff --git a/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj b/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj index 17c54d202..460e329b9 100644 --- a/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj +++ b/src/ImageProcessor.Web.UnitTests/ImageProcessor.Web.UnitTests.csproj @@ -53,6 +53,7 @@ + diff --git a/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj b/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj index d830257ae..12c43700b 100644 --- a/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj +++ b/src/ImageProcessor.Web/NET4/ImageProcessor.Web_NET4.csproj @@ -98,6 +98,9 @@ DirectoryInfoExtensions.cs + + StringExtensions.cs + CommonParameterParserUtility.cs @@ -113,6 +116,9 @@ + + ImageProcessorNativeBinaryModule.cs + Alpha.cs diff --git a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs index 6a23d48ad..be6b88744 100644 --- a/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs +++ b/src/ImageProcessor.Web/NET45/Caching/DiskCache.cs @@ -20,7 +20,6 @@ namespace ImageProcessor.Web.Caching using System.Web; using System.Web.Hosting; - using ImageProcessor.Common.Extensions; using ImageProcessor.Web.Configuration; using ImageProcessor.Web.Extensions; using ImageProcessor.Web.Helpers; @@ -245,7 +244,7 @@ namespace ImageProcessor.Web.Caching fileInfo.Delete(); count -= 1; } - // ReSharper disable once EmptyGeneralCatchClause + // ReSharper disable once EmptyGeneralCatchClause catch { // Do nothing; skip to the next file. diff --git a/src/ImageProcessor.Web/NET45/Configuration/ImageCacheSection.cs b/src/ImageProcessor.Web/NET45/Configuration/ImageCacheSection.cs index 8e5abad95..1f868fc04 100644 --- a/src/ImageProcessor.Web/NET45/Configuration/ImageCacheSection.cs +++ b/src/ImageProcessor.Web/NET45/Configuration/ImageCacheSection.cs @@ -10,16 +10,13 @@ namespace ImageProcessor.Web.Configuration { - #region Using using System.Configuration; using System.IO; using System.Xml; - using ImageProcessor.Common.Extensions; + using ImageProcessor.Web.Extensions; using ImageProcessor.Web.Helpers; - #endregion - /// /// Represents an image cache section within a configuration file. /// diff --git a/src/ImageProcessor.Web/NET45/Configuration/ImageProcessorConfiguration.cs b/src/ImageProcessor.Web/NET45/Configuration/ImageProcessorConfiguration.cs index 9d3ea6be9..95c3123e2 100644 --- a/src/ImageProcessor.Web/NET45/Configuration/ImageProcessorConfiguration.cs +++ b/src/ImageProcessor.Web/NET45/Configuration/ImageProcessorConfiguration.cs @@ -23,6 +23,7 @@ namespace ImageProcessor.Web.Configuration using ImageProcessor.Common.Extensions; using ImageProcessor.Processors; using ImageProcessor.Web.Helpers; + using ImageProcessor.Web.HttpModules; using ImageProcessor.Web.Processors; /// @@ -370,30 +371,14 @@ namespace ImageProcessor.Web.Configuration /// private void EnsureNativeBinariesLoaded() { - string binary = Is64Bit ? "libwebp64.dll" : "libwebp32.dll"; - string sourcePath = HttpContext.Current.Server.MapPath("~/bin"); - string targetPath = new Uri(Assembly.GetExecutingAssembly().Location).LocalPath; - IntPtr pointer = IntPtr.Zero; + // Load the correct method from the native binary module. + // We do it here as on init will cause an UnauthorizedAccessException. + HttpModuleCollection modules = HttpContext.Current.ApplicationInstance.Modules; + ImageProcessorNativeBinaryModule nativeBinaryModule = modules.Get("ImageProcessorNativeBinaryModule") as ImageProcessorNativeBinaryModule; - // Shadow copy the native binaries. - sourcePath = Path.Combine(sourcePath, binary); - targetPath = Path.GetFullPath(Path.Combine(targetPath, "..\\" + binary)); - - File.Copy(sourcePath, targetPath, true); - - try - { - // Load the binary into memory. - pointer = NativeMethods.LoadLibrary(targetPath); - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } - - if (pointer == IntPtr.Zero) + if (nativeBinaryModule != null) { - throw new ApplicationException("Cannot open " + binary); + nativeBinaryModule.LoadNativeBinaries(); } } #endregion diff --git a/src/ImageProcessor/Common/Extensions/StringExtensions.cs b/src/ImageProcessor.Web/NET45/Extensions/StringExtensions.cs similarity index 74% rename from src/ImageProcessor/Common/Extensions/StringExtensions.cs rename to src/ImageProcessor.Web/NET45/Extensions/StringExtensions.cs index fd7d54139..5ec1fdf4b 100644 --- a/src/ImageProcessor/Common/Extensions/StringExtensions.cs +++ b/src/ImageProcessor.Web/NET45/Extensions/StringExtensions.cs @@ -8,7 +8,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace ImageProcessor.Common.Extensions +namespace ImageProcessor.Web.Extensions { using System; using System.Globalization; @@ -64,50 +64,8 @@ namespace ImageProcessor.Common.Extensions .ToString().ToLowerInvariant(); } } - - /// - /// Creates an SHA256 fingerprint of the String. - /// - /// The String instance that this method extends. - /// An SHA256 fingerprint of the String. - public static string ToSHA256Fingerprint(this string expression) - { - byte[] bytes = Encoding.ASCII.GetBytes(expression.ToCharArray()); - - using (SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider()) - { - byte[] hash = sha256.ComputeHash(bytes); - - // Concatenate the hash bytes into one long String. - return hash.Aggregate( - new StringBuilder(64), - (sb, b) => sb.Append(b.ToString("X2", CultureInfo.InvariantCulture))) - .ToString().ToLowerInvariant(); - } - } - - /// - /// Creates an SHA512 fingerprint of the String. - /// - /// The String instance that this method extends. - /// An SHA256 fingerprint of the String. - public static string ToSHA512Fingerprint(this string expression) - { - byte[] bytes = Encoding.ASCII.GetBytes(expression.ToCharArray()); - - using (SHA512CryptoServiceProvider sha512 = new SHA512CryptoServiceProvider()) - { - byte[] hash = sha512.ComputeHash(bytes); - - // Concatenate the hash bytes into one long String. - return hash.Aggregate( - new StringBuilder(70), - (sb, b) => sb.Append(b.ToString("X2", CultureInfo.InvariantCulture))) - .ToString().ToLowerInvariant(); - } - } #endregion - + #region Numbers /// /// Creates an array of integers scraped from the String. diff --git a/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs b/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs index ae71ca7ef..505d80d99 100644 --- a/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs +++ b/src/ImageProcessor.Web/NET45/Helpers/CommonParameterParserUtility.cs @@ -17,6 +17,7 @@ namespace ImageProcessor.Web.Helpers using ImageProcessor.Common.Extensions; using ImageProcessor.Imaging; + using ImageProcessor.Web.Extensions; /// /// Encapsulates methods to correctly parse querystring parameters. diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs index d70e71867..db378fae4 100644 --- a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs +++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessingModule.cs @@ -29,9 +29,9 @@ namespace ImageProcessor.Web.HttpModules using System.Web.Hosting; using System.Web.Security; - using ImageProcessor.Common.Extensions; using ImageProcessor.Web.Caching; using ImageProcessor.Web.Configuration; + using ImageProcessor.Web.Extensions; using ImageProcessor.Web.Helpers; #endregion diff --git a/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessorNativeBinaryModule.cs b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessorNativeBinaryModule.cs new file mode 100644 index 000000000..87120a77b --- /dev/null +++ b/src/ImageProcessor.Web/NET45/HttpModules/ImageProcessorNativeBinaryModule.cs @@ -0,0 +1,159 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// The image processing native binary module. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.HttpModules +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Web; + + using ImageProcessor.Web.Helpers; + + /// + /// Controls the loading and unloading of any native binaries required by ImageProcessor.Web. + /// + public sealed class ImageProcessorNativeBinaryModule : IHttpModule + { + /// + /// Whether the process is running in 64bit mode. Used for calling the correct dllimport method. + /// + private static readonly bool Is64Bit = Environment.Is64BitProcess; + + /// + /// The object to lock against. + /// + private static readonly object SyncRoot = new object(); + + /// + /// The native binaries. + /// + private static readonly List NativeBinaries = new List(); + + /// + /// A value indicating whether this instance of the given entity has been disposed. + /// + /// if this instance has been disposed; otherwise, . + /// + /// If the entity is disposed, it must not be disposed a second + /// time. The isDisposed field is set the first time the entity + /// is disposed. If the isDisposed field is true, then the Dispose() + /// method will not dispose again. This help not to prolong the entity's + /// life in the Garbage Collector. + /// + private bool isDisposed; + + /// + /// Disposes of the resources (other than memory) used by the module that implements + /// . + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + + // Call the appropriate methods to clean up + // unmanaged resources here. + lock (SyncRoot) + { + this.FreeNativeBinaries(); + } + + // Note disposing is done. + this.isDisposed = true; + } + + /// + /// Initializes a module and prepares it to handle requests. + /// + /// An that provides access to + /// the methods, properties, and events common to all application objects within an ASP.NET application + public void Init(HttpApplication context) + { + } + + /// + /// Loads any native ImageProcessor binaries. + /// + public void LoadNativeBinaries() + { + lock (SyncRoot) + { + this.RegisterNativeBinaries(); + } + } + + /// + /// Registers any native binaries. + /// + /// + /// Thrown when a native binary cannot be loaded. + /// + private void RegisterNativeBinaries() + { + if (NativeBinaries.Any()) + { + return; + } + + string folder = Is64Bit ? "x64" : "x86"; + string sourcePath = HttpContext.Current.Server.MapPath("~/bin/" + folder); + string targetBasePath = new Uri(Assembly.GetExecutingAssembly().Location).LocalPath; + + DirectoryInfo directoryInfo = new DirectoryInfo(sourcePath); + if (directoryInfo.Exists) + { + foreach (FileInfo fileInfo in directoryInfo.EnumerateFiles("*.dll")) + { + if (fileInfo.Name.ToUpperInvariant().StartsWith("IMAGEPROCESSOR")) + { + IntPtr pointer; + string targetPath = Path.GetFullPath(Path.Combine(targetBasePath, "..\\" + folder + "\\" + fileInfo.Name)); + File.Copy(sourcePath, targetPath, true); + + try + { + // Load the binary into memory. + pointer = NativeMethods.LoadLibrary(targetPath); + } + catch (Exception ex) + { + throw new ApplicationException(ex.Message); + } + + if (pointer == IntPtr.Zero) + { + throw new ApplicationException("Cannot load " + fileInfo.Name); + } + + NativeBinaries.Add(pointer); + } + } + } + } + + /// + /// Frees the reference to the native binaries. + /// + private void FreeNativeBinaries() + { + foreach (IntPtr nativeBinary in NativeBinaries) + { + // According to http://stackoverflow.com/a/2445558/427899 you need to call this twice. + NativeMethods.FreeLibrary(nativeBinary); + NativeMethods.FreeLibrary(nativeBinary); + } + } + } +} diff --git a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj index 2f5e77f23..47bd96a17 100644 --- a/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj +++ b/src/ImageProcessor.Web/NET45/ImageProcessor.Web_NET45.csproj @@ -54,6 +54,7 @@ + @@ -61,6 +62,7 @@ + diff --git a/src/ImageProcessor.Web/NET45/Processors/Crop.cs b/src/ImageProcessor.Web/NET45/Processors/Crop.cs index c621ebd1a..c669c8e1b 100644 --- a/src/ImageProcessor.Web/NET45/Processors/Crop.cs +++ b/src/ImageProcessor.Web/NET45/Processors/Crop.cs @@ -13,9 +13,9 @@ namespace ImageProcessor.Web.Processors using System.Text; using System.Text.RegularExpressions; - using ImageProcessor.Common.Extensions; using ImageProcessor.Imaging; using ImageProcessor.Processors; + using ImageProcessor.Web.Extensions; /// /// Crops an image to the given directions. diff --git a/src/ImageProcessor.Web/NET45/Processors/Resize.cs b/src/ImageProcessor.Web/NET45/Processors/Resize.cs index 95d962958..09ce9bc3c 100644 --- a/src/ImageProcessor.Web/NET45/Processors/Resize.cs +++ b/src/ImageProcessor.Web/NET45/Processors/Resize.cs @@ -17,9 +17,9 @@ namespace ImageProcessor.Web.Processors using System.Text; using System.Text.RegularExpressions; - using ImageProcessor.Common.Extensions; using ImageProcessor.Imaging; using ImageProcessor.Processors; + using ImageProcessor.Web.Extensions; /// /// Resizes an image to the given dimensions. diff --git a/src/ImageProcessor.Web/NET45/Processors/Watermark.cs b/src/ImageProcessor.Web/NET45/Processors/Watermark.cs index 27e83e288..d87f09388 100644 --- a/src/ImageProcessor.Web/NET45/Processors/Watermark.cs +++ b/src/ImageProcessor.Web/NET45/Processors/Watermark.cs @@ -15,9 +15,9 @@ namespace ImageProcessor.Web.Processors using System.Linq; using System.Text.RegularExpressions; - using ImageProcessor.Common.Extensions; using ImageProcessor.Imaging; using ImageProcessor.Processors; + using ImageProcessor.Web.Extensions; using ImageProcessor.Web.Helpers; /// diff --git a/src/ImageProcessor.sln.DotSettings b/src/ImageProcessor.sln.DotSettings index 94cb9dbcb..4970d0c6c 100644 --- a/src/ImageProcessor.sln.DotSettings +++ b/src/ImageProcessor.sln.DotSettings @@ -1,4 +1,5 @@  + BGRA BPP DT FPX diff --git a/src/ImageProcessor/Common/Extensions/AssemblyExtensions.cs b/src/ImageProcessor/Common/Extensions/AssemblyExtensions.cs index 763697ef0..e37480ff0 100644 --- a/src/ImageProcessor/Common/Extensions/AssemblyExtensions.cs +++ b/src/ImageProcessor/Common/Extensions/AssemblyExtensions.cs @@ -1,4 +1,13 @@ - +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// Encapsulates a series of time saving extension methods to the class. +// +// -------------------------------------------------------------------------------------------------------------------- + namespace ImageProcessor.Common.Extensions { using System; @@ -38,4 +47,4 @@ namespace ImageProcessor.Common.Extensions } } } -} +} \ No newline at end of file diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index 274b3e76d..73ac24ef7 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -66,7 +66,6 @@ - @@ -131,13 +130,14 @@ - + PreserveNewest - + PreserveNewest +