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
}