blob: 01b4e8f108a34f690fd2e6cafcc0c1a7f385c309 [file] [log] [blame]
/* 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.0.4";
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 new Error("Given data is not jpeg.");
}
var segments = splitIntoSegments(jpeg);
var newSegments = segments.filter(function(seg){
return !(seg.slice(0, 2) == "\xff\xe1" &&
seg.slice(4, 10) == "Exif\x00\x00");
});
var new_data = newSegments.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 new Error("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 new Error("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 new Error("'load' gots invalid file data.");
}
} else {
throw new Error("'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 new Error("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 new Error("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 == 9) { // SLONG
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 == 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 new Error("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 new Error("'pack' error. Got invalid type argument.");
}
if ((mark.length - 1) != array.length) {
throw new Error("'pack' error. " + (mark.length - 1) + " marks, " + array.length + " elements.");
}
var littleEndian;
if (mark[0] == "<") {
littleEndian = true;
} else if (mark[0] == ">") {
littleEndian = false;
} else {
throw new Error("");
}
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 new Error("'pack' error.");
} else {
valStr = String.fromCharCode(val);
}
} else if (c == "H") {
val = array[p - 1];
if ((val > 0xffff) || (val < 0)) {
throw new Error("'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 new Error("'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 new Error("'pack' error.");
}
packed += valStr;
p += 1;
}
return packed;
}
function unpack(mark, str) {
if (typeof (str) != "string") {
throw new Error("'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 new Error("'unpack' error. Got invalid mark.");
}
}
if (l != str.length) {
throw new Error("'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 new Error("'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 new Error("'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 new Error("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 new Error("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) {
var hasExifSegment = false;
var additionalAPP1ExifSegments = [];
segments.forEach(function(segment, i) {
// Replace first occurence of APP1:Exif segment
if (segment.slice(0, 2) == "\xff\xe1" &&
segment.slice(4, 10) == "Exif\x00\x00"
) {
if (!hasExifSegment) {
segments[i] = exif;
hasExifSegment = true;
} else {
additionalAPP1ExifSegments.unshift(i);
}
}
});
// Remove additional occurences of APP1:Exif segment
additionalAPP1ExifSegments.forEach(function(segmentIndex) {
segments.splice(segmentIndex, 1);
});
if (!hasExifSegment && 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 degAbs = Math.abs(degFloat);
var minFloat = degAbs % 1 * 60;
var secFloat = minFloat % 1 * 60;
var deg = Math.floor(degAbs);
var min = Math.floor(minFloat);
var sec = Math.round(secFloat * 100);
return [[deg, 1], [min, 1], [sec, 100]];
},
dmsRationalToDeg:function (dmsArray, ref) {
var sign = (ref === 'S' || ref === 'W') ? -1.0 : 1.0;
var deg = dmsArray[0][0] / dmsArray[0][1] +
dmsArray[1][0] / dmsArray[1][1] / 60.0 +
dmsArray[2][0] / dmsArray[2][1] / 3600.0;
return deg * sign;
}
};
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = that;
}
exports.piexif = that;
} else {
window.piexif = that;
}
})();