This side up please

by DotNetNerd 5. March 2020 11:29

On my current project we started seeing issues with images, especially when taken using an IPhone, that were shown as being rotated. Reading up on it I found that this is due to IOS using EXIF orientation, which is not always handled the same way on eg. Windows.

I found a couple of functions, that I modified to work with our TypeScript codebase, so that it utilizes async/await and has a minimum of type information. I suspect they might be useful for others, or myself later on, so here they are.

First there is a function that takes an image as a blob and returnes the orientation.

const getOrientataion = async (file: Blob) : Promise<number> => {

    return await new Promise<any>(resolve => {
         const reader = new FileReader();
        reader.onload = (event: any) => {
            if (event.target === null || event.target.result === null) return;
            const view = new DataView(event.target.result as ArrayBuffer);

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

            let length = view.byteLength,
                offset = 2;

            while (offset < length) {
                const marker = view.getUint16(offset, false);
                offset += 2;

                if (marker == 0xFFE1) {
                    if (view.getUint32(offset += 2, false) != 0x45786966) {
                         return resolve(-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 resolve(view.getUint16(offset + (i * 12) + 8, little));
                }
                else if ((marker & 0xFF00) != 0xFF00) break;
                else offset += view.getUint16(offset, false);
            }
            return resolve(-1);
         };
        reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
    });
}

Next is a small helper for converting the blob into a base64 encoded url.

const blobToBase64Url = async (file: Blob) : Promise<string> => {
    const reader = new FileReader();
     return await new Promise<any>(resolve => {
        reader.onload = (e: any) => resolve(e.target.result);
        reader.readAsDataURL(file);
    });
}

Lastly a function that takes a base64 encoded image url and an orientation, and returnes a new base64 encoded image that is the right way up.

const resetOrientation = async (srcBase64: string, srcOrientation: number) : Promise<string> => {   
    return await new Promise<any>(resolve => {
         const img = new Image();
        img.onload = () => {
             let width = img.width,
                height = img.height,
                canvas = document.createElement('canvas'),
                 ctx = canvas.getContext("2d");

            if (ctx == null) return;

            if (4 < srcOrientation && srcOrientation < 9) {
                canvas.width = height;
                 canvas.height = width;
            } else {
                canvas.width = width;
                canvas.height = height;
             }
           
            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;
            }
            ctx.drawImage(img, 0, 0);
             resolve(canvas.toDataURL());
        };
        img.src = srcBase64;
    });
}

And that is it.

Who am I?

My name is Christian Holm Diget, and I work as an independent consultant, in Denmark, where I write code, give advice on architecture and help with training. On the side I get to do a bit of speaking and help with miscellaneous community events.

Some of my primary focus areas are code quality, programming languages and using new technologies to provide value.

Microsoft Certified Professional Developer

Microsoft Most Valuable Professional

Month List

halk tv