15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_MARK_SOI = 0xffd8;  // Start of image data.
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_MARK_SOS = 0xffda;  // Start of "stream" (the actual image data).
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_MARK_SOF = 0xffc0;  // Start of "frame"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_MARK_EXIF = 0xffe1;  // Start of exif block.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_ALIGN_LITTLE = 0x4949;  // Indicates little endian exif data.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_ALIGN_BIG = 0x4d4d;  // Indicates big endian exif data.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_TIFF = 0x002a;  // First directory containing TIFF data.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_GPSDATA = 0x8825;  // Pointer from TIFF to the GPS directory.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_EXIFDATA = 0x8769;  // Pointer from TIFF to the EXIF IFD.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_SUBIFD = 0x014a;  // Pointer from TIFF to "Extra" IFDs.
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_JPG_THUMB_OFFSET = 0x0201;  // Pointer from TIFF to thumbnail.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_JPG_THUMB_LENGTH = 0x0202;  // Length of thumbnail data.
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_ORIENTATION = 0x0112;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_X_DIMENSION = 0xA002;
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)var EXIF_TAG_Y_DIMENSION = 0xA003;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ExifParser(parent) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageParser.call(this, parent, 'jpeg', /\.jpe?g$/i);
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype = {__proto__: ImageParser.prototype};
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {File} file File object to parse.
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} metadata Metadata object for the file.
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function} callback Callback to be called on success.
371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function} errorCallback Error callback.
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.parse = function(file, metadata, callback, errorCallback) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.requestSlice(file, callback, errorCallback, metadata, 0);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {File} file File object to parse.
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function} callback Callback to be called on success.
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function} errorCallback Error callback.
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} metadata Metadata object.
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number} filePos Position to slice at.
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number=} opt_length Number of bytes to slice. By default 1 KB.
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.requestSlice = function(
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file, callback, errorCallback, metadata, filePos, opt_length) {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Read at least 1Kb so that we do not issue too many read requests.
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  opt_length = Math.max(1024, opt_length || 0);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var reader = new FileReader();
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reader.onerror = errorCallback;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reader.onload = function() { self.parseSlice(
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      file, callback, errorCallback, metadata, filePos, reader.result);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reader.readAsArrayBuffer(file.slice(filePos, filePos + opt_length));
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {File} file File object to parse.
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function} callback Callback to be called on success.
681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {function} errorCallback Error callback.
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} metadata Metadata object.
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number} filePos Position to slice at.
711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ArrayBuffer} buf Buffer to be parsed.
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.parseSlice = function(
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file, callback, errorCallback, metadata, filePos, buf) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var br = new ByteReader(buf);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!br.canRead(4)) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We never ask for less than 4 bytes. This can only mean we reached EOF.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw new Error('Unexpected EOF @' + (filePos + buf.byteLength));
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (filePos == 0) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // First slice, check for the SOI mark.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var firstMark = this.readMark(br);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (firstMark != EXIF_MARK_SOI)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw new Error('Invalid file header: ' + firstMark.toString(16));
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var self = this;
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var reread = function(opt_offset, opt_bytes) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.requestSlice(file, callback, errorCallback, metadata,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          filePos + br.tell() + (opt_offset || 0), opt_bytes);
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while (true) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!br.canRead(4)) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Cannot read the mark and the length, request a minimum-size slice.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        reread();
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var mark = this.readMark(br);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (mark == EXIF_MARK_SOS)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        throw new Error('SOS marker found before SOF');
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var markLength = this.readMarkLength(br);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var nextSectionStart = br.tell() + markLength;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!br.canRead(markLength)) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Get the entire section.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (filePos + br.tell() + markLength > file.size) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          throw new Error(
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'Invalid section length @' + (filePos + br.tell() - 2));
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        reread(-4, markLength + 4);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (mark == EXIF_MARK_EXIF) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.parseExifSection(metadata, buf, br);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (ExifParser.isSOF_(mark)) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The most reliable size information is encoded in the SOF section.
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        br.seek(1, ByteReader.SEEK_CUR); // Skip the precision byte.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var height = br.readScalar(2);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        var width = br.readScalar(2);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ExifParser.setImageSize(metadata, width, height);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        callback(metadata);  // We are done!
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.seek(nextSectionStart, ByteReader.SEEK_BEG);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } catch (e) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    errorCallback(e.toString());
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @private
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number} mark Mark to be checked.
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {boolean} True if the mark is SOF.
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.isSOF_ = function(mark) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There are 13 variants of SOF fragment format distinguished by the last
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // hex digit of the mark, but the part we want is always the same.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((mark & ~0xF) != EXIF_MARK_SOF) return false;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the last digit is 4, 8 or 12 it is not really a SOF.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var type = mark & 0xF;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (type != 4 && type != 8 && type != 12);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} metadata Metadata object.
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ArrayBuffer} buf Buffer to be parsed.
1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ByteReader} br Byte reader to be used.
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.parseExifSection = function(metadata, buf, br) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var magic = br.readString(6);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (magic != 'Exif\0\0') {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Some JPEG files may have sections marked with EXIF_MARK_EXIF
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // but containing something else (e.g. XML text). Ignore such sections.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog('Invalid EXIF magic: ' + magic + br.readString(100));
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Offsets inside the EXIF block are based after the magic string.
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create a new ByteReader based on the current position to make offset
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // calculations simpler.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  br = new ByteReader(buf, br.tell());
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var order = br.readScalar(2);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (order == EXIF_ALIGN_LITTLE) {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.setByteOrder(ByteReader.LITTLE_ENDIAN);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (order != EXIF_ALIGN_BIG) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.log('Invalid alignment value: ' + order.toString(16));
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var tag = br.readScalar(2);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tag != EXIF_TAG_TIFF) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.log('Invalid TIFF tag: ' + tag.toString(16));
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  metadata.littleEndian = (order == EXIF_ALIGN_LITTLE);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  metadata.ifd = {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    image: {},
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thumbnail: {}
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var directoryOffset = br.readScalar(4);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Image directory.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.vlog('Read image directory.');
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  br.seek(directoryOffset);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  directoryOffset = this.readDirectory(br, metadata.ifd.image);
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  metadata.imageTransform = this.parseOrientation(metadata.ifd.image);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Thumbnail Directory chained from the end of the image directory.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (directoryOffset) {
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog('Read thumbnail directory.');
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(directoryOffset);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.readDirectory(br, metadata.ifd.thumbnail);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If no thumbnail orientation is encoded, assume same orientation as
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the primary image.
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.thumbnailTransform =
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.parseOrientation(metadata.ifd.thumbnail) ||
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        metadata.imageTransform;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // EXIF Directory may be specified as a tag in the image directory.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (EXIF_TAG_EXIFDATA in metadata.ifd.image) {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog('Read EXIF directory.');
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    directoryOffset = metadata.ifd.image[EXIF_TAG_EXIFDATA].value;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(directoryOffset);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.ifd.exif = {};
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.readDirectory(br, metadata.ifd.exif);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GPS Directory may also be linked from the image directory.
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (EXIF_TAG_GPSDATA in metadata.ifd.image) {
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog('Read GPS directory.');
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    directoryOffset = metadata.ifd.image[EXIF_TAG_GPSDATA].value;
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(directoryOffset);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.ifd.gps = {};
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.readDirectory(br, metadata.ifd.gps);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Thumbnail may be linked from the image directory.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (EXIF_TAG_JPG_THUMB_OFFSET in metadata.ifd.thumbnail &&
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EXIF_TAG_JPG_THUMB_LENGTH in metadata.ifd.thumbnail) {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog('Read thumbnail image.');
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(metadata.ifd.thumbnail[EXIF_TAG_JPG_THUMB_OFFSET].value);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.thumbnailURL = br.readImage(
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        metadata.ifd.thumbnail[EXIF_TAG_JPG_THUMB_LENGTH].value);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog('Image has EXIF data, but no JPG thumbnail.');
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} metadata Metadata object.
2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number} width Width in pixels.
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {number} height Height in pixels.
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.setImageSize = function(metadata, width, height) {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (metadata.imageTransform && metadata.imageTransform.rotate90) {
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.width = height;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.height = width;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.width = width;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.height = height;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ByteReader} br Byte reader to be used for reading.
2591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {number} Mark value.
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.readMark = function(br) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return br.readScalar(2);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ByteReader} br Bye reader to be used for reading.
2671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {number} Size of the mark at the current position.
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.readMarkLength = function(br) {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Length includes the 2 bytes used to store the length.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return br.readScalar(2) - 2;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ByteReader} br Byte reader to be used for reading.
2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Array.<Object>} tags Array of tags to be written to.
2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {number} Directory offset.
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.readDirectory = function(br, tags) {
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var entryCount = br.readScalar(2);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var i = 0; i < entryCount; i++) {
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var tagId = br.readScalar(2);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var tag = tags[tagId] = {id: tagId};
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tag.format = br.readScalar(2);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tag.componentCount = br.readScalar(4);
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.readTagValue(br, tag);
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return br.readScalar(4);
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {ByteReader} br Byte reader to be used for reading.
2941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} tag Tag object.
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.readTagValue = function(br, tag) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function safeRead(size, readFunction, signed) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unsafeRead(size, readFunction, signed);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } catch (ex) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.log('error reading tag 0x' + tag.id.toString(16) + '/' +
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               tag.format + ', size ' + tag.componentCount + '*' + size + ' ' +
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               (ex.stack || '<no stack>') + ': ' + ex);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tag.value = null;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function unsafeRead(size, readFunction, signed) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!readFunction)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      readFunction = function(size) { return br.readScalar(size, signed) };
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var totalSize = tag.componentCount * size;
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (totalSize < 1) {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This is probably invalid exif data, skip it.
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tag.componentCount = 1;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tag.value = br.readScalar(4);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (totalSize > 4) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the total size is > 4, the next 4 bytes will be a pointer to the
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // actual data.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.pushSeek(br.readScalar(4));
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tag.componentCount == 1) {
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tag.value = readFunction(size);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Read multiple components into an array.
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tag.value = [];
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (var i = 0; i < tag.componentCount; i++)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tag.value[i] = readFunction(size);
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (totalSize > 4) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Go back to the previous position if we had to jump to the data.
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.popSeek();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (totalSize < 4) {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Otherwise, if the value wasn't exactly 4 bytes, skip over the
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // unread data.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.seek(4 - totalSize, ByteReader.SEEK_CUR);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (tag.format) {
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 1: // Byte
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 7: // Undefined
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(1);
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 2: // String
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(1);
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (tag.componentCount == 0) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tag.value = '';
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (tag.componentCount == 1) {
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tag.value = String.fromCharCode(tag.value);
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tag.value = String.fromCharCode.apply(null, tag.value);
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 3: // Short
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(2);
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 4: // Long
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(4);
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 9: // Signed Long
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(4, null, true);
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 5: // Rational
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(8, function() {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return [br.readScalar(4), br.readScalar(4)];
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 10: // Signed Rational
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(8, function() {
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return [br.readScalar(4, true), br.readScalar(4, true)];
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      });
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default: // ???
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.vlog('Unknown tag format 0x' + Number(tag.id).toString(16) +
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                ': ' + tag.format);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      safeRead(4);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.vlog('Read tag: 0x' + tag.id.toString(16) + '/' + tag.format + ': ' +
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            tag.value);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Map from the exif orientation value to the horizontal scale value.
4012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @const
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @type {Array.<number>}
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.SCALEX = [1, -1, -1, 1, 1, 1, -1, -1];
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
4071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Map from the exif orientation value to the vertical scale value.
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @const
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @type {Array.<number>}
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.SCALEY = [1, 1, -1, -1, -1, 1, 1, -1];
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
4141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Map from the exit orientation value to the rotation value.
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @const
4162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @type {Array.<number>}
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.ROTATE90 = [0, 0, 0, 0, 1, 1, 1, 1];
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Transform exif-encoded orientation into a set of parameters compatible with
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * CSS and canvas transforms (scaleX, scaleY, rotation).
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
4241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {Object} ifd Exif property dictionary (image or thumbnail).
4251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @return {Object} Orientation object.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExifParser.prototype.parseOrientation = function(ifd) {
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ifd[EXIF_TAG_ORIENTATION]) {
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var index = (ifd[EXIF_TAG_ORIENTATION].value || 1) - 1;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scaleX: ExifParser.SCALEX[index],
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      scaleY: ExifParser.SCALEY[index],
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rotate90: ExifParser.ROTATE90[index]
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    };
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return null;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MetadataDispatcher.registerParserClass(ExifParser);
440