using System; using BlogEngine.Core; using BlogEngine.Core.Web.Controls; using BlogEngine.Core.Web.HttpHandlers; using System.Text.RegularExpressions; using System.Collections.Generic; using System.Web; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Imaging; /// /// Thumbnailer extension for blogengine.net. /// /// Creates thumbnail images for blog posts and pages while the content is beeing saved. /// /// Date: 2008-01-21 /// Version: 0.2 /// Tested with be.net version: 1.3 /// /// [Extension("Creates thumbnail images for blog posts and pages while the content is beeing saved", "0.1", "René Kuss")] public class Thumbnailer { #region Declarations /// /// Divides different settings in the settings string e.g. [thumb:width=100,height=200] /// private const string SettingDelimeter = ","; /// /// Divides the settings keys and values e.g. width=100 /// private const string SettingKeyValueDelimeter = "="; /// /// List of supported settings /// private static List ValidThumbnailSettings = new List(new string[] { "width", "maxwidth", "height", "maxheight", "link" }); /// /// The folder where the thumbnails are stored. This folder is relative to the App_Data\Files folder /// private const string ThumbnailPath = "Thumbnails"; /// /// The absolute filesystem path to the thumbail folder /// private static string absoluteThumbnailPath; /// /// The pattern for naming the thumbnail file. /// Parameters: 0=original filename, 1=width, 2=height, 3=extension /// private const string thumbnailFilenamePattern = "{0}_thumb_{1}_{2}.{3}"; /// /// Regular expression to extract the thumbnail settings from the image src tags /// private static Regex thumbnailerPatternRegex = new Regex( @"\[thumb:(?.*?)\]", RegexOptions.IgnoreCase | RegexOptions.Compiled); /// /// Regular expression to extract all img tags from the content /// private static Regex imageRegex = new Regex( "", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static ExtensionSettings settings; #endregion #region Constructors /// /// Initializes the static members of the class. /// static Thumbnailer() { absoluteThumbnailPath = HttpContext.Current.Server.MapPath(BlogSettings.Instance.StorageLocation + "/files/" + ThumbnailPath + "/"); } /// /// Initializes a new instance of the class. /// public Thumbnailer() { Post.Saving += new EventHandler(Post_Saving); Page.Saving += new EventHandler(Page_Saving); if (!Directory.Exists(absoluteThumbnailPath)) Directory.CreateDirectory(absoluteThumbnailPath); } #endregion #region Event Handlers void Page_Saving(object sender, SavedEventArgs e) { if (e.Action == SaveAction.Insert || e.Action == SaveAction.Update) { Page page = sender as Page; page.Content = ProcessImages(page.Content); } } void Post_Saving(object sender, SavedEventArgs e) { if (e.Action == SaveAction.Insert || e.Action == SaveAction.Update) { Post post = sender as Post; post.Content = ProcessImages(post.Content); } } #endregion #region Methods #region Post / Page content processing /// /// Processes all image tags. For every image tag that has thumbnail settings applied /// thumbnail images are generated. The image tags will be updated to show up the thumbnail images. /// /// The content of the blog post or page /// The update blog content private static string ProcessImages(string content) { Thumbnailer.GetSettings(); //extract all image src tags MatchCollection imageMatches = imageRegex.Matches(content); if (imageMatches.Count > 0) { foreach (Match imageMatch in imageMatches) { //test each image src for the thumbnailer settings tag e.g. [thumb:width=100] if (imageMatch.Value.ToLower().Contains("[thumb:")) { string oldTag = imageMatch.Value; HtmlImageTag image = new HtmlImageTag(imageMatch.Value); //extract the thumnailer tag Match thumbnailerMatch = thumbnailerPatternRegex.Match(image.Source); if (thumbnailerMatch.Success) { //get the thumbnail settings from the img src value Dictionary thumbnailSettings = ParseThumbnailSettings(thumbnailerMatch.Groups["thumbSettings"].Value); //create the thumbnail HtmlImageTag tag = Thumbnailer.CreateThumbnail(image, thumbnailSettings); if (tag != null) { if (thumbnailSettings.ContainsKey("link")) { string newTagString = Thumbnailer.AddLink(image, tag, thumbnailSettings["link"]); if (!string.IsNullOrEmpty(newTagString)) { content = content.Replace(oldTag, newTagString).Replace(thumbnailerMatch.Value, ""); continue; } } content = content.Replace(oldTag, tag.ToString()); } } } } } return content; } private static void GetSettings() { ExtensionSettings initialSettings = new ExtensionSettings("Thumbnailer"); initialSettings.Help = "Creates thumbnail images in blog posts and pages.

" + "Nore information and help can be found " + "here."; ExtensionManager.ImportSettings(initialSettings); settings = ExtensionManager.GetSettings("Thumbnailer"); } /// /// Parses the thumbnail settings from the img src. /// /// The image src value /// A dictionary of key=value pairs containig the all valid settings. Not supported settings like do=something /// will be ignored private static Dictionary ParseThumbnailSettings(string source) { Dictionary thumbSettings = null; string[] settings = source.Split(new string[] { SettingDelimeter }, StringSplitOptions.RemoveEmptyEntries); if (settings != null && settings.Length > 0) { thumbSettings = new Dictionary(); for (int i = 0; i < settings.Length; i++) { string[] data = settings[i].Split(new string[] { SettingKeyValueDelimeter }, StringSplitOptions.RemoveEmptyEntries); if (data != null && data.Length == 2 && ValidThumbnailSettings.Contains(data[0])) { thumbSettings.Add(data[0], data[1]); } } } return thumbSettings; } private static string AddLink(HtmlImageTag oldTag, HtmlImageTag thumbnailTag, string linkSetting) { string returnValue = string.Empty; string link = "{5}"; string src = oldTag.Source; string title = (!string.IsNullOrEmpty(oldTag.Title)) ? oldTag.Title : ""; string alt = (!string.IsNullOrEmpty(oldTag.AlternateText)) ? oldTag.AlternateText : ""; string rel = string.Empty; string target = string.Empty; bool success = false; switch (linkSetting.ToLower()) { case "lightbox" : rel = "rel=\"" + linkSetting + "\""; success = true; break; case "original" : success = true; break; } if (success) returnValue = string.Format(link, src, title, alt, rel, target, thumbnailTag.ToString()); return returnValue ; } #endregion #region Thumbnail creation /// /// Creates the thumbnail image /// /// The tag containing the image data /// The thumbnail settings. /// The HtmlImageTag for the newly created thumbnail. The value will be null if any errors occured. private static HtmlImageTag CreateThumbnail(HtmlImageTag tag, Dictionary thumbnailSettings) { int width = -1; int height = -1; HtmlImageTag thumbnailTag = null; if (!string.IsNullOrEmpty(tag.Source)) { if (thumbnailSettings.ContainsKey("width")) Int32.TryParse(thumbnailSettings["width"], out width); if (thumbnailSettings.ContainsKey("height")) Int32.TryParse(thumbnailSettings["height"], out height); //At least one value must be specified if (width <= 0 && height <= 0) return thumbnailTag; //Render the thumbnail image and save it. using (System.Drawing.Image newImage = Thumbnailer.RenderImage(tag.AbsolutePath, width, height)) { if (newImage != null) { string newFilename = string.Format(thumbnailFilenamePattern, tag.Filename, newImage.Width, newImage.Height, tag.Extension); // Encoder params - by DSalko// int JPEGQuality = 100; //Create a parameter collection EncoderParameters codecParameters = new EncoderParameters(1); //Fill the only parameter codecParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, JPEGQuality); //Get the codec info ImageCodecInfo codecInfo = FindEncoder(ImageFormat.Jpeg); // - end of encoder params - // newImage.Save(Path.Combine(Thumbnailer.absoluteThumbnailPath, newFilename),codecInfo,codecParameters); thumbnailTag = new HtmlImageTag(); thumbnailTag.AlternateText = tag.AlternateText; thumbnailTag.Border = tag.Border; thumbnailTag.Height = newImage.Height; thumbnailTag.Width = newImage.Width; thumbnailTag.Title = tag.Title; thumbnailTag.Source = Utils.RelativeWebRoot + "image.axd?picture=/" + ThumbnailPath + "/" + HttpUtility.UrlEncode(newFilename); newImage.Dispose(); } } } return thumbnailTag; } // by DSalko // private static ImageCodecInfo FindEncoder(ImageFormat fmt) { ImageCodecInfo[] infoArray1 = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo[] infoArray2 = infoArray1; for (int num1 = 0; num1 < infoArray2.Length; num1++) { ImageCodecInfo info1 = infoArray2[num1]; if (info1.FormatID.Equals(fmt.Guid)) { return info1; } } return null; } // /// /// Renders the thumbnail image with the sized specified by the width and height parameters. /// If both parameters are present (>0) the new image will be rendered in this size. If only /// one parameter is present the other will be calculated to keep the correct aspect ratio. /// /// The image source. /// The width. /// The height. /// The rendered Thumbnail image private static System.Drawing.Image RenderImage(string imageSource, int width, int height) { System.Drawing.Image oldImage, newImage; try { //if the image is on another server load it via http if (imageSource.StartsWith("http")) { using (System.Net.WebClient client = new System.Net.WebClient()) oldImage = System.Drawing.Image.FromStream(client.OpenRead(imageSource)); } else oldImage = System.Drawing.Image.FromFile(imageSource); Size newSize = Thumbnailer.CalculateSize(oldImage.Size, width, height); //newImage = oldImage.GetThumbnailImage(newSize.Width, newSize.Height, null, System.IntPtr.Zero); newImage = new Bitmap(oldImage, newSize); oldImage.Dispose(); } catch (Exception) { newImage = null; } return newImage; } /// /// Calculates the size of the thumbnail. /// /// Size of the original. /// The new width. /// The new height. /// private static Size CalculateSize(Size originalSize, int newWidth, int newHeight) { Size newSize = new Size(originalSize.Width, originalSize.Height); //do nothing, just set the given values if (newWidth > 0 && newHeight > 0) { newSize.Width = newWidth; newSize.Height = newHeight; } else { int width = newWidth; int height = newHeight; if (newWidth > 0) //calculate new height height = Thumbnailer.CalculateHeight(originalSize, width); else //calculate new width width = Thumbnailer.CalculateWidth(originalSize, height); newSize.Width = width; newSize.Height = height; } return newSize; } /// /// Calculates the width for the given height to keep the aspect ratio. /// /// Size of the source image. /// Height of the target. /// private static int CalculateWidth(System.Drawing.Size sourceImageSize, int targetHeight) { int newWidth = (int)((float)sourceImageSize.Width / (((float)sourceImageSize.Height) / targetHeight)); return newWidth; } /// /// Calculates the height for the given width to keep the aspect ratio /// /// Size of the source image. /// Width of the target. /// private static int CalculateHeight(System.Drawing.Size sourceImageSize, int targetWidth) { int newHeight = (int)((float)sourceImageSize.Height / (((float)sourceImageSize.Width) / targetWidth)); return newHeight; } #endregion #endregion #region Helper classes /// /// Encapsulate an html image tag in an easy to use object. /// private class HtmlImageTag { #region Declarations private string source = null; private int width = -1; private int height = -1; private string alternateText = null; private string title = null; private int border = -1; private string absolutePath = null; private string style = null; private string extension; private string filename; private static Regex attributeRegex = new Regex( "(?[^\\s]*)=['|\"](?[^'|\"]*)['|\"]", RegexOptions.IgnoreCase); #endregion #region Properties public string Title { get { return title; } set { title = value; } } public string AlternateText { get { return alternateText; } set { alternateText = value; } } public int Height { get { return height; } set { height = value; } } public int Width { get { return width; } set { width = value; } } public string Source { get { return source; } set { source = value; this.ParseFilename(source); } } public string Filename { get { return filename; } } public string Extension { get { return extension; } } public int Border { get { return border; } set { border = value; } } public string Style { get { return style; } set { style = value; } } public string AbsolutePath { get { return absolutePath; } } #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// A html image tag that will be parsed to initialize the class. public HtmlImageTag(string imgTag) { HtmlImageTag image = HtmlImageTag.Parse(imgTag); this.alternateText = image.AlternateText; this.Border = image.Border; this.Height = image.Height; this.Source = image.Source; this.Title = image.Title; this.Width = image.Width; this.Style = image.Style; } /// /// Initializes a new instance of the class. /// public HtmlImageTag() { } #endregion #region Methods /// /// Parses the specified img tag to create a new instance of class. /// /// The img tag. /// An instance of the class that is initialized with the /// values from the input img tag public static HtmlImageTag Parse(string imgTag) { MatchCollection attributeMatches = attributeRegex.Matches(imgTag); HtmlImageTag tag = null; if (attributeMatches.Count > 0) { tag = new HtmlImageTag(); for (int i = 0; i < attributeMatches.Count; i++) { if (attributeMatches[i].Success && attributeMatches[i].Groups.Count == 3) { switch (attributeMatches[i].Groups["tagname"].Value.ToLower()) { case "src": tag.Source = attributeMatches[i].Groups["tagvalue"].Value; break; case "alt": tag.AlternateText = attributeMatches[i].Groups["tagvalue"].Value; break; case "border": int border = -1; Int32.TryParse(attributeMatches[i].Groups["tagvalue"].Value, out border); tag.Border = border; break; case "height": int height = -1; Int32.TryParse(attributeMatches[i].Groups["tagvalue"].Value, out height); tag.Height = height; break; case "width": int width = -1; Int32.TryParse(attributeMatches[i].Groups["tagvalue"].Value, out width); tag.Width = width; break; case "title": tag.Title = attributeMatches[i].Groups["tagvalue"].Value; break; case "style": tag.Style = attributeMatches[i].Groups["tagvalue"].Value; break; } } } } return tag; } /// /// Returns the html image tag of this class. /// /// /// The image tag string /// public override string ToString() { StringBuilder imgTag = new StringBuilder(); imgTag.Append(" -1) imgTag.Append(" width=\"" + this.Width.ToString() + "\""); if (this.Height > -1) imgTag.Append(" height=\"" + this.Height.ToString() + "\""); if (this.Border > -1) imgTag.Append(" border=\"" + this.Border.ToString() + "\""); if (!string.IsNullOrEmpty(this.Style)) imgTag.Append(" style=\"" + this.Style + "\""); imgTag.Append("/>"); return imgTag.ToString(); } private void ParseFilename(string source) { string path = string.Empty; this.extension = source.Substring(source.LastIndexOf(".") + 1, 3); int endIndex = source.LastIndexOf(this.extension) - 1; int length, startIndex; if (source.StartsWith("http")) { if (source.Contains("picture=")) startIndex = source.LastIndexOf("picture=") + 8; else startIndex = source.LastIndexOf("/") + 1; length = endIndex - startIndex; path = HttpUtility.UrlDecode(source.Substring(startIndex, length)); if (path.Contains("/")) { startIndex = path.LastIndexOf("/") + 1; this.filename = path.Substring(startIndex); } else this.filename = path; this.absolutePath = source.Substring(0, endIndex + 4); } else { if (source.Contains("picture")) { startIndex = source.LastIndexOf("picture=") + 8; length = endIndex - startIndex; path = HttpUtility.UrlDecode(source.Substring(startIndex, length)); if (path.Contains("/")) { startIndex = path.LastIndexOf("/") + 1; this.filename = path.Substring(startIndex); } else this.filename = path; this.absolutePath = HttpContext.Current.Server.MapPath(BlogSettings.Instance.StorageLocation + "/files/" + path + "." + this.extension); } else throw new ArgumentException("Invalid path"); } } #endregion } #endregion }