スマホでカメラ撮影した画像をcanvasに描画すると、横表示されてしまう問題



基本的には、EXIF情報のorientationを参考にビューワーが画像を自動回転してくれるのだが、 **canvasにもってくるとEXIF情報が失われてしまう**。結果、すべての画像が横表示されてしまうバグが発生。

そこでcanvasを作成する前にorientationを取得し、そのデータを元にcanvasを強制的に回転。回転させたcanvasから取得したbase64を使用すると、横向きにならない。


```javascript:index.js
import Exif from './lib/Exif';

const exif = new Exif();

// file api
let file    = document.querySelector('input[type=file]').files[0];
let reader  = new FileReader();

reader.onloadend = function () {
	let inputbase64data = reader.result; // 入力したいbase64データ
	exif.clearOrientation(inputbase64data, function(outputbase64data) {
		// outputbase64data: 横向きが直った画像base64データ
		// これを使って改めてcanvasを描画
	})
};
// fileを読み込んだらonloadendが走る
if (file) { reader.readAsDataURL(file); }
```

```javascript:Exif.js
export default class Exif {

	constructor (opts = {}) {}

    clearOrientation(imgDataURL, callBack) {
        var byteString = atob(imgDataURL.split(',')[1]);
        var orientaion = byteStringToOrientation(byteString);

        var img = new Image();
        img.onload = function() {

            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');
            switch (orientaion) {
                case 3: //画像が１８０度回転している時
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.rotate(Math.PI);
                    ctx.drawImage(img, -img.width, -img.height);
                    ctx.rotate(-Math.PI);
                    break;
                case 6: //画像が時計回りに９０度回っている時
                    canvas.width = img.height;
                    canvas.height = img.width;
                    ctx.rotate(Math.PI * 0.5);
                    ctx.drawImage(img, 0, -img.height);
                    ctx.rotate(-Math.PI * 0.5);
                    break;
                case 8: //画像が反時計回りに９０度回っている時
                    canvas.width = img.height;
                    canvas.height = img.width;
                    ctx.rotate(-Math.PI * 0.5);
                    ctx.drawImage(img, -img.width, 0);
                    ctx.rotate(Math.PI * 0.5);
                    break;
                default:
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
            }
            callBack(canvas.toDataURL("image/jpeg"));
        }
        img.src = imgDataURL;

		// exif情報のorientationを取得
        function byteStringToOrientation(img) {
            var head = 0;
            var orientation;
            while (1) {
                if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 218) {
                    break;
                }
                if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 216) {
                    head += 2;
                } else {
                    var length = img.charCodeAt(head + 2) * 256 + img.charCodeAt(head + 3);
                    var endPoint = head + length + 2;
                    if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 225) {
                        var segment = img.slice(head, endPoint);
                        var bigEndian = segment.charCodeAt(10) == 77;
                        if (bigEndian) {
                            var count = segment.charCodeAt(18) * 256 + segment.charCodeAt(19);
                        } else {
                            var count = segment.charCodeAt(18) + segment.charCodeAt(19) * 256;
                        }
                        for (var i = 0; i < count; i++) {
                            var field = segment.slice(20 + 12 * i, 32 + 12 * i);
                            if ((bigEndian && field.charCodeAt(1) == 18) || (!bigEndian && field.charCodeAt(0) == 18)) {
                                orientation = bigEndian ? field.charCodeAt(9) : field.charCodeAt(8);
                            }
                        }
                        break;
                    }
                    head = endPoint;
                }
                if (head > img.length) {
                    break;
                }
            }
            return orientation;
        }
    }

}
```

## base64が空で返ってくるバグ

※大きい画像を回転させるのには負担がかかる。iPhone5で画像が描画されない（base64が空で返ってきてしまう）バグがあったので、リサイズ後に回転させた。正しい処理の仕方は以下のようになる。


> 
fileAPI(base64取得)
↓
exif(orientation取得)
↓
resize / trimming
↓
rotate

<br>

> 参考
[JPEGからJavascriptでEXIFのOrientation情報のみを取得する](https://egashira.jp/a10)
[[JavaScript] 画像リサイズ＆回転](http://qiita.com/yasumodev/items/ec684e81ee2eac4bdddd)
[ExifのOrientationを見て画像を回転させる](http://hackmylife.net/archives/7400448.html)
↓既存のライブラリ
[ExifのOrientationを見たうえでcanvasに画像を表示できるJSライブラリ](http://am-yu.net/2014/04/11/load-image-js/)
[JavaScript-Load-Image](https://github.com/blueimp/JavaScript-Load-Image)