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