UNPKG

probe-image-size

Version:

Get image size without full download (JPG, GIF, PNG, WebP, BMP, TIFF, PSD)

149 lines (118 loc) 3.56 kB
'use strict' var ParserStream = require('../common').ParserStream var str2arr = require('../common').str2arr var sliceEq = require('../common').sliceEq var exif = require('../exif_utils') var SIG_EXIF = str2arr('Exif\0\0') // part of parseJpegMarker called after skipping initial FF function parseJpegMarker_afterFF (parser, callback) { parser._bytes(1, function (data) { var code = data[0] if (code === 0xFF) { // padding byte, skip it parseJpegMarker_afterFF(parser, callback) return } // standalone markers, according to JPEG 1992, // http://www.w3.org/Graphics/JPEG/itu-t81.pdf, see Table B.1 if ((code >= 0xD0 && code <= 0xD9) || code === 0x01) { callback(code, 0) return } // the rest of the unreserved markers if (code >= 0xC0 && code <= 0xFE) { parser._bytes(2, function (length) { callback(code, length.readUInt16BE(0) - 2) }) return } // unknown markers callback() }) } function parseJpegMarker (parser, sandbox, callback) { var start = sandbox.start sandbox.start = false parser._bytes(1, function (data) { if (data[0] !== 0xFF) { // not a JPEG marker if (start) { // expect JPEG file to start with `FFD8 FFE0`, `FFD8 FFE2` or `FFD8 FFE1`, // don't allow garbage before second marker callback() } else { // skip until we see 0xFF, see https://github.com/nodeca/probe-image-size/issues/68 parseJpegMarker(parser, sandbox, callback) } return } parseJpegMarker_afterFF(parser, callback) }) } // sandbox is a storage for intermediate data retrieved from jpeg while parsing it function getJpegSize (parser, sandbox) { parseJpegMarker(parser, sandbox, function (code, length) { if (!code || length < 0) { // invalid jpeg parser._skipBytes(Infinity) parser.push(null) return } if (code === 0xD9 /* EOI */ || code === 0xDA /* SOS */) { // end of the datastream parser._skipBytes(Infinity) parser.push(null) return } // try to get orientation from Exif segment if (code === 0xE1 && length >= 10) { parser._bytes(length, function (data) { if (sliceEq(data, 0, SIG_EXIF)) { sandbox.orientation = exif.get_orientation(data.slice(6, 6 + length)) } getJpegSize(parser, sandbox) }) return } if (length <= 0) { // e.g. empty comment getJpegSize(parser, sandbox) return } if (length >= 5 && (code >= 0xC0 && code <= 0xCF) && code !== 0xC4 && code !== 0xC8 && code !== 0xCC) { parser._bytes(length, function (data) { parser._skipBytes(Infinity) var result = { width: data.readUInt16BE(3), height: data.readUInt16BE(1), type: 'jpg', mime: 'image/jpeg', wUnits: 'px', hUnits: 'px' } if (sandbox.orientation > 0) result.orientation = sandbox.orientation parser.push(result) parser.push(null) }) return } parser._skipBytes(length, function () { getJpegSize(parser, sandbox) }) }) } module.exports = function () { var parser = new ParserStream() parser._bytes(2, function (data) { if (data[0] !== 0xFF || data[1] !== 0xD8) { // first marker of the file MUST be 0xFFD8 parser._skipBytes(Infinity) parser.push(null) return } getJpegSize(parser, { start: true }) }) return parser }