164

Digital camera photos are often saved as JPEG with an EXIF "orientation" tag. To display correctly, images need to be rotated/mirrored depending on which orientation is set, but browsers ignore this information rendering the image. Even in large commercial web apps, support for EXIF orientation can be spotty 1. The same source also provides a nice summary of the 8 different orientations a JPEG can have:

Summary of EXIF Orientations

Sample images are available at 4.

The question is how to rotate/mirror the image on the client side so that it displays correctly and can be further processed if necessary?

There are JS libraries available to parse EXIF data, including the orientation attribute 2. Flickr noted possible performance problem when parsing large images, requiring use of webworkers 3.

Console tools can correctly re-orient the images 5. A PHP script solving the problem is available at 6

Nick
  • 118,076
  • 20
  • 42
  • 73
flexponsive
  • 5,516
  • 6
  • 23
  • 36

12 Answers12

151

The github project JavaScript-Load-Image provides a complete solution to the EXIF orientation problem, correctly rotating/mirroring images for all 8 exif orientations. See the online demo of javascript exif orientation

The image is drawn onto an HTML5 canvas. Its correct rendering is implemented in js/load-image-orientation.js through canvas operations.

Hope this saves somebody else some time, and teaches the search engines about this open source gem :)

flexponsive
  • 5,516
  • 6
  • 23
  • 36
  • 2
    I have been using this library but recently it broke on iOS 8.3 which is where I need it to work most :( – Gordon Sun Jul 14 '15 at 05:31
  • 3
    I'm really struggling to see how this is helpful. I'm playing around with it trying to learn it so I can either parse a page and rotate images when they need to be rotated, or detect orientation and rotate file (somehow) before actually uploading it. The demo does neither. It simply takes a file from a file input and displays it the right way, when is this useful in the real world? When I parse my page and feed the URLs from the image tags into the loadImage library there is no exif data so can't do that. For the upload it returns a canvas object so I can't send that to the server or anything. – igneosaur May 20 '16 at 08:28
  • 3
    @igneosaur: you can send base64 encoded data to the server with `canvas.toDataURL()` and decode and save it server side. – br4nnigan May 30 '16 at 09:34
  • 1
    rather than use the load-image project, you can use some of its code-base to bake your own - just look in https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.js. – Andy Lorenz Jun 10 '16 at 13:54
  • 1
    I agree with @igneosaur, really struggling on how to use this library to pull jpeg images from a url and orient correctly. I don't have the file(s) local. – lostintranslation Jun 24 '16 at 17:33
  • @lostintranslation please check my answer http://stackoverflow.com/a/39384061/427622 – Fareed Alnamrouti Sep 08 '16 at 06:36
  • 1
    Is there any way to make the canvas that this library produces responsive like a regular `` tag? – Erik Berkun-Drevnig Sep 14 '16 at 18:26
  • Comrads, i really do not understand — Safari rotates an image taken from camera → i can fix its orientation with the loadImage library... but i get a «img src="blob:..."» — how do i change my original File object with the fixed image and upload it to the server? – artuska Dec 27 '17 at 11:22
  • Or i should send an original image to the server and backend should do all the orientation stuff? – artuska Dec 27 '17 at 11:43
  • Very helpful. Thanks. – Amete Blessed May 16 '18 at 15:55
  • I wouldn't consider using this library for multiple reasons because (1) GitHub issue tracker is closed and (2) the library does a lot more than just fix the orientation. Furthermore, as far as I can see it does not exclude the orientation fix from iOS Safari, where orientation is already automatically respected. – Oliver Joseph Ash Jan 16 '19 at 08:32
110

Mederr's context transform works perfectly. If you need to extract orientation only use this function - you don't need any EXIF-reading libs. Below is a function for re-setting orientation in base64 image. Here's a fiddle for it. I've also prepared a fiddle with orientation extraction demo.

function resetOrientation(srcBase64, srcOrientation, callback) {
  var img = new Image();    

  img.onload = function() {
    var width = img.width,
        height = img.height,
        canvas = document.createElement('canvas'),
        ctx = canvas.getContext("2d");

    // set proper canvas dimensions before transform & export
    if (4 < srcOrientation && srcOrientation < 9) {
      canvas.width = height;
      canvas.height = width;
    } else {
      canvas.width = width;
      canvas.height = height;
    }

    // transform context before drawing image
    switch (srcOrientation) {
      case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
      case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
      case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
      case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
      case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
      case 7: ctx.transform(0, -1, -1, 0, height, width); break;
      case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
      default: break;
    }

    // draw image
    ctx.drawImage(img, 0, 0);

    // export base64
    callback(canvas.toDataURL());
  };

  img.src = srcBase64;
};
WDuffy
  • 7,498
  • 5
  • 35
  • 42
WunderBart
  • 1,207
  • 1
  • 7
  • 9
  • 8
    The default case in the orientation `switch` is not needed, since that transformation doesn't do anything. Also, consider using `srcOrientation > 4 && srcOrientation < 9` instead of `[5,6,7,8].indexOf(srcOrientation) > -1`, because it's faster and less resource intensive (both RAM & CPU). There's no need to have an array there. This is important when batching lots of images, where every bit count. Otherwise, pretty good answer. Upvoted! – Ryan Casas Aug 14 '17 at 08:42
  • 4
    @RyanCasas I wasn't aware of how heavy can `indexOf` be comparing to what you proposed. I ran a simple loop with 10M iterations and it was 1400% faster. Nice :D Thanks a bunch! – WunderBart Aug 15 '17 at 13:31
  • This is a great answer. Much tighter than the accepted answer. You can make this faster by testing the orientation before rotating, because if it is the right way around, there is no need to do anything. In my case this is 75% of all images. I have added an example as an answer which includes an integration of both the getorientation and resetorientation functions – statler Oct 30 '17 at 10:19
  • 2
    I used this answer in a Android WebView and it turned out, that there are some Android devices, that don't support WebGL within a WebView (see https://bugs.chromium.org/p/chromium/issues/detail?id=555116) The rotation can take very long on such devices depending on the size of the image. – ndreisg Mar 27 '18 at 14:31
  • 1
    When used with the referenced `getOrientation`, I am curious whether this is efficient in terms of performance. `getOrientation` calls `fileReader.readAsArrayBuffer`, and then we call `URL.createObjectURL` and pass the result into `resetOrientation`, which loads this URL as an image. Does this mean the image file will be "loaded"/read by the browser not once but twice, or do I misunderstand? – Oliver Joseph Ash Jan 14 '19 at 21:23
  • 2
    Also, what to do about browsers where the orientation will already be respected? I believe this is the case for iOS Safari, and in browsers supporting CSS `image-orientation: from-image` when it is used. – Oliver Joseph Ash Jan 15 '19 at 07:49
  • I had to improved by drawing to canvas with this config `ctx.drawImage(img, 0, 0, width, height); return canvas.toDataURL('image/jpeg');` otherwise fantasti. Thank you very much ! – x-magix Jun 01 '20 at 22:42
  • Thanks for this, I've tried this code but resetOrientation image is more than 10 x the original file size image. Is there anything we need to do with? – Ka Tech Aug 13 '20 at 06:25
  • @KaTech, maybe something like this would help? https://stackoverflow.com/a/28297841/7225515 – WunderBart Aug 20 '20 at 17:01
  • @OliverJosephAsh, to detect if the browser handles image orientation, you can open a rectangular image and check if `width` and `height` are inverted. That' the technique used by [blueimp-load-image](https://github.com/blueimp/JavaScript-Load-Image/blob/a238dfb0ff544535a2d3e715845cfa59bb0d7522/js/load-image-orientation.js#L75-L87). – Benoit Blanchon Nov 23 '20 at 20:11
42

If

width = img.width;
height = img.height;
var ctx = canvas.getContext('2d');

Then you can use these transformations to turn the image to orientation 1

From orientation:

  1. ctx.transform(1, 0, 0, 1, 0, 0);
  2. ctx.transform(-1, 0, 0, 1, width, 0);
  3. ctx.transform(-1, 0, 0, -1, width, height);
  4. ctx.transform(1, 0, 0, -1, 0, height);
  5. ctx.transform(0, 1, 1, 0, 0, 0);
  6. ctx.transform(0, 1, -1, 0, height, 0);
  7. ctx.transform(0, -1, -1, 0, height, width);
  8. ctx.transform(0, -1, 1, 0, 0, width);

Before drawing the image on ctx

Francisco C
  • 8,871
  • 4
  • 31
  • 41
Mederr
  • 650
  • 7
  • 10
  • 61
    what is even happening in here? – Muhammad Umer Mar 27 '16 at 14:44
  • 1
    With this you just need to know the orientation (e.g. via EXIF) and then rotate as needed. Half of what I am looking for. – Brenden Apr 26 '16 at 05:59
  • 3
    these transformations didn't work for me - instead I used the code from the load-image project at https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.js to get what I believe is well-proven code which uses both translate, rotate operations on the canvas context plus a width/height swap. Gave me 100% results after many hours of experimentation with other attempts at transforming etc – Andy Lorenz Jun 10 '16 at 13:57
  • 1
    Why does it matter when you draw the image on ctx? You can't rotate the canvas after drawing an image on it? – AlxVallejo Dec 05 '17 at 17:39
  • Rotation for orientation 8 works this way for me: ctx.transform(0, -1, 1, 0, 0, height); – Giorgio Barchiesi Apr 24 '19 at 21:46
25

ok in addition to @user3096626 answer i think it will be more helpful if someone provided code example, the following example will show you how to fix image orientation comes from url (remote images):


Solution 1: using javascript (recommended)

  1. because load-image library doesn't extract exif tags from url images only (file/blob), we will use both exif-js and load-image javascript libraries, so first add these libraries to your page as the follow:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.1.0/exif.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-scale.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-orientation.min.js"></script>
    

    Note the version 2.2 of exif-js seems has issues so we used 2.1

  2. then basically what we will do is

    a - load the image using window.loadImage()

    b - read exif tags using window.EXIF.getData()

    c - convert the image to canvas and fix the image orientation using window.loadImage.scale()

    d - place the canvas into the document

here you go :)

window.loadImage("/your-image.jpg", function (img) {
  if (img.type === "error") {
    console.log("couldn't load image:", img);
  } else {
    window.EXIF.getData(img, function () {
        var orientation = EXIF.getTag(this, "Orientation");
        var canvas = window.loadImage.scale(img, {orientation: orientation || 0, canvas: true});
        document.getElementById("container").appendChild(canvas); 
        // or using jquery $("#container").append(canvas);

    });
  }
});

of course also you can get the image as base64 from the canvas object and place it in the img src attribute, so using jQuery you can do ;)

$("#my-image").attr("src",canvas.toDataURL());

here is the full code on: github: https://github.com/digital-flowers/loadimage-exif-example


Solution 2: using html (browser hack)

there is a very quick and easy hack, most browsers display the image in the right orientation if the image is opened inside a new tab directly without any html (LOL i don't know why), so basically you can display your image using iframe by putting the iframe src attribute as the image url directly:

<iframe src="/my-image.jpg"></iframe>

Solution 3: using css (only firefox & safari on ios)

there is css3 attribute to fix image orientation but the problem it is only working on firefox and safari/ios it is still worth mention because soon it will be available for all browsers (Browser support info from caniuse)

img {
   image-orientation: from-image;
}
Fareed Alnamrouti
  • 26,439
  • 4
  • 77
  • 71
  • Solution 1 did not work for me. It's returning window.loadImage is undefined – Perry Jul 08 '17 at 14:56
  • This happened if you didn't include the libraries I mention in step 1 – Fareed Alnamrouti Jul 08 '17 at 15:00
  • I included the libraries. I like the simplicity of your example but I keep getting that error message. – Perry Jul 08 '17 at 15:23
  • I will like you to have a look at my code, but i am not sure if it is proper to paste to here. – Perry Jul 08 '17 at 15:44
  • 1
    it seems exif-js was broken, please check my edit i also have added full code on github: https://github.com/digital-flowers/loadimage-exif-example – Fareed Alnamrouti Jul 08 '17 at 16:34
  • Yea, this works now. Thanks for taking out time to update your answer. Well, it will be nice to include to that github project an example in which the image is browsed from a file input element. That's `` – Perry Jul 08 '17 at 17:37
  • 1
    ok checkout the github project there is a new file "upload.html", you are welcome :) – Fareed Alnamrouti Jul 08 '17 at 18:05
  • This is the correct solution, the author is correct that Javascript Load Image does not read exif data from urls, hence the need for a secondary library. There is a small delay in reading exif data from large images but its less than a second. – Petraeus Oct 04 '17 at 16:34
10

For those who have a file from an input control, don't know what its orientation is, are a bit lazy and don't want to include a large library below is the code provided by @WunderBart melded with the answer he links to (https://stackoverflow.com/a/32490603) that finds the orientation.

function getDataUrl(file, callback2) {
        var callback = function (srcOrientation) {
            var reader2 = new FileReader();
            reader2.onload = function (e) {
                var srcBase64 = e.target.result;
                var img = new Image();

                img.onload = function () {
                    var width = img.width,
                        height = img.height,
                        canvas = document.createElement('canvas'),
                        ctx = canvas.getContext("2d");

                    // set proper canvas dimensions before transform & export
                    if (4 < srcOrientation && srcOrientation < 9) {
                        canvas.width = height;
                        canvas.height = width;
                    } else {
                        canvas.width = width;
                        canvas.height = height;
                    }

                    // transform context before drawing image
                    switch (srcOrientation) {
                        case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
                        case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
                        case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
                        case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
                        case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
                        case 7: ctx.transform(0, -1, -1, 0, height, width); break;
                        case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
                        default: break;
                    }

                    // draw image
                    ctx.drawImage(img, 0, 0);

                    // export base64
                    callback2(canvas.toDataURL());
                };

                img.src = srcBase64;
            }

            reader2.readAsDataURL(file);
        }

        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);
    }

which can easily be called like such

getDataUrl(input.files[0], function (imgBase64) {
      vm.user.BioPhoto = imgBase64;
});
runxc1 Bret Ferrier
  • 7,508
  • 14
  • 57
  • 99
  • 2
    Thank after over 2-hour search by google I find the right solution. – Mr.Trieu Jun 12 '18 at 09:37
  • 2
    why would someone be lazy to prefer a much lighter solution? – dewd Aug 08 '18 at 18:05
  • 4
    [Ported to Typescript and optimized](https://gist.github.com/mindplay-dk/72f47c1a570e870a375bd3dbcb9328fb) from about 4 seconds to ~20 milliseconds. Base64 encoding was the bottleneck - using `image/jpeg` instead of the default `image/png`, and resizing the output image to a maximum width (400px in my case) made a massive difference. (this works well for thumbnails, but if you have to display the full image, I'd suggest avoiding base64 and simply inject the canvas element directly into the page...) – mindplay.dk Dec 28 '18 at 13:01
9

WunderBart's answer was the best for me. Note that you can speed it up a lot if your images are often the right way around, simply by testing the orientation first and bypassing the rest of the code if no rotation is required.

Putting all of the info from wunderbart together, something like this;

var handleTakePhoto = function () {
    let fileInput: HTMLInputElement = <HTMLInputElement>document.getElementById('photoInput');
    fileInput.addEventListener('change', (e: any) => handleInputUpdated(fileInput, e.target.files));
    fileInput.click();
}

var handleInputUpdated = function (fileInput: HTMLInputElement, fileList) {
    let file = null;

    if (fileList.length > 0 && fileList[0].type.match(/^image\//)) {
        isLoading(true);
        file = fileList[0];
        getOrientation(file, function (orientation) {
            if (orientation == 1) {
                imageBinary(URL.createObjectURL(file));
                isLoading(false);
            }
            else 
            {
                resetOrientation(URL.createObjectURL(file), orientation, function (resetBase64Image) {
                    imageBinary(resetBase64Image);
                    isLoading(false);
                });
            }
        });
    }

    fileInput.removeEventListener('change');
}


// from http://stackoverflow.com/a/32490603
export function getOrientation(file, callback) {
    var reader = new FileReader();

    reader.onload = function (event: any) {
        var view = new DataView(event.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.slice(0, 64 * 1024));
};

export function resetOrientation(srcBase64, srcOrientation, callback) {
    var img = new Image();

    img.onload = function () {
        var width = img.width,
            height = img.height,
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext("2d");

        // set proper canvas dimensions before transform & export
        if (4 < srcOrientation && srcOrientation < 9) {
            canvas.width = height;
            canvas.height = width;
        } else {
            canvas.width = width;
            canvas.height = height;
        }

        // transform context before drawing image
        switch (srcOrientation) {
            case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
            case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
            case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
            case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
            case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
            case 7: ctx.transform(0, -1, -1, 0, height, width); break;
            case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
            default: break;
        }

        // draw image
        ctx.drawImage(img, 0, 0);

        // export base64
        callback(canvas.toDataURL());
    };

    img.src = srcBase64;
}
statler
  • 1,079
  • 12
  • 22
  • 1
    Great approach. You should also consider handling the `-2` and `-1` return from `getOrientation` so you don't try to rotate non-jpgs or images without rotation data. – Steven Lambert Jun 14 '18 at 20:10
  • I made more optimizations, see my [comment above](https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images#comment94755603_46814952) - I just added your `file.slice()` optimization as well, but the real boost comes from using smaller images and `image/jpeg` output format to create smaller data URLs. (there is no noticeable delay even for 10-megapixel images.) – mindplay.dk Dec 28 '18 at 13:13
  • Careful with `file.slice()`, as you will prevent support of base64 image sources. – Mark Dec 06 '19 at 22:34
  • Thanks mark - care to provide an edit for an alternative? – statler Dec 08 '19 at 02:39
8

One liner anyone?

I haven't seen anyone mention the browser-image-compression library. It's got a helper function perfect for this.

Usage: const orientation = await imageCompression.getExifOrientation(file)

Such a useful tool in many other ways too.

gilbert-v
  • 1,036
  • 1
  • 9
  • 20
  • This gets the orientation. But how do I produce a new .jpg File object on the client side using this information? – masterxilo Nov 06 '19 at 08:20
  • You can't create `File` objects as far as I know. The next best thing would be to send the image and the orientation up to the server, and do file manipulation there. Or you could generate a base64 string using `canvas` and the `toDataURL` method. – gilbert-v Nov 13 '19 at 09:49
  • 2
    No you can create a File object from a Blob just fine. File File( Array parts, String filename, BlobPropertyBag properties ); https://developer.mozilla.org/de/docs/Web/API/File – masterxilo Nov 15 '19 at 08:09
  • 2
    Gold sticker! @masterxilo – gilbert-v Nov 16 '19 at 14:39
  • 2
    This worked great for me! I had been struggling with orientations for the last 2 days! All the transform switch statements that were posted wouldn't handle portrait images from my phone for some reason, there would be black bars. Probably because I was trying to compress and rotate at the same time. – cjd82187 Nov 26 '19 at 13:13
  • DItto @cjd82187 's comment. It automatically corrects the orientation in the browser, handles compression and more. After it's compressed your originalFile, just use URL.createObjectURL(compressedFile) to render the compressed (and properly oriented) file instantly client side. – spedley Feb 18 '20 at 20:32
2

I created a class wrapped in an ES6 module that solves exactly this.

It's 103 lines, no dependencies, and fairly nicely structured and documented, meant to be easy to modify/reuse.

Handles all 8 possible orientations, and is Promise-based.

Here you go, hope this still helps someone: https://gist.github.com/vdavid/3f9b66b60f52204317a4cc0e77097913

Dávid Veszelovszki
  • 1,751
  • 18
  • 16
1

I am using mixed solution (php+css).

Containers are needed for:

  • div.imgCont2 container needed to rotate;
  • div.imgCont1 container needed to zoomOut - width:150%;
  • div.imgCont container needed for scrollbars, when image is zoomOut.

.

<?php
    $image_url = 'your image url.jpg';
    $exif = @exif_read_data($image_url,0,true);
    $orientation = @$exif['IFD0']['Orientation'];
?>

<style>
.imgCont{
    width:100%;
    overflow:auto;
}
.imgCont2[data-orientation="8"]{
    transform:rotate(270deg);
    margin:15% 0;
}
.imgCont2[data-orientation="6"]{
    transform:rotate(90deg);
    margin:15% 0;
}
.imgCont2[data-orientation="3"]{
    transform:rotate(180deg);
}
img{
    width:100%;
}
</style>

<div class="imgCont">
  <div class="imgCont1">
    <div class="imgCont2" data-orientation="<?php echo($orientation) ?>">
      <img src="<?php echo($image_url) ?>">
    </div>
  </div>
</div>
MrGeek
  • 19,520
  • 4
  • 24
  • 49
Sergey S
  • 58
  • 8
  • Thanks, this seems like the best solution, for my needs anyways. No need for external libraries and no long blocks of unnecessary code. Just determine orientation from the exif with php, send it to the view and use it to trigger CSS classes that rotate the appropriate number of degrees. Nice and clean! Edit: this will over rotate images in browsers that actually read the exif data and rotate accordingly. AKA it will fix the issue on desktop but creates a new one on ios safari. – cwal Oct 11 '19 at 03:58
0

In addition to @fareed namrouti's answer,

This should be used if the image has to be browsed from a file input element

<input type="file" name="file" id="file-input"><br/>
image after transform: <br/>
<div id="container"></div>

<script>
    document.getElementById('file-input').onchange = function (e) {
        var image = e.target.files[0];
        window.loadImage(image, function (img) {
            if (img.type === "error") {
                console.log("couldn't load image:", img);
            } else {
                window.EXIF.getData(image, function () {
                    console.log("load image done!");
                    var orientation = window.EXIF.getTag(this, "Orientation");
                    var canvas = window.loadImage.scale(img,
                        {orientation: orientation || 0, canvas: true, maxWidth: 200});
                    document.getElementById("container").appendChild(canvas);
                    // or using jquery $("#container").append(canvas);
                });
            }
        });
    };
</script>
Jim Bergman
  • 4,927
  • 2
  • 16
  • 19
Perry
  • 282
  • 1
  • 4
  • 11
0

I've written a little php script which rotates the image. Be sure to store the image in favour of just recalculate it each request.

<?php

header("Content-type: image/jpeg");
$img = 'IMG URL';

$exif = @exif_read_data($img,0,true);
$orientation = @$exif['IFD0']['Orientation'];
if($orientation == 7 || $orientation == 8) {
    $degrees = 90;
} elseif($orientation == 5 || $orientation == 6) {
    $degrees = 270;
} elseif($orientation == 3 || $orientation == 4) {
    $degrees = 180;
} else {
    $degrees = 0;
}
$rotate = imagerotate(imagecreatefromjpeg($img), $degrees, 0);
imagejpeg($rotate);
imagedestroy($rotate);

?>

Cheers

Thom
  • 49
  • 1
0

Wunderbart's post worked for me combined with statler's improvements. Adding a few more comments and syntax cleanup, and also passing back the orientation value and I have the following code feel free to use. Just call readImageFile() function below and you get back the transformed image and the original orientation.

const JpegOrientation = [
    "NOT_JPEG",
    "NORMAL",
    "FLIP-HORIZ",
    "ROT180",
    "FLIP-HORIZ-ROT180",
    "FLIP-HORIZ-ROT270",
    "ROT270",
    "FLIP-HORIZ-ROT90",
    "ROT90"
];


//Provided a image file, determines the orientation of the file based on the EXIF information.
//Calls the "callback" function with an index into the JpegOrientation array. 
//If the image is not a JPEG, returns 0. If  the orientation value cannot be read (corrupted file?) return -1.
function getOrientation(file, callback) {
    
    const reader = new FileReader();
    reader.onload = (e) => {

        const view = new DataView(e.target.result);
        
        if (view.getUint16(0, false) !== 0xFFD8) {
            return callback(0);  //NOT A JPEG FILE
        }
        
        const length = view.byteLength;
        let offset = 2;
        while (offset < length) {
            
            if (view.getUint16(offset+2, false) <= 8)   //unknown?
                return callback(-1);
            
            const marker = view.getUint16(offset, false);
            offset += 2;
            if (marker === 0xFFE1) {
                
                if (view.getUint32(offset += 2, false) !== 0x45786966) 
                    return callback(-1); //unknown?
                

                const little = view.getUint16(offset += 6, false) === 0x4949;
                offset += view.getUint32(offset + 4, little);
                const 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));   //found orientation code
                    }
                }
            }
            else if ((marker & 0xFF00) !== 0xFF00) {
                break;
            }
            else { 
                offset += view.getUint16(offset, false);
            }
        }
        
        return callback(-1); //unknown?
    };
    reader.readAsArrayBuffer(file);
}

//Takes a jpeg image file as base64 and transforms it back to original, providing the
//transformed image in callback.  If the image is not a jpeg or is already in normal orientation,
//just calls the callback directly with the source.
//Set type to the desired output type if transformed, default is image/jpeg for speed.
function resetOrientation(srcBase64, srcOrientation, callback, type = "image/jpeg") {
    
    if (srcOrientation <= 1) {  //no transform needed
        callback(srcBase64);
        return;
    }
    
    const img = new Image();    

    img.onload = () => {
        const width = img.width;
        const height = img.height;
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext("2d");

        // set proper canvas dimensions before transform & export
        if (4 < srcOrientation && srcOrientation < 9) {
            canvas.width = height;
            canvas.height = width;
        } else {
            canvas.width = width;
            canvas.height = height;
        }

        // transform context before drawing image
        switch (srcOrientation) {
              
              //case 1: normal, no transform needed
              
              case 2:  
                  ctx.transform(-1, 0, 0, 1, width, 0); 
                  break;
              case 3:
                  ctx.transform(-1, 0, 0, -1, width, height); 
                  break;
              case 4: 
                  ctx.transform(1, 0, 0, -1, 0, height); 
                  break;
              case 5: 
                  ctx.transform(0, 1, 1, 0, 0, 0); 
                  break;
              case 6: 
                  ctx.transform(0, 1, -1, 0, height, 0); 
                  break;
              case 7: 
                  ctx.transform(0, -1, -1, 0, height, width); 
                  break;
              case 8: 
                  ctx.transform(0, -1, 1, 0, 0, width); 
                  break;
              default: 
                  break;
        }

        // draw image
        ctx.drawImage(img, 0, 0);

        //export base64
        callback(canvas.toDataURL(type), srcOrientation);
    };

    img.src = srcBase64;
};


//Read an image file, providing the returned data to callback. If the image is jpeg
//and is transformed according to EXIF info, transform it first.
//The callback function receives the image data and the orientation value (index into JpegOrientation)
export function readImageFile(file, callback) {

    getOrientation(file, (orientation) => {

        console.log("Read file \"" + file.name + "\" with orientation: " + JpegOrientation[orientation]);

        const reader = new FileReader();
        reader.onload = () => {  //when reading complete

            const img = reader.result;
            resetOrientation(img, orientation, callback);
        };
        reader.readAsDataURL(file);  //start read
        
    });
}
Locutus
  • 258
  • 3
  • 9