Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
940 lines
26 KiB
Plaintext
940 lines
26 KiB
Plaintext
// 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 imageSize = getImageSize(input, 0);
|
||
if (nbImages === 1) return imageSize;
|
||
const images = [];
|
||
for (let imageIndex = 0; imageIndex < nbImages; imageIndex += 1) {
|
||
images.push(getImageSize(input, imageIndex));
|
||
}
|
||
return {
|
||
width: imageSize.width,
|
||
height: imageSize.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 imageSize = getImageSize2(imageHeader[0]);
|
||
images.push(imageSize);
|
||
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());
|
||
|
||
export { typeHandlers, types };
|