Website Structure
This commit is contained in:
parent
62812f2090
commit
71f0676a62
22365 changed files with 4265753 additions and 791 deletions
24
Frontend-Learner/node_modules/image-meta/LICENSE
generated
vendored
Normal file
24
Frontend-Learner/node_modules/image-meta/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Pooya Parsa <pooya@pi0.io>
|
||||
|
||||
Based on https://github.com/image-size/image-size
|
||||
Copyright © Aditya Yadav, http://netroy.in
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
64
Frontend-Learner/node_modules/image-meta/README.md
generated
vendored
Normal file
64
Frontend-Learner/node_modules/image-meta/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# image-meta
|
||||
|
||||
[![npm version][npm-version-src]][npm-version-href]
|
||||
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
||||
[![bundle][bundle-src]][bundle-href]
|
||||
[![Codecov][codecov-src]][codecov-href]
|
||||
|
||||
Detect image type and size using pure javascript.
|
||||
|
||||
## Usage
|
||||
|
||||
Install package:
|
||||
|
||||
```sh
|
||||
# npm
|
||||
npm install image-meta
|
||||
|
||||
# yarn
|
||||
yarn add image-meta
|
||||
|
||||
# pnpm
|
||||
pnpm install image-meta
|
||||
|
||||
# bun
|
||||
bun install image-meta
|
||||
```
|
||||
|
||||
```ts
|
||||
import { imageMeta } from "image-meta";
|
||||
|
||||
const data = await fetch(url).then((res) => res.buffer());
|
||||
|
||||
// Meta contains { type, width?, height?, orientation? }
|
||||
const meta = imageMeta(data);
|
||||
```
|
||||
|
||||
**Note:** `imageMeta` throws an error if either data is not a `Buffer`/`Uint8Array`, or data is invalid or type cannot be determined. You should wrap it into a `try/catch` statement to handle errors.
|
||||
|
||||
## Development
|
||||
|
||||
- Clone this repository
|
||||
- Install the latest LTS version of [Node.js](https://nodejs.org/en/)
|
||||
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
|
||||
- Install dependencies using `pnpm install`
|
||||
- Run interactive tests using `pnpm dev`
|
||||
|
||||
## License
|
||||
|
||||
Made with 💛
|
||||
|
||||
🔀 Based on [image-size](https://github.com/image-size/image-size) by [Aditya Yadav](https://github.com/netroy) and [contributors](https://github.com/image-size/image-size/graphs/contributors).
|
||||
|
||||
Published under [MIT License](./LICENSE).
|
||||
|
||||
<!-- Badges -->
|
||||
|
||||
[npm-version-src]: https://img.shields.io/npm/v/image-meta?style=flat&colorA=18181B&colorB=F0DB4F
|
||||
[npm-version-href]: https://npmjs.com/package/image-meta
|
||||
[npm-downloads-src]: https://img.shields.io/npm/dm/image-meta?style=flat&colorA=18181B&colorB=F0DB4F
|
||||
[npm-downloads-href]: https://npmjs.com/package/image-meta
|
||||
[codecov-src]: https://img.shields.io/codecov/c/gh/unjs/image-meta/main?style=flat&colorA=18181B&colorB=F0DB4F
|
||||
[codecov-href]: https://codecov.io/gh/unjs/image-meta
|
||||
[bundle-src]: https://img.shields.io/bundlephobia/minzip/image-meta?style=flat&colorA=18181B&colorB=F0DB4F
|
||||
[bundle-href]: https://bundlephobia.com/result?p=image-meta
|
||||
868
Frontend-Learner/node_modules/image-meta/dist/index.cjs
generated
vendored
Normal file
868
Frontend-Learner/node_modules/image-meta/dist/index.cjs
generated
vendored
Normal file
|
|
@ -0,0 +1,868 @@
|
|||
'use strict';
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
const toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
|
||||
const toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + ("0" + i.toString(16)).slice(-2), "");
|
||||
const readInt16LE = (input, offset = 0) => {
|
||||
const val = input[offset] + input[offset + 1] * 2 ** 8;
|
||||
return val | (val & 2 ** 15) * 131070;
|
||||
};
|
||||
const readUInt16BE = (input, offset = 0) => input[offset] * 2 ** 8 + input[offset + 1];
|
||||
const readUInt16LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8;
|
||||
const readUInt24LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16;
|
||||
const readInt32LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16 + (input[offset + 3] << 24);
|
||||
const readUInt32BE = (input, offset = 0) => input[offset] * 2 ** 24 + input[offset + 1] * 2 ** 16 + input[offset + 2] * 2 ** 8 + input[offset + 3];
|
||||
const readUInt32LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16 + input[offset + 3] * 2 ** 24;
|
||||
const methods = {
|
||||
readUInt16BE,
|
||||
readUInt16LE,
|
||||
readUInt32BE,
|
||||
readUInt32LE
|
||||
};
|
||||
function readUInt(input, bits, offset, isBigEndian) {
|
||||
offset = offset || 0;
|
||||
const endian = isBigEndian ? "BE" : "LE";
|
||||
const methodName = "readUInt" + bits + endian;
|
||||
return methods[methodName](input, offset);
|
||||
}
|
||||
|
||||
const BMP = {
|
||||
validate: (input) => toUTF8String(input, 0, 2) === "BM",
|
||||
calculate: (input) => ({
|
||||
height: Math.abs(readInt32LE(input, 22)),
|
||||
width: readUInt32LE(input, 18)
|
||||
})
|
||||
};
|
||||
|
||||
const TYPE_ICON = 1;
|
||||
const SIZE_HEADER$1 = 2 + 2 + 2;
|
||||
const 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$1(input, imageIndex) {
|
||||
const offset = SIZE_HEADER$1 + imageIndex * SIZE_IMAGE_ENTRY;
|
||||
return {
|
||||
height: getSizeFromOffset(input, offset + 1),
|
||||
width: getSizeFromOffset(input, offset)
|
||||
};
|
||||
}
|
||||
const 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$1(input, 0);
|
||||
if (nbImages === 1) {
|
||||
return imageSize;
|
||||
}
|
||||
const imgs = [imageSize];
|
||||
for (let imageIndex = 1; imageIndex < nbImages; imageIndex += 1) {
|
||||
imgs.push(getImageSize$1(input, imageIndex));
|
||||
}
|
||||
return {
|
||||
height: imageSize.height,
|
||||
images: imgs,
|
||||
width: imageSize.width
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const TYPE_CURSOR = 2;
|
||||
const 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)
|
||||
};
|
||||
|
||||
const DDS = {
|
||||
validate: (input) => readUInt32LE(input, 0) === 542327876,
|
||||
calculate: (input) => ({
|
||||
height: readUInt32LE(input, 12),
|
||||
width: readUInt32LE(input, 16)
|
||||
})
|
||||
};
|
||||
|
||||
const gifRegexp = /^GIF8[79]a/;
|
||||
const GIF = {
|
||||
validate: (input) => gifRegexp.test(toUTF8String(input, 0, 6)),
|
||||
calculate: (input) => ({
|
||||
height: readUInt16LE(input, 8),
|
||||
width: readUInt16LE(input, 6)
|
||||
})
|
||||
};
|
||||
|
||||
const HEIC = {
|
||||
validate: (input) => {
|
||||
const ftypBox = findBox$1(input, "ftyp");
|
||||
if (!ftypBox) return false;
|
||||
const majorBrand = toUTF8String(
|
||||
input,
|
||||
ftypBox.offset + 8,
|
||||
ftypBox.offset + 12
|
||||
);
|
||||
return ["heic", "heix", "hevc", "hevx", "mif1", "msf1"].includes(
|
||||
majorBrand
|
||||
);
|
||||
},
|
||||
calculate: (input) => {
|
||||
const metaBox = findBox$1(input, "meta");
|
||||
if (!metaBox) throw new TypeError("heic: meta box not found");
|
||||
const iprpBox = findBox$1(
|
||||
input,
|
||||
"iprp",
|
||||
metaBox.offset + 12,
|
||||
metaBox.offset + metaBox.size
|
||||
);
|
||||
if (!iprpBox) throw new TypeError("heic: iprp box not found");
|
||||
const ipcoBox = findBox$1(
|
||||
input,
|
||||
"ipco",
|
||||
iprpBox.offset + 8,
|
||||
iprpBox.offset + iprpBox.size
|
||||
);
|
||||
if (!ipcoBox) throw new TypeError("heic: ipco box not found");
|
||||
const dimensions = findAllBoxes(
|
||||
input,
|
||||
"ispe",
|
||||
ipcoBox.offset + 8,
|
||||
ipcoBox.offset + ipcoBox.size
|
||||
).map((box) => ({
|
||||
width: readUInt32BE(input, box.offset + 12),
|
||||
height: readUInt32BE(input, box.offset + 16)
|
||||
}));
|
||||
if (dimensions.length === 0)
|
||||
throw new TypeError("heic: ispe box not found");
|
||||
let largestDimension = dimensions[0];
|
||||
for (let i = 1; i < dimensions.length; i++) {
|
||||
const curr = dimensions[i];
|
||||
if (curr.width * curr.height > largestDimension.width * largestDimension.height) {
|
||||
largestDimension = curr;
|
||||
}
|
||||
}
|
||||
return largestDimension;
|
||||
}
|
||||
};
|
||||
function findBox$1(input, type, startOffset = 0, endOffset = input.length) {
|
||||
let offset = startOffset;
|
||||
while (offset < endOffset) {
|
||||
const size = readUInt32BE(input, offset);
|
||||
const boxType = toUTF8String(input, offset + 4, offset + 8);
|
||||
if (boxType === type) {
|
||||
return { offset, size };
|
||||
}
|
||||
if (size <= 0 || offset + size > endOffset) {
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
function findAllBoxes(input, type, startOffset = 0, endOffset = input.length) {
|
||||
let offset = startOffset;
|
||||
const boxes = [];
|
||||
while (offset < endOffset) {
|
||||
const size = readUInt32BE(input, offset);
|
||||
const boxType = toUTF8String(input, offset + 4, offset + 8);
|
||||
if (boxType === type) {
|
||||
boxes.push({ offset, size });
|
||||
}
|
||||
if (size <= 0 || offset + size > endOffset) {
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
return boxes;
|
||||
}
|
||||
|
||||
const SIZE_HEADER = 4 + 4;
|
||||
const FILE_LENGTH_OFFSET = 4;
|
||||
const ENTRY_LENGTH_OFFSET = 4;
|
||||
const 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 getImageSize(type) {
|
||||
const size = ICON_TYPE_SIZE[type];
|
||||
return { width: size, height: size, type };
|
||||
}
|
||||
const ICNS = {
|
||||
validate: (input) => toUTF8String(input, 0, 4) === "icns",
|
||||
calculate(input) {
|
||||
const inputLength = input.length;
|
||||
const fileLength = readUInt32BE(input, FILE_LENGTH_OFFSET);
|
||||
let imageOffset = SIZE_HEADER;
|
||||
let imageHeader = readImageHeader(input, imageOffset);
|
||||
let imageSize = getImageSize(imageHeader[0]);
|
||||
imageOffset += imageHeader[1];
|
||||
if (imageOffset === fileLength) {
|
||||
return imageSize;
|
||||
}
|
||||
const result = {
|
||||
height: imageSize.height,
|
||||
images: [imageSize],
|
||||
width: imageSize.width
|
||||
};
|
||||
while (imageOffset < fileLength && imageOffset < inputLength) {
|
||||
imageHeader = readImageHeader(input, imageOffset);
|
||||
imageSize = getImageSize(imageHeader[0]);
|
||||
imageOffset += imageHeader[1];
|
||||
result.images.push(imageSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
const J2C = {
|
||||
// TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC
|
||||
validate: (input) => toHexString(input, 0, 4) === "ff4fff51",
|
||||
calculate: (input) => ({
|
||||
height: readUInt32BE(input, 12),
|
||||
width: readUInt32BE(input, 8)
|
||||
})
|
||||
};
|
||||
|
||||
const BoxTypes = {
|
||||
ftyp: "66747970",
|
||||
jp2h: "6a703268",
|
||||
jp__: "6a502020",
|
||||
rreq: "72726571"};
|
||||
const calculateRREQLength = (box) => {
|
||||
const unit = box[0];
|
||||
let offset = 1 + 2 * unit;
|
||||
const numStdFlags = readUInt16BE(box, offset);
|
||||
const flagsLength = numStdFlags * (2 + unit);
|
||||
offset = offset + 2 + flagsLength;
|
||||
const numVendorFeatures = readUInt16BE(box, offset);
|
||||
const featuresLength = numVendorFeatures * (16 + unit);
|
||||
return offset + 2 + featuresLength;
|
||||
};
|
||||
const parseIHDR = (box) => {
|
||||
return {
|
||||
height: readUInt32BE(box, 4),
|
||||
width: readUInt32BE(box, 8)
|
||||
};
|
||||
};
|
||||
const JP2 = {
|
||||
validate(input) {
|
||||
const signature = toHexString(input, 4, 8);
|
||||
const signatureLength = readUInt32BE(input, 0);
|
||||
if (signature !== BoxTypes.jp__ || signatureLength < 1) {
|
||||
return false;
|
||||
}
|
||||
const ftypeBoxStart = signatureLength + 4;
|
||||
const ftypBoxLength = readUInt32BE(input, signatureLength);
|
||||
const ftypBox = input.slice(ftypeBoxStart, ftypeBoxStart + ftypBoxLength);
|
||||
return toHexString(ftypBox, 0, 4) === BoxTypes.ftyp;
|
||||
},
|
||||
calculate(input) {
|
||||
const signatureLength = readUInt32BE(input, 0);
|
||||
const ftypBoxLength = readUInt16BE(input, signatureLength + 2);
|
||||
let offset = signatureLength + 4 + ftypBoxLength;
|
||||
const nextBoxType = toHexString(input, offset, offset + 4);
|
||||
switch (nextBoxType) {
|
||||
case BoxTypes.rreq: {
|
||||
const MAGIC = 4;
|
||||
offset = offset + 4 + MAGIC + calculateRREQLength(input.slice(offset + 4));
|
||||
return parseIHDR(input.slice(offset + 8, offset + 24));
|
||||
}
|
||||
case BoxTypes.jp2h: {
|
||||
return parseIHDR(input.slice(offset + 8, offset + 24));
|
||||
}
|
||||
default: {
|
||||
throw new TypeError(
|
||||
"Unsupported header found: " + toUTF8String(input, offset, offset + 4)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const EXIF_MARKER = "45786966";
|
||||
const APP1_DATA_SIZE_BYTES = 2;
|
||||
const EXIF_HEADER_BYTES = 6;
|
||||
const TIFF_BYTE_ALIGN_BYTES = 2;
|
||||
const BIG_ENDIAN_BYTE_ALIGN = "4d4d";
|
||||
const LITTLE_ENDIAN_BYTE_ALIGN = "4949";
|
||||
const IDF_ENTRY_BYTES = 12;
|
||||
const 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");
|
||||
}
|
||||
if (input[index] !== 255) {
|
||||
throw new TypeError("Invalid JPG, marker table corrupted");
|
||||
}
|
||||
}
|
||||
const JPG = {
|
||||
validate: (input) => toHexString(input, 0, 2) === "ffd8",
|
||||
calculate(input) {
|
||||
input = input.slice(4);
|
||||
let orientation;
|
||||
let next;
|
||||
while (input.length > 0) {
|
||||
const i = readUInt16BE(input, 0);
|
||||
if (isEXIF(input)) {
|
||||
orientation = validateExifBlock(input, i);
|
||||
}
|
||||
validateInput(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");
|
||||
}
|
||||
};
|
||||
|
||||
const KTX = {
|
||||
validate: (input) => toUTF8String(input, 1, 7) === "KTX 11",
|
||||
calculate: (input) => ({
|
||||
height: readUInt32LE(input, 40),
|
||||
width: readUInt32LE(input, 36)
|
||||
})
|
||||
};
|
||||
|
||||
const pngSignature = "PNG\r\n\n";
|
||||
const pngImageHeaderChunkName = "IHDR";
|
||||
const pngFriedChunkName = "CgBI";
|
||||
const 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)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const PNMTypes = {
|
||||
P1: "pbm/ascii",
|
||||
P2: "pgm/ascii",
|
||||
P3: "ppm/ascii",
|
||||
P4: "pbm",
|
||||
P5: "pgm",
|
||||
P6: "ppm",
|
||||
P7: "pam",
|
||||
PF: "pfm"
|
||||
};
|
||||
const 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)
|
||||
};
|
||||
} else {
|
||||
throw new TypeError("Invalid PNM");
|
||||
}
|
||||
},
|
||||
pam: (lines) => {
|
||||
const size = {};
|
||||
while (lines.length > 0) {
|
||||
const line = lines.shift();
|
||||
if (line.length > 16 || (line.codePointAt(0) || 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
|
||||
};
|
||||
} else {
|
||||
throw new TypeError("Invalid PAM");
|
||||
}
|
||||
}
|
||||
};
|
||||
const 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(/[\n\r]+/);
|
||||
const handler = handlers[type] || handlers.default;
|
||||
return handler(lines);
|
||||
}
|
||||
};
|
||||
|
||||
const PSD = {
|
||||
validate: (input) => toUTF8String(input, 0, 4) === "8BPS",
|
||||
calculate: (input) => ({
|
||||
height: readUInt32BE(input, 14),
|
||||
width: readUInt32BE(input, 18)
|
||||
})
|
||||
};
|
||||
|
||||
const svgReg = /<svg\s([^"'>]|"[^"]*"|'[^']*')*>/;
|
||||
const extractorRegExps = {
|
||||
height: /\sheight=(["'])([^%]+?)\1/,
|
||||
root: svgReg,
|
||||
viewbox: /\sviewbox=(["'])(.+?)\1/i,
|
||||
width: /\swidth=(["'])([^%]+?)\1/
|
||||
};
|
||||
const INCH_CM = 2.54;
|
||||
const 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
|
||||
};
|
||||
const 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
|
||||
};
|
||||
}
|
||||
const 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");
|
||||
}
|
||||
};
|
||||
|
||||
const TGA = {
|
||||
validate(input) {
|
||||
return readUInt16LE(input, 0) === 0 && readUInt16LE(input, 4) === 0;
|
||||
},
|
||||
calculate(input) {
|
||||
return {
|
||||
height: readUInt16LE(input, 14),
|
||||
width: readUInt16LE(input, 12)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function readIFD(buffer, isBigEndian) {
|
||||
const ifdOffset = readUInt(buffer, 32, 4, isBigEndian);
|
||||
let bufferSize = 1024;
|
||||
const fileSize = buffer.length;
|
||||
if (ifdOffset + bufferSize > fileSize) {
|
||||
bufferSize = fileSize - ifdOffset - 10;
|
||||
}
|
||||
return buffer.slice(ifdOffset + 2, ifdOffset + 2 + bufferSize);
|
||||
}
|
||||
function readValue(buffer, isBigEndian) {
|
||||
const low = readUInt(buffer, 16, 8, isBigEndian);
|
||||
const high = readUInt(buffer, 16, 10, isBigEndian);
|
||||
return (high << 16) + low;
|
||||
}
|
||||
function nextTag(buffer) {
|
||||
if (buffer.length > 24) {
|
||||
return buffer.slice(12);
|
||||
}
|
||||
}
|
||||
function extractTags(buffer, isBigEndian) {
|
||||
const tags = {};
|
||||
let temp = buffer;
|
||||
while (temp && temp.length > 0) {
|
||||
const code = readUInt(temp, 16, 0, isBigEndian);
|
||||
const type = readUInt(temp, 16, 2, isBigEndian);
|
||||
const length = readUInt(temp, 32, 4, isBigEndian);
|
||||
if (code === 0) {
|
||||
break;
|
||||
} else {
|
||||
if (length === 1 && (type === 3 || type === 4)) {
|
||||
tags[code] = readValue(temp, isBigEndian);
|
||||
}
|
||||
temp = nextTag(temp);
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
function determineEndianness(input) {
|
||||
const signature = toUTF8String(input, 0, 2);
|
||||
if (signature === "II") {
|
||||
return "LE";
|
||||
} else if (signature === "MM") {
|
||||
return "BE";
|
||||
}
|
||||
}
|
||||
const signatures = /* @__PURE__ */ new Set([
|
||||
// '492049', // currently not supported
|
||||
"49492a00",
|
||||
// Little endian
|
||||
"4d4d002a"
|
||||
// Big Endian
|
||||
// '4d4d002a', // BigTIFF > 4GB. currently not supported
|
||||
]);
|
||||
const TIFF = {
|
||||
validate: (input) => signatures.has(toHexString(input, 0, 4)),
|
||||
calculate(input) {
|
||||
const isBigEndian = determineEndianness(input) === "BE";
|
||||
const ifdBuffer = readIFD(input, isBigEndian);
|
||||
const tags = extractTags(ifdBuffer, isBigEndian);
|
||||
const width = tags[256];
|
||||
const height = tags[257];
|
||||
if (!width || !height) {
|
||||
throw new TypeError("Invalid Tiff. Missing tags");
|
||||
}
|
||||
return { height, width };
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
const WEBP = {
|
||||
validate(input) {
|
||||
const riffHeader = toUTF8String(input, 0, 4) === "RIFF";
|
||||
const webpHeader = toUTF8String(input, 8, 12) === "WEBP";
|
||||
const vp8Header = toUTF8String(input, 12, 15) === "VP8";
|
||||
return riffHeader && webpHeader && vp8Header;
|
||||
},
|
||||
calculate(input) {
|
||||
const chunkHeader = toUTF8String(input, 12, 16);
|
||||
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);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
const AVIF = {
|
||||
validate: (input) => toUTF8String(input, 8, 12) === "avif",
|
||||
calculate: (input) => {
|
||||
const metaBox = findBox(input, "meta");
|
||||
const iprpBox = findBox(
|
||||
input,
|
||||
"iprp",
|
||||
metaBox.offset + 12,
|
||||
metaBox.offset + metaBox.size
|
||||
);
|
||||
const ipcoBox = findBox(
|
||||
input,
|
||||
"ipco",
|
||||
iprpBox.offset + 8,
|
||||
iprpBox.offset + iprpBox.size
|
||||
);
|
||||
const ispeBox = findBox(
|
||||
input,
|
||||
"ispe",
|
||||
ipcoBox.offset + 8,
|
||||
ipcoBox.offset + ipcoBox.size
|
||||
);
|
||||
const width = readUInt32BE(input, ispeBox.offset + 12);
|
||||
const height = readUInt32BE(input, ispeBox.offset + 16);
|
||||
return { width, height };
|
||||
}
|
||||
};
|
||||
function findBox(input, type, startOffset = 0, endOffset = input.length) {
|
||||
for (let offset = startOffset; offset < endOffset; ) {
|
||||
const size = readUInt32BE(input, offset);
|
||||
const boxType = toUTF8String(input, offset + 4, offset + 8);
|
||||
if (boxType === type) {
|
||||
return { offset, size };
|
||||
}
|
||||
if (size <= 0 || offset + size > endOffset) {
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
throw new Error(`${type} box not found`);
|
||||
}
|
||||
|
||||
const typeHandlers = {
|
||||
bmp: BMP,
|
||||
cur: CUR,
|
||||
dds: DDS,
|
||||
gif: GIF,
|
||||
heic: HEIC,
|
||||
icns: ICNS,
|
||||
ico: ICO,
|
||||
j2c: J2C,
|
||||
jp2: JP2,
|
||||
jpg: JPG,
|
||||
ktx: KTX,
|
||||
png: PNG,
|
||||
pnm: PNM,
|
||||
psd: PSD,
|
||||
svg: SVG,
|
||||
tga: TGA,
|
||||
tiff: TIFF,
|
||||
webp: WEBP,
|
||||
avif: AVIF
|
||||
};
|
||||
|
||||
const keys = Object.keys(typeHandlers);
|
||||
const firstBytes = {
|
||||
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];
|
||||
if (byte in firstBytes) {
|
||||
const type = firstBytes[byte];
|
||||
if (type && typeHandlers[type].validate(input)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return keys.find((key) => typeHandlers[key].validate(input));
|
||||
}
|
||||
|
||||
function imageMeta(input) {
|
||||
if (!(input instanceof Uint8Array)) {
|
||||
throw new TypeError("Input should be a Uint8Array");
|
||||
}
|
||||
const type = detector(input);
|
||||
if (type !== void 0 && type in typeHandlers) {
|
||||
const size = typeHandlers[type].calculate(input);
|
||||
if (size !== void 0) {
|
||||
size.type = type;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
throw new TypeError(`Unsupported file type: ${type}`);
|
||||
}
|
||||
|
||||
exports.imageMeta = imageMeta;
|
||||
16
Frontend-Learner/node_modules/image-meta/dist/index.d.cts
generated
vendored
Normal file
16
Frontend-Learner/node_modules/image-meta/dist/index.d.cts
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
type ImageMeta = {
|
||||
images?: Omit<ImageMeta, "images">[];
|
||||
width: number | undefined;
|
||||
height: number | undefined;
|
||||
orientation?: number;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array|string} input - Uint8Array or relative/absolute path of the image file
|
||||
* @param {Function=} [callback] - optional function for async detection
|
||||
*/
|
||||
declare function imageMeta(input: Uint8Array): ImageMeta;
|
||||
|
||||
export { imageMeta };
|
||||
export type { ImageMeta };
|
||||
16
Frontend-Learner/node_modules/image-meta/dist/index.d.mts
generated
vendored
Normal file
16
Frontend-Learner/node_modules/image-meta/dist/index.d.mts
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
type ImageMeta = {
|
||||
images?: Omit<ImageMeta, "images">[];
|
||||
width: number | undefined;
|
||||
height: number | undefined;
|
||||
orientation?: number;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array|string} input - Uint8Array or relative/absolute path of the image file
|
||||
* @param {Function=} [callback] - optional function for async detection
|
||||
*/
|
||||
declare function imageMeta(input: Uint8Array): ImageMeta;
|
||||
|
||||
export { imageMeta };
|
||||
export type { ImageMeta };
|
||||
16
Frontend-Learner/node_modules/image-meta/dist/index.d.ts
generated
vendored
Normal file
16
Frontend-Learner/node_modules/image-meta/dist/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
type ImageMeta = {
|
||||
images?: Omit<ImageMeta, "images">[];
|
||||
width: number | undefined;
|
||||
height: number | undefined;
|
||||
orientation?: number;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array|string} input - Uint8Array or relative/absolute path of the image file
|
||||
* @param {Function=} [callback] - optional function for async detection
|
||||
*/
|
||||
declare function imageMeta(input: Uint8Array): ImageMeta;
|
||||
|
||||
export { imageMeta };
|
||||
export type { ImageMeta };
|
||||
866
Frontend-Learner/node_modules/image-meta/dist/index.mjs
generated
vendored
Normal file
866
Frontend-Learner/node_modules/image-meta/dist/index.mjs
generated
vendored
Normal file
|
|
@ -0,0 +1,866 @@
|
|||
const decoder = new TextDecoder();
|
||||
const toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
|
||||
const toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + ("0" + i.toString(16)).slice(-2), "");
|
||||
const readInt16LE = (input, offset = 0) => {
|
||||
const val = input[offset] + input[offset + 1] * 2 ** 8;
|
||||
return val | (val & 2 ** 15) * 131070;
|
||||
};
|
||||
const readUInt16BE = (input, offset = 0) => input[offset] * 2 ** 8 + input[offset + 1];
|
||||
const readUInt16LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8;
|
||||
const readUInt24LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16;
|
||||
const readInt32LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16 + (input[offset + 3] << 24);
|
||||
const readUInt32BE = (input, offset = 0) => input[offset] * 2 ** 24 + input[offset + 1] * 2 ** 16 + input[offset + 2] * 2 ** 8 + input[offset + 3];
|
||||
const readUInt32LE = (input, offset = 0) => input[offset] + input[offset + 1] * 2 ** 8 + input[offset + 2] * 2 ** 16 + input[offset + 3] * 2 ** 24;
|
||||
const methods = {
|
||||
readUInt16BE,
|
||||
readUInt16LE,
|
||||
readUInt32BE,
|
||||
readUInt32LE
|
||||
};
|
||||
function readUInt(input, bits, offset, isBigEndian) {
|
||||
offset = offset || 0;
|
||||
const endian = isBigEndian ? "BE" : "LE";
|
||||
const methodName = "readUInt" + bits + endian;
|
||||
return methods[methodName](input, offset);
|
||||
}
|
||||
|
||||
const BMP = {
|
||||
validate: (input) => toUTF8String(input, 0, 2) === "BM",
|
||||
calculate: (input) => ({
|
||||
height: Math.abs(readInt32LE(input, 22)),
|
||||
width: readUInt32LE(input, 18)
|
||||
})
|
||||
};
|
||||
|
||||
const TYPE_ICON = 1;
|
||||
const SIZE_HEADER$1 = 2 + 2 + 2;
|
||||
const 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$1(input, imageIndex) {
|
||||
const offset = SIZE_HEADER$1 + imageIndex * SIZE_IMAGE_ENTRY;
|
||||
return {
|
||||
height: getSizeFromOffset(input, offset + 1),
|
||||
width: getSizeFromOffset(input, offset)
|
||||
};
|
||||
}
|
||||
const 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$1(input, 0);
|
||||
if (nbImages === 1) {
|
||||
return imageSize;
|
||||
}
|
||||
const imgs = [imageSize];
|
||||
for (let imageIndex = 1; imageIndex < nbImages; imageIndex += 1) {
|
||||
imgs.push(getImageSize$1(input, imageIndex));
|
||||
}
|
||||
return {
|
||||
height: imageSize.height,
|
||||
images: imgs,
|
||||
width: imageSize.width
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const TYPE_CURSOR = 2;
|
||||
const 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)
|
||||
};
|
||||
|
||||
const DDS = {
|
||||
validate: (input) => readUInt32LE(input, 0) === 542327876,
|
||||
calculate: (input) => ({
|
||||
height: readUInt32LE(input, 12),
|
||||
width: readUInt32LE(input, 16)
|
||||
})
|
||||
};
|
||||
|
||||
const gifRegexp = /^GIF8[79]a/;
|
||||
const GIF = {
|
||||
validate: (input) => gifRegexp.test(toUTF8String(input, 0, 6)),
|
||||
calculate: (input) => ({
|
||||
height: readUInt16LE(input, 8),
|
||||
width: readUInt16LE(input, 6)
|
||||
})
|
||||
};
|
||||
|
||||
const HEIC = {
|
||||
validate: (input) => {
|
||||
const ftypBox = findBox$1(input, "ftyp");
|
||||
if (!ftypBox) return false;
|
||||
const majorBrand = toUTF8String(
|
||||
input,
|
||||
ftypBox.offset + 8,
|
||||
ftypBox.offset + 12
|
||||
);
|
||||
return ["heic", "heix", "hevc", "hevx", "mif1", "msf1"].includes(
|
||||
majorBrand
|
||||
);
|
||||
},
|
||||
calculate: (input) => {
|
||||
const metaBox = findBox$1(input, "meta");
|
||||
if (!metaBox) throw new TypeError("heic: meta box not found");
|
||||
const iprpBox = findBox$1(
|
||||
input,
|
||||
"iprp",
|
||||
metaBox.offset + 12,
|
||||
metaBox.offset + metaBox.size
|
||||
);
|
||||
if (!iprpBox) throw new TypeError("heic: iprp box not found");
|
||||
const ipcoBox = findBox$1(
|
||||
input,
|
||||
"ipco",
|
||||
iprpBox.offset + 8,
|
||||
iprpBox.offset + iprpBox.size
|
||||
);
|
||||
if (!ipcoBox) throw new TypeError("heic: ipco box not found");
|
||||
const dimensions = findAllBoxes(
|
||||
input,
|
||||
"ispe",
|
||||
ipcoBox.offset + 8,
|
||||
ipcoBox.offset + ipcoBox.size
|
||||
).map((box) => ({
|
||||
width: readUInt32BE(input, box.offset + 12),
|
||||
height: readUInt32BE(input, box.offset + 16)
|
||||
}));
|
||||
if (dimensions.length === 0)
|
||||
throw new TypeError("heic: ispe box not found");
|
||||
let largestDimension = dimensions[0];
|
||||
for (let i = 1; i < dimensions.length; i++) {
|
||||
const curr = dimensions[i];
|
||||
if (curr.width * curr.height > largestDimension.width * largestDimension.height) {
|
||||
largestDimension = curr;
|
||||
}
|
||||
}
|
||||
return largestDimension;
|
||||
}
|
||||
};
|
||||
function findBox$1(input, type, startOffset = 0, endOffset = input.length) {
|
||||
let offset = startOffset;
|
||||
while (offset < endOffset) {
|
||||
const size = readUInt32BE(input, offset);
|
||||
const boxType = toUTF8String(input, offset + 4, offset + 8);
|
||||
if (boxType === type) {
|
||||
return { offset, size };
|
||||
}
|
||||
if (size <= 0 || offset + size > endOffset) {
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
function findAllBoxes(input, type, startOffset = 0, endOffset = input.length) {
|
||||
let offset = startOffset;
|
||||
const boxes = [];
|
||||
while (offset < endOffset) {
|
||||
const size = readUInt32BE(input, offset);
|
||||
const boxType = toUTF8String(input, offset + 4, offset + 8);
|
||||
if (boxType === type) {
|
||||
boxes.push({ offset, size });
|
||||
}
|
||||
if (size <= 0 || offset + size > endOffset) {
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
return boxes;
|
||||
}
|
||||
|
||||
const SIZE_HEADER = 4 + 4;
|
||||
const FILE_LENGTH_OFFSET = 4;
|
||||
const ENTRY_LENGTH_OFFSET = 4;
|
||||
const 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 getImageSize(type) {
|
||||
const size = ICON_TYPE_SIZE[type];
|
||||
return { width: size, height: size, type };
|
||||
}
|
||||
const ICNS = {
|
||||
validate: (input) => toUTF8String(input, 0, 4) === "icns",
|
||||
calculate(input) {
|
||||
const inputLength = input.length;
|
||||
const fileLength = readUInt32BE(input, FILE_LENGTH_OFFSET);
|
||||
let imageOffset = SIZE_HEADER;
|
||||
let imageHeader = readImageHeader(input, imageOffset);
|
||||
let imageSize = getImageSize(imageHeader[0]);
|
||||
imageOffset += imageHeader[1];
|
||||
if (imageOffset === fileLength) {
|
||||
return imageSize;
|
||||
}
|
||||
const result = {
|
||||
height: imageSize.height,
|
||||
images: [imageSize],
|
||||
width: imageSize.width
|
||||
};
|
||||
while (imageOffset < fileLength && imageOffset < inputLength) {
|
||||
imageHeader = readImageHeader(input, imageOffset);
|
||||
imageSize = getImageSize(imageHeader[0]);
|
||||
imageOffset += imageHeader[1];
|
||||
result.images.push(imageSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
const J2C = {
|
||||
// TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC
|
||||
validate: (input) => toHexString(input, 0, 4) === "ff4fff51",
|
||||
calculate: (input) => ({
|
||||
height: readUInt32BE(input, 12),
|
||||
width: readUInt32BE(input, 8)
|
||||
})
|
||||
};
|
||||
|
||||
const BoxTypes = {
|
||||
ftyp: "66747970",
|
||||
jp2h: "6a703268",
|
||||
jp__: "6a502020",
|
||||
rreq: "72726571"};
|
||||
const calculateRREQLength = (box) => {
|
||||
const unit = box[0];
|
||||
let offset = 1 + 2 * unit;
|
||||
const numStdFlags = readUInt16BE(box, offset);
|
||||
const flagsLength = numStdFlags * (2 + unit);
|
||||
offset = offset + 2 + flagsLength;
|
||||
const numVendorFeatures = readUInt16BE(box, offset);
|
||||
const featuresLength = numVendorFeatures * (16 + unit);
|
||||
return offset + 2 + featuresLength;
|
||||
};
|
||||
const parseIHDR = (box) => {
|
||||
return {
|
||||
height: readUInt32BE(box, 4),
|
||||
width: readUInt32BE(box, 8)
|
||||
};
|
||||
};
|
||||
const JP2 = {
|
||||
validate(input) {
|
||||
const signature = toHexString(input, 4, 8);
|
||||
const signatureLength = readUInt32BE(input, 0);
|
||||
if (signature !== BoxTypes.jp__ || signatureLength < 1) {
|
||||
return false;
|
||||
}
|
||||
const ftypeBoxStart = signatureLength + 4;
|
||||
const ftypBoxLength = readUInt32BE(input, signatureLength);
|
||||
const ftypBox = input.slice(ftypeBoxStart, ftypeBoxStart + ftypBoxLength);
|
||||
return toHexString(ftypBox, 0, 4) === BoxTypes.ftyp;
|
||||
},
|
||||
calculate(input) {
|
||||
const signatureLength = readUInt32BE(input, 0);
|
||||
const ftypBoxLength = readUInt16BE(input, signatureLength + 2);
|
||||
let offset = signatureLength + 4 + ftypBoxLength;
|
||||
const nextBoxType = toHexString(input, offset, offset + 4);
|
||||
switch (nextBoxType) {
|
||||
case BoxTypes.rreq: {
|
||||
const MAGIC = 4;
|
||||
offset = offset + 4 + MAGIC + calculateRREQLength(input.slice(offset + 4));
|
||||
return parseIHDR(input.slice(offset + 8, offset + 24));
|
||||
}
|
||||
case BoxTypes.jp2h: {
|
||||
return parseIHDR(input.slice(offset + 8, offset + 24));
|
||||
}
|
||||
default: {
|
||||
throw new TypeError(
|
||||
"Unsupported header found: " + toUTF8String(input, offset, offset + 4)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const EXIF_MARKER = "45786966";
|
||||
const APP1_DATA_SIZE_BYTES = 2;
|
||||
const EXIF_HEADER_BYTES = 6;
|
||||
const TIFF_BYTE_ALIGN_BYTES = 2;
|
||||
const BIG_ENDIAN_BYTE_ALIGN = "4d4d";
|
||||
const LITTLE_ENDIAN_BYTE_ALIGN = "4949";
|
||||
const IDF_ENTRY_BYTES = 12;
|
||||
const 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");
|
||||
}
|
||||
if (input[index] !== 255) {
|
||||
throw new TypeError("Invalid JPG, marker table corrupted");
|
||||
}
|
||||
}
|
||||
const JPG = {
|
||||
validate: (input) => toHexString(input, 0, 2) === "ffd8",
|
||||
calculate(input) {
|
||||
input = input.slice(4);
|
||||
let orientation;
|
||||
let next;
|
||||
while (input.length > 0) {
|
||||
const i = readUInt16BE(input, 0);
|
||||
if (isEXIF(input)) {
|
||||
orientation = validateExifBlock(input, i);
|
||||
}
|
||||
validateInput(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");
|
||||
}
|
||||
};
|
||||
|
||||
const KTX = {
|
||||
validate: (input) => toUTF8String(input, 1, 7) === "KTX 11",
|
||||
calculate: (input) => ({
|
||||
height: readUInt32LE(input, 40),
|
||||
width: readUInt32LE(input, 36)
|
||||
})
|
||||
};
|
||||
|
||||
const pngSignature = "PNG\r\n\n";
|
||||
const pngImageHeaderChunkName = "IHDR";
|
||||
const pngFriedChunkName = "CgBI";
|
||||
const 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)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const PNMTypes = {
|
||||
P1: "pbm/ascii",
|
||||
P2: "pgm/ascii",
|
||||
P3: "ppm/ascii",
|
||||
P4: "pbm",
|
||||
P5: "pgm",
|
||||
P6: "ppm",
|
||||
P7: "pam",
|
||||
PF: "pfm"
|
||||
};
|
||||
const 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)
|
||||
};
|
||||
} else {
|
||||
throw new TypeError("Invalid PNM");
|
||||
}
|
||||
},
|
||||
pam: (lines) => {
|
||||
const size = {};
|
||||
while (lines.length > 0) {
|
||||
const line = lines.shift();
|
||||
if (line.length > 16 || (line.codePointAt(0) || 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
|
||||
};
|
||||
} else {
|
||||
throw new TypeError("Invalid PAM");
|
||||
}
|
||||
}
|
||||
};
|
||||
const 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(/[\n\r]+/);
|
||||
const handler = handlers[type] || handlers.default;
|
||||
return handler(lines);
|
||||
}
|
||||
};
|
||||
|
||||
const PSD = {
|
||||
validate: (input) => toUTF8String(input, 0, 4) === "8BPS",
|
||||
calculate: (input) => ({
|
||||
height: readUInt32BE(input, 14),
|
||||
width: readUInt32BE(input, 18)
|
||||
})
|
||||
};
|
||||
|
||||
const svgReg = /<svg\s([^"'>]|"[^"]*"|'[^']*')*>/;
|
||||
const extractorRegExps = {
|
||||
height: /\sheight=(["'])([^%]+?)\1/,
|
||||
root: svgReg,
|
||||
viewbox: /\sviewbox=(["'])(.+?)\1/i,
|
||||
width: /\swidth=(["'])([^%]+?)\1/
|
||||
};
|
||||
const INCH_CM = 2.54;
|
||||
const 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
|
||||
};
|
||||
const 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
|
||||
};
|
||||
}
|
||||
const 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");
|
||||
}
|
||||
};
|
||||
|
||||
const TGA = {
|
||||
validate(input) {
|
||||
return readUInt16LE(input, 0) === 0 && readUInt16LE(input, 4) === 0;
|
||||
},
|
||||
calculate(input) {
|
||||
return {
|
||||
height: readUInt16LE(input, 14),
|
||||
width: readUInt16LE(input, 12)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function readIFD(buffer, isBigEndian) {
|
||||
const ifdOffset = readUInt(buffer, 32, 4, isBigEndian);
|
||||
let bufferSize = 1024;
|
||||
const fileSize = buffer.length;
|
||||
if (ifdOffset + bufferSize > fileSize) {
|
||||
bufferSize = fileSize - ifdOffset - 10;
|
||||
}
|
||||
return buffer.slice(ifdOffset + 2, ifdOffset + 2 + bufferSize);
|
||||
}
|
||||
function readValue(buffer, isBigEndian) {
|
||||
const low = readUInt(buffer, 16, 8, isBigEndian);
|
||||
const high = readUInt(buffer, 16, 10, isBigEndian);
|
||||
return (high << 16) + low;
|
||||
}
|
||||
function nextTag(buffer) {
|
||||
if (buffer.length > 24) {
|
||||
return buffer.slice(12);
|
||||
}
|
||||
}
|
||||
function extractTags(buffer, isBigEndian) {
|
||||
const tags = {};
|
||||
let temp = buffer;
|
||||
while (temp && temp.length > 0) {
|
||||
const code = readUInt(temp, 16, 0, isBigEndian);
|
||||
const type = readUInt(temp, 16, 2, isBigEndian);
|
||||
const length = readUInt(temp, 32, 4, isBigEndian);
|
||||
if (code === 0) {
|
||||
break;
|
||||
} else {
|
||||
if (length === 1 && (type === 3 || type === 4)) {
|
||||
tags[code] = readValue(temp, isBigEndian);
|
||||
}
|
||||
temp = nextTag(temp);
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
function determineEndianness(input) {
|
||||
const signature = toUTF8String(input, 0, 2);
|
||||
if (signature === "II") {
|
||||
return "LE";
|
||||
} else if (signature === "MM") {
|
||||
return "BE";
|
||||
}
|
||||
}
|
||||
const signatures = /* @__PURE__ */ new Set([
|
||||
// '492049', // currently not supported
|
||||
"49492a00",
|
||||
// Little endian
|
||||
"4d4d002a"
|
||||
// Big Endian
|
||||
// '4d4d002a', // BigTIFF > 4GB. currently not supported
|
||||
]);
|
||||
const TIFF = {
|
||||
validate: (input) => signatures.has(toHexString(input, 0, 4)),
|
||||
calculate(input) {
|
||||
const isBigEndian = determineEndianness(input) === "BE";
|
||||
const ifdBuffer = readIFD(input, isBigEndian);
|
||||
const tags = extractTags(ifdBuffer, isBigEndian);
|
||||
const width = tags[256];
|
||||
const height = tags[257];
|
||||
if (!width || !height) {
|
||||
throw new TypeError("Invalid Tiff. Missing tags");
|
||||
}
|
||||
return { height, width };
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
const WEBP = {
|
||||
validate(input) {
|
||||
const riffHeader = toUTF8String(input, 0, 4) === "RIFF";
|
||||
const webpHeader = toUTF8String(input, 8, 12) === "WEBP";
|
||||
const vp8Header = toUTF8String(input, 12, 15) === "VP8";
|
||||
return riffHeader && webpHeader && vp8Header;
|
||||
},
|
||||
calculate(input) {
|
||||
const chunkHeader = toUTF8String(input, 12, 16);
|
||||
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);
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
const AVIF = {
|
||||
validate: (input) => toUTF8String(input, 8, 12) === "avif",
|
||||
calculate: (input) => {
|
||||
const metaBox = findBox(input, "meta");
|
||||
const iprpBox = findBox(
|
||||
input,
|
||||
"iprp",
|
||||
metaBox.offset + 12,
|
||||
metaBox.offset + metaBox.size
|
||||
);
|
||||
const ipcoBox = findBox(
|
||||
input,
|
||||
"ipco",
|
||||
iprpBox.offset + 8,
|
||||
iprpBox.offset + iprpBox.size
|
||||
);
|
||||
const ispeBox = findBox(
|
||||
input,
|
||||
"ispe",
|
||||
ipcoBox.offset + 8,
|
||||
ipcoBox.offset + ipcoBox.size
|
||||
);
|
||||
const width = readUInt32BE(input, ispeBox.offset + 12);
|
||||
const height = readUInt32BE(input, ispeBox.offset + 16);
|
||||
return { width, height };
|
||||
}
|
||||
};
|
||||
function findBox(input, type, startOffset = 0, endOffset = input.length) {
|
||||
for (let offset = startOffset; offset < endOffset; ) {
|
||||
const size = readUInt32BE(input, offset);
|
||||
const boxType = toUTF8String(input, offset + 4, offset + 8);
|
||||
if (boxType === type) {
|
||||
return { offset, size };
|
||||
}
|
||||
if (size <= 0 || offset + size > endOffset) {
|
||||
break;
|
||||
}
|
||||
offset += size;
|
||||
}
|
||||
throw new Error(`${type} box not found`);
|
||||
}
|
||||
|
||||
const typeHandlers = {
|
||||
bmp: BMP,
|
||||
cur: CUR,
|
||||
dds: DDS,
|
||||
gif: GIF,
|
||||
heic: HEIC,
|
||||
icns: ICNS,
|
||||
ico: ICO,
|
||||
j2c: J2C,
|
||||
jp2: JP2,
|
||||
jpg: JPG,
|
||||
ktx: KTX,
|
||||
png: PNG,
|
||||
pnm: PNM,
|
||||
psd: PSD,
|
||||
svg: SVG,
|
||||
tga: TGA,
|
||||
tiff: TIFF,
|
||||
webp: WEBP,
|
||||
avif: AVIF
|
||||
};
|
||||
|
||||
const keys = Object.keys(typeHandlers);
|
||||
const firstBytes = {
|
||||
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];
|
||||
if (byte in firstBytes) {
|
||||
const type = firstBytes[byte];
|
||||
if (type && typeHandlers[type].validate(input)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return keys.find((key) => typeHandlers[key].validate(input));
|
||||
}
|
||||
|
||||
function imageMeta(input) {
|
||||
if (!(input instanceof Uint8Array)) {
|
||||
throw new TypeError("Input should be a Uint8Array");
|
||||
}
|
||||
const type = detector(input);
|
||||
if (type !== void 0 && type in typeHandlers) {
|
||||
const size = typeHandlers[type].calculate(input);
|
||||
if (size !== void 0) {
|
||||
size.type = type;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
throw new TypeError(`Unsupported file type: ${type}`);
|
||||
}
|
||||
|
||||
export { imageMeta };
|
||||
46
Frontend-Learner/node_modules/image-meta/package.json
generated
vendored
Normal file
46
Frontend-Learner/node_modules/image-meta/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "image-meta",
|
||||
"version": "0.2.2",
|
||||
"description": "Detect image type and size using pure javascript",
|
||||
"repository": "unjs/image-meta",
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "unbuild",
|
||||
"dev": "vitest dev",
|
||||
"play": "jiti playground",
|
||||
"lint": "eslint --cache . && prettier -c src test",
|
||||
"lint:fix": "eslint --cache . --fix && prettier -c src test -w",
|
||||
"prepack": "pnpm run build",
|
||||
"release": "pnpm test && changelogen --release && npm publish && git push --follow-tags",
|
||||
"test": "pnpm lint && pnpm test:types && vitest run --coverage",
|
||||
"test:types": "tsc --noEmit --skipLibCheck"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.7.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"changelogen": "^0.6.2",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint-config-unjs": "^0.5.0",
|
||||
"jiti": "^2.6.1",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.3",
|
||||
"unbuild": "^3.6.1",
|
||||
"vitest": "^3.2.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.18.1"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue