152 lines
3.6 KiB
Plaintext
152 lines
3.6 KiB
Plaintext
|
'use strict';
|
||
|
|
||
|
|
||
|
/* eslint-disable consistent-return */
|
||
|
|
||
|
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 ((0xD0 <= code && code <= 0xD9) || code === 0x01) {
|
||
|
callback(code, 0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// the rest of the unreserved markers
|
||
|
if (0xC0 <= code && 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 &&
|
||
|
(0xC0 <= code && 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;
|
||
|
};
|