Maintain Exif Data With Sitecore Image Resizing

Exif or Exchangeable Image File Format up until recently was something I had not heard about (or least didn’t put much thought into).  Being a back end developer I had never really had need for it.  Image sizing in relation to load speed on a page is as far as I had ever dug into data around images.  But it turns out there is a whole world of data that images carry around, and using Sitecore’s built in image resizing is the first step to making it go away!

I recently worked on a project that was very image intensive.  One of the requirements was to place original images in the Sitecore media library, and through the use of the max width and max height properties to render out a resized image to the web page.  This is a strategy I have used many times over and is a very solid solution to handle optimizing images for web.  Jpegs that are 4 to 6 megabytes in size can be brought down in size to as little as 10 kilobytes, using this process.  Add in a CDN on top of caching and you have yourself a screaming fast web page to present to the world.

The one dilemma with this approach is that when Sitecore does the resizing, they make a copy of the image to its resized format and ignore all the Exif data that was in the original image.  The data that can be present is a rather large list and can be viewed here.

I spoke with Sitecore support and we devised a plan to do a little customization in order to maintain this data through on a resize.  The idea is to have two custom classes.  The first gathers the data from the original and stores it in the pipeline arguments. Then later on when we are creating our new image, these pipeline arguments are saved with the image.  Here is the code to make this happen implementing the ColorProfile property:

The first class should be attached before the ResizeProcessor of the getMediaStream pipeline.

public void Process(GetMediaStreamPipelineArgs args)
{
if (args.MediaData.MimeType.StartsWith(“image/”, StringComparison.Ordinal) && args.Options.GetTransformationOptions().ContainsResizing())
{
Bitmap img = new Bitmap(args.OutputStream.Stream); // getting the image from the memory stream
MemoryStream stream = new MemoryStream();
img.Save(stream, img.RawFormat);
args.OutputStream = new MediaStream(stream, args.MediaData.Extension, args.MediaData.MediaItem);
PropertyItem[] items = img.PropertyItems; // EXIF properties are stored here
for (int i = 0; i < img.PropertyIdList.Length; i++)
{
if (img.PropertyIdList[i] == 34675) // 34675 is the ID of the color profile property. 
{
args.CustomData.Add(“ColorProfile”, items[i]); // color profile is saved in the pipeline arguments
break;
}
}
}
}

The second class is attached as the last process in the getMediaStream pipeline.

public void Process(GetMediaStreamPipelineArgs args)
{
if (args.CustomData[“ColorProfile”] as PropertyItem != null)
{
Bitmap img = new Bitmap(args.OutputStream.Stream); // getting the image from the pipeline arguments
img.SetPropertyItem(args.CustomData[“ColorProfile”] as PropertyItem); // setting the color profile property
MemoryStream stream = new MemoryStream();
img.Save(stream, img.RawFormat);
args.OutputStream = new MediaStream(stream, args.MediaData.Extension, args.MediaData.MediaItem); // saving the image as a media stream to the pipeline arguments
}
}

As you saw earlier the list of Exif data is rather extensive and the above code can be matured in one of two different ways.  First you can identify the specific properties you need to maintain and lay out the logic similar to the ColorProfile.  Just map the new IDs you need and build them in.  Of course the list is rather large, so this approach might not be ideal unless you can specifically identify a handful to pass through.

Second option is to dynamically loop through the list that is currently present, save those as pipeline arguments, and then loop through a second time.  This in theory would gather all the data and ensure that it is all pushed through to the new image.  However, this would need to be vetted deeper as some of the Exif data properties revolve around height, width, and sizing.  Presenting this information in a newly resized image could produce some interesting results.  Ideally you would create a list of ids to exclude and check for those before adding a property to the pipeline arguments.

Overall this was an interesting scenario to work through and one that Sitecore Support was very helpful in building a solution for.  I hope to see this as a feature built directly into Sitecore some day with configurations to turn it on and off as well as listing of properties to either include or exclude.

 

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 )

Facebook photo

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

Connecting to %s