probe-image-size
Version:
Get image size without full download (JPG, GIF, PNG, WebP, BMP, TIFF, PSD)
178 lines (141 loc) • 4.44 kB
JavaScript
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
}