Simon Green's Developer Blog
Developing .NET in the cold white north ...

Stupid web trick - displaying an image without an image (Image2Html)

Saturday, 5 January 2008 13:00 by Simon

This is an old project I came across while seaching for a different project called Html2Image (which, given a URL would produce an image of the rendered page). I can only describe this as a 'stupid web trick' although there may be some uses for it. This one will take an image file and convert it to HTML.

Not to an HTML <img src="theimage.gif"/> tag though, to actual HTML - each pixel is an HTML element with the background color coming from a 'palette' of CSS styles. This is done to try and reduce the size of the generated XHTML and some RLE (Run Length Encoding) is also used to shrink repeated pixels of the same color to a single element entry in the output. For large images the output size will be prohibitive but for small images and icons it becomes more usable.

The net result is that you see the same image on the screen in the browser (or in an email client?) even if images are turned off. I did some experiments (again, several years ago) and it was possible to have richer-looking emails without needing images but again, things may have moved on and it may not be viable anymore.

The original project (at least 3 or 4 years years old) used <p> elements for each pixel but browsers must have moved on (and XHTML rendering is different to ye-olde-HTML) so I had to change the element tags to make it work again. It runs ok on IE and Safari but Mozilla / Firefox doesn't render it as it is (I honestly really don't know why people rave about it). I'm sure a bit of playing around with different element types and CSS attributes (line-height, font-size etc...) will produce something that runs. It may require different element types on different browsers (to cater for browsers that refuse to render empty elements and such like) but the approach will be the same.

Here are some screenshots of an example page which shows the same image displayed as a regular XHTML <img> tag and also as XHTML ...

Internet Explorer:

Safari:

Source Code: 

The VS.NET 2008 project is downloadable from the bottom of this post but here is the actual class that does the work and is usable in any version of the .NET runtime:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Text;

namespace InteSoft.Web.Utility
{
    public static class Image2Html
    {
        private const string containerElement = "div";
        private const string containerClass = "image";
        private const string pixelElement = "img";
        private const string pixelClassPrefix = "p";

        public static string Convert(string url)
        {
            if (null == url)
                throw new ArgumentNullException("url");

            if (url.Length == 0)
                throw new ArgumentException("url required", "url");

            Uri uri = new Uri(url);
            return Convert(uri);
        }

        public static string Convert(Uri url)
        {
            if (null == url)
                throw new ArgumentNullException("url");

            if (!url.IsAbsoluteUri)
                throw new ArgumentException("absolute url required", "url");
           
            WebClient wc = new WebClient();
            byte[] imgBytes = wc.DownloadData(url);
            MemoryStream imgStream = new MemoryStream(imgBytes);
            Bitmap bitmap = (Bitmap)Image.FromStream(imgStream);

            return Convert(bitmap);
        }

        public static string Convert(Bitmap bitmap)
        {
            if (null == bitmap)
                throw new ArgumentNullException("bitmap");

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("<style>{0}.{1}{{line-height:1px;}}{0}.{1} {2}{{margin:0;padding:0;border:0;width:1px;height:1px;}}", containerElement, containerClass, pixelElement);
            ColorPalette colorPalette = bitmap.Palette;
            IDictionary<Color, int> paletteClassMap = new Dictionary<Color, int>(colorPalette.Entries.Length);
            for (int idx = 0; idx < colorPalette.Entries.Length; idx++)
            {
                Color color = colorPalette.Entries[idx];
                if (!paletteClassMap.ContainsKey(color))
                {
                    paletteClassMap.Add(color, idx);
                    sb.AppendFormat(".{0}{1:X2}{{background-color:#{2:X2}{3:X2}{4:X2};}}", pixelClassPrefix, idx, color.R, color.G, color.B);
                }
            }
            sb.AppendFormat("</style><{0} class=\"{1}\">", containerElement, containerClass);
            for (int y = 0; y < bitmap.Height; y++)
            {
                Color prevColor = bitmap.GetPixel(0, y);
                int count = 0;

                for(int x = 0; x < bitmap.Width; x++)
                {
                    Color color = bitmap.GetPixel(x, y);
                    count++;

                    if (color != prevColor || x == bitmap.Width - 1)
                    {
                        if (count == 1)
                        {
                            sb.AppendFormat("<{0} class=\"{1}{2:X2}\"/>", pixelElement, pixelClassPrefix, paletteClassMap[prevColor]);
                        }
                        else
                        {
                            sb.AppendFormat("<{0} class=\"{1}{2:X2}\" style=\"width:{3}px\"/>", pixelElement, pixelClassPrefix, paletteClassMap[prevColor], count);
                        }

                        prevColor = color;
                        count = 0;
                    }
                }
                sb.Append("<br/>");
            }

            sb.AppendFormat("</{0}>", containerElement);
           
            return sb.ToString();
        }
    }
}

Enjoy!

Download source: Image2Html.zip (8.23 kb)


Tags:  
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Comments

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading