image-size
Version:
get dimensions of any image file
999 lines (971 loc) • 28 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
// lib/types/utils.ts
var decoder = new TextDecoder();
var toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
var toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + `0${i.toString(16)}`.slice(-2), "");
var getView = (input, offset) => new DataView(input.buffer, input.byteOffset + offset);
var readInt16LE = (input, offset = 0) => getView(input, offset).getInt16(0, true);
var readUInt16BE = (input, offset = 0) => getView(input, offset).getUint16(0, false);
var readUInt16LE = (input, offset = 0) => getView(input, offset).getUint16(0, true);
var readUInt24LE = (input, offset = 0) => {
const view = getView(input, offset);
return view.getUint16(0, true) + (view.getUint8(2) << 16);
};
var readInt32LE = (input, offset = 0) => getView(input, offset).getInt32(0, true);
var readUInt32BE = (input, offset = 0) => getView(input, offset).getUint32(0, false);
var readUInt32LE = (input, offset = 0) => getView(input, offset).getUint32(0, true);
var readUInt64 = (input, offset, isBigEndian) => getView(input, offset).getBigUint64(0, !isBigEndian);
var methods = {
readUInt16BE,
readUInt16LE,
readUInt32BE,
readUInt32LE
};
function readUInt(input, bits, offset = 0, isBigEndian = false) {
const endian = isBigEndian ? "BE" : "LE";
const methodName = `readUInt${bits}${endian}`;
return methods[methodName](input, offset);
}
function readBox(input, offset) {
if (input.length - offset < 4) return;
const boxSize = readUInt32BE(input, offset);
if (input.length - offset < boxSize) return;
return {
name: toUTF8String(input, 4 + offset, 8 + offset),
offset,
size: boxSize
};
}
function findBox(input, boxName, currentOffset) {
while (currentOffset < input.length) {
const box = readBox(input, currentOffset);
if (!box) break;
if (box.name === boxName) return box;
currentOffset += box.size > 0 ? box.size : 8;
}
}
// lib/types/bmp.ts
var BMP = {
validate: (input) => toUTF8String(input, 0, 2) === "BM",
calculate: (input) => ({
height: Math.abs(readInt32LE(input, 22)),
width: readUInt32LE(input, 18)
})
};
// lib/types/ico.ts
var TYPE_ICON = 1;
var SIZE_HEADER = 2 + 2 + 2;
var SIZE_IMAGE_ENTRY = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4;
function getSizeFromOffset(input, offset) {
const value = input[offset];
return value === 0 ? 256 : value;
}
function getImageSize(input, imageIndex) {
const offset = SIZE_HEADER + imageIndex * SIZE_IMAGE_ENTRY;
return {
height: getSizeFromOffset(input, offset + 1),
width: getSizeFromOffset(input, offset)
};
}
var ICO = {
validate(input) {
const reserved = readUInt16LE(input, 0);
const imageCount = readUInt16LE(input, 4);
if (reserved !== 0 || imageCount === 0) return false;
const imageType = readUInt16LE(input, 2);
return imageType === TYPE_ICON;
},
calculate(input) {
const nbImages = readUInt16LE(input, 4);
const imageSize2 = getImageSize(input, 0);
if (nbImages === 1) return imageSize2;
const images = [];
for (let imageIndex = 0; imageIndex < nbImages; imageIndex += 1) {
images.push(getImageSize(input, imageIndex));
}
return {
width: imageSize2.width,
height: imageSize2.height,
images
};
}
};
// lib/types/cur.ts
var TYPE_CURSOR = 2;
var CUR = {
validate(input) {
const reserved = readUInt16LE(input, 0);
const imageCount = readUInt16LE(input, 4);
if (reserved !== 0 || imageCount === 0) return false;
const imageType = readUInt16LE(input, 2);
return imageType === TYPE_CURSOR;
},
calculate: (input) => ICO.calculate(input)
};
// lib/types/dds.ts
var DDS = {
validate: (input) => readUInt32LE(input, 0) === 542327876,
calculate: (input) => ({
height: readUInt32LE(input, 12),
width: readUInt32LE(input, 16)
})
};
// lib/types/gif.ts
var gifRegexp = /^GIF8[79]a/;
var GIF = {
validate: (input) => gifRegexp.test(toUTF8String(input, 0, 6)),
calculate: (input) => ({
height: readUInt16LE(input, 8),
width: readUInt16LE(input, 6)
})
};
// lib/types/heif.ts
var brandMap = {
avif: "avif",
mif1: "heif",
msf1: "heif",
// heif-sequence
heic: "heic",
heix: "heic",
hevc: "heic",
// heic-sequence
hevx: "heic"
// heic-sequence
};
var HEIF = {
validate(input) {
const boxType = toUTF8String(input, 4, 8);
if (boxType !== "ftyp") return false;
const ftypBox = findBox(input, "ftyp", 0);
if (!ftypBox) return false;
const brand = toUTF8String(input, ftypBox.offset + 8, ftypBox.offset + 12);
return brand in brandMap;
},
calculate(input) {
const metaBox = findBox(input, "meta", 0);
const iprpBox = metaBox && findBox(input, "iprp", metaBox.offset + 12);
const ipcoBox = iprpBox && findBox(input, "ipco", iprpBox.offset + 8);
if (!ipcoBox) {
throw new TypeError("Invalid HEIF, no ipco box found");
}
const type = toUTF8String(input, 8, 12);
const images = [];
let currentOffset = ipcoBox.offset + 8;
while (currentOffset < ipcoBox.offset + ipcoBox.size) {
const ispeBox = findBox(input, "ispe", currentOffset);
if (!ispeBox) break;
const rawWidth = readUInt32BE(input, ispeBox.offset + 12);
const rawHeight = readUInt32BE(input, ispeBox.offset + 16);
const clapBox = findBox(input, "clap", currentOffset);
let width = rawWidth;
let height = rawHeight;
if (clapBox && clapBox.offset < ipcoBox.offset + ipcoBox.size) {
const cropRight = readUInt32BE(input, clapBox.offset + 12);
width = rawWidth - cropRight;
}
images.push({ height, width });
currentOffset = ispeBox.offset + ispeBox.size;
}
if (images.length === 0) {
throw new TypeError("Invalid HEIF, no sizes found");
}
return {
width: images[0].width,
height: images[0].height,
type,
...images.length > 1 ? { images } : {}
};
}
};
// lib/types/icns.ts
var SIZE_HEADER2 = 4 + 4;
var FILE_LENGTH_OFFSET = 4;
var ENTRY_LENGTH_OFFSET = 4;
var ICON_TYPE_SIZE = {
ICON: 32,
"ICN#": 32,
// m => 16 x 16
"icm#": 16,
icm4: 16,
icm8: 16,
// s => 16 x 16
"ics#": 16,
ics4: 16,
ics8: 16,
is32: 16,
s8mk: 16,
icp4: 16,
// l => 32 x 32
icl4: 32,
icl8: 32,
il32: 32,
l8mk: 32,
icp5: 32,
ic11: 32,
// h => 48 x 48
ich4: 48,
ich8: 48,
ih32: 48,
h8mk: 48,
// . => 64 x 64
icp6: 64,
ic12: 32,
// t => 128 x 128
it32: 128,
t8mk: 128,
ic07: 128,
// . => 256 x 256
ic08: 256,
ic13: 256,
// . => 512 x 512
ic09: 512,
ic14: 512,
// . => 1024 x 1024
ic10: 1024
};
function readImageHeader(input, imageOffset) {
const imageLengthOffset = imageOffset + ENTRY_LENGTH_OFFSET;
return [
toUTF8String(input, imageOffset, imageLengthOffset),
readUInt32BE(input, imageLengthOffset)
];
}
function getImageSize2(type) {
const size = ICON_TYPE_SIZE[type];
return { width: size, height: size, type };
}
var ICNS = {
validate: (input) => toUTF8String(input, 0, 4) === "icns",
calculate(input) {
const inputLength = input.length;
const fileLength = readUInt32BE(input, FILE_LENGTH_OFFSET);
let imageOffset = SIZE_HEADER2;
const images = [];
while (imageOffset < fileLength && imageOffset < inputLength) {
const imageHeader = readImageHeader(input, imageOffset);
const imageSize2 = getImageSize2(imageHeader[0]);
images.push(imageSize2);
imageOffset += imageHeader[1];
}
if (images.length === 0) {
throw new TypeError("Invalid ICNS, no sizes found");
}
return {
width: images[0].width,
height: images[0].height,
...images.length > 1 ? { images } : {}
};
}
};
// lib/types/j2c.ts
var J2C = {
// TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC
validate: (input) => readUInt32BE(input, 0) === 4283432785,
calculate: (input) => ({
height: readUInt32BE(input, 12),
width: readUInt32BE(input, 8)
})
};
// lib/types/jp2.ts
var JP2 = {
validate(input) {
const boxType = toUTF8String(input, 4, 8);
if (boxType !== "jP ") return false;
const ftypBox = findBox(input, "ftyp", 0);
if (!ftypBox) return false;
const brand = toUTF8String(input, ftypBox.offset + 8, ftypBox.offset + 12);
return brand === "jp2 ";
},
calculate(input) {
const jp2hBox = findBox(input, "jp2h", 0);
const ihdrBox = jp2hBox && findBox(input, "ihdr", jp2hBox.offset + 8);
if (ihdrBox) {
return {
height: readUInt32BE(input, ihdrBox.offset + 8),
width: readUInt32BE(input, ihdrBox.offset + 12)
};
}
throw new TypeError("Unsupported JPEG 2000 format");
}
};
// lib/types/jpg.ts
var EXIF_MARKER = "45786966";
var APP1_DATA_SIZE_BYTES = 2;
var EXIF_HEADER_BYTES = 6;
var TIFF_BYTE_ALIGN_BYTES = 2;
var BIG_ENDIAN_BYTE_ALIGN = "4d4d";
var LITTLE_ENDIAN_BYTE_ALIGN = "4949";
var IDF_ENTRY_BYTES = 12;
var NUM_DIRECTORY_ENTRIES_BYTES = 2;
function isEXIF(input) {
return toHexString(input, 2, 6) === EXIF_MARKER;
}
function extractSize(input, index) {
return {
height: readUInt16BE(input, index),
width: readUInt16BE(input, index + 2)
};
}
function extractOrientation(exifBlock, isBigEndian) {
const idfOffset = 8;
const offset = EXIF_HEADER_BYTES + idfOffset;
const idfDirectoryEntries = readUInt(exifBlock, 16, offset, isBigEndian);
for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++) {
const start = offset + NUM_DIRECTORY_ENTRIES_BYTES + directoryEntryNumber * IDF_ENTRY_BYTES;
const end = start + IDF_ENTRY_BYTES;
if (start > exifBlock.length) {
return;
}
const block = exifBlock.slice(start, end);
const tagNumber = readUInt(block, 16, 0, isBigEndian);
if (tagNumber === 274) {
const dataFormat = readUInt(block, 16, 2, isBigEndian);
if (dataFormat !== 3) {
return;
}
const numberOfComponents = readUInt(block, 32, 4, isBigEndian);
if (numberOfComponents !== 1) {
return;
}
return readUInt(block, 16, 8, isBigEndian);
}
}
}
function validateExifBlock(input, index) {
const exifBlock = input.slice(APP1_DATA_SIZE_BYTES, index);
const byteAlign = toHexString(
exifBlock,
EXIF_HEADER_BYTES,
EXIF_HEADER_BYTES + TIFF_BYTE_ALIGN_BYTES
);
const isBigEndian = byteAlign === BIG_ENDIAN_BYTE_ALIGN;
const isLittleEndian = byteAlign === LITTLE_ENDIAN_BYTE_ALIGN;
if (isBigEndian || isLittleEndian) {
return extractOrientation(exifBlock, isBigEndian);
}
}
function validateInput(input, index) {
if (index > input.length) {
throw new TypeError("Corrupt JPG, exceeded buffer limits");
}
}
var JPG = {
validate: (input) => toHexString(input, 0, 2) === "ffd8",
calculate(_input) {
let input = _input.slice(4);
let orientation;
let next;
while (input.length) {
const i = readUInt16BE(input, 0);
validateInput(input, i);
if (input[i] !== 255) {
input = input.slice(1);
continue;
}
if (isEXIF(input)) {
orientation = validateExifBlock(input, i);
}
next = input[i + 1];
if (next === 192 || next === 193 || next === 194) {
const size = extractSize(input, i + 5);
if (!orientation) {
return size;
}
return {
height: size.height,
orientation,
width: size.width
};
}
input = input.slice(i + 2);
}
throw new TypeError("Invalid JPG, no size found");
}
};
// lib/utils/bit-reader.ts
var BitReader = class {
constructor(input, endianness) {
this.input = input;
this.endianness = endianness;
// Skip the first 16 bits (2 bytes) of signature
this.byteOffset = 2;
this.bitOffset = 0;
}
/** Reads a specified number of bits, and move the offset */
getBits(length = 1) {
let result = 0;
let bitsRead = 0;
while (bitsRead < length) {
if (this.byteOffset >= this.input.length) {
throw new Error("Reached end of input");
}
const currentByte = this.input[this.byteOffset];
const bitsLeft = 8 - this.bitOffset;
const bitsToRead = Math.min(length - bitsRead, bitsLeft);
if (this.endianness === "little-endian") {
const mask = (1 << bitsToRead) - 1;
const bits = currentByte >> this.bitOffset & mask;
result |= bits << bitsRead;
} else {
const mask = (1 << bitsToRead) - 1 << 8 - this.bitOffset - bitsToRead;
const bits = (currentByte & mask) >> 8 - this.bitOffset - bitsToRead;
result = result << bitsToRead | bits;
}
bitsRead += bitsToRead;
this.bitOffset += bitsToRead;
if (this.bitOffset === 8) {
this.byteOffset++;
this.bitOffset = 0;
}
}
return result;
}
};
// lib/types/jxl-stream.ts
function calculateImageDimension(reader, isSmallImage) {
if (isSmallImage) {
return 8 * (1 + reader.getBits(5));
}
const sizeClass = reader.getBits(2);
const extraBits = [9, 13, 18, 30][sizeClass];
return 1 + reader.getBits(extraBits);
}
function calculateImageWidth(reader, isSmallImage, widthMode, height) {
if (isSmallImage && widthMode === 0) {
return 8 * (1 + reader.getBits(5));
}
if (widthMode === 0) {
return calculateImageDimension(reader, false);
}
const aspectRatios = [1, 1.2, 4 / 3, 1.5, 16 / 9, 5 / 4, 2];
return Math.floor(height * aspectRatios[widthMode - 1]);
}
var JXLStream = {
validate: (input) => {
return toHexString(input, 0, 2) === "ff0a";
},
calculate(input) {
const reader = new BitReader(input, "little-endian");
const isSmallImage = reader.getBits(1) === 1;
const height = calculateImageDimension(reader, isSmallImage);
const widthMode = reader.getBits(3);
const width = calculateImageWidth(reader, isSmallImage, widthMode, height);
return { width, height };
}
};
// lib/types/jxl.ts
function extractCodestream(input) {
const jxlcBox = findBox(input, "jxlc", 0);
if (jxlcBox) {
return input.slice(jxlcBox.offset + 8, jxlcBox.offset + jxlcBox.size);
}
const partialStreams = extractPartialStreams(input);
if (partialStreams.length > 0) {
return concatenateCodestreams(partialStreams);
}
return void 0;
}
function extractPartialStreams(input) {
const partialStreams = [];
let offset = 0;
while (offset < input.length) {
const jxlpBox = findBox(input, "jxlp", offset);
if (!jxlpBox) break;
partialStreams.push(
input.slice(jxlpBox.offset + 12, jxlpBox.offset + jxlpBox.size)
);
offset = jxlpBox.offset + jxlpBox.size;
}
return partialStreams;
}
function concatenateCodestreams(partialCodestreams) {
const totalLength = partialCodestreams.reduce(
(acc, curr) => acc + curr.length,
0
);
const codestream = new Uint8Array(totalLength);
let position = 0;
for (const partial of partialCodestreams) {
codestream.set(partial, position);
position += partial.length;
}
return codestream;
}
var JXL = {
validate: (input) => {
const boxType = toUTF8String(input, 4, 8);
if (boxType !== "JXL ") return false;
const ftypBox = findBox(input, "ftyp", 0);
if (!ftypBox) return false;
const brand = toUTF8String(input, ftypBox.offset + 8, ftypBox.offset + 12);
return brand === "jxl ";
},
calculate(input) {
const codestream = extractCodestream(input);
if (codestream) return JXLStream.calculate(codestream);
throw new Error("No codestream found in JXL container");
}
};
// lib/types/ktx.ts
var KTX = {
validate: (input) => {
const signature = toUTF8String(input, 1, 7);
return ["KTX 11", "KTX 20"].includes(signature);
},
calculate: (input) => {
const type = input[5] === 49 ? "ktx" : "ktx2";
const offset = type === "ktx" ? 36 : 20;
return {
height: readUInt32LE(input, offset + 4),
width: readUInt32LE(input, offset),
type
};
}
};
// lib/types/png.ts
var pngSignature = "PNG\r\n\n";
var pngImageHeaderChunkName = "IHDR";
var pngFriedChunkName = "CgBI";
var PNG = {
validate(input) {
if (pngSignature === toUTF8String(input, 1, 8)) {
let chunkName = toUTF8String(input, 12, 16);
if (chunkName === pngFriedChunkName) {
chunkName = toUTF8String(input, 28, 32);
}
if (chunkName !== pngImageHeaderChunkName) {
throw new TypeError("Invalid PNG");
}
return true;
}
return false;
},
calculate(input) {
if (toUTF8String(input, 12, 16) === pngFriedChunkName) {
return {
height: readUInt32BE(input, 36),
width: readUInt32BE(input, 32)
};
}
return {
height: readUInt32BE(input, 20),
width: readUInt32BE(input, 16)
};
}
};
// lib/types/pnm.ts
var PNMTypes = {
P1: "pbm/ascii",
P2: "pgm/ascii",
P3: "ppm/ascii",
P4: "pbm",
P5: "pgm",
P6: "ppm",
P7: "pam",
PF: "pfm"
};
var handlers = {
default: (lines) => {
let dimensions = [];
while (lines.length > 0) {
const line = lines.shift();
if (line[0] === "#") {
continue;
}
dimensions = line.split(" ");
break;
}
if (dimensions.length === 2) {
return {
height: Number.parseInt(dimensions[1], 10),
width: Number.parseInt(dimensions[0], 10)
};
}
throw new TypeError("Invalid PNM");
},
pam: (lines) => {
const size = {};
while (lines.length > 0) {
const line = lines.shift();
if (line.length > 16 || line.charCodeAt(0) > 128) {
continue;
}
const [key, value] = line.split(" ");
if (key && value) {
size[key.toLowerCase()] = Number.parseInt(value, 10);
}
if (size.height && size.width) {
break;
}
}
if (size.height && size.width) {
return {
height: size.height,
width: size.width
};
}
throw new TypeError("Invalid PAM");
}
};
var PNM = {
validate: (input) => toUTF8String(input, 0, 2) in PNMTypes,
calculate(input) {
const signature = toUTF8String(input, 0, 2);
const type = PNMTypes[signature];
const lines = toUTF8String(input, 3).split(/[\r\n]+/);
const handler = handlers[type] || handlers.default;
return handler(lines);
}
};
// lib/types/psd.ts
var PSD = {
validate: (input) => toUTF8String(input, 0, 4) === "8BPS",
calculate: (input) => ({
height: readUInt32BE(input, 14),
width: readUInt32BE(input, 18)
})
};
// lib/types/svg.ts
var svgReg = /<svg\s([^>"']|"[^"]*"|'[^']*')*>/;
var extractorRegExps = {
height: /\sheight=(['"])([^%]+?)\1/,
root: svgReg,
viewbox: /\sviewBox=(['"])(.+?)\1/i,
width: /\swidth=(['"])([^%]+?)\1/
};
var INCH_CM = 2.54;
var 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
};
var unitsReg = new RegExp(
`^([0-9.]+(?:e\\d+)?)(${Object.keys(units).join("|")})?$`
);
function parseLength(len) {
const m = unitsReg.exec(len);
if (!m) {
return void 0;
}
return Math.round(Number(m[1]) * (units[m[2]] || 1));
}
function parseViewbox(viewbox) {
const bounds = viewbox.split(" ");
return {
height: parseLength(bounds[3]),
width: parseLength(bounds[2])
};
}
function parseAttributes(root) {
const width = root.match(extractorRegExps.width);
const height = root.match(extractorRegExps.height);
const viewbox = root.match(extractorRegExps.viewbox);
return {
height: height && parseLength(height[2]),
viewbox: viewbox && parseViewbox(viewbox[2]),
width: width && parseLength(width[2])
};
}
function calculateByDimensions(attrs) {
return {
height: attrs.height,
width: attrs.width
};
}
function calculateByViewbox(attrs, viewbox) {
const ratio = viewbox.width / viewbox.height;
if (attrs.width) {
return {
height: Math.floor(attrs.width / ratio),
width: attrs.width
};
}
if (attrs.height) {
return {
height: attrs.height,
width: Math.floor(attrs.height * ratio)
};
}
return {
height: viewbox.height,
width: viewbox.width
};
}
var SVG = {
// Scan only the first kilo-byte to speed up the check on larger files
validate: (input) => svgReg.test(toUTF8String(input, 0, 1e3)),
calculate(input) {
const root = toUTF8String(input).match(extractorRegExps.root);
if (root) {
const attrs = parseAttributes(root[0]);
if (attrs.width && attrs.height) {
return calculateByDimensions(attrs);
}
if (attrs.viewbox) {
return calculateByViewbox(attrs, attrs.viewbox);
}
}
throw new TypeError("Invalid SVG");
}
};
// lib/types/tga.ts
var TGA = {
validate(input) {
return readUInt16LE(input, 0) === 0 && readUInt16LE(input, 4) === 0;
},
calculate(input) {
return {
height: readUInt16LE(input, 14),
width: readUInt16LE(input, 12)
};
}
};
// lib/types/tiff.ts
var CONSTANTS = {
TAG: {
WIDTH: 256,
HEIGHT: 257,
COMPRESSION: 259
},
TYPE: {
SHORT: 3,
LONG: 4,
LONG8: 16
},
ENTRY_SIZE: {
STANDARD: 12,
BIG: 20
},
COUNT_SIZE: {
STANDARD: 2,
BIG: 8
}
};
function readIFD(input, { isBigEndian, isBigTiff }) {
const ifdOffset = isBigTiff ? Number(readUInt64(input, 8, isBigEndian)) : readUInt(input, 32, 4, isBigEndian);
const entryCountSize = isBigTiff ? CONSTANTS.COUNT_SIZE.BIG : CONSTANTS.COUNT_SIZE.STANDARD;
return input.slice(ifdOffset + entryCountSize);
}
function readTagValue(input, type, offset, isBigEndian) {
switch (type) {
case CONSTANTS.TYPE.SHORT:
return readUInt(input, 16, offset, isBigEndian);
case CONSTANTS.TYPE.LONG:
return readUInt(input, 32, offset, isBigEndian);
case CONSTANTS.TYPE.LONG8: {
const value = Number(readUInt64(input, offset, isBigEndian));
if (value > Number.MAX_SAFE_INTEGER) {
throw new TypeError("Value too large");
}
return value;
}
default:
return 0;
}
}
function nextTag(input, isBigTiff) {
const entrySize = isBigTiff ? CONSTANTS.ENTRY_SIZE.BIG : CONSTANTS.ENTRY_SIZE.STANDARD;
if (input.length > entrySize) {
return input.slice(entrySize);
}
}
function extractTags(input, { isBigEndian, isBigTiff }) {
const tags = {};
let temp = input;
while (temp?.length) {
const code = readUInt(temp, 16, 0, isBigEndian);
const type = readUInt(temp, 16, 2, isBigEndian);
const length = isBigTiff ? Number(readUInt64(temp, 4, isBigEndian)) : readUInt(temp, 32, 4, isBigEndian);
if (code === 0) break;
if (length === 1 && (type === CONSTANTS.TYPE.SHORT || type === CONSTANTS.TYPE.LONG || isBigTiff && type === CONSTANTS.TYPE.LONG8)) {
const valueOffset = isBigTiff ? 12 : 8;
tags[code] = readTagValue(temp, type, valueOffset, isBigEndian);
}
temp = nextTag(temp, isBigTiff);
}
return tags;
}
function determineFormat(input) {
const signature = toUTF8String(input, 0, 2);
const version = readUInt(input, 16, 2, signature === "MM");
return {
isBigEndian: signature === "MM",
isBigTiff: version === 43
};
}
function validateBigTIFFHeader(input, isBigEndian) {
const byteSize = readUInt(input, 16, 4, isBigEndian);
const reserved = readUInt(input, 16, 6, isBigEndian);
if (byteSize !== 8 || reserved !== 0) {
throw new TypeError("Invalid BigTIFF header");
}
}
var signatures = /* @__PURE__ */ new Set([
"49492a00",
// Little Endian
"4d4d002a",
// Big Endian
"49492b00",
// BigTIFF Little Endian
"4d4d002b"
// BigTIFF Big Endian
]);
var TIFF = {
validate: (input) => {
const signature = toHexString(input, 0, 4);
return signatures.has(signature);
},
calculate(input) {
const format = determineFormat(input);
if (format.isBigTiff) {
validateBigTIFFHeader(input, format.isBigEndian);
}
const ifdBuffer = readIFD(input, format);
const tags = extractTags(ifdBuffer, format);
const info = {
height: tags[CONSTANTS.TAG.HEIGHT],
width: tags[CONSTANTS.TAG.WIDTH],
type: format.isBigTiff ? "bigtiff" : "tiff"
};
if (tags[CONSTANTS.TAG.COMPRESSION]) {
info.compression = tags[CONSTANTS.TAG.COMPRESSION];
}
if (!info.width || !info.height) {
throw new TypeError("Invalid Tiff. Missing tags");
}
return info;
}
};
// lib/types/webp.ts
function calculateExtended(input) {
return {
height: 1 + readUInt24LE(input, 7),
width: 1 + readUInt24LE(input, 4)
};
}
function calculateLossless(input) {
return {
height: 1 + ((input[4] & 15) << 10 | input[3] << 2 | (input[2] & 192) >> 6),
width: 1 + ((input[2] & 63) << 8 | input[1])
};
}
function calculateLossy(input) {
return {
height: readInt16LE(input, 8) & 16383,
width: readInt16LE(input, 6) & 16383
};
}
var WEBP = {
validate(input) {
const riffHeader = "RIFF" === toUTF8String(input, 0, 4);
const webpHeader = "WEBP" === toUTF8String(input, 8, 12);
const vp8Header = "VP8" === toUTF8String(input, 12, 15);
return riffHeader && webpHeader && vp8Header;
},
calculate(_input) {
const chunkHeader = toUTF8String(_input, 12, 16);
const input = _input.slice(20, 30);
if (chunkHeader === "VP8X") {
const extendedHeader = input[0];
const validStart = (extendedHeader & 192) === 0;
const validEnd = (extendedHeader & 1) === 0;
if (validStart && validEnd) {
return calculateExtended(input);
}
throw new TypeError("Invalid WebP");
}
if (chunkHeader === "VP8 " && input[0] !== 47) {
return calculateLossy(input);
}
const signature = toHexString(input, 3, 6);
if (chunkHeader === "VP8L" && signature !== "9d012a") {
return calculateLossless(input);
}
throw new TypeError("Invalid WebP");
}
};
// lib/types/index.ts
var typeHandlers = /* @__PURE__ */ new Map([
["bmp", BMP],
["cur", CUR],
["dds", DDS],
["gif", GIF],
["heif", HEIF],
["icns", ICNS],
["ico", ICO],
["j2c", J2C],
["jp2", JP2],
["jpg", JPG],
["jxl", JXL],
["jxl-stream", JXLStream],
["ktx", KTX],
["png", PNG],
["pnm", PNM],
["psd", PSD],
["svg", SVG],
["tga", TGA],
["tiff", TIFF],
["webp", WEBP]
]);
var types = Array.from(typeHandlers.keys());
// lib/detector.ts
var firstBytes = /* @__PURE__ */ new Map([
[0, "heif"],
[56, "psd"],
[66, "bmp"],
[68, "dds"],
[71, "gif"],
[73, "tiff"],
[77, "tiff"],
[82, "webp"],
[105, "icns"],
[137, "png"],
[255, "jpg"]
]);
function detector(input) {
const byte = input[0];
const type = firstBytes.get(byte);
if (type && typeHandlers.get(type).validate(input)) {
return type;
}
return types.find((type2) => typeHandlers.get(type2).validate(input));
}
// lib/lookup.ts
var globalOptions = {
disabledTypes: []
};
function imageSize(input) {
const type = detector(input);
if (typeof type !== "undefined") {
if (globalOptions.disabledTypes.indexOf(type) > -1) {
throw new TypeError(`disabled file type: ${type}`);
}
const size = typeHandlers.get(type).calculate(input);
if (size !== void 0) {
size.type = size.type ?? type;
if (size.images && size.images.length > 1) {
const largestImage = size.images.reduce((largest, current) => {
return current.width * current.height > largest.width * largest.height ? current : largest;
}, size.images[0]);
size.width = largestImage.width;
size.height = largestImage.height;
}
return size;
}
}
throw new TypeError(`unsupported file type: ${type}`);
}
var disableTypes = (types2) => {
globalOptions.disabledTypes = types2;
};
exports.default = imageSize;
exports.disableTypes = disableTypes;
exports.imageSize = imageSize;
exports.types = types;