/* piexifjs The MIT License (MIT) Copyright (c) 2014, 2015 hMatoba(https://github.com/hMatoba) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ (function () { "use strict"; var that = {}; that.version = "1.03"; that.remove = function (jpeg) { var b64 = false; if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg.slice(0, 22) == "data:image/jpg;base64,") { jpeg = atob(jpeg.split(",")[1]); b64 = true; } else { throw ("Given data is not jpeg."); } var segments = splitIntoSegments(jpeg); if (segments[1].slice(0, 2) == "\xff\xe1" && segments[1].slice(4, 10) == "Exif\x00\x00") { segments = [segments[0]].concat(segments.slice(2)); } else if (segments[2].slice(0, 2) == "\xff\xe1" && segments[2].slice(4, 10) == "Exif\x00\x00") { segments = segments.slice(0, 2).concat(segments.slice(3)); } else { throw("Exif not found."); } var new_data = segments.join(""); if (b64) { new_data = "data:image/jpeg;base64," + btoa(new_data); } return new_data; }; that.insert = function (exif, jpeg) { var b64 = false; if (exif.slice(0, 6) != "\x45\x78\x69\x66\x00\x00") { throw ("Given data is not exif."); } if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg.slice(0, 22) == "data:image/jpg;base64,") { jpeg = atob(jpeg.split(",")[1]); b64 = true; } else { throw ("Given data is not jpeg."); } var exifStr = "\xff\xe1" + pack(">H", [exif.length + 2]) + exif; var segments = splitIntoSegments(jpeg); var new_data = mergeSegments(segments, exifStr); if (b64) { new_data = "data:image/jpeg;base64," + btoa(new_data); } return new_data; }; that.load = function (data) { var input_data; if (typeof (data) == "string") { if (data.slice(0, 2) == "\xff\xd8") { input_data = data; } else if (data.slice(0, 23) == "data:image/jpeg;base64," || data.slice(0, 22) == "data:image/jpg;base64,") { input_data = atob(data.split(",")[1]); } else if (data.slice(0, 4) == "Exif") { input_data = data.slice(6); } else { throw ("'load' gots invalid file data."); } } else { throw ("'load' gots invalid type argument."); } var exifDict = {}; var exif_dict = { "0th": {}, "Exif": {}, "GPS": {}, "Interop": {}, "1st": {}, "thumbnail": null }; var exifReader = new ExifReader(input_data); if (exifReader.tiftag === null) { return exif_dict; } if (exifReader.tiftag.slice(0, 2) == "\x49\x49") { exifReader.endian_mark = "<"; } else { exifReader.endian_mark = ">"; } var pointer = unpack(exifReader.endian_mark + "L", exifReader.tiftag.slice(4, 8))[0]; exif_dict["0th"] = exifReader.get_ifd(pointer, "0th"); var first_ifd_pointer = exif_dict["0th"]["first_ifd_pointer"]; delete exif_dict["0th"]["first_ifd_pointer"]; if (34665 in exif_dict["0th"]) { pointer = exif_dict["0th"][34665]; exif_dict["Exif"] = exifReader.get_ifd(pointer, "Exif"); } if (34853 in exif_dict["0th"]) { pointer = exif_dict["0th"][34853]; exif_dict["GPS"] = exifReader.get_ifd(pointer, "GPS"); } if (40965 in exif_dict["Exif"]) { pointer = exif_dict["Exif"][40965]; exif_dict["Interop"] = exifReader.get_ifd(pointer, "Interop"); } if (first_ifd_pointer != "\x00\x00\x00\x00") { pointer = unpack(exifReader.endian_mark + "L", first_ifd_pointer)[0]; exif_dict["1st"] = exifReader.get_ifd(pointer, "1st"); if ((513 in exif_dict["1st"]) && (514 in exif_dict["1st"])) { var end = exif_dict["1st"][513] + exif_dict["1st"][514]; var thumb = exifReader.tiftag.slice(exif_dict["1st"][513], end); exif_dict["thumbnail"] = thumb; } } return exif_dict; }; that.dump = function (exif_dict_original) { var TIFF_HEADER_LENGTH = 8; var exif_dict = copy(exif_dict_original); var header = "Exif\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08"; var exif_is = false; var gps_is = false; var interop_is = false; var first_is = false; var zeroth_ifd, exif_ifd, interop_ifd, gps_ifd, first_ifd; if ("0th" in exif_dict) { zeroth_ifd = exif_dict["0th"]; } else { zeroth_ifd = {}; } if ((("Exif" in exif_dict) && (Object.keys(exif_dict["Exif"]).length)) || (("Interop" in exif_dict) && (Object.keys(exif_dict["Interop"]).length))) { zeroth_ifd[34665] = 1; exif_is = true; exif_ifd = exif_dict["Exif"]; if (("Interop" in exif_dict) && Object.keys(exif_dict["Interop"]).length) { exif_ifd[40965] = 1; interop_is = true; interop_ifd = exif_dict["Interop"]; } else if (Object.keys(exif_ifd).indexOf(that.ExifIFD.InteroperabilityTag.toString()) > -1) { delete exif_ifd[40965]; } } else if (Object.keys(zeroth_ifd).indexOf(that.ImageIFD.ExifTag.toString()) > -1) { delete zeroth_ifd[34665]; } if (("GPS" in exif_dict) && (Object.keys(exif_dict["GPS"]).length)) { zeroth_ifd[that.ImageIFD.GPSTag] = 1; gps_is = true; gps_ifd = exif_dict["GPS"]; } else if (Object.keys(zeroth_ifd).indexOf(that.ImageIFD.GPSTag.toString()) > -1) { delete zeroth_ifd[that.ImageIFD.GPSTag]; } if (("1st" in exif_dict) && ("thumbnail" in exif_dict) && (exif_dict["thumbnail"] != null)) { first_is = true; exif_dict["1st"][513] = 1; exif_dict["1st"][514] = 1; first_ifd = exif_dict["1st"]; } var zeroth_set = _dict_to_bytes(zeroth_ifd, "0th", 0); var zeroth_length = (zeroth_set[0].length + exif_is * 12 + gps_is * 12 + 4 + zeroth_set[1].length); var exif_set, exif_bytes = "", exif_length = 0, gps_set, gps_bytes = "", gps_length = 0, interop_set, interop_bytes = "", interop_length = 0, first_set, first_bytes = "", thumbnail; if (exif_is) { exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length); exif_length = exif_set[0].length + interop_is * 12 + exif_set[1].length; } if (gps_is) { gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length); gps_bytes = gps_set.join(""); gps_length = gps_bytes.length; } if (interop_is) { var offset = zeroth_length + exif_length + gps_length; interop_set = _dict_to_bytes(interop_ifd, "Interop", offset); interop_bytes = interop_set.join(""); interop_length = interop_bytes.length; } if (first_is) { var offset = zeroth_length + exif_length + gps_length + interop_length; first_set = _dict_to_bytes(first_ifd, "1st", offset); thumbnail = _get_thumbnail(exif_dict["thumbnail"]); if (thumbnail.length > 64000) { throw ("Given thumbnail is too large. max 64kB"); } } var exif_pointer = "", gps_pointer = "", interop_pointer = "", first_ifd_pointer = "\x00\x00\x00\x00"; if (exif_is) { var pointer_value = TIFF_HEADER_LENGTH + zeroth_length; var pointer_str = pack(">L", [pointer_value]); var key = 34665; var key_str = pack(">H", [key]); var type_str = pack(">H", [TYPES["Long"]]); var length_str = pack(">L", [1]); exif_pointer = key_str + type_str + length_str + pointer_str; } if (gps_is) { var pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length; var pointer_str = pack(">L", [pointer_value]); var key = 34853; var key_str = pack(">H", [key]); var type_str = pack(">H", [TYPES["Long"]]); var length_str = pack(">L", [1]); gps_pointer = key_str + type_str + length_str + pointer_str; } if (interop_is) { var pointer_value = (TIFF_HEADER_LENGTH + zeroth_length + exif_length + gps_length); var pointer_str = pack(">L", [pointer_value]); var key = 40965; var key_str = pack(">H", [key]); var type_str = pack(">H", [TYPES["Long"]]); var length_str = pack(">L", [1]); interop_pointer = key_str + type_str + length_str + pointer_str; } if (first_is) { var pointer_value = (TIFF_HEADER_LENGTH + zeroth_length + exif_length + gps_length + interop_length); first_ifd_pointer = pack(">L", [pointer_value]); var thumbnail_pointer = (pointer_value + first_set[0].length + 24 + 4 + first_set[1].length); var thumbnail_p_bytes = ("\x02\x01\x00\x04\x00\x00\x00\x01" + pack(">L", [thumbnail_pointer])); var thumbnail_length_bytes = ("\x02\x02\x00\x04\x00\x00\x00\x01" + pack(">L", [thumbnail.length])); first_bytes = (first_set[0] + thumbnail_p_bytes + thumbnail_length_bytes + "\x00\x00\x00\x00" + first_set[1] + thumbnail); } var zeroth_bytes = (zeroth_set[0] + exif_pointer + gps_pointer + first_ifd_pointer + zeroth_set[1]); if (exif_is) { exif_bytes = exif_set[0] + interop_pointer + exif_set[1]; } return (header + zeroth_bytes + exif_bytes + gps_bytes + interop_bytes + first_bytes); }; function copy(obj) { return JSON.parse(JSON.stringify(obj)); } function _get_thumbnail(jpeg) { var segments = splitIntoSegments(jpeg); while (("\xff\xe0" <= segments[1].slice(0, 2)) && (segments[1].slice(0, 2) <= "\xff\xef")) { segments = [segments[0]].concat(segments.slice(2)); } return segments.join(""); } function _pack_byte(array) { return pack(">" + nStr("B", array.length), array); } function _pack_short(array) { return pack(">" + nStr("H", array.length), array); } function _pack_long(array) { return pack(">" + nStr("L", array.length), array); } function _value_to_bytes(raw_value, value_type, offset) { var four_bytes_over = ""; var value_str = ""; var length, new_value, num, den; if (value_type == "Byte") { length = raw_value.length; if (length <= 4) { value_str = (_pack_byte(raw_value) + nStr("\x00", 4 - length)); } else { value_str = pack(">L", [offset]); four_bytes_over = _pack_byte(raw_value); } } else if (value_type == "Short") { length = raw_value.length; if (length <= 2) { value_str = (_pack_short(raw_value) + nStr("\x00\x00", 2 - length)); } else { value_str = pack(">L", [offset]); four_bytes_over = _pack_short(raw_value); } } else if (value_type == "Long") { length = raw_value.length; if (length <= 1) { value_str = _pack_long(raw_value); } else { value_str = pack(">L", [offset]); four_bytes_over = _pack_long(raw_value); } } else if (value_type == "Ascii") { new_value = raw_value + "\x00"; length = new_value.length; if (length > 4) { value_str = pack(">L", [offset]); four_bytes_over = new_value; } else { value_str = new_value + nStr("\x00", 4 - length); } } else if (value_type == "Rational") { if (typeof (raw_value[0]) == "number") { length = 1; num = raw_value[0]; den = raw_value[1]; new_value = pack(">L", [num]) + pack(">L", [den]); } else { length = raw_value.length; new_value = ""; for (var n = 0; n < length; n++) { num = raw_value[n][0]; den = raw_value[n][1]; new_value += (pack(">L", [num]) + pack(">L", [den])); } } value_str = pack(">L", [offset]); four_bytes_over = new_value; } else if (value_type == "SRational") { if (typeof (raw_value[0]) == "number") { length = 1; num = raw_value[0]; den = raw_value[1]; new_value = pack(">l", [num]) + pack(">l", [den]); } else { length = raw_value.length; new_value = ""; for (var n = 0; n < length; n++) { num = raw_value[n][0]; den = raw_value[n][1]; new_value += (pack(">l", [num]) + pack(">l", [den])); } } value_str = pack(">L", [offset]); four_bytes_over = new_value; } else if (value_type == "Undefined") { length = raw_value.length; if (length > 4) { value_str = pack(">L", [offset]); four_bytes_over = raw_value; } else { value_str = raw_value + nStr("\x00", 4 - length); } } var length_str = pack(">L", [length]); return [length_str, value_str, four_bytes_over]; } function _dict_to_bytes(ifd_dict, ifd, ifd_offset) { var TIFF_HEADER_LENGTH = 8; var tag_count = Object.keys(ifd_dict).length; var entry_header = pack(">H", [tag_count]); var entries_length; if (["0th", "1st"].indexOf(ifd) > -1) { entries_length = 2 + tag_count * 12 + 4; } else { entries_length = 2 + tag_count * 12; } var entries = ""; var values = ""; var key; for (var key in ifd_dict) { if (typeof (key) == "string") { key = parseInt(key); } if ((ifd == "0th") && ([34665, 34853].indexOf(key) > -1)) { continue; } else if ((ifd == "Exif") && (key == 40965)) { continue; } else if ((ifd == "1st") && ([513, 514].indexOf(key) > -1)) { continue; } var raw_value = ifd_dict[key]; var key_str = pack(">H", [key]); var value_type = TAGS[ifd][key]["type"]; var type_str = pack(">H", [TYPES[value_type]]); if (typeof (raw_value) == "number") { raw_value = [raw_value]; } var offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + values.length; var b = _value_to_bytes(raw_value, value_type, offset); var length_str = b[0]; var value_str = b[1]; var four_bytes_over = b[2]; entries += key_str + type_str + length_str + value_str; values += four_bytes_over; } return [entry_header + entries, values]; } function ExifReader(data) { var segments, app1; if (data.slice(0, 2) == "\xff\xd8") { // JPEG segments = splitIntoSegments(data); app1 = getExifSeg(segments); if (app1) { this.tiftag = app1.slice(10); } else { this.tiftag = null; } } else if (["\x49\x49", "\x4d\x4d"].indexOf(data.slice(0, 2)) > -1) { // TIFF this.tiftag = data; } else if (data.slice(0, 4) == "Exif") { // Exif this.tiftag = data.slice(6); } else { throw ("Given file is neither JPEG nor TIFF."); } } ExifReader.prototype = { get_ifd: function (pointer, ifd_name) { var ifd_dict = {}; var tag_count = unpack(this.endian_mark + "H", this.tiftag.slice(pointer, pointer + 2))[0]; var offset = pointer + 2; var t; if (["0th", "1st"].indexOf(ifd_name) > -1) { t = "Image"; } else { t = ifd_name; } for (var x = 0; x < tag_count; x++) { pointer = offset + 12 * x; var tag = unpack(this.endian_mark + "H", this.tiftag.slice(pointer, pointer + 2))[0]; var value_type = unpack(this.endian_mark + "H", this.tiftag.slice(pointer + 2, pointer + 4))[0]; var value_num = unpack(this.endian_mark + "L", this.tiftag.slice(pointer + 4, pointer + 8))[0]; var value = this.tiftag.slice(pointer + 8, pointer + 12); var v_set = [value_type, value_num, value]; if (tag in TAGS[t]) { ifd_dict[tag] = this.convert_value(v_set); } } if (ifd_name == "0th") { pointer = offset + 12 * tag_count; ifd_dict["first_ifd_pointer"] = this.tiftag.slice(pointer, pointer + 4); } return ifd_dict; }, convert_value: function (val) { var data = null; var t = val[0]; var length = val[1]; var value = val[2]; var pointer; if (t == 1) { // BYTE if (length > 4) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("B", length), this.tiftag.slice(pointer, pointer + length)); } else { data = unpack(this.endian_mark + nStr("B", length), value.slice(0, length)); } } else if (t == 2) { // ASCII if (length > 4) { pointer = unpack(this.endian_mark + "L", value)[0]; data = this.tiftag.slice(pointer, pointer + length - 1); } else { data = value.slice(0, length - 1); } } else if (t == 3) { // SHORT if (length > 2) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("H", length), this.tiftag.slice(pointer, pointer + length * 2)); } else { data = unpack(this.endian_mark + nStr("H", length), value.slice(0, length * 2)); } } else if (t == 4) { // LONG if (length > 1) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("L", length), this.tiftag.slice(pointer, pointer + length * 4)); } else { data = unpack(this.endian_mark + nStr("L", length), value); } } else if (t == 5) { // RATIONAL pointer = unpack(this.endian_mark + "L", value)[0]; if (length > 1) { data = []; for (var x = 0; x < length; x++) { data.push([unpack(this.endian_mark + "L", this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], unpack(this.endian_mark + "L", this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] ]); } } else { data = [unpack(this.endian_mark + "L", this.tiftag.slice(pointer, pointer + 4))[0], unpack(this.endian_mark + "L", this.tiftag.slice(pointer + 4, pointer + 8))[0] ]; } } else if (t == 7) { // UNDEFINED BYTES if (length > 4) { pointer = unpack(this.endian_mark + "L", value)[0]; data = this.tiftag.slice(pointer, pointer + length); } else { data = value.slice(0, length); } } else if (t == 10) { // SRATIONAL pointer = unpack(this.endian_mark + "L", value)[0]; if (length > 1) { data = []; for (var x = 0; x < length; x++) { data.push([unpack(this.endian_mark + "l", this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], unpack(this.endian_mark + "l", this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] ]); } } else { data = [unpack(this.endian_mark + "l", this.tiftag.slice(pointer, pointer + 4))[0], unpack(this.endian_mark + "l", this.tiftag.slice(pointer + 4, pointer + 8))[0] ]; } } else { throw ("Exif might be wrong. Got incorrect value " + "type to decode. type:" + t); } if ((data instanceof Array) && (data.length == 1)) { return data[0]; } else { return data; } }, }; if (typeof window !== "undefined" && typeof window.btoa === "function") { var btoa = window.btoa; } if (typeof btoa === "undefined") { var btoa = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); } return output; }; } if (typeof window !== "undefined" && typeof window.atob === "function") { var atob = window.atob; } if (typeof atob === "undefined") { var atob = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } return output; }; } function getImageSize(imageArray) { var segments = slice2Segments(imageArray); var seg, width, height, SOF = [192, 193, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207]; for (var x = 0; x < segments.length; x++) { seg = segments[x]; if (SOF.indexOf(seg[1]) >= 0) { height = seg[5] * 256 + seg[6]; width = seg[7] * 256 + seg[8]; break; } } return [width, height]; } function pack(mark, array) { if (!(array instanceof Array)) { throw ("'pack' error. Got invalid type argument."); } if ((mark.length - 1) != array.length) { throw ("'pack' error. " + (mark.length - 1) + " marks, " + array.length + " elements."); } var littleEndian; if (mark[0] == "<") { littleEndian = true; } else if (mark[0] == ">") { littleEndian = false; } else { throw (""); } var packed = ""; var p = 1; var val = null; var c = null; var valStr = null; while (c = mark[p]) { if (c.toLowerCase() == "b") { val = array[p - 1]; if ((c == "b") && (val < 0)) { val += 0x100; } if ((val > 0xff) || (val < 0)) { throw ("'pack' error."); } else { valStr = String.fromCharCode(val); } } else if (c == "H") { val = array[p - 1]; if ((val > 0xffff) || (val < 0)) { throw ("'pack' error."); } else { valStr = String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + String.fromCharCode(val % 0x100); if (littleEndian) { valStr = valStr.split("").reverse().join(""); } } } else if (c.toLowerCase() == "l") { val = array[p - 1]; if ((c == "l") && (val < 0)) { val += 0x100000000; } if ((val > 0xffffffff) || (val < 0)) { throw ("'pack' error."); } else { valStr = String.fromCharCode(Math.floor(val / 0x1000000)) + String.fromCharCode(Math.floor((val % 0x1000000) / 0x10000)) + String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + String.fromCharCode(val % 0x100); if (littleEndian) { valStr = valStr.split("").reverse().join(""); } } } else { throw ("'pack' error."); } packed += valStr; p += 1; } return packed; } function unpack(mark, str) { if (typeof (str) != "string") { throw ("'unpack' error. Got invalid type argument."); } var l = 0; for (var markPointer = 1; markPointer < mark.length; markPointer++) { if (mark[markPointer].toLowerCase() == "b") { l += 1; } else if (mark[markPointer].toLowerCase() == "h") { l += 2; } else if (mark[markPointer].toLowerCase() == "l") { l += 4; } else { throw ("'unpack' error. Got invalid mark."); } } if (l != str.length) { throw ("'unpack' error. Mismatch between symbol and string length. " + l + ":" + str.length); } var littleEndian; if (mark[0] == "<") { littleEndian = true; } else if (mark[0] == ">") { littleEndian = false; } else { throw ("'unpack' error."); } var unpacked = []; var strPointer = 0; var p = 1; var val = null; var c = null; var length = null; var sliced = ""; while (c = mark[p]) { if (c.toLowerCase() == "b") { length = 1; sliced = str.slice(strPointer, strPointer + length); val = sliced.charCodeAt(0); if ((c == "b") && (val >= 0x80)) { val -= 0x100; } } else if (c == "H") { length = 2; sliced = str.slice(strPointer, strPointer + length); if (littleEndian) { sliced = sliced.split("").reverse().join(""); } val = sliced.charCodeAt(0) * 0x100 + sliced.charCodeAt(1); } else if (c.toLowerCase() == "l") { length = 4; sliced = str.slice(strPointer, strPointer + length); if (littleEndian) { sliced = sliced.split("").reverse().join(""); } val = sliced.charCodeAt(0) * 0x1000000 + sliced.charCodeAt(1) * 0x10000 + sliced.charCodeAt(2) * 0x100 + sliced.charCodeAt(3); if ((c == "l") && (val >= 0x80000000)) { val -= 0x100000000; } } else { throw ("'unpack' error. " + c); } unpacked.push(val); strPointer += length; p += 1; } return unpacked; } function nStr(ch, num) { var str = ""; for (var i = 0; i < num; i++) { str += ch; } return str; } function splitIntoSegments(data) { if (data.slice(0, 2) != "\xff\xd8") { throw ("Given data isn't JPEG."); } var head = 2; var segments = ["\xff\xd8"]; while (true) { if (data.slice(head, head + 2) == "\xff\xda") { segments.push(data.slice(head)); break; } else { var length = unpack(">H", data.slice(head + 2, head + 4))[0]; var endPoint = head + length + 2; segments.push(data.slice(head, endPoint)); head = endPoint; } if (head >= data.length) { throw ("Wrong JPEG data."); } } return segments; } function getExifSeg(segments) { var seg; for (var i = 0; i < segments.length; i++) { seg = segments[i]; if (seg.slice(0, 2) == "\xff\xe1" && seg.slice(4, 10) == "Exif\x00\x00") { return seg; } } return null; } function mergeSegments(segments, exif) { if (segments[1].slice(0, 2) == "\xff\xe0" && (segments[2].slice(0, 2) == "\xff\xe1" && segments[2].slice(4, 10) == "Exif\x00\x00")) { if (exif) { segments[2] = exif; segments = ["\xff\xd8"].concat(segments.slice(2)); } else if (exif == null) { segments = segments.slice(0, 2).concat(segments.slice(3)); } else { segments = segments.slice(0).concat(segments.slice(2)); } } else if (segments[1].slice(0, 2) == "\xff\xe0") { if (exif) { segments[1] = exif; } } else if (segments[1].slice(0, 2) == "\xff\xe1" && segments[1].slice(4, 10) == "Exif\x00\x00") { if (exif) { segments[1] = exif; } else if (exif == null) { segments = segments.slice(0).concat(segments.slice(2)); } } else { if (exif) { segments = [segments[0], exif].concat(segments.slice(1)); } } return segments.join(""); } function toHex(str) { var hexStr = ""; for (var i = 0; i < str.length; i++) { var h = str.charCodeAt(i); var hex = ((h < 10) ? "0" : "") + h.toString(16); hexStr += hex + " "; } return hexStr; } var TYPES = { "Byte": 1, "Ascii": 2, "Short": 3, "Long": 4, "Rational": 5, "Undefined": 7, "SLong": 9, "SRational": 10 }; var TAGS = { 'Image': { 11: { 'name': 'ProcessingSoftware', 'type': 'Ascii' }, 254: { 'name': 'NewSubfileType', 'type': 'Long' }, 255: { 'name': 'SubfileType', 'type': 'Short' }, 256: { 'name': 'ImageWidth', 'type': 'Long' }, 257: { 'name': 'ImageLength', 'type': 'Long' }, 258: { 'name': 'BitsPerSample', 'type': 'Short' }, 259: { 'name': 'Compression', 'type': 'Short' }, 262: { 'name': 'PhotometricInterpretation', 'type': 'Short' }, 263: { 'name': 'Threshholding', 'type': 'Short' }, 264: { 'name': 'CellWidth', 'type': 'Short' }, 265: { 'name': 'CellLength', 'type': 'Short' }, 266: { 'name': 'FillOrder', 'type': 'Short' }, 269: { 'name': 'DocumentName', 'type': 'Ascii' }, 270: { 'name': 'ImageDescription', 'type': 'Ascii' }, 271: { 'name': 'Make', 'type': 'Ascii' }, 272: { 'name': 'Model', 'type': 'Ascii' }, 273: { 'name': 'StripOffsets', 'type': 'Long' }, 274: { 'name': 'Orientation', 'type': 'Short' }, 277: { 'name': 'SamplesPerPixel', 'type': 'Short' }, 278: { 'name': 'RowsPerStrip', 'type': 'Long' }, 279: { 'name': 'StripByteCounts', 'type': 'Long' }, 282: { 'name': 'XResolution', 'type': 'Rational' }, 283: { 'name': 'YResolution', 'type': 'Rational' }, 284: { 'name': 'PlanarConfiguration', 'type': 'Short' }, 290: { 'name': 'GrayResponseUnit', 'type': 'Short' }, 291: { 'name': 'GrayResponseCurve', 'type': 'Short' }, 292: { 'name': 'T4Options', 'type': 'Long' }, 293: { 'name': 'T6Options', 'type': 'Long' }, 296: { 'name': 'ResolutionUnit', 'type': 'Short' }, 301: { 'name': 'TransferFunction', 'type': 'Short' }, 305: { 'name': 'Software', 'type': 'Ascii' }, 306: { 'name': 'DateTime', 'type': 'Ascii' }, 315: { 'name': 'Artist', 'type': 'Ascii' }, 316: { 'name': 'HostComputer', 'type': 'Ascii' }, 317: { 'name': 'Predictor', 'type': 'Short' }, 318: { 'name': 'WhitePoint', 'type': 'Rational' }, 319: { 'name': 'PrimaryChromaticities', 'type': 'Rational' }, 320: { 'name': 'ColorMap', 'type': 'Short' }, 321: { 'name': 'HalftoneHints', 'type': 'Short' }, 322: { 'name': 'TileWidth', 'type': 'Short' }, 323: { 'name': 'TileLength', 'type': 'Short' }, 324: { 'name': 'TileOffsets', 'type': 'Short' }, 325: { 'name': 'TileByteCounts', 'type': 'Short' }, 330: { 'name': 'SubIFDs', 'type': 'Long' }, 332: { 'name': 'InkSet', 'type': 'Short' }, 333: { 'name': 'InkNames', 'type': 'Ascii' }, 334: { 'name': 'NumberOfInks', 'type': 'Short' }, 336: { 'name': 'DotRange', 'type': 'Byte' }, 337: { 'name': 'TargetPrinter', 'type': 'Ascii' }, 338: { 'name': 'ExtraSamples', 'type': 'Short' }, 339: { 'name': 'SampleFormat', 'type': 'Short' }, 340: { 'name': 'SMinSampleValue', 'type': 'Short' }, 341: { 'name': 'SMaxSampleValue', 'type': 'Short' }, 342: { 'name': 'TransferRange', 'type': 'Short' }, 343: { 'name': 'ClipPath', 'type': 'Byte' }, 344: { 'name': 'XClipPathUnits', 'type': 'Long' }, 345: { 'name': 'YClipPathUnits', 'type': 'Long' }, 346: { 'name': 'Indexed', 'type': 'Short' }, 347: { 'name': 'JPEGTables', 'type': 'Undefined' }, 351: { 'name': 'OPIProxy', 'type': 'Short' }, 512: { 'name': 'JPEGProc', 'type': 'Long' }, 513: { 'name': 'JPEGInterchangeFormat', 'type': 'Long' }, 514: { 'name': 'JPEGInterchangeFormatLength', 'type': 'Long' }, 515: { 'name': 'JPEGRestartInterval', 'type': 'Short' }, 517: { 'name': 'JPEGLosslessPredictors', 'type': 'Short' }, 518: { 'name': 'JPEGPointTransforms', 'type': 'Short' }, 519: { 'name': 'JPEGQTables', 'type': 'Long' }, 520: { 'name': 'JPEGDCTables', 'type': 'Long' }, 521: { 'name': 'JPEGACTables', 'type': 'Long' }, 529: { 'name': 'YCbCrCoefficients', 'type': 'Rational' }, 530: { 'name': 'YCbCrSubSampling', 'type': 'Short' }, 531: { 'name': 'YCbCrPositioning', 'type': 'Short' }, 532: { 'name': 'ReferenceBlackWhite', 'type': 'Rational' }, 700: { 'name': 'XMLPacket', 'type': 'Byte' }, 18246: { 'name': 'Rating', 'type': 'Short' }, 18249: { 'name': 'RatingPercent', 'type': 'Short' }, 32781: { 'name': 'ImageID', 'type': 'Ascii' }, 33421: { 'name': 'CFARepeatPatternDim', 'type': 'Short' }, 33422: { 'name': 'CFAPattern', 'type': 'Byte' }, 33423: { 'name': 'BatteryLevel', 'type': 'Rational' }, 33432: { 'name': 'Copyright', 'type': 'Ascii' }, 33434: { 'name': 'ExposureTime', 'type': 'Rational' }, 34377: { 'name': 'ImageResources', 'type': 'Byte' }, 34665: { 'name': 'ExifTag', 'type': 'Long' }, 34675: { 'name': 'InterColorProfile', 'type': 'Undefined' }, 34853: { 'name': 'GPSTag', 'type': 'Long' }, 34857: { 'name': 'Interlace', 'type': 'Short' }, 34858: { 'name': 'TimeZoneOffset', 'type': 'Long' }, 34859: { 'name': 'SelfTimerMode', 'type': 'Short' }, 37387: { 'name': 'FlashEnergy', 'type': 'Rational' }, 37388: { 'name': 'SpatialFrequencyResponse', 'type': 'Undefined' }, 37389: { 'name': 'Noise', 'type': 'Undefined' }, 37390: { 'name': 'FocalPlaneXResolution', 'type': 'Rational' }, 37391: { 'name': 'FocalPlaneYResolution', 'type': 'Rational' }, 37392: { 'name': 'FocalPlaneResolutionUnit', 'type': 'Short' }, 37393: { 'name': 'ImageNumber', 'type': 'Long' }, 37394: { 'name': 'SecurityClassification', 'type': 'Ascii' }, 37395: { 'name': 'ImageHistory', 'type': 'Ascii' }, 37397: { 'name': 'ExposureIndex', 'type': 'Rational' }, 37398: { 'name': 'TIFFEPStandardID', 'type': 'Byte' }, 37399: { 'name': 'SensingMethod', 'type': 'Short' }, 40091: { 'name': 'XPTitle', 'type': 'Byte' }, 40092: { 'name': 'XPComment', 'type': 'Byte' }, 40093: { 'name': 'XPAuthor', 'type': 'Byte' }, 40094: { 'name': 'XPKeywords', 'type': 'Byte' }, 40095: { 'name': 'XPSubject', 'type': 'Byte' }, 50341: { 'name': 'PrintImageMatching', 'type': 'Undefined' }, 50706: { 'name': 'DNGVersion', 'type': 'Byte' }, 50707: { 'name': 'DNGBackwardVersion', 'type': 'Byte' }, 50708: { 'name': 'UniqueCameraModel', 'type': 'Ascii' }, 50709: { 'name': 'LocalizedCameraModel', 'type': 'Byte' }, 50710: { 'name': 'CFAPlaneColor', 'type': 'Byte' }, 50711: { 'name': 'CFALayout', 'type': 'Short' }, 50712: { 'name': 'LinearizationTable', 'type': 'Short' }, 50713: { 'name': 'BlackLevelRepeatDim', 'type': 'Short' }, 50714: { 'name': 'BlackLevel', 'type': 'Rational' }, 50715: { 'name': 'BlackLevelDeltaH', 'type': 'SRational' }, 50716: { 'name': 'BlackLevelDeltaV', 'type': 'SRational' }, 50717: { 'name': 'WhiteLevel', 'type': 'Short' }, 50718: { 'name': 'DefaultScale', 'type': 'Rational' }, 50719: { 'name': 'DefaultCropOrigin', 'type': 'Short' }, 50720: { 'name': 'DefaultCropSize', 'type': 'Short' }, 50721: { 'name': 'ColorMatrix1', 'type': 'SRational' }, 50722: { 'name': 'ColorMatrix2', 'type': 'SRational' }, 50723: { 'name': 'CameraCalibration1', 'type': 'SRational' }, 50724: { 'name': 'CameraCalibration2', 'type': 'SRational' }, 50725: { 'name': 'ReductionMatrix1', 'type': 'SRational' }, 50726: { 'name': 'ReductionMatrix2', 'type': 'SRational' }, 50727: { 'name': 'AnalogBalance', 'type': 'Rational' }, 50728: { 'name': 'AsShotNeutral', 'type': 'Short' }, 50729: { 'name': 'AsShotWhiteXY', 'type': 'Rational' }, 50730: { 'name': 'BaselineExposure', 'type': 'SRational' }, 50731: { 'name': 'BaselineNoise', 'type': 'Rational' }, 50732: { 'name': 'BaselineSharpness', 'type': 'Rational' }, 50733: { 'name': 'BayerGreenSplit', 'type': 'Long' }, 50734: { 'name': 'LinearResponseLimit', 'type': 'Rational' }, 50735: { 'name': 'CameraSerialNumber', 'type': 'Ascii' }, 50736: { 'name': 'LensInfo', 'type': 'Rational' }, 50737: { 'name': 'ChromaBlurRadius', 'type': 'Rational' }, 50738: { 'name': 'AntiAliasStrength', 'type': 'Rational' }, 50739: { 'name': 'ShadowScale', 'type': 'SRational' }, 50740: { 'name': 'DNGPrivateData', 'type': 'Byte' }, 50741: { 'name': 'MakerNoteSafety', 'type': 'Short' }, 50778: { 'name': 'CalibrationIlluminant1', 'type': 'Short' }, 50779: { 'name': 'CalibrationIlluminant2', 'type': 'Short' }, 50780: { 'name': 'BestQualityScale', 'type': 'Rational' }, 50781: { 'name': 'RawDataUniqueID', 'type': 'Byte' }, 50827: { 'name': 'OriginalRawFileName', 'type': 'Byte' }, 50828: { 'name': 'OriginalRawFileData', 'type': 'Undefined' }, 50829: { 'name': 'ActiveArea', 'type': 'Short' }, 50830: { 'name': 'MaskedAreas', 'type': 'Short' }, 50831: { 'name': 'AsShotICCProfile', 'type': 'Undefined' }, 50832: { 'name': 'AsShotPreProfileMatrix', 'type': 'SRational' }, 50833: { 'name': 'CurrentICCProfile', 'type': 'Undefined' }, 50834: { 'name': 'CurrentPreProfileMatrix', 'type': 'SRational' }, 50879: { 'name': 'ColorimetricReference', 'type': 'Short' }, 50931: { 'name': 'CameraCalibrationSignature', 'type': 'Byte' }, 50932: { 'name': 'ProfileCalibrationSignature', 'type': 'Byte' }, 50934: { 'name': 'AsShotProfileName', 'type': 'Byte' }, 50935: { 'name': 'NoiseReductionApplied', 'type': 'Rational' }, 50936: { 'name': 'ProfileName', 'type': 'Byte' }, 50937: { 'name': 'ProfileHueSatMapDims', 'type': 'Long' }, 50938: { 'name': 'ProfileHueSatMapData1', 'type': 'Float' }, 50939: { 'name': 'ProfileHueSatMapData2', 'type': 'Float' }, 50940: { 'name': 'ProfileToneCurve', 'type': 'Float' }, 50941: { 'name': 'ProfileEmbedPolicy', 'type': 'Long' }, 50942: { 'name': 'ProfileCopyright', 'type': 'Byte' }, 50964: { 'name': 'ForwardMatrix1', 'type': 'SRational' }, 50965: { 'name': 'ForwardMatrix2', 'type': 'SRational' }, 50966: { 'name': 'PreviewApplicationName', 'type': 'Byte' }, 50967: { 'name': 'PreviewApplicationVersion', 'type': 'Byte' }, 50968: { 'name': 'PreviewSettingsName', 'type': 'Byte' }, 50969: { 'name': 'PreviewSettingsDigest', 'type': 'Byte' }, 50970: { 'name': 'PreviewColorSpace', 'type': 'Long' }, 50971: { 'name': 'PreviewDateTime', 'type': 'Ascii' }, 50972: { 'name': 'RawImageDigest', 'type': 'Undefined' }, 50973: { 'name': 'OriginalRawFileDigest', 'type': 'Undefined' }, 50974: { 'name': 'SubTileBlockSize', 'type': 'Long' }, 50975: { 'name': 'RowInterleaveFactor', 'type': 'Long' }, 50981: { 'name': 'ProfileLookTableDims', 'type': 'Long' }, 50982: { 'name': 'ProfileLookTableData', 'type': 'Float' }, 51008: { 'name': 'OpcodeList1', 'type': 'Undefined' }, 51009: { 'name': 'OpcodeList2', 'type': 'Undefined' }, 51022: { 'name': 'OpcodeList3', 'type': 'Undefined' } }, 'Exif': { 33434: { 'name': 'ExposureTime', 'type': 'Rational' }, 33437: { 'name': 'FNumber', 'type': 'Rational' }, 34850: { 'name': 'ExposureProgram', 'type': 'Short' }, 34852: { 'name': 'SpectralSensitivity', 'type': 'Ascii' }, 34855: { 'name': 'ISOSpeedRatings', 'type': 'Short' }, 34856: { 'name': 'OECF', 'type': 'Undefined' }, 34864: { 'name': 'SensitivityType', 'type': 'Short' }, 34865: { 'name': 'StandardOutputSensitivity', 'type': 'Long' }, 34866: { 'name': 'RecommendedExposureIndex', 'type': 'Long' }, 34867: { 'name': 'ISOSpeed', 'type': 'Long' }, 34868: { 'name': 'ISOSpeedLatitudeyyy', 'type': 'Long' }, 34869: { 'name': 'ISOSpeedLatitudezzz', 'type': 'Long' }, 36864: { 'name': 'ExifVersion', 'type': 'Undefined' }, 36867: { 'name': 'DateTimeOriginal', 'type': 'Ascii' }, 36868: { 'name': 'DateTimeDigitized', 'type': 'Ascii' }, 37121: { 'name': 'ComponentsConfiguration', 'type': 'Undefined' }, 37122: { 'name': 'CompressedBitsPerPixel', 'type': 'Rational' }, 37377: { 'name': 'ShutterSpeedValue', 'type': 'SRational' }, 37378: { 'name': 'ApertureValue', 'type': 'Rational' }, 37379: { 'name': 'BrightnessValue', 'type': 'SRational' }, 37380: { 'name': 'ExposureBiasValue', 'type': 'SRational' }, 37381: { 'name': 'MaxApertureValue', 'type': 'Rational' }, 37382: { 'name': 'SubjectDistance', 'type': 'Rational' }, 37383: { 'name': 'MeteringMode', 'type': 'Short' }, 37384: { 'name': 'LightSource', 'type': 'Short' }, 37385: { 'name': 'Flash', 'type': 'Short' }, 37386: { 'name': 'FocalLength', 'type': 'Rational' }, 37396: { 'name': 'SubjectArea', 'type': 'Short' }, 37500: { 'name': 'MakerNote', 'type': 'Undefined' }, 37510: { 'name': 'UserComment', 'type': 'Ascii' }, 37520: { 'name': 'SubSecTime', 'type': 'Ascii' }, 37521: { 'name': 'SubSecTimeOriginal', 'type': 'Ascii' }, 37522: { 'name': 'SubSecTimeDigitized', 'type': 'Ascii' }, 40960: { 'name': 'FlashpixVersion', 'type': 'Undefined' }, 40961: { 'name': 'ColorSpace', 'type': 'Short' }, 40962: { 'name': 'PixelXDimension', 'type': 'Long' }, 40963: { 'name': 'PixelYDimension', 'type': 'Long' }, 40964: { 'name': 'RelatedSoundFile', 'type': 'Ascii' }, 40965: { 'name': 'InteroperabilityTag', 'type': 'Long' }, 41483: { 'name': 'FlashEnergy', 'type': 'Rational' }, 41484: { 'name': 'SpatialFrequencyResponse', 'type': 'Undefined' }, 41486: { 'name': 'FocalPlaneXResolution', 'type': 'Rational' }, 41487: { 'name': 'FocalPlaneYResolution', 'type': 'Rational' }, 41488: { 'name': 'FocalPlaneResolutionUnit', 'type': 'Short' }, 41492: { 'name': 'SubjectLocation', 'type': 'Short' }, 41493: { 'name': 'ExposureIndex', 'type': 'Rational' }, 41495: { 'name': 'SensingMethod', 'type': 'Short' }, 41728: { 'name': 'FileSource', 'type': 'Undefined' }, 41729: { 'name': 'SceneType', 'type': 'Undefined' }, 41730: { 'name': 'CFAPattern', 'type': 'Undefined' }, 41985: { 'name': 'CustomRendered', 'type': 'Short' }, 41986: { 'name': 'ExposureMode', 'type': 'Short' }, 41987: { 'name': 'WhiteBalance', 'type': 'Short' }, 41988: { 'name': 'DigitalZoomRatio', 'type': 'Rational' }, 41989: { 'name': 'FocalLengthIn35mmFilm', 'type': 'Short' }, 41990: { 'name': 'SceneCaptureType', 'type': 'Short' }, 41991: { 'name': 'GainControl', 'type': 'Short' }, 41992: { 'name': 'Contrast', 'type': 'Short' }, 41993: { 'name': 'Saturation', 'type': 'Short' }, 41994: { 'name': 'Sharpness', 'type': 'Short' }, 41995: { 'name': 'DeviceSettingDescription', 'type': 'Undefined' }, 41996: { 'name': 'SubjectDistanceRange', 'type': 'Short' }, 42016: { 'name': 'ImageUniqueID', 'type': 'Ascii' }, 42032: { 'name': 'CameraOwnerName', 'type': 'Ascii' }, 42033: { 'name': 'BodySerialNumber', 'type': 'Ascii' }, 42034: { 'name': 'LensSpecification', 'type': 'Rational' }, 42035: { 'name': 'LensMake', 'type': 'Ascii' }, 42036: { 'name': 'LensModel', 'type': 'Ascii' }, 42037: { 'name': 'LensSerialNumber', 'type': 'Ascii' }, 42240: { 'name': 'Gamma', 'type': 'Rational' } }, 'GPS': { 0: { 'name': 'GPSVersionID', 'type': 'Byte' }, 1: { 'name': 'GPSLatitudeRef', 'type': 'Ascii' }, 2: { 'name': 'GPSLatitude', 'type': 'Rational' }, 3: { 'name': 'GPSLongitudeRef', 'type': 'Ascii' }, 4: { 'name': 'GPSLongitude', 'type': 'Rational' }, 5: { 'name': 'GPSAltitudeRef', 'type': 'Byte' }, 6: { 'name': 'GPSAltitude', 'type': 'Rational' }, 7: { 'name': 'GPSTimeStamp', 'type': 'Rational' }, 8: { 'name': 'GPSSatellites', 'type': 'Ascii' }, 9: { 'name': 'GPSStatus', 'type': 'Ascii' }, 10: { 'name': 'GPSMeasureMode', 'type': 'Ascii' }, 11: { 'name': 'GPSDOP', 'type': 'Rational' }, 12: { 'name': 'GPSSpeedRef', 'type': 'Ascii' }, 13: { 'name': 'GPSSpeed', 'type': 'Rational' }, 14: { 'name': 'GPSTrackRef', 'type': 'Ascii' }, 15: { 'name': 'GPSTrack', 'type': 'Rational' }, 16: { 'name': 'GPSImgDirectionRef', 'type': 'Ascii' }, 17: { 'name': 'GPSImgDirection', 'type': 'Rational' }, 18: { 'name': 'GPSMapDatum', 'type': 'Ascii' }, 19: { 'name': 'GPSDestLatitudeRef', 'type': 'Ascii' }, 20: { 'name': 'GPSDestLatitude', 'type': 'Rational' }, 21: { 'name': 'GPSDestLongitudeRef', 'type': 'Ascii' }, 22: { 'name': 'GPSDestLongitude', 'type': 'Rational' }, 23: { 'name': 'GPSDestBearingRef', 'type': 'Ascii' }, 24: { 'name': 'GPSDestBearing', 'type': 'Rational' }, 25: { 'name': 'GPSDestDistanceRef', 'type': 'Ascii' }, 26: { 'name': 'GPSDestDistance', 'type': 'Rational' }, 27: { 'name': 'GPSProcessingMethod', 'type': 'Undefined' }, 28: { 'name': 'GPSAreaInformation', 'type': 'Undefined' }, 29: { 'name': 'GPSDateStamp', 'type': 'Ascii' }, 30: { 'name': 'GPSDifferential', 'type': 'Short' }, 31: { 'name': 'GPSHPositioningError', 'type': 'Rational' } }, 'Interop': { 1: { 'name': 'InteroperabilityIndex', 'type': 'Ascii' } }, }; TAGS["0th"] = TAGS["Image"]; TAGS["1st"] = TAGS["Image"]; that.TAGS = TAGS; that.ImageIFD = { ProcessingSoftware:11, NewSubfileType:254, SubfileType:255, ImageWidth:256, ImageLength:257, BitsPerSample:258, Compression:259, PhotometricInterpretation:262, Threshholding:263, CellWidth:264, CellLength:265, FillOrder:266, DocumentName:269, ImageDescription:270, Make:271, Model:272, StripOffsets:273, Orientation:274, SamplesPerPixel:277, RowsPerStrip:278, StripByteCounts:279, XResolution:282, YResolution:283, PlanarConfiguration:284, GrayResponseUnit:290, GrayResponseCurve:291, T4Options:292, T6Options:293, ResolutionUnit:296, TransferFunction:301, Software:305, DateTime:306, Artist:315, HostComputer:316, Predictor:317, WhitePoint:318, PrimaryChromaticities:319, ColorMap:320, HalftoneHints:321, TileWidth:322, TileLength:323, TileOffsets:324, TileByteCounts:325, SubIFDs:330, InkSet:332, InkNames:333, NumberOfInks:334, DotRange:336, TargetPrinter:337, ExtraSamples:338, SampleFormat:339, SMinSampleValue:340, SMaxSampleValue:341, TransferRange:342, ClipPath:343, XClipPathUnits:344, YClipPathUnits:345, Indexed:346, JPEGTables:347, OPIProxy:351, JPEGProc:512, JPEGInterchangeFormat:513, JPEGInterchangeFormatLength:514, JPEGRestartInterval:515, JPEGLosslessPredictors:517, JPEGPointTransforms:518, JPEGQTables:519, JPEGDCTables:520, JPEGACTables:521, YCbCrCoefficients:529, YCbCrSubSampling:530, YCbCrPositioning:531, ReferenceBlackWhite:532, XMLPacket:700, Rating:18246, RatingPercent:18249, ImageID:32781, CFARepeatPatternDim:33421, CFAPattern:33422, BatteryLevel:33423, Copyright:33432, ExposureTime:33434, ImageResources:34377, ExifTag:34665, InterColorProfile:34675, GPSTag:34853, Interlace:34857, TimeZoneOffset:34858, SelfTimerMode:34859, FlashEnergy:37387, SpatialFrequencyResponse:37388, Noise:37389, FocalPlaneXResolution:37390, FocalPlaneYResolution:37391, FocalPlaneResolutionUnit:37392, ImageNumber:37393, SecurityClassification:37394, ImageHistory:37395, ExposureIndex:37397, TIFFEPStandardID:37398, SensingMethod:37399, XPTitle:40091, XPComment:40092, XPAuthor:40093, XPKeywords:40094, XPSubject:40095, PrintImageMatching:50341, DNGVersion:50706, DNGBackwardVersion:50707, UniqueCameraModel:50708, LocalizedCameraModel:50709, CFAPlaneColor:50710, CFALayout:50711, LinearizationTable:50712, BlackLevelRepeatDim:50713, BlackLevel:50714, BlackLevelDeltaH:50715, BlackLevelDeltaV:50716, WhiteLevel:50717, DefaultScale:50718, DefaultCropOrigin:50719, DefaultCropSize:50720, ColorMatrix1:50721, ColorMatrix2:50722, CameraCalibration1:50723, CameraCalibration2:50724, ReductionMatrix1:50725, ReductionMatrix2:50726, AnalogBalance:50727, AsShotNeutral:50728, AsShotWhiteXY:50729, BaselineExposure:50730, BaselineNoise:50731, BaselineSharpness:50732, BayerGreenSplit:50733, LinearResponseLimit:50734, CameraSerialNumber:50735, LensInfo:50736, ChromaBlurRadius:50737, AntiAliasStrength:50738, ShadowScale:50739, DNGPrivateData:50740, MakerNoteSafety:50741, CalibrationIlluminant1:50778, CalibrationIlluminant2:50779, BestQualityScale:50780, RawDataUniqueID:50781, OriginalRawFileName:50827, OriginalRawFileData:50828, ActiveArea:50829, MaskedAreas:50830, AsShotICCProfile:50831, AsShotPreProfileMatrix:50832, CurrentICCProfile:50833, CurrentPreProfileMatrix:50834, ColorimetricReference:50879, CameraCalibrationSignature:50931, ProfileCalibrationSignature:50932, AsShotProfileName:50934, NoiseReductionApplied:50935, ProfileName:50936, ProfileHueSatMapDims:50937, ProfileHueSatMapData1:50938, ProfileHueSatMapData2:50939, ProfileToneCurve:50940, ProfileEmbedPolicy:50941, ProfileCopyright:50942, ForwardMatrix1:50964, ForwardMatrix2:50965, PreviewApplicationName:50966, PreviewApplicationVersion:50967, PreviewSettingsName:50968, PreviewSettingsDigest:50969, PreviewColorSpace:50970, PreviewDateTime:50971, RawImageDigest:50972, OriginalRawFileDigest:50973, SubTileBlockSize:50974, RowInterleaveFactor:50975, ProfileLookTableDims:50981, ProfileLookTableData:50982, OpcodeList1:51008, OpcodeList2:51009, OpcodeList3:51022, NoiseProfile:51041, }; that.ExifIFD = { ExposureTime:33434, FNumber:33437, ExposureProgram:34850, SpectralSensitivity:34852, ISOSpeedRatings:34855, OECF:34856, SensitivityType:34864, StandardOutputSensitivity:34865, RecommendedExposureIndex:34866, ISOSpeed:34867, ISOSpeedLatitudeyyy:34868, ISOSpeedLatitudezzz:34869, ExifVersion:36864, DateTimeOriginal:36867, DateTimeDigitized:36868, ComponentsConfiguration:37121, CompressedBitsPerPixel:37122, ShutterSpeedValue:37377, ApertureValue:37378, BrightnessValue:37379, ExposureBiasValue:37380, MaxApertureValue:37381, SubjectDistance:37382, MeteringMode:37383, LightSource:37384, Flash:37385, FocalLength:37386, SubjectArea:37396, MakerNote:37500, UserComment:37510, SubSecTime:37520, SubSecTimeOriginal:37521, SubSecTimeDigitized:37522, FlashpixVersion:40960, ColorSpace:40961, PixelXDimension:40962, PixelYDimension:40963, RelatedSoundFile:40964, InteroperabilityTag:40965, FlashEnergy:41483, SpatialFrequencyResponse:41484, FocalPlaneXResolution:41486, FocalPlaneYResolution:41487, FocalPlaneResolutionUnit:41488, SubjectLocation:41492, ExposureIndex:41493, SensingMethod:41495, FileSource:41728, SceneType:41729, CFAPattern:41730, CustomRendered:41985, ExposureMode:41986, WhiteBalance:41987, DigitalZoomRatio:41988, FocalLengthIn35mmFilm:41989, SceneCaptureType:41990, GainControl:41991, Contrast:41992, Saturation:41993, Sharpness:41994, DeviceSettingDescription:41995, SubjectDistanceRange:41996, ImageUniqueID:42016, CameraOwnerName:42032, BodySerialNumber:42033, LensSpecification:42034, LensMake:42035, LensModel:42036, LensSerialNumber:42037, Gamma:42240, }; that.GPSIFD = { GPSVersionID:0, GPSLatitudeRef:1, GPSLatitude:2, GPSLongitudeRef:3, GPSLongitude:4, GPSAltitudeRef:5, GPSAltitude:6, GPSTimeStamp:7, GPSSatellites:8, GPSStatus:9, GPSMeasureMode:10, GPSDOP:11, GPSSpeedRef:12, GPSSpeed:13, GPSTrackRef:14, GPSTrack:15, GPSImgDirectionRef:16, GPSImgDirection:17, GPSMapDatum:18, GPSDestLatitudeRef:19, GPSDestLatitude:20, GPSDestLongitudeRef:21, GPSDestLongitude:22, GPSDestBearingRef:23, GPSDestBearing:24, GPSDestDistanceRef:25, GPSDestDistance:26, GPSProcessingMethod:27, GPSAreaInformation:28, GPSDateStamp:29, GPSDifferential:30, GPSHPositioningError:31, }; that.InteropIFD = { InteroperabilityIndex:1, }; that.GPSHelper = { degToDmsRational:function (degFloat) { var minFloat = degFloat % 1 * 60; var secFloat = minFloat % 1 * 60; var deg = Math.floor(degFloat); var min = Math.floor(minFloat); var sec = Math.round(secFloat * 100); return [[deg, 1], [min, 1], [sec, 100]]; } }; if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = that; } exports.piexif = that; } else { window.piexif = that; } })();