From 28aae731a02ae63e4be01f671e15f8038f61b4ea Mon Sep 17 00:00:00 2001 From: James South Date: Thu, 18 Dec 2014 00:40:53 +0000 Subject: [PATCH] Adding Web PostProcessor project. Former-commit-id: e865fcd09f0aa9249ba9f1116fcf027bd3e48e14 Former-commit-id: 530db416ac1747fb424c93ab286efb673af738c7 --- .../ApplicationEvents.cs | 36 ++++++ .../ImageProcessor.Web.PostProcessor.csproj | 78 ++++++++++++ .../PostProcessingResultEventArgs.cs | 103 ++++++++++++++++ .../PostProcessor.cs | 111 ++++++++++++++++++ .../PostProcessorBootstrapper.cs | 84 +++++++++++++ .../Properties/AssemblyInfo.cs | 36 ++++++ .../README.md | 13 ++ .../Unmanaged/x64/gifsicle.exe.REMOVED.git-id | 1 + .../Unmanaged/x86/gifsicle.exe.REMOVED.git-id | 1 + .../Unmanaged/x86/jpegtran.exe.REMOVED.git-id | 1 + .../Unmanaged/x86/optipng.exe.REMOVED.git-id | 1 + .../Resources/Unmanaged/x86/png.cmd | 2 + .../Resources/Unmanaged/x86/pngout.exe | Bin 0 -> 38912 bytes .../Settings.StyleCop | 1 + .../Helpers/ImageHelpers.cs | 5 +- .../Properties/AssemblyInfo.cs | 4 +- src/ImageProcessor.sln | 19 ++- src/TestWebsites/MVC/Test_Website_MVC.csproj | 4 + 18 files changed, 495 insertions(+), 5 deletions(-) create mode 100644 src/ImageProcessor.Web.PostProcessor/ApplicationEvents.cs create mode 100644 src/ImageProcessor.Web.PostProcessor/ImageProcessor.Web.PostProcessor.csproj create mode 100644 src/ImageProcessor.Web.PostProcessor/PostProcessingResultEventArgs.cs create mode 100644 src/ImageProcessor.Web.PostProcessor/PostProcessor.cs create mode 100644 src/ImageProcessor.Web.PostProcessor/PostProcessorBootstrapper.cs create mode 100644 src/ImageProcessor.Web.PostProcessor/Properties/AssemblyInfo.cs create mode 100644 src/ImageProcessor.Web.PostProcessor/README.md create mode 100644 src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x64/gifsicle.exe.REMOVED.git-id create mode 100644 src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/gifsicle.exe.REMOVED.git-id create mode 100644 src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/jpegtran.exe.REMOVED.git-id create mode 100644 src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/optipng.exe.REMOVED.git-id create mode 100644 src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/png.cmd create mode 100644 src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/pngout.exe create mode 100644 src/ImageProcessor.Web.PostProcessor/Settings.StyleCop diff --git a/src/ImageProcessor.Web.PostProcessor/ApplicationEvents.cs b/src/ImageProcessor.Web.PostProcessor/ApplicationEvents.cs new file mode 100644 index 000000000..27162b32e --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/ApplicationEvents.cs @@ -0,0 +1,36 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// PostProcesses any image requests within the web application. +// Many thanks to Azure Image Optimizer +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Web; + +[assembly: PreApplicationStartMethod(typeof(ImageProcessor.Web.PostProcessor.ApplicationEvents), "Start")] +namespace ImageProcessor.Web.PostProcessor +{ + using ImageProcessor.Web.Helpers; + using ImageProcessor.Web.HttpModules; + + /// + /// PostProcesses any image requests within the web application. + /// Many thanks to Azure Image Optimizer + /// + public static class ApplicationEvents + { + public static void Start() + { + ImageProcessingModule.OnPostProcessing += PostProcess; + } + + private static async void PostProcess(object sender, PostProcessingEventArgs e) + { + await PostProcessor.PostProcessImage(e.CachedImagePath); + } + } +} diff --git a/src/ImageProcessor.Web.PostProcessor/ImageProcessor.Web.PostProcessor.csproj b/src/ImageProcessor.Web.PostProcessor/ImageProcessor.Web.PostProcessor.csproj new file mode 100644 index 000000000..fcfad149a --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/ImageProcessor.Web.PostProcessor.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {55D08737-7D7E-4995-8892-BD9F944329E6} + Library + Properties + ImageProcessor.Web.PostProcessor + ImageProcessor.Web.PostProcessor + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {D011A778-59C8-4BFA-A770-C350216BF161} + ImageProcessor.Web + + + {3B5DD734-FB7A-487D-8CE6-55E7AF9AEA7E} + ImageProcessor + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/PostProcessingResultEventArgs.cs b/src/ImageProcessor.Web.PostProcessor/PostProcessingResultEventArgs.cs new file mode 100644 index 000000000..eb019872e --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/PostProcessingResultEventArgs.cs @@ -0,0 +1,103 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// The post processing result event arguments. +// Many thanks to Azure Image Optimizer +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.PostProcessor +{ + using System; + using System.IO; + using System.Text; + + /// + /// The post processing result event arguments. + /// Many thanks to Azure Image Optimizer + /// + public class PostProcessingResultEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The original file name. + /// The result file name. + public PostProcessingResultEventArgs(string originalFileName, string resultFileName) + { + FileInfo original = new FileInfo(originalFileName); + FileInfo result = new FileInfo(resultFileName); + + if (original.Exists) + { + this.OriginalFileName = original.FullName; + this.OriginalFileSize = original.Length; + } + + if (result.Exists) + { + this.ResultFileName = result.FullName; + this.ResultFileSize = result.Length; + } + } + + /// + /// Gets the original file size in bytes. + /// + public long OriginalFileSize { get; set; } + + /// + /// Gets the original file name. + /// + public string OriginalFileName { get; set; } + + /// + /// Gets the result file size in bytes. + /// + public long ResultFileSize { get; set; } + + /// + /// + /// + public string ResultFileName { get; set; } + + /// + /// Gets the difference in filesize in bytes. + /// + public long Saving + { + get { return this.OriginalFileSize - this.ResultFileSize; } + } + + /// + /// Gets the difference in filesize as a percentage. + /// + public double Percent + { + get + { + return Math.Round(100 - this.ResultFileSize / (double)this.OriginalFileSize * 100, 1); + } + } + + /// + /// Returns a string that represents the current object. + /// + /// + /// A string that represents the current object. + /// + public override string ToString() + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Optimized " + Path.GetFileName(this.OriginalFileName)); + stringBuilder.AppendLine("Before: " + this.OriginalFileSize + " bytes"); + stringBuilder.AppendLine("After: " + this.ResultFileSize + " bytes"); + stringBuilder.AppendLine("Saving: " + this.Saving + " bytes / " + Percent + "%"); + + return stringBuilder.ToString(); + } + } +} \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/PostProcessor.cs b/src/ImageProcessor.Web.PostProcessor/PostProcessor.cs new file mode 100644 index 000000000..7508c3dcf --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/PostProcessor.cs @@ -0,0 +1,111 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// The image postprocessor. +// Many thanks to Azure Image Optimizer +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.PostProcessor +{ + using System; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Threading.Tasks; + + /// + /// The image postprocessor. + /// Many thanks to Azure Image Optimizer + /// + internal static class PostProcessor + { + public async static Task PostProcessImage(string sourceFile) + { + string targetFile = Path.GetTempFileName(); + PostProcessingResultEventArgs result = await RunProcessAsync(sourceFile, targetFile); + + if (result != null && result.Saving > 0 && result.ResultFileSize > 0) + { + File.Copy(result.ResultFileName, result.OriginalFileName, true); + File.Delete(result.ResultFileName); + } + else + { + File.Delete(targetFile); + } + } + + private static Task RunProcessAsync(string sourceFile, string targetFile) + { + TaskCompletionSource tcs = new TaskCompletionSource(); + ProcessStartInfo start = new ProcessStartInfo("cmd") + { + WindowStyle = ProcessWindowStyle.Hidden, + WorkingDirectory = PostProcessorBootstrapper.WorkingPath, + Arguments = GetArguments(sourceFile, targetFile), + UseShellExecute = false, + CreateNoWindow = true, + }; + + if (string.IsNullOrWhiteSpace(start.Arguments)) + { + tcs.SetResult(null); + return tcs.Task; + } + + Process process = new Process + { + StartInfo = start, + EnableRaisingEvents = true + }; + + process.Exited += (sender, args) => + { + tcs.SetResult(new PostProcessingResultEventArgs(sourceFile, targetFile)); + process.Dispose(); + }; + + process.Start(); + + return tcs.Task; + } + + private static string GetArguments(string sourceFile, string targetFile) + { + if (!Uri.IsWellFormedUriString(sourceFile, UriKind.RelativeOrAbsolute) && !File.Exists(sourceFile)) + { + return null; + } + + string ext; + + string extension = Path.GetExtension(sourceFile); + if (extension != null) + { + ext = extension.ToLowerInvariant(); + } + else + { + return null; + } + + switch (ext) + { + case ".png": + return string.Format(CultureInfo.CurrentCulture, "/c png.cmd \"{0}\" \"{1}\"", sourceFile, targetFile); + + case ".jpg": + case ".jpeg": + return string.Format(CultureInfo.CurrentCulture, "/c jpegtran -copy all -optimize -progressive \"{0}\" \"{1}\"", sourceFile, targetFile); + + case ".gif": + return string.Format(CultureInfo.CurrentCulture, "/c gifsicle --crop-transparency --no-comments --no-extensions --no-names --optimize=3 --batch \"{0}\" --output=\"{1}\"", sourceFile, targetFile); + } + return null; + } + } +} diff --git a/src/ImageProcessor.Web.PostProcessor/PostProcessorBootstrapper.cs b/src/ImageProcessor.Web.PostProcessor/PostProcessorBootstrapper.cs new file mode 100644 index 000000000..929799504 --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/PostProcessorBootstrapper.cs @@ -0,0 +1,84 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) James South. +// Licensed under the Apache License, Version 2.0. +// +// +// The postprocessor bootstrapper. +// Many thanks to Azure Image Optimizer +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Web.PostProcessor +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Reflection; + + using ImageProcessor.Configuration; + + /// + /// The postprocessor bootstrapper. + /// Many thanks to Azure Image Optimizer + /// + internal static class PostProcessorBootstrapper + { + /// + /// Initializes static members of the class. + /// + static PostProcessorBootstrapper() + { + RegisterExecutables(); + } + + /// + /// Gets the working directory path. + /// + public static string WorkingPath { get; private set; } + + /// + /// Registers the embedded executables. + /// + public static void RegisterExecutables() + { + // None of the tools used here are called using dllimport so we don't go through the normal registration channel. + string folder = ImageProcessorBootstrapper.Instance.NativeBinaryFactory.Is64BitEnvironment ? "x64" : "x86"; + Assembly assembly = Assembly.GetExecutingAssembly(); + WorkingPath = Path.GetFullPath(Path.Combine(new Uri(assembly.Location).LocalPath, "..\\imageprocessor.postprocessor\\")); + + // Create the folder for storing temporary images. + // ReSharper disable once AssignNullToNotNullAttribute + DirectoryInfo directoryInfo = new DirectoryInfo(Path.GetDirectoryName(WorkingPath)); + if (!directoryInfo.Exists) + { + directoryInfo.Create(); + } + + // Get the resources and copy them across. + Dictionary resources = new Dictionary + { + {"gifsicle.exe","ImageProcessor.Web.PostProcessor.Resources.Unmanaged." + folder + ".gifsicle.exe"}, + {"jpegtran.exe","ImageProcessor.Web.PostProcessor.Resources.Unmanaged.x86.jpegtran.exe"}, + {"optipng.exe","ImageProcessor.Web.PostProcessor.Resources.Unmanaged.x86.optipng.exe"}, + {"pngout.exe","ImageProcessor.Web.PostProcessor.Resources.Unmanaged.x86.pngout.exe"}, + {"png.cmd","ImageProcessor.Web.PostProcessor.Resources.Unmanaged.x86.png.cmd" } + }; + + // Write the files out to the bin folder. + foreach (KeyValuePair resource in resources) + { + using (Stream resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource.Value)) + { + if (resourceStream != null) + { + using (FileStream fileStream = File.OpenWrite(Path.Combine(WorkingPath, resource.Key))) + { + resourceStream.CopyTo(fileStream); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Properties/AssemblyInfo.cs b/src/ImageProcessor.Web.PostProcessor/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..cd5035c52 --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ImageProcessor.Web.PostProcessor")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ImageProcessor.Web.PostProcessor")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("089f0bac-b115-4c40-a955-64da08aa0989")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/ImageProcessor.Web.PostProcessor/README.md b/src/ImageProcessor.Web.PostProcessor/README.md new file mode 100644 index 000000000..05c331aeb --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/README.md @@ -0,0 +1,13 @@ +#Resource locations + +###gifsicle +http://www.lcdf.org/gifsicle/ + +###jpegtran +http://jpegclub.org/jpegtran/ + +###optipng +http://optipng.sourceforge.net/ + +###pngout +http://advsys.net/ken/utils.htm \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x64/gifsicle.exe.REMOVED.git-id b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x64/gifsicle.exe.REMOVED.git-id new file mode 100644 index 000000000..1e5525042 --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x64/gifsicle.exe.REMOVED.git-id @@ -0,0 +1 @@ +8fdba772314bc58249bc68118054cadfabf200ba \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/gifsicle.exe.REMOVED.git-id b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/gifsicle.exe.REMOVED.git-id new file mode 100644 index 000000000..ffc8d42a9 --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/gifsicle.exe.REMOVED.git-id @@ -0,0 +1 @@ +58c7376ac5c2b3f95a36c286d35d4151abc8fafc \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/jpegtran.exe.REMOVED.git-id b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/jpegtran.exe.REMOVED.git-id new file mode 100644 index 000000000..a934efad1 --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/jpegtran.exe.REMOVED.git-id @@ -0,0 +1 @@ +c4b33334d3a5ce903fee18d9573571d93ac81bd4 \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/optipng.exe.REMOVED.git-id b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/optipng.exe.REMOVED.git-id new file mode 100644 index 000000000..3e52481dd --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/optipng.exe.REMOVED.git-id @@ -0,0 +1 @@ +81cff3c5fe3cf669ef6e158240cc0034ed3711fd \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/png.cmd b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/png.cmd new file mode 100644 index 000000000..3b515d8b5 --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/png.cmd @@ -0,0 +1,2 @@ +optipng %1 -out %2 -o3 -i0 -quiet +pngout %2 %2 /s1 /y /v /q \ No newline at end of file diff --git a/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/pngout.exe b/src/ImageProcessor.Web.PostProcessor/Resources/Unmanaged/x86/pngout.exe new file mode 100644 index 0000000000000000000000000000000000000000..0a0d7d3fa11ff6ad84bd79fabce177650114968d GIT binary patch literal 38912 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 literal 0 HcmV?d00001 diff --git a/src/ImageProcessor.Web.PostProcessor/Settings.StyleCop b/src/ImageProcessor.Web.PostProcessor/Settings.StyleCop new file mode 100644 index 000000000..bb05f99bc --- /dev/null +++ b/src/ImageProcessor.Web.PostProcessor/Settings.StyleCop @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs index fee71b606..6b2ea9eb5 100644 --- a/src/ImageProcessor.Web/Helpers/ImageHelpers.cs +++ b/src/ImageProcessor.Web/Helpers/ImageHelpers.cs @@ -63,7 +63,7 @@ namespace ImageProcessor.Web.Helpers /// public static string GetExtension(string fullPath, string queryString) { - Match match; + Match match = null; // First check to see if the format processor is being used and test against that. IWebGraphicsProcessor format = ImageProcessorConfiguration.Instance.GraphicsProcessors @@ -73,7 +73,8 @@ namespace ImageProcessor.Web.Helpers { match = format.RegexPattern.Match(queryString); } - else + + if (match == null || !match.Success) { // Test against the path minus the querystring so any other // processors don't interere. diff --git a/src/ImageProcessor.Web/Properties/AssemblyInfo.cs b/src/ImageProcessor.Web/Properties/AssemblyInfo.cs index c344110de..3024be284 100644 --- a/src/ImageProcessor.Web/Properties/AssemblyInfo.cs +++ b/src/ImageProcessor.Web/Properties/AssemblyInfo.cs @@ -40,5 +40,5 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("4.1.3.0")] -[assembly: AssemblyFileVersion("4.1.3.0")] +[assembly: AssemblyVersion("4.1.4.0")] +[assembly: AssemblyFileVersion("4.1.4.0")] diff --git a/src/ImageProcessor.sln b/src/ImageProcessor.sln index adac94cc4..8caaeabc7 100644 --- a/src/ImageProcessor.sln +++ b/src/ImageProcessor.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C427A497-74DC-49B1-8420-D6E68354F29B}" ProjectSection(SolutionItems) = preProject @@ -34,6 +34,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.Plugins.Cair EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.Playground", "ImageProcessor.Playground\ImageProcessor.Playground.csproj", "{7BF5274B-56A7-4B62-8105-E9BDF25BAFE7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageProcessor.Web.PostProcessor", "ImageProcessor.Web.PostProcessor\ImageProcessor.Web.PostProcessor.csproj", "{55D08737-7D7E-4995-8892-BD9F944329E6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution All|Any CPU = All|Any CPU @@ -181,6 +183,21 @@ Global {7BF5274B-56A7-4B62-8105-E9BDF25BAFE7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {7BF5274B-56A7-4B62-8105-E9BDF25BAFE7}.Release|Mixed Platforms.Build.0 = Release|Any CPU {7BF5274B-56A7-4B62-8105-E9BDF25BAFE7}.Release|x86.ActiveCfg = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.All|Any CPU.ActiveCfg = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.All|Any CPU.Build.0 = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.All|Mixed Platforms.ActiveCfg = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.All|Mixed Platforms.Build.0 = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.All|x86.ActiveCfg = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Debug|x86.ActiveCfg = Debug|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Release|Any CPU.Build.0 = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {55D08737-7D7E-4995-8892-BD9F944329E6}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/TestWebsites/MVC/Test_Website_MVC.csproj b/src/TestWebsites/MVC/Test_Website_MVC.csproj index 9075ef587..6c6b551c4 100644 --- a/src/TestWebsites/MVC/Test_Website_MVC.csproj +++ b/src/TestWebsites/MVC/Test_Website_MVC.csproj @@ -152,6 +152,10 @@ + + {55d08737-7d7e-4995-8892-bd9f944329e6} + ImageProcessor.Web.PostProcessor + {d011a778-59c8-4bfa-a770-c350216bf161} ImageProcessor.Web