Guide to Images

Published on July 22, 2010 by in Front End, Velocity

8

Images are highly important to web content. There are numerous ways in which one can incorporate them into a page to enhance what is there. In dotCMS, you are presented with several ways in which you can implement and manipulate image files in your system. Today, we’ll be reviewing several of them.

First off, one of the most common ways that you will work with images is right in a structure. Structures support image specific upload fields that you can then access from the content map on a page or in a widget to display them. However, many newcomers quickly learn that it’s not as simple as just echoing out $content.image onto a page, since that only returns an identifier for the image. When an image is uploaded via a field in a structure, a number of fields are actually created and added to the contentlet, all of which you can access and use. Those fields are:

  • ImageExtension
  • ImageFriendlyName
  • ImageIdentifier
  • ImageInode
  • ImageName
  • ImagePath
  • ImageTitle
  • ImageURI
  • ImageHeight
  • ImageWidth

So in a page, assuming you have a structure with an image upload field simply named “image” an example of some of these fields in use would look like:

## MAKE SURE AN IMAGE IS ACTUALLY SET
#if($UtilMethods.isSet($content.image) && $content.image != '0')
<img src="$!{content.imageImageURI}" alt="$!{content.imageImageTitle}" height="$!{content.imageImageHeight}" width="$!{content.imageImageWidth}" />
#end

Alternatively, it’s possible to use dotCMS’s /dotAsset servlet functionality to get the same result using other fields:

## MAKE SURE AN IMAGE IS ACTUALLY SET
#if($UtilMethods.isSet($content.image) && $content.image != '0')
<img src="/dotAsset/$!{content.imageImageIdentifier}.$!{content.imageImageExtension}" alt="$!{content.imageImageTitle}" height="$!{content.imageImageHeight}" width="$!{content.imageImageWidth}" />
#end

Either example returns the same end result in the browser, all that changes is the path to the image. That’s all there is to basic image embedding. But, we’re not nearly done. Let’s say you have users who sometimes upload pictures that are way too big for a space, so you need to scale them down. That is where the /resize_image servlet comes in handy, which can create scaled versions of images on the fly for you.

Example of conditional /resize_image

#if($UtilMethods.isSet($content.image) && $content.image != '0')
    ## SEE IF THE IMAGE IS OVER 250 PIXELS WIDE
    #if($content.imageImageWidth > 250)
<img src="/resize_image?id=$!{content.imageImageIdentifier}&w=250" alt="$!{content.imageImageTitle}" />
    #else
<img src="$!{content.imageImageURI}" alt="$!{content.imageImageTitle}" height="$!{content.imageImageHeight}" width="$!{content.imageImageWidth}" />
    #end
#end

Using that example, we can restrict our code to a specific width, and if it’s over it, force it down to the maximum allowable size. But wait, there’s more! You can play values against each other:

#if($UtilMethods.isSet($content.image) && $content.image != '0')
    ## SEE IF THE IMAGE IS OVER 250 PIXELS WIDE
    #if($content.imageImageWidth > 250)
        ## CHECK IF IT IS PORTRAIT OR LANDSCAPE
        #if($content.imageImageHeight > $content.imageImageWidth)
<img src="/resize_image?id=$!{content.imageImageIdentifier}&maxw=250&maxh=400" alt="$!{content.imageImageTitle}" />
        #else
<img src="/resize_image?id=$!{content.imageImageIdentifier}&w=250" alt="$!{content.imageImageTitle}" />
        #end
    #else
<img src="$!{content.imageImageURI}" alt="$!{content.imageImageTitle}" height="$!{content.imageImageHeight}" width="$!{content.imageImageWidth}" />
    #end
#end
Image resized based on width

Depending on landscape or portrait, this image's size will change

Note that this time I see if the height is more than the width, if it is, I use the maxw and maxh values (If you use one of the max parameters, you must use them both, unlike h and w, which can be used individually). This kind of code can help prevent image blowouts if you are unable to predict whether a portrait or landscape image is going to be used, so that after scaling one side down, the other side can be kept within additional length constraints.

Now that you can use images from structures and do some manipulation with them, let’s look at a few other image tools in dotCMS. There are a couple servlets and macros that are designed to give you a means of putting images on pages and doing things with them. /resize_image you are already familiar with from the previous example. Another servlet is the /thumbnail servlet, which is very similar to /resize_image and is used as an <img src> exactly the same, except that it also is capable of producing a background fill around images by including the parameters r, g, and b to represent the RGB values you want to use for the border (each one from 0 to 255). Some macros to take note of are: photoCarrousel(), titleImage(), slideshow(), photoGallery(), and randomImage(). Usage of the macros is more extensively covered in the dotCMS documentation, which will give you more information on customization variables.

A 250x250 thumbnail with a blue RGB value

A 250x250 thumbnail with a blue RGB value

The /thumbnail servlet has a fairly simple purpose: It scales an image down to fit inside the parameters you give it, and fills any remaining space around the image with the background color you set. For example, if you have an image that is 640×480, you can set the height and width 320×320. The resulting image will scale the longest side down to 320 (in this case the width), and the resulting space above and below the image would be filled in 40px above and below it (320 = (480/2) + (40*2)). The difference here between /thumbnail and /resize_image is that if you told /resize_image to have a height and width of 320, the image would get stretched and pulled out of proportion while /thumbnail will always scale the longest side to fit inside the proportions and fill the leftover.

Thumbnail Example:

<img src="/thumbnail?id=61560&r=34&g=126&b=203&h=250&w=250" alt="Image Thumbnail Using identifier" />
...or...
<img src="/thumbnail?path=/test/images/PICT1208.jpg&r=34&g=126&b=203&h=250&w=250" alt="Image Thumbnail Using path" />

Let’s try a somewhat more advanced example of image manipulation. Let’s say that you have a structure for products that includes a header image field. What you would like to do is put a widget on the page to display the product; it should show the specific header if one has been provided, and if one hasn’t, then show one at random from a group of generic banners you have already designed.

Set up a header image based on conditionally supplied image with fallback:

<div id="header">
#if($UtilMethods.isSet($content.headerImage) && $content.headerImage != '0')
    ## SEE IF THE HEADER WIDTH IS WHAT IT SHOULD BE, RESIZE IF NOT
    #if($content.headerImageImageWidth != 640)
    <img src="/resize_image?id=$!{content.headerImageImageIdentifier}&w=640" alt="Buy $!{content.productName} from Company X" />
    #else
    <img src="$!{content.headerImageImageURI}" alt="Buy $!{content.productName} from Company X" />
    #end
## IF AN IMAGE ISN'T SET, USE A MACRO TO GET ONE AT RANDOM FROM A FOLDER
#else
    ## GETS A RANDOM IMAGE AND RETURNS IT AS $image
    #randomImage('/global/images/generic-product-headers')
    <img src="/dotAsset/$!{image.identifier}.png" alt="Buy Company X Products" />
#end
</div>  <!-- #header -->
Example of an image resized down and then cropped with a div

Example of an image resized down and then cropped with a div

I have one last trick that I want to share that’s part Velocity and part CSS. One thing that the /resize_image servlet can’t do is crop images, which is pretty handy when you need to fill a space that is a set size, and you can’t predict the height and width of an image. For instance, making a rectangular image fit a square, or vice versa, without it looking squished or stretched. There are some jQuery tools for this, but they have to use the original file, which if it is very large, can be inconvenient for download speeds. This method shrinks the image to the shortest side, and centers it using a <div> as a container.

Scale and crop image using Velocity and CSS:

## MAKE SURE AN IMAGE IS THERE
#if($UtilMethods.isSet($content.imageImageURI))
    ## SEE IF IT'S TALLER OR WIDER AND SET THE APPROPRIATE SIDE MAX LENGTH
    #if($content.imageImageHeight > $content.imageImageWidth)
        #set($imageSize = "w=250")
    #else
        #set($imageSize = "h=250")
    #end
<style type="text/css">
.scaleAndCenter {
    background-color:transparent;
    background-position:center;
    background-repeat:no-repeat;
    height:250px;
    width:250px;
}
</style>
<div class="scaleAndCenter" style="background-image:url(/resize_image?$!{imageSize}&id=${issue.imageImageIdentifier});"></div>
#end

So that’s that, a look at getting images out of content and folders and into pages and widgets. Feel free to share any other tricks you’ve learned in the comments.

Comparing /resize_image in 1.7 vs. 1.9

Both images resized from the same 606kB, 1024x768 pixel original file

Both images resized from the same 606kB, 1024x768 pixel original file

There have been some questions recently regarding the quality of of images produced by the /resize_image servlet in 1.7, and if it has been improved in 1.9.  There have indeed been changes in 1.9. However, my understanding is that while the logic wrapping the servlet has been modified, the core library codec (sun.awt.image.codec.JPEGImageEncoderImpl) has not. So, what does this mean? Well, if you look at the comparison to the right, very little. Actually, it would seem the image quality has actually gone DOWN in 1.9, with more JPG artifacts being introduced than previously. However, this is offset by the fact that the image size has been substantially reduced. So it’s half of one, six of a dozen of another. There is now a config property in the 1.9 dotmarketing-config.properties file that sets DEFAULT_KEY_ANTIALIASING=java.awt.RenderingHints.VALUE_ANTIALIAS_ON, but looking at the final results, it doesn’t seem to be having an effect on smoothing the lines out or preventing hard, pixelated edges for the most part (though to be fair, the darkest, top part of the stem of the central flower does look slightly better in 1.9, that’s still offset by the fact that the highlights are clearly jaggy).

Image Gallery Recap


Photo credit: Attribution Some rights reserved by David Jackmanson


8 Responses to “Guide to Images”

  1. Joel says:

    Michael, is the resize process cleaner in 1.9? I know that in 1.7 thumbnails were super pixellated.

    • Michael Fienen says:

      My understanding is that the library and codec is the same in 1.9, but that they have changed some of the the code involved with the implementation. I did see this related entry indicated several revisions in the 1.9 codebase: http://jira.dotmarketing.net/browse/DOTCMS-3572

      I’ll do some side-by-side comparisons shortly and add them to this post if there’s any meaningful difference from 1.7 to 1.9.

    • Michael Fienen says:

      Okay, I added in a section at the end comparing the 1.7 vs. 1.9 implementations of /resize_image. The differences are small, but there, and not entirely what I had expected.

  2. Paul Seal says:

    What a great resource!

  3. Ed Eliot says:

    Very useful. Do the resize_image and thumbnail command place an upper limit on the size of image that can be generated. In my experience they scale up and down and if someone where to specify a very large size they could use this to launch a denial of service attack against a server. Also, do you know if they cache images once generated or re-generate on each page view.

    • Michael Fienen says:

      I don’t know of a way to restrict sizes on the servlets. To prevent usage as a DDoS tool, I might recommend limiting access to the servlets by direct requests (Just like you would use .htaccess in Apache to stop hotlinking of images). Now, how to do this in dotCMS? I haven’t tried this mind you, but I suspect that you could write a URLRewrite Filter rule that applies to those paths that checks a header condition for the referrer, and if it isn’t your site, issue a 403 Forbidden. As I mentioned, I haven’t actually tried this, but I’m pretty sure it should work.

      To answer your second question: yes, images generated by those servlets are cached, so they are only generated once. From that point forward, dotCMS will reference the static file.

      • Ed Eliot says:

        By size of image I mean dimensions (width and height). They’re passed as get parameters so couldn’t the servlet reject any specified over a certain size rather than going ahead and generating a graphic regardless?

Trackbacks/Pingbacks

  1. Tweets that mention Guide to Images | Learn dotCMS -- Topsy.com - July 23, 2010

    […] This post was mentioned on Twitter by Michael Fienen and dotCMS, Christopher Falzone. Christopher Falzone said: RT @dotCMS: Great tips on using images in #dotCMS. RT @fienen: Guide to Images | Learn dotCMS http://ow.ly/2fB8u […]

Leave a Reply