|
 Monday, September 17, 2007
« Belkin is Gangster | Main | A Sandbox/Test Server Is a Sandbox For A... »

Watermarking Images in ASP.NET with an HttpHandler

This will be a first of a couple posts about this ImageHandler and ways it can be used.

At times I've worked for different very creative and artistic companies (here, here, here and here) and during those times I have wanted to watermark an image for one reason or another. Either the image was protected and was not supposed to be redistributed or the client just wanted to make sure their URL was present on the image. The reasons are plentiful.

The image(s) I'm using in this project is the logo for the company I work for.

You can download the handler and a sample web site to run this handler in at the bottom of this post.

How To

I've written a quick example of how to watermark all images (jpg, png, gif or bmp) on a web site. 

It uses a simple HttpHandler that you can drop into the App_Code folder of your ASP.NET Project. With a quick addition to your web.config file you start using this.

Video

To understand what I'm saying, watch this screen cast (this screen cast has no sound):

ImageHandlerScreenCast

Now lets look at some code...

Code

(web.Config)

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <add verb="GET" type="ImageHandler" path="*.jpg,*.png,*.gif,*.bmp"/>
        </httpHandlers>
        <compilation debug="true"/></system.web>
</configuration>

ImageHandler.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net.Mime;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

/// <summary>
/// Summary description for ImageHandler
/// </summary>
public class ImageHandler : IHttpHandler
{
    public ImageHandler()
    {
    }

    public string GetContentType(String path)
    {
        switch (Path.GetExtension(path))
        {
            case ".bmp": return "Image/bmp";
            case ".gif": return "Image/gif";
            case ".jpg": return "Image/jpeg";
            case ".png": return "Image/png";
            default: break;
        }
        return String.Empty; 
    }

    public ImageFormat GetImageFormat(String path)
    {
        switch (Path.GetExtension(path).ToLower())
        {
            case ".bmp": return ImageFormat.Bmp;
            case ".gif": return ImageFormat.Gif;
            case ".jpg": return ImageFormat.Jpeg;
            case ".png": return ImageFormat.Png;
            default: return null;
        }
    }
    
    protected byte[] WatermarkImage(HttpContext context)
    {

        byte[] imageBytes = null;
        if (File.Exists(context.Request.PhysicalPath))
        {
            // Normally you'd put this in a config file somewhere.
            string watermark = "John Doe - © EXAMPLE Company 2007";

            Image image = Image.FromFile(context.Request.PhysicalPath);

            Graphics graphic;
            if (image.PixelFormat != PixelFormat.Indexed && 
image.PixelFormat != PixelFormat.Format8bppIndexed && 
image.PixelFormat != PixelFormat.Format4bppIndexed && 
image.PixelFormat != PixelFormat.Format1bppIndexed)
            {
                // Graphic is not a Indexed (GIF) image
                graphic = Graphics.FromImage(image);
            }
            else
            {
                /* Cannot create a graphics object from an indexed (GIF) image. 
                 * So we're going to copy the image into a new bitmap so 
                 * we can work with it. */
                Bitmap indexedImage = new Bitmap(image);
                graphic = Graphics.FromImage(indexedImage);

                // Draw the contents of the original bitmap onto the new bitmap. 
                graphic.DrawImage(image, 0, 0, image.Width, image.Height);
                image = indexedImage;
            }
            graphic.SmoothingMode = SmoothingMode.AntiAlias & SmoothingMode.HighQuality;

            Font myFont = new Font("Arial", 15);
            SolidBrush brush = new SolidBrush(Color.FromArgb(80, Color.White));

            /* This gets the size of the graphic so we can determine 
             * the loop counts and placement of the watermarked text. */
            SizeF textSize = graphic.MeasureString(watermark, myFont);

            // Write the text across the image. 
            for (int y = 0; y < image.Height; y++)
            {
                for (int x = 0; x < image.Width; x++)
                {
                    PointF pointF = new PointF(x, y);
                    graphic.DrawString(watermark, myFont, brush, pointF);
                    x += Convert.ToInt32(textSize.Width);
                }
                y += Convert.ToInt32(textSize.Height);
            }


            using (MemoryStream memoryStream = new MemoryStream())
            {
                image.Save(memoryStream, GetImageFormat(context.Request.PhysicalPath));
                imageBytes = memoryStream.ToArray();
            }

        }
        return imageBytes;
    }

    #region IHttpHandler Members

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
        context.Response.Clear();
        context.Response.ContentType = GetContentType(context.Request.PhysicalPath);
        byte[] imageBytes = WatermarkImage(context);
        if (imageBytes != null)
        {
            context.Response.OutputStream.Write(imageBytes, 0, imageBytes.Length);
        }
        else
        {
            // No bytes = no image which equals NO FILE. :)  
            // Therefore send a 404 - not found response. 
            context.Response.StatusCode = 404;
        }
        context.Response.End();
    }

    #endregion
}

Explanation

With the configuration above, we will capture all jpg, png, gif and bmp images as they are requested from the HttpRequest object.

As we encounter the image, we find it, then create an image and graphics object from it and then loop through the images height/width ratio and write some text on top of the image.

A key note about watermarking. You can simply write some text over the screen but then the image behind it would not be visible. You should ensure that the font you're overlaying is transparent therefore the image shows through the font that is on top. This is done by using the Color.FromArgpb method, which is in this piece of code:

Color.FromArgb(80, Color.White)

The "80" is a alpha value. Valid values are 0-255. If you're at 0, the color is invisible. If you're at 255, the color is solid. Here, I'm using 80. You can see the text, but you can see through it. Play with this number to see what works best for you.

Conclusion

Its easy to watermark images in ASP.NET. In future posts I'll show you how to protect certain directories images as well as how to do some other cool stuff with it like denying access (anti-leech and leech re-routing).

Download ImageHandler.zip (1.57 KB)  (This is just the cs file)

Download Example ImageHandlerDemoSite.zip (84.7 KB) (Demo site for demonstration)

#    Comments [15] |
Tuesday, September 18, 2007 1:25:52 AM (Eastern Standard Time, UTC-05:00)
How about performance wise ? I would think this would slow things quite a bit. Did you do any performance testing ?
sirrocco
Tuesday, January 15, 2008 5:33:27 AM (Eastern Standard Time, UTC-05:00)
Dear,
Donn Felker

"Watermarking Images in ASP.NET with an HttpHandler" its a great example on watermarking images in asp.net. But my problem is I want to watermark a word document with an image on the fly i.e when downloading a word document it should be watermark with logo. Can you help me in this regard...

Anees
Anees
Wednesday, February 06, 2008 11:47:53 AM (Eastern Standard Time, UTC-05:00)
Hi

Thanks for the code, it didn't helped me exactly because I need something more specific, but still I checked your code and it's a wonderful way to handle the watermarking issues, I specially liked the way you coded it, compact, clean and simple, keep up the good work and thanks again for posting this, I'll save it for some other time, and I'm using some of the concepts you used here to finally fix the problem I have, c'a!
Soki
Monday, February 18, 2008 5:50:58 PM (Eastern Standard Time, UTC-05:00)
Hi

I tried the code ,but i didn't get a result. I dont have any compilation error. What could be?
Jose
Monday, March 10, 2008 11:27:40 AM (Eastern Standard Time, UTC-05:00)
How do you specify the specific images or sections of images you want to watermark? is there a way to do this with this class or its either all or none in this casE?
Alex
Monday, March 10, 2008 1:57:55 PM (Eastern Standard Time, UTC-05:00)
In this case, its all or nothing. You could specifiy specific images in the web.config if you wanted. Or you could do it in code.

Good luck!
Monday, March 10, 2008 10:38:44 PM (Eastern Standard Time, UTC-05:00)
I found out a great way to specify certain files. Having a naming convention when you save your files and specifying it in the web.config. E.G. thumbnails could be saved as thm_thumbnail.jpg and if you want to only watermark thumbnails reference in the web.config as thm_*.jpg; just thought i would share because this kept me puzzled all day today.
Alex
Monday, March 10, 2008 11:11:58 PM (Eastern Standard Time, UTC-05:00)
does this code work with any instance of an image or just with <img src tag? It doesn't seem to like my imagebuttons very well. It leaves them unhandled. Any suggestions?
Alex
Monday, March 10, 2008 11:54:23 PM (Eastern Standard Time, UTC-05:00)
This should work with any jpg image in that ASP.NET site. Is this image outside of the site? For example, is your site in a subdirectory and is the image above the subdirectory that has the aspnet site?

ASPNET SITE: c:\inetpub\wwwroot\MySite\
Image Path: c:\inetpub\wwwroot\images\mybutton.jpg

I believe (I havent tested it yet) that if the file is not in same site, it WILL NOT be watermarked because that site is not handling the image request. The root site is handling the image request.

It would be like linking an image from another site and using it on your site.
Tuesday, March 11, 2008 11:13:45 AM (Eastern Standard Time, UTC-05:00)
no the file is in a subdirectory of the website and for some odd reason it worked today at work :S but yesterday it wouldnt work at my home computer. Anyway, that issue is fixed. Are there any measures that have to be taken to set up the server for this? I can't get it to work in my development server...I'm sorry, im just very new to this system.drawing class...
Alex
Tuesday, March 11, 2008 4:31:50 PM (Eastern Standard Time, UTC-05:00)
If you're on IIS6 (Windows Server 2003) trying to do this.....you MUST implement this important step or this WILL NOT WORK.

1) launch the IIS Management Console. Once the window is open we need to find the web site that we want to allow to have wild card mapping enabled on and go to the properties for the web site. Once we have the properties open then we need to click on the Home Directory for the application. This will allow us to click on the Application Configuration button. In the window that opens the bottom half of the form allows us to define Wildcard application maps. We need to click on the Insert button which will then allow us to enter the path to file that we want to process requests. In this case the file is c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll. We then have the option of checking that the file exists. This should be turned off. Once we have this completed all of this. We can click all the OK button's and close out the IIS management console.

Jay
Monday, March 24, 2008 9:34:20 PM (Eastern Standard Time, UTC-05:00)
hai to every one , i have try the coding for inserting watermark ... but while retriving image from database the watermark have not insert in that image , i have retrive the image by using binary writer .... help me guy's .... have a good day
Monday, March 24, 2008 11:21:22 PM (Eastern Standard Time, UTC-05:00)
sateesh,
I'm not sure I fully understand your problem. Can you post a sample snippet of code?
Thanks
Donn
Saturday, March 29, 2008 1:15:48 PM (Eastern Standard Time, UTC-05:00)
i am use the above code which u have given ,but it only work for the image which r in image field's like(imagebutton,image,imagemap),i am retriving the image from database and bing in gridview , while doing this watermark doesn't apply to that imagewhich r in gridview ,,,

thk u reply me ,,,,,,,,,,,,

have a good day...
Sunday, March 30, 2008 1:37:07 PM (Eastern Standard Time, UTC-05:00)
Sateesh,
To get this to work you'll have to chane the "WatermarkImage" method. It will have to accespt a byte[] array as the parameter and then as you bind the image you'll have to send the bytes from the database through this method. Therefore, to get this to work, you'll have to extract out the WatermarkImage method and so forth into its own library and then reference the library fromy our web project as well as in your business logic layer to access the watermarking feature.

Good luck!
Name
E-mail
(will show your gravatar icon)
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview