Watermarking images using Sitecore

A little while ago someone asked me if they can automate the addition of watermarks using Sitecore. My normal response here is ‘mumble mumble custom code’ or something along those lines, but I found it a pretty cool idea actually so I decided to build it. A couple of options came to mind:

  1. Create a component to output the image with the watermark. I can then use Rendering Parameters to define things like the watermark text and position of the watermark. The advantage of this would be that I wouldn’t need 2 or more images (one per watermark and an unwatermarked image)
  2. Create an image handler which adds the watermark immediately. I can set up a template which the Image template will inherit. This means I have full visibility on the image and watermark, but does mean I’ll have to have an added image if I want to display it without watermark, and an additional copy of image per watermark if I want to have different text
  3. Similar to the 2nd option, but then instead of adding the fields per image I can have a Watermark Settings item. This way the watermark text will always be the same across the site, and I could build something smart with Sitecore’s Rules Engine to decide whether or not the image should get the watermark added to it.

I decided to go for option 2 because while potentially it might mean more maintenance work (at least for content editors) it’s a nice and simple way of getting it set up.

First thing to do is create a Watermark base template. I’ve kept things simple and only added 2 fields: a checkbox for whether the image should be watermarked and a single-line text field for the watermark text. After setting this new template as a base template for the Image field we can go look at the interesting stuff.

So, first thing to do is replace Sitecore’s image handler with my own. Sadly, the handler is defined in the <system.webServer> node, so I won’t be able to swap things out with a web.config.

In the <handlers> node under the <system.webServer> find this line:

<add verb="*" path="sitecore_media.ashx" type="Sitecore.Resources.Media.MediaRequestHandler, Sitecore.Kernel" name="Sitecore.MediaRequestHandler" />

The type needs to be replaced with our custom assembly:

<add verb="*" path="sitecore_media.ashx" type="Sandbox.ImageWatermark.CustomMediaRequestHandler, Sandbox" name="Sitecore.MediaRequestHandler" />

Now we can create our new class and have that inherit from Sitecore’s MediaRequestHandler.

public class CustomMediaRequestHandler : MediaRequestHandler

Most of the stuff we need to do is done by Sitecore’s base class, and I don’t want to mess with it. I’ll override the DoProcessRequest though.

protected override bool DoProcessRequest(HttpContext context, MediaRequest request, Media media)
{
    …
}

First things first: I’ll check whether the image needs to be watermarked, and if it doesn’t Sitecore can do its normal things.

var item = media.MediaData.MediaItem.InnerItem;
if (!MainUtil.GetBool(item["Watermark"], false))
{
    return base.DoProcessRequest(context, request, media);
}

Most of my code is actually the same as what Sitecore does normally as well, so I’m not going to copy and paste that code. However, there’s 2 areas where Sitecore handles the media requests which need to change.

if (Settings.Media.EnableRangeRetrievalRequest && Settings.Media.CachingEnabled) 
{ 
    using (mediaStream) 
    { 
        … 
        Stream stream = new MemoryStream(); 
        AddWatermark(mediaStream, item["Watermark Text"], stream); 
        MediaStream s = new MediaStream(stream, item["Extension"], media.MediaData.MediaItem); 
        new RangeRetrievalResponse(RangeRetrievalRequest.BuildRequest(context, media), s).ExecuteRequest(context); 
        … 
    } 
} 

More on the streams later. If that if statement is evaluated as false we’ll have to transmit the stream in another way.

…
using (mediaStream)
{
    context.Response.AddHeader("Content-Length", mediaStream.Stream.Length.ToString());
    Stream stream = new MemoryStream();
    AddWatermark(mediaStream, item["Watermark Text"], stream);
    WebUtil.TransmitStream(stream, context.Response, Settings.Media.StreamBufferSize);
    stream.Dispose();
}
…

Then all that’s left is the AddWatermark method:

private void AddWatermark(MediaStream stream, string watermark, Stream outputStream)
{
    Image image = Image.FromStream(stream.Stream);
    Font font = new Font("Verdana", 16, FontStyle.Bold, GraphicsUnit.Pixel);
    Color color = Color.FromArgb(100, 0, 0, 0);
    Point point = new Point(10, 30);
    SolidBrush brush = new SolidBrush(color);
    Graphics graphics = null;

    try
    {
        graphics = Graphics.FromImage(image);
    }
    catch
    {
        Image img = image;
        image = new Bitmap(image.Width, image.Height);
        graphics = Graphics.FromImage(img);
        graphics.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
        img.Dispose();
    }

    graphics.DrawString(watermark, font, brush, point);
    graphics.Dispose();
    font.Dispose();
    brush.Dispose();

    image.Save(outputStream, ImageFormat.Jpeg);
    image.Dispose();
}

So first we load the image from a stream, then use the Graphics object to add the watermark text in some arbitrary font and size (which I could let Sitecore dictate as well of course). We save the image to a stream, which is used again. In my code I’m always saving the images as Jpeg, which might have some effect on the images (for example when you have a transparent image). Something a bit more clever should be written to replace that of course.

And then here is our end result:

results

Of course, there are some things we could improve on here – we could give content editors more flexibility by allowing them to specify the font, the text colour, the position etc. but the point is clear that it is possible to automatically add watermarks – in fact it was quite easy to do.

And as an additional bonus which is completely logical but I didn’t think about when I was writing my code: The watermark gets added to the media library as well!

library

Advertisements

3 thoughts on “Watermarking images using Sitecore

  1. I was thinking about this the other day as well, since it was closely related to my latest blog post. The only downside I can see to this is that the watermark would get added for every request. Depending on how resource intensive that is it may not be what you want and instead you may want to cache the watermarked image instead so it does not need to be added every single time. Check the Dianoga project for some inspiration.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s