| Index: chrome/browser/resources/file_manager/js/metadata/exif_parser.js
|
| diff --git a/chrome/browser/resources/file_manager/js/metadata/exif_parser.js b/chrome/browser/resources/file_manager/js/metadata/exif_parser.js
|
| deleted file mode 100644
|
| index f81e5cc0b9f3068202988bf7a6cec331453ee19b..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/resources/file_manager/js/metadata/exif_parser.js
|
| +++ /dev/null
|
| @@ -1,439 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -'use strict';
|
| -
|
| -var EXIF_MARK_SOI = 0xffd8; // Start of image data.
|
| -var EXIF_MARK_SOS = 0xffda; // Start of "stream" (the actual image data).
|
| -var EXIF_MARK_SOF = 0xffc0; // Start of "frame"
|
| -var EXIF_MARK_EXIF = 0xffe1; // Start of exif block.
|
| -
|
| -var EXIF_ALIGN_LITTLE = 0x4949; // Indicates little endian exif data.
|
| -var EXIF_ALIGN_BIG = 0x4d4d; // Indicates big endian exif data.
|
| -
|
| -var EXIF_TAG_TIFF = 0x002a; // First directory containing TIFF data.
|
| -var EXIF_TAG_GPSDATA = 0x8825; // Pointer from TIFF to the GPS directory.
|
| -var EXIF_TAG_EXIFDATA = 0x8769; // Pointer from TIFF to the EXIF IFD.
|
| -var EXIF_TAG_SUBIFD = 0x014a; // Pointer from TIFF to "Extra" IFDs.
|
| -
|
| -var EXIF_TAG_JPG_THUMB_OFFSET = 0x0201; // Pointer from TIFF to thumbnail.
|
| -var EXIF_TAG_JPG_THUMB_LENGTH = 0x0202; // Length of thumbnail data.
|
| -
|
| -var EXIF_TAG_ORIENTATION = 0x0112;
|
| -var EXIF_TAG_X_DIMENSION = 0xA002;
|
| -var EXIF_TAG_Y_DIMENSION = 0xA003;
|
| -
|
| -function ExifParser(parent) {
|
| - ImageParser.call(this, parent, 'jpeg', /\.jpe?g$/i);
|
| -}
|
| -
|
| -ExifParser.prototype = {__proto__: ImageParser.prototype};
|
| -
|
| -/**
|
| - * @param {File} file // TODO(JSDOC).
|
| - * @param {Object} metadata // TODO(JSDOC).
|
| - * @param {function} callback // TODO(JSDOC).
|
| - * @param {function} errorCallback // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.parse = function(file, metadata, callback, errorCallback) {
|
| - this.requestSlice(file, callback, errorCallback, metadata, 0);
|
| -};
|
| -
|
| -/**
|
| - * @param {File} file // TODO(JSDOC).
|
| - * @param {function} callback // TODO(JSDOC).
|
| - * @param {function} errorCallback // TODO(JSDOC).
|
| - * @param {Object} metadata // TODO(JSDOC).
|
| - * @param {number} filePos // TODO(JSDOC).
|
| - * @param {number=} opt_length // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.requestSlice = function(
|
| - file, callback, errorCallback, metadata, filePos, opt_length) {
|
| - // Read at least 1Kb so that we do not issue too many read requests.
|
| - opt_length = Math.max(1024, opt_length || 0);
|
| -
|
| - var self = this;
|
| - var reader = new FileReader();
|
| - reader.onerror = errorCallback;
|
| - reader.onload = function() { self.parseSlice(
|
| - file, callback, errorCallback, metadata, filePos, reader.result);
|
| - };
|
| - reader.readAsArrayBuffer(file.slice(filePos, filePos + opt_length));
|
| -};
|
| -
|
| -/**
|
| - * @param {File} file // TODO(JSDOC).
|
| - * @param {function} callback // TODO(JSDOC).
|
| - * @param {function} errorCallback // TODO(JSDOC).
|
| - * @param {Object} metadata // TODO(JSDOC).
|
| - * @param {number} filePos // TODO(JSDOC).
|
| - * @param {ArrayBuffer} buf // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.parseSlice = function(
|
| - file, callback, errorCallback, metadata, filePos, buf) {
|
| - try {
|
| - var br = new ByteReader(buf);
|
| -
|
| - if (!br.canRead(4)) {
|
| - // We never ask for less than 4 bytes. This can only mean we reached EOF.
|
| - throw new Error('Unexpected EOF @' + (filePos + buf.byteLength));
|
| - }
|
| -
|
| - if (filePos == 0) {
|
| - // First slice, check for the SOI mark.
|
| - var firstMark = this.readMark(br);
|
| - if (firstMark != EXIF_MARK_SOI)
|
| - throw new Error('Invalid file header: ' + firstMark.toString(16));
|
| - }
|
| -
|
| - var self = this;
|
| - var reread = function(opt_offset, opt_bytes) {
|
| - self.requestSlice(file, callback, errorCallback, metadata,
|
| - filePos + br.tell() + (opt_offset || 0), opt_bytes);
|
| - };
|
| -
|
| - while (true) {
|
| - if (!br.canRead(4)) {
|
| - // Cannot read the mark and the length, request a minimum-size slice.
|
| - reread();
|
| - return;
|
| - }
|
| -
|
| - var mark = this.readMark(br);
|
| - if (mark == EXIF_MARK_SOS)
|
| - throw new Error('SOS marker found before SOF');
|
| -
|
| - var markLength = this.readMarkLength(br);
|
| -
|
| - var nextSectionStart = br.tell() + markLength;
|
| - if (!br.canRead(markLength)) {
|
| - // Get the entire section.
|
| - if (filePos + br.tell() + markLength > file.size) {
|
| - throw new Error(
|
| - 'Invalid section length @' + (filePos + br.tell() - 2));
|
| - }
|
| - reread(-4, markLength + 4);
|
| - return;
|
| - }
|
| -
|
| - if (mark == EXIF_MARK_EXIF) {
|
| - this.parseExifSection(metadata, buf, br);
|
| - } else if (ExifParser.isSOF_(mark)) {
|
| - // The most reliable size information is encoded in the SOF section.
|
| - br.seek(1, ByteReader.SEEK_CUR); // Skip the precision byte.
|
| - var height = br.readScalar(2);
|
| - var width = br.readScalar(2);
|
| - ExifParser.setImageSize(metadata, width, height);
|
| - callback(metadata); // We are done!
|
| - return;
|
| - }
|
| -
|
| - br.seek(nextSectionStart, ByteReader.SEEK_BEG);
|
| - }
|
| - } catch (e) {
|
| - errorCallback(e.toString());
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * @private
|
| - * @param {number} mark // TODO(JSDOC).
|
| - * @return {boolean} // TODO(JSDOC).
|
| - */
|
| -ExifParser.isSOF_ = function(mark) {
|
| - // There are 13 variants of SOF fragment format distinguished by the last
|
| - // hex digit of the mark, but the part we want is always the same.
|
| - if ((mark & ~0xF) != EXIF_MARK_SOF) return false;
|
| -
|
| - // If the last digit is 4, 8 or 12 it is not really a SOF.
|
| - var type = mark & 0xF;
|
| - return (type != 4 && type != 8 && type != 12);
|
| -};
|
| -
|
| -/**
|
| - * @param {Object} metadata // TODO(JSDOC).
|
| - * @param {ArrayBuffer} buf // TODO(JSDOC).
|
| - * @param {ByteReader} br // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.parseExifSection = function(metadata, buf, br) {
|
| - var magic = br.readString(6);
|
| - if (magic != 'Exif\0\0') {
|
| - // Some JPEG files may have sections marked with EXIF_MARK_EXIF
|
| - // but containing something else (e.g. XML text). Ignore such sections.
|
| - this.vlog('Invalid EXIF magic: ' + magic + br.readString(100));
|
| - return;
|
| - }
|
| -
|
| - // Offsets inside the EXIF block are based after the magic string.
|
| - // Create a new ByteReader based on the current position to make offset
|
| - // calculations simpler.
|
| - br = new ByteReader(buf, br.tell());
|
| -
|
| - var order = br.readScalar(2);
|
| - if (order == EXIF_ALIGN_LITTLE) {
|
| - br.setByteOrder(ByteReader.LITTLE_ENDIAN);
|
| - } else if (order != EXIF_ALIGN_BIG) {
|
| - this.log('Invalid alignment value: ' + order.toString(16));
|
| - return;
|
| - }
|
| -
|
| - var tag = br.readScalar(2);
|
| - if (tag != EXIF_TAG_TIFF) {
|
| - this.log('Invalid TIFF tag: ' + tag.toString(16));
|
| - return;
|
| - }
|
| -
|
| - metadata.littleEndian = (order == EXIF_ALIGN_LITTLE);
|
| - metadata.ifd = {
|
| - image: {},
|
| - thumbnail: {}
|
| - };
|
| - var directoryOffset = br.readScalar(4);
|
| -
|
| - // Image directory.
|
| - this.vlog('Read image directory.');
|
| - br.seek(directoryOffset);
|
| - directoryOffset = this.readDirectory(br, metadata.ifd.image);
|
| - metadata.imageTransform = this.parseOrientation(metadata.ifd.image);
|
| -
|
| - // Thumbnail Directory chained from the end of the image directory.
|
| - if (directoryOffset) {
|
| - this.vlog('Read thumbnail directory.');
|
| - br.seek(directoryOffset);
|
| - this.readDirectory(br, metadata.ifd.thumbnail);
|
| - // If no thumbnail orientation is encoded, assume same orientation as
|
| - // the primary image.
|
| - metadata.thumbnailTransform =
|
| - this.parseOrientation(metadata.ifd.thumbnail) ||
|
| - metadata.imageTransform;
|
| - }
|
| -
|
| - // EXIF Directory may be specified as a tag in the image directory.
|
| - if (EXIF_TAG_EXIFDATA in metadata.ifd.image) {
|
| - this.vlog('Read EXIF directory.');
|
| - directoryOffset = metadata.ifd.image[EXIF_TAG_EXIFDATA].value;
|
| - br.seek(directoryOffset);
|
| - metadata.ifd.exif = {};
|
| - this.readDirectory(br, metadata.ifd.exif);
|
| - }
|
| -
|
| - // GPS Directory may also be linked from the image directory.
|
| - if (EXIF_TAG_GPSDATA in metadata.ifd.image) {
|
| - this.vlog('Read GPS directory.');
|
| - directoryOffset = metadata.ifd.image[EXIF_TAG_GPSDATA].value;
|
| - br.seek(directoryOffset);
|
| - metadata.ifd.gps = {};
|
| - this.readDirectory(br, metadata.ifd.gps);
|
| - }
|
| -
|
| - // Thumbnail may be linked from the image directory.
|
| - if (EXIF_TAG_JPG_THUMB_OFFSET in metadata.ifd.thumbnail &&
|
| - EXIF_TAG_JPG_THUMB_LENGTH in metadata.ifd.thumbnail) {
|
| - this.vlog('Read thumbnail image.');
|
| - br.seek(metadata.ifd.thumbnail[EXIF_TAG_JPG_THUMB_OFFSET].value);
|
| - metadata.thumbnailURL = br.readImage(
|
| - metadata.ifd.thumbnail[EXIF_TAG_JPG_THUMB_LENGTH].value);
|
| - } else {
|
| - this.vlog('Image has EXIF data, but no JPG thumbnail.');
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * @param {Object} metadata // TODO(JSDOC).
|
| - * @param {number} width // TODO(JSDOC).
|
| - * @param {number} height // TODO(JSDOC).
|
| - */
|
| -ExifParser.setImageSize = function(metadata, width, height) {
|
| - if (metadata.imageTransform && metadata.imageTransform.rotate90) {
|
| - metadata.width = height;
|
| - metadata.height = width;
|
| - } else {
|
| - metadata.width = width;
|
| - metadata.height = height;
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * @param {ByteReader} br // TODO(JSDOC).
|
| - * @return {number} // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.readMark = function(br) {
|
| - return br.readScalar(2);
|
| -};
|
| -
|
| -/**
|
| - * @param {ByteReader} br // TODO(JSDOC).
|
| - * @return {number} // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.readMarkLength = function(br) {
|
| - // Length includes the 2 bytes used to store the length.
|
| - return br.readScalar(2) - 2;
|
| -};
|
| -
|
| -/**
|
| - * @param {ByteReader} br // TODO(JSDOC).
|
| - * @param {Array.<Object>} tags // TODO(JSDOC).
|
| - * @return {number} // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.readDirectory = function(br, tags) {
|
| - var entryCount = br.readScalar(2);
|
| - for (var i = 0; i < entryCount; i++) {
|
| - var tagId = br.readScalar(2);
|
| - var tag = tags[tagId] = {id: tagId};
|
| - tag.format = br.readScalar(2);
|
| - tag.componentCount = br.readScalar(4);
|
| - this.readTagValue(br, tag);
|
| - }
|
| -
|
| - return br.readScalar(4);
|
| -};
|
| -
|
| -/**
|
| - * @param {ByteReader} br // TODO(JSDOC).
|
| - * @param {Object} tag // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.readTagValue = function(br, tag) {
|
| - var self = this;
|
| -
|
| - function safeRead(size, readFunction, signed) {
|
| - try {
|
| - unsafeRead(size, readFunction, signed);
|
| - } catch (ex) {
|
| - self.log('error reading tag 0x' + tag.id.toString(16) + '/' +
|
| - tag.format + ', size ' + tag.componentCount + '*' + size + ' ' +
|
| - (ex.stack || '<no stack>') + ': ' + ex);
|
| - tag.value = null;
|
| - }
|
| - }
|
| -
|
| - function unsafeRead(size, readFunction, signed) {
|
| - if (!readFunction)
|
| - readFunction = function(size) { return br.readScalar(size, signed) };
|
| -
|
| - var totalSize = tag.componentCount * size;
|
| - if (totalSize < 1) {
|
| - // This is probably invalid exif data, skip it.
|
| - tag.componentCount = 1;
|
| - tag.value = br.readScalar(4);
|
| - return;
|
| - }
|
| -
|
| - if (totalSize > 4) {
|
| - // If the total size is > 4, the next 4 bytes will be a pointer to the
|
| - // actual data.
|
| - br.pushSeek(br.readScalar(4));
|
| - }
|
| -
|
| - if (tag.componentCount == 1) {
|
| - tag.value = readFunction(size);
|
| - } else {
|
| - // Read multiple components into an array.
|
| - tag.value = [];
|
| - for (var i = 0; i < tag.componentCount; i++)
|
| - tag.value[i] = readFunction(size);
|
| - }
|
| -
|
| - if (totalSize > 4) {
|
| - // Go back to the previous position if we had to jump to the data.
|
| - br.popSeek();
|
| - } else if (totalSize < 4) {
|
| - // Otherwise, if the value wasn't exactly 4 bytes, skip over the
|
| - // unread data.
|
| - br.seek(4 - totalSize, ByteReader.SEEK_CUR);
|
| - }
|
| - }
|
| -
|
| - switch (tag.format) {
|
| - case 1: // Byte
|
| - case 7: // Undefined
|
| - safeRead(1);
|
| - break;
|
| -
|
| - case 2: // String
|
| - safeRead(1);
|
| - if (tag.componentCount == 0) {
|
| - tag.value = '';
|
| - } else if (tag.componentCount == 1) {
|
| - tag.value = String.fromCharCode(tag.value);
|
| - } else {
|
| - tag.value = String.fromCharCode.apply(null, tag.value);
|
| - }
|
| - break;
|
| -
|
| - case 3: // Short
|
| - safeRead(2);
|
| - break;
|
| -
|
| - case 4: // Long
|
| - safeRead(4);
|
| - break;
|
| -
|
| - case 9: // Signed Long
|
| - safeRead(4, null, true);
|
| - break;
|
| -
|
| - case 5: // Rational
|
| - safeRead(8, function() {
|
| - return [br.readScalar(4), br.readScalar(4)];
|
| - });
|
| - break;
|
| -
|
| - case 10: // Signed Rational
|
| - safeRead(8, function() {
|
| - return [br.readScalar(4, true), br.readScalar(4, true)];
|
| - });
|
| - break;
|
| -
|
| - default: // ???
|
| - this.vlog('Unknown tag format 0x' + Number(tag.id).toString(16) +
|
| - ': ' + tag.format);
|
| - safeRead(4);
|
| - break;
|
| - }
|
| -
|
| - this.vlog('Read tag: 0x' + tag.id.toString(16) + '/' + tag.format + ': ' +
|
| - tag.value);
|
| -};
|
| -
|
| -/**
|
| - * TODO(JSDOC)
|
| - * @const
|
| - * @type {Array.<number>}
|
| - */
|
| -ExifParser.SCALEX = [1, -1, -1, 1, 1, 1, -1, -1];
|
| -
|
| -/**
|
| - * TODO(JSDOC)
|
| - * @const
|
| - * @type {Array.<number>}
|
| - */
|
| -ExifParser.SCALEY = [1, 1, -1, -1, -1, 1, 1, -1];
|
| -
|
| -/**
|
| - * TODO(JSDOC)
|
| - * @const
|
| - * @type {Array.<number>}
|
| - */
|
| -ExifParser.ROTATE90 = [0, 0, 0, 0, 1, 1, 1, 1];
|
| -
|
| -/**
|
| - * Transform exif-encoded orientation into a set of parameters compatible with
|
| - * CSS and canvas transforms (scaleX, scaleY, rotation).
|
| - *
|
| - * @param {Object} ifd exif property dictionary (image or thumbnail).
|
| - * @return {Object} // TODO(JSDOC).
|
| - */
|
| -ExifParser.prototype.parseOrientation = function(ifd) {
|
| - if (ifd[EXIF_TAG_ORIENTATION]) {
|
| - var index = (ifd[EXIF_TAG_ORIENTATION].value || 1) - 1;
|
| - return {
|
| - scaleX: ExifParser.SCALEX[index],
|
| - scaleY: ExifParser.SCALEY[index],
|
| - rotate90: ExifParser.ROTATE90[index]
|
| - };
|
| - }
|
| - return null;
|
| -};
|
| -
|
| -MetadataDispatcher.registerParserClass(ExifParser);
|
|
|