UNPKG

pdfjs-dist

Version:

Generic build of Mozilla's PDF.js library.

1,787 lines (1,779 loc) 798 kB
/** * @licstart The following is the entire license notice for the * JavaScript code in this page * * Copyright 2024 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @licend The above is the entire license notice for the * JavaScript code in this page */ /** * pdfjsVersion = 5.4.449 * pdfjsBuild = f44e5f0e6 */ /******/ // The require scope /******/ var __webpack_require__ = {}; /******/ /************************************************************************/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; ;// ./src/shared/util.js const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; const LINE_FACTOR = 1.35; const LINE_DESCENT_FACTOR = 0.35; const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR; const RenderingIntentFlag = { ANY: 0x01, DISPLAY: 0x02, PRINT: 0x04, SAVE: 0x08, ANNOTATIONS_FORMS: 0x10, ANNOTATIONS_STORAGE: 0x20, ANNOTATIONS_DISABLE: 0x40, IS_EDITING: 0x80, OPLIST: 0x100 }; const AnnotationMode = { DISABLE: 0, ENABLE: 1, ENABLE_FORMS: 2, ENABLE_STORAGE: 3 }; const AnnotationEditorPrefix = "pdfjs_internal_editor_"; const AnnotationEditorType = { DISABLE: -1, NONE: 0, FREETEXT: 3, HIGHLIGHT: 9, STAMP: 13, INK: 15, POPUP: 16, SIGNATURE: 101, COMMENT: 102 }; const AnnotationEditorParamsType = { RESIZE: 1, CREATE: 2, FREETEXT_SIZE: 11, FREETEXT_COLOR: 12, FREETEXT_OPACITY: 13, INK_COLOR: 21, INK_THICKNESS: 22, INK_OPACITY: 23, HIGHLIGHT_COLOR: 31, HIGHLIGHT_THICKNESS: 32, HIGHLIGHT_FREE: 33, HIGHLIGHT_SHOW_ALL: 34, DRAW_STEP: 41 }; const PermissionFlag = { PRINT: 0x04, MODIFY_CONTENTS: 0x08, COPY: 0x10, MODIFY_ANNOTATIONS: 0x20, FILL_INTERACTIVE_FORMS: 0x100, COPY_FOR_ACCESSIBILITY: 0x200, ASSEMBLE: 0x400, PRINT_HIGH_QUALITY: 0x800 }; const MeshFigureType = { TRIANGLES: 1, LATTICE: 2, PATCH: 3 }; const TextRenderingMode = { FILL: 0, STROKE: 1, FILL_STROKE: 2, INVISIBLE: 3, FILL_ADD_TO_PATH: 4, STROKE_ADD_TO_PATH: 5, FILL_STROKE_ADD_TO_PATH: 6, ADD_TO_PATH: 7, FILL_STROKE_MASK: 3, ADD_TO_PATH_FLAG: 4 }; const util_ImageKind = { GRAYSCALE_1BPP: 1, RGB_24BPP: 2, RGBA_32BPP: 3 }; const AnnotationType = { TEXT: 1, LINK: 2, FREETEXT: 3, LINE: 4, SQUARE: 5, CIRCLE: 6, POLYGON: 7, POLYLINE: 8, HIGHLIGHT: 9, UNDERLINE: 10, SQUIGGLY: 11, STRIKEOUT: 12, STAMP: 13, CARET: 14, INK: 15, POPUP: 16, FILEATTACHMENT: 17, SOUND: 18, MOVIE: 19, WIDGET: 20, SCREEN: 21, PRINTERMARK: 22, TRAPNET: 23, WATERMARK: 24, THREED: 25, REDACT: 26 }; const AnnotationReplyType = { GROUP: "Group", REPLY: "R" }; const AnnotationFlag = { INVISIBLE: 0x01, HIDDEN: 0x02, PRINT: 0x04, NOZOOM: 0x08, NOROTATE: 0x10, NOVIEW: 0x20, READONLY: 0x40, LOCKED: 0x80, TOGGLENOVIEW: 0x100, LOCKEDCONTENTS: 0x200 }; const AnnotationFieldFlag = { READONLY: 0x0000001, REQUIRED: 0x0000002, NOEXPORT: 0x0000004, MULTILINE: 0x0001000, PASSWORD: 0x0002000, NOTOGGLETOOFF: 0x0004000, RADIO: 0x0008000, PUSHBUTTON: 0x0010000, COMBO: 0x0020000, EDIT: 0x0040000, SORT: 0x0080000, FILESELECT: 0x0100000, MULTISELECT: 0x0200000, DONOTSPELLCHECK: 0x0400000, DONOTSCROLL: 0x0800000, COMB: 0x1000000, RICHTEXT: 0x2000000, RADIOSINUNISON: 0x2000000, COMMITONSELCHANGE: 0x4000000 }; const AnnotationBorderStyleType = { SOLID: 1, DASHED: 2, BEVELED: 3, INSET: 4, UNDERLINE: 5 }; const AnnotationActionEventType = { E: "Mouse Enter", X: "Mouse Exit", D: "Mouse Down", U: "Mouse Up", Fo: "Focus", Bl: "Blur", PO: "PageOpen", PC: "PageClose", PV: "PageVisible", PI: "PageInvisible", K: "Keystroke", F: "Format", V: "Validate", C: "Calculate" }; const DocumentActionEventType = { WC: "WillClose", WS: "WillSave", DS: "DidSave", WP: "WillPrint", DP: "DidPrint" }; const PageActionEventType = { O: "PageOpen", C: "PageClose" }; const VerbosityLevel = { ERRORS: 0, WARNINGS: 1, INFOS: 5 }; const OPS = { dependency: 1, setLineWidth: 2, setLineCap: 3, setLineJoin: 4, setMiterLimit: 5, setDash: 6, setRenderingIntent: 7, setFlatness: 8, setGState: 9, save: 10, restore: 11, transform: 12, moveTo: 13, lineTo: 14, curveTo: 15, curveTo2: 16, curveTo3: 17, closePath: 18, rectangle: 19, stroke: 20, closeStroke: 21, fill: 22, eoFill: 23, fillStroke: 24, eoFillStroke: 25, closeFillStroke: 26, closeEOFillStroke: 27, endPath: 28, clip: 29, eoClip: 30, beginText: 31, endText: 32, setCharSpacing: 33, setWordSpacing: 34, setHScale: 35, setLeading: 36, setFont: 37, setTextRenderingMode: 38, setTextRise: 39, moveText: 40, setLeadingMoveText: 41, setTextMatrix: 42, nextLine: 43, showText: 44, showSpacedText: 45, nextLineShowText: 46, nextLineSetSpacingShowText: 47, setCharWidth: 48, setCharWidthAndBounds: 49, setStrokeColorSpace: 50, setFillColorSpace: 51, setStrokeColor: 52, setStrokeColorN: 53, setFillColor: 54, setFillColorN: 55, setStrokeGray: 56, setFillGray: 57, setStrokeRGBColor: 58, setFillRGBColor: 59, setStrokeCMYKColor: 60, setFillCMYKColor: 61, shadingFill: 62, beginInlineImage: 63, beginImageData: 64, endInlineImage: 65, paintXObject: 66, markPoint: 67, markPointProps: 68, beginMarkedContent: 69, beginMarkedContentProps: 70, endMarkedContent: 71, beginCompat: 72, endCompat: 73, paintFormXObjectBegin: 74, paintFormXObjectEnd: 75, beginGroup: 76, endGroup: 77, beginAnnotation: 80, endAnnotation: 81, paintImageMaskXObject: 83, paintImageMaskXObjectGroup: 84, paintImageXObject: 85, paintInlineImageXObject: 86, paintInlineImageXObjectGroup: 87, paintImageXObjectRepeat: 88, paintImageMaskXObjectRepeat: 89, paintSolidColorImageMask: 90, constructPath: 91, setStrokeTransparent: 92, setFillTransparent: 93, rawFillPath: 94 }; const DrawOPS = { moveTo: 0, lineTo: 1, curveTo: 2, quadraticCurveTo: 3, closePath: 4 }; const PasswordResponses = { NEED_PASSWORD: 1, INCORRECT_PASSWORD: 2 }; let verbosity = VerbosityLevel.WARNINGS; function setVerbosityLevel(level) { if (Number.isInteger(level)) { verbosity = level; } } function getVerbosityLevel() { return verbosity; } function info(msg) { if (verbosity >= VerbosityLevel.INFOS) { console.info(`Info: ${msg}`); } } function warn(msg) { if (verbosity >= VerbosityLevel.WARNINGS) { console.warn(`Warning: ${msg}`); } } function unreachable(msg) { throw new Error(msg); } function assert(cond, msg) { if (!cond) { unreachable(msg); } } function _isValidProtocol(url) { switch (url?.protocol) { case "http:": case "https:": case "ftp:": case "mailto:": case "tel:": return true; default: return false; } } function createValidAbsoluteUrl(url, baseUrl = null, options = null) { if (!url) { return null; } if (options && typeof url === "string") { if (options.addDefaultProtocol && url.startsWith("www.")) { const dots = url.match(/\./g); if (dots?.length >= 2) { url = `http://${url}`; } } if (options.tryConvertEncoding) { try { url = stringToUTF8String(url); } catch {} } } const absoluteUrl = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url); return _isValidProtocol(absoluteUrl) ? absoluteUrl : null; } function updateUrlHash(url, hash, allowRel = false) { const res = URL.parse(url); if (res) { res.hash = hash; return res.href; } if (allowRel && createValidAbsoluteUrl(url, "http://example.com")) { return url.split("#", 1)[0] + `${hash ? `#${hash}` : ""}`; } return ""; } function shadow(obj, prop, value, nonSerializable = false) { Object.defineProperty(obj, prop, { value, enumerable: !nonSerializable, configurable: true, writable: false }); return value; } const BaseException = function BaseExceptionClosure() { function BaseException(message, name) { this.message = message; this.name = name; } BaseException.prototype = new Error(); BaseException.constructor = BaseException; return BaseException; }(); class PasswordException extends BaseException { constructor(msg, code) { super(msg, "PasswordException"); this.code = code; } } class UnknownErrorException extends BaseException { constructor(msg, details) { super(msg, "UnknownErrorException"); this.details = details; } } class InvalidPDFException extends BaseException { constructor(msg) { super(msg, "InvalidPDFException"); } } class ResponseException extends BaseException { constructor(msg, status, missing) { super(msg, "ResponseException"); this.status = status; this.missing = missing; } } class FormatError extends BaseException { constructor(msg) { super(msg, "FormatError"); } } class AbortException extends BaseException { constructor(msg) { super(msg, "AbortException"); } } function bytesToString(bytes) { if (typeof bytes !== "object" || bytes?.length === undefined) { unreachable("Invalid argument for bytesToString"); } const length = bytes.length; const MAX_ARGUMENT_COUNT = 8192; if (length < MAX_ARGUMENT_COUNT) { return String.fromCharCode.apply(null, bytes); } const strBuf = []; for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) { const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); const chunk = bytes.subarray(i, chunkEnd); strBuf.push(String.fromCharCode.apply(null, chunk)); } return strBuf.join(""); } function stringToBytes(str) { if (typeof str !== "string") { unreachable("Invalid argument for stringToBytes"); } const length = str.length; const bytes = new Uint8Array(length); for (let i = 0; i < length; ++i) { bytes[i] = str.charCodeAt(i) & 0xff; } return bytes; } function string32(value) { return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff); } function objectSize(obj) { return Object.keys(obj).length; } function isLittleEndian() { const buffer8 = new Uint8Array(4); buffer8[0] = 1; const view32 = new Uint32Array(buffer8.buffer, 0, 1); return view32[0] === 1; } function isEvalSupported() { try { new Function(""); return true; } catch { return false; } } class util_FeatureTest { static get isLittleEndian() { return shadow(this, "isLittleEndian", isLittleEndian()); } static get isEvalSupported() { return shadow(this, "isEvalSupported", isEvalSupported()); } static get isOffscreenCanvasSupported() { return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined"); } static get isImageDecoderSupported() { return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined"); } static get isFloat16ArraySupported() { return shadow(this, "isFloat16ArraySupported", typeof Float16Array !== "undefined"); } static get isSanitizerSupported() { return shadow(this, "isSanitizerSupported", typeof Sanitizer !== "undefined"); } static get platform() { const { platform, userAgent } = navigator; return shadow(this, "platform", { isAndroid: userAgent.includes("Android"), isLinux: platform.includes("Linux"), isMac: platform.includes("Mac"), isWindows: platform.includes("Win"), isFirefox: userAgent.includes("Firefox") }); } static get isCSSRoundSupported() { return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)")); } } const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0")); class Util { static makeHexColor(r, g, b) { return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; } static domMatrixToTransform(dm) { return [dm.a, dm.b, dm.c, dm.d, dm.e, dm.f]; } static scaleMinMax(transform, minMax) { let temp; if (transform[0]) { if (transform[0] < 0) { temp = minMax[0]; minMax[0] = minMax[2]; minMax[2] = temp; } minMax[0] *= transform[0]; minMax[2] *= transform[0]; if (transform[3] < 0) { temp = minMax[1]; minMax[1] = minMax[3]; minMax[3] = temp; } minMax[1] *= transform[3]; minMax[3] *= transform[3]; } else { temp = minMax[0]; minMax[0] = minMax[1]; minMax[1] = temp; temp = minMax[2]; minMax[2] = minMax[3]; minMax[3] = temp; if (transform[1] < 0) { temp = minMax[1]; minMax[1] = minMax[3]; minMax[3] = temp; } minMax[1] *= transform[1]; minMax[3] *= transform[1]; if (transform[2] < 0) { temp = minMax[0]; minMax[0] = minMax[2]; minMax[2] = temp; } minMax[0] *= transform[2]; minMax[2] *= transform[2]; } minMax[0] += transform[4]; minMax[1] += transform[5]; minMax[2] += transform[4]; minMax[3] += transform[5]; } static transform(m1, m2) { return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]]; } static multiplyByDOMMatrix(m, md) { return [m[0] * md.a + m[2] * md.b, m[1] * md.a + m[3] * md.b, m[0] * md.c + m[2] * md.d, m[1] * md.c + m[3] * md.d, m[0] * md.e + m[2] * md.f + m[4], m[1] * md.e + m[3] * md.f + m[5]]; } static applyTransform(p, m, pos = 0) { const p0 = p[pos]; const p1 = p[pos + 1]; p[pos] = p0 * m[0] + p1 * m[2] + m[4]; p[pos + 1] = p0 * m[1] + p1 * m[3] + m[5]; } static applyTransformToBezier(p, transform, pos = 0) { const m0 = transform[0]; const m1 = transform[1]; const m2 = transform[2]; const m3 = transform[3]; const m4 = transform[4]; const m5 = transform[5]; for (let i = 0; i < 6; i += 2) { const pI = p[pos + i]; const pI1 = p[pos + i + 1]; p[pos + i] = pI * m0 + pI1 * m2 + m4; p[pos + i + 1] = pI * m1 + pI1 * m3 + m5; } } static applyInverseTransform(p, m) { const p0 = p[0]; const p1 = p[1]; const d = m[0] * m[3] - m[1] * m[2]; p[0] = (p0 * m[3] - p1 * m[2] + m[2] * m[5] - m[4] * m[3]) / d; p[1] = (-p0 * m[1] + p1 * m[0] + m[4] * m[1] - m[5] * m[0]) / d; } static axialAlignedBoundingBox(rect, transform, output) { const m0 = transform[0]; const m1 = transform[1]; const m2 = transform[2]; const m3 = transform[3]; const m4 = transform[4]; const m5 = transform[5]; const r0 = rect[0]; const r1 = rect[1]; const r2 = rect[2]; const r3 = rect[3]; let a0 = m0 * r0 + m4; let a2 = a0; let a1 = m0 * r2 + m4; let a3 = a1; let b0 = m3 * r1 + m5; let b2 = b0; let b1 = m3 * r3 + m5; let b3 = b1; if (m1 !== 0 || m2 !== 0) { const m1r0 = m1 * r0; const m1r2 = m1 * r2; const m2r1 = m2 * r1; const m2r3 = m2 * r3; a0 += m2r1; a3 += m2r1; a1 += m2r3; a2 += m2r3; b0 += m1r0; b3 += m1r0; b1 += m1r2; b2 += m1r2; } output[0] = Math.min(output[0], a0, a1, a2, a3); output[1] = Math.min(output[1], b0, b1, b2, b3); output[2] = Math.max(output[2], a0, a1, a2, a3); output[3] = Math.max(output[3], b0, b1, b2, b3); } static inverseTransform(m) { const d = m[0] * m[3] - m[1] * m[2]; return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; } static singularValueDecompose2dScale(matrix, output) { const m0 = matrix[0]; const m1 = matrix[1]; const m2 = matrix[2]; const m3 = matrix[3]; const a = m0 ** 2 + m1 ** 2; const b = m0 * m2 + m1 * m3; const c = m2 ** 2 + m3 ** 2; const first = (a + c) / 2; const second = Math.sqrt(first ** 2 - (a * c - b ** 2)); output[0] = Math.sqrt(first + second || 1); output[1] = Math.sqrt(first - second || 1); } static normalizeRect(rect) { const r = rect.slice(0); if (rect[0] > rect[2]) { r[0] = rect[2]; r[2] = rect[0]; } if (rect[1] > rect[3]) { r[1] = rect[3]; r[3] = rect[1]; } return r; } static intersect(rect1, rect2) { const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2])); const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2])); if (xLow > xHigh) { return null; } const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3])); const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3])); if (yLow > yHigh) { return null; } return [xLow, yLow, xHigh, yHigh]; } static pointBoundingBox(x, y, minMax) { minMax[0] = Math.min(minMax[0], x); minMax[1] = Math.min(minMax[1], y); minMax[2] = Math.max(minMax[2], x); minMax[3] = Math.max(minMax[3], y); } static rectBoundingBox(x0, y0, x1, y1, minMax) { minMax[0] = Math.min(minMax[0], x0, x1); minMax[1] = Math.min(minMax[1], y0, y1); minMax[2] = Math.max(minMax[2], x0, x1); minMax[3] = Math.max(minMax[3], y0, y1); } static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) { if (t <= 0 || t >= 1) { return; } const mt = 1 - t; const tt = t * t; const ttt = tt * t; const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3; const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3; minMax[0] = Math.min(minMax[0], x); minMax[1] = Math.min(minMax[1], y); minMax[2] = Math.max(minMax[2], x); minMax[3] = Math.max(minMax[3], y); } static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) { if (Math.abs(a) < 1e-12) { if (Math.abs(b) >= 1e-12) { this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax); } return; } const delta = b ** 2 - 4 * c * a; if (delta < 0) { return; } const sqrtDelta = Math.sqrt(delta); const a2 = 2 * a; this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax); this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax); } static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) { minMax[0] = Math.min(minMax[0], x0, x3); minMax[1] = Math.min(minMax[1], y0, y3); minMax[2] = Math.max(minMax[2], x0, x3); minMax[3] = Math.max(minMax[3], y0, y3); this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-x0 + 3 * (x1 - x2) + x3), 6 * (x0 - 2 * x1 + x2), 3 * (x1 - x0), minMax); this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-y0 + 3 * (y1 - y2) + y3), 6 * (y0 - 2 * y1 + y2), 3 * (y1 - y0), minMax); } } const PDFStringTranslateTable = (/* unused pure expression or super */ null && ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac])); function stringToPDFString(str, keepEscapeSequence = false) { if (str[0] >= "\xEF") { let encoding; if (str[0] === "\xFE" && str[1] === "\xFF") { encoding = "utf-16be"; if (str.length % 2 === 1) { str = str.slice(0, -1); } } else if (str[0] === "\xFF" && str[1] === "\xFE") { encoding = "utf-16le"; if (str.length % 2 === 1) { str = str.slice(0, -1); } } else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") { encoding = "utf-8"; } if (encoding) { try { const decoder = new TextDecoder(encoding, { fatal: true }); const buffer = stringToBytes(str); const decoded = decoder.decode(buffer); if (keepEscapeSequence || !decoded.includes("\x1b")) { return decoded; } return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, ""); } catch (ex) { warn(`stringToPDFString: "${ex}".`); } } } const strBuf = []; for (let i = 0, ii = str.length; i < ii; i++) { const charCode = str.charCodeAt(i); if (!keepEscapeSequence && charCode === 0x1b) { while (++i < ii && str.charCodeAt(i) !== 0x1b) {} continue; } const code = PDFStringTranslateTable[charCode]; strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); } return strBuf.join(""); } function stringToUTF8String(str) { return decodeURIComponent(escape(str)); } function utf8StringToString(str) { return unescape(encodeURIComponent(str)); } function isArrayEqual(arr1, arr2) { if (arr1.length !== arr2.length) { return false; } for (let i = 0, ii = arr1.length; i < ii; i++) { if (arr1[i] !== arr2[i]) { return false; } } return true; } function getModificationDate(date = new Date()) { if (!(date instanceof Date)) { date = new Date(date); } const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")]; return buffer.join(""); } let NormalizeRegex = null; let NormalizationMap = null; function normalizeUnicode(str) { if (!NormalizeRegex) { NormalizeRegex = /([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu; NormalizationMap = new Map([["ſt", "ſt"]]); } return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2)); } function getUuid() { if (typeof crypto.randomUUID === "function") { return crypto.randomUUID(); } const buf = new Uint8Array(32); crypto.getRandomValues(buf); return bytesToString(buf); } const AnnotationPrefix = "pdfjs_internal_id_"; function _isValidExplicitDest(validRef, validName, dest) { if (!Array.isArray(dest) || dest.length < 2) { return false; } const [page, zoom, ...args] = dest; if (!validRef(page) && !Number.isInteger(page)) { return false; } if (!validName(zoom)) { return false; } const argsLen = args.length; let allowNull = true; switch (zoom.name) { case "XYZ": if (argsLen < 2 || argsLen > 3) { return false; } break; case "Fit": case "FitB": return argsLen === 0; case "FitH": case "FitBH": case "FitV": case "FitBV": if (argsLen > 1) { return false; } break; case "FitR": if (argsLen !== 4) { return false; } allowNull = false; break; default: return false; } for (const arg of args) { if (typeof arg === "number" || allowNull && arg === null) { continue; } return false; } return true; } function MathClamp(v, min, max) { return Math.min(Math.max(v, min), max); } function toHexUtil(arr) { if (Uint8Array.prototype.toHex) { return arr.toHex(); } return Array.from(arr, num => hexNumbers[num]).join(""); } function toBase64Util(arr) { if (Uint8Array.prototype.toBase64) { return arr.toBase64(); } return btoa(bytesToString(arr)); } function fromBase64Util(str) { if (Uint8Array.fromBase64) { return Uint8Array.fromBase64(str); } return stringToBytes(atob(str)); } if (typeof Promise.try !== "function") { Promise.try = function (fn, ...args) { return new Promise(resolve => { resolve(fn(...args)); }); }; } if (typeof Math.sumPrecise !== "function") { Math.sumPrecise = function (numbers) { return numbers.reduce((a, b) => a + b, 0); }; } ;// ./src/display/xfa_text.js class XfaText { static textContent(xfa) { const items = []; const output = { items, styles: Object.create(null) }; function walk(node) { if (!node) { return; } let str = null; const name = node.name; if (name === "#text") { str = node.value; } else if (!XfaText.shouldBuildText(name)) { return; } else if (node?.attributes?.textContent) { str = node.attributes.textContent; } else if (node.value) { str = node.value; } if (str !== null) { items.push({ str }); } if (!node.children) { return; } for (const child of node.children) { walk(child); } } walk(xfa); return output; } static shouldBuildText(name) { return !(name === "textarea" || name === "input" || name === "option" || name === "select"); } } ;// ./src/display/xfa_layer.js class XfaLayer { static setupStorage(html, id, element, storage, intent) { const storedData = storage.getValue(id, { value: null }); switch (element.name) { case "textarea": if (storedData.value !== null) { html.textContent = storedData.value; } if (intent === "print") { break; } html.addEventListener("input", event => { storage.setValue(id, { value: event.target.value }); }); break; case "input": if (element.attributes.type === "radio" || element.attributes.type === "checkbox") { if (storedData.value === element.attributes.xfaOn) { html.setAttribute("checked", true); } else if (storedData.value === element.attributes.xfaOff) { html.removeAttribute("checked"); } if (intent === "print") { break; } html.addEventListener("change", event => { storage.setValue(id, { value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff") }); }); } else { if (storedData.value !== null) { html.setAttribute("value", storedData.value); } if (intent === "print") { break; } html.addEventListener("input", event => { storage.setValue(id, { value: event.target.value }); }); } break; case "select": if (storedData.value !== null) { html.setAttribute("value", storedData.value); for (const option of element.children) { if (option.attributes.value === storedData.value) { option.attributes.selected = true; } else if (option.attributes.hasOwnProperty("selected")) { delete option.attributes.selected; } } } html.addEventListener("input", event => { const options = event.target.options; const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value; storage.setValue(id, { value }); }); break; } } static setAttributes({ html, element, storage = null, intent, linkService }) { const { attributes } = element; const isHTMLAnchorElement = html instanceof HTMLAnchorElement; if (attributes.type === "radio") { attributes.name = `${attributes.name}-${intent}`; } for (const [key, value] of Object.entries(attributes)) { if (value === null || value === undefined) { continue; } switch (key) { case "class": if (value.length) { html.setAttribute(key, value.join(" ")); } break; case "dataId": break; case "id": html.setAttribute("data-element-id", value); break; case "style": Object.assign(html.style, value); break; case "textContent": html.textContent = value; break; default: if (!isHTMLAnchorElement || key !== "href" && key !== "newWindow") { html.setAttribute(key, value); } } } if (isHTMLAnchorElement) { linkService.addLinkAttributes(html, attributes.href, attributes.newWindow); } if (storage && attributes.dataId) { this.setupStorage(html, attributes.dataId, element, storage); } } static render(parameters) { const storage = parameters.annotationStorage; const linkService = parameters.linkService; const root = parameters.xfaHtml; const intent = parameters.intent || "display"; const rootHtml = document.createElement(root.name); if (root.attributes) { this.setAttributes({ html: rootHtml, element: root, intent, linkService }); } const isNotForRichText = intent !== "richText"; const rootDiv = parameters.div; rootDiv.append(rootHtml); if (parameters.viewport) { const transform = `matrix(${parameters.viewport.transform.join(",")})`; rootDiv.style.transform = transform; } if (isNotForRichText) { rootDiv.setAttribute("class", "xfaLayer xfaFont"); } const textDivs = []; if (root.children.length === 0) { if (root.value) { const node = document.createTextNode(root.value); rootHtml.append(node); if (isNotForRichText && XfaText.shouldBuildText(root.name)) { textDivs.push(node); } } return { textDivs }; } const stack = [[root, -1, rootHtml]]; while (stack.length > 0) { const [parent, i, html] = stack.at(-1); if (i + 1 === parent.children.length) { stack.pop(); continue; } const child = parent.children[++stack.at(-1)[1]]; if (child === null) { continue; } const { name } = child; if (name === "#text") { const node = document.createTextNode(child.value); textDivs.push(node); html.append(node); continue; } const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name); html.append(childHtml); if (child.attributes) { this.setAttributes({ html: childHtml, element: child, storage, intent, linkService }); } if (child.children?.length > 0) { stack.push([child, -1, childHtml]); } else if (child.value) { const node = document.createTextNode(child.value); if (isNotForRichText && XfaText.shouldBuildText(name)) { textDivs.push(node); } childHtml.append(node); } } for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) { el.setAttribute("readOnly", true); } return { textDivs }; } static update(parameters) { const transform = `matrix(${parameters.viewport.transform.join(",")})`; parameters.div.style.transform = transform; parameters.div.hidden = false; } } ;// ./src/display/display_utils.js const SVG_NS = "http://www.w3.org/2000/svg"; class PixelsPerInch { static CSS = 96.0; static PDF = 72.0; static PDF_TO_CSS_UNITS = this.CSS / this.PDF; } async function fetchData(url, type = "text") { if (isValidFetchUrl(url, document.baseURI)) { const response = await fetch(url); if (!response.ok) { throw new Error(response.statusText); } switch (type) { case "arraybuffer": return response.arrayBuffer(); case "blob": return response.blob(); case "json": return response.json(); } return response.text(); } return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.open("GET", url, true); request.responseType = type; request.onreadystatechange = () => { if (request.readyState !== XMLHttpRequest.DONE) { return; } if (request.status === 200 || request.status === 0) { switch (type) { case "arraybuffer": case "blob": case "json": resolve(request.response); return; } resolve(request.responseText); return; } reject(new Error(request.statusText)); }; request.send(null); }); } class PageViewport { constructor({ viewBox, userUnit, scale, rotation, offsetX = 0, offsetY = 0, dontFlip = false }) { this.viewBox = viewBox; this.userUnit = userUnit; this.scale = scale; this.rotation = rotation; this.offsetX = offsetX; this.offsetY = offsetY; scale *= userUnit; const centerX = (viewBox[2] + viewBox[0]) / 2; const centerY = (viewBox[3] + viewBox[1]) / 2; let rotateA, rotateB, rotateC, rotateD; rotation %= 360; if (rotation < 0) { rotation += 360; } switch (rotation) { case 180: rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; break; case 90: rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; break; case 270: rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; break; case 0: rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; break; default: throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees."); } if (dontFlip) { rotateC = -rotateC; rotateD = -rotateD; } let offsetCanvasX, offsetCanvasY; let width, height; if (rotateA === 0) { offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; width = (viewBox[3] - viewBox[1]) * scale; height = (viewBox[2] - viewBox[0]) * scale; } else { offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; width = (viewBox[2] - viewBox[0]) * scale; height = (viewBox[3] - viewBox[1]) * scale; } this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY]; this.width = width; this.height = height; } get rawDims() { const dims = this.viewBox; return shadow(this, "rawDims", { pageWidth: dims[2] - dims[0], pageHeight: dims[3] - dims[1], pageX: dims[0], pageY: dims[1] }); } clone({ scale = this.scale, rotation = this.rotation, offsetX = this.offsetX, offsetY = this.offsetY, dontFlip = false } = {}) { return new PageViewport({ viewBox: this.viewBox.slice(), userUnit: this.userUnit, scale, rotation, offsetX, offsetY, dontFlip }); } convertToViewportPoint(x, y) { const p = [x, y]; Util.applyTransform(p, this.transform); return p; } convertToViewportRectangle(rect) { const topLeft = [rect[0], rect[1]]; Util.applyTransform(topLeft, this.transform); const bottomRight = [rect[2], rect[3]]; Util.applyTransform(bottomRight, this.transform); return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]]; } convertToPdfPoint(x, y) { const p = [x, y]; Util.applyInverseTransform(p, this.transform); return p; } } class RenderingCancelledException extends BaseException { constructor(msg, extraDelay = 0) { super(msg, "RenderingCancelledException"); this.extraDelay = extraDelay; } } function isDataScheme(url) { const ii = url.length; let i = 0; while (i < ii && url[i].trim() === "") { i++; } return url.substring(i, i + 5).toLowerCase() === "data:"; } function isPdfFile(filename) { return typeof filename === "string" && /\.pdf$/i.test(filename); } function getFilenameFromUrl(url) { [url] = url.split(/[#?]/, 1); return url.substring(url.lastIndexOf("/") + 1); } function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") { if (typeof url !== "string") { return defaultFilename; } if (isDataScheme(url)) { warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.'); return defaultFilename; } const getURL = urlString => { try { return new URL(urlString); } catch { try { return new URL(decodeURIComponent(urlString)); } catch { try { return new URL(urlString, "https://foo.bar"); } catch { try { return new URL(decodeURIComponent(urlString), "https://foo.bar"); } catch { return null; } } } } }; const newURL = getURL(url); if (!newURL) { return defaultFilename; } const decode = name => { try { let decoded = decodeURIComponent(name); if (decoded.includes("/")) { decoded = decoded.split("/").at(-1); if (decoded.test(/^\.pdf$/i)) { return decoded; } return name; } return decoded; } catch { return name; } }; const pdfRegex = /\.pdf$/i; const filename = newURL.pathname.split("/").at(-1); if (pdfRegex.test(filename)) { return decode(filename); } if (newURL.searchParams.size > 0) { const values = Array.from(newURL.searchParams.values()).reverse(); for (const value of values) { if (pdfRegex.test(value)) { return decode(value); } } const keys = Array.from(newURL.searchParams.keys()).reverse(); for (const key of keys) { if (pdfRegex.test(key)) { return decode(key); } } } if (newURL.hash) { const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i; const hashFilename = reFilename.exec(newURL.hash); if (hashFilename) { return decode(hashFilename[0]); } } return defaultFilename; } class StatTimer { started = Object.create(null); times = []; time(name) { if (name in this.started) { warn(`Timer is already running for ${name}`); } this.started[name] = Date.now(); } timeEnd(name) { if (!(name in this.started)) { warn(`Timer has not been started for ${name}`); } this.times.push({ name, start: this.started[name], end: Date.now() }); delete this.started[name]; } toString() { const outBuf = []; let longest = 0; for (const { name } of this.times) { longest = Math.max(name.length, longest); } for (const { name, start, end } of this.times) { outBuf.push(`${name.padEnd(longest)} ${end - start}ms\n`); } return outBuf.join(""); } } function isValidFetchUrl(url, baseUrl) { const res = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url); return res?.protocol === "http:" || res?.protocol === "https:"; } function noContextMenu(e) { e.preventDefault(); } function stopEvent(e) { e.preventDefault(); e.stopPropagation(); } function deprecated(details) { console.log("Deprecated API usage: " + details); } class PDFDateString { static #regex; static toDateObject(input) { if (input instanceof Date) { return input; } if (!input || typeof input !== "string") { return null; } this.#regex ||= new RegExp("^D:" + "(\\d{4})" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "([Z|+|-])?" + "(\\d{2})?" + "'?" + "(\\d{2})?" + "'?"); const matches = this.#regex.exec(input); if (!matches) { return null; } const year = parseInt(matches[1], 10); let month = parseInt(matches[2], 10); month = month >= 1 && month <= 12 ? month - 1 : 0; let day = parseInt(matches[3], 10); day = day >= 1 && day <= 31 ? day : 1; let hour = parseInt(matches[4], 10); hour = hour >= 0 && hour <= 23 ? hour : 0; let minute = parseInt(matches[5], 10); minute = minute >= 0 && minute <= 59 ? minute : 0; let second = parseInt(matches[6], 10); second = second >= 0 && second <= 59 ? second : 0; const universalTimeRelation = matches[7] || "Z"; let offsetHour = parseInt(matches[8], 10); offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0; let offsetMinute = parseInt(matches[9], 10) || 0; offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0; if (universalTimeRelation === "-") { hour += offsetHour; minute += offsetMinute; } else if (universalTimeRelation === "+") { hour -= offsetHour; minute -= offsetMinute; } return new Date(Date.UTC(year, month, day, hour, minute, second)); } } function getXfaPageViewport(xfaPage, { scale = 1, rotation = 0 }) { const { width, height } = xfaPage.attributes.style; const viewBox = [0, 0, parseInt(width), parseInt(height)]; return new PageViewport({ viewBox, userUnit: 1, scale, rotation }); } function getRGB(color) { if (color.startsWith("#")) { const colorRGB = parseInt(color.slice(1), 16); return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff]; } if (color.startsWith("rgb(")) { return color.slice(4, -1).split(",").map(x => parseInt(x)); } if (color.startsWith("rgba(")) { return color.slice(5, -1).split(",").map(x => parseInt(x)).slice(0, 3); } warn(`Not a valid color format: "${color}"`); return [0, 0, 0]; } function getColorValues(colors) { const span = document.createElement("span"); span.style.visibility = "hidden"; span.style.colorScheme = "only light"; document.body.append(span); for (const name of colors.keys()) { span.style.color = name; const computedColor = window.getComputedStyle(span).color; colors.set(name, getRGB(computedColor)); } span.remove(); } function getCurrentTransform(ctx) { const { a, b, c, d, e, f } = ctx.getTransform(); return [a, b, c, d, e, f]; } function getCurrentTransformInverse(ctx) { const { a, b, c, d, e, f } = ctx.getTransform().invertSelf(); return [a, b, c, d, e, f]; } function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) { if (viewport instanceof PageViewport) { const { pageWidth, pageHeight } = viewport.rawDims; const { style } = div; const useRound = util_FeatureTest.isCSSRoundSupported; const w = `var(--total-scale-factor) * ${pageWidth}px`, h = `var(--total-scale-factor) * ${pageHeight}px`; const widthStr = useRound ? `round(down, ${w}, var(--scale-round-x))` : `calc(${w})`, heightStr = useRound ? `round(down, ${h}, var(--scale-round-y))` : `calc(${h})`; if (!mustFlip || viewport.rotation % 180 === 0) { style.width = widthStr; style.height = heightStr; } else { style.width = heightStr; style.height = widthStr; } } if (mustRotate) { div.setAttribute("data-main-rotation", viewport.rotation); } } class OutputScale { constructor() { const { pixelRatio } = OutputScale; this.sx = pixelRatio; this.sy = pixelRatio; } get scaled() { return this.sx !== 1 || this.sy !== 1; } get symmetric() { return this.sx === this.sy; } limitCanvas(width, height, maxPixels, maxDim, capAreaFactor = -1) { let maxAreaScale = Infinity, maxWidthScale = Infinity, maxHeightScale = Infinity; maxPixels = OutputScale.capPixels(maxPixels, capAreaFactor); if (maxPixels > 0) { maxAreaScale = Math.sqrt(maxPixels / (width * height)); } if (maxDim !== -1) { maxWidthScale = maxDim / width; maxHeightScale = maxDim / height; } const maxScale = Math.min(maxAreaScale, maxWidthScale, maxHeightScale); if (this.sx > maxScale || this.sy > maxScale) { this.sx = maxScale; this.sy = maxScale; return true; } return false; } static get pixelRatio() { return globalThis.devicePixelRatio || 1; } static capPixels(maxPixels, capAreaFactor) { if (capAreaFactor >= 0) { const winPixels = Math.ceil(window.screen.availWidth * window.screen.availHeight * this.pixelRatio ** 2 * (1 + capAreaFactor / 100)); return maxPixels > 0 ? Math.min(maxPixels, winPixels) : winPixels; } return maxPixels; } } const SupportedImageMimeTypes = ["image/apng", "image/avif", "image/bmp", "image/gif", "image/jpeg", "image/png", "image/svg+xml", "image/webp", "image/x-icon"]; class ColorScheme { static get isDarkMode() { return shadow(this, "isDarkMode", !!window?.matchMedia?.("(prefers-color-scheme: dark)").matches); } } class CSSConstants { static get commentForegroundColor() { const element = document.createElement("span"); element.classList.add("comment", "sidebar"); const { style } = element; style.width = style.height = "0"; style.display = "none"; style.color = "var(--comment-fg-color)"; document.body.append(element); const { color } = window.getComputedStyle(element); element.remove(); return shadow(this, "commentForegroundColor", getRGB(color)); } } function applyOpacity(r, g, b, opacity) { opacity = Math.min(Math.max(opacity ?? 1, 0), 1); const white = 255 * (1 - opacity); r = Math.round(r * opacity + white); g = Math.round(g * opacity + white); b = Math.round(b * opacity + white); return [r, g, b]; } function RGBToHSL(rgb, output) { const r = rgb[0] / 255; const g = rgb[1] / 255; const b = rgb[2] / 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); const l = (max + min) / 2; if (max === min) { output[0] = output[1] = 0; } else { const d = max - min; output[1] = l < 0.5 ? d / (max + min) : d / (2 - max - min); switch (max) { case r: output[0] = ((g - b) / d + (g < b ? 6 : 0)) * 60; break; case g: output[0] = ((b - r) / d + 2) * 60; break; case b: output[0] = ((r - g) / d + 4) * 60; break; } } output[2] = l; } function HSLToRGB(hsl, output) { const h = hsl[0]; const s = hsl[1]; const l = hsl[2]; const c = (1 - Math.abs(2 * l - 1)) * s; const x = c * (1 - Math.abs(h / 60 % 2 - 1)); const m = l - c / 2; switch (Math.floor(h / 60)) { case 0: output[0] = c + m; output[1] = x + m; output[2] = m; break; case 1: output[0] = x + m; output[1] = c + m; output[2] = m; break; case 2: output[0] = m; output[1] = c + m; output[2] = x + m; break; case 3: output[0] = m; output[1] = x + m; output[2] = c + m; break; case 4: output[0] = x + m; output[1] = m; output[2] = c + m; break; case 5: case 6: output[0] = c + m; output[1] = m; output[2] = x + m; break; } } function computeLuminance(x) { return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; } function contrastRatio(hsl1, hsl2, output) { HSLToRGB(hsl1, output); output.map(computeLuminance); const lum1 = 0.2126 * output[0] + 0.7152 * output[1] + 0.0722 * output[2]; HSLToRGB(hsl2, output); output.map(computeLuminance); const lum2 = 0.2126 * output[0] + 0.7152 * output[1] + 0.0722 * output[2]; return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05); } const contrastCache = new Map(); function findContrastColor(baseColor, fixedColor) { const key = baseColor[0] + baseColor[1] * 0x100 + baseColor[2] * 0x10000 + fixedColor[0] * 0x1000000 + fixedColor[1] * 0x100000000 + fixedColor[2] * 0x10000000000; let cac