0

I was looking here for a method to orientate images client side using javascript. I found something and finally got this script. It seems the image is being passed correctly but then the result of ctx.drawImage(img, 0, 0); is undefined and I don't get why. I just need the function to return the new image as base64 so that I can handle properly for inserting in the dom. I am not sure I am returning it correctly either but there's a prior problem where as stated drawImage is returning undefined, it seems like it is trying a route undefined:1 GET http://myproject.dev/model/undefined 404 (Not Found) probably I'm missing something stupid.

the event part

$(document).ready(function() {
    $('#modelpic').on("change", function () {

        //shows image preview in DOM
        var files = !!this.files ? this.files : [];

        if (!files.length || !window.FileReader) return; // no file selected, or no FileReader support

        if (/^image/.test( files[0].type)){ // only image file
            var reader = new FileReader(); // instance of the FileReader

            reader.readAsDataURL(this.files[0]); // read the local file

            reader.onloadend = function(){ // show image in div

                var base64img = this.result;

                var exif = EXIF.readFromBinaryFile(base64ToArrayBuffer(this.result));
                var srcOrientation = exif.Orientation;

                var img = resetOrientation(base64img, srcOrientation);

                $('#imagePreview').show().html('<img id="theImage" src="' + img + '" class="img-fluid" alt="Your Image" />');

            }
        }
    })
});

The function that takes care of orientation

function resetOrientation(srcBase64, srcOrientation) {

    var img = new Image();

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

        console.log(ctx);

        // set proper canvas dimensions before transform & export
        if ([5,6,7,8].indexOf(srcOrientation) > -1) {
            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: ctx.transform(1, 0, 0, 1, 0, 0);
        }

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

        // export base64
        return img;
    };

    img.src = srcBase64;
};

The function to convert base64 to array for EXIF checking.

function base64ToArrayBuffer (base64) {
    base64 = base64.replace(/^data:([^;]+);base64,/gmi, '');
    var binaryString = window.atob(base64);
    var len = binaryString.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes.buffer;
}
Chriz74
  • 1,410
  • 3
  • 23
  • 47

1 Answers1

1
  1. you are not returning anything inside "resetOrientation"
  2. Looks like you tried to return canvas element, wich is still bad as it won't work inside src=""
  3. img.onload is async

I think this is the way to go (although not tested)

function resetOrientation(srcBase64, srcOrientation) {
    return new Promise(function(resolve, reject){

      var img = new Image();

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

        console.log(ctx);

        // set proper canvas dimensions before transform & export
        if ([5,6,7,8].indexOf(srcOrientation) > -1) {
            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: ctx.transform(1, 0, 0, 1, 0, 0);
        }

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

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

    img.src = srcBase64;

    })

};

and then

$(document).ready(function() {
    $('#modelpic').on("change", function () {

        //shows image preview in DOM
        var files = !!this.files ? this.files : [];

        if (!files.length || !window.FileReader) return; // no file selected, or no FileReader support

        if (/^image/.test( files[0].type)){ // only image file
            var reader = new FileReader(); // instance of the FileReader

            reader.readAsDataURL(this.files[0]); // read the local file

            reader.onloadend = function(){ // show image in div

                var base64img = this.result;

                var exif = EXIF.readFromBinaryFile(base64ToArrayBuffer(this.result));
                var srcOrientation = exif.Orientation;

                resetOrientation(base64img, srcOrientation).then((img)=>{
                  $('#imagePreview').show().html('<img id="theImage" src="' + img + '" class="img-fluid" alt="Your Image" />');
                })



            }
        }
    })
});
Michał Sałaciński
  • 2,256
  • 1
  • 11
  • 10
  • Hero! It works, albeit being a little slow but it works. Thanks! I see that there is a reject in the promise function. I never did any promise if not with jquery so I don't really know how that resolve reject works. Could you explain? – Chriz74 May 23 '17 at 16:41
  • Sure, there are many promise implementations, but this ES6 standard promise works like this: you make "new Promise(..)" passing 1 function as a constructor, with 2 paramateres. those parameters can be used as a functions, like "resolve(x)" or "reject(x)", where x are any variables that will be passed to "then" (or other like that) - in our case "DataURL" is passed, and there's no "reject" call. – Michał Sałaciński May 23 '17 at 16:56
  • ok so I added a few more constraints to the resulting image like resize and encoding, the problem now is to set the new image as input of the form so that the new one is uploaded to the server (save bandwidth). I was thinking I could set img to the form but it's not working. – Chriz74 May 24 '17 at 16:29
  • It depends if you want to send base64 or blob, and if you need to use an existing form or create FormData. For each of those cases, you should find enough examples at SO, like [this one](https://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata/5100158) – Michał Sałaciński May 24 '17 at 17:12
  • I need to replace the resulting image to the existing form. What I am doing is I have a form so the user selects an image, I then manipulate that (orientate, resize) to show it in a preview element inside the form. What I need however is to upload the new image and not the original (the one the user selected). – Chriz74 May 24 '17 at 17:44