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
141 lines
4.5 KiB
Plaintext
141 lines
4.5 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 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 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);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
};
|
|
|
|
export { TIFF };
|