Initial kitchen scaffold — Phase 1 kitchen port (build-verified 2026-07-11)
FastAPI backend (Python 3.11, MSSQL ODBC for SambaPOS, Azure DI OCR),
kitchen_db on central PG. React/TS/Vite frontend with navy sidebar layout.
Backend: auth.py (APP_SLUG=kitchen, SimpleNamespace — archive routes use
.kitchen_id/.is_admin without modification), main.py (51 migrations, scheduler,
internal router for KDS bookings feed), api/internal.py, full archive API
(31 routers: invoices, recipes, menus, sambapos, resos, newbook, disputes,
purchase_orders, etc.), models, migrations, OCR pipeline.
kitchen_id pinned to 1 (B1 — single hotel).
Frontend: AuthGate (app=kitchen, token shim for archive compat — B5b pending),
Layout (navy sidebar, 6 sections, Lucide icons, teal --app-primary),
App.tsx (Outlet pattern, UploadApp outside Layout), index.css (full :root block).
strict: false — archive components have type issues; build clean.
Note: 45 archive components call fetch('/api/...') without /kitchen/ prefix
(B5b). Runtime 404s; deferred until after initial testing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
8d688b459d
10003 changed files with 1928395 additions and 0 deletions
26
frontend/node_modules/pdf-lib/src/utils/Cache.ts
generated
vendored
Normal file
26
frontend/node_modules/pdf-lib/src/utils/Cache.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
class Cache<T> {
|
||||
static readonly populatedBy = <T>(populate: () => T) => new Cache(populate);
|
||||
|
||||
private readonly populate: () => T;
|
||||
private value: T | undefined;
|
||||
|
||||
private constructor(populate: () => T) {
|
||||
this.populate = populate;
|
||||
this.value = undefined;
|
||||
}
|
||||
|
||||
getValue(): T | undefined {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
access(): T {
|
||||
if (!this.value) this.value = this.populate();
|
||||
return this.value;
|
||||
}
|
||||
|
||||
invalidate(): void {
|
||||
this.value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export default Cache;
|
||||
146
frontend/node_modules/pdf-lib/src/utils/arrays.ts
generated
vendored
Normal file
146
frontend/node_modules/pdf-lib/src/utils/arrays.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import { decodeFromBase64DataUri } from 'src/utils/base64';
|
||||
import { charFromCode } from 'src/utils/strings';
|
||||
|
||||
export const last = <T>(array: T[]): T => array[array.length - 1];
|
||||
|
||||
// export const dropLast = <T>(array: T[]): T[] =>
|
||||
// array.slice(0, array.length - 1);
|
||||
|
||||
export const typedArrayFor = (value: string | Uint8Array): Uint8Array => {
|
||||
if (value instanceof Uint8Array) return value;
|
||||
const length = value.length;
|
||||
const typedArray = new Uint8Array(length);
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
typedArray[idx] = value.charCodeAt(idx);
|
||||
}
|
||||
return typedArray;
|
||||
};
|
||||
|
||||
export const mergeIntoTypedArray = (...arrays: (string | Uint8Array)[]) => {
|
||||
const arrayCount = arrays.length;
|
||||
|
||||
const typedArrays: Uint8Array[] = [];
|
||||
for (let idx = 0; idx < arrayCount; idx++) {
|
||||
const element = arrays[idx];
|
||||
typedArrays[idx] =
|
||||
element instanceof Uint8Array ? element : typedArrayFor(element);
|
||||
}
|
||||
|
||||
let totalSize = 0;
|
||||
for (let idx = 0; idx < arrayCount; idx++) {
|
||||
totalSize += arrays[idx].length;
|
||||
}
|
||||
|
||||
const merged = new Uint8Array(totalSize);
|
||||
let offset = 0;
|
||||
for (let arrIdx = 0; arrIdx < arrayCount; arrIdx++) {
|
||||
const arr = typedArrays[arrIdx];
|
||||
for (let byteIdx = 0, arrLen = arr.length; byteIdx < arrLen; byteIdx++) {
|
||||
merged[offset++] = arr[byteIdx];
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
};
|
||||
|
||||
export const mergeUint8Arrays = (arrays: Uint8Array[]): Uint8Array => {
|
||||
let totalSize = 0;
|
||||
for (let idx = 0, len = arrays.length; idx < len; idx++) {
|
||||
totalSize += arrays[idx].length;
|
||||
}
|
||||
|
||||
const mergedBuffer = new Uint8Array(totalSize);
|
||||
let offset = 0;
|
||||
for (let idx = 0, len = arrays.length; idx < len; idx++) {
|
||||
const array = arrays[idx];
|
||||
mergedBuffer.set(array, offset);
|
||||
offset += array.length;
|
||||
}
|
||||
|
||||
return mergedBuffer;
|
||||
};
|
||||
|
||||
export const arrayAsString = (array: Uint8Array | number[]): string => {
|
||||
let str = '';
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
str += charFromCode(array[idx]);
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
export const byAscendingId = <T extends { id: any }>(a: T, b: T) => a.id - b.id;
|
||||
|
||||
export const sortedUniq = <T>(array: T[], indexer: (elem: T) => any): T[] => {
|
||||
const uniq: T[] = [];
|
||||
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
const curr = array[idx];
|
||||
const prev = array[idx - 1];
|
||||
if (idx === 0 || indexer(curr) !== indexer(prev)) {
|
||||
uniq.push(curr);
|
||||
}
|
||||
}
|
||||
|
||||
return uniq;
|
||||
};
|
||||
|
||||
// Arrays and TypedArrays in JS both have .reverse() methods, which would seem
|
||||
// to negate the need for this function. However, not all runtimes support this
|
||||
// method (e.g. React Native). This function compensates for that fact.
|
||||
export const reverseArray = (array: Uint8Array) => {
|
||||
const arrayLen = array.length;
|
||||
for (let idx = 0, len = Math.floor(arrayLen / 2); idx < len; idx++) {
|
||||
const leftIdx = idx;
|
||||
const rightIdx = arrayLen - idx - 1;
|
||||
const temp = array[idx];
|
||||
|
||||
array[leftIdx] = array[rightIdx];
|
||||
array[rightIdx] = temp;
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
export const sum = (array: number[] | Uint8Array): number => {
|
||||
let total = 0;
|
||||
for (let idx = 0, len = array.length; idx < len; idx++) {
|
||||
total += array[idx];
|
||||
}
|
||||
return total;
|
||||
};
|
||||
|
||||
export const range = (start: number, end: number): number[] => {
|
||||
const arr = new Array(end - start);
|
||||
for (let idx = 0, len = arr.length; idx < len; idx++) {
|
||||
arr[idx] = start + idx;
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
export const pluckIndices = <T>(arr: T[], indices: number[]) => {
|
||||
const plucked = new Array<T>(indices.length);
|
||||
for (let idx = 0, len = indices.length; idx < len; idx++) {
|
||||
plucked[idx] = arr[indices[idx]];
|
||||
}
|
||||
return plucked;
|
||||
};
|
||||
|
||||
export const canBeConvertedToUint8Array = (
|
||||
input: any,
|
||||
): input is string | ArrayBuffer | Uint8Array =>
|
||||
input instanceof Uint8Array ||
|
||||
input instanceof ArrayBuffer ||
|
||||
typeof input === 'string';
|
||||
|
||||
export const toUint8Array = (input: string | ArrayBuffer | Uint8Array) => {
|
||||
if (typeof input === 'string') {
|
||||
return decodeFromBase64DataUri(input);
|
||||
} else if (input instanceof ArrayBuffer) {
|
||||
return new Uint8Array(input);
|
||||
} else if (input instanceof Uint8Array) {
|
||||
return input;
|
||||
} else {
|
||||
throw new TypeError(
|
||||
'`input` must be one of `string | ArrayBuffer | Uint8Array`',
|
||||
);
|
||||
}
|
||||
};
|
||||
8
frontend/node_modules/pdf-lib/src/utils/async.ts
generated
vendored
Normal file
8
frontend/node_modules/pdf-lib/src/utils/async.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Returns a Promise that resolves after at least one tick of the
|
||||
* Macro Task Queue occurs.
|
||||
*/
|
||||
export const waitForTick = (): Promise<void> =>
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => resolve(), 0);
|
||||
});
|
||||
99
frontend/node_modules/pdf-lib/src/utils/base64.ts
generated
vendored
Normal file
99
frontend/node_modules/pdf-lib/src/utils/base64.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* The `chars`, `lookup`, `encode`, and `decode` members of this file are
|
||||
* licensed under the following:
|
||||
*
|
||||
* base64-arraybuffer
|
||||
* https://github.com/niklasvh/base64-arraybuffer
|
||||
*
|
||||
* Copyright (c) 2012 Niklas von Hertzen
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
*/
|
||||
|
||||
const chars =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
// Use a lookup table to find the index.
|
||||
const lookup = new Uint8Array(256);
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
lookup[chars.charCodeAt(i)] = i;
|
||||
}
|
||||
|
||||
export const encodeToBase64 = (bytes: Uint8Array): string => {
|
||||
let base64 = '';
|
||||
const len = bytes.length;
|
||||
for (let i = 0; i < len; i += 3) {
|
||||
base64 += chars[bytes[i] >> 2];
|
||||
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
|
||||
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
|
||||
base64 += chars[bytes[i + 2] & 63];
|
||||
}
|
||||
|
||||
if (len % 3 === 2) {
|
||||
base64 = base64.substring(0, base64.length - 1) + '=';
|
||||
} else if (len % 3 === 1) {
|
||||
base64 = base64.substring(0, base64.length - 2) + '==';
|
||||
}
|
||||
|
||||
return base64;
|
||||
};
|
||||
|
||||
export const decodeFromBase64 = (base64: string): Uint8Array => {
|
||||
let bufferLength = base64.length * 0.75;
|
||||
const len = base64.length;
|
||||
let i;
|
||||
let p = 0;
|
||||
let encoded1;
|
||||
let encoded2;
|
||||
let encoded3;
|
||||
let encoded4;
|
||||
|
||||
if (base64[base64.length - 1] === '=') {
|
||||
bufferLength--;
|
||||
if (base64[base64.length - 2] === '=') {
|
||||
bufferLength--;
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(bufferLength);
|
||||
|
||||
for (i = 0; i < len; i += 4) {
|
||||
encoded1 = lookup[base64.charCodeAt(i)];
|
||||
encoded2 = lookup[base64.charCodeAt(i + 1)];
|
||||
encoded3 = lookup[base64.charCodeAt(i + 2)];
|
||||
encoded4 = lookup[base64.charCodeAt(i + 3)];
|
||||
|
||||
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
|
||||
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
|
||||
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
};
|
||||
|
||||
// This regex is designed to be as flexible as possible. It will parse certain
|
||||
// invalid data URIs.
|
||||
const DATA_URI_PREFIX_REGEX = /^(data)?:?([\w\/\+]+)?;?(charset=[\w-]+|base64)?.*,/i;
|
||||
|
||||
/**
|
||||
* If the `dataUri` input is a data URI, then the data URI prefix must not be
|
||||
* longer than 100 characters, or this function will fail to decode it.
|
||||
*
|
||||
* @param dataUri a base64 data URI or plain base64 string
|
||||
* @returns a Uint8Array containing the decoded input
|
||||
*/
|
||||
export const decodeFromBase64DataUri = (dataUri: string): Uint8Array => {
|
||||
const trimmedUri = dataUri.trim();
|
||||
|
||||
const prefix = trimmedUri.substring(0, 100);
|
||||
const res = prefix.match(DATA_URI_PREFIX_REGEX);
|
||||
|
||||
// Assume it's not a data URI - just a plain base64 string
|
||||
if (!res) return decodeFromBase64(trimmedUri);
|
||||
|
||||
// Remove the data URI prefix and parse the remainder as a base64 string
|
||||
const [fullMatch] = res;
|
||||
const data = trimmedUri.substring(fullMatch.length);
|
||||
|
||||
return decodeFromBase64(data);
|
||||
};
|
||||
3
frontend/node_modules/pdf-lib/src/utils/errors.ts
generated
vendored
Normal file
3
frontend/node_modules/pdf-lib/src/utils/errors.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export const error = (msg: string) => {
|
||||
throw new Error(msg);
|
||||
};
|
||||
11
frontend/node_modules/pdf-lib/src/utils/index.ts
generated
vendored
Normal file
11
frontend/node_modules/pdf-lib/src/utils/index.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export * from 'src/utils/arrays';
|
||||
export * from 'src/utils/async';
|
||||
export * from 'src/utils/strings';
|
||||
export * from 'src/utils/unicode';
|
||||
export * from 'src/utils/numbers';
|
||||
export * from 'src/utils/errors';
|
||||
export * from 'src/utils/base64';
|
||||
export * from 'src/utils/objects';
|
||||
export * from 'src/utils/validators';
|
||||
export * from 'src/utils/pdfDocEncoding';
|
||||
export { default as Cache } from 'src/utils/Cache';
|
||||
55
frontend/node_modules/pdf-lib/src/utils/numbers.ts
generated
vendored
Normal file
55
frontend/node_modules/pdf-lib/src/utils/numbers.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// tslint:disable radix
|
||||
|
||||
/**
|
||||
* Converts a number to its string representation in decimal. This function
|
||||
* differs from simply converting a number to a string with `.toString()`
|
||||
* because this function's output string will **not** contain exponential
|
||||
* notation.
|
||||
*
|
||||
* Credit: https://stackoverflow.com/a/46545519
|
||||
*/
|
||||
export const numberToString = (num: number) => {
|
||||
let numStr = String(num);
|
||||
|
||||
if (Math.abs(num) < 1.0) {
|
||||
const e = parseInt(num.toString().split('e-')[1]);
|
||||
if (e) {
|
||||
const negative = num < 0;
|
||||
if (negative) num *= -1;
|
||||
num *= Math.pow(10, e - 1);
|
||||
numStr = '0.' + new Array(e).join('0') + num.toString().substring(2);
|
||||
if (negative) numStr = '-' + numStr;
|
||||
}
|
||||
} else {
|
||||
let e = parseInt(num.toString().split('+')[1]);
|
||||
if (e > 20) {
|
||||
e -= 20;
|
||||
num /= Math.pow(10, e);
|
||||
numStr = num.toString() + new Array(e + 1).join('0');
|
||||
}
|
||||
}
|
||||
|
||||
return numStr;
|
||||
};
|
||||
|
||||
export const sizeInBytes = (n: number) => Math.ceil(n.toString(2).length / 8);
|
||||
|
||||
/**
|
||||
* Converts a number into its constituent bytes and returns them as
|
||||
* a number[].
|
||||
*
|
||||
* Returns most significant byte as first element in array. It may be necessary
|
||||
* to call .reverse() to get the bits in the desired order.
|
||||
*
|
||||
* Example:
|
||||
* bytesFor(0x02A41E) => [ 0b10, 0b10100100, 0b11110 ]
|
||||
*
|
||||
* Credit for algorithm: https://stackoverflow.com/a/1936865
|
||||
*/
|
||||
export const bytesFor = (n: number) => {
|
||||
const bytes = new Uint8Array(sizeInBytes(n));
|
||||
for (let i = 1; i <= bytes.length; i++) {
|
||||
bytes[i - 1] = n >> ((bytes.length - i) * 8);
|
||||
}
|
||||
return bytes;
|
||||
};
|
||||
13
frontend/node_modules/pdf-lib/src/utils/objects.ts
generated
vendored
Normal file
13
frontend/node_modules/pdf-lib/src/utils/objects.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { FontNames } from '@pdf-lib/standard-fonts';
|
||||
|
||||
export const values = (obj: any) => Object.keys(obj).map((k) => obj[k]);
|
||||
|
||||
export const StandardFontValues = values(FontNames);
|
||||
|
||||
export const isStandardFont = (input: any): input is FontNames =>
|
||||
StandardFontValues.includes(input);
|
||||
|
||||
export const rectanglesAreEqual = (
|
||||
a: { x: number; y: number; width: number; height: number },
|
||||
b: { x: number; y: number; width: number; height: number },
|
||||
) => a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
||||
69
frontend/node_modules/pdf-lib/src/utils/pdfDocEncoding.ts
generated
vendored
Normal file
69
frontend/node_modules/pdf-lib/src/utils/pdfDocEncoding.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { toCharCode } from 'src/utils/strings';
|
||||
|
||||
// Mapping from PDFDocEncoding to Unicode code point
|
||||
const pdfDocEncodingToUnicode = new Uint16Array(256);
|
||||
|
||||
// Initialize the code points which are the same
|
||||
for (let idx = 0; idx < 256; idx++) {
|
||||
pdfDocEncodingToUnicode[idx] = idx;
|
||||
}
|
||||
|
||||
// Set differences (see "Table D.2 – PDFDocEncoding Character Set" of the PDF spec)
|
||||
pdfDocEncodingToUnicode[0x16] = toCharCode('\u0017'); // SYNCRONOUS IDLE
|
||||
pdfDocEncodingToUnicode[0x18] = toCharCode('\u02D8'); // BREVE
|
||||
pdfDocEncodingToUnicode[0x19] = toCharCode('\u02C7'); // CARON
|
||||
pdfDocEncodingToUnicode[0x1a] = toCharCode('\u02C6'); // MODIFIER LETTER CIRCUMFLEX ACCENT
|
||||
pdfDocEncodingToUnicode[0x1b] = toCharCode('\u02D9'); // DOT ABOVE
|
||||
pdfDocEncodingToUnicode[0x1c] = toCharCode('\u02DD'); // DOUBLE ACUTE ACCENT
|
||||
pdfDocEncodingToUnicode[0x1d] = toCharCode('\u02DB'); // OGONEK
|
||||
pdfDocEncodingToUnicode[0x1e] = toCharCode('\u02DA'); // RING ABOVE
|
||||
pdfDocEncodingToUnicode[0x1f] = toCharCode('\u02DC'); // SMALL TILDE
|
||||
pdfDocEncodingToUnicode[0x7f] = toCharCode('\uFFFD'); // REPLACEMENT CHARACTER (box with questionmark)
|
||||
pdfDocEncodingToUnicode[0x80] = toCharCode('\u2022'); // BULLET
|
||||
pdfDocEncodingToUnicode[0x81] = toCharCode('\u2020'); // DAGGER
|
||||
pdfDocEncodingToUnicode[0x82] = toCharCode('\u2021'); // DOUBLE DAGGER
|
||||
pdfDocEncodingToUnicode[0x83] = toCharCode('\u2026'); // HORIZONTAL ELLIPSIS
|
||||
pdfDocEncodingToUnicode[0x84] = toCharCode('\u2014'); // EM DASH
|
||||
pdfDocEncodingToUnicode[0x85] = toCharCode('\u2013'); // EN DASH
|
||||
pdfDocEncodingToUnicode[0x86] = toCharCode('\u0192'); // LATIN SMALL LETTER SCRIPT F
|
||||
pdfDocEncodingToUnicode[0x87] = toCharCode('\u2044'); // FRACTION SLASH (solidus)
|
||||
pdfDocEncodingToUnicode[0x88] = toCharCode('\u2039'); // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
|
||||
pdfDocEncodingToUnicode[0x89] = toCharCode('\u203A'); // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
|
||||
pdfDocEncodingToUnicode[0x8a] = toCharCode('\u2212'); // MINUS SIGN
|
||||
pdfDocEncodingToUnicode[0x8b] = toCharCode('\u2030'); // PER MILLE SIGN
|
||||
pdfDocEncodingToUnicode[0x8c] = toCharCode('\u201E'); // DOUBLE LOW-9 QUOTATION MARK (quotedblbase)
|
||||
pdfDocEncodingToUnicode[0x8d] = toCharCode('\u201C'); // LEFT DOUBLE QUOTATION MARK (quotedblleft)
|
||||
pdfDocEncodingToUnicode[0x8e] = toCharCode('\u201D'); // RIGHT DOUBLE QUOTATION MARK (quotedblright)
|
||||
pdfDocEncodingToUnicode[0x8f] = toCharCode('\u2018'); // LEFT SINGLE QUOTATION MARK (quoteleft)
|
||||
pdfDocEncodingToUnicode[0x90] = toCharCode('\u2019'); // RIGHT SINGLE QUOTATION MARK (quoteright)
|
||||
pdfDocEncodingToUnicode[0x91] = toCharCode('\u201A'); // SINGLE LOW-9 QUOTATION MARK (quotesinglbase)
|
||||
pdfDocEncodingToUnicode[0x92] = toCharCode('\u2122'); // TRADE MARK SIGN
|
||||
pdfDocEncodingToUnicode[0x93] = toCharCode('\uFB01'); // LATIN SMALL LIGATURE FI
|
||||
pdfDocEncodingToUnicode[0x94] = toCharCode('\uFB02'); // LATIN SMALL LIGATURE FL
|
||||
pdfDocEncodingToUnicode[0x95] = toCharCode('\u0141'); // LATIN CAPITAL LETTER L WITH STROKE
|
||||
pdfDocEncodingToUnicode[0x96] = toCharCode('\u0152'); // LATIN CAPITAL LIGATURE OE
|
||||
pdfDocEncodingToUnicode[0x97] = toCharCode('\u0160'); // LATIN CAPITAL LETTER S WITH CARON
|
||||
pdfDocEncodingToUnicode[0x98] = toCharCode('\u0178'); // LATIN CAPITAL LETTER Y WITH DIAERESIS
|
||||
pdfDocEncodingToUnicode[0x99] = toCharCode('\u017D'); // LATIN CAPITAL LETTER Z WITH CARON
|
||||
pdfDocEncodingToUnicode[0x9a] = toCharCode('\u0131'); // LATIN SMALL LETTER DOTLESS I
|
||||
pdfDocEncodingToUnicode[0x9b] = toCharCode('\u0142'); // LATIN SMALL LETTER L WITH STROKE
|
||||
pdfDocEncodingToUnicode[0x9c] = toCharCode('\u0153'); // LATIN SMALL LIGATURE OE
|
||||
pdfDocEncodingToUnicode[0x9d] = toCharCode('\u0161'); // LATIN SMALL LETTER S WITH CARON
|
||||
pdfDocEncodingToUnicode[0x9e] = toCharCode('\u017E'); // LATIN SMALL LETTER Z WITH CARON
|
||||
pdfDocEncodingToUnicode[0x9f] = toCharCode('\uFFFD'); // REPLACEMENT CHARACTER (box with questionmark)
|
||||
pdfDocEncodingToUnicode[0xa0] = toCharCode('\u20AC'); // EURO SIGN
|
||||
pdfDocEncodingToUnicode[0xad] = toCharCode('\uFFFD'); // REPLACEMENT CHARACTER (box with questionmark)
|
||||
|
||||
/**
|
||||
* Decode a byte array into a string using PDFDocEncoding.
|
||||
*
|
||||
* @param bytes a byte array (decimal representation) containing a string
|
||||
* encoded with PDFDocEncoding.
|
||||
*/
|
||||
export const pdfDocEncodingDecode = (bytes: Uint8Array): string => {
|
||||
const codePoints = new Array(bytes.length);
|
||||
for (let idx = 0, len = bytes.length; idx < len; idx++) {
|
||||
codePoints[idx] = pdfDocEncodingToUnicode[bytes[idx]];
|
||||
}
|
||||
return String.fromCodePoint(...codePoints);
|
||||
};
|
||||
70
frontend/node_modules/pdf-lib/src/utils/png.ts
generated
vendored
Normal file
70
frontend/node_modules/pdf-lib/src/utils/png.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import UPNG from '@pdf-lib/upng';
|
||||
|
||||
const getImageType = (ctype: number) => {
|
||||
if (ctype === 0) return PngType.Greyscale;
|
||||
if (ctype === 2) return PngType.Truecolour;
|
||||
if (ctype === 3) return PngType.IndexedColour;
|
||||
if (ctype === 4) return PngType.GreyscaleWithAlpha;
|
||||
if (ctype === 6) return PngType.TruecolourWithAlpha;
|
||||
throw new Error(`Unknown color type: ${ctype}`);
|
||||
};
|
||||
|
||||
const splitAlphaChannel = (rgbaChannel: Uint8Array) => {
|
||||
const pixelCount = Math.floor(rgbaChannel.length / 4);
|
||||
|
||||
const rgbChannel = new Uint8Array(pixelCount * 3);
|
||||
const alphaChannel = new Uint8Array(pixelCount * 1);
|
||||
|
||||
let rgbaOffset = 0;
|
||||
let rgbOffset = 0;
|
||||
let alphaOffset = 0;
|
||||
|
||||
while (rgbaOffset < rgbaChannel.length) {
|
||||
rgbChannel[rgbOffset++] = rgbaChannel[rgbaOffset++];
|
||||
rgbChannel[rgbOffset++] = rgbaChannel[rgbaOffset++];
|
||||
rgbChannel[rgbOffset++] = rgbaChannel[rgbaOffset++];
|
||||
alphaChannel[alphaOffset++] = rgbaChannel[rgbaOffset++];
|
||||
}
|
||||
|
||||
return { rgbChannel, alphaChannel };
|
||||
};
|
||||
|
||||
export enum PngType {
|
||||
Greyscale = 'Greyscale',
|
||||
Truecolour = 'Truecolour',
|
||||
IndexedColour = 'IndexedColour',
|
||||
GreyscaleWithAlpha = 'GreyscaleWithAlpha',
|
||||
TruecolourWithAlpha = 'TruecolourWithAlpha',
|
||||
}
|
||||
|
||||
export class PNG {
|
||||
static load = (pngData: Uint8Array) => new PNG(pngData);
|
||||
|
||||
readonly rgbChannel: Uint8Array;
|
||||
readonly alphaChannel?: Uint8Array;
|
||||
readonly type: PngType;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
readonly bitsPerComponent: number;
|
||||
|
||||
private constructor(pngData: Uint8Array) {
|
||||
const upng = UPNG.decode(pngData);
|
||||
const frames = UPNG.toRGBA8(upng);
|
||||
|
||||
if (frames.length > 1) throw new Error(`Animated PNGs are not supported`);
|
||||
|
||||
const frame = new Uint8Array(frames[0]);
|
||||
const { rgbChannel, alphaChannel } = splitAlphaChannel(frame);
|
||||
|
||||
this.rgbChannel = rgbChannel;
|
||||
|
||||
const hasAlphaValues = alphaChannel.some((a) => a < 255);
|
||||
if (hasAlphaValues) this.alphaChannel = alphaChannel;
|
||||
|
||||
this.type = getImageType(upng.ctype);
|
||||
|
||||
this.width = upng.width;
|
||||
this.height = upng.height;
|
||||
this.bitsPerComponent = 8;
|
||||
}
|
||||
}
|
||||
21
frontend/node_modules/pdf-lib/src/utils/rng.ts
generated
vendored
Normal file
21
frontend/node_modules/pdf-lib/src/utils/rng.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Generates a pseudo random number. Although it is not cryptographically secure
|
||||
* and uniformly distributed, it is not a concern for the intended use-case,
|
||||
* which is to generate distinct numbers.
|
||||
*
|
||||
* Credit: https://stackoverflow.com/a/19303725/10254049
|
||||
*/
|
||||
export class SimpleRNG {
|
||||
static withSeed = (seed: number) => new SimpleRNG(seed);
|
||||
|
||||
private seed: number;
|
||||
|
||||
private constructor(seed: number) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
nextInt(): number {
|
||||
const x = Math.sin(this.seed++) * 10000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
}
|
||||
182
frontend/node_modules/pdf-lib/src/utils/strings.ts
generated
vendored
Normal file
182
frontend/node_modules/pdf-lib/src/utils/strings.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
export const toCharCode = (character: string) => character.charCodeAt(0);
|
||||
|
||||
export const toCodePoint = (character: string) => character.codePointAt(0);
|
||||
|
||||
export const toHexStringOfMinLength = (num: number, minLength: number) =>
|
||||
padStart(num.toString(16), minLength, '0').toUpperCase();
|
||||
|
||||
export const toHexString = (num: number) => toHexStringOfMinLength(num, 2);
|
||||
|
||||
export const charFromCode = (code: number) => String.fromCharCode(code);
|
||||
|
||||
export const charFromHexCode = (hex: string) => charFromCode(parseInt(hex, 16));
|
||||
|
||||
export const padStart = (value: string, length: number, padChar: string) => {
|
||||
let padding = '';
|
||||
for (let idx = 0, len = length - value.length; idx < len; idx++) {
|
||||
padding += padChar;
|
||||
}
|
||||
return padding + value;
|
||||
};
|
||||
|
||||
export const copyStringIntoBuffer = (
|
||||
str: string,
|
||||
buffer: Uint8Array,
|
||||
offset: number,
|
||||
): number => {
|
||||
const length = str.length;
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
buffer[offset++] = str.charCodeAt(idx);
|
||||
}
|
||||
return length;
|
||||
};
|
||||
|
||||
export const addRandomSuffix = (prefix: string, suffixLength = 4) =>
|
||||
`${prefix}-${Math.floor(Math.random() * 10 ** suffixLength)}`;
|
||||
|
||||
export const escapeRegExp = (str: string) =>
|
||||
str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
export const cleanText = (text: string) =>
|
||||
text.replace(/\t|\u0085|\u2028|\u2029/g, ' ').replace(/[\b\v]/g, '');
|
||||
|
||||
export const escapedNewlineChars = ['\\n', '\\f', '\\r', '\\u000B'];
|
||||
|
||||
export const newlineChars = ['\n', '\f', '\r', '\u000B'];
|
||||
|
||||
export const isNewlineChar = (text: string) => /^[\n\f\r\u000B]$/.test(text);
|
||||
|
||||
export const lineSplit = (text: string) => text.split(/[\n\f\r\u000B]/);
|
||||
|
||||
export const mergeLines = (text: string) =>
|
||||
text.replace(/[\n\f\r\u000B]/g, ' ');
|
||||
|
||||
// JavaScript's String.charAt() method doesn work on strings containing UTF-16
|
||||
// characters (with high and low surrogate pairs), such as 💩 (poo emoji). This
|
||||
// `charAtIndex()` function does.
|
||||
//
|
||||
// Credit: https://github.com/mathiasbynens/String.prototype.at/blob/master/at.js#L14-L48
|
||||
export const charAtIndex = (text: string, index: number): [string, number] => {
|
||||
// Get the first code unit and code unit value
|
||||
const cuFirst = text.charCodeAt(index);
|
||||
let cuSecond: number;
|
||||
const nextIndex = index + 1;
|
||||
let length = 1;
|
||||
if (
|
||||
// Check if it's the start of a surrogate pair.
|
||||
cuFirst >= 0xd800 &&
|
||||
cuFirst <= 0xdbff && // high surrogate
|
||||
text.length > nextIndex // there is a next code unit
|
||||
) {
|
||||
cuSecond = text.charCodeAt(nextIndex);
|
||||
if (cuSecond >= 0xdc00 && cuSecond <= 0xdfff) length = 2; // low surrogate
|
||||
}
|
||||
return [text.slice(index, index + length), length];
|
||||
};
|
||||
|
||||
export const charSplit = (text: string) => {
|
||||
const chars: string[] = [];
|
||||
|
||||
for (let idx = 0, len = text.length; idx < len; ) {
|
||||
const [c, cLen] = charAtIndex(text, idx);
|
||||
chars.push(c);
|
||||
idx += cLen;
|
||||
}
|
||||
|
||||
return chars;
|
||||
};
|
||||
|
||||
const buildWordBreakRegex = (wordBreaks: string[]) => {
|
||||
const newlineCharUnion = escapedNewlineChars.join('|');
|
||||
|
||||
const escapedRules: string[] = ['$'];
|
||||
for (let idx = 0, len = wordBreaks.length; idx < len; idx++) {
|
||||
const wordBreak = wordBreaks[idx];
|
||||
if (isNewlineChar(wordBreak)) {
|
||||
throw new TypeError(`\`wordBreak\` must not include ${newlineCharUnion}`);
|
||||
}
|
||||
escapedRules.push(wordBreak === '' ? '.' : escapeRegExp(wordBreak));
|
||||
}
|
||||
|
||||
const breakRules = escapedRules.join('|');
|
||||
return new RegExp(`(${newlineCharUnion})|((.*?)(${breakRules}))`, 'gm');
|
||||
};
|
||||
|
||||
export const breakTextIntoLines = (
|
||||
text: string,
|
||||
wordBreaks: string[],
|
||||
maxWidth: number,
|
||||
computeWidthOfText: (t: string) => number,
|
||||
): string[] => {
|
||||
const regex = buildWordBreakRegex(wordBreaks);
|
||||
|
||||
const words = cleanText(text).match(regex)!;
|
||||
|
||||
let currLine = '';
|
||||
let currWidth = 0;
|
||||
const lines: string[] = [];
|
||||
|
||||
const pushCurrLine = () => {
|
||||
if (currLine !== '') lines.push(currLine);
|
||||
currLine = '';
|
||||
currWidth = 0;
|
||||
};
|
||||
|
||||
for (let idx = 0, len = words.length; idx < len; idx++) {
|
||||
const word = words[idx];
|
||||
if (isNewlineChar(word)) {
|
||||
pushCurrLine();
|
||||
} else {
|
||||
const width = computeWidthOfText(word);
|
||||
if (currWidth + width > maxWidth) pushCurrLine();
|
||||
currLine += word;
|
||||
currWidth += width;
|
||||
}
|
||||
}
|
||||
pushCurrLine();
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
// See section "7.9.4 Dates" of the PDF specification
|
||||
const dateRegex = /^D:(\d\d\d\d)(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([+\-Z])?(\d\d)?'?(\d\d)?'?$/;
|
||||
|
||||
export const parseDate = (dateStr: string): Date | undefined => {
|
||||
const match = dateStr.match(dateRegex);
|
||||
|
||||
if (!match) return undefined;
|
||||
|
||||
const [
|
||||
,
|
||||
year,
|
||||
month = '01',
|
||||
day = '01',
|
||||
hours = '00',
|
||||
mins = '00',
|
||||
secs = '00',
|
||||
offsetSign = 'Z',
|
||||
offsetHours = '00',
|
||||
offsetMins = '00',
|
||||
] = match;
|
||||
|
||||
// http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
|
||||
const tzOffset =
|
||||
offsetSign === 'Z' ? 'Z' : `${offsetSign}${offsetHours}:${offsetMins}`;
|
||||
const date = new Date(
|
||||
`${year}-${month}-${day}T${hours}:${mins}:${secs}${tzOffset}`,
|
||||
);
|
||||
|
||||
return date;
|
||||
};
|
||||
|
||||
export const findLastMatch = (value: string, regex: RegExp) => {
|
||||
let position = 0;
|
||||
let lastMatch: RegExpMatchArray | undefined;
|
||||
while (position < value.length) {
|
||||
const match = value.substring(position).match(regex);
|
||||
if (!match) return { match: lastMatch, pos: position };
|
||||
lastMatch = match;
|
||||
position += (match.index ?? 0) + match[0].length;
|
||||
}
|
||||
return { match: lastMatch, pos: position };
|
||||
};
|
||||
386
frontend/node_modules/pdf-lib/src/utils/unicode.ts
generated
vendored
Normal file
386
frontend/node_modules/pdf-lib/src/utils/unicode.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
import { toHexString } from 'src/utils/strings';
|
||||
|
||||
/**
|
||||
* Encodes a string to UTF-8.
|
||||
*
|
||||
* @param input The string to be encoded.
|
||||
* @param byteOrderMark Whether or not a byte order marker (BOM) should be added
|
||||
* to the start of the encoding. (default `true`)
|
||||
* @returns A Uint8Array containing the UTF-8 encoding of the input string.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* JavaScript strings are composed of Unicode code points. Code points are
|
||||
* integers in the range 0 to 1,114,111 (0x10FFFF). When serializing a string,
|
||||
* it must be encoded as a sequence of words. A word is typically 8, 16, or 32
|
||||
* bytes in size. As such, Unicode defines three encoding forms: UTF-8, UTF-16,
|
||||
* and UTF-32. These encoding forms are described in the Unicode standard [1].
|
||||
* This function implements the UTF-8 encoding form.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* In UTF-8, each code point is mapped to a sequence of 1, 2, 3, or 4 bytes.
|
||||
* Note that the logic which defines this mapping is slightly convoluted, and
|
||||
* not as straightforward as the mapping logic for UTF-16 or UTF-32. The UTF-8
|
||||
* mapping logic is as follows [2]:
|
||||
*
|
||||
* • If a code point is in the range U+0000..U+007F, then view it as a 7-bit
|
||||
* integer: 0bxxxxxxx. Map the code point to 1 byte with the first high order
|
||||
* bit set to 0:
|
||||
*
|
||||
* b1=0b0xxxxxxx
|
||||
*
|
||||
* • If a code point is in the range U+0080..U+07FF, then view it as an 11-bit
|
||||
* integer: 0byyyyyxxxxxx. Map the code point to 2 bytes with the first 5 bits
|
||||
* of the code point stored in the first byte, and the last 6 bits stored in
|
||||
* the second byte:
|
||||
*
|
||||
* b1=0b110yyyyy b2=0b10xxxxxx
|
||||
*
|
||||
* • If a code point is in the range U+0800..U+FFFF, then view it as a 16-bit
|
||||
* integer, 0bzzzzyyyyyyxxxxxx. Map the code point to 3 bytes with the first
|
||||
* 4 bits stored in the first byte, the next 6 bits stored in the second byte,
|
||||
* and the last 6 bits in the third byte:
|
||||
*
|
||||
* b1=0b1110zzzz b2=0b10yyyyyy b3=0b10xxxxxx
|
||||
*
|
||||
* • If a code point is in the range U+10000...U+10FFFF, then view it as a
|
||||
* 21-bit integer, 0bvvvzzzzzzyyyyyyxxxxxx. Map the code point to 4 bytes with
|
||||
* the first 3 bits stored in the first byte, the next 6 bits stored in the
|
||||
* second byte, the next 6 bits stored in the third byte, and the last 6 bits
|
||||
* stored in the fourth byte:
|
||||
*
|
||||
* b1=0b11110xxx b2=0b10zzzzzz b3=0b10yyyyyy b4=0b10xxxxxx
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* It is important to note, when iterating through the code points of a string
|
||||
* in JavaScript, that if a character is encoded as a surrogate pair it will
|
||||
* increase the string's length by 2 instead of 1 [4]. For example:
|
||||
*
|
||||
* ```
|
||||
* > 'a'.length
|
||||
* 1
|
||||
* > '💩'.length
|
||||
* 2
|
||||
* > '語'.length
|
||||
* 1
|
||||
* > 'a💩語'.length
|
||||
* 4
|
||||
* ```
|
||||
*
|
||||
* The results of the above example are explained by the fact that the
|
||||
* characters 'a' and '語' are not represented by surrogate pairs, but '💩' is.
|
||||
*
|
||||
* Because of this idiosyncrasy in JavaScript's string implementation and APIs,
|
||||
* we must "jump" an extra index after encoding a character as a surrogate
|
||||
* pair. In practice, this means we must increment the index of our for loop by
|
||||
* 2 if we encode a surrogate pair, and 1 in all other cases.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* References:
|
||||
* - [1] https://www.unicode.org/versions/Unicode12.0.0/UnicodeStandard-12.0.pdf
|
||||
* 3.9 Unicode Encoding Forms - UTF-8
|
||||
* - [2] http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding.html
|
||||
* - [3] http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding-Algorithm.html
|
||||
* - [4] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length#Description
|
||||
*
|
||||
*/
|
||||
export const utf8Encode = (input: string, byteOrderMark = true): Uint8Array => {
|
||||
const encoded = [];
|
||||
|
||||
if (byteOrderMark) encoded.push(0xef, 0xbb, 0xbf);
|
||||
|
||||
for (let idx = 0, len = input.length; idx < len; ) {
|
||||
const codePoint = input.codePointAt(idx)!;
|
||||
|
||||
// One byte encoding
|
||||
if (codePoint < 0x80) {
|
||||
const byte1 = codePoint & 0x7f;
|
||||
encoded.push(byte1);
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
// Two byte encoding
|
||||
else if (codePoint < 0x0800) {
|
||||
const byte1 = ((codePoint >> 6) & 0x1f) | 0xc0;
|
||||
const byte2 = (codePoint & 0x3f) | 0x80;
|
||||
encoded.push(byte1, byte2);
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
// Three byte encoding
|
||||
else if (codePoint < 0x010000) {
|
||||
const byte1 = ((codePoint >> 12) & 0x0f) | 0xe0;
|
||||
const byte2 = ((codePoint >> 6) & 0x3f) | 0x80;
|
||||
const byte3 = (codePoint & 0x3f) | 0x80;
|
||||
encoded.push(byte1, byte2, byte3);
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
// Four byte encoding (surrogate pair)
|
||||
else if (codePoint < 0x110000) {
|
||||
const byte1 = ((codePoint >> 18) & 0x07) | 0xf0;
|
||||
const byte2 = ((codePoint >> 12) & 0x3f) | 0x80;
|
||||
const byte3 = ((codePoint >> 6) & 0x3f) | 0x80;
|
||||
const byte4 = ((codePoint >> 0) & 0x3f) | 0x80;
|
||||
encoded.push(byte1, byte2, byte3, byte4);
|
||||
idx += 2;
|
||||
}
|
||||
|
||||
// Should never reach this case
|
||||
else throw new Error(`Invalid code point: 0x${toHexString(codePoint)}`);
|
||||
}
|
||||
|
||||
return new Uint8Array(encoded);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes a string to UTF-16.
|
||||
*
|
||||
* @param input The string to be encoded.
|
||||
* @param byteOrderMark Whether or not a byte order marker (BOM) should be added
|
||||
* to the start of the encoding. (default `true`)
|
||||
* @returns A Uint16Array containing the UTF-16 encoding of the input string.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* JavaScript strings are composed of Unicode code points. Code points are
|
||||
* integers in the range 0 to 1,114,111 (0x10FFFF). When serializing a string,
|
||||
* it must be encoded as a sequence of words. A word is typically 8, 16, or 32
|
||||
* bytes in size. As such, Unicode defines three encoding forms: UTF-8, UTF-16,
|
||||
* and UTF-32. These encoding forms are described in the Unicode standard [1].
|
||||
* This function implements the UTF-16 encoding form.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* In UTF-16, each code point is mapped to one or two 16-bit integers. The
|
||||
* UTF-16 mapping logic is as follows [2]:
|
||||
*
|
||||
* • If a code point is in the range U+0000..U+FFFF, then map the code point to
|
||||
* a 16-bit integer with the most significant byte first.
|
||||
*
|
||||
* • If a code point is in the range U+10000..U+10000, then map the code point
|
||||
* to two 16-bit integers. The first integer should contain the high surrogate
|
||||
* and the second integer should contain the low surrogate. Both surrogates
|
||||
* should be written with the most significant byte first.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* It is important to note, when iterating through the code points of a string
|
||||
* in JavaScript, that if a character is encoded as a surrogate pair it will
|
||||
* increase the string's length by 2 instead of 1 [4]. For example:
|
||||
*
|
||||
* ```
|
||||
* > 'a'.length
|
||||
* 1
|
||||
* > '💩'.length
|
||||
* 2
|
||||
* > '語'.length
|
||||
* 1
|
||||
* > 'a💩語'.length
|
||||
* 4
|
||||
* ```
|
||||
*
|
||||
* The results of the above example are explained by the fact that the
|
||||
* characters 'a' and '語' are not represented by surrogate pairs, but '💩' is.
|
||||
*
|
||||
* Because of this idiosyncrasy in JavaScript's string implementation and APIs,
|
||||
* we must "jump" an extra index after encoding a character as a surrogate
|
||||
* pair. In practice, this means we must increment the index of our for loop by
|
||||
* 2 if we encode a surrogate pair, and 1 in all other cases.
|
||||
*
|
||||
* -----------------------------------------------------------------------------
|
||||
*
|
||||
* References:
|
||||
* - [1] https://www.unicode.org/versions/Unicode12.0.0/UnicodeStandard-12.0.pdf
|
||||
* 3.9 Unicode Encoding Forms - UTF-8
|
||||
* - [2] http://www.herongyang.com/Unicode/UTF-16-UTF-16-Encoding.html
|
||||
* - [3] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length#Description
|
||||
*
|
||||
*/
|
||||
export const utf16Encode = (
|
||||
input: string,
|
||||
byteOrderMark = true,
|
||||
): Uint16Array => {
|
||||
const encoded = [];
|
||||
|
||||
if (byteOrderMark) encoded.push(0xfeff);
|
||||
|
||||
for (let idx = 0, len = input.length; idx < len; ) {
|
||||
const codePoint = input.codePointAt(idx)!;
|
||||
|
||||
// Two byte encoding
|
||||
if (codePoint < 0x010000) {
|
||||
encoded.push(codePoint);
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
// Four byte encoding (surrogate pair)
|
||||
else if (codePoint < 0x110000) {
|
||||
encoded.push(highSurrogate(codePoint), lowSurrogate(codePoint));
|
||||
idx += 2;
|
||||
}
|
||||
|
||||
// Should never reach this case
|
||||
else throw new Error(`Invalid code point: 0x${toHexString(codePoint)}`);
|
||||
}
|
||||
|
||||
return new Uint16Array(encoded);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if the `codePoint` is within the
|
||||
* Basic Multilingual Plane (BMP). Code points inside the BMP are not encoded
|
||||
* with surrogate pairs.
|
||||
* @param codePoint The code point to be evaluated.
|
||||
*
|
||||
* Reference: https://en.wikipedia.org/wiki/UTF-16#Description
|
||||
*/
|
||||
export const isWithinBMP = (codePoint: number) =>
|
||||
codePoint >= 0 && codePoint <= 0xffff;
|
||||
|
||||
/**
|
||||
* Returns `true` if the given `codePoint` is valid and must be represented
|
||||
* with a surrogate pair when encoded.
|
||||
* @param codePoint The code point to be evaluated.
|
||||
*
|
||||
* Reference: https://en.wikipedia.org/wiki/UTF-16#Description
|
||||
*/
|
||||
export const hasSurrogates = (codePoint: number) =>
|
||||
codePoint >= 0x010000 && codePoint <= 0x10ffff;
|
||||
|
||||
// From Unicode 3.0 spec, section 3.7:
|
||||
// http://unicode.org/versions/Unicode3.0.0/ch03.pdf
|
||||
export const highSurrogate = (codePoint: number) =>
|
||||
Math.floor((codePoint - 0x10000) / 0x400) + 0xd800;
|
||||
|
||||
// From Unicode 3.0 spec, section 3.7:
|
||||
// http://unicode.org/versions/Unicode3.0.0/ch03.pdf
|
||||
export const lowSurrogate = (codePoint: number) =>
|
||||
((codePoint - 0x10000) % 0x400) + 0xdc00;
|
||||
|
||||
enum ByteOrder {
|
||||
BigEndian = 'BigEndian',
|
||||
LittleEndian = 'LittleEndian',
|
||||
}
|
||||
|
||||
const REPLACEMENT = '<27>'.codePointAt(0)!;
|
||||
|
||||
/**
|
||||
* Decodes a Uint8Array of data to a string using UTF-16.
|
||||
*
|
||||
* Note that this function attempts to recover from erronous input by
|
||||
* inserting the replacement character (<EFBFBD>) to mark invalid code points
|
||||
* and surrogate pairs.
|
||||
*
|
||||
* @param input A Uint8Array containing UTF-16 encoded data
|
||||
* @param byteOrderMark Whether or not a byte order marker (BOM) should be read
|
||||
* at the start of the encoding. (default `true`)
|
||||
* @returns The decoded string.
|
||||
*/
|
||||
export const utf16Decode = (
|
||||
input: Uint8Array,
|
||||
byteOrderMark = true,
|
||||
): string => {
|
||||
// Need at least 2 bytes of data in UTF-16 encodings
|
||||
if (input.length <= 1) return String.fromCodePoint(REPLACEMENT);
|
||||
|
||||
const byteOrder = byteOrderMark ? readBOM(input) : ByteOrder.BigEndian;
|
||||
|
||||
// Skip byte order mark if needed
|
||||
let idx = byteOrderMark ? 2 : 0;
|
||||
|
||||
const codePoints: number[] = [];
|
||||
|
||||
while (input.length - idx >= 2) {
|
||||
const first = decodeValues(input[idx++], input[idx++], byteOrder);
|
||||
|
||||
if (isHighSurrogate(first)) {
|
||||
if (input.length - idx < 2) {
|
||||
// Need at least 2 bytes left for the low surrogate that is required
|
||||
codePoints.push(REPLACEMENT);
|
||||
} else {
|
||||
const second = decodeValues(input[idx++], input[idx++], byteOrder);
|
||||
if (isLowSurrogate(second)) {
|
||||
codePoints.push(first, second);
|
||||
} else {
|
||||
// Low surrogates should always follow high surrogates
|
||||
codePoints.push(REPLACEMENT);
|
||||
}
|
||||
}
|
||||
} else if (isLowSurrogate(first)) {
|
||||
// High surrogates should always come first since `decodeValues()`
|
||||
// accounts for the byte ordering
|
||||
idx += 2;
|
||||
codePoints.push(REPLACEMENT);
|
||||
} else {
|
||||
codePoints.push(first);
|
||||
}
|
||||
}
|
||||
|
||||
// There shouldn't be extra byte(s) left over
|
||||
if (idx < input.length) codePoints.push(REPLACEMENT);
|
||||
|
||||
return String.fromCodePoint(...codePoints);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if the given `codePoint` is a high surrogate.
|
||||
* @param codePoint The code point to be evaluated.
|
||||
*
|
||||
* Reference: https://en.wikipedia.org/wiki/UTF-16#Description
|
||||
*/
|
||||
const isHighSurrogate = (codePoint: number) =>
|
||||
codePoint >= 0xd800 && codePoint <= 0xdbff;
|
||||
|
||||
/**
|
||||
* Returns `true` if the given `codePoint` is a low surrogate.
|
||||
* @param codePoint The code point to be evaluated.
|
||||
*
|
||||
* Reference: https://en.wikipedia.org/wiki/UTF-16#Description
|
||||
*/
|
||||
const isLowSurrogate = (codePoint: number) =>
|
||||
codePoint >= 0xdc00 && codePoint <= 0xdfff;
|
||||
|
||||
/**
|
||||
* Decodes the given utf-16 values first and second using the specified
|
||||
* byte order.
|
||||
* @param first The first byte of the encoding.
|
||||
* @param second The second byte of the encoding.
|
||||
* @param byteOrder The byte order of the encoding.
|
||||
* Reference: https://en.wikipedia.org/wiki/UTF-16#Examples
|
||||
*/
|
||||
const decodeValues = (first: number, second: number, byteOrder: ByteOrder) => {
|
||||
// Append the binary representation of the preceding byte by shifting the
|
||||
// first one 8 to the left and than applying a bitwise or-operator to append
|
||||
// the second one.
|
||||
if (byteOrder === ByteOrder.LittleEndian) return (second << 8) | first;
|
||||
if (byteOrder === ByteOrder.BigEndian) return (first << 8) | second;
|
||||
throw new Error(`Invalid byteOrder: ${byteOrder}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the given array contains a byte order mark for the
|
||||
* UTF-16BE or UTF-16LE encoding. If it has neither, BigEndian is assumed.
|
||||
*
|
||||
* Reference: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-16
|
||||
*
|
||||
* @param bytes The byte array to be evaluated.
|
||||
*/
|
||||
// prettier-ignore
|
||||
const readBOM = (bytes: Uint8Array): ByteOrder => (
|
||||
hasUtf16BigEndianBOM(bytes) ? ByteOrder.BigEndian
|
||||
: hasUtf16LittleEndianBOM(bytes) ? ByteOrder.LittleEndian
|
||||
: ByteOrder.BigEndian
|
||||
);
|
||||
|
||||
const hasUtf16BigEndianBOM = (bytes: Uint8Array) =>
|
||||
bytes[0] === 0xfe && bytes[1] === 0xff;
|
||||
|
||||
const hasUtf16LittleEndianBOM = (bytes: Uint8Array) =>
|
||||
bytes[0] === 0xff && bytes[1] === 0xfe;
|
||||
|
||||
export const hasUtf16BOM = (bytes: Uint8Array) =>
|
||||
hasUtf16BigEndianBOM(bytes) || hasUtf16LittleEndianBOM(bytes);
|
||||
228
frontend/node_modules/pdf-lib/src/utils/validators.ts
generated
vendored
Normal file
228
frontend/node_modules/pdf-lib/src/utils/validators.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/* tslint:disable:ban-types */
|
||||
|
||||
import { values as objectValues } from 'src/utils/objects';
|
||||
|
||||
export const backtick = (val: any) => `\`${val}\``;
|
||||
export const singleQuote = (val: any) => `'${val}'`;
|
||||
|
||||
type Primitive = string | number | boolean | undefined | null;
|
||||
|
||||
// prettier-ignore
|
||||
const formatValue = (value: any) => {
|
||||
const type = typeof value;
|
||||
if (type ==='string') return singleQuote(value);
|
||||
else if (type ==='undefined') return backtick(value);
|
||||
else return value;
|
||||
};
|
||||
|
||||
export const createValueErrorMsg = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
values: Primitive[],
|
||||
) => {
|
||||
const allowedValues = new Array(values.length);
|
||||
|
||||
for (let idx = 0, len = values.length; idx < len; idx++) {
|
||||
const v = values[idx];
|
||||
allowedValues[idx] = formatValue(v);
|
||||
}
|
||||
|
||||
const joinedValues = allowedValues.join(' or ');
|
||||
|
||||
// prettier-ignore
|
||||
return `${backtick(valueName)} must be one of ${joinedValues}, but was actually ${formatValue(value)}`;
|
||||
};
|
||||
|
||||
export const assertIsOneOf = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
allowedValues: Primitive[] | { [key: string]: Primitive },
|
||||
) => {
|
||||
if (!Array.isArray(allowedValues)) {
|
||||
allowedValues = objectValues(allowedValues);
|
||||
}
|
||||
for (let idx = 0, len = allowedValues.length; idx < len; idx++) {
|
||||
if (value === allowedValues[idx]) return;
|
||||
}
|
||||
throw new TypeError(createValueErrorMsg(value, valueName, allowedValues));
|
||||
};
|
||||
|
||||
export const assertIsOneOfOrUndefined = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
allowedValues: Primitive[] | { [key: string]: Primitive },
|
||||
) => {
|
||||
if (!Array.isArray(allowedValues)) {
|
||||
allowedValues = objectValues(allowedValues);
|
||||
}
|
||||
assertIsOneOf(value, valueName, allowedValues.concat(undefined));
|
||||
};
|
||||
|
||||
export const assertIsSubset = (
|
||||
values: any[],
|
||||
valueName: string,
|
||||
allowedValues: Primitive[] | { [key: string]: Primitive },
|
||||
) => {
|
||||
if (!Array.isArray(allowedValues)) {
|
||||
allowedValues = objectValues(allowedValues);
|
||||
}
|
||||
for (let idx = 0, len = values.length; idx < len; idx++) {
|
||||
assertIsOneOf(values[idx], valueName, allowedValues);
|
||||
}
|
||||
};
|
||||
|
||||
export const getType = (val: any) => {
|
||||
if (val === null) return 'null';
|
||||
if (val === undefined) return 'undefined';
|
||||
if (typeof val === 'string') return 'string';
|
||||
if (isNaN(val)) return 'NaN';
|
||||
if (typeof val === 'number') return 'number';
|
||||
if (typeof val === 'boolean') return 'boolean';
|
||||
if (typeof val === 'symbol') return 'symbol';
|
||||
if (typeof val === 'bigint') return 'bigint';
|
||||
if (val.constructor && val.constructor.name) return val.constructor.name;
|
||||
if (val.name) return val.name;
|
||||
if (val.constructor) return String(val.constructor);
|
||||
return String(val);
|
||||
};
|
||||
|
||||
export type TypeDescriptor =
|
||||
| 'null'
|
||||
| 'undefined'
|
||||
| 'string'
|
||||
| 'number'
|
||||
| 'boolean'
|
||||
| 'symbol'
|
||||
| 'bigint'
|
||||
| DateConstructor
|
||||
| ArrayConstructor
|
||||
| Uint8ArrayConstructor
|
||||
| ArrayBufferConstructor
|
||||
| FunctionConstructor
|
||||
| [Function, string];
|
||||
|
||||
export const isType = (value: any, type: TypeDescriptor) => {
|
||||
if (type === 'null') return value === null;
|
||||
if (type === 'undefined') return value === undefined;
|
||||
if (type === 'string') return typeof value === 'string';
|
||||
if (type === 'number') return typeof value === 'number' && !isNaN(value);
|
||||
if (type === 'boolean') return typeof value === 'boolean';
|
||||
if (type === 'symbol') return typeof value === 'symbol';
|
||||
if (type === 'bigint') return typeof value === 'bigint';
|
||||
if (type === Date) return value instanceof Date;
|
||||
if (type === Array) return value instanceof Array;
|
||||
if (type === Uint8Array) return value instanceof Uint8Array;
|
||||
if (type === ArrayBuffer) return value instanceof ArrayBuffer;
|
||||
if (type === Function) return value instanceof Function;
|
||||
return value instanceof (type as [Function, string])[0];
|
||||
};
|
||||
|
||||
export const createTypeErrorMsg = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
types: TypeDescriptor[],
|
||||
) => {
|
||||
const allowedTypes = new Array(types.length);
|
||||
|
||||
for (let idx = 0, len = types.length; idx < len; idx++) {
|
||||
const type = types[idx];
|
||||
if (type === 'null') allowedTypes[idx] = backtick('null');
|
||||
if (type === 'undefined') allowedTypes[idx] = backtick('undefined');
|
||||
if (type === 'string') allowedTypes[idx] = backtick('string');
|
||||
else if (type === 'number') allowedTypes[idx] = backtick('number');
|
||||
else if (type === 'boolean') allowedTypes[idx] = backtick('boolean');
|
||||
else if (type === 'symbol') allowedTypes[idx] = backtick('symbol');
|
||||
else if (type === 'bigint') allowedTypes[idx] = backtick('bigint');
|
||||
else if (type === Array) allowedTypes[idx] = backtick('Array');
|
||||
else if (type === Uint8Array) allowedTypes[idx] = backtick('Uint8Array');
|
||||
else if (type === ArrayBuffer) allowedTypes[idx] = backtick('ArrayBuffer');
|
||||
else allowedTypes[idx] = backtick((type as [Function, string])[1]);
|
||||
}
|
||||
|
||||
const joinedTypes = allowedTypes.join(' or ');
|
||||
|
||||
// prettier-ignore
|
||||
return `${backtick(valueName)} must be of type ${joinedTypes}, but was actually of type ${backtick(getType(value))}`;
|
||||
};
|
||||
|
||||
export const assertIs = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
types: TypeDescriptor[],
|
||||
) => {
|
||||
for (let idx = 0, len = types.length; idx < len; idx++) {
|
||||
if (isType(value, types[idx])) return;
|
||||
}
|
||||
throw new TypeError(createTypeErrorMsg(value, valueName, types));
|
||||
};
|
||||
|
||||
export const assertOrUndefined = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
types: TypeDescriptor[],
|
||||
) => {
|
||||
assertIs(value, valueName, types.concat('undefined'));
|
||||
};
|
||||
|
||||
export const assertEachIs = (
|
||||
values: any[],
|
||||
valueName: string,
|
||||
types: TypeDescriptor[],
|
||||
) => {
|
||||
for (let idx = 0, len = values.length; idx < len; idx++) {
|
||||
assertIs(values[idx], valueName, types);
|
||||
}
|
||||
};
|
||||
|
||||
export const assertRange = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
min: number,
|
||||
max: number,
|
||||
) => {
|
||||
assertIs(value, valueName, ['number']);
|
||||
assertIs(min, 'min', ['number']);
|
||||
assertIs(max, 'max', ['number']);
|
||||
max = Math.max(min, max);
|
||||
if (value < min || value > max) {
|
||||
// prettier-ignore
|
||||
throw new Error(`${backtick(valueName)} must be at least ${min} and at most ${max}, but was actually ${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const assertRangeOrUndefined = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
min: number,
|
||||
max: number,
|
||||
) => {
|
||||
assertIs(value, valueName, ['number', 'undefined']);
|
||||
if (typeof value === 'number') assertRange(value, valueName, min, max);
|
||||
};
|
||||
|
||||
export const assertMultiple = (
|
||||
value: any,
|
||||
valueName: string,
|
||||
multiplier: number,
|
||||
) => {
|
||||
assertIs(value, valueName, ['number']);
|
||||
if (value % multiplier !== 0) {
|
||||
// prettier-ignore
|
||||
throw new Error(`${backtick(valueName)} must be a multiple of ${multiplier}, but was actually ${value}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const assertInteger = (value: any, valueName: string) => {
|
||||
if (!Number.isInteger(value)) {
|
||||
throw new Error(
|
||||
`${backtick(valueName)} must be an integer, but was actually ${value}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const assertPositive = (value: number, valueName: string) => {
|
||||
if (![1, 0].includes(Math.sign(value))) {
|
||||
// prettier-ignore
|
||||
throw new Error(`${backtick(valueName)} must be a positive number or 0, but was actually ${value}`);
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue