@khoohaoyit/image-size
Version:
A stream based image size extractor in Node
216 lines (215 loc) • 10.4 kB
JavaScript
;
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
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.extractSvgSize = void 0;
const buffer_1 = require("buffer");
exports.extractSvgSize = function (stream) {
return __asyncGenerator(this, arguments, function* () {
var _a, e_1, _b, _c, _d, e_2, _e, _f, _g, e_3, _h, _j;
let offset = 0;
const stringIterator = stringParser();
let width, height, viewBox;
loop: while (true) {
// speedup `<svg` search
const peekBuffer = yield __await(stream.stream.peek({ start: offset, end: Infinity }, true));
if (!isBufferPartialUtf8(peekBuffer))
return yield __await(void 0);
const foundSvgStart = peekBuffer.indexOf('<svg');
if (foundSvgStart === -1) {
offset += Math.max(peekBuffer.length - 4, 0);
continue;
}
else
offset += foundSvgStart;
// end of speedup `<svg` search
if ((yield __await(stringIterator.next())).value !== '<'
|| (yield __await(stringIterator.next())).value !== 's'
|| (yield __await(stringIterator.next())).value !== 'v'
|| (yield __await(stringIterator.next())).value !== 'g')
continue;
while (true) {
try {
for (var _k = true, _l = (e_1 = void 0, __asyncValues(stringParser())), _m; _m = yield __await(_l.next()), _a = _m.done, !_a; _k = true) {
_c = _m.value;
_k = false;
const c = _c;
if (/^\s$/.test(c))
continue;
break;
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_k && !_a && (_b = _l.return)) yield __await(_b.call(_l));
}
finally { if (e_1) throw e_1.error; }
}
--offset;
const nameChars = [];
try {
for (var _o = true, _p = (e_2 = void 0, __asyncValues(stringParser())), _q; _q = yield __await(_p.next()), _d = _q.done, !_d; _o = true) {
_f = _q.value;
_o = false;
const c = _f;
if (c === '>')
break loop;
if (c !== '=') {
nameChars.push(c);
continue;
}
const valueChars = [];
const { value: delimiter } = yield __await(stringIterator.next());
try {
for (var _r = true, _s = (e_3 = void 0, __asyncValues(stringParser())), _t; _t = yield __await(_s.next()), _g = _t.done, !_g; _r = true) {
_j = _t.value;
_r = false;
const c = _j;
if (c === delimiter)
break;
valueChars.push(c);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (!_r && !_g && (_h = _s.return)) yield __await(_h.call(_s));
}
finally { if (e_3) throw e_3.error; }
}
const name = nameChars.join('').toLocaleLowerCase();
const value = valueChars.join(''); // TODO: myabe lower case??
switch (name) {
default:
break;
case 'width':
{
width = value;
}
break;
case 'height':
{
height = value;
}
break;
case 'viewbox':
{
viewBox = value;
}
break;
}
if ((width && height && viewBox) !== undefined)
break loop;
break;
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (!_o && !_d && (_e = _p.return)) yield __await(_e.call(_p));
}
finally { if (e_2) throw e_2.error; }
}
}
}
const widthPx = parseLength(width);
const heightPx = parseLength(height);
if (widthPx && heightPx)
return yield __await(yield yield __await({ width: widthPx, height: heightPx }));
if (viewBox) {
const [, , vbWidth, vbHeight] = viewBox.split(' ').map(val => +val);
if (widthPx)
return yield __await(yield yield __await({ width: widthPx, height: Math.floor(widthPx * vbHeight / vbWidth) }));
if (heightPx)
return yield __await(yield yield __await({ width: Math.floor(heightPx * vbWidth / vbHeight), height: heightPx }));
return yield __await(yield yield __await({ width: vbWidth, height: vbHeight }));
}
function stringParser() {
return __asyncGenerator(this, arguments, function* stringParser_1() {
loop: while (true) {
const charBufs = [];
const validIndexes = new Set(utf8formats.map((_, index) => index));
for (let utf8Index = 0;; ++utf8Index) {
const byte = yield __await(stream.read(offset++, 1));
charBufs.push(byte);
for (const formatIndex of validIndexes) {
const utf8format = utf8formats[formatIndex];
const [start, end] = utf8format[utf8Index];
if (byte[0] >= start && byte[0] <= end) {
if (utf8format.length - 1 !== utf8Index)
continue;
yield yield __await(Buffer.concat(charBufs)
.toString('utf8'));
continue loop;
}
validIndexes.delete(formatIndex);
}
if (!validIndexes.size)
return yield __await(void 0);
}
}
});
}
});
};
function isBufferPartialUtf8(buf) {
return (0, buffer_1.isUtf8)(buf)
|| (0, buffer_1.isUtf8)(buf.subarray(0, Math.max(buf.length - 1, 0)))
|| (0, buffer_1.isUtf8)(buf.subarray(0, Math.max(buf.length - 2, 0)))
|| (0, buffer_1.isUtf8)(buf.subarray(0, Math.max(buf.length - 3, 0)));
}
function parseLength(length) {
var _a;
const found = length === null || length === void 0 ? void 0 : length.match(/^([0-9.]+(?:e\d+)?)([a-z]+)?$/);
if (!found)
return;
return Math.round(+found[1] * ((_a = units[found[2]]) !== null && _a !== void 0 ? _a : 1));
}
const INCH_CM = 2.54;
const units = {
in: 96,
cm: 96 / INCH_CM,
em: 16,
ex: 8,
m: (96 / INCH_CM) * 100,
mm: 96 / INCH_CM / 10,
pc: 96 / 72 / 12,
pt: 96 / 72,
px: 1,
};
const utf8tail = [0x80, 0xbf];
// https://github.com/hcodes/isutf8/blob/master/src/index.ts
// https://datatracker.ietf.org/doc/html/rfc3629
const utf8formats = [
// utf8-1
[[0x00, 0x7f]],
// utf8-2
[[0xc2, 0xdf], utf8tail],
// utf8-3
[[0xe0, 0xe0], [0xa0, 0xbf], utf8tail],
[[0xe1, 0xec], utf8tail, utf8tail],
[[0xed, 0xed], [0x80, 0x9f], utf8tail],
[[0xee, 0xef], utf8tail, utf8tail],
// utf8-4
[[0xf0, 0xf0], [0x90, 0xbf], utf8tail, utf8tail],
[[0xf1, 0xf3], utf8tail, utf8tail, utf8tail],
[[0xf4, 0xf4], [0x80, 0x8f], utf8tail, utf8tail],
];