UNPKG

probe-image-size

Version:

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

178 lines (141 loc) 4.44 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_RIFF = str2arr('RIFF') var SIG_WEBP = str2arr('WEBP') function safeSkip (parser, count, callback) { if (count === 0) { // parser._skipBytes throws error if count === 0 callback() return } parser._skipBytes(count, callback) } function parseVP8 (parser, length, sandbox) { parser._bytes(10, function (data) { // check code block signature if (data[3] === 0x9D && data[4] === 0x01 && data[5] === 0x2A) { sandbox.result = sandbox.result || { width: data.readUInt16LE(6) & 0x3FFF, height: data.readUInt16LE(8) & 0x3FFF, type: 'webp', mime: 'image/webp', wUnits: 'px', hUnits: 'px' } } safeSkip(parser, length - 10, function () { sandbox.offset += length getWebpSize(parser, sandbox) }) }) } function parseVP8L (parser, length, sandbox) { parser._bytes(5, function (data) { // check code block signature if (data[0] === 0x2F) { var bits = data.readUInt32LE(1) sandbox.result = sandbox.result || { width: (bits & 0x3FFF) + 1, height: ((bits >> 14) & 0x3FFF) + 1, type: 'webp', mime: 'image/webp', wUnits: 'px', hUnits: 'px' } } safeSkip(parser, length - 5, function () { sandbox.offset += length getWebpSize(parser, sandbox) }) }) } function parseVP8X (parser, length, sandbox) { parser._bytes(10, function (data) { sandbox.result = sandbox.result || { // TODO: replace with `data.readUIntLE(8, 3) + 1` // when 0.10 support is dropped width: ((data[6] << 16) | (data[5] << 8) | data[4]) + 1, height: ((data[9] << 16) | (data[8] << 8) | data[7]) + 1, type: 'webp', mime: 'image/webp', wUnits: 'px', hUnits: 'px' } safeSkip(parser, length - 10, function () { sandbox.offset += length getWebpSize(parser, sandbox) }) }) } function parseExif (parser, length, sandbox) { parser._bytes(length, function (data) { // exif is the last chunk we care about, stop after it sandbox.offset = Infinity sandbox.exif_orientation = exif.get_orientation(data) getWebpSize(parser, sandbox) }) } function getWebpSize (parser, sandbox) { if (sandbox.fileLength - 8 <= sandbox.offset) { parser._skipBytes(Infinity) if (sandbox.result) { var result = sandbox.result if (sandbox.exif_orientation > 0) { result.orientation = sandbox.exif_orientation } parser.push(result) } parser.push(null) return } parser._bytes(4 - sandbox.bufferedChunkHeader.length, function (data) { sandbox.offset += 4 - sandbox.bufferedChunkHeader.length var header = sandbox.bufferedChunkHeader + String.fromCharCode.apply(null, data) // after each chunk of odd size there should be 0 byte of padding, skip those header = header.replace(/^\0+/, '') if (header.length < 4) { sandbox.bufferedChunkHeader = header getWebpSize(parser, sandbox) return } sandbox.bufferedChunkHeader = '' parser._bytes(4, function (data) { sandbox.offset += 4 var length = data.readUInt32LE(0) if (header === 'VP8 ' && length >= 10) { parseVP8(parser, length, sandbox) } else if (header === 'VP8L' && length >= 5) { parseVP8L(parser, length, sandbox) } else if (header === 'VP8X' && length >= 10) { parseVP8X(parser, length, sandbox) } else if (header === 'EXIF' && length >= 4) { parseExif(parser, length, sandbox) } else { safeSkip(parser, length, function () { sandbox.offset += length getWebpSize(parser, sandbox) }) } }) }) } module.exports = function () { var parser = new ParserStream() parser._bytes(12, function (data) { // check /^RIFF....WEBPVP8([ LX])$/ signature if (sliceEq(data, 0, SIG_RIFF) && sliceEq(data, 8, SIG_WEBP)) { getWebpSize(parser, { fileLength: data.readUInt32LE(4) + 8, offset: 12, exif_orientation: 0, bufferedChunkHeader: '' // for dealing with padding }) } else { parser._skipBytes(Infinity) parser.push(null) } }) return parser }