Tuesday 1 November 2011

Image Gen, Image Cropper, DAMP, and Sencha with Umbraco


In this post I just want to share some tricks that have helped me with Umbraco and images.

I've been using Image Gen in conjunction with the Umbraco Image Cropper. It's great because you can specify a crop (with Image Cropper), then apply as many sizes of that crop as you want using Image Gen. Imagine you have a 16:9 crop and 4 different sizes of them.

I've also discovered the magic of Digibiz Advanced Media Picker (DAMP). This is a great package which allows you to upload and select images when using documents. Unfortunately the image cropper (when inserted in a document) does not directly work with DAMP, because DAMP saves as XML, and Image Cropper requires a url (eg. /media/123456/file.jpg). So there is a little trick to get that wired up.

Finally, when I was working for Cogworks, a requirement was to use Sencha for device specific image optimisation. This is a great easy to use online service which will download your image, then pass it to the brower in an optimised size for the device.

In this post I will show how to
- Wire up the Umbraco Image cropper to use the Digibiz Advanced Media Picker
- Use ImageGen in a cshtml file to make different sizes for those cropped images
- Use Sencha to make images device optimised

Update: This post assumes you have put your Image Cropper in a document (not media).

Wiring up the Umbraco Image cropper to use the Digibiz Advanced Media Picker 
The Umbraco Image Cropper requires a url (eg. /media/123456/file.jpg). DAMP stores XML. E.g:
<DAMP fullMedia="">
  <mediaItem>
 <Image id="1489" version="23113919-ca7a-4ab2-9e0e-3160cb3168af" parentID="1488" level="3" writerID="0" nodeType="1032" template="0" sortOrder="1" createDate="2011-08-12T12:06:09" updateDate="2011-08-12T12:05:52" nodeName="promo-img" urlName="promo-img" writerName="admin" nodeTypeAlias="Image" path="-1,1243,1488,1489">
   <umbracoFile>/media/36523/promo-img.jpg</umbracoFile>
   <umbracoWidth>1200</umbracoWidth>
   <umbracoHeight>519</umbracoHeight>
   <umbracoBytes>118873</umbracoBytes>
   <umbracoExtension>jpg</umbracoExtension>
   <imageCaption />
 </Image>
  </mediaItem>
</DAMP>
To work around this, all you need is to add a property (in my case a label), and assign the value of umbracoFile to that property on the AfterSave event. Then you can crop as normal.
void Document_AfterSave(Document sender, SaveEventArgs e)
{
 // make sure "umbracoFile" has value of the DAMP parsed umbracoFile
 string dampString = sender.getProperty("dampImage").Value.ToString();
 if (dampString.Contains("<DAMP"))
 {
  string url = XDocument.Parse(dampString).Descendants("umbracoFile").Single().Value;
  sender.getProperty("umbracoFile").Value = url;
 }
 else
 {
  sender.getProperty("umbracoFile").Value = string.Empty;
 }
} 

Now that you have your image uploaded and cropped, wouldn't it be great to be able to render your image like this?
@RenderPage("ShowImage.cshtml", node.Id, "dampImage", "some suffix", "152", "115", "some css class", "some title")
The parameters: node Id, the property alias (DAMP or normal file upload), a suffix for the image, width, height, a css class, a title for the image.



The magic script
The following razor script allows you to render DAMP images using ImageGen and Sencha! The great thing about this script is that it be called from another script or from macro markup. It will give you a default image if the image does not exist. It allows you to pass in a file suffix (useful because image cropper creates new files), define width and height, add css class, and a title! AND it will use Sencha to optimise your images depending on the viewing device!


@{ /* Displays a list of news items. */}
@using System.Linq;
@using umbraco.MacroEngines;
@using System.Xml.Linq;
@{
    DynamicNode d;
    string alias;
    string suffix;
    string width;
    string height;
    string cssClass;
    string title;
      
    if (PageData.Count > 0){
        // get values from PageData   
        d = new DynamicNode(PageData[0]);
        alias = PageData[1];
        suffix = PageData[2];
        width = PageData[3];
        height = PageData[4];
        cssClass = PageData[5];
        title = PageData[6];
    }
    else{
        d = new DynamicNode(@Model.Id);
        alias = @Parameter.PropertyAlias; 
        suffix = @Parameter.Suffix;
        width = @Parameter.ImageWidth;
        height = @Parameter.ImageHeight;
        cssClass = @Parameter.ImageClass;
        title = @Parameter.ImageTitle
    }
   
    string url = string.Empty;
    if(d.GetProperty(alias) != null)
    {
        // get image url or DAMP xml
        string storedUrlValue = d.GetProperty(alias).Value;
        if (!string.IsNullOrEmpty(storedUrlValue))
        {
            if (storedUrlValue.Contains("<DAMP"))
            {
                // parse DAMP xml to get image url 
                url = XDocument.Parse(storedUrlValue).Descendants("umbracoFile").FirstOrDefault().Value.GetUrl(suffix);
            }
            else
            {
                // not DAMP XML, just a url
                url = storedUrlValue.GetUrl(@suffix);
            }
        }
        else
        {
            // no value so render a default image
            <img src="/images/no-image.png"  alt="No-Image" />
            return;
        }

        // add image gen parameters
        url += "&width=" + width + "&height=" + height + "&constrain=true&compression=100";

        // get host - check for https if you wish
        var host = string.Format("http://{0}/", HttpContext.Current.Request.ServerVariables["HTTP_HOST"]);

        // finally render the image
        <img src="http://src.sencha.io/jpg95/@host/ImageGen.ashx?image=@url" alt="@title" class="@cssClass" />
    }
    else
    {
        <img src="/images/no-image.png"  alt="No-Image" />
    }
}

Here is the extension method for adding a suffix to the filename
public static string GetUrl(this string s, string suffix)
{
     return string.Format("{0}/{1}{2}{3}", Path.GetDirectoryName(s).Replace("\\", "/"), Path.GetFileNameWithoutExtension(s), suffix, Path.GetExtension(s));
}
I hope you find this useful. I definitely use it in every project... with the odd tweak here and there :)