There are a lot of tutorials on the web about making thumbnails in ASP.NET. I’ve used some my self over the years. They all show how easy it is, but they all lack some fundamental functionality:

  • Create good thumbnails from gif images
  • Calculate the height based on the width
  • Be able to use more formats than jpg and gif

Thumbnails from gif images always turns out ugly and kind of dirty looking. If the original gif image has transparency, it turns black when it gets reformatted to a smaller gif or jpeg. That’s why I use the PNG format for gif images. The PNG format is well supported by all browsers and clients except for the older ones.

If you have an image gallery of thumbnails, you probably want all thumbnails to have the same width. The height can vary but the width is fixed. So, I added a method that calculates the height based on the width.

A JPG and TIFF is almost always better to keep as a JPG in a thumbnail, so I have added a method that keeps it that way. All other image types are thumbnailed to PNG images.

Example:
Original gif image
ASP.NET generated thumbnail

Here is the entire code used to make it work.

<%@ Import namespace="System" %>
<%@ Import namespace="System.IO" %>
<%@ Import namespace="System.Drawing" %>
<%@ Import namespace="System.Drawing.Imaging" %>

<script runat="server" language="C#">
private void Page_Load(object sender, System.EventArgs e)
{
    string filename = Request.QueryString["file"];
    int width = int.Parse(Request.QueryString["width"]);

    this.GenerateThumbnail(Server.MapPath(filename), width);
}

private void GenerateThumbnail(string filename, int width)
{
    using (System.Drawing.Image orig = System.Drawing.Image.FromFile(filename))
    {
        this.GenerateThumbnail(orig, new Size(width, CalculateHeight(orig, width)), GetFormat(filename));
    }
}

private void GenerateThumbnail(System.Drawing.Image orig, Size size, ImageFormat format)
{
    using (MemoryStream stream = new MemoryStream())
    {
        System.Drawing.Image.GetThumbnailImageAbort callback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
        System.Drawing.Image img = orig.GetThumbnailImage(size.Width, size.Height, callback, IntPtr.Zero);
        img.Save(stream, format);                    
        Response.BinaryWrite(stream.ToArray());
        Response.ContentType = "image/" + format.ToString();
    }
}

private static ImageFormat GetFormat(string filename)
{
    if (filename.EndsWith("jpg") || filename.EndsWith("jpeg") || filename.EndsWith("tiff"))
        return ImageFormat.Jpeg;

    return ImageFormat.Png;
}

private static int CalculateHeight(System.Drawing.Image img, double desiredWidth)
{
    double power = img.Width / desiredWidth;
    return (int)(img.Height / power);
}

private bool ThumbnailCallback()
{
    return false;
}
</script>

Notice the use of the MemoryStream class. This is a very important step in creating the PNG file or any other format other than JPG and GIF. This is because the ASP.NET response stream isn't searchable, but a MemoryStream is, and that what's the other formats need. This is the same for ASP.NET 1.x as well as ASP.NET 2.0. Enjoy.

The XHTML definition demands all tags to be lower-cased. Your page will not validate otherwise and will therefore not be valid XHTML. If you write all your XHTML by yourself, it shouldn’t be an issue. You simply write all tags in lower-case.

Now, imaging situations where you’re not in control over the code being written. One situation is when you let visitors/users of the website write HTML in a text box or even better, a rich text editor like FCKeditor or FreeTextBox. For some reason, no rich text editor I know of can write flawless XHTML in all situations, correct me if I’m wrong.

So, I wrote a little static helper method in C# that converts HTML tags to lower-case.

/// <summary>
/// Convert HTML tags from upper case to lower case. This is important in order
/// to make it XHTML compliant. It also includes some tags that are not
/// XHTML compliant, you can remove them if you want.
/// </summary>
private static string LowerCaseHtml(string html)
{
    string[] tags = new string[] {
    "p", "a", "br", "span", "div", "i", "u", "b", "h1", "h2",
    "h3", "h4", "h5", "h6", "h7", "ul", "ol", "li", "img",
    "tr", "table", "th", "td", "tbody", "thead", "tfoot",
    "input", "select", "option", "textarea", "em", "strong"
    };

    foreach (string s in tags)
    {
        html = html.Replace("<" + s.ToUpper(), "<" + s).Replace("/" + s.ToUpper() + ">", "/" + s + ">");;
    }

    return html;
}

If you also want to lower-case the HTML attributes, you can do it almost the same way as the HTML tags. I probably missed some attributes, but you can easily add them to the string array in the method below.

/// <summary>
/// Convert HTML attribues from upper case to lower case. This is important in order
/// to make it XHTML compliant.
/// </summary>
private static string LowerCaseAttributes(string html)
{
    string[] attributes = new string[] {
    "align", "cellspacing", "cellpadding", "valign", "border",
    "style", "alt", "title", "for", "col", "header", "clear",
    "colspan", "rows", "cols", "type", "name", "id", "target", "method"
    };

    foreach (string s in attributes)
    {
        html = html.Replace(s.ToUpper() + "=", s + "=");
    }

    return html;
}

You can use this method when you save the input from a text box or you can use it when you render the page. Here's how you change the output of the ASP.NET page by overriding the Render method. You can remove the tags you don't need from the method to optimize the performance.

protected override void Render(HtmlTextWriter writer)
{
    using (HtmlTextWriter htmlwriter = new HtmlTextWriter(new System.IO.StringWriter()))
    {
        base.Render(htmlwriter);
        writer.Write(LowerCaseHtml(htmlwriter.InnerWriter.ToString()));
    }
}

You can use this approach in conjunction with my whitespace removal method. It also uses the page's Render method.