image-size
Version:
get dimensions of any image file
117 lines (114 loc) • 3.8 kB
JavaScript
// lib/types/utils.ts
new TextDecoder();
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 readUInt16BE = (input, offset = 0) => getView(input, offset).getUint16(0, false);
var readUInt16LE = (input, offset = 0) => getView(input, offset).getUint16(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 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);
}
// 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");
}
};
export { JPG };