@khoohaoyit/image-size
Version:
A stream based image size extractor in Node
80 lines (79 loc) • 4.71 kB
JavaScript
;
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractJpgSize = void 0;
const EXIF_MARKER = 0x45786966;
const APP1_DATA_SIZE_BYTES = 2;
const EXIF_HEADER_BYTES = 6;
const TIFF_BYTE_ALIGN_BYTES = 2;
const BIG_ENDIAN_BYTE_ALIGN = 0x4d4d;
const LITTLE_ENDIAN_BYTE_ALIGN = 0x4949;
// Each entry is exactly 12 bytes
const IDF_ENTRY_BYTES = 12;
const NUM_DIRECTORY_ENTRIES_BYTES = 2;
exports.extractJpgSize = function (stream) {
return __asyncGenerator(this, arguments, function* () {
if ((yield __await(stream.readUInt16BE(0))) !== 0xffd8)
return yield __await(void 0);
let currentOffset = 4, orientation = null;
while (true) {
const blockSize = yield __await(stream.readUInt16BE(currentOffset));
if ((yield __await(stream.readUInt32BE(currentOffset + 2))) === EXIF_MARKER) {
const byteAlignOffset = currentOffset + APP1_DATA_SIZE_BYTES + EXIF_HEADER_BYTES;
const byteAlign = yield __await(stream.readUint16BE(byteAlignOffset));
if (byteAlign === BIG_ENDIAN_BYTE_ALIGN || byteAlign === LITTLE_ENDIAN_BYTE_ALIGN) {
stream.setEndianness(byteAlign === BIG_ENDIAN_BYTE_ALIGN ? 'BE' : 'LE');
// TODO: assert that this contains 0x002A
// let STATIC_MOTOROLA_TIFF_HEADER_BYTES = 2
// let TIFF_IMAGE_FILE_DIRECTORY_BYTES = 4
// TODO: derive from TIFF_IMAGE_FILE_DIRECTORY_BYTES
const idfOffset = 8;
// IDF osset works from right after the header bytes
// (so the offset includes the tiff byte align)
const offset = byteAlignOffset + idfOffset;
const idfDirectoryEntries = yield __await(stream.readUInt16(offset));
let currentOffset = offset + NUM_DIRECTORY_ENTRIES_BYTES;
for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++, currentOffset += IDF_ENTRY_BYTES) {
const tagNumber = yield __await(stream.readUInt16(currentOffset));
// 0x0112 (decimal: 274) is the `orientation` tag ID
if (tagNumber === 274) {
const dataFormat = yield __await(stream.readUInt16(currentOffset + 2));
if (dataFormat !== 3)
break;
// unsinged int has 2 bytes per component
// if there would more than 4 bytes in total it's a pointer
const numberOfComponents = yield __await(stream.readUInt32(currentOffset + 4));
if (numberOfComponents !== 1)
break;
orientation = yield __await(stream.readUInt16(currentOffset + 8));
break;
}
}
stream.setEndianness();
}
}
currentOffset += blockSize;
const type = yield __await(stream.readUInt8(currentOffset + 1));
if ([0xc0, 0xc1, 0xc2].indexOf(type) === -1) {
currentOffset += 2;
continue;
}
return yield __await(yield yield __await({
height: yield __await(stream.readUInt16BE(currentOffset + 5)),
width: yield __await(stream.readUInt16BE(currentOffset + 7)),
orientation,
}));
}
});
};