• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5'use strict';
6
7/* Base class for image metadata parsers that only need to look at a short
8  fragment at the start of the file */
9function SimpleImageParser(parent, type, urlFilter, headerSize) {
10  ImageParser.call(this, parent, type, urlFilter);
11  this.headerSize = headerSize;
12}
13
14SimpleImageParser.prototype = {__proto__: ImageParser.prototype};
15
16/**
17 * @param {File} file File to be parses.
18 * @param {Object} metadata Metadata object of the file.
19 * @param {function(Object)} callback Success callback.
20 * @param {function(string)} errorCallback Error callback.
21 */
22SimpleImageParser.prototype.parse = function(
23    file, metadata, callback, errorCallback) {
24  var self = this;
25  util.readFileBytes(
26      file, 0, this.headerSize,
27      function(file, br) {
28        try {
29          self.parseHeader(metadata, br);
30          callback(metadata);
31        } catch (e) {
32          errorCallback(e.toString());
33        }
34      },
35      errorCallback);
36};
37
38
39function PngParser(parent) {
40  SimpleImageParser.call(this, parent, 'png', /\.png$/i, 24);
41}
42
43PngParser.prototype = {__proto__: SimpleImageParser.prototype};
44
45/**
46 * @param {Object} metadata Metadata object.
47 * @param {ByteReader} br Byte reader to read from.
48 */
49PngParser.prototype.parseHeader = function(metadata, br) {
50  br.setByteOrder(ByteReader.BIG_ENDIAN);
51
52  var signature = br.readString(8);
53  if (signature != '\x89PNG\x0D\x0A\x1A\x0A')
54    throw new Error('Invalid PNG signature: ' + signature);
55
56  br.seek(12);
57  var ihdr = br.readString(4);
58  if (ihdr != 'IHDR')
59    throw new Error('Missing IHDR chunk');
60
61  metadata.width = br.readScalar(4);
62  metadata.height = br.readScalar(4);
63};
64
65MetadataDispatcher.registerParserClass(PngParser);
66
67
68function BmpParser(parent) {
69  SimpleImageParser.call(this, parent, 'bmp', /\.bmp$/i, 28);
70}
71
72BmpParser.prototype = {__proto__: SimpleImageParser.prototype};
73
74/**
75 * @param {Object} metadata Metadata object.
76 * @param {ByteReader} br Byte reader to read from.
77 */
78BmpParser.prototype.parseHeader = function(metadata, br) {
79  br.setByteOrder(ByteReader.LITTLE_ENDIAN);
80
81  var signature = br.readString(2);
82  if (signature != 'BM')
83    throw new Error('Invalid BMP signature: ' + signature);
84
85  br.seek(18);
86  metadata.width = br.readScalar(4);
87  metadata.height = br.readScalar(4);
88};
89
90MetadataDispatcher.registerParserClass(BmpParser);
91
92
93function GifParser(parent) {
94  SimpleImageParser.call(this, parent, 'gif', /\.Gif$/i, 10);
95}
96
97GifParser.prototype = {__proto__: SimpleImageParser.prototype};
98
99/**
100 * @param {Object} metadata Metadata object.
101 * @param {ByteReader} br Byte reader to read from.
102 */
103GifParser.prototype.parseHeader = function(metadata, br) {
104  br.setByteOrder(ByteReader.LITTLE_ENDIAN);
105
106  var signature = br.readString(6);
107  if (!signature.match(/GIF8(7|9)a/))
108    throw new Error('Invalid GIF signature: ' + signature);
109
110  metadata.width = br.readScalar(2);
111  metadata.height = br.readScalar(2);
112};
113
114MetadataDispatcher.registerParserClass(GifParser);
115
116
117function WebpParser(parent) {
118  SimpleImageParser.call(this, parent, 'webp', /\.webp$/i, 30);
119}
120
121WebpParser.prototype = {__proto__: SimpleImageParser.prototype};
122
123/**
124 * @param {Object} metadata Metadata object.
125 * @param {ByteReader} br Byte reader to read from.
126 */
127WebpParser.prototype.parseHeader = function(metadata, br) {
128  br.setByteOrder(ByteReader.LITTLE_ENDIAN);
129
130  var riffSignature = br.readString(4);
131  if (riffSignature != 'RIFF')
132    throw new Error('Invalid RIFF signature: ' + riffSignature);
133
134  br.seek(8);
135  var webpSignature = br.readString(4);
136  if (webpSignature != 'WEBP')
137    throw new Error('Invalid WEBP signature: ' + webpSignature);
138
139  var chunkFormat = br.readString(4);
140  switch (chunkFormat) {
141    // VP8 lossy bitstream format.
142    case 'VP8 ':
143      br.seek(23);
144      var lossySignature = br.readScalar(2) | (br.readScalar(1) << 16);
145      if (lossySignature != 0x2a019d) {
146        throw new Error('Invalid VP8 lossy bitstream signature: ' +
147            lossySignature);
148      }
149      var dimensionBits = br.readScalar(4);
150      metadata.width = dimensionBits & 0x3fff;
151      metadata.height = (dimensionBits >> 16) & 0x3fff;
152      break;
153
154    // VP8 lossless bitstream format.
155    case 'VP8L':
156      br.seek(20);
157      var losslessSignature = br.readScalar(1);
158      if (losslessSignature != 0x2f) {
159        throw new Error('Invalid VP8 lossless bitstream signature: ' +
160            losslessSignature);
161      }
162      var dimensionBits = br.readScalar(4);
163      metadata.width = (dimensionBits & 0x3fff) + 1;
164      metadata.height = ((dimensionBits >> 14) & 0x3fff) + 1;
165      break;
166
167    // VP8 extended file format.
168    case 'VP8X':
169      br.seek(20);
170      // Read 24-bit value. ECMAScript assures left-to-right evaluation order.
171      metadata.width = (br.readScalar(2) | (br.readScalar(1) << 16)) + 1;
172      metadata.height = (br.readScalar(2) | (br.readScalar(1) << 16)) + 1;
173      break;
174
175    default:
176      throw new Error('Invalid chunk format: ' + chunkFormat);
177  }
178};
179
180MetadataDispatcher.registerParserClass(WebpParser);
181
182/**
183 * Parser for the header of .ico icon files.
184 * @param {MetadataDispatcher} parent Parent metadata dispatcher object.
185 * @constructor
186 * @extends {SimpleImageParser}
187 */
188function IcoParser(parent) {
189  SimpleImageParser.call(this, parent, 'ico', /\.ico$/i, 8);
190}
191
192IcoParser.prototype = {__proto__: SimpleImageParser.prototype};
193
194/**
195 * Parse the binary data as a ico header and stores to metadata.
196 * @param {Object} metadata Dictionary to store the parser metadata.
197 * @param {ByteReader} byteReader Reader for header binary data.
198 */
199IcoParser.prototype.parseHeader = function(metadata, byteReader) {
200  byteReader.setByteOrder(ByteReader.LITTLE_ENDIAN);
201
202  var signature = byteReader.readString(4);
203  if (signature !== '\x00\x00\x00\x01')
204    throw new Error('Invalid ICO signature: ' + signature);
205
206  byteReader.seek(2);
207  metadata.width = byteReader.readScalar(1);
208  metadata.height = byteReader.readScalar(1);
209};
210
211MetadataDispatcher.registerParserClass(IcoParser);
212