By Mikko Ohtamaa


2011-09-28 14:25:33 8 Comments

I'd like to rotate photos based on their original rotation, as set by the camera in JPEG EXIF image data. The trick is that all this should happen in the browser, using JavaScript and <canvas>.

How could JavaScript access JPEG, a local file API object, local <img> or remote <img>, EXIF data to read the rotation information?

Server-side answers are not OK; I am looking for a client-side solution.

8 comments

@Ali 2015-09-09 23:18:31

If you only want the orientation tag and nothing else and don't like to include another huge javascript library I wrote a little code that extracts the orientation tag as fast as possible (It uses DataView and readAsArrayBuffer which are available in IE10+, but you can write your own data reader for older browsers):

function getOrientation(file, callback) {
    var reader = new FileReader();
    reader.onload = function(e) {

        var view = new DataView(e.target.result);
        if (view.getUint16(0, false) != 0xFFD8)
        {
            return callback(-2);
        }
        var length = view.byteLength, offset = 2;
        while (offset < length) 
        {
            if (view.getUint16(offset+2, false) <= 8) return callback(-1);
            var marker = view.getUint16(offset, false);
            offset += 2;
            if (marker == 0xFFE1) 
            {
                if (view.getUint32(offset += 2, false) != 0x45786966) 
                {
                    return callback(-1);
                }

                var little = view.getUint16(offset += 6, false) == 0x4949;
                offset += view.getUint32(offset + 4, little);
                var tags = view.getUint16(offset, little);
                offset += 2;
                for (var i = 0; i < tags; i++)
                {
                    if (view.getUint16(offset + (i * 12), little) == 0x0112)
                    {
                        return callback(view.getUint16(offset + (i * 12) + 8, little));
                    }
                }
            }
            else if ((marker & 0xFF00) != 0xFF00)
            {
                break;
            }
            else
            { 
                offset += view.getUint16(offset, false);
            }
        }
        return callback(-1);
    };
    reader.readAsArrayBuffer(file);
}

// usage:
var input = document.getElementById('input');
input.onchange = function(e) {
    getOrientation(input.files[0], function(orientation) {
        alert('orientation: ' + orientation);
    });
}
<input id='input' type='file' />

values:

-2: not jpeg
-1: not defined

enter image description here

For those using Typescript, you can use the following code:

export const getOrientation = (file: File, callback: Function) => {
  var reader = new FileReader();

  reader.onload = (event: ProgressEvent) => {

    if (! event.target) {
      return;
    }

    const file = event.target as FileReader;
    const view = new DataView(file.result as ArrayBuffer);

    if (view.getUint16(0, false) != 0xFFD8) {
        return callback(-2);
    }

    const length = view.byteLength
    let offset = 2;

    while (offset < length)
    {
        if (view.getUint16(offset+2, false) <= 8) return callback(-1);
        let marker = view.getUint16(offset, false);
        offset += 2;

        if (marker == 0xFFE1) {
          if (view.getUint32(offset += 2, false) != 0x45786966) {
            return callback(-1);
          }

          let little = view.getUint16(offset += 6, false) == 0x4949;
          offset += view.getUint32(offset + 4, little);
          let tags = view.getUint16(offset, little);
          offset += 2;
          for (let i = 0; i < tags; i++) {
            if (view.getUint16(offset + (i * 12), little) == 0x0112) {
              return callback(view.getUint16(offset + (i * 12) + 8, little));
            }
          }
        } else if ((marker & 0xFF00) != 0xFF00) {
            break;
        }
        else {
            offset += view.getUint16(offset, false);
        }
    }
    return callback(-1);
  };

  reader.readAsArrayBuffer(file);
}

@Muhammad Umer 2016-03-27 14:39:13

for 2,4,5,7 to get correct image you need to rotate and flip, right?

@Lucy 2016-04-17 16:12:06

The orientation of my image is 3..How do i set the orientation to 1??

@SimeriaIonut 2016-05-03 09:12:07

I tried using a few jpg's and most of them returned -1, regardless of their orientation. Does it mean it can't acces the EXIF data?

@Ali 2016-05-03 09:15:24

@SimeriaIonut I believe it means, I checked different jpeg from different phones and it worked, you can use a hex reader to check how EXIF data is stored, or send me one of the so I can refine the code!

@SimeriaIonut 2016-05-03 09:22:17

@Ali Sure, here is one of them: i.imgur.com/gOnS43q.jpg EDIT: Better yet, I took one with my phone (iPhone 6) just a minute ago in portrait mode, it returned -1. i.imgur.com/MCxV975.jpg

@Ali 2016-05-03 11:02:05

@SimeriaIonut I checked the image in photoshop, actually it has no EXIF data, I believe services like imgur remove EXIF data from images.

@Mick 2016-10-26 13:10:07

What about png / gif?

@Ali 2016-10-26 13:28:08

@Mick PNG or GIF don't have any standard format to store image orientation stackoverflow.com/questions/9542359/…

@Hoagy Carmichael 2016-10-26 21:19:45

@Ali Very nice. Is there a reason why you specifically chose 64 * 1024 for the second parameter to slice?

@Ali 2016-10-27 02:42:19

@HoagyCarmichael The exif data wont be after the first 64KB of file data, it can even be less or you can pass the whole file.

@Philip Murphy 2016-12-05 17:31:22

Working for me, but I needed to change the last line to just reader.readAsArrayBuffer(file); without the slice as I intend to use the buffer for my base64 image, otherwise, you'll just see the first slice of the image. BTW, this is not required if you just need the orientation information. Thanks

@Dara Java 2017-06-20 11:46:57

@PhilipMurphy How exactly did you extend this to get other parts of EXIF data? I don't see any slice in the source, I want to use it to get flash data

@Ali 2017-06-20 15:28:50

@DaraJava I removed the slice part because sometimes the tag came in after the limit, but it will slow the operation if the tag is never found. Anyway, unlike orientation tag, Flash tag is not in the IFD0 directory and my code only search this part. to get Flash tag you must search SubIFD directory. You can find a good tutorial on EXIF here: media.mit.edu/pia/Research/deepview/exif.html

@Dara Java 2017-06-20 17:24:02

Thank you very much! That should be enough for me.

@Success Man 2017-08-12 13:04:20

@Ali, Seems I need you help. Look at this : stackoverflow.com/questions/45650465/…

@Hassan Baig 2018-02-06 17:21:37

@Ali: is it critical to read the whole file, as you do in reader.readAsArrayBuffer(file);? Or can we settle for reading lesser bytes? I ask because I'd want the solution to be independent of size of file selected by user. Afterall, we solely need the orientation tag, nothing more.

@Ali 2018-02-06 18:31:29

@HassanBaig, no, the beginning is enough, I used slice before, but due larger headers I removed it.

@Hassan Baig 2018-03-09 21:25:28

In some cases the line ar tags = view.getUint16(offset, little); throws RangeError: argument 1 accesses an index that is out of range. For instance, try this image: user-images.githubusercontent.com/18588945/… For answer completeness, what work-around would you suggest?

@Chuck Bergeron 2018-07-27 21:16:08

@HassanBaig I gave that image a try and received -1 (no Orientation data / not defined). When I checked it out with exiftool there is exif data there but not much, and it's missing the Orientation entirely

@martisj 2018-12-07 19:15:13

How would you do this with an <img> tag?

@Oliver Joseph Ash 2019-01-15 08:26:38

@Ali This used to include a slice for performance reasons but this was removed due to "large headers". What is meant by large headers? I'm keen to include slice if it's more performant, but I would like to understand the implications of doing so.

@Oliver Joseph Ash 2019-01-28 17:25:11

To answer my own question: Exif should appear in the first 64 KB, however this is not guaranteed—it may be possible for the Exif to extend beyond (stackoverflow.com/questions/3248946/…). Therefore we have to read the whole file for safety. Alternatively, instead of reading the file as an ArrayBuffer, we could use streams (where supported) so we lazily read the file until the Exif has been found: mobile.twitter.com/jaffathecake/status/1085443592678752256. If anyone has success with this approach, please share!

@Tom 2020-02-17 06:52:38

Thank you for you answer and for the included typescript version!

@Mirza Obaid 2020-03-12 04:14:45

your answer saved my time man. Not 100% but it solved my solution

@Kevin Grant 2018-10-26 21:14:01

Improving / Adding more functionality to Ali's answer from earlier, I created a util method in Typescript that suited my needs for this issue. This version returns rotation in degrees that you might also need for your project.

ImageUtils.ts

/**
 * Based on StackOverflow answer: https://stackoverflow.com/a/32490603
 *
 * @param imageFile The image file to inspect
 * @param onRotationFound callback when the rotation is discovered. Will return 0 if if it fails, otherwise 0, 90, 180, or 270
 */
export function getOrientation(imageFile: File, onRotationFound: (rotationInDegrees: number) => void) {
  const reader = new FileReader();
  reader.onload = (event: ProgressEvent) => {
    if (!event.target) {
      return;
    }

    const innerFile = event.target as FileReader;
    const view = new DataView(innerFile.result as ArrayBuffer);

    if (view.getUint16(0, false) !== 0xffd8) {
      return onRotationFound(convertRotationToDegrees(-2));
    }

    const length = view.byteLength;
    let offset = 2;

    while (offset < length) {
      if (view.getUint16(offset + 2, false) <= 8) {
        return onRotationFound(convertRotationToDegrees(-1));
      }
      const marker = view.getUint16(offset, false);
      offset += 2;

      if (marker === 0xffe1) {
        if (view.getUint32((offset += 2), false) !== 0x45786966) {
          return onRotationFound(convertRotationToDegrees(-1));
        }

        const little = view.getUint16((offset += 6), false) === 0x4949;
        offset += view.getUint32(offset + 4, little);
        const tags = view.getUint16(offset, little);
        offset += 2;
        for (let i = 0; i < tags; i++) {
          if (view.getUint16(offset + i * 12, little) === 0x0112) {
            return onRotationFound(convertRotationToDegrees(view.getUint16(offset + i * 12 + 8, little)));
          }
        }
        // tslint:disable-next-line:no-bitwise
      } else if ((marker & 0xff00) !== 0xff00) {
        break;
      } else {
        offset += view.getUint16(offset, false);
      }
    }
    return onRotationFound(convertRotationToDegrees(-1));
  };
  reader.readAsArrayBuffer(imageFile);
}

/**
 * Based off snippet here: https://github.com/mosch/react-avatar-editor/issues/123#issuecomment-354896008
 * @param rotation converts the int into a degrees rotation.
 */
function convertRotationToDegrees(rotation: number): number {
  let rotationInDegrees = 0;
  switch (rotation) {
    case 8:
      rotationInDegrees = 270;
      break;
    case 6:
      rotationInDegrees = 90;
      break;
    case 3:
      rotationInDegrees = 180;
      break;
    default:
      rotationInDegrees = 0;
  }
  return rotationInDegrees;
}

Usage:

import { getOrientation } from './ImageUtils';
...
onDrop = (pics: any) => {
  getOrientation(pics[0], rotationInDegrees => {
    this.setState({ image: pics[0], rotate: rotationInDegrees });
  });
};

@pimvdb 2011-09-28 15:01:23

You can use the exif-js library in combination with the HTML5 File API: http://jsfiddle.net/xQnMd/1/.

$("input").change(function() {
    var file = this.files[0];  // file
        fr   = new FileReader; // to read file contents

    fr.onloadend = function() {
        // get EXIF data
        var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));

        // alert a value
        alert(exif.Make);
    };

    fr.readAsBinaryString(file); // read the file
});

@Mikko Ohtamaa 2011-10-21 16:09:06

Thanks. The JS lib in the question looks little bit outdated, but would probably work.

@Rob Juurlink 2013-02-13 20:17:05

See also my demo of a file upload widget I just wrote. It uses the EXIF.js library mentioned above to read the EXIF orientation flag in the image file’s metatdata. Based on the information, it applies the rotation using a canvas element... sandbox.juurlink.org/html5imageuploader

@Obi Wan 2013-10-18 20:53:22

Attempting to even include binaryajax.js in my project causes an access denied error.

@jrista 2013-11-04 23:57:03

Where does the EXIF object come from? The BinaryFile script does not seem to contain it, and as far as I can tell, it isn't part of jquery or any other script I regularly use...

@DemiImp 2014-04-18 16:13:29

@jrista There's 2 js files you need to add. Check the jsfiddle link in the answer: nihilogic.dk/labs/exif/exif.js + nihilogic.dk/labs/binaryajax/binaryajax.js

@jrista 2014-04-18 16:54:41

Thanks! That's what I needed...I eventually found the exif.js file.

@Praxis Ashelin 2014-12-24 13:25:18

The library website seems down, and the only other ExifReader libraries I have found were limited in browser support. Is there any good alternative?

@Peppelorum 2015-05-05 08:29:04

@Sam Dutton 2013-09-20 09:09:45

Firefox 26 supports image-orientation: from-image: images are displayed portrait or landscape, depending on EXIF data. (See sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation.)

There is also a bug to implement this in Chrome.

Beware that this property is only supported by Firefox and is likely to be deprecated.

@DemiImp 2014-04-18 16:16:43

Thanks for the link to the bug report. I starred it so that the Chrome team knows more people want this.

@jeff forest 2020-01-15 08:59:47

According to this comment bugs.chromium.org/p/chromium/issues/detail?id=158753#c104 by a Chromium project member: "The change is in Chrome 81. That will roll out to the public as the Stable version in 8-10 week's time"

@Robin Métral 2020-04-09 12:10:29

Implemented on Chrome starting with 81 🎉 It will take a while before people update their browser though - keep an eye on caniuse

@Wonhyuk Cho 2017-04-04 08:30:52

I upload expansion code to show photo by android camera on html as normal on some img tag with right rotaion, especially for img tag whose width is wider than height. I know this code is ugly but you don't need to install any other packages. (I used above code to obtain exif rotation value, Thank you.)

function getOrientation(file, callback) {
  var reader = new FileReader();
  reader.onload = function(e) {

    var view = new DataView(e.target.result);
    if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
    var length = view.byteLength, offset = 2;
    while (offset < length) {
      var marker = view.getUint16(offset, false);
      offset += 2;
      if (marker == 0xFFE1) {
        if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
        var little = view.getUint16(offset += 6, false) == 0x4949;
        offset += view.getUint32(offset + 4, little);
        var tags = view.getUint16(offset, little);
        offset += 2;
        for (var i = 0; i < tags; i++)
          if (view.getUint16(offset + (i * 12), little) == 0x0112)
            return callback(view.getUint16(offset + (i * 12) + 8, little));
      }
      else if ((marker & 0xFF00) != 0xFF00) break;
      else offset += view.getUint16(offset, false);
    }
    return callback(-1);
  };
  reader.readAsArrayBuffer(file);
}

var isChanged = false;
function rotate(elem, orientation) {
    if (isIPhone()) return;

    var degree = 0;
    switch (orientation) {
        case 1:
            degree = 0;
            break;
        case 2:
            degree = 0;
            break;
        case 3:
            degree = 180;
            break;
        case 4:
            degree = 180;
            break;
        case 5:
            degree = 90;
            break;
        case 6:
            degree = 90;
            break;
        case 7:
            degree = 270;
            break;
        case 8:
            degree = 270;
            break;
    }
    $(elem).css('transform', 'rotate('+ degree +'deg)')
    if(degree == 90 || degree == 270) {
        if (!isChanged) {
            changeWidthAndHeight(elem)
            isChanged = true
        }
    } else if ($(elem).css('height') > $(elem).css('width')) {
        if (!isChanged) {
            changeWidthAndHeightWithOutMargin(elem)
            isChanged = true
        } else if(degree == 180 || degree == 0) {
            changeWidthAndHeightWithOutMargin(elem)
            if (!isChanged)
                isChanged = true
            else
                isChanged = false
        }
    }
}


function changeWidthAndHeight(elem){
    var e = $(elem)
    var width = e.css('width')
    var height = e.css('height')
    e.css('width', height)
    e.css('height', width)
    e.css('margin-top', ((getPxInt(height) - getPxInt(width))/2).toString() + 'px')
    e.css('margin-left', ((getPxInt(width) - getPxInt(height))/2).toString() + 'px')
}

function changeWidthAndHeightWithOutMargin(elem){
    var e = $(elem)
    var width = e.css('width')
    var height = e.css('height')
    e.css('width', height)
    e.css('height', width)
    e.css('margin-top', '0')
    e.css('margin-left', '0')
}

function getPxInt(pxValue) {
    return parseInt(pxValue.trim("px"))
}

function isIPhone(){
    return (
        (navigator.platform.indexOf("iPhone") != -1) ||
        (navigator.platform.indexOf("iPod") != -1)
    );
}

and then use such as

$("#banner-img").change(function () {
    var reader = new FileReader();
    getOrientation(this.files[0], function(orientation) {
        rotate($('#banner-img-preview'), orientation, 1)
    });

    reader.onload = function (e) {
        $('#banner-img-preview').attr('src', e.target.result)
        $('#banner-img-preview').css('display', 'inherit')

    };

    // read the image file as a data URL.
    reader.readAsDataURL(this.files[0]);

});

@zavr 2017-01-06 17:43:19

Check out a module I've written (you can use it in browser) which converts exif orientation to CSS transform: https://github.com/Sobesednik/exif2css

There is also this node program to generate JPEG fixtures with all orientations: https://github.com/Sobesednik/generate-exif-fixtures

@Mikko Ohtamaa 2017-01-06 18:51:06

Nice module! However, how does it get EXIF information out from JPEG in the first place?

@zavr 2017-01-07 06:41:32

@MikkoOhtamaa thanks and nah it does not, you have to do it with exif-js or exiftool server-side

@Sridhar Sarnobat 2017-04-04 05:45:51

This is useful. But it seems to me like it only works correctly for portrait photos, not landscape ones.

@flexponsive 2013-12-15 22:47:42

https://github.com/blueimp/JavaScript-Load-Image is a modern javascript library that can not only extract the exif orientation flag - it can also correctly mirror/rotate JPEG images on the client side.

I just solved the same problem with this library: JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images

@Alex Turpin 2011-09-28 14:28:34

If you want it cross-browser, your best bet is to do it on the server. You could have an API that takes a file URL and returns you the EXIF data; PHP has a module for that.

This could be done using Ajax so it would be seamless to the user. If you don't care about cross-browser compatibility, and can rely on HTML5 file functionality, look into the library JsJPEGmeta that will allow you to get that data in native JavaScript.

@Alex Turpin 2011-09-28 14:30:10

The last part of this article explains how the library works.

@Mikko Ohtamaa 2011-10-21 16:08:39

Thank's. The JS script looks sweet. I am not using PHP (in fact I hate it) and I was looking for pure client side Javascript solution.

@Mikko Ohtamaa 2012-11-28 09:10:46

@Jon Skeet 2012-12-01 23:32:53

@MikkoOhtamaa: You need to understand that Stack Overflow answers questions for everybody, just just the original person asking it. The next person who has the same objective as you may be a PHP developer - why would you want to deny them the information that Xeon06 included? It was inappropriate to edit that out, just because you don't want a PHP solution.

@Mikko Ohtamaa 2012-12-03 14:02:40

The question says "in Javascript" so the part was irrelevant. There are many other similar questions and answers for PHP already on the site and it is unnecessary noise regarding this question.

@Mikko Ohtamaa 2012-12-03 14:03:27

If people ask for Javascript solution they don't want to see PHP solution as the first post.

@Mikko Ohtamaa 2012-12-03 14:04:13

I'll add PHP part here for the comments for the sake of those poor PHP programming bastards: php.net/manual/en/book.exif.php

@Alex Turpin 2012-12-03 15:33:41

@MikkoOhtamaa it would seem like most disagree with you meta.stackexchange.com/questions/157338/… You seem to have some wrongful sense of ownership on the answers to your questions.

@Mikko Ohtamaa 2012-12-04 04:30:35

I edited the answer to have the correct answer at the beginning. Sorry for the fuzz.

@Obi Wan 2013-10-21 14:41:00

I tried using the jsJPEGmeta.js library and it does not seem to work for me. When trying to view the orientation of a camera image from an iPhone it gives the result as undefined when I know the orientation flag is set.

@Alex Turpin 2013-10-21 22:51:36

@ObiWan I wouldn't be sure what the problem is, I would try contacting the authors of the library or making a new question here on Stack Overflow.

@Success Man 2017-08-12 13:07:52

@Alex Turpin, Seems I need you help. Look at this : stackoverflow.com/questions/45650465/…

Related Questions

Sponsored Content

3 Answered Questions

[SOLVED] JavaScript - How to take a picture in webcam with EXIF data?

51 Answered Questions

[SOLVED] Convert form data to JavaScript object with jQuery

21 Answered Questions

[SOLVED] How do you access the matched groups in a JavaScript regular expression?

  • 2009-01-11 07:21:20
  • nickf
  • 787499 View
  • 1392 Score
  • 21 Answer
  • Tags:   javascript regex

11 Answered Questions

[SOLVED] JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images

13 Answered Questions

[SOLVED] JavaScript: client-side vs. server-side validation

1 Answered Questions

[SOLVED] Saving EXIF data to JPEG - Swift

  • 2018-10-15 06:26:34
  • Guido Kaiser
  • 608 View
  • 1 Score
  • 1 Answer
  • Tags:   swift save jpeg exif

5 Answered Questions

[SOLVED] Problem reading JPEG Metadata (Orientation)

Sponsored Content