diff options
Diffstat (limited to 'javascript/pdfjs/build/pdf.mjs')
| -rwxr-xr-x | javascript/pdfjs/build/pdf.mjs | 26465 |
1 files changed, 0 insertions, 26465 deletions
diff --git a/javascript/pdfjs/build/pdf.mjs b/javascript/pdfjs/build/pdf.mjs deleted file mode 100755 index bd28128..0000000 --- a/javascript/pdfjs/build/pdf.mjs +++ /dev/null @@ -1,26465 +0,0 @@ -/** - * @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.5.207 - * pdfjsBuild = 527964698 - */ -/******/ // 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 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 stripPath(str) { - return str.substring(str.lastIndexOf("/") + 1); -} -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 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; -} -const makeArr = () => []; -const makeMap = () => new Map(); -const makeObj = () => Object.create(null); -function MathClamp(v, min, max) { - return Math.min(Math.max(v, min), max); -} -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 "blob": - return response.blob(); - case "bytes": - return response.bytes(); - 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 === "bytes" ? "arraybuffer" : type; - request.onreadystatechange = () => { - if (request.readyState !== XMLHttpRequest.DONE) { - return; - } - if (request.status === 200 || request.status === 0) { - switch (type) { - case "bytes": - resolve(new Uint8Array(request.response)); - return; - 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 stripPath(url); -} -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 = stripPath(decoded); - if (/^\.pdf$/i.test(decoded)) { - return name; - } - } - return decoded; - } catch { - return name; - } - }; - const pdfRegex = /\.pdf$/i; - const filename = stripPath(newURL.pathname); - if (pdfRegex.test(filename)) { - return decode(filename); - } - if (newURL.searchParams.size > 0) { - const getLast = iterator => [...iterator].findLast(v => pdfRegex.test(v)); - const name = getLast(newURL.searchParams.values()) ?? getLast(newURL.searchParams.keys()); - if (name) { - return decode(name); - } - } - 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 /https?:/.test(res?.protocol ?? ""); -} -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 = 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 = MathClamp(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 cachedValue = contrastCache.get(key); - if (cachedValue) { - return cachedValue; - } - const array = new Float32Array(9); - const output = array.subarray(0, 3); - const baseHSL = array.subarray(3, 6); - RGBToHSL(baseColor, baseHSL); - const fixedHSL = array.subarray(6, 9); - RGBToHSL(fixedColor, fixedHSL); - const isFixedColorDark = fixedHSL[2] < 0.5; - const minContrast = isFixedColorDark ? 12 : 4.5; - baseHSL[2] = isFixedColorDark ? Math.sqrt(baseHSL[2]) : 1 - Math.sqrt(1 - baseHSL[2]); - if (contrastRatio(baseHSL, fixedHSL, output) < minContrast) { - let start, end; - if (isFixedColorDark) { - start = baseHSL[2]; - end = 1; - } else { - start = 0; - end = baseHSL[2]; - } - const PRECISION = 0.005; - while (end - start > PRECISION) { - const mid = baseHSL[2] = (start + end) / 2; - if (isFixedColorDark === contrastRatio(baseHSL, fixedHSL, output) < minContrast) { - start = mid; - } else { - end = mid; - } - } - baseHSL[2] = isFixedColorDark ? end : start; - } - HSLToRGB(baseHSL, output); - cachedValue = Util.makeHexColor(Math.round(output[0] * 255), Math.round(output[1] * 255), Math.round(output[2] * 255)); - contrastCache.set(key, cachedValue); - return cachedValue; -} -function renderRichText({ - html, - dir, - className -}, container) { - const fragment = document.createDocumentFragment(); - if (typeof html === "string") { - const p = document.createElement("p"); - p.dir = dir || "auto"; - const lines = html.split(/(?:\r\n?|\n)/); - for (let i = 0, ii = lines.length; i < ii; ++i) { - const line = lines[i]; - p.append(document.createTextNode(line)); - if (i < ii - 1) { - p.append(document.createElement("br")); - } - } - fragment.append(p); - } else { - XfaLayer.render({ - xfaHtml: html, - div: fragment, - intent: "richText" - }); - } - fragment.firstElementChild.classList.add("richText", className); - container.append(fragment); -} -function makePathFromDrawOPS(data) { - const path = new Path2D(); - if (!data) { - return path; - } - for (let i = 0, ii = data.length; i < ii;) { - switch (data[i++]) { - case DrawOPS.moveTo: - path.moveTo(data[i++], data[i++]); - break; - case DrawOPS.lineTo: - path.lineTo(data[i++], data[i++]); - break; - case DrawOPS.curveTo: - path.bezierCurveTo(data[i++], data[i++], data[i++], data[i++], data[i++], data[i++]); - break; - case DrawOPS.quadraticCurveTo: - path.quadraticCurveTo(data[i++], data[i++], data[i++], data[i++]); - break; - case DrawOPS.closePath: - path.closePath(); - break; - default: - warn(`Unrecognized drawing path operator: ${data[i - 1]}`); - break; - } - } - return path; -} -class PagesMapper { - #idToPageNumber = null; - #pageNumberToId = null; - #prevPageNumbers = null; - #pagesNumber = 0; - #listeners = []; - #copiedPageIds = null; - #copiedPageNumbers = null; - get pagesNumber() { - return this.#pagesNumber; - } - set pagesNumber(n) { - if (this.#pagesNumber === n) { - return; - } - this.#pagesNumber = n; - this.#reset(); - } - #reset() { - this.#pageNumberToId = null; - this.#idToPageNumber = null; - } - addListener(listener) { - this.#listeners.push(listener); - } - removeListener(listener) { - const index = this.#listeners.indexOf(listener); - if (index >= 0) { - this.#listeners.splice(index, 1); - } - } - #updateListeners(data) { - for (const listener of this.#listeners) { - listener(data); - } - } - #init(mustInit) { - if (this.#pageNumberToId) { - return; - } - const n = this.#pagesNumber; - const pageNumberToId = this.#pageNumberToId = new Uint32Array(n); - this.#prevPageNumbers = new Int32Array(pageNumberToId); - const idToPageNumber = this.#idToPageNumber = new Map(); - if (mustInit) { - for (let i = 1; i <= n; i++) { - pageNumberToId[i - 1] = i; - idToPageNumber.set(i, [i]); - } - } - } - #updateIdToPageNumber() { - const idToPageNumber = this.#idToPageNumber; - const pageNumberToId = this.#pageNumberToId; - idToPageNumber.clear(); - for (let i = 0, ii = this.#pagesNumber; i < ii; i++) { - const id = pageNumberToId[i]; - const pageNumbers = idToPageNumber.get(id); - if (pageNumbers) { - pageNumbers.push(i + 1); - } else { - idToPageNumber.set(id, [i + 1]); - } - } - } - movePages(selectedPages, pagesToMove, index) { - this.#init(true); - const pageNumberToId = this.#pageNumberToId; - const idToPageNumber = this.#idToPageNumber; - const movedCount = pagesToMove.length; - const mappedPagesToMove = new Uint32Array(movedCount); - let removedBeforeTarget = 0; - for (let i = 0; i < movedCount; i++) { - const pageIndex = pagesToMove[i] - 1; - mappedPagesToMove[i] = pageNumberToId[pageIndex]; - if (pageIndex < index) { - removedBeforeTarget += 1; - } - } - const pagesNumber = this.#pagesNumber; - let adjustedTarget = index - removedBeforeTarget; - const remainingLen = pagesNumber - movedCount; - adjustedTarget = MathClamp(adjustedTarget, 0, remainingLen); - for (let i = 0, r = 0; i < pagesNumber; i++) { - if (!selectedPages.has(i + 1)) { - pageNumberToId[r++] = pageNumberToId[i]; - } - } - pageNumberToId.copyWithin(adjustedTarget + movedCount, adjustedTarget, remainingLen); - pageNumberToId.set(mappedPagesToMove, adjustedTarget); - this.#setPrevPageNumbers(idToPageNumber, null); - this.#updateIdToPageNumber(); - this.#updateListeners({ - type: "move" - }); - if (pageNumberToId.every((id, i) => id === i + 1)) { - this.#reset(); - } - } - deletePages(pagesToDelete) { - this.#init(true); - const pageNumberToId = this.#pageNumberToId; - const prevIdToPageNumber = this.#idToPageNumber; - this.pagesNumber -= pagesToDelete.length; - this.#init(false); - const newPageNumberToId = this.#pageNumberToId; - let sourceIndex = 0; - let destIndex = 0; - for (const pageNumber of pagesToDelete) { - const pageIndex = pageNumber - 1; - if (pageIndex !== sourceIndex) { - newPageNumberToId.set(pageNumberToId.subarray(sourceIndex, pageIndex), destIndex); - destIndex += pageIndex - sourceIndex; - } - sourceIndex = pageIndex + 1; - } - if (sourceIndex < pageNumberToId.length) { - newPageNumberToId.set(pageNumberToId.subarray(sourceIndex), destIndex); - } - this.#setPrevPageNumbers(prevIdToPageNumber, null); - this.#updateIdToPageNumber(); - this.#updateListeners({ - type: "delete", - pageNumbers: pagesToDelete - }); - } - copyPages(pagesToCopy) { - this.#init(true); - this.#copiedPageNumbers = pagesToCopy; - this.#copiedPageIds = pagesToCopy.map(pageNumber => this.#pageNumberToId[pageNumber - 1]); - this.#updateListeners({ - type: "copy", - pageNumbers: pagesToCopy - }); - } - pastePages(index) { - this.#init(true); - const pageNumberToId = this.#pageNumberToId; - const prevIdToPageNumber = this.#idToPageNumber; - const copiedPageNumbers = this.#copiedPageNumbers; - const copiedPageMapping = new Map(); - let base = index; - for (const pageNumber of copiedPageNumbers) { - copiedPageMapping.set(++base, pageNumber); - } - this.pagesNumber += copiedPageNumbers.length; - this.#init(false); - const newPageNumberToId = this.#pageNumberToId; - newPageNumberToId.set(pageNumberToId.subarray(0, index), 0); - newPageNumberToId.set(this.#copiedPageIds, index); - newPageNumberToId.set(pageNumberToId.subarray(index), index + copiedPageNumbers.length); - this.#setPrevPageNumbers(prevIdToPageNumber, copiedPageMapping); - this.#updateIdToPageNumber(); - this.#updateListeners({ - type: "paste" - }); - this.#copiedPageIds = null; - } - #setPrevPageNumbers(prevIdToPageNumber, copiedPageMapping) { - const prevPageNumbers = this.#prevPageNumbers; - const newPageNumberToId = this.#pageNumberToId; - const idsIndices = new Map(); - for (let i = 0, ii = this.#pagesNumber; i < ii; i++) { - const oldPageNumber = copiedPageMapping?.get(i + 1); - if (oldPageNumber) { - prevPageNumbers[i] = -oldPageNumber; - continue; - } - const id = newPageNumberToId[i]; - const j = idsIndices.get(id) || 0; - prevPageNumbers[i] = prevIdToPageNumber.get(id)?.[j]; - idsIndices.set(id, j + 1); - } - } - hasBeenAltered() { - return this.#pageNumberToId !== null; - } - getPageMappingForSaving() { - const idToPageNumber = this.#idToPageNumber; - let nCopy = 0; - for (const pageNumbers of idToPageNumber.values()) { - nCopy = Math.max(nCopy, pageNumbers.length); - } - const extractParams = new Array(nCopy); - for (let i = 0; i < nCopy; i++) { - extractParams[i] = { - document: null, - pageIndices: [], - includePages: [] - }; - } - for (const [id, pageNumbers] of idToPageNumber) { - for (let i = 0, ii = pageNumbers.length; i < ii; i++) { - extractParams[i].includePages.push([id - 1, pageNumbers[i] - 1]); - } - } - for (const { - includePages, - pageIndices - } of extractParams) { - includePages.sort((a, b) => a[0] - b[0]); - for (let i = 0, ii = includePages.length; i < ii; i++) { - pageIndices.push(includePages[i][1]); - includePages[i] = includePages[i][0]; - } - } - return extractParams; - } - getPrevPageNumber(pageNumber) { - return this.#prevPageNumbers[pageNumber - 1] ?? 0; - } - getPageNumber(id) { - return this.#idToPageNumber ? this.#idToPageNumber.get(id)?.[0] ?? 0 : id; - } - getPageId(pageNumber) { - return this.#pageNumberToId?.[pageNumber - 1] ?? pageNumber; - } - getMapping() { - return this.#pageNumberToId.subarray(0, this.pagesNumber); - } -} - -;// ./src/display/editor/toolbar.js - -class EditorToolbar { - #toolbar = null; - #colorPicker = null; - #editor; - #buttons = null; - #altText = null; - #comment = null; - #commentButtonDivider = null; - #signatureDescriptionButton = null; - static #l10nRemove = null; - constructor(editor) { - this.#editor = editor; - EditorToolbar.#l10nRemove ||= Object.freeze({ - freetext: "pdfjs-editor-remove-freetext-button", - highlight: "pdfjs-editor-remove-highlight-button", - ink: "pdfjs-editor-remove-ink-button", - stamp: "pdfjs-editor-remove-stamp-button", - signature: "pdfjs-editor-remove-signature-button" - }); - } - render() { - const editToolbar = this.#toolbar = document.createElement("div"); - editToolbar.classList.add("editToolbar", "hidden"); - editToolbar.setAttribute("role", "toolbar"); - const signal = this.#editor._uiManager._signal; - if (signal instanceof AbortSignal && !signal.aborted) { - editToolbar.addEventListener("contextmenu", noContextMenu, { - signal - }); - editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, { - signal - }); - } - const buttons = this.#buttons = document.createElement("div"); - buttons.className = "buttons"; - editToolbar.append(buttons); - const position = this.#editor.toolbarPosition; - if (position) { - const { - style - } = editToolbar; - const x = this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0]; - style.insetInlineEnd = `${100 * x}%`; - style.top = `calc(${100 * position[1]}% + var(--editor-toolbar-vert-offset))`; - } - return editToolbar; - } - get div() { - return this.#toolbar; - } - static #pointerDown(e) { - e.stopPropagation(); - } - #focusIn(e) { - this.#editor._focusEventsAllowed = false; - stopEvent(e); - } - #focusOut(e) { - this.#editor._focusEventsAllowed = true; - stopEvent(e); - } - #addListenersToElement(element) { - const signal = this.#editor._uiManager._signal; - if (!(signal instanceof AbortSignal) || signal.aborted) { - return false; - } - element.addEventListener("focusin", this.#focusIn.bind(this), { - capture: true, - signal - }); - element.addEventListener("focusout", this.#focusOut.bind(this), { - capture: true, - signal - }); - element.addEventListener("contextmenu", noContextMenu, { - signal - }); - return true; - } - hide() { - this.#toolbar.classList.add("hidden"); - this.#colorPicker?.hideDropdown(); - } - show() { - this.#toolbar.classList.remove("hidden"); - this.#altText?.shown(); - this.#comment?.shown(); - } - addDeleteButton() { - const { - editorType, - _uiManager - } = this.#editor; - const button = document.createElement("button"); - button.classList.add("basic", "deleteButton"); - button.tabIndex = 0; - button.setAttribute("data-l10n-id", EditorToolbar.#l10nRemove[editorType]); - if (this.#addListenersToElement(button)) { - button.addEventListener("click", e => { - _uiManager.delete(); - }, { - signal: _uiManager._signal - }); - } - this.#buttons.append(button); - } - get #divider() { - const divider = document.createElement("div"); - divider.className = "divider"; - return divider; - } - async addAltText(altText) { - const button = await altText.render(); - this.#addListenersToElement(button); - this.#buttons.append(button, this.#divider); - this.#altText = altText; - } - addComment(comment, beforeElement = null) { - if (this.#comment) { - return; - } - const button = comment.renderForToolbar(); - if (!button) { - return; - } - this.#addListenersToElement(button); - const divider = this.#commentButtonDivider = this.#divider; - if (!beforeElement) { - this.#buttons.append(button, divider); - } else { - this.#buttons.insertBefore(button, beforeElement); - this.#buttons.insertBefore(divider, beforeElement); - } - this.#comment = comment; - comment.toolbar = this; - } - addColorPicker(colorPicker) { - if (this.#colorPicker) { - return; - } - this.#colorPicker = colorPicker; - const button = colorPicker.renderButton(); - this.#addListenersToElement(button); - this.#buttons.append(button, this.#divider); - } - async addEditSignatureButton(signatureManager) { - const button = this.#signatureDescriptionButton = await signatureManager.renderEditButton(this.#editor); - this.#addListenersToElement(button); - this.#buttons.append(button, this.#divider); - } - removeButton(name) { - switch (name) { - case "comment": - this.#comment?.removeToolbarCommentButton(); - this.#comment = null; - this.#commentButtonDivider?.remove(); - this.#commentButtonDivider = null; - break; - } - } - async addButton(name, tool) { - switch (name) { - case "colorPicker": - if (tool) { - this.addColorPicker(tool); - } - break; - case "altText": - if (tool) { - await this.addAltText(tool); - } - break; - case "editSignature": - if (tool) { - await this.addEditSignatureButton(tool); - } - break; - case "delete": - this.addDeleteButton(); - break; - case "comment": - if (tool) { - this.addComment(tool); - } - break; - } - } - async addButtonBefore(name, tool, beforeSelector) { - if (!tool && name === "comment") { - return; - } - const beforeElement = this.#buttons.querySelector(beforeSelector); - if (!beforeElement) { - return; - } - if (name === "comment") { - this.addComment(tool, beforeElement); - } - } - updateEditSignatureButton(description) { - if (this.#signatureDescriptionButton) { - this.#signatureDescriptionButton.title = description; - } - } - remove() { - this.#toolbar.remove(); - this.#colorPicker?.destroy(); - this.#colorPicker = null; - } -} -class FloatingToolbar { - #buttons = null; - #toolbar = null; - #uiManager; - constructor(uiManager) { - this.#uiManager = uiManager; - } - #render() { - const editToolbar = this.#toolbar = document.createElement("div"); - editToolbar.className = "editToolbar"; - editToolbar.setAttribute("role", "toolbar"); - const signal = this.#uiManager._signal; - if (signal instanceof AbortSignal && !signal.aborted) { - editToolbar.addEventListener("contextmenu", noContextMenu, { - signal - }); - } - const buttons = this.#buttons = document.createElement("div"); - buttons.className = "buttons"; - editToolbar.append(buttons); - if (this.#uiManager.hasCommentManager()) { - this.#makeButton("commentButton", `pdfjs-comment-floating-button`, "pdfjs-comment-floating-button-label", () => { - this.#uiManager.commentSelection("floating_button"); - }); - } - this.#makeButton("highlightButton", `pdfjs-highlight-floating-button1`, "pdfjs-highlight-floating-button-label", () => { - this.#uiManager.highlightSelection("floating_button"); - }); - return editToolbar; - } - #getLastPoint(boxes, isLTR) { - let lastY = 0; - let lastX = 0; - for (const box of boxes) { - const y = box.y + box.height; - if (y < lastY) { - continue; - } - const x = box.x + (isLTR ? box.width : 0); - if (y > lastY) { - lastX = x; - lastY = y; - continue; - } - if (isLTR) { - if (x > lastX) { - lastX = x; - } - } else if (x < lastX) { - lastX = x; - } - } - return [isLTR ? 1 - lastX : lastX, lastY]; - } - show(parent, boxes, isLTR) { - const [x, y] = this.#getLastPoint(boxes, isLTR); - const { - style - } = this.#toolbar ||= this.#render(); - parent.append(this.#toolbar); - style.insetInlineEnd = `${100 * x}%`; - style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`; - } - hide() { - this.#toolbar.remove(); - } - #makeButton(buttonClass, l10nId, labelL10nId, clickHandler) { - const button = document.createElement("button"); - button.classList.add("basic", buttonClass); - button.tabIndex = 0; - button.setAttribute("data-l10n-id", l10nId); - const span = document.createElement("span"); - button.append(span); - span.className = "visuallyHidden"; - span.setAttribute("data-l10n-id", labelL10nId); - const signal = this.#uiManager._signal; - if (signal instanceof AbortSignal && !signal.aborted) { - button.addEventListener("contextmenu", noContextMenu, { - signal - }); - button.addEventListener("click", clickHandler, { - signal - }); - } - this.#buttons.append(button); - } -} - -;// ./src/display/editor/tools.js - - - -function bindEvents(obj, element, names) { - for (const name of names) { - element.addEventListener(name, obj[name].bind(obj)); - } -} -class CurrentPointers { - static #pointerId = NaN; - static #pointerIds = null; - static #moveTimestamp = NaN; - static #pointerType = null; - static initializeAndAddPointerId(pointerId) { - (CurrentPointers.#pointerIds ||= new Set()).add(pointerId); - } - static setPointer(pointerType, pointerId) { - CurrentPointers.#pointerId ||= pointerId; - CurrentPointers.#pointerType ??= pointerType; - } - static setTimeStamp(timeStamp) { - CurrentPointers.#moveTimestamp = timeStamp; - } - static isSamePointerId(pointerId) { - return CurrentPointers.#pointerId === pointerId; - } - static isSamePointerIdOrRemove(pointerId) { - if (CurrentPointers.#pointerId === pointerId) { - return true; - } - CurrentPointers.#pointerIds?.delete(pointerId); - return false; - } - static isSamePointerType(pointerType) { - return CurrentPointers.#pointerType === pointerType; - } - static isInitializedAndDifferentPointerType(pointerType) { - return CurrentPointers.#pointerType !== null && !CurrentPointers.isSamePointerType(pointerType); - } - static isSameTimeStamp(timeStamp) { - return CurrentPointers.#moveTimestamp === timeStamp; - } - static isUsingMultiplePointers() { - return CurrentPointers.#pointerIds?.size >= 1; - } - static clearPointerType() { - CurrentPointers.#pointerType = null; - } - static clearPointerIds() { - CurrentPointers.#pointerId = NaN; - CurrentPointers.#pointerIds = null; - } - static clearTimeStamp() { - CurrentPointers.#moveTimestamp = NaN; - } -} -class IdManager { - #id = 0; - get id() { - return `${AnnotationEditorPrefix}${this.#id++}`; - } -} -class ImageManager { - #baseId = getUuid(); - #id = 0; - #cache = null; - static get _isSVGFittingCanvas() { - const svg = `data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 1 1" width="1" height="1" xmlns="http://www.w3.org/2000/svg"><rect width="1" height="1" style="fill:red;"/></svg>`; - const canvas = new OffscreenCanvas(1, 3); - const ctx = canvas.getContext("2d", { - willReadFrequently: true - }); - const image = new Image(); - image.src = svg; - const promise = image.decode().then(() => { - ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3); - return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0; - }); - return shadow(this, "_isSVGFittingCanvas", promise); - } - async #get(key, rawData) { - this.#cache ||= new Map(); - let data = this.#cache.get(key); - if (data === null) { - return null; - } - if (data?.bitmap) { - data.refCounter += 1; - return data; - } - try { - data ||= { - bitmap: null, - id: `image_${this.#baseId}_${this.#id++}`, - refCounter: 0, - isSvg: false - }; - let image; - if (typeof rawData === "string") { - data.url = rawData; - image = await fetchData(rawData, "blob"); - } else if (rawData instanceof File) { - image = data.file = rawData; - } else if (rawData instanceof Blob) { - image = rawData; - } - if (image.type === "image/svg+xml") { - const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas; - const fileReader = new FileReader(); - const imageElement = new Image(); - const imagePromise = new Promise((resolve, reject) => { - imageElement.onload = () => { - data.bitmap = imageElement; - data.isSvg = true; - resolve(); - }; - fileReader.onload = async () => { - const url = data.svgUrl = fileReader.result; - imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url; - }; - imageElement.onerror = fileReader.onerror = reject; - }); - fileReader.readAsDataURL(image); - await imagePromise; - } else { - data.bitmap = await createImageBitmap(image); - } - data.refCounter = 1; - } catch (e) { - warn(e); - data = null; - } - this.#cache.set(key, data); - if (data) { - this.#cache.set(data.id, data); - } - return data; - } - async getFromFile(file) { - const { - lastModified, - name, - size, - type - } = file; - return this.#get(`${lastModified}_${name}_${size}_${type}`, file); - } - async getFromUrl(url) { - return this.#get(url, url); - } - async getFromBlob(id, blobPromise) { - const blob = await blobPromise; - return this.#get(id, blob); - } - async getFromId(id) { - this.#cache ||= new Map(); - const data = this.#cache.get(id); - if (!data) { - return null; - } - if (data.bitmap) { - data.refCounter += 1; - return data; - } - if (data.file) { - return this.getFromFile(data.file); - } - if (data.blobPromise) { - const { - blobPromise - } = data; - delete data.blobPromise; - return this.getFromBlob(data.id, blobPromise); - } - return this.getFromUrl(data.url); - } - getFromCanvas(id, canvas) { - this.#cache ||= new Map(); - let data = this.#cache.get(id); - if (data?.bitmap) { - data.refCounter += 1; - return data; - } - const offscreen = new OffscreenCanvas(canvas.width, canvas.height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(canvas, 0, 0); - data = { - bitmap: offscreen.transferToImageBitmap(), - id: `image_${this.#baseId}_${this.#id++}`, - refCounter: 1, - isSvg: false - }; - this.#cache.set(id, data); - this.#cache.set(data.id, data); - return data; - } - getSvgUrl(id) { - const data = this.#cache.get(id); - if (!data?.isSvg) { - return null; - } - return data.svgUrl; - } - deleteId(id) { - this.#cache ||= new Map(); - const data = this.#cache.get(id); - if (!data) { - return; - } - data.refCounter -= 1; - if (data.refCounter !== 0) { - return; - } - const { - bitmap - } = data; - if (!data.url && !data.file) { - const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); - const ctx = canvas.getContext("bitmaprenderer"); - ctx.transferFromImageBitmap(bitmap); - data.blobPromise = canvas.convertToBlob(); - } - bitmap.close?.(); - data.bitmap = null; - } - isValidId(id) { - return id.startsWith(`image_${this.#baseId}_`); - } -} -class CommandManager { - #commands = []; - #locked = false; - #maxSize; - #position = -1; - constructor(maxSize = 128) { - this.#maxSize = maxSize; - } - add({ - cmd, - undo, - post, - mustExec, - type = NaN, - overwriteIfSameType = false, - keepUndo = false - }) { - if (mustExec) { - cmd(); - } - if (this.#locked) { - return; - } - const save = { - cmd, - undo, - post, - type - }; - if (this.#position === -1) { - if (this.#commands.length > 0) { - this.#commands.length = 0; - } - this.#position = 0; - this.#commands.push(save); - return; - } - if (overwriteIfSameType && this.#commands[this.#position].type === type) { - if (keepUndo) { - save.undo = this.#commands[this.#position].undo; - } - this.#commands[this.#position] = save; - return; - } - const next = this.#position + 1; - if (next === this.#maxSize) { - this.#commands.splice(0, 1); - } else { - this.#position = next; - if (next < this.#commands.length) { - this.#commands.splice(next); - } - } - this.#commands.push(save); - } - undo() { - if (this.#position === -1) { - return; - } - this.#locked = true; - const { - undo, - post - } = this.#commands[this.#position]; - undo(); - post?.(); - this.#locked = false; - this.#position -= 1; - } - redo() { - if (this.#position < this.#commands.length - 1) { - this.#position += 1; - this.#locked = true; - const { - cmd, - post - } = this.#commands[this.#position]; - cmd(); - post?.(); - this.#locked = false; - } - } - hasSomethingToUndo() { - return this.#position !== -1; - } - hasSomethingToRedo() { - return this.#position < this.#commands.length - 1; - } - cleanType(type) { - if (this.#position === -1) { - return; - } - for (let i = this.#position; i >= 0; i--) { - if (this.#commands[i].type !== type) { - this.#commands.splice(i + 1, this.#position - i); - this.#position = i; - return; - } - } - this.#commands.length = 0; - this.#position = -1; - } - destroy() { - this.#commands = null; - } -} -class KeyboardManager { - constructor(callbacks) { - this.buffer = []; - this.callbacks = new Map(); - this.allKeys = new Set(); - const { - isMac - } = FeatureTest.platform; - for (const [keys, callback, options = {}] of callbacks) { - for (const key of keys) { - const isMacKey = key.startsWith("mac+"); - if (isMac && isMacKey) { - this.callbacks.set(key.slice(4), { - callback, - options - }); - this.allKeys.add(key.split("+").at(-1)); - } else if (!isMac && !isMacKey) { - this.callbacks.set(key, { - callback, - options - }); - this.allKeys.add(key.split("+").at(-1)); - } - } - } - } - #serialize(event) { - if (event.altKey) { - this.buffer.push("alt"); - } - if (event.ctrlKey) { - this.buffer.push("ctrl"); - } - if (event.metaKey) { - this.buffer.push("meta"); - } - if (event.shiftKey) { - this.buffer.push("shift"); - } - this.buffer.push(event.key); - const str = this.buffer.join("+"); - this.buffer.length = 0; - return str; - } - exec(self, event) { - if (!this.allKeys.has(event.key)) { - return; - } - const info = this.callbacks.get(this.#serialize(event)); - if (!info) { - return; - } - const { - callback, - options: { - bubbles = false, - args = [], - checker = null - } - } = info; - if (checker && !checker(self, event)) { - return; - } - callback.bind(self, ...args, event)(); - if (!bubbles) { - stopEvent(event); - } - } -} -class ColorManager { - static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]); - get _colors() { - const colors = new Map([["CanvasText", null], ["Canvas", null]]); - getColorValues(colors); - return shadow(this, "_colors", colors); - } - convert(color) { - const rgb = getRGB(color); - if (!window.matchMedia("(forced-colors: active)").matches) { - return rgb; - } - for (const [name, RGB] of this._colors) { - if (RGB.every((x, i) => x === rgb[i])) { - return ColorManager._colorsMapping.get(name); - } - } - return rgb; - } - getHexCode(name) { - const rgb = this._colors.get(name); - if (!rgb) { - return name; - } - return Util.makeHexColor(...rgb); - } -} -class AnnotationEditorUIManager { - #abortController = new AbortController(); - #activeEditor = null; - #allEditableAnnotations = null; - #allEditors = new Map(); - #allLayers = new Map(); - #altTextManager = null; - #annotationStorage = null; - #changedExistingAnnotations = null; - #commandManager = new CommandManager(); - #commentManager = null; - #copyPasteAC = null; - #currentDrawingSession = null; - #currentPageIndex = 0; - #deletedAnnotationsElementIds = new Set(); - #draggingEditors = null; - #editorTypes = null; - #editorsToRescale = new Set(); - _editorUndoBar = null; - #enableHighlightFloatingButton = false; - #enableUpdatedAddImage = false; - #enableNewAltTextWhenAddingImage = false; - #filterFactory = null; - #focusMainContainerTimeoutId = null; - #focusManagerAC = null; - #highlightColors = null; - #highlightWhenShiftUp = false; - #floatingToolbar = null; - #idManager = new IdManager(); - #isEnabled = false; - #isPointerDown = false; - #isWaiting = false; - #keyboardManagerAC = null; - #lastActiveElement = null; - #mainHighlightColorPicker = null; - #missingCanvases = null; - #mlManager = null; - #mode = AnnotationEditorType.NONE; - #selectedEditors = new Set(); - #selectedTextNode = null; - #signatureManager = null; - #pageColors = null; - #showAllStates = null; - #pdfDocument = null; - #previousStates = { - isEditing: false, - isEmpty: true, - hasSomethingToUndo: false, - hasSomethingToRedo: false, - hasSelectedEditor: false, - hasSelectedText: false - }; - #translation = [0, 0]; - #translationTimeoutId = null; - #container = null; - #viewer = null; - #viewerAlert = null; - #updateModeCapability = null; - static TRANSLATE_SMALL = 1; - static TRANSLATE_BIG = 10; - static get _keyboardManager() { - const proto = AnnotationEditorUIManager.prototype; - const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl(); - const textInputChecker = (_self, { - target: el - }) => { - if (el instanceof HTMLInputElement) { - const { - type - } = el; - return type !== "text" && type !== "number"; - } - return true; - }; - const small = this.TRANSLATE_SMALL; - const big = this.TRANSLATE_BIG; - return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, { - checker: textInputChecker - }], [["ctrl+z", "mac+meta+z"], proto.undo, { - checker: textInputChecker - }], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, { - checker: textInputChecker - }], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, { - checker: textInputChecker - }], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, { - checker: (self, { - target: el - }) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled - }], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, { - checker: (self, { - target: el - }) => !(el instanceof HTMLButtonElement) && self.#container.contains(document.activeElement) - }], [["Escape", "mac+Escape"], proto.unselectAll], [["ArrowLeft", "mac+ArrowLeft"], proto.translateSelectedEditors, { - args: [-small, 0], - checker: arrowChecker - }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto.translateSelectedEditors, { - args: [-big, 0], - checker: arrowChecker - }], [["ArrowRight", "mac+ArrowRight"], proto.translateSelectedEditors, { - args: [small, 0], - checker: arrowChecker - }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto.translateSelectedEditors, { - args: [big, 0], - checker: arrowChecker - }], [["ArrowUp", "mac+ArrowUp"], proto.translateSelectedEditors, { - args: [0, -small], - checker: arrowChecker - }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto.translateSelectedEditors, { - args: [0, -big], - checker: arrowChecker - }], [["ArrowDown", "mac+ArrowDown"], proto.translateSelectedEditors, { - args: [0, small], - checker: arrowChecker - }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto.translateSelectedEditors, { - args: [0, big], - checker: arrowChecker - }]])); - } - constructor(container, viewer, viewerAlert, altTextManager, commentManager, signatureManager, eventBus, pdfDocument, pageColors, highlightColors, enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, mlManager, editorUndoBar, supportsPinchToZoom) { - const signal = this._signal = this.#abortController.signal; - this.#container = container; - this.#viewer = viewer; - this.#viewerAlert = viewerAlert; - this.#altTextManager = altTextManager; - this.#commentManager = commentManager; - this.#signatureManager = signatureManager; - this.#pdfDocument = pdfDocument; - this._eventBus = eventBus; - eventBus._on("editingaction", this.onEditingAction.bind(this), { - signal - }); - eventBus._on("pagechanging", this.onPageChanging.bind(this), { - signal - }); - eventBus._on("scalechanging", this.onScaleChanging.bind(this), { - signal - }); - eventBus._on("rotationchanging", this.onRotationChanging.bind(this), { - signal - }); - eventBus._on("setpreference", this.onSetPreference.bind(this), { - signal - }); - eventBus._on("switchannotationeditorparams", evt => this.updateParams(evt.type, evt.value), { - signal - }); - window.addEventListener("pointerdown", () => { - this.#isPointerDown = true; - }, { - capture: true, - signal - }); - window.addEventListener("pointerup", () => { - this.#isPointerDown = false; - }, { - capture: true, - signal - }); - window.addEventListener("beforeunload", this.#beforeUnload.bind(this), { - capture: true, - signal - }); - this.#addSelectionListener(); - this.#addDragAndDropListeners(); - this.#addKeyboardManager(); - this.#annotationStorage = pdfDocument.annotationStorage; - this.#filterFactory = pdfDocument.filterFactory; - this.#pageColors = pageColors; - this.#highlightColors = highlightColors || null; - this.#enableHighlightFloatingButton = enableHighlightFloatingButton; - this.#enableUpdatedAddImage = enableUpdatedAddImage; - this.#enableNewAltTextWhenAddingImage = enableNewAltTextWhenAddingImage; - this.#mlManager = mlManager || null; - this.viewParameters = { - realScale: PixelsPerInch.PDF_TO_CSS_UNITS, - rotation: 0 - }; - this.isShiftKeyDown = false; - this._editorUndoBar = editorUndoBar || null; - this._supportsPinchToZoom = supportsPinchToZoom !== false; - commentManager?.setSidebarUiManager(this); - } - destroy() { - this.#updateModeCapability?.resolve(); - this.#updateModeCapability = null; - this.#abortController?.abort(); - this.#abortController = null; - this._signal = null; - for (const layer of this.#allLayers.values()) { - layer.destroy(); - } - this.#allLayers.clear(); - this.#allEditors.clear(); - this.#editorsToRescale.clear(); - this.#missingCanvases?.clear(); - this.#activeEditor = null; - this.#selectedEditors.clear(); - this.#commandManager.destroy(); - this.#altTextManager?.destroy(); - this.#commentManager?.destroy(); - this.#signatureManager?.destroy(); - this.#floatingToolbar?.hide(); - this.#floatingToolbar = null; - this.#mainHighlightColorPicker?.destroy(); - this.#mainHighlightColorPicker = null; - this.#allEditableAnnotations = null; - if (this.#focusMainContainerTimeoutId) { - clearTimeout(this.#focusMainContainerTimeoutId); - this.#focusMainContainerTimeoutId = null; - } - if (this.#translationTimeoutId) { - clearTimeout(this.#translationTimeoutId); - this.#translationTimeoutId = null; - } - this._editorUndoBar?.destroy(); - this.#pdfDocument = null; - } - combinedSignal(ac) { - return AbortSignal.any([this._signal, ac.signal]); - } - get mlManager() { - return this.#mlManager; - } - get useNewAltTextFlow() { - return this.#enableUpdatedAddImage; - } - get useNewAltTextWhenAddingImage() { - return this.#enableNewAltTextWhenAddingImage; - } - get hcmFilter() { - return shadow(this, "hcmFilter", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : "none"); - } - get direction() { - return shadow(this, "direction", getComputedStyle(this.#container).direction); - } - get _highlightColors() { - return shadow(this, "_highlightColors", this.#highlightColors ? new Map(this.#highlightColors.split(",").map(pair => { - pair = pair.split("=").map(x => x.trim()); - pair[1] = pair[1].toUpperCase(); - return pair; - })) : null); - } - get highlightColors() { - const { - _highlightColors - } = this; - if (!_highlightColors) { - return shadow(this, "highlightColors", null); - } - const map = new Map(); - const hasHCM = !!this.#pageColors; - for (const [name, color] of _highlightColors) { - const isNameForHCM = name.endsWith("_HCM"); - if (hasHCM && isNameForHCM) { - map.set(name.replace("_HCM", ""), color); - continue; - } - if (!hasHCM && !isNameForHCM) { - map.set(name, color); - } - } - return shadow(this, "highlightColors", map); - } - get highlightColorNames() { - return shadow(this, "highlightColorNames", this.highlightColors ? new Map(Array.from(this.highlightColors, e => e.reverse())) : null); - } - getNonHCMColor(color) { - if (!this._highlightColors) { - return color; - } - const colorName = this.highlightColorNames.get(color); - return this._highlightColors.get(colorName) || color; - } - getNonHCMColorName(color) { - return this.highlightColorNames.get(color) || color; - } - setCurrentDrawingSession(layer) { - if (layer) { - this.unselectAll(); - this.disableUserSelect(true); - } else { - this.disableUserSelect(false); - } - this.#currentDrawingSession = layer; - } - setMainHighlightColorPicker(colorPicker) { - this.#mainHighlightColorPicker = colorPicker; - } - editAltText(editor, firstTime = false) { - this.#altTextManager?.editAltText(this, editor, firstTime); - } - hasCommentManager() { - return !!this.#commentManager; - } - editComment(editor, posX, posY, options) { - this.#commentManager?.showDialog(this, editor, posX, posY, options); - } - selectComment(pageIndex, uid) { - const layer = this.#allLayers.get(pageIndex); - const editor = layer?.getEditorByUID(uid); - editor?.toggleComment(true, true); - } - updateComment(editor) { - this.#commentManager?.updateComment(editor.getData()); - } - updatePopupColor(editor) { - this.#commentManager?.updatePopupColor(editor); - } - removeComment(editor) { - this.#commentManager?.removeComments([editor.uid]); - } - deleteComment(editor, savedData) { - const undo = () => { - editor.comment = savedData; - }; - const cmd = () => { - this._editorUndoBar?.show(undo, "comment"); - this.toggleComment(null); - editor.comment = null; - }; - this.addCommands({ - cmd, - undo, - mustExec: true - }); - } - toggleComment(editor, isSelected, visibility = undefined) { - this.#commentManager?.toggleCommentPopup(editor, isSelected, visibility); - } - makeCommentColor(color, opacity) { - return color && this.#commentManager?.makeCommentColor(color, opacity) || null; - } - getCommentDialogElement() { - return this.#commentManager?.dialogElement || null; - } - async waitForEditorsRendered(pageNumber) { - if (this.#allLayers.has(pageNumber - 1)) { - return; - } - const { - resolve, - promise - } = Promise.withResolvers(); - const onEditorsRendered = evt => { - if (evt.pageNumber === pageNumber) { - this._eventBus._off("editorsrendered", onEditorsRendered); - resolve(); - } - }; - this._eventBus.on("editorsrendered", onEditorsRendered); - await promise; - } - getSignature(editor) { - this.#signatureManager?.getSignature({ - uiManager: this, - editor - }); - } - get signatureManager() { - return this.#signatureManager; - } - switchToMode(mode, callback) { - this._eventBus.on("annotationeditormodechanged", callback, { - once: true, - signal: this._signal - }); - this._eventBus.dispatch("showannotationeditorui", { - source: this, - mode - }); - } - setPreference(name, value) { - this._eventBus.dispatch("setpreference", { - source: this, - name, - value - }); - } - onSetPreference({ - name, - value - }) { - switch (name) { - case "enableNewAltTextWhenAddingImage": - this.#enableNewAltTextWhenAddingImage = value; - break; - } - } - onPageChanging({ - pageNumber - }) { - this.#currentPageIndex = pageNumber - 1; - } - deletePage(id) { - for (const editor of this.getEditors(id)) { - editor.remove(); - } - this.#allLayers.delete(id); - if (this.#currentPageIndex === id) { - this.#currentPageIndex = 0; - } - } - focusMainContainer() { - this.#container.focus(); - } - findParent(x, y) { - for (const layer of this.#allLayers.values()) { - const { - x: layerX, - y: layerY, - width, - height - } = layer.div.getBoundingClientRect(); - if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) { - return layer; - } - } - return null; - } - disableUserSelect(value = false) { - this.#viewer.classList.toggle("noUserSelect", value); - } - addShouldRescale(editor) { - this.#editorsToRescale.add(editor); - } - removeShouldRescale(editor) { - this.#editorsToRescale.delete(editor); - } - onScaleChanging({ - scale - }) { - this.commitOrRemove(); - this.viewParameters.realScale = scale * PixelsPerInch.PDF_TO_CSS_UNITS; - for (const editor of this.#editorsToRescale) { - editor.onScaleChanging(); - } - this.#currentDrawingSession?.onScaleChanging(); - } - onRotationChanging({ - pagesRotation - }) { - this.commitOrRemove(); - this.viewParameters.rotation = pagesRotation; - } - #getAnchorElementForSelection({ - anchorNode - }) { - return anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode; - } - #getLayerForTextLayer(textLayer) { - const { - currentLayer - } = this; - if (currentLayer.hasTextLayer(textLayer)) { - return currentLayer; - } - for (const layer of this.#allLayers.values()) { - if (layer.hasTextLayer(textLayer)) { - return layer; - } - } - return null; - } - highlightSelection(methodOfCreation = "", comment = false) { - const selection = document.getSelection(); - if (!selection || selection.isCollapsed) { - return; - } - const { - anchorNode, - anchorOffset, - focusNode, - focusOffset - } = selection; - const text = selection.toString(); - const anchorElement = this.#getAnchorElementForSelection(selection); - const textLayer = anchorElement.closest(".textLayer"); - const boxes = this.getSelectionBoxes(textLayer); - if (!boxes) { - return; - } - selection.empty(); - const layer = this.#getLayerForTextLayer(textLayer); - const isNoneMode = this.#mode === AnnotationEditorType.NONE; - const callback = () => { - const editor = layer?.createAndAddNewEditor({ - x: 0, - y: 0 - }, false, { - methodOfCreation, - boxes, - anchorNode, - anchorOffset, - focusNode, - focusOffset, - text - }); - if (isNoneMode) { - this.showAllEditors("highlight", true, true); - } - if (comment) { - editor?.editComment(); - } - }; - if (isNoneMode) { - this.switchToMode(AnnotationEditorType.HIGHLIGHT, callback); - return; - } - callback(); - } - commentSelection(methodOfCreation = "") { - this.highlightSelection(methodOfCreation, true); - } - #beforeUnload(e) { - this.commitOrRemove(); - this.currentLayer?.endDrawingSession(false); - } - #displayFloatingToolbar() { - const selection = document.getSelection(); - if (!selection || selection.isCollapsed) { - return; - } - const anchorElement = this.#getAnchorElementForSelection(selection); - const textLayer = anchorElement.closest(".textLayer"); - const boxes = this.getSelectionBoxes(textLayer); - if (!boxes) { - return; - } - this.#floatingToolbar ||= new FloatingToolbar(this); - this.#floatingToolbar.show(textLayer, boxes, this.direction === "ltr"); - } - getAndRemoveDataFromAnnotationStorage(annotationId) { - if (!this.#annotationStorage) { - return null; - } - const key = `${AnnotationEditorPrefix}${annotationId}`; - const storedValue = this.#annotationStorage.getRawValue(key); - if (storedValue) { - this.#annotationStorage.remove(key); - } - return storedValue; - } - addToAnnotationStorage(editor) { - if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) { - this.#annotationStorage.setValue(editor.id, editor); - } - } - a11yAlert(messageId, args = null) { - const viewerAlert = this.#viewerAlert; - if (!viewerAlert) { - return; - } - viewerAlert.setAttribute("data-l10n-id", messageId); - if (args) { - viewerAlert.setAttribute("data-l10n-args", JSON.stringify(args)); - } else { - viewerAlert.removeAttribute("data-l10n-args"); - } - } - #selectionChange() { - const selection = document.getSelection(); - if (!selection || selection.isCollapsed) { - if (this.#selectedTextNode) { - this.#floatingToolbar?.hide(); - this.#selectedTextNode = null; - this.#dispatchUpdateStates({ - hasSelectedText: false - }); - } - return; - } - const { - anchorNode - } = selection; - if (anchorNode === this.#selectedTextNode) { - return; - } - const anchorElement = this.#getAnchorElementForSelection(selection); - const textLayer = anchorElement.closest(".textLayer"); - if (!textLayer) { - if (this.#selectedTextNode) { - this.#floatingToolbar?.hide(); - this.#selectedTextNode = null; - this.#dispatchUpdateStates({ - hasSelectedText: false - }); - } - return; - } - this.#floatingToolbar?.hide(); - this.#selectedTextNode = anchorNode; - this.#dispatchUpdateStates({ - hasSelectedText: true - }); - if (this.#mode !== AnnotationEditorType.HIGHLIGHT && this.#mode !== AnnotationEditorType.NONE) { - return; - } - if (this.#mode === AnnotationEditorType.HIGHLIGHT) { - this.showAllEditors("highlight", true, true); - } - this.#highlightWhenShiftUp = this.isShiftKeyDown; - if (!this.isShiftKeyDown) { - const activeLayer = this.#mode === AnnotationEditorType.HIGHLIGHT ? this.#getLayerForTextLayer(textLayer) : null; - activeLayer?.toggleDrawing(); - if (this.#isPointerDown) { - const ac = new AbortController(); - const signal = this.combinedSignal(ac); - const pointerup = e => { - if (e.type === "pointerup" && e.button !== 0) { - return; - } - ac.abort(); - activeLayer?.toggleDrawing(true); - if (e.type === "pointerup") { - this.#onSelectEnd("main_toolbar"); - } - }; - window.addEventListener("pointerup", pointerup, { - signal - }); - window.addEventListener("blur", pointerup, { - signal - }); - } else { - activeLayer?.toggleDrawing(true); - this.#onSelectEnd("main_toolbar"); - } - } - } - #onSelectEnd(methodOfCreation = "") { - if (this.#mode === AnnotationEditorType.HIGHLIGHT) { - this.highlightSelection(methodOfCreation); - } else if (this.#enableHighlightFloatingButton) { - this.#displayFloatingToolbar(); - } - } - #addSelectionListener() { - document.addEventListener("selectionchange", this.#selectionChange.bind(this), { - signal: this._signal - }); - } - #addFocusManager() { - if (this.#focusManagerAC) { - return; - } - this.#focusManagerAC = new AbortController(); - const signal = this.combinedSignal(this.#focusManagerAC); - window.addEventListener("focus", this.focus.bind(this), { - signal - }); - window.addEventListener("blur", this.blur.bind(this), { - signal - }); - } - #removeFocusManager() { - this.#focusManagerAC?.abort(); - this.#focusManagerAC = null; - } - blur() { - this.isShiftKeyDown = false; - if (this.#highlightWhenShiftUp) { - this.#highlightWhenShiftUp = false; - this.#onSelectEnd("main_toolbar"); - } - if (!this.hasSelection) { - return; - } - const { - activeElement - } = document; - for (const editor of this.#selectedEditors) { - if (editor.div.contains(activeElement)) { - this.#lastActiveElement = [editor, activeElement]; - editor._focusEventsAllowed = false; - break; - } - } - } - focus() { - if (!this.#lastActiveElement) { - return; - } - const [lastEditor, lastActiveElement] = this.#lastActiveElement; - this.#lastActiveElement = null; - lastActiveElement.addEventListener("focusin", () => { - lastEditor._focusEventsAllowed = true; - }, { - once: true, - signal: this._signal - }); - lastActiveElement.focus(); - } - #addKeyboardManager() { - if (this.#keyboardManagerAC) { - return; - } - this.#keyboardManagerAC = new AbortController(); - const signal = this.combinedSignal(this.#keyboardManagerAC); - window.addEventListener("keydown", this.keydown.bind(this), { - signal - }); - window.addEventListener("keyup", this.keyup.bind(this), { - signal - }); - } - #removeKeyboardManager() { - this.#keyboardManagerAC?.abort(); - this.#keyboardManagerAC = null; - } - #addCopyPasteListeners() { - if (this.#copyPasteAC) { - return; - } - this.#copyPasteAC = new AbortController(); - const signal = this.combinedSignal(this.#copyPasteAC); - document.addEventListener("copy", this.copy.bind(this), { - signal - }); - document.addEventListener("cut", this.cut.bind(this), { - signal - }); - document.addEventListener("paste", this.paste.bind(this), { - signal - }); - } - #removeCopyPasteListeners() { - this.#copyPasteAC?.abort(); - this.#copyPasteAC = null; - } - #addDragAndDropListeners() { - const signal = this._signal; - document.addEventListener("dragover", this.dragOver.bind(this), { - signal - }); - document.addEventListener("drop", this.drop.bind(this), { - signal - }); - } - addEditListeners() { - this.#addKeyboardManager(); - this.setEditingState(true); - } - removeEditListeners() { - this.#removeKeyboardManager(); - this.setEditingState(false); - } - dragOver(event) { - for (const { - type - } of event.dataTransfer.items) { - for (const editorType of this.#editorTypes) { - if (editorType.isHandlingMimeForPasting(type)) { - event.dataTransfer.dropEffect = "copy"; - event.preventDefault(); - return; - } - } - } - } - drop(event) { - for (const item of event.dataTransfer.items) { - for (const editorType of this.#editorTypes) { - if (editorType.isHandlingMimeForPasting(item.type)) { - editorType.paste(item, this.currentLayer); - event.preventDefault(); - return; - } - } - } - } - copy(event) { - event.preventDefault(); - this.#activeEditor?.commitOrRemove(); - if (!this.hasSelection) { - return; - } - const editors = []; - for (const editor of this.#selectedEditors) { - const serialized = editor.serialize(true); - if (serialized) { - editors.push(serialized); - } - } - if (editors.length === 0) { - return; - } - event.clipboardData.setData("application/pdfjs", JSON.stringify(editors)); - } - cut(event) { - this.copy(event); - this.delete(); - } - async paste(event) { - event.preventDefault(); - const { - clipboardData - } = event; - for (const item of clipboardData.items) { - for (const editorType of this.#editorTypes) { - if (editorType.isHandlingMimeForPasting(item.type)) { - editorType.paste(item, this.currentLayer); - return; - } - } - } - let data = clipboardData.getData("application/pdfjs"); - if (!data) { - return; - } - try { - data = JSON.parse(data); - } catch (ex) { - warn(`paste: "${ex.message}".`); - return; - } - if (!Array.isArray(data)) { - return; - } - this.unselectAll(); - const layer = this.currentLayer; - try { - const newEditors = []; - for (const editor of data) { - const deserializedEditor = await layer.deserialize(editor); - if (!deserializedEditor) { - return; - } - newEditors.push(deserializedEditor); - } - const cmd = () => { - for (const editor of newEditors) { - this.#addEditorToLayer(editor); - } - this.#selectEditors(newEditors); - }; - const undo = () => { - for (const editor of newEditors) { - editor.remove(); - } - }; - this.addCommands({ - cmd, - undo, - mustExec: true - }); - } catch (ex) { - warn(`paste: "${ex.message}".`); - } - } - keydown(event) { - if (!this.isShiftKeyDown && event.key === "Shift") { - this.isShiftKeyDown = true; - } - if (this.#mode !== AnnotationEditorType.NONE && !this.isEditorHandlingKeyboard) { - AnnotationEditorUIManager._keyboardManager.exec(this, event); - } - } - keyup(event) { - if (this.isShiftKeyDown && event.key === "Shift") { - this.isShiftKeyDown = false; - if (this.#highlightWhenShiftUp) { - this.#highlightWhenShiftUp = false; - this.#onSelectEnd("main_toolbar"); - } - } - } - onEditingAction({ - name - }) { - switch (name) { - case "undo": - case "redo": - case "delete": - case "selectAll": - this[name](); - break; - case "highlightSelection": - this.highlightSelection("context_menu"); - break; - case "commentSelection": - this.commentSelection("context_menu"); - break; - } - } - #dispatchUpdateStates(details) { - const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value); - if (hasChanged) { - this._eventBus.dispatch("annotationeditorstateschanged", { - source: this, - details: Object.assign(this.#previousStates, details) - }); - if (this.#mode === AnnotationEditorType.HIGHLIGHT && details.hasSelectedEditor === false) { - this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_FREE, true]]); - } - } - } - #dispatchUpdateUI(details) { - this._eventBus.dispatch("annotationeditorparamschanged", { - source: this, - details - }); - } - setEditingState(isEditing) { - if (isEditing) { - this.#addFocusManager(); - this.#addCopyPasteListeners(); - this.#dispatchUpdateStates({ - isEditing: this.#mode !== AnnotationEditorType.NONE, - isEmpty: this.#isEmpty(), - hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), - hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), - hasSelectedEditor: false - }); - } else { - this.#removeFocusManager(); - this.#removeCopyPasteListeners(); - this.#dispatchUpdateStates({ - isEditing: false - }); - this.disableUserSelect(false); - } - } - registerEditorTypes(types) { - if (this.#editorTypes) { - return; - } - this.#editorTypes = types; - for (const editorType of this.#editorTypes) { - this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); - } - } - getId() { - return this.#idManager.id; - } - get currentLayer() { - return this.#allLayers.get(this.#currentPageIndex); - } - getLayer(pageIndex) { - return this.#allLayers.get(pageIndex); - } - get currentPageIndex() { - return this.#currentPageIndex; - } - addLayer(layer) { - this.#allLayers.set(layer.pageIndex, layer); - if (this.#isEnabled) { - layer.enable(); - } else { - layer.disable(); - } - } - removeLayer(layer) { - this.#allLayers.delete(layer.pageIndex); - } - async updateMode(mode, editId = null, isFromUser = false, isFromKeyboard = false, mustEnterInEditMode = false, editComment = false) { - if (this.#mode === mode) { - return; - } - if (this.#updateModeCapability) { - await this.#updateModeCapability.promise; - if (!this.#updateModeCapability) { - return; - } - } - this.#updateModeCapability = Promise.withResolvers(); - this.#currentDrawingSession?.commitOrRemove(); - if (this.#mode === AnnotationEditorType.POPUP) { - this.#commentManager?.hideSidebar(); - } - this.#commentManager?.destroyPopup(); - this.#mode = mode; - if (mode === AnnotationEditorType.NONE) { - this.setEditingState(false); - this.#disableAll(); - for (const editor of this.#allEditors.values()) { - editor.hideStandaloneCommentButton(); - } - this._editorUndoBar?.hide(); - this.toggleComment(null); - this.#updateModeCapability.resolve(); - return; - } - for (const editor of this.#allEditors.values()) { - editor.addStandaloneCommentButton(); - } - if (mode === AnnotationEditorType.SIGNATURE) { - await this.#signatureManager?.loadSignatures(); - } - if (isFromUser) { - CurrentPointers.clearPointerType(); - } - this.setEditingState(true); - await this.#enableAll(); - this.unselectAll(); - for (const layer of this.#allLayers.values()) { - layer.updateMode(mode); - } - if (mode === AnnotationEditorType.POPUP) { - this.#allEditableAnnotations ||= await this.#pdfDocument.getAnnotationsByType(new Set(this.#editorTypes.map(editorClass => editorClass._editorType))); - const elementIds = new Set(); - const allComments = []; - for (const editor of this.#allEditors.values()) { - const { - annotationElementId, - hasComment, - deleted - } = editor; - if (annotationElementId) { - elementIds.add(annotationElementId); - } - if (hasComment && !deleted) { - allComments.push(editor.getData()); - } - } - for (const annotation of this.#allEditableAnnotations) { - const { - id, - popupRef, - contentsObj - } = annotation; - if (popupRef && contentsObj?.str && !elementIds.has(id) && !this.#deletedAnnotationsElementIds.has(id)) { - allComments.push(annotation); - } - } - this.#commentManager?.showSidebar(allComments); - } - if (!editId) { - if (isFromKeyboard) { - this.addNewEditorFromKeyboard(); - } - this.#updateModeCapability.resolve(); - return; - } - for (const editor of this.#allEditors.values()) { - if (editor.uid === editId) { - this.setSelected(editor); - if (editComment) { - editor.editComment(); - } else if (mustEnterInEditMode) { - editor.enterInEditMode(); - } else { - editor.focus(); - } - } else { - editor.unselect(); - } - } - this.#updateModeCapability.resolve(); - } - addNewEditorFromKeyboard() { - if (this.currentLayer.canCreateNewEmptyEditor()) { - this.currentLayer.addNewEditor(); - } - } - updateToolbar(options) { - if (options.mode === this.#mode) { - return; - } - this._eventBus.dispatch("switchannotationeditormode", { - source: this, - ...options - }); - } - updateParams(type, value) { - if (!this.#editorTypes) { - return; - } - switch (type) { - case AnnotationEditorParamsType.CREATE: - this.currentLayer.addNewEditor(value); - return; - case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL: - this._eventBus.dispatch("reporttelemetry", { - source: this, - details: { - type: "editing", - data: { - type: "highlight", - action: "toggle_visibility" - } - } - }); - (this.#showAllStates ||= new Map()).set(type, value); - this.showAllEditors("highlight", value); - break; - } - if (this.hasSelection) { - for (const editor of this.#selectedEditors) { - editor.updateParams(type, value); - } - } else { - for (const editorType of this.#editorTypes) { - editorType.updateDefaultParams(type, value); - } - } - } - showAllEditors(type, visible, updateButton = false) { - for (const editor of this.#allEditors.values()) { - if (editor.editorType === type) { - editor.show(visible); - } - } - const state = this.#showAllStates?.get(AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL) ?? true; - if (state !== visible) { - this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL, visible]]); - } - } - enableWaiting(mustWait = false) { - if (this.#isWaiting === mustWait) { - return; - } - this.#isWaiting = mustWait; - for (const layer of this.#allLayers.values()) { - if (mustWait) { - layer.disableClick(); - } else { - layer.enableClick(); - } - layer.div.classList.toggle("waiting", mustWait); - } - } - async #enableAll() { - if (!this.#isEnabled) { - this.#isEnabled = true; - const promises = []; - for (const layer of this.#allLayers.values()) { - promises.push(layer.enable()); - } - await Promise.all(promises); - for (const editor of this.#allEditors.values()) { - editor.enable(); - } - } - } - #disableAll() { - this.unselectAll(); - if (this.#isEnabled) { - this.#isEnabled = false; - for (const layer of this.#allLayers.values()) { - layer.disable(); - } - for (const editor of this.#allEditors.values()) { - editor.disable(); - } - } - } - *getEditors(pageIndex) { - for (const editor of this.#allEditors.values()) { - if (editor.pageIndex === pageIndex) { - yield editor; - } - } - } - getEditor(id) { - return this.#allEditors.get(id); - } - addEditor(editor) { - this.#allEditors.set(editor.id, editor); - } - removeEditor(editor) { - if (editor.div.contains(document.activeElement)) { - if (this.#focusMainContainerTimeoutId) { - clearTimeout(this.#focusMainContainerTimeoutId); - } - this.#focusMainContainerTimeoutId = setTimeout(() => { - this.focusMainContainer(); - this.#focusMainContainerTimeoutId = null; - }, 0); - } - this.#allEditors.delete(editor.id); - if (editor.annotationElementId) { - this.#missingCanvases?.delete(editor.annotationElementId); - } - this.unselect(editor); - if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) { - this.#annotationStorage?.remove(editor.id); - } - } - addDeletedAnnotationElement(editor) { - this.#deletedAnnotationsElementIds.add(editor.annotationElementId); - this.addChangedExistingAnnotation(editor); - editor.deleted = true; - } - isDeletedAnnotationElement(annotationElementId) { - return this.#deletedAnnotationsElementIds.has(annotationElementId); - } - removeDeletedAnnotationElement(editor) { - this.#deletedAnnotationsElementIds.delete(editor.annotationElementId); - this.removeChangedExistingAnnotation(editor); - editor.deleted = false; - } - #addEditorToLayer(editor) { - const layer = this.#allLayers.get(editor.pageIndex); - if (layer) { - layer.addOrRebuild(editor); - } else { - this.addEditor(editor); - this.addToAnnotationStorage(editor); - } - } - setActiveEditor(editor) { - if (this.#activeEditor === editor) { - return; - } - this.#activeEditor = editor; - if (editor) { - this.#dispatchUpdateUI(editor.propertiesToUpdate); - } - } - get #lastSelectedEditor() { - let ed = null; - for (ed of this.#selectedEditors) {} - return ed; - } - updateUI(editor) { - if (this.#lastSelectedEditor === editor) { - this.#dispatchUpdateUI(editor.propertiesToUpdate); - } - } - updateUIForDefaultProperties(editorType) { - this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); - } - toggleSelected(editor) { - if (this.#selectedEditors.has(editor)) { - this.#selectedEditors.delete(editor); - editor.unselect(); - this.#dispatchUpdateStates({ - hasSelectedEditor: this.hasSelection - }); - return; - } - this.#selectedEditors.add(editor); - editor.select(); - this.#dispatchUpdateUI(editor.propertiesToUpdate); - this.#dispatchUpdateStates({ - hasSelectedEditor: true - }); - } - setSelected(editor) { - this.updateToolbar({ - mode: editor.mode, - editId: editor.uid - }); - this.#currentDrawingSession?.commitOrRemove(); - for (const ed of this.#selectedEditors) { - if (ed !== editor) { - ed.unselect(); - } - } - this.#selectedEditors.clear(); - this.#selectedEditors.add(editor); - editor.select(); - this.#dispatchUpdateUI(editor.propertiesToUpdate); - this.#dispatchUpdateStates({ - hasSelectedEditor: true - }); - } - isSelected(editor) { - return this.#selectedEditors.has(editor); - } - get firstSelectedEditor() { - return this.#selectedEditors.values().next().value; - } - unselect(editor) { - editor.unselect(); - this.#selectedEditors.delete(editor); - this.#dispatchUpdateStates({ - hasSelectedEditor: this.hasSelection - }); - } - get hasSelection() { - return this.#selectedEditors.size !== 0; - } - get isEnterHandled() { - return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled; - } - undo() { - this.#commandManager.undo(); - this.#dispatchUpdateStates({ - hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), - hasSomethingToRedo: true, - isEmpty: this.#isEmpty() - }); - this._editorUndoBar?.hide(); - } - redo() { - this.#commandManager.redo(); - this.#dispatchUpdateStates({ - hasSomethingToUndo: true, - hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), - isEmpty: this.#isEmpty() - }); - } - addCommands(params) { - this.#commandManager.add(params); - this.#dispatchUpdateStates({ - hasSomethingToUndo: true, - hasSomethingToRedo: false, - isEmpty: this.#isEmpty() - }); - } - cleanUndoStack(type) { - this.#commandManager.cleanType(type); - } - #isEmpty() { - if (this.#allEditors.size === 0) { - return true; - } - if (this.#allEditors.size === 1) { - for (const editor of this.#allEditors.values()) { - return editor.isEmpty(); - } - } - return false; - } - delete() { - this.commitOrRemove(); - const drawingEditor = this.currentLayer?.endDrawingSession(true); - if (!this.hasSelection && !drawingEditor) { - return; - } - const editors = drawingEditor ? [drawingEditor] : [...this.#selectedEditors]; - const cmd = () => { - this._editorUndoBar?.show(undo, editors.length === 1 ? editors[0].editorType : editors.length); - for (const editor of editors) { - editor.remove(); - } - }; - const undo = () => { - for (const editor of editors) { - this.#addEditorToLayer(editor); - } - }; - this.addCommands({ - cmd, - undo, - mustExec: true - }); - } - commitOrRemove() { - this.#activeEditor?.commitOrRemove(); - } - hasSomethingToControl() { - return this.#activeEditor || this.hasSelection; - } - #selectEditors(editors) { - for (const editor of this.#selectedEditors) { - editor.unselect(); - } - this.#selectedEditors.clear(); - for (const editor of editors) { - if (editor.isEmpty()) { - continue; - } - this.#selectedEditors.add(editor); - editor.select(); - } - this.#dispatchUpdateStates({ - hasSelectedEditor: this.hasSelection - }); - } - selectAll() { - for (const editor of this.#selectedEditors) { - editor.commit(); - } - this.#selectEditors(this.#allEditors.values()); - } - unselectAll() { - if (this.#activeEditor) { - this.#activeEditor.commitOrRemove(); - if (this.#mode !== AnnotationEditorType.NONE) { - return; - } - } - if (this.#currentDrawingSession?.commitOrRemove()) { - return; - } - if (!this.hasSelection) { - return; - } - for (const editor of this.#selectedEditors) { - editor.unselect(); - } - this.#selectedEditors.clear(); - this.#dispatchUpdateStates({ - hasSelectedEditor: false - }); - } - translateSelectedEditors(x, y, noCommit = false) { - if (!noCommit) { - this.commitOrRemove(); - } - if (!this.hasSelection) { - return; - } - this.#translation[0] += x; - this.#translation[1] += y; - const [totalX, totalY] = this.#translation; - const editors = [...this.#selectedEditors]; - const TIME_TO_WAIT = 1000; - if (this.#translationTimeoutId) { - clearTimeout(this.#translationTimeoutId); - } - this.#translationTimeoutId = setTimeout(() => { - this.#translationTimeoutId = null; - this.#translation[0] = this.#translation[1] = 0; - this.addCommands({ - cmd: () => { - for (const editor of editors) { - if (this.#allEditors.has(editor.id)) { - editor.translateInPage(totalX, totalY); - editor.translationDone(); - } - } - }, - undo: () => { - for (const editor of editors) { - if (this.#allEditors.has(editor.id)) { - editor.translateInPage(-totalX, -totalY); - editor.translationDone(); - } - } - }, - mustExec: false - }); - }, TIME_TO_WAIT); - for (const editor of editors) { - editor.translateInPage(x, y); - editor.translationDone(); - } - } - setUpDragSession() { - if (!this.hasSelection) { - return; - } - this.disableUserSelect(true); - this.#draggingEditors = new Map(); - for (const editor of this.#selectedEditors) { - this.#draggingEditors.set(editor, { - savedX: editor.x, - savedY: editor.y, - savedPageIndex: editor.pageIndex, - newX: 0, - newY: 0, - newPageIndex: -1 - }); - } - } - endDragSession() { - if (!this.#draggingEditors) { - return false; - } - this.disableUserSelect(false); - const map = this.#draggingEditors; - this.#draggingEditors = null; - let mustBeAddedInUndoStack = false; - for (const [{ - x, - y, - pageIndex - }, value] of map) { - value.newX = x; - value.newY = y; - value.newPageIndex = pageIndex; - mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex; - } - if (!mustBeAddedInUndoStack) { - return false; - } - const move = (editor, x, y, pageIndex) => { - if (this.#allEditors.has(editor.id)) { - const parent = this.#allLayers.get(pageIndex); - if (parent) { - editor._setParentAndPosition(parent, x, y); - } else { - editor.pageIndex = pageIndex; - editor.x = x; - editor.y = y; - } - } - }; - this.addCommands({ - cmd: () => { - for (const [editor, { - newX, - newY, - newPageIndex - }] of map) { - move(editor, newX, newY, newPageIndex); - } - }, - undo: () => { - for (const [editor, { - savedX, - savedY, - savedPageIndex - }] of map) { - move(editor, savedX, savedY, savedPageIndex); - } - }, - mustExec: true - }); - return true; - } - dragSelectedEditors(tx, ty) { - if (!this.#draggingEditors) { - return; - } - for (const editor of this.#draggingEditors.keys()) { - editor.drag(tx, ty); - } - } - rebuild(editor) { - if (editor.parent === null) { - const parent = this.getLayer(editor.pageIndex); - if (parent) { - parent.changeParent(editor); - parent.addOrRebuild(editor); - } else { - this.addEditor(editor); - this.addToAnnotationStorage(editor); - editor.rebuild(); - } - } else { - editor.parent.addOrRebuild(editor); - } - } - get isEditorHandlingKeyboard() { - return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents(); - } - isActive(editor) { - return this.#activeEditor === editor; - } - getActive() { - return this.#activeEditor; - } - getMode() { - return this.#mode; - } - isEditingMode() { - return this.#mode !== AnnotationEditorType.NONE; - } - get imageManager() { - return shadow(this, "imageManager", new ImageManager()); - } - getSelectionBoxes(textLayer) { - if (!textLayer) { - return null; - } - const selection = document.getSelection(); - for (let i = 0, ii = selection.rangeCount; i < ii; i++) { - if (!textLayer.contains(selection.getRangeAt(i).commonAncestorContainer)) { - return null; - } - } - const { - x: layerX, - y: layerY, - width: parentWidth, - height: parentHeight - } = textLayer.getBoundingClientRect(); - let rotator; - switch (textLayer.getAttribute("data-main-rotation")) { - case "90": - rotator = (x, y, w, h) => ({ - x: (y - layerY) / parentHeight, - y: 1 - (x + w - layerX) / parentWidth, - width: h / parentHeight, - height: w / parentWidth - }); - break; - case "180": - rotator = (x, y, w, h) => ({ - x: 1 - (x + w - layerX) / parentWidth, - y: 1 - (y + h - layerY) / parentHeight, - width: w / parentWidth, - height: h / parentHeight - }); - break; - case "270": - rotator = (x, y, w, h) => ({ - x: 1 - (y + h - layerY) / parentHeight, - y: (x - layerX) / parentWidth, - width: h / parentHeight, - height: w / parentWidth - }); - break; - default: - rotator = (x, y, w, h) => ({ - x: (x - layerX) / parentWidth, - y: (y - layerY) / parentHeight, - width: w / parentWidth, - height: h / parentHeight - }); - break; - } - const boxes = []; - for (let i = 0, ii = selection.rangeCount; i < ii; i++) { - const range = selection.getRangeAt(i); - if (range.collapsed) { - continue; - } - for (const { - x, - y, - width, - height - } of range.getClientRects()) { - if (width === 0 || height === 0) { - continue; - } - boxes.push(rotator(x, y, width, height)); - } - } - return boxes.length === 0 ? null : boxes; - } - addChangedExistingAnnotation({ - annotationElementId, - id - }) { - (this.#changedExistingAnnotations ||= new Map()).set(annotationElementId, id); - } - removeChangedExistingAnnotation({ - annotationElementId - }) { - this.#changedExistingAnnotations?.delete(annotationElementId); - } - renderAnnotationElement(annotation) { - const editorId = this.#changedExistingAnnotations?.get(annotation.data.id); - if (!editorId) { - return; - } - const editor = this.#annotationStorage.getRawValue(editorId); - if (!editor) { - return; - } - if (this.#mode === AnnotationEditorType.NONE && !editor.hasBeenModified) { - return; - } - editor.renderAnnotationElement(annotation); - } - setMissingCanvas(annotationId, annotationElementId, canvas) { - const editor = this.#missingCanvases?.get(annotationId); - if (!editor) { - return; - } - editor.setCanvas(annotationElementId, canvas); - this.#missingCanvases.delete(annotationId); - } - addMissingCanvas(annotationId, editor) { - (this.#missingCanvases ||= new Map()).set(annotationId, editor); - } -} - -;// ./src/display/editor/alt_text.js - -class AltText { - #altText = null; - #altTextDecorative = false; - #altTextButton = null; - #altTextButtonLabel = null; - #altTextTooltip = null; - #altTextTooltipTimeout = null; - #altTextWasFromKeyBoard = false; - #badge = null; - #editor = null; - #guessedText = null; - #textWithDisclaimer = null; - #useNewAltTextFlow = false; - static #l10nNewButton = null; - static _l10n = null; - constructor(editor) { - this.#editor = editor; - this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow; - AltText.#l10nNewButton ||= Object.freeze({ - added: "pdfjs-editor-new-alt-text-added-button", - "added-label": "pdfjs-editor-new-alt-text-added-button-label", - missing: "pdfjs-editor-new-alt-text-missing-button", - "missing-label": "pdfjs-editor-new-alt-text-missing-button-label", - review: "pdfjs-editor-new-alt-text-to-review-button", - "review-label": "pdfjs-editor-new-alt-text-to-review-button-label" - }); - } - static initialize(l10n) { - AltText._l10n ??= l10n; - } - async render() { - const altText = this.#altTextButton = document.createElement("button"); - altText.className = "altText"; - altText.tabIndex = "0"; - const label = this.#altTextButtonLabel = document.createElement("span"); - altText.append(label); - if (this.#useNewAltTextFlow) { - altText.classList.add("new"); - altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing); - label.setAttribute("data-l10n-id", AltText.#l10nNewButton["missing-label"]); - } else { - altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button"); - label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label"); - } - const signal = this.#editor._uiManager._signal; - altText.addEventListener("contextmenu", noContextMenu, { - signal - }); - altText.addEventListener("pointerdown", event => event.stopPropagation(), { - signal - }); - const onClick = event => { - event.preventDefault(); - this.#editor._uiManager.editAltText(this.#editor); - if (this.#useNewAltTextFlow) { - this.#editor._reportTelemetry({ - action: "pdfjs.image.alt_text.image_status_label_clicked", - data: { - label: this.#label - } - }); - } - }; - altText.addEventListener("click", onClick, { - capture: true, - signal - }); - altText.addEventListener("keydown", event => { - if (event.target === altText && event.key === "Enter") { - this.#altTextWasFromKeyBoard = true; - onClick(event); - } - }, { - signal - }); - await this.#setState(); - return altText; - } - get #label() { - return this.#altText && "added" || this.#altText === null && this.guessedText && "review" || "missing"; - } - finish() { - if (!this.#altTextButton) { - return; - } - this.#altTextButton.focus({ - focusVisible: this.#altTextWasFromKeyBoard - }); - this.#altTextWasFromKeyBoard = false; - } - isEmpty() { - if (this.#useNewAltTextFlow) { - return this.#altText === null; - } - return !this.#altText && !this.#altTextDecorative; - } - hasData() { - if (this.#useNewAltTextFlow) { - return this.#altText !== null || !!this.#guessedText; - } - return this.isEmpty(); - } - get guessedText() { - return this.#guessedText; - } - async setGuessedText(guessedText) { - if (this.#altText !== null) { - return; - } - this.#guessedText = guessedText; - this.#textWithDisclaimer = await AltText._l10n.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", { - generatedAltText: guessedText - }); - this.#setState(); - } - toggleAltTextBadge(visibility = false) { - if (!this.#useNewAltTextFlow || this.#altText) { - this.#badge?.remove(); - this.#badge = null; - return; - } - if (!this.#badge) { - const badge = this.#badge = document.createElement("div"); - badge.className = "noAltTextBadge"; - this.#editor.div.append(badge); - } - this.#badge.classList.toggle("hidden", !visibility); - } - serialize(isForCopying) { - let altText = this.#altText; - if (!isForCopying && this.#guessedText === altText) { - altText = this.#textWithDisclaimer; - } - return { - altText, - decorative: this.#altTextDecorative, - guessedText: this.#guessedText, - textWithDisclaimer: this.#textWithDisclaimer - }; - } - get data() { - return { - altText: this.#altText, - decorative: this.#altTextDecorative - }; - } - set data({ - altText, - decorative, - guessedText, - textWithDisclaimer, - cancel = false - }) { - if (guessedText) { - this.#guessedText = guessedText; - this.#textWithDisclaimer = textWithDisclaimer; - } - if (this.#altText === altText && this.#altTextDecorative === decorative) { - return; - } - if (!cancel) { - this.#altText = altText; - this.#altTextDecorative = decorative; - } - this.#setState(); - } - toggle(enabled = false) { - if (!this.#altTextButton) { - return; - } - if (!enabled && this.#altTextTooltipTimeout) { - clearTimeout(this.#altTextTooltipTimeout); - this.#altTextTooltipTimeout = null; - } - this.#altTextButton.disabled = !enabled; - } - shown() { - this.#editor._reportTelemetry({ - action: "pdfjs.image.alt_text.image_status_label_displayed", - data: { - label: this.#label - } - }); - } - destroy() { - this.#altTextButton?.remove(); - this.#altTextButton = null; - this.#altTextButtonLabel = null; - this.#altTextTooltip = null; - this.#badge?.remove(); - this.#badge = null; - } - async #setState() { - const button = this.#altTextButton; - if (!button) { - return; - } - if (this.#useNewAltTextFlow) { - button.classList.toggle("done", !!this.#altText); - button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]); - this.#altTextButtonLabel?.setAttribute("data-l10n-id", AltText.#l10nNewButton[`${this.#label}-label`]); - if (!this.#altText) { - this.#altTextTooltip?.remove(); - return; - } - } else { - if (!this.#altText && !this.#altTextDecorative) { - button.classList.remove("done"); - this.#altTextTooltip?.remove(); - return; - } - button.classList.add("done"); - button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button"); - } - let tooltip = this.#altTextTooltip; - if (!tooltip) { - this.#altTextTooltip = tooltip = document.createElement("span"); - tooltip.className = "tooltip"; - tooltip.setAttribute("role", "tooltip"); - tooltip.id = `alt-text-tooltip-${this.#editor.id}`; - const DELAY_TO_SHOW_TOOLTIP = 100; - const signal = this.#editor._uiManager._signal; - signal.addEventListener("abort", () => { - clearTimeout(this.#altTextTooltipTimeout); - this.#altTextTooltipTimeout = null; - }, { - once: true - }); - button.addEventListener("mouseenter", () => { - this.#altTextTooltipTimeout = setTimeout(() => { - this.#altTextTooltipTimeout = null; - this.#altTextTooltip.classList.add("show"); - this.#editor._reportTelemetry({ - action: "alt_text_tooltip" - }); - }, DELAY_TO_SHOW_TOOLTIP); - }, { - signal - }); - button.addEventListener("mouseleave", () => { - if (this.#altTextTooltipTimeout) { - clearTimeout(this.#altTextTooltipTimeout); - this.#altTextTooltipTimeout = null; - } - this.#altTextTooltip?.classList.remove("show"); - }, { - signal - }); - } - if (this.#altTextDecorative) { - tooltip.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-decorative-tooltip"); - } else { - tooltip.removeAttribute("data-l10n-id"); - tooltip.textContent = this.#altText; - } - if (!tooltip.parentNode) { - button.append(tooltip); - } - const element = this.#editor.getElementForAltText(); - element?.setAttribute("aria-describedby", tooltip.id); - } -} - -;// ./src/display/editor/comment.js - -class Comment { - #commentStandaloneButton = null; - #commentToolbarButton = null; - #commentWasFromKeyBoard = false; - #editor = null; - #initialText = null; - #richText = null; - #text = null; - #date = null; - #deleted = false; - #popupPosition = null; - constructor(editor) { - this.#editor = editor; - } - renderForToolbar() { - const button = this.#commentToolbarButton = document.createElement("button"); - button.className = "comment"; - return this.#render(button, false); - } - renderForStandalone() { - const button = this.#commentStandaloneButton = document.createElement("button"); - button.className = "annotationCommentButton"; - const position = this.#editor.commentButtonPosition; - if (position) { - const { - style - } = button; - style.insetInlineEnd = `calc(${100 * (this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0])}% - var(--comment-button-dim))`; - style.top = `calc(${100 * position[1]}% - var(--comment-button-dim))`; - const color = this.#editor.commentButtonColor; - if (color) { - style.backgroundColor = color; - } - } - return this.#render(button, true); - } - focusButton() { - setTimeout(() => { - (this.#commentStandaloneButton ?? this.#commentToolbarButton)?.focus(); - }, 0); - } - onUpdatedColor() { - if (!this.#commentStandaloneButton) { - return; - } - const color = this.#editor.commentButtonColor; - if (color) { - this.#commentStandaloneButton.style.backgroundColor = color; - } - this.#editor._uiManager.updatePopupColor(this.#editor); - } - get commentButtonWidth() { - return (this.#commentStandaloneButton?.getBoundingClientRect().width ?? 0) / this.#editor.parent.boundingClientRect.width; - } - get commentPopupPositionInLayer() { - if (this.#popupPosition) { - return this.#popupPosition; - } - if (!this.#commentStandaloneButton) { - return null; - } - const { - x, - y, - height - } = this.#commentStandaloneButton.getBoundingClientRect(); - const { - x: parentX, - y: parentY, - width: parentWidth, - height: parentHeight - } = this.#editor.parent.boundingClientRect; - return [(x - parentX) / parentWidth, (y + height - parentY) / parentHeight]; - } - set commentPopupPositionInLayer(pos) { - this.#popupPosition = pos; - } - hasDefaultPopupPosition() { - return this.#popupPosition === null; - } - removeStandaloneCommentButton() { - this.#commentStandaloneButton?.remove(); - this.#commentStandaloneButton = null; - } - removeToolbarCommentButton() { - this.#commentToolbarButton?.remove(); - this.#commentToolbarButton = null; - } - setCommentButtonStates({ - selected, - hasPopup - }) { - if (!this.#commentStandaloneButton) { - return; - } - this.#commentStandaloneButton.classList.toggle("selected", selected); - this.#commentStandaloneButton.ariaExpanded = hasPopup; - } - #render(comment, isStandalone) { - if (!this.#editor._uiManager.hasCommentManager()) { - return null; - } - comment.tabIndex = "0"; - comment.ariaHasPopup = "dialog"; - if (isStandalone) { - comment.ariaControls = "commentPopup"; - comment.setAttribute("data-l10n-id", "pdfjs-show-comment-button"); - } else { - comment.ariaControlsElements = [this.#editor._uiManager.getCommentDialogElement()]; - comment.setAttribute("data-l10n-id", "pdfjs-editor-add-comment-button"); - } - const signal = this.#editor._uiManager._signal; - if (!(signal instanceof AbortSignal) || signal.aborted) { - return comment; - } - comment.addEventListener("contextmenu", noContextMenu, { - signal - }); - if (isStandalone) { - comment.addEventListener("focusin", e => { - this.#editor._focusEventsAllowed = false; - stopEvent(e); - }, { - capture: true, - signal - }); - comment.addEventListener("focusout", e => { - this.#editor._focusEventsAllowed = true; - stopEvent(e); - }, { - capture: true, - signal - }); - } - comment.addEventListener("pointerdown", event => event.stopPropagation(), { - signal - }); - const onClick = event => { - event.preventDefault(); - if (comment === this.#commentToolbarButton) { - this.edit(); - } else { - this.#editor.toggleComment(true); - } - }; - comment.addEventListener("click", onClick, { - capture: true, - signal - }); - comment.addEventListener("keydown", event => { - if (event.target === comment && event.key === "Enter") { - this.#commentWasFromKeyBoard = true; - onClick(event); - } - }, { - signal - }); - comment.addEventListener("pointerenter", () => { - this.#editor.toggleComment(false, true); - }, { - signal - }); - comment.addEventListener("pointerleave", () => { - this.#editor.toggleComment(false, false); - }, { - signal - }); - return comment; - } - edit(options) { - const position = this.commentPopupPositionInLayer; - let posX, posY; - if (position) { - [posX, posY] = position; - } else { - [posX, posY] = this.#editor.commentButtonPosition; - const { - width, - height, - x, - y - } = this.#editor; - posX = x + posX * width; - posY = y + posY * height; - } - const parentDimensions = this.#editor.parent.boundingClientRect; - const { - x: parentX, - y: parentY, - width: parentWidth, - height: parentHeight - } = parentDimensions; - this.#editor._uiManager.editComment(this.#editor, parentX + posX * parentWidth, parentY + posY * parentHeight, { - ...options, - parentDimensions - }); - } - finish() { - if (!this.#commentToolbarButton) { - return; - } - this.#commentToolbarButton.focus({ - focusVisible: this.#commentWasFromKeyBoard - }); - this.#commentWasFromKeyBoard = false; - } - isDeleted() { - return this.#deleted || this.#text === ""; - } - isEmpty() { - return this.#text === null; - } - hasBeenEdited() { - return this.isDeleted() || this.#text !== this.#initialText; - } - serialize() { - return this.data; - } - get data() { - return { - text: this.#text, - richText: this.#richText, - date: this.#date, - deleted: this.isDeleted() - }; - } - set data(text) { - if (text !== this.#text) { - this.#richText = null; - } - if (text === null) { - this.#text = ""; - this.#deleted = true; - return; - } - this.#text = text; - this.#date = new Date(); - this.#deleted = false; - } - restoreData({ - text, - richText, - date - }) { - this.#text = text; - this.#richText = richText; - this.#date = date; - this.#deleted = false; - } - setInitialText(text, richText = null) { - this.#initialText = text; - this.data = text; - this.#date = null; - this.#richText = richText; - } - shown() {} - destroy() { - this.#commentToolbarButton?.remove(); - this.#commentToolbarButton = null; - this.#commentStandaloneButton?.remove(); - this.#commentStandaloneButton = null; - this.#text = ""; - this.#richText = null; - this.#date = null; - this.#editor = null; - this.#commentWasFromKeyBoard = false; - this.#deleted = false; - } -} - -;// ./src/display/touch_manager.js - -class TouchManager { - #container; - #isPinching = false; - #isPinchingStopped = null; - #isPinchingDisabled; - #onPinchStart; - #onPinching; - #onPinchEnd; - #pointerDownAC = null; - #signal; - #touchInfo = null; - #touchManagerAC; - #touchMoveAC = null; - constructor({ - container, - isPinchingDisabled = null, - isPinchingStopped = null, - onPinchStart = null, - onPinching = null, - onPinchEnd = null, - signal - }) { - this.#container = container; - this.#isPinchingStopped = isPinchingStopped; - this.#isPinchingDisabled = isPinchingDisabled; - this.#onPinchStart = onPinchStart; - this.#onPinching = onPinching; - this.#onPinchEnd = onPinchEnd; - this.#touchManagerAC = new AbortController(); - this.#signal = AbortSignal.any([signal, this.#touchManagerAC.signal]); - container.addEventListener("touchstart", this.#onTouchStart.bind(this), { - passive: false, - signal: this.#signal - }); - } - get MIN_TOUCH_DISTANCE_TO_PINCH() { - return 35 / OutputScale.pixelRatio; - } - #onTouchStart(evt) { - if (this.#isPinchingDisabled?.()) { - return; - } - if (evt.touches.length === 1) { - if (this.#pointerDownAC) { - return; - } - const pointerDownAC = this.#pointerDownAC = new AbortController(); - const signal = AbortSignal.any([this.#signal, pointerDownAC.signal]); - const container = this.#container; - const opts = { - capture: true, - signal, - passive: false - }; - const cancelPointerDown = e => { - if (e.pointerType === "touch") { - this.#pointerDownAC?.abort(); - this.#pointerDownAC = null; - } - }; - container.addEventListener("pointerdown", e => { - if (e.pointerType === "touch") { - stopEvent(e); - cancelPointerDown(e); - } - }, opts); - container.addEventListener("pointerup", cancelPointerDown, opts); - container.addEventListener("pointercancel", cancelPointerDown, opts); - return; - } - if (!this.#touchMoveAC) { - this.#touchMoveAC = new AbortController(); - const signal = AbortSignal.any([this.#signal, this.#touchMoveAC.signal]); - const container = this.#container; - const opt = { - signal, - capture: false, - passive: false - }; - container.addEventListener("touchmove", this.#onTouchMove.bind(this), opt); - const onTouchEnd = this.#onTouchEnd.bind(this); - container.addEventListener("touchend", onTouchEnd, opt); - container.addEventListener("touchcancel", onTouchEnd, opt); - opt.capture = true; - container.addEventListener("pointerdown", stopEvent, opt); - container.addEventListener("pointermove", stopEvent, opt); - container.addEventListener("pointercancel", stopEvent, opt); - container.addEventListener("pointerup", stopEvent, opt); - this.#onPinchStart?.(); - } - stopEvent(evt); - if (evt.touches.length !== 2 || this.#isPinchingStopped?.()) { - this.#touchInfo = null; - return; - } - let [touch0, touch1] = evt.touches; - if (touch0.identifier > touch1.identifier) { - [touch0, touch1] = [touch1, touch0]; - } - this.#touchInfo = { - touch0X: touch0.screenX, - touch0Y: touch0.screenY, - touch1X: touch1.screenX, - touch1Y: touch1.screenY - }; - } - #onTouchMove(evt) { - if (!this.#touchInfo || evt.touches.length !== 2) { - return; - } - stopEvent(evt); - let [touch0, touch1] = evt.touches; - if (touch0.identifier > touch1.identifier) { - [touch0, touch1] = [touch1, touch0]; - } - const { - screenX: screen0X, - screenY: screen0Y - } = touch0; - const { - screenX: screen1X, - screenY: screen1Y - } = touch1; - const touchInfo = this.#touchInfo; - const { - touch0X: pTouch0X, - touch0Y: pTouch0Y, - touch1X: pTouch1X, - touch1Y: pTouch1Y - } = touchInfo; - const prevGapX = pTouch1X - pTouch0X; - const prevGapY = pTouch1Y - pTouch0Y; - const currGapX = screen1X - screen0X; - const currGapY = screen1Y - screen0Y; - const distance = Math.hypot(currGapX, currGapY) || 1; - const pDistance = Math.hypot(prevGapX, prevGapY) || 1; - if (!this.#isPinching && Math.abs(pDistance - distance) <= TouchManager.MIN_TOUCH_DISTANCE_TO_PINCH) { - return; - } - touchInfo.touch0X = screen0X; - touchInfo.touch0Y = screen0Y; - touchInfo.touch1X = screen1X; - touchInfo.touch1Y = screen1Y; - if (!this.#isPinching) { - this.#isPinching = true; - return; - } - const origin = [(screen0X + screen1X) / 2, (screen0Y + screen1Y) / 2]; - this.#onPinching?.(origin, pDistance, distance); - } - #onTouchEnd(evt) { - if (evt.touches.length >= 2) { - return; - } - if (this.#touchMoveAC) { - this.#touchMoveAC.abort(); - this.#touchMoveAC = null; - this.#onPinchEnd?.(); - } - if (!this.#touchInfo) { - return; - } - stopEvent(evt); - this.#touchInfo = null; - this.#isPinching = false; - } - destroy() { - this.#touchManagerAC?.abort(); - this.#touchManagerAC = null; - this.#pointerDownAC?.abort(); - this.#pointerDownAC = null; - } -} - -;// ./src/display/editor/editor.js - - - - - - - -class AnnotationEditor { - #accessibilityData = null; - #allResizerDivs = null; - #altText = null; - #comment = null; - #commentStandaloneButton = null; - #disabled = false; - #dragPointerId = null; - #dragPointerType = ""; - #resizersDiv = null; - #lastPointerCoords = null; - #savedDimensions = null; - #fakeAnnotation = null; - #focusAC = null; - #focusedResizerName = ""; - #hasBeenClicked = false; - #initialRect = null; - #isEditing = false; - #isInEditMode = false; - #isResizerEnabledForKeyboard = false; - #moveInDOMTimeout = null; - #prevDragX = 0; - #prevDragY = 0; - #telemetryTimeouts = null; - #touchManager = null; - isSelected = false; - _isCopy = false; - _editToolbar = null; - _initialOptions = Object.create(null); - _initialData = null; - _isVisible = true; - _uiManager = null; - _focusEventsAllowed = true; - static _l10n = null; - static _l10nResizer = null; - #isDraggable = false; - #zIndex = AnnotationEditor._zIndex++; - static _borderLineWidth = -1; - static _colorManager = new ColorManager(); - static _zIndex = 1; - static _telemetryTimeout = 1000; - static get _resizerKeyboardManager() { - const resize = AnnotationEditor.prototype._resizeWithKeyboard; - const small = AnnotationEditorUIManager.TRANSLATE_SMALL; - const big = AnnotationEditorUIManager.TRANSLATE_BIG; - return shadow(this, "_resizerKeyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], resize, { - args: [-small, 0] - }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], resize, { - args: [-big, 0] - }], [["ArrowRight", "mac+ArrowRight"], resize, { - args: [small, 0] - }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], resize, { - args: [big, 0] - }], [["ArrowUp", "mac+ArrowUp"], resize, { - args: [0, -small] - }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], resize, { - args: [0, -big] - }], [["ArrowDown", "mac+ArrowDown"], resize, { - args: [0, small] - }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], resize, { - args: [0, big] - }], [["Escape", "mac+Escape"], AnnotationEditor.prototype._stopResizingWithKeyboard]])); - } - constructor(parameters) { - this.parent = parameters.parent; - this.id = parameters.id; - this.width = this.height = null; - this.pageIndex = parameters.parent.pageIndex; - this.name = parameters.name; - this.div = null; - this._uiManager = parameters.uiManager; - this.annotationElementId = null; - this._willKeepAspectRatio = false; - this._initialOptions.isCentered = parameters.isCentered; - this._structTreeParentId = null; - this.annotationElementId = parameters.annotationElementId || null; - this.creationDate = parameters.creationDate || new Date(); - this.modificationDate = parameters.modificationDate || null; - this.canAddComment = true; - const { - rotation, - rawDims: { - pageWidth, - pageHeight, - pageX, - pageY - } - } = this.parent.viewport; - this.rotation = rotation; - this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360; - this.pageDimensions = [pageWidth, pageHeight]; - this.pageTranslation = [pageX, pageY]; - const [width, height] = this.parentDimensions; - this.x = parameters.x / width; - this.y = parameters.y / height; - this.isAttachedToDOM = false; - this.deleted = false; - } - updatePageIndex(newPageIndex) { - this.pageIndex = newPageIndex; - } - get editorType() { - return Object.getPrototypeOf(this).constructor._type; - } - get mode() { - return Object.getPrototypeOf(this).constructor._editorType; - } - static get isDrawer() { - return false; - } - static get _defaultLineColor() { - return shadow(this, "_defaultLineColor", this._colorManager.getHexCode("CanvasText")); - } - static deleteAnnotationElement(editor) { - const fakeEditor = new FakeEditor({ - id: editor.parent.getNextId(), - parent: editor.parent, - uiManager: editor._uiManager - }); - fakeEditor.annotationElementId = editor.annotationElementId; - fakeEditor.deleted = true; - fakeEditor._uiManager.addToAnnotationStorage(fakeEditor); - } - static initialize(l10n, _uiManager) { - AnnotationEditor._l10n ??= l10n; - AnnotationEditor._l10nResizer ||= Object.freeze({ - topLeft: "pdfjs-editor-resizer-top-left", - topMiddle: "pdfjs-editor-resizer-top-middle", - topRight: "pdfjs-editor-resizer-top-right", - middleRight: "pdfjs-editor-resizer-middle-right", - bottomRight: "pdfjs-editor-resizer-bottom-right", - bottomMiddle: "pdfjs-editor-resizer-bottom-middle", - bottomLeft: "pdfjs-editor-resizer-bottom-left", - middleLeft: "pdfjs-editor-resizer-middle-left" - }); - if (AnnotationEditor._borderLineWidth !== -1) { - return; - } - const style = getComputedStyle(document.documentElement); - AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue("--outline-width")) || 0; - } - static updateDefaultParams(_type, _value) {} - static get defaultPropertiesToUpdate() { - return []; - } - static isHandlingMimeForPasting(mime) { - return false; - } - static paste(item, parent) { - unreachable("Not implemented"); - } - get propertiesToUpdate() { - return []; - } - get _isDraggable() { - return this.#isDraggable; - } - set _isDraggable(value) { - this.#isDraggable = value; - this.div?.classList.toggle("draggable", value); - } - get uid() { - return this.annotationElementId || this.id; - } - get isEnterHandled() { - return true; - } - center() { - const [pageWidth, pageHeight] = this.pageDimensions; - switch (this.parentRotation) { - case 90: - this.x -= this.height * pageHeight / (pageWidth * 2); - this.y += this.width * pageWidth / (pageHeight * 2); - break; - case 180: - this.x += this.width / 2; - this.y += this.height / 2; - break; - case 270: - this.x += this.height * pageHeight / (pageWidth * 2); - this.y -= this.width * pageWidth / (pageHeight * 2); - break; - default: - this.x -= this.width / 2; - this.y -= this.height / 2; - break; - } - this.fixAndSetPosition(); - } - addCommands(params) { - this._uiManager.addCommands(params); - } - get currentLayer() { - return this._uiManager.currentLayer; - } - setInBackground() { - this.div.style.zIndex = 0; - } - setInForeground() { - this.div.style.zIndex = this.#zIndex; - } - setParent(parent) { - if (parent !== null) { - this.pageIndex = parent.pageIndex; - this.pageDimensions = parent.pageDimensions; - } else { - this.#stopResizing(); - this.#fakeAnnotation?.remove(); - this.#fakeAnnotation = null; - } - this.parent = parent; - } - focusin(event) { - if (!this._focusEventsAllowed) { - return; - } - if (!this.#hasBeenClicked) { - this.parent.setSelected(this); - } else { - this.#hasBeenClicked = false; - } - } - focusout(event) { - if (!this._focusEventsAllowed) { - return; - } - if (!this.isAttachedToDOM) { - return; - } - const target = event.relatedTarget; - if (target?.closest(`#${this.id}`)) { - return; - } - event.preventDefault(); - if (!this.parent?.isMultipleSelection) { - this.commitOrRemove(); - } - } - commitOrRemove() { - if (this.isEmpty()) { - this.remove(); - } else { - this.commit(); - } - } - commit() { - if (!this.isInEditMode()) { - return; - } - this.addToAnnotationStorage(); - } - addToAnnotationStorage() { - this._uiManager.addToAnnotationStorage(this); - } - setAt(x, y, tx, ty) { - const [width, height] = this.parentDimensions; - [tx, ty] = this.screenToPageTranslation(tx, ty); - this.x = (x + tx) / width; - this.y = (y + ty) / height; - this.fixAndSetPosition(); - } - _moveAfterPaste(baseX, baseY) { - const [parentWidth, parentHeight] = this.parentDimensions; - this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); - this._onTranslated(); - } - #translate([width, height], x, y) { - [x, y] = this.screenToPageTranslation(x, y); - this.x += x / width; - this.y += y / height; - this._onTranslating(this.x, this.y); - this.fixAndSetPosition(); - } - translate(x, y) { - this.#translate(this.parentDimensions, x, y); - } - translateInPage(x, y) { - this.#initialRect ||= [this.x, this.y, this.width, this.height]; - this.#translate(this.pageDimensions, x, y); - this.div.scrollIntoView({ - block: "nearest" - }); - } - translationDone() { - this._onTranslated(this.x, this.y); - } - drag(tx, ty) { - this.#initialRect ||= [this.x, this.y, this.width, this.height]; - const { - div, - parentDimensions: [parentWidth, parentHeight] - } = this; - this.x += tx / parentWidth; - this.y += ty / parentHeight; - if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) { - const { - x, - y - } = this.div.getBoundingClientRect(); - if (this.parent.findNewParent(this, x, y)) { - this.x -= Math.floor(this.x); - this.y -= Math.floor(this.y); - } - } - let { - x, - y - } = this; - const [bx, by] = this.getBaseTranslation(); - x += bx; - y += by; - const { - style - } = div; - style.left = `${(100 * x).toFixed(2)}%`; - style.top = `${(100 * y).toFixed(2)}%`; - this._onTranslating(x, y); - div.scrollIntoView({ - block: "nearest" - }); - } - _onTranslating(x, y) {} - _onTranslated(x, y) {} - get _hasBeenMoved() { - return !!this.#initialRect && (this.#initialRect[0] !== this.x || this.#initialRect[1] !== this.y); - } - get _hasBeenResized() { - return !!this.#initialRect && (this.#initialRect[2] !== this.width || this.#initialRect[3] !== this.height); - } - getBaseTranslation() { - const [parentWidth, parentHeight] = this.parentDimensions; - const { - _borderLineWidth - } = AnnotationEditor; - const x = _borderLineWidth / parentWidth; - const y = _borderLineWidth / parentHeight; - switch (this.rotation) { - case 90: - return [-x, y]; - case 180: - return [x, y]; - case 270: - return [x, -y]; - default: - return [-x, -y]; - } - } - get _mustFixPosition() { - return true; - } - fixAndSetPosition(rotation = this.rotation) { - const { - div: { - style - }, - pageDimensions: [pageWidth, pageHeight] - } = this; - let { - x, - y, - width, - height - } = this; - width *= pageWidth; - height *= pageHeight; - x *= pageWidth; - y *= pageHeight; - if (this._mustFixPosition) { - switch (rotation) { - case 0: - x = MathClamp(x, 0, pageWidth - width); - y = MathClamp(y, 0, pageHeight - height); - break; - case 90: - x = MathClamp(x, 0, pageWidth - height); - y = MathClamp(y, width, pageHeight); - break; - case 180: - x = MathClamp(x, width, pageWidth); - y = MathClamp(y, height, pageHeight); - break; - case 270: - x = MathClamp(x, height, pageWidth); - y = MathClamp(y, 0, pageHeight - width); - break; - } - } - this.x = x /= pageWidth; - this.y = y /= pageHeight; - const [bx, by] = this.getBaseTranslation(); - x += bx; - y += by; - style.left = `${(100 * x).toFixed(2)}%`; - style.top = `${(100 * y).toFixed(2)}%`; - this.moveInDOM(); - } - static #rotatePoint(x, y, angle) { - switch (angle) { - case 90: - return [y, -x]; - case 180: - return [-x, -y]; - case 270: - return [-y, x]; - default: - return [x, y]; - } - } - screenToPageTranslation(x, y) { - return AnnotationEditor.#rotatePoint(x, y, this.parentRotation); - } - pageTranslationToScreen(x, y) { - return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation); - } - #getRotationMatrix(rotation) { - switch (rotation) { - case 90: - { - const [pageWidth, pageHeight] = this.pageDimensions; - return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0]; - } - case 180: - return [-1, 0, 0, -1]; - case 270: - { - const [pageWidth, pageHeight] = this.pageDimensions; - return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0]; - } - default: - return [1, 0, 0, 1]; - } - } - get parentScale() { - return this._uiManager.viewParameters.realScale; - } - get parentRotation() { - return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360; - } - get parentDimensions() { - const { - parentScale, - pageDimensions: [pageWidth, pageHeight] - } = this; - return [pageWidth * parentScale, pageHeight * parentScale]; - } - setDims() { - const { - div: { - style - }, - width, - height - } = this; - style.width = `${(100 * width).toFixed(2)}%`; - style.height = `${(100 * height).toFixed(2)}%`; - } - getInitialTranslation() { - return [0, 0]; - } - #createResizers() { - if (this.#resizersDiv) { - return; - } - this.#resizersDiv = document.createElement("div"); - this.#resizersDiv.classList.add("resizers"); - const classes = this._willKeepAspectRatio ? ["topLeft", "topRight", "bottomRight", "bottomLeft"] : ["topLeft", "topMiddle", "topRight", "middleRight", "bottomRight", "bottomMiddle", "bottomLeft", "middleLeft"]; - const signal = this._uiManager._signal; - for (const name of classes) { - const div = document.createElement("div"); - this.#resizersDiv.append(div); - div.classList.add("resizer", name); - div.setAttribute("data-resizer-name", name); - div.addEventListener("pointerdown", this.#resizerPointerdown.bind(this, name), { - signal - }); - div.addEventListener("contextmenu", noContextMenu, { - signal - }); - div.tabIndex = -1; - } - this.div.prepend(this.#resizersDiv); - } - #resizerPointerdown(name, event) { - event.preventDefault(); - const { - isMac - } = FeatureTest.platform; - if (event.button !== 0 || event.ctrlKey && isMac) { - return; - } - this.#altText?.toggle(false); - const savedDraggable = this._isDraggable; - this._isDraggable = false; - this.#lastPointerCoords = [event.screenX, event.screenY]; - const ac = new AbortController(); - const signal = this._uiManager.combinedSignal(ac); - this.parent.togglePointerEvents(false); - window.addEventListener("pointermove", this.#resizerPointermove.bind(this, name), { - passive: true, - capture: true, - signal - }); - window.addEventListener("touchmove", stopEvent, { - passive: false, - signal - }); - window.addEventListener("contextmenu", noContextMenu, { - signal - }); - this.#savedDimensions = { - savedX: this.x, - savedY: this.y, - savedWidth: this.width, - savedHeight: this.height - }; - const savedParentCursor = this.parent.div.style.cursor; - const savedCursor = this.div.style.cursor; - this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor; - const pointerUpCallback = () => { - ac.abort(); - this.parent.togglePointerEvents(true); - this.#altText?.toggle(true); - this._isDraggable = savedDraggable; - this.parent.div.style.cursor = savedParentCursor; - this.div.style.cursor = savedCursor; - this.#addResizeToUndoStack(); - }; - window.addEventListener("pointerup", pointerUpCallback, { - signal - }); - window.addEventListener("blur", pointerUpCallback, { - signal - }); - } - #resize(x, y, width, height) { - this.width = width; - this.height = height; - this.x = x; - this.y = y; - this.setDims(); - this.fixAndSetPosition(); - this._onResized(); - } - _onResized() {} - #addResizeToUndoStack() { - if (!this.#savedDimensions) { - return; - } - const { - savedX, - savedY, - savedWidth, - savedHeight - } = this.#savedDimensions; - this.#savedDimensions = null; - const newX = this.x; - const newY = this.y; - const newWidth = this.width; - const newHeight = this.height; - if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) { - return; - } - this.addCommands({ - cmd: this.#resize.bind(this, newX, newY, newWidth, newHeight), - undo: this.#resize.bind(this, savedX, savedY, savedWidth, savedHeight), - mustExec: true - }); - } - static _round(x) { - return Math.round(x * 10000) / 10000; - } - #resizerPointermove(name, event) { - const [parentWidth, parentHeight] = this.parentDimensions; - const savedX = this.x; - const savedY = this.y; - const savedWidth = this.width; - const savedHeight = this.height; - const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; - const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; - const rotationMatrix = this.#getRotationMatrix(this.rotation); - const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; - const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation); - const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y]; - let getPoint; - let getOpposite; - let isDiagonal = false; - let isHorizontal = false; - switch (name) { - case "topLeft": - isDiagonal = true; - getPoint = (w, h) => [0, 0]; - getOpposite = (w, h) => [w, h]; - break; - case "topMiddle": - getPoint = (w, h) => [w / 2, 0]; - getOpposite = (w, h) => [w / 2, h]; - break; - case "topRight": - isDiagonal = true; - getPoint = (w, h) => [w, 0]; - getOpposite = (w, h) => [0, h]; - break; - case "middleRight": - isHorizontal = true; - getPoint = (w, h) => [w, h / 2]; - getOpposite = (w, h) => [0, h / 2]; - break; - case "bottomRight": - isDiagonal = true; - getPoint = (w, h) => [w, h]; - getOpposite = (w, h) => [0, 0]; - break; - case "bottomMiddle": - getPoint = (w, h) => [w / 2, h]; - getOpposite = (w, h) => [w / 2, 0]; - break; - case "bottomLeft": - isDiagonal = true; - getPoint = (w, h) => [0, h]; - getOpposite = (w, h) => [w, 0]; - break; - case "middleLeft": - isHorizontal = true; - getPoint = (w, h) => [0, h / 2]; - getOpposite = (w, h) => [w, h / 2]; - break; - } - const point = getPoint(savedWidth, savedHeight); - const oppositePoint = getOpposite(savedWidth, savedHeight); - let transfOppositePoint = transf(...oppositePoint); - const oppositeX = AnnotationEditor._round(savedX + transfOppositePoint[0]); - const oppositeY = AnnotationEditor._round(savedY + transfOppositePoint[1]); - let ratioX = 1; - let ratioY = 1; - let deltaX, deltaY; - if (!event.fromKeyboard) { - const { - screenX, - screenY - } = event; - const [lastScreenX, lastScreenY] = this.#lastPointerCoords; - [deltaX, deltaY] = this.screenToPageTranslation(screenX - lastScreenX, screenY - lastScreenY); - this.#lastPointerCoords[0] = screenX; - this.#lastPointerCoords[1] = screenY; - } else { - ({ - deltaX, - deltaY - } = event); - } - [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight); - if (isDiagonal) { - const oldDiag = Math.hypot(savedWidth, savedHeight); - ratioX = ratioY = Math.max(Math.min(Math.hypot(oppositePoint[0] - point[0] - deltaX, oppositePoint[1] - point[1] - deltaY) / oldDiag, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight); - } else if (isHorizontal) { - ratioX = MathClamp(Math.abs(oppositePoint[0] - point[0] - deltaX), minWidth, 1) / savedWidth; - } else { - ratioY = MathClamp(Math.abs(oppositePoint[1] - point[1] - deltaY), minHeight, 1) / savedHeight; - } - const newWidth = AnnotationEditor._round(savedWidth * ratioX); - const newHeight = AnnotationEditor._round(savedHeight * ratioY); - transfOppositePoint = transf(...getOpposite(newWidth, newHeight)); - const newX = oppositeX - transfOppositePoint[0]; - const newY = oppositeY - transfOppositePoint[1]; - this.#initialRect ||= [this.x, this.y, this.width, this.height]; - this.width = newWidth; - this.height = newHeight; - this.x = newX; - this.y = newY; - this.setDims(); - this.fixAndSetPosition(); - this._onResizing(); - } - _onResizing() {} - altTextFinish() { - this.#altText?.finish(); - } - get toolbarButtons() { - return null; - } - async addEditToolbar() { - if (this._editToolbar || this.#isInEditMode) { - return this._editToolbar; - } - this._editToolbar = new EditorToolbar(this); - this.div.append(this._editToolbar.render()); - const { - toolbarButtons - } = this; - if (toolbarButtons) { - for (const [name, tool] of toolbarButtons) { - await this._editToolbar.addButton(name, tool); - } - } - if (!this.hasComment) { - this._editToolbar.addButton("comment", this.addCommentButton()); - } - this._editToolbar.addButton("delete"); - return this._editToolbar; - } - addCommentButtonInToolbar() { - this._editToolbar?.addButtonBefore("comment", this.addCommentButton(), ".deleteButton"); - } - removeCommentButtonFromToolbar() { - this._editToolbar?.removeButton("comment"); - } - removeEditToolbar() { - this._editToolbar?.remove(); - this._editToolbar = null; - this.#altText?.destroy(); - } - addContainer(container) { - const editToolbarDiv = this._editToolbar?.div; - if (editToolbarDiv) { - editToolbarDiv.before(container); - } else { - this.div.append(container); - } - } - getClientDimensions() { - return this.div.getBoundingClientRect(); - } - createAltText() { - if (!this.#altText) { - AltText.initialize(AnnotationEditor._l10n); - this.#altText = new AltText(this); - if (this.#accessibilityData) { - this.#altText.data = this.#accessibilityData; - this.#accessibilityData = null; - } - } - return this.#altText; - } - get altTextData() { - return this.#altText?.data; - } - set altTextData(data) { - if (!this.#altText) { - return; - } - this.#altText.data = data; - } - get guessedAltText() { - return this.#altText?.guessedText; - } - async setGuessedAltText(text) { - await this.#altText?.setGuessedText(text); - } - serializeAltText(isForCopying) { - return this.#altText?.serialize(isForCopying); - } - hasAltText() { - return !!this.#altText && !this.#altText.isEmpty(); - } - hasAltTextData() { - return this.#altText?.hasData() ?? false; - } - focusCommentButton() { - this.#comment?.focusButton(); - } - addCommentButton() { - return this.canAddComment ? this.#comment ||= new Comment(this) : null; - } - addStandaloneCommentButton() { - if (!this._uiManager.hasCommentManager()) { - return; - } - if (this.#commentStandaloneButton) { - if (this._uiManager.isEditingMode()) { - this.#commentStandaloneButton.classList.remove("hidden"); - } - return; - } - if (!this.hasComment) { - return; - } - this.#commentStandaloneButton = this.#comment.renderForStandalone(); - this.div.append(this.#commentStandaloneButton); - } - removeStandaloneCommentButton() { - this.#comment.removeStandaloneCommentButton(); - this.#commentStandaloneButton = null; - } - hideStandaloneCommentButton() { - this.#commentStandaloneButton?.classList.add("hidden"); - } - get comment() { - if (!this.#comment) { - return null; - } - const { - data: { - richText, - text, - date, - deleted - } - } = this.#comment; - return { - text, - richText, - date, - deleted, - color: this.getNonHCMColor(), - opacity: this.opacity ?? 1 - }; - } - set comment(value) { - this.#comment ||= new Comment(this); - if (typeof value === "object" && value !== null) { - this.#comment.restoreData(value); - } else { - this.#comment.data = value; - } - if (this.hasComment) { - this.removeCommentButtonFromToolbar(); - this.addStandaloneCommentButton(); - this._uiManager.updateComment(this); - } else { - this.addCommentButtonInToolbar(); - this.removeStandaloneCommentButton(); - this._uiManager.removeComment(this); - } - } - setCommentData({ - comment, - popupRef, - richText - }) { - if (!popupRef) { - return; - } - this.#comment ||= new Comment(this); - this.#comment.setInitialText(comment, richText); - if (!this.annotationElementId) { - return; - } - const storedData = this._uiManager.getAndRemoveDataFromAnnotationStorage(this.annotationElementId); - if (storedData) { - this.updateFromAnnotationLayer(storedData); - } - } - get hasEditedComment() { - return this.#comment?.hasBeenEdited(); - } - get hasDeletedComment() { - return this.#comment?.isDeleted(); - } - get hasComment() { - return !!this.#comment && !this.#comment.isEmpty() && !this.#comment.isDeleted(); - } - async editComment(options) { - this.#comment ||= new Comment(this); - this.#comment.edit(options); - } - toggleComment(isSelected, visibility = undefined) { - if (this.hasComment) { - this._uiManager.toggleComment(this, isSelected, visibility); - } - } - setSelectedCommentButton(selected) { - this.#comment.setSelectedButton(selected); - } - addComment(serialized) { - if (this.hasEditedComment) { - const DEFAULT_POPUP_WIDTH = 180; - const DEFAULT_POPUP_HEIGHT = 100; - const [,,, trY] = serialized.rect; - const [pageWidth] = this.pageDimensions; - const [pageX] = this.pageTranslation; - const blX = pageX + pageWidth + 1; - const blY = trY - DEFAULT_POPUP_HEIGHT; - const trX = blX + DEFAULT_POPUP_WIDTH; - serialized.popup = { - contents: this.comment.text, - deleted: this.comment.deleted, - rect: [blX, blY, trX, trY] - }; - } - } - updateFromAnnotationLayer({ - popup: { - contents, - deleted - } - }) { - this.#comment.data = deleted ? null : contents; - } - get parentBoundingClientRect() { - return this.parent.boundingClientRect; - } - render() { - const div = this.div = document.createElement("div"); - div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360); - div.className = this.name; - div.setAttribute("id", this.id); - div.tabIndex = this.#disabled ? -1 : 0; - div.setAttribute("role", "application"); - if (this.defaultL10nId) { - div.setAttribute("data-l10n-id", this.defaultL10nId); - } - if (!this._isVisible) { - div.classList.add("hidden"); - } - this.setInForeground(); - this.#addFocusListeners(); - const [parentWidth, parentHeight] = this.parentDimensions; - if (this.parentRotation % 180 !== 0) { - div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`; - div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`; - } - const [tx, ty] = this.getInitialTranslation(); - this.translate(tx, ty); - bindEvents(this, div, ["keydown", "pointerdown", "dblclick"]); - if (this.isResizable && this._uiManager._supportsPinchToZoom) { - this.#touchManager ||= new TouchManager({ - container: div, - isPinchingDisabled: () => !this.isSelected, - onPinchStart: this.#touchPinchStartCallback.bind(this), - onPinching: this.#touchPinchCallback.bind(this), - onPinchEnd: this.#touchPinchEndCallback.bind(this), - signal: this._uiManager._signal - }); - } - this.addStandaloneCommentButton(); - this._uiManager._editorUndoBar?.hide(); - return div; - } - #touchPinchStartCallback() { - this.#savedDimensions = { - savedX: this.x, - savedY: this.y, - savedWidth: this.width, - savedHeight: this.height - }; - this.#altText?.toggle(false); - this.parent.togglePointerEvents(false); - } - #touchPinchCallback(_origin, prevDistance, distance) { - const slowDownFactor = 0.7; - let factor = slowDownFactor * (distance / prevDistance) + 1 - slowDownFactor; - if (factor === 1) { - return; - } - const rotationMatrix = this.#getRotationMatrix(this.rotation); - const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; - const [parentWidth, parentHeight] = this.parentDimensions; - const savedX = this.x; - const savedY = this.y; - const savedWidth = this.width; - const savedHeight = this.height; - const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; - const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; - factor = Math.max(Math.min(factor, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight); - const newWidth = AnnotationEditor._round(savedWidth * factor); - const newHeight = AnnotationEditor._round(savedHeight * factor); - if (newWidth === savedWidth && newHeight === savedHeight) { - return; - } - this.#initialRect ||= [savedX, savedY, savedWidth, savedHeight]; - const transfCenterPoint = transf(savedWidth / 2, savedHeight / 2); - const centerX = AnnotationEditor._round(savedX + transfCenterPoint[0]); - const centerY = AnnotationEditor._round(savedY + transfCenterPoint[1]); - const newTransfCenterPoint = transf(newWidth / 2, newHeight / 2); - this.x = centerX - newTransfCenterPoint[0]; - this.y = centerY - newTransfCenterPoint[1]; - this.width = newWidth; - this.height = newHeight; - this.setDims(); - this.fixAndSetPosition(); - this._onResizing(); - } - #touchPinchEndCallback() { - this.#altText?.toggle(true); - this.parent.togglePointerEvents(true); - this.#addResizeToUndoStack(); - } - pointerdown(event) { - const { - isMac - } = FeatureTest.platform; - if (event.button !== 0 || event.ctrlKey && isMac) { - event.preventDefault(); - return; - } - this.#hasBeenClicked = true; - if (this._isDraggable) { - this.#setUpDragSession(event); - return; - } - this.#selectOnPointerEvent(event); - } - #selectOnPointerEvent(event) { - const { - isMac - } = FeatureTest.platform; - if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) { - this.parent.toggleSelected(this); - } else { - this.parent.setSelected(this); - } - } - #setUpDragSession(event) { - const { - isSelected - } = this; - this._uiManager.setUpDragSession(); - let hasDraggingStarted = false; - const ac = new AbortController(); - const signal = this._uiManager.combinedSignal(ac); - const opts = { - capture: true, - passive: false, - signal - }; - const cancelDrag = e => { - ac.abort(); - this.#dragPointerId = null; - this.#hasBeenClicked = false; - if (!this._uiManager.endDragSession()) { - this.#selectOnPointerEvent(e); - } - if (hasDraggingStarted) { - this._onStopDragging(); - } - }; - if (isSelected) { - this.#prevDragX = event.clientX; - this.#prevDragY = event.clientY; - this.#dragPointerId = event.pointerId; - this.#dragPointerType = event.pointerType; - window.addEventListener("pointermove", e => { - if (!hasDraggingStarted) { - hasDraggingStarted = true; - this._uiManager.toggleComment(this, true, false); - this._onStartDragging(); - } - const { - clientX: x, - clientY: y, - pointerId - } = e; - if (pointerId !== this.#dragPointerId) { - stopEvent(e); - return; - } - const [tx, ty] = this.screenToPageTranslation(x - this.#prevDragX, y - this.#prevDragY); - this.#prevDragX = x; - this.#prevDragY = y; - this._uiManager.dragSelectedEditors(tx, ty); - }, opts); - window.addEventListener("touchmove", stopEvent, opts); - window.addEventListener("pointerdown", e => { - if (e.pointerType === this.#dragPointerType) { - if (this.#touchManager || e.isPrimary) { - cancelDrag(e); - } - } - stopEvent(e); - }, opts); - } - const pointerUpCallback = e => { - if (!this.#dragPointerId || this.#dragPointerId === e.pointerId) { - cancelDrag(e); - return; - } - stopEvent(e); - }; - window.addEventListener("pointerup", pointerUpCallback, { - signal - }); - window.addEventListener("blur", pointerUpCallback, { - signal - }); - } - _onStartDragging() {} - _onStopDragging() {} - moveInDOM() { - if (this.#moveInDOMTimeout) { - clearTimeout(this.#moveInDOMTimeout); - } - this.#moveInDOMTimeout = setTimeout(() => { - this.#moveInDOMTimeout = null; - this.parent?.moveEditorInDOM(this); - }, 0); - } - _setParentAndPosition(parent, x, y) { - parent.changeParent(this); - this.x = x; - this.y = y; - this.fixAndSetPosition(); - this._onTranslated(); - } - getRect(tx, ty, rotation = this.rotation) { - const scale = this.parentScale; - const [pageWidth, pageHeight] = this.pageDimensions; - const [pageX, pageY] = this.pageTranslation; - const shiftX = tx / scale; - const shiftY = ty / scale; - const x = this.x * pageWidth; - const y = this.y * pageHeight; - const width = this.width * pageWidth; - const height = this.height * pageHeight; - switch (rotation) { - case 0: - return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY]; - case 90: - return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY]; - case 180: - return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY]; - case 270: - return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY]; - default: - throw new Error("Invalid rotation"); - } - } - getRectInCurrentCoords(rect, pageHeight) { - const [x1, y1, x2, y2] = rect; - const width = x2 - x1; - const height = y2 - y1; - switch (this.rotation) { - case 0: - return [x1, pageHeight - y2, width, height]; - case 90: - return [x1, pageHeight - y1, height, width]; - case 180: - return [x2, pageHeight - y1, width, height]; - case 270: - return [x2, pageHeight - y2, height, width]; - default: - throw new Error("Invalid rotation"); - } - } - getPDFRect() { - return this.getRect(0, 0); - } - getNonHCMColor() { - return this.color && AnnotationEditor._colorManager.convert(this._uiManager.getNonHCMColor(this.color)); - } - onUpdatedColor() { - this.#comment?.onUpdatedColor(); - } - getData() { - const { - comment: { - text: str, - color, - date, - opacity, - deleted, - richText - }, - uid: id, - pageIndex, - creationDate, - modificationDate - } = this; - return { - id, - pageIndex, - rect: this.getPDFRect(), - richText, - contentsObj: { - str - }, - creationDate, - modificationDate: date || modificationDate, - popupRef: !deleted, - color, - opacity - }; - } - onceAdded(focus) {} - isEmpty() { - return false; - } - enableEditMode() { - if (this.isInEditMode()) { - return false; - } - this.parent.setEditingState(false); - this.#isInEditMode = true; - return true; - } - disableEditMode() { - if (!this.isInEditMode()) { - return false; - } - this.parent.setEditingState(true); - this.#isInEditMode = false; - return true; - } - isInEditMode() { - return this.#isInEditMode; - } - shouldGetKeyboardEvents() { - return this.#isResizerEnabledForKeyboard; - } - needsToBeRebuilt() { - return this.div && !this.isAttachedToDOM; - } - get isOnScreen() { - const { - top, - left, - bottom, - right - } = this.getClientDimensions(); - const { - innerHeight, - innerWidth - } = window; - return left < innerWidth && right > 0 && top < innerHeight && bottom > 0; - } - #addFocusListeners() { - if (this.#focusAC || !this.div) { - return; - } - this.#focusAC = new AbortController(); - const signal = this._uiManager.combinedSignal(this.#focusAC); - this.div.addEventListener("focusin", this.focusin.bind(this), { - signal - }); - this.div.addEventListener("focusout", this.focusout.bind(this), { - signal - }); - } - rebuild() { - this.#addFocusListeners(); - } - rotate(_angle) {} - resize() {} - serializeDeleted() { - return { - id: this.annotationElementId, - deleted: true, - pageIndex: this.pageIndex, - popupRef: this._initialData?.popupRef || "" - }; - } - serialize(isForCopying = false, context = null) { - return { - annotationType: this.mode, - pageIndex: this.pageIndex, - rect: this.getPDFRect(), - rotation: this.rotation, - structTreeParentId: this._structTreeParentId, - popupRef: this._initialData?.popupRef || "" - }; - } - static async deserialize(data, parent, uiManager) { - const editor = new this.prototype.constructor({ - parent, - id: parent.getNextId(), - uiManager, - annotationElementId: data.annotationElementId, - creationDate: data.creationDate, - modificationDate: data.modificationDate - }); - editor.rotation = data.rotation; - editor.#accessibilityData = data.accessibilityData; - editor._isCopy = data.isCopy || false; - const [pageWidth, pageHeight] = editor.pageDimensions; - const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight); - editor.x = x / pageWidth; - editor.y = y / pageHeight; - editor.width = width / pageWidth; - editor.height = height / pageHeight; - return editor; - } - get hasBeenModified() { - return !!this.annotationElementId && (this.deleted || this.serialize() !== null); - } - remove() { - this.#focusAC?.abort(); - this.#focusAC = null; - if (!this.isEmpty()) { - this.commit(); - } - if (this.parent) { - this.parent.remove(this); - } else { - this._uiManager.removeEditor(this); - } - this.hideCommentPopup(); - if (this.#moveInDOMTimeout) { - clearTimeout(this.#moveInDOMTimeout); - this.#moveInDOMTimeout = null; - } - this.#stopResizing(); - this.removeEditToolbar(); - if (this.#telemetryTimeouts) { - for (const timeout of this.#telemetryTimeouts.values()) { - clearTimeout(timeout); - } - this.#telemetryTimeouts = null; - } - this.parent = null; - this.#touchManager?.destroy(); - this.#touchManager = null; - this.#fakeAnnotation?.remove(); - this.#fakeAnnotation = null; - } - get isResizable() { - return false; - } - makeResizable() { - if (this.isResizable) { - this.#createResizers(); - this.#resizersDiv.classList.remove("hidden"); - } - } - get toolbarPosition() { - return null; - } - get commentButtonPosition() { - return this._uiManager.direction === "ltr" ? [1, 0] : [0, 0]; - } - get commentButtonPositionInPage() { - const { - commentButtonPosition: [posX, posY] - } = this; - const [blX, blY, trX, trY] = this.getPDFRect(); - return [AnnotationEditor._round(blX + (trX - blX) * posX), AnnotationEditor._round(blY + (trY - blY) * (1 - posY))]; - } - get commentButtonColor() { - return this._uiManager.makeCommentColor(this.getNonHCMColor(), this.opacity); - } - get commentPopupPosition() { - return this.#comment.commentPopupPositionInLayer; - } - set commentPopupPosition(pos) { - this.#comment.commentPopupPositionInLayer = pos; - } - hasDefaultPopupPosition() { - return this.#comment.hasDefaultPopupPosition(); - } - get commentButtonWidth() { - return this.#comment.commentButtonWidth; - } - get elementBeforePopup() { - return this.div; - } - setCommentButtonStates(options) { - this.#comment?.setCommentButtonStates(options); - } - keydown(event) { - if (!this.isResizable || event.target !== this.div || event.key !== "Enter") { - return; - } - this._uiManager.setSelected(this); - this.#savedDimensions = { - savedX: this.x, - savedY: this.y, - savedWidth: this.width, - savedHeight: this.height - }; - const children = this.#resizersDiv.children; - if (!this.#allResizerDivs) { - this.#allResizerDivs = Array.from(children); - const boundResizerKeydown = this.#resizerKeydown.bind(this); - const boundResizerBlur = this.#resizerBlur.bind(this); - const signal = this._uiManager._signal; - for (const div of this.#allResizerDivs) { - const name = div.getAttribute("data-resizer-name"); - div.setAttribute("role", "spinbutton"); - div.addEventListener("keydown", boundResizerKeydown, { - signal - }); - div.addEventListener("blur", boundResizerBlur, { - signal - }); - div.addEventListener("focus", this.#resizerFocus.bind(this, name), { - signal - }); - div.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); - } - } - const first = this.#allResizerDivs[0]; - let firstPosition = 0; - for (const div of children) { - if (div === first) { - break; - } - firstPosition++; - } - const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4); - if (nextFirstPosition !== firstPosition) { - if (nextFirstPosition < firstPosition) { - for (let i = 0; i < firstPosition - nextFirstPosition; i++) { - this.#resizersDiv.append(this.#resizersDiv.firstElementChild); - } - } else if (nextFirstPosition > firstPosition) { - for (let i = 0; i < nextFirstPosition - firstPosition; i++) { - this.#resizersDiv.firstElementChild.before(this.#resizersDiv.lastElementChild); - } - } - let i = 0; - for (const child of children) { - const div = this.#allResizerDivs[i++]; - const name = div.getAttribute("data-resizer-name"); - child.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); - } - } - this.#setResizerTabIndex(0); - this.#isResizerEnabledForKeyboard = true; - this.#resizersDiv.firstElementChild.focus({ - focusVisible: true - }); - event.preventDefault(); - event.stopImmediatePropagation(); - } - #resizerKeydown(event) { - AnnotationEditor._resizerKeyboardManager.exec(this, event); - } - #resizerBlur(event) { - if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) { - this.#stopResizing(); - } - } - #resizerFocus(name) { - this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : ""; - } - #setResizerTabIndex(value) { - if (!this.#allResizerDivs) { - return; - } - for (const div of this.#allResizerDivs) { - div.tabIndex = value; - } - } - _resizeWithKeyboard(x, y) { - if (!this.#isResizerEnabledForKeyboard) { - return; - } - this.#resizerPointermove(this.#focusedResizerName, { - deltaX: x, - deltaY: y, - fromKeyboard: true - }); - } - #stopResizing() { - this.#isResizerEnabledForKeyboard = false; - this.#setResizerTabIndex(-1); - this.#addResizeToUndoStack(); - } - _stopResizingWithKeyboard() { - this.#stopResizing(); - this.div.focus(); - } - select() { - if (this.isSelected && this._editToolbar) { - this._editToolbar.show(); - return; - } - this.isSelected = true; - this.makeResizable(); - this.div?.classList.add("selectedEditor"); - if (!this._editToolbar) { - this.addEditToolbar().then(() => { - if (this.div?.classList.contains("selectedEditor")) { - this._editToolbar?.show(); - } - }); - return; - } - this._editToolbar?.show(); - this.#altText?.toggleAltTextBadge(false); - } - focus() { - if (this.div && !this.div.contains(document.activeElement)) { - setTimeout(() => this.div?.focus({ - preventScroll: true - }), 0); - } - } - unselect() { - if (!this.isSelected) { - return; - } - this.isSelected = false; - this.#resizersDiv?.classList.add("hidden"); - this.div?.classList.remove("selectedEditor"); - if (this.div?.contains(document.activeElement)) { - this._uiManager.currentLayer.div.focus({ - preventScroll: true - }); - } - this._editToolbar?.hide(); - this.#altText?.toggleAltTextBadge(true); - this.hideCommentPopup(); - } - hideCommentPopup() { - if (this.hasComment) { - this._uiManager.toggleComment(null); - } - } - updateParams(type, value) {} - disableEditing() {} - enableEditing() {} - get canChangeContent() { - return false; - } - enterInEditMode() { - if (!this.canChangeContent) { - return; - } - this.enableEditMode(); - this.div.focus(); - } - dblclick(event) { - if (event.target.nodeName === "BUTTON") { - return; - } - this.enterInEditMode(); - this.parent.updateToolbar({ - mode: this.constructor._editorType, - editId: this.uid - }); - } - getElementForAltText() { - return this.div; - } - get contentDiv() { - return this.div; - } - get isEditing() { - return this.#isEditing; - } - set isEditing(value) { - this.#isEditing = value; - if (!this.parent) { - return; - } - if (value) { - this.parent.setSelected(this); - this.parent.setActiveEditor(this); - } else { - this.parent.setActiveEditor(null); - } - } - static get MIN_SIZE() { - return 16; - } - static canCreateNewEmptyEditor() { - return true; - } - get telemetryInitialData() { - return { - action: "added" - }; - } - get telemetryFinalData() { - return null; - } - _reportTelemetry(data, mustWait = false) { - if (mustWait) { - this.#telemetryTimeouts ||= new Map(); - const { - action - } = data; - let timeout = this.#telemetryTimeouts.get(action); - if (timeout) { - clearTimeout(timeout); - } - timeout = setTimeout(() => { - this._reportTelemetry(data); - this.#telemetryTimeouts.delete(action); - if (this.#telemetryTimeouts.size === 0) { - this.#telemetryTimeouts = null; - } - }, AnnotationEditor._telemetryTimeout); - this.#telemetryTimeouts.set(action, timeout); - return; - } - data.type ||= this.editorType; - this._uiManager._eventBus.dispatch("reporttelemetry", { - source: this, - details: { - type: "editing", - data - } - }); - } - show(visible = this._isVisible) { - this.div.classList.toggle("hidden", !visible); - this._isVisible = visible; - } - enable() { - if (this.div) { - this.div.tabIndex = 0; - } - this.#disabled = false; - } - disable() { - if (this.div) { - this.div.tabIndex = -1; - } - this.#disabled = true; - } - updateFakeAnnotationElement(annotationLayer) { - if (!this.#fakeAnnotation && !this.deleted) { - this.#fakeAnnotation = annotationLayer.addFakeAnnotation(this); - return; - } - if (this.deleted) { - this.#fakeAnnotation.remove(); - this.#fakeAnnotation = null; - return; - } - if (this.hasEditedComment || this._hasBeenMoved || this._hasBeenResized) { - this.#fakeAnnotation.updateEdited({ - rect: this.getPDFRect(), - popup: this.comment - }); - } - } - renderAnnotationElement(annotation) { - if (this.deleted) { - annotation.hide(); - return null; - } - let content = annotation.container.querySelector(".annotationContent"); - if (!content) { - content = document.createElement("div"); - content.classList.add("annotationContent", this.editorType); - annotation.container.prepend(content); - } else if (content.nodeName === "CANVAS") { - const canvas = content; - content = document.createElement("div"); - content.classList.add("annotationContent", this.editorType); - canvas.before(content); - } - return content; - } - resetAnnotationElement(annotation) { - const { - firstElementChild - } = annotation.container; - if (firstElementChild?.nodeName === "DIV" && firstElementChild.classList.contains("annotationContent")) { - firstElementChild.remove(); - } - } -} -class FakeEditor extends AnnotationEditor { - constructor(params) { - super(params); - this.annotationElementId = params.annotationElementId; - this.deleted = true; - } - serialize() { - return this.serializeDeleted(); - } -} - -;// ./src/shared/murmurhash3.js -const SEED = 0xc3d2e1f0; -const MASK_HIGH = 0xffff0000; -const MASK_LOW = 0xffff; -class MurmurHash3_64 { - constructor(seed) { - this.h1 = seed ? seed & 0xffffffff : SEED; - this.h2 = seed ? seed & 0xffffffff : SEED; - } - update(input) { - let data, length; - if (typeof input === "string") { - data = new Uint8Array(input.length * 2); - length = 0; - for (let i = 0, ii = input.length; i < ii; i++) { - const code = input.charCodeAt(i); - if (code <= 0xff) { - data[length++] = code; - } else { - data[length++] = code >>> 8; - data[length++] = code & 0xff; - } - } - } else if (ArrayBuffer.isView(input)) { - data = input.slice(); - length = data.byteLength; - } else { - throw new Error("Invalid data format, must be a string or TypedArray."); - } - const blockCounts = length >> 2; - const tailLength = length - blockCounts * 4; - const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts); - let k1 = 0, - k2 = 0; - let h1 = this.h1, - h2 = this.h2; - const C1 = 0xcc9e2d51, - C2 = 0x1b873593; - const C1_LOW = C1 & MASK_LOW, - C2_LOW = C2 & MASK_LOW; - for (let i = 0; i < blockCounts; i++) { - if (i & 1) { - k1 = dataUint32[i]; - k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; - k1 = k1 << 15 | k1 >>> 17; - k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; - h1 ^= k1; - h1 = h1 << 13 | h1 >>> 19; - h1 = h1 * 5 + 0xe6546b64; - } else { - k2 = dataUint32[i]; - k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW; - k2 = k2 << 15 | k2 >>> 17; - k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW; - h2 ^= k2; - h2 = h2 << 13 | h2 >>> 19; - h2 = h2 * 5 + 0xe6546b64; - } - } - k1 = 0; - switch (tailLength) { - case 3: - k1 ^= data[blockCounts * 4 + 2] << 16; - case 2: - k1 ^= data[blockCounts * 4 + 1] << 8; - case 1: - k1 ^= data[blockCounts * 4]; - k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; - k1 = k1 << 15 | k1 >>> 17; - k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; - if (blockCounts & 1) { - h1 ^= k1; - } else { - h2 ^= k1; - } - } - this.h1 = h1; - this.h2 = h2; - } - hexdigest() { - let h1 = this.h1, - h2 = this.h2; - h1 ^= h2 >>> 1; - h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW; - h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16; - h1 ^= h2 >>> 1; - h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW; - h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16; - h1 ^= h2 >>> 1; - return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0"); - } -} - -;// ./src/display/annotation_storage.js - - - -const SerializableEmpty = Object.freeze({ - map: null, - hash: "", - transfer: undefined -}); -class AnnotationStorage { - #modified = false; - #modifiedIds = null; - #editorsMap = null; - #storage = new Map(); - onSetModified = null; - onResetModified = null; - onAnnotationEditor = null; - getValue(key, defaultValue) { - const value = this.#storage.get(key); - if (value === undefined) { - return defaultValue; - } - return Object.assign(defaultValue, value); - } - getRawValue(key) { - return this.#storage.get(key); - } - remove(key) { - const storedValue = this.#storage.get(key); - if (storedValue === undefined) { - return; - } - if (storedValue instanceof AnnotationEditor) { - this.#editorsMap.delete(storedValue.annotationElementId); - } - this.#storage.delete(key); - if (this.#storage.size === 0) { - this.resetModified(); - } - if (typeof this.onAnnotationEditor === "function") { - for (const value of this.#storage.values()) { - if (value instanceof AnnotationEditor) { - return; - } - } - this.onAnnotationEditor(null); - } - } - setValue(key, value) { - const obj = this.#storage.get(key); - let modified = false; - if (obj !== undefined) { - for (const [entry, val] of Object.entries(value)) { - if (obj[entry] !== val) { - modified = true; - obj[entry] = val; - } - } - } else { - modified = true; - this.#storage.set(key, value); - } - if (modified) { - this.#setModified(); - } - if (value instanceof AnnotationEditor) { - (this.#editorsMap ||= new Map()).set(value.annotationElementId, value); - if (typeof this.onAnnotationEditor === "function") { - this.onAnnotationEditor(value.constructor._type); - } - } - } - has(key) { - return this.#storage.has(key); - } - get size() { - return this.#storage.size; - } - #setModified() { - if (!this.#modified) { - this.#modified = true; - if (typeof this.onSetModified === "function") { - this.onSetModified(); - } - } - } - resetModified() { - if (this.#modified) { - this.#modified = false; - if (typeof this.onResetModified === "function") { - this.onResetModified(); - } - } - } - get print() { - return new PrintAnnotationStorage(this); - } - get serializable() { - if (this.#storage.size === 0) { - return SerializableEmpty; - } - const map = new Map(), - hash = new MurmurHash3_64(), - transfer = []; - const context = Object.create(null); - let hasBitmap = false; - for (const [key, val] of this.#storage) { - const serialized = val instanceof AnnotationEditor ? val.serialize(false, context) : val; - if (val.page) { - val.pageIndex = val.page._pageIndex; - delete val.page; - } - if (serialized) { - map.set(key, serialized); - hash.update(`${key}:${JSON.stringify(serialized)}`); - hasBitmap ||= !!serialized.bitmap; - } - } - if (hasBitmap) { - for (const value of map.values()) { - if (value.bitmap) { - transfer.push(value.bitmap); - } - } - } - return map.size > 0 ? { - map, - hash: hash.hexdigest(), - transfer - } : SerializableEmpty; - } - get editorStats() { - let stats = null; - const typeToEditor = new Map(); - let numberOfEditedComments = 0; - let numberOfDeletedComments = 0; - for (const value of this.#storage.values()) { - if (!(value instanceof AnnotationEditor)) { - if (value.popup) { - if (value.popup.deleted) { - numberOfDeletedComments += 1; - } else { - numberOfEditedComments += 1; - } - } - continue; - } - if (value.isCommentDeleted) { - numberOfDeletedComments += 1; - } else if (value.hasEditedComment) { - numberOfEditedComments += 1; - } - const editorStats = value.telemetryFinalData; - if (!editorStats) { - continue; - } - const { - type - } = editorStats; - if (!typeToEditor.has(type)) { - typeToEditor.set(type, Object.getPrototypeOf(value).constructor); - } - stats ||= Object.create(null); - const map = stats[type] ||= new Map(); - for (const [key, val] of Object.entries(editorStats)) { - if (key === "type") { - continue; - } - const counters = map.getOrInsertComputed(key, makeMap); - counters.set(val, (counters.get(val) ?? 0) + 1); - } - } - if (numberOfDeletedComments > 0 || numberOfEditedComments > 0) { - stats ||= Object.create(null); - stats.comments = { - deleted: numberOfDeletedComments, - edited: numberOfEditedComments - }; - } - if (!stats) { - return null; - } - for (const [type, editor] of typeToEditor) { - stats[type] = editor.computeTelemetryFinalData(stats[type]); - } - return stats; - } - resetModifiedIds() { - this.#modifiedIds = null; - } - updateEditor(annotationId, data) { - const value = this.#editorsMap?.get(annotationId); - if (value) { - value.updateFromAnnotationLayer(data); - return true; - } - return false; - } - getEditor(annotationId) { - return this.#editorsMap?.get(annotationId) || null; - } - get modifiedIds() { - if (this.#modifiedIds) { - return this.#modifiedIds; - } - const ids = []; - if (this.#editorsMap) { - for (const value of this.#editorsMap.values()) { - if (!value.serialize()) { - continue; - } - ids.push(value.annotationElementId); - } - } - return this.#modifiedIds = { - ids: new Set(ids), - hash: ids.join(",") - }; - } - [Symbol.iterator]() { - return this.#storage.entries(); - } -} -class PrintAnnotationStorage extends AnnotationStorage { - #serializable = SerializableEmpty; - constructor(parent) { - super(); - const { - serializable - } = parent; - if (serializable === SerializableEmpty) { - return; - } - const { - map, - hash, - transfer - } = serializable; - const clone = structuredClone(map, transfer ? { - transfer - } : null); - this.#serializable = { - map: clone, - hash, - transfer: [] - }; - } - get print() { - unreachable("Should not call PrintAnnotationStorage.print"); - } - get serializable() { - return this.#serializable; - } - get modifiedIds() { - return shadow(this, "modifiedIds", { - ids: new Set(), - hash: "" - }); - } -} - -;// ./src/display/font_loader.js - - -class FontLoader { - #systemFonts = new Set(); - constructor({ - ownerDocument = globalThis.document, - styleElement = null - }) { - this._document = ownerDocument; - this.nativeFontFaces = new Set(); - this.styleElement = null; - this.loadingRequests = []; - this.loadTestFontId = 0; - } - addNativeFontFace(nativeFontFace) { - this.nativeFontFaces.add(nativeFontFace); - this._document.fonts.add(nativeFontFace); - } - removeNativeFontFace(nativeFontFace) { - this.nativeFontFaces.delete(nativeFontFace); - this._document.fonts.delete(nativeFontFace); - } - insertRule(rule) { - if (!this.styleElement) { - this.styleElement = this._document.createElement("style"); - this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement); - } - const styleSheet = this.styleElement.sheet; - styleSheet.insertRule(rule, styleSheet.cssRules.length); - } - clear() { - for (const nativeFontFace of this.nativeFontFaces) { - this._document.fonts.delete(nativeFontFace); - } - this.nativeFontFaces.clear(); - this.#systemFonts.clear(); - if (this.styleElement) { - this.styleElement.remove(); - this.styleElement = null; - } - } - async loadSystemFont({ - systemFontInfo: info, - disableFontFace, - _inspectFont - }) { - if (!info || this.#systemFonts.has(info.loadedName)) { - return; - } - assert(!disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set."); - if (this.isFontLoadingAPISupported) { - const { - loadedName, - src, - style - } = info; - const fontFace = new FontFace(loadedName, src, style); - this.addNativeFontFace(fontFace); - try { - await fontFace.load(); - this.#systemFonts.add(loadedName); - _inspectFont?.(info); - } catch { - warn(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`); - this.removeNativeFontFace(fontFace); - } - return; - } - unreachable("Not implemented: loadSystemFont without the Font Loading API."); - } - async bind(font) { - if (font.attached || font.missingFile && !font.systemFontInfo) { - return; - } - font.attached = true; - if (font.systemFontInfo) { - await this.loadSystemFont(font); - return; - } - if (this.isFontLoadingAPISupported) { - const nativeFontFace = font.createNativeFontFace(); - if (nativeFontFace) { - this.addNativeFontFace(nativeFontFace); - try { - await nativeFontFace.loaded; - } catch (ex) { - warn(`Failed to load font '${nativeFontFace.family}': '${ex}'.`); - font.disableFontFace = true; - throw ex; - } - } - return; - } - const rule = font.createFontFaceRule(); - if (rule) { - this.insertRule(rule); - if (this.isSyncFontLoadingSupported) { - return; - } - await new Promise(resolve => { - const request = this._queueLoadingCallback(resolve); - this._prepareFontLoadEvent(font, request); - }); - } - } - get isFontLoadingAPISupported() { - const hasFonts = !!this._document?.fonts; - return shadow(this, "isFontLoadingAPISupported", hasFonts); - } - get isSyncFontLoadingSupported() { - return shadow(this, "isSyncFontLoadingSupported", isNodeJS || FeatureTest.platform.isFirefox); - } - _queueLoadingCallback(callback) { - function completeRequest() { - assert(!request.done, "completeRequest() cannot be called twice."); - request.done = true; - while (loadingRequests.length > 0 && loadingRequests[0].done) { - const otherRequest = loadingRequests.shift(); - setTimeout(otherRequest.callback, 0); - } - } - const { - loadingRequests - } = this; - const request = { - done: false, - complete: completeRequest, - callback - }; - loadingRequests.push(request); - return request; - } - get _loadTestFont() { - const testFont = atob("T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQA" + "FQAABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAA" + "ALwAAAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgA" + "AAAGbmFtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1" + "AAsD6AAAAADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD" + "6AAAAAAD6AABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACM" + "AooCvAAAAeAAMQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4D" + "IP84AFoDIQAAAAAAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAA" + "AAEAAQAAAAEAAAAAAAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUA" + "AQAAAAEAAAAAAAYAAQAAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgAB" + "AAMAAQQJAAMAAgABAAMAAQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABY" + "AAAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAA" + "AC7////TAAEAAAAAAAABBgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAA" + "AAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgcA/gXBIwMAYuL+nz5tQXkD5j3CBLnEQAC" + "AQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAAABAQAADwACAQEEE/t3" + "Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQAAAAAAAABAAAAAMmJbzEAAAAAzgTj" + "FQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAgABAAAAAAAAAAAD6AAAAAAAAA=="); - return shadow(this, "_loadTestFont", testFont); - } - _prepareFontLoadEvent(font, request) { - function int32(data, offset) { - return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff; - } - function spliceString(s, offset, remove, insert) { - const chunk1 = s.substring(0, offset); - const chunk2 = s.substring(offset + remove); - return chunk1 + insert + chunk2; - } - let i, ii; - const canvas = this._document.createElement("canvas"); - canvas.width = 1; - canvas.height = 1; - const ctx = canvas.getContext("2d"); - let called = 0; - function isFontReady(name, callback) { - if (++called > 30) { - warn("Load test font never loaded."); - callback(); - return; - } - ctx.font = "30px " + name; - ctx.fillText(".", 0, 20); - const imageData = ctx.getImageData(0, 0, 1, 1); - if (imageData.data[3] > 0) { - callback(); - return; - } - setTimeout(isFontReady.bind(null, name, callback)); - } - const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`; - let data = this._loadTestFont; - const COMMENT_OFFSET = 976; - data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId); - const CFF_CHECKSUM_OFFSET = 16; - const XXXX_VALUE = 0x58585858; - let checksum = int32(data, CFF_CHECKSUM_OFFSET); - for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) { - checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0; - } - if (i < loadTestFontId.length) { - checksum = checksum - XXXX_VALUE + int32(loadTestFontId + "XXX", i) | 0; - } - data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum)); - const url = `url(data:font/opentype;base64,${btoa(data)});`; - const rule = `@font-face {font-family:"${loadTestFontId}";src:${url}}`; - this.insertRule(rule); - const div = this._document.createElement("div"); - div.style.visibility = "hidden"; - div.style.width = div.style.height = "10px"; - div.style.position = "absolute"; - div.style.top = div.style.left = "0px"; - for (const name of [font.loadedName, loadTestFontId]) { - const span = this._document.createElement("span"); - span.textContent = "Hi"; - span.style.fontFamily = name; - div.append(span); - } - this._document.body.append(div); - isFontReady(loadTestFontId, () => { - div.remove(); - request.complete(); - }); - } -} -class FontFaceObject { - #fontData; - constructor(translatedData, inspectFont = null, extra, charProcOperatorList) { - this.compiledGlyphs = Object.create(null); - this.#fontData = translatedData; - this._inspectFont = inspectFont; - if (extra) { - Object.assign(this, extra); - } - if (charProcOperatorList) { - this.charProcOperatorList = charProcOperatorList; - } - } - createNativeFontFace() { - if (!this.data || this.disableFontFace) { - return null; - } - let nativeFontFace; - if (!this.cssFontInfo) { - nativeFontFace = new FontFace(this.loadedName, this.data, {}); - } else { - const css = { - weight: this.cssFontInfo.fontWeight - }; - if (this.cssFontInfo.italicAngle) { - css.style = `oblique ${this.cssFontInfo.italicAngle}deg`; - } - nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css); - } - this._inspectFont?.(this); - return nativeFontFace; - } - createFontFaceRule() { - if (!this.data || this.disableFontFace) { - return null; - } - const url = `url(data:${this.mimetype};base64,${this.data.toBase64()});`; - let rule; - if (!this.cssFontInfo) { - rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; - } else { - let css = `font-weight: ${this.cssFontInfo.fontWeight};`; - if (this.cssFontInfo.italicAngle) { - css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`; - } - rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`; - } - this._inspectFont?.(this, url); - return rule; - } - getPathGenerator(objs, character) { - if (this.compiledGlyphs[character] !== undefined) { - return this.compiledGlyphs[character]; - } - const objId = this.loadedName + "_path_" + character; - let cmds; - try { - cmds = objs.get(objId); - } catch (ex) { - warn(`getPathGenerator - ignoring character: "${ex}".`); - } - const path = makePathFromDrawOPS(cmds?.path); - if (!this.fontExtraProperties) { - objs.delete(objId); - } - return this.compiledGlyphs[character] = path; - } - get black() { - return this.#fontData.black; - } - get bold() { - return this.#fontData.bold; - } - get disableFontFace() { - return this.#fontData.disableFontFace ?? false; - } - set disableFontFace(value) { - shadow(this, "disableFontFace", !!value); - } - get fontExtraProperties() { - return this.#fontData.fontExtraProperties ?? false; - } - get isInvalidPDFjsFont() { - return this.#fontData.isInvalidPDFjsFont; - } - get isType3Font() { - return this.#fontData.isType3Font; - } - get italic() { - return this.#fontData.italic; - } - get missingFile() { - return this.#fontData.missingFile; - } - get remeasure() { - return this.#fontData.remeasure; - } - get vertical() { - return this.#fontData.vertical; - } - get ascent() { - return this.#fontData.ascent; - } - get defaultWidth() { - return this.#fontData.defaultWidth; - } - get descent() { - return this.#fontData.descent; - } - get bbox() { - return this.#fontData.bbox; - } - set bbox(bbox) { - shadow(this, "bbox", bbox); - } - get fontMatrix() { - return this.#fontData.fontMatrix; - } - get fallbackName() { - return this.#fontData.fallbackName; - } - get loadedName() { - return this.#fontData.loadedName; - } - get mimetype() { - return this.#fontData.mimetype; - } - get name() { - return this.#fontData.name; - } - get data() { - return this.#fontData.data; - } - clearData() { - this.#fontData.clearData(); - } - get cssFontInfo() { - return this.#fontData.cssFontInfo; - } - get systemFontInfo() { - return this.#fontData.systemFontInfo; - } - get defaultVMetrics() { - return this.#fontData.defaultVMetrics; - } -} - -;// ./src/shared/obj-bin-transform.js - -class CssFontInfo { - #buffer; - #view; - #decoder; - static strings = ["fontFamily", "fontWeight", "italicAngle"]; - static write(info) { - const encoder = new TextEncoder(); - const encodedStrings = {}; - let stringsLength = 0; - for (const prop of CssFontInfo.strings) { - const encoded = encoder.encode(info[prop]); - encodedStrings[prop] = encoded; - stringsLength += 4 + encoded.length; - } - const buffer = new ArrayBuffer(stringsLength); - const data = new Uint8Array(buffer); - const view = new DataView(buffer); - let offset = 0; - for (const prop of CssFontInfo.strings) { - const encoded = encodedStrings[prop]; - const length = encoded.length; - view.setUint32(offset, length); - data.set(encoded, offset + 4); - offset += 4 + length; - } - assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow"); - return buffer; - } - constructor(buffer) { - this.#buffer = buffer; - this.#view = new DataView(this.#buffer); - this.#decoder = new TextDecoder(); - } - #readString(index) { - assert(index < CssFontInfo.strings.length, "Invalid string index"); - let offset = 0; - for (let i = 0; i < index; i++) { - offset += this.#view.getUint32(offset) + 4; - } - const length = this.#view.getUint32(offset); - return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length)); - } - get fontFamily() { - return this.#readString(0); - } - get fontWeight() { - return this.#readString(1); - } - get italicAngle() { - return this.#readString(2); - } -} -class SystemFontInfo { - #buffer; - #view; - #decoder; - static strings = ["css", "loadedName", "baseFontName", "src"]; - static write(info) { - const encoder = new TextEncoder(); - const encodedStrings = {}; - let stringsLength = 0; - for (const prop of SystemFontInfo.strings) { - const encoded = encoder.encode(info[prop]); - encodedStrings[prop] = encoded; - stringsLength += 4 + encoded.length; - } - stringsLength += 4; - let encodedStyleStyle, - encodedStyleWeight, - lengthEstimate = 1 + stringsLength; - if (info.style) { - encodedStyleStyle = encoder.encode(info.style.style); - encodedStyleWeight = encoder.encode(info.style.weight); - lengthEstimate += 4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length; - } - const buffer = new ArrayBuffer(lengthEstimate); - const data = new Uint8Array(buffer); - const view = new DataView(buffer); - let offset = 0; - view.setUint8(offset++, info.guessFallback ? 1 : 0); - view.setUint32(offset, 0); - offset += 4; - stringsLength = 0; - for (const prop of SystemFontInfo.strings) { - const encoded = encodedStrings[prop]; - const length = encoded.length; - stringsLength += 4 + length; - view.setUint32(offset, length); - data.set(encoded, offset + 4); - offset += 4 + length; - } - view.setUint32(offset - stringsLength - 4, stringsLength); - if (info.style) { - view.setUint32(offset, encodedStyleStyle.length); - data.set(encodedStyleStyle, offset + 4); - offset += 4 + encodedStyleStyle.length; - view.setUint32(offset, encodedStyleWeight.length); - data.set(encodedStyleWeight, offset + 4); - offset += 4 + encodedStyleWeight.length; - } - assert(offset <= buffer.byteLength, "SubstitionInfo.write: Buffer overflow"); - return buffer.transferToFixedLength(offset); - } - constructor(buffer) { - this.#buffer = buffer; - this.#view = new DataView(this.#buffer); - this.#decoder = new TextDecoder(); - } - get guessFallback() { - return this.#view.getUint8(0) !== 0; - } - #readString(index) { - assert(index < SystemFontInfo.strings.length, "Invalid string index"); - let offset = 5; - for (let i = 0; i < index; i++) { - offset += this.#view.getUint32(offset) + 4; - } - const length = this.#view.getUint32(offset); - return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length)); - } - get css() { - return this.#readString(0); - } - get loadedName() { - return this.#readString(1); - } - get baseFontName() { - return this.#readString(2); - } - get src() { - return this.#readString(3); - } - get style() { - let offset = 1; - offset += 4 + this.#view.getUint32(offset); - const styleLength = this.#view.getUint32(offset); - const style = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, styleLength)); - offset += 4 + styleLength; - const weightLength = this.#view.getUint32(offset); - const weight = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, weightLength)); - return { - style, - weight - }; - } -} -class FontInfo { - static bools = ["black", "bold", "disableFontFace", "fontExtraProperties", "isInvalidPDFjsFont", "isType3Font", "italic", "missingFile", "remeasure", "vertical"]; - static numbers = ["ascent", "defaultWidth", "descent"]; - static strings = ["fallbackName", "loadedName", "mimetype", "name"]; - static #OFFSET_NUMBERS = Math.ceil(this.bools.length * 2 / 8); - static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8; - static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4; - static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6; - static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3; - #buffer; - #decoder; - #view; - constructor({ - data, - extra - }) { - this.#buffer = data; - this.#decoder = new TextDecoder(); - this.#view = new DataView(this.#buffer); - if (extra) { - Object.assign(this, extra); - } - } - #readBoolean(index) { - assert(index < FontInfo.bools.length, "Invalid boolean index"); - const byteOffset = Math.floor(index / 4); - const bitOffset = index * 2 % 8; - const value = this.#view.getUint8(byteOffset) >> bitOffset & 0x03; - return value === 0x00 ? undefined : value === 0x02; - } - get black() { - return this.#readBoolean(0); - } - get bold() { - return this.#readBoolean(1); - } - get disableFontFace() { - return this.#readBoolean(2); - } - get fontExtraProperties() { - return this.#readBoolean(3); - } - get isInvalidPDFjsFont() { - return this.#readBoolean(4); - } - get isType3Font() { - return this.#readBoolean(5); - } - get italic() { - return this.#readBoolean(6); - } - get missingFile() { - return this.#readBoolean(7); - } - get remeasure() { - return this.#readBoolean(8); - } - get vertical() { - return this.#readBoolean(9); - } - #readNumber(index) { - assert(index < FontInfo.numbers.length, "Invalid number index"); - return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8); - } - get ascent() { - return this.#readNumber(0); - } - get defaultWidth() { - return this.#readNumber(1); - } - get descent() { - return this.#readNumber(2); - } - get bbox() { - let offset = FontInfo.#OFFSET_BBOX; - const numCoords = this.#view.getUint8(offset); - if (numCoords === 0) { - return undefined; - } - offset += 1; - const bbox = []; - for (let i = 0; i < 4; i++) { - bbox.push(this.#view.getInt16(offset, true)); - offset += 2; - } - return bbox; - } - get fontMatrix() { - let offset = FontInfo.#OFFSET_FONT_MATRIX; - const numPoints = this.#view.getUint8(offset); - if (numPoints === 0) { - return undefined; - } - offset += 1; - const fontMatrix = []; - for (let i = 0; i < 6; i++) { - fontMatrix.push(this.#view.getFloat64(offset, true)); - offset += 8; - } - return fontMatrix; - } - get defaultVMetrics() { - let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS; - const numMetrics = this.#view.getUint8(offset); - if (numMetrics === 0) { - return undefined; - } - offset += 1; - const defaultVMetrics = []; - for (let i = 0; i < 3; i++) { - defaultVMetrics.push(this.#view.getInt16(offset, true)); - offset += 2; - } - return defaultVMetrics; - } - #readString(index) { - assert(index < FontInfo.strings.length, "Invalid string index"); - let offset = FontInfo.#OFFSET_STRINGS + 4; - for (let i = 0; i < index; i++) { - offset += this.#view.getUint32(offset) + 4; - } - const length = this.#view.getUint32(offset); - const stringData = new Uint8Array(length); - stringData.set(new Uint8Array(this.#buffer, offset + 4, length)); - return this.#decoder.decode(stringData); - } - get fallbackName() { - return this.#readString(0); - } - get loadedName() { - return this.#readString(1); - } - get mimetype() { - return this.#readString(2); - } - get name() { - return this.#readString(3); - } - get data() { - let offset = FontInfo.#OFFSET_STRINGS; - const stringsLength = this.#view.getUint32(offset); - offset += 4 + stringsLength; - const systemFontInfoLength = this.#view.getUint32(offset); - offset += 4 + systemFontInfoLength; - const cssFontInfoLength = this.#view.getUint32(offset); - offset += 4 + cssFontInfoLength; - const length = this.#view.getUint32(offset); - if (length === 0) { - return undefined; - } - return new Uint8Array(this.#buffer, offset + 4, length); - } - clearData() { - let offset = FontInfo.#OFFSET_STRINGS; - const stringsLength = this.#view.getUint32(offset); - offset += 4 + stringsLength; - const systemFontInfoLength = this.#view.getUint32(offset); - offset += 4 + systemFontInfoLength; - const cssFontInfoLength = this.#view.getUint32(offset); - offset += 4 + cssFontInfoLength; - const length = this.#view.getUint32(offset); - const data = new Uint8Array(this.#buffer, offset + 4, length); - data.fill(0); - this.#view.setUint32(offset, 0); - } - get cssFontInfo() { - let offset = FontInfo.#OFFSET_STRINGS; - const stringsLength = this.#view.getUint32(offset); - offset += 4 + stringsLength; - const systemFontInfoLength = this.#view.getUint32(offset); - offset += 4 + systemFontInfoLength; - const cssFontInfoLength = this.#view.getUint32(offset); - if (cssFontInfoLength === 0) { - return null; - } - const cssFontInfoData = new Uint8Array(cssFontInfoLength); - cssFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength)); - return new CssFontInfo(cssFontInfoData.buffer); - } - get systemFontInfo() { - let offset = FontInfo.#OFFSET_STRINGS; - const stringsLength = this.#view.getUint32(offset); - offset += 4 + stringsLength; - const systemFontInfoLength = this.#view.getUint32(offset); - if (systemFontInfoLength === 0) { - return null; - } - const systemFontInfoData = new Uint8Array(systemFontInfoLength); - systemFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength)); - return new SystemFontInfo(systemFontInfoData.buffer); - } - static write(font) { - const systemFontInfoBuffer = font.systemFontInfo ? SystemFontInfo.write(font.systemFontInfo) : null; - const cssFontInfoBuffer = font.cssFontInfo ? CssFontInfo.write(font.cssFontInfo) : null; - const encoder = new TextEncoder(); - const encodedStrings = {}; - let stringsLength = 0; - for (const prop of FontInfo.strings) { - encodedStrings[prop] = encoder.encode(font[prop]); - stringsLength += 4 + encodedStrings[prop].length; - } - const lengthEstimate = FontInfo.#OFFSET_STRINGS + 4 + stringsLength + 4 + (systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) + 4 + (cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) + 4 + (font.data ? font.data.length : 0); - const buffer = new ArrayBuffer(lengthEstimate); - const data = new Uint8Array(buffer); - const view = new DataView(buffer); - let offset = 0; - const numBools = FontInfo.bools.length; - let boolByte = 0, - boolBit = 0; - for (let i = 0; i < numBools; i++) { - const value = font[FontInfo.bools[i]]; - const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01; - boolByte |= bits << boolBit; - boolBit += 2; - if (boolBit === 8 || i === numBools - 1) { - view.setUint8(offset++, boolByte); - boolByte = 0; - boolBit = 0; - } - } - assert(offset === FontInfo.#OFFSET_NUMBERS, "FontInfo.write: Boolean properties offset mismatch"); - for (const prop of FontInfo.numbers) { - view.setFloat64(offset, font[prop]); - offset += 8; - } - assert(offset === FontInfo.#OFFSET_BBOX, "FontInfo.write: Number properties offset mismatch"); - if (font.bbox) { - view.setUint8(offset++, 4); - for (const coord of font.bbox) { - view.setInt16(offset, coord, true); - offset += 2; - } - } else { - view.setUint8(offset++, 0); - offset += 2 * 4; - } - assert(offset === FontInfo.#OFFSET_FONT_MATRIX, "FontInfo.write: BBox properties offset mismatch"); - if (font.fontMatrix) { - view.setUint8(offset++, 6); - for (const point of font.fontMatrix) { - view.setFloat64(offset, point, true); - offset += 8; - } - } else { - view.setUint8(offset++, 0); - offset += 8 * 6; - } - assert(offset === FontInfo.#OFFSET_DEFAULT_VMETRICS, "FontInfo.write: FontMatrix properties offset mismatch"); - if (font.defaultVMetrics) { - view.setUint8(offset++, 1); - for (const metric of font.defaultVMetrics) { - view.setInt16(offset, metric, true); - offset += 2; - } - } else { - view.setUint8(offset++, 0); - offset += 3 * 2; - } - assert(offset === FontInfo.#OFFSET_STRINGS, "FontInfo.write: DefaultVMetrics properties offset mismatch"); - view.setUint32(FontInfo.#OFFSET_STRINGS, 0); - offset += 4; - for (const prop of FontInfo.strings) { - const encoded = encodedStrings[prop]; - const length = encoded.length; - view.setUint32(offset, length); - data.set(encoded, offset + 4); - offset += 4 + length; - } - view.setUint32(FontInfo.#OFFSET_STRINGS, offset - FontInfo.#OFFSET_STRINGS - 4); - if (!systemFontInfoBuffer) { - view.setUint32(offset, 0); - offset += 4; - } else { - const length = systemFontInfoBuffer.byteLength; - view.setUint32(offset, length); - assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at systemFontInfo"); - data.set(new Uint8Array(systemFontInfoBuffer), offset + 4); - offset += 4 + length; - } - if (!cssFontInfoBuffer) { - view.setUint32(offset, 0); - offset += 4; - } else { - const length = cssFontInfoBuffer.byteLength; - view.setUint32(offset, length); - assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at cssFontInfo"); - data.set(new Uint8Array(cssFontInfoBuffer), offset + 4); - offset += 4 + length; - } - if (font.data === undefined) { - view.setUint32(offset, 0); - offset += 4; - } else { - view.setUint32(offset, font.data.length); - data.set(font.data, offset + 4); - offset += 4 + font.data.length; - } - assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow"); - return buffer.transferToFixedLength(offset); - } -} -class PatternInfo { - static #KIND = 0; - static #HAS_BBOX = 1; - static #HAS_BACKGROUND = 2; - static #SHADING_TYPE = 3; - static #N_COORD = 4; - static #N_COLOR = 8; - static #N_STOP = 12; - static #N_FIGURES = 16; - constructor(buffer) { - this.buffer = buffer; - this.view = new DataView(buffer); - this.data = new Uint8Array(buffer); - } - static write(ir) { - let kind, - bbox = null, - coords = [], - colors = [], - colorStops = [], - figures = [], - shadingType = null, - background = null; - switch (ir[0]) { - case "RadialAxial": - kind = ir[1] === "axial" ? 1 : 2; - bbox = ir[2]; - colorStops = ir[3]; - if (kind === 1) { - coords.push(...ir[4], ...ir[5]); - } else { - coords.push(ir[4][0], ir[4][1], ir[6], ir[5][0], ir[5][1], ir[7]); - } - break; - case "Mesh": - kind = 3; - shadingType = ir[1]; - coords = ir[2]; - colors = ir[3]; - figures = ir[4] || []; - bbox = ir[6]; - background = ir[7]; - break; - default: - throw new Error(`Unsupported pattern type: ${ir[0]}`); - } - const nCoord = Math.floor(coords.length / 2); - const nColor = Math.floor(colors.length / 3); - const nStop = colorStops.length; - const nFigures = figures.length; - let figuresSize = 0; - for (const figure of figures) { - figuresSize += 1; - figuresSize = Math.ceil(figuresSize / 4) * 4; - figuresSize += 4 + figure.coords.length * 4; - figuresSize += 4 + figure.colors.length * 4; - if (figure.verticesPerRow !== undefined) { - figuresSize += 4; - } - } - const byteLen = 20 + nCoord * 8 + nColor * 3 + nStop * 8 + (bbox ? 16 : 0) + (background ? 3 : 0) + figuresSize; - const buffer = new ArrayBuffer(byteLen); - const dataView = new DataView(buffer); - const u8data = new Uint8Array(buffer); - dataView.setUint8(PatternInfo.#KIND, kind); - dataView.setUint8(PatternInfo.#HAS_BBOX, bbox ? 1 : 0); - dataView.setUint8(PatternInfo.#HAS_BACKGROUND, background ? 1 : 0); - dataView.setUint8(PatternInfo.#SHADING_TYPE, shadingType); - dataView.setUint32(PatternInfo.#N_COORD, nCoord, true); - dataView.setUint32(PatternInfo.#N_COLOR, nColor, true); - dataView.setUint32(PatternInfo.#N_STOP, nStop, true); - dataView.setUint32(PatternInfo.#N_FIGURES, nFigures, true); - let offset = 20; - const coordsView = new Float32Array(buffer, offset, nCoord * 2); - coordsView.set(coords); - offset += nCoord * 8; - u8data.set(colors, offset); - offset += nColor * 3; - for (const [pos, hex] of colorStops) { - dataView.setFloat32(offset, pos, true); - offset += 4; - dataView.setUint32(offset, parseInt(hex.slice(1), 16), true); - offset += 4; - } - if (bbox) { - for (const v of bbox) { - dataView.setFloat32(offset, v, true); - offset += 4; - } - } - if (background) { - u8data.set(background, offset); - offset += 3; - } - for (let i = 0; i < figures.length; i++) { - const figure = figures[i]; - dataView.setUint8(offset, figure.type); - offset += 1; - offset = Math.ceil(offset / 4) * 4; - dataView.setUint32(offset, figure.coords.length, true); - offset += 4; - const figureCoordsView = new Int32Array(buffer, offset, figure.coords.length); - figureCoordsView.set(figure.coords); - offset += figure.coords.length * 4; - dataView.setUint32(offset, figure.colors.length, true); - offset += 4; - const colorsView = new Int32Array(buffer, offset, figure.colors.length); - colorsView.set(figure.colors); - offset += figure.colors.length * 4; - if (figure.verticesPerRow !== undefined) { - dataView.setUint32(offset, figure.verticesPerRow, true); - offset += 4; - } - } - return buffer; - } - getIR() { - const dataView = this.view; - const kind = this.data[PatternInfo.#KIND]; - const hasBBox = !!this.data[PatternInfo.#HAS_BBOX]; - const hasBackground = !!this.data[PatternInfo.#HAS_BACKGROUND]; - const nCoord = dataView.getUint32(PatternInfo.#N_COORD, true); - const nColor = dataView.getUint32(PatternInfo.#N_COLOR, true); - const nStop = dataView.getUint32(PatternInfo.#N_STOP, true); - const nFigures = dataView.getUint32(PatternInfo.#N_FIGURES, true); - let offset = 20; - const coords = new Float32Array(this.buffer, offset, nCoord * 2); - offset += nCoord * 8; - const colors = new Uint8Array(this.buffer, offset, nColor * 3); - offset += nColor * 3; - const stops = []; - for (let i = 0; i < nStop; ++i) { - const p = dataView.getFloat32(offset, true); - offset += 4; - const rgb = dataView.getUint32(offset, true); - offset += 4; - stops.push([p, `#${rgb.toString(16).padStart(6, "0")}`]); - } - let bbox = null; - if (hasBBox) { - bbox = []; - for (let i = 0; i < 4; ++i) { - bbox.push(dataView.getFloat32(offset, true)); - offset += 4; - } - } - let background = null; - if (hasBackground) { - background = new Uint8Array(this.buffer, offset, 3); - offset += 3; - } - const figures = []; - for (let i = 0; i < nFigures; ++i) { - const type = dataView.getUint8(offset); - offset += 1; - offset = Math.ceil(offset / 4) * 4; - const coordsLength = dataView.getUint32(offset, true); - offset += 4; - const figureCoords = new Int32Array(this.buffer, offset, coordsLength); - offset += coordsLength * 4; - const colorsLength = dataView.getUint32(offset, true); - offset += 4; - const figureColors = new Int32Array(this.buffer, offset, colorsLength); - offset += colorsLength * 4; - const figure = { - type, - coords: figureCoords, - colors: figureColors - }; - if (type === MeshFigureType.LATTICE) { - figure.verticesPerRow = dataView.getUint32(offset, true); - offset += 4; - } - figures.push(figure); - } - if (kind === 1) { - return ["RadialAxial", "axial", bbox, stops, Array.from(coords.slice(0, 2)), Array.from(coords.slice(2, 4)), null, null]; - } - if (kind === 2) { - return ["RadialAxial", "radial", bbox, stops, [coords[0], coords[1]], [coords[3], coords[4]], coords[2], coords[5]]; - } - if (kind === 3) { - const shadingType = this.data[PatternInfo.#SHADING_TYPE]; - let bounds = null; - if (coords.length > 0) { - let minX = coords[0], - maxX = coords[0]; - let minY = coords[1], - maxY = coords[1]; - for (let i = 0; i < coords.length; i += 2) { - const x = coords[i], - y = coords[i + 1]; - minX = minX > x ? x : minX; - minY = minY > y ? y : minY; - maxX = maxX < x ? x : maxX; - maxY = maxY < y ? y : maxY; - } - bounds = [minX, minY, maxX, maxY]; - } - return ["Mesh", shadingType, coords, colors, figures, bounds, bbox, background]; - } - throw new Error(`Unsupported pattern kind: ${kind}`); - } -} -class FontPathInfo { - static write(path) { - let data; - let buffer; - if (FeatureTest.isFloat16ArraySupported) { - buffer = new ArrayBuffer(path.length * 2); - data = new Float16Array(buffer); - } else { - buffer = new ArrayBuffer(path.length * 4); - data = new Float32Array(buffer); - } - data.set(path); - return buffer; - } - #buffer; - constructor(buffer) { - this.#buffer = buffer; - } - get path() { - if (FeatureTest.isFloat16ArraySupported) { - return new Float16Array(this.#buffer); - } - return new Float32Array(this.#buffer); - } -} - -;// ./src/display/api_utils.js - -function getUrlProp(val) { - if (val instanceof URL) { - return val; - } - if (typeof val === "string") { - if (isNodeJS) { - if (/^[a-z][a-z0-9\-+.]+:/i.test(val)) { - return new URL(val); - } - const url = process.getBuiltinModule("url"); - return new URL(url.pathToFileURL(val)); - } - const url = URL.parse(val, window.location); - if (url) { - return url; - } - } - throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property."); -} -function getDataProp(val) { - if (isNodeJS && typeof Buffer !== "undefined" && val instanceof Buffer) { - throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`."); - } - if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) { - return val; - } - if (typeof val === "string") { - return stringToBytes(val); - } - if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) { - return new Uint8Array(val); - } - throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property."); -} -function getFactoryUrlProp(val) { - if (typeof val !== "string") { - return null; - } - if (val.endsWith("/")) { - return val; - } - throw new Error(`Invalid factory url: "${val}" must include trailing slash.`); -} -const isRefProxy = v => typeof v === "object" && Number.isInteger(v?.num) && v.num >= 0 && Number.isInteger(v?.gen) && v.gen >= 0; -const isNameProxy = v => typeof v === "object" && typeof v?.name === "string"; -const isValidExplicitDest = _isValidExplicitDest.bind(null, isRefProxy, isNameProxy); -class LoopbackPort { - #listeners = new Map(); - #deferred = Promise.resolve(); - postMessage(obj, transfer) { - const event = { - data: structuredClone(obj, transfer ? { - transfer - } : null) - }; - this.#deferred.then(() => { - for (const [listener] of this.#listeners) { - listener.call(this, event); - } - }); - } - addEventListener(name, listener, options = null) { - let rmAbort = null; - if (options?.signal instanceof AbortSignal) { - const { - signal - } = options; - if (signal.aborted) { - warn("LoopbackPort - cannot use an `aborted` signal."); - return; - } - const onAbort = () => this.removeEventListener(name, listener); - rmAbort = () => signal.removeEventListener("abort", onAbort); - signal.addEventListener("abort", onAbort); - } - this.#listeners.set(listener, rmAbort); - } - removeEventListener(name, listener) { - const rmAbort = this.#listeners.get(listener); - rmAbort?.(); - this.#listeners.delete(listener); - } - terminate() { - for (const [, rmAbort] of this.#listeners) { - rmAbort?.(); - } - this.#listeners.clear(); - } -} - -;// ./src/shared/message_handler.js - -const CallbackKind = { - DATA: 1, - ERROR: 2 -}; -const StreamKind = { - CANCEL: 1, - CANCEL_COMPLETE: 2, - CLOSE: 3, - ENQUEUE: 4, - ERROR: 5, - PULL: 6, - PULL_COMPLETE: 7, - START_COMPLETE: 8 -}; -function onFn() {} -function wrapReason(ex) { - if (ex instanceof AbortException || ex instanceof InvalidPDFException || ex instanceof PasswordException || ex instanceof ResponseException || ex instanceof UnknownErrorException) { - return ex; - } - if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) { - unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.'); - } - switch (ex.name) { - case "AbortException": - return new AbortException(ex.message); - case "InvalidPDFException": - return new InvalidPDFException(ex.message); - case "PasswordException": - return new PasswordException(ex.message, ex.code); - case "ResponseException": - return new ResponseException(ex.message, ex.status, ex.missing); - case "UnknownErrorException": - return new UnknownErrorException(ex.message, ex.details); - } - return new UnknownErrorException(ex.message, ex.toString()); -} -class MessageHandler { - #messageAC = new AbortController(); - constructor(sourceName, targetName, comObj) { - this.sourceName = sourceName; - this.targetName = targetName; - this.comObj = comObj; - this.callbackId = 1; - this.streamId = 1; - this.streamSinks = Object.create(null); - this.streamControllers = Object.create(null); - this.callbackCapabilities = Object.create(null); - this.actionHandler = Object.create(null); - comObj.addEventListener("message", this.#onMessage.bind(this), { - signal: this.#messageAC.signal - }); - } - #onMessage({ - data - }) { - if (data.targetName !== this.sourceName) { - return; - } - if (data.stream) { - this.#processStreamMessage(data); - return; - } - if (data.callback) { - const callbackId = data.callbackId; - const capability = this.callbackCapabilities[callbackId]; - if (!capability) { - throw new Error(`Cannot resolve callback ${callbackId}`); - } - delete this.callbackCapabilities[callbackId]; - if (data.callback === CallbackKind.DATA) { - capability.resolve(data.data); - } else if (data.callback === CallbackKind.ERROR) { - capability.reject(wrapReason(data.reason)); - } else { - throw new Error("Unexpected callback case"); - } - return; - } - const action = this.actionHandler[data.action]; - if (!action) { - throw new Error(`Unknown action from worker: ${data.action}`); - } - if (data.callbackId) { - const sourceName = this.sourceName, - targetName = data.sourceName, - comObj = this.comObj; - Promise.try(action, data.data).then(function (result) { - comObj.postMessage({ - sourceName, - targetName, - callback: CallbackKind.DATA, - callbackId: data.callbackId, - data: result - }); - }, function (reason) { - comObj.postMessage({ - sourceName, - targetName, - callback: CallbackKind.ERROR, - callbackId: data.callbackId, - reason: wrapReason(reason) - }); - }); - return; - } - if (data.streamId) { - this.#createStreamSink(data); - return; - } - action(data.data); - } - on(actionName, handler) { - const ah = this.actionHandler; - if (ah[actionName]) { - throw new Error(`There is already an actionName called "${actionName}"`); - } - ah[actionName] = handler; - } - send(actionName, data, transfers) { - this.comObj.postMessage({ - sourceName: this.sourceName, - targetName: this.targetName, - action: actionName, - data - }, transfers); - } - sendWithPromise(actionName, data, transfers) { - const callbackId = this.callbackId++; - const capability = Promise.withResolvers(); - this.callbackCapabilities[callbackId] = capability; - try { - this.comObj.postMessage({ - sourceName: this.sourceName, - targetName: this.targetName, - action: actionName, - callbackId, - data - }, transfers); - } catch (ex) { - capability.reject(ex); - } - return capability.promise; - } - sendWithStream(actionName, data, queueingStrategy, transfers) { - const streamId = this.streamId++, - sourceName = this.sourceName, - targetName = this.targetName, - comObj = this.comObj; - return new ReadableStream({ - start: controller => { - const startCapability = Promise.withResolvers(); - this.streamControllers[streamId] = { - controller, - startCall: startCapability, - pullCall: null, - cancelCall: null, - isClosed: false - }; - comObj.postMessage({ - sourceName, - targetName, - action: actionName, - streamId, - data, - desiredSize: controller.desiredSize - }, transfers); - return startCapability.promise; - }, - pull: controller => { - const pullCapability = Promise.withResolvers(); - this.streamControllers[streamId].pullCall = pullCapability; - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.PULL, - streamId, - desiredSize: controller.desiredSize - }); - return pullCapability.promise; - }, - cancel: reason => { - assert(reason instanceof Error, "cancel must have a valid reason"); - const cancelCapability = Promise.withResolvers(); - this.streamControllers[streamId].cancelCall = cancelCapability; - this.streamControllers[streamId].isClosed = true; - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.CANCEL, - streamId, - reason: wrapReason(reason) - }); - return cancelCapability.promise; - } - }, queueingStrategy); - } - #createStreamSink(data) { - const streamId = data.streamId, - sourceName = this.sourceName, - targetName = data.sourceName, - comObj = this.comObj; - const self = this, - action = this.actionHandler[data.action]; - const streamSink = { - enqueue(chunk, size = 1, transfers) { - if (this.isCancelled) { - return; - } - const lastDesiredSize = this.desiredSize; - this.desiredSize -= size; - if (lastDesiredSize > 0 && this.desiredSize <= 0) { - this.sinkCapability = Promise.withResolvers(); - this.ready = this.sinkCapability.promise; - } - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.ENQUEUE, - streamId, - chunk - }, transfers); - }, - close() { - if (this.isCancelled) { - return; - } - this.isCancelled = true; - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.CLOSE, - streamId - }); - delete self.streamSinks[streamId]; - }, - error(reason) { - assert(reason instanceof Error, "error must have a valid reason"); - if (this.isCancelled) { - return; - } - this.isCancelled = true; - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.ERROR, - streamId, - reason: wrapReason(reason) - }); - }, - sinkCapability: Promise.withResolvers(), - onPull: null, - onCancel: null, - isCancelled: false, - desiredSize: data.desiredSize, - ready: null - }; - streamSink.sinkCapability.resolve(); - streamSink.ready = streamSink.sinkCapability.promise; - this.streamSinks[streamId] = streamSink; - Promise.try(action, data.data, streamSink).then(function () { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.START_COMPLETE, - streamId, - success: true - }); - }, function (reason) { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.START_COMPLETE, - streamId, - reason: wrapReason(reason) - }); - }); - } - #processStreamMessage(data) { - const streamId = data.streamId, - sourceName = this.sourceName, - targetName = data.sourceName, - comObj = this.comObj; - const streamController = this.streamControllers[streamId], - streamSink = this.streamSinks[streamId]; - switch (data.stream) { - case StreamKind.START_COMPLETE: - if (data.success) { - streamController.startCall.resolve(); - } else { - streamController.startCall.reject(wrapReason(data.reason)); - } - break; - case StreamKind.PULL_COMPLETE: - if (data.success) { - streamController.pullCall.resolve(); - } else { - streamController.pullCall.reject(wrapReason(data.reason)); - } - break; - case StreamKind.PULL: - if (!streamSink) { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.PULL_COMPLETE, - streamId, - success: true - }); - break; - } - if (streamSink.desiredSize <= 0 && data.desiredSize > 0) { - streamSink.sinkCapability.resolve(); - } - streamSink.desiredSize = data.desiredSize; - Promise.try(streamSink.onPull || onFn).then(function () { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.PULL_COMPLETE, - streamId, - success: true - }); - }, function (reason) { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.PULL_COMPLETE, - streamId, - reason: wrapReason(reason) - }); - }); - break; - case StreamKind.ENQUEUE: - assert(streamController, "enqueue should have stream controller"); - if (streamController.isClosed) { - break; - } - streamController.controller.enqueue(data.chunk); - break; - case StreamKind.CLOSE: - assert(streamController, "close should have stream controller"); - if (streamController.isClosed) { - break; - } - streamController.isClosed = true; - streamController.controller.close(); - this.#deleteStreamController(streamController, streamId); - break; - case StreamKind.ERROR: - assert(streamController, "error should have stream controller"); - streamController.controller.error(wrapReason(data.reason)); - this.#deleteStreamController(streamController, streamId); - break; - case StreamKind.CANCEL_COMPLETE: - if (data.success) { - streamController.cancelCall.resolve(); - } else { - streamController.cancelCall.reject(wrapReason(data.reason)); - } - this.#deleteStreamController(streamController, streamId); - break; - case StreamKind.CANCEL: - if (!streamSink) { - break; - } - const dataReason = wrapReason(data.reason); - Promise.try(streamSink.onCancel || onFn, dataReason).then(function () { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.CANCEL_COMPLETE, - streamId, - success: true - }); - }, function (reason) { - comObj.postMessage({ - sourceName, - targetName, - stream: StreamKind.CANCEL_COMPLETE, - streamId, - reason: wrapReason(reason) - }); - }); - streamSink.sinkCapability.reject(dataReason); - streamSink.isCancelled = true; - delete this.streamSinks[streamId]; - break; - default: - throw new Error("Unexpected stream case"); - } - } - async #deleteStreamController(streamController, streamId) { - await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]); - delete this.streamControllers[streamId]; - } - destroy() { - this.#messageAC?.abort(); - this.#messageAC = null; - } -} - -;// ./src/display/canvas_factory.js - -class BaseCanvasFactory { - #enableHWA = false; - constructor({ - enableHWA = false - }) { - this.#enableHWA = enableHWA; - } - create(width, height) { - if (width <= 0 || height <= 0) { - throw new Error("Invalid canvas size"); - } - const canvas = this._createCanvas(width, height); - return { - canvas, - context: canvas.getContext("2d", { - willReadFrequently: !this.#enableHWA - }) - }; - } - reset(canvasAndContext, width, height) { - if (!canvasAndContext.canvas) { - throw new Error("Canvas is not specified"); - } - if (width <= 0 || height <= 0) { - throw new Error("Invalid canvas size"); - } - canvasAndContext.canvas.width = width; - canvasAndContext.canvas.height = height; - } - destroy(canvasAndContext) { - if (!canvasAndContext.canvas) { - throw new Error("Canvas is not specified"); - } - canvasAndContext.canvas.width = 0; - canvasAndContext.canvas.height = 0; - canvasAndContext.canvas = null; - canvasAndContext.context = null; - } - _createCanvas(width, height) { - unreachable("Abstract method `_createCanvas` called."); - } -} -class DOMCanvasFactory extends BaseCanvasFactory { - constructor({ - ownerDocument = globalThis.document, - enableHWA = false - }) { - super({ - enableHWA - }); - this._document = ownerDocument; - } - _createCanvas(width, height) { - const canvas = this._document.createElement("canvas"); - canvas.width = width; - canvas.height = height; - return canvas; - } -} - -;// ./src/display/cmap_reader_factory.js - - -class BaseCMapReaderFactory { - constructor({ - baseUrl = null, - isCompressed = true - }) { - this.baseUrl = baseUrl; - this.isCompressed = isCompressed; - } - async fetch({ - name - }) { - if (!this.baseUrl) { - throw new Error("Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided."); - } - if (!name) { - throw new Error("CMap name must be specified."); - } - const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : ""); - return this._fetch(url).then(cMapData => ({ - cMapData, - isCompressed: this.isCompressed - })).catch(reason => { - throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`); - }); - } - async _fetch(url) { - unreachable("Abstract method `_fetch` called."); - } -} -class DOMCMapReaderFactory extends BaseCMapReaderFactory { - async _fetch(url) { - const data = await fetchData(url, this.isCompressed ? "bytes" : "text"); - return data instanceof Uint8Array ? data : stringToBytes(data); - } -} - -;// ./src/display/filter_factory.js - - -class BaseFilterFactory { - addFilter(maps) { - return "none"; - } - addHCMFilter(fgColor, bgColor) { - return "none"; - } - addAlphaFilter(map) { - return "none"; - } - addLuminosityFilter(map) { - return "none"; - } - addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { - return "none"; - } - destroy(keepHCM = false) {} -} -class DOMFilterFactory extends BaseFilterFactory { - #baseUrl; - #_cache; - #_defs; - #docId; - #document; - #_hcmCache; - #id = 0; - constructor({ - docId, - ownerDocument = globalThis.document - }) { - super(); - this.#docId = docId; - this.#document = ownerDocument; - } - get #cache() { - return this.#_cache ||= new Map(); - } - get #hcmCache() { - return this.#_hcmCache ||= new Map(); - } - get #defs() { - if (!this.#_defs) { - const div = this.#document.createElement("div"); - const { - style - } = div; - style.visibility = "hidden"; - style.contain = "strict"; - style.width = style.height = 0; - style.position = "absolute"; - style.top = style.left = 0; - style.zIndex = -1; - const svg = this.#document.createElementNS(SVG_NS, "svg"); - svg.setAttribute("width", 0); - svg.setAttribute("height", 0); - this.#_defs = this.#document.createElementNS(SVG_NS, "defs"); - div.append(svg); - svg.append(this.#_defs); - this.#document.body.append(div); - } - return this.#_defs; - } - #createTables(maps) { - if (maps.length === 1) { - const mapR = maps[0]; - const buffer = new Array(256); - for (let i = 0; i < 256; i++) { - buffer[i] = mapR[i] / 255; - } - const table = buffer.join(","); - return [table, table, table]; - } - const [mapR, mapG, mapB] = maps; - const bufferR = new Array(256); - const bufferG = new Array(256); - const bufferB = new Array(256); - for (let i = 0; i < 256; i++) { - bufferR[i] = mapR[i] / 255; - bufferG[i] = mapG[i] / 255; - bufferB[i] = mapB[i] / 255; - } - return [bufferR.join(","), bufferG.join(","), bufferB.join(",")]; - } - #createUrl(id) { - if (this.#baseUrl === undefined) { - this.#baseUrl = ""; - const url = this.#document.URL; - if (url !== this.#document.baseURI) { - if (isDataScheme(url)) { - warn('#createUrl: ignore "data:"-URL for performance reasons.'); - } else { - this.#baseUrl = updateUrlHash(url, ""); - } - } - } - return `url(${this.#baseUrl}#${id})`; - } - addFilter(maps) { - if (!maps) { - return "none"; - } - let value = this.#cache.get(maps); - if (value) { - return value; - } - const [tableR, tableG, tableB] = this.#createTables(maps); - const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`; - value = this.#cache.get(key); - if (value) { - this.#cache.set(maps, value); - return value; - } - const id = `g_${this.#docId}_transfer_map_${this.#id++}`; - const url = this.#createUrl(id); - this.#cache.set(maps, url); - this.#cache.set(key, url); - const filter = this.#createFilter(id); - this.#addTransferMapConversion(tableR, tableG, tableB, filter); - return url; - } - addHCMFilter(fgColor, bgColor) { - const key = `${fgColor}-${bgColor}`; - const filterName = "base"; - let info = this.#hcmCache.get(filterName); - if (info?.key === key) { - return info.url; - } - if (info) { - info.filter?.remove(); - info.key = key; - info.url = "none"; - info.filter = null; - } else { - info = { - key, - url: "none", - filter: null - }; - this.#hcmCache.set(filterName, info); - } - if (!fgColor || !bgColor) { - return info.url; - } - const fgRGB = this.#getRGB(fgColor); - fgColor = Util.makeHexColor(...fgRGB); - const bgRGB = this.#getRGB(bgColor); - bgColor = Util.makeHexColor(...bgRGB); - this.#defs.style.color = ""; - if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) { - return info.url; - } - const map = new Array(256); - for (let i = 0; i <= 255; i++) { - const x = i / 255; - map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; - } - const table = map.join(","); - const id = `g_${this.#docId}_hcm_filter`; - const filter = info.filter = this.#createFilter(id); - this.#addTransferMapConversion(table, table, table, filter); - this.#addGrayConversion(filter); - const getSteps = (c, n) => { - const start = fgRGB[c] / 255; - const end = bgRGB[c] / 255; - const arr = new Array(n + 1); - for (let i = 0; i <= n; i++) { - arr[i] = start + i / n * (end - start); - } - return arr.join(","); - }; - this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter); - info.url = this.#createUrl(id); - return info.url; - } - addAlphaFilter(map) { - let value = this.#cache.get(map); - if (value) { - return value; - } - const [tableA] = this.#createTables([map]); - const key = `alpha_${tableA}`; - value = this.#cache.get(key); - if (value) { - this.#cache.set(map, value); - return value; - } - const id = `g_${this.#docId}_alpha_map_${this.#id++}`; - const url = this.#createUrl(id); - this.#cache.set(map, url); - this.#cache.set(key, url); - const filter = this.#createFilter(id); - this.#addTransferMapAlphaConversion(tableA, filter); - return url; - } - addLuminosityFilter(map) { - let value = this.#cache.get(map || "luminosity"); - if (value) { - return value; - } - let tableA, key; - if (map) { - [tableA] = this.#createTables([map]); - key = `luminosity_${tableA}`; - } else { - key = "luminosity"; - } - value = this.#cache.get(key); - if (value) { - this.#cache.set(map, value); - return value; - } - const id = `g_${this.#docId}_luminosity_map_${this.#id++}`; - const url = this.#createUrl(id); - this.#cache.set(map, url); - this.#cache.set(key, url); - const filter = this.#createFilter(id); - this.#addLuminosityConversion(filter); - if (map) { - this.#addTransferMapAlphaConversion(tableA, filter); - } - return url; - } - addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { - const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`; - let info = this.#hcmCache.get(filterName); - if (info?.key === key) { - return info.url; - } - if (info) { - info.filter?.remove(); - info.key = key; - info.url = "none"; - info.filter = null; - } else { - info = { - key, - url: "none", - filter: null - }; - this.#hcmCache.set(filterName, info); - } - if (!fgColor || !bgColor) { - return info.url; - } - const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this)); - let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]); - let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]); - let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this)); - if (bgGray < fgGray) { - [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB]; - } - this.#defs.style.color = ""; - const getSteps = (fg, bg, n) => { - const arr = new Array(256); - const step = (bgGray - fgGray) / n; - const newStart = fg / 255; - const newStep = (bg - fg) / (255 * n); - let prev = 0; - for (let i = 0; i <= n; i++) { - const k = Math.round(fgGray + i * step); - const value = newStart + i * newStep; - for (let j = prev; j <= k; j++) { - arr[j] = value; - } - prev = k + 1; - } - for (let i = prev; i < 256; i++) { - arr[i] = arr[prev - 1]; - } - return arr.join(","); - }; - const id = `g_${this.#docId}_hcm_${filterName}_filter`; - const filter = info.filter = this.#createFilter(id); - this.#addGrayConversion(filter); - this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter); - info.url = this.#createUrl(id); - return info.url; - } - destroy(keepHCM = false) { - if (keepHCM && this.#_hcmCache?.size) { - return; - } - this.#_defs?.parentNode.parentNode.remove(); - this.#_defs = null; - this.#_cache?.clear(); - this.#_cache = null; - this.#_hcmCache?.clear(); - this.#_hcmCache = null; - this.#id = 0; - } - #addLuminosityConversion(filter) { - const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute("values", "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0"); - filter.append(feColorMatrix); - } - #addGrayConversion(filter) { - const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); - feColorMatrix.setAttribute("type", "matrix"); - feColorMatrix.setAttribute("values", "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"); - filter.append(feColorMatrix); - } - #createFilter(id) { - const filter = this.#document.createElementNS(SVG_NS, "filter"); - filter.setAttribute("color-interpolation-filters", "sRGB"); - filter.setAttribute("id", id); - this.#defs.append(filter); - return filter; - } - #appendFeFunc(feComponentTransfer, func, table) { - const feFunc = this.#document.createElementNS(SVG_NS, func); - feFunc.setAttribute("type", "discrete"); - feFunc.setAttribute("tableValues", table); - feComponentTransfer.append(feFunc); - } - #addTransferMapConversion(rTable, gTable, bTable, filter) { - const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); - filter.append(feComponentTransfer); - this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable); - this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable); - this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable); - } - #addTransferMapAlphaConversion(aTable, filter) { - const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); - filter.append(feComponentTransfer); - this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable); - } - #getRGB(color) { - this.#defs.style.color = color; - return getRGB(getComputedStyle(this.#defs).getPropertyValue("color")); - } -} - -;// ./src/display/standard_fontdata_factory.js - - -class BaseStandardFontDataFactory { - constructor({ - baseUrl = null - }) { - this.baseUrl = baseUrl; - } - async fetch({ - filename - }) { - if (!this.baseUrl) { - throw new Error("Ensure that the `standardFontDataUrl` API parameter is provided."); - } - if (!filename) { - throw new Error("Font filename must be specified."); - } - const url = `${this.baseUrl}${filename}`; - return this._fetch(url).catch(reason => { - throw new Error(`Unable to load font data at: ${url}`); - }); - } - async _fetch(url) { - unreachable("Abstract method `_fetch` called."); - } -} -class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { - async _fetch(url) { - return fetchData(url, "bytes"); - } -} - -;// ./src/display/wasm_factory.js - - -class BaseWasmFactory { - constructor({ - baseUrl = null - }) { - this.baseUrl = baseUrl; - } - async fetch({ - filename - }) { - if (!this.baseUrl) { - throw new Error("Ensure that the `wasmUrl` API parameter is provided."); - } - if (!filename) { - throw new Error("Wasm filename must be specified."); - } - const url = `${this.baseUrl}${filename}`; - return this._fetch(url).catch(reason => { - throw new Error(`Unable to load wasm data at: ${url}`); - }); - } - async _fetch(url) { - unreachable("Abstract method `_fetch` called."); - } -} -class DOMWasmFactory extends BaseWasmFactory { - async _fetch(url) { - return fetchData(url, "bytes"); - } -} - -;// ./src/display/node_utils.js - - - - - - -if (isNodeJS) { - warn("Please use the `legacy` build in Node.js environments."); -} -async function node_utils_fetchData(url) { - const fs = process.getBuiltinModule("fs"); - const data = await fs.promises.readFile(url); - return new Uint8Array(data); -} -class NodeFilterFactory extends BaseFilterFactory {} -class NodeCanvasFactory extends BaseCanvasFactory { - _createCanvas(width, height) { - const require = process.getBuiltinModule("module").createRequire(import.meta.url); - const canvas = require("@napi-rs/canvas"); - return canvas.createCanvas(width, height); - } -} -class NodeCMapReaderFactory extends BaseCMapReaderFactory { - async _fetch(url) { - return node_utils_fetchData(url); - } -} -class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { - async _fetch(url) { - return node_utils_fetchData(url); - } -} -class NodeWasmFactory extends BaseWasmFactory { - async _fetch(url) { - return node_utils_fetchData(url); - } -} - -;// ./src/display/canvas_dependency_tracker.js - -const FORCED_DEPENDENCY_LABEL = "__forcedDependency"; -const { - floor, - ceil -} = Math; -function expandBBox(array, index, minX, minY, maxX, maxY) { - array[index * 4 + 0] = Math.min(array[index * 4 + 0], minX); - array[index * 4 + 1] = Math.min(array[index * 4 + 1], minY); - array[index * 4 + 2] = Math.max(array[index * 4 + 2], maxX); - array[index * 4 + 3] = Math.max(array[index * 4 + 3], maxY); -} -const EMPTY_BBOX = new Uint32Array(new Uint8Array([255, 255, 0, 0]).buffer)[0]; -class BBoxReader { - #bboxes; - #coords; - constructor(bboxes, coords) { - this.#bboxes = bboxes; - this.#coords = coords; - } - get length() { - return this.#bboxes.length; - } - isEmpty(i) { - return this.#bboxes[i] === EMPTY_BBOX; - } - minX(i) { - return this.#coords[i * 4 + 0] / 256; - } - minY(i) { - return this.#coords[i * 4 + 1] / 256; - } - maxX(i) { - return (this.#coords[i * 4 + 2] + 1) / 256; - } - maxY(i) { - return (this.#coords[i * 4 + 3] + 1) / 256; - } -} -const ensureDebugMetadata = (map, key) => map?.getOrInsertComputed(key, () => ({ - dependencies: new Set(), - isRenderingOperation: false -})); -class CanvasDependencyTracker { - #simple = { - __proto__: null - }; - #incremental = { - __proto__: null, - transform: [], - moveText: [], - sameLineText: [], - [FORCED_DEPENDENCY_LABEL]: [] - }; - #namedDependencies = new Map(); - #savesStack = []; - #markedContentStack = []; - #baseTransformStack = [[1, 0, 0, 1, 0, 0]]; - #clipBox = [-Infinity, -Infinity, Infinity, Infinity]; - #pendingBBox = new Float64Array([Infinity, Infinity, -Infinity, -Infinity]); - #pendingBBoxIdx = -1; - #pendingDependencies = new Set(); - #operations = new Map(); - #fontBBoxTrustworthy = new Map(); - #canvasWidth; - #canvasHeight; - #bboxesCoords; - #bboxes; - #debugMetadata; - constructor(canvas, operationsCount, recordDebugMetadata = false) { - this.#canvasWidth = canvas.width; - this.#canvasHeight = canvas.height; - this.#initializeBBoxes(operationsCount); - if (recordDebugMetadata) { - this.#debugMetadata = new Map(); - } - } - growOperationsCount(operationsCount) { - if (operationsCount >= this.#bboxes.length) { - this.#initializeBBoxes(operationsCount, this.#bboxes); - } - } - #initializeBBoxes(operationsCount, oldBBoxes) { - const buffer = new ArrayBuffer(operationsCount * 4); - this.#bboxesCoords = new Uint8ClampedArray(buffer); - this.#bboxes = new Uint32Array(buffer); - if (oldBBoxes && oldBBoxes.length > 0) { - this.#bboxes.set(oldBBoxes); - this.#bboxes.fill(EMPTY_BBOX, oldBBoxes.length); - } else { - this.#bboxes.fill(EMPTY_BBOX); - } - } - save(opIdx) { - this.#simple = { - __proto__: this.#simple - }; - this.#incremental = { - __proto__: this.#incremental, - transform: { - __proto__: this.#incremental.transform - }, - moveText: { - __proto__: this.#incremental.moveText - }, - sameLineText: { - __proto__: this.#incremental.sameLineText - }, - [FORCED_DEPENDENCY_LABEL]: { - __proto__: this.#incremental[FORCED_DEPENDENCY_LABEL] - } - }; - this.#clipBox = { - __proto__: this.#clipBox - }; - this.#savesStack.push(opIdx); - return this; - } - restore(opIdx) { - const previous = Object.getPrototypeOf(this.#simple); - if (previous === null) { - return this; - } - this.#simple = previous; - this.#incremental = Object.getPrototypeOf(this.#incremental); - this.#clipBox = Object.getPrototypeOf(this.#clipBox); - const lastSave = this.#savesStack.pop(); - if (lastSave !== undefined) { - ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave); - this.#bboxes[opIdx] = this.#bboxes[lastSave]; - } - return this; - } - recordOpenMarker(idx) { - this.#savesStack.push(idx); - return this; - } - getOpenMarker() { - if (this.#savesStack.length === 0) { - return null; - } - return this.#savesStack.at(-1); - } - recordCloseMarker(opIdx) { - const lastSave = this.#savesStack.pop(); - if (lastSave !== undefined) { - ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave); - this.#bboxes[opIdx] = this.#bboxes[lastSave]; - } - return this; - } - beginMarkedContent(opIdx) { - this.#markedContentStack.push(opIdx); - return this; - } - endMarkedContent(opIdx) { - const lastSave = this.#markedContentStack.pop(); - if (lastSave !== undefined) { - ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave); - this.#bboxes[opIdx] = this.#bboxes[lastSave]; - } - return this; - } - pushBaseTransform(ctx) { - this.#baseTransformStack.push(Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform())); - return this; - } - popBaseTransform() { - if (this.#baseTransformStack.length > 1) { - this.#baseTransformStack.pop(); - } - return this; - } - recordSimpleData(name, idx) { - this.#simple[name] = idx; - return this; - } - recordIncrementalData(name, idx) { - this.#incremental[name].push(idx); - return this; - } - resetIncrementalData(name, idx) { - this.#incremental[name].length = 0; - return this; - } - recordNamedData(name, idx) { - this.#namedDependencies.set(name, idx); - return this; - } - recordSimpleDataFromNamed(name, depName, fallbackIdx) { - this.#simple[name] = this.#namedDependencies.get(depName) ?? fallbackIdx; - } - recordFutureForcedDependency(name, idx) { - this.recordIncrementalData(FORCED_DEPENDENCY_LABEL, idx); - return this; - } - inheritSimpleDataAsFutureForcedDependencies(names) { - for (const name of names) { - if (name in this.#simple) { - this.recordFutureForcedDependency(name, this.#simple[name]); - } - } - return this; - } - inheritPendingDependenciesAsFutureForcedDependencies() { - for (const dep of this.#pendingDependencies) { - this.recordFutureForcedDependency(FORCED_DEPENDENCY_LABEL, dep); - } - return this; - } - resetBBox(idx) { - if (this.#pendingBBoxIdx !== idx) { - this.#pendingBBoxIdx = idx; - this.#pendingBBox[0] = Infinity; - this.#pendingBBox[1] = Infinity; - this.#pendingBBox[2] = -Infinity; - this.#pendingBBox[3] = -Infinity; - } - return this; - } - recordClipBox(idx, ctx, minX, maxX, minY, maxY) { - const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform()); - const clipBox = [Infinity, Infinity, -Infinity, -Infinity]; - Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, clipBox); - const intersection = Util.intersect(this.#clipBox, clipBox); - if (intersection) { - this.#clipBox[0] = intersection[0]; - this.#clipBox[1] = intersection[1]; - this.#clipBox[2] = intersection[2]; - this.#clipBox[3] = intersection[3]; - } else { - this.#clipBox[0] = this.#clipBox[1] = Infinity; - this.#clipBox[2] = this.#clipBox[3] = -Infinity; - } - return this; - } - recordBBox(idx, ctx, minX, maxX, minY, maxY) { - const clipBox = this.#clipBox; - if (clipBox[0] === Infinity) { - return this; - } - const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform()); - if (clipBox[0] === -Infinity) { - Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, this.#pendingBBox); - return this; - } - const bbox = [Infinity, Infinity, -Infinity, -Infinity]; - Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, bbox); - this.#pendingBBox[0] = Math.min(this.#pendingBBox[0], Math.max(bbox[0], clipBox[0])); - this.#pendingBBox[1] = Math.min(this.#pendingBBox[1], Math.max(bbox[1], clipBox[1])); - this.#pendingBBox[2] = Math.max(this.#pendingBBox[2], Math.min(bbox[2], clipBox[2])); - this.#pendingBBox[3] = Math.max(this.#pendingBBox[3], Math.min(bbox[3], clipBox[3])); - return this; - } - recordCharacterBBox(idx, ctx, font, scale = 1, x = 0, y = 0, getMeasure) { - const fontBBox = font.bbox; - let isBBoxTrustworthy; - let computedBBox; - if (fontBBox) { - isBBoxTrustworthy = fontBBox[2] !== fontBBox[0] && fontBBox[3] !== fontBBox[1] && this.#fontBBoxTrustworthy.get(font); - if (isBBoxTrustworthy !== false) { - computedBBox = [0, 0, 0, 0]; - Util.axialAlignedBoundingBox(fontBBox, font.fontMatrix, computedBBox); - if (scale !== 1 || x !== 0 || y !== 0) { - Util.scaleMinMax([scale, 0, 0, -scale, x, y], computedBBox); - } - if (isBBoxTrustworthy) { - return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]); - } - } - } - if (!getMeasure) { - return this.recordFullPageBBox(idx); - } - const measure = getMeasure(); - if (fontBBox && computedBBox && isBBoxTrustworthy === undefined) { - isBBoxTrustworthy = computedBBox[0] <= x - measure.actualBoundingBoxLeft && computedBBox[2] >= x + measure.actualBoundingBoxRight && computedBBox[1] <= y - measure.actualBoundingBoxAscent && computedBBox[3] >= y + measure.actualBoundingBoxDescent; - this.#fontBBoxTrustworthy.set(font, isBBoxTrustworthy); - if (isBBoxTrustworthy) { - return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]); - } - } - return this.recordBBox(idx, ctx, x - measure.actualBoundingBoxLeft, x + measure.actualBoundingBoxRight, y - measure.actualBoundingBoxAscent, y + measure.actualBoundingBoxDescent); - } - recordFullPageBBox(idx) { - this.#pendingBBox[0] = Math.max(0, this.#clipBox[0]); - this.#pendingBBox[1] = Math.max(0, this.#clipBox[1]); - this.#pendingBBox[2] = Math.min(this.#canvasWidth, this.#clipBox[2]); - this.#pendingBBox[3] = Math.min(this.#canvasHeight, this.#clipBox[3]); - return this; - } - getSimpleIndex(dependencyName) { - return this.#simple[dependencyName]; - } - recordDependencies(idx, dependencyNames) { - const pendingDependencies = this.#pendingDependencies; - const simple = this.#simple; - const incremental = this.#incremental; - for (const name of dependencyNames) { - if (name in this.#simple) { - pendingDependencies.add(simple[name]); - } else if (name in incremental) { - incremental[name].forEach(pendingDependencies.add, pendingDependencies); - } - } - return this; - } - recordNamedDependency(idx, name) { - if (this.#namedDependencies.has(name)) { - this.#pendingDependencies.add(this.#namedDependencies.get(name)); - } - return this; - } - recordOperation(idx, preserve = false) { - this.recordDependencies(idx, [FORCED_DEPENDENCY_LABEL]); - if (this.#debugMetadata) { - const metadata = ensureDebugMetadata(this.#debugMetadata, idx); - const { - dependencies - } = metadata; - this.#pendingDependencies.forEach(dependencies.add, dependencies); - this.#savesStack.forEach(dependencies.add, dependencies); - this.#markedContentStack.forEach(dependencies.add, dependencies); - dependencies.delete(idx); - metadata.isRenderingOperation = true; - } - if (this.#pendingBBoxIdx === idx) { - const minX = floor(this.#pendingBBox[0] * 256 / this.#canvasWidth); - const minY = floor(this.#pendingBBox[1] * 256 / this.#canvasHeight); - const maxX = ceil(this.#pendingBBox[2] * 256 / this.#canvasWidth); - const maxY = ceil(this.#pendingBBox[3] * 256 / this.#canvasHeight); - expandBBox(this.#bboxesCoords, idx, minX, minY, maxX, maxY); - for (const depIdx of this.#pendingDependencies) { - if (depIdx !== idx) { - expandBBox(this.#bboxesCoords, depIdx, minX, minY, maxX, maxY); - } - } - for (const saveIdx of this.#savesStack) { - if (saveIdx !== idx) { - expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY); - } - } - for (const saveIdx of this.#markedContentStack) { - if (saveIdx !== idx) { - expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY); - } - } - if (!preserve) { - this.#pendingDependencies.clear(); - this.#pendingBBoxIdx = -1; - } - } - return this; - } - recordShowTextOperation(idx, preserve = false) { - const deps = Array.from(this.#pendingDependencies); - this.recordOperation(idx, preserve); - this.recordIncrementalData("sameLineText", idx); - for (const dep of deps) { - this.recordIncrementalData("sameLineText", dep); - } - return this; - } - bboxToClipBoxDropOperation(idx, preserve = false) { - if (this.#pendingBBoxIdx === idx) { - this.#pendingBBoxIdx = -1; - this.#clipBox[0] = Math.max(this.#clipBox[0], this.#pendingBBox[0]); - this.#clipBox[1] = Math.max(this.#clipBox[1], this.#pendingBBox[1]); - this.#clipBox[2] = Math.min(this.#clipBox[2], this.#pendingBBox[2]); - this.#clipBox[3] = Math.min(this.#clipBox[3], this.#pendingBBox[3]); - if (!preserve) { - this.#pendingDependencies.clear(); - } - } - return this; - } - _takePendingDependencies() { - const pendingDependencies = this.#pendingDependencies; - this.#pendingDependencies = new Set(); - return pendingDependencies; - } - _extractOperation(idx) { - const operation = this.#operations.get(idx); - this.#operations.delete(idx); - return operation; - } - _pushPendingDependencies(dependencies) { - for (const dep of dependencies) { - this.#pendingDependencies.add(dep); - } - } - take() { - this.#fontBBoxTrustworthy.clear(); - return new BBoxReader(this.#bboxes, this.#bboxesCoords); - } - takeDebugMetadata() { - return this.#debugMetadata; - } -} -class CanvasNestedDependencyTracker { - #dependencyTracker; - #opIdx; - #ignoreBBoxes; - #nestingLevel = 0; - #savesLevel = 0; - constructor(dependencyTracker, opIdx, ignoreBBoxes) { - if (dependencyTracker instanceof CanvasNestedDependencyTracker && dependencyTracker.#ignoreBBoxes === !!ignoreBBoxes) { - return dependencyTracker; - } - this.#dependencyTracker = dependencyTracker; - this.#opIdx = opIdx; - this.#ignoreBBoxes = !!ignoreBBoxes; - } - growOperationsCount() { - throw new Error("Unreachable"); - } - save(opIdx) { - this.#savesLevel++; - this.#dependencyTracker.save(this.#opIdx); - return this; - } - restore(opIdx) { - if (this.#savesLevel > 0) { - this.#dependencyTracker.restore(this.#opIdx); - this.#savesLevel--; - } - return this; - } - recordOpenMarker(idx) { - this.#nestingLevel++; - return this; - } - getOpenMarker() { - return this.#nestingLevel > 0 ? this.#opIdx : this.#dependencyTracker.getOpenMarker(); - } - recordCloseMarker(idx) { - this.#nestingLevel--; - return this; - } - beginMarkedContent(opIdx) { - return this; - } - endMarkedContent(opIdx) { - return this; - } - pushBaseTransform(ctx) { - this.#dependencyTracker.pushBaseTransform(ctx); - return this; - } - popBaseTransform() { - this.#dependencyTracker.popBaseTransform(); - return this; - } - recordSimpleData(name, idx) { - this.#dependencyTracker.recordSimpleData(name, this.#opIdx); - return this; - } - recordIncrementalData(name, idx) { - this.#dependencyTracker.recordIncrementalData(name, this.#opIdx); - return this; - } - resetIncrementalData(name, idx) { - this.#dependencyTracker.resetIncrementalData(name, this.#opIdx); - return this; - } - recordNamedData(name, idx) { - return this; - } - recordSimpleDataFromNamed(name, depName, fallbackIdx) { - this.#dependencyTracker.recordSimpleDataFromNamed(name, depName, this.#opIdx); - return this; - } - recordFutureForcedDependency(name, idx) { - this.#dependencyTracker.recordFutureForcedDependency(name, this.#opIdx); - return this; - } - inheritSimpleDataAsFutureForcedDependencies(names) { - this.#dependencyTracker.inheritSimpleDataAsFutureForcedDependencies(names); - return this; - } - inheritPendingDependenciesAsFutureForcedDependencies() { - this.#dependencyTracker.inheritPendingDependenciesAsFutureForcedDependencies(); - return this; - } - resetBBox(idx) { - if (!this.#ignoreBBoxes) { - this.#dependencyTracker.resetBBox(this.#opIdx); - } - return this; - } - recordClipBox(idx, ctx, minX, maxX, minY, maxY) { - if (!this.#ignoreBBoxes) { - this.#dependencyTracker.recordClipBox(this.#opIdx, ctx, minX, maxX, minY, maxY); - } - return this; - } - recordBBox(idx, ctx, minX, maxX, minY, maxY) { - if (!this.#ignoreBBoxes) { - this.#dependencyTracker.recordBBox(this.#opIdx, ctx, minX, maxX, minY, maxY); - } - return this; - } - recordCharacterBBox(idx, ctx, font, scale, x, y, getMeasure) { - if (!this.#ignoreBBoxes) { - this.#dependencyTracker.recordCharacterBBox(this.#opIdx, ctx, font, scale, x, y, getMeasure); - } - return this; - } - recordFullPageBBox(idx) { - if (!this.#ignoreBBoxes) { - this.#dependencyTracker.recordFullPageBBox(this.#opIdx); - } - return this; - } - getSimpleIndex(dependencyName) { - return this.#dependencyTracker.getSimpleIndex(dependencyName); - } - recordDependencies(idx, dependencyNames) { - this.#dependencyTracker.recordDependencies(this.#opIdx, dependencyNames); - return this; - } - recordNamedDependency(idx, name) { - this.#dependencyTracker.recordNamedDependency(this.#opIdx, name); - return this; - } - recordOperation(idx) { - this.#dependencyTracker.recordOperation(this.#opIdx, true); - return this; - } - recordShowTextOperation(idx) { - this.#dependencyTracker.recordShowTextOperation(this.#opIdx, true); - return this; - } - bboxToClipBoxDropOperation(idx) { - if (!this.#ignoreBBoxes) { - this.#dependencyTracker.bboxToClipBoxDropOperation(this.#opIdx, true); - } - return this; - } - take() { - throw new Error("Unreachable"); - } - takeDebugMetadata() { - throw new Error("Unreachable"); - } -} -const Dependencies = { - stroke: ["path", "transform", "filter", "strokeColor", "strokeAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "dash"], - fill: ["path", "transform", "filter", "fillColor", "fillAlpha", "globalCompositeOperation", "SMask"], - imageXObject: ["transform", "SMask", "filter", "fillAlpha", "strokeAlpha", "globalCompositeOperation"], - rawFillPath: ["filter", "fillColor", "fillAlpha"], - showText: ["transform", "leading", "charSpacing", "wordSpacing", "hScale", "textRise", "moveText", "textMatrix", "font", "fontObj", "filter", "fillColor", "textRenderingMode", "SMask", "fillAlpha", "strokeAlpha", "globalCompositeOperation", "sameLineText"], - transform: ["transform"], - transformAndFill: ["transform", "fillColor"] -}; - -;// ./src/display/pattern_helper.js - - -const PathType = { - FILL: "Fill", - STROKE: "Stroke", - SHADING: "Shading" -}; -function applyBoundingBox(ctx, bbox) { - if (!bbox) { - return; - } - const width = bbox[2] - bbox[0]; - const height = bbox[3] - bbox[1]; - const region = new Path2D(); - region.rect(bbox[0], bbox[1], width, height); - ctx.clip(region); -} -class BaseShadingPattern { - isModifyingCurrentTransform() { - return false; - } - getPattern() { - unreachable("Abstract method `getPattern` called."); - } -} -class RadialAxialShadingPattern extends BaseShadingPattern { - constructor(IR) { - super(); - this._type = IR[1]; - this._bbox = IR[2]; - this._colorStops = IR[3]; - this._p0 = IR[4]; - this._p1 = IR[5]; - this._r0 = IR[6]; - this._r1 = IR[7]; - this.matrix = null; - } - isOriginBased() { - return this._p0[0] === 0 && this._p0[1] === 0 && (!this.isRadial() || this._p1[0] === 0 && this._p1[1] === 0); - } - isRadial() { - return this._type === "radial"; - } - _createGradient(ctx, transform = null) { - let grad; - let firstPoint = this._p0; - let secondPoint = this._p1; - if (transform) { - firstPoint = firstPoint.slice(); - secondPoint = secondPoint.slice(); - Util.applyTransform(firstPoint, transform); - Util.applyTransform(secondPoint, transform); - } - if (this._type === "axial") { - grad = ctx.createLinearGradient(firstPoint[0], firstPoint[1], secondPoint[0], secondPoint[1]); - } else if (this._type === "radial") { - let r0 = this._r0; - let r1 = this._r1; - if (transform) { - const scale = new Float32Array(2); - Util.singularValueDecompose2dScale(transform, scale); - r0 *= scale[0]; - r1 *= scale[0]; - } - grad = ctx.createRadialGradient(firstPoint[0], firstPoint[1], r0, secondPoint[0], secondPoint[1], r1); - } - for (const colorStop of this._colorStops) { - grad.addColorStop(colorStop[0], colorStop[1]); - } - return grad; - } - getPattern(ctx, owner, inverse, pathType) { - let pattern; - if (pathType === PathType.STROKE || pathType === PathType.FILL) { - if (this.isOriginBased()) { - let transf = Util.transform(inverse, owner.baseTransform); - if (this.matrix) { - transf = Util.transform(transf, this.matrix); - } - const precision = 1e-3; - const n1 = Math.hypot(transf[0], transf[1]); - const n2 = Math.hypot(transf[2], transf[3]); - const ps = (transf[0] * transf[2] + transf[1] * transf[3]) / (n1 * n2); - if (Math.abs(ps) < precision) { - if (this.isRadial()) { - if (Math.abs(n1 - n2) < precision) { - return this._createGradient(ctx, transf); - } - } else { - return this._createGradient(ctx, transf); - } - } - } - const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0]; - const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1; - const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1; - const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height); - const tmpCtx = tmpCanvas.context; - tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); - tmpCtx.beginPath(); - tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); - tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]); - inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]); - tmpCtx.transform(...owner.baseTransform); - if (this.matrix) { - tmpCtx.transform(...this.matrix); - } - applyBoundingBox(tmpCtx, this._bbox); - tmpCtx.fillStyle = this._createGradient(tmpCtx); - tmpCtx.fill(); - pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat"); - const domMatrix = new DOMMatrix(inverse); - pattern.setTransform(domMatrix); - } else { - applyBoundingBox(ctx, this._bbox); - pattern = this._createGradient(ctx); - } - return pattern; - } -} -function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) { - const coords = context.coords, - colors = context.colors; - const bytes = data.data, - rowSize = data.width * 4; - let tmp; - if (coords[p1 + 1] > coords[p2 + 1]) { - tmp = p1; - p1 = p2; - p2 = tmp; - tmp = c1; - c1 = c2; - c2 = tmp; - } - if (coords[p2 + 1] > coords[p3 + 1]) { - tmp = p2; - p2 = p3; - p3 = tmp; - tmp = c2; - c2 = c3; - c3 = tmp; - } - if (coords[p1 + 1] > coords[p2 + 1]) { - tmp = p1; - p1 = p2; - p2 = tmp; - tmp = c1; - c1 = c2; - c2 = tmp; - } - const x1 = (coords[p1] + context.offsetX) * context.scaleX; - const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY; - const x2 = (coords[p2] + context.offsetX) * context.scaleX; - const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY; - const x3 = (coords[p3] + context.offsetX) * context.scaleX; - const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY; - if (y1 >= y3) { - return; - } - const c1r = colors[c1], - c1g = colors[c1 + 1], - c1b = colors[c1 + 2]; - const c2r = colors[c2], - c2g = colors[c2 + 1], - c2b = colors[c2 + 2]; - const c3r = colors[c3], - c3g = colors[c3 + 1], - c3b = colors[c3 + 2]; - const minY = Math.round(y1), - maxY = Math.round(y3); - let xa, car, cag, cab; - let xb, cbr, cbg, cbb; - for (let y = minY; y <= maxY; y++) { - if (y < y2) { - const k = y < y1 ? 0 : (y1 - y) / (y1 - y2); - xa = x1 - (x1 - x2) * k; - car = c1r - (c1r - c2r) * k; - cag = c1g - (c1g - c2g) * k; - cab = c1b - (c1b - c2b) * k; - } else { - let k; - if (y > y3) { - k = 1; - } else if (y2 === y3) { - k = 0; - } else { - k = (y2 - y) / (y2 - y3); - } - xa = x2 - (x2 - x3) * k; - car = c2r - (c2r - c3r) * k; - cag = c2g - (c2g - c3g) * k; - cab = c2b - (c2b - c3b) * k; - } - let k; - if (y < y1) { - k = 0; - } else if (y > y3) { - k = 1; - } else { - k = (y1 - y) / (y1 - y3); - } - xb = x1 - (x1 - x3) * k; - cbr = c1r - (c1r - c3r) * k; - cbg = c1g - (c1g - c3g) * k; - cbb = c1b - (c1b - c3b) * k; - const x1_ = Math.round(Math.min(xa, xb)); - const x2_ = Math.round(Math.max(xa, xb)); - let j = rowSize * y + x1_ * 4; - for (let x = x1_; x <= x2_; x++) { - k = (xa - x) / (xa - xb); - if (k < 0) { - k = 0; - } else if (k > 1) { - k = 1; - } - bytes[j++] = car - (car - cbr) * k | 0; - bytes[j++] = cag - (cag - cbg) * k | 0; - bytes[j++] = cab - (cab - cbb) * k | 0; - bytes[j++] = 255; - } - } -} -function drawFigure(data, figure, context) { - const ps = figure.coords; - const cs = figure.colors; - let i, ii; - switch (figure.type) { - case MeshFigureType.LATTICE: - const verticesPerRow = figure.verticesPerRow; - const rows = Math.floor(ps.length / verticesPerRow) - 1; - const cols = verticesPerRow - 1; - for (i = 0; i < rows; i++) { - let q = i * verticesPerRow; - for (let j = 0; j < cols; j++, q++) { - drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]); - drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]); - } - } - break; - case MeshFigureType.TRIANGLES: - for (i = 0, ii = ps.length; i < ii; i += 3) { - drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]); - } - break; - default: - throw new Error("illegal figure"); - } -} -class MeshShadingPattern extends BaseShadingPattern { - constructor(IR) { - super(); - this._coords = IR[2]; - this._colors = IR[3]; - this._figures = IR[4]; - this._bounds = IR[5]; - this._bbox = IR[6]; - this._background = IR[7]; - this.matrix = null; - } - _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) { - const EXPECTED_SCALE = 1.1; - const MAX_PATTERN_SIZE = 3000; - const BORDER_SIZE = 2; - const offsetX = Math.floor(this._bounds[0]); - const offsetY = Math.floor(this._bounds[1]); - const boundsWidth = Math.ceil(this._bounds[2]) - offsetX; - const boundsHeight = Math.ceil(this._bounds[3]) - offsetY; - const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); - const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); - const scaleX = boundsWidth / width; - const scaleY = boundsHeight / height; - const context = { - coords: this._coords, - colors: this._colors, - offsetX: -offsetX, - offsetY: -offsetY, - scaleX: 1 / scaleX, - scaleY: 1 / scaleY - }; - const paddedWidth = width + BORDER_SIZE * 2; - const paddedHeight = height + BORDER_SIZE * 2; - const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight); - const tmpCtx = tmpCanvas.context; - const data = tmpCtx.createImageData(width, height); - if (backgroundColor) { - const bytes = data.data; - for (let i = 0, ii = bytes.length; i < ii; i += 4) { - bytes[i] = backgroundColor[0]; - bytes[i + 1] = backgroundColor[1]; - bytes[i + 2] = backgroundColor[2]; - bytes[i + 3] = 255; - } - } - for (const figure of this._figures) { - drawFigure(data, figure, context); - } - tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE); - const canvas = tmpCanvas.canvas; - return { - canvas, - offsetX: offsetX - BORDER_SIZE * scaleX, - offsetY: offsetY - BORDER_SIZE * scaleY, - scaleX, - scaleY - }; - } - isModifyingCurrentTransform() { - return true; - } - getPattern(ctx, owner, inverse, pathType) { - applyBoundingBox(ctx, this._bbox); - const scale = new Float32Array(2); - if (pathType === PathType.SHADING) { - Util.singularValueDecompose2dScale(getCurrentTransform(ctx), scale); - } else if (this.matrix) { - Util.singularValueDecompose2dScale(this.matrix, scale); - const [matrixScaleX, matrixScaleY] = scale; - Util.singularValueDecompose2dScale(owner.baseTransform, scale); - scale[0] *= matrixScaleX; - scale[1] *= matrixScaleY; - } else { - Util.singularValueDecompose2dScale(owner.baseTransform, scale); - } - const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases); - if (pathType !== PathType.SHADING) { - ctx.setTransform(...owner.baseTransform); - if (this.matrix) { - ctx.transform(...this.matrix); - } - } - ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); - ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY); - return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat"); - } -} -class DummyShadingPattern extends BaseShadingPattern { - getPattern() { - return "hotpink"; - } -} -function getShadingPattern(IR) { - switch (IR[0]) { - case "RadialAxial": - return new RadialAxialShadingPattern(IR); - case "Mesh": - return new MeshShadingPattern(IR); - case "Dummy": - return new DummyShadingPattern(); - } - throw new Error(`Unknown IR type: ${IR[0]}`); -} -const PaintType = { - COLORED: 1, - UNCOLORED: 2 -}; -class TilingPattern { - static MAX_PATTERN_SIZE = 3000; - constructor(IR, ctx, canvasGraphicsFactory, baseTransform) { - this.color = IR[1]; - this.operatorList = IR[2]; - this.matrix = IR[3]; - this.bbox = IR[4]; - this.xstep = IR[5]; - this.ystep = IR[6]; - this.paintType = IR[7]; - this.tilingType = IR[8]; - this.ctx = ctx; - this.canvasGraphicsFactory = canvasGraphicsFactory; - this.baseTransform = baseTransform; - } - createPatternCanvas(owner, opIdx) { - const { - bbox, - operatorList, - paintType, - tilingType, - color, - canvasGraphicsFactory - } = this; - let { - xstep, - ystep - } = this; - xstep = Math.abs(xstep); - ystep = Math.abs(ystep); - info("TilingType: " + tilingType); - const x0 = bbox[0], - y0 = bbox[1], - x1 = bbox[2], - y1 = bbox[3]; - const width = x1 - x0; - const height = y1 - y0; - const scale = new Float32Array(2); - Util.singularValueDecompose2dScale(this.matrix, scale); - const [matrixScaleX, matrixScaleY] = scale; - Util.singularValueDecompose2dScale(this.baseTransform, scale); - const combinedScaleX = matrixScaleX * scale[0]; - const combinedScaleY = matrixScaleY * scale[1]; - let canvasWidth = width, - canvasHeight = height, - redrawHorizontally = false, - redrawVertically = false; - const xScaledStep = Math.ceil(xstep * combinedScaleX); - const yScaledStep = Math.ceil(ystep * combinedScaleY); - const xScaledWidth = Math.ceil(width * combinedScaleX); - const yScaledHeight = Math.ceil(height * combinedScaleY); - if (xScaledStep >= xScaledWidth) { - canvasWidth = xstep; - } else { - redrawHorizontally = true; - } - if (yScaledStep >= yScaledHeight) { - canvasHeight = ystep; - } else { - redrawVertically = true; - } - const dimx = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); - const dimy = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); - const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size); - const tmpCtx = tmpCanvas.context; - const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx, opIdx); - graphics.groupLevel = owner.groupLevel; - this.setFillAndStrokeStyleToContext(graphics, paintType, color); - tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0); - graphics.transform(0, dimx.scale, 0, 0, dimy.scale, 0, 0); - tmpCtx.save(); - graphics.dependencyTracker?.save(); - this.clipBbox(graphics, x0, y0, x1, y1); - graphics.baseTransform = getCurrentTransform(graphics.ctx); - graphics.executeOperatorList(operatorList); - graphics.endDrawing(); - graphics.dependencyTracker?.restore(); - tmpCtx.restore(); - if (redrawHorizontally || redrawVertically) { - const image = tmpCanvas.canvas; - if (redrawHorizontally) { - canvasWidth = xstep; - } - if (redrawVertically) { - canvasHeight = ystep; - } - const dimx2 = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); - const dimy2 = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); - const xSize = dimx2.size; - const ySize = dimy2.size; - const tmpCanvas2 = owner.cachedCanvases.getCanvas("pattern-workaround", xSize, ySize); - const tmpCtx2 = tmpCanvas2.context; - const ii = redrawHorizontally ? Math.floor(width / xstep) : 0; - const jj = redrawVertically ? Math.floor(height / ystep) : 0; - for (let i = 0; i <= ii; i++) { - for (let j = 0; j <= jj; j++) { - tmpCtx2.drawImage(image, xSize * i, ySize * j, xSize, ySize, 0, 0, xSize, ySize); - } - } - return { - canvas: tmpCanvas2.canvas, - scaleX: dimx2.scale, - scaleY: dimy2.scale, - offsetX: x0, - offsetY: y0 - }; - } - return { - canvas: tmpCanvas.canvas, - scaleX: dimx.scale, - scaleY: dimy.scale, - offsetX: x0, - offsetY: y0 - }; - } - getSizeAndScale(step, realOutputSize, scale) { - const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize); - let size = Math.ceil(step * scale); - if (size >= maxSize) { - size = maxSize; - } else { - scale = size / step; - } - return { - scale, - size - }; - } - clipBbox(graphics, x0, y0, x1, y1) { - const bboxWidth = x1 - x0; - const bboxHeight = y1 - y0; - graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight); - Util.axialAlignedBoundingBox([x0, y0, x1, y1], getCurrentTransform(graphics.ctx), graphics.current.minMax); - graphics.clip(); - graphics.endPath(); - } - setFillAndStrokeStyleToContext(graphics, paintType, color) { - const context = graphics.ctx, - current = graphics.current; - switch (paintType) { - case PaintType.COLORED: - const { - fillStyle, - strokeStyle - } = this.ctx; - context.fillStyle = current.fillColor = fillStyle; - context.strokeStyle = current.strokeColor = strokeStyle; - break; - case PaintType.UNCOLORED: - context.fillStyle = context.strokeStyle = color; - current.fillColor = current.strokeColor = color; - break; - default: - throw new FormatError(`Unsupported paint type: ${paintType}`); - } - } - isModifyingCurrentTransform() { - return false; - } - getPattern(ctx, owner, inverse, pathType, opIdx) { - let matrix = inverse; - if (pathType !== PathType.SHADING) { - matrix = Util.transform(matrix, owner.baseTransform); - if (this.matrix) { - matrix = Util.transform(matrix, this.matrix); - } - } - const temporaryPatternCanvas = this.createPatternCanvas(owner, opIdx); - let domMatrix = new DOMMatrix(matrix); - domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); - domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY); - const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat"); - pattern.setTransform(domMatrix); - return pattern; - } -} - -;// ./src/shared/image_utils.js -/* unused harmony import specifier */ var image_utils_ImageKind; -/* unused harmony import specifier */ var image_utils_FeatureTest; - -function convertToRGBA(params) { - switch (params.kind) { - case image_utils_ImageKind.GRAYSCALE_1BPP: - return convertBlackAndWhiteToRGBA(params); - case image_utils_ImageKind.RGB_24BPP: - return convertRGBToRGBA(params); - } - return null; -} -function convertBlackAndWhiteToRGBA({ - src, - srcPos = 0, - dest, - width, - height, - nonBlackColor = 0xffffffff, - inverseDecode = false -}) { - const black = FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; - const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor]; - const widthInSource = width >> 3; - const widthRemainder = width & 7; - const xorMask = zeroMapping ^ oneMapping; - const srcLength = src.length; - dest = new Uint32Array(dest.buffer); - let destPos = 0; - for (let i = 0; i < height; ++i) { - for (const max = srcPos + widthInSource; srcPos < max; ++srcPos, destPos += 8) { - const elem = src[srcPos]; - dest[destPos] = zeroMapping ^ -(elem >> 7 & 1) & xorMask; - dest[destPos + 1] = zeroMapping ^ -(elem >> 6 & 1) & xorMask; - dest[destPos + 2] = zeroMapping ^ -(elem >> 5 & 1) & xorMask; - dest[destPos + 3] = zeroMapping ^ -(elem >> 4 & 1) & xorMask; - dest[destPos + 4] = zeroMapping ^ -(elem >> 3 & 1) & xorMask; - dest[destPos + 5] = zeroMapping ^ -(elem >> 2 & 1) & xorMask; - dest[destPos + 6] = zeroMapping ^ -(elem >> 1 & 1) & xorMask; - dest[destPos + 7] = zeroMapping ^ -(elem & 1) & xorMask; - } - if (widthRemainder === 0) { - continue; - } - const elem = srcPos < srcLength ? src[srcPos++] : 255; - for (let j = 0; j < widthRemainder; ++j, ++destPos) { - dest[destPos] = zeroMapping ^ -(elem >> 7 - j & 1) & xorMask; - } - } - return { - srcPos, - destPos - }; -} -function convertRGBToRGBA({ - src, - srcPos = 0, - dest, - destPos = 0, - width, - height -}) { - let i = 0; - const len = width * height * 3; - const len32 = len >> 2; - const src32 = new Uint32Array(src.buffer, srcPos, len32); - const alphaMask = image_utils_FeatureTest.isLittleEndian ? 0xff000000 : 0xff; - if (image_utils_FeatureTest.isLittleEndian) { - for (; i < len32 - 2; i += 3, destPos += 4) { - const s1 = src32[i], - s2 = src32[i + 1], - s3 = src32[i + 2]; - dest[destPos] = s1 | alphaMask; - dest[destPos + 1] = s1 >>> 24 | s2 << 8 | alphaMask; - dest[destPos + 2] = s2 >>> 16 | s3 << 16 | alphaMask; - dest[destPos + 3] = s3 >>> 8 | alphaMask; - } - for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { - dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | alphaMask; - } - } else { - for (; i < len32 - 2; i += 3, destPos += 4) { - const s1 = src32[i], - s2 = src32[i + 1], - s3 = src32[i + 2]; - dest[destPos] = s1 | alphaMask; - dest[destPos + 1] = s1 << 24 | s2 >>> 8 | alphaMask; - dest[destPos + 2] = s2 << 16 | s3 >>> 16 | alphaMask; - dest[destPos + 3] = s3 << 8 | alphaMask; - } - for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { - dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | alphaMask; - } - } - return { - srcPos: srcPos + len, - destPos - }; -} -function grayToRGBA(src, dest) { - if (image_utils_FeatureTest.isLittleEndian) { - for (let i = 0, ii = src.length; i < ii; i++) { - dest[i] = src[i] * 0x10101 | 0xff000000; - } - } else { - for (let i = 0, ii = src.length; i < ii; i++) { - dest[i] = src[i] * 0x1010100 | 0x000000ff; - } - } -} - -;// ./src/display/canvas.js - - - - - -const MIN_FONT_SIZE = 16; -const MAX_FONT_SIZE = 100; -const EXECUTION_TIME = 15; -const EXECUTION_STEPS = 10; -const FULL_CHUNK_HEIGHT = 16; -const SCALE_MATRIX = new DOMMatrix(); -const XY = new Float32Array(2); -const MIN_MAX_INIT = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]); -function mirrorContextOperations(ctx, destCtx) { - if (ctx._removeMirroring) { - throw new Error("Context is already forwarding operations."); - } - ctx.__originalSave = ctx.save; - ctx.__originalRestore = ctx.restore; - ctx.__originalRotate = ctx.rotate; - ctx.__originalScale = ctx.scale; - ctx.__originalTranslate = ctx.translate; - ctx.__originalTransform = ctx.transform; - ctx.__originalSetTransform = ctx.setTransform; - ctx.__originalResetTransform = ctx.resetTransform; - ctx.__originalClip = ctx.clip; - ctx.__originalMoveTo = ctx.moveTo; - ctx.__originalLineTo = ctx.lineTo; - ctx.__originalBezierCurveTo = ctx.bezierCurveTo; - ctx.__originalRect = ctx.rect; - ctx.__originalClosePath = ctx.closePath; - ctx.__originalBeginPath = ctx.beginPath; - ctx._removeMirroring = () => { - ctx.save = ctx.__originalSave; - ctx.restore = ctx.__originalRestore; - ctx.rotate = ctx.__originalRotate; - ctx.scale = ctx.__originalScale; - ctx.translate = ctx.__originalTranslate; - ctx.transform = ctx.__originalTransform; - ctx.setTransform = ctx.__originalSetTransform; - ctx.resetTransform = ctx.__originalResetTransform; - ctx.clip = ctx.__originalClip; - ctx.moveTo = ctx.__originalMoveTo; - ctx.lineTo = ctx.__originalLineTo; - ctx.bezierCurveTo = ctx.__originalBezierCurveTo; - ctx.rect = ctx.__originalRect; - ctx.closePath = ctx.__originalClosePath; - ctx.beginPath = ctx.__originalBeginPath; - delete ctx._removeMirroring; - }; - ctx.save = function () { - destCtx.save(); - this.__originalSave(); - }; - ctx.restore = function () { - destCtx.restore(); - this.__originalRestore(); - }; - ctx.translate = function (x, y) { - destCtx.translate(x, y); - this.__originalTranslate(x, y); - }; - ctx.scale = function (x, y) { - destCtx.scale(x, y); - this.__originalScale(x, y); - }; - ctx.transform = function (a, b, c, d, e, f) { - destCtx.transform(a, b, c, d, e, f); - this.__originalTransform(a, b, c, d, e, f); - }; - ctx.setTransform = function (a, b, c, d, e, f) { - destCtx.setTransform(a, b, c, d, e, f); - this.__originalSetTransform(a, b, c, d, e, f); - }; - ctx.resetTransform = function () { - destCtx.resetTransform(); - this.__originalResetTransform(); - }; - ctx.rotate = function (angle) { - destCtx.rotate(angle); - this.__originalRotate(angle); - }; - ctx.clip = function (rule) { - destCtx.clip(rule); - this.__originalClip(rule); - }; - ctx.moveTo = function (x, y) { - destCtx.moveTo(x, y); - this.__originalMoveTo(x, y); - }; - ctx.lineTo = function (x, y) { - destCtx.lineTo(x, y); - this.__originalLineTo(x, y); - }; - ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { - destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); - this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); - }; - ctx.rect = function (x, y, width, height) { - destCtx.rect(x, y, width, height); - this.__originalRect(x, y, width, height); - }; - ctx.closePath = function () { - destCtx.closePath(); - this.__originalClosePath(); - }; - ctx.beginPath = function () { - destCtx.beginPath(); - this.__originalBeginPath(); - }; -} -class CachedCanvases { - constructor(canvasFactory) { - this.canvasFactory = canvasFactory; - this.cache = Object.create(null); - } - getCanvas(id, width, height) { - let canvasEntry; - if (this.cache[id] !== undefined) { - canvasEntry = this.cache[id]; - this.canvasFactory.reset(canvasEntry, width, height); - } else { - canvasEntry = this.canvasFactory.create(width, height); - this.cache[id] = canvasEntry; - } - return canvasEntry; - } - delete(id) { - delete this.cache[id]; - } - clear() { - for (const id in this.cache) { - const canvasEntry = this.cache[id]; - this.canvasFactory.destroy(canvasEntry); - delete this.cache[id]; - } - } -} -function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) { - const [a, b, c, d, tx, ty] = getCurrentTransform(ctx); - if (b === 0 && c === 0) { - const tlX = destX * a + tx; - const rTlX = Math.round(tlX); - const tlY = destY * d + ty; - const rTlY = Math.round(tlY); - const brX = (destX + destW) * a + tx; - const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; - const brY = (destY + destH) * d + ty; - const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; - ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY); - ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight); - ctx.setTransform(a, b, c, d, tx, ty); - return [rWidth, rHeight]; - } - if (a === 0 && d === 0) { - const tlX = destY * c + tx; - const rTlX = Math.round(tlX); - const tlY = destX * b + ty; - const rTlY = Math.round(tlY); - const brX = (destY + destH) * c + tx; - const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; - const brY = (destX + destW) * b + ty; - const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; - ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY); - ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth); - ctx.setTransform(a, b, c, d, tx, ty); - return [rHeight, rWidth]; - } - ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH); - const scaleX = Math.hypot(a, b); - const scaleY = Math.hypot(c, d); - return [scaleX * destW, scaleY * destH]; -} -class CanvasExtraState { - alphaIsShape = false; - fontSize = 0; - fontSizeScale = 1; - textMatrix = null; - textMatrixScale = 1; - fontMatrix = FONT_IDENTITY_MATRIX; - leading = 0; - x = 0; - y = 0; - lineX = 0; - lineY = 0; - charSpacing = 0; - wordSpacing = 0; - textHScale = 1; - textRenderingMode = TextRenderingMode.FILL; - textRise = 0; - fillColor = "#000000"; - strokeColor = "#000000"; - patternFill = false; - patternStroke = false; - fillAlpha = 1; - strokeAlpha = 1; - lineWidth = 1; - activeSMask = null; - transferMaps = "none"; - constructor(width, height, preInit) { - preInit?.(this); - this.clipBox = new Float32Array([0, 0, width, height]); - this.minMax = MIN_MAX_INIT.slice(); - } - clone() { - const clone = Object.create(this); - clone.clipBox = this.clipBox.slice(); - clone.minMax = this.minMax.slice(); - return clone; - } - getPathBoundingBox(pathType = PathType.FILL, transform = null) { - const box = this.minMax.slice(); - if (pathType === PathType.STROKE) { - if (!transform) { - unreachable("Stroke bounding box must include transform."); - } - Util.singularValueDecompose2dScale(transform, XY); - const xStrokePad = XY[0] * this.lineWidth / 2; - const yStrokePad = XY[1] * this.lineWidth / 2; - box[0] -= xStrokePad; - box[1] -= yStrokePad; - box[2] += xStrokePad; - box[3] += yStrokePad; - } - return box; - } - updateClipFromPath() { - const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox()); - this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]); - } - isEmptyClip() { - return this.minMax[0] === Infinity; - } - startNewPathAndClipBox(box) { - this.clipBox.set(box, 0); - this.minMax.set(MIN_MAX_INIT, 0); - } - getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) { - return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform)); - } -} -function putBinaryImageData(ctx, imgData) { - if (imgData instanceof ImageData) { - ctx.putImageData(imgData, 0, 0); - return; - } - const height = imgData.height, - width = imgData.width; - const partialChunkHeight = height % FULL_CHUNK_HEIGHT; - const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; - const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; - const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); - let srcPos = 0, - destPos; - const src = imgData.data; - const dest = chunkImgData.data; - let i, j, thisChunkHeight, elemsInThisChunk; - if (imgData.kind === ImageKind.GRAYSCALE_1BPP) { - const srcLength = src.byteLength; - const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2); - const dest32DataLength = dest32.length; - const fullSrcDiff = width + 7 >> 3; - const white = 0xffffffff; - const black = FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; - for (i = 0; i < totalChunks; i++) { - thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; - destPos = 0; - for (j = 0; j < thisChunkHeight; j++) { - const srcDiff = srcLength - srcPos; - let k = 0; - const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7; - const kEndUnrolled = kEnd & ~7; - let mask = 0; - let srcByte = 0; - for (; k < kEndUnrolled; k += 8) { - srcByte = src[srcPos++]; - dest32[destPos++] = srcByte & 128 ? white : black; - dest32[destPos++] = srcByte & 64 ? white : black; - dest32[destPos++] = srcByte & 32 ? white : black; - dest32[destPos++] = srcByte & 16 ? white : black; - dest32[destPos++] = srcByte & 8 ? white : black; - dest32[destPos++] = srcByte & 4 ? white : black; - dest32[destPos++] = srcByte & 2 ? white : black; - dest32[destPos++] = srcByte & 1 ? white : black; - } - for (; k < kEnd; k++) { - if (mask === 0) { - srcByte = src[srcPos++]; - mask = 128; - } - dest32[destPos++] = srcByte & mask ? white : black; - mask >>= 1; - } - } - while (destPos < dest32DataLength) { - dest32[destPos++] = 0; - } - ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); - } - } else if (imgData.kind === ImageKind.RGBA_32BPP) { - j = 0; - elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; - for (i = 0; i < fullChunks; i++) { - dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); - srcPos += elemsInThisChunk; - ctx.putImageData(chunkImgData, 0, j); - j += FULL_CHUNK_HEIGHT; - } - if (i < totalChunks) { - elemsInThisChunk = width * partialChunkHeight * 4; - dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); - ctx.putImageData(chunkImgData, 0, j); - } - } else if (imgData.kind === ImageKind.RGB_24BPP) { - thisChunkHeight = FULL_CHUNK_HEIGHT; - elemsInThisChunk = width * thisChunkHeight; - for (i = 0; i < totalChunks; i++) { - if (i >= fullChunks) { - thisChunkHeight = partialChunkHeight; - elemsInThisChunk = width * thisChunkHeight; - } - destPos = 0; - for (j = elemsInThisChunk; j--;) { - dest[destPos++] = src[srcPos++]; - dest[destPos++] = src[srcPos++]; - dest[destPos++] = src[srcPos++]; - dest[destPos++] = 255; - } - ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); - } - } else { - throw new Error(`bad image kind: ${imgData.kind}`); - } -} -function putBinaryImageMask(ctx, imgData) { - if (imgData.bitmap) { - ctx.drawImage(imgData.bitmap, 0, 0); - return; - } - const height = imgData.height, - width = imgData.width; - const partialChunkHeight = height % FULL_CHUNK_HEIGHT; - const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; - const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; - const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); - let srcPos = 0; - const src = imgData.data; - const dest = chunkImgData.data; - for (let i = 0; i < totalChunks; i++) { - const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; - ({ - srcPos - } = convertBlackAndWhiteToRGBA({ - src, - srcPos, - dest, - width, - height: thisChunkHeight, - nonBlackColor: 0 - })); - ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); - } -} -function copyCtxState(sourceCtx, destCtx) { - const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"]; - for (const property of properties) { - if (sourceCtx[property] !== undefined) { - destCtx[property] = sourceCtx[property]; - } - } - if (sourceCtx.setLineDash !== undefined) { - destCtx.setLineDash(sourceCtx.getLineDash()); - destCtx.lineDashOffset = sourceCtx.lineDashOffset; - } -} -function resetCtxToDefault(ctx) { - ctx.strokeStyle = ctx.fillStyle = "#000000"; - ctx.fillRule = "nonzero"; - ctx.globalAlpha = 1; - ctx.lineWidth = 1; - ctx.lineCap = "butt"; - ctx.lineJoin = "miter"; - ctx.miterLimit = 10; - ctx.globalCompositeOperation = "source-over"; - ctx.font = "10px sans-serif"; - if (ctx.setLineDash !== undefined) { - ctx.setLineDash([]); - ctx.lineDashOffset = 0; - } - const { - filter - } = ctx; - if (filter !== "none" && filter !== "") { - ctx.filter = "none"; - } -} -function getImageSmoothingEnabled(transform, interpolate) { - if (interpolate) { - return true; - } - Util.singularValueDecompose2dScale(transform, XY); - const actualScale = Math.fround(OutputScale.pixelRatio * PixelsPerInch.PDF_TO_CSS_UNITS); - return XY[0] <= actualScale && XY[1] <= actualScale; -} -const LINE_CAP_STYLES = ["butt", "round", "square"]; -const LINE_JOIN_STYLES = ["miter", "round", "bevel"]; -const NORMAL_CLIP = {}; -const EO_CLIP = {}; -class CanvasGraphics { - constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, { - optionalContentConfig, - markedContentStack = null - }, annotationCanvasMap, pageColors, dependencyTracker) { - this.ctx = canvasCtx; - this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); - this.stateStack = []; - this.pendingClip = null; - this.pendingEOFill = false; - this.commonObjs = commonObjs; - this.objs = objs; - this.canvasFactory = canvasFactory; - this.filterFactory = filterFactory; - this.groupStack = []; - this.baseTransform = null; - this.baseTransformStack = []; - this.groupLevel = 0; - this.smaskStack = []; - this.smaskCounter = 0; - this.tempSMask = null; - this.suspendedCtx = null; - this.contentVisible = true; - this.markedContentStack = markedContentStack || []; - this.optionalContentConfig = optionalContentConfig; - this.cachedCanvases = new CachedCanvases(this.canvasFactory); - this.cachedPatterns = new Map(); - this.annotationCanvasMap = annotationCanvasMap; - this.viewportScale = 1; - this.outputScaleX = 1; - this.outputScaleY = 1; - this.pageColors = pageColors; - this._cachedScaleForStroking = [-1, 0]; - this._cachedGetSinglePixelWidth = null; - this._cachedBitmapsMap = new Map(); - this.dependencyTracker = dependencyTracker ?? null; - } - getObject(opIdx, data, fallback = null) { - if (typeof data === "string") { - this.dependencyTracker?.recordNamedDependency(opIdx, data); - return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data); - } - return fallback; - } - beginDrawing({ - transform, - viewport, - transparency = false, - background = null - }) { - const width = this.ctx.canvas.width; - const height = this.ctx.canvas.height; - const savedFillStyle = this.ctx.fillStyle; - this.ctx.fillStyle = background || "#ffffff"; - this.ctx.fillRect(0, 0, width, height); - this.ctx.fillStyle = savedFillStyle; - if (transparency) { - const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height); - this.compositeCtx = this.ctx; - this.transparentCanvas = transparentCanvas.canvas; - this.ctx = transparentCanvas.context; - this.ctx.save(); - this.ctx.transform(...getCurrentTransform(this.compositeCtx)); - } - this.ctx.save(); - resetCtxToDefault(this.ctx); - if (transform) { - this.ctx.transform(...transform); - this.outputScaleX = transform[0]; - this.outputScaleY = transform[0]; - } - this.ctx.transform(...viewport.transform); - this.viewportScale = viewport.scale; - this.baseTransform = getCurrentTransform(this.ctx); - } - executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper, operationsFilter) { - const argsArray = operatorList.argsArray; - const fnArray = operatorList.fnArray; - let i = executionStartIdx || 0; - const argsArrayLen = argsArray.length; - if (argsArrayLen === i) { - return i; - } - const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function"; - const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0; - let steps = 0; - const commonObjs = this.commonObjs; - const objs = this.objs; - let fnId, fnArgs; - while (true) { - if (stepper !== undefined && i === stepper.nextBreakPoint) { - stepper.breakIt(i, continueCallback); - return i; - } - if (!operationsFilter || operationsFilter(i)) { - fnId = fnArray[i]; - fnArgs = argsArray[i] ?? null; - if (fnId !== OPS.dependency) { - if (fnArgs === null) { - this[fnId](i); - } else { - this[fnId](i, ...fnArgs); - } - } else { - for (const depObjId of fnArgs) { - this.dependencyTracker?.recordNamedData(depObjId, i); - const objsPool = depObjId.startsWith("g_") ? commonObjs : objs; - if (!objsPool.has(depObjId)) { - objsPool.get(depObjId, continueCallback); - return i; - } - } - } - } - i++; - if (i === argsArrayLen) { - return i; - } - if (chunkOperations && ++steps > EXECUTION_STEPS) { - if (Date.now() > endTime) { - continueCallback(); - return i; - } - steps = 0; - } - } - } - #restoreInitialState() { - while (this.stateStack.length || this.inSMaskMode) { - this.restore(); - } - this.current.activeSMask = null; - this.ctx.restore(); - if (this.transparentCanvas) { - this.ctx = this.compositeCtx; - this.ctx.save(); - this.ctx.setTransform(1, 0, 0, 1, 0, 0); - this.ctx.drawImage(this.transparentCanvas, 0, 0); - this.ctx.restore(); - this.transparentCanvas = null; - } - } - endDrawing() { - this.#restoreInitialState(); - this.cachedCanvases.clear(); - this.cachedPatterns.clear(); - for (const cache of this._cachedBitmapsMap.values()) { - for (const canvas of cache.values()) { - if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) { - canvas.width = canvas.height = 0; - } - } - cache.clear(); - } - this._cachedBitmapsMap.clear(); - this.#drawFilter(); - } - #drawFilter() { - if (this.pageColors) { - const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background); - if (hcmFilterId !== "none") { - const savedFilter = this.ctx.filter; - this.ctx.filter = hcmFilterId; - this.ctx.drawImage(this.ctx.canvas, 0, 0); - this.ctx.filter = savedFilter; - } - } - } - _scaleImage(img, inverseTransform) { - const width = img.width ?? img.displayWidth; - const height = img.height ?? img.displayHeight; - let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1); - let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1); - let paintWidth = width, - paintHeight = height; - let tmpCanvasId = "prescale1"; - let tmpCanvas, tmpCtx; - while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) { - let newWidth = paintWidth, - newHeight = paintHeight; - if (widthScale > 2 && paintWidth > 1) { - newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2); - widthScale /= paintWidth / newWidth; - } - if (heightScale > 2 && paintHeight > 1) { - newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2; - heightScale /= paintHeight / newHeight; - } - tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); - tmpCtx = tmpCanvas.context; - tmpCtx.clearRect(0, 0, newWidth, newHeight); - tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight); - img = tmpCanvas.canvas; - paintWidth = newWidth; - paintHeight = newHeight; - tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1"; - } - return { - img, - paintWidth, - paintHeight - }; - } - _createMaskCanvas(opIdx, img) { - const ctx = this.ctx; - const { - width, - height - } = img; - const fillColor = this.current.fillColor; - const isPatternFill = this.current.patternFill; - const currentTransform = getCurrentTransform(ctx); - let cache, cacheKey, scaled, maskCanvas; - if ((img.bitmap || img.data) && img.count > 1) { - const mainKey = img.bitmap || img.data.buffer; - cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]); - cache = this._cachedBitmapsMap.getOrInsertComputed(mainKey, makeMap); - const cachedImage = cache.get(cacheKey); - if (cachedImage && !isPatternFill) { - const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]); - const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]); - this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill); - return { - canvas: cachedImage, - offsetX, - offsetY - }; - } - scaled = cachedImage; - } - if (!scaled) { - maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); - putBinaryImageMask(maskCanvas.context, img); - } - let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]); - maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]); - const minMax = MIN_MAX_INIT.slice(); - Util.axialAlignedBoundingBox([0, 0, width, height], maskToCanvas, minMax); - const [minX, minY, maxX, maxY] = minMax; - const drawnWidth = Math.round(maxX - minX) || 1; - const drawnHeight = Math.round(maxY - minY) || 1; - const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight); - const fillCtx = fillCanvas.context; - const offsetX = minX; - const offsetY = minY; - fillCtx.translate(-offsetX, -offsetY); - fillCtx.transform(...maskToCanvas); - if (!scaled) { - scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx)); - scaled = scaled.img; - if (cache && isPatternFill) { - cache.set(cacheKey, scaled); - } - } - fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate); - drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height); - fillCtx.globalCompositeOperation = "source-in"; - const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]); - fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL, opIdx) : fillColor; - fillCtx.fillRect(0, 0, width, height); - if (cache && !isPatternFill) { - this.cachedCanvases.delete("fillCanvas"); - cache.set(cacheKey, fillCanvas.canvas); - } - this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill); - return { - canvas: fillCanvas.canvas, - offsetX: Math.round(offsetX), - offsetY: Math.round(offsetY) - }; - } - setLineWidth(opIdx, width) { - this.dependencyTracker?.recordSimpleData("lineWidth", opIdx); - if (width !== this.current.lineWidth) { - this._cachedScaleForStroking[0] = -1; - } - this.current.lineWidth = width; - this.ctx.lineWidth = width; - } - setLineCap(opIdx, style) { - this.dependencyTracker?.recordSimpleData("lineCap", opIdx); - this.ctx.lineCap = LINE_CAP_STYLES[style]; - } - setLineJoin(opIdx, style) { - this.dependencyTracker?.recordSimpleData("lineJoin", opIdx); - this.ctx.lineJoin = LINE_JOIN_STYLES[style]; - } - setMiterLimit(opIdx, limit) { - this.dependencyTracker?.recordSimpleData("miterLimit", opIdx); - this.ctx.miterLimit = limit; - } - setDash(opIdx, dashArray, dashPhase) { - this.dependencyTracker?.recordSimpleData("dash", opIdx); - const ctx = this.ctx; - if (ctx.setLineDash !== undefined) { - ctx.setLineDash(dashArray); - ctx.lineDashOffset = dashPhase; - } - } - setRenderingIntent(opIdx, intent) {} - setFlatness(opIdx, flatness) {} - setGState(opIdx, states) { - for (const [key, value] of states) { - switch (key) { - case "LW": - this.setLineWidth(opIdx, value); - break; - case "LC": - this.setLineCap(opIdx, value); - break; - case "LJ": - this.setLineJoin(opIdx, value); - break; - case "ML": - this.setMiterLimit(opIdx, value); - break; - case "D": - this.setDash(opIdx, value[0], value[1]); - break; - case "RI": - this.setRenderingIntent(opIdx, value); - break; - case "FL": - this.setFlatness(opIdx, value); - break; - case "Font": - this.setFont(opIdx, value[0], value[1]); - break; - case "CA": - this.dependencyTracker?.recordSimpleData("strokeAlpha", opIdx); - this.current.strokeAlpha = value; - break; - case "ca": - this.dependencyTracker?.recordSimpleData("fillAlpha", opIdx); - this.ctx.globalAlpha = this.current.fillAlpha = value; - break; - case "BM": - this.dependencyTracker?.recordSimpleData("globalCompositeOperation", opIdx); - this.ctx.globalCompositeOperation = value; - break; - case "SMask": - this.dependencyTracker?.recordSimpleData("SMask", opIdx); - this.current.activeSMask = value ? this.tempSMask : null; - this.tempSMask = null; - this.checkSMaskState(); - break; - case "TR": - this.dependencyTracker?.recordSimpleData("filter", opIdx); - this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value); - break; - } - } - } - get inSMaskMode() { - return !!this.suspendedCtx; - } - checkSMaskState() { - const inSMaskMode = this.inSMaskMode; - if (this.current.activeSMask && !inSMaskMode) { - this.beginSMaskMode(); - } else if (!this.current.activeSMask && inSMaskMode) { - this.endSMaskMode(); - } - } - beginSMaskMode(opIdx) { - if (this.inSMaskMode) { - throw new Error("beginSMaskMode called while already in smask mode"); - } - const drawnWidth = this.ctx.canvas.width; - const drawnHeight = this.ctx.canvas.height; - const cacheId = "smaskGroupAt" + this.groupLevel; - const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); - this.suspendedCtx = this.ctx; - const ctx = this.ctx = scratchCanvas.context; - ctx.setTransform(this.suspendedCtx.getTransform()); - copyCtxState(this.suspendedCtx, ctx); - mirrorContextOperations(ctx, this.suspendedCtx); - this.setGState(opIdx, [["BM", "source-over"]]); - } - endSMaskMode() { - if (!this.inSMaskMode) { - throw new Error("endSMaskMode called while not in smask mode"); - } - this.ctx._removeMirroring(); - copyCtxState(this.ctx, this.suspendedCtx); - this.ctx = this.suspendedCtx; - this.suspendedCtx = null; - } - compose(dirtyBox) { - if (!this.current.activeSMask) { - return; - } - if (!dirtyBox) { - dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height]; - } else { - dirtyBox[0] = Math.floor(dirtyBox[0]); - dirtyBox[1] = Math.floor(dirtyBox[1]); - dirtyBox[2] = Math.ceil(dirtyBox[2]); - dirtyBox[3] = Math.ceil(dirtyBox[3]); - } - const smask = this.current.activeSMask; - const suspendedCtx = this.suspendedCtx; - this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox); - this.ctx.save(); - this.ctx.setTransform(1, 0, 0, 1, 0, 0); - this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); - this.ctx.restore(); - } - composeSMask(ctx, smask, layerCtx, layerBox) { - const layerOffsetX = layerBox[0]; - const layerOffsetY = layerBox[1]; - const layerWidth = layerBox[2] - layerOffsetX; - const layerHeight = layerBox[3] - layerOffsetY; - if (layerWidth === 0 || layerHeight === 0) { - return; - } - this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY); - ctx.save(); - ctx.globalAlpha = 1; - ctx.globalCompositeOperation = "source-over"; - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.drawImage(layerCtx.canvas, 0, 0); - ctx.restore(); - } - genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) { - let maskCanvas = maskCtx.canvas; - let maskX = layerOffsetX - maskOffsetX; - let maskY = layerOffsetY - maskOffsetY; - if (backdrop) { - if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) { - const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height); - const ctx = canvas.context; - ctx.drawImage(maskCanvas, -maskX, -maskY); - ctx.globalCompositeOperation = "destination-atop"; - ctx.fillStyle = backdrop; - ctx.fillRect(0, 0, width, height); - ctx.globalCompositeOperation = "source-over"; - maskCanvas = canvas.canvas; - maskX = maskY = 0; - } else { - maskCtx.save(); - maskCtx.globalAlpha = 1; - maskCtx.setTransform(1, 0, 0, 1, 0, 0); - const clip = new Path2D(); - clip.rect(maskX, maskY, width, height); - maskCtx.clip(clip); - maskCtx.globalCompositeOperation = "destination-atop"; - maskCtx.fillStyle = backdrop; - maskCtx.fillRect(maskX, maskY, width, height); - maskCtx.restore(); - } - } - layerCtx.save(); - layerCtx.globalAlpha = 1; - layerCtx.setTransform(1, 0, 0, 1, 0, 0); - if (subtype === "Alpha" && transferMap) { - layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap); - } else if (subtype === "Luminosity") { - layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap); - } - const clip = new Path2D(); - clip.rect(layerOffsetX, layerOffsetY, width, height); - layerCtx.clip(clip); - layerCtx.globalCompositeOperation = "destination-in"; - layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height); - layerCtx.restore(); - } - save(opIdx) { - if (this.inSMaskMode) { - copyCtxState(this.ctx, this.suspendedCtx); - } - this.ctx.save(); - const old = this.current; - this.stateStack.push(old); - this.current = old.clone(); - this.dependencyTracker?.save(opIdx); - } - restore(opIdx) { - this.dependencyTracker?.restore(opIdx); - if (this.stateStack.length === 0) { - if (this.inSMaskMode) { - this.endSMaskMode(); - } - return; - } - this.current = this.stateStack.pop(); - this.ctx.restore(); - if (this.inSMaskMode) { - copyCtxState(this.suspendedCtx, this.ctx); - } - this.checkSMaskState(); - this.pendingClip = null; - this._cachedScaleForStroking[0] = -1; - this._cachedGetSinglePixelWidth = null; - } - transform(opIdx, a, b, c, d, e, f) { - this.dependencyTracker?.recordIncrementalData("transform", opIdx); - this.ctx.transform(a, b, c, d, e, f); - this._cachedScaleForStroking[0] = -1; - this._cachedGetSinglePixelWidth = null; - } - constructPath(opIdx, op, data, minMax) { - let [path] = data; - if (!minMax) { - path ||= data[0] = new Path2D(); - this[op](opIdx, path); - return; - } - if (this.dependencyTracker !== null) { - const outerExtraSize = op === OPS.stroke ? this.current.lineWidth / 2 : 0; - this.dependencyTracker.resetBBox(opIdx).recordBBox(opIdx, this.ctx, minMax[0] - outerExtraSize, minMax[2] + outerExtraSize, minMax[1] - outerExtraSize, minMax[3] + outerExtraSize).recordDependencies(opIdx, ["transform"]); - } - if (!(path instanceof Path2D)) { - path = data[0] = makePathFromDrawOPS(path); - } - Util.axialAlignedBoundingBox(minMax, getCurrentTransform(this.ctx), this.current.minMax); - this[op](opIdx, path); - this._pathStartIdx = opIdx; - } - closePath(opIdx) { - this.ctx.closePath(); - } - stroke(opIdx, path, consumePath = true) { - const ctx = this.ctx; - const strokeColor = this.current.strokeColor; - ctx.globalAlpha = this.current.strokeAlpha; - if (this.contentVisible) { - if (typeof strokeColor === "object" && strokeColor?.getPattern) { - const baseTransform = strokeColor.isModifyingCurrentTransform() ? ctx.getTransform() : null; - ctx.save(); - ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx); - if (baseTransform) { - const newPath = new Path2D(); - newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform)); - path = newPath; - } - this.rescaleAndStroke(path, false); - ctx.restore(); - } else { - this.rescaleAndStroke(path, true); - } - } - this.dependencyTracker?.recordDependencies(opIdx, Dependencies.stroke); - if (consumePath) { - this.consumePath(opIdx, path, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx))); - } - ctx.globalAlpha = this.current.fillAlpha; - } - closeStroke(opIdx, path) { - this.stroke(opIdx, path); - } - fill(opIdx, path, consumePath = true) { - const ctx = this.ctx; - const fillColor = this.current.fillColor; - const isPatternFill = this.current.patternFill; - let needRestore = false; - if (isPatternFill) { - const baseTransform = fillColor.isModifyingCurrentTransform() ? ctx.getTransform() : null; - this.dependencyTracker?.save(opIdx); - ctx.save(); - ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx); - if (baseTransform) { - const newPath = new Path2D(); - newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform)); - path = newPath; - } - needRestore = true; - } - const intersect = this.current.getClippedPathBoundingBox(); - if (this.contentVisible && intersect !== null) { - if (this.pendingEOFill) { - ctx.fill(path, "evenodd"); - this.pendingEOFill = false; - } else { - ctx.fill(path); - } - } - this.dependencyTracker?.recordDependencies(opIdx, Dependencies.fill); - if (needRestore) { - ctx.restore(); - this.dependencyTracker?.restore(opIdx); - } - if (consumePath) { - this.consumePath(opIdx, path, intersect); - } - } - eoFill(opIdx, path) { - this.pendingEOFill = true; - this.fill(opIdx, path); - } - fillStroke(opIdx, path) { - this.fill(opIdx, path, false); - this.stroke(opIdx, path, false); - this.consumePath(opIdx, path); - } - eoFillStroke(opIdx, path) { - this.pendingEOFill = true; - this.fillStroke(opIdx, path); - } - closeFillStroke(opIdx, path) { - this.fillStroke(opIdx, path); - } - closeEOFillStroke(opIdx, path) { - this.pendingEOFill = true; - this.fillStroke(opIdx, path); - } - endPath(opIdx, path) { - this.consumePath(opIdx, path); - } - rawFillPath(opIdx, path) { - this.ctx.fill(path); - this.dependencyTracker?.recordDependencies(opIdx, Dependencies.rawFillPath).recordOperation(opIdx); - } - clip(opIdx) { - this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx); - this.pendingClip = NORMAL_CLIP; - } - eoClip(opIdx) { - this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx); - this.pendingClip = EO_CLIP; - } - beginText(opIdx) { - this.current.textMatrix = null; - this.current.textMatrixScale = 1; - this.current.x = this.current.lineX = 0; - this.current.y = this.current.lineY = 0; - this.dependencyTracker?.recordOpenMarker(opIdx).resetIncrementalData("sameLineText").resetIncrementalData("moveText", opIdx); - } - endText(opIdx) { - const paths = this.pendingTextPaths; - const ctx = this.ctx; - if (this.dependencyTracker) { - const { - dependencyTracker - } = this; - if (paths !== undefined) { - dependencyTracker.recordFutureForcedDependency("textClip", dependencyTracker.getOpenMarker()).recordFutureForcedDependency("textClip", opIdx); - } - dependencyTracker.recordCloseMarker(opIdx); - } - if (paths !== undefined) { - const newPath = new Path2D(); - const invTransf = ctx.getTransform().invertSelf(); - for (const { - transform, - x, - y, - fontSize, - path - } of paths) { - if (!path) { - continue; - } - newPath.addPath(path, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x, y).scale(fontSize, -fontSize)); - } - ctx.clip(newPath); - } - delete this.pendingTextPaths; - } - setCharSpacing(opIdx, spacing) { - this.dependencyTracker?.recordSimpleData("charSpacing", opIdx); - this.current.charSpacing = spacing; - } - setWordSpacing(opIdx, spacing) { - this.dependencyTracker?.recordSimpleData("wordSpacing", opIdx); - this.current.wordSpacing = spacing; - } - setHScale(opIdx, scale) { - this.dependencyTracker?.recordSimpleData("hScale", opIdx); - this.current.textHScale = scale / 100; - } - setLeading(opIdx, leading) { - this.dependencyTracker?.recordSimpleData("leading", opIdx); - this.current.leading = -leading; - } - setFont(opIdx, fontRefName, size) { - this.dependencyTracker?.recordSimpleData("font", opIdx).recordSimpleDataFromNamed("fontObj", fontRefName, opIdx); - const fontObj = this.commonObjs.get(fontRefName); - const current = this.current; - if (!fontObj) { - throw new Error(`Can't find font for ${fontRefName}`); - } - current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX; - if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) { - warn("Invalid font matrix for font " + fontRefName); - } - if (size < 0) { - size = -size; - current.fontDirection = -1; - } else { - current.fontDirection = 1; - } - this.current.font = fontObj; - this.current.fontSize = size; - if (fontObj.isType3Font) { - return; - } - const name = fontObj.loadedName || "sans-serif"; - const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`; - let bold = "normal"; - if (fontObj.black) { - bold = "900"; - } else if (fontObj.bold) { - bold = "bold"; - } - const italic = fontObj.italic ? "italic" : "normal"; - let browserFontSize = size; - if (size < MIN_FONT_SIZE) { - browserFontSize = MIN_FONT_SIZE; - } else if (size > MAX_FONT_SIZE) { - browserFontSize = MAX_FONT_SIZE; - } - this.current.fontSizeScale = size / browserFontSize; - this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`; - } - setTextRenderingMode(opIdx, mode) { - this.dependencyTracker?.recordSimpleData("textRenderingMode", opIdx); - this.current.textRenderingMode = mode; - } - setTextRise(opIdx, rise) { - this.dependencyTracker?.recordSimpleData("textRise", opIdx); - this.current.textRise = rise; - } - moveText(opIdx, x, y) { - this.dependencyTracker?.resetIncrementalData("sameLineText").recordIncrementalData("moveText", opIdx); - this.current.x = this.current.lineX += x; - this.current.y = this.current.lineY += y; - } - setLeadingMoveText(opIdx, x, y) { - this.setLeading(opIdx, -y); - this.moveText(opIdx, x, y); - } - setTextMatrix(opIdx, matrix) { - this.dependencyTracker?.resetIncrementalData("sameLineText").recordSimpleData("textMatrix", opIdx); - const { - current - } = this; - current.textMatrix = matrix; - current.textMatrixScale = Math.hypot(matrix[0], matrix[1]); - current.x = current.lineX = 0; - current.y = current.lineY = 0; - } - nextLine(opIdx) { - this.moveText(opIdx, 0, this.current.leading); - this.dependencyTracker?.recordIncrementalData("moveText", this.dependencyTracker.getSimpleIndex("leading") ?? opIdx); - } - #getScaledPath(path, currentTransform, transform) { - const newPath = new Path2D(); - newPath.addPath(path, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform)); - return newPath; - } - paintChar(opIdx, character, x, y, patternFillTransform, patternStrokeTransform) { - const ctx = this.ctx; - const current = this.current; - const font = current.font; - const textRenderingMode = current.textRenderingMode; - const fontSize = current.fontSize / current.fontSizeScale; - const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; - const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); - const patternFill = current.patternFill && !font.missingFile; - const patternStroke = current.patternStroke && !font.missingFile; - let path; - if ((font.disableFontFace || isAddToPathSet || patternFill || patternStroke) && !font.missingFile) { - path = font.getPathGenerator(this.commonObjs, character); - } - if (path && (font.disableFontFace || patternFill || patternStroke)) { - ctx.save(); - ctx.translate(x, y); - ctx.scale(fontSize, -fontSize); - this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font); - let currentTransform; - if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { - if (patternFillTransform) { - currentTransform = ctx.getTransform(); - ctx.setTransform(...patternFillTransform); - const scaledPath = this.#getScaledPath(path, currentTransform, patternFillTransform); - ctx.fill(scaledPath); - } else { - ctx.fill(path); - } - } - if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { - if (patternStrokeTransform) { - currentTransform ||= ctx.getTransform(); - ctx.setTransform(...patternStrokeTransform); - const { - a, - b, - c, - d - } = currentTransform; - const invPatternTransform = Util.inverseTransform(patternStrokeTransform); - const transf = Util.transform([a, b, c, d, 0, 0], invPatternTransform); - Util.singularValueDecompose2dScale(transf, XY); - ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize; - ctx.stroke(this.#getScaledPath(path, currentTransform, patternStrokeTransform)); - } else { - ctx.lineWidth /= fontSize; - ctx.stroke(path); - } - } - ctx.restore(); - } else { - if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { - ctx.fillText(character, x, y); - this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character)); - } - if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { - if (this.dependencyTracker) { - this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character)).recordDependencies(opIdx, Dependencies.stroke); - } - ctx.strokeText(character, x, y); - } - } - if (isAddToPathSet) { - const paths = this.pendingTextPaths ||= []; - paths.push({ - transform: getCurrentTransform(ctx), - x, - y, - fontSize, - path - }); - this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y); - } - } - get isFontSubpixelAAEnabled() { - const { - context: ctx - } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10); - ctx.scale(1.5, 1); - ctx.fillText("I", 0, 10); - const data = ctx.getImageData(0, 0, 10, 10).data; - let enabled = false; - for (let i = 3; i < data.length; i += 4) { - if (data[i] > 0 && data[i] < 255) { - enabled = true; - break; - } - } - return shadow(this, "isFontSubpixelAAEnabled", enabled); - } - showText(opIdx, glyphs) { - if (this.dependencyTracker) { - this.dependencyTracker.recordDependencies(opIdx, Dependencies.showText).resetBBox(opIdx); - if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) { - this.dependencyTracker.recordFutureForcedDependency("textClip", opIdx).inheritPendingDependenciesAsFutureForcedDependencies(); - } - } - const current = this.current; - const font = current.font; - if (font.isType3Font) { - this.showType3Text(opIdx, glyphs); - this.dependencyTracker?.recordShowTextOperation(opIdx); - return undefined; - } - const fontSize = current.fontSize; - if (fontSize === 0) { - this.dependencyTracker?.recordOperation(opIdx); - return undefined; - } - const ctx = this.ctx; - const fontSizeScale = current.fontSizeScale; - const charSpacing = current.charSpacing; - const wordSpacing = current.wordSpacing; - const fontDirection = current.fontDirection; - const textHScale = current.textHScale * fontDirection; - const glyphsLength = glyphs.length; - const vertical = font.vertical; - const spacingDir = vertical ? 1 : -1; - const defaultVMetrics = font.defaultVMetrics; - const widthAdvanceScale = fontSize * current.fontMatrix[0]; - const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill; - ctx.save(); - if (current.textMatrix) { - ctx.transform(...current.textMatrix); - } - ctx.translate(current.x, current.y + current.textRise); - if (fontDirection > 0) { - ctx.scale(textHScale, -1); - } else { - ctx.scale(textHScale, 1); - } - let patternFillTransform, patternStrokeTransform; - const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; - const needsFill = fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE; - const needsStroke = fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE; - if (needsFill && current.patternFill) { - ctx.save(); - const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx); - patternFillTransform = getCurrentTransform(ctx); - ctx.restore(); - ctx.fillStyle = pattern; - } - if (needsStroke && current.patternStroke) { - ctx.save(); - const pattern = current.strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx); - patternStrokeTransform = getCurrentTransform(ctx); - ctx.restore(); - ctx.strokeStyle = pattern; - } - let lineWidth = current.lineWidth; - const scale = current.textMatrixScale; - if (scale === 0 || lineWidth === 0) { - if (needsStroke) { - lineWidth = this.getSinglePixelWidth(); - } - } else { - lineWidth /= scale; - } - if (fontSizeScale !== 1.0) { - ctx.scale(fontSizeScale, fontSizeScale); - lineWidth /= fontSizeScale; - } - ctx.lineWidth = lineWidth; - if (font.isInvalidPDFjsFont) { - const chars = []; - let width = 0; - for (const glyph of glyphs) { - chars.push(glyph.unicode); - width += glyph.width; - } - const joinedChars = chars.join(""); - ctx.fillText(joinedChars, 0, 0); - if (this.dependencyTracker !== null) { - const measure = ctx.measureText(joinedChars); - this.dependencyTracker.recordBBox(opIdx, this.ctx, -measure.actualBoundingBoxLeft, measure.actualBoundingBoxRight, -measure.actualBoundingBoxAscent, measure.actualBoundingBoxDescent).recordShowTextOperation(opIdx); - } - current.x += width * widthAdvanceScale * textHScale; - ctx.restore(); - this.compose(); - return undefined; - } - let x = 0, - i; - for (i = 0; i < glyphsLength; ++i) { - const glyph = glyphs[i]; - if (typeof glyph === "number") { - x += spacingDir * glyph * fontSize / 1000; - continue; - } - let restoreNeeded = false; - const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; - const character = glyph.fontChar; - const accent = glyph.accent; - let scaledX, scaledY; - let width = glyph.width; - if (vertical) { - const vmetric = glyph.vmetric || defaultVMetrics; - const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale; - const vy = vmetric[2] * widthAdvanceScale; - width = vmetric ? -vmetric[0] : width; - scaledX = vx / fontSizeScale; - scaledY = (x + vy) / fontSizeScale; - } else { - scaledX = x / fontSizeScale; - scaledY = 0; - } - let measure; - if (font.remeasure && width > 0) { - measure = ctx.measureText(character); - const measuredWidth = measure.width * 1000 / fontSize * fontSizeScale; - if (width < measuredWidth && this.isFontSubpixelAAEnabled) { - const characterScaleX = width / measuredWidth; - restoreNeeded = true; - ctx.save(); - ctx.scale(characterScaleX, 1); - scaledX /= characterScaleX; - } else if (width !== measuredWidth) { - scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale; - } - } - if (this.contentVisible && (glyph.isInFont || font.missingFile)) { - if (simpleFillText && !accent) { - ctx.fillText(character, scaledX, scaledY); - this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, measure ? { - bbox: null - } : font, fontSize / fontSizeScale, scaledX, scaledY, () => measure ?? ctx.measureText(character)); - } else { - this.paintChar(opIdx, character, scaledX, scaledY, patternFillTransform, patternStrokeTransform); - if (accent) { - const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale; - const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale; - this.paintChar(opIdx, accent.fontChar, scaledAccentX, scaledAccentY, patternFillTransform, patternStrokeTransform); - } - } - } - const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection; - x += charWidth; - if (restoreNeeded) { - ctx.restore(); - } - } - if (vertical) { - current.y -= x; - } else { - current.x += x * textHScale; - } - ctx.restore(); - this.compose(); - this.dependencyTracker?.recordShowTextOperation(opIdx); - return undefined; - } - showType3Text(opIdx, glyphs) { - const ctx = this.ctx; - const current = this.current; - const font = current.font; - const fontSize = current.fontSize; - const fontDirection = current.fontDirection; - const spacingDir = font.vertical ? 1 : -1; - const charSpacing = current.charSpacing; - const wordSpacing = current.wordSpacing; - const textHScale = current.textHScale * fontDirection; - const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; - const glyphsLength = glyphs.length; - const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE; - let i, glyph, width, spacingLength; - if (isTextInvisible || fontSize === 0) { - return; - } - this._cachedScaleForStroking[0] = -1; - this._cachedGetSinglePixelWidth = null; - ctx.save(); - if (current.textMatrix) { - ctx.transform(...current.textMatrix); - } - ctx.translate(current.x, current.y + current.textRise); - ctx.scale(textHScale, fontDirection); - const dependencyTracker = this.dependencyTracker; - this.dependencyTracker = dependencyTracker ? new CanvasNestedDependencyTracker(dependencyTracker, opIdx) : null; - for (i = 0; i < glyphsLength; ++i) { - glyph = glyphs[i]; - if (typeof glyph === "number") { - spacingLength = spacingDir * glyph * fontSize / 1000; - this.ctx.translate(spacingLength, 0); - current.x += spacingLength * textHScale; - continue; - } - const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; - const operatorList = font.charProcOperatorList[glyph.operatorListId]; - if (!operatorList) { - warn(`Type3 character "${glyph.operatorListId}" is not available.`); - } else if (this.contentVisible) { - this.save(); - ctx.scale(fontSize, fontSize); - ctx.transform(...fontMatrix); - this.executeOperatorList(operatorList); - this.restore(); - } - const p = [glyph.width, 0]; - Util.applyTransform(p, fontMatrix); - width = p[0] * fontSize + spacing; - ctx.translate(width, 0); - current.x += width * textHScale; - } - ctx.restore(); - if (dependencyTracker) { - this.dependencyTracker = dependencyTracker; - } - } - setCharWidth(opIdx, xWidth, yWidth) {} - setCharWidthAndBounds(opIdx, xWidth, yWidth, llx, lly, urx, ury) { - const clip = new Path2D(); - clip.rect(llx, lly, urx - llx, ury - lly); - this.ctx.clip(clip); - this.dependencyTracker?.recordBBox(opIdx, this.ctx, llx, urx, lly, ury).recordClipBox(opIdx, this.ctx, llx, urx, lly, ury); - this.endPath(opIdx); - } - getColorN_Pattern(opIdx, IR) { - let pattern; - if (IR[0] === "TilingPattern") { - const baseTransform = this.baseTransform || getCurrentTransform(this.ctx); - const canvasGraphicsFactory = { - createCanvasGraphics: (ctx, renderingOpIdx) => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { - optionalContentConfig: this.optionalContentConfig, - markedContentStack: this.markedContentStack - }, undefined, undefined, this.dependencyTracker ? new CanvasNestedDependencyTracker(this.dependencyTracker, renderingOpIdx, true) : null) - }; - pattern = new TilingPattern(IR, this.ctx, canvasGraphicsFactory, baseTransform); - } else { - pattern = this._getPattern(opIdx, IR[1], IR[2]); - } - return pattern; - } - setStrokeColorN(opIdx, ...args) { - this.dependencyTracker?.recordSimpleData("strokeColor", opIdx); - this.current.strokeColor = this.getColorN_Pattern(opIdx, args); - this.current.patternStroke = true; - } - setFillColorN(opIdx, ...args) { - this.dependencyTracker?.recordSimpleData("fillColor", opIdx); - this.current.fillColor = this.getColorN_Pattern(opIdx, args); - this.current.patternFill = true; - } - setStrokeRGBColor(opIdx, color) { - this.dependencyTracker?.recordSimpleData("strokeColor", opIdx); - this.ctx.strokeStyle = this.current.strokeColor = color; - this.current.patternStroke = false; - } - setStrokeTransparent(opIdx) { - this.dependencyTracker?.recordSimpleData("strokeColor", opIdx); - this.ctx.strokeStyle = this.current.strokeColor = "transparent"; - this.current.patternStroke = false; - } - setFillRGBColor(opIdx, color) { - this.dependencyTracker?.recordSimpleData("fillColor", opIdx); - this.ctx.fillStyle = this.current.fillColor = color; - this.current.patternFill = false; - } - setFillTransparent(opIdx) { - this.dependencyTracker?.recordSimpleData("fillColor", opIdx); - this.ctx.fillStyle = this.current.fillColor = "transparent"; - this.current.patternFill = false; - } - _getPattern(opIdx, objId, matrix = null) { - let pattern; - if (this.cachedPatterns.has(objId)) { - pattern = this.cachedPatterns.get(objId); - } else { - pattern = getShadingPattern(this.getObject(opIdx, objId)); - this.cachedPatterns.set(objId, pattern); - } - if (matrix) { - pattern.matrix = matrix; - } - return pattern; - } - shadingFill(opIdx, objId) { - if (!this.contentVisible) { - return; - } - const ctx = this.ctx; - this.save(opIdx); - const pattern = this._getPattern(opIdx, objId); - ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING, opIdx); - const inv = getCurrentTransformInverse(ctx); - if (inv) { - const { - width, - height - } = ctx.canvas; - const minMax = MIN_MAX_INIT.slice(); - Util.axialAlignedBoundingBox([0, 0, width, height], inv, minMax); - const [x0, y0, x1, y1] = minMax; - this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0); - } else { - this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); - } - this.dependencyTracker?.resetBBox(opIdx).recordFullPageBBox(opIdx).recordDependencies(opIdx, Dependencies.transform).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx); - this.compose(this.current.getClippedPathBoundingBox()); - this.restore(opIdx); - } - beginInlineImage() { - unreachable("Should not call beginInlineImage"); - } - beginImageData() { - unreachable("Should not call beginImageData"); - } - paintFormXObjectBegin(opIdx, matrix, bbox) { - if (!this.contentVisible) { - return; - } - this.save(opIdx); - this.baseTransformStack.push(this.baseTransform); - if (matrix) { - this.transform(opIdx, ...matrix); - } - this.baseTransform = getCurrentTransform(this.ctx); - if (bbox) { - Util.axialAlignedBoundingBox(bbox, this.baseTransform, this.current.minMax); - const [x0, y0, x1, y1] = bbox; - const clip = new Path2D(); - clip.rect(x0, y0, x1 - x0, y1 - y0); - this.ctx.clip(clip); - this.dependencyTracker?.recordClipBox(opIdx, this.ctx, x0, x1, y0, y1); - this.endPath(opIdx); - } - } - paintFormXObjectEnd(opIdx) { - if (!this.contentVisible) { - return; - } - this.restore(opIdx); - this.baseTransform = this.baseTransformStack.pop(); - } - beginGroup(opIdx, group) { - if (!this.contentVisible) { - return; - } - this.save(opIdx); - if (this.inSMaskMode) { - this.endSMaskMode(); - this.current.activeSMask = null; - } - const currentCtx = this.ctx; - if (!group.isolated) { - info("TODO: Support non-isolated groups."); - } - if (group.knockout) { - warn("Knockout groups not supported."); - } - const currentTransform = getCurrentTransform(currentCtx); - if (group.matrix) { - currentCtx.transform(...group.matrix); - } - if (!group.bbox) { - throw new Error("Bounding box is required."); - } - let bounds = MIN_MAX_INIT.slice(); - Util.axialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx), bounds); - const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height]; - bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; - const offsetX = Math.floor(bounds[0]); - const offsetY = Math.floor(bounds[1]); - const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1); - const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1); - this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]); - let cacheId = "groupAt" + this.groupLevel; - if (group.smask) { - cacheId += "_smask_" + this.smaskCounter++ % 2; - } - const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); - const groupCtx = scratchCanvas.context; - groupCtx.translate(-offsetX, -offsetY); - groupCtx.transform(...currentTransform); - let clip = new Path2D(); - const [x0, y0, x1, y1] = group.bbox; - clip.rect(x0, y0, x1 - x0, y1 - y0); - if (group.matrix) { - const path = new Path2D(); - path.addPath(clip, new DOMMatrix(group.matrix)); - clip = path; - } - groupCtx.clip(clip); - if (group.smask) { - this.smaskStack.push({ - canvas: scratchCanvas.canvas, - context: groupCtx, - offsetX, - offsetY, - subtype: group.smask.subtype, - backdrop: group.smask.backdrop, - transferMap: group.smask.transferMap || null, - startTransformInverse: null - }); - } - if (!group.smask || this.dependencyTracker) { - currentCtx.setTransform(1, 0, 0, 1, 0, 0); - currentCtx.translate(offsetX, offsetY); - currentCtx.save(); - } - copyCtxState(currentCtx, groupCtx); - this.ctx = groupCtx; - this.dependencyTracker?.inheritSimpleDataAsFutureForcedDependencies(["fillAlpha", "strokeAlpha", "globalCompositeOperation"]).pushBaseTransform(currentCtx); - this.setGState(opIdx, [["BM", "source-over"], ["ca", 1], ["CA", 1], ["TR", null]]); - this.groupStack.push(currentCtx); - this.groupLevel++; - } - endGroup(opIdx, group) { - if (!this.contentVisible) { - return; - } - this.groupLevel--; - const groupCtx = this.ctx; - const ctx = this.groupStack.pop(); - this.ctx = ctx; - this.ctx.imageSmoothingEnabled = false; - this.dependencyTracker?.popBaseTransform(); - if (group.smask) { - this.tempSMask = this.smaskStack.pop(); - this.restore(opIdx); - if (this.dependencyTracker) { - this.ctx.restore(); - } - } else { - this.ctx.restore(); - const currentMtx = getCurrentTransform(this.ctx); - this.restore(opIdx); - this.ctx.save(); - this.ctx.setTransform(...currentMtx); - const dirtyBox = MIN_MAX_INIT.slice(); - Util.axialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx, dirtyBox); - this.ctx.drawImage(groupCtx.canvas, 0, 0); - this.ctx.restore(); - this.compose(dirtyBox); - } - } - beginAnnotation(opIdx, id, rect, transform, matrix, hasOwnCanvas) { - this.#restoreInitialState(); - resetCtxToDefault(this.ctx); - this.ctx.save(); - this.save(opIdx); - if (this.baseTransform) { - this.ctx.setTransform(...this.baseTransform); - } - if (rect) { - const width = rect[2] - rect[0]; - const height = rect[3] - rect[1]; - if (hasOwnCanvas && this.annotationCanvasMap) { - transform = transform.slice(); - transform[4] -= rect[0]; - transform[5] -= rect[1]; - rect = rect.slice(); - rect[0] = rect[1] = 0; - rect[2] = width; - rect[3] = height; - Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx), XY); - const { - viewportScale - } = this; - const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale); - const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale); - this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight); - const { - canvas, - context - } = this.annotationCanvas; - this.annotationCanvasMap.set(id, canvas); - this.annotationCanvas.savedCtx = this.ctx; - this.ctx = context; - this.ctx.save(); - this.ctx.setTransform(XY[0], 0, 0, -XY[1], 0, height * XY[1]); - resetCtxToDefault(this.ctx); - } else { - resetCtxToDefault(this.ctx); - this.endPath(opIdx); - const clip = new Path2D(); - clip.rect(rect[0], rect[1], width, height); - this.ctx.clip(clip); - } - } - this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); - this.transform(opIdx, ...transform); - this.transform(opIdx, ...matrix); - } - endAnnotation(opIdx) { - if (this.annotationCanvas) { - this.ctx.restore(); - this.#drawFilter(); - this.ctx = this.annotationCanvas.savedCtx; - delete this.annotationCanvas.savedCtx; - delete this.annotationCanvas; - } - } - paintImageMaskXObject(opIdx, img) { - if (!this.contentVisible) { - return; - } - const count = img.count; - img = this.getObject(opIdx, img.data, img); - img.count = count; - const ctx = this.ctx; - const mask = this._createMaskCanvas(opIdx, img); - const maskCanvas = mask.canvas; - ctx.save(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY); - this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, mask.offsetX, mask.offsetX + maskCanvas.width, mask.offsetY, mask.offsetY + maskCanvas.height).recordOperation(opIdx); - ctx.restore(); - this.compose(); - } - paintImageMaskXObjectRepeat(opIdx, img, scaleX, skewX = 0, skewY = 0, scaleY, positions) { - if (!this.contentVisible) { - return; - } - img = this.getObject(opIdx, img.data, img); - const ctx = this.ctx; - ctx.save(); - const currentTransform = getCurrentTransform(ctx); - ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0); - const mask = this._createMaskCanvas(opIdx, img); - ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]); - this.dependencyTracker?.resetBBox(opIdx); - for (let i = 0, ii = positions.length; i < ii; i += 2) { - const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]); - ctx.drawImage(mask.canvas, trans[4], trans[5]); - this.dependencyTracker?.recordBBox(opIdx, this.ctx, trans[4], trans[4] + mask.canvas.width, trans[5], trans[5] + mask.canvas.height); - } - ctx.restore(); - this.compose(); - this.dependencyTracker?.recordOperation(opIdx); - } - paintImageMaskXObjectGroup(opIdx, images) { - if (!this.contentVisible) { - return; - } - const ctx = this.ctx; - const fillColor = this.current.fillColor; - const isPatternFill = this.current.patternFill; - this.dependencyTracker?.resetBBox(opIdx).recordDependencies(opIdx, Dependencies.transformAndFill); - for (const image of images) { - const { - data, - width, - height, - transform - } = image; - const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); - const maskCtx = maskCanvas.context; - maskCtx.save(); - const img = this.getObject(opIdx, data, image); - putBinaryImageMask(maskCtx, img); - maskCtx.globalCompositeOperation = "source-in"; - maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx) : fillColor; - maskCtx.fillRect(0, 0, width, height); - maskCtx.restore(); - ctx.save(); - ctx.transform(...transform); - ctx.scale(1, -1); - drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); - this.dependencyTracker?.recordBBox(opIdx, ctx, 0, width, 0, height); - ctx.restore(); - } - this.compose(); - this.dependencyTracker?.recordOperation(opIdx); - } - paintImageXObject(opIdx, objId) { - if (!this.contentVisible) { - return; - } - const imgData = this.getObject(opIdx, objId); - if (!imgData) { - warn("Dependent image isn't ready yet"); - return; - } - this.paintInlineImageXObject(opIdx, imgData); - } - paintImageXObjectRepeat(opIdx, objId, scaleX, scaleY, positions) { - if (!this.contentVisible) { - return; - } - const imgData = this.getObject(opIdx, objId); - if (!imgData) { - warn("Dependent image isn't ready yet"); - return; - } - const width = imgData.width; - const height = imgData.height; - const map = []; - for (let i = 0, ii = positions.length; i < ii; i += 2) { - map.push({ - transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]], - x: 0, - y: 0, - w: width, - h: height - }); - } - this.paintInlineImageXObjectGroup(opIdx, imgData, map); - } - applyTransferMapsToCanvas(ctx) { - if (this.current.transferMaps !== "none") { - ctx.filter = this.current.transferMaps; - ctx.drawImage(ctx.canvas, 0, 0); - ctx.filter = "none"; - } - return ctx.canvas; - } - applyTransferMapsToBitmap(imgData) { - if (this.current.transferMaps === "none") { - return imgData.bitmap; - } - const { - bitmap, - width, - height - } = imgData; - const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); - const tmpCtx = tmpCanvas.context; - tmpCtx.filter = this.current.transferMaps; - tmpCtx.drawImage(bitmap, 0, 0); - tmpCtx.filter = "none"; - return tmpCanvas.canvas; - } - paintInlineImageXObject(opIdx, imgData) { - if (!this.contentVisible) { - return; - } - const width = imgData.width; - const height = imgData.height; - const ctx = this.ctx; - this.save(opIdx); - const { - filter - } = ctx; - if (filter !== "none" && filter !== "") { - ctx.filter = "none"; - } - ctx.scale(1 / width, -1 / height); - let imgToPaint; - if (imgData.bitmap) { - imgToPaint = this.applyTransferMapsToBitmap(imgData); - } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) { - imgToPaint = imgData; - } else { - const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); - const tmpCtx = tmpCanvas.context; - putBinaryImageData(tmpCtx, imgData); - imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); - } - const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx)); - ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate); - this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, ctx, 0, width, -height, 0).recordDependencies(opIdx, Dependencies.imageXObject).recordOperation(opIdx); - drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height); - this.compose(); - this.restore(opIdx); - } - paintInlineImageXObjectGroup(opIdx, imgData, map) { - if (!this.contentVisible) { - return; - } - const ctx = this.ctx; - let imgToPaint; - if (imgData.bitmap) { - imgToPaint = imgData.bitmap; - } else { - const w = imgData.width; - const h = imgData.height; - const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h); - const tmpCtx = tmpCanvas.context; - putBinaryImageData(tmpCtx, imgData); - imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); - } - this.dependencyTracker?.resetBBox(opIdx); - for (const entry of map) { - ctx.save(); - ctx.transform(...entry.transform); - ctx.scale(1, -1); - drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); - this.dependencyTracker?.recordBBox(opIdx, ctx, 0, 1, -1, 0); - ctx.restore(); - } - this.dependencyTracker?.recordOperation(opIdx); - this.compose(); - } - paintSolidColorImageMask(opIdx) { - if (!this.contentVisible) { - return; - } - this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, 0, 1, 0, 1).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx); - this.ctx.fillRect(0, 0, 1, 1); - this.compose(); - } - markPoint(opIdx, tag) {} - markPointProps(opIdx, tag, properties) {} - beginMarkedContent(opIdx, tag) { - this.dependencyTracker?.beginMarkedContent(opIdx); - this.markedContentStack.push({ - visible: true - }); - } - beginMarkedContentProps(opIdx, tag, properties) { - this.dependencyTracker?.beginMarkedContent(opIdx); - if (tag === "OC") { - this.markedContentStack.push({ - visible: this.optionalContentConfig.isVisible(properties) - }); - } else { - this.markedContentStack.push({ - visible: true - }); - } - this.contentVisible = this.isContentVisible(); - } - endMarkedContent(opIdx) { - this.dependencyTracker?.endMarkedContent(opIdx); - this.markedContentStack.pop(); - this.contentVisible = this.isContentVisible(); - } - beginCompat(opIdx) {} - endCompat(opIdx) {} - consumePath(opIdx, path, clipBox) { - const isEmpty = this.current.isEmptyClip(); - if (this.pendingClip) { - this.current.updateClipFromPath(); - } - if (!this.pendingClip) { - this.compose(clipBox); - } - const ctx = this.ctx; - if (this.pendingClip) { - if (!isEmpty) { - if (this.pendingClip === EO_CLIP) { - ctx.clip(path, "evenodd"); - } else { - ctx.clip(path); - } - } - this.pendingClip = null; - this.dependencyTracker?.bboxToClipBoxDropOperation(opIdx).recordFutureForcedDependency("clipPath", opIdx); - } else { - this.dependencyTracker?.recordOperation(opIdx); - } - this.current.startNewPathAndClipBox(this.current.clipBox); - } - getSinglePixelWidth() { - if (!this._cachedGetSinglePixelWidth) { - const m = getCurrentTransform(this.ctx); - if (m[1] === 0 && m[2] === 0) { - this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3])); - } else { - const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]); - const normX = Math.hypot(m[0], m[2]); - const normY = Math.hypot(m[1], m[3]); - this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet; - } - } - return this._cachedGetSinglePixelWidth; - } - getScaleForStroking() { - if (this._cachedScaleForStroking[0] === -1) { - const { - lineWidth - } = this.current; - const { - a, - b, - c, - d - } = this.ctx.getTransform(); - let scaleX, scaleY; - if (b === 0 && c === 0) { - const normX = Math.abs(a); - const normY = Math.abs(d); - if (normX === normY) { - if (lineWidth === 0) { - scaleX = scaleY = 1 / normX; - } else { - const scaledLineWidth = normX * lineWidth; - scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1; - } - } else if (lineWidth === 0) { - scaleX = 1 / normX; - scaleY = 1 / normY; - } else { - const scaledXLineWidth = normX * lineWidth; - const scaledYLineWidth = normY * lineWidth; - scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1; - scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1; - } - } else { - const absDet = Math.abs(a * d - b * c); - const normX = Math.hypot(a, b); - const normY = Math.hypot(c, d); - if (lineWidth === 0) { - scaleX = normY / absDet; - scaleY = normX / absDet; - } else { - const baseArea = lineWidth * absDet; - scaleX = normY > baseArea ? normY / baseArea : 1; - scaleY = normX > baseArea ? normX / baseArea : 1; - } - } - this._cachedScaleForStroking[0] = scaleX; - this._cachedScaleForStroking[1] = scaleY; - } - return this._cachedScaleForStroking; - } - rescaleAndStroke(path, saveRestore) { - const { - ctx, - current: { - lineWidth - } - } = this; - const [scaleX, scaleY] = this.getScaleForStroking(); - if (scaleX === scaleY) { - ctx.lineWidth = (lineWidth || 1) * scaleX; - ctx.stroke(path); - return; - } - const dashes = ctx.getLineDash(); - if (saveRestore) { - ctx.save(); - } - ctx.scale(scaleX, scaleY); - SCALE_MATRIX.a = 1 / scaleX; - SCALE_MATRIX.d = 1 / scaleY; - const newPath = new Path2D(); - newPath.addPath(path, SCALE_MATRIX); - if (dashes.length > 0) { - const scale = Math.max(scaleX, scaleY); - ctx.setLineDash(dashes.map(x => x / scale)); - ctx.lineDashOffset /= scale; - } - ctx.lineWidth = lineWidth || 1; - ctx.stroke(newPath); - if (saveRestore) { - ctx.restore(); - } - } - isContentVisible() { - for (let i = this.markedContentStack.length - 1; i >= 0; i--) { - if (!this.markedContentStack[i].visible) { - return false; - } - } - return true; - } -} -for (const op in OPS) { - if (CanvasGraphics.prototype[op] !== undefined) { - CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; - } -} - -;// ./src/display/worker_options.js -class GlobalWorkerOptions { - static #port = null; - static #src = ""; - static get workerPort() { - return this.#port; - } - static set workerPort(val) { - if (!(typeof Worker !== "undefined" && val instanceof Worker) && val !== null) { - throw new Error("Invalid `workerPort` type."); - } - this.#port = val; - } - static get workerSrc() { - return this.#src; - } - static set workerSrc(val) { - if (typeof val !== "string") { - throw new Error("Invalid `workerSrc` type."); - } - this.#src = val; - } -} - -;// ./src/display/metadata.js -class Metadata { - #map; - #data; - constructor({ - parsedData, - rawData - }) { - this.#map = parsedData; - this.#data = rawData; - } - getRaw() { - return this.#data; - } - get(name) { - return this.#map.get(name) ?? null; - } - [Symbol.iterator]() { - return this.#map.entries(); - } -} - -;// ./src/display/optional_content_config.js - - -const INTERNAL = Symbol("INTERNAL"); -class OptionalContentGroup { - #isDisplay = false; - #isPrint = false; - #userSet = false; - #visible = true; - constructor(renderingIntent, { - name, - intent, - usage, - rbGroups - }) { - this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY); - this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); - this.name = name; - this.intent = intent; - this.usage = usage; - this.rbGroups = rbGroups; - } - get visible() { - if (this.#userSet) { - return this.#visible; - } - if (!this.#visible) { - return false; - } - const { - print, - view - } = this.usage; - if (this.#isDisplay) { - return view?.viewState !== "OFF"; - } else if (this.#isPrint) { - return print?.printState !== "OFF"; - } - return true; - } - _setVisible(internal, visible, userSet = false) { - if (internal !== INTERNAL) { - unreachable("Internal method `_setVisible` called."); - } - this.#userSet = userSet; - this.#visible = visible; - } -} -class OptionalContentConfig { - #cachedGetHash = null; - #groups = new Map(); - #initialHash = null; - #order = null; - constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) { - this.renderingIntent = renderingIntent; - this.name = null; - this.creator = null; - if (data === null) { - return; - } - this.name = data.name; - this.creator = data.creator; - this.#order = data.order; - for (const group of data.groups) { - this.#groups.set(group.id, new OptionalContentGroup(renderingIntent, group)); - } - if (data.baseState === "OFF") { - for (const group of this.#groups.values()) { - group._setVisible(INTERNAL, false); - } - } - for (const on of data.on) { - this.#groups.get(on)._setVisible(INTERNAL, true); - } - for (const off of data.off) { - this.#groups.get(off)._setVisible(INTERNAL, false); - } - this.#initialHash = this.getHash(); - } - #evaluateVisibilityExpression(array) { - const length = array.length; - if (length < 2) { - return true; - } - const operator = array[0]; - for (let i = 1; i < length; i++) { - const element = array[i]; - let state; - if (Array.isArray(element)) { - state = this.#evaluateVisibilityExpression(element); - } else if (this.#groups.has(element)) { - state = this.#groups.get(element).visible; - } else { - warn(`Optional content group not found: ${element}`); - return true; - } - switch (operator) { - case "And": - if (!state) { - return false; - } - break; - case "Or": - if (state) { - return true; - } - break; - case "Not": - return !state; - default: - return true; - } - } - return operator === "And"; - } - isVisible(group) { - if (this.#groups.size === 0) { - return true; - } - if (!group) { - info("Optional content group not defined."); - return true; - } - if (group.type === "OCG") { - if (!this.#groups.has(group.id)) { - warn(`Optional content group not found: ${group.id}`); - return true; - } - return this.#groups.get(group.id).visible; - } else if (group.type === "OCMD") { - if (group.expression) { - return this.#evaluateVisibilityExpression(group.expression); - } - if (!group.policy || group.policy === "AnyOn") { - for (const id of group.ids) { - if (!this.#groups.has(id)) { - warn(`Optional content group not found: ${id}`); - return true; - } - if (this.#groups.get(id).visible) { - return true; - } - } - return false; - } else if (group.policy === "AllOn") { - for (const id of group.ids) { - if (!this.#groups.has(id)) { - warn(`Optional content group not found: ${id}`); - return true; - } - if (!this.#groups.get(id).visible) { - return false; - } - } - return true; - } else if (group.policy === "AnyOff") { - for (const id of group.ids) { - if (!this.#groups.has(id)) { - warn(`Optional content group not found: ${id}`); - return true; - } - if (!this.#groups.get(id).visible) { - return true; - } - } - return false; - } else if (group.policy === "AllOff") { - for (const id of group.ids) { - if (!this.#groups.has(id)) { - warn(`Optional content group not found: ${id}`); - return true; - } - if (this.#groups.get(id).visible) { - return false; - } - } - return true; - } - warn(`Unknown optional content policy ${group.policy}.`); - return true; - } - warn(`Unknown group type ${group.type}.`); - return true; - } - setVisibility(id, visible = true, preserveRB = true) { - const group = this.#groups.get(id); - if (!group) { - warn(`Optional content group not found: ${id}`); - return; - } - if (preserveRB && visible && group.rbGroups.length) { - for (const rbGroup of group.rbGroups) { - for (const otherId of rbGroup) { - if (otherId !== id) { - this.#groups.get(otherId)?._setVisible(INTERNAL, false, true); - } - } - } - } - group._setVisible(INTERNAL, !!visible, true); - this.#cachedGetHash = null; - } - setOCGState({ - state, - preserveRB - }) { - let operator; - for (const elem of state) { - switch (elem) { - case "ON": - case "OFF": - case "Toggle": - operator = elem; - continue; - } - const group = this.#groups.get(elem); - if (!group) { - continue; - } - switch (operator) { - case "ON": - this.setVisibility(elem, true, preserveRB); - break; - case "OFF": - this.setVisibility(elem, false, preserveRB); - break; - case "Toggle": - this.setVisibility(elem, !group.visible, preserveRB); - break; - } - } - this.#cachedGetHash = null; - } - get hasInitialVisibility() { - return this.#initialHash === null || this.getHash() === this.#initialHash; - } - getOrder() { - if (!this.#groups.size) { - return null; - } - if (this.#order) { - return this.#order.slice(); - } - return [...this.#groups.keys()]; - } - getGroup(id) { - return this.#groups.get(id) || null; - } - getHash() { - if (this.#cachedGetHash !== null) { - return this.#cachedGetHash; - } - const hash = new MurmurHash3_64(); - for (const [id, group] of this.#groups) { - hash.update(`${id}:${group.visible}`); - } - return this.#cachedGetHash = hash.hexdigest(); - } - [Symbol.iterator]() { - return this.#groups.entries(); - } -} - -;// ./src/shared/base_pdf_stream.js - -class BasePDFStream { - #PDFStreamReader = null; - #PDFStreamRangeReader = null; - _fullReader = null; - _rangeReaders = new Set(); - _source = null; - constructor(source, PDFStreamReader, PDFStreamRangeReader) { - this._source = source; - this.#PDFStreamReader = PDFStreamReader; - this.#PDFStreamRangeReader = PDFStreamRangeReader; - } - get _progressiveDataLength() { - return this._fullReader?._loaded ?? 0; - } - getFullReader() { - assert(!this._fullReader, "BasePDFStream.getFullReader can only be called once."); - return this._fullReader = new this.#PDFStreamReader(this); - } - getRangeReader(begin, end) { - if (end <= this._progressiveDataLength) { - return null; - } - const reader = new this.#PDFStreamRangeReader(this, begin, end); - this._rangeReaders.add(reader); - return reader; - } - cancelAllRequests(reason) { - this._fullReader?.cancel(reason); - for (const reader of new Set(this._rangeReaders)) { - reader.cancel(reason); - } - } -} -class BasePDFStreamReader { - onProgress = null; - _contentLength = 0; - _filename = null; - _headersCapability = Promise.withResolvers(); - _isRangeSupported = false; - _isStreamingSupported = false; - _loaded = 0; - _stream = null; - constructor(stream) { - this._stream = stream; - } - _callOnProgress() { - this.onProgress?.({ - loaded: this._loaded, - total: this._contentLength - }); - } - get headersReady() { - return this._headersCapability.promise; - } - get filename() { - return this._filename; - } - get contentLength() { - return this._contentLength; - } - get isRangeSupported() { - return this._isRangeSupported; - } - get isStreamingSupported() { - return this._isStreamingSupported; - } - async read() { - unreachable("Abstract method `read` called"); - } - cancel(reason) { - unreachable("Abstract method `cancel` called"); - } -} -class BasePDFStreamRangeReader { - _stream = null; - constructor(stream, begin, end) { - this._stream = stream; - } - async read() { - unreachable("Abstract method `read` called"); - } - cancel(reason) { - unreachable("Abstract method `cancel` called"); - } -} - -;// ./src/display/transport_stream.js - - - -function getArrayBuffer(val) { - return val instanceof Uint8Array && val.byteLength === val.buffer.byteLength ? val.buffer : new Uint8Array(val).buffer; -} -function endRequests() { - for (const capability of this._requests) { - capability.resolve({ - value: undefined, - done: true - }); - } - this._requests.length = 0; -} -class PDFDataTransportStream extends BasePDFStream { - _progressiveDone = false; - _queuedChunks = []; - constructor(source) { - super(source, PDFDataTransportStreamReader, PDFDataTransportStreamRangeReader); - const { - pdfDataRangeTransport - } = source; - const { - initialData, - progressiveDone - } = pdfDataRangeTransport; - if (initialData?.length > 0) { - const buffer = getArrayBuffer(initialData); - this._queuedChunks.push(buffer); - } - this._progressiveDone = progressiveDone; - pdfDataRangeTransport.addRangeListener((begin, chunk) => { - this.#onReceiveData(begin, chunk); - }); - pdfDataRangeTransport.addProgressiveReadListener(chunk => { - this.#onReceiveData(undefined, chunk); - }); - pdfDataRangeTransport.addProgressiveDoneListener(() => { - this._fullReader?.progressiveDone(); - this._progressiveDone = true; - }); - pdfDataRangeTransport.transportReady(); - } - #onReceiveData(begin, chunk) { - const buffer = getArrayBuffer(chunk); - if (begin === undefined) { - if (this._fullReader) { - this._fullReader._enqueue(buffer); - } else { - this._queuedChunks.push(buffer); - } - } else { - const rangeReader = this._rangeReaders.keys().find(r => r._begin === begin); - assert(rangeReader, "#onReceiveData - no `PDFDataTransportStreamRangeReader` instance found."); - rangeReader._enqueue(buffer); - } - } - getFullReader() { - const reader = super.getFullReader(); - this._queuedChunks = null; - return reader; - } - getRangeReader(begin, end) { - const reader = super.getRangeReader(begin, end); - if (reader) { - reader.onDone = () => this._rangeReaders.delete(reader); - this._source.pdfDataRangeTransport.requestDataRange(begin, end); - } - return reader; - } - cancelAllRequests(reason) { - super.cancelAllRequests(reason); - this._source.pdfDataRangeTransport.abort(); - } -} -class PDFDataTransportStreamReader extends BasePDFStreamReader { - #endRequests = endRequests.bind(this); - _done = false; - _queuedChunks = null; - _requests = []; - constructor(stream) { - super(stream); - const { - pdfDataRangeTransport, - disableRange, - disableStream - } = stream._source; - const { - length, - contentDispositionFilename - } = pdfDataRangeTransport; - this._queuedChunks = stream._queuedChunks || []; - for (const chunk of this._queuedChunks) { - this._loaded += chunk.byteLength; - } - this._done = stream._progressiveDone; - this._contentLength = length; - this._isStreamingSupported = !disableStream; - this._isRangeSupported = !disableRange; - if (isPdfFile(contentDispositionFilename)) { - this._filename = contentDispositionFilename; - } - this._headersCapability.resolve(); - const loaded = this._loaded; - Promise.resolve().then(() => { - if (loaded > 0 && this._loaded === loaded) { - this._callOnProgress(); - } - }); - } - _enqueue(chunk) { - if (this._done) { - return; - } - if (this._requests.length > 0) { - const capability = this._requests.shift(); - capability.resolve({ - value: chunk, - done: false - }); - } else { - this._queuedChunks.push(chunk); - } - this._loaded += chunk.byteLength; - this._callOnProgress(); - } - async read() { - if (this._queuedChunks.length > 0) { - const chunk = this._queuedChunks.shift(); - return { - value: chunk, - done: false - }; - } - if (this._done) { - return { - value: undefined, - done: true - }; - } - const capability = Promise.withResolvers(); - this._requests.push(capability); - return capability.promise; - } - cancel(reason) { - this._done = true; - this.#endRequests(); - } - progressiveDone() { - this._done ||= true; - if (this._queuedChunks.length === 0) { - this.#endRequests(); - } - } -} -class PDFDataTransportStreamRangeReader extends BasePDFStreamRangeReader { - #endRequests = endRequests.bind(this); - onDone = null; - _begin = -1; - _done = false; - _queuedChunk = null; - _requests = []; - constructor(stream, begin, end) { - super(stream, begin, end); - this._begin = begin; - } - _enqueue(chunk) { - if (this._done) { - return; - } - if (this._requests.length === 0) { - this._queuedChunk = chunk; - } else { - const capability = this._requests.shift(); - capability.resolve({ - value: chunk, - done: false - }); - this.#endRequests(); - } - this._done = true; - this.onDone?.(); - } - async read() { - if (this._queuedChunk) { - const chunk = this._queuedChunk; - this._queuedChunk = null; - return { - value: chunk, - done: false - }; - } - if (this._done) { - return { - value: undefined, - done: true - }; - } - const capability = Promise.withResolvers(); - this._requests.push(capability); - return capability.promise; - } - cancel(reason) { - this._done = true; - this.#endRequests(); - this.onDone?.(); - } -} - -;// ./src/display/content_disposition.js - -function getFilenameFromContentDispositionHeader(contentDisposition) { - let needsEncodingFixup = true; - let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition); - if (tmp) { - tmp = tmp[1]; - let filename = rfc2616unquote(tmp); - filename = unescape(filename); - filename = rfc5987decode(filename); - filename = rfc2047decode(filename); - return fixupEncoding(filename); - } - tmp = rfc2231getparam(contentDisposition); - if (tmp) { - const filename = rfc2047decode(tmp); - return fixupEncoding(filename); - } - tmp = toParamRegExp("filename", "i").exec(contentDisposition); - if (tmp) { - tmp = tmp[1]; - let filename = rfc2616unquote(tmp); - filename = rfc2047decode(filename); - return fixupEncoding(filename); - } - function toParamRegExp(attributePattern, flags) { - return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags); - } - function textdecode(encoding, value) { - if (encoding) { - if (!/^[\x00-\xFF]+$/.test(value)) { - return value; - } - try { - const decoder = new TextDecoder(encoding, { - fatal: true - }); - const buffer = stringToBytes(value); - value = decoder.decode(buffer); - needsEncodingFixup = false; - } catch {} - } - return value; - } - function fixupEncoding(value) { - if (needsEncodingFixup && /[\x80-\xff]/.test(value)) { - value = textdecode("utf-8", value); - if (needsEncodingFixup) { - value = textdecode("iso-8859-1", value); - } - } - return value; - } - function rfc2231getparam(contentDispositionStr) { - const matches = []; - let match; - const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig"); - while ((match = iter.exec(contentDispositionStr)) !== null) { - let [, n, quot, part] = match; - n = parseInt(n, 10); - if (n in matches) { - if (n === 0) { - break; - } - continue; - } - matches[n] = [quot, part]; - } - const parts = []; - for (let n = 0; n < matches.length; ++n) { - if (!(n in matches)) { - break; - } - let [quot, part] = matches[n]; - part = rfc2616unquote(part); - if (quot) { - part = unescape(part); - if (n === 0) { - part = rfc5987decode(part); - } - } - parts.push(part); - } - return parts.join(""); - } - function rfc2616unquote(value) { - if (value.startsWith('"')) { - const parts = value.slice(1).split('\\"'); - for (let i = 0; i < parts.length; ++i) { - const quotindex = parts[i].indexOf('"'); - if (quotindex !== -1) { - parts[i] = parts[i].slice(0, quotindex); - parts.length = i + 1; - } - parts[i] = parts[i].replaceAll(/\\(.)/g, "$1"); - } - value = parts.join('"'); - } - return value; - } - function rfc5987decode(extvalue) { - const encodingend = extvalue.indexOf("'"); - if (encodingend === -1) { - return extvalue; - } - const encoding = extvalue.slice(0, encodingend); - const langvalue = extvalue.slice(encodingend + 1); - const value = langvalue.replace(/^[^']*'/, ""); - return textdecode(encoding, value); - } - function rfc2047decode(value) { - if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) { - return value; - } - return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) { - if (encoding === "q" || encoding === "Q") { - text = text.replaceAll("_", " "); - text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) { - return String.fromCharCode(parseInt(hex, 16)); - }); - return textdecode(charset, text); - } - try { - text = atob(text); - } catch {} - return textdecode(charset, text); - }); - } - return ""; -} - -;// ./src/display/network_utils.js - - - -function createHeaders(isHttp, httpHeaders) { - const headers = new Headers(); - if (!isHttp || !httpHeaders || typeof httpHeaders !== "object") { - return headers; - } - for (const key in httpHeaders) { - const val = httpHeaders[key]; - if (val !== undefined) { - headers.append(key, val); - } - } - return headers; -} -function getResponseOrigin(url) { - return URL.parse(url)?.origin ?? null; -} -function validateRangeRequestCapabilities({ - responseHeaders, - isHttp, - rangeChunkSize, - disableRange -}) { - const returnValues = { - allowRangeRequests: false, - suggestedLength: undefined - }; - const length = parseInt(responseHeaders.get("Content-Length"), 10); - if (!Number.isInteger(length)) { - return returnValues; - } - returnValues.suggestedLength = length; - if (length <= 2 * rangeChunkSize) { - return returnValues; - } - if (disableRange || !isHttp) { - return returnValues; - } - if (responseHeaders.get("Accept-Ranges") !== "bytes") { - return returnValues; - } - const contentEncoding = responseHeaders.get("Content-Encoding") || "identity"; - if (contentEncoding !== "identity") { - return returnValues; - } - returnValues.allowRangeRequests = true; - return returnValues; -} -function extractFilenameFromHeader(responseHeaders) { - const contentDisposition = responseHeaders.get("Content-Disposition"); - if (contentDisposition) { - let filename = getFilenameFromContentDispositionHeader(contentDisposition); - if (filename.includes("%")) { - try { - filename = decodeURIComponent(filename); - } catch {} - } - if (isPdfFile(filename)) { - return filename; - } - } - return null; -} -function createResponseError(status, url) { - return new ResponseException(`Unexpected server response (${status}) while retrieving PDF "${url.href}".`, status, status === 404 || status === 0 && url.protocol === "file:"); -} -function ensureResponseOrigin(rangeOrigin, origin) { - if (rangeOrigin !== origin) { - throw new Error(`Expected range response-origin "${rangeOrigin}" to match "${origin}".`); - } -} - -;// ./src/display/fetch_stream.js - - - -function fetchUrl(url, headers, withCredentials, abortController) { - return fetch(url, { - method: "GET", - headers, - signal: abortController.signal, - mode: "cors", - credentials: withCredentials ? "include" : "same-origin", - redirect: "follow" - }); -} -function ensureResponseStatus(status, url) { - if (status !== 200 && status !== 206) { - throw createResponseError(status, url); - } -} -function fetch_stream_getArrayBuffer(val) { - if (val instanceof Uint8Array) { - return val.buffer; - } - if (val instanceof ArrayBuffer) { - return val; - } - throw new Error(`getArrayBuffer - unexpected data: ${val}`); -} -class PDFFetchStream extends BasePDFStream { - _responseOrigin = null; - constructor(source) { - super(source, PDFFetchStreamReader, PDFFetchStreamRangeReader); - const { - httpHeaders, - url - } = source; - assert(/https?:/.test(url.protocol), "PDFFetchStream only supports http(s):// URLs."); - this.headers = createHeaders(true, httpHeaders); - } -} -class PDFFetchStreamReader extends BasePDFStreamReader { - _abortController = new AbortController(); - _reader = null; - constructor(stream) { - super(stream); - const { - disableRange, - disableStream, - length, - rangeChunkSize, - url, - withCredentials - } = stream._source; - this._contentLength = length; - this._isStreamingSupported = !disableStream; - this._isRangeSupported = !disableRange; - const headers = new Headers(stream.headers); - fetchUrl(url, headers, withCredentials, this._abortController).then(response => { - stream._responseOrigin = getResponseOrigin(response.url); - ensureResponseStatus(response.status, url); - this._reader = response.body.getReader(); - const responseHeaders = response.headers; - const { - allowRangeRequests, - suggestedLength - } = validateRangeRequestCapabilities({ - responseHeaders, - isHttp: true, - rangeChunkSize, - disableRange - }); - this._isRangeSupported = allowRangeRequests; - this._contentLength = suggestedLength || this._contentLength; - this._filename = extractFilenameFromHeader(responseHeaders); - if (!this._isStreamingSupported && this._isRangeSupported) { - this.cancel(new AbortException("Streaming is disabled.")); - } - this._headersCapability.resolve(); - }).catch(this._headersCapability.reject); - } - async read() { - await this._headersCapability.promise; - const { - value, - done - } = await this._reader.read(); - if (done) { - return { - value, - done - }; - } - this._loaded += value.byteLength; - this._callOnProgress(); - return { - value: fetch_stream_getArrayBuffer(value), - done: false - }; - } - cancel(reason) { - this._reader?.cancel(reason); - this._abortController.abort(); - } -} -class PDFFetchStreamRangeReader extends BasePDFStreamRangeReader { - _abortController = new AbortController(); - _readCapability = Promise.withResolvers(); - _reader = null; - constructor(stream, begin, end) { - super(stream, begin, end); - const { - url, - withCredentials - } = stream._source; - const headers = new Headers(stream.headers); - headers.append("Range", `bytes=${begin}-${end - 1}`); - fetchUrl(url, headers, withCredentials, this._abortController).then(response => { - const responseOrigin = getResponseOrigin(response.url); - ensureResponseOrigin(responseOrigin, stream._responseOrigin); - ensureResponseStatus(response.status, url); - this._reader = response.body.getReader(); - this._readCapability.resolve(); - }).catch(this._readCapability.reject); - } - async read() { - await this._readCapability.promise; - const { - value, - done - } = await this._reader.read(); - if (done) { - return { - value, - done - }; - } - return { - value: fetch_stream_getArrayBuffer(value), - done: false - }; - } - cancel(reason) { - this._reader?.cancel(reason); - this._abortController.abort(); - } -} - -;// ./src/display/network.js - - - - -const OK_RESPONSE = 200; -const PARTIAL_CONTENT_RESPONSE = 206; -function network_getArrayBuffer(val) { - return typeof val !== "string" ? val : stringToBytes(val).buffer; -} -class PDFNetworkStream extends BasePDFStream { - #pendingRequests = new WeakMap(); - _responseOrigin = null; - constructor(source) { - super(source, PDFNetworkStreamReader, PDFNetworkStreamRangeReader); - const { - httpHeaders, - url - } = source; - this.url = url; - this.isHttp = /https?:/.test(url.protocol); - this.headers = createHeaders(this.isHttp, httpHeaders); - } - _request(args) { - const xhr = new XMLHttpRequest(); - const pendingRequest = { - validateStatus: null, - onHeadersReceived: args.onHeadersReceived, - onDone: args.onDone, - onError: args.onError, - onProgress: args.onProgress - }; - this.#pendingRequests.set(xhr, pendingRequest); - xhr.open("GET", this.url); - xhr.withCredentials = this._source.withCredentials; - for (const [key, val] of this.headers) { - xhr.setRequestHeader(key, val); - } - if (this.isHttp && "begin" in args && "end" in args) { - xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`); - pendingRequest.validateStatus = status => status === PARTIAL_CONTENT_RESPONSE || status === OK_RESPONSE; - } else { - pendingRequest.validateStatus = status => status === OK_RESPONSE; - } - xhr.responseType = "arraybuffer"; - assert(args.onError, "Expected `onError` callback to be provided."); - xhr.onerror = () => args.onError(xhr.status); - xhr.onreadystatechange = this.#onStateChange.bind(this, xhr); - xhr.onprogress = this.#onProgress.bind(this, xhr); - xhr.send(null); - return xhr; - } - #onProgress(xhr, evt) { - const pendingRequest = this.#pendingRequests.get(xhr); - pendingRequest?.onProgress?.(evt); - } - #onStateChange(xhr, evt) { - const pendingRequest = this.#pendingRequests.get(xhr); - if (!pendingRequest) { - return; - } - if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { - pendingRequest.onHeadersReceived(); - delete pendingRequest.onHeadersReceived; - } - if (xhr.readyState !== 4) { - return; - } - if (!this.#pendingRequests.has(xhr)) { - return; - } - this.#pendingRequests.delete(xhr); - if (xhr.status === 0 && this.isHttp) { - pendingRequest.onError(xhr.status); - return; - } - const xhrStatus = xhr.status || OK_RESPONSE; - if (!pendingRequest.validateStatus(xhrStatus)) { - pendingRequest.onError(xhr.status); - return; - } - const chunk = network_getArrayBuffer(xhr.response); - if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { - const rangeHeader = xhr.getResponseHeader("Content-Range"); - if (/bytes (\d+)-(\d+)\/(\d+)/.test(rangeHeader)) { - pendingRequest.onDone(chunk); - } else { - warn(`Missing or invalid "Content-Range" header.`); - pendingRequest.onError(0); - } - } else if (chunk) { - pendingRequest.onDone(chunk); - } else { - pendingRequest.onError(xhr.status); - } - } - _abortRequest(xhr) { - if (this.#pendingRequests.has(xhr)) { - this.#pendingRequests.delete(xhr); - xhr.abort(); - } - } - getRangeReader(begin, end) { - const reader = super.getRangeReader(begin, end); - if (reader) { - reader.onClosed = () => this._rangeReaders.delete(reader); - } - return reader; - } -} -class PDFNetworkStreamReader extends BasePDFStreamReader { - #endRequests = endRequests.bind(this); - _cachedChunks = []; - _done = false; - _requests = []; - _storedError = null; - constructor(stream) { - super(stream); - const { - length - } = stream._source; - this._contentLength = length; - this._fullRequestXhr = stream._request({ - onHeadersReceived: this.#onHeadersReceived.bind(this), - onDone: this.#onDone.bind(this), - onError: this.#onError.bind(this), - onProgress: this.#onProgress.bind(this) - }); - } - #onHeadersReceived() { - const stream = this._stream; - const { - disableRange, - rangeChunkSize - } = stream._source; - const fullRequestXhr = this._fullRequestXhr; - stream._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL); - const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders(); - const responseHeaders = new Headers(rawResponseHeaders ? rawResponseHeaders.trimStart().replace(/[^\S ]+$/, "").split(/[\r\n]+/).map(x => { - const [key, ...val] = x.split(": "); - return [key, val.join(": ")]; - }) : []); - const { - allowRangeRequests, - suggestedLength - } = validateRangeRequestCapabilities({ - responseHeaders, - isHttp: stream.isHttp, - rangeChunkSize, - disableRange - }); - if (allowRangeRequests) { - this._isRangeSupported = true; - } - this._contentLength = suggestedLength || this._contentLength; - this._filename = extractFilenameFromHeader(responseHeaders); - if (this._isRangeSupported) { - stream._abortRequest(fullRequestXhr); - } - this._headersCapability.resolve(); - } - #onDone(chunk) { - if (this._requests.length > 0) { - const capability = this._requests.shift(); - capability.resolve({ - value: chunk, - done: false - }); - } else { - this._cachedChunks.push(chunk); - } - this._done = true; - if (this._cachedChunks.length === 0) { - this.#endRequests(); - } - } - #onError(status) { - this._storedError = createResponseError(status, this._stream.url); - this._headersCapability.reject(this._storedError); - for (const capability of this._requests) { - capability.reject(this._storedError); - } - this._requests.length = 0; - this._cachedChunks.length = 0; - } - #onProgress(evt) { - this.onProgress?.({ - loaded: evt.loaded, - total: evt.lengthComputable ? evt.total : this._contentLength - }); - } - async read() { - await this._headersCapability.promise; - if (this._storedError) { - throw this._storedError; - } - if (this._cachedChunks.length > 0) { - const chunk = this._cachedChunks.shift(); - return { - value: chunk, - done: false - }; - } - if (this._done) { - return { - value: undefined, - done: true - }; - } - const capability = Promise.withResolvers(); - this._requests.push(capability); - return capability.promise; - } - cancel(reason) { - this._done = true; - this._headersCapability.reject(reason); - this.#endRequests(); - this._stream._abortRequest(this._fullRequestXhr); - this._fullRequestXhr = null; - } -} -class PDFNetworkStreamRangeReader extends BasePDFStreamRangeReader { - #endRequests = endRequests.bind(this); - onClosed = null; - _done = false; - _queuedChunk = null; - _requests = []; - _storedError = null; - constructor(stream, begin, end) { - super(stream, begin, end); - this._requestXhr = stream._request({ - begin, - end, - onHeadersReceived: this.#onHeadersReceived.bind(this), - onDone: this.#onDone.bind(this), - onError: this.#onError.bind(this), - onProgress: null - }); - } - #onHeadersReceived() { - const responseOrigin = getResponseOrigin(this._requestXhr?.responseURL); - try { - ensureResponseOrigin(responseOrigin, this._stream._responseOrigin); - } catch (ex) { - this._storedError = ex; - this.#onError(0); - } - } - #onDone(chunk) { - if (this._requests.length > 0) { - const capability = this._requests.shift(); - capability.resolve({ - value: chunk, - done: false - }); - } else { - this._queuedChunk = chunk; - } - this._done = true; - this.#endRequests(); - this.onClosed?.(); - } - #onError(status) { - this._storedError ??= createResponseError(status, this._stream.url); - for (const capability of this._requests) { - capability.reject(this._storedError); - } - this._requests.length = 0; - this._queuedChunk = null; - } - async read() { - if (this._storedError) { - throw this._storedError; - } - if (this._queuedChunk !== null) { - const chunk = this._queuedChunk; - this._queuedChunk = null; - return { - value: chunk, - done: false - }; - } - if (this._done) { - return { - value: undefined, - done: true - }; - } - const capability = Promise.withResolvers(); - this._requests.push(capability); - return capability.promise; - } - cancel(reason) { - this._done = true; - this.#endRequests(); - this._stream._abortRequest(this._requestXhr); - this.onClosed?.(); - } -} - -;// ./src/display/node_stream.js - - - - -function getReadableStream(readStream) { - const { - Readable - } = process.getBuiltinModule("stream"); - if (typeof Readable.toWeb === "function") { - return Readable.toWeb(readStream); - } - const require = process.getBuiltinModule("module").createRequire(import.meta.url); - const polyfill = require("node-readable-to-web-readable-stream"); - return polyfill.makeDefaultReadableStreamFromNodeReadable(readStream); -} -class PDFNodeStream extends BasePDFStream { - constructor(source) { - super(source, PDFNodeStreamReader, PDFNodeStreamRangeReader); - const { - url - } = source; - assert(url.protocol === "file:", "PDFNodeStream only supports file:// URLs."); - } -} -class PDFNodeStreamReader extends BasePDFStreamReader { - _reader = null; - constructor(stream) { - super(stream); - const { - disableRange, - disableStream, - length, - rangeChunkSize, - url - } = stream._source; - this._contentLength = length; - this._isStreamingSupported = !disableStream; - this._isRangeSupported = !disableRange; - const fs = process.getBuiltinModule("fs"); - fs.promises.lstat(url).then(stat => { - const readStream = fs.createReadStream(url); - const readableStream = getReadableStream(readStream); - this._reader = readableStream.getReader(); - const { - size - } = stat; - if (size <= 2 * rangeChunkSize) { - this._isRangeSupported = false; - } - this._contentLength = size; - if (!this._isStreamingSupported && this._isRangeSupported) { - this.cancel(new AbortException("Streaming is disabled.")); - } - this._headersCapability.resolve(); - }).catch(error => { - if (error.code === "ENOENT") { - error = createResponseError(0, url); - } - this._headersCapability.reject(error); - }); - } - async read() { - await this._headersCapability.promise; - const { - value, - done - } = await this._reader.read(); - if (done) { - return { - value, - done - }; - } - this._loaded += value.byteLength; - this._callOnProgress(); - return { - value: fetch_stream_getArrayBuffer(value), - done: false - }; - } - cancel(reason) { - this._reader?.cancel(reason); - } -} -class PDFNodeStreamRangeReader extends BasePDFStreamRangeReader { - _readCapability = Promise.withResolvers(); - _reader = null; - constructor(stream, begin, end) { - super(stream, begin, end); - const { - url - } = stream._source; - const fs = process.getBuiltinModule("fs"); - try { - const readStream = fs.createReadStream(url, { - start: begin, - end: end - 1 - }); - const readableStream = getReadableStream(readStream); - this._reader = readableStream.getReader(); - this._readCapability.resolve(); - } catch (error) { - this._readCapability.reject(error); - } - } - async read() { - await this._readCapability.promise; - const { - value, - done - } = await this._reader.read(); - if (done) { - return { - value, - done - }; - } - return { - value: fetch_stream_getArrayBuffer(value), - done: false - }; - } - cancel(reason) { - this._reader?.cancel(reason); - } -} - -;// ./src/display/pdf_objects.js -const INITIAL_DATA = Symbol("INITIAL_DATA"); -const dataObj = () => ({ - ...Promise.withResolvers(), - data: INITIAL_DATA -}); -class PDFObjects { - #objs = new Map(); - #ensureObj(objId) { - return this.#objs.getOrInsertComputed(objId, dataObj); - } - get(objId, callback = null) { - if (callback) { - const obj = this.#ensureObj(objId); - obj.promise.then(() => callback(obj.data)); - return null; - } - const obj = this.#objs.get(objId); - if (!obj || obj.data === INITIAL_DATA) { - throw new Error(`Requesting object that isn't resolved yet ${objId}.`); - } - return obj.data; - } - has(objId) { - const obj = this.#objs.get(objId); - return !!obj && obj.data !== INITIAL_DATA; - } - delete(objId) { - const obj = this.#objs.get(objId); - if (!obj || obj.data === INITIAL_DATA) { - return false; - } - this.#objs.delete(objId); - return true; - } - resolve(objId, data = null) { - const obj = this.#ensureObj(objId); - obj.data = data; - obj.resolve(); - } - clear() { - for (const { - data - } of this.#objs.values()) { - data?.bitmap?.close(); - } - this.#objs.clear(); - } - *[Symbol.iterator]() { - for (const [objId, { - data - }] of this.#objs) { - if (data !== INITIAL_DATA) { - yield [objId, data]; - } - } - } -} - -;// ./src/display/text_layer.js - - -const MAX_TEXT_DIVS_TO_RENDER = 100000; -const DEFAULT_FONT_SIZE = 30; -class TextLayer { - #capability = Promise.withResolvers(); - #container = null; - #disableProcessItems = false; - #fontInspectorEnabled = !!globalThis.FontInspector?.enabled; - #lang = null; - #layoutTextParams = null; - #pageHeight = 0; - #pageWidth = 0; - #reader = null; - #rootContainer = null; - #rotation = 0; - #scale = 0; - #styleCache = Object.create(null); - #textContentItemsStr = []; - #textContentSource = null; - #textDivs = []; - #textDivProperties = new WeakMap(); - #transform = null; - static #ascentCache = new Map(); - static #canvasContexts = new Map(); - static #canvasCtxFonts = new WeakMap(); - static #minFontSize = null; - static #pendingTextLayers = new Set(); - constructor({ - textContentSource, - container, - viewport - }) { - if (textContentSource instanceof ReadableStream) { - this.#textContentSource = textContentSource; - } else if (typeof textContentSource === "object") { - this.#textContentSource = new ReadableStream({ - start(controller) { - controller.enqueue(textContentSource); - controller.close(); - } - }); - } else { - throw new Error('No "textContentSource" parameter specified.'); - } - this.#container = this.#rootContainer = container; - this.#scale = viewport.scale * OutputScale.pixelRatio; - this.#rotation = viewport.rotation; - this.#layoutTextParams = { - div: null, - properties: null, - ctx: null - }; - const { - pageWidth, - pageHeight, - pageX, - pageY - } = viewport.rawDims; - this.#transform = [1, 0, 0, -1, -pageX, pageY + pageHeight]; - this.#pageWidth = pageWidth; - this.#pageHeight = pageHeight; - TextLayer.#ensureMinFontSizeComputed(); - container.style.setProperty("--min-font-size", TextLayer.#minFontSize); - setLayerDimensions(container, viewport); - this.#capability.promise.finally(() => { - TextLayer.#pendingTextLayers.delete(this); - this.#layoutTextParams = null; - this.#styleCache = null; - }).catch(() => {}); - } - static get fontFamilyMap() { - const { - isWindows, - isFirefox - } = FeatureTest.platform; - return shadow(this, "fontFamilyMap", new Map([["sans-serif", `${isWindows && isFirefox ? "Calibri, " : ""}sans-serif`], ["monospace", `${isWindows && isFirefox ? "Lucida Console, " : ""}monospace`]])); - } - render() { - const pump = () => { - this.#reader.read().then(({ - value, - done - }) => { - if (done) { - this.#capability.resolve(); - return; - } - this.#lang ??= value.lang; - Object.assign(this.#styleCache, value.styles); - this.#processItems(value.items); - pump(); - }, this.#capability.reject); - }; - this.#reader = this.#textContentSource.getReader(); - TextLayer.#pendingTextLayers.add(this); - pump(); - return this.#capability.promise; - } - update({ - viewport, - onBefore = null - }) { - const scale = viewport.scale * OutputScale.pixelRatio; - const rotation = viewport.rotation; - if (rotation !== this.#rotation) { - onBefore?.(); - this.#rotation = rotation; - setLayerDimensions(this.#rootContainer, { - rotation - }); - } - if (scale !== this.#scale) { - onBefore?.(); - this.#scale = scale; - const params = { - div: null, - properties: null, - ctx: TextLayer.#getCtx(this.#lang) - }; - for (const div of this.#textDivs) { - params.properties = this.#textDivProperties.get(div); - params.div = div; - this.#layout(params); - } - } - } - cancel() { - const abortEx = new AbortException("TextLayer task cancelled."); - this.#reader?.cancel(abortEx).catch(() => {}); - this.#reader = null; - this.#capability.reject(abortEx); - } - get textDivs() { - return this.#textDivs; - } - get textContentItemsStr() { - return this.#textContentItemsStr; - } - #processItems(items) { - if (this.#disableProcessItems) { - return; - } - this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang); - const textDivs = this.#textDivs, - textContentItemsStr = this.#textContentItemsStr; - for (const item of items) { - if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) { - warn("Ignoring additional textDivs for performance reasons."); - this.#disableProcessItems = true; - return; - } - if (item.str === undefined) { - if (item.type === "beginMarkedContentProps" || item.type === "beginMarkedContent") { - const parent = this.#container; - this.#container = document.createElement("span"); - this.#container.classList.add("markedContent"); - if (item.id) { - this.#container.setAttribute("id", `${item.id}`); - } - if (item.tag === "Artifact") { - this.#container.ariaHidden = true; - } - parent.append(this.#container); - } else if (item.type === "endMarkedContent") { - this.#container = this.#container.parentNode; - } - continue; - } - textContentItemsStr.push(item.str); - this.#appendText(item); - } - } - #appendText(geom) { - const textDiv = document.createElement("span"); - const textDivProperties = { - angle: 0, - canvasWidth: 0, - hasText: geom.str !== "", - hasEOL: geom.hasEOL, - fontSize: 0 - }; - this.#textDivs.push(textDiv); - const tx = Util.transform(this.#transform, geom.transform); - let angle = Math.atan2(tx[1], tx[0]); - const style = this.#styleCache[geom.fontName]; - if (style.vertical) { - angle += Math.PI / 2; - } - let fontFamily = this.#fontInspectorEnabled && style.fontSubstitution || style.fontFamily; - fontFamily = TextLayer.fontFamilyMap.get(fontFamily) || fontFamily; - const fontHeight = Math.hypot(tx[2], tx[3]); - const fontAscent = fontHeight * TextLayer.#getAscent(fontFamily, style, this.#lang); - let left, top; - if (angle === 0) { - left = tx[4]; - top = tx[5] - fontAscent; - } else { - left = tx[4] + fontAscent * Math.sin(angle); - top = tx[5] - fontAscent * Math.cos(angle); - } - const divStyle = textDiv.style; - divStyle.left = `${(100 * left / this.#pageWidth).toFixed(2)}%`; - divStyle.top = `${(100 * top / this.#pageHeight).toFixed(2)}%`; - divStyle.setProperty("--font-height", `${fontHeight.toFixed(2)}px`); - divStyle.fontFamily = fontFamily; - textDivProperties.fontSize = fontHeight; - textDiv.setAttribute("role", "presentation"); - textDiv.textContent = geom.str; - textDiv.dir = geom.dir; - if (this.#fontInspectorEnabled) { - textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName; - } - if (angle !== 0) { - textDivProperties.angle = angle * (180 / Math.PI); - } - let shouldScaleText = false; - if (geom.str.length > 1) { - shouldScaleText = true; - } else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) { - const absScaleX = Math.abs(geom.transform[0]), - absScaleY = Math.abs(geom.transform[3]); - if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) { - shouldScaleText = true; - } - } - if (shouldScaleText) { - textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width; - } - this.#textDivProperties.set(textDiv, textDivProperties); - this.#layoutTextParams.div = textDiv; - this.#layoutTextParams.properties = textDivProperties; - this.#layout(this.#layoutTextParams); - if (textDivProperties.hasText) { - this.#container.append(textDiv); - } - if (textDivProperties.hasEOL) { - const br = document.createElement("br"); - br.setAttribute("role", "presentation"); - this.#container.append(br); - } - } - #layout(params) { - const { - div, - properties, - ctx - } = params; - const { - style - } = div; - if (properties.canvasWidth !== 0 && properties.hasText) { - const { - fontFamily - } = style; - const { - canvasWidth, - fontSize - } = properties; - TextLayer.#ensureCtxFont(ctx, fontSize * this.#scale, fontFamily); - const { - width - } = ctx.measureText(div.textContent); - if (width > 0) { - style.setProperty("--scale-x", canvasWidth * this.#scale / width); - } - } - if (properties.angle !== 0) { - style.setProperty("--rotate", `${properties.angle}deg`); - } - } - static cleanup() { - if (this.#pendingTextLayers.size > 0) { - return; - } - this.#ascentCache.clear(); - for (const { - canvas - } of this.#canvasContexts.values()) { - canvas.remove(); - } - this.#canvasContexts.clear(); - } - static #getCtx(lang = null) { - let ctx = this.#canvasContexts.get(lang ||= ""); - if (!ctx) { - const canvas = document.createElement("canvas"); - canvas.className = "hiddenCanvasElement"; - canvas.lang = lang; - document.body.append(canvas); - ctx = canvas.getContext("2d", { - alpha: false, - willReadFrequently: true - }); - this.#canvasContexts.set(lang, ctx); - this.#canvasCtxFonts.set(ctx, { - size: 0, - family: "" - }); - } - return ctx; - } - static #ensureCtxFont(ctx, size, family) { - const cached = this.#canvasCtxFonts.get(ctx); - if (size === cached.size && family === cached.family) { - return; - } - ctx.font = `${size}px ${family}`; - cached.size = size; - cached.family = family; - } - static #ensureMinFontSizeComputed() { - if (this.#minFontSize !== null) { - return; - } - const div = document.createElement("div"); - div.style.opacity = 0; - div.style.lineHeight = 1; - div.style.fontSize = "1px"; - div.style.position = "absolute"; - div.textContent = "X"; - document.body.append(div); - this.#minFontSize = div.getBoundingClientRect().height; - div.remove(); - } - static #getAscent(fontFamily, style, lang) { - const cachedAscent = this.#ascentCache.get(fontFamily); - if (cachedAscent) { - return cachedAscent; - } - const ctx = this.#getCtx(lang); - ctx.canvas.width = ctx.canvas.height = DEFAULT_FONT_SIZE; - this.#ensureCtxFont(ctx, DEFAULT_FONT_SIZE, fontFamily); - const metrics = ctx.measureText(""); - const ascent = metrics.fontBoundingBoxAscent; - const descent = Math.abs(metrics.fontBoundingBoxDescent); - ctx.canvas.width = ctx.canvas.height = 0; - let ratio = 0.8; - if (ascent) { - ratio = ascent / (ascent + descent); - } else { - if (FeatureTest.platform.isFirefox) { - warn("Enable the `dom.textMetrics.fontBoundingBox.enabled` preference " + "in `about:config` to improve TextLayer rendering."); - } - if (style.ascent) { - ratio = style.ascent; - } else if (style.descent) { - ratio = 1 + style.descent; - } - } - this.#ascentCache.set(fontFamily, ratio); - return ratio; - } -} - -;// ./src/display/api.js - - - - - - - - - - - - - - - - - - - - - - - - - -const RENDERING_CANCELLED_TIMEOUT = 100; -function getDocument(src = {}) { - if (typeof src === "string" || src instanceof URL) { - src = { - url: src - }; - } else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) { - src = { - data: src - }; - } - const task = new PDFDocumentLoadingTask(); - const { - docId - } = task; - const url = src.url ? getUrlProp(src.url) : null; - const data = src.data ? getDataProp(src.data) : null; - const httpHeaders = src.httpHeaders || null; - const withCredentials = src.withCredentials === true; - const password = src.password ?? null; - const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null; - const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : 2 ** 16; - let worker = src.worker instanceof PDFWorker ? src.worker : null; - const verbosity = src.verbosity; - const docBaseUrl = typeof src.docBaseUrl === "string" && !isDataScheme(src.docBaseUrl) ? src.docBaseUrl : null; - const cMapUrl = getFactoryUrlProp(src.cMapUrl); - const cMapPacked = src.cMapPacked !== false; - const CMapReaderFactory = src.CMapReaderFactory || (isNodeJS ? NodeCMapReaderFactory : DOMCMapReaderFactory); - const iccUrl = getFactoryUrlProp(src.iccUrl); - const standardFontDataUrl = getFactoryUrlProp(src.standardFontDataUrl); - const StandardFontDataFactory = src.StandardFontDataFactory || (isNodeJS ? NodeStandardFontDataFactory : DOMStandardFontDataFactory); - const wasmUrl = getFactoryUrlProp(src.wasmUrl); - const WasmFactory = src.WasmFactory || (isNodeJS ? NodeWasmFactory : DOMWasmFactory); - const ignoreErrors = src.stopAtErrors !== true; - const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1; - const isEvalSupported = src.isEvalSupported !== false; - const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !isNodeJS; - const isImageDecoderSupported = typeof src.isImageDecoderSupported === "boolean" ? src.isImageDecoderSupported : !isNodeJS && (FeatureTest.platform.isFirefox || !globalThis.chrome); - const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1; - const disableFontFace = typeof src.disableFontFace === "boolean" ? src.disableFontFace : isNodeJS; - const fontExtraProperties = src.fontExtraProperties === true; - const enableXfa = src.enableXfa === true; - const ownerDocument = src.ownerDocument || globalThis.document; - const disableRange = src.disableRange === true; - const disableStream = src.disableStream === true; - const disableAutoFetch = src.disableAutoFetch === true; - const pdfBug = src.pdfBug === true; - const CanvasFactory = src.CanvasFactory || (isNodeJS ? NodeCanvasFactory : DOMCanvasFactory); - const FilterFactory = src.FilterFactory || (isNodeJS ? NodeFilterFactory : DOMFilterFactory); - const enableHWA = src.enableHWA === true; - const useWasm = src.useWasm !== false; - const pagesMapper = src.pagesMapper || new PagesMapper(); - const length = rangeTransport ? rangeTransport.length : src.length ?? NaN; - const useSystemFonts = typeof src.useSystemFonts === "boolean" ? src.useSystemFonts : !isNodeJS && !disableFontFace; - const useWorkerFetch = typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : !!(CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && WasmFactory === DOMWasmFactory && cMapUrl && standardFontDataUrl && wasmUrl && isValidFetchUrl(cMapUrl, document.baseURI) && isValidFetchUrl(standardFontDataUrl, document.baseURI) && isValidFetchUrl(wasmUrl, document.baseURI)); - const styleElement = null; - setVerbosityLevel(verbosity); - const transportFactory = { - canvasFactory: new CanvasFactory({ - ownerDocument, - enableHWA - }), - filterFactory: new FilterFactory({ - docId, - ownerDocument - }), - cMapReaderFactory: useWorkerFetch ? null : new CMapReaderFactory({ - baseUrl: cMapUrl, - isCompressed: cMapPacked - }), - standardFontDataFactory: useWorkerFetch ? null : new StandardFontDataFactory({ - baseUrl: standardFontDataUrl - }), - wasmFactory: useWorkerFetch ? null : new WasmFactory({ - baseUrl: wasmUrl - }) - }; - if (!worker) { - worker = PDFWorker.create({ - verbosity, - port: GlobalWorkerOptions.workerPort - }); - task._worker = worker; - } - const docParams = { - docId, - apiVersion: "5.5.207", - data, - password, - disableAutoFetch, - rangeChunkSize, - length, - docBaseUrl, - enableXfa, - evaluatorOptions: { - maxImageSize, - disableFontFace, - ignoreErrors, - isEvalSupported, - isOffscreenCanvasSupported, - isImageDecoderSupported, - canvasMaxAreaInBytes, - fontExtraProperties, - useSystemFonts, - useWasm, - useWorkerFetch, - cMapUrl, - iccUrl, - standardFontDataUrl, - wasmUrl - } - }; - const transportParams = { - ownerDocument, - pdfBug, - styleElement, - enableHWA, - loadingParams: { - disableAutoFetch, - enableXfa - } - }; - worker.promise.then(function () { - if (task.destroyed) { - throw new Error("Loading aborted"); - } - if (worker.destroyed) { - throw new Error("Worker was destroyed"); - } - const workerIdPromise = worker.messageHandler.sendWithPromise("GetDocRequest", docParams, data ? [data.buffer] : null); - let networkStream; - if (rangeTransport) { - networkStream = new PDFDataTransportStream({ - pdfDataRangeTransport: rangeTransport, - disableRange, - disableStream - }); - } else if (!data) { - if (!url) { - throw new Error("getDocument - no `url` parameter provided."); - } - const NetworkStream = isValidFetchUrl(url) ? PDFFetchStream : isNodeJS ? PDFNodeStream : PDFNetworkStream; - networkStream = new NetworkStream({ - url, - length, - httpHeaders, - withCredentials, - rangeChunkSize, - disableRange, - disableStream - }); - } - return workerIdPromise.then(workerId => { - if (task.destroyed) { - throw new Error("Loading aborted"); - } - if (worker.destroyed) { - throw new Error("Worker was destroyed"); - } - const messageHandler = new MessageHandler(docId, workerId, worker.port); - const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory, pagesMapper); - task._transport = transport; - messageHandler.send("Ready", null); - }); - }).catch(task._capability.reject); - return task; -} -class PDFDocumentLoadingTask { - static #docId = 0; - _capability = Promise.withResolvers(); - _transport = null; - _worker = null; - docId = `d${PDFDocumentLoadingTask.#docId++}`; - destroyed = false; - onPassword = null; - onProgress = null; - get promise() { - return this._capability.promise; - } - async destroy() { - this.destroyed = true; - try { - if (this._worker?.port) { - this._worker._pendingDestroy = true; - } - await this._transport?.destroy(); - } catch (ex) { - if (this._worker?.port) { - delete this._worker._pendingDestroy; - } - throw ex; - } - this._transport = null; - this._worker?.destroy(); - this._worker = null; - } - async getData() { - return this._transport.getData(); - } -} -class PDFDataRangeTransport { - #capability = Promise.withResolvers(); - #progressiveDoneListeners = []; - #progressiveReadListeners = []; - #rangeListeners = []; - constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) { - this.length = length; - this.initialData = initialData; - this.progressiveDone = progressiveDone; - this.contentDispositionFilename = contentDispositionFilename; - Object.defineProperty(this, "onDataProgress", { - value: () => { - deprecated("`PDFDataRangeTransport.prototype.onDataProgress` - method was " + "removed, since loading progress is now reported automatically " + "through the `PDFDataTransportStream` class (and related code)."); - } - }); - } - addRangeListener(listener) { - this.#rangeListeners.push(listener); - } - addProgressiveReadListener(listener) { - this.#progressiveReadListeners.push(listener); - } - addProgressiveDoneListener(listener) { - this.#progressiveDoneListeners.push(listener); - } - onDataRange(begin, chunk) { - for (const listener of this.#rangeListeners) { - listener(begin, chunk); - } - } - onDataProgressiveRead(chunk) { - this.#capability.promise.then(() => { - for (const listener of this.#progressiveReadListeners) { - listener(chunk); - } - }); - } - onDataProgressiveDone() { - this.#capability.promise.then(() => { - for (const listener of this.#progressiveDoneListeners) { - listener(); - } - }); - } - transportReady() { - this.#capability.resolve(); - } - requestDataRange(begin, end) { - unreachable("Abstract method PDFDataRangeTransport.requestDataRange"); - } - abort() {} -} -class PDFDocumentProxy { - constructor(pdfInfo, transport) { - this._pdfInfo = pdfInfo; - this._transport = transport; - } - get pagesMapper() { - return this._transport.pagesMapper; - } - get annotationStorage() { - return this._transport.annotationStorage; - } - get canvasFactory() { - return this._transport.canvasFactory; - } - get filterFactory() { - return this._transport.filterFactory; - } - get numPages() { - return this._pdfInfo.numPages; - } - get fingerprints() { - return this._pdfInfo.fingerprints; - } - get isPureXfa() { - return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); - } - get allXfaHtml() { - return this._transport._htmlForXfa; - } - getPage(pageNumber) { - return this._transport.getPage(pageNumber); - } - getPageIndex(ref) { - return this._transport.getPageIndex(ref); - } - getDestinations() { - return this._transport.getDestinations(); - } - getDestination(id) { - return this._transport.getDestination(id); - } - getPageLabels() { - return this._transport.getPageLabels(); - } - getPageLayout() { - return this._transport.getPageLayout(); - } - getPageMode() { - return this._transport.getPageMode(); - } - getViewerPreferences() { - return this._transport.getViewerPreferences(); - } - getOpenAction() { - return this._transport.getOpenAction(); - } - getAttachments() { - return this._transport.getAttachments(); - } - getAnnotationsByType(types, pageIndexesToSkip) { - return this._transport.getAnnotationsByType(types, pageIndexesToSkip); - } - getJSActions() { - return this._transport.getDocJSActions(); - } - getOutline() { - return this._transport.getOutline(); - } - getOptionalContentConfig({ - intent = "display" - } = {}) { - const { - renderingIntent - } = this._transport.getRenderingIntent(intent); - return this._transport.getOptionalContentConfig(renderingIntent); - } - getPermissions() { - return this._transport.getPermissions(); - } - getMetadata() { - return this._transport.getMetadata(); - } - getMarkInfo() { - return this._transport.getMarkInfo(); - } - getData() { - return this._transport.getData(); - } - saveDocument() { - return this._transport.saveDocument(); - } - extractPages(pageInfos) { - return this._transport.extractPages(pageInfos); - } - getDownloadInfo() { - return this._transport.downloadInfoCapability.promise; - } - cleanup(keepLoadedFonts = false) { - return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa); - } - destroy() { - return this.loadingTask.destroy(); - } - cachedPageNumber(ref) { - return this._transport.cachedPageNumber(ref); - } - get loadingParams() { - return this._transport.loadingParams; - } - get loadingTask() { - return this._transport.loadingTask; - } - getFieldObjects() { - return this._transport.getFieldObjects(); - } - hasJSActions() { - return this._transport.hasJSActions(); - } - getCalculationOrderIds() { - return this._transport.getCalculationOrderIds(); - } -} -class PDFPageProxy { - #pendingCleanup = false; - #pagesMapper = null; - constructor(pageIndex, pageInfo, transport, pagesMapper, pdfBug = false) { - this._pageIndex = pageIndex; - this._pageInfo = pageInfo; - this._transport = transport; - this._stats = pdfBug ? new StatTimer() : null; - this._pdfBug = pdfBug; - this.commonObjs = transport.commonObjs; - this.objs = new PDFObjects(); - this._intentStates = new Map(); - this.destroyed = false; - this.recordedBBoxes = null; - this.#pagesMapper = pagesMapper; - } - get pageNumber() { - return this._pageIndex + 1; - } - set pageNumber(value) { - this._pageIndex = value - 1; - } - get rotate() { - return this._pageInfo.rotate; - } - get ref() { - return this._pageInfo.ref; - } - get userUnit() { - return this._pageInfo.userUnit; - } - get view() { - return this._pageInfo.view; - } - getViewport({ - scale, - rotation = this.rotate, - offsetX = 0, - offsetY = 0, - dontFlip = false - } = {}) { - return new PageViewport({ - viewBox: this.view, - userUnit: this.userUnit, - scale, - rotation, - offsetX, - offsetY, - dontFlip - }); - } - getAnnotations({ - intent = "display" - } = {}) { - const { - renderingIntent - } = this._transport.getRenderingIntent(intent); - return this._transport.getAnnotations(this._pageIndex, renderingIntent); - } - getJSActions() { - return this._transport.getPageJSActions(this._pageIndex); - } - get filterFactory() { - return this._transport.filterFactory; - } - get isPureXfa() { - return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); - } - async getXfa() { - return this._transport._htmlForXfa?.children[this._pageIndex] || null; - } - render({ - canvasContext, - canvas = canvasContext.canvas, - viewport, - intent = "display", - annotationMode = AnnotationMode.ENABLE, - transform = null, - background = null, - optionalContentConfigPromise = null, - annotationCanvasMap = null, - pageColors = null, - printAnnotationStorage = null, - isEditing = false, - recordOperations = false, - operationsFilter = null - }) { - this._stats?.time("Overall"); - const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing); - const { - renderingIntent, - cacheKey - } = intentArgs; - this.#pendingCleanup = false; - optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent); - const intentState = this._intentStates.getOrInsertComputed(cacheKey, makeObj); - if (intentState.streamReaderCancelTimeout) { - clearTimeout(intentState.streamReaderCancelTimeout); - intentState.streamReaderCancelTimeout = null; - } - const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); - if (!intentState.displayReadyCapability) { - intentState.displayReadyCapability = Promise.withResolvers(); - intentState.operatorList = { - fnArray: [], - argsArray: [], - lastChunk: false, - separateAnnots: null - }; - this._stats?.time("Page Request"); - this._pumpOperatorList(intentArgs); - } - const recordForDebugger = Boolean(this._pdfBug && globalThis.StepperManager?.enabled); - const shouldRecordOperations = !this.recordedBBoxes && (recordOperations || recordForDebugger); - const complete = error => { - intentState.renderTasks.delete(internalRenderTask); - if (shouldRecordOperations) { - const recordedBBoxes = internalRenderTask.gfx?.dependencyTracker.take(); - if (recordedBBoxes) { - if (internalRenderTask.stepper) { - internalRenderTask.stepper.setOperatorBBoxes(recordedBBoxes, internalRenderTask.gfx.dependencyTracker.takeDebugMetadata()); - } - if (recordOperations) { - this.recordedBBoxes = recordedBBoxes; - } - } - } - if (intentPrint) { - this.#pendingCleanup = true; - } - this.#tryCleanup(); - if (error) { - internalRenderTask.capability.reject(error); - this._abortOperatorList({ - intentState, - reason: error instanceof Error ? error : new Error(error) - }); - } else { - internalRenderTask.capability.resolve(); - } - if (this._stats) { - this._stats.timeEnd("Rendering"); - this._stats.timeEnd("Overall"); - if (globalThis.Stats?.enabled) { - globalThis.Stats.add(this.pageNumber, this._stats); - } - } - }; - const internalRenderTask = new InternalRenderTask({ - callback: complete, - params: { - canvas, - canvasContext, - dependencyTracker: shouldRecordOperations ? new CanvasDependencyTracker(canvas, intentState.operatorList.length, recordForDebugger) : null, - viewport, - transform, - background - }, - objs: this.objs, - commonObjs: this.commonObjs, - annotationCanvasMap, - operatorList: intentState.operatorList, - pageIndex: this._pageIndex, - canvasFactory: this._transport.canvasFactory, - filterFactory: this._transport.filterFactory, - useRequestAnimationFrame: !intentPrint, - pdfBug: this._pdfBug, - pageColors, - enableHWA: this._transport.enableHWA, - operationsFilter - }); - (intentState.renderTasks ||= new Set()).add(internalRenderTask); - const renderTask = internalRenderTask.task; - Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => { - if (this.destroyed) { - complete(); - return; - } - this._stats?.time("Rendering"); - if (!(optionalContentConfig.renderingIntent & renderingIntent)) { - throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` " + "and `PDFDocumentProxy.getOptionalContentConfig` methods."); - } - internalRenderTask.initializeGraphics({ - transparency, - optionalContentConfig - }); - internalRenderTask.operatorListChanged(); - }).catch(complete); - return renderTask; - } - getOperatorList({ - intent = "display", - annotationMode = AnnotationMode.ENABLE, - printAnnotationStorage = null, - isEditing = false - } = {}) { - function operatorListChanged() { - if (intentState.operatorList.lastChunk) { - intentState.opListReadCapability.resolve(intentState.operatorList); - intentState.renderTasks.delete(opListTask); - } - } - const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing, true); - const intentState = this._intentStates.getOrInsertComputed(intentArgs.cacheKey, makeObj); - let opListTask; - if (!intentState.opListReadCapability) { - opListTask = Object.create(null); - opListTask.operatorListChanged = operatorListChanged; - intentState.opListReadCapability = Promise.withResolvers(); - (intentState.renderTasks ||= new Set()).add(opListTask); - intentState.operatorList = { - fnArray: [], - argsArray: [], - lastChunk: false, - separateAnnots: null - }; - this._stats?.time("Page Request"); - this._pumpOperatorList(intentArgs); - } - return intentState.opListReadCapability.promise; - } - streamTextContent({ - includeMarkedContent = false, - disableNormalization = false - } = {}) { - const TEXT_CONTENT_CHUNK_SIZE = 100; - return this._transport.messageHandler.sendWithStream("GetTextContent", { - pageId: this.#pagesMapper.getPageId(this._pageIndex + 1) - 1, - pageIndex: this._pageIndex, - includeMarkedContent: includeMarkedContent === true, - disableNormalization: disableNormalization === true - }, { - highWaterMark: TEXT_CONTENT_CHUNK_SIZE, - size(textContent) { - return textContent.items.length; - } - }); - } - async getTextContent(params = {}) { - if (this._transport._htmlForXfa) { - return this.getXfa().then(xfa => XfaText.textContent(xfa)); - } - const readableStream = this.streamTextContent(params); - const textContent = { - items: [], - styles: Object.create(null), - lang: null - }; - for await (const value of readableStream) { - textContent.lang ??= value.lang; - Object.assign(textContent.styles, value.styles); - textContent.items.push(...value.items); - } - return textContent; - } - getStructTree() { - return this._transport.getStructTree(this._pageIndex); - } - _destroy() { - this.destroyed = true; - const waitOn = []; - for (const intentState of this._intentStates.values()) { - this._abortOperatorList({ - intentState, - reason: new Error("Page was destroyed."), - force: true - }); - if (intentState.opListReadCapability) { - continue; - } - for (const internalRenderTask of intentState.renderTasks) { - waitOn.push(internalRenderTask.completed); - internalRenderTask.cancel(); - } - } - this.objs.clear(); - this.#pendingCleanup = false; - return Promise.all(waitOn); - } - cleanup(resetStats = false) { - this.#pendingCleanup = true; - const success = this.#tryCleanup(); - if (resetStats && success) { - this._stats &&= new StatTimer(); - } - return success; - } - #tryCleanup() { - if (!this.#pendingCleanup || this.destroyed) { - return false; - } - for (const { - renderTasks, - operatorList - } of this._intentStates.values()) { - if (renderTasks.size > 0 || !operatorList.lastChunk) { - return false; - } - } - this._intentStates.clear(); - this.objs.clear(); - this.#pendingCleanup = false; - return true; - } - _startRenderPage(transparency, cacheKey) { - const intentState = this._intentStates.get(cacheKey); - if (!intentState) { - return; - } - this._stats?.timeEnd("Page Request"); - intentState.displayReadyCapability?.resolve(transparency); - } - _renderPageChunk(operatorListChunk, intentState) { - for (let i = 0, ii = operatorListChunk.length; i < ii; i++) { - intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); - intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]); - } - intentState.operatorList.lastChunk = operatorListChunk.lastChunk; - intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots; - for (const internalRenderTask of intentState.renderTasks) { - internalRenderTask.operatorListChanged(); - } - if (operatorListChunk.lastChunk) { - this.#tryCleanup(); - } - } - _pumpOperatorList({ - renderingIntent, - cacheKey, - annotationStorageSerializable, - modifiedIds - }) { - const { - map, - transfer - } = annotationStorageSerializable; - const readableStream = this._transport.messageHandler.sendWithStream("GetOperatorList", { - pageId: this.#pagesMapper.getPageId(this._pageIndex + 1) - 1, - pageIndex: this._pageIndex, - intent: renderingIntent, - cacheKey, - annotationStorage: map, - modifiedIds - }, undefined, transfer); - const reader = readableStream.getReader(); - const intentState = this._intentStates.get(cacheKey); - intentState.streamReader = reader; - const pump = () => { - reader.read().then(({ - value, - done - }) => { - if (done) { - intentState.streamReader = null; - return; - } - if (this._transport.destroyed) { - return; - } - this._renderPageChunk(value, intentState); - pump(); - }, reason => { - intentState.streamReader = null; - if (this._transport.destroyed) { - return; - } - if (intentState.operatorList) { - intentState.operatorList.lastChunk = true; - for (const internalRenderTask of intentState.renderTasks) { - internalRenderTask.operatorListChanged(); - } - this.#tryCleanup(); - } - if (intentState.displayReadyCapability) { - intentState.displayReadyCapability.reject(reason); - } else if (intentState.opListReadCapability) { - intentState.opListReadCapability.reject(reason); - } else { - throw reason; - } - }); - }; - pump(); - } - _abortOperatorList({ - intentState, - reason, - force = false - }) { - if (!intentState.streamReader) { - return; - } - if (intentState.streamReaderCancelTimeout) { - clearTimeout(intentState.streamReaderCancelTimeout); - intentState.streamReaderCancelTimeout = null; - } - if (!force) { - if (intentState.renderTasks.size > 0) { - return; - } - if (reason instanceof RenderingCancelledException) { - let delay = RENDERING_CANCELLED_TIMEOUT; - if (reason.extraDelay > 0 && reason.extraDelay < 1000) { - delay += reason.extraDelay; - } - intentState.streamReaderCancelTimeout = setTimeout(() => { - intentState.streamReaderCancelTimeout = null; - this._abortOperatorList({ - intentState, - reason, - force: true - }); - }, delay); - return; - } - } - intentState.streamReader.cancel(new AbortException(reason.message)).catch(() => {}); - intentState.streamReader = null; - if (this._transport.destroyed) { - return; - } - for (const [curCacheKey, curIntentState] of this._intentStates) { - if (curIntentState === intentState) { - this._intentStates.delete(curCacheKey); - break; - } - } - this.cleanup(); - } - get stats() { - return this._stats; - } -} -class PDFWorker { - #capability = Promise.withResolvers(); - #messageHandler = null; - #port = null; - #webWorker = null; - static #fakeWorkerId = 0; - static #isWorkerDisabled = false; - static #workerPorts = new WeakMap(); - static { - if (isNodeJS) { - this.#isWorkerDisabled = true; - GlobalWorkerOptions.workerSrc ||= "./pdf.worker.mjs"; - } - this._isSameOrigin = (baseUrl, otherUrl) => { - const base = URL.parse(baseUrl); - if (!base?.origin || base.origin === "null") { - return false; - } - const other = new URL(otherUrl, base); - return base.origin === other.origin; - }; - this._createCDNWrapper = url => { - const wrapper = `await import("${url}");`; - return URL.createObjectURL(new Blob([wrapper], { - type: "text/javascript" - })); - }; - this.fromPort = params => { - deprecated("`PDFWorker.fromPort` - please use `PDFWorker.create` instead."); - if (!params?.port) { - throw new Error("PDFWorker.fromPort - invalid method signature."); - } - return this.create(params); - }; - } - constructor({ - name = null, - port = null, - verbosity = getVerbosityLevel() - } = {}) { - this.name = name; - this.destroyed = false; - this.verbosity = verbosity; - if (port) { - if (PDFWorker.#workerPorts.has(port)) { - throw new Error("Cannot use more than one PDFWorker per port."); - } - PDFWorker.#workerPorts.set(port, this); - this.#initializeFromPort(port); - } else { - this.#initialize(); - } - } - get promise() { - return this.#capability.promise; - } - #resolve() { - this.#capability.resolve(); - this.#messageHandler.send("configure", { - verbosity: this.verbosity - }); - } - get port() { - return this.#port; - } - get messageHandler() { - return this.#messageHandler; - } - #initializeFromPort(port) { - this.#port = port; - this.#messageHandler = new MessageHandler("main", "worker", port); - this.#messageHandler.on("ready", () => {}); - this.#resolve(); - } - #initialize() { - if (PDFWorker.#isWorkerDisabled || PDFWorker.#mainThreadWorkerMessageHandler) { - this.#setupFakeWorker(); - return; - } - let { - workerSrc - } = PDFWorker; - try { - if (!PDFWorker._isSameOrigin(window.location, workerSrc)) { - workerSrc = PDFWorker._createCDNWrapper(new URL(workerSrc, window.location).href); - } - const worker = new Worker(workerSrc, { - type: "module" - }); - const messageHandler = new MessageHandler("main", "worker", worker); - const terminateEarly = () => { - ac.abort(); - messageHandler.destroy(); - worker.terminate(); - if (this.destroyed) { - this.#capability.reject(new Error("Worker was destroyed")); - } else { - this.#setupFakeWorker(); - } - }; - const ac = new AbortController(); - worker.addEventListener("error", () => { - if (!this.#webWorker) { - terminateEarly(); - } - }, { - signal: ac.signal - }); - messageHandler.on("test", data => { - ac.abort(); - if (this.destroyed || !data) { - terminateEarly(); - return; - } - this.#messageHandler = messageHandler; - this.#port = worker; - this.#webWorker = worker; - this.#resolve(); - }); - messageHandler.on("ready", data => { - ac.abort(); - if (this.destroyed) { - terminateEarly(); - return; - } - try { - sendTest(); - } catch { - this.#setupFakeWorker(); - } - }); - const sendTest = () => { - const testObj = new Uint8Array(); - messageHandler.send("test", testObj, [testObj.buffer]); - }; - sendTest(); - return; - } catch { - info("The worker has been disabled."); - } - this.#setupFakeWorker(); - } - #setupFakeWorker() { - if (!PDFWorker.#isWorkerDisabled) { - warn("Setting up fake worker."); - PDFWorker.#isWorkerDisabled = true; - } - PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => { - if (this.destroyed) { - this.#capability.reject(new Error("Worker was destroyed")); - return; - } - const port = new LoopbackPort(); - this.#port = port; - const id = `fake${PDFWorker.#fakeWorkerId++}`; - const workerHandler = new MessageHandler(id + "_worker", id, port); - WorkerMessageHandler.setup(workerHandler, port); - this.#messageHandler = new MessageHandler(id, id + "_worker", port); - this.#resolve(); - }).catch(reason => { - this.#capability.reject(new Error(`Setting up fake worker failed: "${reason.message}".`)); - }); - } - destroy() { - this.destroyed = true; - this.#webWorker?.terminate(); - this.#webWorker = null; - PDFWorker.#workerPorts.delete(this.#port); - this.#port = null; - this.#messageHandler?.destroy(); - this.#messageHandler = null; - } - static create(params) { - const cachedPort = this.#workerPorts.get(params?.port); - if (cachedPort) { - if (cachedPort._pendingDestroy) { - throw new Error("PDFWorker.create - the worker is being destroyed.\n" + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls."); - } - return cachedPort; - } - return new PDFWorker(params); - } - static get workerSrc() { - if (GlobalWorkerOptions.workerSrc) { - return GlobalWorkerOptions.workerSrc; - } - throw new Error('No "GlobalWorkerOptions.workerSrc" specified.'); - } - static get #mainThreadWorkerMessageHandler() { - try { - return globalThis.pdfjsWorker?.WorkerMessageHandler || null; - } catch { - return null; - } - } - static get _setupFakeWorkerGlobal() { - const loader = async () => { - if (this.#mainThreadWorkerMessageHandler) { - return this.#mainThreadWorkerMessageHandler; - } - const worker = await import( - /*webpackIgnore: true*/ - /*@vite-ignore*/ - this.workerSrc); - return worker.WorkerMessageHandler; - }; - return shadow(this, "_setupFakeWorkerGlobal", loader()); - } -} -class WorkerTransport { - downloadInfoCapability = Promise.withResolvers(); - #fullReader = null; - #methodPromises = new Map(); - #networkStream = null; - #pageCache = new Map(); - #pagePromises = new Map(); - #pageRefCache = new Map(); - #passwordCapability = null; - #copiedPageInfo = null; - constructor(messageHandler, loadingTask, networkStream, params, factory, pagesMapper) { - this.messageHandler = messageHandler; - this.loadingTask = loadingTask; - this.#networkStream = networkStream; - this.commonObjs = new PDFObjects(); - this.fontLoader = new FontLoader({ - ownerDocument: params.ownerDocument, - styleElement: params.styleElement - }); - this.enableHWA = params.enableHWA; - this.loadingParams = params.loadingParams; - this._params = params; - this.canvasFactory = factory.canvasFactory; - this.filterFactory = factory.filterFactory; - this.cMapReaderFactory = factory.cMapReaderFactory; - this.standardFontDataFactory = factory.standardFontDataFactory; - this.wasmFactory = factory.wasmFactory; - this.destroyed = false; - this.destroyCapability = null; - this.setupMessageHandler(); - this.pagesMapper = pagesMapper; - this.pagesMapper.addListener(this.#updateCaches.bind(this)); - } - #updateCaches({ - type, - pageNumbers - }) { - if (type === "copy") { - this.#copiedPageInfo = new Map(); - for (const pageNum of pageNumbers) { - this.#copiedPageInfo.set(pageNum, { - proxy: this.#pageCache.get(pageNum - 1) || null, - promise: this.#pagePromises.get(pageNum - 1) || null - }); - } - return; - } - if (type === "delete") { - for (const pageNum of pageNumbers) { - this.#pageCache.delete(pageNum - 1); - this.#pagePromises.delete(pageNum - 1); - } - } - const newPageCache = new Map(); - const newPromiseCache = new Map(); - const { - pagesMapper - } = this; - for (let i = 0, ii = pagesMapper.pagesNumber; i < ii; i++) { - const prevPageNumber = pagesMapper.getPrevPageNumber(i + 1); - if (prevPageNumber < 0) { - const { - proxy, - promise - } = this.#copiedPageInfo?.get(-prevPageNumber) || {}; - if (proxy) { - newPageCache.set(i, proxy); - } - if (promise) { - newPromiseCache.set(i, promise); - } - continue; - } - const prevPageIndex = prevPageNumber - 1; - const page = this.#pageCache.get(prevPageIndex); - if (page) { - newPageCache.set(i, page); - } - const promise = this.#pagePromises.get(prevPageIndex); - if (promise) { - newPromiseCache.set(i, promise); - } - } - this.#pageCache = newPageCache; - this.#pagePromises = newPromiseCache; - } - #cacheSimpleMethod(name, data = null) { - return this.#methodPromises.getOrInsertComputed(name, () => this.messageHandler.sendWithPromise(name, data)); - } - #onProgress({ - loaded, - total - }) { - this.loadingTask.onProgress?.({ - loaded, - total, - percent: MathClamp(Math.round(loaded / total * 100), 0, 100) - }); - } - get annotationStorage() { - return shadow(this, "annotationStorage", new AnnotationStorage()); - } - getRenderingIntent(intent, annotationMode = AnnotationMode.ENABLE, printAnnotationStorage = null, isEditing = false, isOpList = false) { - let renderingIntent = RenderingIntentFlag.DISPLAY; - let annotationStorageSerializable = SerializableEmpty; - switch (intent) { - case "any": - renderingIntent = RenderingIntentFlag.ANY; - break; - case "display": - break; - case "print": - renderingIntent = RenderingIntentFlag.PRINT; - break; - default: - warn(`getRenderingIntent - invalid intent: ${intent}`); - } - const annotationStorage = renderingIntent & RenderingIntentFlag.PRINT && printAnnotationStorage instanceof PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage; - switch (annotationMode) { - case AnnotationMode.DISABLE: - renderingIntent += RenderingIntentFlag.ANNOTATIONS_DISABLE; - break; - case AnnotationMode.ENABLE: - break; - case AnnotationMode.ENABLE_FORMS: - renderingIntent += RenderingIntentFlag.ANNOTATIONS_FORMS; - break; - case AnnotationMode.ENABLE_STORAGE: - renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE; - annotationStorageSerializable = annotationStorage.serializable; - break; - default: - warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`); - } - if (isEditing) { - renderingIntent += RenderingIntentFlag.IS_EDITING; - } - if (isOpList) { - renderingIntent += RenderingIntentFlag.OPLIST; - } - const { - ids: modifiedIds, - hash: modifiedIdsHash - } = annotationStorage.modifiedIds; - const cacheKeyBuf = [renderingIntent, annotationStorageSerializable.hash, modifiedIdsHash]; - return { - renderingIntent, - cacheKey: cacheKeyBuf.join("_"), - annotationStorageSerializable, - modifiedIds - }; - } - destroy() { - if (this.destroyCapability) { - return this.destroyCapability.promise; - } - this.destroyed = true; - this.destroyCapability = Promise.withResolvers(); - this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback")); - const waitOn = []; - for (const page of this.#pageCache.values()) { - waitOn.push(page._destroy()); - } - this.#pageCache.clear(); - this.#pagePromises.clear(); - this.#pageRefCache.clear(); - if (this.hasOwnProperty("annotationStorage")) { - this.annotationStorage.resetModified(); - } - const terminated = this.messageHandler.sendWithPromise("Terminate", null); - waitOn.push(terminated); - Promise.all(waitOn).then(() => { - this.commonObjs.clear(); - this.fontLoader.clear(); - this.#methodPromises.clear(); - this.filterFactory.destroy(); - TextLayer.cleanup(); - this.#networkStream?.cancelAllRequests(new AbortException("Worker was terminated.")); - this.messageHandler?.destroy(); - this.messageHandler = null; - this.destroyCapability.resolve(); - }, this.destroyCapability.reject); - return this.destroyCapability.promise; - } - setupMessageHandler() { - const { - messageHandler, - loadingTask - } = this; - messageHandler.on("GetReader", (data, sink) => { - assert(this.#networkStream, "GetReader - no `BasePDFStream` instance available."); - this.#fullReader = this.#networkStream.getFullReader(); - this.#fullReader.onProgress = evt => this.#onProgress(evt); - sink.onPull = () => { - this.#fullReader.read().then(function ({ - value, - done - }) { - if (done) { - sink.close(); - return; - } - assert(value instanceof ArrayBuffer, "GetReader - expected an ArrayBuffer."); - sink.enqueue(new Uint8Array(value), 1, [value]); - }).catch(reason => { - sink.error(reason); - }); - }; - sink.onCancel = reason => { - this.#fullReader.cancel(reason); - sink.ready.catch(readyReason => { - if (this.destroyed) { - return; - } - throw readyReason; - }); - }; - }); - messageHandler.on("ReaderHeadersReady", async data => { - await this.#fullReader.headersReady; - const { - isStreamingSupported, - isRangeSupported, - contentLength - } = this.#fullReader; - if (isStreamingSupported && isRangeSupported) { - this.#fullReader.onProgress = null; - } - return { - isStreamingSupported, - isRangeSupported, - contentLength - }; - }); - messageHandler.on("GetRangeReader", (data, sink) => { - assert(this.#networkStream, "GetRangeReader - no `BasePDFStream` instance available."); - const rangeReader = this.#networkStream.getRangeReader(data.begin, data.end); - if (!rangeReader) { - sink.close(); - return; - } - sink.onPull = () => { - rangeReader.read().then(function ({ - value, - done - }) { - if (done) { - sink.close(); - return; - } - assert(value instanceof ArrayBuffer, "GetRangeReader - expected an ArrayBuffer."); - sink.enqueue(new Uint8Array(value), 1, [value]); - }).catch(reason => { - sink.error(reason); - }); - }; - sink.onCancel = reason => { - rangeReader.cancel(reason); - sink.ready.catch(readyReason => { - if (this.destroyed) { - return; - } - throw readyReason; - }); - }; - }); - messageHandler.on("GetDoc", ({ - pdfInfo - }) => { - this.pagesMapper.pagesNumber = pdfInfo.numPages; - this._numPages = pdfInfo.numPages; - this._htmlForXfa = pdfInfo.htmlForXfa; - delete pdfInfo.htmlForXfa; - loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this)); - }); - messageHandler.on("DocException", ex => { - loadingTask._capability.reject(wrapReason(ex)); - }); - messageHandler.on("PasswordRequest", ex => { - this.#passwordCapability = Promise.withResolvers(); - try { - if (!loadingTask.onPassword) { - throw wrapReason(ex); - } - const updatePassword = password => { - if (password instanceof Error) { - this.#passwordCapability.reject(password); - } else { - this.#passwordCapability.resolve({ - password - }); - } - }; - loadingTask.onPassword(updatePassword, ex.code); - } catch (err) { - this.#passwordCapability.reject(err); - } - return this.#passwordCapability.promise; - }); - messageHandler.on("DataLoaded", data => { - this.#onProgress({ - loaded: data.length, - total: data.length - }); - this.downloadInfoCapability.resolve(data); - }); - messageHandler.on("StartRenderPage", data => { - if (this.destroyed) { - return; - } - const page = this.#pageCache.get(data.pageIndex); - page._startRenderPage(data.transparency, data.cacheKey); - }); - messageHandler.on("commonobj", ([id, type, exportedData]) => { - if (this.destroyed) { - return null; - } - if (this.commonObjs.has(id)) { - return null; - } - switch (type) { - case "Font": - if ("error" in exportedData) { - const exportedError = exportedData.error; - warn(`Error during font loading: ${exportedError}`); - this.commonObjs.resolve(id, exportedError); - break; - } - const fontData = new FontInfo(exportedData); - const inspectFont = this._params.pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null; - const font = new FontFaceObject(fontData, inspectFont, exportedData.extra, exportedData.charProcOperatorList); - this.fontLoader.bind(font).catch(() => messageHandler.sendWithPromise("FontFallback", { - id - })).finally(() => { - if (!font.fontExtraProperties && font.data) { - font.clearData(); - } - this.commonObjs.resolve(id, font); - }); - break; - case "CopyLocalImage": - const { - imageRef - } = exportedData; - assert(imageRef, "The imageRef must be defined."); - for (const pageProxy of this.#pageCache.values()) { - for (const [, data] of pageProxy.objs) { - if (data?.ref !== imageRef) { - continue; - } - if (!data.dataLen) { - return null; - } - this.commonObjs.resolve(id, structuredClone(data)); - return data.dataLen; - } - } - break; - case "FontPath": - this.commonObjs.resolve(id, new FontPathInfo(exportedData)); - break; - case "Image": - this.commonObjs.resolve(id, exportedData); - break; - case "Pattern": - const pattern = new PatternInfo(exportedData); - this.commonObjs.resolve(id, pattern.getIR()); - break; - default: - throw new Error(`Got unknown common object type ${type}`); - } - return null; - }); - messageHandler.on("obj", ([id, pageIndex, type, imageData]) => { - if (this.destroyed) { - return; - } - const pageProxy = this.#pageCache.get(pageIndex); - if (pageProxy.objs.has(id)) { - return; - } - if (pageProxy._intentStates.size === 0) { - imageData?.bitmap?.close(); - return; - } - switch (type) { - case "Image": - case "Pattern": - pageProxy.objs.resolve(id, imageData); - break; - default: - throw new Error(`Got unknown object type ${type}`); - } - }); - messageHandler.on("DocProgress", data => { - if (this.destroyed) { - return; - } - this.#onProgress(data); - }); - messageHandler.on("FetchBinaryData", async data => { - if (this.destroyed) { - throw new Error("Worker was destroyed."); - } - const factory = this[data.type]; - if (!factory) { - throw new Error(`${data.type} not initialized, see the \`useWorkerFetch\` parameter.`); - } - return factory.fetch(data); - }); - } - getData() { - return this.messageHandler.sendWithPromise("GetData", null); - } - saveDocument() { - if (this.annotationStorage.size <= 0) { - warn("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead."); - } - const { - map, - transfer - } = this.annotationStorage.serializable; - return this.messageHandler.sendWithPromise("SaveDocument", { - isPureXfa: !!this._htmlForXfa, - numPages: this._numPages, - annotationStorage: map, - filename: this.#fullReader?.filename ?? null - }, transfer).finally(() => { - this.annotationStorage.resetModified(); - }); - } - extractPages(pageInfos) { - return this.messageHandler.sendWithPromise("ExtractPages", { - pageInfos - }); - } - getPage(pageNumber) { - if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this.pagesMapper.pagesNumber) { - return Promise.reject(new Error("Invalid page request.")); - } - const pageIndex = pageNumber - 1; - const newPageIndex = this.pagesMapper.getPageId(pageNumber) - 1; - const cachedPromise = this.#pagePromises.get(pageIndex); - if (cachedPromise) { - return cachedPromise; - } - const promise = this.messageHandler.sendWithPromise("GetPage", { - pageIndex: newPageIndex - }).then(pageInfo => { - if (this.destroyed) { - throw new Error("Transport destroyed"); - } - if (pageInfo.refStr) { - this.#pageRefCache.set(pageInfo.refStr, newPageIndex); - } - const page = new PDFPageProxy(pageIndex, pageInfo, this, this.pagesMapper, this._params.pdfBug); - this.#pageCache.set(pageIndex, page); - return page; - }); - this.#pagePromises.set(pageIndex, promise); - return promise; - } - async getPageIndex(ref) { - if (!isRefProxy(ref)) { - throw new Error("Invalid pageIndex request."); - } - const index = await this.messageHandler.sendWithPromise("GetPageIndex", { - num: ref.num, - gen: ref.gen - }); - const pageNumber = this.pagesMapper.getPageNumber(index + 1); - if (pageNumber === 0) { - throw new Error("GetPageIndex: page has been removed."); - } - return pageNumber - 1; - } - getAnnotations(pageIndex, intent) { - return this.messageHandler.sendWithPromise("GetAnnotations", { - pageIndex: this.pagesMapper.getPageId(pageIndex + 1) - 1, - intent - }); - } - getFieldObjects() { - return this.#cacheSimpleMethod("GetFieldObjects"); - } - hasJSActions() { - return this.#cacheSimpleMethod("HasJSActions"); - } - getCalculationOrderIds() { - return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null); - } - getDestinations() { - return this.messageHandler.sendWithPromise("GetDestinations", null); - } - getDestination(id) { - if (typeof id !== "string") { - return Promise.reject(new Error("Invalid destination request.")); - } - return this.messageHandler.sendWithPromise("GetDestination", { - id - }); - } - getPageLabels() { - return this.messageHandler.sendWithPromise("GetPageLabels", null); - } - getPageLayout() { - return this.messageHandler.sendWithPromise("GetPageLayout", null); - } - getPageMode() { - return this.messageHandler.sendWithPromise("GetPageMode", null); - } - getViewerPreferences() { - return this.messageHandler.sendWithPromise("GetViewerPreferences", null); - } - getOpenAction() { - return this.messageHandler.sendWithPromise("GetOpenAction", null); - } - getAttachments() { - return this.messageHandler.sendWithPromise("GetAttachments", null); - } - getAnnotationsByType(types, pageIndexesToSkip) { - return this.messageHandler.sendWithPromise("GetAnnotationsByType", { - types, - pageIndexesToSkip - }); - } - getDocJSActions() { - return this.#cacheSimpleMethod("GetDocJSActions"); - } - getPageJSActions(pageIndex) { - return this.messageHandler.sendWithPromise("GetPageJSActions", { - pageIndex: this.pagesMapper.getPageId(pageIndex + 1) - 1 - }); - } - getStructTree(pageIndex) { - return this.messageHandler.sendWithPromise("GetStructTree", { - pageIndex: this.pagesMapper.getPageId(pageIndex + 1) - 1 - }); - } - getOutline() { - return this.messageHandler.sendWithPromise("GetOutline", null); - } - getOptionalContentConfig(renderingIntent) { - return this.#cacheSimpleMethod("GetOptionalContentConfig").then(data => new OptionalContentConfig(data, renderingIntent)); - } - getPermissions() { - return this.messageHandler.sendWithPromise("GetPermissions", null); - } - getMetadata() { - const name = "GetMetadata"; - return this.#methodPromises.getOrInsertComputed(name, () => this.messageHandler.sendWithPromise(name, null).then(results => ({ - info: results[0], - metadata: results[1] ? new Metadata(results[1]) : null, - contentDispositionFilename: this.#fullReader?.filename ?? null, - contentLength: this.#fullReader?.contentLength ?? null, - hasStructTree: results[2] - }))); - } - getMarkInfo() { - return this.messageHandler.sendWithPromise("GetMarkInfo", null); - } - async startCleanup(keepLoadedFonts = false) { - if (this.destroyed) { - return; - } - await this.messageHandler.sendWithPromise("Cleanup", null); - for (const page of this.#pageCache.values()) { - const cleanupSuccessful = page.cleanup(); - if (!cleanupSuccessful) { - throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`); - } - } - this.commonObjs.clear(); - if (!keepLoadedFonts) { - this.fontLoader.clear(); - } - this.#methodPromises.clear(); - this.filterFactory.destroy(true); - TextLayer.cleanup(); - } - cachedPageNumber(ref) { - if (!isRefProxy(ref)) { - return null; - } - const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`; - const pageIndex = this.#pageRefCache.get(refStr); - if (pageIndex >= 0) { - const pageNumber = this.pagesMapper.getPageNumber(pageIndex + 1); - if (pageNumber !== 0) { - return pageNumber; - } - } - return null; - } -} -class RenderTask { - _internalRenderTask = null; - onContinue = null; - onError = null; - constructor(internalRenderTask) { - this._internalRenderTask = internalRenderTask; - } - get promise() { - return this._internalRenderTask.capability.promise; - } - cancel(extraDelay = 0) { - this._internalRenderTask.cancel(null, extraDelay); - } - get separateAnnots() { - const { - separateAnnots - } = this._internalRenderTask.operatorList; - if (!separateAnnots) { - return false; - } - const { - annotationCanvasMap - } = this._internalRenderTask; - return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0; - } -} -class InternalRenderTask { - #rAF = null; - static #canvasInUse = new WeakSet(); - constructor({ - callback, - params, - objs, - commonObjs, - annotationCanvasMap, - operatorList, - pageIndex, - canvasFactory, - filterFactory, - useRequestAnimationFrame = false, - pdfBug = false, - pageColors = null, - enableHWA = false, - operationsFilter = null - }) { - this.callback = callback; - this.params = params; - this.objs = objs; - this.commonObjs = commonObjs; - this.annotationCanvasMap = annotationCanvasMap; - this.operatorListIdx = null; - this.operatorList = operatorList; - this._pageIndex = pageIndex; - this.canvasFactory = canvasFactory; - this.filterFactory = filterFactory; - this._pdfBug = pdfBug; - this.pageColors = pageColors; - this.running = false; - this.graphicsReadyCallback = null; - this.graphicsReady = false; - this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined"; - this.cancelled = false; - this.capability = Promise.withResolvers(); - this.task = new RenderTask(this); - this._cancelBound = this.cancel.bind(this); - this._continueBound = this._continue.bind(this); - this._scheduleNextBound = this._scheduleNext.bind(this); - this._nextBound = this._next.bind(this); - this._canvas = params.canvas; - this._canvasContext = params.canvas ? null : params.canvasContext; - this._enableHWA = enableHWA; - this._dependencyTracker = params.dependencyTracker; - this._operationsFilter = operationsFilter; - } - get completed() { - return this.capability.promise.catch(function () {}); - } - initializeGraphics({ - transparency = false, - optionalContentConfig - }) { - if (this.cancelled) { - return; - } - if (this._canvas) { - if (InternalRenderTask.#canvasInUse.has(this._canvas)) { - throw new Error("Cannot use the same canvas during multiple render() operations. " + "Use different canvas or ensure previous operations were " + "cancelled or completed."); - } - InternalRenderTask.#canvasInUse.add(this._canvas); - } - if (this._pdfBug && globalThis.StepperManager?.enabled) { - this.stepper = globalThis.StepperManager.create(this._pageIndex); - this.stepper.init(this.operatorList); - this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); - } - const { - viewport, - transform, - background, - dependencyTracker - } = this.params; - const canvasContext = this._canvasContext || this._canvas.getContext("2d", { - alpha: false, - willReadFrequently: !this._enableHWA - }); - this.gfx = new CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { - optionalContentConfig - }, this.annotationCanvasMap, this.pageColors, dependencyTracker); - this.gfx.beginDrawing({ - transform, - viewport, - transparency, - background - }); - this.operatorListIdx = 0; - this.graphicsReady = true; - this.graphicsReadyCallback?.(); - } - cancel(error = null, extraDelay = 0) { - this.running = false; - this.cancelled = true; - this.gfx?.endDrawing(); - if (this.#rAF) { - window.cancelAnimationFrame(this.#rAF); - this.#rAF = null; - } - InternalRenderTask.#canvasInUse.delete(this._canvas); - error ||= new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay); - this.callback(error); - this.task.onError?.(error); - } - operatorListChanged() { - if (!this.graphicsReady) { - this.graphicsReadyCallback ||= this._continueBound; - return; - } - this.gfx.dependencyTracker?.growOperationsCount(this.operatorList.fnArray.length); - this.stepper?.updateOperatorList(this.operatorList); - if (this.running) { - return; - } - this._continue(); - } - _continue() { - this.running = true; - if (this.cancelled) { - return; - } - if (this.task.onContinue) { - this.task.onContinue(this._scheduleNextBound); - } else { - this._scheduleNext(); - } - } - _scheduleNext() { - if (this._useRequestAnimationFrame) { - this.#rAF = window.requestAnimationFrame(() => { - this.#rAF = null; - this._nextBound().catch(this._cancelBound); - }); - } else { - Promise.resolve().then(this._nextBound).catch(this._cancelBound); - } - } - async _next() { - if (this.cancelled) { - return; - } - this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper, this._operationsFilter); - if (this.operatorListIdx === this.operatorList.argsArray.length) { - this.running = false; - if (this.operatorList.lastChunk) { - this.gfx.endDrawing(); - InternalRenderTask.#canvasInUse.delete(this._canvas); - this.callback(); - } - } - } -} -const version = "5.5.207"; -const build = "527964698"; - -;// ./src/display/editor/color_picker.js - - - -class ColorPicker { - #button = null; - #buttonSwatch = null; - #defaultColor; - #dropdown = null; - #dropdownWasFromKeyboard = false; - #isMainColorPicker = false; - #editor = null; - #eventBus; - #openDropdownAC = null; - #uiManager = null; - static #l10nColor = null; - static get _keyboardManager() { - return shadow(this, "_keyboardManager", new KeyboardManager([[["Escape", "mac+Escape"], ColorPicker.prototype._hideDropdownFromKeyboard], [[" ", "mac+ "], ColorPicker.prototype._colorSelectFromKeyboard], [["ArrowDown", "ArrowRight", "mac+ArrowDown", "mac+ArrowRight"], ColorPicker.prototype._moveToNext], [["ArrowUp", "ArrowLeft", "mac+ArrowUp", "mac+ArrowLeft"], ColorPicker.prototype._moveToPrevious], [["Home", "mac+Home"], ColorPicker.prototype._moveToBeginning], [["End", "mac+End"], ColorPicker.prototype._moveToEnd]])); - } - constructor({ - editor = null, - uiManager = null - }) { - if (editor) { - this.#isMainColorPicker = false; - this.#editor = editor; - } else { - this.#isMainColorPicker = true; - } - this.#uiManager = editor?._uiManager || uiManager; - this.#eventBus = this.#uiManager._eventBus; - this.#defaultColor = editor?.color?.toUpperCase() || this.#uiManager?.highlightColors.values().next().value || "#FFFF98"; - ColorPicker.#l10nColor ||= Object.freeze({ - blue: "pdfjs-editor-colorpicker-blue", - green: "pdfjs-editor-colorpicker-green", - pink: "pdfjs-editor-colorpicker-pink", - red: "pdfjs-editor-colorpicker-red", - yellow: "pdfjs-editor-colorpicker-yellow" - }); - } - renderButton() { - const button = this.#button = document.createElement("button"); - button.className = "colorPicker"; - button.tabIndex = "0"; - button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button"); - button.ariaHasPopup = "true"; - if (this.#editor) { - button.ariaControls = `${this.#editor.id}_colorpicker_dropdown`; - } - const signal = this.#uiManager._signal; - button.addEventListener("click", this.#openDropdown.bind(this), { - signal - }); - button.addEventListener("keydown", this.#keyDown.bind(this), { - signal - }); - const swatch = this.#buttonSwatch = document.createElement("span"); - swatch.className = "swatch"; - swatch.ariaHidden = "true"; - swatch.style.backgroundColor = this.#defaultColor; - button.append(swatch); - return button; - } - renderMainDropdown() { - const dropdown = this.#dropdown = this.#getDropdownRoot(); - dropdown.ariaOrientation = "horizontal"; - dropdown.ariaLabelledBy = "highlightColorPickerLabel"; - return dropdown; - } - #getDropdownRoot() { - const div = document.createElement("div"); - const signal = this.#uiManager._signal; - div.addEventListener("contextmenu", noContextMenu, { - signal - }); - div.className = "dropdown"; - div.role = "listbox"; - div.ariaMultiSelectable = "false"; - div.ariaOrientation = "vertical"; - div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown"); - if (this.#editor) { - div.id = `${this.#editor.id}_colorpicker_dropdown`; - } - for (const [name, color] of this.#uiManager.highlightColors) { - const button = document.createElement("button"); - button.tabIndex = "0"; - button.role = "option"; - button.setAttribute("data-color", color); - button.title = name; - button.setAttribute("data-l10n-id", ColorPicker.#l10nColor[name]); - const swatch = document.createElement("span"); - button.append(swatch); - swatch.className = "swatch"; - swatch.style.backgroundColor = color; - button.ariaSelected = color === this.#defaultColor; - button.addEventListener("click", this.#colorSelect.bind(this, color), { - signal - }); - div.append(button); - } - div.addEventListener("keydown", this.#keyDown.bind(this), { - signal - }); - return div; - } - #colorSelect(color, event) { - event.stopPropagation(); - this.#eventBus.dispatch("switchannotationeditorparams", { - source: this, - type: AnnotationEditorParamsType.HIGHLIGHT_COLOR, - value: color - }); - this.updateColor(color); - } - _colorSelectFromKeyboard(event) { - if (event.target === this.#button) { - this.#openDropdown(event); - return; - } - const color = event.target.getAttribute("data-color"); - if (!color) { - return; - } - this.#colorSelect(color, event); - } - _moveToNext(event) { - if (!this.#isDropdownVisible) { - this.#openDropdown(event); - return; - } - if (event.target === this.#button) { - this.#dropdown.firstElementChild?.focus(); - return; - } - event.target.nextSibling?.focus(); - } - _moveToPrevious(event) { - if (event.target === this.#dropdown?.firstElementChild || event.target === this.#button) { - if (this.#isDropdownVisible) { - this._hideDropdownFromKeyboard(); - } - return; - } - if (!this.#isDropdownVisible) { - this.#openDropdown(event); - } - event.target.previousSibling?.focus(); - } - _moveToBeginning(event) { - if (!this.#isDropdownVisible) { - this.#openDropdown(event); - return; - } - this.#dropdown.firstElementChild?.focus(); - } - _moveToEnd(event) { - if (!this.#isDropdownVisible) { - this.#openDropdown(event); - return; - } - this.#dropdown.lastElementChild?.focus(); - } - #keyDown(event) { - ColorPicker._keyboardManager.exec(this, event); - } - #openDropdown(event) { - if (this.#isDropdownVisible) { - this.hideDropdown(); - return; - } - this.#dropdownWasFromKeyboard = event.detail === 0; - if (!this.#openDropdownAC) { - this.#openDropdownAC = new AbortController(); - window.addEventListener("pointerdown", this.#pointerDown.bind(this), { - signal: this.#uiManager.combinedSignal(this.#openDropdownAC) - }); - } - this.#button.ariaExpanded = "true"; - if (this.#dropdown) { - this.#dropdown.classList.remove("hidden"); - return; - } - const root = this.#dropdown = this.#getDropdownRoot(); - this.#button.append(root); - } - #pointerDown(event) { - if (this.#dropdown?.contains(event.target)) { - return; - } - this.hideDropdown(); - } - hideDropdown() { - this.#dropdown?.classList.add("hidden"); - this.#button.ariaExpanded = "false"; - this.#openDropdownAC?.abort(); - this.#openDropdownAC = null; - } - get #isDropdownVisible() { - return this.#dropdown && !this.#dropdown.classList.contains("hidden"); - } - _hideDropdownFromKeyboard() { - if (this.#isMainColorPicker) { - return; - } - if (!this.#isDropdownVisible) { - this.#editor?.unselect(); - return; - } - this.hideDropdown(); - this.#button.focus({ - preventScroll: true, - focusVisible: this.#dropdownWasFromKeyboard - }); - } - updateColor(color) { - if (this.#buttonSwatch) { - this.#buttonSwatch.style.backgroundColor = color; - } - if (!this.#dropdown) { - return; - } - const i = this.#uiManager.highlightColors.values(); - for (const child of this.#dropdown.children) { - child.ariaSelected = i.next().value === color.toUpperCase(); - } - } - destroy() { - this.#button?.remove(); - this.#button = null; - this.#buttonSwatch = null; - this.#dropdown?.remove(); - this.#dropdown = null; - } -} -class BasicColorPicker { - #input = null; - #editor = null; - #uiManager = null; - static #l10nColor = null; - constructor(editor) { - this.#editor = editor; - this.#uiManager = editor._uiManager; - BasicColorPicker.#l10nColor ||= Object.freeze({ - freetext: "pdfjs-editor-color-picker-free-text-input", - ink: "pdfjs-editor-color-picker-ink-input" - }); - } - renderButton() { - if (this.#input) { - return this.#input; - } - const { - editorType, - colorType, - color - } = this.#editor; - const input = this.#input = document.createElement("input"); - input.type = "color"; - input.value = color || "#000000"; - input.className = "basicColorPicker"; - input.tabIndex = 0; - input.setAttribute("data-l10n-id", BasicColorPicker.#l10nColor[editorType]); - input.addEventListener("input", () => { - this.#uiManager.updateParams(colorType, input.value); - }, { - signal: this.#uiManager._signal - }); - return input; - } - update(value) { - if (!this.#input) { - return; - } - this.#input.value = value; - } - destroy() { - this.#input?.remove(); - this.#input = null; - } - hideDropdown() {} -} - -;// ./src/shared/scripting_utils.js -function makeColorComp(n) { - return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0"); -} -function scaleAndClamp(x) { - return Math.max(0, Math.min(255, 255 * x)); -} -class ColorConverters { - static CMYK_G([c, y, m, k]) { - return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)]; - } - static G_CMYK([g]) { - return ["CMYK", 0, 0, 0, 1 - g]; - } - static G_RGB([g]) { - return ["RGB", g, g, g]; - } - static G_rgb([g]) { - g = scaleAndClamp(g); - return [g, g, g]; - } - static G_HTML([g]) { - const G = makeColorComp(g); - return `#${G}${G}${G}`; - } - static RGB_G([r, g, b]) { - return ["G", 0.3 * r + 0.59 * g + 0.11 * b]; - } - static RGB_rgb(color) { - return color.map(scaleAndClamp); - } - static RGB_HTML(color) { - return `#${color.map(makeColorComp).join("")}`; - } - static T_HTML() { - return "#00000000"; - } - static T_rgb() { - return [null]; - } - static CMYK_RGB([c, y, m, k]) { - return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)]; - } - static CMYK_rgb([c, y, m, k]) { - return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))]; - } - static CMYK_HTML(components) { - const rgb = this.CMYK_RGB(components).slice(1); - return this.RGB_HTML(rgb); - } - static RGB_CMYK([r, g, b]) { - const c = 1 - r; - const m = 1 - g; - const y = 1 - b; - const k = Math.min(c, m, y); - return ["CMYK", c, m, y, k]; - } -} -const DateFormats = (/* unused pure expression or super */ null && (["m/d", "m/d/yy", "mm/dd/yy", "mm/yy", "d-mmm", "d-mmm-yy", "dd-mmm-yy", "yy-mm-dd", "mmm-yy", "mmmm-yy", "mmm d, yyyy", "mmmm d, yyyy", "m/d/yy h:MM tt", "m/d/yy HH:MM"])); -const TimeFormats = (/* unused pure expression or super */ null && (["HH:MM", "h:MM tt", "HH:MM:ss", "h:MM:ss tt"])); - -;// ./src/display/svg_factory.js - - -class BaseSVGFactory { - create(width, height, skipDimensions = false) { - if (width <= 0 || height <= 0) { - throw new Error("Invalid SVG dimensions"); - } - const svg = this._createSVG("svg:svg"); - svg.setAttribute("version", "1.1"); - if (!skipDimensions) { - svg.setAttribute("width", `${width}px`); - svg.setAttribute("height", `${height}px`); - } - svg.setAttribute("preserveAspectRatio", "none"); - svg.setAttribute("viewBox", `0 0 ${width} ${height}`); - return svg; - } - createElement(type) { - if (typeof type !== "string") { - throw new Error("Invalid SVG element type"); - } - return this._createSVG(type); - } - _createSVG(type) { - unreachable("Abstract method `_createSVG` called."); - } -} -class DOMSVGFactory extends BaseSVGFactory { - _createSVG(type) { - return document.createElementNS(SVG_NS, type); - } -} - -;// ./src/display/annotation_layer.js - - - - - -const annotation_layer_DEFAULT_FONT_SIZE = 9; -const GetElementsByNameSet = new WeakSet(); -const TIMEZONE_OFFSET = new Date().getTimezoneOffset() * 60 * 1000; -class AnnotationElementFactory { - static create(parameters) { - const subtype = parameters.data.annotationType; - switch (subtype) { - case AnnotationType.LINK: - return new LinkAnnotationElement(parameters); - case AnnotationType.TEXT: - return new TextAnnotationElement(parameters); - case AnnotationType.WIDGET: - const fieldType = parameters.data.fieldType; - switch (fieldType) { - case "Tx": - return new TextWidgetAnnotationElement(parameters); - case "Btn": - if (parameters.data.radioButton) { - return new RadioButtonWidgetAnnotationElement(parameters); - } else if (parameters.data.checkBox) { - return new CheckboxWidgetAnnotationElement(parameters); - } - return new PushButtonWidgetAnnotationElement(parameters); - case "Ch": - return new ChoiceWidgetAnnotationElement(parameters); - case "Sig": - return new SignatureWidgetAnnotationElement(parameters); - } - return new WidgetAnnotationElement(parameters); - case AnnotationType.POPUP: - return new PopupAnnotationElement(parameters); - case AnnotationType.FREETEXT: - return new FreeTextAnnotationElement(parameters); - case AnnotationType.LINE: - return new LineAnnotationElement(parameters); - case AnnotationType.SQUARE: - return new SquareAnnotationElement(parameters); - case AnnotationType.CIRCLE: - return new CircleAnnotationElement(parameters); - case AnnotationType.POLYLINE: - return new PolylineAnnotationElement(parameters); - case AnnotationType.CARET: - return new CaretAnnotationElement(parameters); - case AnnotationType.INK: - return new InkAnnotationElement(parameters); - case AnnotationType.POLYGON: - return new PolygonAnnotationElement(parameters); - case AnnotationType.HIGHLIGHT: - return new HighlightAnnotationElement(parameters); - case AnnotationType.UNDERLINE: - return new UnderlineAnnotationElement(parameters); - case AnnotationType.SQUIGGLY: - return new SquigglyAnnotationElement(parameters); - case AnnotationType.STRIKEOUT: - return new StrikeOutAnnotationElement(parameters); - case AnnotationType.STAMP: - return new StampAnnotationElement(parameters); - case AnnotationType.FILEATTACHMENT: - return new FileAttachmentAnnotationElement(parameters); - default: - return new AnnotationElement(parameters); - } - } -} -class AnnotationElement { - #updates = null; - #hasBorder = false; - #popupElement = null; - constructor(parameters, { - isRenderable = false, - ignoreBorder = false, - createQuadrilaterals = false - } = {}) { - this.isRenderable = isRenderable; - this.data = parameters.data; - this.layer = parameters.layer; - this.linkService = parameters.linkService; - this.downloadManager = parameters.downloadManager; - this.imageResourcesPath = parameters.imageResourcesPath; - this.renderForms = parameters.renderForms; - this.svgFactory = parameters.svgFactory; - this.annotationStorage = parameters.annotationStorage; - this.enableComment = parameters.enableComment; - this.enableScripting = parameters.enableScripting; - this.hasJSActions = parameters.hasJSActions; - this._fieldObjects = parameters.fieldObjects; - this.parent = parameters.parent; - this.hasOwnCommentButton = false; - if (isRenderable) { - this.contentElement = this.container = this._createContainer(ignoreBorder); - } - if (createQuadrilaterals) { - this._createQuadrilaterals(); - } - } - static _hasPopupData({ - contentsObj, - richText - }) { - return !!(contentsObj?.str || richText?.str); - } - get _isEditable() { - return this.data.isEditable; - } - get hasPopupData() { - return AnnotationElement._hasPopupData(this.data) || this.enableComment && !!this.commentText; - } - get commentData() { - const { - data - } = this; - const editor = this.annotationStorage?.getEditor(data.id); - if (editor) { - return editor.getData(); - } - return data; - } - get hasCommentButton() { - return this.enableComment && this.hasPopupElement; - } - get commentButtonPosition() { - const editor = this.annotationStorage?.getEditor(this.data.id); - if (editor) { - return editor.commentButtonPositionInPage; - } - const { - quadPoints, - inkLists, - rect - } = this.data; - let maxX = -Infinity; - let maxY = -Infinity; - if (quadPoints?.length >= 8) { - for (let i = 0; i < quadPoints.length; i += 8) { - if (quadPoints[i + 1] > maxY) { - maxY = quadPoints[i + 1]; - maxX = quadPoints[i + 2]; - } else if (quadPoints[i + 1] === maxY) { - maxX = Math.max(maxX, quadPoints[i + 2]); - } - } - return [maxX, maxY]; - } - if (inkLists?.length >= 1) { - for (const inkList of inkLists) { - for (let i = 0, ii = inkList.length; i < ii; i += 2) { - if (inkList[i + 1] > maxY) { - maxY = inkList[i + 1]; - maxX = inkList[i]; - } else if (inkList[i + 1] === maxY) { - maxX = Math.max(maxX, inkList[i]); - } - } - } - if (maxX !== Infinity) { - return [maxX, maxY]; - } - } - if (rect) { - return [rect[2], rect[3]]; - } - return null; - } - _normalizePoint(point) { - const { - page: { - view - }, - viewport: { - rawDims: { - pageWidth, - pageHeight, - pageX, - pageY - } - } - } = this.parent; - point[1] = view[3] - point[1] + view[1]; - point[0] = 100 * (point[0] - pageX) / pageWidth; - point[1] = 100 * (point[1] - pageY) / pageHeight; - return point; - } - get commentText() { - const { - data - } = this; - return this.annotationStorage.getRawValue(`${AnnotationEditorPrefix}${data.id}`)?.popup?.contents || data.contentsObj?.str || ""; - } - set commentText(text) { - const { - data - } = this; - const popup = { - deleted: !text, - contents: text || "" - }; - if (!this.annotationStorage.updateEditor(data.id, { - popup - })) { - this.annotationStorage.setValue(`${AnnotationEditorPrefix}${data.id}`, { - id: data.id, - annotationType: data.annotationType, - page: this.parent.page, - popup, - popupRef: data.popupRef, - modificationDate: new Date() - }); - } - if (!text) { - this.removePopup(); - } - } - removePopup() { - (this.#popupElement?.popup || this.popup)?.remove(); - this.#popupElement = this.popup = null; - } - updateEdited(params) { - if (!this.container) { - return; - } - if (params.rect) { - this.#updates ||= { - rect: this.data.rect.slice(0) - }; - } - const { - rect, - popup: newPopup - } = params; - if (rect) { - this.#setRectEdited(rect); - } - let popup = this.#popupElement?.popup || this.popup; - if (!popup && newPopup?.text) { - this._createPopup(newPopup); - popup = this.#popupElement.popup; - } - if (!popup) { - return; - } - popup.updateEdited(params); - if (newPopup?.deleted) { - popup.remove(); - this.#popupElement = null; - this.popup = null; - } - } - resetEdited() { - if (!this.#updates) { - return; - } - this.#setRectEdited(this.#updates.rect); - this.#popupElement?.popup.resetEdited(); - this.#updates = null; - } - #setRectEdited(rect) { - const { - container: { - style - }, - data: { - rect: currentRect, - rotation - }, - parent: { - viewport: { - rawDims: { - pageWidth, - pageHeight, - pageX, - pageY - } - } - } - } = this; - currentRect?.splice(0, 4, ...rect); - style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; - style.top = `${100 * (pageHeight - rect[3] + pageY) / pageHeight}%`; - if (rotation === 0) { - style.width = `${100 * (rect[2] - rect[0]) / pageWidth}%`; - style.height = `${100 * (rect[3] - rect[1]) / pageHeight}%`; - } else { - this.setRotation(rotation); - } - } - _createContainer(ignoreBorder) { - const { - data, - parent: { - page, - viewport - } - } = this; - const container = document.createElement("section"); - container.setAttribute("data-annotation-id", data.id); - if (!(this instanceof WidgetAnnotationElement) && !(this instanceof LinkAnnotationElement)) { - container.tabIndex = 0; - } - const { - style - } = container; - style.zIndex = this.parent.zIndex; - this.parent.zIndex += 2; - if (data.alternativeText) { - container.title = data.alternativeText; - } - if (data.noRotate) { - container.classList.add("norotate"); - } - if (!data.rect || this instanceof PopupAnnotationElement) { - const { - rotation - } = data; - if (!data.hasOwnCanvas && rotation !== 0) { - this.setRotation(rotation, container); - } - return container; - } - const { - width, - height - } = this; - if (!ignoreBorder && data.borderStyle.width > 0) { - style.borderWidth = `${data.borderStyle.width}px`; - const horizontalRadius = data.borderStyle.horizontalCornerRadius; - const verticalRadius = data.borderStyle.verticalCornerRadius; - if (horizontalRadius > 0 || verticalRadius > 0) { - const radius = `calc(${horizontalRadius}px * var(--total-scale-factor)) / calc(${verticalRadius}px * var(--total-scale-factor))`; - style.borderRadius = radius; - } else if (this instanceof RadioButtonWidgetAnnotationElement) { - const radius = `calc(${width}px * var(--total-scale-factor)) / calc(${height}px * var(--total-scale-factor))`; - style.borderRadius = radius; - } - switch (data.borderStyle.style) { - case AnnotationBorderStyleType.SOLID: - style.borderStyle = "solid"; - break; - case AnnotationBorderStyleType.DASHED: - style.borderStyle = "dashed"; - break; - case AnnotationBorderStyleType.BEVELED: - warn("Unimplemented border style: beveled"); - break; - case AnnotationBorderStyleType.INSET: - warn("Unimplemented border style: inset"); - break; - case AnnotationBorderStyleType.UNDERLINE: - style.borderBottomStyle = "solid"; - break; - default: - break; - } - const borderColor = data.borderColor || null; - if (borderColor) { - this.#hasBorder = true; - style.borderColor = Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0); - } else { - style.borderWidth = 0; - } - } - const rect = Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]); - const { - pageWidth, - pageHeight, - pageX, - pageY - } = viewport.rawDims; - style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; - style.top = `${100 * (rect[1] - pageY) / pageHeight}%`; - const { - rotation - } = data; - if (data.hasOwnCanvas || rotation === 0) { - style.width = `${100 * width / pageWidth}%`; - style.height = `${100 * height / pageHeight}%`; - } else { - this.setRotation(rotation, container); - } - return container; - } - setRotation(angle, container = this.container) { - if (!this.data.rect) { - return; - } - const { - pageWidth, - pageHeight - } = this.parent.viewport.rawDims; - let { - width, - height - } = this; - if (angle % 180 !== 0) { - [width, height] = [height, width]; - } - container.style.width = `${100 * width / pageWidth}%`; - container.style.height = `${100 * height / pageHeight}%`; - container.setAttribute("data-main-rotation", (360 - angle) % 360); - } - get _commonActions() { - const setColor = (jsName, styleName, event) => { - const color = event.detail[jsName]; - const colorType = color[0]; - const colorArray = color.slice(1); - event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray); - this.annotationStorage.setValue(this.data.id, { - [styleName]: ColorConverters[`${colorType}_rgb`](colorArray) - }); - }; - return shadow(this, "_commonActions", { - display: event => { - const { - display - } = event.detail; - const hidden = display % 2 === 1; - this.container.style.visibility = hidden ? "hidden" : "visible"; - this.annotationStorage.setValue(this.data.id, { - noView: hidden, - noPrint: display === 1 || display === 2 - }); - }, - print: event => { - this.annotationStorage.setValue(this.data.id, { - noPrint: !event.detail.print - }); - }, - hidden: event => { - const { - hidden - } = event.detail; - this.container.style.visibility = hidden ? "hidden" : "visible"; - this.annotationStorage.setValue(this.data.id, { - noPrint: hidden, - noView: hidden - }); - }, - focus: event => { - setTimeout(() => event.target.focus({ - preventScroll: false - }), 0); - }, - userName: event => { - event.target.title = event.detail.userName; - }, - readonly: event => { - event.target.disabled = event.detail.readonly; - }, - required: event => { - this._setRequired(event.target, event.detail.required); - }, - bgColor: event => { - setColor("bgColor", "backgroundColor", event); - }, - fillColor: event => { - setColor("fillColor", "backgroundColor", event); - }, - fgColor: event => { - setColor("fgColor", "color", event); - }, - textColor: event => { - setColor("textColor", "color", event); - }, - borderColor: event => { - setColor("borderColor", "borderColor", event); - }, - strokeColor: event => { - setColor("strokeColor", "borderColor", event); - }, - rotation: event => { - const angle = event.detail.rotation; - this.setRotation(angle); - this.annotationStorage.setValue(this.data.id, { - rotation: angle - }); - } - }); - } - _dispatchEventFromSandbox(actions, jsEvent) { - const commonActions = this._commonActions; - for (const name of Object.keys(jsEvent.detail)) { - const action = actions[name] || commonActions[name]; - action?.(jsEvent); - } - } - _setDefaultPropertiesFromJS(element) { - if (!this.enableScripting) { - return; - } - const storedData = this.annotationStorage.getRawValue(this.data.id); - if (!storedData) { - return; - } - const commonActions = this._commonActions; - for (const [actionName, detail] of Object.entries(storedData)) { - const action = commonActions[actionName]; - if (action) { - const eventProxy = { - detail: { - [actionName]: detail - }, - target: element - }; - action(eventProxy); - delete storedData[actionName]; - } - } - } - _createQuadrilaterals() { - if (!this.container) { - return; - } - const { - quadPoints - } = this.data; - if (!quadPoints) { - return; - } - const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect.map(x => Math.fround(x)); - if (quadPoints.length === 8) { - const [trX, trY, blX, blY] = quadPoints.subarray(2, 6); - if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) { - return; - } - } - const { - style - } = this.container; - let svgBuffer; - if (this.#hasBorder) { - const { - borderColor, - borderWidth - } = style; - style.borderWidth = 0; - svgBuffer = ["url('data:image/svg+xml;utf8,", `<svg xmlns="http://www.w3.org/2000/svg"`, ` preserveAspectRatio="none" viewBox="0 0 1 1">`, `<g fill="transparent" stroke="${borderColor}" stroke-width="${borderWidth}">`]; - this.container.classList.add("hasBorder"); - } - const width = rectTrX - rectBlX; - const height = rectTrY - rectBlY; - const { - svgFactory - } = this; - const svg = svgFactory.createElement("svg"); - svg.classList.add("quadrilateralsContainer"); - svg.setAttribute("width", 0); - svg.setAttribute("height", 0); - svg.role = "none"; - const defs = svgFactory.createElement("defs"); - svg.append(defs); - const clipPath = svgFactory.createElement("clipPath"); - const id = `clippath_${this.data.id}`; - clipPath.setAttribute("id", id); - clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); - defs.append(clipPath); - for (let i = 2, ii = quadPoints.length; i < ii; i += 8) { - const trX = quadPoints[i]; - const trY = quadPoints[i + 1]; - const blX = quadPoints[i + 2]; - const blY = quadPoints[i + 3]; - const rect = svgFactory.createElement("rect"); - const x = (blX - rectBlX) / width; - const y = (rectTrY - trY) / height; - const rectWidth = (trX - blX) / width; - const rectHeight = (trY - blY) / height; - rect.setAttribute("x", x); - rect.setAttribute("y", y); - rect.setAttribute("width", rectWidth); - rect.setAttribute("height", rectHeight); - clipPath.append(rect); - svgBuffer?.push(`<rect vector-effect="non-scaling-stroke" x="${x}" y="${y}" width="${rectWidth}" height="${rectHeight}"/>`); - } - if (this.#hasBorder) { - svgBuffer.push(`</g></svg>')`); - style.backgroundImage = svgBuffer.join(""); - } - this.container.append(svg); - this.container.style.clipPath = `url(#${id})`; - } - _createPopup(popupData = null) { - const { - data - } = this; - let contentsObj, modificationDate; - if (popupData) { - contentsObj = { - str: popupData.text - }; - modificationDate = popupData.date; - } else { - contentsObj = data.contentsObj; - modificationDate = data.modificationDate; - } - this.#popupElement = new PopupAnnotationElement({ - data: { - color: data.color, - titleObj: data.titleObj, - modificationDate, - contentsObj, - richText: data.richText, - parentRect: data.rect, - borderStyle: 0, - id: `popup_${data.id}`, - rotation: data.rotation, - noRotate: true - }, - linkService: this.linkService, - parent: this.parent, - elements: [this] - }); - } - get hasPopupElement() { - return !!(this.#popupElement || this.popup || this.data.popupRef); - } - get extraPopupElement() { - return this.#popupElement; - } - render() { - unreachable("Abstract method `AnnotationElement.render` called"); - } - _getElementsByName(name, skipId = null) { - const fields = []; - if (this._fieldObjects) { - const fieldObj = this._fieldObjects[name]; - if (fieldObj) { - for (const { - page, - id, - exportValues - } of fieldObj) { - if (page === -1) { - continue; - } - if (id === skipId) { - continue; - } - const exportValue = typeof exportValues === "string" ? exportValues : null; - const domElement = document.querySelector(`[data-element-id="${id}"]`); - if (domElement && !GetElementsByNameSet.has(domElement)) { - warn(`_getElementsByName - element not allowed: ${id}`); - continue; - } - fields.push({ - id, - exportValue, - domElement - }); - } - } - return fields; - } - for (const domElement of document.getElementsByName(name)) { - const { - exportValue - } = domElement; - const id = domElement.getAttribute("data-element-id"); - if (id === skipId) { - continue; - } - if (!GetElementsByNameSet.has(domElement)) { - continue; - } - fields.push({ - id, - exportValue, - domElement - }); - } - return fields; - } - show() { - if (this.container) { - this.container.hidden = false; - } - this.popup?.maybeShow(); - } - hide() { - if (this.container) { - this.container.hidden = true; - } - this.popup?.forceHide(); - } - getElementsToTriggerPopup() { - return this.container; - } - addHighlightArea() { - const triggers = this.getElementsToTriggerPopup(); - if (Array.isArray(triggers)) { - for (const element of triggers) { - element.classList.add("highlightArea"); - } - } else { - triggers.classList.add("highlightArea"); - } - } - _editOnDoubleClick() { - if (!this._isEditable) { - return; - } - const { - annotationEditorType: mode, - data: { - id: editId - } - } = this; - this.container.addEventListener("dblclick", () => { - this.linkService.eventBus?.dispatch("switchannotationeditormode", { - source: this, - mode, - editId, - mustEnterInEditMode: true - }); - }); - } - get width() { - return this.data.rect[2] - this.data.rect[0]; - } - get height() { - return this.data.rect[3] - this.data.rect[1]; - } -} -class EditorAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - this.editor = parameters.editor; - } - render() { - this.container.className = "editorAnnotation"; - return this.container; - } - createOrUpdatePopup() { - const { - editor - } = this; - if (!editor.hasComment) { - return; - } - this._createPopup(editor.comment); - } - get hasCommentButton() { - return this.enableComment && this.editor.hasComment; - } - get commentButtonPosition() { - return this.editor.commentButtonPositionInPage; - } - get commentText() { - return this.editor.comment.text; - } - set commentText(text) { - this.editor.comment = text; - if (!text) { - this.removePopup(); - } - } - get commentData() { - return this.editor.getData(); - } - remove() { - this.parent.removeAnnotation(this.data.id); - this.container.remove(); - this.container = null; - this.removePopup(); - } -} -class LinkAnnotationElement extends AnnotationElement { - constructor(parameters, options = null) { - super(parameters, { - isRenderable: true, - ignoreBorder: !!options?.ignoreBorder, - createQuadrilaterals: true - }); - this.isTooltipOnly = parameters.data.isTooltipOnly; - } - render() { - const { - data, - linkService - } = this; - const link = document.createElement("a"); - link.setAttribute("data-element-id", data.id); - let isBound = false; - if (data.url) { - linkService.addLinkAttributes(link, data.url, data.newWindow); - isBound = true; - } else if (data.action) { - this._bindNamedAction(link, data.action, data.overlaidText); - isBound = true; - } else if (data.attachment) { - this.#bindAttachment(link, data.attachment, data.overlaidText, data.attachmentDest); - isBound = true; - } else if (data.setOCGState) { - this.#bindSetOCGState(link, data.setOCGState, data.overlaidText); - isBound = true; - } else if (data.dest) { - this._bindLink(link, data.dest, data.overlaidText); - isBound = true; - } else { - if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) { - this._bindJSAction(link, data); - isBound = true; - } - if (data.resetForm) { - this._bindResetFormAction(link, data.resetForm); - isBound = true; - } else if (this.isTooltipOnly && !isBound) { - this._bindLink(link, ""); - isBound = true; - } - } - this.container.classList.add("linkAnnotation"); - if (isBound) { - this.contentElement = link; - this.container.append(link); - } - return this.container; - } - #setInternalLink() { - this.container.setAttribute("data-internal-link", ""); - } - _bindLink(link, destination, overlaidText = "") { - link.href = this.linkService.getDestinationHash(destination); - link.onclick = () => { - if (destination) { - this.linkService.goToDestination(destination); - } - return false; - }; - if (destination || destination === "") { - this.#setInternalLink(); - } - if (overlaidText) { - link.title = overlaidText; - } - } - _bindNamedAction(link, action, overlaidText = "") { - link.href = this.linkService.getAnchorUrl(""); - link.onclick = () => { - this.linkService.executeNamedAction(action); - return false; - }; - if (overlaidText) { - link.title = overlaidText; - } - this.#setInternalLink(); - } - #bindAttachment(link, attachment, overlaidText = "", dest = null) { - link.href = this.linkService.getAnchorUrl(""); - if (attachment.description) { - link.title = attachment.description; - } else if (overlaidText) { - link.title = overlaidText; - } - link.onclick = () => { - this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest); - return false; - }; - this.#setInternalLink(); - } - #bindSetOCGState(link, action, overlaidText = "") { - link.href = this.linkService.getAnchorUrl(""); - link.onclick = () => { - this.linkService.executeSetOCGState(action); - return false; - }; - if (overlaidText) { - link.title = overlaidText; - } - this.#setInternalLink(); - } - _bindJSAction(link, data) { - link.href = this.linkService.getAnchorUrl(""); - const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]); - for (const name of Object.keys(data.actions)) { - const jsName = map.get(name); - if (!jsName) { - continue; - } - link[jsName] = () => { - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id: data.id, - name - } - }); - return false; - }; - } - if (data.overlaidText) { - link.title = data.overlaidText; - } - if (!link.onclick) { - link.onclick = () => false; - } - this.#setInternalLink(); - } - _bindResetFormAction(link, resetForm) { - const otherClickAction = link.onclick; - if (!otherClickAction) { - link.href = this.linkService.getAnchorUrl(""); - } - this.#setInternalLink(); - if (!this._fieldObjects) { - warn(`_bindResetFormAction - "resetForm" action not supported, ` + "ensure that the `fieldObjects` parameter is provided."); - if (!otherClickAction) { - link.onclick = () => false; - } - return; - } - link.onclick = () => { - otherClickAction?.(); - const { - fields: resetFormFields, - refs: resetFormRefs, - include - } = resetForm; - const allFields = []; - if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) { - const fieldIds = new Set(resetFormRefs); - for (const fieldName of resetFormFields) { - const fields = this._fieldObjects[fieldName] || []; - for (const { - id - } of fields) { - fieldIds.add(id); - } - } - for (const fields of Object.values(this._fieldObjects)) { - for (const field of fields) { - if (fieldIds.has(field.id) === include) { - allFields.push(field); - } - } - } - } else { - for (const fields of Object.values(this._fieldObjects)) { - allFields.push(...fields); - } - } - const storage = this.annotationStorage; - const allIds = []; - for (const field of allFields) { - const { - id - } = field; - allIds.push(id); - switch (field.type) { - case "text": - { - const value = field.defaultValue || ""; - storage.setValue(id, { - value - }); - break; - } - case "checkbox": - case "radiobutton": - { - const value = field.defaultValue === field.exportValues; - storage.setValue(id, { - value - }); - break; - } - case "combobox": - case "listbox": - { - const value = field.defaultValue || ""; - storage.setValue(id, { - value - }); - break; - } - default: - continue; - } - const domElement = document.querySelector(`[data-element-id="${id}"]`); - if (!domElement) { - continue; - } else if (!GetElementsByNameSet.has(domElement)) { - warn(`_bindResetFormAction - element not allowed: ${id}`); - continue; - } - domElement.dispatchEvent(new Event("resetform")); - } - if (this.enableScripting) { - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id: "app", - ids: allIds, - name: "ResetForm" - } - }); - } - return false; - }; - } -} -class TextAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true - }); - } - render() { - this.container.classList.add("textAnnotation"); - const image = document.createElement("img"); - image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg"; - image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type"); - image.setAttribute("data-l10n-args", JSON.stringify({ - type: this.data.name - })); - if (!this.data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this.container.append(image); - return this.container; - } -} -class WidgetAnnotationElement extends AnnotationElement { - render() { - return this.container; - } - showElementAndHideCanvas(element) { - if (this.data.hasOwnCanvas) { - if (element.previousSibling?.nodeName === "CANVAS") { - element.previousSibling.hidden = true; - } - element.hidden = false; - } - } - _getKeyModifier(event) { - return FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey; - } - _setEventListener(element, elementData, baseName, eventName, valueGetter) { - if (baseName.includes("mouse")) { - element.addEventListener(baseName, event => { - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id: this.data.id, - name: eventName, - value: valueGetter(event), - shift: event.shiftKey, - modifier: this._getKeyModifier(event) - } - }); - }); - } else { - element.addEventListener(baseName, event => { - if (baseName === "blur") { - if (!elementData.focused || !event.relatedTarget) { - return; - } - elementData.focused = false; - } else if (baseName === "focus") { - if (elementData.focused) { - return; - } - elementData.focused = true; - } - if (!valueGetter) { - return; - } - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id: this.data.id, - name: eventName, - value: valueGetter(event) - } - }); - }); - } - } - _setEventListeners(element, elementData, names, getter) { - for (const [baseName, eventName] of names) { - if (eventName === "Action" || this.data.actions?.[eventName]) { - if (eventName === "Focus" || eventName === "Blur") { - elementData ||= { - focused: false - }; - } - this._setEventListener(element, elementData, baseName, eventName, getter); - if (eventName === "Focus" && !this.data.actions?.Blur) { - this._setEventListener(element, elementData, "blur", "Blur", null); - } else if (eventName === "Blur" && !this.data.actions?.Focus) { - this._setEventListener(element, elementData, "focus", "Focus", null); - } - } - } - } - _setBackgroundColor(element) { - const color = this.data.backgroundColor || null; - element.style.backgroundColor = color === null ? "transparent" : Util.makeHexColor(color[0], color[1], color[2]); - } - _setTextStyle(element) { - const TEXT_ALIGNMENT = ["left", "center", "right"]; - const { - fontColor - } = this.data.defaultAppearanceData; - const fontSize = this.data.defaultAppearanceData.fontSize || annotation_layer_DEFAULT_FONT_SIZE; - const style = element.style; - let computedFontSize; - const BORDER_SIZE = 2; - const roundToOneDecimal = x => Math.round(10 * x) / 10; - if (this.data.multiLine) { - const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); - const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; - const lineHeight = height / numberOfLines; - computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / LINE_FACTOR)); - } else { - const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); - computedFontSize = Math.min(fontSize, roundToOneDecimal(height / LINE_FACTOR)); - } - style.fontSize = `calc(${computedFontSize}px * var(--total-scale-factor))`; - style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); - if (this.data.textAlignment !== null) { - style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; - } - } - _setRequired(element, isRequired) { - if (isRequired) { - element.setAttribute("required", true); - } else { - element.removeAttribute("required"); - } - element.setAttribute("aria-required", isRequired); - } -} -class TextWidgetAnnotationElement extends WidgetAnnotationElement { - constructor(parameters) { - const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue; - super(parameters, { - isRenderable - }); - } - setPropertyOnSiblings(base, key, value, keyInStorage) { - const storage = this.annotationStorage; - for (const element of this._getElementsByName(base.name, base.id)) { - if (element.domElement) { - element.domElement[key] = value; - } - storage.setValue(element.id, { - [keyInStorage]: value - }); - } - } - render() { - const storage = this.annotationStorage; - const id = this.data.id; - this.container.classList.add("textWidgetAnnotation"); - let element = null; - if (this.renderForms) { - const storedData = storage.getValue(id, { - value: this.data.fieldValue - }); - let textContent = storedData.value || ""; - const maxLen = storage.getValue(id, { - charLimit: this.data.maxLen - }).charLimit; - if (maxLen && textContent.length > maxLen) { - textContent = textContent.slice(0, maxLen); - } - let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join("\n") || null; - if (fieldFormattedValues && this.data.comb) { - fieldFormattedValues = fieldFormattedValues.replaceAll(/\s+/g, ""); - } - const elementData = { - userValue: textContent, - formattedValue: fieldFormattedValues, - lastCommittedValue: null, - commitKey: 1, - focused: false - }; - if (this.data.multiLine) { - element = document.createElement("textarea"); - element.textContent = fieldFormattedValues ?? textContent; - if (this.data.doNotScroll) { - element.style.overflowY = "hidden"; - } - } else { - element = document.createElement("input"); - element.type = this.data.password ? "password" : "text"; - element.setAttribute("value", fieldFormattedValues ?? textContent); - if (this.data.doNotScroll) { - element.style.overflowX = "hidden"; - } - } - if (this.data.hasOwnCanvas) { - element.hidden = true; - } - GetElementsByNameSet.add(element); - this.contentElement = element; - element.setAttribute("data-element-id", id); - element.disabled = this.data.readOnly; - element.name = this.data.fieldName; - element.tabIndex = 0; - const { - datetimeFormat, - datetimeType, - timeStep - } = this.data; - const hasDateOrTime = !!datetimeType && this.enableScripting; - if (datetimeFormat) { - element.title = datetimeFormat; - } - this._setRequired(element, this.data.required); - if (maxLen) { - element.maxLength = maxLen; - } - element.addEventListener("input", event => { - storage.setValue(id, { - value: event.target.value - }); - this.setPropertyOnSiblings(element, "value", event.target.value, "value"); - elementData.formattedValue = null; - }); - element.addEventListener("resetform", event => { - const defaultValue = this.data.defaultFieldValue ?? ""; - element.value = elementData.userValue = defaultValue; - elementData.formattedValue = null; - }); - let blurListener = event => { - const { - formattedValue - } = elementData; - if (formattedValue !== null && formattedValue !== undefined) { - event.target.value = formattedValue; - } - event.target.scrollLeft = 0; - }; - if (this.enableScripting && this.hasJSActions) { - element.addEventListener("focus", event => { - if (elementData.focused) { - return; - } - const { - target - } = event; - if (hasDateOrTime) { - target.type = datetimeType; - if (timeStep) { - target.step = timeStep; - } - } - if (elementData.userValue) { - const value = elementData.userValue; - if (hasDateOrTime) { - if (datetimeType === "time") { - const date = new Date(value); - const parts = [date.getHours(), date.getMinutes(), date.getSeconds()]; - target.value = parts.map(v => v.toString().padStart(2, "0")).join(":"); - } else { - target.value = new Date(value - TIMEZONE_OFFSET).toISOString().split(datetimeType === "date" ? "T" : ".", 1)[0]; - } - } else { - target.value = value; - } - } - elementData.lastCommittedValue = target.value; - elementData.commitKey = 1; - if (!this.data.actions?.Focus) { - elementData.focused = true; - } - }); - element.addEventListener("updatefromsandbox", jsEvent => { - this.showElementAndHideCanvas(jsEvent.target); - const actions = { - value(event) { - elementData.userValue = event.detail.value ?? ""; - if (!hasDateOrTime) { - storage.setValue(id, { - value: elementData.userValue.toString() - }); - } - event.target.value = elementData.userValue; - }, - formattedValue(event) { - const { - formattedValue - } = event.detail; - elementData.formattedValue = formattedValue; - if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) { - event.target.value = formattedValue; - } - const data = { - formattedValue - }; - if (hasDateOrTime) { - data.value = formattedValue; - } - storage.setValue(id, data); - }, - selRange(event) { - event.target.setSelectionRange(...event.detail.selRange); - }, - charLimit: event => { - const { - charLimit - } = event.detail; - const { - target - } = event; - if (charLimit === 0) { - target.removeAttribute("maxLength"); - return; - } - target.setAttribute("maxLength", charLimit); - let value = elementData.userValue; - if (!value || value.length <= charLimit) { - return; - } - value = value.slice(0, charLimit); - target.value = elementData.userValue = value; - storage.setValue(id, { - value - }); - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id, - name: "Keystroke", - value, - willCommit: true, - commitKey: 1, - selStart: target.selectionStart, - selEnd: target.selectionEnd - } - }); - } - }; - this._dispatchEventFromSandbox(actions, jsEvent); - }); - element.addEventListener("keydown", event => { - elementData.commitKey = 1; - let commitKey = -1; - if (event.key === "Escape") { - commitKey = 0; - } else if (event.key === "Enter" && !this.data.multiLine) { - commitKey = 2; - } else if (event.key === "Tab") { - elementData.commitKey = 3; - } - if (commitKey === -1) { - return; - } - const { - value - } = event.target; - if (elementData.lastCommittedValue === value) { - return; - } - elementData.lastCommittedValue = value; - elementData.userValue = value; - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id, - name: "Keystroke", - value, - willCommit: true, - commitKey, - selStart: event.target.selectionStart, - selEnd: event.target.selectionEnd - } - }); - }); - const _blurListener = blurListener; - blurListener = null; - element.addEventListener("blur", event => { - if (!elementData.focused || !event.relatedTarget) { - return; - } - if (!this.data.actions?.Blur) { - elementData.focused = false; - } - const { - target - } = event; - let { - value - } = target; - if (hasDateOrTime) { - if (value && datetimeType === "time") { - const parts = value.split(":").map(v => parseInt(v, 10)); - value = new Date(2000, 0, 1, parts[0], parts[1], parts[2] || 0).valueOf(); - target.step = ""; - } else { - if (!value.includes("T")) { - value = `${value}T00:00`; - } - value = new Date(value).valueOf(); - } - target.type = "text"; - } - elementData.userValue = value; - if (elementData.lastCommittedValue !== value) { - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id, - name: "Keystroke", - value, - willCommit: true, - commitKey: elementData.commitKey, - selStart: event.target.selectionStart, - selEnd: event.target.selectionEnd - } - }); - } - _blurListener(event); - }); - if (this.data.actions?.Keystroke) { - element.addEventListener("beforeinput", event => { - elementData.lastCommittedValue = null; - const { - data, - target - } = event; - const { - value, - selectionStart, - selectionEnd - } = target; - let selStart = selectionStart, - selEnd = selectionEnd; - switch (event.inputType) { - case "deleteWordBackward": - { - const match = value.substring(0, selectionStart).match(/\w*[^\w]*$/); - if (match) { - selStart -= match[0].length; - } - break; - } - case "deleteWordForward": - { - const match = value.substring(selectionStart).match(/^[^\w]*\w*/); - if (match) { - selEnd += match[0].length; - } - break; - } - case "deleteContentBackward": - if (selectionStart === selectionEnd) { - selStart -= 1; - } - break; - case "deleteContentForward": - if (selectionStart === selectionEnd) { - selEnd += 1; - } - break; - } - event.preventDefault(); - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id, - name: "Keystroke", - value, - change: data || "", - willCommit: false, - selStart, - selEnd - } - }); - }); - } - this._setEventListeners(element, elementData, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value); - } - if (blurListener) { - element.addEventListener("blur", blurListener); - } - if (this.data.comb) { - const fieldWidth = this.data.rect[2] - this.data.rect[0]; - const combWidth = fieldWidth / maxLen; - element.classList.add("comb"); - element.style.letterSpacing = `calc(${combWidth}px * var(--total-scale-factor) - 1ch)`; - } - } else { - element = document.createElement("div"); - element.textContent = this.data.fieldValue; - element.style.verticalAlign = "middle"; - element.style.display = "table-cell"; - if (this.data.hasOwnCanvas) { - element.hidden = true; - } - } - this._setTextStyle(element); - this._setBackgroundColor(element); - this._setDefaultPropertiesFromJS(element); - this.container.append(element); - return this.container; - } -} -class SignatureWidgetAnnotationElement extends WidgetAnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: !!parameters.data.hasOwnCanvas - }); - } -} -class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: parameters.renderForms - }); - } - render() { - const storage = this.annotationStorage; - const data = this.data; - const id = data.id; - let value = storage.getValue(id, { - value: data.exportValue === data.fieldValue - }).value; - if (typeof value === "string") { - value = value !== "Off"; - storage.setValue(id, { - value - }); - } - this.container.classList.add("buttonWidgetAnnotation", "checkBox"); - const element = document.createElement("input"); - GetElementsByNameSet.add(element); - element.setAttribute("data-element-id", id); - element.disabled = data.readOnly; - this._setRequired(element, this.data.required); - element.type = "checkbox"; - element.name = data.fieldName; - if (value) { - element.setAttribute("checked", true); - } - element.setAttribute("exportValue", data.exportValue); - element.tabIndex = 0; - element.addEventListener("change", event => { - const { - name, - checked - } = event.target; - for (const checkbox of this._getElementsByName(name, id)) { - const curChecked = checked && checkbox.exportValue === data.exportValue; - if (checkbox.domElement) { - checkbox.domElement.checked = curChecked; - } - storage.setValue(checkbox.id, { - value: curChecked - }); - } - storage.setValue(id, { - value: checked - }); - }); - element.addEventListener("resetform", event => { - const defaultValue = data.defaultFieldValue || "Off"; - event.target.checked = defaultValue === data.exportValue; - }); - if (this.enableScripting && this.hasJSActions) { - element.addEventListener("updatefromsandbox", jsEvent => { - const actions = { - value(event) { - event.target.checked = event.detail.value !== "Off"; - storage.setValue(id, { - value: event.target.checked - }); - } - }; - this._dispatchEventFromSandbox(actions, jsEvent); - }); - this._setEventListeners(element, null, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked); - } - this._setBackgroundColor(element); - this._setDefaultPropertiesFromJS(element); - this.container.append(element); - return this.container; - } -} -class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: parameters.renderForms - }); - } - render() { - this.container.classList.add("buttonWidgetAnnotation", "radioButton"); - const storage = this.annotationStorage; - const data = this.data; - const id = data.id; - let value = storage.getValue(id, { - value: data.fieldValue === data.buttonValue - }).value; - if (typeof value === "string") { - value = value !== data.buttonValue; - storage.setValue(id, { - value - }); - } - if (value) { - for (const radio of this._getElementsByName(data.fieldName, id)) { - storage.setValue(radio.id, { - value: false - }); - } - } - const element = document.createElement("input"); - GetElementsByNameSet.add(element); - element.setAttribute("data-element-id", id); - element.disabled = data.readOnly; - this._setRequired(element, this.data.required); - element.type = "radio"; - element.name = data.fieldName; - if (value) { - element.setAttribute("checked", true); - } - element.tabIndex = 0; - element.addEventListener("change", event => { - const { - name, - checked - } = event.target; - for (const radio of this._getElementsByName(name, id)) { - storage.setValue(radio.id, { - value: false - }); - } - storage.setValue(id, { - value: checked - }); - }); - element.addEventListener("resetform", event => { - const defaultValue = data.defaultFieldValue; - event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue; - }); - if (this.enableScripting && this.hasJSActions) { - const pdfButtonValue = data.buttonValue; - element.addEventListener("updatefromsandbox", jsEvent => { - const actions = { - value: event => { - const checked = pdfButtonValue === event.detail.value; - for (const radio of this._getElementsByName(event.target.name)) { - const curChecked = checked && radio.id === id; - if (radio.domElement) { - radio.domElement.checked = curChecked; - } - storage.setValue(radio.id, { - value: curChecked - }); - } - } - }; - this._dispatchEventFromSandbox(actions, jsEvent); - }); - this._setEventListeners(element, null, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked); - } - this._setBackgroundColor(element); - this._setDefaultPropertiesFromJS(element); - this.container.append(element); - return this.container; - } -} -class PushButtonWidgetAnnotationElement extends LinkAnnotationElement { - constructor(parameters) { - super(parameters, { - ignoreBorder: parameters.data.hasAppearance - }); - } - render() { - const container = super.render(); - container.classList.add("buttonWidgetAnnotation", "pushButton"); - const linkElement = container.lastChild; - if (this.enableScripting && this.hasJSActions && linkElement) { - this._setDefaultPropertiesFromJS(linkElement); - linkElement.addEventListener("updatefromsandbox", jsEvent => { - this._dispatchEventFromSandbox({}, jsEvent); - }); - } - return container; - } -} -class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: parameters.renderForms - }); - } - render() { - this.container.classList.add("choiceWidgetAnnotation"); - const storage = this.annotationStorage; - const id = this.data.id; - const storedData = storage.getValue(id, { - value: this.data.fieldValue - }); - const selectElement = document.createElement("select"); - GetElementsByNameSet.add(selectElement); - selectElement.setAttribute("data-element-id", id); - selectElement.disabled = this.data.readOnly; - this._setRequired(selectElement, this.data.required); - selectElement.name = this.data.fieldName; - selectElement.tabIndex = 0; - let addAnEmptyEntry = this.data.combo && this.data.options.length > 0; - if (!this.data.combo) { - selectElement.size = this.data.options.length; - if (this.data.multiSelect) { - selectElement.multiple = true; - } - } - selectElement.addEventListener("resetform", event => { - const defaultValue = this.data.defaultFieldValue; - for (const option of selectElement.options) { - option.selected = option.value === defaultValue; - } - }); - for (const option of this.data.options) { - const optionElement = document.createElement("option"); - optionElement.textContent = option.displayValue; - optionElement.value = option.exportValue; - if (storedData.value.includes(option.exportValue)) { - optionElement.setAttribute("selected", true); - addAnEmptyEntry = false; - } - selectElement.append(optionElement); - } - let removeEmptyEntry = null; - if (addAnEmptyEntry) { - const noneOptionElement = document.createElement("option"); - noneOptionElement.value = " "; - noneOptionElement.setAttribute("hidden", true); - noneOptionElement.setAttribute("selected", true); - selectElement.prepend(noneOptionElement); - removeEmptyEntry = () => { - noneOptionElement.remove(); - selectElement.removeEventListener("input", removeEmptyEntry); - removeEmptyEntry = null; - }; - selectElement.addEventListener("input", removeEmptyEntry); - } - const getValue = isExport => { - const name = isExport ? "value" : "textContent"; - const { - options, - multiple - } = selectElement; - if (!multiple) { - return options.selectedIndex === -1 ? null : options[options.selectedIndex][name]; - } - return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]); - }; - let selectedValues = getValue(false); - const getItems = event => { - const options = event.target.options; - return Array.prototype.map.call(options, option => ({ - displayValue: option.textContent, - exportValue: option.value - })); - }; - if (this.enableScripting && this.hasJSActions) { - selectElement.addEventListener("updatefromsandbox", jsEvent => { - const actions = { - value(event) { - removeEmptyEntry?.(); - const value = event.detail.value; - const values = new Set(Array.isArray(value) ? value : [value]); - for (const option of selectElement.options) { - option.selected = values.has(option.value); - } - storage.setValue(id, { - value: getValue(true) - }); - selectedValues = getValue(false); - }, - multipleSelection(event) { - selectElement.multiple = true; - }, - remove(event) { - const options = selectElement.options; - const index = event.detail.remove; - options[index].selected = false; - selectElement.remove(index); - if (options.length > 0) { - const i = Array.prototype.findIndex.call(options, option => option.selected); - if (i === -1) { - options[0].selected = true; - } - } - storage.setValue(id, { - value: getValue(true), - items: getItems(event) - }); - selectedValues = getValue(false); - }, - clear(event) { - while (selectElement.length !== 0) { - selectElement.remove(0); - } - storage.setValue(id, { - value: null, - items: [] - }); - selectedValues = getValue(false); - }, - insert(event) { - const { - index, - displayValue, - exportValue - } = event.detail.insert; - const selectChild = selectElement.children[index]; - const optionElement = document.createElement("option"); - optionElement.textContent = displayValue; - optionElement.value = exportValue; - if (selectChild) { - selectChild.before(optionElement); - } else { - selectElement.append(optionElement); - } - storage.setValue(id, { - value: getValue(true), - items: getItems(event) - }); - selectedValues = getValue(false); - }, - items(event) { - const { - items - } = event.detail; - while (selectElement.length !== 0) { - selectElement.remove(0); - } - for (const item of items) { - const { - displayValue, - exportValue - } = item; - const optionElement = document.createElement("option"); - optionElement.textContent = displayValue; - optionElement.value = exportValue; - selectElement.append(optionElement); - } - if (selectElement.options.length > 0) { - selectElement.options[0].selected = true; - } - storage.setValue(id, { - value: getValue(true), - items: getItems(event) - }); - selectedValues = getValue(false); - }, - indices(event) { - const indices = new Set(event.detail.indices); - for (const option of event.target.options) { - option.selected = indices.has(option.index); - } - storage.setValue(id, { - value: getValue(true) - }); - selectedValues = getValue(false); - }, - editable(event) { - event.target.disabled = !event.detail.editable; - } - }; - this._dispatchEventFromSandbox(actions, jsEvent); - }); - selectElement.addEventListener("input", event => { - const exportValue = getValue(true); - const change = getValue(false); - storage.setValue(id, { - value: exportValue - }); - event.preventDefault(); - this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { - source: this, - detail: { - id, - name: "Keystroke", - value: selectedValues, - change, - changeEx: exportValue, - willCommit: false, - commitKey: 1, - keyDown: false - } - }); - }); - this._setEventListeners(selectElement, null, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"], ["input", "Action"], ["input", "Validate"]], event => event.target.value); - } else { - selectElement.addEventListener("input", function (event) { - storage.setValue(id, { - value: getValue(true) - }); - }); - } - if (this.data.combo) { - this._setTextStyle(selectElement); - } else {} - this._setBackgroundColor(selectElement); - this._setDefaultPropertiesFromJS(selectElement); - this.container.append(selectElement); - return this.container; - } -} -class PopupAnnotationElement extends AnnotationElement { - constructor(parameters) { - const { - data, - elements, - parent - } = parameters; - const hasCommentManager = !!parent._commentManager; - super(parameters, { - isRenderable: !hasCommentManager && AnnotationElement._hasPopupData(data) - }); - this.elements = elements; - if (hasCommentManager && AnnotationElement._hasPopupData(data)) { - const popup = this.popup = this.#createPopup(); - for (const element of elements) { - element.popup = popup; - } - } else { - this.popup = null; - } - } - #createPopup() { - return new PopupElement({ - container: this.container, - color: this.data.color, - titleObj: this.data.titleObj, - modificationDate: this.data.modificationDate || this.data.creationDate, - contentsObj: this.data.contentsObj, - richText: this.data.richText, - rect: this.data.rect, - parentRect: this.data.parentRect || null, - parent: this.parent, - elements: this.elements, - open: this.data.open, - commentManager: this.parent._commentManager - }); - } - render() { - const { - container - } = this; - container.classList.add("popupAnnotation"); - container.role = "comment"; - const popup = this.popup = this.#createPopup(); - const elementIds = []; - for (const element of this.elements) { - element.popup = popup; - element.container.ariaHasPopup = "dialog"; - elementIds.push(element.data.id); - element.addHighlightArea(); - } - this.container.setAttribute("aria-controls", elementIds.map(id => `${AnnotationPrefix}${id}`).join(",")); - return this.container; - } -} -class PopupElement { - #commentManager = null; - #boundKeyDown = this.#keyDown.bind(this); - #boundHide = this.#hide.bind(this); - #boundShow = this.#show.bind(this); - #boundToggle = this.#toggle.bind(this); - #color = null; - #container = null; - #contentsObj = null; - #dateObj = null; - #elements = null; - #parent = null; - #parentRect = null; - #pinned = false; - #popup = null; - #popupAbortController = null; - #position = null; - #commentButton = null; - #commentButtonPosition = null; - #popupPosition = null; - #rect = null; - #richText = null; - #titleObj = null; - #updates = null; - #wasVisible = false; - #firstElement = null; - #commentText = null; - constructor({ - container, - color, - elements, - titleObj, - modificationDate, - contentsObj, - richText, - parent, - rect, - parentRect, - open, - commentManager = null - }) { - this.#container = container; - this.#titleObj = titleObj; - this.#contentsObj = contentsObj; - this.#richText = richText; - this.#parent = parent; - this.#color = color; - this.#rect = rect; - this.#parentRect = parentRect; - this.#elements = elements; - this.#commentManager = commentManager; - this.#firstElement = elements[0]; - this.#dateObj = PDFDateString.toDateObject(modificationDate); - this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup()); - if (!commentManager) { - this.#addEventListeners(); - this.#container.hidden = true; - if (open) { - this.#toggle(); - } - } - } - #addEventListeners() { - if (this.#popupAbortController) { - return; - } - this.#popupAbortController = new AbortController(); - const { - signal - } = this.#popupAbortController; - for (const element of this.trigger) { - element.addEventListener("click", this.#boundToggle, { - signal - }); - element.addEventListener("pointerenter", this.#boundShow, { - signal - }); - element.addEventListener("pointerleave", this.#boundHide, { - signal - }); - element.classList.add("popupTriggerArea"); - } - for (const element of this.#elements) { - element.container?.addEventListener("keydown", this.#boundKeyDown, { - signal - }); - } - } - #setCommentButtonPosition() { - const element = this.#elements.find(e => e.hasCommentButton); - if (!element) { - return; - } - this.#commentButtonPosition = element._normalizePoint(element.commentButtonPosition); - } - renderCommentButton() { - if (this.#commentButton) { - if (!this.#commentButton.parentNode) { - this.#firstElement.container.after(this.#commentButton); - } - return; - } - if (!this.#commentButtonPosition) { - this.#setCommentButtonPosition(); - } - if (!this.#commentButtonPosition) { - return; - } - const { - signal - } = this.#popupAbortController = new AbortController(); - const hasOwnButton = this.#firstElement.hasOwnCommentButton; - const togglePopup = () => { - this.#commentManager.toggleCommentPopup(this, true, undefined, !hasOwnButton); - }; - const showPopup = () => { - this.#commentManager.toggleCommentPopup(this, false, true, !hasOwnButton); - }; - const hidePopup = () => { - this.#commentManager.toggleCommentPopup(this, false, false); - }; - if (!hasOwnButton) { - const button = this.#commentButton = document.createElement("button"); - button.className = "annotationCommentButton"; - const parentContainer = this.#firstElement.container; - button.style.zIndex = parentContainer.style.zIndex + 1; - button.tabIndex = 0; - button.ariaHasPopup = "dialog"; - button.ariaControls = "commentPopup"; - button.setAttribute("data-l10n-id", "pdfjs-show-comment-button"); - this.#updateColor(); - this.#updateCommentButtonPosition(); - button.addEventListener("keydown", this.#boundKeyDown, { - signal - }); - button.addEventListener("click", togglePopup, { - signal - }); - button.addEventListener("pointerenter", showPopup, { - signal - }); - button.addEventListener("pointerleave", hidePopup, { - signal - }); - parentContainer.after(button); - } else { - this.#commentButton = this.#firstElement.container; - for (const element of this.trigger) { - element.ariaHasPopup = "dialog"; - element.ariaControls = "commentPopup"; - element.addEventListener("keydown", this.#boundKeyDown, { - signal - }); - element.addEventListener("click", togglePopup, { - signal - }); - element.addEventListener("pointerenter", showPopup, { - signal - }); - element.addEventListener("pointerleave", hidePopup, { - signal - }); - element.classList.add("popupTriggerArea"); - } - } - } - #updateCommentButtonPosition() { - if (this.#firstElement.extraPopupElement && !this.#firstElement.editor) { - return; - } - if (!this.#commentButton) { - this.renderCommentButton(); - } - const [x, y] = this.#commentButtonPosition; - const { - style - } = this.#commentButton; - style.left = `calc(${x}%)`; - style.top = `calc(${y}% - var(--comment-button-dim))`; - } - #updateColor() { - if (this.#firstElement.extraPopupElement) { - return; - } - if (!this.#commentButton) { - this.renderCommentButton(); - } - this.#commentButton.style.backgroundColor = this.commentButtonColor || ""; - } - get commentButtonColor() { - const { - color, - opacity - } = this.#firstElement.commentData; - if (!color) { - return null; - } - return this.#parent._commentManager.makeCommentColor(color, opacity); - } - focusCommentButton() { - setTimeout(() => { - this.#commentButton?.focus(); - }, 0); - } - getData() { - const { - richText, - color, - opacity, - creationDate, - modificationDate - } = this.#firstElement.commentData; - return { - contentsObj: { - str: this.comment - }, - richText, - color, - opacity, - creationDate, - modificationDate - }; - } - get elementBeforePopup() { - return this.#commentButton; - } - get comment() { - this.#commentText ||= this.#firstElement.commentText; - return this.#commentText; - } - set comment(text) { - if (text === this.comment) { - return; - } - this.#firstElement.commentText = this.#commentText = text; - } - focus() { - this.#firstElement.container?.focus(); - } - get parentBoundingClientRect() { - return this.#firstElement.layer.getBoundingClientRect(); - } - setCommentButtonStates({ - selected, - hasPopup - }) { - if (!this.#commentButton) { - return; - } - this.#commentButton.classList.toggle("selected", selected); - this.#commentButton.ariaExpanded = hasPopup; - } - setSelectedCommentButton(selected) { - this.#commentButton.classList.toggle("selected", selected); - } - get commentPopupPosition() { - if (this.#popupPosition) { - return this.#popupPosition; - } - const { - x, - y, - height - } = this.#commentButton.getBoundingClientRect(); - const { - x: parentX, - y: parentY, - width: parentWidth, - height: parentHeight - } = this.#firstElement.layer.getBoundingClientRect(); - return [(x - parentX) / parentWidth, (y + height - parentY) / parentHeight]; - } - set commentPopupPosition(pos) { - this.#popupPosition = pos; - } - hasDefaultPopupPosition() { - return this.#popupPosition === null; - } - get commentButtonPosition() { - return this.#commentButtonPosition; - } - get commentButtonWidth() { - return this.#commentButton.getBoundingClientRect().width / this.parentBoundingClientRect.width; - } - editComment(options) { - const [posX, posY] = this.#popupPosition || this.commentButtonPosition.map(x => x / 100); - const parentDimensions = this.parentBoundingClientRect; - const { - x: parentX, - y: parentY, - width: parentWidth, - height: parentHeight - } = parentDimensions; - this.#commentManager.showDialog(null, this, parentX + posX * parentWidth, parentY + posY * parentHeight, { - ...options, - parentDimensions - }); - } - render() { - if (this.#popup) { - return; - } - const popup = this.#popup = document.createElement("div"); - popup.className = "popup"; - if (this.#color) { - const baseColor = popup.style.outlineColor = Util.makeHexColor(...this.#color); - popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`; - } - const header = document.createElement("span"); - header.className = "header"; - if (this.#titleObj?.str) { - const title = document.createElement("span"); - title.className = "title"; - header.append(title); - ({ - dir: title.dir, - str: title.textContent - } = this.#titleObj); - } - popup.append(header); - if (this.#dateObj) { - const modificationDate = document.createElement("time"); - modificationDate.className = "popupDate"; - modificationDate.setAttribute("data-l10n-id", "pdfjs-annotation-date-time-string"); - modificationDate.setAttribute("data-l10n-args", JSON.stringify({ - dateObj: this.#dateObj.valueOf() - })); - modificationDate.dateTime = this.#dateObj.toISOString(); - header.append(modificationDate); - } - renderRichText({ - html: this.#html || this.#contentsObj.str, - dir: this.#contentsObj?.dir, - className: "popupContent" - }, popup); - this.#container.append(popup); - } - get #html() { - const richText = this.#richText; - const contentsObj = this.#contentsObj; - if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) { - return this.#richText.html || null; - } - return null; - } - get #fontSize() { - return this.#html?.attributes?.style?.fontSize || 0; - } - get #fontColor() { - return this.#html?.attributes?.style?.color || null; - } - #makePopupContent(text) { - const popupLines = []; - const popupContent = { - str: text, - html: { - name: "div", - attributes: { - dir: "auto" - }, - children: [{ - name: "p", - children: popupLines - }] - } - }; - const lineAttributes = { - style: { - color: this.#fontColor, - fontSize: this.#fontSize ? `calc(${this.#fontSize}px * var(--total-scale-factor))` : "" - } - }; - for (const line of text.split("\n")) { - popupLines.push({ - name: "span", - value: line, - attributes: lineAttributes - }); - } - return popupContent; - } - #keyDown(event) { - if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) { - return; - } - if (event.key === "Enter" || event.key === "Escape" && this.#pinned) { - this.#toggle(); - } - } - updateEdited({ - rect, - popup, - deleted - }) { - if (this.#commentManager) { - if (deleted) { - this.remove(); - this.#commentText = null; - } else if (popup) { - if (popup.deleted) { - this.remove(); - } else { - this.#updateColor(); - this.#commentText = popup.text; - } - } - if (rect) { - this.#commentButtonPosition = null; - this.#setCommentButtonPosition(); - this.#updateCommentButtonPosition(); - } - return; - } - if (deleted || popup?.deleted) { - this.remove(); - return; - } - this.#addEventListeners(); - this.#updates ||= { - contentsObj: this.#contentsObj, - richText: this.#richText - }; - if (rect) { - this.#position = null; - } - if (popup && popup.text) { - this.#richText = this.#makePopupContent(popup.text); - this.#dateObj = PDFDateString.toDateObject(popup.date); - this.#contentsObj = null; - } - this.#popup?.remove(); - this.#popup = null; - } - resetEdited() { - if (!this.#updates) { - return; - } - ({ - contentsObj: this.#contentsObj, - richText: this.#richText - } = this.#updates); - this.#updates = null; - this.#popup?.remove(); - this.#popup = null; - this.#position = null; - } - remove() { - this.#popupAbortController?.abort(); - this.#popupAbortController = null; - this.#popup?.remove(); - this.#popup = null; - this.#wasVisible = false; - this.#pinned = false; - this.#commentButton?.remove(); - this.#commentButton = null; - if (this.trigger) { - for (const element of this.trigger) { - element.classList.remove("popupTriggerArea"); - } - } - } - #setPosition() { - if (this.#position !== null) { - return; - } - const { - page: { - view - }, - viewport: { - rawDims: { - pageWidth, - pageHeight, - pageX, - pageY - } - } - } = this.#parent; - let useParentRect = !!this.#parentRect; - let rect = useParentRect ? this.#parentRect : this.#rect; - for (const element of this.#elements) { - if (!rect || Util.intersect(element.data.rect, rect) !== null) { - rect = element.data.rect; - useParentRect = true; - break; - } - } - const normalizedRect = Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]); - const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5; - const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0; - const popupLeft = normalizedRect[0] + parentWidth; - const popupTop = normalizedRect[1]; - this.#position = [100 * (popupLeft - pageX) / pageWidth, 100 * (popupTop - pageY) / pageHeight]; - const { - style - } = this.#container; - style.left = `${this.#position[0]}%`; - style.top = `${this.#position[1]}%`; - } - #toggle() { - if (this.#commentManager) { - this.#commentManager.toggleCommentPopup(this, false); - return; - } - this.#pinned = !this.#pinned; - if (this.#pinned) { - this.#show(); - this.#container.addEventListener("click", this.#boundToggle); - this.#container.addEventListener("keydown", this.#boundKeyDown); - } else { - this.#hide(); - this.#container.removeEventListener("click", this.#boundToggle); - this.#container.removeEventListener("keydown", this.#boundKeyDown); - } - } - #show() { - if (!this.#popup) { - this.render(); - } - if (!this.isVisible) { - this.#setPosition(); - this.#container.hidden = false; - this.#container.style.zIndex = parseInt(this.#container.style.zIndex) + 1000; - } else if (this.#pinned) { - this.#container.classList.add("focused"); - } - } - #hide() { - this.#container.classList.remove("focused"); - if (this.#pinned || !this.isVisible) { - return; - } - this.#container.hidden = true; - this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000; - } - forceHide() { - this.#wasVisible = this.isVisible; - if (!this.#wasVisible) { - return; - } - this.#container.hidden = true; - } - maybeShow() { - if (this.#commentManager) { - return; - } - this.#addEventListeners(); - if (!this.#wasVisible) { - return; - } - if (!this.#popup) { - this.#show(); - } - this.#wasVisible = false; - this.#container.hidden = false; - } - get isVisible() { - if (this.#commentManager) { - return false; - } - return this.#container.hidden === false; - } -} -class FreeTextAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - this.textContent = parameters.data.textContent; - this.textPosition = parameters.data.textPosition; - this.annotationEditorType = AnnotationEditorType.FREETEXT; - } - render() { - this.container.classList.add("freeTextAnnotation"); - if (this.textContent) { - const content = this.contentElement = document.createElement("div"); - content.classList.add("annotationTextContent"); - content.setAttribute("role", "comment"); - for (const line of this.textContent) { - const lineSpan = document.createElement("span"); - lineSpan.textContent = line; - content.append(lineSpan); - } - this.container.append(content); - } - if (!this.data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this._editOnDoubleClick(); - return this.container; - } -} -class LineAnnotationElement extends AnnotationElement { - #line = null; - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - } - render() { - this.container.classList.add("lineAnnotation"); - const { - data, - width, - height - } = this; - const svg = this.svgFactory.create(width, height, true); - const line = this.#line = this.svgFactory.createElement("svg:line"); - line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]); - line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]); - line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]); - line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]); - line.setAttribute("stroke-width", data.borderStyle.width || 1); - line.setAttribute("stroke", "transparent"); - line.setAttribute("fill", "transparent"); - svg.append(line); - this.container.append(svg); - if (!data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - return this.container; - } - getElementsToTriggerPopup() { - return this.#line; - } - addHighlightArea() { - this.container.classList.add("highlightArea"); - } -} -class SquareAnnotationElement extends AnnotationElement { - #square = null; - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - } - render() { - this.container.classList.add("squareAnnotation"); - const { - data, - width, - height - } = this; - const svg = this.svgFactory.create(width, height, true); - const borderWidth = data.borderStyle.width; - const square = this.#square = this.svgFactory.createElement("svg:rect"); - square.setAttribute("x", borderWidth / 2); - square.setAttribute("y", borderWidth / 2); - square.setAttribute("width", width - borderWidth); - square.setAttribute("height", height - borderWidth); - square.setAttribute("stroke-width", borderWidth || 1); - square.setAttribute("stroke", "transparent"); - square.setAttribute("fill", "transparent"); - svg.append(square); - this.container.append(svg); - if (!data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - return this.container; - } - getElementsToTriggerPopup() { - return this.#square; - } - addHighlightArea() { - this.container.classList.add("highlightArea"); - } -} -class CircleAnnotationElement extends AnnotationElement { - #circle = null; - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - } - render() { - this.container.classList.add("circleAnnotation"); - const { - data, - width, - height - } = this; - const svg = this.svgFactory.create(width, height, true); - const borderWidth = data.borderStyle.width; - const circle = this.#circle = this.svgFactory.createElement("svg:ellipse"); - circle.setAttribute("cx", width / 2); - circle.setAttribute("cy", height / 2); - circle.setAttribute("rx", width / 2 - borderWidth / 2); - circle.setAttribute("ry", height / 2 - borderWidth / 2); - circle.setAttribute("stroke-width", borderWidth || 1); - circle.setAttribute("stroke", "transparent"); - circle.setAttribute("fill", "transparent"); - svg.append(circle); - this.container.append(svg); - if (!data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - return this.container; - } - getElementsToTriggerPopup() { - return this.#circle; - } - addHighlightArea() { - this.container.classList.add("highlightArea"); - } -} -class PolylineAnnotationElement extends AnnotationElement { - #polyline = null; - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - this.containerClassName = "polylineAnnotation"; - this.svgElementName = "svg:polyline"; - } - render() { - this.container.classList.add(this.containerClassName); - const { - data: { - rect, - vertices, - borderStyle, - popupRef - }, - width, - height - } = this; - if (!vertices) { - return this.container; - } - const svg = this.svgFactory.create(width, height, true); - let points = []; - for (let i = 0, ii = vertices.length; i < ii; i += 2) { - const x = vertices[i] - rect[0]; - const y = rect[3] - vertices[i + 1]; - points.push(`${x},${y}`); - } - points = points.join(" "); - const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName); - polyline.setAttribute("points", points); - polyline.setAttribute("stroke-width", borderStyle.width || 1); - polyline.setAttribute("stroke", "transparent"); - polyline.setAttribute("fill", "transparent"); - svg.append(polyline); - this.container.append(svg); - if (!popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - return this.container; - } - getElementsToTriggerPopup() { - return this.#polyline; - } - addHighlightArea() { - this.container.classList.add("highlightArea"); - } -} -class PolygonAnnotationElement extends PolylineAnnotationElement { - constructor(parameters) { - super(parameters); - this.containerClassName = "polygonAnnotation"; - this.svgElementName = "svg:polygon"; - } -} -class CaretAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - } - render() { - this.container.classList.add("caretAnnotation"); - if (!this.data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - return this.container; - } -} -class InkAnnotationElement extends AnnotationElement { - #polylinesGroupElement = null; - #polylines = []; - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - this.containerClassName = "inkAnnotation"; - this.svgElementName = "svg:polyline"; - this.annotationEditorType = this.data.it === "InkHighlight" ? AnnotationEditorType.HIGHLIGHT : AnnotationEditorType.INK; - } - #getTransform(rotation, rect) { - switch (rotation) { - case 90: - return { - transform: `rotate(90) translate(${-rect[0]},${rect[1]}) scale(1,-1)`, - width: rect[3] - rect[1], - height: rect[2] - rect[0] - }; - case 180: - return { - transform: `rotate(180) translate(${-rect[2]},${rect[1]}) scale(1,-1)`, - width: rect[2] - rect[0], - height: rect[3] - rect[1] - }; - case 270: - return { - transform: `rotate(270) translate(${-rect[2]},${rect[3]}) scale(1,-1)`, - width: rect[3] - rect[1], - height: rect[2] - rect[0] - }; - default: - return { - transform: `translate(${-rect[0]},${rect[3]}) scale(1,-1)`, - width: rect[2] - rect[0], - height: rect[3] - rect[1] - }; - } - } - render() { - this.container.classList.add(this.containerClassName); - const { - data: { - rect, - rotation, - inkLists, - borderStyle, - popupRef - } - } = this; - const { - transform, - width, - height - } = this.#getTransform(rotation, rect); - const svg = this.svgFactory.create(width, height, true); - const g = this.#polylinesGroupElement = this.svgFactory.createElement("svg:g"); - svg.append(g); - g.setAttribute("stroke-width", borderStyle.width || 1); - g.setAttribute("stroke-linecap", "round"); - g.setAttribute("stroke-linejoin", "round"); - g.setAttribute("stroke-miterlimit", 10); - g.setAttribute("stroke", "transparent"); - g.setAttribute("fill", "transparent"); - g.setAttribute("transform", transform); - for (let i = 0, ii = inkLists.length; i < ii; i++) { - const polyline = this.svgFactory.createElement(this.svgElementName); - this.#polylines.push(polyline); - polyline.setAttribute("points", inkLists[i].join(",")); - g.append(polyline); - } - if (!popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this.container.append(svg); - this._editOnDoubleClick(); - return this.container; - } - updateEdited(params) { - super.updateEdited(params); - const { - thickness, - points, - rect - } = params; - const g = this.#polylinesGroupElement; - if (thickness >= 0) { - g.setAttribute("stroke-width", thickness || 1); - } - if (points) { - for (let i = 0, ii = this.#polylines.length; i < ii; i++) { - this.#polylines[i].setAttribute("points", points[i].join(",")); - } - } - if (rect) { - const { - transform, - width, - height - } = this.#getTransform(this.data.rotation, rect); - const root = g.parentElement; - root.setAttribute("viewBox", `0 0 ${width} ${height}`); - g.setAttribute("transform", transform); - } - } - getElementsToTriggerPopup() { - return this.#polylines; - } - addHighlightArea() { - this.container.classList.add("highlightArea"); - } -} -class HighlightAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true, - createQuadrilaterals: true - }); - this.annotationEditorType = AnnotationEditorType.HIGHLIGHT; - } - render() { - const { - data: { - overlaidText, - popupRef - } - } = this; - if (!popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this.container.classList.add("highlightAnnotation"); - this._editOnDoubleClick(); - if (overlaidText) { - const mark = document.createElement("mark"); - mark.classList.add("overlaidText"); - mark.textContent = overlaidText; - this.container.append(mark); - } - return this.container; - } -} -class UnderlineAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true, - createQuadrilaterals: true - }); - } - render() { - const { - data: { - overlaidText, - popupRef - } - } = this; - if (!popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this.container.classList.add("underlineAnnotation"); - if (overlaidText) { - const underline = document.createElement("u"); - underline.classList.add("overlaidText"); - underline.textContent = overlaidText; - this.container.append(underline); - } - return this.container; - } -} -class SquigglyAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true, - createQuadrilaterals: true - }); - } - render() { - const { - data: { - overlaidText, - popupRef - } - } = this; - if (!popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this.container.classList.add("squigglyAnnotation"); - if (overlaidText) { - const underline = document.createElement("u"); - underline.classList.add("overlaidText"); - underline.textContent = overlaidText; - this.container.append(underline); - } - return this.container; - } -} -class StrikeOutAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true, - createQuadrilaterals: true - }); - } - render() { - const { - data: { - overlaidText, - popupRef - } - } = this; - if (!popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this.container.classList.add("strikeoutAnnotation"); - if (overlaidText) { - const strikeout = document.createElement("s"); - strikeout.classList.add("overlaidText"); - strikeout.textContent = overlaidText; - this.container.append(strikeout); - } - return this.container; - } -} -class StampAnnotationElement extends AnnotationElement { - constructor(parameters) { - super(parameters, { - isRenderable: true, - ignoreBorder: true - }); - this.annotationEditorType = AnnotationEditorType.STAMP; - } - render() { - this.container.classList.add("stampAnnotation"); - this.container.setAttribute("role", "img"); - if (!this.data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } - this._editOnDoubleClick(); - return this.container; - } -} -class FileAttachmentAnnotationElement extends AnnotationElement { - #trigger = null; - constructor(parameters) { - super(parameters, { - isRenderable: true - }); - const { - file - } = this.data; - this.filename = file.filename; - this.content = file.content; - this.linkService.eventBus?.dispatch("fileattachmentannotation", { - source: this, - ...file - }); - } - render() { - this.container.classList.add("fileAttachmentAnnotation"); - const { - container, - data - } = this; - let trigger; - if (data.hasAppearance || data.fillAlpha === 0) { - trigger = document.createElement("div"); - } else { - trigger = document.createElement("img"); - trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`; - if (data.fillAlpha && data.fillAlpha < 1) { - trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`; - } - } - trigger.addEventListener("dblclick", this.#download.bind(this)); - this.#trigger = trigger; - const { - isMac - } = FeatureTest.platform; - container.addEventListener("keydown", evt => { - if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) { - this.#download(); - } - }); - if (!data.popupRef && this.hasPopupData) { - this.hasOwnCommentButton = true; - this._createPopup(); - } else { - trigger.classList.add("popupTriggerArea"); - } - container.append(trigger); - return container; - } - getElementsToTriggerPopup() { - return this.#trigger; - } - addHighlightArea() { - this.container.classList.add("highlightArea"); - } - #download() { - this.downloadManager?.openOrDownloadData(this.content, this.filename); - } -} -class AnnotationLayer { - #accessibilityManager = null; - #annotationCanvasMap = null; - #annotationStorage = null; - #editableAnnotations = new Map(); - #structTreeLayer = null; - #linkService = null; - #elements = []; - #hasAriaAttributesFromStructTree = false; - constructor({ - div, - accessibilityManager, - annotationCanvasMap, - annotationEditorUIManager, - page, - viewport, - structTreeLayer, - commentManager, - linkService, - annotationStorage - }) { - this.div = div; - this.#accessibilityManager = accessibilityManager; - this.#annotationCanvasMap = annotationCanvasMap; - this.#structTreeLayer = structTreeLayer || null; - this.#linkService = linkService || null; - this.#annotationStorage = annotationStorage || new AnnotationStorage(); - this.page = page; - this.viewport = viewport; - this.zIndex = 0; - this._annotationEditorUIManager = annotationEditorUIManager; - this._commentManager = commentManager || null; - } - hasEditableAnnotations() { - return this.#editableAnnotations.size > 0; - } - async render(params) { - const { - annotations - } = params; - const layer = this.div; - setLayerDimensions(layer, this.viewport); - const popupToElements = new Map(); - const popupAnnotations = []; - const elementParams = { - data: null, - layer, - linkService: this.#linkService, - downloadManager: params.downloadManager, - imageResourcesPath: params.imageResourcesPath || "", - renderForms: params.renderForms !== false, - svgFactory: new DOMSVGFactory(), - annotationStorage: this.#annotationStorage, - enableComment: params.enableComment === true, - enableScripting: params.enableScripting === true, - hasJSActions: params.hasJSActions, - fieldObjects: params.fieldObjects, - parent: this, - elements: null - }; - for (const data of annotations) { - if (data.noHTML) { - continue; - } - const isPopupAnnotation = data.annotationType === AnnotationType.POPUP; - if (!isPopupAnnotation) { - if (data.rect[2] === data.rect[0] || data.rect[3] === data.rect[1]) { - continue; - } - } else { - const elements = popupToElements.get(data.id); - if (!elements) { - continue; - } - if (!this._commentManager) { - popupAnnotations.push(data); - continue; - } - elementParams.elements = elements; - } - elementParams.data = data; - const element = AnnotationElementFactory.create(elementParams); - if (!element.isRenderable) { - continue; - } - if (!isPopupAnnotation) { - this.#elements.push(element); - if (data.popupRef) { - popupToElements.getOrInsertComputed(data.popupRef, makeArr).push(element); - } - } - const rendered = element.render(); - if (data.hidden) { - rendered.style.visibility = "hidden"; - } - if (element._isEditable) { - this.#editableAnnotations.set(element.data.id, element); - this._annotationEditorUIManager?.renderAnnotationElement(element); - } - } - await this.#addElementsToDOM(); - for (const data of popupAnnotations) { - const elements = elementParams.elements = popupToElements.get(data.id); - elementParams.data = data; - const element = AnnotationElementFactory.create(elementParams); - if (!element.isRenderable) { - continue; - } - const rendered = element.render(); - element.contentElement.id = `${AnnotationPrefix}${data.id}`; - if (data.hidden) { - rendered.style.visibility = "hidden"; - } - elements.at(-1).container.after(rendered); - } - this.#setAnnotationCanvasMap(); - } - async #addElementsToDOM() { - if (this.#elements.length === 0) { - return; - } - this.div.replaceChildren(); - const promises = []; - if (!this.#hasAriaAttributesFromStructTree) { - this.#hasAriaAttributesFromStructTree = true; - for (const { - contentElement, - data: { - id - } - } of this.#elements) { - const annotationId = contentElement.id = `${AnnotationPrefix}${id}`; - promises.push(this.#structTreeLayer?.getAriaAttributes(annotationId).then(ariaAttributes => { - if (ariaAttributes) { - for (const [key, value] of ariaAttributes) { - contentElement.setAttribute(key, value); - } - } - })); - } - } - this.#elements.sort(({ - data: { - rect: [a0, a1, a2, a3] - } - }, { - data: { - rect: [b0, b1, b2, b3] - } - }) => { - if (a0 === a2 && a1 === a3) { - return +1; - } - if (b0 === b2 && b1 === b3) { - return -1; - } - const top1 = a3; - const bot1 = a1; - const mid1 = (a1 + a3) / 2; - const top2 = b3; - const bot2 = b1; - const mid2 = (b1 + b3) / 2; - if (mid1 >= top2 && mid2 <= bot1) { - return -1; - } - if (mid2 >= top1 && mid1 <= bot2) { - return +1; - } - const centerX1 = (a0 + a2) / 2; - const centerX2 = (b0 + b2) / 2; - return centerX1 - centerX2; - }); - const fragment = document.createDocumentFragment(); - for (const element of this.#elements) { - fragment.append(element.container); - if (this._commentManager) { - (element.extraPopupElement?.popup || element.popup)?.renderCommentButton(); - } else if (element.extraPopupElement) { - fragment.append(element.extraPopupElement.render()); - } - } - this.div.append(fragment); - await Promise.all(promises); - if (this.#accessibilityManager) { - for (const element of this.#elements) { - this.#accessibilityManager.addPointerInTextLayer(element.contentElement, false); - } - } - } - async addLinkAnnotations(annotations) { - const elementParams = { - data: null, - layer: this.div, - linkService: this.#linkService, - svgFactory: new DOMSVGFactory(), - parent: this - }; - for (const data of annotations) { - data.borderStyle ||= AnnotationLayer._defaultBorderStyle; - elementParams.data = data; - const element = AnnotationElementFactory.create(elementParams); - if (!element.isRenderable) { - continue; - } - element.render(); - element.contentElement.id = `${AnnotationPrefix}${data.id}`; - this.#elements.push(element); - } - await this.#addElementsToDOM(); - } - update({ - viewport - }) { - const layer = this.div; - this.viewport = viewport; - setLayerDimensions(layer, { - rotation: viewport.rotation - }); - this.#setAnnotationCanvasMap(); - layer.hidden = false; - } - #setAnnotationCanvasMap() { - if (!this.#annotationCanvasMap) { - return; - } - const layer = this.div; - for (const [id, canvas] of this.#annotationCanvasMap) { - const element = layer.querySelector(`[data-annotation-id="${id}"]`); - if (!element) { - continue; - } - canvas.className = "annotationContent"; - const { - firstChild - } = element; - if (!firstChild) { - element.append(canvas); - } else if (firstChild.nodeName === "CANVAS") { - firstChild.replaceWith(canvas); - } else if (!firstChild.classList.contains("annotationContent")) { - firstChild.before(canvas); - } else { - firstChild.after(canvas); - } - const editableAnnotation = this.#editableAnnotations.get(id); - if (!editableAnnotation) { - continue; - } - if (editableAnnotation._hasNoCanvas) { - this._annotationEditorUIManager?.setMissingCanvas(id, element.id, canvas); - editableAnnotation._hasNoCanvas = false; - } else { - editableAnnotation.canvas = canvas; - } - } - this.#annotationCanvasMap.clear(); - } - getEditableAnnotations() { - return Array.from(this.#editableAnnotations.values()); - } - getEditableAnnotation(id) { - return this.#editableAnnotations.get(id); - } - addFakeAnnotation(editor) { - const { - div - } = this; - const { - id, - rotation - } = editor; - const element = new EditorAnnotationElement({ - data: { - id, - rect: editor.getPDFRect(), - rotation - }, - editor, - layer: div, - parent: this, - enableComment: !!this._commentManager, - linkService: this.#linkService, - annotationStorage: this.#annotationStorage - }); - element.render(); - element.contentElement.id = `${AnnotationPrefix}${id}`; - element.createOrUpdatePopup(); - this.#elements.push(element); - return element; - } - removeAnnotation(id) { - const index = this.#elements.findIndex(el => el.data.id === id); - if (index < 0) { - return; - } - const [element] = this.#elements.splice(index, 1); - this.#accessibilityManager?.removePointerInTextLayer(element.contentElement); - } - updateFakeAnnotations(editors) { - if (editors.length === 0) { - return; - } - for (const editor of editors) { - editor.updateFakeAnnotationElement(this); - } - this.#addElementsToDOM(); - } - togglePointerEvents(enabled = false) { - this.div.classList.toggle("disabled", !enabled); - } - static get _defaultBorderStyle() { - return shadow(this, "_defaultBorderStyle", Object.freeze({ - width: 1, - rawWidth: 1, - style: AnnotationBorderStyleType.SOLID, - dashArray: [3], - horizontalCornerRadius: 0, - verticalCornerRadius: 0 - })); - } -} - -;// ./src/display/editor/freetext.js - - - - - -const EOL_PATTERN = /\r\n?|\n/g; -class FreeTextEditor extends AnnotationEditor { - #content = ""; - #editorDivId = `${this.id}-editor`; - #editModeAC = null; - #fontSize; - _colorPicker = null; - static _freeTextDefaultContent = ""; - static _internalPadding = 0; - static _defaultColor = null; - static _defaultFontSize = 10; - static get _keyboardManager() { - const proto = FreeTextEditor.prototype; - const arrowChecker = self => self.isEmpty(); - const small = AnnotationEditorUIManager.TRANSLATE_SMALL; - const big = AnnotationEditorUIManager.TRANSLATE_BIG; - return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, { - bubbles: true - }], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, { - args: [-small, 0], - checker: arrowChecker - }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, { - args: [-big, 0], - checker: arrowChecker - }], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, { - args: [small, 0], - checker: arrowChecker - }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, { - args: [big, 0], - checker: arrowChecker - }], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, { - args: [0, -small], - checker: arrowChecker - }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, { - args: [0, -big], - checker: arrowChecker - }], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, { - args: [0, small], - checker: arrowChecker - }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, { - args: [0, big], - checker: arrowChecker - }]])); - } - static _type = "freetext"; - static _editorType = AnnotationEditorType.FREETEXT; - constructor(params) { - super({ - ...params, - name: "freeTextEditor" - }); - this.color = params.color || FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor; - this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize; - if (!this.annotationElementId) { - this._uiManager.a11yAlert("pdfjs-editor-freetext-added-alert"); - } - this.canAddComment = false; - } - static initialize(l10n, uiManager) { - AnnotationEditor.initialize(l10n, uiManager); - const style = getComputedStyle(document.documentElement); - this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding")); - } - static updateDefaultParams(type, value) { - switch (type) { - case AnnotationEditorParamsType.FREETEXT_SIZE: - FreeTextEditor._defaultFontSize = value; - break; - case AnnotationEditorParamsType.FREETEXT_COLOR: - FreeTextEditor._defaultColor = value; - break; - } - } - updateParams(type, value) { - switch (type) { - case AnnotationEditorParamsType.FREETEXT_SIZE: - this.#updateFontSize(value); - break; - case AnnotationEditorParamsType.FREETEXT_COLOR: - this.#updateColor(value); - break; - } - } - static get defaultPropertiesToUpdate() { - return [[AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor]]; - } - get propertiesToUpdate() { - return [[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, this.color]]; - } - get toolbarButtons() { - this._colorPicker ||= new BasicColorPicker(this); - return [["colorPicker", this._colorPicker]]; - } - get colorType() { - return AnnotationEditorParamsType.FREETEXT_COLOR; - } - #updateFontSize(fontSize) { - const setFontsize = size => { - this.editorDiv.style.fontSize = `calc(${size}px * var(--total-scale-factor))`; - this.translate(0, -(size - this.#fontSize) * this.parentScale); - this.#fontSize = size; - this.#setEditorDimensions(); - }; - const savedFontsize = this.#fontSize; - this.addCommands({ - cmd: setFontsize.bind(this, fontSize), - undo: setFontsize.bind(this, savedFontsize), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.FREETEXT_SIZE, - overwriteIfSameType: true, - keepUndo: true - }); - } - onUpdatedColor() { - this.editorDiv.style.color = this.color; - this._colorPicker?.update(this.color); - super.onUpdatedColor(); - } - #updateColor(color) { - const setColor = col => { - this.color = col; - this.onUpdatedColor(); - }; - const savedColor = this.color; - this.addCommands({ - cmd: setColor.bind(this, color), - undo: setColor.bind(this, savedColor), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.FREETEXT_COLOR, - overwriteIfSameType: true, - keepUndo: true - }); - } - _translateEmpty(x, y) { - this._uiManager.translateSelectedEditors(x, y, true); - } - getInitialTranslation() { - const scale = this.parentScale; - return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale]; - } - rebuild() { - if (!this.parent) { - return; - } - super.rebuild(); - if (this.div === null) { - return; - } - if (!this.isAttachedToDOM) { - this.parent.add(this); - } - } - enableEditMode() { - if (!super.enableEditMode()) { - return false; - } - this.overlayDiv.classList.remove("enabled"); - this.editorDiv.contentEditable = true; - this._isDraggable = false; - this.div.removeAttribute("aria-activedescendant"); - this.#editModeAC = new AbortController(); - const signal = this._uiManager.combinedSignal(this.#editModeAC); - this.editorDiv.addEventListener("keydown", this.editorDivKeydown.bind(this), { - signal - }); - this.editorDiv.addEventListener("focus", this.editorDivFocus.bind(this), { - signal - }); - this.editorDiv.addEventListener("blur", this.editorDivBlur.bind(this), { - signal - }); - this.editorDiv.addEventListener("input", this.editorDivInput.bind(this), { - signal - }); - this.editorDiv.addEventListener("paste", this.editorDivPaste.bind(this), { - signal - }); - return true; - } - disableEditMode() { - if (!super.disableEditMode()) { - return false; - } - this.overlayDiv.classList.add("enabled"); - this.editorDiv.contentEditable = false; - this.div.setAttribute("aria-activedescendant", this.#editorDivId); - this._isDraggable = true; - this.#editModeAC?.abort(); - this.#editModeAC = null; - this.div.focus({ - preventScroll: true - }); - this.isEditing = false; - this.parent.div.classList.add("freetextEditing"); - return true; - } - focusin(event) { - if (!this._focusEventsAllowed) { - return; - } - super.focusin(event); - if (event.target !== this.editorDiv) { - this.editorDiv.focus(); - } - } - onceAdded(focus) { - if (this.width) { - return; - } - this.enableEditMode(); - if (focus) { - this.editorDiv.focus(); - } - if (this._initialOptions?.isCentered) { - this.center(); - } - this._initialOptions = null; - } - isEmpty() { - return !this.editorDiv || this.editorDiv.innerText.trim() === ""; - } - remove() { - this.isEditing = false; - if (this.parent) { - this.parent.setEditingState(true); - this.parent.div.classList.add("freetextEditing"); - } - super.remove(); - } - #extractText() { - const buffer = []; - this.editorDiv.normalize(); - let prevChild = null; - for (const child of this.editorDiv.childNodes) { - if (prevChild?.nodeType === Node.TEXT_NODE && child.nodeName === "BR") { - continue; - } - buffer.push(FreeTextEditor.#getNodeContent(child)); - prevChild = child; - } - return buffer.join("\n"); - } - #setEditorDimensions() { - const [parentWidth, parentHeight] = this.parentDimensions; - let rect; - if (this.isAttachedToDOM) { - rect = this.div.getBoundingClientRect(); - } else { - const { - currentLayer, - div - } = this; - const savedDisplay = div.style.display; - const savedVisibility = div.classList.contains("hidden"); - div.classList.remove("hidden"); - div.style.display = "hidden"; - currentLayer.div.append(this.div); - rect = div.getBoundingClientRect(); - div.remove(); - div.style.display = savedDisplay; - div.classList.toggle("hidden", savedVisibility); - } - if (this.rotation % 180 === this.parentRotation % 180) { - this.width = rect.width / parentWidth; - this.height = rect.height / parentHeight; - } else { - this.width = rect.height / parentWidth; - this.height = rect.width / parentHeight; - } - this.fixAndSetPosition(); - } - commit() { - if (!this.isInEditMode()) { - return; - } - super.commit(); - this.disableEditMode(); - const savedText = this.#content; - const newText = this.#content = this.#extractText().trimEnd(); - if (savedText === newText) { - return; - } - const setText = text => { - this.#content = text; - if (!text) { - this.remove(); - return; - } - this.#setContent(); - this._uiManager.rebuild(this); - this.#setEditorDimensions(); - }; - this.addCommands({ - cmd: () => { - setText(newText); - }, - undo: () => { - setText(savedText); - }, - mustExec: false - }); - this.#setEditorDimensions(); - } - shouldGetKeyboardEvents() { - return this.isInEditMode(); - } - enterInEditMode() { - this.enableEditMode(); - this.editorDiv.focus(); - } - keydown(event) { - if (event.target === this.div && event.key === "Enter") { - this.enterInEditMode(); - event.preventDefault(); - } - } - editorDivKeydown(event) { - FreeTextEditor._keyboardManager.exec(this, event); - } - editorDivFocus(event) { - this.isEditing = true; - } - editorDivBlur(event) { - this.isEditing = false; - } - editorDivInput(event) { - this.parent.div.classList.toggle("freetextEditing", this.isEmpty()); - } - disableEditing() { - this.editorDiv.setAttribute("role", "comment"); - this.editorDiv.removeAttribute("aria-multiline"); - } - enableEditing() { - this.editorDiv.setAttribute("role", "textbox"); - this.editorDiv.setAttribute("aria-multiline", true); - } - get canChangeContent() { - return true; - } - render() { - if (this.div) { - return this.div; - } - let baseX, baseY; - if (this._isCopy || this.annotationElementId) { - baseX = this.x; - baseY = this.y; - } - super.render(); - this.editorDiv = document.createElement("div"); - this.editorDiv.className = "internal"; - this.editorDiv.setAttribute("id", this.#editorDivId); - this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text2"); - this.editorDiv.setAttribute("data-l10n-attrs", "default-content"); - this.enableEditing(); - this.editorDiv.contentEditable = true; - const { - style - } = this.editorDiv; - style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; - style.color = this.color; - this.div.append(this.editorDiv); - this.overlayDiv = document.createElement("div"); - this.overlayDiv.classList.add("overlay", "enabled"); - this.div.append(this.overlayDiv); - if (this._isCopy || this.annotationElementId) { - const [parentWidth, parentHeight] = this.parentDimensions; - if (this.annotationElementId) { - const { - position - } = this._initialData; - let [tx, ty] = this.getInitialTranslation(); - [tx, ty] = this.pageTranslationToScreen(tx, ty); - const [pageWidth, pageHeight] = this.pageDimensions; - const [pageX, pageY] = this.pageTranslation; - let posX, posY; - switch (this.rotation) { - case 0: - posX = baseX + (position[0] - pageX) / pageWidth; - posY = baseY + this.height - (position[1] - pageY) / pageHeight; - break; - case 90: - posX = baseX + (position[0] - pageX) / pageWidth; - posY = baseY - (position[1] - pageY) / pageHeight; - [tx, ty] = [ty, -tx]; - break; - case 180: - posX = baseX - this.width + (position[0] - pageX) / pageWidth; - posY = baseY - (position[1] - pageY) / pageHeight; - [tx, ty] = [-tx, -ty]; - break; - case 270: - posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth; - posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight; - [tx, ty] = [-ty, tx]; - break; - } - this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); - } else { - this._moveAfterPaste(baseX, baseY); - } - this.#setContent(); - this._isDraggable = true; - this.editorDiv.contentEditable = false; - } else { - this._isDraggable = false; - this.editorDiv.contentEditable = true; - } - return this.div; - } - static #getNodeContent(node) { - return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, ""); - } - editorDivPaste(event) { - const clipboardData = event.clipboardData || window.clipboardData; - const { - types - } = clipboardData; - if (types.length === 1 && types[0] === "text/plain") { - return; - } - event.preventDefault(); - const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n"); - if (!paste) { - return; - } - const selection = window.getSelection(); - if (!selection.rangeCount) { - return; - } - this.editorDiv.normalize(); - selection.deleteFromDocument(); - const range = selection.getRangeAt(0); - if (!paste.includes("\n")) { - range.insertNode(document.createTextNode(paste)); - this.editorDiv.normalize(); - selection.collapseToStart(); - return; - } - const { - startContainer, - startOffset - } = range; - const bufferBefore = []; - const bufferAfter = []; - if (startContainer.nodeType === Node.TEXT_NODE) { - const parent = startContainer.parentElement; - bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, "")); - if (parent !== this.editorDiv) { - let buffer = bufferBefore; - for (const child of this.editorDiv.childNodes) { - if (child === parent) { - buffer = bufferAfter; - continue; - } - buffer.push(FreeTextEditor.#getNodeContent(child)); - } - } - bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, "")); - } else if (startContainer === this.editorDiv) { - let buffer = bufferBefore; - let i = 0; - for (const child of this.editorDiv.childNodes) { - if (i++ === startOffset) { - buffer = bufferAfter; - } - buffer.push(FreeTextEditor.#getNodeContent(child)); - } - } - this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`; - this.#setContent(); - const newRange = new Range(); - let beforeLength = Math.sumPrecise(bufferBefore.map(line => line.length)); - for (const { - firstChild - } of this.editorDiv.childNodes) { - if (firstChild.nodeType === Node.TEXT_NODE) { - const length = firstChild.nodeValue.length; - if (beforeLength <= length) { - newRange.setStart(firstChild, beforeLength); - newRange.setEnd(firstChild, beforeLength); - break; - } - beforeLength -= length; - } - } - selection.removeAllRanges(); - selection.addRange(newRange); - } - #setContent() { - this.editorDiv.replaceChildren(); - if (!this.#content) { - return; - } - for (const line of this.#content.split("\n")) { - const div = document.createElement("div"); - div.append(line ? document.createTextNode(line) : document.createElement("br")); - this.editorDiv.append(div); - } - } - #serializeContent() { - return this.#content.replaceAll("\xa0", " "); - } - static #deserializeContent(content) { - return content.replaceAll(" ", "\xa0"); - } - get contentDiv() { - return this.editorDiv; - } - getPDFRect() { - const padding = FreeTextEditor._internalPadding * this.parentScale; - return this.getRect(padding, padding); - } - static async deserialize(data, parent, uiManager) { - let initialData = null; - if (data instanceof FreeTextAnnotationElement) { - const { - data: { - defaultAppearanceData: { - fontSize, - fontColor - }, - rect, - rotation, - id, - popupRef, - richText, - contentsObj, - creationDate, - modificationDate - }, - textContent, - textPosition, - parent: { - page: { - pageNumber - } - } - } = data; - if (!textContent || textContent.length === 0) { - return null; - } - initialData = data = { - annotationType: AnnotationEditorType.FREETEXT, - color: Array.from(fontColor), - fontSize, - value: textContent.join("\n"), - position: textPosition, - pageIndex: pageNumber - 1, - rect: rect.slice(0), - rotation, - annotationElementId: id, - id, - deleted: false, - popupRef, - comment: contentsObj?.str || null, - richText, - creationDate, - modificationDate - }; - } - const editor = await super.deserialize(data, parent, uiManager); - editor.#fontSize = data.fontSize; - editor.color = Util.makeHexColor(...data.color); - editor.#content = FreeTextEditor.#deserializeContent(data.value); - editor._initialData = initialData; - if (data.comment) { - editor.setCommentData(data); - } - return editor; - } - serialize(isForCopying = false) { - if (this.isEmpty()) { - return null; - } - if (this.deleted) { - return this.serializeDeleted(); - } - const color = AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.color); - const serialized = Object.assign(super.serialize(isForCopying), { - color, - fontSize: this.#fontSize, - value: this.#serializeContent() - }); - this.addComment(serialized); - if (isForCopying) { - serialized.isCopy = true; - return serialized; - } - if (this.annotationElementId && !this.#hasElementChanged(serialized)) { - return null; - } - serialized.id = this.annotationElementId; - return serialized; - } - #hasElementChanged(serialized) { - const { - value, - fontSize, - color, - pageIndex - } = this._initialData; - return this.hasEditedComment || this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex; - } - renderAnnotationElement(annotation) { - const content = super.renderAnnotationElement(annotation); - if (!content) { - return null; - } - const { - style - } = content; - style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; - style.color = this.color; - content.replaceChildren(); - for (const line of this.#content.split("\n")) { - const div = document.createElement("div"); - div.append(line ? document.createTextNode(line) : document.createElement("br")); - content.append(div); - } - annotation.updateEdited({ - rect: this.getPDFRect(), - popup: this._uiManager.hasCommentManager() || this.hasEditedComment ? this.comment : { - text: this.#content - } - }); - return content; - } - resetAnnotationElement(annotation) { - super.resetAnnotationElement(annotation); - annotation.resetEdited(); - } -} - -;// ./src/display/editor/drawers/outline.js - -class Outline { - static PRECISION = 1e-4; - toSVGPath() { - unreachable("Abstract method `toSVGPath` must be implemented."); - } - get box() { - unreachable("Abstract getter `box` must be implemented."); - } - serialize(_bbox, _rotation) { - unreachable("Abstract method `serialize` must be implemented."); - } - static _rescale(src, tx, ty, sx, sy, dest) { - dest ||= new Float32Array(src.length); - for (let i = 0, ii = src.length; i < ii; i += 2) { - dest[i] = tx + src[i] * sx; - dest[i + 1] = ty + src[i + 1] * sy; - } - return dest; - } - static _rescaleAndSwap(src, tx, ty, sx, sy, dest) { - dest ||= new Float32Array(src.length); - for (let i = 0, ii = src.length; i < ii; i += 2) { - dest[i] = tx + src[i + 1] * sx; - dest[i + 1] = ty + src[i] * sy; - } - return dest; - } - static _translate(src, tx, ty, dest) { - dest ||= new Float32Array(src.length); - for (let i = 0, ii = src.length; i < ii; i += 2) { - dest[i] = tx + src[i]; - dest[i + 1] = ty + src[i + 1]; - } - return dest; - } - static svgRound(x) { - return Math.round(x * 10000); - } - static _normalizePoint(x, y, parentWidth, parentHeight, rotation) { - switch (rotation) { - case 90: - return [1 - y / parentWidth, x / parentHeight]; - case 180: - return [1 - x / parentWidth, 1 - y / parentHeight]; - case 270: - return [y / parentWidth, 1 - x / parentHeight]; - default: - return [x / parentWidth, y / parentHeight]; - } - } - static _normalizePagePoint(x, y, rotation) { - switch (rotation) { - case 90: - return [1 - y, x]; - case 180: - return [1 - x, 1 - y]; - case 270: - return [y, 1 - x]; - default: - return [x, y]; - } - } - static createBezierPoints(x1, y1, x2, y2, x3, y3) { - return [(x1 + 5 * x2) / 6, (y1 + 5 * y2) / 6, (5 * x2 + x3) / 6, (5 * y2 + y3) / 6, (x2 + x3) / 2, (y2 + y3) / 2]; - } -} - -;// ./src/display/editor/drawers/freedraw.js - - -class FreeDrawOutliner { - #box; - #bottom = []; - #innerMargin; - #isLTR; - #top = []; - #last = new Float32Array(18); - #lastX; - #lastY; - #min; - #min_dist; - #scaleFactor; - #thickness; - #points = []; - static #MIN_DIST = 8; - static #MIN_DIFF = 2; - static #MIN = FreeDrawOutliner.#MIN_DIST + FreeDrawOutliner.#MIN_DIFF; - constructor({ - x, - y - }, box, scaleFactor, thickness, isLTR, innerMargin = 0) { - this.#box = box; - this.#thickness = thickness * scaleFactor; - this.#isLTR = isLTR; - this.#last.set([NaN, NaN, NaN, NaN, x, y], 6); - this.#innerMargin = innerMargin; - this.#min_dist = FreeDrawOutliner.#MIN_DIST * scaleFactor; - this.#min = FreeDrawOutliner.#MIN * scaleFactor; - this.#scaleFactor = scaleFactor; - this.#points.push(x, y); - } - isEmpty() { - return isNaN(this.#last[8]); - } - #getLastCoords() { - const lastTop = this.#last.subarray(4, 6); - const lastBottom = this.#last.subarray(16, 18); - const [x, y, width, height] = this.#box; - return [(this.#lastX + (lastTop[0] - lastBottom[0]) / 2 - x) / width, (this.#lastY + (lastTop[1] - lastBottom[1]) / 2 - y) / height, (this.#lastX + (lastBottom[0] - lastTop[0]) / 2 - x) / width, (this.#lastY + (lastBottom[1] - lastTop[1]) / 2 - y) / height]; - } - add({ - x, - y - }) { - this.#lastX = x; - this.#lastY = y; - const [layerX, layerY, layerWidth, layerHeight] = this.#box; - let [x1, y1, x2, y2] = this.#last.subarray(8, 12); - const diffX = x - x2; - const diffY = y - y2; - const d = Math.hypot(diffX, diffY); - if (d < this.#min) { - return false; - } - const diffD = d - this.#min_dist; - const K = diffD / d; - const shiftX = K * diffX; - const shiftY = K * diffY; - let x0 = x1; - let y0 = y1; - x1 = x2; - y1 = y2; - x2 += shiftX; - y2 += shiftY; - this.#points?.push(x, y); - const nX = -shiftY / diffD; - const nY = shiftX / diffD; - const thX = nX * this.#thickness; - const thY = nY * this.#thickness; - this.#last.set(this.#last.subarray(2, 8), 0); - this.#last.set([x2 + thX, y2 + thY], 4); - this.#last.set(this.#last.subarray(14, 18), 12); - this.#last.set([x2 - thX, y2 - thY], 16); - if (isNaN(this.#last[6])) { - if (this.#top.length === 0) { - this.#last.set([x1 + thX, y1 + thY], 2); - this.#top.push(NaN, NaN, NaN, NaN, (x1 + thX - layerX) / layerWidth, (y1 + thY - layerY) / layerHeight); - this.#last.set([x1 - thX, y1 - thY], 14); - this.#bottom.push(NaN, NaN, NaN, NaN, (x1 - thX - layerX) / layerWidth, (y1 - thY - layerY) / layerHeight); - } - this.#last.set([x0, y0, x1, y1, x2, y2], 6); - return !this.isEmpty(); - } - this.#last.set([x0, y0, x1, y1, x2, y2], 6); - const angle = Math.abs(Math.atan2(y0 - y1, x0 - x1) - Math.atan2(shiftY, shiftX)); - if (angle < Math.PI / 2) { - [x1, y1, x2, y2] = this.#last.subarray(2, 6); - this.#top.push(NaN, NaN, NaN, NaN, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); - [x1, y1, x0, y0] = this.#last.subarray(14, 18); - this.#bottom.push(NaN, NaN, NaN, NaN, ((x0 + x1) / 2 - layerX) / layerWidth, ((y0 + y1) / 2 - layerY) / layerHeight); - return true; - } - [x0, y0, x1, y1, x2, y2] = this.#last.subarray(0, 6); - this.#top.push(((x0 + 5 * x1) / 6 - layerX) / layerWidth, ((y0 + 5 * y1) / 6 - layerY) / layerHeight, ((5 * x1 + x2) / 6 - layerX) / layerWidth, ((5 * y1 + y2) / 6 - layerY) / layerHeight, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); - [x2, y2, x1, y1, x0, y0] = this.#last.subarray(12, 18); - this.#bottom.push(((x0 + 5 * x1) / 6 - layerX) / layerWidth, ((y0 + 5 * y1) / 6 - layerY) / layerHeight, ((5 * x1 + x2) / 6 - layerX) / layerWidth, ((5 * y1 + y2) / 6 - layerY) / layerHeight, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); - return true; - } - toSVGPath() { - if (this.isEmpty()) { - return ""; - } - const top = this.#top; - const bottom = this.#bottom; - if (isNaN(this.#last[6]) && !this.isEmpty()) { - return this.#toSVGPathTwoPoints(); - } - const buffer = []; - buffer.push(`M${top[4]} ${top[5]}`); - for (let i = 6; i < top.length; i += 6) { - if (isNaN(top[i])) { - buffer.push(`L${top[i + 4]} ${top[i + 5]}`); - } else { - buffer.push(`C${top[i]} ${top[i + 1]} ${top[i + 2]} ${top[i + 3]} ${top[i + 4]} ${top[i + 5]}`); - } - } - this.#toSVGPathEnd(buffer); - for (let i = bottom.length - 6; i >= 6; i -= 6) { - if (isNaN(bottom[i])) { - buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`); - } else { - buffer.push(`C${bottom[i]} ${bottom[i + 1]} ${bottom[i + 2]} ${bottom[i + 3]} ${bottom[i + 4]} ${bottom[i + 5]}`); - } - } - this.#toSVGPathStart(buffer); - return buffer.join(" "); - } - #toSVGPathTwoPoints() { - const [x, y, width, height] = this.#box; - const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); - return `M${(this.#last[2] - x) / width} ${(this.#last[3] - y) / height} L${(this.#last[4] - x) / width} ${(this.#last[5] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${(this.#last[16] - x) / width} ${(this.#last[17] - y) / height} L${(this.#last[14] - x) / width} ${(this.#last[15] - y) / height} Z`; - } - #toSVGPathStart(buffer) { - const bottom = this.#bottom; - buffer.push(`L${bottom[4]} ${bottom[5]} Z`); - } - #toSVGPathEnd(buffer) { - const [x, y, width, height] = this.#box; - const lastTop = this.#last.subarray(4, 6); - const lastBottom = this.#last.subarray(16, 18); - const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); - buffer.push(`L${(lastTop[0] - x) / width} ${(lastTop[1] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${(lastBottom[0] - x) / width} ${(lastBottom[1] - y) / height}`); - } - newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { - return new FreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR); - } - getOutlines() { - const top = this.#top; - const bottom = this.#bottom; - const last = this.#last; - const [layerX, layerY, layerWidth, layerHeight] = this.#box; - const points = new Float32Array((this.#points?.length ?? 0) + 2); - for (let i = 0, ii = points.length - 2; i < ii; i += 2) { - points[i] = (this.#points[i] - layerX) / layerWidth; - points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight; - } - points[points.length - 2] = (this.#lastX - layerX) / layerWidth; - points[points.length - 1] = (this.#lastY - layerY) / layerHeight; - if (isNaN(last[6]) && !this.isEmpty()) { - return this.#getOutlineTwoPoints(points); - } - const outline = new Float32Array(this.#top.length + 24 + this.#bottom.length); - let N = top.length; - for (let i = 0; i < N; i += 2) { - if (isNaN(top[i])) { - outline[i] = outline[i + 1] = NaN; - continue; - } - outline[i] = top[i]; - outline[i + 1] = top[i + 1]; - } - N = this.#getOutlineEnd(outline, N); - for (let i = bottom.length - 6; i >= 6; i -= 6) { - for (let j = 0; j < 6; j += 2) { - if (isNaN(bottom[i + j])) { - outline[N] = outline[N + 1] = NaN; - N += 2; - continue; - } - outline[N] = bottom[i + j]; - outline[N + 1] = bottom[i + j + 1]; - N += 2; - } - } - this.#getOutlineStart(outline, N); - return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); - } - #getOutlineTwoPoints(points) { - const last = this.#last; - const [layerX, layerY, layerWidth, layerHeight] = this.#box; - const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); - const outline = new Float32Array(36); - outline.set([NaN, NaN, NaN, NaN, (last[2] - layerX) / layerWidth, (last[3] - layerY) / layerHeight, NaN, NaN, NaN, NaN, (last[4] - layerX) / layerWidth, (last[5] - layerY) / layerHeight, NaN, NaN, NaN, NaN, lastTopX, lastTopY, NaN, NaN, NaN, NaN, lastBottomX, lastBottomY, NaN, NaN, NaN, NaN, (last[16] - layerX) / layerWidth, (last[17] - layerY) / layerHeight, NaN, NaN, NaN, NaN, (last[14] - layerX) / layerWidth, (last[15] - layerY) / layerHeight], 0); - return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); - } - #getOutlineStart(outline, pos) { - const bottom = this.#bottom; - outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], pos); - return pos += 6; - } - #getOutlineEnd(outline, pos) { - const lastTop = this.#last.subarray(4, 6); - const lastBottom = this.#last.subarray(16, 18); - const [layerX, layerY, layerWidth, layerHeight] = this.#box; - const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); - outline.set([NaN, NaN, NaN, NaN, (lastTop[0] - layerX) / layerWidth, (lastTop[1] - layerY) / layerHeight, NaN, NaN, NaN, NaN, lastTopX, lastTopY, NaN, NaN, NaN, NaN, lastBottomX, lastBottomY, NaN, NaN, NaN, NaN, (lastBottom[0] - layerX) / layerWidth, (lastBottom[1] - layerY) / layerHeight], pos); - return pos += 24; - } -} -class FreeDrawOutline extends Outline { - #box; - #bbox = new Float32Array(4); - #innerMargin; - #isLTR; - #points; - #scaleFactor; - #outline; - constructor(outline, points, box, scaleFactor, innerMargin, isLTR) { - super(); - this.#outline = outline; - this.#points = points; - this.#box = box; - this.#scaleFactor = scaleFactor; - this.#innerMargin = innerMargin; - this.#isLTR = isLTR; - this.firstPoint = [NaN, NaN]; - this.lastPoint = [NaN, NaN]; - this.#computeMinMax(isLTR); - const [x, y, width, height] = this.#bbox; - for (let i = 0, ii = outline.length; i < ii; i += 2) { - outline[i] = (outline[i] - x) / width; - outline[i + 1] = (outline[i + 1] - y) / height; - } - for (let i = 0, ii = points.length; i < ii; i += 2) { - points[i] = (points[i] - x) / width; - points[i + 1] = (points[i + 1] - y) / height; - } - } - toSVGPath() { - const buffer = [`M${this.#outline[4]} ${this.#outline[5]}`]; - for (let i = 6, ii = this.#outline.length; i < ii; i += 6) { - if (isNaN(this.#outline[i])) { - buffer.push(`L${this.#outline[i + 4]} ${this.#outline[i + 5]}`); - continue; - } - buffer.push(`C${this.#outline[i]} ${this.#outline[i + 1]} ${this.#outline[i + 2]} ${this.#outline[i + 3]} ${this.#outline[i + 4]} ${this.#outline[i + 5]}`); - } - buffer.push("Z"); - return buffer.join(" "); - } - serialize([blX, blY, trX, trY], rotation) { - const width = trX - blX; - const height = trY - blY; - let outline; - let points; - switch (rotation) { - case 0: - outline = Outline._rescale(this.#outline, blX, trY, width, -height); - points = Outline._rescale(this.#points, blX, trY, width, -height); - break; - case 90: - outline = Outline._rescaleAndSwap(this.#outline, blX, blY, width, height); - points = Outline._rescaleAndSwap(this.#points, blX, blY, width, height); - break; - case 180: - outline = Outline._rescale(this.#outline, trX, blY, -width, height); - points = Outline._rescale(this.#points, trX, blY, -width, height); - break; - case 270: - outline = Outline._rescaleAndSwap(this.#outline, trX, trY, -width, -height); - points = Outline._rescaleAndSwap(this.#points, trX, trY, -width, -height); - break; - } - return { - outline: Array.from(outline), - points: [Array.from(points)] - }; - } - #computeMinMax(isLTR) { - const outline = this.#outline; - let lastX = outline[4]; - let lastY = outline[5]; - const minMax = [lastX, lastY, lastX, lastY]; - let firstPointX = lastX; - let firstPointY = lastY; - let lastPointX = lastX; - let lastPointY = lastY; - const ltrCallback = isLTR ? Math.max : Math.min; - const bezierBbox = new Float32Array(4); - for (let i = 6, ii = outline.length; i < ii; i += 6) { - const x = outline[i + 4], - y = outline[i + 5]; - if (isNaN(outline[i])) { - Util.pointBoundingBox(x, y, minMax); - if (firstPointY > y) { - firstPointX = x; - firstPointY = y; - } else if (firstPointY === y) { - firstPointX = ltrCallback(firstPointX, x); - } - if (lastPointY < y) { - lastPointX = x; - lastPointY = y; - } else if (lastPointY === y) { - lastPointX = ltrCallback(lastPointX, x); - } - } else { - bezierBbox[0] = bezierBbox[1] = Infinity; - bezierBbox[2] = bezierBbox[3] = -Infinity; - Util.bezierBoundingBox(lastX, lastY, ...outline.slice(i, i + 6), bezierBbox); - Util.rectBoundingBox(bezierBbox[0], bezierBbox[1], bezierBbox[2], bezierBbox[3], minMax); - if (firstPointY > bezierBbox[1]) { - firstPointX = bezierBbox[0]; - firstPointY = bezierBbox[1]; - } else if (firstPointY === bezierBbox[1]) { - firstPointX = ltrCallback(firstPointX, bezierBbox[0]); - } - if (lastPointY < bezierBbox[3]) { - lastPointX = bezierBbox[2]; - lastPointY = bezierBbox[3]; - } else if (lastPointY === bezierBbox[3]) { - lastPointX = ltrCallback(lastPointX, bezierBbox[2]); - } - } - lastX = x; - lastY = y; - } - const bbox = this.#bbox; - bbox[0] = minMax[0] - this.#innerMargin; - bbox[1] = minMax[1] - this.#innerMargin; - bbox[2] = minMax[2] - minMax[0] + 2 * this.#innerMargin; - bbox[3] = minMax[3] - minMax[1] + 2 * this.#innerMargin; - this.firstPoint = [firstPointX, firstPointY]; - this.lastPoint = [lastPointX, lastPointY]; - } - get box() { - return this.#bbox; - } - newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { - return new FreeDrawOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); - } - getNewOutline(thickness, innerMargin) { - const [x, y, width, height] = this.#bbox; - const [layerX, layerY, layerWidth, layerHeight] = this.#box; - const sx = width * layerWidth; - const sy = height * layerHeight; - const tx = x * layerWidth + layerX; - const ty = y * layerHeight + layerY; - const outliner = this.newOutliner({ - x: this.#points[0] * sx + tx, - y: this.#points[1] * sy + ty - }, this.#box, this.#scaleFactor, thickness, this.#isLTR, innerMargin ?? this.#innerMargin); - for (let i = 2; i < this.#points.length; i += 2) { - outliner.add({ - x: this.#points[i] * sx + tx, - y: this.#points[i + 1] * sy + ty - }); - } - return outliner.getOutlines(); - } -} - -;// ./src/display/editor/drawers/highlight.js - - - -class HighlightOutliner { - #box; - #firstPoint; - #lastPoint; - #verticalEdges = []; - #intervals = []; - constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) { - const minMax = [Infinity, Infinity, -Infinity, -Infinity]; - const NUMBER_OF_DIGITS = 4; - const EPSILON = 10 ** -NUMBER_OF_DIGITS; - for (const { - x, - y, - width, - height - } of boxes) { - const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON; - const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON; - const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON; - const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON; - const left = [x1, y1, y2, true]; - const right = [x2, y1, y2, false]; - this.#verticalEdges.push(left, right); - Util.rectBoundingBox(x1, y1, x2, y2, minMax); - } - const bboxWidth = minMax[2] - minMax[0] + 2 * innerMargin; - const bboxHeight = minMax[3] - minMax[1] + 2 * innerMargin; - const shiftedMinX = minMax[0] - innerMargin; - const shiftedMinY = minMax[1] - innerMargin; - let firstPointX = isLTR ? -Infinity : Infinity; - let firstPointY = Infinity; - const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2); - const lastPoint = [lastEdge[0], lastEdge[2]]; - for (const edge of this.#verticalEdges) { - const [x, y1, y2, left] = edge; - if (!left && isLTR) { - if (y1 < firstPointY) { - firstPointY = y1; - firstPointX = x; - } else if (y1 === firstPointY) { - firstPointX = Math.max(firstPointX, x); - } - } else if (left && !isLTR) { - if (y1 < firstPointY) { - firstPointY = y1; - firstPointX = x; - } else if (y1 === firstPointY) { - firstPointX = Math.min(firstPointX, x); - } - } - edge[0] = (x - shiftedMinX) / bboxWidth; - edge[1] = (y1 - shiftedMinY) / bboxHeight; - edge[2] = (y2 - shiftedMinY) / bboxHeight; - } - this.#box = new Float32Array([shiftedMinX, shiftedMinY, bboxWidth, bboxHeight]); - this.#firstPoint = [firstPointX, firstPointY]; - this.#lastPoint = lastPoint; - } - getOutlines() { - this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]); - const outlineVerticalEdges = []; - for (const edge of this.#verticalEdges) { - if (edge[3]) { - outlineVerticalEdges.push(...this.#breakEdge(edge)); - this.#insert(edge); - } else { - this.#remove(edge); - outlineVerticalEdges.push(...this.#breakEdge(edge)); - } - } - return this.#getOutlines(outlineVerticalEdges); - } - #getOutlines(outlineVerticalEdges) { - const edges = []; - const allEdges = new Set(); - for (const edge of outlineVerticalEdges) { - const [x, y1, y2] = edge; - edges.push([x, y1, edge], [x, y2, edge]); - } - edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]); - for (let i = 0, ii = edges.length; i < ii; i += 2) { - const edge1 = edges[i][2]; - const edge2 = edges[i + 1][2]; - edge1.push(edge2); - edge2.push(edge1); - allEdges.add(edge1); - allEdges.add(edge2); - } - const outlines = []; - let outline; - while (allEdges.size > 0) { - const edge = allEdges.values().next().value; - let [x, y1, y2, edge1, edge2] = edge; - allEdges.delete(edge); - let lastPointX = x; - let lastPointY = y1; - outline = [x, y2]; - outlines.push(outline); - while (true) { - let e; - if (allEdges.has(edge1)) { - e = edge1; - } else if (allEdges.has(edge2)) { - e = edge2; - } else { - break; - } - allEdges.delete(e); - [x, y1, y2, edge1, edge2] = e; - if (lastPointX !== x) { - outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2); - lastPointX = x; - } - lastPointY = lastPointY === y1 ? y2 : y1; - } - outline.push(lastPointX, lastPointY); - } - return new HighlightOutline(outlines, this.#box, this.#firstPoint, this.#lastPoint); - } - #binarySearch(y) { - const array = this.#intervals; - let start = 0; - let end = array.length - 1; - while (start <= end) { - const middle = start + end >> 1; - const y1 = array[middle][0]; - if (y1 === y) { - return middle; - } - if (y1 < y) { - start = middle + 1; - } else { - end = middle - 1; - } - } - return end + 1; - } - #insert([, y1, y2]) { - const index = this.#binarySearch(y1); - this.#intervals.splice(index, 0, [y1, y2]); - } - #remove([, y1, y2]) { - const index = this.#binarySearch(y1); - for (let i = index; i < this.#intervals.length; i++) { - const [start, end] = this.#intervals[i]; - if (start !== y1) { - break; - } - if (start === y1 && end === y2) { - this.#intervals.splice(i, 1); - return; - } - } - for (let i = index - 1; i >= 0; i--) { - const [start, end] = this.#intervals[i]; - if (start !== y1) { - break; - } - if (start === y1 && end === y2) { - this.#intervals.splice(i, 1); - return; - } - } - } - #breakEdge(edge) { - const [x, y1, y2] = edge; - const results = [[x, y1, y2]]; - const index = this.#binarySearch(y2); - for (let i = 0; i < index; i++) { - const [start, end] = this.#intervals[i]; - for (let j = 0, jj = results.length; j < jj; j++) { - const [, y3, y4] = results[j]; - if (end <= y3 || y4 <= start) { - continue; - } - if (y3 >= start) { - if (y4 > end) { - results[j][1] = end; - } else { - if (jj === 1) { - return []; - } - results.splice(j, 1); - j--; - jj--; - } - continue; - } - results[j][2] = start; - if (y4 > end) { - results.push([x, end, y4]); - } - } - } - return results; - } -} -class HighlightOutline extends Outline { - #box; - #outlines; - constructor(outlines, box, firstPoint, lastPoint) { - super(); - this.#outlines = outlines; - this.#box = box; - this.firstPoint = firstPoint; - this.lastPoint = lastPoint; - } - toSVGPath() { - const buffer = []; - for (const polygon of this.#outlines) { - let [prevX, prevY] = polygon; - buffer.push(`M${prevX} ${prevY}`); - for (let i = 2; i < polygon.length; i += 2) { - const x = polygon[i]; - const y = polygon[i + 1]; - if (x === prevX) { - buffer.push(`V${y}`); - prevY = y; - } else if (y === prevY) { - buffer.push(`H${x}`); - prevX = x; - } - } - buffer.push("Z"); - } - return buffer.join(" "); - } - serialize([blX, blY, trX, trY], _rotation) { - const outlines = []; - const width = trX - blX; - const height = trY - blY; - for (const outline of this.#outlines) { - const points = new Array(outline.length); - for (let i = 0; i < outline.length; i += 2) { - points[i] = blX + outline[i] * width; - points[i + 1] = trY - outline[i + 1] * height; - } - outlines.push(points); - } - return outlines; - } - get box() { - return this.#box; - } - get classNamesForOutlining() { - return ["highlightOutline"]; - } -} -class FreeHighlightOutliner extends FreeDrawOutliner { - newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { - return new FreeHighlightOutline(outline, points, box, scaleFactor, innerMargin, isLTR); - } -} -class FreeHighlightOutline extends FreeDrawOutline { - newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { - return new FreeHighlightOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); - } -} - -;// ./src/display/editor/highlight.js - - - - - - - -class HighlightEditor extends AnnotationEditor { - #anchorNode = null; - #anchorOffset = 0; - #boxes; - #clipPathId = null; - #colorPicker = null; - #focusOutlines = null; - #focusNode = null; - #focusOffset = 0; - #highlightDiv = null; - #highlightOutlines = null; - #id = null; - #isFreeHighlight = false; - #firstPoint = null; - #lastPoint = null; - #outlineId = null; - #text = ""; - #thickness; - #methodOfCreation = ""; - static _defaultColor = null; - static _defaultOpacity = 1; - static _defaultThickness = 12; - static _type = "highlight"; - static _editorType = AnnotationEditorType.HIGHLIGHT; - static _freeHighlightId = -1; - static _freeHighlight = null; - static _freeHighlightClipId = ""; - static get _keyboardManager() { - const proto = HighlightEditor.prototype; - return shadow(this, "_keyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, { - args: [0] - }], [["ArrowRight", "mac+ArrowRight"], proto._moveCaret, { - args: [1] - }], [["ArrowUp", "mac+ArrowUp"], proto._moveCaret, { - args: [2] - }], [["ArrowDown", "mac+ArrowDown"], proto._moveCaret, { - args: [3] - }]])); - } - constructor(params) { - super({ - ...params, - name: "highlightEditor" - }); - this.color = params.color || HighlightEditor._defaultColor; - this.#thickness = params.thickness || HighlightEditor._defaultThickness; - this.opacity = params.opacity || HighlightEditor._defaultOpacity; - this.#boxes = params.boxes || null; - this.#methodOfCreation = params.methodOfCreation || ""; - this.#text = params.text || ""; - this._isDraggable = false; - this.defaultL10nId = "pdfjs-editor-highlight-editor"; - if (params.highlightId > -1) { - this.#isFreeHighlight = true; - this.#createFreeOutlines(params); - this.#addToDrawLayer(); - } else if (this.#boxes) { - this.#anchorNode = params.anchorNode; - this.#anchorOffset = params.anchorOffset; - this.#focusNode = params.focusNode; - this.#focusOffset = params.focusOffset; - this.#createOutlines(); - this.#addToDrawLayer(); - this.rotate(this.rotation); - } - if (!this.annotationElementId) { - this._uiManager.a11yAlert("pdfjs-editor-highlight-added-alert"); - } - } - get telemetryInitialData() { - return { - action: "added", - type: this.#isFreeHighlight ? "free_highlight" : "highlight", - color: this._uiManager.getNonHCMColorName(this.color), - thickness: this.#thickness, - methodOfCreation: this.#methodOfCreation - }; - } - get telemetryFinalData() { - return { - type: "highlight", - color: this._uiManager.getNonHCMColorName(this.color) - }; - } - static computeTelemetryFinalData(data) { - return { - numberOfColors: data.get("color").size - }; - } - #createOutlines() { - const outliner = new HighlightOutliner(this.#boxes, 0.001); - this.#highlightOutlines = outliner.getOutlines(); - [this.x, this.y, this.width, this.height] = this.#highlightOutlines.box; - const outlinerForOutline = new HighlightOutliner(this.#boxes, 0.0025, 0.001, this._uiManager.direction === "ltr"); - this.#focusOutlines = outlinerForOutline.getOutlines(); - const { - firstPoint - } = this.#highlightOutlines; - this.#firstPoint = [(firstPoint[0] - this.x) / this.width, (firstPoint[1] - this.y) / this.height]; - const { - lastPoint - } = this.#focusOutlines; - this.#lastPoint = [(lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height]; - } - #createFreeOutlines({ - highlightOutlines, - highlightId, - clipPathId - }) { - this.#highlightOutlines = highlightOutlines; - const extraThickness = 1.5; - this.#focusOutlines = highlightOutlines.getNewOutline(this.#thickness / 2 + extraThickness, 0.0025); - if (highlightId >= 0) { - this.#id = highlightId; - this.#clipPathId = clipPathId; - this.parent.drawLayer.finalizeDraw(highlightId, { - bbox: highlightOutlines.box, - path: { - d: highlightOutlines.toSVGPath() - } - }); - this.#outlineId = this.parent.drawLayer.drawOutline({ - rootClass: { - highlightOutline: true, - free: true - }, - bbox: this.#focusOutlines.box, - path: { - d: this.#focusOutlines.toSVGPath() - } - }, true); - } else if (this.parent) { - const angle = this.parent.viewport.rotation; - this.parent.drawLayer.updateProperties(this.#id, { - bbox: HighlightEditor.#rotateBbox(this.#highlightOutlines.box, (angle - this.rotation + 360) % 360), - path: { - d: highlightOutlines.toSVGPath() - } - }); - this.parent.drawLayer.updateProperties(this.#outlineId, { - bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), - path: { - d: this.#focusOutlines.toSVGPath() - } - }); - } - const [x, y, width, height] = highlightOutlines.box; - switch (this.rotation) { - case 0: - this.x = x; - this.y = y; - this.width = width; - this.height = height; - break; - case 90: - { - const [pageWidth, pageHeight] = this.parentDimensions; - this.x = y; - this.y = 1 - x; - this.width = width * pageHeight / pageWidth; - this.height = height * pageWidth / pageHeight; - break; - } - case 180: - this.x = 1 - x; - this.y = 1 - y; - this.width = width; - this.height = height; - break; - case 270: - { - const [pageWidth, pageHeight] = this.parentDimensions; - this.x = 1 - y; - this.y = x; - this.width = width * pageHeight / pageWidth; - this.height = height * pageWidth / pageHeight; - break; - } - } - const { - firstPoint - } = highlightOutlines; - this.#firstPoint = [(firstPoint[0] - x) / width, (firstPoint[1] - y) / height]; - const { - lastPoint - } = this.#focusOutlines; - this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height]; - } - static initialize(l10n, uiManager) { - AnnotationEditor.initialize(l10n, uiManager); - HighlightEditor._defaultColor ||= uiManager.highlightColors?.values().next().value || "#fff066"; - } - static updateDefaultParams(type, value) { - switch (type) { - case AnnotationEditorParamsType.HIGHLIGHT_COLOR: - HighlightEditor._defaultColor = value; - break; - case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: - HighlightEditor._defaultThickness = value; - break; - } - } - translateInPage(x, y) {} - get toolbarPosition() { - return this.#lastPoint; - } - get commentButtonPosition() { - return this.#firstPoint; - } - updateParams(type, value) { - switch (type) { - case AnnotationEditorParamsType.HIGHLIGHT_COLOR: - this.#updateColor(value); - break; - case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: - this.#updateThickness(value); - break; - } - } - static get defaultPropertiesToUpdate() { - return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, HighlightEditor._defaultThickness]]; - } - get propertiesToUpdate() { - return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, this.color || HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, this.#thickness || HighlightEditor._defaultThickness], [AnnotationEditorParamsType.HIGHLIGHT_FREE, this.#isFreeHighlight]]; - } - onUpdatedColor() { - this.parent?.drawLayer.updateProperties(this.#id, { - root: { - fill: this.color, - "fill-opacity": this.opacity - } - }); - this.#colorPicker?.updateColor(this.color); - super.onUpdatedColor(); - } - #updateColor(color) { - const setColorAndOpacity = (col, opa) => { - this.color = col; - this.opacity = opa; - this.onUpdatedColor(); - }; - const savedColor = this.color; - const savedOpacity = this.opacity; - this.addCommands({ - cmd: setColorAndOpacity.bind(this, color, HighlightEditor._defaultOpacity), - undo: setColorAndOpacity.bind(this, savedColor, savedOpacity), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.HIGHLIGHT_COLOR, - overwriteIfSameType: true, - keepUndo: true - }); - this._reportTelemetry({ - action: "color_changed", - color: this._uiManager.getNonHCMColorName(color) - }, true); - } - #updateThickness(thickness) { - const savedThickness = this.#thickness; - const setThickness = th => { - this.#thickness = th; - this.#changeThickness(th); - }; - this.addCommands({ - cmd: setThickness.bind(this, thickness), - undo: setThickness.bind(this, savedThickness), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type: AnnotationEditorParamsType.INK_THICKNESS, - overwriteIfSameType: true, - keepUndo: true - }); - this._reportTelemetry({ - action: "thickness_changed", - thickness - }, true); - } - get toolbarButtons() { - if (this._uiManager.highlightColors) { - const colorPicker = this.#colorPicker = new ColorPicker({ - editor: this - }); - return [["colorPicker", colorPicker]]; - } - return super.toolbarButtons; - } - disableEditing() { - super.disableEditing(); - this.div.classList.toggle("disabled", true); - } - enableEditing() { - super.enableEditing(); - this.div.classList.toggle("disabled", false); - } - fixAndSetPosition() { - return super.fixAndSetPosition(this.#getRotation()); - } - getBaseTranslation() { - return [0, 0]; - } - getRect(tx, ty) { - return super.getRect(tx, ty, this.#getRotation()); - } - onceAdded(focus) { - if (!this.annotationElementId) { - this.parent.addUndoableEditor(this); - } - if (focus) { - this.div.focus(); - } - } - remove() { - this.#cleanDrawLayer(); - this._reportTelemetry({ - action: "deleted" - }); - super.remove(); - } - rebuild() { - if (!this.parent) { - return; - } - super.rebuild(); - if (this.div === null) { - return; - } - this.#addToDrawLayer(); - if (!this.isAttachedToDOM) { - this.parent.add(this); - } - } - setParent(parent) { - let mustBeSelected = false; - if (this.parent && !parent) { - this.#cleanDrawLayer(); - } else if (parent) { - this.#addToDrawLayer(parent); - mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); - } - super.setParent(parent); - this.show(this._isVisible); - if (mustBeSelected) { - this.select(); - } - } - #changeThickness(thickness) { - if (!this.#isFreeHighlight) { - return; - } - this.#createFreeOutlines({ - highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2) - }); - this.fixAndSetPosition(); - this.setDims(); - } - #cleanDrawLayer() { - if (this.#id === null || !this.parent) { - return; - } - this.parent.drawLayer.remove(this.#id); - this.#id = null; - this.parent.drawLayer.remove(this.#outlineId); - this.#outlineId = null; - } - #addToDrawLayer(parent = this.parent) { - if (this.#id !== null) { - return; - } - ({ - id: this.#id, - clipPathId: this.#clipPathId - } = parent.drawLayer.draw({ - bbox: this.#highlightOutlines.box, - root: { - viewBox: "0 0 1 1", - fill: this.color, - "fill-opacity": this.opacity - }, - rootClass: { - highlight: true, - free: this.#isFreeHighlight - }, - path: { - d: this.#highlightOutlines.toSVGPath() - } - }, false, true)); - this.#outlineId = parent.drawLayer.drawOutline({ - rootClass: { - highlightOutline: true, - free: this.#isFreeHighlight - }, - bbox: this.#focusOutlines.box, - path: { - d: this.#focusOutlines.toSVGPath() - } - }, this.#isFreeHighlight); - if (this.#highlightDiv) { - this.#highlightDiv.style.clipPath = this.#clipPathId; - } - } - static #rotateBbox([x, y, width, height], angle) { - switch (angle) { - case 90: - return [1 - y - height, x, height, width]; - case 180: - return [1 - x - width, 1 - y - height, width, height]; - case 270: - return [y, 1 - x - width, height, width]; - } - return [x, y, width, height]; - } - rotate(angle) { - const { - drawLayer - } = this.parent; - let box; - if (this.#isFreeHighlight) { - angle = (angle - this.rotation + 360) % 360; - box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle); - } else { - box = HighlightEditor.#rotateBbox([this.x, this.y, this.width, this.height], angle); - } - drawLayer.updateProperties(this.#id, { - bbox: box, - root: { - "data-main-rotation": angle - } - }); - drawLayer.updateProperties(this.#outlineId, { - bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), - root: { - "data-main-rotation": angle - } - }); - } - render() { - if (this.div) { - return this.div; - } - const div = super.render(); - if (this.#text) { - div.setAttribute("aria-label", this.#text); - div.setAttribute("role", "mark"); - } - if (this.#isFreeHighlight) { - div.classList.add("free"); - } else { - this.div.addEventListener("keydown", this.#keydown.bind(this), { - signal: this._uiManager._signal - }); - } - const highlightDiv = this.#highlightDiv = document.createElement("div"); - div.append(highlightDiv); - highlightDiv.setAttribute("aria-hidden", "true"); - highlightDiv.className = "internal"; - highlightDiv.style.clipPath = this.#clipPathId; - this.setDims(); - bindEvents(this, this.#highlightDiv, ["pointerover", "pointerleave"]); - this.enableEditing(); - return div; - } - pointerover() { - if (!this.isSelected) { - this.parent?.drawLayer.updateProperties(this.#outlineId, { - rootClass: { - hovered: true - } - }); - } - } - pointerleave() { - if (!this.isSelected) { - this.parent?.drawLayer.updateProperties(this.#outlineId, { - rootClass: { - hovered: false - } - }); - } - } - #keydown(event) { - HighlightEditor._keyboardManager.exec(this, event); - } - _moveCaret(direction) { - this.parent.unselect(this); - switch (direction) { - case 0: - case 2: - this.#setCaret(true); - break; - case 1: - case 3: - this.#setCaret(false); - break; - } - } - #setCaret(start) { - if (!this.#anchorNode) { - return; - } - const selection = window.getSelection(); - if (start) { - selection.setPosition(this.#anchorNode, this.#anchorOffset); - } else { - selection.setPosition(this.#focusNode, this.#focusOffset); - } - } - select() { - super.select(); - if (!this.#outlineId) { - return; - } - this.parent?.drawLayer.updateProperties(this.#outlineId, { - rootClass: { - hovered: false, - selected: true - } - }); - } - unselect() { - super.unselect(); - if (!this.#outlineId) { - return; - } - this.parent?.drawLayer.updateProperties(this.#outlineId, { - rootClass: { - selected: false - } - }); - if (!this.#isFreeHighlight) { - this.#setCaret(false); - } - } - get _mustFixPosition() { - return !this.#isFreeHighlight; - } - show(visible = this._isVisible) { - super.show(visible); - if (this.parent) { - this.parent.drawLayer.updateProperties(this.#id, { - rootClass: { - hidden: !visible - } - }); - this.parent.drawLayer.updateProperties(this.#outlineId, { - rootClass: { - hidden: !visible - } - }); - } - } - #getRotation() { - return this.#isFreeHighlight ? this.rotation : 0; - } - #serializeBoxes() { - if (this.#isFreeHighlight) { - return null; - } - const [pageWidth, pageHeight] = this.pageDimensions; - const [pageX, pageY] = this.pageTranslation; - const boxes = this.#boxes; - const quadPoints = new Float32Array(boxes.length * 8); - let i = 0; - for (const { - x, - y, - width, - height - } of boxes) { - const sx = x * pageWidth + pageX; - const sy = (1 - y) * pageHeight + pageY; - quadPoints[i] = quadPoints[i + 4] = sx; - quadPoints[i + 1] = quadPoints[i + 3] = sy; - quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth; - quadPoints[i + 5] = quadPoints[i + 7] = sy - height * pageHeight; - i += 8; - } - return quadPoints; - } - #serializeOutlines(rect) { - return this.#highlightOutlines.serialize(rect, this.#getRotation()); - } - static startHighlighting(parent, isLTR, { - target: textLayer, - x, - y - }) { - const { - x: layerX, - y: layerY, - width: parentWidth, - height: parentHeight - } = textLayer.getBoundingClientRect(); - const ac = new AbortController(); - const signal = parent.combinedSignal(ac); - const pointerUpCallback = e => { - ac.abort(); - this.#endHighlight(parent, e); - }; - window.addEventListener("blur", pointerUpCallback, { - signal - }); - window.addEventListener("pointerup", pointerUpCallback, { - signal - }); - window.addEventListener("pointerdown", stopEvent, { - capture: true, - passive: false, - signal - }); - window.addEventListener("contextmenu", noContextMenu, { - signal - }); - textLayer.addEventListener("pointermove", this.#highlightMove.bind(this, parent), { - signal - }); - this._freeHighlight = new FreeHighlightOutliner({ - x, - y - }, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001); - ({ - id: this._freeHighlightId, - clipPathId: this._freeHighlightClipId - } = parent.drawLayer.draw({ - bbox: [0, 0, 1, 1], - root: { - viewBox: "0 0 1 1", - fill: this._defaultColor, - "fill-opacity": this._defaultOpacity - }, - rootClass: { - highlight: true, - free: true - }, - path: { - d: this._freeHighlight.toSVGPath() - } - }, true, true)); - } - static #highlightMove(parent, event) { - if (this._freeHighlight.add(event)) { - parent.drawLayer.updateProperties(this._freeHighlightId, { - path: { - d: this._freeHighlight.toSVGPath() - } - }); - } - } - static #endHighlight(parent, event) { - if (!this._freeHighlight.isEmpty()) { - parent.createAndAddNewEditor(event, false, { - highlightId: this._freeHighlightId, - highlightOutlines: this._freeHighlight.getOutlines(), - clipPathId: this._freeHighlightClipId, - methodOfCreation: "main_toolbar" - }); - } else { - parent.drawLayer.remove(this._freeHighlightId); - } - this._freeHighlightId = -1; - this._freeHighlight = null; - this._freeHighlightClipId = ""; - } - static async deserialize(data, parent, uiManager) { - let initialData = null; - if (data instanceof HighlightAnnotationElement) { - const { - data: { - quadPoints, - rect, - rotation, - id, - color, - opacity, - popupRef, - richText, - contentsObj, - creationDate, - modificationDate - }, - parent: { - page: { - pageNumber - } - } - } = data; - initialData = data = { - annotationType: AnnotationEditorType.HIGHLIGHT, - color: Array.from(color), - opacity, - quadPoints, - boxes: null, - pageIndex: pageNumber - 1, - rect: rect.slice(0), - rotation, - annotationElementId: id, - id, - deleted: false, - popupRef, - richText, - comment: contentsObj?.str || null, - creationDate, - modificationDate - }; - } else if (data instanceof InkAnnotationElement) { - const { - data: { - inkLists, - rect, - rotation, - id, - color, - borderStyle: { - rawWidth: thickness - }, - popupRef, - richText, - contentsObj, - creationDate, - modificationDate - }, - parent: { - page: { - pageNumber - } - } - } = data; - initialData = data = { - annotationType: AnnotationEditorType.HIGHLIGHT, - color: Array.from(color), - thickness, - inkLists, - boxes: null, - pageIndex: pageNumber - 1, - rect: rect.slice(0), - rotation, - annotationElementId: id, - id, - deleted: false, - popupRef, - richText, - comment: contentsObj?.str || null, - creationDate, - modificationDate - }; - } - const { - color, - quadPoints, - inkLists, - opacity - } = data; - const editor = await super.deserialize(data, parent, uiManager); - editor.color = Util.makeHexColor(...color); - editor.opacity = opacity || 1; - if (inkLists) { - editor.#thickness = data.thickness; - } - editor._initialData = initialData; - if (data.comment) { - editor.setCommentData(data); - } - const [pageWidth, pageHeight] = editor.pageDimensions; - const [pageX, pageY] = editor.pageTranslation; - if (quadPoints) { - const boxes = editor.#boxes = []; - for (let i = 0; i < quadPoints.length; i += 8) { - boxes.push({ - x: (quadPoints[i] - pageX) / pageWidth, - y: 1 - (quadPoints[i + 1] - pageY) / pageHeight, - width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth, - height: (quadPoints[i + 1] - quadPoints[i + 5]) / pageHeight - }); - } - editor.#createOutlines(); - editor.#addToDrawLayer(); - editor.rotate(editor.rotation); - } else if (inkLists) { - editor.#isFreeHighlight = true; - const points = inkLists[0]; - const point = { - x: points[0] - pageX, - y: pageHeight - (points[1] - pageY) - }; - const outliner = new FreeHighlightOutliner(point, [0, 0, pageWidth, pageHeight], 1, editor.#thickness / 2, true, 0.001); - for (let i = 0, ii = points.length; i < ii; i += 2) { - point.x = points[i] - pageX; - point.y = pageHeight - (points[i + 1] - pageY); - outliner.add(point); - } - const { - id, - clipPathId - } = parent.drawLayer.draw({ - bbox: [0, 0, 1, 1], - root: { - viewBox: "0 0 1 1", - fill: editor.color, - "fill-opacity": editor._defaultOpacity - }, - rootClass: { - highlight: true, - free: true - }, - path: { - d: outliner.toSVGPath() - } - }, true, true); - editor.#createFreeOutlines({ - highlightOutlines: outliner.getOutlines(), - highlightId: id, - clipPathId - }); - editor.#addToDrawLayer(); - editor.rotate(editor.parentRotation); - } - return editor; - } - serialize(isForCopying = false) { - if (this.isEmpty() || isForCopying) { - return null; - } - if (this.deleted) { - return this.serializeDeleted(); - } - const color = AnnotationEditor._colorManager.convert(this._uiManager.getNonHCMColor(this.color)); - const serialized = super.serialize(isForCopying); - Object.assign(serialized, { - color, - opacity: this.opacity, - thickness: this.#thickness, - quadPoints: this.#serializeBoxes(), - outlines: this.#serializeOutlines(serialized.rect) - }); - this.addComment(serialized); - if (this.annotationElementId && !this.#hasElementChanged(serialized)) { - return null; - } - serialized.id = this.annotationElementId; - return serialized; - } - #hasElementChanged(serialized) { - const { - color - } = this._initialData; - return this.hasEditedComment || serialized.color.some((c, i) => c !== color[i]); - } - renderAnnotationElement(annotation) { - if (this.deleted) { - annotation.hide(); - return null; - } - annotation.updateEdited({ - rect: this.getPDFRect(), - popup: this.comment - }); - return null; - } - static canCreateNewEmptyEditor() { - return false; - } -} - -;// ./src/display/editor/draw.js - - - - -class DrawingOptions { - #svgProperties = Object.create(null); - updateProperty(name, value) { - this[name] = value; - this.updateSVGProperty(name, value); - } - updateProperties(properties) { - if (!properties) { - return; - } - for (const [name, value] of Object.entries(properties)) { - if (!name.startsWith("_")) { - this.updateProperty(name, value); - } - } - } - updateSVGProperty(name, value) { - this.#svgProperties[name] = value; - } - toSVGProperties() { - const root = this.#svgProperties; - this.#svgProperties = Object.create(null); - return { - root - }; - } - reset() { - this.#svgProperties = Object.create(null); - } - updateAll(options = this) { - this.updateProperties(options); - } - clone() { - unreachable("Not implemented"); - } -} -class DrawingEditor extends AnnotationEditor { - #drawOutlines = null; - #mustBeCommitted; - _colorPicker = null; - _drawId = null; - static _currentDrawId = -1; - static _currentParent = null; - static #currentDraw = null; - static #currentDrawingAC = null; - static #currentDrawingOptions = null; - static _INNER_MARGIN = 3; - constructor(params) { - super(params); - this.#mustBeCommitted = params.mustBeCommitted || false; - this._addOutlines(params); - } - onUpdatedColor() { - this._colorPicker?.update(this.color); - super.onUpdatedColor(); - } - _addOutlines(params) { - if (params.drawOutlines) { - this.#createDrawOutlines(params); - this.#addToDrawLayer(); - } - } - #createDrawOutlines({ - drawOutlines, - drawId, - drawingOptions - }) { - this.#drawOutlines = drawOutlines; - this._drawingOptions ||= drawingOptions; - if (!this.annotationElementId) { - this._uiManager.a11yAlert(`pdfjs-editor-${this.editorType}-added-alert`); - } - if (drawId >= 0) { - this._drawId = drawId; - this.parent.drawLayer.finalizeDraw(drawId, drawOutlines.defaultProperties); - } else { - this._drawId = this.#createDrawing(drawOutlines, this.parent); - } - this.#updateBbox(drawOutlines.box); - } - #createDrawing(drawOutlines, parent) { - const { - id - } = parent.drawLayer.draw(DrawingEditor._mergeSVGProperties(this._drawingOptions.toSVGProperties(), drawOutlines.defaultSVGProperties), false, false); - return id; - } - static _mergeSVGProperties(p1, p2) { - const p1Keys = new Set(Object.keys(p1)); - for (const [key, value] of Object.entries(p2)) { - if (p1Keys.has(key)) { - Object.assign(p1[key], value); - } else { - p1[key] = value; - } - } - return p1; - } - static getDefaultDrawingOptions(_options) { - unreachable("Not implemented"); - } - static get typesMap() { - unreachable("Not implemented"); - } - static get isDrawer() { - return true; - } - static get supportMultipleDrawings() { - return false; - } - static updateDefaultParams(type, value) { - const propertyName = this.typesMap.get(type); - if (propertyName) { - this._defaultDrawingOptions.updateProperty(propertyName, value); - } - if (this._currentParent) { - DrawingEditor.#currentDraw.updateProperty(propertyName, value); - this._currentParent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties()); - } - } - updateParams(type, value) { - const propertyName = this.constructor.typesMap.get(type); - if (propertyName) { - this._updateProperty(type, propertyName, value); - } - } - static get defaultPropertiesToUpdate() { - const properties = []; - const options = this._defaultDrawingOptions; - for (const [type, name] of this.typesMap) { - properties.push([type, options[name]]); - } - return properties; - } - get propertiesToUpdate() { - const properties = []; - const { - _drawingOptions - } = this; - for (const [type, name] of this.constructor.typesMap) { - properties.push([type, _drawingOptions[name]]); - } - return properties; - } - _updateProperty(type, name, value) { - const options = this._drawingOptions; - const savedValue = options[name]; - const setter = val => { - options.updateProperty(name, val); - const bbox = this.#drawOutlines.updateProperty(name, val); - if (bbox) { - this.#updateBbox(bbox); - } - this.parent?.drawLayer.updateProperties(this._drawId, options.toSVGProperties()); - if (type === this.colorType) { - this.onUpdatedColor(); - } - }; - this.addCommands({ - cmd: setter.bind(this, value), - undo: setter.bind(this, savedValue), - post: this._uiManager.updateUI.bind(this._uiManager, this), - mustExec: true, - type, - overwriteIfSameType: true, - keepUndo: true - }); - } - _onResizing() { - this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizingSVGProperties(this.#convertToDrawSpace()), { - bbox: this.#rotateBox() - })); - } - _onResized() { - this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizedSVGProperties(this.#convertToDrawSpace()), { - bbox: this.#rotateBox() - })); - } - _onTranslating(_x, _y) { - this.parent?.drawLayer.updateProperties(this._drawId, { - bbox: this.#rotateBox() - }); - } - _onTranslated() { - this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathTranslatedSVGProperties(this.#convertToDrawSpace(), this.parentDimensions), { - bbox: this.#rotateBox() - })); - } - _onStartDragging() { - this.parent?.drawLayer.updateProperties(this._drawId, { - rootClass: { - moving: true - } - }); - } - _onStopDragging() { - this.parent?.drawLayer.updateProperties(this._drawId, { - rootClass: { - moving: false - } - }); - } - commit() { - super.commit(); - this.disableEditMode(); - this.disableEditing(); - } - disableEditing() { - super.disableEditing(); - this.div.classList.toggle("disabled", true); - } - enableEditing() { - super.enableEditing(); - this.div.classList.toggle("disabled", false); - } - getBaseTranslation() { - return [0, 0]; - } - get isResizable() { - return true; - } - onceAdded(focus) { - if (!this.annotationElementId) { - this.parent.addUndoableEditor(this); - } - this._isDraggable = true; - if (this.#mustBeCommitted) { - this.#mustBeCommitted = false; - this.commit(); - this.parent.setSelected(this); - if (focus && this.isOnScreen) { - this.div.focus(); - } - } - } - remove() { - this.#cleanDrawLayer(); - super.remove(); - } - rebuild() { - if (!this.parent) { - return; - } - super.rebuild(); - if (this.div === null) { - return; - } - this.#addToDrawLayer(); - this.#updateBbox(this.#drawOutlines.box); - if (!this.isAttachedToDOM) { - this.parent.add(this); - } - } - setParent(parent) { - let mustBeSelected = false; - if (this.parent && !parent) { - this._uiManager.removeShouldRescale(this); - this.#cleanDrawLayer(); - } else if (parent) { - this._uiManager.addShouldRescale(this); - this.#addToDrawLayer(parent); - mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); - } - super.setParent(parent); - if (mustBeSelected) { - this.select(); - } - } - #cleanDrawLayer() { - if (this._drawId === null || !this.parent) { - return; - } - this.parent.drawLayer.remove(this._drawId); - this._drawId = null; - this._drawingOptions.reset(); - } - #addToDrawLayer(parent = this.parent) { - if (this._drawId !== null && this.parent === parent) { - return; - } - if (this._drawId !== null) { - this.parent.drawLayer.updateParent(this._drawId, parent.drawLayer); - return; - } - this._drawingOptions.updateAll(); - this._drawId = this.#createDrawing(this.#drawOutlines, parent); - } - #convertToParentSpace([x, y, width, height]) { - const { - parentDimensions: [pW, pH], - rotation - } = this; - switch (rotation) { - case 90: - return [y, 1 - x, width * (pH / pW), height * (pW / pH)]; - case 180: - return [1 - x, 1 - y, width, height]; - case 270: - return [1 - y, x, width * (pH / pW), height * (pW / pH)]; - default: - return [x, y, width, height]; - } - } - #convertToDrawSpace() { - const { - x, - y, - width, - height, - parentDimensions: [pW, pH], - rotation - } = this; - switch (rotation) { - case 90: - return [1 - y, x, width * (pW / pH), height * (pH / pW)]; - case 180: - return [1 - x, 1 - y, width, height]; - case 270: - return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; - default: - return [x, y, width, height]; - } - } - #updateBbox(bbox) { - [this.x, this.y, this.width, this.height] = this.#convertToParentSpace(bbox); - if (this.div) { - this.fixAndSetPosition(); - this.setDims(); - } - this._onResized(); - } - #rotateBox() { - const { - x, - y, - width, - height, - rotation, - parentRotation, - parentDimensions: [pW, pH] - } = this; - switch ((rotation * 4 + parentRotation) / 90) { - case 1: - return [1 - y - height, x, height, width]; - case 2: - return [1 - x - width, 1 - y - height, width, height]; - case 3: - return [y, 1 - x - width, height, width]; - case 4: - return [x, y - width * (pW / pH), height * (pH / pW), width * (pW / pH)]; - case 5: - return [1 - y, x, width * (pW / pH), height * (pH / pW)]; - case 6: - return [1 - x - height * (pH / pW), 1 - y, height * (pH / pW), width * (pW / pH)]; - case 7: - return [y - width * (pW / pH), 1 - x - height * (pH / pW), width * (pW / pH), height * (pH / pW)]; - case 8: - return [x - width, y - height, width, height]; - case 9: - return [1 - y, x - width, height, width]; - case 10: - return [1 - x, 1 - y, width, height]; - case 11: - return [y - height, 1 - x, height, width]; - case 12: - return [x - height * (pH / pW), y, height * (pH / pW), width * (pW / pH)]; - case 13: - return [1 - y - width * (pW / pH), x - height * (pH / pW), width * (pW / pH), height * (pH / pW)]; - case 14: - return [1 - x, 1 - y - width * (pW / pH), height * (pH / pW), width * (pW / pH)]; - case 15: - return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; - default: - return [x, y, width, height]; - } - } - rotate() { - if (!this.parent) { - return; - } - this.parent.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties({ - bbox: this.#rotateBox() - }, this.#drawOutlines.updateRotation((this.parentRotation - this.rotation + 360) % 360))); - } - onScaleChanging() { - if (!this.parent) { - return; - } - this.#updateBbox(this.#drawOutlines.updateParentDimensions(this.parentDimensions, this.parent.scale)); - } - static onScaleChangingWhenDrawing() {} - render() { - if (this.div) { - return this.div; - } - let baseX, baseY; - if (this._isCopy) { - baseX = this.x; - baseY = this.y; - } - const div = super.render(); - div.classList.add("draw"); - const drawDiv = document.createElement("div"); - div.append(drawDiv); - drawDiv.setAttribute("aria-hidden", "true"); - drawDiv.className = "internal"; - this.setDims(); - this._uiManager.addShouldRescale(this); - this.disableEditing(); - if (this._isCopy) { - this._moveAfterPaste(baseX, baseY); - } - return div; - } - static createDrawerInstance(_x, _y, _parentWidth, _parentHeight, _rotation) { - unreachable("Not implemented"); - } - static startDrawing(parent, uiManager, _isLTR, event) { - const { - target, - offsetX: x, - offsetY: y, - pointerId, - pointerType - } = event; - if (CurrentPointers.isInitializedAndDifferentPointerType(pointerType)) { - return; - } - const { - viewport: { - rotation - } - } = parent; - const { - width: parentWidth, - height: parentHeight - } = target.getBoundingClientRect(); - const ac = DrawingEditor.#currentDrawingAC = new AbortController(); - const signal = parent.combinedSignal(ac); - CurrentPointers.setPointer(pointerType, pointerId); - window.addEventListener("pointerup", e => { - if (CurrentPointers.isSamePointerIdOrRemove(e.pointerId)) { - this._endDraw(e); - } - }, { - signal - }); - window.addEventListener("pointercancel", e => { - if (CurrentPointers.isSamePointerIdOrRemove(e.pointerId)) { - this._currentParent.endDrawingSession(); - } - }, { - signal - }); - window.addEventListener("pointerdown", e => { - if (!CurrentPointers.isSamePointerType(e.pointerType)) { - return; - } - CurrentPointers.initializeAndAddPointerId(e.pointerId); - if (DrawingEditor.#currentDraw.isCancellable()) { - DrawingEditor.#currentDraw.removeLastElement(); - if (DrawingEditor.#currentDraw.isEmpty()) { - this._currentParent.endDrawingSession(true); - } else { - this._endDraw(null); - } - } - }, { - capture: true, - passive: false, - signal - }); - window.addEventListener("contextmenu", noContextMenu, { - signal - }); - target.addEventListener("pointermove", this._drawMove.bind(this), { - signal - }); - target.addEventListener("touchmove", e => { - if (CurrentPointers.isSameTimeStamp(e.timeStamp)) { - stopEvent(e); - } - }, { - signal - }); - parent.toggleDrawing(); - uiManager._editorUndoBar?.hide(); - if (DrawingEditor.#currentDraw) { - parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.startNew(x, y, parentWidth, parentHeight, rotation)); - return; - } - uiManager.updateUIForDefaultProperties(this); - DrawingEditor.#currentDraw = this.createDrawerInstance(x, y, parentWidth, parentHeight, rotation); - DrawingEditor.#currentDrawingOptions = this.getDefaultDrawingOptions(); - this._currentParent = parent; - ({ - id: this._currentDrawId - } = parent.drawLayer.draw(this._mergeSVGProperties(DrawingEditor.#currentDrawingOptions.toSVGProperties(), DrawingEditor.#currentDraw.defaultSVGProperties), true, false)); - } - static _drawMove(event) { - CurrentPointers.isSameTimeStamp(event.timeStamp); - if (!DrawingEditor.#currentDraw) { - return; - } - const { - offsetX, - offsetY, - pointerId - } = event; - if (!CurrentPointers.isSamePointerId(pointerId)) { - return; - } - if (CurrentPointers.isUsingMultiplePointers()) { - this._endDraw(event); - return; - } - this._currentParent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.add(offsetX, offsetY)); - CurrentPointers.setTimeStamp(event.timeStamp); - stopEvent(event); - } - static _cleanup(all) { - if (all) { - this._currentDrawId = -1; - this._currentParent = null; - DrawingEditor.#currentDraw = null; - DrawingEditor.#currentDrawingOptions = null; - CurrentPointers.clearTimeStamp(); - } - if (DrawingEditor.#currentDrawingAC) { - DrawingEditor.#currentDrawingAC.abort(); - DrawingEditor.#currentDrawingAC = null; - CurrentPointers.clearPointerIds(); - } - } - static _endDraw(event) { - const parent = this._currentParent; - if (!parent) { - return; - } - parent.toggleDrawing(true); - this._cleanup(false); - if (event?.target === parent.div) { - parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.end(event.offsetX, event.offsetY)); - } - if (this.supportMultipleDrawings) { - const draw = DrawingEditor.#currentDraw; - const drawId = this._currentDrawId; - const lastElement = draw.getLastElement(); - parent.addCommands({ - cmd: () => { - parent.drawLayer.updateProperties(drawId, draw.setLastElement(lastElement)); - }, - undo: () => { - parent.drawLayer.updateProperties(drawId, draw.removeLastElement()); - }, - mustExec: false, - type: AnnotationEditorParamsType.DRAW_STEP - }); - return; - } - this.endDrawing(false); - } - static endDrawing(isAborted) { - const parent = this._currentParent; - if (!parent) { - return null; - } - parent.toggleDrawing(true); - parent.cleanUndoStack(AnnotationEditorParamsType.DRAW_STEP); - if (!DrawingEditor.#currentDraw.isEmpty()) { - const { - pageDimensions: [pageWidth, pageHeight], - scale - } = parent; - const editor = parent.createAndAddNewEditor({ - offsetX: 0, - offsetY: 0 - }, false, { - drawId: this._currentDrawId, - drawOutlines: DrawingEditor.#currentDraw.getOutlines(pageWidth * scale, pageHeight * scale, scale, this._INNER_MARGIN), - drawingOptions: DrawingEditor.#currentDrawingOptions, - mustBeCommitted: !isAborted - }); - this._cleanup(true); - return editor; - } - parent.drawLayer.remove(this._currentDrawId); - this._cleanup(true); - return null; - } - createDrawingOptions(_data) {} - static deserializeDraw(_pageX, _pageY, _pageWidth, _pageHeight, _innerWidth, _data) { - unreachable("Not implemented"); - } - static async deserialize(data, parent, uiManager) { - const { - rawDims: { - pageWidth, - pageHeight, - pageX, - pageY - } - } = parent.viewport; - const drawOutlines = this.deserializeDraw(pageX, pageY, pageWidth, pageHeight, this._INNER_MARGIN, data); - const editor = await super.deserialize(data, parent, uiManager); - editor.createDrawingOptions(data); - editor.#createDrawOutlines({ - drawOutlines - }); - editor.#addToDrawLayer(); - editor.onScaleChanging(); - editor.rotate(); - return editor; - } - serializeDraw(isForCopying) { - const [pageX, pageY] = this.pageTranslation; - const [pageWidth, pageHeight] = this.pageDimensions; - return this.#drawOutlines.serialize([pageX, pageY, pageWidth, pageHeight], isForCopying); - } - renderAnnotationElement(annotation) { - annotation.updateEdited({ - rect: this.getPDFRect() - }); - return null; - } - static canCreateNewEmptyEditor() { - return false; - } -} - -;// ./src/display/editor/drawers/inkdraw.js - - -class InkDrawOutliner { - #last = new Float64Array(6); - #line; - #lines; - #rotation; - #thickness; - #points; - #lastSVGPath = ""; - #lastIndex = 0; - #outlines = new InkDrawOutline(); - #parentWidth; - #parentHeight; - constructor(x, y, parentWidth, parentHeight, rotation, thickness) { - this.#parentWidth = parentWidth; - this.#parentHeight = parentHeight; - this.#rotation = rotation; - this.#thickness = thickness; - [x, y] = this.#normalizePoint(x, y); - const line = this.#line = [NaN, NaN, NaN, NaN, x, y]; - this.#points = [x, y]; - this.#lines = [{ - line, - points: this.#points - }]; - this.#last.set(line, 0); - } - updateProperty(name, value) { - if (name === "stroke-width") { - this.#thickness = value; - } - } - #normalizePoint(x, y) { - return Outline._normalizePoint(x, y, this.#parentWidth, this.#parentHeight, this.#rotation); - } - isEmpty() { - return !this.#lines || this.#lines.length === 0; - } - isCancellable() { - return this.#points.length <= 10; - } - add(x, y) { - [x, y] = this.#normalizePoint(x, y); - const [x1, y1, x2, y2] = this.#last.subarray(2, 6); - const diffX = x - x2; - const diffY = y - y2; - const d = Math.hypot(this.#parentWidth * diffX, this.#parentHeight * diffY); - if (d <= 2) { - return null; - } - this.#points.push(x, y); - if (isNaN(x1)) { - this.#last.set([x2, y2, x, y], 2); - this.#line.push(NaN, NaN, NaN, NaN, x, y); - return { - path: { - d: this.toSVGPath() - } - }; - } - if (isNaN(this.#last[0])) { - this.#line.splice(6, 6); - } - this.#last.set([x1, y1, x2, y2, x, y], 0); - this.#line.push(...Outline.createBezierPoints(x1, y1, x2, y2, x, y)); - return { - path: { - d: this.toSVGPath() - } - }; - } - end(x, y) { - const change = this.add(x, y); - if (change) { - return change; - } - if (this.#points.length === 2) { - return { - path: { - d: this.toSVGPath() - } - }; - } - return null; - } - startNew(x, y, parentWidth, parentHeight, rotation) { - this.#parentWidth = parentWidth; - this.#parentHeight = parentHeight; - this.#rotation = rotation; - [x, y] = this.#normalizePoint(x, y); - const line = this.#line = [NaN, NaN, NaN, NaN, x, y]; - this.#points = [x, y]; - const last = this.#lines.at(-1); - if (last) { - last.line = new Float32Array(last.line); - last.points = new Float32Array(last.points); - } - this.#lines.push({ - line, - points: this.#points - }); - this.#last.set(line, 0); - this.#lastIndex = 0; - this.toSVGPath(); - return null; - } - getLastElement() { - return this.#lines.at(-1); - } - setLastElement(element) { - if (!this.#lines) { - return this.#outlines.setLastElement(element); - } - this.#lines.push(element); - this.#line = element.line; - this.#points = element.points; - this.#lastIndex = 0; - return { - path: { - d: this.toSVGPath() - } - }; - } - removeLastElement() { - if (!this.#lines) { - return this.#outlines.removeLastElement(); - } - this.#lines.pop(); - this.#lastSVGPath = ""; - for (let i = 0, ii = this.#lines.length; i < ii; i++) { - const { - line, - points - } = this.#lines[i]; - this.#line = line; - this.#points = points; - this.#lastIndex = 0; - this.toSVGPath(); - } - return { - path: { - d: this.#lastSVGPath - } - }; - } - toSVGPath() { - const firstX = Outline.svgRound(this.#line[4]); - const firstY = Outline.svgRound(this.#line[5]); - if (this.#points.length === 2) { - this.#lastSVGPath = `${this.#lastSVGPath} M ${firstX} ${firstY} Z`; - return this.#lastSVGPath; - } - if (this.#points.length <= 6) { - const i = this.#lastSVGPath.lastIndexOf("M"); - this.#lastSVGPath = `${this.#lastSVGPath.slice(0, i)} M ${firstX} ${firstY}`; - this.#lastIndex = 6; - } - if (this.#points.length === 4) { - const secondX = Outline.svgRound(this.#line[10]); - const secondY = Outline.svgRound(this.#line[11]); - this.#lastSVGPath = `${this.#lastSVGPath} L ${secondX} ${secondY}`; - this.#lastIndex = 12; - return this.#lastSVGPath; - } - const buffer = []; - if (this.#lastIndex === 0) { - buffer.push(`M ${firstX} ${firstY}`); - this.#lastIndex = 6; - } - for (let i = this.#lastIndex, ii = this.#line.length; i < ii; i += 6) { - const [c1x, c1y, c2x, c2y, x, y] = this.#line.slice(i, i + 6).map(Outline.svgRound); - buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); - } - this.#lastSVGPath += buffer.join(" "); - this.#lastIndex = this.#line.length; - return this.#lastSVGPath; - } - getOutlines(parentWidth, parentHeight, scale, innerMargin) { - const last = this.#lines.at(-1); - last.line = new Float32Array(last.line); - last.points = new Float32Array(last.points); - this.#outlines.build(this.#lines, parentWidth, parentHeight, scale, this.#rotation, this.#thickness, innerMargin); - this.#last = null; - this.#line = null; - this.#lines = null; - this.#lastSVGPath = null; - return this.#outlines; - } - get defaultSVGProperties() { - return { - root: { - viewBox: "0 0 10000 10000" - }, - rootClass: { - draw: true - }, - bbox: [0, 0, 1, 1] - }; - } -} -class InkDrawOutline extends Outline { - #bbox; - #currentRotation = 0; - #innerMargin; - #lines; - #parentWidth; - #parentHeight; - #parentScale; - #rotation; - #thickness; - build(lines, parentWidth, parentHeight, parentScale, rotation, thickness, innerMargin) { - this.#parentWidth = parentWidth; - this.#parentHeight = parentHeight; - this.#parentScale = parentScale; - this.#rotation = rotation; - this.#thickness = thickness; - this.#innerMargin = innerMargin ?? 0; - this.#lines = lines; - this.#computeBbox(); - } - get thickness() { - return this.#thickness; - } - setLastElement(element) { - this.#lines.push(element); - return { - path: { - d: this.toSVGPath() - } - }; - } - removeLastElement() { - this.#lines.pop(); - return { - path: { - d: this.toSVGPath() - } - }; - } - toSVGPath() { - const buffer = []; - for (const { - line - } of this.#lines) { - buffer.push(`M${Outline.svgRound(line[4])} ${Outline.svgRound(line[5])}`); - if (line.length === 6) { - buffer.push("Z"); - continue; - } - if (line.length === 12 && isNaN(line[6])) { - buffer.push(`L${Outline.svgRound(line[10])} ${Outline.svgRound(line[11])}`); - continue; - } - for (let i = 6, ii = line.length; i < ii; i += 6) { - const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6).map(Outline.svgRound); - buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); - } - } - return buffer.join(""); - } - serialize([pageX, pageY, pageWidth, pageHeight], isForCopying) { - const serializedLines = []; - const serializedPoints = []; - const [x, y, width, height] = this.#getBBoxWithNoMargin(); - let tx, ty, sx, sy, x1, y1, x2, y2, rescaleFn; - switch (this.#rotation) { - case 0: - rescaleFn = Outline._rescale; - tx = pageX; - ty = pageY + pageHeight; - sx = pageWidth; - sy = -pageHeight; - x1 = pageX + x * pageWidth; - y1 = pageY + (1 - y - height) * pageHeight; - x2 = pageX + (x + width) * pageWidth; - y2 = pageY + (1 - y) * pageHeight; - break; - case 90: - rescaleFn = Outline._rescaleAndSwap; - tx = pageX; - ty = pageY; - sx = pageWidth; - sy = pageHeight; - x1 = pageX + y * pageWidth; - y1 = pageY + x * pageHeight; - x2 = pageX + (y + height) * pageWidth; - y2 = pageY + (x + width) * pageHeight; - break; - case 180: - rescaleFn = Outline._rescale; - tx = pageX + pageWidth; - ty = pageY; - sx = -pageWidth; - sy = pageHeight; - x1 = pageX + (1 - x - width) * pageWidth; - y1 = pageY + y * pageHeight; - x2 = pageX + (1 - x) * pageWidth; - y2 = pageY + (y + height) * pageHeight; - break; - case 270: - rescaleFn = Outline._rescaleAndSwap; - tx = pageX + pageWidth; - ty = pageY + pageHeight; - sx = -pageWidth; - sy = -pageHeight; - x1 = pageX + (1 - y - height) * pageWidth; - y1 = pageY + (1 - x - width) * pageHeight; - x2 = pageX + (1 - y) * pageWidth; - y2 = pageY + (1 - x) * pageHeight; - break; - } - for (const { - line, - points - } of this.#lines) { - serializedLines.push(rescaleFn(line, tx, ty, sx, sy, isForCopying ? new Array(line.length) : null)); - serializedPoints.push(rescaleFn(points, tx, ty, sx, sy, isForCopying ? new Array(points.length) : null)); - } - return { - lines: serializedLines, - points: serializedPoints, - rect: [x1, y1, x2, y2] - }; - } - static deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, { - paths: { - lines, - points - }, - rotation, - thickness - }) { - const newLines = []; - let tx, ty, sx, sy, rescaleFn; - switch (rotation) { - case 0: - rescaleFn = Outline._rescale; - tx = -pageX / pageWidth; - ty = pageY / pageHeight + 1; - sx = 1 / pageWidth; - sy = -1 / pageHeight; - break; - case 90: - rescaleFn = Outline._rescaleAndSwap; - tx = -pageY / pageHeight; - ty = -pageX / pageWidth; - sx = 1 / pageHeight; - sy = 1 / pageWidth; - break; - case 180: - rescaleFn = Outline._rescale; - tx = pageX / pageWidth + 1; - ty = -pageY / pageHeight; - sx = -1 / pageWidth; - sy = 1 / pageHeight; - break; - case 270: - rescaleFn = Outline._rescaleAndSwap; - tx = pageY / pageHeight + 1; - ty = pageX / pageWidth + 1; - sx = -1 / pageHeight; - sy = -1 / pageWidth; - break; - } - if (!lines) { - lines = []; - for (const point of points) { - const len = point.length; - if (len === 2) { - lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1]])); - continue; - } - if (len === 4) { - lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1], NaN, NaN, NaN, NaN, point[2], point[3]])); - continue; - } - const line = new Float32Array(3 * (len - 2)); - lines.push(line); - let [x1, y1, x2, y2] = point.subarray(0, 4); - line.set([NaN, NaN, NaN, NaN, x1, y1], 0); - for (let i = 4; i < len; i += 2) { - const x = point[i]; - const y = point[i + 1]; - line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3); - [x1, y1, x2, y2] = [x2, y2, x, y]; - } - } - } - for (let i = 0, ii = lines.length; i < ii; i++) { - newLines.push({ - line: rescaleFn(lines[i].map(x => x ?? NaN), tx, ty, sx, sy), - points: rescaleFn(points[i].map(x => x ?? NaN), tx, ty, sx, sy) - }); - } - const outlines = new this.prototype.constructor(); - outlines.build(newLines, pageWidth, pageHeight, 1, rotation, thickness, innerMargin); - return outlines; - } - #getMarginComponents(thickness = this.#thickness) { - const margin = this.#innerMargin + thickness / 2 * this.#parentScale; - return this.#rotation % 180 === 0 ? [margin / this.#parentWidth, margin / this.#parentHeight] : [margin / this.#parentHeight, margin / this.#parentWidth]; - } - #getBBoxWithNoMargin() { - const [x, y, width, height] = this.#bbox; - const [marginX, marginY] = this.#getMarginComponents(0); - return [x + marginX, y + marginY, width - 2 * marginX, height - 2 * marginY]; - } - #computeBbox() { - const bbox = this.#bbox = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]); - for (const { - line - } of this.#lines) { - if (line.length <= 12) { - for (let i = 4, ii = line.length; i < ii; i += 6) { - Util.pointBoundingBox(line[i], line[i + 1], bbox); - } - continue; - } - let lastX = line[4], - lastY = line[5]; - for (let i = 6, ii = line.length; i < ii; i += 6) { - const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6); - Util.bezierBoundingBox(lastX, lastY, c1x, c1y, c2x, c2y, x, y, bbox); - lastX = x; - lastY = y; - } - } - const [marginX, marginY] = this.#getMarginComponents(); - bbox[0] = MathClamp(bbox[0] - marginX, 0, 1); - bbox[1] = MathClamp(bbox[1] - marginY, 0, 1); - bbox[2] = MathClamp(bbox[2] + marginX, 0, 1); - bbox[3] = MathClamp(bbox[3] + marginY, 0, 1); - bbox[2] -= bbox[0]; - bbox[3] -= bbox[1]; - } - get box() { - return this.#bbox; - } - updateProperty(name, value) { - if (name === "stroke-width") { - return this.#updateThickness(value); - } - return null; - } - #updateThickness(thickness) { - const [oldMarginX, oldMarginY] = this.#getMarginComponents(); - this.#thickness = thickness; - const [newMarginX, newMarginY] = this.#getMarginComponents(); - const [diffMarginX, diffMarginY] = [newMarginX - oldMarginX, newMarginY - oldMarginY]; - const bbox = this.#bbox; - bbox[0] -= diffMarginX; - bbox[1] -= diffMarginY; - bbox[2] += 2 * diffMarginX; - bbox[3] += 2 * diffMarginY; - return bbox; - } - updateParentDimensions([width, height], scale) { - const [oldMarginX, oldMarginY] = this.#getMarginComponents(); - this.#parentWidth = width; - this.#parentHeight = height; - this.#parentScale = scale; - const [newMarginX, newMarginY] = this.#getMarginComponents(); - const diffMarginX = newMarginX - oldMarginX; - const diffMarginY = newMarginY - oldMarginY; - const bbox = this.#bbox; - bbox[0] -= diffMarginX; - bbox[1] -= diffMarginY; - bbox[2] += 2 * diffMarginX; - bbox[3] += 2 * diffMarginY; - return bbox; - } - updateRotation(rotation) { - this.#currentRotation = rotation; - return { - path: { - transform: this.rotationTransform - } - }; - } - get viewBox() { - return this.#bbox.map(Outline.svgRound).join(" "); - } - get defaultProperties() { - const [x, y] = this.#bbox; - return { - root: { - viewBox: this.viewBox - }, - path: { - "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}` - } - }; - } - get rotationTransform() { - const [,, width, height] = this.#bbox; - let a = 0, - b = 0, - c = 0, - d = 0, - e = 0, - f = 0; - switch (this.#currentRotation) { - case 90: - b = height / width; - c = -width / height; - e = width; - break; - case 180: - a = -1; - d = -1; - e = width; - f = height; - break; - case 270: - b = -height / width; - c = width / height; - f = height; - break; - default: - return ""; - } - return `matrix(${a} ${b} ${c} ${d} ${Outline.svgRound(e)} ${Outline.svgRound(f)})`; - } - getPathResizingSVGProperties([newX, newY, newWidth, newHeight]) { - const [marginX, marginY] = this.#getMarginComponents(); - const [x, y, width, height] = this.#bbox; - if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) { - const tx = newX + newWidth / 2 - (x + width / 2); - const ty = newY + newHeight / 2 - (y + height / 2); - return { - path: { - "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, - transform: `${this.rotationTransform} translate(${tx} ${ty})` - } - }; - } - const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); - const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); - const s2x = width / newWidth; - const s2y = height / newHeight; - return { - path: { - "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`, - transform: `${this.rotationTransform} scale(${s2x} ${s2y}) ` + `translate(${Outline.svgRound(marginX)} ${Outline.svgRound(marginY)}) scale(${s1x} ${s1y}) ` + `translate(${Outline.svgRound(-marginX)} ${Outline.svgRound(-marginY)})` - } - }; - } - getPathResizedSVGProperties([newX, newY, newWidth, newHeight]) { - const [marginX, marginY] = this.#getMarginComponents(); - const bbox = this.#bbox; - const [x, y, width, height] = bbox; - bbox[0] = newX; - bbox[1] = newY; - bbox[2] = newWidth; - bbox[3] = newHeight; - if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) { - const tx = newX + newWidth / 2 - (x + width / 2); - const ty = newY + newHeight / 2 - (y + height / 2); - for (const { - line, - points - } of this.#lines) { - Outline._translate(line, tx, ty, line); - Outline._translate(points, tx, ty, points); - } - return { - root: { - viewBox: this.viewBox - }, - path: { - "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, - transform: this.rotationTransform || null, - d: this.toSVGPath() - } - }; - } - const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); - const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); - const tx = -s1x * (x + marginX) + newX + marginX; - const ty = -s1y * (y + marginY) + newY + marginY; - if (s1x !== 1 || s1y !== 1 || tx !== 0 || ty !== 0) { - for (const { - line, - points - } of this.#lines) { - Outline._rescale(line, tx, ty, s1x, s1y, line); - Outline._rescale(points, tx, ty, s1x, s1y, points); - } - } - return { - root: { - viewBox: this.viewBox - }, - path: { - "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, - transform: this.rotationTransform || null, - d: this.toSVGPath() - } - }; - } - getPathTranslatedSVGProperties([newX, newY], parentDimensions) { - const [newParentWidth, newParentHeight] = parentDimensions; - const bbox = this.#bbox; - const tx = newX - bbox[0]; - const ty = newY - bbox[1]; - if (this.#parentWidth === newParentWidth && this.#parentHeight === newParentHeight) { - for (const { - line, - points - } of this.#lines) { - Outline._translate(line, tx, ty, line); - Outline._translate(points, tx, ty, points); - } - } else { - const sx = this.#parentWidth / newParentWidth; - const sy = this.#parentHeight / newParentHeight; - this.#parentWidth = newParentWidth; - this.#parentHeight = newParentHeight; - for (const { - line, - points - } of this.#lines) { - Outline._rescale(line, tx, ty, sx, sy, line); - Outline._rescale(points, tx, ty, sx, sy, points); - } - bbox[2] *= sx; - bbox[3] *= sy; - } - bbox[0] = newX; - bbox[1] = newY; - return { - root: { - viewBox: this.viewBox - }, - path: { - d: this.toSVGPath(), - "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}` - } - }; - } - get defaultSVGProperties() { - const bbox = this.#bbox; - return { - root: { - viewBox: this.viewBox - }, - rootClass: { - draw: true - }, - path: { - d: this.toSVGPath(), - "transform-origin": `${Outline.svgRound(bbox[0])} ${Outline.svgRound(bbox[1])}`, - transform: this.rotationTransform || null - }, - bbox - }; - } -} - -;// ./src/display/editor/ink.js - - - - - - -class InkDrawingOptions extends DrawingOptions { - constructor(viewerParameters) { - super(); - this._viewParameters = viewerParameters; - super.updateProperties({ - fill: "none", - stroke: AnnotationEditor._defaultLineColor, - "stroke-opacity": 1, - "stroke-width": 1, - "stroke-linecap": "round", - "stroke-linejoin": "round", - "stroke-miterlimit": 10 - }); - } - updateSVGProperty(name, value) { - if (name === "stroke-width") { - value ??= this["stroke-width"]; - value *= this._viewParameters.realScale; - } - super.updateSVGProperty(name, value); - } - clone() { - const clone = new InkDrawingOptions(this._viewParameters); - clone.updateAll(this); - return clone; - } -} -class InkEditor extends DrawingEditor { - static _type = "ink"; - static _editorType = AnnotationEditorType.INK; - static _defaultDrawingOptions = null; - constructor(params) { - super({ - ...params, - name: "inkEditor" - }); - this._willKeepAspectRatio = true; - this.defaultL10nId = "pdfjs-editor-ink-editor"; - } - static initialize(l10n, uiManager) { - AnnotationEditor.initialize(l10n, uiManager); - this._defaultDrawingOptions = new InkDrawingOptions(uiManager.viewParameters); - } - static getDefaultDrawingOptions(options) { - const clone = this._defaultDrawingOptions.clone(); - clone.updateProperties(options); - return clone; - } - static get supportMultipleDrawings() { - return true; - } - static get typesMap() { - return shadow(this, "typesMap", new Map([[AnnotationEditorParamsType.INK_THICKNESS, "stroke-width"], [AnnotationEditorParamsType.INK_COLOR, "stroke"], [AnnotationEditorParamsType.INK_OPACITY, "stroke-opacity"]])); - } - static createDrawerInstance(x, y, parentWidth, parentHeight, rotation) { - return new InkDrawOutliner(x, y, parentWidth, parentHeight, rotation, this._defaultDrawingOptions["stroke-width"]); - } - static deserializeDraw(pageX, pageY, pageWidth, pageHeight, innerMargin, data) { - return InkDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); - } - static async deserialize(data, parent, uiManager) { - let initialData = null; - if (data instanceof InkAnnotationElement) { - const { - data: { - inkLists, - rect, - rotation, - id, - color, - opacity, - borderStyle: { - rawWidth: thickness - }, - popupRef, - richText, - contentsObj, - creationDate, - modificationDate - }, - parent: { - page: { - pageNumber - } - } - } = data; - initialData = data = { - annotationType: AnnotationEditorType.INK, - color: Array.from(color), - thickness, - opacity, - paths: { - points: inkLists - }, - boxes: null, - pageIndex: pageNumber - 1, - rect: rect.slice(0), - rotation, - annotationElementId: id, - id, - deleted: false, - popupRef, - richText, - comment: contentsObj?.str || null, - creationDate, - modificationDate - }; - } - const editor = await super.deserialize(data, parent, uiManager); - editor._initialData = initialData; - if (data.comment) { - editor.setCommentData(data); - } - return editor; - } - get toolbarButtons() { - this._colorPicker ||= new BasicColorPicker(this); - return [["colorPicker", this._colorPicker]]; - } - get colorType() { - return AnnotationEditorParamsType.INK_COLOR; - } - get color() { - return this._drawingOptions.stroke; - } - get opacity() { - return this._drawingOptions["stroke-opacity"]; - } - onScaleChanging() { - if (!this.parent) { - return; - } - super.onScaleChanging(); - const { - _drawId, - _drawingOptions, - parent - } = this; - _drawingOptions.updateSVGProperty("stroke-width"); - parent.drawLayer.updateProperties(_drawId, _drawingOptions.toSVGProperties()); - } - static onScaleChangingWhenDrawing() { - const parent = this._currentParent; - if (!parent) { - return; - } - super.onScaleChangingWhenDrawing(); - this._defaultDrawingOptions.updateSVGProperty("stroke-width"); - parent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties()); - } - createDrawingOptions({ - color, - thickness, - opacity - }) { - this._drawingOptions = InkEditor.getDefaultDrawingOptions({ - stroke: Util.makeHexColor(...color), - "stroke-width": thickness, - "stroke-opacity": opacity - }); - } - serialize(isForCopying = false) { - if (this.isEmpty()) { - return null; - } - if (this.deleted) { - return this.serializeDeleted(); - } - const { - lines, - points - } = this.serializeDraw(isForCopying); - const { - _drawingOptions: { - stroke, - "stroke-opacity": opacity, - "stroke-width": thickness - } - } = this; - const serialized = Object.assign(super.serialize(isForCopying), { - color: AnnotationEditor._colorManager.convert(stroke), - opacity, - thickness, - paths: { - lines, - points - } - }); - this.addComment(serialized); - if (isForCopying) { - serialized.isCopy = true; - return serialized; - } - if (this.annotationElementId && !this.#hasElementChanged(serialized)) { - return null; - } - serialized.id = this.annotationElementId; - return serialized; - } - #hasElementChanged(serialized) { - const { - color, - thickness, - opacity, - pageIndex - } = this._initialData; - return this.hasEditedComment || this._hasBeenMoved || this._hasBeenResized || serialized.color.some((c, i) => c !== color[i]) || serialized.thickness !== thickness || serialized.opacity !== opacity || serialized.pageIndex !== pageIndex; - } - renderAnnotationElement(annotation) { - if (this.deleted) { - annotation.hide(); - return null; - } - const { - points, - rect - } = this.serializeDraw(false); - annotation.updateEdited({ - rect, - thickness: this._drawingOptions["stroke-width"], - points, - popup: this.comment - }); - return null; - } -} - -;// ./src/display/editor/drawers/contour.js - -class ContourDrawOutline extends InkDrawOutline { - toSVGPath() { - let path = super.toSVGPath(); - if (!path.endsWith("Z")) { - path += "Z"; - } - return path; - } -} - -;// ./src/display/editor/drawers/signaturedraw.js - - - - -const BASE_HEADER_LENGTH = 8; -const POINTS_PROPERTIES_NUMBER = 3; -class SignatureExtractor { - static #PARAMETERS = { - maxDim: 512, - sigmaSFactor: 0.02, - sigmaR: 25, - kernelSize: 16 - }; - static #neighborIndexToId(i0, j0, i, j) { - i -= i0; - j -= j0; - if (i === 0) { - return j > 0 ? 0 : 4; - } - if (i === 1) { - return j + 6; - } - return 2 - j; - } - static #neighborIdToIndex = new Int32Array([0, 1, -1, 1, -1, 0, -1, -1, 0, -1, 1, -1, 1, 0, 1, 1]); - static #clockwiseNonZero(buf, width, i0, j0, i, j, offset) { - const id = this.#neighborIndexToId(i0, j0, i, j); - for (let k = 0; k < 8; k++) { - const kk = (-k + id - offset + 16) % 8; - const shiftI = this.#neighborIdToIndex[2 * kk]; - const shiftJ = this.#neighborIdToIndex[2 * kk + 1]; - if (buf[(i0 + shiftI) * width + (j0 + shiftJ)] !== 0) { - return kk; - } - } - return -1; - } - static #counterClockwiseNonZero(buf, width, i0, j0, i, j, offset) { - const id = this.#neighborIndexToId(i0, j0, i, j); - for (let k = 0; k < 8; k++) { - const kk = (k + id + offset + 16) % 8; - const shiftI = this.#neighborIdToIndex[2 * kk]; - const shiftJ = this.#neighborIdToIndex[2 * kk + 1]; - if (buf[(i0 + shiftI) * width + (j0 + shiftJ)] !== 0) { - return kk; - } - } - return -1; - } - static #findContours(buf, width, height, threshold) { - const N = buf.length; - const types = new Int32Array(N); - for (let i = 0; i < N; i++) { - types[i] = buf[i] <= threshold ? 1 : 0; - } - for (let i = 1; i < height - 1; i++) { - types[i * width] = types[i * width + width - 1] = 0; - } - for (let i = 0; i < width; i++) { - types[i] = types[width * height - 1 - i] = 0; - } - let nbd = 1; - let lnbd; - const contours = []; - for (let i = 1; i < height - 1; i++) { - lnbd = 1; - for (let j = 1; j < width - 1; j++) { - const ij = i * width + j; - const pix = types[ij]; - if (pix === 0) { - continue; - } - let i2 = i; - let j2 = j; - if (pix === 1 && types[ij - 1] === 0) { - nbd += 1; - j2 -= 1; - } else if (pix >= 1 && types[ij + 1] === 0) { - nbd += 1; - j2 += 1; - if (pix > 1) { - lnbd = pix; - } - } else { - if (pix !== 1) { - lnbd = Math.abs(pix); - } - continue; - } - const points = [j, i]; - const isHole = j2 === j + 1; - const contour = { - isHole, - points, - id: nbd, - parent: 0 - }; - contours.push(contour); - let contour0; - for (const c of contours) { - if (c.id === lnbd) { - contour0 = c; - break; - } - } - if (!contour0) { - contour.parent = isHole ? lnbd : 0; - } else if (contour0.isHole) { - contour.parent = isHole ? contour0.parent : lnbd; - } else { - contour.parent = isHole ? lnbd : contour0.parent; - } - const k = this.#clockwiseNonZero(types, width, i, j, i2, j2, 0); - if (k === -1) { - types[ij] = -nbd; - if (types[ij] !== 1) { - lnbd = Math.abs(types[ij]); - } - continue; - } - let shiftI = this.#neighborIdToIndex[2 * k]; - let shiftJ = this.#neighborIdToIndex[2 * k + 1]; - const i1 = i + shiftI; - const j1 = j + shiftJ; - i2 = i1; - j2 = j1; - let i3 = i; - let j3 = j; - while (true) { - const kk = this.#counterClockwiseNonZero(types, width, i3, j3, i2, j2, 1); - shiftI = this.#neighborIdToIndex[2 * kk]; - shiftJ = this.#neighborIdToIndex[2 * kk + 1]; - const i4 = i3 + shiftI; - const j4 = j3 + shiftJ; - points.push(j4, i4); - const ij3 = i3 * width + j3; - if (types[ij3 + 1] === 0) { - types[ij3] = -nbd; - } else if (types[ij3] === 1) { - types[ij3] = nbd; - } - if (i4 === i && j4 === j && i3 === i1 && j3 === j1) { - if (types[ij] !== 1) { - lnbd = Math.abs(types[ij]); - } - break; - } else { - i2 = i3; - j2 = j3; - i3 = i4; - j3 = j4; - } - } - } - } - return contours; - } - static #douglasPeuckerHelper(points, start, end, output) { - if (end - start <= 4) { - for (let i = start; i < end - 2; i += 2) { - output.push(points[i], points[i + 1]); - } - return; - } - const ax = points[start]; - const ay = points[start + 1]; - const abx = points[end - 4] - ax; - const aby = points[end - 3] - ay; - const dist = Math.hypot(abx, aby); - const nabx = abx / dist; - const naby = aby / dist; - const aa = nabx * ay - naby * ax; - const m = aby / abx; - const invS = 1 / dist; - const phi = Math.atan(m); - const cosPhi = Math.cos(phi); - const sinPhi = Math.sin(phi); - const tmax = invS * (Math.abs(cosPhi) + Math.abs(sinPhi)); - const poly = invS * (1 - tmax + tmax ** 2); - const partialPhi = Math.max(Math.atan(Math.abs(sinPhi + cosPhi) * poly), Math.atan(Math.abs(sinPhi - cosPhi) * poly)); - let dmax = 0; - let index = start; - for (let i = start + 2; i < end - 2; i += 2) { - const d = Math.abs(aa - nabx * points[i + 1] + naby * points[i]); - if (d > dmax) { - index = i; - dmax = d; - } - } - if (dmax > (dist * partialPhi) ** 2) { - this.#douglasPeuckerHelper(points, start, index + 2, output); - this.#douglasPeuckerHelper(points, index, end, output); - } else { - output.push(ax, ay); - } - } - static #douglasPeucker(points) { - const output = []; - const len = points.length; - this.#douglasPeuckerHelper(points, 0, len, output); - output.push(points[len - 2], points[len - 1]); - return output.length <= 4 ? null : output; - } - static #bilateralFilter(buf, width, height, sigmaS, sigmaR, kernelSize) { - const kernel = new Float32Array(kernelSize ** 2); - const sigmaS2 = -2 * sigmaS ** 2; - const halfSize = kernelSize >> 1; - for (let i = 0; i < kernelSize; i++) { - const x = (i - halfSize) ** 2; - for (let j = 0; j < kernelSize; j++) { - kernel[i * kernelSize + j] = Math.exp((x + (j - halfSize) ** 2) / sigmaS2); - } - } - const rangeValues = new Float32Array(256); - const sigmaR2 = -2 * sigmaR ** 2; - for (let i = 0; i < 256; i++) { - rangeValues[i] = Math.exp(i ** 2 / sigmaR2); - } - const N = buf.length; - const out = new Uint8Array(N); - const histogram = new Uint32Array(256); - for (let i = 0; i < height; i++) { - for (let j = 0; j < width; j++) { - const ij = i * width + j; - const center = buf[ij]; - let sum = 0; - let norm = 0; - for (let k = 0; k < kernelSize; k++) { - const y = i + k - halfSize; - if (y < 0 || y >= height) { - continue; - } - for (let l = 0; l < kernelSize; l++) { - const x = j + l - halfSize; - if (x < 0 || x >= width) { - continue; - } - const neighbour = buf[y * width + x]; - const w = kernel[k * kernelSize + l] * rangeValues[Math.abs(neighbour - center)]; - sum += neighbour * w; - norm += w; - } - } - const pix = out[ij] = Math.round(sum / norm); - histogram[pix]++; - } - } - return [out, histogram]; - } - static #getHistogram(buf) { - const histogram = new Uint32Array(256); - for (const g of buf) { - histogram[g]++; - } - return histogram; - } - static #toUint8(buf) { - const N = buf.length; - const out = new Uint8ClampedArray(N >> 2); - let max = -Infinity; - let min = Infinity; - for (let i = 0, ii = out.length; i < ii; i++) { - const pix = out[i] = buf[i << 2]; - max = Math.max(max, pix); - min = Math.min(min, pix); - } - const ratio = 255 / (max - min); - for (let i = 0, ii = out.length; i < ii; i++) { - out[i] = (out[i] - min) * ratio; - } - return out; - } - static #guessThreshold(histogram) { - let i; - let M = -Infinity; - let L = -Infinity; - const min = histogram.findIndex(v => v !== 0); - let pos = min; - let spos = min; - for (i = min; i < 256; i++) { - const v = histogram[i]; - if (v > M) { - if (i - pos > L) { - L = i - pos; - spos = i - 1; - } - M = v; - pos = i; - } - } - for (i = spos - 1; i >= 0; i--) { - if (histogram[i] > histogram[i + 1]) { - break; - } - } - return i; - } - static #getGrayPixels(bitmap) { - const originalBitmap = bitmap; - const { - width, - height - } = bitmap; - const { - maxDim - } = this.#PARAMETERS; - let newWidth = width; - let newHeight = height; - if (width > maxDim || height > maxDim) { - let prevWidth = width; - let prevHeight = height; - let steps = Math.log2(Math.max(width, height) / maxDim); - const isteps = Math.floor(steps); - steps = steps === isteps ? isteps - 1 : isteps; - for (let i = 0; i < steps; i++) { - newWidth = Math.ceil(prevWidth / 2); - newHeight = Math.ceil(prevHeight / 2); - const offscreen = new OffscreenCanvas(newWidth, newHeight); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); - prevWidth = newWidth; - prevHeight = newHeight; - if (bitmap !== originalBitmap) { - bitmap.close(); - } - bitmap = offscreen.transferToImageBitmap(); - } - const ratio = Math.min(maxDim / newWidth, maxDim / newHeight); - newWidth = Math.round(newWidth * ratio); - newHeight = Math.round(newHeight * ratio); - } - const offscreen = new OffscreenCanvas(newWidth, newHeight); - const ctx = offscreen.getContext("2d", { - willReadFrequently: true - }); - ctx.fillStyle = "white"; - ctx.fillRect(0, 0, newWidth, newHeight); - ctx.filter = "grayscale(1)"; - ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, newWidth, newHeight); - const grayImage = ctx.getImageData(0, 0, newWidth, newHeight).data; - const uint8Buf = this.#toUint8(grayImage); - return [uint8Buf, newWidth, newHeight]; - } - static extractContoursFromText(text, { - fontFamily, - fontStyle, - fontWeight - }, pageWidth, pageHeight, rotation, innerMargin) { - let canvas = new OffscreenCanvas(1, 1); - let ctx = canvas.getContext("2d", { - alpha: false - }); - const fontSize = 200; - const font = ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; - const { - actualBoundingBoxLeft, - actualBoundingBoxRight, - actualBoundingBoxAscent, - actualBoundingBoxDescent, - fontBoundingBoxAscent, - fontBoundingBoxDescent, - width - } = ctx.measureText(text); - const SCALE = 1.5; - const canvasWidth = Math.ceil(Math.max(Math.abs(actualBoundingBoxLeft) + Math.abs(actualBoundingBoxRight) || 0, width) * SCALE); - const canvasHeight = Math.ceil(Math.max(Math.abs(actualBoundingBoxAscent) + Math.abs(actualBoundingBoxDescent) || fontSize, Math.abs(fontBoundingBoxAscent) + Math.abs(fontBoundingBoxDescent) || fontSize) * SCALE); - canvas = new OffscreenCanvas(canvasWidth, canvasHeight); - ctx = canvas.getContext("2d", { - alpha: true, - willReadFrequently: true - }); - ctx.font = font; - ctx.filter = "grayscale(1)"; - ctx.fillStyle = "white"; - ctx.fillRect(0, 0, canvasWidth, canvasHeight); - ctx.fillStyle = "black"; - ctx.fillText(text, canvasWidth * (SCALE - 1) / 2, canvasHeight * (3 - SCALE) / 2); - const uint8Buf = this.#toUint8(ctx.getImageData(0, 0, canvasWidth, canvasHeight).data); - const histogram = this.#getHistogram(uint8Buf); - const threshold = this.#guessThreshold(histogram); - const contourList = this.#findContours(uint8Buf, canvasWidth, canvasHeight, threshold); - return this.processDrawnLines({ - lines: { - curves: contourList, - width: canvasWidth, - height: canvasHeight - }, - pageWidth, - pageHeight, - rotation, - innerMargin, - mustSmooth: true, - areContours: true - }); - } - static process(bitmap, pageWidth, pageHeight, rotation, innerMargin) { - const [uint8Buf, width, height] = this.#getGrayPixels(bitmap); - const [buffer, histogram] = this.#bilateralFilter(uint8Buf, width, height, Math.hypot(width, height) * this.#PARAMETERS.sigmaSFactor, this.#PARAMETERS.sigmaR, this.#PARAMETERS.kernelSize); - const threshold = this.#guessThreshold(histogram); - const contourList = this.#findContours(buffer, width, height, threshold); - return this.processDrawnLines({ - lines: { - curves: contourList, - width, - height - }, - pageWidth, - pageHeight, - rotation, - innerMargin, - mustSmooth: true, - areContours: true - }); - } - static processDrawnLines({ - lines, - pageWidth, - pageHeight, - rotation, - innerMargin, - mustSmooth, - areContours - }) { - if (rotation % 180 !== 0) { - [pageWidth, pageHeight] = [pageHeight, pageWidth]; - } - const { - curves, - width, - height - } = lines; - const thickness = lines.thickness ?? 0; - const linesAndPoints = []; - const ratio = Math.min(pageWidth / width, pageHeight / height); - const xScale = ratio / pageWidth; - const yScale = ratio / pageHeight; - const newCurves = []; - for (const { - points - } of curves) { - const reducedPoints = mustSmooth ? this.#douglasPeucker(points) : points; - if (!reducedPoints) { - continue; - } - newCurves.push(reducedPoints); - const len = reducedPoints.length; - const newPoints = new Float32Array(len); - const line = new Float32Array(3 * (len === 2 ? 2 : len - 2)); - linesAndPoints.push({ - line, - points: newPoints - }); - if (len === 2) { - newPoints[0] = reducedPoints[0] * xScale; - newPoints[1] = reducedPoints[1] * yScale; - line.set([NaN, NaN, NaN, NaN, newPoints[0], newPoints[1]], 0); - continue; - } - let [x1, y1, x2, y2] = reducedPoints; - x1 *= xScale; - y1 *= yScale; - x2 *= xScale; - y2 *= yScale; - newPoints.set([x1, y1, x2, y2], 0); - line.set([NaN, NaN, NaN, NaN, x1, y1], 0); - for (let i = 4; i < len; i += 2) { - const x = newPoints[i] = reducedPoints[i] * xScale; - const y = newPoints[i + 1] = reducedPoints[i + 1] * yScale; - line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3); - [x1, y1, x2, y2] = [x2, y2, x, y]; - } - } - if (linesAndPoints.length === 0) { - return null; - } - const outline = areContours ? new ContourDrawOutline() : new InkDrawOutline(); - outline.build(linesAndPoints, pageWidth, pageHeight, 1, rotation, areContours ? 0 : thickness, innerMargin); - return { - outline, - newCurves, - areContours, - thickness, - width, - height - }; - } - static async compressSignature({ - outlines, - areContours, - thickness, - width, - height - }) { - let minDiff = Infinity; - let maxDiff = -Infinity; - let outlinesLength = 0; - for (const points of outlines) { - outlinesLength += points.length; - for (let i = 2, ii = points.length; i < ii; i++) { - const dx = points[i] - points[i - 2]; - minDiff = Math.min(minDiff, dx); - maxDiff = Math.max(maxDiff, dx); - } - } - let bufferType; - if (minDiff >= -128 && maxDiff <= 127) { - bufferType = Int8Array; - } else if (minDiff >= -32768 && maxDiff <= 32767) { - bufferType = Int16Array; - } else { - bufferType = Int32Array; - } - const len = outlines.length; - const headerLength = BASE_HEADER_LENGTH + POINTS_PROPERTIES_NUMBER * len; - const header = new Uint32Array(headerLength); - let offset = 0; - header[offset++] = headerLength * Uint32Array.BYTES_PER_ELEMENT + (outlinesLength - 2 * len) * bufferType.BYTES_PER_ELEMENT; - header[offset++] = 0; - header[offset++] = width; - header[offset++] = height; - header[offset++] = areContours ? 0 : 1; - header[offset++] = Math.max(0, Math.floor(thickness ?? 0)); - header[offset++] = len; - header[offset++] = bufferType.BYTES_PER_ELEMENT; - for (const points of outlines) { - header[offset++] = points.length - 2; - header[offset++] = points[0]; - header[offset++] = points[1]; - } - const cs = new CompressionStream("deflate-raw"); - const writer = cs.writable.getWriter(); - await writer.ready; - writer.write(header); - const BufferCtor = bufferType.prototype.constructor; - for (const points of outlines) { - const diffs = new BufferCtor(points.length - 2); - for (let i = 2, ii = points.length; i < ii; i++) { - diffs[i - 2] = points[i] - points[i - 2]; - } - writer.write(diffs); - } - writer.close(); - const buf = await new Response(cs.readable).arrayBuffer(); - const bytes = new Uint8Array(buf); - return bytes.toBase64(); - } - static async decompressSignature(signatureData) { - try { - const bytes = Uint8Array.fromBase64(signatureData); - const { - readable, - writable - } = new DecompressionStream("deflate-raw"); - const writer = writable.getWriter(); - await writer.ready; - writer.write(bytes).then(async () => { - await writer.ready; - await writer.close(); - }).catch(() => {}); - let data = null; - let offset = 0; - for await (const chunk of readable) { - data ||= new Uint8Array(new Uint32Array(chunk.buffer, 0, 4)[0]); - data.set(chunk, offset); - offset += chunk.length; - } - const header = new Uint32Array(data.buffer, 0, data.length >> 2); - const version = header[1]; - if (version !== 0) { - throw new Error(`Invalid version: ${version}`); - } - const width = header[2]; - const height = header[3]; - const areContours = header[4] === 0; - const thickness = header[5]; - const numberOfDrawings = header[6]; - const bufferType = header[7]; - const outlines = []; - const diffsOffset = (BASE_HEADER_LENGTH + POINTS_PROPERTIES_NUMBER * numberOfDrawings) * Uint32Array.BYTES_PER_ELEMENT; - let diffs; - switch (bufferType) { - case Int8Array.BYTES_PER_ELEMENT: - diffs = new Int8Array(data.buffer, diffsOffset); - break; - case Int16Array.BYTES_PER_ELEMENT: - diffs = new Int16Array(data.buffer, diffsOffset); - break; - case Int32Array.BYTES_PER_ELEMENT: - diffs = new Int32Array(data.buffer, diffsOffset); - break; - } - offset = 0; - for (let i = 0; i < numberOfDrawings; i++) { - const len = header[POINTS_PROPERTIES_NUMBER * i + BASE_HEADER_LENGTH]; - const points = new Float32Array(len + 2); - outlines.push(points); - for (let j = 0; j < POINTS_PROPERTIES_NUMBER - 1; j++) { - points[j] = header[POINTS_PROPERTIES_NUMBER * i + BASE_HEADER_LENGTH + j + 1]; - } - for (let j = 0; j < len; j++) { - points[j + 2] = points[j] + diffs[offset++]; - } - } - return { - areContours, - thickness, - outlines, - width, - height - }; - } catch (e) { - warn(`decompressSignature: ${e}`); - return null; - } - } -} - -;// ./src/display/editor/signature.js - - - - - - - -class SignatureOptions extends DrawingOptions { - constructor() { - super(); - super.updateProperties({ - fill: AnnotationEditor._defaultLineColor, - "stroke-width": 0 - }); - } - clone() { - const clone = new SignatureOptions(); - clone.updateAll(this); - return clone; - } -} -class DrawnSignatureOptions extends InkDrawingOptions { - constructor(viewerParameters) { - super(viewerParameters); - super.updateProperties({ - stroke: AnnotationEditor._defaultLineColor, - "stroke-width": 1 - }); - } - clone() { - const clone = new DrawnSignatureOptions(this._viewParameters); - clone.updateAll(this); - return clone; - } -} -class SignatureEditor extends DrawingEditor { - #isExtracted = false; - #description = null; - #signatureData = null; - #signatureUUID = null; - static _type = "signature"; - static _editorType = AnnotationEditorType.SIGNATURE; - static _defaultDrawingOptions = null; - constructor(params) { - super({ - ...params, - mustBeCommitted: true, - name: "signatureEditor" - }); - this._willKeepAspectRatio = true; - this.#signatureData = params.signatureData || null; - this.#description = null; - this.defaultL10nId = "pdfjs-editor-signature-editor1"; - } - static initialize(l10n, uiManager) { - AnnotationEditor.initialize(l10n, uiManager); - this._defaultDrawingOptions = new SignatureOptions(); - this._defaultDrawnSignatureOptions = new DrawnSignatureOptions(uiManager.viewParameters); - } - static getDefaultDrawingOptions(options) { - const clone = this._defaultDrawingOptions.clone(); - clone.updateProperties(options); - return clone; - } - static get supportMultipleDrawings() { - return false; - } - static get typesMap() { - return shadow(this, "typesMap", new Map()); - } - static get isDrawer() { - return false; - } - get telemetryFinalData() { - return { - type: "signature", - hasDescription: !!this.#description - }; - } - static computeTelemetryFinalData(data) { - const hasDescriptionStats = data.get("hasDescription"); - return { - hasAltText: hasDescriptionStats.get(true) ?? 0, - hasNoAltText: hasDescriptionStats.get(false) ?? 0 - }; - } - get isResizable() { - return true; - } - onScaleChanging() { - if (this._drawId === null) { - return; - } - super.onScaleChanging(); - } - render() { - if (this.div) { - return this.div; - } - let baseX, baseY; - const { - _isCopy - } = this; - if (_isCopy) { - this._isCopy = false; - baseX = this.x; - baseY = this.y; - } - super.render(); - if (this._drawId === null) { - if (this.#signatureData) { - const { - lines, - mustSmooth, - areContours, - description, - uuid, - heightInPage - } = this.#signatureData; - const { - rawDims: { - pageWidth, - pageHeight - }, - rotation - } = this.parent.viewport; - const outline = SignatureExtractor.processDrawnLines({ - lines, - pageWidth, - pageHeight, - rotation, - innerMargin: SignatureEditor._INNER_MARGIN, - mustSmooth, - areContours - }); - this.addSignature(outline, heightInPage, description, uuid); - } else { - this.div.setAttribute("data-l10n-args", JSON.stringify({ - description: "" - })); - this.div.hidden = true; - this._uiManager.getSignature(this); - } - } else { - this.div.setAttribute("data-l10n-args", JSON.stringify({ - description: this.#description || "" - })); - } - if (_isCopy) { - this._isCopy = true; - this._moveAfterPaste(baseX, baseY); - } - return this.div; - } - setUuid(uuid) { - this.#signatureUUID = uuid; - this.addEditToolbar(); - } - getUuid() { - return this.#signatureUUID; - } - get description() { - return this.#description; - } - set description(description) { - this.#description = description; - if (!this.div) { - return; - } - this.div.setAttribute("data-l10n-args", JSON.stringify({ - description - })); - super.addEditToolbar().then(toolbar => { - toolbar?.updateEditSignatureButton(description); - }); - } - getSignaturePreview() { - const { - newCurves, - areContours, - thickness, - width, - height - } = this.#signatureData; - const maxDim = Math.max(width, height); - const outlineData = SignatureExtractor.processDrawnLines({ - lines: { - curves: newCurves.map(points => ({ - points - })), - thickness, - width, - height - }, - pageWidth: maxDim, - pageHeight: maxDim, - rotation: 0, - innerMargin: 0, - mustSmooth: false, - areContours - }); - return { - areContours, - outline: outlineData.outline - }; - } - get toolbarButtons() { - if (this._uiManager.signatureManager) { - return [["editSignature", this._uiManager.signatureManager]]; - } - return super.toolbarButtons; - } - addSignature(data, heightInPage, description, uuid) { - const { - x: savedX, - y: savedY - } = this; - const { - outline - } = this.#signatureData = data; - this.#isExtracted = outline instanceof ContourDrawOutline; - this.description = description; - let drawingOptions; - if (this.#isExtracted) { - drawingOptions = SignatureEditor.getDefaultDrawingOptions(); - } else { - drawingOptions = SignatureEditor._defaultDrawnSignatureOptions.clone(); - drawingOptions.updateProperties({ - "stroke-width": outline.thickness - }); - } - this._addOutlines({ - drawOutlines: outline, - drawingOptions - }); - const [, pageHeight] = this.pageDimensions; - let newHeight = heightInPage / pageHeight; - newHeight = newHeight >= 1 ? 0.5 : newHeight; - this.width *= newHeight / this.height; - if (this.width >= 1) { - newHeight *= 0.9 / this.width; - this.width = 0.9; - } - this.height = newHeight; - this.setDims(); - this.x = savedX; - this.y = savedY; - this.center(); - this._onResized(); - this.onScaleChanging(); - this.rotate(); - this._uiManager.addToAnnotationStorage(this); - this.setUuid(uuid); - this._reportTelemetry({ - action: "pdfjs.signature.inserted", - data: { - hasBeenSaved: !!uuid, - hasDescription: !!description - } - }); - this.div.hidden = false; - } - getFromImage(bitmap) { - const { - rawDims: { - pageWidth, - pageHeight - }, - rotation - } = this.parent.viewport; - return SignatureExtractor.process(bitmap, pageWidth, pageHeight, rotation, SignatureEditor._INNER_MARGIN); - } - getFromText(text, fontInfo) { - const { - rawDims: { - pageWidth, - pageHeight - }, - rotation - } = this.parent.viewport; - return SignatureExtractor.extractContoursFromText(text, fontInfo, pageWidth, pageHeight, rotation, SignatureEditor._INNER_MARGIN); - } - getDrawnSignature(curves) { - const { - rawDims: { - pageWidth, - pageHeight - }, - rotation - } = this.parent.viewport; - return SignatureExtractor.processDrawnLines({ - lines: curves, - pageWidth, - pageHeight, - rotation, - innerMargin: SignatureEditor._INNER_MARGIN, - mustSmooth: false, - areContours: false - }); - } - createDrawingOptions({ - areContours, - thickness - }) { - if (areContours) { - this._drawingOptions = SignatureEditor.getDefaultDrawingOptions(); - } else { - this._drawingOptions = SignatureEditor._defaultDrawnSignatureOptions.clone(); - this._drawingOptions.updateProperties({ - "stroke-width": thickness - }); - } - } - serialize(isForCopying = false) { - if (this.isEmpty()) { - return null; - } - const { - lines, - points - } = this.serializeDraw(isForCopying); - const { - _drawingOptions: { - "stroke-width": thickness - } - } = this; - const serialized = Object.assign(super.serialize(isForCopying), { - isSignature: true, - areContours: this.#isExtracted, - color: [0, 0, 0], - thickness: this.#isExtracted ? 0 : thickness - }); - this.addComment(serialized); - if (isForCopying) { - serialized.paths = { - lines, - points - }; - serialized.uuid = this.#signatureUUID; - serialized.isCopy = true; - } else { - serialized.lines = lines; - } - if (this.#description) { - serialized.accessibilityData = { - type: "Figure", - alt: this.#description - }; - } - return serialized; - } - static deserializeDraw(pageX, pageY, pageWidth, pageHeight, innerMargin, data) { - if (data.areContours) { - return ContourDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); - } - return InkDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); - } - static async deserialize(data, parent, uiManager) { - const editor = await super.deserialize(data, parent, uiManager); - editor.#isExtracted = data.areContours; - editor.description = data.accessibilityData?.alt || ""; - editor.#signatureUUID = data.uuid; - return editor; - } -} - -;// ./src/display/editor/stamp.js - - - - -class StampEditor extends AnnotationEditor { - #bitmap = null; - #bitmapId = null; - #bitmapPromise = null; - #bitmapUrl = null; - #bitmapFile = null; - #bitmapFileName = ""; - #canvas = null; - #missingCanvas = false; - #resizeTimeoutId = null; - #isSvg = false; - #hasBeenAddedInUndoStack = false; - static _type = "stamp"; - static _editorType = AnnotationEditorType.STAMP; - constructor(params) { - super({ - ...params, - name: "stampEditor" - }); - this.#bitmapUrl = params.bitmapUrl; - this.#bitmapFile = params.bitmapFile; - this.defaultL10nId = "pdfjs-editor-stamp-editor"; - } - static initialize(l10n, uiManager) { - AnnotationEditor.initialize(l10n, uiManager); - } - static isHandlingMimeForPasting(mime) { - return SupportedImageMimeTypes.includes(mime); - } - static paste(item, parent) { - parent.pasteEditor({ - mode: AnnotationEditorType.STAMP - }, { - bitmapFile: item.getAsFile() - }); - } - altTextFinish() { - if (this._uiManager.useNewAltTextFlow) { - this.div.hidden = false; - } - super.altTextFinish(); - } - get telemetryFinalData() { - return { - type: "stamp", - hasAltText: !!this.altTextData?.altText - }; - } - static computeTelemetryFinalData(data) { - const hasAltTextStats = data.get("hasAltText"); - return { - hasAltText: hasAltTextStats.get(true) ?? 0, - hasNoAltText: hasAltTextStats.get(false) ?? 0 - }; - } - #getBitmapFetched(data, fromId = false) { - if (!data) { - this.remove(); - return; - } - this.#bitmap = data.bitmap; - if (!fromId) { - this.#bitmapId = data.id; - this.#isSvg = data.isSvg; - } - if (data.file) { - this.#bitmapFileName = data.file.name; - } - this.#createCanvas(); - } - #getBitmapDone() { - this.#bitmapPromise = null; - this._uiManager.enableWaiting(false); - if (!this.#canvas) { - return; - } - if (this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { - this.addEditToolbar().then(() => { - this._editToolbar.hide(); - this._uiManager.editAltText(this, true); - }); - return; - } - if (!this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { - this._reportTelemetry({ - action: "pdfjs.image.image_added", - data: { - alt_text_modal: false, - alt_text_type: "empty" - } - }); - try { - this.mlGuessAltText(); - } catch {} - } - this.div.focus(); - } - async mlGuessAltText(imageData = null, updateAltTextData = true) { - if (this.hasAltTextData()) { - return null; - } - const { - mlManager - } = this._uiManager; - if (!mlManager) { - throw new Error("No ML."); - } - if (!(await mlManager.isEnabledFor("altText"))) { - throw new Error("ML isn't enabled for alt text."); - } - const { - data, - width, - height - } = imageData || this.copyCanvas(null, null, true).imageData; - const response = await mlManager.guess({ - name: "altText", - request: { - data, - width, - height, - channels: data.length / (width * height) - } - }); - if (!response) { - throw new Error("No response from the AI service."); - } - if (response.error) { - throw new Error("Error from the AI service."); - } - if (response.cancel) { - return null; - } - if (!response.output) { - throw new Error("No valid response from the AI service."); - } - const altText = response.output; - await this.setGuessedAltText(altText); - if (updateAltTextData && !this.hasAltTextData()) { - this.altTextData = { - alt: altText, - decorative: false - }; - } - return altText; - } - #getBitmap() { - if (this.#bitmapId) { - this._uiManager.enableWaiting(true); - this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone()); - return; - } - if (this.#bitmapUrl) { - const url = this.#bitmapUrl; - this.#bitmapUrl = null; - this._uiManager.enableWaiting(true); - this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); - return; - } - if (this.#bitmapFile) { - const file = this.#bitmapFile; - this.#bitmapFile = null; - this._uiManager.enableWaiting(true); - this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); - return; - } - const input = document.createElement("input"); - input.type = "file"; - input.accept = SupportedImageMimeTypes.join(","); - const signal = this._uiManager._signal; - this.#bitmapPromise = new Promise(resolve => { - input.addEventListener("change", async () => { - if (!input.files || input.files.length === 0) { - this.remove(); - } else { - this._uiManager.enableWaiting(true); - const data = await this._uiManager.imageManager.getFromFile(input.files[0]); - this._reportTelemetry({ - action: "pdfjs.image.image_selected", - data: { - alt_text_modal: this._uiManager.useNewAltTextFlow - } - }); - this.#getBitmapFetched(data); - } - resolve(); - }, { - signal - }); - input.addEventListener("cancel", () => { - this.remove(); - resolve(); - }, { - signal - }); - }).finally(() => this.#getBitmapDone()); - input.click(); - } - remove() { - if (this.#bitmapId) { - this.#bitmap = null; - this._uiManager.imageManager.deleteId(this.#bitmapId); - this.#canvas?.remove(); - this.#canvas = null; - if (this.#resizeTimeoutId) { - clearTimeout(this.#resizeTimeoutId); - this.#resizeTimeoutId = null; - } - } - super.remove(); - } - rebuild() { - if (!this.parent) { - if (this.#bitmapId) { - this.#getBitmap(); - } - return; - } - super.rebuild(); - if (this.div === null) { - return; - } - if (this.#bitmapId && this.#canvas === null) { - this.#getBitmap(); - } - if (!this.isAttachedToDOM) { - this.parent.add(this); - } - } - onceAdded(focus) { - this._isDraggable = true; - if (focus) { - this.div.focus(); - } - } - isEmpty() { - return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId || this.#missingCanvas); - } - get toolbarButtons() { - return [["altText", this.createAltText()]]; - } - get isResizable() { - return true; - } - render() { - if (this.div) { - return this.div; - } - let baseX, baseY; - if (this._isCopy) { - baseX = this.x; - baseY = this.y; - } - super.render(); - this.div.hidden = true; - this.createAltText(); - if (!this.#missingCanvas) { - if (this.#bitmap) { - this.#createCanvas(); - } else { - this.#getBitmap(); - } - } - if (this._isCopy) { - this._moveAfterPaste(baseX, baseY); - } - this._uiManager.addShouldRescale(this); - return this.div; - } - setCanvas(annotationElementId, canvas) { - const { - id: bitmapId, - bitmap - } = this._uiManager.imageManager.getFromCanvas(annotationElementId, canvas); - canvas.remove(); - if (bitmapId && this._uiManager.imageManager.isValidId(bitmapId)) { - this.#bitmapId = bitmapId; - if (bitmap) { - this.#bitmap = bitmap; - } - this.#missingCanvas = false; - this.#createCanvas(); - } - } - _onResized() { - this.onScaleChanging(); - } - onScaleChanging() { - if (!this.parent) { - return; - } - if (this.#resizeTimeoutId !== null) { - clearTimeout(this.#resizeTimeoutId); - } - const TIME_TO_WAIT = 200; - this.#resizeTimeoutId = setTimeout(() => { - this.#resizeTimeoutId = null; - this.#drawBitmap(); - }, TIME_TO_WAIT); - } - #createCanvas() { - const { - div - } = this; - let { - width, - height - } = this.#bitmap; - const [pageWidth, pageHeight] = this.pageDimensions; - const MAX_RATIO = 0.75; - if (this.width) { - width = this.width * pageWidth; - height = this.height * pageHeight; - } else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) { - const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height); - width *= factor; - height *= factor; - } - this._uiManager.enableWaiting(false); - const canvas = this.#canvas = document.createElement("canvas"); - canvas.setAttribute("role", "img"); - this.addContainer(canvas); - this.width = width / pageWidth; - this.height = height / pageHeight; - this.setDims(); - if (this._initialOptions?.isCentered) { - this.center(); - } else { - this.fixAndSetPosition(); - } - this._initialOptions = null; - if (!this._uiManager.useNewAltTextWhenAddingImage || !this._uiManager.useNewAltTextFlow || this.annotationElementId) { - div.hidden = false; - } - this.#drawBitmap(); - if (!this.#hasBeenAddedInUndoStack) { - this.parent.addUndoableEditor(this); - this.#hasBeenAddedInUndoStack = true; - } - this._reportTelemetry({ - action: "inserted_image" - }); - if (this.#bitmapFileName) { - this.div.setAttribute("aria-description", this.#bitmapFileName); - } - if (!this.annotationElementId) { - this._uiManager.a11yAlert("pdfjs-editor-stamp-added-alert"); - } - } - copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) { - if (!maxDataDimension) { - maxDataDimension = 224; - } - const { - width: bitmapWidth, - height: bitmapHeight - } = this.#bitmap; - const outputScale = new OutputScale(); - let bitmap = this.#bitmap; - let width = bitmapWidth, - height = bitmapHeight; - let canvas = null; - if (maxPreviewDimension) { - if (bitmapWidth > maxPreviewDimension || bitmapHeight > maxPreviewDimension) { - const ratio = Math.min(maxPreviewDimension / bitmapWidth, maxPreviewDimension / bitmapHeight); - width = Math.floor(bitmapWidth * ratio); - height = Math.floor(bitmapHeight * ratio); - } - canvas = document.createElement("canvas"); - const scaledWidth = canvas.width = Math.ceil(width * outputScale.sx); - const scaledHeight = canvas.height = Math.ceil(height * outputScale.sy); - if (!this.#isSvg) { - bitmap = this.#scaleBitmap(scaledWidth, scaledHeight); - } - const ctx = canvas.getContext("2d"); - ctx.filter = this._uiManager.hcmFilter; - let white = "white", - black = "#cfcfd8"; - if (this._uiManager.hcmFilter !== "none") { - black = "black"; - } else if (ColorScheme.isDarkMode) { - white = "#8f8f9d"; - black = "#42414d"; - } - const boxDim = 15; - const boxDimWidth = boxDim * outputScale.sx; - const boxDimHeight = boxDim * outputScale.sy; - const pattern = new OffscreenCanvas(boxDimWidth * 2, boxDimHeight * 2); - const patternCtx = pattern.getContext("2d"); - patternCtx.fillStyle = white; - patternCtx.fillRect(0, 0, boxDimWidth * 2, boxDimHeight * 2); - patternCtx.fillStyle = black; - patternCtx.fillRect(0, 0, boxDimWidth, boxDimHeight); - patternCtx.fillRect(boxDimWidth, boxDimHeight, boxDimWidth, boxDimHeight); - ctx.fillStyle = ctx.createPattern(pattern, "repeat"); - ctx.fillRect(0, 0, scaledWidth, scaledHeight); - ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); - } - let imageData = null; - if (createImageData) { - let dataWidth, dataHeight; - if (outputScale.symmetric && bitmap.width < maxDataDimension && bitmap.height < maxDataDimension) { - dataWidth = bitmap.width; - dataHeight = bitmap.height; - } else { - bitmap = this.#bitmap; - if (bitmapWidth > maxDataDimension || bitmapHeight > maxDataDimension) { - const ratio = Math.min(maxDataDimension / bitmapWidth, maxDataDimension / bitmapHeight); - dataWidth = Math.floor(bitmapWidth * ratio); - dataHeight = Math.floor(bitmapHeight * ratio); - if (!this.#isSvg) { - bitmap = this.#scaleBitmap(dataWidth, dataHeight); - } - } - } - const offscreen = new OffscreenCanvas(dataWidth, dataHeight); - const offscreenCtx = offscreen.getContext("2d", { - willReadFrequently: true - }); - offscreenCtx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, dataWidth, dataHeight); - imageData = { - width: dataWidth, - height: dataHeight, - data: offscreenCtx.getImageData(0, 0, dataWidth, dataHeight).data - }; - } - return { - canvas, - width, - height, - imageData - }; - } - #scaleBitmap(width, height) { - const { - width: bitmapWidth, - height: bitmapHeight - } = this.#bitmap; - let newWidth = bitmapWidth; - let newHeight = bitmapHeight; - let bitmap = this.#bitmap; - while (newWidth > 2 * width || newHeight > 2 * height) { - const prevWidth = newWidth; - const prevHeight = newHeight; - if (newWidth > 2 * width) { - newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2); - } - if (newHeight > 2 * height) { - newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2); - } - const offscreen = new OffscreenCanvas(newWidth, newHeight); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); - bitmap = offscreen.transferToImageBitmap(); - } - return bitmap; - } - #drawBitmap() { - const [parentWidth, parentHeight] = this.parentDimensions; - const { - width, - height - } = this; - const outputScale = new OutputScale(); - const scaledWidth = Math.ceil(width * parentWidth * outputScale.sx); - const scaledHeight = Math.ceil(height * parentHeight * outputScale.sy); - const canvas = this.#canvas; - if (!canvas || canvas.width === scaledWidth && canvas.height === scaledHeight) { - return; - } - canvas.width = scaledWidth; - canvas.height = scaledHeight; - const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(scaledWidth, scaledHeight); - const ctx = canvas.getContext("2d"); - ctx.filter = this._uiManager.hcmFilter; - ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); - } - #serializeBitmap(toUrl) { - if (toUrl) { - if (this.#isSvg) { - const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId); - if (url) { - return url; - } - } - const canvas = document.createElement("canvas"); - ({ - width: canvas.width, - height: canvas.height - } = this.#bitmap); - const ctx = canvas.getContext("2d"); - ctx.drawImage(this.#bitmap, 0, 0); - return canvas.toDataURL(); - } - if (this.#isSvg) { - const [pageWidth, pageHeight] = this.pageDimensions; - const width = Math.round(this.width * pageWidth * PixelsPerInch.PDF_TO_CSS_UNITS); - const height = Math.round(this.height * pageHeight * PixelsPerInch.PDF_TO_CSS_UNITS); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height); - return offscreen.transferToImageBitmap(); - } - return structuredClone(this.#bitmap); - } - static async deserialize(data, parent, uiManager) { - let initialData = null; - let missingCanvas = false; - if (data instanceof StampAnnotationElement) { - const { - data: { - rect, - rotation, - id, - structParent, - popupRef, - richText, - contentsObj, - creationDate, - modificationDate - }, - container, - parent: { - page: { - pageNumber - } - }, - canvas - } = data; - let bitmapId, bitmap; - if (canvas) { - delete data.canvas; - ({ - id: bitmapId, - bitmap - } = uiManager.imageManager.getFromCanvas(container.id, canvas)); - canvas.remove(); - } else { - missingCanvas = true; - data._hasNoCanvas = true; - } - const altText = (await parent._structTree.getAriaAttributes(`${AnnotationPrefix}${id}`))?.get("aria-label") || ""; - initialData = data = { - annotationType: AnnotationEditorType.STAMP, - bitmapId, - bitmap, - pageIndex: pageNumber - 1, - rect: rect.slice(0), - rotation, - annotationElementId: id, - id, - deleted: false, - accessibilityData: { - decorative: false, - altText - }, - isSvg: false, - structParent, - popupRef, - richText, - comment: contentsObj?.str || null, - creationDate, - modificationDate - }; - } - const editor = await super.deserialize(data, parent, uiManager); - const { - rect, - bitmap, - bitmapUrl, - bitmapId, - isSvg, - accessibilityData - } = data; - if (missingCanvas) { - uiManager.addMissingCanvas(data.id, editor); - editor.#missingCanvas = true; - } else if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { - editor.#bitmapId = bitmapId; - if (bitmap) { - editor.#bitmap = bitmap; - } - } else { - editor.#bitmapUrl = bitmapUrl; - } - editor.#isSvg = isSvg; - const [parentWidth, parentHeight] = editor.pageDimensions; - editor.width = (rect[2] - rect[0]) / parentWidth; - editor.height = (rect[3] - rect[1]) / parentHeight; - if (accessibilityData) { - editor.altTextData = accessibilityData; - } - editor._initialData = initialData; - if (data.comment) { - editor.setCommentData(data); - } - editor.#hasBeenAddedInUndoStack = !!initialData; - return editor; - } - serialize(isForCopying = false, context = null) { - if (this.isEmpty()) { - return null; - } - if (this.deleted) { - return this.serializeDeleted(); - } - const serialized = Object.assign(super.serialize(isForCopying), { - bitmapId: this.#bitmapId, - isSvg: this.#isSvg - }); - this.addComment(serialized); - if (isForCopying) { - serialized.bitmapUrl = this.#serializeBitmap(true); - serialized.accessibilityData = this.serializeAltText(true); - serialized.isCopy = true; - return serialized; - } - const { - decorative, - altText - } = this.serializeAltText(false); - if (!decorative && altText) { - serialized.accessibilityData = { - type: "Figure", - alt: altText - }; - } - if (this.annotationElementId) { - const changes = this.#hasElementChanged(serialized); - if (changes.isSame) { - return null; - } - if (changes.isSameAltText) { - delete serialized.accessibilityData; - } else { - serialized.accessibilityData.structParent = this._initialData.structParent ?? -1; - } - serialized.id = this.annotationElementId; - delete serialized.bitmapId; - return serialized; - } - if (context === null) { - return serialized; - } - context.stamps ||= new Map(); - const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null; - if (!context.stamps.has(this.#bitmapId)) { - context.stamps.set(this.#bitmapId, { - area, - serialized - }); - serialized.bitmap = this.#serializeBitmap(false); - } else if (this.#isSvg) { - const prevData = context.stamps.get(this.#bitmapId); - if (area > prevData.area) { - prevData.area = area; - prevData.serialized.bitmap.close(); - prevData.serialized.bitmap = this.#serializeBitmap(false); - } - } - return serialized; - } - #hasElementChanged(serialized) { - const { - pageIndex, - accessibilityData: { - altText - } - } = this._initialData; - const isSamePageIndex = serialized.pageIndex === pageIndex; - const isSameAltText = (serialized.accessibilityData?.alt || "") === altText; - return { - isSame: !this.hasEditedComment && !this._hasBeenMoved && !this._hasBeenResized && isSamePageIndex && isSameAltText, - isSameAltText - }; - } - renderAnnotationElement(annotation) { - if (this.deleted) { - annotation.hide(); - return null; - } - annotation.updateEdited({ - rect: this.getPDFRect(), - popup: this.comment - }); - return null; - } -} - -;// ./src/display/editor/annotation_editor_layer.js - - - - - - - - -class AnnotationEditorLayer { - #accessibilityManager; - #allowClick = false; - #annotationLayer = null; - #clickAC = null; - #editorFocusTimeoutId = null; - #editors = new Map(); - #hadPointerDown = false; - #isDisabling = false; - #isEnabling = false; - #drawingAC = null; - #focusedElement = null; - #textLayer = null; - #textSelectionAC = null; - #textLayerDblClickAC = null; - #lastPointerDownTimestamp = -1; - #uiManager; - static _initialized = false; - static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor, SignatureEditor].map(type => [type._editorType, type])); - constructor({ - uiManager, - pageIndex, - div, - structTreeLayer, - accessibilityManager, - annotationLayer, - drawLayer, - textLayer, - viewport, - l10n - }) { - const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()]; - if (!AnnotationEditorLayer._initialized) { - AnnotationEditorLayer._initialized = true; - for (const editorType of editorTypes) { - editorType.initialize(l10n, uiManager); - } - } - uiManager.registerEditorTypes(editorTypes); - this.#uiManager = uiManager; - this.pageIndex = pageIndex; - this.div = div; - this.#accessibilityManager = accessibilityManager; - this.#annotationLayer = annotationLayer; - this.viewport = viewport; - this.#textLayer = textLayer; - this.drawLayer = drawLayer; - this._structTree = structTreeLayer; - this.#uiManager.addLayer(this); - } - updatePageIndex(newPageIndex) { - for (const editor of this.#allEditorsIterator) { - editor.updatePageIndex(newPageIndex); - } - this.pageIndex = newPageIndex; - this.#uiManager.addLayer(this); - } - async setClonedFrom(clonedFrom) { - if (!clonedFrom) { - return; - } - const promises = []; - for (const editor of clonedFrom.#allEditorsIterator) { - const serialized = editor.serialize(true); - if (!serialized) { - continue; - } - serialized.isCopy = false; - promises.push(this.deserialize(serialized).then(deserialized => { - if (deserialized) { - this.addOrRebuild(deserialized); - } - })); - } - await Promise.all(promises); - } - get isEmpty() { - return this.#editors.size === 0; - } - get isInvisible() { - return this.isEmpty && this.#uiManager.getMode() === AnnotationEditorType.NONE; - } - updateToolbar(options) { - this.#uiManager.updateToolbar(options); - } - updateMode(mode = this.#uiManager.getMode()) { - this.#cleanup(); - switch (mode) { - case AnnotationEditorType.NONE: - this.div.classList.toggle("nonEditing", true); - this.disableTextSelection(); - this.togglePointerEvents(false); - this.toggleAnnotationLayerPointerEvents(true); - this.disableClick(); - return; - case AnnotationEditorType.INK: - this.disableTextSelection(); - this.togglePointerEvents(true); - this.enableClick(); - break; - case AnnotationEditorType.HIGHLIGHT: - this.enableTextSelection(); - this.togglePointerEvents(false); - this.disableClick(); - break; - default: - this.disableTextSelection(); - this.togglePointerEvents(true); - this.enableClick(); - } - this.toggleAnnotationLayerPointerEvents(false); - const { - classList - } = this.div; - classList.toggle("nonEditing", false); - if (mode === AnnotationEditorType.POPUP) { - classList.toggle("commentEditing", true); - } else { - classList.toggle("commentEditing", false); - for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { - classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType); - } - } - this.div.hidden = false; - } - hasTextLayer(textLayer) { - return textLayer === this.#textLayer?.div; - } - setEditingState(isEditing) { - this.#uiManager.setEditingState(isEditing); - } - addCommands(params) { - this.#uiManager.addCommands(params); - } - cleanUndoStack(type) { - this.#uiManager.cleanUndoStack(type); - } - toggleDrawing(enabled = false) { - this.div.classList.toggle("drawing", !enabled); - } - togglePointerEvents(enabled = false) { - this.div.classList.toggle("disabled", !enabled); - } - toggleAnnotationLayerPointerEvents(enabled = false) { - this.#annotationLayer?.togglePointerEvents(enabled); - } - get #allEditorsIterator() { - return this.#editors.size !== 0 ? this.#editors.values() : this.#uiManager.getEditors(this.pageIndex); - } - async enable() { - this.#isEnabling = true; - this.div.tabIndex = 0; - this.togglePointerEvents(true); - this.div.classList.toggle("nonEditing", false); - this.#textLayerDblClickAC?.abort(); - this.#textLayerDblClickAC = null; - const annotationElementIds = new Set(); - for (const editor of this.#allEditorsIterator) { - editor.enableEditing(); - editor.show(true); - if (editor.annotationElementId) { - this.#uiManager.removeChangedExistingAnnotation(editor); - annotationElementIds.add(editor.annotationElementId); - } - } - const annotationLayer = this.#annotationLayer; - if (annotationLayer) { - for (const editable of annotationLayer.getEditableAnnotations()) { - editable.hide(); - if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) { - continue; - } - if (annotationElementIds.has(editable.data.id)) { - continue; - } - const editor = await this.deserialize(editable); - if (!editor) { - continue; - } - this.addOrRebuild(editor); - editor.enableEditing(); - } - } - this.#isEnabling = false; - this.#uiManager._eventBus.dispatch("editorsrendered", { - source: this, - pageNumber: this.pageIndex + 1 - }); - } - disable() { - this.#isDisabling = true; - this.div.tabIndex = -1; - this.togglePointerEvents(false); - this.div.classList.toggle("nonEditing", true); - if (this.#textLayer && !this.#textLayerDblClickAC) { - this.#textLayerDblClickAC = new AbortController(); - const signal = this.#uiManager.combinedSignal(this.#textLayerDblClickAC); - this.#textLayer.div.addEventListener("pointerdown", e => { - const DBL_CLICK_THRESHOLD = 500; - const { - clientX, - clientY, - timeStamp - } = e; - const lastPointerDownTimestamp = this.#lastPointerDownTimestamp; - if (timeStamp - lastPointerDownTimestamp > DBL_CLICK_THRESHOLD) { - this.#lastPointerDownTimestamp = timeStamp; - return; - } - this.#lastPointerDownTimestamp = -1; - const { - classList - } = this.div; - classList.toggle("getElements", true); - const elements = document.elementsFromPoint(clientX, clientY); - classList.toggle("getElements", false); - if (!this.div.contains(elements[0])) { - return; - } - let id; - const regex = new RegExp(`^${AnnotationEditorPrefix}[0-9]+$`); - for (const element of elements) { - if (regex.test(element.id)) { - id = element.id; - break; - } - } - if (!id) { - return; - } - const editor = this.#editors.get(id); - if (editor?.annotationElementId === null) { - e.stopPropagation(); - e.preventDefault(); - editor.dblclick(e); - } - }, { - signal, - capture: true - }); - } - const annotationLayer = this.#annotationLayer; - const needFakeAnnotation = []; - if (annotationLayer) { - const changedAnnotations = new Map(); - const resetAnnotations = new Map(); - for (const editor of this.#allEditorsIterator) { - editor.disableEditing(); - if (!editor.annotationElementId) { - needFakeAnnotation.push(editor); - continue; - } - if (editor.serialize() !== null) { - changedAnnotations.set(editor.annotationElementId, editor); - continue; - } else { - resetAnnotations.set(editor.annotationElementId, editor); - } - this.getEditableAnnotation(editor.annotationElementId)?.show(); - editor.remove(); - } - const editables = annotationLayer.getEditableAnnotations(); - for (const editable of editables) { - const { - id - } = editable.data; - if (this.#uiManager.isDeletedAnnotationElement(id)) { - editable.updateEdited({ - deleted: true - }); - continue; - } - let editor = resetAnnotations.get(id); - if (editor) { - editor.resetAnnotationElement(editable); - editor.show(false); - editable.show(); - continue; - } - editor = changedAnnotations.get(id); - if (editor) { - this.#uiManager.addChangedExistingAnnotation(editor); - if (editor.renderAnnotationElement(editable)) { - editor.show(false); - } - } - editable.show(); - } - } - this.#cleanup(); - if (this.isEmpty) { - this.div.hidden = true; - } - const { - classList - } = this.div; - for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { - classList.remove(`${editorType._type}Editing`); - } - this.disableTextSelection(); - this.toggleAnnotationLayerPointerEvents(true); - annotationLayer?.updateFakeAnnotations(needFakeAnnotation); - this.#isDisabling = false; - } - getEditableAnnotation(id) { - return this.#annotationLayer?.getEditableAnnotation(id) || null; - } - setActiveEditor(editor) { - const currentActive = this.#uiManager.getActive(); - if (currentActive === editor) { - return; - } - this.#uiManager.setActiveEditor(editor); - } - enableTextSelection() { - this.div.tabIndex = -1; - if (this.#textLayer?.div && !this.#textSelectionAC) { - this.#textSelectionAC = new AbortController(); - const signal = this.#uiManager.combinedSignal(this.#textSelectionAC); - this.#textLayer.div.addEventListener("pointerdown", this.#textLayerPointerDown.bind(this), { - signal - }); - this.#textLayer.div.classList.add("highlighting"); - } - } - disableTextSelection() { - this.div.tabIndex = 0; - if (this.#textLayer?.div && this.#textSelectionAC) { - this.#textSelectionAC.abort(); - this.#textSelectionAC = null; - this.#textLayer.div.classList.remove("highlighting"); - } - } - #textLayerPointerDown(event) { - this.#uiManager.unselectAll(); - const { - target - } = event; - if (target === this.#textLayer.div || (target.getAttribute("role") === "img" || target.classList.contains("endOfContent")) && this.#textLayer.div.contains(target)) { - const { - isMac - } = FeatureTest.platform; - if (event.button !== 0 || event.ctrlKey && isMac) { - return; - } - this.#uiManager.showAllEditors("highlight", true, true); - this.#textLayer.div.classList.add("free"); - this.toggleDrawing(); - HighlightEditor.startHighlighting(this, this.#uiManager.direction === "ltr", { - target: this.#textLayer.div, - x: event.x, - y: event.y - }); - this.#textLayer.div.addEventListener("pointerup", () => { - this.#textLayer.div.classList.remove("free"); - this.toggleDrawing(true); - }, { - once: true, - signal: this.#uiManager._signal - }); - event.preventDefault(); - } - } - enableClick() { - if (this.#clickAC) { - return; - } - this.#clickAC = new AbortController(); - const signal = this.#uiManager.combinedSignal(this.#clickAC); - this.div.addEventListener("pointerdown", this.pointerdown.bind(this), { - signal - }); - const pointerup = this.pointerup.bind(this); - this.div.addEventListener("pointerup", pointerup, { - signal - }); - this.div.addEventListener("pointercancel", pointerup, { - signal - }); - } - disableClick() { - this.#clickAC?.abort(); - this.#clickAC = null; - } - attach(editor) { - this.#editors.set(editor.id, editor); - const { - annotationElementId - } = editor; - if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) { - this.#uiManager.removeDeletedAnnotationElement(editor); - } - } - detach(editor) { - this.#editors.delete(editor.id); - this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); - if (!this.#isDisabling && editor.annotationElementId) { - this.#uiManager.addDeletedAnnotationElement(editor); - } - } - remove(editor) { - this.detach(editor); - this.#uiManager.removeEditor(editor); - editor.div.remove(); - editor.isAttachedToDOM = false; - } - changeParent(editor) { - if (editor.parent === this) { - return; - } - if (editor.parent && editor.annotationElementId) { - this.#uiManager.addDeletedAnnotationElement(editor); - AnnotationEditor.deleteAnnotationElement(editor); - editor.annotationElementId = null; - } - this.attach(editor); - editor.parent?.detach(editor); - editor.setParent(this); - if (editor.div && editor.isAttachedToDOM) { - editor.div.remove(); - this.div.append(editor.div); - } - } - add(editor) { - if (editor.parent === this && editor.isAttachedToDOM) { - return; - } - this.changeParent(editor); - this.#uiManager.addEditor(editor); - this.attach(editor); - if (!editor.isAttachedToDOM) { - const div = editor.render(); - this.div.append(div); - editor.isAttachedToDOM = true; - } - editor.fixAndSetPosition(); - editor.onceAdded(!this.#isEnabling); - this.#uiManager.addToAnnotationStorage(editor); - editor._reportTelemetry(editor.telemetryInitialData); - } - moveEditorInDOM(editor) { - if (!editor.isAttachedToDOM) { - return; - } - const { - activeElement - } = document; - if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) { - editor._focusEventsAllowed = false; - this.#editorFocusTimeoutId = setTimeout(() => { - this.#editorFocusTimeoutId = null; - if (!editor.div.contains(document.activeElement)) { - editor.div.addEventListener("focusin", () => { - editor._focusEventsAllowed = true; - }, { - once: true, - signal: this.#uiManager._signal - }); - activeElement.focus(); - } else { - editor._focusEventsAllowed = true; - } - }, 0); - } - editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true); - } - addOrRebuild(editor) { - if (editor.needsToBeRebuilt()) { - editor.parent ||= this; - editor.rebuild(); - editor.show(); - } else { - this.add(editor); - } - } - addUndoableEditor(editor) { - const cmd = () => editor._uiManager.rebuild(editor); - const undo = () => { - editor.remove(); - }; - this.addCommands({ - cmd, - undo, - mustExec: false - }); - } - getEditorByUID(uid) { - for (const editor of this.#editors.values()) { - if (editor.uid === uid) { - return editor; - } - } - return null; - } - getNextId() { - return this.#uiManager.getId(); - } - get #currentEditorType() { - return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode()); - } - combinedSignal(ac) { - return this.#uiManager.combinedSignal(ac); - } - #createNewEditor(params) { - const editorType = this.#currentEditorType; - return editorType ? new editorType.prototype.constructor(params) : null; - } - canCreateNewEmptyEditor() { - return this.#currentEditorType?.canCreateNewEmptyEditor(); - } - async pasteEditor(options, params) { - this.updateToolbar(options); - await this.#uiManager.updateMode(options.mode); - const { - offsetX, - offsetY - } = this.#getCenterPoint(); - const id = this.getNextId(); - const editor = this.#createNewEditor({ - parent: this, - id, - x: offsetX, - y: offsetY, - uiManager: this.#uiManager, - isCentered: true, - ...params - }); - if (editor) { - this.add(editor); - } - } - async deserialize(data) { - return (await AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager)) || null; - } - createAndAddNewEditor(event, isCentered, data = {}) { - const id = this.getNextId(); - const editor = this.#createNewEditor({ - parent: this, - id, - x: event.offsetX, - y: event.offsetY, - uiManager: this.#uiManager, - isCentered, - ...data - }); - if (editor) { - this.add(editor); - } - return editor; - } - get boundingClientRect() { - return this.div.getBoundingClientRect(); - } - #getCenterPoint() { - const { - x, - y, - width, - height - } = this.boundingClientRect; - const tlX = Math.max(0, x); - const tlY = Math.max(0, y); - const brX = Math.min(window.innerWidth, x + width); - const brY = Math.min(window.innerHeight, y + height); - const centerX = (tlX + brX) / 2 - x; - const centerY = (tlY + brY) / 2 - y; - const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX]; - return { - offsetX, - offsetY - }; - } - addNewEditor(data = {}) { - this.createAndAddNewEditor(this.#getCenterPoint(), true, data); - } - setSelected(editor) { - this.#uiManager.setSelected(editor); - } - toggleSelected(editor) { - this.#uiManager.toggleSelected(editor); - } - unselect(editor) { - this.#uiManager.unselect(editor); - } - pointerup(event) { - const { - isMac - } = FeatureTest.platform; - if (event.button !== 0 || event.ctrlKey && isMac) { - return; - } - if (event.target !== this.div) { - return; - } - if (!this.#hadPointerDown) { - return; - } - this.#hadPointerDown = false; - if (this.#currentEditorType?.isDrawer && this.#currentEditorType.supportMultipleDrawings) { - return; - } - if (!this.#allowClick) { - this.#allowClick = true; - return; - } - const currentMode = this.#uiManager.getMode(); - if (currentMode === AnnotationEditorType.STAMP || currentMode === AnnotationEditorType.SIGNATURE) { - this.#uiManager.unselectAll(); - return; - } - this.createAndAddNewEditor(event, false); - } - pointerdown(event) { - if (this.#uiManager.getMode() === AnnotationEditorType.HIGHLIGHT) { - this.enableTextSelection(); - } - if (this.#hadPointerDown) { - this.#hadPointerDown = false; - return; - } - const { - isMac - } = FeatureTest.platform; - if (event.button !== 0 || event.ctrlKey && isMac) { - return; - } - if (event.target !== this.div) { - return; - } - this.#hadPointerDown = true; - if (this.#currentEditorType?.isDrawer) { - this.startDrawingSession(event); - return; - } - const editor = this.#uiManager.getActive(); - this.#allowClick = !editor || editor.isEmpty(); - } - startDrawingSession(event) { - this.div.focus({ - preventScroll: true - }); - if (this.#drawingAC) { - this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); - return; - } - this.#uiManager.setCurrentDrawingSession(this); - this.#drawingAC = new AbortController(); - const signal = this.#uiManager.combinedSignal(this.#drawingAC); - this.div.addEventListener("blur", ({ - relatedTarget - }) => { - if (relatedTarget && !this.div.contains(relatedTarget)) { - this.#focusedElement = null; - this.commitOrRemove(); - } - }, { - signal - }); - this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); - } - pause(on) { - if (on) { - const { - activeElement - } = document; - if (this.div.contains(activeElement)) { - this.#focusedElement = activeElement; - } - return; - } - if (this.#focusedElement) { - setTimeout(() => { - this.#focusedElement?.focus(); - this.#focusedElement = null; - }, 0); - } - } - endDrawingSession(isAborted = false) { - if (!this.#drawingAC) { - return null; - } - this.#uiManager.setCurrentDrawingSession(null); - this.#drawingAC.abort(); - this.#drawingAC = null; - this.#focusedElement = null; - return this.#currentEditorType.endDrawing(isAborted); - } - findNewParent(editor, x, y) { - const layer = this.#uiManager.findParent(x, y); - if (layer === null || layer === this) { - return false; - } - layer.changeParent(editor); - return true; - } - commitOrRemove() { - if (this.#drawingAC) { - this.endDrawingSession(); - return true; - } - return false; - } - onScaleChanging() { - if (!this.#drawingAC) { - return; - } - this.#currentEditorType.onScaleChangingWhenDrawing(this); - } - destroy() { - this.commitOrRemove(); - if (this.#uiManager.getActive()?.parent === this) { - this.#uiManager.commitOrRemove(); - this.#uiManager.setActiveEditor(null); - } - if (this.#editorFocusTimeoutId) { - clearTimeout(this.#editorFocusTimeoutId); - this.#editorFocusTimeoutId = null; - } - for (const editor of this.#editors.values()) { - this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); - editor.setParent(null); - editor.isAttachedToDOM = false; - editor.div.remove(); - } - this.div = null; - this.#editors.clear(); - this.#uiManager.removeLayer(this); - } - #cleanup() { - for (const editor of this.#editors.values()) { - if (editor.isEmpty()) { - editor.remove(); - } - } - } - render({ - viewport - }) { - this.viewport = viewport; - setLayerDimensions(this.div, viewport); - for (const editor of this.#uiManager.getEditors(this.pageIndex)) { - this.add(editor); - editor.rebuild(); - } - this.updateMode(); - } - update({ - viewport - }) { - this.#uiManager.commitOrRemove(); - this.#cleanup(); - const oldRotation = this.viewport.rotation; - const rotation = viewport.rotation; - this.viewport = viewport; - setLayerDimensions(this.div, { - rotation - }); - if (oldRotation !== rotation) { - for (const editor of this.#editors.values()) { - editor.rotate(rotation); - } - } - } - get pageDimensions() { - const { - pageWidth, - pageHeight - } = this.viewport.rawDims; - return [pageWidth, pageHeight]; - } - get scale() { - return this.#uiManager.viewParameters.realScale; - } -} - -;// ./src/display/draw_layer.js - - -class DrawLayer { - #parent = null; - #mapping = new Map(); - #toUpdate = new Map(); - static #id = 0; - setParent(parent) { - if (!this.#parent) { - this.#parent = parent; - return; - } - if (this.#parent !== parent) { - if (this.#mapping.size > 0) { - for (const root of this.#mapping.values()) { - root.remove(); - parent.append(root); - } - } - this.#parent = parent; - } - } - static get _svgFactory() { - return shadow(this, "_svgFactory", new DOMSVGFactory()); - } - static #setBox(element, [x, y, width, height]) { - const { - style - } = element; - style.top = `${100 * y}%`; - style.left = `${100 * x}%`; - style.width = `${100 * width}%`; - style.height = `${100 * height}%`; - } - #createSVG() { - const svg = DrawLayer._svgFactory.create(1, 1, true); - this.#parent.append(svg); - svg.setAttribute("aria-hidden", true); - return svg; - } - #createClipPath(defs, pathId) { - const clipPath = DrawLayer._svgFactory.createElement("clipPath"); - defs.append(clipPath); - const clipPathId = `clip_${pathId}`; - clipPath.setAttribute("id", clipPathId); - clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); - const clipPathUse = DrawLayer._svgFactory.createElement("use"); - clipPath.append(clipPathUse); - clipPathUse.setAttribute("href", `#${pathId}`); - clipPathUse.classList.add("clip"); - return clipPathId; - } - #updateProperties(element, properties) { - for (const [key, value] of Object.entries(properties)) { - if (value === null) { - element.removeAttribute(key); - } else { - element.setAttribute(key, value); - } - } - } - draw(properties, isPathUpdatable = false, hasClip = false) { - const id = DrawLayer.#id++; - const root = this.#createSVG(); - const defs = DrawLayer._svgFactory.createElement("defs"); - root.append(defs); - const path = DrawLayer._svgFactory.createElement("path"); - defs.append(path); - const pathId = `path_${id}`; - path.setAttribute("id", pathId); - path.setAttribute("vector-effect", "non-scaling-stroke"); - if (isPathUpdatable) { - this.#toUpdate.set(id, path); - } - const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null; - const use = DrawLayer._svgFactory.createElement("use"); - root.append(use); - use.setAttribute("href", `#${pathId}`); - this.updateProperties(root, properties); - this.#mapping.set(id, root); - return { - id, - clipPathId: `url(#${clipPathId})` - }; - } - drawOutline(properties, mustRemoveSelfIntersections) { - const id = DrawLayer.#id++; - const root = this.#createSVG(); - const defs = DrawLayer._svgFactory.createElement("defs"); - root.append(defs); - const path = DrawLayer._svgFactory.createElement("path"); - defs.append(path); - const pathId = `path_${id}`; - path.setAttribute("id", pathId); - path.setAttribute("vector-effect", "non-scaling-stroke"); - let maskId; - if (mustRemoveSelfIntersections) { - const mask = DrawLayer._svgFactory.createElement("mask"); - defs.append(mask); - maskId = `mask_${id}`; - mask.setAttribute("id", maskId); - mask.setAttribute("maskUnits", "objectBoundingBox"); - const rect = DrawLayer._svgFactory.createElement("rect"); - mask.append(rect); - rect.setAttribute("width", "1"); - rect.setAttribute("height", "1"); - rect.setAttribute("fill", "white"); - const use = DrawLayer._svgFactory.createElement("use"); - mask.append(use); - use.setAttribute("href", `#${pathId}`); - use.setAttribute("stroke", "none"); - use.setAttribute("fill", "black"); - use.setAttribute("fill-rule", "nonzero"); - use.classList.add("mask"); - } - const use1 = DrawLayer._svgFactory.createElement("use"); - root.append(use1); - use1.setAttribute("href", `#${pathId}`); - if (maskId) { - use1.setAttribute("mask", `url(#${maskId})`); - } - const use2 = use1.cloneNode(); - root.append(use2); - use1.classList.add("mainOutline"); - use2.classList.add("secondaryOutline"); - this.updateProperties(root, properties); - this.#mapping.set(id, root); - return id; - } - finalizeDraw(id, properties) { - this.#toUpdate.delete(id); - this.updateProperties(id, properties); - } - updateProperties(elementOrId, properties) { - if (!properties) { - return; - } - const { - root, - bbox, - rootClass, - path - } = properties; - const element = typeof elementOrId === "number" ? this.#mapping.get(elementOrId) : elementOrId; - if (!element) { - return; - } - if (root) { - this.#updateProperties(element, root); - } - if (bbox) { - DrawLayer.#setBox(element, bbox); - } - if (rootClass) { - const { - classList - } = element; - for (const [className, value] of Object.entries(rootClass)) { - classList.toggle(className, value); - } - } - if (path) { - const defs = element.firstElementChild; - const pathElement = defs.firstElementChild; - this.#updateProperties(pathElement, path); - } - } - updateParent(id, layer) { - if (layer === this) { - return; - } - const root = this.#mapping.get(id); - if (!root) { - return; - } - layer.#parent.append(root); - this.#mapping.delete(id); - layer.#mapping.set(id, root); - } - remove(id) { - this.#toUpdate.delete(id); - if (this.#parent === null) { - return; - } - this.#mapping.get(id).remove(); - this.#mapping.delete(id); - } - destroy() { - this.#parent = null; - for (const root of this.#mapping.values()) { - root.remove(); - } - this.#mapping.clear(); - this.#toUpdate.clear(); - } -} - -;// ./src/pdf.js - - - - - - - - - - - - - - - - -{ - globalThis._pdfjsTestingUtils = { - HighlightOutliner: HighlightOutliner - }; -} -globalThis.pdfjsLib = { - AbortException: AbortException, - AnnotationEditorLayer: AnnotationEditorLayer, - AnnotationEditorParamsType: AnnotationEditorParamsType, - AnnotationEditorType: AnnotationEditorType, - AnnotationEditorUIManager: AnnotationEditorUIManager, - AnnotationLayer: AnnotationLayer, - AnnotationMode: AnnotationMode, - AnnotationType: AnnotationType, - applyOpacity: applyOpacity, - build: build, - ColorPicker: ColorPicker, - createValidAbsoluteUrl: createValidAbsoluteUrl, - CSSConstants: CSSConstants, - DOMSVGFactory: DOMSVGFactory, - DrawLayer: DrawLayer, - FeatureTest: FeatureTest, - fetchData: fetchData, - findContrastColor: findContrastColor, - getDocument: getDocument, - getFilenameFromUrl: getFilenameFromUrl, - getPdfFilenameFromUrl: getPdfFilenameFromUrl, - getRGB: getRGB, - getUuid: getUuid, - getXfaPageViewport: getXfaPageViewport, - GlobalWorkerOptions: GlobalWorkerOptions, - ImageKind: ImageKind, - InvalidPDFException: InvalidPDFException, - isDataScheme: isDataScheme, - isPdfFile: isPdfFile, - isValidExplicitDest: isValidExplicitDest, - makeArr: makeArr, - makeMap: makeMap, - makeObj: makeObj, - MathClamp: MathClamp, - noContextMenu: noContextMenu, - normalizeUnicode: normalizeUnicode, - OPS: OPS, - OutputScale: OutputScale, - PasswordResponses: PasswordResponses, - PDFDataRangeTransport: PDFDataRangeTransport, - PDFDateString: PDFDateString, - PDFWorker: PDFWorker, - PermissionFlag: PermissionFlag, - PixelsPerInch: PixelsPerInch, - RenderingCancelledException: RenderingCancelledException, - renderRichText: renderRichText, - ResponseException: ResponseException, - setLayerDimensions: setLayerDimensions, - shadow: shadow, - SignatureExtractor: SignatureExtractor, - stopEvent: stopEvent, - SupportedImageMimeTypes: SupportedImageMimeTypes, - TextLayer: TextLayer, - TouchManager: TouchManager, - updateUrlHash: updateUrlHash, - Util: Util, - VerbosityLevel: VerbosityLevel, - version: version, - XfaLayer: XfaLayer -}; - -export { AbortException, AnnotationEditorLayer, AnnotationEditorParamsType, AnnotationEditorType, AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, AnnotationType, CSSConstants, ColorPicker, DOMSVGFactory, DrawLayer, FeatureTest, GlobalWorkerOptions, ImageKind, InvalidPDFException, MathClamp, OPS, OutputScale, PDFDataRangeTransport, PDFDateString, PDFWorker, PasswordResponses, PermissionFlag, PixelsPerInch, RenderingCancelledException, ResponseException, SignatureExtractor, SupportedImageMimeTypes, TextLayer, TouchManager, Util, VerbosityLevel, XfaLayer, applyOpacity, build, createValidAbsoluteUrl, fetchData, findContrastColor, getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, getRGB, getUuid, getXfaPageViewport, isDataScheme, isPdfFile, isValidExplicitDest, makeArr, makeMap, makeObj, noContextMenu, normalizeUnicode, renderRichText, setLayerDimensions, shadow, stopEvent, updateUrlHash, version }; - -//# sourceMappingURL=pdf.mjs.map
\ No newline at end of file |
