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