One of my favourite services at the moment is Transloadit, who provide an image processing API that works a treat on top of platforms like Heroku, where there are strict request timeout limits that make large uploads difficult. They handle auto-orientation of images automagically by default, and normally I’m not even aware of it happening during testing since my camera and OSX also handle auto-orientation transparently.
Recently one particular image stuck out on the staging server as un-rotated, while it worked fine locally using ImageMagick/Dragonfly. Not only that, but the width and height seemed to be wrong, warping the image (the app I’m working on uses a tiled layout, and requires the width and height of each image to be explicitly set in CSS).
I put together a few test images, and have been talking it over via email with Transloadit for the last couple of days (who’ve been super-helpful), and just for kicks thought I’d try uploading the test images to a few other sites to see how everyone else handles it. So began a descent into EXIF madness…
Eh? EXIF Orientation?
The EXIF (exchangeable image file format) standard specifies a set of tags that can be embedded in images (among other things). One of these tags specifies the orientation of the photo, and has 8 possible values which cover every possible combination of rotation and mirroring of an image. This enables you to take a picture with your camera sideways or upside-down (or even inside-out), and stand a reasonable chance of having it display properly on your computer.
This diagram from 80sidea explains the 8 orientations pretty succinctly:
Sounds simple enough…
The problem is that there doesn’t seem to be any consensus on how to handle these orientation tags on the web. Results vary wildly across sites, between different products from the same company, between browsers, and even within a single browser depending on context (yes I’m looking at you Safari). Images with the same orientation value may also be rotated differently on some sites depending on whether they’re landscape or portrait.
Yet another complication is that the seemingly basic concepts of width and height become a bit more abstract when you throw orientation tags into the mix. Is a 640x480 landscape picture with a 90° rotation tag technically landscape or portrait? Is it 640x480 or 480x640? (Short answer - it depends).
Consider the following ImageMagick example using a (technically) ‘Landscape’ image, with an orientation tag value of 5 that makes it a ‘Portrait’ image:
> identify Portrait_5.jpg
Portrait_5.jpg JPEG 600x450 600x450+0+0 8-bit DirectClass 134KB 0.000u 0:00.009
> convert Portrait_5.jpg -auto-orient Portrait_5_Oriented.jpg
> identify Portrait_5_Oriented.jpg
Portrait_5_Oriented.jpg JPEG 450x600 450x600+0+0 8-bit DirectClass 130KB 0.000u 0:00.000
According to ImageMagick, the original image is 600x450, and the second (auto-oriented) image is 450x600. According to OSX however, they’re both 450x600. Who is correct? I have no idea.
Without further ado…
Let’s take a look at how some of the biggest names on the internet handle EXIF orientation tags. All of the screenshots are of Chrome on OSX unless otherwise specified. A couple of the screenshots (e.g. Gmail) have been cropped and joined for ease of viewing as they are normally displayed as a vertical list, but none of the test images within the screenshots have been altered in any way.
Some of the sites on the ‘bad’ list are of the didn’t-even-try variety, which is possibly one of the saner reactions to this problem and not necessarily ‘bad’. I do feel there should be a consensus on how to handle this though.
The test images are all available on GitHub - try uploading them to your own site to experience the fun first-hand (the good news is that if you’re using ImageMagick you’re probably ok… -auto-orient seems pretty solid apart from the width/height reporting).
The Bad
Surprisingly, Google tops the list here (and how), failing to even display images tagged with 4 out of the 8 orientations in the EXIF standard (the four mirrored orientations). They’re clearly using a centralized image-handling service across Google+, Blogger and Picasa.
Google Plus
Broken.
Blogger
Broken.
Picasa
Broken.
Google Drive
Didn’t even try - a reasonable approach for a file storage service I guess.
Gmail
This is a composite of screenshots laid side-by-side, since they’re usually in a vertical list.
- Gets tag values 1, 2, 3 and 4 right for landscape pictures (these only involve 0° or 180° rotations, so the aspect-ratio doesn’t change).
- Fails to rotate 90° rotated landscape images (tag values 5, 6, 7 and 8).
- Handles portrait tag values 1, 5, 6 and 8 ok (odd, since 6 and 8 don’t work for landscape images), but not 2, 3, 4 or 7 (odd, since 3 and 4 do work for landscape images).
Dropbox
Handles rotation ok, but not mirroring. This is a composite of large-sized image screenshots, since the thumbnails were too small to make out.
Github
GitHub is interesting because it highlights some of the differences that can occur even within the same browser. In desktop Safari, images are not rotated in the context of an HTML page, but the same image is auto-rotated when opened in a new tab by itself (there’s a long discussion related to this on the webkit bug tracker):
On mobile safari however, the orientation tags are interpreted correctly in the context of an HTML page, which trips up GitHub since they’re obviously depending on dimensions returned by something like ImageMagick, which have width and height swapped for 90°-tagged images (note the mis-matched checkered background on landscape-5):
Posterous
Posterous appear to have mirroring figured out, but choke on 90° rotation tags (5, 6, 7 and 8) for landscape images. Interestingly, portrait image rotation for tag values 5, 6, 7 and 8 works. Posterous was the only service to accidentally (I assume) crop photos - portrait-5 and portrait-7. This is a composite image, since the thumbnail view was too small to make out.
SmugMug
This is a weird one - they’ve managed to rotate all of the ‘mirrored’ images (tag values 2, 4, 5 and 7) 180° so they appear upside-down. They do appear to have un-mirrored them though.
Imgur
Close, but chokes on 90° rotated, mirrored images (tag values 5 and 7). A half-SmugMug, if you will.
Snapfish
The same problems as Posterous, but without the random cropping.
SkyDrive
Didn’t even try. As a file storage service, fair enough I guess.
Didn’t even try.
Tumblr
Didn’t even try.
WordPress.com
Didn’t even try.
Shutterfly
Didn’t even try.
Close, but no cigar
I forgot about 500px.com originally - thanks to Sebastion in the comments for mentioning them. They get all of the rotation and mirroring right, but have a couple of unexpected cropping issues with mirrored, 90°-rotated images (tag values 5 & 7).
They don’t have the same problem when viewing a larger version of the image however, which is interesting:
Better than most.
The Good
Only a couple of the sites I looked at handle orientation as you’d expect. No great surprises with the first 3 considering their heavy photo pedigree, but nice to see TwitPic on the list.
Flickr
Photoshop.com
TwitPic
So what?
Web Standards.
However, people smarter than I’ll ever hope to be have been wrestling with this for a while by the look of it:
- A Mozilla bug on the issue (from 2005 and still going strong!).
- A Chromium discussion.
- Related WebKit discussion (marked RESOLVED FIXED).
- CSS Working Group discussion.
There’s also some suggestion of ditching the EXIF orientation tag entirely, and putting the onus on device makers to rotate photos properly. This could be problematic for embedded devices with memory or CPU restrictions, but I imagine most modern cameras and phones could manage it.
With the rise of smartphone cameras, where portrait is the default orientation, the problem is only going to get worse. I don’t claim to have any answers, but the one thing that stands out is that every site out there is re-inventing the wheel, and a lot of them are getting it wrong.
If Google can’t handle images consistently across their own sites, on a browser that they built, what hope do the rest of us have?