November 25, 2014

Tridion: Image handling in Responsive design.

In responsive websites, we usually keep different breakpoints for different devices & different breakpoints require different image sizes. There are different ways to use images in responsive layouts.
  • Same image on all Devices
  • Manual upload of device specific Image
  • Generate images on Fly.
  • Tridion CID
Same image on all devices: This is simplest of all as point of development, let user upload single image and same image is published on delivery. But there are few points which needs to be taken care for this.

UI Designer has to maintain the aspect ratio in his designs across Desktop, tablet, mobile or whatever breakpoints you have. e.g

Desktop:  200  : 100
Tablet:      100 :  50
Mobile:     50:    25

so if you see the aspect ration has been maintained at 2:1.  so Maintaining aspect-ratio is very important as it can go haywire if the objects start scaling based on the screen dimension. To maintain aspect ratio of the objects, select Auto for either width or height of the object in the Primary view

Tridion side there is no special change is required. normal image publishing.

Cons: Mobile has to download the same big image, which could be bandwidth issue, also in sites where lots of images are used, this approach on tablet/mobile would not be considered best

Manual upload of device specific Image:  There are different reasons where device specific images are required:
  • Designers are not totally versed with it. so ratios are not maintained  across devices
  • Performance and bandwidth issue
  • For mobile instead of whole image only part of image is required for more cleared and focused view.
these images could be manually-generated or from some DAM System.

Tridion Implementation:

Create a category with Keywords having device information. 
Category : Device Type
Keywords - large, medium, small

Create a embedded schema "Responsive Multimedia" as shown below
ScreenSize refer above category.

For this we created a generic helper class in razor, it generate the Data-Interchange tags in images. We are using Foundation Framework for handle responsiveness of site  here. 

<img data-interchange="[@Publication.MultimediaUrl/@mobile, (default)], [@Publication.MultimediaUrl/@tablet, (medium)], [@desktop, (large)]"></img>

Now you can read the component and read the values of uploaded images and generate the image tag with data-interchange as shown above. 

Generate images on Fly  Other approach is to upload a single image and generate the rest of the device specific images on Fly using custom code. But design should maintain aspect ratio of the images. 

Tridion code: we have written a c# tbb to handle it, here is the code for it. you can customize it 

[TcmTemplateTitle("AddAssets")]
    public class AddAssets : TemplateBase
    {
        
        public override void Transform(Engine engine, Package package)
        {
            Initialize(engine, package);
            var items = m_Package.GetEntries();
            var dynamicbinaries = new Dictionary<string, string[]>();
            foreach (var i in items)
            {
                Logger.Info(i.Key + ":" + i.Value);

                if (i.Key != "GenerateDynamic") continue;
                Logger.Info("Generate Dynamic parsing:");

                var arr = i.Value.GetAsString();
                var binaries = arr.Split(',');

                dynamicbinaries.Add(binaries[0], binaries);
           }

            var allowedContentTypes = new Dictionary<ContentType, ImageFormat>()
                                   {
                                       {ContentType.Bmp, ImageFormat.Bmp},
                                       {ContentType.Jpeg, ImageFormat.Jpeg},
                                       {ContentType.Png, ImageFormat.Png},
                                       {ContentType.Gif, ImageFormat.Gif}
                                   };
            foreach (var contentType in allowedContentTypes.Keys)
            {
                foreach (var item in package.GetAllByType(contentType))
                {
                    if (dynamicbinaries.ContainsKey(item.Properties[Item.ItemPropertyTcmUri]))
                    {
                        InsertRelatedBinaries(item, allowedContentTypes[contentType], dynamicbinaries[item.Properties[Item.ItemPropertyTcmUri]]);
                    }
                }

            }
        }

        private void GenerateDeviceSpecificBinaries(Item item, ImageFormat imageFormat, string[] binary)
        {

            var mmc = new Component(new TcmUri(item.Properties[Item.ItemPropertyTcmUri]), m_Engine.GetSession());

            var binaryContent = mmc.BinaryContent;
            var binaryBytes = binaryContent.GetByteArray();
            using (var bitmap = new Bitmap(new MemoryStream(binaryBytes)))
            {
                var tabPercentage = Int32.Parse(m_Package.GetValue("TabPercentage"));
                var mobilePercentage = Int32.Parse(m_Package.GetValue("MobilePercentage"));
                var resizedBitmap = ResizeBitmap(bitmap, tabPercentage);
                AddBitmapAsBinary(item, imageFormat, mmc, binaryContent, resizedBitmap, "2", binary[2]);//Tablet Images

                resizedBitmap = ResizeBitmap(bitmap, mobilePercentage);

                AddBitmapAsBinary(item, imageFormat, mmc, binaryContent, resizedBitmap, "3", binary[1]); // Mobile Images
                resizedBitmap.Dispose();
            }
        }

        private void AddBitmapAsBinary(Item item, ImageFormat format, Component mmc, BinaryContent binaryContent, Bitmap resizedBitmap, string variant, string filename)
        {
            var resizedStream = new MemoryStream();
            resizedBitmap.Save(resizedStream, format);
            var binary = this.m_Engine.PublishingContext.RenderedItem.AddBinary(
                resizedStream, filename, variant, mmc,
                binaryContent.MultimediaType.MimeType);
            resizedStream.Dispose();
        }

        private Bitmap ResizeBitmap(Bitmap sourceBitmap, int per)
        {
          
                var sourceWidth = sourceBitmap.Width;
                var sourceHeight = sourceBitmap.Height;

                float nPercentW = per;
                float nPercentH = per;

                var  destWidth = (int)(sourceWidth * (nPercentW / 100));
                var  destHeight = (int)(sourceHeight * (nPercentH / 100));
           

            var resultBitmap = new Bitmap(destWidth, destHeight);
            var graphicsd = Graphics.FromImage(resultBitmap);
            graphicsd.InterpolationMode = InterpolationMode.HighQualityBicubic;

            graphicsd.DrawImage(sourceBitmap, 0, 0, destWidth, destHeight);
            graphicsd.Dispose();

            return resultBitmap;
        }

    }
}

Tridion CID  Contextual Image delivery  is a product from SDL which helps in transforming the images in various ways such as
  • Resize with Cropping
  • Resize without Cropping
  • Trim image
  • Change Image format
Please read this very good article for further explanation of this module. What Does Contextual Image Delivery Mean for SDL Tridion Images?