Created
October 19, 2016 22:25
-
-
Save yepitschunked/9d2e73d9228f5a0b300d75babe2c3796 to your computer and use it in GitHub Desktop.
extract image orientation from exif
This file contains 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
export function extractOrientation(imgBlob) { | |
// written based on https://www.media.mit.edu/pia/Research/deepview/exif.html | |
// the exif orientation values are pretty nonsensical. | |
const orientationValueToRotateDegrees = { | |
1: 0, | |
3: 180, | |
6: 90, | |
8: 270, | |
}; | |
return new Promise((resolve) => { | |
const fileReader = new FileReader(); | |
fileReader.onload = () => resolve(fileReader); | |
fileReader.readAsArrayBuffer(imgBlob); | |
}) | |
.then((reader) => { | |
const dataView = new DataView(reader.result); | |
let cursor = 0; | |
// look for jpeg headers | |
if (dataView.getUint16(cursor) !== 0xFFD8) { | |
throw new Error('not a valid jpeg'); | |
} | |
// advance past the jpeg header | |
cursor += 2; | |
while (cursor < dataView.byteLength - 1) { // we read two bytes at a time | |
if (dataView.getUint16(cursor) === 0xFFE1) { // look for exif header | |
cursor += 2; // advance past exif header | |
// read the size of exif | |
// NB: this includes the 2 bytes of the size field itself, so we have to subtract 2 | |
const exifSize = dataView.getUint16(cursor) - 2; | |
cursor += 2; // advance past exif size field | |
// return a new dataview with the exif region | |
return new DataView(reader.result, cursor, exifSize); | |
} | |
cursor += 2; | |
} | |
throw new Error('could not find exif start tag'); | |
}) | |
.then((exifView) => { | |
let cursor = 0; // skip the Exif string header | |
if (exifView.getUint32(cursor) !== 0x45786966) { // ascii hex values of the string 'Exif' | |
throw new Error('could not find Exif header'); | |
} | |
cursor += 4; // advance past the exif header | |
// Now determine the endianness of the tags by checking the 8 byte TIFF header | |
const littleEndian = exifView.getUint16(cursor) === 0x4949; | |
cursor += 8; // advance past the TIFF header | |
// Now sloppily search for the orientation tag id | |
while (cursor < exifView.byteLength - 1) { | |
if (exifView.getUint16(cursor, littleEndian) === 0x0112) { | |
// advance past 2 byte tag id + 2 bytes of tag data type + 4 bytes of tag value count | |
cursor += 8; | |
const orientation = exifView.getUint16(cursor, littleEndian); | |
const rotate = orientationValueToRotateDegrees[orientation] || 0; | |
return rotate; | |
} | |
cursor += 2; | |
} | |
throw new Error('no orientation defined'); | |
}) | |
.catch(() => 0); // default to 0 rotation | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ran into a bug with detection of the Exif byte order (
II
orMM
).On line 44 you look for the Exif header as
Exif
:Then you advance the cursor by 4 bytes in order to look for the byte order marker.
The problem is that the Exif header is not
Exif
butExif00
. Which means you have to advance the cursor by 6, not 4. (See https://www.media.mit.edu/pia/Research/deepview/exif.html#ExifData)As written here,
littleEndian
is nevertrue
becauseexifView.getUint16(cursor)
always equals0
right after theExif
part of the Exif header.If you change line 47 from
cursor += 4
tocursor +=6
, then you're at the correct position to look for the byte-order marker.Thanks for the code, works great otherwise! Extremely helpful.