Last active
June 30, 2020 08:50
-
-
Save laziel/1141609653e02bf1519db44136b9043a to your computer and use it in GitHub Desktop.
Get image orientation from EXIF header
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
JPEG orientation transform | |
See https://magnushoff.com/jpeg-orientation.html | |
*/ | |
img[data-orientation="2"] { | |
transform: rotateY(180deg); | |
} | |
img[data-orientation="3"] { | |
transform: rotate(180deg); | |
} | |
img[data-orientation="4"] { | |
transform: rotate(180deg) rotateY(180deg); | |
} | |
img[data-orientation="5"] { | |
transform: rotate(270deg) rotateY(180deg); | |
transform-origin: top left; | |
} | |
img[data-orientation="6"] { | |
transform: translateY(-100%) rotate(90deg); | |
transform-origin: bottom left; | |
} | |
img[data-orientation="7"] { | |
transform: translateY(-100%) translateX(-100%) rotate(90deg) rotateY(180deg); | |
transform-origin: bottom right; | |
} | |
img[data-orientation="8"] { | |
transform: translateX(-100%) rotate(270deg); | |
transform-origin: top right; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Send request a URL, and get a response as ArrayBuffer. | |
* | |
* Note: | |
* - Only the URL which is http(s):// protocol allowed to use fetch API. | |
* - XHR.onload doesn't works. Use 'readystatechange' event instead. | |
* | |
* @param {String} url | |
* @return {Promise} | |
*/ | |
const fetchAsArrayBuffer = url => { | |
return new Promise(resolve => { | |
const xhr = new XMLHttpRequest(); | |
xhr.open(`get`, url, true); | |
xhr.responseType = `arraybuffer`; | |
xhr.timeout = 2000; | |
xhr.ontimeout = () => { throw new Error(`Request timed out`); }; | |
xhr.onabort = () => { throw new Error(`Request has aborted`); }; | |
xhr.onerror = () => { throw new Error(`Cannot get the file`); }; | |
xhr.onreadystatechange = () => { | |
if (xhr.readyState === 4) { | |
resolve(xhr.response); | |
} | |
}; | |
xhr.send(); | |
}); | |
}; | |
/** | |
* Get image orientation from EXIF header | |
* | |
* Referenced: | |
* - https://gist.github.com/nezed/d536ccdace84c6f2ef13da47a8fd6bdb | |
* - https://gist.github.com/wonnage/9d2e73d9228f5a0b300d75babe2c3796 | |
* - https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-meta.js | |
* | |
* @param {String} imageUrl | |
* @return {Promise} | |
*/ | |
const getImageOrientation = async imageUrl => { | |
try { | |
const arrayBuffer = await fetchAsArrayBuffer(imageUrl); | |
const dataView = new DataView(arrayBuffer.slice(0, 64 * 1024)); | |
// Check for the JPEG marker | |
if (dataView.getUint16(0, false) !== 0xFFD8) { | |
throw new Error(`File is not a .jpeg`); | |
} | |
let offset = 2; // advance past the jpeg header | |
while (offset < dataView.byteLength) { | |
const marker = dataView.getUint16(offset, false); | |
offset += 2; | |
if (marker == 0xFFE1) { // APP1 marker | |
// ascii hex values of the string 'Exif' | |
if (dataView.getUint32(offset += 2, false) !== 0x45786966) { | |
throw new Error(`No EXIF start tag found`); | |
} | |
// Now determine the endianness of the tags | |
// by checking the 8 byte TIFF header | |
const littleEndian = | |
dataView.getUint16(offset += 6, false) === 0x4949; | |
offset += dataView.getUint32(offset + 4, littleEndian); | |
const tags = dataView.getUint16(offset, littleEndian); | |
offset += 2; | |
for (let i = 0; i < tags; i++) { | |
const pos = offset + i * 12; | |
const tag = dataView.getUint16(pos, littleEndian); | |
if (tag === 0x0112) { // orientaion tag found | |
return dataView.getUint16(pos + 8, littleEndian); | |
} | |
} | |
} else if ((marker & 0xFF00) !== 0xFF00) { | |
break; | |
} else { | |
offset += dataView.getUint16(offset, false); | |
} | |
} | |
throw new Error(`No orientation defined`); | |
} catch (e) { | |
return 0; | |
} | |
}; | |
window.addEventListener(`load`, () => { | |
document.querySelectorAll(`img`).forEach(async el => { | |
const orientation = await getImageOrientation(el.src); | |
el.dataset.orientation = orientation; | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment