Skip to content

Instantly share code, notes, and snippets.

@paddyobrien
Last active November 6, 2015 00:54
Show Gist options
  • Save paddyobrien/8833a0471a636a7f44e9 to your computer and use it in GitHub Desktop.
Save paddyobrien/8833a0471a636a7f44e9 to your computer and use it in GitHub Desktop.
A simplified JPEG orientation checker
/* Based off https://github.com/mattiasw/ExifReader */
(function() {
(typeof exports !== "undefined" && exports !== null ? exports : this).ExifReader = (function() {
ExifReader.prototype._MIN_DATA_BUFFER_LENGTH = 2;
ExifReader.prototype._JPEG_ID_SIZE = 2;
ExifReader.prototype._JPEG_ID = 0xffd8;
ExifReader.prototype._APP_MARKER_SIZE = 2;
ExifReader.prototype._APP0_MARKER = 0xffe0;
ExifReader.prototype._APP1_MARKER = 0xffe1;
ExifReader.prototype._APP15_MARKER = 0xffef;
ExifReader.prototype._APP_ID_OFFSET = 4;
ExifReader.prototype._BYTES_Exif = 0x45786966;
ExifReader.prototype._TIFF_HEADER_OFFSET = 10;
ExifReader.prototype._BYTE_ORDER_BIG_ENDIAN = 0x4949;
ExifReader.prototype._BYTE_ORDER_LITTLE_ENDIAN = 0x4d4d;
ExifReader.prototype._ORIENTATION_TAG_CODE = 274;
function ExifReader() {
var _this = this;
this._tiffHeaderOffset = 0;
}
ExifReader.prototype.getOrientation = function(data) {
this._dataView = new DataView(data);
this._checkImageHeader();
this._readTags();
this._dataView = null;
return this.orientation;
};
ExifReader.prototype._checkImageHeader = function() {
if (this._dataView.byteLength < this._MIN_DATA_BUFFER_LENGTH || this._dataView.getUint16(0, false) !== this._JPEG_ID) {
throw new Error('Invalid image format');
}
this._parseAppMarkers(this._dataView);
if (!this._hasExifData()) {
throw new Error('No Exif data');
}
};
ExifReader.prototype._parseAppMarkers = function(dataView) {
var appMarkerPosition, fieldLength, _results;
appMarkerPosition = this._JPEG_ID_SIZE;
_results = [];
while (true) {
if (dataView.byteLength < appMarkerPosition + this._APP_ID_OFFSET + 5) {
break;
}
if (this._isApp1ExifMarker(dataView, appMarkerPosition)) {
fieldLength = dataView.getUint16(appMarkerPosition + this._APP_MARKER_SIZE, false);
this._tiffHeaderOffset = appMarkerPosition + this._TIFF_HEADER_OFFSET;
} else if (this._isAppMarker(dataView, appMarkerPosition)) {
fieldLength = dataView.getUint16(appMarkerPosition + this._APP_MARKER_SIZE, false);
} else {
break;
}
_results.push(appMarkerPosition += this._APP_MARKER_SIZE + fieldLength);
}
return _results;
};
ExifReader.prototype._isApp1ExifMarker = function(dataView, appMarkerPosition) {
return dataView.getUint16(appMarkerPosition, false) === this._APP1_MARKER && dataView.getUint32(appMarkerPosition + this._APP_ID_OFFSET, false) === this._BYTES_Exif && dataView.getUint8(appMarkerPosition + this._APP_ID_OFFSET + 4, false) === 0x00;
};
ExifReader.prototype._isAppMarker = function(dataView, appMarkerPosition) {
var appMarker;
appMarker = dataView.getUint16(appMarkerPosition, false);
return appMarker >= this._APP0_MARKER && appMarker <= this._APP15_MARKER;
};
ExifReader.prototype._hasExifData = function() {
return this._tiffHeaderOffset !== 0;
};
ExifReader.prototype._readTags = function() {
this._setByteOrder();
this._readIfd(this._getIfdOffset());
};
ExifReader.prototype._setByteOrder = function() {
if (this._dataView.getUint16(this._tiffHeaderOffset) === this._BYTE_ORDER_BIG_ENDIAN) {
this._littleEndian = true;
} else if (this._dataView.getUint16(this._tiffHeaderOffset) === this._BYTE_ORDER_LITTLE_ENDIAN) {
this._littleEndian = false;
} else {
throw new Error('Illegal byte order value. Faulty image.');
}
};
ExifReader.prototype._getIfdOffset = function() {
return this._tiffHeaderOffset + this._dataView.getUint32(this._tiffHeaderOffset + 4);
};
ExifReader.prototype._readIfd = function(offset) {
var fieldIndex, numberOfFields, tag, _i;
numberOfFields = this._dataView.getUint16(offset, this._littleEndian);
offset += 2;
for (fieldIndex = _i = 0; 0 <= numberOfFields ? _i < numberOfFields : _i > numberOfFields; fieldIndex = 0 <= numberOfFields ? ++_i : --_i) {
tag = this._readTag(offset);
if (tag) {
this.orientation = tag;
break;
}
offset += 12;
}
return tag;
};
ExifReader.prototype._readTag = function(offset) {
var tagCode, value;
tagCode = this._dataView.getUint16(offset, this._littleEndian);
if (tagCode != this._ORIENTATION_TAG_CODE) {
return;
}
value = this._dataView.getUint16(offset + 8, this._littleEndian);
return this._orientationName(value);
};
ExifReader.prototype._orientationName = function(value) {
switch (value) {
case 1:
return 'top-left';
case 2:
return 'top-right';
case 3:
return 'bottom-right';
case 4:
return 'bottom-left';
case 5:
return 'left-top';
case 6:
return 'right-top';
case 7:
return 'right-bottom';
case 8:
return 'left-bottom';
default:
return 'Undefined';
}
};
return ExifReader;
})();
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment