UNPKG

image-size

Version:
117 lines (114 loc) 3.8 kB
// 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 };