Oh my Exif!
I’ve been developing hybrid applications for the last year and a half, and one of the most required features is the ability to select and upload a photo, either directly from the camera, or by choosing it from the photo library.
When we take a photo with the mobile camera, there’s a great number of metadata that it is stored with it, namely:
- Camera and lens details
- Location (only if the option is allowed)
You can check more details on what is stored here. The one that we will focus on is EXIF.
The EXIF (exchangeable image file format) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras. The specification uses (…) existing file formats with the addition of specific metadata tags (…).
- Wikipedia: Exif
One of these tags is the Orientation Tag, which specifies how the picture was taken. It has 8 possible values for every possible combination of rotation and mirroring of an image.
With this, it is possible to inform an image viewer (say GIMP for example), the position that the photo was taken without transforming the image. The image viewer will have to rotate and/or mirror the image to show the image correctly.
When an image has an orientation value:
- 1, it means it needs no rotation;
- 2, it means it needs to flip right;
- 3, it means it needs to rotate 180°;
- 4, it means it needs to flip right and rotate 180°;
- 5, it means it needs to flip right and rotate 90°;
- 6, it means it needs to rotate 90°;
- 7, it means it needs to flip right rotate -90°;
- 8, it means it needs to rotate -90°;
Check the image below, for a better understanding.
The mobile world and the browsers situation
This Exif standard seems very straight forward yet the orientation is handled differently, depending on the browser, it’s support for it, and on how you want to use the photo.
I’ve performed the following simple test, with the browers Chrome, Firefox, Safari and Opera:
- Take or load a photo from a mobile phone (I’ve used an IOS 9.3, Iphone 5c), using a simple HTML form;
- Show a preview of the photo in an image tag, <img/>;
On every browser, the image was shown correctly orientated.
I’ve found the results strange, since there’s this CSS property for <img/>, image-orientation, and with it one can say to use the exif declared on the photo metadata to fix the orientation. The default value is 0deg. If you want show the image with the correct orientation, the value must be from-image.
The curious thing is that according to MDN only Firefox supports image-orientation. Both Chrome and Safari have open issues for it. Opera does not support it at all. At the time I’m writing this I couldn’t find any open issue for image-orientation support on Opera. Inspecting the elements on the tested browsers, I could not find a image-orientation declared.
I’ve also performed the test on Desktop browsers and I just got more confusion. Rendering the same photo either on Chrome, Safari, Firefox and Opera, the result was an image without rotation.
Things got even stranger when loading the photo from the filesystem on the browser. On both Chrome and Firefox, the photo was correctly rotated. On Firefox, the image-orientation property was even declared.
So we have something that tells the picture to have the correct orientation, yet there are open issues for some browsers, and documentation telling that there’s no support for the property in one browser. The pictures set as source of an <img/> are correctly orientated on the mobile versions of the browsers yet on the desktop versions, it depends if the picture is being shown directly from the file system or not. What…?
<img/> vs background-image
Performing the same simple test, yet setting the photo as backgroung image, the photos were not rendered with the orientation fixed.
On stackoverflow, there’s an excelent thread explaining on when to use <img/> or a background image. To put it shortly, <img/> should be used when images are part of the content of the page. Background-image should be used when the image is not part of the content and will not always be shown on every screen size.
There’s also this interesting discussion on google groups about no apparent support on Firefox, at that time, for Exif orientation. During the discussion, it was mentioned that there’s no Exif orientation fix, when setting an image as background image. It was also mentioned that the engineer’s issue was a different one, since Firefox has support for the CSS property image-orientation on <img/>, and setting an image as a background image, when it should be content, is probably wrong.
Nevertheless with background-image, there’s consistency. When using an app and if we take a photo, we probably want to use <img/> to show it to the user, since it is content. When setting the image as a background image, the goal might be a design one. If the image needs editing, that can be done with the proper tools before setting as a background image. If you are worried about scaling and fitting the image inside of a specific container, <img/> supports object-fit and object-position.
The wrap up (so far)
Despite the good intentions, from the small test cases I´ve performed, the documentation around the exif and it’s support on the mobile browsers are not consistent. I haven’t checked how Edge, or IE (yes it still counts), are faring on this yet there are several more test combinations that I haven’t covered here. Though scratching only a bit of the surface, I’ve managed to reach some level of inconsistency between browser versions and platforms.
This inconsistency has been here for a while and I understand that standards take time to become stable, or even implemented. Sometimes it depends on the community to help things go forward.
Until then, to get the correct orientation for the photo, you can write code and solve this issue yourself with the help of these 2 libraries:
RotateJS uses canvas. Transforming images with canvas can be slow and resource consuming, specially on a mobile phone, yet it does what I need. Instead of using RotateJS, you can always do rotation with CSS. I can only say it’s faster but that’s a different subject. You can check by yourself with a tiny project that I’ve done to check the rotation.
It will be re-inventing the wheel or implementing a new a solution for something that it is has already been dealt with (or people are still dealing with it) yet I find it better than to depend on something that is not quite there yet (for now).
Even so don’t forget to go to the issues, comment and check how you make a contribution to help.