vendor: 5,331 bytes, lines 1-130
1/** 2 * @licstart The following is the entire license notice for the 3 * JavaScript code in this page 4 * 5 * Copyright 2024 Mozilla Foundation 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 * @licend The above is the entire license notice for the 20 * JavaScript code in this page 21 */ 22 23/******/ // The require scope 24/******/ var __webpack_require__ = {}; 25/******/ 26/************************************************************************/ 27/******/ /* webpack/runtime/define property getters */ 28/******/ (() => { 29/******/ // define getter functions for harmony exports 30/******/ __webpack_require__.d = (exports, definition) => { 31/******/ for(var key in definition) { 32/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 33/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 34/******/ } 35/******/ } 36/******/ }; 37/******/ })(); 38/******/ 39/******/ /* webpack/runtime/hasOwnProperty shorthand */ 40/******/ (() => { 41/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 42/******/ })(); 43/******/ 44/************************************************************************/ 45var __webpack_exports__ = globalThis.pdfjsLib = {}; 46 47// EXPORTS 48__webpack_require__.d(__webpack_exports__, { 49 AbortException: () => (/* reexport */ AbortException), 50 AnnotationEditorLayer: () => (/* reexport */ AnnotationEditorLayer), 51 AnnotationEditorParamsType: () => (/* reexport */ AnnotationEditorParamsType), 52 AnnotationEditorType: () => (/* reexport */ AnnotationEditorType), 53 AnnotationEditorUIManager: () => (/* reexport */ AnnotationEditorUIManager), 54 AnnotationLayer: () => (/* reexport */ AnnotationLayer), 55 AnnotationMode: () => (/* reexport */ AnnotationMode), 56 ColorPicker: () => (/* reexport */ ColorPicker), 57 DOMSVGFactory: () => (/* reexport */ DOMSVGFactory), 58 DrawLayer: () => (/* reexport */ DrawLayer), 59 FeatureTest: () => (/* reexport */ util_FeatureTest), 60 GlobalWorkerOptions: () => (/* reexport */ GlobalWorkerOptions), 61 ImageKind: () => (/* reexport */ util_ImageKind), 62 InvalidPDFException: () => (/* reexport */ InvalidPDFException), 63 MissingPDFException: () => (/* reexport */ MissingPDFException), 64 OPS: () => (/* reexport */ OPS), 65 OutputScale: () => (/* reexport */ OutputScale), 66 PDFDataRangeTransport: () => (/* reexport */ PDFDataRangeTransport), 67 PDFDateString: () => (/* reexport */ PDFDateString), 68 PDFWorker: () => (/* reexport */ PDFWorker), 69 PasswordResponses: () => (/* reexport */ PasswordResponses), 70 PermissionFlag: () => (/* reexport */ PermissionFlag), 71 PixelsPerInch: () => (/* reexport */ PixelsPerInch), 72 RenderingCancelledException: () => (/* reexport */ RenderingCancelledException), 73 TextLayer: () => (/* reexport */ TextLayer), 74 UnexpectedResponseException: () => (/* reexport */ UnexpectedResponseException), 75 Util: () => (/* reexport */ Util), 76 VerbosityLevel: () => (/* reexport */ VerbosityLevel), 77 XfaLayer: () => (/* reexport */ XfaLayer), 78 build: () => (/* reexport */ build), 79 createValidAbsoluteUrl: () => (/* reexport */ createValidAbsoluteUrl), 80 fetchData: () => (/* reexport */ fetchData), 81 getDocument: () => (/* reexport */ getDocument), 82 getFilenameFromUrl: () => (/* reexport */ getFilenameFromUrl), 83 getPdfFilenameFromUrl: () => (/* reexport */ getPdfFilenameFromUrl), 84 getXfaPageViewport: () => (/* reexport */ getXfaPageViewport), 85 isDataScheme: () => (/* reexport */ isDataScheme), 86 isPdfFile: () => (/* reexport */ isPdfFile), 87 noContextMenu: () => (/* reexport */ noContextMenu), 88 normalizeUnicode: () => (/* reexport */ normalizeUnicode), 89 setLayerDimensions: () => (/* reexport */ setLayerDimensions), 90 shadow: () => (/* reexport */ shadow), 91 version: () => (/* reexport */ version) 92}); 93 94;// ./src/shared/util.js 95const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); 96const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; 97const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; 98const MAX_IMAGE_SIZE_TO_CACHE = 10e6; 99const LINE_FACTOR = 1.35; 100const LINE_DESCENT_FACTOR = 0.35; 101const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR; 102const RenderingIntentFlag = { 103 ANY: 0x01, 104 DISPLAY: 0x02, 105 PRINT: 0x04, 106 SAVE: 0x08, 107 ANNOTATIONS_FORMS: 0x10, 108 ANNOTATIONS_STORAGE: 0x20, 109 ANNOTATIONS_DISABLE: 0x40, 110 IS_EDITING: 0x80, 111 OPLIST: 0x100 112}; 113const AnnotationMode = { 114 DISABLE: 0, 115 ENABLE: 1, 116 ENABLE_FORMS: 2, 117 ENABLE_STORAGE: 3 118}; 119const AnnotationEditorPrefix = "pdfjs_internal_editor_"; 120const AnnotationEditorType = { 121 DISABLE: -1, 122 NONE: 0, 123 FREETEXT: 3, 124 HIGHLIGHT: 9, 125 STAMP: 13, 126 INK: 15 127}; 128const AnnotationEditorParamsType = { 129 RESIZE: 1, 130 CREATE: 2,
131 FREETEXT_SIZE: 11, 132 FREETEXT_COLOR: 12, 133 FREETEXT_OPACITY: 13, 134 INK_COLOR: 21, 135 INK_THICKNESS: 22, 136 INK_OPACITY: 23, 137 HIGHLIGHT_COLOR: 31, 138 HIGHLIGHT_DEFAULT_COLOR: 32, 139 HIGHLIGHT_THICKNESS: 33, 140 HIGHLIGHT_FREE: 34, 141 HIGHLIGHT_SHOW_ALL: 35 142}; 143const PermissionFlag = { 144 PRINT: 0x04, 145 MODIFY_CONTENTS: 0x08, 146 COPY: 0x10, 147 MODIFY_ANNOTATIONS: 0x20, 148 FILL_INTERACTIVE_FORMS: 0x100, 149 COPY_FOR_ACCESSIBILITY: 0x200, 150 ASSEMBLE: 0x400, 151 PRINT_HIGH_QUALITY: 0x800 152}; 153const TextRenderingMode = { 154 FILL: 0, 155 STROKE: 1, 156 FILL_STROKE: 2, 157 INVISIBLE: 3, 158 FILL_ADD_TO_PATH: 4, 159 STROKE_ADD_TO_PATH: 5, 160 FILL_STROKE_ADD_TO_PATH: 6, 161 ADD_TO_PATH: 7, 162 FILL_STROKE_MASK: 3, 163 ADD_TO_PATH_FLAG: 4 164}; 165const util_ImageKind = { 166 GRAYSCALE_1BPP: 1, 167 RGB_24BPP: 2, 168 RGBA_32BPP: 3 169}; 170const AnnotationType = { 171 TEXT: 1, 172 LINK: 2, 173 FREETEXT: 3, 174 LINE: 4, 175 SQUARE: 5, 176 CIRCLE: 6, 177 POLYGON: 7, 178 POLYLINE: 8, 179 HIGHLIGHT: 9, 180 UNDERLINE: 10, 181 SQUIGGLY: 11, 182 STRIKEOUT: 12, 183 STAMP: 13, 184 CARET: 14, 185 INK: 15, 186 POPUP: 16, 187 FILEATTACHMENT: 17, 188 SOUND: 18, 189 MOVIE: 19, 190 WIDGET: 20, 191 SCREEN: 21, 192 PRINTERMARK: 22, 193 TRAPNET: 23, 194 WATERMARK: 24, 195 THREED: 25, 196 REDACT: 26 197}; 198const AnnotationReplyType = { 199 GROUP: "Group", 200 REPLY: "R" 201}; 202const AnnotationFlag = { 203 INVISIBLE: 0x01, 204 HIDDEN: 0x02, 205 PRINT: 0x04, 206 NOZOOM: 0x08, 207 NOROTATE: 0x10, 208 NOVIEW: 0x20, 209 READONLY: 0x40, 210 LOCKED: 0x80, 211 TOGGLENOVIEW: 0x100, 212 LOCKEDCONTENTS: 0x200 213}; 214const AnnotationFieldFlag = { 215 READONLY: 0x0000001, 216 REQUIRED: 0x0000002, 217 NOEXPORT: 0x0000004, 218 MULTILINE: 0x0001000, 219 PASSWORD: 0x0002000, 220 NOTOGGLETOOFF: 0x0004000, 221 RADIO: 0x0008000, 222 PUSHBUTTON: 0x0010000, 223 COMBO: 0x0020000, 224 EDIT: 0x0040000, 225 SORT: 0x0080000, 226 FILESELECT: 0x0100000, 227 MULTISELECT: 0x0200000, 228 DONOTSPELLCHECK: 0x0400000, 229 DONOTSCROLL: 0x0800000, 230 COMB: 0x1000000, 231 RICHTEXT: 0x2000000, 232 RADIOSINUNISON: 0x2000000, 233 COMMITONSELCHANGE: 0x4000000 234}; 235const AnnotationBorderStyleType = { 236 SOLID: 1, 237 DASHED: 2, 238 BEVELED: 3, 239 INSET: 4, 240 UNDERLINE: 5 241}; 242const AnnotationActionEventType = { 243 E: "Mouse Enter", 244 X: "Mouse Exit", 245 D: "Mouse Down", 246 U: "Mouse Up", 247 Fo: "Focus", 248 Bl: "Blur", 249 PO: "PageOpen", 250 PC: "PageClose", 251 PV: "PageVisible", 252 PI: "PageInvisible", 253 K: "Keystroke", 254 F: "Format", 255 V: "Validate", 256 C: "Calculate" 257}; 258const DocumentActionEventType = { 259 WC: "WillClose", 260 WS: "WillSave", 261 DS: "DidSave", 262 WP: "WillPrint", 263 DP: "DidPrint" 264}; 265const PageActionEventType = { 266 O: "PageOpen", 267 C: "PageClose" 268}; 269const VerbosityLevel = { 270 ERRORS: 0, 271 WARNINGS: 1, 272 INFOS: 5 273}; 274const OPS = { 275 dependency: 1, 276 setLineWidth: 2, 277 setLineCap: 3, 278 setLineJoin: 4, 279 setMiterLimit: 5, 280 setDash: 6, 281 setRenderingIntent: 7, 282 setFlatness: 8, 283 setGState: 9, 284 save: 10, 285 restore: 11, 286 transform: 12, 287 moveTo: 13, 288 lineTo: 14, 289 curveTo: 15, 290 curveTo2: 16,
vendor: 5,342 bytes, lines 291-514
291 curveTo3: 17, 292 closePath: 18, 293 rectangle: 19, 294 stroke: 20, 295 closeStroke: 21, 296 fill: 22, 297 eoFill: 23, 298 fillStroke: 24, 299 eoFillStroke: 25, 300 closeFillStroke: 26, 301 closeEOFillStroke: 27, 302 endPath: 28, 303 clip: 29, 304 eoClip: 30, 305 beginText: 31, 306 endText: 32, 307 setCharSpacing: 33, 308 setWordSpacing: 34, 309 setHScale: 35, 310 setLeading: 36, 311 setFont: 37, 312 setTextRenderingMode: 38, 313 setTextRise: 39, 314 moveText: 40, 315 setLeadingMoveText: 41, 316 setTextMatrix: 42, 317 nextLine: 43, 318 showText: 44, 319 showSpacedText: 45, 320 nextLineShowText: 46, 321 nextLineSetSpacingShowText: 47, 322 setCharWidth: 48, 323 setCharWidthAndBounds: 49, 324 setStrokeColorSpace: 50, 325 setFillColorSpace: 51, 326 setStrokeColor: 52, 327 setStrokeColorN: 53, 328 setFillColor: 54, 329 setFillColorN: 55, 330 setStrokeGray: 56, 331 setFillGray: 57, 332 setStrokeRGBColor: 58, 333 setFillRGBColor: 59, 334 setStrokeCMYKColor: 60, 335 setFillCMYKColor: 61, 336 shadingFill: 62, 337 beginInlineImage: 63, 338 beginImageData: 64, 339 endInlineImage: 65, 340 paintXObject: 66, 341 markPoint: 67, 342 markPointProps: 68, 343 beginMarkedContent: 69, 344 beginMarkedContentProps: 70, 345 endMarkedContent: 71, 346 beginCompat: 72, 347 endCompat: 73, 348 paintFormXObjectBegin: 74, 349 paintFormXObjectEnd: 75, 350 beginGroup: 76, 351 endGroup: 77, 352 beginAnnotation: 80, 353 endAnnotation: 81, 354 paintImageMaskXObject: 83, 355 paintImageMaskXObjectGroup: 84, 356 paintImageXObject: 85, 357 paintInlineImageXObject: 86, 358 paintInlineImageXObjectGroup: 87, 359 paintImageXObjectRepeat: 88, 360 paintImageMaskXObjectRepeat: 89, 361 paintSolidColorImageMask: 90, 362 constructPath: 91, 363 setStrokeTransparent: 92, 364 setFillTransparent: 93 365}; 366const PasswordResponses = { 367 NEED_PASSWORD: 1, 368 INCORRECT_PASSWORD: 2 369}; 370let verbosity = VerbosityLevel.WARNINGS; 371function setVerbosityLevel(level) { 372 if (Number.isInteger(level)) { 373 verbosity = level; 374 } 375} 376function getVerbosityLevel() { 377 return verbosity; 378} 379function info(msg) { 380 if (verbosity >= VerbosityLevel.INFOS) { 381 console.log(`Info: ${msg}`); 382 } 383} 384function warn(msg) { 385 if (verbosity >= VerbosityLevel.WARNINGS) { 386 console.log(`Warning: ${msg}`); 387 } 388} 389function unreachable(msg) { 390 throw new Error(msg); 391} 392function assert(cond, msg) { 393 if (!cond) { 394 unreachable(msg); 395 } 396} 397function _isValidProtocol(url) { 398 switch (url?.protocol) { 399 case "http:": 400 case "https:": 401 case "ftp:": 402 case "mailto:": 403 case "tel:": 404 return true; 405 default: 406 return false; 407 } 408} 409function createValidAbsoluteUrl(url, baseUrl = null, options = null) { 410 if (!url) { 411 return null; 412 } 413 try { 414 if (options && typeof url === "string") { 415 if (options.addDefaultProtocol && url.startsWith("www.")) { 416 const dots = url.match(/\./g); 417 if (dots?.length >= 2) { 418 url = `http://${url}`; 419 } 420 } 421 if (options.tryConvertEncoding) { 422 try { 423 url = stringToUTF8String(url); 424 } catch {} 425 } 426 } 427 const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); 428 if (_isValidProtocol(absoluteUrl)) { 429 return absoluteUrl; 430 } 431 } catch {} 432 return null; 433} 434function shadow(obj, prop, value, nonSerializable = false) { 435 Object.defineProperty(obj, prop, { 436 value, 437 enumerable: !nonSerializable, 438 configurable: true, 439 writable: false 440 }); 441 return value; 442} 443const BaseException = function BaseExceptionClosure() { 444 function BaseException(message, name) { 445 this.message = message; 446 this.name = name; 447 } 448 BaseException.prototype = new Error(); 449 BaseException.constructor = BaseException; 450 return BaseException; 451}(); 452class PasswordException extends BaseException { 453 constructor(msg, code) { 454 super(msg, "PasswordException"); 455 this.code = code; 456 } 457} 458class UnknownErrorException extends BaseException { 459 constructor(msg, details) { 460 super(msg, "UnknownErrorException"); 461 this.details = details; 462 } 463} 464class InvalidPDFException extends BaseException { 465 constructor(msg) { 466 super(msg, "InvalidPDFException"); 467 } 468} 469class MissingPDFException extends BaseException { 470 constructor(msg) { 471 super(msg, "MissingPDFException"); 472 } 473} 474class UnexpectedResponseException extends BaseException { 475 constructor(msg, status) { 476 super(msg, "UnexpectedResponseException"); 477 this.status = status; 478 } 479} 480class FormatError extends BaseException { 481 constructor(msg) { 482 super(msg, "FormatError"); 483 } 484} 485class AbortException extends BaseException { 486 constructor(msg) { 487 super(msg, "AbortException"); 488 } 489} 490function bytesToString(bytes) { 491 if (typeof bytes !== "object" || bytes?.length === undefined) { 492 unreachable("Invalid argument for bytesToString"); 493 } 494 const length = bytes.length; 495 const MAX_ARGUMENT_COUNT = 8192; 496 if (length < MAX_ARGUMENT_COUNT) { 497 return String.fromCharCode.apply(null, bytes); 498 } 499 const strBuf = []; 500 for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) { 501 const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); 502 const chunk = bytes.subarray(i, chunkEnd); 503 strBuf.push(String.fromCharCode.apply(null, chunk)); 504 } 505 return strBuf.join(""); 506} 507function stringToBytes(str) { 508 if (typeof str !== "string") { 509 unreachable("Invalid argument for stringToBytes"); 510 } 511 const length = str.length; 512 const bytes = new Uint8Array(length); 513 for (let i = 0; i < length; ++i) { 514 bytes[i] = str.charCodeAt(i) & 0xff;
515 } 516 return bytes; 517} 518function string32(value) { 519 return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff); 520} 521function objectSize(obj) { 522 return Object.keys(obj).length; 523} 524function objectFromMap(map) { 525 const obj = Object.create(null); 526 for (const [key, value] of map) { 527 obj[key] = value; 528 } 529 return obj; 530} 531function isLittleEndian() { 532 const buffer8 = new Uint8Array(4); 533 buffer8[0] = 1; 534 const view32 = new Uint32Array(buffer8.buffer, 0, 1); 535 return view32[0] === 1; 536} 537function isEvalSupported() { 538 // Odoo: don't support scripting 539 return false; 540} 541class util_FeatureTest { 542 static get isLittleEndian() { 543 return shadow(this, "isLittleEndian", isLittleEndian()); 544 } 545 static get isEvalSupported() { 546 return shadow(this, "isEvalSupported", isEvalSupported()); 547 } 548 static get isOffscreenCanvasSupported() { 549 return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined"); 550 } 551 static get platform() { 552 if (typeof navigator !== "undefined" && typeof navigator?.platform === "string") { 553 return shadow(this, "platform", { 554 isMac: navigator.platform.includes("Mac"), 555 isWindows: navigator.platform.includes("Win"), 556 isFirefox: typeof navigator?.userAgent === "string" && navigator.userAgent.includes("Firefox") 557 }); 558 } 559 return shadow(this, "platform", { 560 isMac: false, 561 isWindows: false, 562 isFirefox: false 563 }); 564 } 565 static get isCSSRoundSupported() { 566 return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)")); 567 } 568} 569const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0")); 570class Util { 571 static makeHexColor(r, g, b) { 572 return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; 573 } 574 static scaleMinMax(transform, minMax) { 575 let temp; 576 if (transform[0]) { 577 if (transform[0] < 0) { 578 temp = minMax[0]; 579 minMax[0] = minMax[2]; 580 minMax[2] = temp; 581 } 582 minMax[0] *= transform[0]; 583 minMax[2] *= transform[0]; 584 if (transform[3] < 0) { 585 temp = minMax[1]; 586 minMax[1] = minMax[3]; 587 minMax[3] = temp; 588 } 589 minMax[1] *= transform[3]; 590 minMax[3] *= transform[3]; 591 } else { 592 temp = minMax[0]; 593 minMax[0] = minMax[1]; 594 minMax[1] = temp; 595 temp = minMax[2]; 596 minMax[2] = minMax[3]; 597 minMax[3] = temp; 598 if (transform[1] < 0) { 599 temp = minMax[1]; 600 minMax[1] = minMax[3]; 601 minMax[3] = temp; 602 } 603 minMax[1] *= transform[1]; 604 minMax[3] *= transform[1]; 605 if (transform[2] < 0) { 606 temp = minMax[0]; 607 minMax[0] = minMax[2]; 608 minMax[2] = temp; 609 } 610 minMax[0] *= transform[2]; 611 minMax[2] *= transform[2]; 612 } 613 minMax[0] += transform[4]; 614 minMax[1] += transform[5]; 615 minMax[2] += transform[4]; 616 minMax[3] += transform[5]; 617 } 618 static transform(m1, m2) { 619 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]]; 620 } 621 static applyTransform(p, m) { 622 const xt = p[0] * m[0] + p[1] * m[2] + m[4]; 623 const yt = p[0] * m[1] + p[1] * m[3] + m[5]; 624 return [xt, yt]; 625 } 626 static applyInverseTransform(p, m) { 627 const d = m[0] * m[3] - m[1] * m[2]; 628 const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; 629 const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; 630 return [xt, yt]; 631 } 632 static getAxialAlignedBoundingBox(r, m) { 633 const p1 = this.applyTransform(r, m); 634 const p2 = this.applyTransform(r.slice(2, 4), m); 635 const p3 = this.applyTransform([r[0], r[3]], m); 636 const p4 = this.applyTransform([r[2], r[1]], m); 637 return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])]; 638 } 639 static inverseTransform(m) { 640 const d = m[0] * m[3] - m[1] * m[2]; 641 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]; 642 } 643 static singularValueDecompose2dScale(m) { 644 const transpose = [m[0], m[2], m[1], m[3]]; 645 const a = m[0] * transpose[0] + m[1] * transpose[2]; 646 const b = m[0] * transpose[1] + m[1] * transpose[3]; 647 const c = m[2] * transpose[0] + m[3] * transpose[2]; 648 const d = m[2] * transpose[1] + m[3] * transpose[3]; 649 const first = (a + d) / 2; 650 const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2; 651 const sx = first + second || 1; 652 const sy = first - second || 1; 653 return [Math.sqrt(sx), Math.sqrt(sy)]; 654 } 655 static normalizeRect(rect) { 656 const r = rect.slice(0); 657 if (rect[0] > rect[2]) { 658 r[0] = rect[2]; 659 r[2] = rect[0]; 660 } 661 if (rect[1] > rect[3]) { 662 r[1] = rect[3]; 663 r[3] = rect[1]; 664 } 665 return r; 666 } 667 static intersect(rect1, rect2) { 668 const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2])); 669 const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2])); 670 if (xLow > xHigh) { 671 return null; 672 } 673 const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3])); 674 const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3])); 675 if (yLow > yHigh) { 676 return null; 677 } 678 return [xLow, yLow, xHigh, yHigh]; 679 } 680 static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) { 681 if (t <= 0 || t >= 1) { 682 return; 683 } 684 const mt = 1 - t; 685 const tt = t * t; 686 const ttt = tt * t; 687 const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3; 688 const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3; 689 minMax[0] = Math.min(minMax[0], x); 690 minMax[1] = Math.min(minMax[1], y); 691 minMax[2] = Math.max(minMax[2], x); 692 minMax[3] = Math.max(minMax[3], y); 693 } 694 static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) { 695 if (Math.abs(a) < 1e-12) { 696 if (Math.abs(b) >= 1e-12) { 697 this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax); 698 } 699 return; 700 } 701 const delta = b ** 2 - 4 * c * a; 702 if (delta < 0) { 703 return; 704 } 705 const sqrtDelta = Math.sqrt(delta); 706 const a2 = 2 * a; 707 this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax); 708 this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax); 709 } 710 static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) { 711 if (minMax) { 712 minMax[0] = Math.min(minMax[0], x0, x3); 713 minMax[1] = Math.min(minMax[1], y0, y3); 714 minMax[2] = Math.max(minMax[2], x0, x3); 715 minMax[3] = Math.max(minMax[3], y0, y3); 716 } else { 717 minMax = [Math.min(x0, x3), Math.min(y0, y3), Math.max(x0, x3), Math.max(y0, y3)]; 718 } 719 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); 720 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); 721 return minMax; 722 } 723} 724const 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])); 725function stringToPDFString(str) { 726 if (str[0] >= "\xEF") { 727 let encoding; 728 if (str[0] === "\xFE" && str[1] === "\xFF") { 729 encoding = "utf-16be"; 730 if (str.length % 2 === 1) { 731 str = str.slice(0, -1); 732 } 733 } else if (str[0] === "\xFF" && str[1] === "\xFE") { 734 encoding = "utf-16le"; 735 if (str.length % 2 === 1) { 736 str = str.slice(0, -1); 737 } 738 } else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") { 739 encoding = "utf-8"; 740 } 741 if (encoding) {
742 try { 743 const decoder = new TextDecoder(encoding, { 744 fatal: true 745 }); 746 const buffer = stringToBytes(str); 747 const decoded = decoder.decode(buffer); 748 if (!decoded.includes("\x1b")) { 749 return decoded; 750 } 751 return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, ""); 752 } catch (ex) { 753 warn(`stringToPDFString: "${ex}".`); 754 } 755 } 756 } 757 const strBuf = []; 758 for (let i = 0, ii = str.length; i < ii; i++) { 759 const charCode = str.charCodeAt(i); 760 if (charCode === 0x1b) { 761 while (++i < ii && str.charCodeAt(i) !== 0x1b) {} 762 continue; 763 } 764 const code = PDFStringTranslateTable[charCode]; 765 strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); 766 } 767 return strBuf.join(""); 768} 769function stringToUTF8String(str) { 770 return decodeURIComponent(escape(str)); 771} 772function utf8StringToString(str) { 773 return unescape(encodeURIComponent(str)); 774} 775function isArrayEqual(arr1, arr2) { 776 if (arr1.length !== arr2.length) { 777 return false; 778 } 779 for (let i = 0, ii = arr1.length; i < ii; i++) { 780 if (arr1[i] !== arr2[i]) { 781 return false; 782 } 783 } 784 return true; 785} 786function getModificationDate(date = new Date()) { 787 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")]; 788 return buffer.join(""); 789} 790let NormalizeRegex = null; 791let NormalizationMap = null; 792function normalizeUnicode(str) { 793 if (!NormalizeRegex) { 794 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; 795 NormalizationMap = new Map([["ſt", "ſt"]]); 796 } 797 return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2)); 798} 799function getUuid() { 800 if (typeof crypto !== "undefined" && typeof crypto?.randomUUID === "function") { 801 return crypto.randomUUID(); 802 } 803 const buf = new Uint8Array(32); 804 if (typeof crypto !== "undefined" && typeof crypto?.getRandomValues === "function") { 805 crypto.getRandomValues(buf); 806 } else { 807 for (let i = 0; i < 32; i++) { 808 buf[i] = Math.floor(Math.random() * 255); 809 } 810 } 811 return bytesToString(buf); 812} 813const AnnotationPrefix = "pdfjs_internal_id_"; 814const FontRenderOps = { 815 BEZIER_CURVE_TO: 0, 816 MOVE_TO: 1, 817 LINE_TO: 2, 818 QUADRATIC_CURVE_TO: 3, 819 RESTORE: 4, 820 SAVE: 5, 821 SCALE: 6, 822 TRANSFORM: 7, 823 TRANSLATE: 8 824}; 825function toHexUtil(arr) { 826 if (Uint8Array.prototype.toHex) { 827 return arr.toHex(); 828 } 829 return Array.from(arr, num => hexNumbers[num]).join(""); 830} 831function toBase64Util(arr) { 832 if (Uint8Array.prototype.toBase64) { 833 return arr.toBase64(); 834 } 835 return btoa(bytesToString(arr)); 836} 837function fromBase64Util(str) { 838 if (Uint8Array.fromBase64) { 839 return Uint8Array.fromBase64(str); 840 } 841 return stringToBytes(atob(str)); 842} 843 844;// ./src/display/display_utils.js 845 846const SVG_NS = "http://www.w3.org/2000/svg"; 847class PixelsPerInch { 848 static CSS = 96.0; 849 static PDF = 72.0; 850 static PDF_TO_CSS_UNITS = this.CSS / this.PDF; 851} 852async function fetchData(url, type = "text") { 853 if (isValidFetchUrl(url, document.baseURI)) { 854 const response = await fetch(url); 855 if (!response.ok) { 856 throw new Error(response.statusText); 857 } 858 switch (type) { 859 case "arraybuffer": 860 return response.arrayBuffer(); 861 case "blob": 862 return response.blob(); 863 case "json": 864 return response.json(); 865 } 866 return response.text(); 867 } 868 return new Promise((resolve, reject) => { 869 const request = new XMLHttpRequest(); 870 request.open("GET", url, true); 871 request.responseType = type; 872 request.onreadystatechange = () => { 873 if (request.readyState !== XMLHttpRequest.DONE) { 874 return; 875 } 876 if (request.status === 200 || request.status === 0) { 877 switch (type) { 878 case "arraybuffer": 879 case "blob": 880 case "json": 881 resolve(request.response); 882 return; 883 } 884 resolve(request.responseText); 885 return; 886 } 887 reject(new Error(request.statusText)); 888 }; 889 request.send(null); 890 }); 891} 892class PageViewport { 893 constructor({
vendor: 4,278 bytes, lines 894-1036
894 viewBox, 895 scale, 896 rotation, 897 offsetX = 0, 898 offsetY = 0, 899 dontFlip = false 900 }) { 901 this.viewBox = viewBox; 902 this.scale = scale; 903 this.rotation = rotation; 904 this.offsetX = offsetX; 905 this.offsetY = offsetY; 906 const centerX = (viewBox[2] + viewBox[0]) / 2; 907 const centerY = (viewBox[3] + viewBox[1]) / 2; 908 let rotateA, rotateB, rotateC, rotateD; 909 rotation %= 360; 910 if (rotation < 0) { 911 rotation += 360; 912 } 913 switch (rotation) { 914 case 180: 915 rotateA = -1; 916 rotateB = 0; 917 rotateC = 0; 918 rotateD = 1; 919 break; 920 case 90: 921 rotateA = 0; 922 rotateB = 1; 923 rotateC = 1; 924 rotateD = 0; 925 break; 926 case 270: 927 rotateA = 0; 928 rotateB = -1; 929 rotateC = -1; 930 rotateD = 0; 931 break; 932 case 0: 933 rotateA = 1; 934 rotateB = 0; 935 rotateC = 0; 936 rotateD = -1; 937 break; 938 default: 939 throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees."); 940 } 941 if (dontFlip) { 942 rotateC = -rotateC; 943 rotateD = -rotateD; 944 } 945 let offsetCanvasX, offsetCanvasY; 946 let width, height; 947 if (rotateA === 0) { 948 offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; 949 offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; 950 width = (viewBox[3] - viewBox[1]) * scale; 951 height = (viewBox[2] - viewBox[0]) * scale; 952 } else { 953 offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; 954 offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; 955 width = (viewBox[2] - viewBox[0]) * scale; 956 height = (viewBox[3] - viewBox[1]) * scale; 957 } 958 this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY]; 959 this.width = width; 960 this.height = height; 961 } 962 get rawDims() { 963 const { 964 viewBox 965 } = this; 966 return shadow(this, "rawDims", { 967 pageWidth: viewBox[2] - viewBox[0], 968 pageHeight: viewBox[3] - viewBox[1], 969 pageX: viewBox[0], 970 pageY: viewBox[1] 971 }); 972 } 973 clone({ 974 scale = this.scale, 975 rotation = this.rotation, 976 offsetX = this.offsetX, 977 offsetY = this.offsetY, 978 dontFlip = false 979 } = {}) { 980 return new PageViewport({ 981 viewBox: this.viewBox.slice(), 982 scale, 983 rotation, 984 offsetX, 985 offsetY, 986 dontFlip 987 }); 988 } 989 convertToViewportPoint(x, y) { 990 return Util.applyTransform([x, y], this.transform); 991 } 992 convertToViewportRectangle(rect) { 993 const topLeft = Util.applyTransform([rect[0], rect[1]], this.transform); 994 const bottomRight = Util.applyTransform([rect[2], rect[3]], this.transform); 995 return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]]; 996 } 997 convertToPdfPoint(x, y) { 998 return Util.applyInverseTransform([x, y], this.transform); 999 } 1000} 1001class RenderingCancelledException extends BaseException { 1002 constructor(msg, extraDelay = 0) { 1003 super(msg, "RenderingCancelledException"); 1004 this.extraDelay = extraDelay; 1005 } 1006} 1007function isDataScheme(url) { 1008 const ii = url.length; 1009 let i = 0; 1010 while (i < ii && url[i].trim() === "") { 1011 i++; 1012 } 1013 return url.substring(i, i + 5).toLowerCase() === "data:"; 1014} 1015function isPdfFile(filename) { 1016 return typeof filename === "string" && /\.pdf$/i.test(filename); 1017} 1018function getFilenameFromUrl(url) { 1019 [url] = url.split(/[#?]/, 1); 1020 return url.substring(url.lastIndexOf("/") + 1); 1021} 1022function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") { 1023 if (typeof url !== "string") { 1024 return defaultFilename; 1025 } 1026 if (isDataScheme(url)) { 1027 warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.'); 1028 return defaultFilename; 1029 } 1030 const reURI = /^(?:(?:[^:]+:)?\/\/[^/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/; 1031 const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i; 1032 const splitURI = reURI.exec(url); 1033 let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]); 1034 if (suggestedFilename) { 1035 suggestedFilename = suggestedFilename[0]; 1036 if (suggestedFilename.includes("%")) {
1037 try { 1038 suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0]; 1039 } catch {} 1040 } 1041 } 1042 return suggestedFilename || defaultFilename; 1043} 1044class StatTimer { 1045 started = Object.create(null); 1046 times = []; 1047 time(name) { 1048 if (name in this.started) { 1049 warn(`Timer is already running for ${name}`); 1050 } 1051 this.started[name] = Date.now(); 1052 } 1053 timeEnd(name) { 1054 if (!(name in this.started)) { 1055 warn(`Timer has not been started for ${name}`); 1056 } 1057 this.times.push({ 1058 name, 1059 start: this.started[name], 1060 end: Date.now() 1061 }); 1062 delete this.started[name]; 1063 } 1064 toString() { 1065 const outBuf = []; 1066 let longest = 0; 1067 for (const { 1068 name 1069 } of this.times) { 1070 longest = Math.max(name.length, longest); 1071 } 1072 for (const { 1073 name, 1074 start, 1075 end 1076 } of this.times) { 1077 outBuf.push(`${name.padEnd(longest)} ${end - start}ms\n`); 1078 } 1079 return outBuf.join(""); 1080 } 1081} 1082function isValidFetchUrl(url, baseUrl) { 1083 try { 1084 const { 1085 protocol 1086 } = baseUrl ? new URL(url, baseUrl) : new URL(url); 1087 return protocol === "http:" || protocol === "https:"; 1088 } catch { 1089 return false; 1090 } 1091} 1092function noContextMenu(e) { 1093 e.preventDefault(); 1094} 1095function deprecated(details) { 1096 console.log("Deprecated API usage: " + details); 1097} 1098class PDFDateString { 1099 static #regex; 1100 static toDateObject(input) { 1101 if (!input || typeof input !== "string") { 1102 return null; 1103 } 1104 this.#regex ||= new RegExp("^D:" + "(\\d{4})" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "([Z|+|-])?" + "(\\d{2})?" + "'?" + "(\\d{2})?" + "'?"); 1105 const matches = this.#regex.exec(input); 1106 if (!matches) { 1107 return null; 1108 } 1109 const year = parseInt(matches[1], 10); 1110 let month = parseInt(matches[2], 10);
vendor: 4,468 bytes, lines 1111-1263
1111 month = month >= 1 && month <= 12 ? month - 1 : 0; 1112 let day = parseInt(matches[3], 10); 1113 day = day >= 1 && day <= 31 ? day : 1; 1114 let hour = parseInt(matches[4], 10); 1115 hour = hour >= 0 && hour <= 23 ? hour : 0; 1116 let minute = parseInt(matches[5], 10); 1117 minute = minute >= 0 && minute <= 59 ? minute : 0; 1118 let second = parseInt(matches[6], 10); 1119 second = second >= 0 && second <= 59 ? second : 0; 1120 const universalTimeRelation = matches[7] || "Z"; 1121 let offsetHour = parseInt(matches[8], 10); 1122 offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0; 1123 let offsetMinute = parseInt(matches[9], 10) || 0; 1124 offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0; 1125 if (universalTimeRelation === "-") { 1126 hour += offsetHour; 1127 minute += offsetMinute; 1128 } else if (universalTimeRelation === "+") { 1129 hour -= offsetHour; 1130 minute -= offsetMinute; 1131 } 1132 return new Date(Date.UTC(year, month, day, hour, minute, second)); 1133 } 1134} 1135function getXfaPageViewport(xfaPage, { 1136 scale = 1, 1137 rotation = 0 1138}) { 1139 const { 1140 width, 1141 height 1142 } = xfaPage.attributes.style; 1143 const viewBox = [0, 0, parseInt(width), parseInt(height)]; 1144 return new PageViewport({ 1145 viewBox, 1146 scale, 1147 rotation 1148 }); 1149} 1150function getRGB(color) { 1151 if (color.startsWith("#")) { 1152 const colorRGB = parseInt(color.slice(1), 16); 1153 return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff]; 1154 } 1155 if (color.startsWith("rgb(")) { 1156 return color.slice(4, -1).split(",").map(x => parseInt(x)); 1157 } 1158 if (color.startsWith("rgba(")) { 1159 return color.slice(5, -1).split(",").map(x => parseInt(x)).slice(0, 3); 1160 } 1161 warn(`Not a valid color format: "${color}"`); 1162 return [0, 0, 0]; 1163} 1164function getColorValues(colors) { 1165 const span = document.createElement("span"); 1166 span.style.visibility = "hidden"; 1167 document.body.append(span); 1168 for (const name of colors.keys()) { 1169 span.style.color = name; 1170 const computedColor = window.getComputedStyle(span).color; 1171 colors.set(name, getRGB(computedColor)); 1172 } 1173 span.remove(); 1174} 1175function getCurrentTransform(ctx) { 1176 const { 1177 a, 1178 b, 1179 c, 1180 d, 1181 e, 1182 f 1183 } = ctx.getTransform(); 1184 return [a, b, c, d, e, f]; 1185} 1186function getCurrentTransformInverse(ctx) { 1187 const { 1188 a, 1189 b, 1190 c, 1191 d, 1192 e, 1193 f 1194 } = ctx.getTransform().invertSelf(); 1195 return [a, b, c, d, e, f]; 1196} 1197function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) { 1198 if (viewport instanceof PageViewport) { 1199 const { 1200 pageWidth, 1201 pageHeight 1202 } = viewport.rawDims; 1203 const { 1204 style 1205 } = div; 1206 const useRound = util_FeatureTest.isCSSRoundSupported; 1207 const w = `var(--scale-factor) * ${pageWidth}px`, 1208 h = `var(--scale-factor) * ${pageHeight}px`; 1209 const widthStr = useRound ? `round(down, ${w}, var(--scale-round-x, 1px))` : `calc(${w})`, 1210 heightStr = useRound ? `round(down, ${h}, var(--scale-round-y, 1px))` : `calc(${h})`; 1211 if (!mustFlip || viewport.rotation % 180 === 0) { 1212 style.width = widthStr; 1213 style.height = heightStr; 1214 } else { 1215 style.width = heightStr; 1216 style.height = widthStr; 1217 } 1218 } 1219 if (mustRotate) { 1220 div.setAttribute("data-main-rotation", viewport.rotation); 1221 } 1222} 1223class OutputScale { 1224 constructor() { 1225 const pixelRatio = window.devicePixelRatio || 1; 1226 this.sx = pixelRatio; 1227 this.sy = pixelRatio; 1228 } 1229 get scaled() { 1230 return this.sx !== 1 || this.sy !== 1; 1231 } 1232 get symmetric() { 1233 return this.sx === this.sy; 1234 } 1235} 1236 1237;// ./src/display/editor/toolbar.js 1238 1239class EditorToolbar { 1240 #toolbar = null; 1241 #colorPicker = null; 1242 #editor; 1243 #buttons = null; 1244 #altText = null; 1245 static #l10nRemove = null; 1246 constructor(editor) { 1247 this.#editor = editor; 1248 EditorToolbar.#l10nRemove ||= Object.freeze({ 1249 freetext: "pdfjs-editor-remove-freetext-button", 1250 highlight: "pdfjs-editor-remove-highlight-button", 1251 ink: "pdfjs-editor-remove-ink-button", 1252 stamp: "pdfjs-editor-remove-stamp-button" 1253 }); 1254 } 1255 render() { 1256 const editToolbar = this.#toolbar = document.createElement("div"); 1257 editToolbar.classList.add("editToolbar", "hidden"); 1258 editToolbar.setAttribute("role", "toolbar"); 1259 const signal = this.#editor._uiManager._signal; 1260 editToolbar.addEventListener("contextmenu", noContextMenu, { 1261 signal 1262 }); 1263 editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, {
vendor: 11,500 bytes, lines 1264-1701
1264 signal 1265 }); 1266 const buttons = this.#buttons = document.createElement("div"); 1267 buttons.className = "buttons"; 1268 editToolbar.append(buttons); 1269 const position = this.#editor.toolbarPosition; 1270 if (position) { 1271 const { 1272 style 1273 } = editToolbar; 1274 const x = this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0]; 1275 style.insetInlineEnd = `${100 * x}%`; 1276 style.top = `calc(${100 * position[1]}% + var(--editor-toolbar-vert-offset))`; 1277 } 1278 this.#addDeleteButton(); 1279 return editToolbar; 1280 } 1281 get div() { 1282 return this.#toolbar; 1283 } 1284 static #pointerDown(e) { 1285 e.stopPropagation(); 1286 } 1287 #focusIn(e) { 1288 this.#editor._focusEventsAllowed = false; 1289 e.preventDefault(); 1290 e.stopPropagation(); 1291 } 1292 #focusOut(e) { 1293 this.#editor._focusEventsAllowed = true; 1294 e.preventDefault(); 1295 e.stopPropagation(); 1296 } 1297 #addListenersToElement(element) { 1298 const signal = this.#editor._uiManager._signal; 1299 element.addEventListener("focusin", this.#focusIn.bind(this), { 1300 capture: true, 1301 signal 1302 }); 1303 element.addEventListener("focusout", this.#focusOut.bind(this), { 1304 capture: true, 1305 signal 1306 }); 1307 element.addEventListener("contextmenu", noContextMenu, { 1308 signal 1309 }); 1310 } 1311 hide() { 1312 this.#toolbar.classList.add("hidden"); 1313 this.#colorPicker?.hideDropdown(); 1314 } 1315 show() { 1316 this.#toolbar.classList.remove("hidden"); 1317 this.#altText?.shown(); 1318 } 1319 #addDeleteButton() { 1320 const { 1321 editorType, 1322 _uiManager 1323 } = this.#editor; 1324 const button = document.createElement("button"); 1325 button.className = "delete"; 1326 button.tabIndex = 0; 1327 button.setAttribute("data-l10n-id", EditorToolbar.#l10nRemove[editorType]); 1328 this.#addListenersToElement(button); 1329 button.addEventListener("click", e => { 1330 _uiManager.delete(); 1331 }, { 1332 signal: _uiManager._signal 1333 }); 1334 this.#buttons.append(button); 1335 } 1336 get #divider() { 1337 const divider = document.createElement("div"); 1338 divider.className = "divider"; 1339 return divider; 1340 } 1341 async addAltText(altText) { 1342 const button = await altText.render(); 1343 this.#addListenersToElement(button); 1344 this.#buttons.prepend(button, this.#divider); 1345 this.#altText = altText; 1346 } 1347 addColorPicker(colorPicker) { 1348 this.#colorPicker = colorPicker; 1349 const button = colorPicker.renderButton(); 1350 this.#addListenersToElement(button); 1351 this.#buttons.prepend(button, this.#divider); 1352 } 1353 remove() { 1354 this.#toolbar.remove(); 1355 this.#colorPicker?.destroy(); 1356 this.#colorPicker = null; 1357 } 1358} 1359class HighlightToolbar { 1360 #buttons = null; 1361 #toolbar = null; 1362 #uiManager; 1363 constructor(uiManager) { 1364 this.#uiManager = uiManager; 1365 } 1366 #render() { 1367 const editToolbar = this.#toolbar = document.createElement("div"); 1368 editToolbar.className = "editToolbar"; 1369 editToolbar.setAttribute("role", "toolbar"); 1370 editToolbar.addEventListener("contextmenu", noContextMenu, { 1371 signal: this.#uiManager._signal 1372 }); 1373 const buttons = this.#buttons = document.createElement("div"); 1374 buttons.className = "buttons"; 1375 editToolbar.append(buttons); 1376 this.#addHighlightButton(); 1377 return editToolbar; 1378 } 1379 #getLastPoint(boxes, isLTR) { 1380 let lastY = 0; 1381 let lastX = 0; 1382 for (const box of boxes) { 1383 const y = box.y + box.height; 1384 if (y < lastY) { 1385 continue; 1386 } 1387 const x = box.x + (isLTR ? box.width : 0); 1388 if (y > lastY) { 1389 lastX = x; 1390 lastY = y; 1391 continue; 1392 } 1393 if (isLTR) { 1394 if (x > lastX) { 1395 lastX = x; 1396 } 1397 } else if (x < lastX) { 1398 lastX = x; 1399 } 1400 } 1401 return [isLTR ? 1 - lastX : lastX, lastY]; 1402 } 1403 show(parent, boxes, isLTR) { 1404 const [x, y] = this.#getLastPoint(boxes, isLTR); 1405 const { 1406 style 1407 } = this.#toolbar ||= this.#render(); 1408 parent.append(this.#toolbar); 1409 style.insetInlineEnd = `${100 * x}%`; 1410 style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`; 1411 } 1412 hide() { 1413 this.#toolbar.remove(); 1414 } 1415 #addHighlightButton() { 1416 const button = document.createElement("button"); 1417 button.className = "highlightButton"; 1418 button.tabIndex = 0; 1419 button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button1`); 1420 const span = document.createElement("span"); 1421 button.append(span); 1422 span.className = "visuallyHidden"; 1423 span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label"); 1424 const signal = this.#uiManager._signal; 1425 button.addEventListener("contextmenu", noContextMenu, { 1426 signal 1427 }); 1428 button.addEventListener("click", () => { 1429 this.#uiManager.highlightSelection("floating_button"); 1430 }, { 1431 signal 1432 }); 1433 this.#buttons.append(button); 1434 } 1435} 1436 1437;// ./src/display/editor/tools.js 1438 1439 1440 1441function bindEvents(obj, element, names) { 1442 for (const name of names) { 1443 element.addEventListener(name, obj[name].bind(obj)); 1444 } 1445} 1446function opacityToHex(opacity) { 1447 return Math.round(Math.min(255, Math.max(1, 255 * opacity))).toString(16).padStart(2, "0"); 1448} 1449class IdManager { 1450 #id = 0; 1451 get id() { 1452 return `${AnnotationEditorPrefix}${this.#id++}`; 1453 } 1454} 1455class ImageManager { 1456 #baseId = getUuid(); 1457 #id = 0; 1458 #cache = null; 1459 static get _isSVGFittingCanvas() { 1460 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>`; 1461 const canvas = new OffscreenCanvas(1, 3); 1462 const ctx = canvas.getContext("2d", { 1463 willReadFrequently: true 1464 }); 1465 const image = new Image(); 1466 image.src = svg; 1467 const promise = image.decode().then(() => { 1468 ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3); 1469 return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0; 1470 }); 1471 return shadow(this, "_isSVGFittingCanvas", promise); 1472 } 1473 async #get(key, rawData) { 1474 this.#cache ||= new Map(); 1475 let data = this.#cache.get(key); 1476 if (data === null) { 1477 return null; 1478 } 1479 if (data?.bitmap) { 1480 data.refCounter += 1; 1481 return data; 1482 } 1483 try { 1484 data ||= { 1485 bitmap: null, 1486 id: `image_${this.#baseId}_${this.#id++}`, 1487 refCounter: 0, 1488 isSvg: false 1489 }; 1490 let image; 1491 if (typeof rawData === "string") { 1492 data.url = rawData; 1493 image = await fetchData(rawData, "blob"); 1494 } else if (rawData instanceof File) { 1495 image = data.file = rawData; 1496 } else if (rawData instanceof Blob) { 1497 image = rawData; 1498 } 1499 if (image.type === "image/svg+xml") { 1500 const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas; 1501 const fileReader = new FileReader(); 1502 const imageElement = new Image(); 1503 const imagePromise = new Promise((resolve, reject) => { 1504 imageElement.onload = () => { 1505 data.bitmap = imageElement; 1506 data.isSvg = true; 1507 resolve(); 1508 }; 1509 fileReader.onload = async () => { 1510 const url = data.svgUrl = fileReader.result; 1511 imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url; 1512 }; 1513 imageElement.onerror = fileReader.onerror = reject; 1514 }); 1515 fileReader.readAsDataURL(image); 1516 await imagePromise; 1517 } else { 1518 data.bitmap = await createImageBitmap(image); 1519 } 1520 data.refCounter = 1; 1521 } catch (e) { 1522 console.error(e); 1523 data = null; 1524 } 1525 this.#cache.set(key, data); 1526 if (data) { 1527 this.#cache.set(data.id, data); 1528 } 1529 return data; 1530 } 1531 async getFromFile(file) { 1532 const { 1533 lastModified, 1534 name, 1535 size, 1536 type 1537 } = file; 1538 return this.#get(`${lastModified}_${name}_${size}_${type}`, file); 1539 } 1540 async getFromUrl(url) { 1541 return this.#get(url, url); 1542 } 1543 async getFromBlob(id, blobPromise) { 1544 const blob = await blobPromise; 1545 return this.#get(id, blob); 1546 } 1547 async getFromId(id) { 1548 this.#cache ||= new Map(); 1549 const data = this.#cache.get(id); 1550 if (!data) { 1551 return null; 1552 } 1553 if (data.bitmap) { 1554 data.refCounter += 1; 1555 return data; 1556 } 1557 if (data.file) { 1558 return this.getFromFile(data.file); 1559 } 1560 if (data.blobPromise) { 1561 const { 1562 blobPromise 1563 } = data; 1564 delete data.blobPromise; 1565 return this.getFromBlob(data.id, blobPromise); 1566 } 1567 return this.getFromUrl(data.url); 1568 } 1569 getFromCanvas(id, canvas) { 1570 this.#cache ||= new Map(); 1571 let data = this.#cache.get(id); 1572 if (data?.bitmap) { 1573 data.refCounter += 1; 1574 return data; 1575 } 1576 const offscreen = new OffscreenCanvas(canvas.width, canvas.height); 1577 const ctx = offscreen.getContext("2d"); 1578 ctx.drawImage(canvas, 0, 0); 1579 data = { 1580 bitmap: offscreen.transferToImageBitmap(), 1581 id: `image_${this.#baseId}_${this.#id++}`, 1582 refCounter: 1, 1583 isSvg: false 1584 }; 1585 this.#cache.set(id, data); 1586 this.#cache.set(data.id, data); 1587 return data; 1588 } 1589 getSvgUrl(id) { 1590 const data = this.#cache.get(id); 1591 if (!data?.isSvg) { 1592 return null; 1593 } 1594 return data.svgUrl; 1595 } 1596 deleteId(id) { 1597 this.#cache ||= new Map(); 1598 const data = this.#cache.get(id); 1599 if (!data) { 1600 return; 1601 } 1602 data.refCounter -= 1; 1603 if (data.refCounter !== 0) { 1604 return; 1605 } 1606 const { 1607 bitmap 1608 } = data; 1609 if (!data.url && !data.file) { 1610 const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); 1611 const ctx = canvas.getContext("bitmaprenderer"); 1612 ctx.transferFromImageBitmap(bitmap); 1613 data.blobPromise = canvas.convertToBlob(); 1614 } 1615 bitmap.close?.(); 1616 data.bitmap = null; 1617 } 1618 isValidId(id) { 1619 return id.startsWith(`image_${this.#baseId}_`); 1620 } 1621} 1622class CommandManager { 1623 #commands = []; 1624 #locked = false; 1625 #maxSize; 1626 #position = -1; 1627 constructor(maxSize = 128) { 1628 this.#maxSize = maxSize; 1629 } 1630 add({ 1631 cmd, 1632 undo, 1633 post, 1634 mustExec, 1635 type = NaN, 1636 overwriteIfSameType = false, 1637 keepUndo = false 1638 }) { 1639 if (mustExec) { 1640 cmd(); 1641 } 1642 if (this.#locked) { 1643 return; 1644 } 1645 const save = { 1646 cmd, 1647 undo, 1648 post, 1649 type 1650 }; 1651 if (this.#position === -1) { 1652 if (this.#commands.length > 0) { 1653 this.#commands.length = 0; 1654 } 1655 this.#position = 0; 1656 this.#commands.push(save); 1657 return; 1658 } 1659 if (overwriteIfSameType && this.#commands[this.#position].type === type) { 1660 if (keepUndo) { 1661 save.undo = this.#commands[this.#position].undo; 1662 } 1663 this.#commands[this.#position] = save; 1664 return; 1665 } 1666 const next = this.#position + 1; 1667 if (next === this.#maxSize) { 1668 this.#commands.splice(0, 1); 1669 } else { 1670 this.#position = next; 1671 if (next < this.#commands.length) { 1672 this.#commands.splice(next); 1673 } 1674 } 1675 this.#commands.push(save); 1676 } 1677 undo() { 1678 if (this.#position === -1) { 1679 return; 1680 } 1681 this.#locked = true; 1682 const { 1683 undo, 1684 post 1685 } = this.#commands[this.#position]; 1686 undo(); 1687 post?.(); 1688 this.#locked = false; 1689 this.#position -= 1; 1690 } 1691 redo() { 1692 if (this.#position < this.#commands.length - 1) { 1693 this.#position += 1; 1694 this.#locked = true; 1695 const { 1696 cmd, 1697 post 1698 } = this.#commands[this.#position]; 1699 cmd(); 1700 post?.(); 1701 this.#locked = false;
vendor: 7,958 bytes, lines 1702-1949
1702 } 1703 } 1704 hasSomethingToUndo() { 1705 return this.#position !== -1; 1706 } 1707 hasSomethingToRedo() { 1708 return this.#position < this.#commands.length - 1; 1709 } 1710 destroy() { 1711 this.#commands = null; 1712 } 1713} 1714class KeyboardManager { 1715 constructor(callbacks) { 1716 this.buffer = []; 1717 this.callbacks = new Map(); 1718 this.allKeys = new Set(); 1719 const { 1720 isMac 1721 } = util_FeatureTest.platform; 1722 for (const [keys, callback, options = {}] of callbacks) { 1723 for (const key of keys) { 1724 const isMacKey = key.startsWith("mac+"); 1725 if (isMac && isMacKey) { 1726 this.callbacks.set(key.slice(4), { 1727 callback, 1728 options 1729 }); 1730 this.allKeys.add(key.split("+").at(-1)); 1731 } else if (!isMac && !isMacKey) { 1732 this.callbacks.set(key, { 1733 callback, 1734 options 1735 }); 1736 this.allKeys.add(key.split("+").at(-1)); 1737 } 1738 } 1739 } 1740 } 1741 #serialize(event) { 1742 if (event.altKey) { 1743 this.buffer.push("alt"); 1744 } 1745 if (event.ctrlKey) { 1746 this.buffer.push("ctrl"); 1747 } 1748 if (event.metaKey) { 1749 this.buffer.push("meta"); 1750 } 1751 if (event.shiftKey) { 1752 this.buffer.push("shift"); 1753 } 1754 this.buffer.push(event.key); 1755 const str = this.buffer.join("+"); 1756 this.buffer.length = 0; 1757 return str; 1758 } 1759 exec(self, event) { 1760 if (!this.allKeys.has(event.key)) { 1761 return; 1762 } 1763 const info = this.callbacks.get(this.#serialize(event)); 1764 if (!info) { 1765 return; 1766 } 1767 const { 1768 callback, 1769 options: { 1770 bubbles = false, 1771 args = [], 1772 checker = null 1773 } 1774 } = info; 1775 if (checker && !checker(self, event)) { 1776 return; 1777 } 1778 callback.bind(self, ...args, event)(); 1779 if (!bubbles) { 1780 event.stopPropagation(); 1781 event.preventDefault(); 1782 } 1783 } 1784} 1785class ColorManager { 1786 static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]); 1787 get _colors() { 1788 const colors = new Map([["CanvasText", null], ["Canvas", null]]); 1789 getColorValues(colors); 1790 return shadow(this, "_colors", colors); 1791 } 1792 convert(color) { 1793 const rgb = getRGB(color); 1794 if (!window.matchMedia("(forced-colors: active)").matches) { 1795 return rgb; 1796 } 1797 for (const [name, RGB] of this._colors) { 1798 if (RGB.every((x, i) => x === rgb[i])) { 1799 return ColorManager._colorsMapping.get(name); 1800 } 1801 } 1802 return rgb; 1803 } 1804 getHexCode(name) { 1805 const rgb = this._colors.get(name); 1806 if (!rgb) { 1807 return name; 1808 } 1809 return Util.makeHexColor(...rgb); 1810 } 1811} 1812class AnnotationEditorUIManager { 1813 #abortController = new AbortController(); 1814 #activeEditor = null; 1815 #allEditors = new Map(); 1816 #allLayers = new Map(); 1817 #altTextManager = null; 1818 #annotationStorage = null; 1819 #changedExistingAnnotations = null; 1820 #commandManager = new CommandManager(); 1821 #copyPasteAC = null; 1822 #currentPageIndex = 0; 1823 #deletedAnnotationsElementIds = new Set(); 1824 #draggingEditors = null; 1825 #editorTypes = null; 1826 #editorsToRescale = new Set(); 1827 #enableHighlightFloatingButton = false; 1828 #enableUpdatedAddImage = false; 1829 #enableNewAltTextWhenAddingImage = false; 1830 #filterFactory = null; 1831 #focusMainContainerTimeoutId = null; 1832 #focusManagerAC = null; 1833 #highlightColors = null; 1834 #highlightWhenShiftUp = false; 1835 #highlightToolbar = null; 1836 #idManager = new IdManager(); 1837 #isEnabled = false; 1838 #isWaiting = false; 1839 #keyboardManagerAC = null; 1840 #lastActiveElement = null; 1841 #mainHighlightColorPicker = null; 1842 #mlManager = null; 1843 #mode = AnnotationEditorType.NONE; 1844 #selectedEditors = new Set(); 1845 #selectedTextNode = null; 1846 #pageColors = null; 1847 #showAllStates = null; 1848 #previousStates = { 1849 isEditing: false, 1850 isEmpty: true, 1851 hasSomethingToUndo: false, 1852 hasSomethingToRedo: false, 1853 hasSelectedEditor: false, 1854 hasSelectedText: false 1855 }; 1856 #translation = [0, 0]; 1857 #translationTimeoutId = null; 1858 #container = null; 1859 #viewer = null; 1860 #updateModeCapability = null; 1861 static TRANSLATE_SMALL = 1; 1862 static TRANSLATE_BIG = 10; 1863 static get _keyboardManager() { 1864 const proto = AnnotationEditorUIManager.prototype; 1865 const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl(); 1866 const textInputChecker = (_self, { 1867 target: el 1868 }) => { 1869 if (el instanceof HTMLInputElement) { 1870 const { 1871 type 1872 } = el; 1873 return type !== "text" && type !== "number"; 1874 } 1875 return true; 1876 }; 1877 const small = this.TRANSLATE_SMALL; 1878 const big = this.TRANSLATE_BIG; 1879 return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, { 1880 checker: textInputChecker 1881 }], [["ctrl+z", "mac+meta+z"], proto.undo, { 1882 checker: textInputChecker 1883 }], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, { 1884 checker: textInputChecker 1885 }], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, { 1886 checker: textInputChecker 1887 }], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, { 1888 checker: (self, { 1889 target: el 1890 }) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled 1891 }], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, { 1892 checker: (self, { 1893 target: el 1894 }) => !(el instanceof HTMLButtonElement) && self.#container.contains(document.activeElement) 1895 }], [["Escape", "mac+Escape"], proto.unselectAll], [["ArrowLeft", "mac+ArrowLeft"], proto.translateSelectedEditors, { 1896 args: [-small, 0], 1897 checker: arrowChecker 1898 }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto.translateSelectedEditors, { 1899 args: [-big, 0], 1900 checker: arrowChecker 1901 }], [["ArrowRight", "mac+ArrowRight"], proto.translateSelectedEditors, { 1902 args: [small, 0], 1903 checker: arrowChecker 1904 }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto.translateSelectedEditors, { 1905 args: [big, 0], 1906 checker: arrowChecker 1907 }], [["ArrowUp", "mac+ArrowUp"], proto.translateSelectedEditors, { 1908 args: [0, -small], 1909 checker: arrowChecker 1910 }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto.translateSelectedEditors, { 1911 args: [0, -big], 1912 checker: arrowChecker 1913 }], [["ArrowDown", "mac+ArrowDown"], proto.translateSelectedEditors, { 1914 args: [0, small], 1915 checker: arrowChecker 1916 }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto.translateSelectedEditors, { 1917 args: [0, big], 1918 checker: arrowChecker 1919 }]])); 1920 } 1921 constructor(container, viewer, altTextManager, eventBus, pdfDocument, pageColors, highlightColors, enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, mlManager) { 1922 const signal = this._signal = this.#abortController.signal; 1923 this.#container = container; 1924 this.#viewer = viewer; 1925 this.#altTextManager = altTextManager; 1926 this._eventBus = eventBus; 1927 eventBus._on("editingaction", this.onEditingAction.bind(this), { 1928 signal 1929 }); 1930 eventBus._on("pagechanging", this.onPageChanging.bind(this), { 1931 signal 1932 }); 1933 eventBus._on("scalechanging", this.onScaleChanging.bind(this), { 1934 signal 1935 }); 1936 eventBus._on("rotationchanging", this.onRotationChanging.bind(this), { 1937 signal 1938 }); 1939 eventBus._on("setpreference", this.onSetPreference.bind(this), { 1940 signal 1941 }); 1942 eventBus._on("switchannotationeditorparams", evt => this.updateParams(evt.type, evt.value), { 1943 signal 1944 }); 1945 this.#addSelectionListener(); 1946 this.#addDragAndDropListeners(); 1947 this.#addKeyboardManager(); 1948 this.#annotationStorage = pdfDocument.annotationStorage; 1949 this.#filterFactory = pdfDocument.filterFactory;
vendor: 9,411 bytes, lines 1950-2269
1950 this.#pageColors = pageColors; 1951 this.#highlightColors = highlightColors || null; 1952 this.#enableHighlightFloatingButton = enableHighlightFloatingButton; 1953 this.#enableUpdatedAddImage = enableUpdatedAddImage; 1954 this.#enableNewAltTextWhenAddingImage = enableNewAltTextWhenAddingImage; 1955 this.#mlManager = mlManager || null; 1956 this.viewParameters = { 1957 realScale: PixelsPerInch.PDF_TO_CSS_UNITS, 1958 rotation: 0 1959 }; 1960 this.isShiftKeyDown = false; 1961 } 1962 destroy() { 1963 this.#updateModeCapability?.resolve(); 1964 this.#updateModeCapability = null; 1965 this.#abortController?.abort(); 1966 this.#abortController = null; 1967 this._signal = null; 1968 for (const layer of this.#allLayers.values()) { 1969 layer.destroy(); 1970 } 1971 this.#allLayers.clear(); 1972 this.#allEditors.clear(); 1973 this.#editorsToRescale.clear(); 1974 this.#activeEditor = null; 1975 this.#selectedEditors.clear(); 1976 this.#commandManager.destroy(); 1977 this.#altTextManager?.destroy(); 1978 this.#highlightToolbar?.hide(); 1979 this.#highlightToolbar = null; 1980 if (this.#focusMainContainerTimeoutId) { 1981 clearTimeout(this.#focusMainContainerTimeoutId); 1982 this.#focusMainContainerTimeoutId = null; 1983 } 1984 if (this.#translationTimeoutId) { 1985 clearTimeout(this.#translationTimeoutId); 1986 this.#translationTimeoutId = null; 1987 } 1988 } 1989 combinedSignal(ac) { 1990 return AbortSignal.any([this._signal, ac.signal]); 1991 } 1992 get mlManager() { 1993 return this.#mlManager; 1994 } 1995 get useNewAltTextFlow() { 1996 return this.#enableUpdatedAddImage; 1997 } 1998 get useNewAltTextWhenAddingImage() { 1999 return this.#enableNewAltTextWhenAddingImage; 2000 } 2001 get hcmFilter() { 2002 return shadow(this, "hcmFilter", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : "none"); 2003 } 2004 get direction() { 2005 return shadow(this, "direction", getComputedStyle(this.#container).direction); 2006 } 2007 get highlightColors() { 2008 return shadow(this, "highlightColors", this.#highlightColors ? new Map(this.#highlightColors.split(",").map(pair => pair.split("=").map(x => x.trim()))) : null); 2009 } 2010 get highlightColorNames() { 2011 return shadow(this, "highlightColorNames", this.highlightColors ? new Map(Array.from(this.highlightColors, e => e.reverse())) : null); 2012 } 2013 setMainHighlightColorPicker(colorPicker) { 2014 this.#mainHighlightColorPicker = colorPicker; 2015 } 2016 editAltText(editor, firstTime = false) { 2017 this.#altTextManager?.editAltText(this, editor, firstTime); 2018 } 2019 switchToMode(mode, callback) { 2020 this._eventBus.on("annotationeditormodechanged", callback, { 2021 once: true, 2022 signal: this._signal 2023 }); 2024 this._eventBus.dispatch("showannotationeditorui", { 2025 source: this, 2026 mode 2027 }); 2028 } 2029 setPreference(name, value) { 2030 this._eventBus.dispatch("setpreference", { 2031 source: this, 2032 name, 2033 value 2034 }); 2035 } 2036 onSetPreference({ 2037 name, 2038 value 2039 }) { 2040 switch (name) { 2041 case "enableNewAltTextWhenAddingImage": 2042 this.#enableNewAltTextWhenAddingImage = value; 2043 break; 2044 } 2045 } 2046 onPageChanging({ 2047 pageNumber 2048 }) { 2049 this.#currentPageIndex = pageNumber - 1; 2050 } 2051 focusMainContainer() { 2052 this.#container.focus(); 2053 } 2054 findParent(x, y) { 2055 for (const layer of this.#allLayers.values()) { 2056 const { 2057 x: layerX, 2058 y: layerY, 2059 width, 2060 height 2061 } = layer.div.getBoundingClientRect(); 2062 if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) { 2063 return layer; 2064 } 2065 } 2066 return null; 2067 } 2068 disableUserSelect(value = false) { 2069 this.#viewer.classList.toggle("noUserSelect", value); 2070 } 2071 addShouldRescale(editor) { 2072 this.#editorsToRescale.add(editor); 2073 } 2074 removeShouldRescale(editor) { 2075 this.#editorsToRescale.delete(editor); 2076 } 2077 onScaleChanging({ 2078 scale 2079 }) { 2080 this.commitOrRemove(); 2081 this.viewParameters.realScale = scale * PixelsPerInch.PDF_TO_CSS_UNITS; 2082 for (const editor of this.#editorsToRescale) { 2083 editor.onScaleChanging(); 2084 } 2085 } 2086 onRotationChanging({ 2087 pagesRotation 2088 }) { 2089 this.commitOrRemove(); 2090 this.viewParameters.rotation = pagesRotation; 2091 } 2092 #getAnchorElementForSelection({ 2093 anchorNode 2094 }) { 2095 return anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode; 2096 } 2097 #getLayerForTextLayer(textLayer) { 2098 const { 2099 currentLayer 2100 } = this; 2101 if (currentLayer.hasTextLayer(textLayer)) { 2102 return currentLayer; 2103 } 2104 for (const layer of this.#allLayers.values()) { 2105 if (layer.hasTextLayer(textLayer)) { 2106 return layer; 2107 } 2108 } 2109 return null; 2110 } 2111 highlightSelection(methodOfCreation = "") { 2112 const selection = document.getSelection(); 2113 if (!selection || selection.isCollapsed) { 2114 return; 2115 } 2116 const { 2117 anchorNode, 2118 anchorOffset, 2119 focusNode, 2120 focusOffset 2121 } = selection; 2122 const text = selection.toString(); 2123 const anchorElement = this.#getAnchorElementForSelection(selection); 2124 const textLayer = anchorElement.closest(".textLayer"); 2125 const boxes = this.getSelectionBoxes(textLayer); 2126 if (!boxes) { 2127 return; 2128 } 2129 selection.empty(); 2130 const layer = this.#getLayerForTextLayer(textLayer); 2131 const isNoneMode = this.#mode === AnnotationEditorType.NONE; 2132 const callback = () => { 2133 layer?.createAndAddNewEditor({ 2134 x: 0, 2135 y: 0 2136 }, false, { 2137 methodOfCreation, 2138 boxes, 2139 anchorNode, 2140 anchorOffset, 2141 focusNode, 2142 focusOffset, 2143 text 2144 }); 2145 if (isNoneMode) { 2146 this.showAllEditors("highlight", true, true); 2147 } 2148 }; 2149 if (isNoneMode) { 2150 this.switchToMode(AnnotationEditorType.HIGHLIGHT, callback); 2151 return; 2152 } 2153 callback(); 2154 } 2155 #displayHighlightToolbar() { 2156 const selection = document.getSelection(); 2157 if (!selection || selection.isCollapsed) { 2158 return; 2159 } 2160 const anchorElement = this.#getAnchorElementForSelection(selection); 2161 const textLayer = anchorElement.closest(".textLayer"); 2162 const boxes = this.getSelectionBoxes(textLayer); 2163 if (!boxes) { 2164 return; 2165 } 2166 this.#highlightToolbar ||= new HighlightToolbar(this); 2167 this.#highlightToolbar.show(textLayer, boxes, this.direction === "ltr"); 2168 } 2169 addToAnnotationStorage(editor) { 2170 if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) { 2171 this.#annotationStorage.setValue(editor.id, editor); 2172 } 2173 } 2174 #selectionChange() { 2175 const selection = document.getSelection(); 2176 if (!selection || selection.isCollapsed) { 2177 if (this.#selectedTextNode) { 2178 this.#highlightToolbar?.hide(); 2179 this.#selectedTextNode = null; 2180 this.#dispatchUpdateStates({ 2181 hasSelectedText: false 2182 }); 2183 } 2184 return; 2185 } 2186 const { 2187 anchorNode 2188 } = selection; 2189 if (anchorNode === this.#selectedTextNode) { 2190 return; 2191 } 2192 const anchorElement = this.#getAnchorElementForSelection(selection); 2193 const textLayer = anchorElement.closest(".textLayer"); 2194 if (!textLayer) { 2195 if (this.#selectedTextNode) { 2196 this.#highlightToolbar?.hide(); 2197 this.#selectedTextNode = null; 2198 this.#dispatchUpdateStates({ 2199 hasSelectedText: false 2200 }); 2201 } 2202 return; 2203 } 2204 this.#highlightToolbar?.hide(); 2205 this.#selectedTextNode = anchorNode; 2206 this.#dispatchUpdateStates({ 2207 hasSelectedText: true 2208 }); 2209 if (this.#mode !== AnnotationEditorType.HIGHLIGHT && this.#mode !== AnnotationEditorType.NONE) { 2210 return; 2211 } 2212 if (this.#mode === AnnotationEditorType.HIGHLIGHT) { 2213 this.showAllEditors("highlight", true, true); 2214 } 2215 this.#highlightWhenShiftUp = this.isShiftKeyDown; 2216 if (!this.isShiftKeyDown) { 2217 const activeLayer = this.#mode === AnnotationEditorType.HIGHLIGHT ? this.#getLayerForTextLayer(textLayer) : null; 2218 activeLayer?.toggleDrawing(); 2219 const ac = new AbortController(); 2220 const signal = this.combinedSignal(ac); 2221 const pointerup = e => { 2222 if (e.type === "pointerup" && e.button !== 0) { 2223 return; 2224 } 2225 ac.abort(); 2226 activeLayer?.toggleDrawing(true); 2227 if (e.type === "pointerup") { 2228 this.#onSelectEnd("main_toolbar"); 2229 } 2230 }; 2231 window.addEventListener("pointerup", pointerup, { 2232 signal 2233 }); 2234 window.addEventListener("blur", pointerup, { 2235 signal 2236 }); 2237 } 2238 } 2239 #onSelectEnd(methodOfCreation = "") { 2240 if (this.#mode === AnnotationEditorType.HIGHLIGHT) { 2241 this.highlightSelection(methodOfCreation); 2242 } else if (this.#enableHighlightFloatingButton) { 2243 this.#displayHighlightToolbar(); 2244 } 2245 } 2246 #addSelectionListener() { 2247 document.addEventListener("selectionchange", this.#selectionChange.bind(this), { 2248 signal: this._signal 2249 }); 2250 } 2251 #addFocusManager() { 2252 if (this.#focusManagerAC) { 2253 return; 2254 } 2255 this.#focusManagerAC = new AbortController(); 2256 const signal = this.combinedSignal(this.#focusManagerAC); 2257 window.addEventListener("focus", this.focus.bind(this), { 2258 signal 2259 }); 2260 window.addEventListener("blur", this.blur.bind(this), { 2261 signal 2262 }); 2263 } 2264 #removeFocusManager() { 2265 this.#focusManagerAC?.abort(); 2266 this.#focusManagerAC = null; 2267 } 2268 blur() { 2269 this.isShiftKeyDown = false;
vendor: 15,894 bytes, lines 2270-2853
2270 if (this.#highlightWhenShiftUp) { 2271 this.#highlightWhenShiftUp = false; 2272 this.#onSelectEnd("main_toolbar"); 2273 } 2274 if (!this.hasSelection) { 2275 return; 2276 } 2277 const { 2278 activeElement 2279 } = document; 2280 for (const editor of this.#selectedEditors) { 2281 if (editor.div.contains(activeElement)) { 2282 this.#lastActiveElement = [editor, activeElement]; 2283 editor._focusEventsAllowed = false; 2284 break; 2285 } 2286 } 2287 } 2288 focus() { 2289 if (!this.#lastActiveElement) { 2290 return; 2291 } 2292 const [lastEditor, lastActiveElement] = this.#lastActiveElement; 2293 this.#lastActiveElement = null; 2294 lastActiveElement.addEventListener("focusin", () => { 2295 lastEditor._focusEventsAllowed = true; 2296 }, { 2297 once: true, 2298 signal: this._signal 2299 }); 2300 lastActiveElement.focus(); 2301 } 2302 #addKeyboardManager() { 2303 if (this.#keyboardManagerAC) { 2304 return; 2305 } 2306 this.#keyboardManagerAC = new AbortController(); 2307 const signal = this.combinedSignal(this.#keyboardManagerAC); 2308 window.addEventListener("keydown", this.keydown.bind(this), { 2309 signal 2310 }); 2311 window.addEventListener("keyup", this.keyup.bind(this), { 2312 signal 2313 }); 2314 } 2315 #removeKeyboardManager() { 2316 this.#keyboardManagerAC?.abort(); 2317 this.#keyboardManagerAC = null; 2318 } 2319 #addCopyPasteListeners() { 2320 if (this.#copyPasteAC) { 2321 return; 2322 } 2323 this.#copyPasteAC = new AbortController(); 2324 const signal = this.combinedSignal(this.#copyPasteAC); 2325 document.addEventListener("copy", this.copy.bind(this), { 2326 signal 2327 }); 2328 document.addEventListener("cut", this.cut.bind(this), { 2329 signal 2330 }); 2331 document.addEventListener("paste", this.paste.bind(this), { 2332 signal 2333 }); 2334 } 2335 #removeCopyPasteListeners() { 2336 this.#copyPasteAC?.abort(); 2337 this.#copyPasteAC = null; 2338 } 2339 #addDragAndDropListeners() { 2340 const signal = this._signal; 2341 document.addEventListener("dragover", this.dragOver.bind(this), { 2342 signal 2343 }); 2344 document.addEventListener("drop", this.drop.bind(this), { 2345 signal 2346 }); 2347 } 2348 addEditListeners() { 2349 this.#addKeyboardManager(); 2350 this.#addCopyPasteListeners(); 2351 } 2352 removeEditListeners() { 2353 this.#removeKeyboardManager(); 2354 this.#removeCopyPasteListeners(); 2355 } 2356 dragOver(event) { 2357 for (const { 2358 type 2359 } of event.dataTransfer.items) { 2360 for (const editorType of this.#editorTypes) { 2361 if (editorType.isHandlingMimeForPasting(type)) { 2362 event.dataTransfer.dropEffect = "copy"; 2363 event.preventDefault(); 2364 return; 2365 } 2366 } 2367 } 2368 } 2369 drop(event) { 2370 for (const item of event.dataTransfer.items) { 2371 for (const editorType of this.#editorTypes) { 2372 if (editorType.isHandlingMimeForPasting(item.type)) { 2373 editorType.paste(item, this.currentLayer); 2374 event.preventDefault(); 2375 return; 2376 } 2377 } 2378 } 2379 } 2380 copy(event) { 2381 event.preventDefault(); 2382 this.#activeEditor?.commitOrRemove(); 2383 if (!this.hasSelection) { 2384 return; 2385 } 2386 const editors = []; 2387 for (const editor of this.#selectedEditors) { 2388 const serialized = editor.serialize(true); 2389 if (serialized) { 2390 editors.push(serialized); 2391 } 2392 } 2393 if (editors.length === 0) { 2394 return; 2395 } 2396 event.clipboardData.setData("application/pdfjs", JSON.stringify(editors)); 2397 } 2398 cut(event) { 2399 this.copy(event); 2400 this.delete(); 2401 } 2402 async paste(event) { 2403 event.preventDefault(); 2404 const { 2405 clipboardData 2406 } = event; 2407 for (const item of clipboardData.items) { 2408 for (const editorType of this.#editorTypes) { 2409 if (editorType.isHandlingMimeForPasting(item.type)) { 2410 editorType.paste(item, this.currentLayer); 2411 return; 2412 } 2413 } 2414 } 2415 let data = clipboardData.getData("application/pdfjs"); 2416 if (!data) { 2417 return; 2418 } 2419 try { 2420 data = JSON.parse(data); 2421 } catch (ex) { 2422 warn(`paste: "${ex.message}".`); 2423 return; 2424 } 2425 if (!Array.isArray(data)) { 2426 return; 2427 } 2428 this.unselectAll(); 2429 const layer = this.currentLayer; 2430 try { 2431 const newEditors = []; 2432 for (const editor of data) { 2433 const deserializedEditor = await layer.deserialize(editor); 2434 if (!deserializedEditor) { 2435 return; 2436 } 2437 newEditors.push(deserializedEditor); 2438 } 2439 const cmd = () => { 2440 for (const editor of newEditors) { 2441 this.#addEditorToLayer(editor); 2442 } 2443 this.#selectEditors(newEditors); 2444 }; 2445 const undo = () => { 2446 for (const editor of newEditors) { 2447 editor.remove(); 2448 } 2449 }; 2450 this.addCommands({ 2451 cmd, 2452 undo, 2453 mustExec: true 2454 }); 2455 } catch (ex) { 2456 warn(`paste: "${ex.message}".`); 2457 } 2458 } 2459 keydown(event) { 2460 if (!this.isShiftKeyDown && event.key === "Shift") { 2461 this.isShiftKeyDown = true; 2462 } 2463 if (this.#mode !== AnnotationEditorType.NONE && !this.isEditorHandlingKeyboard) { 2464 AnnotationEditorUIManager._keyboardManager.exec(this, event); 2465 } 2466 } 2467 keyup(event) { 2468 if (this.isShiftKeyDown && event.key === "Shift") { 2469 this.isShiftKeyDown = false; 2470 if (this.#highlightWhenShiftUp) { 2471 this.#highlightWhenShiftUp = false; 2472 this.#onSelectEnd("main_toolbar"); 2473 } 2474 } 2475 } 2476 onEditingAction({ 2477 name 2478 }) { 2479 switch (name) { 2480 case "undo": 2481 case "redo": 2482 case "delete": 2483 case "selectAll": 2484 this[name](); 2485 break; 2486 case "highlightSelection": 2487 this.highlightSelection("context_menu"); 2488 break; 2489 } 2490 } 2491 #dispatchUpdateStates(details) { 2492 const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value); 2493 if (hasChanged) { 2494 this._eventBus.dispatch("annotationeditorstateschanged", { 2495 source: this, 2496 details: Object.assign(this.#previousStates, details) 2497 }); 2498 if (this.#mode === AnnotationEditorType.HIGHLIGHT && details.hasSelectedEditor === false) { 2499 this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_FREE, true]]); 2500 } 2501 } 2502 } 2503 #dispatchUpdateUI(details) { 2504 this._eventBus.dispatch("annotationeditorparamschanged", { 2505 source: this, 2506 details 2507 }); 2508 } 2509 setEditingState(isEditing) { 2510 if (isEditing) { 2511 this.#addFocusManager(); 2512 this.#addCopyPasteListeners(); 2513 this.#dispatchUpdateStates({ 2514 isEditing: this.#mode !== AnnotationEditorType.NONE, 2515 isEmpty: this.#isEmpty(), 2516 hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), 2517 hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), 2518 hasSelectedEditor: false 2519 }); 2520 } else { 2521 this.#removeFocusManager(); 2522 this.#removeCopyPasteListeners(); 2523 this.#dispatchUpdateStates({ 2524 isEditing: false 2525 }); 2526 this.disableUserSelect(false); 2527 } 2528 } 2529 registerEditorTypes(types) { 2530 if (this.#editorTypes) { 2531 return; 2532 } 2533 this.#editorTypes = types; 2534 for (const editorType of this.#editorTypes) { 2535 this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); 2536 } 2537 } 2538 getId() { 2539 return this.#idManager.id; 2540 } 2541 get currentLayer() { 2542 return this.#allLayers.get(this.#currentPageIndex); 2543 } 2544 getLayer(pageIndex) { 2545 return this.#allLayers.get(pageIndex); 2546 } 2547 get currentPageIndex() { 2548 return this.#currentPageIndex; 2549 } 2550 addLayer(layer) { 2551 this.#allLayers.set(layer.pageIndex, layer); 2552 if (this.#isEnabled) { 2553 layer.enable(); 2554 } else { 2555 layer.disable(); 2556 } 2557 } 2558 removeLayer(layer) { 2559 this.#allLayers.delete(layer.pageIndex); 2560 } 2561 async updateMode(mode, editId = null, isFromKeyboard = false) { 2562 if (this.#mode === mode) { 2563 return; 2564 } 2565 if (this.#updateModeCapability) { 2566 await this.#updateModeCapability.promise; 2567 if (!this.#updateModeCapability) { 2568 return; 2569 } 2570 } 2571 this.#updateModeCapability = Promise.withResolvers(); 2572 this.#mode = mode; 2573 if (mode === AnnotationEditorType.NONE) { 2574 this.setEditingState(false); 2575 this.#disableAll(); 2576 this.#updateModeCapability.resolve(); 2577 return; 2578 } 2579 this.setEditingState(true); 2580 await this.#enableAll(); 2581 this.unselectAll(); 2582 for (const layer of this.#allLayers.values()) { 2583 layer.updateMode(mode); 2584 } 2585 if (!editId) { 2586 if (isFromKeyboard) { 2587 this.addNewEditorFromKeyboard(); 2588 } 2589 this.#updateModeCapability.resolve(); 2590 return; 2591 } 2592 for (const editor of this.#allEditors.values()) { 2593 if (editor.annotationElementId === editId) { 2594 this.setSelected(editor); 2595 editor.enterInEditMode(); 2596 } else { 2597 editor.unselect(); 2598 } 2599 } 2600 this.#updateModeCapability.resolve(); 2601 } 2602 addNewEditorFromKeyboard() { 2603 if (this.currentLayer.canCreateNewEmptyEditor()) { 2604 this.currentLayer.addNewEditor(); 2605 } 2606 } 2607 updateToolbar(mode) { 2608 if (mode === this.#mode) { 2609 return; 2610 } 2611 this._eventBus.dispatch("switchannotationeditormode", { 2612 source: this, 2613 mode 2614 }); 2615 } 2616 updateParams(type, value) { 2617 if (!this.#editorTypes) { 2618 return; 2619 } 2620 switch (type) { 2621 case AnnotationEditorParamsType.CREATE: 2622 this.currentLayer.addNewEditor(); 2623 return; 2624 case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR: 2625 this.#mainHighlightColorPicker?.updateColor(value); 2626 break; 2627 case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL: 2628 this._eventBus.dispatch("reporttelemetry", { 2629 source: this, 2630 details: { 2631 type: "editing", 2632 data: { 2633 type: "highlight", 2634 action: "toggle_visibility" 2635 } 2636 } 2637 }); 2638 (this.#showAllStates ||= new Map()).set(type, value); 2639 this.showAllEditors("highlight", value); 2640 break; 2641 } 2642 for (const editor of this.#selectedEditors) { 2643 editor.updateParams(type, value); 2644 } 2645 for (const editorType of this.#editorTypes) { 2646 editorType.updateDefaultParams(type, value); 2647 } 2648 } 2649 showAllEditors(type, visible, updateButton = false) { 2650 for (const editor of this.#allEditors.values()) { 2651 if (editor.editorType === type) { 2652 editor.show(visible); 2653 } 2654 } 2655 const state = this.#showAllStates?.get(AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL) ?? true; 2656 if (state !== visible) { 2657 this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL, visible]]); 2658 } 2659 } 2660 enableWaiting(mustWait = false) { 2661 if (this.#isWaiting === mustWait) { 2662 return; 2663 } 2664 this.#isWaiting = mustWait; 2665 for (const layer of this.#allLayers.values()) { 2666 if (mustWait) { 2667 layer.disableClick(); 2668 } else { 2669 layer.enableClick(); 2670 } 2671 layer.div.classList.toggle("waiting", mustWait); 2672 } 2673 } 2674 async #enableAll() { 2675 if (!this.#isEnabled) { 2676 this.#isEnabled = true; 2677 const promises = []; 2678 for (const layer of this.#allLayers.values()) { 2679 promises.push(layer.enable()); 2680 } 2681 await Promise.all(promises); 2682 for (const editor of this.#allEditors.values()) { 2683 editor.enable(); 2684 } 2685 } 2686 } 2687 #disableAll() { 2688 this.unselectAll(); 2689 if (this.#isEnabled) { 2690 this.#isEnabled = false; 2691 for (const layer of this.#allLayers.values()) { 2692 layer.disable(); 2693 } 2694 for (const editor of this.#allEditors.values()) { 2695 editor.disable(); 2696 } 2697 } 2698 } 2699 getEditors(pageIndex) { 2700 const editors = []; 2701 for (const editor of this.#allEditors.values()) { 2702 if (editor.pageIndex === pageIndex) { 2703 editors.push(editor); 2704 } 2705 } 2706 return editors; 2707 } 2708 getEditor(id) { 2709 return this.#allEditors.get(id); 2710 } 2711 addEditor(editor) { 2712 this.#allEditors.set(editor.id, editor); 2713 } 2714 removeEditor(editor) { 2715 if (editor.div.contains(document.activeElement)) { 2716 if (this.#focusMainContainerTimeoutId) { 2717 clearTimeout(this.#focusMainContainerTimeoutId); 2718 } 2719 this.#focusMainContainerTimeoutId = setTimeout(() => { 2720 this.focusMainContainer(); 2721 this.#focusMainContainerTimeoutId = null; 2722 }, 0); 2723 } 2724 this.#allEditors.delete(editor.id); 2725 this.unselect(editor); 2726 if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) { 2727 this.#annotationStorage?.remove(editor.id); 2728 } 2729 } 2730 addDeletedAnnotationElement(editor) { 2731 this.#deletedAnnotationsElementIds.add(editor.annotationElementId); 2732 this.addChangedExistingAnnotation(editor); 2733 editor.deleted = true; 2734 } 2735 isDeletedAnnotationElement(annotationElementId) { 2736 return this.#deletedAnnotationsElementIds.has(annotationElementId); 2737 } 2738 removeDeletedAnnotationElement(editor) { 2739 this.#deletedAnnotationsElementIds.delete(editor.annotationElementId); 2740 this.removeChangedExistingAnnotation(editor); 2741 editor.deleted = false; 2742 } 2743 #addEditorToLayer(editor) { 2744 const layer = this.#allLayers.get(editor.pageIndex); 2745 if (layer) { 2746 layer.addOrRebuild(editor); 2747 } else { 2748 this.addEditor(editor); 2749 this.addToAnnotationStorage(editor); 2750 } 2751 } 2752 setActiveEditor(editor) { 2753 if (this.#activeEditor === editor) { 2754 return; 2755 } 2756 this.#activeEditor = editor; 2757 if (editor) { 2758 this.#dispatchUpdateUI(editor.propertiesToUpdate); 2759 } 2760 } 2761 get #lastSelectedEditor() { 2762 let ed = null; 2763 for (ed of this.#selectedEditors) {} 2764 return ed; 2765 } 2766 updateUI(editor) { 2767 if (this.#lastSelectedEditor === editor) { 2768 this.#dispatchUpdateUI(editor.propertiesToUpdate); 2769 } 2770 } 2771 toggleSelected(editor) { 2772 if (this.#selectedEditors.has(editor)) { 2773 this.#selectedEditors.delete(editor); 2774 editor.unselect(); 2775 this.#dispatchUpdateStates({ 2776 hasSelectedEditor: this.hasSelection 2777 }); 2778 return; 2779 } 2780 this.#selectedEditors.add(editor); 2781 editor.select(); 2782 this.#dispatchUpdateUI(editor.propertiesToUpdate); 2783 this.#dispatchUpdateStates({ 2784 hasSelectedEditor: true 2785 }); 2786 } 2787 setSelected(editor) { 2788 for (const ed of this.#selectedEditors) { 2789 if (ed !== editor) { 2790 ed.unselect(); 2791 } 2792 } 2793 this.#selectedEditors.clear(); 2794 this.#selectedEditors.add(editor); 2795 editor.select(); 2796 this.#dispatchUpdateUI(editor.propertiesToUpdate); 2797 this.#dispatchUpdateStates({ 2798 hasSelectedEditor: true 2799 }); 2800 } 2801 isSelected(editor) { 2802 return this.#selectedEditors.has(editor); 2803 } 2804 get firstSelectedEditor() { 2805 return this.#selectedEditors.values().next().value; 2806 } 2807 unselect(editor) { 2808 editor.unselect(); 2809 this.#selectedEditors.delete(editor); 2810 this.#dispatchUpdateStates({ 2811 hasSelectedEditor: this.hasSelection 2812 }); 2813 } 2814 get hasSelection() { 2815 return this.#selectedEditors.size !== 0; 2816 } 2817 get isEnterHandled() { 2818 return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled; 2819 } 2820 undo() { 2821 this.#commandManager.undo(); 2822 this.#dispatchUpdateStates({ 2823 hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), 2824 hasSomethingToRedo: true, 2825 isEmpty: this.#isEmpty() 2826 }); 2827 } 2828 redo() { 2829 this.#commandManager.redo(); 2830 this.#dispatchUpdateStates({ 2831 hasSomethingToUndo: true, 2832 hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), 2833 isEmpty: this.#isEmpty() 2834 }); 2835 } 2836 addCommands(params) { 2837 this.#commandManager.add(params); 2838 this.#dispatchUpdateStates({ 2839 hasSomethingToUndo: true, 2840 hasSomethingToRedo: false, 2841 isEmpty: this.#isEmpty() 2842 }); 2843 } 2844 #isEmpty() { 2845 if (this.#allEditors.size === 0) { 2846 return true; 2847 } 2848 if (this.#allEditors.size === 1) { 2849 for (const editor of this.#allEditors.values()) { 2850 return editor.isEmpty(); 2851 } 2852 } 2853 return false;
vendor: 16,438 bytes, lines 2854-3447
2854 } 2855 delete() { 2856 this.commitOrRemove(); 2857 if (!this.hasSelection) { 2858 return; 2859 } 2860 const editors = [...this.#selectedEditors]; 2861 const cmd = () => { 2862 for (const editor of editors) { 2863 editor.remove(); 2864 } 2865 }; 2866 const undo = () => { 2867 for (const editor of editors) { 2868 this.#addEditorToLayer(editor); 2869 } 2870 }; 2871 this.addCommands({ 2872 cmd, 2873 undo, 2874 mustExec: true 2875 }); 2876 } 2877 commitOrRemove() { 2878 this.#activeEditor?.commitOrRemove(); 2879 } 2880 hasSomethingToControl() { 2881 return this.#activeEditor || this.hasSelection; 2882 } 2883 #selectEditors(editors) { 2884 for (const editor of this.#selectedEditors) { 2885 editor.unselect(); 2886 } 2887 this.#selectedEditors.clear(); 2888 for (const editor of editors) { 2889 if (editor.isEmpty()) { 2890 continue; 2891 } 2892 this.#selectedEditors.add(editor); 2893 editor.select(); 2894 } 2895 this.#dispatchUpdateStates({ 2896 hasSelectedEditor: this.hasSelection 2897 }); 2898 } 2899 selectAll() { 2900 for (const editor of this.#selectedEditors) { 2901 editor.commit(); 2902 } 2903 this.#selectEditors(this.#allEditors.values()); 2904 } 2905 unselectAll() { 2906 if (this.#activeEditor) { 2907 this.#activeEditor.commitOrRemove(); 2908 if (this.#mode !== AnnotationEditorType.NONE) { 2909 return; 2910 } 2911 } 2912 if (!this.hasSelection) { 2913 return; 2914 } 2915 for (const editor of this.#selectedEditors) { 2916 editor.unselect(); 2917 } 2918 this.#selectedEditors.clear(); 2919 this.#dispatchUpdateStates({ 2920 hasSelectedEditor: false 2921 }); 2922 } 2923 translateSelectedEditors(x, y, noCommit = false) { 2924 if (!noCommit) { 2925 this.commitOrRemove(); 2926 } 2927 if (!this.hasSelection) { 2928 return; 2929 } 2930 this.#translation[0] += x; 2931 this.#translation[1] += y; 2932 const [totalX, totalY] = this.#translation; 2933 const editors = [...this.#selectedEditors]; 2934 const TIME_TO_WAIT = 1000; 2935 if (this.#translationTimeoutId) { 2936 clearTimeout(this.#translationTimeoutId); 2937 } 2938 this.#translationTimeoutId = setTimeout(() => { 2939 this.#translationTimeoutId = null; 2940 this.#translation[0] = this.#translation[1] = 0; 2941 this.addCommands({ 2942 cmd: () => { 2943 for (const editor of editors) { 2944 if (this.#allEditors.has(editor.id)) { 2945 editor.translateInPage(totalX, totalY); 2946 } 2947 } 2948 }, 2949 undo: () => { 2950 for (const editor of editors) { 2951 if (this.#allEditors.has(editor.id)) { 2952 editor.translateInPage(-totalX, -totalY); 2953 } 2954 } 2955 }, 2956 mustExec: false 2957 }); 2958 }, TIME_TO_WAIT); 2959 for (const editor of editors) { 2960 editor.translateInPage(x, y); 2961 } 2962 } 2963 setUpDragSession() { 2964 if (!this.hasSelection) { 2965 return; 2966 } 2967 this.disableUserSelect(true); 2968 this.#draggingEditors = new Map(); 2969 for (const editor of this.#selectedEditors) { 2970 this.#draggingEditors.set(editor, { 2971 savedX: editor.x, 2972 savedY: editor.y, 2973 savedPageIndex: editor.pageIndex, 2974 newX: 0, 2975 newY: 0, 2976 newPageIndex: -1 2977 }); 2978 } 2979 } 2980 endDragSession() { 2981 if (!this.#draggingEditors) { 2982 return false; 2983 } 2984 this.disableUserSelect(false); 2985 const map = this.#draggingEditors; 2986 this.#draggingEditors = null; 2987 let mustBeAddedInUndoStack = false; 2988 for (const [{ 2989 x, 2990 y, 2991 pageIndex 2992 }, value] of map) { 2993 value.newX = x; 2994 value.newY = y; 2995 value.newPageIndex = pageIndex; 2996 mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex; 2997 } 2998 if (!mustBeAddedInUndoStack) { 2999 return false; 3000 } 3001 const move = (editor, x, y, pageIndex) => { 3002 if (this.#allEditors.has(editor.id)) { 3003 const parent = this.#allLayers.get(pageIndex); 3004 if (parent) { 3005 editor._setParentAndPosition(parent, x, y); 3006 } else { 3007 editor.pageIndex = pageIndex; 3008 editor.x = x; 3009 editor.y = y; 3010 } 3011 } 3012 }; 3013 this.addCommands({ 3014 cmd: () => { 3015 for (const [editor, { 3016 newX, 3017 newY, 3018 newPageIndex 3019 }] of map) { 3020 move(editor, newX, newY, newPageIndex); 3021 } 3022 }, 3023 undo: () => { 3024 for (const [editor, { 3025 savedX, 3026 savedY, 3027 savedPageIndex 3028 }] of map) { 3029 move(editor, savedX, savedY, savedPageIndex); 3030 } 3031 }, 3032 mustExec: true 3033 }); 3034 return true; 3035 } 3036 dragSelectedEditors(tx, ty) { 3037 if (!this.#draggingEditors) { 3038 return; 3039 } 3040 for (const editor of this.#draggingEditors.keys()) { 3041 editor.drag(tx, ty); 3042 } 3043 } 3044 rebuild(editor) { 3045 if (editor.parent === null) { 3046 const parent = this.getLayer(editor.pageIndex); 3047 if (parent) { 3048 parent.changeParent(editor); 3049 parent.addOrRebuild(editor); 3050 } else { 3051 this.addEditor(editor); 3052 this.addToAnnotationStorage(editor); 3053 editor.rebuild(); 3054 } 3055 } else { 3056 editor.parent.addOrRebuild(editor); 3057 } 3058 } 3059 get isEditorHandlingKeyboard() { 3060 return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents(); 3061 } 3062 isActive(editor) { 3063 return this.#activeEditor === editor; 3064 } 3065 getActive() { 3066 return this.#activeEditor; 3067 } 3068 getMode() { 3069 return this.#mode; 3070 } 3071 get imageManager() { 3072 return shadow(this, "imageManager", new ImageManager()); 3073 } 3074 getSelectionBoxes(textLayer) { 3075 if (!textLayer) { 3076 return null; 3077 } 3078 const selection = document.getSelection(); 3079 for (let i = 0, ii = selection.rangeCount; i < ii; i++) { 3080 if (!textLayer.contains(selection.getRangeAt(i).commonAncestorContainer)) { 3081 return null; 3082 } 3083 } 3084 const { 3085 x: layerX, 3086 y: layerY, 3087 width: parentWidth, 3088 height: parentHeight 3089 } = textLayer.getBoundingClientRect(); 3090 let rotator; 3091 switch (textLayer.getAttribute("data-main-rotation")) { 3092 case "90": 3093 rotator = (x, y, w, h) => ({ 3094 x: (y - layerY) / parentHeight, 3095 y: 1 - (x + w - layerX) / parentWidth, 3096 width: h / parentHeight, 3097 height: w / parentWidth 3098 }); 3099 break; 3100 case "180": 3101 rotator = (x, y, w, h) => ({ 3102 x: 1 - (x + w - layerX) / parentWidth, 3103 y: 1 - (y + h - layerY) / parentHeight, 3104 width: w / parentWidth, 3105 height: h / parentHeight 3106 }); 3107 break; 3108 case "270": 3109 rotator = (x, y, w, h) => ({ 3110 x: 1 - (y + h - layerY) / parentHeight, 3111 y: (x - layerX) / parentWidth, 3112 width: h / parentHeight, 3113 height: w / parentWidth 3114 }); 3115 break; 3116 default: 3117 rotator = (x, y, w, h) => ({ 3118 x: (x - layerX) / parentWidth, 3119 y: (y - layerY) / parentHeight, 3120 width: w / parentWidth, 3121 height: h / parentHeight 3122 }); 3123 break; 3124 } 3125 const boxes = []; 3126 for (let i = 0, ii = selection.rangeCount; i < ii; i++) { 3127 const range = selection.getRangeAt(i); 3128 if (range.collapsed) { 3129 continue; 3130 } 3131 for (const { 3132 x, 3133 y, 3134 width, 3135 height 3136 } of range.getClientRects()) { 3137 if (width === 0 || height === 0) { 3138 continue; 3139 } 3140 boxes.push(rotator(x, y, width, height)); 3141 } 3142 } 3143 return boxes.length === 0 ? null : boxes; 3144 } 3145 addChangedExistingAnnotation({ 3146 annotationElementId, 3147 id 3148 }) { 3149 (this.#changedExistingAnnotations ||= new Map()).set(annotationElementId, id); 3150 } 3151 removeChangedExistingAnnotation({ 3152 annotationElementId 3153 }) { 3154 this.#changedExistingAnnotations?.delete(annotationElementId); 3155 } 3156 renderAnnotationElement(annotation) { 3157 const editorId = this.#changedExistingAnnotations?.get(annotation.data.id); 3158 if (!editorId) { 3159 return; 3160 } 3161 const editor = this.#annotationStorage.getRawValue(editorId); 3162 if (!editor) { 3163 return; 3164 } 3165 if (this.#mode === AnnotationEditorType.NONE && !editor.hasBeenModified) { 3166 return; 3167 } 3168 editor.renderAnnotationElement(annotation); 3169 } 3170} 3171 3172;// ./src/display/editor/alt_text.js 3173 3174class AltText { 3175 #altText = null; 3176 #altTextDecorative = false; 3177 #altTextButton = null; 3178 #altTextButtonLabel = null; 3179 #altTextTooltip = null; 3180 #altTextTooltipTimeout = null; 3181 #altTextWasFromKeyBoard = false; 3182 #badge = null; 3183 #editor = null; 3184 #guessedText = null; 3185 #textWithDisclaimer = null; 3186 #useNewAltTextFlow = false; 3187 static #l10nNewButton = null; 3188 static _l10n = null; 3189 constructor(editor) { 3190 this.#editor = editor; 3191 this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow; 3192 AltText.#l10nNewButton ||= Object.freeze({ 3193 added: "pdfjs-editor-new-alt-text-added-button", 3194 "added-label": "pdfjs-editor-new-alt-text-added-button-label", 3195 missing: "pdfjs-editor-new-alt-text-missing-button", 3196 "missing-label": "pdfjs-editor-new-alt-text-missing-button-label", 3197 review: "pdfjs-editor-new-alt-text-to-review-button", 3198 "review-label": "pdfjs-editor-new-alt-text-to-review-button-label" 3199 }); 3200 } 3201 static initialize(l10n) { 3202 AltText._l10n ??= l10n; 3203 } 3204 async render() { 3205 const altText = this.#altTextButton = document.createElement("button"); 3206 altText.className = "altText"; 3207 altText.tabIndex = "0"; 3208 const label = this.#altTextButtonLabel = document.createElement("span"); 3209 altText.append(label); 3210 if (this.#useNewAltTextFlow) { 3211 altText.classList.add("new"); 3212 altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing); 3213 label.setAttribute("data-l10n-id", AltText.#l10nNewButton["missing-label"]); 3214 } else { 3215 altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button"); 3216 label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label"); 3217 } 3218 const signal = this.#editor._uiManager._signal; 3219 altText.addEventListener("contextmenu", noContextMenu, { 3220 signal 3221 }); 3222 altText.addEventListener("pointerdown", event => event.stopPropagation(), { 3223 signal 3224 }); 3225 const onClick = event => { 3226 event.preventDefault(); 3227 this.#editor._uiManager.editAltText(this.#editor); 3228 if (this.#useNewAltTextFlow) { 3229 this.#editor._reportTelemetry({ 3230 action: "pdfjs.image.alt_text.image_status_label_clicked", 3231 data: { 3232 label: this.#label 3233 } 3234 }); 3235 } 3236 }; 3237 altText.addEventListener("click", onClick, { 3238 capture: true, 3239 signal 3240 }); 3241 altText.addEventListener("keydown", event => { 3242 if (event.target === altText && event.key === "Enter") { 3243 this.#altTextWasFromKeyBoard = true; 3244 onClick(event); 3245 } 3246 }, { 3247 signal 3248 }); 3249 await this.#setState(); 3250 return altText; 3251 } 3252 get #label() { 3253 return this.#altText && "added" || this.#altText === null && this.guessedText && "review" || "missing"; 3254 } 3255 finish() { 3256 if (!this.#altTextButton) { 3257 return; 3258 } 3259 this.#altTextButton.focus({ 3260 focusVisible: this.#altTextWasFromKeyBoard 3261 }); 3262 this.#altTextWasFromKeyBoard = false; 3263 } 3264 isEmpty() { 3265 if (this.#useNewAltTextFlow) { 3266 return this.#altText === null; 3267 } 3268 return !this.#altText && !this.#altTextDecorative; 3269 } 3270 hasData() { 3271 if (this.#useNewAltTextFlow) { 3272 return this.#altText !== null || !!this.#guessedText; 3273 } 3274 return this.isEmpty(); 3275 } 3276 get guessedText() { 3277 return this.#guessedText; 3278 } 3279 async setGuessedText(guessedText) { 3280 if (this.#altText !== null) { 3281 return; 3282 } 3283 this.#guessedText = guessedText; 3284 this.#textWithDisclaimer = await AltText._l10n.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", { 3285 generatedAltText: guessedText 3286 }); 3287 this.#setState(); 3288 } 3289 toggleAltTextBadge(visibility = false) { 3290 if (!this.#useNewAltTextFlow || this.#altText) { 3291 this.#badge?.remove(); 3292 this.#badge = null; 3293 return; 3294 } 3295 if (!this.#badge) { 3296 const badge = this.#badge = document.createElement("div"); 3297 badge.className = "noAltTextBadge"; 3298 this.#editor.div.append(badge); 3299 } 3300 this.#badge.classList.toggle("hidden", !visibility); 3301 } 3302 serialize(isForCopying) { 3303 let altText = this.#altText; 3304 if (!isForCopying && this.#guessedText === altText) { 3305 altText = this.#textWithDisclaimer; 3306 } 3307 return { 3308 altText, 3309 decorative: this.#altTextDecorative, 3310 guessedText: this.#guessedText, 3311 textWithDisclaimer: this.#textWithDisclaimer 3312 }; 3313 } 3314 get data() { 3315 return { 3316 altText: this.#altText, 3317 decorative: this.#altTextDecorative 3318 }; 3319 } 3320 set data({ 3321 altText, 3322 decorative, 3323 guessedText, 3324 textWithDisclaimer, 3325 cancel = false 3326 }) { 3327 if (guessedText) { 3328 this.#guessedText = guessedText; 3329 this.#textWithDisclaimer = textWithDisclaimer; 3330 } 3331 if (this.#altText === altText && this.#altTextDecorative === decorative) { 3332 return; 3333 } 3334 if (!cancel) { 3335 this.#altText = altText; 3336 this.#altTextDecorative = decorative; 3337 } 3338 this.#setState(); 3339 } 3340 toggle(enabled = false) { 3341 if (!this.#altTextButton) { 3342 return; 3343 } 3344 if (!enabled && this.#altTextTooltipTimeout) { 3345 clearTimeout(this.#altTextTooltipTimeout); 3346 this.#altTextTooltipTimeout = null; 3347 } 3348 this.#altTextButton.disabled = !enabled; 3349 } 3350 shown() { 3351 this.#editor._reportTelemetry({ 3352 action: "pdfjs.image.alt_text.image_status_label_displayed", 3353 data: { 3354 label: this.#label 3355 } 3356 }); 3357 } 3358 destroy() { 3359 this.#altTextButton?.remove(); 3360 this.#altTextButton = null; 3361 this.#altTextButtonLabel = null; 3362 this.#altTextTooltip = null; 3363 this.#badge?.remove(); 3364 this.#badge = null; 3365 } 3366 async #setState() { 3367 const button = this.#altTextButton; 3368 if (!button) { 3369 return; 3370 } 3371 if (this.#useNewAltTextFlow) { 3372 button.classList.toggle("done", !!this.#altText); 3373 button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]); 3374 this.#altTextButtonLabel?.setAttribute("data-l10n-id", AltText.#l10nNewButton[`${this.#label}-label`]); 3375 if (!this.#altText) { 3376 this.#altTextTooltip?.remove(); 3377 return; 3378 } 3379 } else { 3380 if (!this.#altText && !this.#altTextDecorative) { 3381 button.classList.remove("done"); 3382 this.#altTextTooltip?.remove(); 3383 return; 3384 } 3385 button.classList.add("done"); 3386 button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button"); 3387 } 3388 let tooltip = this.#altTextTooltip; 3389 if (!tooltip) { 3390 this.#altTextTooltip = tooltip = document.createElement("span"); 3391 tooltip.className = "tooltip"; 3392 tooltip.setAttribute("role", "tooltip"); 3393 tooltip.id = `alt-text-tooltip-${this.#editor.id}`; 3394 const DELAY_TO_SHOW_TOOLTIP = 100; 3395 const signal = this.#editor._uiManager._signal; 3396 signal.addEventListener("abort", () => { 3397 clearTimeout(this.#altTextTooltipTimeout); 3398 this.#altTextTooltipTimeout = null; 3399 }, { 3400 once: true 3401 }); 3402 button.addEventListener("mouseenter", () => { 3403 this.#altTextTooltipTimeout = setTimeout(() => { 3404 this.#altTextTooltipTimeout = null; 3405 this.#altTextTooltip.classList.add("show"); 3406 this.#editor._reportTelemetry({ 3407 action: "alt_text_tooltip" 3408 }); 3409 }, DELAY_TO_SHOW_TOOLTIP); 3410 }, { 3411 signal 3412 }); 3413 button.addEventListener("mouseleave", () => { 3414 if (this.#altTextTooltipTimeout) { 3415 clearTimeout(this.#altTextTooltipTimeout); 3416 this.#altTextTooltipTimeout = null; 3417 } 3418 this.#altTextTooltip?.classList.remove("show"); 3419 }, { 3420 signal 3421 }); 3422 } 3423 if (this.#altTextDecorative) { 3424 tooltip.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-decorative-tooltip"); 3425 } else { 3426 tooltip.removeAttribute("data-l10n-id"); 3427 tooltip.textContent = this.#altText; 3428 } 3429 if (!tooltip.parentNode) { 3430 button.append(tooltip); 3431 } 3432 const element = this.#editor.getImageForAltText(); 3433 element?.setAttribute("aria-describedby", tooltip.id); 3434 } 3435} 3436 3437;// ./src/display/editor/editor.js 3438 3439 3440 3441 3442 3443class AnnotationEditor { 3444 #accessibilityData = null; 3445 #allResizerDivs = null; 3446 #altText = null; 3447 #disabled = false;
vendor: 4,328 bytes, lines 3448-3568
3448 #keepAspectRatio = false; 3449 #resizersDiv = null; 3450 #savedDimensions = null; 3451 #focusAC = null; 3452 #focusedResizerName = ""; 3453 #hasBeenClicked = false; 3454 #initialPosition = null; 3455 #isEditing = false; 3456 #isInEditMode = false; 3457 #isResizerEnabledForKeyboard = false; 3458 #moveInDOMTimeout = null; 3459 #prevDragX = 0; 3460 #prevDragY = 0; 3461 #telemetryTimeouts = null; 3462 _editToolbar = null; 3463 _initialOptions = Object.create(null); 3464 _initialData = null; 3465 _isVisible = true; 3466 _uiManager = null; 3467 _focusEventsAllowed = true; 3468 static _l10n = null; 3469 static _l10nResizer = null; 3470 #isDraggable = false; 3471 #zIndex = AnnotationEditor._zIndex++; 3472 static _borderLineWidth = -1; 3473 static _colorManager = new ColorManager(); 3474 static _zIndex = 1; 3475 static _telemetryTimeout = 1000; 3476 static get _resizerKeyboardManager() { 3477 const resize = AnnotationEditor.prototype._resizeWithKeyboard; 3478 const small = AnnotationEditorUIManager.TRANSLATE_SMALL; 3479 const big = AnnotationEditorUIManager.TRANSLATE_BIG; 3480 return shadow(this, "_resizerKeyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], resize, { 3481 args: [-small, 0] 3482 }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], resize, { 3483 args: [-big, 0] 3484 }], [["ArrowRight", "mac+ArrowRight"], resize, { 3485 args: [small, 0] 3486 }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], resize, { 3487 args: [big, 0] 3488 }], [["ArrowUp", "mac+ArrowUp"], resize, { 3489 args: [0, -small] 3490 }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], resize, { 3491 args: [0, -big] 3492 }], [["ArrowDown", "mac+ArrowDown"], resize, { 3493 args: [0, small] 3494 }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], resize, { 3495 args: [0, big] 3496 }], [["Escape", "mac+Escape"], AnnotationEditor.prototype._stopResizingWithKeyboard]])); 3497 } 3498 constructor(parameters) { 3499 this.parent = parameters.parent; 3500 this.id = parameters.id; 3501 this.width = this.height = null; 3502 this.pageIndex = parameters.parent.pageIndex; 3503 this.name = parameters.name; 3504 this.div = null; 3505 this._uiManager = parameters.uiManager; 3506 this.annotationElementId = null; 3507 this._willKeepAspectRatio = false; 3508 this._initialOptions.isCentered = parameters.isCentered; 3509 this._structTreeParentId = null; 3510 const { 3511 rotation, 3512 rawDims: { 3513 pageWidth, 3514 pageHeight, 3515 pageX, 3516 pageY 3517 } 3518 } = this.parent.viewport; 3519 this.rotation = rotation; 3520 this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360; 3521 this.pageDimensions = [pageWidth, pageHeight]; 3522 this.pageTranslation = [pageX, pageY]; 3523 const [width, height] = this.parentDimensions; 3524 this.x = parameters.x / width; 3525 this.y = parameters.y / height; 3526 this.isAttachedToDOM = false; 3527 this.deleted = false; 3528 } 3529 get editorType() { 3530 return Object.getPrototypeOf(this).constructor._type; 3531 } 3532 static get _defaultLineColor() { 3533 return shadow(this, "_defaultLineColor", this._colorManager.getHexCode("CanvasText")); 3534 } 3535 static deleteAnnotationElement(editor) { 3536 const fakeEditor = new FakeEditor({ 3537 id: editor.parent.getNextId(), 3538 parent: editor.parent, 3539 uiManager: editor._uiManager 3540 }); 3541 fakeEditor.annotationElementId = editor.annotationElementId; 3542 fakeEditor.deleted = true; 3543 fakeEditor._uiManager.addToAnnotationStorage(fakeEditor); 3544 } 3545 static initialize(l10n, _uiManager) { 3546 AnnotationEditor._l10n ??= l10n; 3547 AnnotationEditor._l10nResizer ||= Object.freeze({ 3548 topLeft: "pdfjs-editor-resizer-top-left", 3549 topMiddle: "pdfjs-editor-resizer-top-middle", 3550 topRight: "pdfjs-editor-resizer-top-right", 3551 middleRight: "pdfjs-editor-resizer-middle-right", 3552 bottomRight: "pdfjs-editor-resizer-bottom-right", 3553 bottomMiddle: "pdfjs-editor-resizer-bottom-middle", 3554 bottomLeft: "pdfjs-editor-resizer-bottom-left", 3555 middleLeft: "pdfjs-editor-resizer-middle-left" 3556 }); 3557 if (AnnotationEditor._borderLineWidth !== -1) { 3558 return; 3559 } 3560 const style = getComputedStyle(document.documentElement); 3561 AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue("--outline-width")) || 0; 3562 } 3563 static updateDefaultParams(_type, _value) {} 3564 static get defaultPropertiesToUpdate() { 3565 return []; 3566 } 3567 static isHandlingMimeForPasting(mime) { 3568 return false;
vendor: 12,170 bytes, lines 3569-3980
3569 } 3570 static paste(item, parent) { 3571 unreachable("Not implemented"); 3572 } 3573 get propertiesToUpdate() { 3574 return []; 3575 } 3576 get _isDraggable() { 3577 return this.#isDraggable; 3578 } 3579 set _isDraggable(value) { 3580 this.#isDraggable = value; 3581 this.div?.classList.toggle("draggable", value); 3582 } 3583 get isEnterHandled() { 3584 return true; 3585 } 3586 center() { 3587 const [pageWidth, pageHeight] = this.pageDimensions; 3588 switch (this.parentRotation) { 3589 case 90: 3590 this.x -= this.height * pageHeight / (pageWidth * 2); 3591 this.y += this.width * pageWidth / (pageHeight * 2); 3592 break; 3593 case 180: 3594 this.x += this.width / 2; 3595 this.y += this.height / 2; 3596 break; 3597 case 270: 3598 this.x += this.height * pageHeight / (pageWidth * 2); 3599 this.y -= this.width * pageWidth / (pageHeight * 2); 3600 break; 3601 default: 3602 this.x -= this.width / 2; 3603 this.y -= this.height / 2; 3604 break; 3605 } 3606 this.fixAndSetPosition(); 3607 } 3608 addCommands(params) { 3609 this._uiManager.addCommands(params); 3610 } 3611 get currentLayer() { 3612 return this._uiManager.currentLayer; 3613 } 3614 setInBackground() { 3615 this.div.style.zIndex = 0; 3616 } 3617 setInForeground() { 3618 this.div.style.zIndex = this.#zIndex; 3619 } 3620 setParent(parent) { 3621 if (parent !== null) { 3622 this.pageIndex = parent.pageIndex; 3623 this.pageDimensions = parent.pageDimensions; 3624 } else { 3625 this.#stopResizing(); 3626 } 3627 this.parent = parent; 3628 } 3629 focusin(event) { 3630 if (!this._focusEventsAllowed) { 3631 return; 3632 } 3633 if (!this.#hasBeenClicked) { 3634 this.parent.setSelected(this); 3635 } else { 3636 this.#hasBeenClicked = false; 3637 } 3638 } 3639 focusout(event) { 3640 if (!this._focusEventsAllowed) { 3641 return; 3642 } 3643 if (!this.isAttachedToDOM) { 3644 return; 3645 } 3646 const target = event.relatedTarget; 3647 if (target?.closest(`#${this.id}`)) { 3648 return; 3649 } 3650 event.preventDefault(); 3651 if (!this.parent?.isMultipleSelection) { 3652 this.commitOrRemove(); 3653 } 3654 } 3655 commitOrRemove() { 3656 if (this.isEmpty()) { 3657 this.remove(); 3658 } else { 3659 this.commit(); 3660 } 3661 } 3662 commit() { 3663 this.addToAnnotationStorage(); 3664 } 3665 addToAnnotationStorage() { 3666 this._uiManager.addToAnnotationStorage(this); 3667 } 3668 setAt(x, y, tx, ty) { 3669 const [width, height] = this.parentDimensions; 3670 [tx, ty] = this.screenToPageTranslation(tx, ty); 3671 this.x = (x + tx) / width; 3672 this.y = (y + ty) / height; 3673 this.fixAndSetPosition(); 3674 } 3675 #translate([width, height], x, y) { 3676 [x, y] = this.screenToPageTranslation(x, y); 3677 this.x += x / width; 3678 this.y += y / height; 3679 this.fixAndSetPosition(); 3680 } 3681 translate(x, y) { 3682 this.#translate(this.parentDimensions, x, y); 3683 } 3684 translateInPage(x, y) { 3685 this.#initialPosition ||= [this.x, this.y]; 3686 this.#translate(this.pageDimensions, x, y); 3687 this.div.scrollIntoView({ 3688 block: "nearest" 3689 }); 3690 } 3691 drag(tx, ty) { 3692 this.#initialPosition ||= [this.x, this.y]; 3693 const [parentWidth, parentHeight] = this.parentDimensions; 3694 this.x += tx / parentWidth; 3695 this.y += ty / parentHeight; 3696 if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) { 3697 const { 3698 x, 3699 y 3700 } = this.div.getBoundingClientRect(); 3701 if (this.parent.findNewParent(this, x, y)) { 3702 this.x -= Math.floor(this.x); 3703 this.y -= Math.floor(this.y); 3704 } 3705 } 3706 let { 3707 x, 3708 y 3709 } = this; 3710 const [bx, by] = this.getBaseTranslation(); 3711 x += bx; 3712 y += by; 3713 this.div.style.left = `${(100 * x).toFixed(2)}%`; 3714 this.div.style.top = `${(100 * y).toFixed(2)}%`; 3715 this.div.scrollIntoView({ 3716 block: "nearest" 3717 }); 3718 } 3719 get _hasBeenMoved() { 3720 return !!this.#initialPosition && (this.#initialPosition[0] !== this.x || this.#initialPosition[1] !== this.y); 3721 } 3722 getBaseTranslation() { 3723 const [parentWidth, parentHeight] = this.parentDimensions; 3724 const { 3725 _borderLineWidth 3726 } = AnnotationEditor; 3727 const x = _borderLineWidth / parentWidth; 3728 const y = _borderLineWidth / parentHeight; 3729 switch (this.rotation) { 3730 case 90: 3731 return [-x, y]; 3732 case 180: 3733 return [x, y]; 3734 case 270: 3735 return [x, -y]; 3736 default: 3737 return [-x, -y]; 3738 } 3739 } 3740 get _mustFixPosition() { 3741 return true; 3742 } 3743 fixAndSetPosition(rotation = this.rotation) { 3744 const [pageWidth, pageHeight] = this.pageDimensions; 3745 let { 3746 x, 3747 y, 3748 width, 3749 height 3750 } = this; 3751 width *= pageWidth; 3752 height *= pageHeight; 3753 x *= pageWidth; 3754 y *= pageHeight; 3755 if (this._mustFixPosition) { 3756 switch (rotation) { 3757 case 0: 3758 x = Math.max(0, Math.min(pageWidth - width, x)); 3759 y = Math.max(0, Math.min(pageHeight - height, y)); 3760 break; 3761 case 90: 3762 x = Math.max(0, Math.min(pageWidth - height, x)); 3763 y = Math.min(pageHeight, Math.max(width, y)); 3764 break; 3765 case 180: 3766 x = Math.min(pageWidth, Math.max(width, x)); 3767 y = Math.min(pageHeight, Math.max(height, y)); 3768 break; 3769 case 270: 3770 x = Math.min(pageWidth, Math.max(height, x)); 3771 y = Math.max(0, Math.min(pageHeight - width, y)); 3772 break; 3773 } 3774 } 3775 this.x = x /= pageWidth; 3776 this.y = y /= pageHeight; 3777 const [bx, by] = this.getBaseTranslation(); 3778 x += bx; 3779 y += by; 3780 const { 3781 style 3782 } = this.div; 3783 style.left = `${(100 * x).toFixed(2)}%`; 3784 style.top = `${(100 * y).toFixed(2)}%`; 3785 this.moveInDOM(); 3786 } 3787 static #rotatePoint(x, y, angle) { 3788 switch (angle) { 3789 case 90: 3790 return [y, -x]; 3791 case 180: 3792 return [-x, -y]; 3793 case 270: 3794 return [-y, x]; 3795 default: 3796 return [x, y]; 3797 } 3798 } 3799 screenToPageTranslation(x, y) { 3800 return AnnotationEditor.#rotatePoint(x, y, this.parentRotation); 3801 } 3802 pageTranslationToScreen(x, y) { 3803 return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation); 3804 } 3805 #getRotationMatrix(rotation) { 3806 switch (rotation) { 3807 case 90: 3808 { 3809 const [pageWidth, pageHeight] = this.pageDimensions; 3810 return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0]; 3811 } 3812 case 180: 3813 return [-1, 0, 0, -1]; 3814 case 270: 3815 { 3816 const [pageWidth, pageHeight] = this.pageDimensions; 3817 return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0]; 3818 } 3819 default: 3820 return [1, 0, 0, 1]; 3821 } 3822 } 3823 get parentScale() { 3824 return this._uiManager.viewParameters.realScale; 3825 } 3826 get parentRotation() { 3827 return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360; 3828 } 3829 get parentDimensions() { 3830 const { 3831 parentScale, 3832 pageDimensions: [pageWidth, pageHeight] 3833 } = this; 3834 return [pageWidth * parentScale, pageHeight * parentScale]; 3835 } 3836 setDims(width, height) { 3837 const [parentWidth, parentHeight] = this.parentDimensions; 3838 this.div.style.width = `${(100 * width / parentWidth).toFixed(2)}%`; 3839 if (!this.#keepAspectRatio) { 3840 this.div.style.height = `${(100 * height / parentHeight).toFixed(2)}%`; 3841 } 3842 } 3843 fixDims() { 3844 const { 3845 style 3846 } = this.div; 3847 const { 3848 height, 3849 width 3850 } = style; 3851 const widthPercent = width.endsWith("%"); 3852 const heightPercent = !this.#keepAspectRatio && height.endsWith("%"); 3853 if (widthPercent && heightPercent) { 3854 return; 3855 } 3856 const [parentWidth, parentHeight] = this.parentDimensions; 3857 if (!widthPercent) { 3858 style.width = `${(100 * parseFloat(width) / parentWidth).toFixed(2)}%`; 3859 } 3860 if (!this.#keepAspectRatio && !heightPercent) { 3861 style.height = `${(100 * parseFloat(height) / parentHeight).toFixed(2)}%`; 3862 } 3863 } 3864 getInitialTranslation() { 3865 return [0, 0]; 3866 } 3867 #createResizers() { 3868 if (this.#resizersDiv) { 3869 return; 3870 } 3871 this.#resizersDiv = document.createElement("div"); 3872 this.#resizersDiv.classList.add("resizers"); 3873 const classes = this._willKeepAspectRatio ? ["topLeft", "topRight", "bottomRight", "bottomLeft"] : ["topLeft", "topMiddle", "topRight", "middleRight", "bottomRight", "bottomMiddle", "bottomLeft", "middleLeft"]; 3874 const signal = this._uiManager._signal; 3875 for (const name of classes) { 3876 const div = document.createElement("div"); 3877 this.#resizersDiv.append(div); 3878 div.classList.add("resizer", name); 3879 div.setAttribute("data-resizer-name", name); 3880 div.addEventListener("pointerdown", this.#resizerPointerdown.bind(this, name), { 3881 signal 3882 }); 3883 div.addEventListener("contextmenu", noContextMenu, { 3884 signal 3885 }); 3886 div.tabIndex = -1; 3887 } 3888 this.div.prepend(this.#resizersDiv); 3889 } 3890 #resizerPointerdown(name, event) { 3891 event.preventDefault(); 3892 const { 3893 isMac 3894 } = util_FeatureTest.platform; 3895 if (event.button !== 0 || event.ctrlKey && isMac) { 3896 return; 3897 } 3898 this.#altText?.toggle(false); 3899 const savedDraggable = this._isDraggable; 3900 this._isDraggable = false; 3901 const ac = new AbortController(); 3902 const signal = this._uiManager.combinedSignal(ac); 3903 this.parent.togglePointerEvents(false); 3904 window.addEventListener("pointermove", this.#resizerPointermove.bind(this, name), { 3905 passive: true, 3906 capture: true, 3907 signal 3908 }); 3909 window.addEventListener("contextmenu", noContextMenu, { 3910 signal 3911 }); 3912 const savedX = this.x; 3913 const savedY = this.y; 3914 const savedWidth = this.width; 3915 const savedHeight = this.height; 3916 const savedParentCursor = this.parent.div.style.cursor; 3917 const savedCursor = this.div.style.cursor; 3918 this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor; 3919 const pointerUpCallback = () => { 3920 ac.abort(); 3921 this.parent.togglePointerEvents(true); 3922 this.#altText?.toggle(true); 3923 this._isDraggable = savedDraggable; 3924 this.parent.div.style.cursor = savedParentCursor; 3925 this.div.style.cursor = savedCursor; 3926 this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight); 3927 }; 3928 window.addEventListener("pointerup", pointerUpCallback, { 3929 signal 3930 }); 3931 window.addEventListener("blur", pointerUpCallback, { 3932 signal 3933 }); 3934 } 3935 #addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight) { 3936 const newX = this.x; 3937 const newY = this.y; 3938 const newWidth = this.width; 3939 const newHeight = this.height; 3940 if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) { 3941 return; 3942 } 3943 this.addCommands({ 3944 cmd: () => { 3945 this.width = newWidth; 3946 this.height = newHeight; 3947 this.x = newX; 3948 this.y = newY; 3949 const [parentWidth, parentHeight] = this.parentDimensions; 3950 this.setDims(parentWidth * newWidth, parentHeight * newHeight); 3951 this.fixAndSetPosition(); 3952 }, 3953 undo: () => { 3954 this.width = savedWidth; 3955 this.height = savedHeight; 3956 this.x = savedX; 3957 this.y = savedY; 3958 const [parentWidth, parentHeight] = this.parentDimensions; 3959 this.setDims(parentWidth * savedWidth, parentHeight * savedHeight); 3960 this.fixAndSetPosition(); 3961 }, 3962 mustExec: true 3963 }); 3964 } 3965 #resizerPointermove(name, event) { 3966 const [parentWidth, parentHeight] = this.parentDimensions; 3967 const savedX = this.x; 3968 const savedY = this.y; 3969 const savedWidth = this.width; 3970 const savedHeight = this.height; 3971 const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; 3972 const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; 3973 const round = x => Math.round(x * 10000) / 10000; 3974 const rotationMatrix = this.#getRotationMatrix(this.rotation); 3975 const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; 3976 const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation); 3977 const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y]; 3978 let getPoint; 3979 let getOpposite; 3980 let isDiagonal = false;
3981 let isHorizontal = false; 3982 switch (name) { 3983 case "topLeft": 3984 isDiagonal = true; 3985 getPoint = (w, h) => [0, 0]; 3986 getOpposite = (w, h) => [w, h]; 3987 break; 3988 case "topMiddle": 3989 getPoint = (w, h) => [w / 2, 0]; 3990 getOpposite = (w, h) => [w / 2, h]; 3991 break; 3992 case "topRight": 3993 isDiagonal = true; 3994 getPoint = (w, h) => [w, 0]; 3995 getOpposite = (w, h) => [0, h]; 3996 break; 3997 case "middleRight": 3998 isHorizontal = true; 3999 getPoint = (w, h) => [w, h / 2]; 4000 getOpposite = (w, h) => [0, h / 2]; 4001 break; 4002 case "bottomRight": 4003 isDiagonal = true; 4004 getPoint = (w, h) => [w, h]; 4005 getOpposite = (w, h) => [0, 0]; 4006 break; 4007 case "bottomMiddle": 4008 getPoint = (w, h) => [w / 2, h]; 4009 getOpposite = (w, h) => [w / 2, 0]; 4010 break; 4011 case "bottomLeft": 4012 isDiagonal = true; 4013 getPoint = (w, h) => [0, h]; 4014 getOpposite = (w, h) => [w, 0]; 4015 break; 4016 case "middleLeft": 4017 isHorizontal = true; 4018 getPoint = (w, h) => [0, h / 2]; 4019 getOpposite = (w, h) => [w, h / 2]; 4020 break; 4021 } 4022 const point = getPoint(savedWidth, savedHeight); 4023 const oppositePoint = getOpposite(savedWidth, savedHeight); 4024 let transfOppositePoint = transf(...oppositePoint); 4025 const oppositeX = round(savedX + transfOppositePoint[0]); 4026 const oppositeY = round(savedY + transfOppositePoint[1]); 4027 let ratioX = 1; 4028 let ratioY = 1; 4029 let [deltaX, deltaY] = this.screenToPageTranslation(event.movementX, event.movementY); 4030 [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight); 4031 if (isDiagonal) { 4032 const oldDiag = Math.hypot(savedWidth, savedHeight); 4033 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); 4034 } else if (isHorizontal) { 4035 ratioX = Math.max(minWidth, Math.min(1, Math.abs(oppositePoint[0] - point[0] - deltaX))) / savedWidth; 4036 } else { 4037 ratioY = Math.max(minHeight, Math.min(1, Math.abs(oppositePoint[1] - point[1] - deltaY))) / savedHeight; 4038 } 4039 const newWidth = round(savedWidth * ratioX); 4040 const newHeight = round(savedHeight * ratioY); 4041 transfOppositePoint = transf(...getOpposite(newWidth, newHeight)); 4042 const newX = oppositeX - transfOppositePoint[0]; 4043 const newY = oppositeY - transfOppositePoint[1]; 4044 this.width = newWidth; 4045 this.height = newHeight; 4046 this.x = newX; 4047 this.y = newY; 4048 this.setDims(parentWidth * newWidth, parentHeight * newHeight); 4049 this.fixAndSetPosition(); 4050 } 4051 altTextFinish() { 4052 this.#altText?.finish(); 4053 } 4054 async addEditToolbar() { 4055 if (this._editToolbar || this.#isInEditMode) { 4056 return this._editToolbar; 4057 } 4058 this._editToolbar = new EditorToolbar(this); 4059 this.div.append(this._editToolbar.render()); 4060 if (this.#altText) { 4061 await this._editToolbar.addAltText(this.#altText); 4062 } 4063 return this._editToolbar; 4064 } 4065 removeEditToolbar() { 4066 if (!this._editToolbar) { 4067 return; 4068 } 4069 this._editToolbar.remove(); 4070 this._editToolbar = null; 4071 this.#altText?.destroy(); 4072 } 4073 addContainer(container) { 4074 const editToolbarDiv = this._editToolbar?.div; 4075 if (editToolbarDiv) { 4076 editToolbarDiv.before(container); 4077 } else { 4078 this.div.append(container); 4079 } 4080 } 4081 getClientDimensions() { 4082 return this.div.getBoundingClientRect(); 4083 } 4084 async addAltTextButton() { 4085 if (this.#altText) { 4086 return; 4087 } 4088 AltText.initialize(AnnotationEditor._l10n); 4089 this.#altText = new AltText(this); 4090 if (this.#accessibilityData) { 4091 this.#altText.data = this.#accessibilityData; 4092 this.#accessibilityData = null; 4093 } 4094 await this.addEditToolbar(); 4095 } 4096 get altTextData() { 4097 return this.#altText?.data; 4098 } 4099 set altTextData(data) { 4100 if (!this.#altText) { 4101 return; 4102 } 4103 this.#altText.data = data; 4104 } 4105 get guessedAltText() { 4106 return this.#altText?.guessedText; 4107 } 4108 async setGuessedAltText(text) { 4109 await this.#altText?.setGuessedText(text); 4110 } 4111 serializeAltText(isForCopying) { 4112 return this.#altText?.serialize(isForCopying); 4113 } 4114 hasAltText() { 4115 return !!this.#altText && !this.#altText.isEmpty(); 4116 } 4117 hasAltTextData() { 4118 return this.#altText?.hasData() ?? false; 4119 } 4120 render() { 4121 this.div = document.createElement("div"); 4122 this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360); 4123 this.div.className = this.name; 4124 this.div.setAttribute("id", this.id); 4125 this.div.tabIndex = this.#disabled ? -1 : 0; 4126 if (!this._isVisible) { 4127 this.div.classList.add("hidden"); 4128 } 4129 this.setInForeground(); 4130 this.#addFocusListeners(); 4131 const [parentWidth, parentHeight] = this.parentDimensions; 4132 if (this.parentRotation % 180 !== 0) { 4133 this.div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`; 4134 this.div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`; 4135 } 4136 const [tx, ty] = this.getInitialTranslation(); 4137 this.translate(tx, ty); 4138 bindEvents(this, this.div, ["pointerdown"]); 4139 return this.div; 4140 } 4141 pointerdown(event) { 4142 const { 4143 isMac 4144 } = util_FeatureTest.platform; 4145 if (event.button !== 0 || event.ctrlKey && isMac) { 4146 event.preventDefault(); 4147 return; 4148 } 4149 this.#hasBeenClicked = true; 4150 if (this._isDraggable) { 4151 this.#setUpDragSession(event); 4152 return; 4153 } 4154 this.#selectOnPointerEvent(event); 4155 } 4156 get isSelected() { 4157 return this._uiManager.isSelected(this); 4158 } 4159 #selectOnPointerEvent(event) { 4160 const { 4161 isMac 4162 } = util_FeatureTest.platform; 4163 if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) { 4164 this.parent.toggleSelected(this); 4165 } else { 4166 this.parent.setSelected(this); 4167 } 4168 } 4169 #setUpDragSession(event) { 4170 const { 4171 isSelected 4172 } = this; 4173 this._uiManager.setUpDragSession(); 4174 const ac = new AbortController(); 4175 const signal = this._uiManager.combinedSignal(ac); 4176 if (isSelected) { 4177 this.div.classList.add("moving"); 4178 this.#prevDragX = event.clientX; 4179 this.#prevDragY = event.clientY; 4180 const pointerMoveCallback = e => { 4181 const { 4182 clientX: x, 4183 clientY: y 4184 } = e; 4185 const [tx, ty] = this.screenToPageTranslation(x - this.#prevDragX, y - this.#prevDragY); 4186 this.#prevDragX = x;
vendor: 5,152 bytes, lines 4187-4360
4187 this.#prevDragY = y; 4188 this._uiManager.dragSelectedEditors(tx, ty); 4189 }; 4190 window.addEventListener("pointermove", pointerMoveCallback, { 4191 passive: true, 4192 capture: true, 4193 signal 4194 }); 4195 } 4196 const pointerUpCallback = () => { 4197 ac.abort(); 4198 if (isSelected) { 4199 this.div.classList.remove("moving"); 4200 } 4201 this.#hasBeenClicked = false; 4202 if (!this._uiManager.endDragSession()) { 4203 this.#selectOnPointerEvent(event); 4204 } 4205 }; 4206 window.addEventListener("pointerup", pointerUpCallback, { 4207 signal 4208 }); 4209 window.addEventListener("blur", pointerUpCallback, { 4210 signal 4211 }); 4212 } 4213 moveInDOM() { 4214 if (this.#moveInDOMTimeout) { 4215 clearTimeout(this.#moveInDOMTimeout); 4216 } 4217 this.#moveInDOMTimeout = setTimeout(() => { 4218 this.#moveInDOMTimeout = null; 4219 this.parent?.moveEditorInDOM(this); 4220 }, 0); 4221 } 4222 _setParentAndPosition(parent, x, y) { 4223 parent.changeParent(this); 4224 this.x = x; 4225 this.y = y; 4226 this.fixAndSetPosition(); 4227 } 4228 getRect(tx, ty, rotation = this.rotation) { 4229 const scale = this.parentScale; 4230 const [pageWidth, pageHeight] = this.pageDimensions; 4231 const [pageX, pageY] = this.pageTranslation; 4232 const shiftX = tx / scale; 4233 const shiftY = ty / scale; 4234 const x = this.x * pageWidth; 4235 const y = this.y * pageHeight; 4236 const width = this.width * pageWidth; 4237 const height = this.height * pageHeight; 4238 switch (rotation) { 4239 case 0: 4240 return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY]; 4241 case 90: 4242 return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY]; 4243 case 180: 4244 return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY]; 4245 case 270: 4246 return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY]; 4247 default: 4248 throw new Error("Invalid rotation"); 4249 } 4250 } 4251 getRectInCurrentCoords(rect, pageHeight) { 4252 const [x1, y1, x2, y2] = rect; 4253 const width = x2 - x1; 4254 const height = y2 - y1; 4255 switch (this.rotation) { 4256 case 0: 4257 return [x1, pageHeight - y2, width, height]; 4258 case 90: 4259 return [x1, pageHeight - y1, height, width]; 4260 case 180: 4261 return [x2, pageHeight - y1, width, height]; 4262 case 270: 4263 return [x2, pageHeight - y2, height, width]; 4264 default: 4265 throw new Error("Invalid rotation"); 4266 } 4267 } 4268 onceAdded() {} 4269 isEmpty() { 4270 return false; 4271 } 4272 enableEditMode() { 4273 this.#isInEditMode = true; 4274 } 4275 disableEditMode() { 4276 this.#isInEditMode = false; 4277 } 4278 isInEditMode() { 4279 return this.#isInEditMode; 4280 } 4281 shouldGetKeyboardEvents() { 4282 return this.#isResizerEnabledForKeyboard; 4283 } 4284 needsToBeRebuilt() { 4285 return this.div && !this.isAttachedToDOM; 4286 } 4287 #addFocusListeners() { 4288 if (this.#focusAC || !this.div) { 4289 return; 4290 } 4291 this.#focusAC = new AbortController(); 4292 const signal = this._uiManager.combinedSignal(this.#focusAC); 4293 this.div.addEventListener("focusin", this.focusin.bind(this), { 4294 signal 4295 }); 4296 this.div.addEventListener("focusout", this.focusout.bind(this), { 4297 signal 4298 }); 4299 } 4300 rebuild() { 4301 this.#addFocusListeners(); 4302 } 4303 rotate(_angle) {} 4304 serializeDeleted() { 4305 return { 4306 id: this.annotationElementId, 4307 deleted: true, 4308 pageIndex: this.pageIndex, 4309 popupRef: this._initialData?.popupRef || "" 4310 }; 4311 } 4312 serialize(isForCopying = false, context = null) { 4313 unreachable("An editor must be serializable"); 4314 } 4315 static async deserialize(data, parent, uiManager) { 4316 const editor = new this.prototype.constructor({ 4317 parent, 4318 id: parent.getNextId(), 4319 uiManager 4320 }); 4321 editor.rotation = data.rotation; 4322 editor.#accessibilityData = data.accessibilityData; 4323 const [pageWidth, pageHeight] = editor.pageDimensions; 4324 const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight); 4325 editor.x = x / pageWidth; 4326 editor.y = y / pageHeight; 4327 editor.width = width / pageWidth; 4328 editor.height = height / pageHeight; 4329 return editor; 4330 } 4331 get hasBeenModified() { 4332 return !!this.annotationElementId && (this.deleted || this.serialize() !== null); 4333 } 4334 remove() { 4335 this.#focusAC?.abort(); 4336 this.#focusAC = null; 4337 if (!this.isEmpty()) { 4338 this.commit(); 4339 } 4340 if (this.parent) { 4341 this.parent.remove(this); 4342 } else { 4343 this._uiManager.removeEditor(this); 4344 } 4345 if (this.#moveInDOMTimeout) { 4346 clearTimeout(this.#moveInDOMTimeout); 4347 this.#moveInDOMTimeout = null; 4348 } 4349 this.#stopResizing(); 4350 this.removeEditToolbar(); 4351 if (this.#telemetryTimeouts) { 4352 for (const timeout of this.#telemetryTimeouts.values()) { 4353 clearTimeout(timeout); 4354 } 4355 this.#telemetryTimeouts = null; 4356 } 4357 this.parent = null; 4358 } 4359 get isResizable() { 4360 return false;
vendor: 10,691 bytes, lines 4361-4739
4361 } 4362 makeResizable() { 4363 if (this.isResizable) { 4364 this.#createResizers(); 4365 this.#resizersDiv.classList.remove("hidden"); 4366 bindEvents(this, this.div, ["keydown"]); 4367 } 4368 } 4369 get toolbarPosition() { 4370 return null; 4371 } 4372 keydown(event) { 4373 if (!this.isResizable || event.target !== this.div || event.key !== "Enter") { 4374 return; 4375 } 4376 this._uiManager.setSelected(this); 4377 this.#savedDimensions = { 4378 savedX: this.x, 4379 savedY: this.y, 4380 savedWidth: this.width, 4381 savedHeight: this.height 4382 }; 4383 const children = this.#resizersDiv.children; 4384 if (!this.#allResizerDivs) { 4385 this.#allResizerDivs = Array.from(children); 4386 const boundResizerKeydown = this.#resizerKeydown.bind(this); 4387 const boundResizerBlur = this.#resizerBlur.bind(this); 4388 const signal = this._uiManager._signal; 4389 for (const div of this.#allResizerDivs) { 4390 const name = div.getAttribute("data-resizer-name"); 4391 div.setAttribute("role", "spinbutton"); 4392 div.addEventListener("keydown", boundResizerKeydown, { 4393 signal 4394 }); 4395 div.addEventListener("blur", boundResizerBlur, { 4396 signal 4397 }); 4398 div.addEventListener("focus", this.#resizerFocus.bind(this, name), { 4399 signal 4400 }); 4401 div.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); 4402 } 4403 } 4404 const first = this.#allResizerDivs[0]; 4405 let firstPosition = 0; 4406 for (const div of children) { 4407 if (div === first) { 4408 break; 4409 } 4410 firstPosition++; 4411 } 4412 const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4); 4413 if (nextFirstPosition !== firstPosition) { 4414 if (nextFirstPosition < firstPosition) { 4415 for (let i = 0; i < firstPosition - nextFirstPosition; i++) { 4416 this.#resizersDiv.append(this.#resizersDiv.firstChild); 4417 } 4418 } else if (nextFirstPosition > firstPosition) { 4419 for (let i = 0; i < nextFirstPosition - firstPosition; i++) { 4420 this.#resizersDiv.firstChild.before(this.#resizersDiv.lastChild); 4421 } 4422 } 4423 let i = 0; 4424 for (const child of children) { 4425 const div = this.#allResizerDivs[i++]; 4426 const name = div.getAttribute("data-resizer-name"); 4427 child.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); 4428 } 4429 } 4430 this.#setResizerTabIndex(0); 4431 this.#isResizerEnabledForKeyboard = true; 4432 this.#resizersDiv.firstChild.focus({ 4433 focusVisible: true 4434 }); 4435 event.preventDefault(); 4436 event.stopImmediatePropagation(); 4437 } 4438 #resizerKeydown(event) { 4439 AnnotationEditor._resizerKeyboardManager.exec(this, event); 4440 } 4441 #resizerBlur(event) { 4442 if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) { 4443 this.#stopResizing(); 4444 } 4445 } 4446 #resizerFocus(name) { 4447 this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : ""; 4448 } 4449 #setResizerTabIndex(value) { 4450 if (!this.#allResizerDivs) { 4451 return; 4452 } 4453 for (const div of this.#allResizerDivs) { 4454 div.tabIndex = value; 4455 } 4456 } 4457 _resizeWithKeyboard(x, y) { 4458 if (!this.#isResizerEnabledForKeyboard) { 4459 return; 4460 } 4461 this.#resizerPointermove(this.#focusedResizerName, { 4462 movementX: x, 4463 movementY: y 4464 }); 4465 } 4466 #stopResizing() { 4467 this.#isResizerEnabledForKeyboard = false; 4468 this.#setResizerTabIndex(-1); 4469 if (this.#savedDimensions) { 4470 const { 4471 savedX, 4472 savedY, 4473 savedWidth, 4474 savedHeight 4475 } = this.#savedDimensions; 4476 this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight); 4477 this.#savedDimensions = null; 4478 } 4479 } 4480 _stopResizingWithKeyboard() { 4481 this.#stopResizing(); 4482 this.div.focus(); 4483 } 4484 select() { 4485 this.makeResizable(); 4486 this.div?.classList.add("selectedEditor"); 4487 if (!this._editToolbar) { 4488 this.addEditToolbar().then(() => { 4489 if (this.div?.classList.contains("selectedEditor")) { 4490 this._editToolbar?.show(); 4491 } 4492 }); 4493 return; 4494 } 4495 this._editToolbar?.show(); 4496 this.#altText?.toggleAltTextBadge(false); 4497 } 4498 unselect() { 4499 this.#resizersDiv?.classList.add("hidden"); 4500 this.div?.classList.remove("selectedEditor"); 4501 if (this.div?.contains(document.activeElement)) { 4502 this._uiManager.currentLayer.div.focus({ 4503 preventScroll: true 4504 }); 4505 } 4506 this._editToolbar?.hide(); 4507 this.#altText?.toggleAltTextBadge(true); 4508 } 4509 updateParams(type, value) {} 4510 disableEditing() {} 4511 enableEditing() {} 4512 enterInEditMode() {} 4513 getImageForAltText() { 4514 return null; 4515 } 4516 get contentDiv() { 4517 return this.div; 4518 } 4519 get isEditing() { 4520 return this.#isEditing; 4521 } 4522 set isEditing(value) { 4523 this.#isEditing = value; 4524 if (!this.parent) { 4525 return; 4526 } 4527 if (value) { 4528 this.parent.setSelected(this); 4529 this.parent.setActiveEditor(this); 4530 } else { 4531 this.parent.setActiveEditor(null); 4532 } 4533 } 4534 setAspectRatio(width, height) { 4535 this.#keepAspectRatio = true; 4536 const aspectRatio = width / height; 4537 const { 4538 style 4539 } = this.div; 4540 style.aspectRatio = aspectRatio; 4541 style.height = "auto"; 4542 } 4543 static get MIN_SIZE() { 4544 return 16; 4545 } 4546 static canCreateNewEmptyEditor() { 4547 return true; 4548 } 4549 get telemetryInitialData() { 4550 return { 4551 action: "added" 4552 }; 4553 } 4554 get telemetryFinalData() { 4555 return null; 4556 } 4557 _reportTelemetry(data, mustWait = false) { 4558 if (mustWait) { 4559 this.#telemetryTimeouts ||= new Map(); 4560 const { 4561 action 4562 } = data; 4563 let timeout = this.#telemetryTimeouts.get(action); 4564 if (timeout) { 4565 clearTimeout(timeout); 4566 } 4567 timeout = setTimeout(() => { 4568 this._reportTelemetry(data); 4569 this.#telemetryTimeouts.delete(action); 4570 if (this.#telemetryTimeouts.size === 0) { 4571 this.#telemetryTimeouts = null; 4572 } 4573 }, AnnotationEditor._telemetryTimeout); 4574 this.#telemetryTimeouts.set(action, timeout); 4575 return; 4576 } 4577 data.type ||= this.editorType; 4578 this._uiManager._eventBus.dispatch("reporttelemetry", { 4579 source: this, 4580 details: { 4581 type: "editing", 4582 data 4583 } 4584 }); 4585 } 4586 show(visible = this._isVisible) { 4587 this.div.classList.toggle("hidden", !visible); 4588 this._isVisible = visible; 4589 } 4590 enable() { 4591 if (this.div) { 4592 this.div.tabIndex = 0; 4593 } 4594 this.#disabled = false; 4595 } 4596 disable() { 4597 if (this.div) { 4598 this.div.tabIndex = -1; 4599 } 4600 this.#disabled = true; 4601 } 4602 renderAnnotationElement(annotation) { 4603 let content = annotation.container.querySelector(".annotationContent"); 4604 if (!content) { 4605 content = document.createElement("div"); 4606 content.classList.add("annotationContent", this.editorType); 4607 annotation.container.prepend(content); 4608 } else if (content.nodeName === "CANVAS") { 4609 const canvas = content; 4610 content = document.createElement("div"); 4611 content.classList.add("annotationContent", this.editorType); 4612 canvas.before(content); 4613 } 4614 return content; 4615 } 4616 resetAnnotationElement(annotation) { 4617 const { 4618 firstChild 4619 } = annotation.container; 4620 if (firstChild?.nodeName === "DIV" && firstChild.classList.contains("annotationContent")) { 4621 firstChild.remove(); 4622 } 4623 } 4624} 4625class FakeEditor extends AnnotationEditor { 4626 constructor(params) { 4627 super(params); 4628 this.annotationElementId = params.annotationElementId; 4629 this.deleted = true; 4630 } 4631 serialize() { 4632 return this.serializeDeleted(); 4633 } 4634} 4635 4636;// ./src/shared/murmurhash3.js 4637const SEED = 0xc3d2e1f0; 4638const MASK_HIGH = 0xffff0000; 4639const MASK_LOW = 0xffff; 4640class MurmurHash3_64 { 4641 constructor(seed) { 4642 this.h1 = seed ? seed & 0xffffffff : SEED; 4643 this.h2 = seed ? seed & 0xffffffff : SEED; 4644 } 4645 update(input) { 4646 let data, length; 4647 if (typeof input === "string") { 4648 data = new Uint8Array(input.length * 2); 4649 length = 0; 4650 for (let i = 0, ii = input.length; i < ii; i++) { 4651 const code = input.charCodeAt(i); 4652 if (code <= 0xff) { 4653 data[length++] = code; 4654 } else { 4655 data[length++] = code >>> 8; 4656 data[length++] = code & 0xff; 4657 } 4658 } 4659 } else if (ArrayBuffer.isView(input)) { 4660 data = input.slice(); 4661 length = data.byteLength; 4662 } else { 4663 throw new Error("Invalid data format, must be a string or TypedArray."); 4664 } 4665 const blockCounts = length >> 2; 4666 const tailLength = length - blockCounts * 4; 4667 const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts); 4668 let k1 = 0, 4669 k2 = 0; 4670 let h1 = this.h1, 4671 h2 = this.h2; 4672 const C1 = 0xcc9e2d51, 4673 C2 = 0x1b873593; 4674 const C1_LOW = C1 & MASK_LOW, 4675 C2_LOW = C2 & MASK_LOW; 4676 for (let i = 0; i < blockCounts; i++) { 4677 if (i & 1) { 4678 k1 = dataUint32[i]; 4679 k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; 4680 k1 = k1 << 15 | k1 >>> 17; 4681 k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; 4682 h1 ^= k1; 4683 h1 = h1 << 13 | h1 >>> 19; 4684 h1 = h1 * 5 + 0xe6546b64; 4685 } else { 4686 k2 = dataUint32[i]; 4687 k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW; 4688 k2 = k2 << 15 | k2 >>> 17; 4689 k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW; 4690 h2 ^= k2; 4691 h2 = h2 << 13 | h2 >>> 19; 4692 h2 = h2 * 5 + 0xe6546b64; 4693 } 4694 } 4695 k1 = 0; 4696 switch (tailLength) { 4697 case 3: 4698 k1 ^= data[blockCounts * 4 + 2] << 16; 4699 case 2: 4700 k1 ^= data[blockCounts * 4 + 1] << 8; 4701 case 1: 4702 k1 ^= data[blockCounts * 4]; 4703 k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; 4704 k1 = k1 << 15 | k1 >>> 17; 4705 k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; 4706 if (blockCounts & 1) { 4707 h1 ^= k1; 4708 } else { 4709 h2 ^= k1; 4710 } 4711 } 4712 this.h1 = h1; 4713 this.h2 = h2; 4714 } 4715 hexdigest() { 4716 let h1 = this.h1, 4717 h2 = this.h2; 4718 h1 ^= h2 >>> 1; 4719 h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW; 4720 h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16; 4721 h1 ^= h2 >>> 1; 4722 h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW; 4723 h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16; 4724 h1 ^= h2 >>> 1; 4725 return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0"); 4726 } 4727} 4728 4729;// ./src/display/annotation_storage.js 4730 4731 4732 4733const SerializableEmpty = Object.freeze({ 4734 map: null, 4735 hash: "", 4736 transfer: undefined 4737}); 4738class AnnotationStorage { 4739 #modified = false;
vendor: 6,371 bytes, lines 4740-4986
4740 #modifiedIds = null; 4741 #storage = new Map(); 4742 constructor() { 4743 this.onSetModified = null; 4744 this.onResetModified = null; 4745 this.onAnnotationEditor = null; 4746 } 4747 getValue(key, defaultValue) { 4748 const value = this.#storage.get(key); 4749 if (value === undefined) { 4750 return defaultValue; 4751 } 4752 return Object.assign(defaultValue, value); 4753 } 4754 getRawValue(key) { 4755 return this.#storage.get(key); 4756 } 4757 remove(key) { 4758 this.#storage.delete(key); 4759 if (this.#storage.size === 0) { 4760 this.resetModified(); 4761 } 4762 if (typeof this.onAnnotationEditor === "function") { 4763 for (const value of this.#storage.values()) { 4764 if (value instanceof AnnotationEditor) { 4765 return; 4766 } 4767 } 4768 this.onAnnotationEditor(null); 4769 } 4770 } 4771 setValue(key, value) { 4772 const obj = this.#storage.get(key); 4773 let modified = false; 4774 if (obj !== undefined) { 4775 for (const [entry, val] of Object.entries(value)) { 4776 if (obj[entry] !== val) { 4777 modified = true; 4778 obj[entry] = val; 4779 } 4780 } 4781 } else { 4782 modified = true; 4783 this.#storage.set(key, value); 4784 } 4785 if (modified) { 4786 this.#setModified(); 4787 } 4788 if (value instanceof AnnotationEditor && typeof this.onAnnotationEditor === "function") { 4789 this.onAnnotationEditor(value.constructor._type); 4790 } 4791 } 4792 has(key) { 4793 return this.#storage.has(key); 4794 } 4795 getAll() { 4796 return this.#storage.size > 0 ? objectFromMap(this.#storage) : null; 4797 } 4798 setAll(obj) { 4799 for (const [key, val] of Object.entries(obj)) { 4800 this.setValue(key, val); 4801 } 4802 } 4803 get size() { 4804 return this.#storage.size; 4805 } 4806 #setModified() { 4807 if (!this.#modified) { 4808 this.#modified = true; 4809 if (typeof this.onSetModified === "function") { 4810 this.onSetModified(); 4811 } 4812 } 4813 } 4814 resetModified() { 4815 if (this.#modified) { 4816 this.#modified = false; 4817 if (typeof this.onResetModified === "function") { 4818 this.onResetModified(); 4819 } 4820 } 4821 } 4822 get print() { 4823 return new PrintAnnotationStorage(this); 4824 } 4825 get serializable() { 4826 if (this.#storage.size === 0) { 4827 return SerializableEmpty; 4828 } 4829 const map = new Map(), 4830 hash = new MurmurHash3_64(), 4831 transfer = []; 4832 const context = Object.create(null); 4833 let hasBitmap = false; 4834 for (const [key, val] of this.#storage) { 4835 const serialized = val instanceof AnnotationEditor ? val.serialize(false, context) : val; 4836 if (serialized) { 4837 map.set(key, serialized); 4838 hash.update(`${key}:${JSON.stringify(serialized)}`); 4839 hasBitmap ||= !!serialized.bitmap; 4840 } 4841 } 4842 if (hasBitmap) { 4843 for (const value of map.values()) { 4844 if (value.bitmap) { 4845 transfer.push(value.bitmap); 4846 } 4847 } 4848 } 4849 return map.size > 0 ? { 4850 map, 4851 hash: hash.hexdigest(), 4852 transfer 4853 } : SerializableEmpty; 4854 } 4855 get editorStats() { 4856 let stats = null; 4857 const typeToEditor = new Map(); 4858 for (const value of this.#storage.values()) { 4859 if (!(value instanceof AnnotationEditor)) { 4860 continue; 4861 } 4862 const editorStats = value.telemetryFinalData; 4863 if (!editorStats) { 4864 continue; 4865 } 4866 const { 4867 type 4868 } = editorStats; 4869 if (!typeToEditor.has(type)) { 4870 typeToEditor.set(type, Object.getPrototypeOf(value).constructor); 4871 } 4872 stats ||= Object.create(null); 4873 const map = stats[type] ||= new Map(); 4874 for (const [key, val] of Object.entries(editorStats)) { 4875 if (key === "type") { 4876 continue; 4877 } 4878 let counters = map.get(key); 4879 if (!counters) { 4880 counters = new Map(); 4881 map.set(key, counters); 4882 } 4883 const count = counters.get(val) ?? 0; 4884 counters.set(val, count + 1); 4885 } 4886 } 4887 for (const [type, editor] of typeToEditor) { 4888 stats[type] = editor.computeTelemetryFinalData(stats[type]); 4889 } 4890 return stats; 4891 } 4892 resetModifiedIds() { 4893 this.#modifiedIds = null; 4894 } 4895 get modifiedIds() { 4896 if (this.#modifiedIds) { 4897 return this.#modifiedIds; 4898 } 4899 const ids = []; 4900 for (const value of this.#storage.values()) { 4901 if (!(value instanceof AnnotationEditor) || !value.annotationElementId || !value.serialize()) { 4902 continue; 4903 } 4904 ids.push(value.annotationElementId); 4905 } 4906 return this.#modifiedIds = { 4907 ids: new Set(ids), 4908 hash: ids.join(",") 4909 }; 4910 } 4911} 4912class PrintAnnotationStorage extends AnnotationStorage { 4913 #serializable; 4914 constructor(parent) { 4915 super(); 4916 const { 4917 map, 4918 hash, 4919 transfer 4920 } = parent.serializable; 4921 const clone = structuredClone(map, transfer ? { 4922 transfer 4923 } : null); 4924 this.#serializable = { 4925 map: clone, 4926 hash, 4927 transfer 4928 }; 4929 } 4930 get print() { 4931 unreachable("Should not call PrintAnnotationStorage.print"); 4932 } 4933 get serializable() { 4934 return this.#serializable; 4935 } 4936 get modifiedIds() { 4937 return shadow(this, "modifiedIds", { 4938 ids: new Set(), 4939 hash: "" 4940 }); 4941 } 4942} 4943 4944;// ./src/display/font_loader.js 4945 4946class FontLoader { 4947 #systemFonts = new Set(); 4948 constructor({ 4949 ownerDocument = globalThis.document, 4950 styleElement = null 4951 }) { 4952 this._document = ownerDocument; 4953 this.nativeFontFaces = new Set(); 4954 this.styleElement = null; 4955 this.loadingRequests = []; 4956 this.loadTestFontId = 0; 4957 } 4958 addNativeFontFace(nativeFontFace) { 4959 this.nativeFontFaces.add(nativeFontFace); 4960 this._document.fonts.add(nativeFontFace); 4961 } 4962 removeNativeFontFace(nativeFontFace) { 4963 this.nativeFontFaces.delete(nativeFontFace); 4964 this._document.fonts.delete(nativeFontFace); 4965 } 4966 insertRule(rule) { 4967 if (!this.styleElement) { 4968 this.styleElement = this._document.createElement("style"); 4969 this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement); 4970 } 4971 const styleSheet = this.styleElement.sheet; 4972 styleSheet.insertRule(rule, styleSheet.cssRules.length); 4973 } 4974 clear() { 4975 for (const nativeFontFace of this.nativeFontFaces) { 4976 this._document.fonts.delete(nativeFontFace); 4977 } 4978 this.nativeFontFaces.clear(); 4979 this.#systemFonts.clear(); 4980 if (this.styleElement) { 4981 this.styleElement.remove(); 4982 this.styleElement = null; 4983 } 4984 } 4985 async loadSystemFont({ 4986 systemFontInfo: info,
vendor: 4,823 bytes, lines 4987-5087
4987 _inspectFont 4988 }) { 4989 if (!info || this.#systemFonts.has(info.loadedName)) { 4990 return; 4991 } 4992 assert(!this.disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set."); 4993 if (this.isFontLoadingAPISupported) { 4994 const { 4995 loadedName, 4996 src, 4997 style 4998 } = info; 4999 const fontFace = new FontFace(loadedName, src, style); 5000 this.addNativeFontFace(fontFace); 5001 try { 5002 await fontFace.load(); 5003 this.#systemFonts.add(loadedName); 5004 _inspectFont?.(info); 5005 } catch { 5006 warn(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`); 5007 this.removeNativeFontFace(fontFace); 5008 } 5009 return; 5010 } 5011 unreachable("Not implemented: loadSystemFont without the Font Loading API."); 5012 } 5013 async bind(font) { 5014 if (font.attached || font.missingFile && !font.systemFontInfo) { 5015 return; 5016 } 5017 font.attached = true; 5018 if (font.systemFontInfo) { 5019 await this.loadSystemFont(font); 5020 return; 5021 } 5022 if (this.isFontLoadingAPISupported) { 5023 const nativeFontFace = font.createNativeFontFace(); 5024 if (nativeFontFace) { 5025 this.addNativeFontFace(nativeFontFace); 5026 try { 5027 await nativeFontFace.loaded; 5028 } catch (ex) { 5029 warn(`Failed to load font '${nativeFontFace.family}': '${ex}'.`); 5030 font.disableFontFace = true; 5031 throw ex; 5032 } 5033 } 5034 return; 5035 } 5036 const rule = font.createFontFaceRule(); 5037 if (rule) { 5038 this.insertRule(rule); 5039 if (this.isSyncFontLoadingSupported) { 5040 return; 5041 } 5042 await new Promise(resolve => { 5043 const request = this._queueLoadingCallback(resolve); 5044 this._prepareFontLoadEvent(font, request); 5045 }); 5046 } 5047 } 5048 get isFontLoadingAPISupported() { 5049 const hasFonts = !!this._document?.fonts; 5050 return shadow(this, "isFontLoadingAPISupported", hasFonts); 5051 } 5052 get isSyncFontLoadingSupported() { 5053 let supported = false; 5054 if (isNodeJS) { 5055 supported = true; 5056 } else if (typeof navigator !== "undefined" && typeof navigator?.userAgent === "string" && /Mozilla\/5.0.*?rv:\d+.*? Gecko/.test(navigator.userAgent)) { 5057 supported = true; 5058 } 5059 return shadow(this, "isSyncFontLoadingSupported", supported); 5060 } 5061 _queueLoadingCallback(callback) { 5062 function completeRequest() { 5063 assert(!request.done, "completeRequest() cannot be called twice."); 5064 request.done = true; 5065 while (loadingRequests.length > 0 && loadingRequests[0].done) { 5066 const otherRequest = loadingRequests.shift(); 5067 setTimeout(otherRequest.callback, 0); 5068 } 5069 } 5070 const { 5071 loadingRequests 5072 } = this; 5073 const request = { 5074 done: false, 5075 complete: completeRequest, 5076 callback 5077 }; 5078 loadingRequests.push(request); 5079 return request; 5080 } 5081 get _loadTestFont() { 5082 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=="); 5083 return shadow(this, "_loadTestFont", testFont); 5084 } 5085 _prepareFontLoadEvent(font, request) { 5086 function int32(data, offset) { 5087 return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff;
5088 } 5089 function spliceString(s, offset, remove, insert) { 5090 const chunk1 = s.substring(0, offset); 5091 const chunk2 = s.substring(offset + remove); 5092 return chunk1 + insert + chunk2; 5093 } 5094 let i, ii; 5095 const canvas = this._document.createElement("canvas"); 5096 canvas.width = 1; 5097 canvas.height = 1; 5098 const ctx = canvas.getContext("2d"); 5099 let called = 0; 5100 function isFontReady(name, callback) { 5101 if (++called > 30) { 5102 warn("Load test font never loaded."); 5103 callback(); 5104 return; 5105 } 5106 ctx.font = "30px " + name; 5107 ctx.fillText(".", 0, 20); 5108 const imageData = ctx.getImageData(0, 0, 1, 1); 5109 if (imageData.data[3] > 0) { 5110 callback(); 5111 return; 5112 } 5113 setTimeout(isFontReady.bind(null, name, callback)); 5114 } 5115 const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`; 5116 let data = this._loadTestFont; 5117 const COMMENT_OFFSET = 976; 5118 data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId); 5119 const CFF_CHECKSUM_OFFSET = 16; 5120 const XXXX_VALUE = 0x58585858; 5121 let checksum = int32(data, CFF_CHECKSUM_OFFSET); 5122 for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) { 5123 checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0; 5124 } 5125 if (i < loadTestFontId.length) { 5126 checksum = checksum - XXXX_VALUE + int32(loadTestFontId + "XXX", i) | 0; 5127 } 5128 data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum)); 5129 const url = `url(data:font/opentype;base64,${btoa(data)});`; 5130 const rule = `@font-face {font-family:"${loadTestFontId}";src:${url}}`; 5131 this.insertRule(rule); 5132 const div = this._document.createElement("div"); 5133 div.style.visibility = "hidden"; 5134 div.style.width = div.style.height = "10px"; 5135 div.style.position = "absolute"; 5136 div.style.top = div.style.left = "0px"; 5137 for (const name of [font.loadedName, loadTestFontId]) { 5138 const span = this._document.createElement("span"); 5139 span.textContent = "Hi"; 5140 span.style.fontFamily = name; 5141 div.append(span); 5142 } 5143 this._document.body.append(div); 5144 isFontReady(loadTestFontId, () => { 5145 div.remove(); 5146 request.complete(); 5147 }); 5148 } 5149} 5150class FontFaceObject { 5151 constructor(translatedData, { 5152 disableFontFace = false, 5153 inspectFont = null 5154 }) { 5155 this.compiledGlyphs = Object.create(null); 5156 for (const i in translatedData) { 5157 this[i] = translatedData[i]; 5158 } 5159 this.disableFontFace = disableFontFace === true; 5160 this._inspectFont = inspectFont; 5161 } 5162 createNativeFontFace() { 5163 if (!this.data || this.disableFontFace) { 5164 return null; 5165 } 5166 let nativeFontFace; 5167 if (!this.cssFontInfo) { 5168 nativeFontFace = new FontFace(this.loadedName, this.data, {}); 5169 } else { 5170 const css = { 5171 weight: this.cssFontInfo.fontWeight 5172 }; 5173 if (this.cssFontInfo.italicAngle) { 5174 css.style = `oblique ${this.cssFontInfo.italicAngle}deg`; 5175 } 5176 nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css); 5177 } 5178 this._inspectFont?.(this); 5179 return nativeFontFace; 5180 } 5181 createFontFaceRule() { 5182 if (!this.data || this.disableFontFace) { 5183 return null; 5184 } 5185 const url = `url(data:${this.mimetype};base64,${toBase64Util(this.data)});`; 5186 let rule; 5187 if (!this.cssFontInfo) { 5188 rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; 5189 } else { 5190 let css = `font-weight: ${this.cssFontInfo.fontWeight};`; 5191 if (this.cssFontInfo.italicAngle) { 5192 css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`; 5193 } 5194 rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`; 5195 } 5196 this._inspectFont?.(this, url); 5197 return rule; 5198 } 5199 getPathGenerator(objs, character) { 5200 if (this.compiledGlyphs[character] !== undefined) { 5201 return this.compiledGlyphs[character]; 5202 } 5203 let cmds; 5204 try { 5205 cmds = objs.get(this.loadedName + "_path_" + character); 5206 } catch (ex) { 5207 warn(`getPathGenerator - ignoring character: "${ex}".`); 5208 } 5209 if (!Array.isArray(cmds) || cmds.length === 0) { 5210 return this.compiledGlyphs[character] = function (c, size) {}; 5211 } 5212 const commands = []; 5213 for (let i = 0, ii = cmds.length; i < ii;) { 5214 switch (cmds[i++]) { 5215 case FontRenderOps.BEZIER_CURVE_TO: 5216 { 5217 const [a, b, c, d, e, f] = cmds.slice(i, i + 6); 5218 commands.push(ctx => ctx.bezierCurveTo(a, b, c, d, e, f)); 5219 i += 6; 5220 } 5221 break; 5222 case FontRenderOps.MOVE_TO: 5223 { 5224 const [a, b] = cmds.slice(i, i + 2); 5225 commands.push(ctx => ctx.moveTo(a, b)); 5226 i += 2; 5227 } 5228 break; 5229 case FontRenderOps.LINE_TO: 5230 { 5231 const [a, b] = cmds.slice(i, i + 2); 5232 commands.push(ctx => ctx.lineTo(a, b)); 5233 i += 2; 5234 } 5235 break; 5236 case FontRenderOps.QUADRATIC_CURVE_TO: 5237 { 5238 const [a, b, c, d] = cmds.slice(i, i + 4); 5239 commands.push(ctx => ctx.quadraticCurveTo(a, b, c, d)); 5240 i += 4; 5241 } 5242 break; 5243 case FontRenderOps.RESTORE: 5244 commands.push(ctx =>
5244 ctx.restore()); 5245 break; 5246 case FontRenderOps.SAVE: 5247 commands.push(ctx => ctx.save()); 5248 break; 5249 case FontRenderOps.SCALE: 5250 assert(commands.length === 2, "Scale command is only valid at the third position."); 5251 break; 5252 case FontRenderOps.TRANSFORM: 5253 { 5254 const [a, b, c, d, e, f] = cmds.slice(i, i + 6); 5255 commands.push(ctx => ctx.transform(a, b, c, d, e, f)); 5256 i += 6; 5257 } 5258 break; 5259 case FontRenderOps.TRANSLATE: 5260 { 5261 const [a, b] = cmds.slice(i, i + 2); 5262 commands.push(ctx => ctx.translate(a, b)); 5263 i += 2; 5264 } 5265 break; 5266 } 5267 } 5268 return this.compiledGlyphs[character] = function glyphDrawer(ctx, size) { 5269 commands[0](ctx); 5270 commands[1](ctx); 5271 ctx.scale(size, -size); 5272 for (let i = 2, ii = commands.length; i < ii; i++) { 5273 commands[i](ctx); 5274 } 5275 }; 5276 } 5277} 5278 5279;// ./src/display/canvas_factory.js 5280 5281class BaseCanvasFactory { 5282 #enableHWA = false; 5283 constructor({ 5284 enableHWA = false 5285 }) { 5286 this.#enableHWA = enableHWA; 5287 } 5288 create(width, height) { 5289 if (width <= 0 || height <= 0) { 5290 throw new Error("Invalid canvas size"); 5291 } 5292 const canvas = this._createCanvas(width, height); 5293 return { 5294 canvas, 5295 context: canvas.getContext("2d", { 5296 willReadFrequently: !this.#enableHWA 5297 }) 5298 }; 5299 } 5300 reset(canvasAndContext, width, height) { 5301 if (!canvasAndContext.canvas) { 5302 throw new Error("Canvas is not specified"); 5303 } 5304 if (width <= 0 || height <= 0) { 5305 throw new Error("Invalid canvas size"); 5306 } 5307 canvasAndContext.canvas.width = width; 5308 canvasAndContext.canvas.height = height; 5309 } 5310 destroy(canvasAndContext) { 5311 if (!canvasAndContext.canvas) { 5312 throw new Error("Canvas is not specified"); 5313 } 5314 canvasAndContext.canvas.width = 0; 5315 canvasAndContext.canvas.height = 0; 5316 canvasAndContext.canvas = null; 5317 canvasAndContext.context = null; 5318 } 5319 _createCanvas(width, height) { 5320 unreachable("Abstract method `_createCanvas` called."); 5321 } 5322} 5323class DOMCanvasFactory extends BaseCanvasFactory { 5324 constructor({ 5325 ownerDocument = globalThis.document, 5326 enableHWA = false 5327 }) { 5328 super({ 5329 enableHWA 5330 }); 5331 this._document = ownerDocument; 5332 } 5333 _createCanvas(width, height) { 5334 const canvas = this._document.createElement("canvas"); 5335 canvas.width = width; 5336 canvas.height = height; 5337 return canvas; 5338 } 5339} 5340 5341;// ./src/display/cmap_reader_factory.js 5342 5343 5344class BaseCMapReaderFactory { 5345 constructor({ 5346 baseUrl = null, 5347 isCompressed = true 5348 }) { 5349 this.baseUrl = baseUrl; 5350 this.isCompressed = isCompressed; 5351 } 5352 async fetch({ 5353 name 5354 }) { 5355 if (!this.baseUrl) { 5356 throw new Error("Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided."); 5357 } 5358 if (!name) { 5359 throw new Error("CMap name must be specified."); 5360 } 5361 const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : ""); 5362 return this._fetch(url).then(cMapData => ({ 5363 cMapData, 5364 isCompressed: this.isCompressed 5365 })).catch(reason => { 5366 throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`); 5367 }); 5368 } 5369 async _fetch(url) { 5370 unreachable("Abstract method `_fetch` called."); 5371 } 5372} 5373class DOMCMapReaderFactory extends BaseCMapReaderFactory { 5374 async _fetch(url) { 5375 const data = await fetchData(url, this.isCompressed ? "arraybuffer" : "text"); 5376 return data instanceof ArrayBuffer ? new Uint8Array(data) : stringToBytes(data); 5377 } 5378} 5379 5380;// ./src/display/filter_factory.js 5381 5382 5383class BaseFilterFactory { 5384 addFilter(maps) { 5385 return "none"; 5386 } 5387 addHCMFilter(fgColor, bgColor) { 5388 return "none"; 5389 } 5390 addAlphaFilter(map) { 5391 return "none"; 5392 } 5393 addLuminosityFilter(map) { 5394 return "none"; 5395 } 5396 addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { 5397 return "none"; 5398 } 5399 destroy(keepHCM = false) {} 5400} 5401class DOMFilterFactory extends BaseFilterFactory { 5402 #baseUrl; 5403 #_cache; 5404 #_defs; 5405 #docId; 5406 #document;
vendor: 9,357 bytes, lines 5407-5702
5407 #_hcmCache; 5408 #id = 0; 5409 constructor({ 5410 docId, 5411 ownerDocument = globalThis.document 5412 }) { 5413 super(); 5414 this.#docId = docId; 5415 this.#document = ownerDocument; 5416 } 5417 get #cache() { 5418 return this.#_cache ||= new Map(); 5419 } 5420 get #hcmCache() { 5421 return this.#_hcmCache ||= new Map(); 5422 } 5423 get #defs() { 5424 if (!this.#_defs) { 5425 const div = this.#document.createElement("div"); 5426 const { 5427 style 5428 } = div; 5429 style.visibility = "hidden"; 5430 style.contain = "strict"; 5431 style.width = style.height = 0; 5432 style.position = "absolute"; 5433 style.top = style.left = 0; 5434 style.zIndex = -1; 5435 const svg = this.#document.createElementNS(SVG_NS, "svg"); 5436 svg.setAttribute("width", 0); 5437 svg.setAttribute("height", 0); 5438 this.#_defs = this.#document.createElementNS(SVG_NS, "defs"); 5439 div.append(svg); 5440 svg.append(this.#_defs); 5441 this.#document.body.append(div); 5442 } 5443 return this.#_defs; 5444 } 5445 #createTables(maps) { 5446 if (maps.length === 1) { 5447 const mapR = maps[0]; 5448 const buffer = new Array(256); 5449 for (let i = 0; i < 256; i++) { 5450 buffer[i] = mapR[i] / 255; 5451 } 5452 const table = buffer.join(","); 5453 return [table, table, table]; 5454 } 5455 const [mapR, mapG, mapB] = maps; 5456 const bufferR = new Array(256); 5457 const bufferG = new Array(256); 5458 const bufferB = new Array(256); 5459 for (let i = 0; i < 256; i++) { 5460 bufferR[i] = mapR[i] / 255; 5461 bufferG[i] = mapG[i] / 255; 5462 bufferB[i] = mapB[i] / 255; 5463 } 5464 return [bufferR.join(","), bufferG.join(","), bufferB.join(",")]; 5465 } 5466 #createUrl(id) { 5467 if (this.#baseUrl === undefined) { 5468 this.#baseUrl = ""; 5469 const url = this.#document.URL; 5470 if (url !== this.#document.baseURI) { 5471 if (isDataScheme(url)) { 5472 warn('#createUrl: ignore "data:"-URL for performance reasons.'); 5473 } else { 5474 this.#baseUrl = url.split("#", 1)[0]; 5475 } 5476 } 5477 } 5478 return `url(${this.#baseUrl}#${id})`; 5479 } 5480 addFilter(maps) { 5481 if (!maps) { 5482 return "none"; 5483 } 5484 let value = this.#cache.get(maps); 5485 if (value) { 5486 return value; 5487 } 5488 const [tableR, tableG, tableB] = this.#createTables(maps); 5489 const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`; 5490 value = this.#cache.get(key); 5491 if (value) { 5492 this.#cache.set(maps, value); 5493 return value; 5494 } 5495 const id = `g_${this.#docId}_transfer_map_${this.#id++}`; 5496 const url = this.#createUrl(id); 5497 this.#cache.set(maps, url); 5498 this.#cache.set(key, url); 5499 const filter = this.#createFilter(id); 5500 this.#addTransferMapConversion(tableR, tableG, tableB, filter); 5501 return url; 5502 } 5503 addHCMFilter(fgColor, bgColor) { 5504 const key = `${fgColor}-${bgColor}`; 5505 const filterName = "base"; 5506 let info = this.#hcmCache.get(filterName); 5507 if (info?.key === key) { 5508 return info.url; 5509 } 5510 if (info) { 5511 info.filter?.remove(); 5512 info.key = key; 5513 info.url = "none"; 5514 info.filter = null; 5515 } else { 5516 info = { 5517 key, 5518 url: "none", 5519 filter: null 5520 }; 5521 this.#hcmCache.set(filterName, info); 5522 } 5523 if (!fgColor || !bgColor) { 5524 return info.url; 5525 } 5526 const fgRGB = this.#getRGB(fgColor); 5527 fgColor = Util.makeHexColor(...fgRGB); 5528 const bgRGB = this.#getRGB(bgColor); 5529 bgColor = Util.makeHexColor(...bgRGB); 5530 this.#defs.style.color = ""; 5531 if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) { 5532 return info.url; 5533 } 5534 const map = new Array(256); 5535 for (let i = 0; i <= 255; i++) { 5536 const x = i / 255; 5537 map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; 5538 } 5539 const table = map.join(","); 5540 const id = `g_${this.#docId}_hcm_filter`; 5541 const filter = info.filter = this.#createFilter(id); 5542 this.#addTransferMapConversion(table, table, table, filter); 5543 this.#addGrayConversion(filter); 5544 const getSteps = (c, n) => { 5545 const start = fgRGB[c] / 255; 5546 const end = bgRGB[c] / 255; 5547 const arr = new Array(n + 1); 5548 for (let i = 0; i <= n; i++) { 5549 arr[i] = start + i / n * (end - start); 5550 } 5551 return arr.join(","); 5552 }; 5553 this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter); 5554 info.url = this.#createUrl(id); 5555 return info.url; 5556 } 5557 addAlphaFilter(map) { 5558 let value = this.#cache.get(map); 5559 if (value) { 5560 return value; 5561 } 5562 const [tableA] = this.#createTables([map]); 5563 const key = `alpha_${tableA}`; 5564 value = this.#cache.get(key); 5565 if (value) { 5566 this.#cache.set(map, value); 5567 return value; 5568 } 5569 const id = `g_${this.#docId}_alpha_map_${this.#id++}`; 5570 const url = this.#createUrl(id); 5571 this.#cache.set(map, url); 5572 this.#cache.set(key, url); 5573 const filter = this.#createFilter(id); 5574 this.#addTransferMapAlphaConversion(tableA, filter); 5575 return url; 5576 } 5577 addLuminosityFilter(map) { 5578 let value = this.#cache.get(map || "luminosity"); 5579 if (value) { 5580 return value; 5581 } 5582 let tableA, key; 5583 if (map) { 5584 [tableA] = this.#createTables([map]); 5585 key = `luminosity_${tableA}`; 5586 } else { 5587 key = "luminosity"; 5588 } 5589 value = this.#cache.get(key); 5590 if (value) { 5591 this.#cache.set(map, value); 5592 return value; 5593 } 5594 const id = `g_${this.#docId}_luminosity_map_${this.#id++}`; 5595 const url = this.#createUrl(id); 5596 this.#cache.set(map, url); 5597 this.#cache.set(key, url); 5598 const filter = this.#createFilter(id); 5599 this.#addLuminosityConversion(filter); 5600 if (map) { 5601 this.#addTransferMapAlphaConversion(tableA, filter); 5602 } 5603 return url; 5604 } 5605 addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { 5606 const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`; 5607 let info = this.#hcmCache.get(filterName); 5608 if (info?.key === key) { 5609 return info.url; 5610 } 5611 if (info) { 5612 info.filter?.remove(); 5613 info.key = key; 5614 info.url = "none"; 5615 info.filter = null; 5616 } else { 5617 info = { 5618 key, 5619 url: "none", 5620 filter: null 5621 }; 5622 this.#hcmCache.set(filterName, info); 5623 } 5624 if (!fgColor || !bgColor) { 5625 return info.url; 5626 } 5627 const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this)); 5628 let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]); 5629 let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]); 5630 let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this)); 5631 if (bgGray < fgGray) { 5632 [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB]; 5633 } 5634 this.#defs.style.color = ""; 5635 const getSteps = (fg, bg, n) => { 5636 const arr = new Array(256); 5637 const step = (bgGray - fgGray) / n; 5638 const newStart = fg / 255; 5639 const newStep = (bg - fg) / (255 * n); 5640 let prev = 0; 5641 for (let i = 0; i <= n; i++) { 5642 const k = Math.round(fgGray + i * step); 5643 const value = newStart + i * newStep; 5644 for (let j = prev; j <= k; j++) { 5645 arr[j] = value; 5646 } 5647 prev = k + 1; 5648 } 5649 for (let i = prev; i < 256; i++) { 5650 arr[i] = arr[prev - 1]; 5651 } 5652 return arr.join(","); 5653 }; 5654 const id = `g_${this.#docId}_hcm_${filterName}_filter`; 5655 const filter = info.filter = this.#createFilter(id); 5656 this.#addGrayConversion(filter); 5657 this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter); 5658 info.url = this.#createUrl(id); 5659 return info.url; 5660 } 5661 destroy(keepHCM = false) { 5662 if (keepHCM && this.#hcmCache.size !== 0) { 5663 return; 5664 } 5665 if (this.#_defs) { 5666 this.#_defs.parentNode.parentNode.remove(); 5667 this.#_defs = null; 5668 } 5669 if (this.#_cache) { 5670 this.#_cache.clear(); 5671 this.#_cache = null; 5672 } 5673 this.#id = 0; 5674 } 5675 #addLuminosityConversion(filter) { 5676 const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); 5677 feColorMatrix.setAttribute("type", "matrix"); 5678 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"); 5679 filter.append(feColorMatrix); 5680 } 5681 #addGrayConversion(filter) { 5682 const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); 5683 feColorMatrix.setAttribute("type", "matrix"); 5684 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"); 5685 filter.append(feColorMatrix); 5686 } 5687 #createFilter(id) { 5688 const filter = this.#document.createElementNS(SVG_NS, "filter"); 5689 filter.setAttribute("color-interpolation-filters", "sRGB"); 5690 filter.setAttribute("id", id); 5691 this.#defs.append(filter); 5692 return filter; 5693 } 5694 #appendFeFunc(feComponentTransfer, func, table) { 5695 const feFunc = this.#document.createElementNS(SVG_NS, func); 5696 feFunc.setAttribute("type", "discrete"); 5697 feFunc.setAttribute("tableValues", table); 5698 feComponentTransfer.append(feFunc); 5699 } 5700 #addTransferMapConversion(rTable, gTable, bTable, filter) { 5701 const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); 5702 filter.append(feComponentTransfer);
5703 this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable); 5704 this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable); 5705 this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable); 5706 } 5707 #addTransferMapAlphaConversion(aTable, filter) { 5708 const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); 5709 filter.append(feComponentTransfer); 5710 this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable); 5711 } 5712 #getRGB(color) { 5713 this.#defs.style.color = color; 5714 return getRGB(getComputedStyle(this.#defs).getPropertyValue("color")); 5715 } 5716} 5717 5718;// ./src/display/standard_fontdata_factory.js 5719 5720 5721class BaseStandardFontDataFactory { 5722 constructor({ 5723 baseUrl = null 5724 }) { 5725 this.baseUrl = baseUrl; 5726 } 5727 async fetch({ 5728 filename 5729 }) { 5730 if (!this.baseUrl) { 5731 throw new Error("Ensure that the `standardFontDataUrl` API parameter is provided."); 5732 } 5733 if (!filename) { 5734 throw new Error("Font filename must be specified."); 5735 } 5736 const url = `${this.baseUrl}${filename}`; 5737 return this._fetch(url).catch(reason => { 5738 throw new Error(`Unable to load font data at: ${url}`); 5739 }); 5740 } 5741 async _fetch(url) { 5742 unreachable("Abstract method `_fetch` called."); 5743 } 5744} 5745class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { 5746 async _fetch(url) { 5747 const data = await fetchData(url, "arraybuffer"); 5748 return new Uint8Array(data); 5749 } 5750} 5751 5752;// ./src/display/node_utils.js 5753 5754 5755 5756 5757 5758if (isNodeJS) { 5759 var packageCapability = Promise.withResolvers(); 5760 var packageMap = null; 5761 const loadPackages = async () => { 5762 const fs = await import(/*webpackIgnore: true*/"fs"), 5763 http = await import(/*webpackIgnore: true*/"http"), 5764 https = await import(/*webpackIgnore: true*/"https"), 5765 url = await import(/*webpackIgnore: true*/"url"); 5766 let canvas, path2d; 5767 return new Map(Object.entries({ 5768 fs, 5769 http, 5770 https, 5771 url, 5772 canvas, 5773 path2d 5774 })); 5775 }; 5776 loadPackages().then(map => { 5777 packageMap = map; 5778 packageCapability.resolve(); 5779 }, reason => { 5780 warn(`loadPackages: ${reason}`); 5781 packageMap = new Map(); 5782 packageCapability.resolve(); 5783 }); 5784} 5785class NodePackages { 5786 static get promise() { 5787 return packageCapability.promise; 5788 } 5789 static get(name) { 5790 return packageMap?.get(name); 5791 } 5792} 5793async function node_utils_fetchData(url) { 5794 const fs = NodePackages.get("fs"); 5795 const data = await fs.promises.readFile(url); 5796 return new Uint8Array(data); 5797} 5798class NodeFilterFactory extends BaseFilterFactory {} 5799class NodeCanvasFactory extends BaseCanvasFactory { 5800 _createCanvas(width, height) { 5801 const canvas = NodePackages.get("canvas"); 5802 return canvas.createCanvas(width, height); 5803 } 5804} 5805class NodeCMapReaderFactory extends BaseCMapReaderFactory { 5806 async _fetch(url) { 5807 return node_utils_fetchData(url); 5808 } 5809} 5810class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { 5811 async _fetch(url) { 5812 return node_utils_fetchData(url); 5813 } 5814} 5815 5816;// ./src/display/pattern_helper.js 5817 5818 5819const PathType = { 5820 FILL: "Fill", 5821 STROKE: "Stroke", 5822 SHADING: "Shading" 5823}; 5824function applyBoundingBox(ctx, bbox) { 5825 if (!bbox) { 5826 return; 5827 } 5828 const width = bbox[2] - bbox[0]; 5829 const height = bbox[3] - bbox[1]; 5830 const region = new Path2D(); 5831 region.rect(bbox[0], bbox[1], width, height); 5832 ctx.clip(region); 5833} 5834class BaseShadingPattern { 5835 getPattern() { 5836 unreachable("Abstract method `getPattern` called."); 5837 } 5838} 5839class RadialAxialShadingPattern extends BaseShadingPattern { 5840 constructor(IR) { 5841 super(); 5842 this._type = IR[1]; 5843 this._bbox = IR[2]; 5844 this._colorStops = IR[3]; 5845 this._p0 = IR[4]; 5846 this._p1 = IR[5]; 5847 this._r0 = IR[6]; 5848 this._r1 = IR[7]; 5849 this.matrix = null; 5850 } 5851 _createGradient(ctx) { 5852 let grad; 5853 if (this._type === "axial") { 5854 grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]); 5855 } else if (this._type === "radial") { 5856 grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1); 5857 } 5858 for (const colorStop of this._colorStops) { 5859 grad.addColorStop(colorStop[0], colorStop[1]); 5860 } 5861 return grad; 5862 } 5863 getPattern(ctx, owner, inverse, pathType) { 5864 let pattern; 5865 if (pathType === PathType.STROKE || pathType === PathType.FILL) { 5866 const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0]; 5867 const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;
vendor: 13,602 bytes, lines 5868-6280
5868 const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1; 5869 const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height); 5870 const tmpCtx = tmpCanvas.context; 5871 tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); 5872 tmpCtx.beginPath(); 5873 tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); 5874 tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]); 5875 inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]); 5876 tmpCtx.transform(...owner.baseTransform); 5877 if (this.matrix) { 5878 tmpCtx.transform(...this.matrix); 5879 } 5880 applyBoundingBox(tmpCtx, this._bbox); 5881 tmpCtx.fillStyle = this._createGradient(tmpCtx); 5882 tmpCtx.fill(); 5883 pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat"); 5884 const domMatrix = new DOMMatrix(inverse); 5885 pattern.setTransform(domMatrix); 5886 } else { 5887 applyBoundingBox(ctx, this._bbox); 5888 pattern = this._createGradient(ctx); 5889 } 5890 return pattern; 5891 } 5892} 5893function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) { 5894 const coords = context.coords, 5895 colors = context.colors; 5896 const bytes = data.data, 5897 rowSize = data.width * 4; 5898 let tmp; 5899 if (coords[p1 + 1] > coords[p2 + 1]) { 5900 tmp = p1; 5901 p1 = p2; 5902 p2 = tmp; 5903 tmp = c1; 5904 c1 = c2; 5905 c2 = tmp; 5906 } 5907 if (coords[p2 + 1] > coords[p3 + 1]) { 5908 tmp = p2; 5909 p2 = p3; 5910 p3 = tmp; 5911 tmp = c2; 5912 c2 = c3; 5913 c3 = tmp; 5914 } 5915 if (coords[p1 + 1] > coords[p2 + 1]) { 5916 tmp = p1; 5917 p1 = p2; 5918 p2 = tmp; 5919 tmp = c1; 5920 c1 = c2; 5921 c2 = tmp; 5922 } 5923 const x1 = (coords[p1] + context.offsetX) * context.scaleX; 5924 const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY; 5925 const x2 = (coords[p2] + context.offsetX) * context.scaleX; 5926 const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY; 5927 const x3 = (coords[p3] + context.offsetX) * context.scaleX; 5928 const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY; 5929 if (y1 >= y3) { 5930 return; 5931 } 5932 const c1r = colors[c1], 5933 c1g = colors[c1 + 1], 5934 c1b = colors[c1 + 2]; 5935 const c2r = colors[c2], 5936 c2g = colors[c2 + 1], 5937 c2b = colors[c2 + 2]; 5938 const c3r = colors[c3], 5939 c3g = colors[c3 + 1], 5940 c3b = colors[c3 + 2]; 5941 const minY = Math.round(y1), 5942 maxY = Math.round(y3); 5943 let xa, car, cag, cab; 5944 let xb, cbr, cbg, cbb; 5945 for (let y = minY; y <= maxY; y++) { 5946 if (y < y2) { 5947 const k = y < y1 ? 0 : (y1 - y) / (y1 - y2); 5948 xa = x1 - (x1 - x2) * k; 5949 car = c1r - (c1r - c2r) * k; 5950 cag = c1g - (c1g - c2g) * k; 5951 cab = c1b - (c1b - c2b) * k; 5952 } else { 5953 let k; 5954 if (y > y3) { 5955 k = 1; 5956 } else if (y2 === y3) { 5957 k = 0; 5958 } else { 5959 k = (y2 - y) / (y2 - y3); 5960 } 5961 xa = x2 - (x2 - x3) * k; 5962 car = c2r - (c2r - c3r) * k; 5963 cag = c2g - (c2g - c3g) * k; 5964 cab = c2b - (c2b - c3b) * k; 5965 } 5966 let k; 5967 if (y < y1) { 5968 k = 0; 5969 } else if (y > y3) { 5970 k = 1; 5971 } else { 5972 k = (y1 - y) / (y1 - y3); 5973 } 5974 xb = x1 - (x1 - x3) * k; 5975 cbr = c1r - (c1r - c3r) * k; 5976 cbg = c1g - (c1g - c3g) * k; 5977 cbb = c1b - (c1b - c3b) * k; 5978 const x1_ = Math.round(Math.min(xa, xb)); 5979 const x2_ = Math.round(Math.max(xa, xb)); 5980 let j = rowSize * y + x1_ * 4; 5981 for (let x = x1_; x <= x2_; x++) { 5982 k = (xa - x) / (xa - xb); 5983 if (k < 0) { 5984 k = 0; 5985 } else if (k > 1) { 5986 k = 1; 5987 } 5988 bytes[j++] = car - (car - cbr) * k | 0; 5989 bytes[j++] = cag - (cag - cbg) * k | 0; 5990 bytes[j++] = cab - (cab - cbb) * k | 0; 5991 bytes[j++] = 255; 5992 } 5993 } 5994} 5995function drawFigure(data, figure, context) { 5996 const ps = figure.coords; 5997 const cs = figure.colors; 5998 let i, ii; 5999 switch (figure.type) { 6000 case "lattice": 6001 const verticesPerRow = figure.verticesPerRow; 6002 const rows = Math.floor(ps.length / verticesPerRow) - 1; 6003 const cols = verticesPerRow - 1; 6004 for (i = 0; i < rows; i++) { 6005 let q = i * verticesPerRow; 6006 for (let j = 0; j < cols; j++, q++) { 6007 drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]); 6008 drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]); 6009 } 6010 } 6011 break; 6012 case "triangles": 6013 for (i = 0, ii = ps.length; i < ii; i += 3) { 6014 drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]); 6015 } 6016 break; 6017 default: 6018 throw new Error("illegal figure"); 6019 } 6020} 6021class MeshShadingPattern extends BaseShadingPattern { 6022 constructor(IR) { 6023 super(); 6024 this._coords = IR[2]; 6025 this._colors = IR[3]; 6026 this._figures = IR[4]; 6027 this._bounds = IR[5]; 6028 this._bbox = IR[7]; 6029 this._background = IR[8]; 6030 this.matrix = null; 6031 } 6032 _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) { 6033 const EXPECTED_SCALE = 1.1; 6034 const MAX_PATTERN_SIZE = 3000; 6035 const BORDER_SIZE = 2; 6036 const offsetX = Math.floor(this._bounds[0]); 6037 const offsetY = Math.floor(this._bounds[1]); 6038 const boundsWidth = Math.ceil(this._bounds[2]) - offsetX; 6039 const boundsHeight = Math.ceil(this._bounds[3]) - offsetY; 6040 const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); 6041 const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); 6042 const scaleX = boundsWidth / width; 6043 const scaleY = boundsHeight / height; 6044 const context = { 6045 coords: this._coords, 6046 colors: this._colors, 6047 offsetX: -offsetX, 6048 offsetY: -offsetY, 6049 scaleX: 1 / scaleX, 6050 scaleY: 1 / scaleY 6051 }; 6052 const paddedWidth = width + BORDER_SIZE * 2; 6053 const paddedHeight = height + BORDER_SIZE * 2; 6054 const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight); 6055 const tmpCtx = tmpCanvas.context; 6056 const data = tmpCtx.createImageData(width, height); 6057 if (backgroundColor) { 6058 const bytes = data.data; 6059 for (let i = 0, ii = bytes.length; i < ii; i += 4) { 6060 bytes[i] = backgroundColor[0]; 6061 bytes[i + 1] = backgroundColor[1]; 6062 bytes[i + 2] = backgroundColor[2]; 6063 bytes[i + 3] = 255; 6064 } 6065 } 6066 for (const figure of this._figures) { 6067 drawFigure(data, figure, context); 6068 } 6069 tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE); 6070 const canvas = tmpCanvas.canvas; 6071 return { 6072 canvas, 6073 offsetX: offsetX - BORDER_SIZE * scaleX, 6074 offsetY: offsetY - BORDER_SIZE * scaleY, 6075 scaleX, 6076 scaleY 6077 }; 6078 } 6079 getPattern(ctx, owner, inverse, pathType) { 6080 applyBoundingBox(ctx, this._bbox); 6081 let scale; 6082 if (pathType === PathType.SHADING) { 6083 scale = Util.singularValueDecompose2dScale(getCurrentTransform(ctx)); 6084 } else { 6085 scale = Util.singularValueDecompose2dScale(owner.baseTransform); 6086 if (this.matrix) { 6087 const matrixScale = Util.singularValueDecompose2dScale(this.matrix); 6088 scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]]; 6089 } 6090 } 6091 const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases); 6092 if (pathType !== PathType.SHADING) { 6093 ctx.setTransform(...owner.baseTransform); 6094 if (this.matrix) { 6095 ctx.transform(...this.matrix); 6096 } 6097 } 6098 ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); 6099 ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY); 6100 return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat"); 6101 } 6102} 6103class DummyShadingPattern extends BaseShadingPattern { 6104 getPattern() { 6105 return "hotpink"; 6106 } 6107} 6108function getShadingPattern(IR) { 6109 switch (IR[0]) { 6110 case "RadialAxial": 6111 return new RadialAxialShadingPattern(IR); 6112 case "Mesh": 6113 return new MeshShadingPattern(IR); 6114 case "Dummy": 6115 return new DummyShadingPattern(); 6116 } 6117 throw new Error(`Unknown IR type: ${IR[0]}`); 6118} 6119const PaintType = { 6120 COLORED: 1, 6121 UNCOLORED: 2 6122}; 6123class TilingPattern { 6124 static MAX_PATTERN_SIZE = 3000; 6125 constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) { 6126 this.operatorList = IR[2]; 6127 this.matrix = IR[3]; 6128 this.bbox = IR[4]; 6129 this.xstep = IR[5]; 6130 this.ystep = IR[6]; 6131 this.paintType = IR[7]; 6132 this.tilingType = IR[8]; 6133 this.color = color; 6134 this.ctx = ctx; 6135 this.canvasGraphicsFactory = canvasGraphicsFactory; 6136 this.baseTransform = baseTransform; 6137 } 6138 createPatternCanvas(owner) { 6139 const { 6140 bbox, 6141 operatorList, 6142 paintType, 6143 tilingType, 6144 color, 6145 canvasGraphicsFactory 6146 } = this; 6147 let { 6148 xstep, 6149 ystep 6150 } = this; 6151 xstep = Math.abs(xstep); 6152 ystep = Math.abs(ystep); 6153 info("TilingType: " + tilingType); 6154 const x0 = bbox[0], 6155 y0 = bbox[1], 6156 x1 = bbox[2], 6157 y1 = bbox[3]; 6158 const width = x1 - x0; 6159 const height = y1 - y0; 6160 const matrixScale = Util.singularValueDecompose2dScale(this.matrix); 6161 const curMatrixScale = Util.singularValueDecompose2dScale(this.baseTransform); 6162 const combinedScaleX = matrixScale[0] * curMatrixScale[0]; 6163 const combinedScaleY = matrixScale[1] * curMatrixScale[1]; 6164 let canvasWidth = width, 6165 canvasHeight = height, 6166 redrawHorizontally = false, 6167 redrawVertically = false; 6168 const xScaledStep = Math.ceil(xstep * combinedScaleX); 6169 const yScaledStep = Math.ceil(ystep * combinedScaleY); 6170 const xScaledWidth = Math.ceil(width * combinedScaleX); 6171 const yScaledHeight = Math.ceil(height * combinedScaleY); 6172 if (xScaledStep >= xScaledWidth) { 6173 canvasWidth = xstep; 6174 } else { 6175 redrawHorizontally = true; 6176 } 6177 if (yScaledStep >= yScaledHeight) { 6178 canvasHeight = ystep; 6179 } else { 6180 redrawVertically = true; 6181 } 6182 const dimx = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); 6183 const dimy = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); 6184 const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size); 6185 const tmpCtx = tmpCanvas.context; 6186 const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx); 6187 graphics.groupLevel = owner.groupLevel; 6188 this.setFillAndStrokeStyleToContext(graphics, paintType, color); 6189 tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0); 6190 graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0); 6191 tmpCtx.save(); 6192 this.clipBbox(graphics, x0, y0, x1, y1); 6193 graphics.baseTransform = getCurrentTransform(graphics.ctx); 6194 graphics.executeOperatorList(operatorList); 6195 graphics.endDrawing(); 6196 tmpCtx.restore(); 6197 if (redrawHorizontally || redrawVertically) { 6198 const image = tmpCanvas.canvas; 6199 if (redrawHorizontally) { 6200 canvasWidth = xstep; 6201 } 6202 if (redrawVertically) { 6203 canvasHeight = ystep; 6204 } 6205 const dimx2 = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); 6206 const dimy2 = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); 6207 const xSize = dimx2.size; 6208 const ySize = dimy2.size; 6209 const tmpCanvas2 = owner.cachedCanvases.getCanvas("pattern-workaround", xSize, ySize); 6210 const tmpCtx2 = tmpCanvas2.context; 6211 const ii = redrawHorizontally ? Math.floor(width / xstep) : 0; 6212 const jj = redrawVertically ? Math.floor(height / ystep) : 0; 6213 for (let i = 0; i <= ii; i++) { 6214 for (let j = 0; j <= jj; j++) { 6215 tmpCtx2.drawImage(image, xSize * i, ySize * j, xSize, ySize, 0, 0, xSize, ySize); 6216 } 6217 } 6218 return { 6219 canvas: tmpCanvas2.canvas, 6220 scaleX: dimx2.scale, 6221 scaleY: dimy2.scale, 6222 offsetX: x0, 6223 offsetY: y0 6224 }; 6225 } 6226 return { 6227 canvas: tmpCanvas.canvas, 6228 scaleX: dimx.scale, 6229 scaleY: dimy.scale, 6230 offsetX: x0, 6231 offsetY: y0 6232 }; 6233 } 6234 getSizeAndScale(step, realOutputSize, scale) { 6235 const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize); 6236 let size = Math.ceil(step * scale); 6237 if (size >= maxSize) { 6238 size = maxSize; 6239 } else { 6240 scale = size / step; 6241 } 6242 return { 6243 scale, 6244 size 6245 }; 6246 } 6247 clipBbox(graphics, x0, y0, x1, y1) { 6248 const bboxWidth = x1 - x0; 6249 const bboxHeight = y1 - y0; 6250 graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight); 6251 graphics.current.updateRectMinMax(getCurrentTransform(graphics.ctx), [x0, y0, x1, y1]); 6252 graphics.clip(); 6253 graphics.endPath(); 6254 } 6255 setFillAndStrokeStyleToContext(graphics, paintType, color) { 6256 const context = graphics.ctx, 6257 current = graphics.current; 6258 switch (paintType) { 6259 case PaintType.COLORED: 6260 const ctx = this.ctx; 6261 context.fillStyle = ctx.fillStyle; 6262 context.strokeStyle = ctx.strokeStyle; 6263 current.fillColor = ctx.fillStyle; 6264 current.strokeColor = ctx.strokeStyle; 6265 break; 6266 case PaintType.UNCOLORED: 6267 const cssColor = Util.makeHexColor(color[0], color[1], color[2]); 6268 context.fillStyle = cssColor; 6269 context.strokeStyle = cssColor; 6270 current.fillColor = cssColor; 6271 current.strokeColor = cssColor; 6272 break; 6273 default: 6274 throw new FormatError(`Unsupported paint type: ${paintType}`); 6275 } 6276 } 6277 getPattern(ctx, owner, inverse, pathType) { 6278 let matrix = inverse; 6279 if (pathType !== PathType.SHADING) { 6280 matrix = Util.transform(matrix, owner.baseTransform);
vendor: 5,655 bytes, lines 6281-6454
6281 if (this.matrix) { 6282 matrix = Util.transform(matrix, this.matrix); 6283 } 6284 } 6285 const temporaryPatternCanvas = this.createPatternCanvas(owner); 6286 let domMatrix = new DOMMatrix(matrix); 6287 domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); 6288 domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY); 6289 const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat"); 6290 pattern.setTransform(domMatrix); 6291 return pattern; 6292 } 6293} 6294 6295;// ./src/shared/image_utils.js 6296 6297function convertToRGBA(params) { 6298 switch (params.kind) { 6299 case ImageKind.GRAYSCALE_1BPP: 6300 return convertBlackAndWhiteToRGBA(params); 6301 case ImageKind.RGB_24BPP: 6302 return convertRGBToRGBA(params); 6303 } 6304 return null; 6305} 6306function convertBlackAndWhiteToRGBA({ 6307 src, 6308 srcPos = 0, 6309 dest, 6310 width, 6311 height, 6312 nonBlackColor = 0xffffffff, 6313 inverseDecode = false 6314}) { 6315 const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; 6316 const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor]; 6317 const widthInSource = width >> 3; 6318 const widthRemainder = width & 7; 6319 const srcLength = src.length; 6320 dest = new Uint32Array(dest.buffer); 6321 let destPos = 0; 6322 for (let i = 0; i < height; i++) { 6323 for (const max = srcPos + widthInSource; srcPos < max; srcPos++) { 6324 const elem = srcPos < srcLength ? src[srcPos] : 255; 6325 dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping; 6326 dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping; 6327 dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping; 6328 dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping; 6329 dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping; 6330 dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping; 6331 dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping; 6332 dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping; 6333 } 6334 if (widthRemainder === 0) { 6335 continue; 6336 } 6337 const elem = srcPos < srcLength ? src[srcPos++] : 255; 6338 for (let j = 0; j < widthRemainder; j++) { 6339 dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping; 6340 } 6341 } 6342 return { 6343 srcPos, 6344 destPos 6345 }; 6346} 6347function convertRGBToRGBA({ 6348 src, 6349 srcPos = 0, 6350 dest, 6351 destPos = 0, 6352 width, 6353 height 6354}) { 6355 let i = 0; 6356 const len32 = src.length >> 2; 6357 const src32 = new Uint32Array(src.buffer, srcPos, len32); 6358 if (FeatureTest.isLittleEndian) { 6359 for (; i < len32 - 2; i += 3, destPos += 4) { 6360 const s1 = src32[i]; 6361 const s2 = src32[i + 1]; 6362 const s3 = src32[i + 2]; 6363 dest[destPos] = s1 | 0xff000000; 6364 dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000; 6365 dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000; 6366 dest[destPos + 3] = s3 >>> 8 | 0xff000000; 6367 } 6368 for (let j = i * 4, jj = src.length; j < jj; j += 3) { 6369 dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000; 6370 } 6371 } else { 6372 for (; i < len32 - 2; i += 3, destPos += 4) { 6373 const s1 = src32[i]; 6374 const s2 = src32[i + 1]; 6375 const s3 = src32[i + 2]; 6376 dest[destPos] = s1 | 0xff; 6377 dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff; 6378 dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff; 6379 dest[destPos + 3] = s3 << 8 | 0xff; 6380 } 6381 for (let j = i * 4, jj = src.length; j < jj; j += 3) { 6382 dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff; 6383 } 6384 } 6385 return { 6386 srcPos, 6387 destPos 6388 }; 6389} 6390function grayToRGBA(src, dest) { 6391 if (FeatureTest.isLittleEndian) { 6392 for (let i = 0, ii = src.length; i < ii; i++) { 6393 dest[i] = src[i] * 0x10101 | 0xff000000; 6394 } 6395 } else { 6396 for (let i = 0, ii = src.length; i < ii; i++) { 6397 dest[i] = src[i] * 0x1010100 | 0x000000ff; 6398 } 6399 } 6400} 6401 6402;// ./src/display/canvas.js 6403 6404 6405 6406 6407const MIN_FONT_SIZE = 16; 6408const MAX_FONT_SIZE = 100; 6409const EXECUTION_TIME = 15; 6410const EXECUTION_STEPS = 10; 6411const MAX_SIZE_TO_COMPILE = 1000; 6412const FULL_CHUNK_HEIGHT = 16; 6413function mirrorContextOperations(ctx, destCtx) { 6414 if (ctx._removeMirroring) { 6415 throw new Error("Context is already forwarding operations."); 6416 } 6417 ctx.__originalSave = ctx.save; 6418 ctx.__originalRestore = ctx.restore; 6419 ctx.__originalRotate = ctx.rotate; 6420 ctx.__originalScale = ctx.scale; 6421 ctx.__originalTranslate = ctx.translate; 6422 ctx.__originalTransform = ctx.transform; 6423 ctx.__originalSetTransform = ctx.setTransform; 6424 ctx.__originalResetTransform = ctx.resetTransform; 6425 ctx.__originalClip = ctx.clip; 6426 ctx.__originalMoveTo = ctx.moveTo; 6427 ctx.__originalLineTo = ctx.lineTo; 6428 ctx.__originalBezierCurveTo = ctx.bezierCurveTo; 6429 ctx.__originalRect = ctx.rect; 6430 ctx.__originalClosePath = ctx.closePath; 6431 ctx.__originalBeginPath = ctx.beginPath; 6432 ctx._removeMirroring = () => { 6433 ctx.save = ctx.__originalSave; 6434 ctx.restore = ctx.__originalRestore; 6435 ctx.rotate = ctx.__originalRotate; 6436 ctx.scale = ctx.__originalScale; 6437 ctx.translate = ctx.__originalTranslate; 6438 ctx.transform = ctx.__originalTransform; 6439 ctx.setTransform = ctx.__originalSetTransform; 6440 ctx.resetTransform = ctx.__originalResetTransform; 6441 ctx.clip = ctx.__originalClip; 6442 ctx.moveTo = ctx.__originalMoveTo; 6443 ctx.lineTo = ctx.__originalLineTo; 6444 ctx.bezierCurveTo = ctx.__originalBezierCurveTo; 6445 ctx.rect = ctx.__originalRect; 6446 ctx.closePath = ctx.__originalClosePath; 6447 ctx.beginPath = ctx.__originalBeginPath; 6448 delete ctx._removeMirroring; 6449 }; 6450 ctx.save = function ctxSave() { 6451 destCtx.save(); 6452 this.__originalSave(); 6453 }; 6454 ctx.restore = function ctxRestore() {
6455 destCtx.restore(); 6456 this.__originalRestore(); 6457 }; 6458 ctx.translate = function ctxTranslate(x, y) { 6459 destCtx.translate(x, y); 6460 this.__originalTranslate(x, y); 6461 }; 6462 ctx.scale = function ctxScale(x, y) { 6463 destCtx.scale(x, y); 6464 this.__originalScale(x, y); 6465 }; 6466 ctx.transform = function ctxTransform(a, b, c, d, e, f) { 6467 destCtx.transform(a, b, c, d, e, f); 6468 this.__originalTransform(a, b, c, d, e, f); 6469 }; 6470 ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) { 6471 destCtx.setTransform(a, b, c, d, e, f); 6472 this.__originalSetTransform(a, b, c, d, e, f); 6473 }; 6474 ctx.resetTransform = function ctxResetTransform() { 6475 destCtx.resetTransform(); 6476 this.__originalResetTransform(); 6477 }; 6478 ctx.rotate = function ctxRotate(angle) { 6479 destCtx.rotate(angle); 6480 this.__originalRotate(angle); 6481 }; 6482 ctx.clip = function ctxRotate(rule) { 6483 destCtx.clip(rule); 6484 this.__originalClip(rule); 6485 }; 6486 ctx.moveTo = function (x, y) { 6487 destCtx.moveTo(x, y); 6488 this.__originalMoveTo(x, y); 6489 }; 6490 ctx.lineTo = function (x, y) { 6491 destCtx.lineTo(x, y); 6492 this.__originalLineTo(x, y); 6493 }; 6494 ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { 6495 destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); 6496 this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); 6497 }; 6498 ctx.rect = function (x, y, width, height) { 6499 destCtx.rect(x, y, width, height); 6500 this.__originalRect(x, y, width, height); 6501 }; 6502 ctx.closePath = function () { 6503 destCtx.closePath(); 6504 this.__originalClosePath(); 6505 }; 6506 ctx.beginPath = function () { 6507 destCtx.beginPath(); 6508 this.__originalBeginPath(); 6509 }; 6510} 6511class CachedCanvases { 6512 constructor(canvasFactory) { 6513 this.canvasFactory = canvasFactory; 6514 this.cache = Object.create(null); 6515 } 6516 getCanvas(id, width, height) { 6517 let canvasEntry; 6518 if (this.cache[id] !== undefined) { 6519 canvasEntry = this.cache[id]; 6520 this.canvasFactory.reset(canvasEntry, width, height); 6521 } else { 6522 canvasEntry = this.canvasFactory.create(width, height); 6523 this.cache[id] = canvasEntry; 6524 } 6525 return canvasEntry; 6526 } 6527 delete(id) { 6528 delete this.cache[id]; 6529 } 6530 clear() { 6531 for (const id in this.cache) { 6532 const canvasEntry = this.cache[id]; 6533 this.canvasFactory.destroy(canvasEntry); 6534 delete this.cache[id]; 6535 } 6536 } 6537} 6538function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) { 6539 const [a, b, c, d, tx, ty] = getCurrentTransform(ctx); 6540 if (b === 0 && c === 0) { 6541 const tlX = destX * a + tx; 6542 const rTlX = Math.round(tlX); 6543 const tlY = destY * d + ty; 6544 const rTlY = Math.round(tlY); 6545 const brX = (destX + destW) * a + tx; 6546 const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; 6547 const brY = (destY + destH) * d + ty; 6548 const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; 6549 ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY); 6550 ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight); 6551 ctx.setTransform(a, b, c, d, tx, ty); 6552 return [rWidth, rHeight]; 6553 } 6554 if (a === 0 && d === 0) { 6555 const tlX = destY * c + tx; 6556 const rTlX = Math.round(tlX); 6557 const tlY = destX * b + ty; 6558 const rTlY = Math.round(tlY); 6559 const brX = (destY + destH) * c + tx; 6560 const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; 6561 const brY = (destX + destW) * b + ty; 6562 const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; 6563 ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY); 6564 ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth); 6565 ctx.setTransform(a, b, c, d, tx, ty); 6566 return [rHeight, rWidth]; 6567 } 6568 ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH); 6569 const scaleX = Math.hypot(a, b); 6570 const scaleY = Math.hypot(c, d); 6571 return [scaleX * destW, scaleY * destH]; 6572} 6573function compileType3Glyph(imgData) { 6574 const { 6575 width, 6576 height 6577 } = imgData; 6578 if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) { 6579 return null; 6580 } 6581 const POINT_TO_PROCESS_LIMIT = 1000; 6582 const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); 6583 const width1 = width + 1; 6584 let points = new Uint8Array(width1 * (height + 1)); 6585 let i, j, j0; 6586 const lineSize = width + 7 & ~7; 6587 let data = new Uint8Array(lineSize * height), 6588 pos = 0; 6589 for (const elem of imgData.data) { 6590 let mask = 128; 6591 while (mask > 0) { 6592 data[pos++] = elem & mask ? 0 : 255; 6593 mask >>= 1; 6594 } 6595 } 6596 let count = 0; 6597 pos = 0; 6598 if (data[pos] !== 0) { 6599 points[0] = 1; 6600 ++count; 6601 } 6602 for (j = 1; j < width; j++) { 6603 if (data[pos] !== data[pos + 1]) { 6604 points[j] = data[pos] ? 2 : 1; 6605 ++count; 6606 } 6607 pos++; 6608 } 6609 if (data[pos] !== 0) { 6610 points[j] = 2; 6611 ++count; 6612 } 6613 for (i = 1; i < height; i++) { 6614 pos = i * lineSize; 6615 j0 = i * width1; 6616 if (data[pos - lineSize] !== data[pos]) { 6617 points[j0] = data[pos] ? 1 : 8; 6618 ++count; 6619 } 6620 let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0); 6621 for (j = 1; j < width; j++) { 6622 sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + (data[pos - lineSize + 1] ? 8 : 0); 6623 if (POINT_TYPES[sum]) { 6624 points[j0 + j] = POINT_TYPES[sum]; 6625 ++count; 6626 } 6627 pos++; 6628 } 6629 if (data[pos - lineSize] !== data[pos]) { 6630 points[j0 + j] = data[pos] ? 2 : 4; 6631 ++count; 6632 } 6633 if (count > POINT_TO_PROCESS_LIMIT) { 6634 return null; 6635 } 6636 } 6637 pos = lineSize * (height - 1); 6638 j0 = i * width1; 6639 if (data[pos] !== 0) { 6640 points[j0] = 8; 6641 ++count; 6642 } 6643 for (j = 1; j < width; j++) { 6644 if (data[pos] !== data[pos + 1]) { 6645 points[j0 + j] = data[pos] ? 4 : 8; 6646 ++count; 6647 } 6648 pos++; 6649 } 6650 if (data[pos] !== 0) { 6651 points[j0 + j] = 4; 6652 ++count; 6653 } 6654 if (count > POINT_TO_PROCESS_LIMIT) { 6655 return null; 6656 } 6657 const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]); 6658 const path = new Path2D(); 6659 for (i = 0; count && i <= height; i++) { 6660 let p = i * width1; 6661 const end = p + width; 6662 while (p < end && !points[p]) { 6663 p++; 6664 } 6665 if (p === end) { 6666 continue; 6667 } 6668 path.moveTo(p % width1, i); 6669 const p0 = p; 6670 let type = points[p]; 6671 do { 6672 const step = steps[type]; 6673 do { 6674 p += step; 6675 } while (!points[p]); 6676 const pp = points[p]; 6677 if (pp !== 5 && pp !== 10) { 6678 type = pp; 6679 points[p] = 0; 6680 } else { 6681 type = pp & 0x33 * type >> 4; 6682 points[p] &= type >> 2 | type << 2; 6683 } 6684 path.lineTo(p % width1, p / width1 | 0); 6685 if (!points[p]) { 6686 --count; 6687 } 6688 } while (p0 !== p); 6689 --i; 6690 } 6691 data = null; 6692 points = null; 6693 const drawOutline = function (c) { 6694 c.save(); 6695 c.scale(1 / width, -1 / height); 6696 c.translate(0, -height); 6697 c.fill(path); 6698 c.beginPath();
6699 c.restore(); 6700 }; 6701 return drawOutline; 6702} 6703class CanvasExtraState { 6704 constructor(width, height) { 6705 this.alphaIsShape = false; 6706 this.fontSize = 0; 6707 this.fontSizeScale = 1; 6708 this.textMatrix = IDENTITY_MATRIX; 6709 this.textMatrixScale = 1; 6710 this.fontMatrix = FONT_IDENTITY_MATRIX; 6711 this.leading = 0; 6712 this.x = 0; 6713 this.y = 0; 6714 this.lineX = 0; 6715 this.lineY = 0; 6716 this.charSpacing = 0; 6717 this.wordSpacing = 0; 6718 this.textHScale = 1; 6719 this.textRenderingMode = TextRenderingMode.FILL; 6720 this.textRise = 0; 6721 this.fillColor = "#000000"; 6722 this.strokeColor = "#000000"; 6723 this.patternFill = false; 6724 this.fillAlpha = 1; 6725 this.strokeAlpha = 1; 6726 this.lineWidth = 1; 6727 this.activeSMask = null; 6728 this.transferMaps = "none"; 6729 this.startNewPathAndClipBox([0, 0, width, height]); 6730 } 6731 clone() { 6732 const clone = Object.create(this); 6733 clone.clipBox = this.clipBox.slice(); 6734 return clone; 6735 } 6736 setCurrentPoint(x, y) { 6737 this.x = x; 6738 this.y = y; 6739 } 6740 updatePathMinMax(transform, x, y) { 6741 [x, y] = Util.applyTransform([x, y], transform); 6742 this.minX = Math.min(this.minX, x); 6743 this.minY = Math.min(this.minY, y); 6744 this.maxX = Math.max(this.maxX, x); 6745 this.maxY = Math.max(this.maxY, y); 6746 } 6747 updateRectMinMax(transform, rect) { 6748 const p1 = Util.applyTransform(rect, transform); 6749 const p2 = Util.applyTransform(rect.slice(2), transform); 6750 const p3 = Util.applyTransform([rect[0], rect[3]], transform); 6751 const p4 = Util.applyTransform([rect[2], rect[1]], transform); 6752 this.minX = Math.min(this.minX, p1[0], p2[0], p3[0], p4[0]); 6753 this.minY = Math.min(this.minY, p1[1], p2[1], p3[1], p4[1]); 6754 this.maxX = Math.max(this.maxX, p1[0], p2[0], p3[0], p4[0]); 6755 this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]); 6756 } 6757 updateScalingPathMinMax(transform, minMax) { 6758 Util.scaleMinMax(transform, minMax); 6759 this.minX = Math.min(this.minX, minMax[0]); 6760 this.minY = Math.min(this.minY, minMax[1]); 6761 this.maxX = Math.max(this.maxX, minMax[2]); 6762 this.maxY = Math.max(this.maxY, minMax[3]); 6763 } 6764 updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) { 6765 const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax); 6766 if (minMax) { 6767 return; 6768 } 6769 this.updateRectMinMax(transform, box); 6770 } 6771 getPathBoundingBox(pathType = PathType.FILL, transform = null) { 6772 const box = [this.minX, this.minY, this.maxX, this.maxY]; 6773 if (pathType === PathType.STROKE) { 6774 if (!transform) { 6775 unreachable("Stroke bounding box must include transform."); 6776 } 6777 const scale = Util.singularValueDecompose2dScale(transform); 6778 const xStrokePad = scale[0] * this.lineWidth / 2; 6779 const yStrokePad = scale[1] * this.lineWidth / 2; 6780 box[0] -= xStrokePad; 6781 box[1] -= yStrokePad; 6782 box[2] += xStrokePad; 6783 box[3] += yStrokePad; 6784 } 6785 return box; 6786 } 6787 updateClipFromPath() { 6788 const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox()); 6789 this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]); 6790 } 6791 isEmptyClip() { 6792 return this.minX === Infinity; 6793 } 6794 startNewPathAndClipBox(box) { 6795 this.clipBox = box; 6796 this.minX = Infinity; 6797 this.minY = Infinity; 6798 this.maxX = 0; 6799 this.maxY = 0; 6800 } 6801 getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) { 6802 return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform)); 6803 } 6804} 6805function putBinaryImageData(ctx, imgData) { 6806 if (typeof ImageData !== "undefined" && imgData instanceof ImageData) { 6807 ctx.putImageData(imgData, 0, 0); 6808 return; 6809 } 6810 const height = imgData.height, 6811 width = imgData.width; 6812 const partialChunkHeight = height % FULL_CHUNK_HEIGHT; 6813 const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; 6814 const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; 6815 const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); 6816 let srcPos = 0, 6817 destPos; 6818 const src = imgData.data; 6819 const dest = chunkImgData.data; 6820 let i, j, thisChunkHeight, elemsInThisChunk; 6821 if (imgData.kind === util_ImageKind.GRAYSCALE_1BPP) { 6822 const srcLength = src.byteLength; 6823 const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2); 6824 const dest32DataLength = dest32.length; 6825 const fullSrcDiff = width + 7 >> 3; 6826 const white = 0xffffffff; 6827 const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; 6828 for (i = 0; i < totalChunks; i++) { 6829 thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; 6830 destPos = 0; 6831 for (j = 0; j < thisChunkHeight; j++) { 6832 const srcDiff = srcLength - srcPos; 6833 let k = 0;
vendor: 14,515 bytes, lines 6834-7234
6834 const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7; 6835 const kEndUnrolled = kEnd & ~7; 6836 let mask = 0; 6837 let srcByte = 0; 6838 for (; k < kEndUnrolled; k += 8) { 6839 srcByte = src[srcPos++]; 6840 dest32[destPos++] = srcByte & 128 ? white : black; 6841 dest32[destPos++] = srcByte & 64 ? white : black; 6842 dest32[destPos++] = srcByte & 32 ? white : black; 6843 dest32[destPos++] = srcByte & 16 ? white : black; 6844 dest32[destPos++] = srcByte & 8 ? white : black; 6845 dest32[destPos++] = srcByte & 4 ? white : black; 6846 dest32[destPos++] = srcByte & 2 ? white : black; 6847 dest32[destPos++] = srcByte & 1 ? white : black; 6848 } 6849 for (; k < kEnd; k++) { 6850 if (mask === 0) { 6851 srcByte = src[srcPos++]; 6852 mask = 128; 6853 } 6854 dest32[destPos++] = srcByte & mask ? white : black; 6855 mask >>= 1; 6856 } 6857 } 6858 while (destPos < dest32DataLength) { 6859 dest32[destPos++] = 0; 6860 } 6861 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 6862 } 6863 } else if (imgData.kind === util_ImageKind.RGBA_32BPP) { 6864 j = 0; 6865 elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; 6866 for (i = 0; i < fullChunks; i++) { 6867 dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); 6868 srcPos += elemsInThisChunk; 6869 ctx.putImageData(chunkImgData, 0, j); 6870 j += FULL_CHUNK_HEIGHT; 6871 } 6872 if (i < totalChunks) { 6873 elemsInThisChunk = width * partialChunkHeight * 4; 6874 dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); 6875 ctx.putImageData(chunkImgData, 0, j); 6876 } 6877 } else if (imgData.kind === util_ImageKind.RGB_24BPP) { 6878 thisChunkHeight = FULL_CHUNK_HEIGHT; 6879 elemsInThisChunk = width * thisChunkHeight; 6880 for (i = 0; i < totalChunks; i++) { 6881 if (i >= fullChunks) { 6882 thisChunkHeight = partialChunkHeight; 6883 elemsInThisChunk = width * thisChunkHeight; 6884 } 6885 destPos = 0; 6886 for (j = elemsInThisChunk; j--;) { 6887 dest[destPos++] = src[srcPos++]; 6888 dest[destPos++] = src[srcPos++]; 6889 dest[destPos++] = src[srcPos++]; 6890 dest[destPos++] = 255; 6891 } 6892 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 6893 } 6894 } else { 6895 throw new Error(`bad image kind: ${imgData.kind}`); 6896 } 6897} 6898function putBinaryImageMask(ctx, imgData) { 6899 if (imgData.bitmap) { 6900 ctx.drawImage(imgData.bitmap, 0, 0); 6901 return; 6902 } 6903 const height = imgData.height, 6904 width = imgData.width; 6905 const partialChunkHeight = height % FULL_CHUNK_HEIGHT; 6906 const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; 6907 const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; 6908 const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); 6909 let srcPos = 0; 6910 const src = imgData.data; 6911 const dest = chunkImgData.data; 6912 for (let i = 0; i < totalChunks; i++) { 6913 const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; 6914 ({ 6915 srcPos 6916 } = convertBlackAndWhiteToRGBA({ 6917 src, 6918 srcPos, 6919 dest, 6920 width, 6921 height: thisChunkHeight, 6922 nonBlackColor: 0 6923 })); 6924 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 6925 } 6926} 6927function copyCtxState(sourceCtx, destCtx) { 6928 const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"]; 6929 for (const property of properties) { 6930 if (sourceCtx[property] !== undefined) { 6931 destCtx[property] = sourceCtx[property]; 6932 } 6933 } 6934 if (sourceCtx.setLineDash !== undefined) { 6935 destCtx.setLineDash(sourceCtx.getLineDash()); 6936 destCtx.lineDashOffset = sourceCtx.lineDashOffset; 6937 } 6938} 6939function resetCtxToDefault(ctx) { 6940 ctx.strokeStyle = ctx.fillStyle = "#000000"; 6941 ctx.fillRule = "nonzero"; 6942 ctx.globalAlpha = 1; 6943 ctx.lineWidth = 1; 6944 ctx.lineCap = "butt"; 6945 ctx.lineJoin = "miter"; 6946 ctx.miterLimit = 10; 6947 ctx.globalCompositeOperation = "source-over"; 6948 ctx.font = "10px sans-serif"; 6949 if (ctx.setLineDash !== undefined) { 6950 ctx.setLineDash([]); 6951 ctx.lineDashOffset = 0; 6952 } 6953 if (!isNodeJS) { 6954 const { 6955 filter 6956 } = ctx; 6957 if (filter !== "none" && filter !== "") { 6958 ctx.filter = "none"; 6959 } 6960 } 6961} 6962function getImageSmoothingEnabled(transform, interpolate) { 6963 if (interpolate) { 6964 return true; 6965 } 6966 const scale = Util.singularValueDecompose2dScale(transform); 6967 scale[0] = Math.fround(scale[0]); 6968 scale[1] = Math.fround(scale[1]); 6969 const actualScale = Math.fround((globalThis.devicePixelRatio || 1) * PixelsPerInch.PDF_TO_CSS_UNITS); 6970 return scale[0] <= actualScale && scale[1] <= actualScale; 6971} 6972const LINE_CAP_STYLES = ["butt", "round", "square"]; 6973const LINE_JOIN_STYLES = ["miter", "round", "bevel"]; 6974const NORMAL_CLIP = {}; 6975const EO_CLIP = {}; 6976class CanvasGraphics { 6977 constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, { 6978 optionalContentConfig, 6979 markedContentStack = null 6980 }, annotationCanvasMap, pageColors) { 6981 this.ctx = canvasCtx; 6982 this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); 6983 this.stateStack = []; 6984 this.pendingClip = null; 6985 this.pendingEOFill = false; 6986 this.res = null; 6987 this.xobjs = null; 6988 this.commonObjs = commonObjs; 6989 this.objs = objs; 6990 this.canvasFactory = canvasFactory; 6991 this.filterFactory = filterFactory; 6992 this.groupStack = []; 6993 this.processingType3 = null; 6994 this.baseTransform = null; 6995 this.baseTransformStack = []; 6996 this.groupLevel = 0; 6997 this.smaskStack = []; 6998 this.smaskCounter = 0; 6999 this.tempSMask = null; 7000 this.suspendedCtx = null; 7001 this.contentVisible = true; 7002 this.markedContentStack = markedContentStack || []; 7003 this.optionalContentConfig = optionalContentConfig; 7004 this.cachedCanvases = new CachedCanvases(this.canvasFactory); 7005 this.cachedPatterns = new Map(); 7006 this.annotationCanvasMap = annotationCanvasMap; 7007 this.viewportScale = 1; 7008 this.outputScaleX = 1; 7009 this.outputScaleY = 1; 7010 this.pageColors = pageColors; 7011 this._cachedScaleForStroking = [-1, 0]; 7012 this._cachedGetSinglePixelWidth = null; 7013 this._cachedBitmapsMap = new Map(); 7014 } 7015 getObject(data, fallback = null) { 7016 if (typeof data === "string") { 7017 return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data); 7018 } 7019 return fallback; 7020 } 7021 beginDrawing({ 7022 transform, 7023 viewport, 7024 transparency = false, 7025 background = null 7026 }) { 7027 const width = this.ctx.canvas.width; 7028 const height = this.ctx.canvas.height; 7029 const savedFillStyle = this.ctx.fillStyle; 7030 this.ctx.fillStyle = background || "#ffffff"; 7031 this.ctx.fillRect(0, 0, width, height); 7032 this.ctx.fillStyle = savedFillStyle; 7033 if (transparency) { 7034 const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height); 7035 this.compositeCtx = this.ctx; 7036 this.transparentCanvas = transparentCanvas.canvas; 7037 this.ctx = transparentCanvas.context; 7038 this.ctx.save(); 7039 this.ctx.transform(...getCurrentTransform(this.compositeCtx)); 7040 } 7041 this.ctx.save(); 7042 resetCtxToDefault(this.ctx); 7043 if (transform) { 7044 this.ctx.transform(...transform); 7045 this.outputScaleX = transform[0]; 7046 this.outputScaleY = transform[0]; 7047 } 7048 this.ctx.transform(...viewport.transform); 7049 this.viewportScale = viewport.scale; 7050 this.baseTransform = getCurrentTransform(this.ctx); 7051 } 7052 executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper) { 7053 const argsArray = operatorList.argsArray; 7054 const fnArray = operatorList.fnArray; 7055 let i = executionStartIdx || 0; 7056 const argsArrayLen = argsArray.length; 7057 if (argsArrayLen === i) { 7058 return i; 7059 } 7060 const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function"; 7061 const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0; 7062 let steps = 0; 7063 const commonObjs = this.commonObjs; 7064 const objs = this.objs; 7065 let fnId; 7066 while (true) { 7067 if (stepper !== undefined && i === stepper.nextBreakPoint) { 7068 stepper.breakIt(i, continueCallback); 7069 return i; 7070 } 7071 fnId = fnArray[i]; 7072 if (fnId !== OPS.dependency) { 7073 this[fnId].apply(this, argsArray[i]); 7074 } else { 7075 for (const depObjId of argsArray[i]) { 7076 const objsPool = depObjId.startsWith("g_") ? commonObjs : objs; 7077 if (!objsPool.has(depObjId)) { 7078 objsPool.get(depObjId, continueCallback); 7079 return i; 7080 } 7081 } 7082 } 7083 i++; 7084 if (i === argsArrayLen) { 7085 return i; 7086 } 7087 if (chunkOperations && ++steps > EXECUTION_STEPS) { 7088 if (Date.now() > endTime) { 7089 continueCallback(); 7090 return i; 7091 } 7092 steps = 0; 7093 } 7094 } 7095 } 7096 #restoreInitialState() { 7097 while (this.stateStack.length || this.inSMaskMode) { 7098 this.restore(); 7099 } 7100 this.current.activeSMask = null; 7101 this.ctx.restore(); 7102 if (this.transparentCanvas) { 7103 this.ctx = this.compositeCtx; 7104 this.ctx.save(); 7105 this.ctx.setTransform(1, 0, 0, 1, 0, 0); 7106 this.ctx.drawImage(this.transparentCanvas, 0, 0); 7107 this.ctx.restore(); 7108 this.transparentCanvas = null; 7109 } 7110 } 7111 endDrawing() { 7112 this.#restoreInitialState(); 7113 this.cachedCanvases.clear(); 7114 this.cachedPatterns.clear(); 7115 for (const cache of this._cachedBitmapsMap.values()) { 7116 for (const canvas of cache.values()) { 7117 if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) { 7118 canvas.width = canvas.height = 0; 7119 } 7120 } 7121 cache.clear(); 7122 } 7123 this._cachedBitmapsMap.clear(); 7124 this.#drawFilter(); 7125 } 7126 #drawFilter() { 7127 if (this.pageColors) { 7128 const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background); 7129 if (hcmFilterId !== "none") { 7130 const savedFilter = this.ctx.filter; 7131 this.ctx.filter = hcmFilterId; 7132 this.ctx.drawImage(this.ctx.canvas, 0, 0); 7133 this.ctx.filter = savedFilter; 7134 } 7135 } 7136 } 7137 _scaleImage(img, inverseTransform) { 7138 const width = img.width ?? img.displayWidth; 7139 const height = img.height ?? img.displayHeight; 7140 let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1); 7141 let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1); 7142 let paintWidth = width, 7143 paintHeight = height; 7144 let tmpCanvasId = "prescale1"; 7145 let tmpCanvas, tmpCtx; 7146 while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) { 7147 let newWidth = paintWidth, 7148 newHeight = paintHeight; 7149 if (widthScale > 2 && paintWidth > 1) { 7150 newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2); 7151 widthScale /= paintWidth / newWidth; 7152 } 7153 if (heightScale > 2 && paintHeight > 1) { 7154 newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2; 7155 heightScale /= paintHeight / newHeight; 7156 } 7157 tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); 7158 tmpCtx = tmpCanvas.context; 7159 tmpCtx.clearRect(0, 0, newWidth, newHeight); 7160 tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight); 7161 img = tmpCanvas.canvas; 7162 paintWidth = newWidth; 7163 paintHeight = newHeight; 7164 tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1"; 7165 } 7166 return { 7167 img, 7168 paintWidth, 7169 paintHeight 7170 }; 7171 } 7172 _createMaskCanvas(img) { 7173 const ctx = this.ctx; 7174 const { 7175 width, 7176 height 7177 } = img; 7178 const fillColor = this.current.fillColor; 7179 const isPatternFill = this.current.patternFill; 7180 const currentTransform = getCurrentTransform(ctx); 7181 let cache, cacheKey, scaled, maskCanvas; 7182 if ((img.bitmap || img.data) && img.count > 1) { 7183 const mainKey = img.bitmap || img.data.buffer; 7184 cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]); 7185 cache = this._cachedBitmapsMap.get(mainKey); 7186 if (!cache) { 7187 cache = new Map(); 7188 this._cachedBitmapsMap.set(mainKey, cache); 7189 } 7190 const cachedImage = cache.get(cacheKey); 7191 if (cachedImage && !isPatternFill) { 7192 const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]); 7193 const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]); 7194 return { 7195 canvas: cachedImage, 7196 offsetX, 7197 offsetY 7198 }; 7199 } 7200 scaled = cachedImage; 7201 } 7202 if (!scaled) { 7203 maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); 7204 putBinaryImageMask(maskCanvas.context, img); 7205 } 7206 let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]); 7207 maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]); 7208 const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox([0, 0, width, height], maskToCanvas); 7209 const drawnWidth = Math.round(maxX - minX) || 1; 7210 const drawnHeight = Math.round(maxY - minY) || 1; 7211 const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight); 7212 const fillCtx = fillCanvas.context; 7213 const offsetX = minX; 7214 const offsetY = minY; 7215 fillCtx.translate(-offsetX, -offsetY); 7216 fillCtx.transform(...maskToCanvas); 7217 if (!scaled) { 7218 scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx)); 7219 scaled = scaled.img; 7220 if (cache && isPatternFill) { 7221 cache.set(cacheKey, scaled); 7222 } 7223 } 7224 fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate); 7225 drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height); 7226 fillCtx.globalCompositeOperation = "source-in"; 7227 const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]); 7228 fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL) : fillColor; 7229 fillCtx.fillRect(0, 0, width, height); 7230 if (cache && !isPatternFill) { 7231 this.cachedCanvases.delete("fillCanvas"); 7232 cache.set(cacheKey, fillCanvas.canvas); 7233 } 7234 return {
7235 canvas: fillCanvas.canvas, 7236 offsetX: Math.round(offsetX), 7237 offsetY: Math.round(offsetY) 7238 }; 7239 } 7240 setLineWidth(width) { 7241 if (width !== this.current.lineWidth) { 7242 this._cachedScaleForStroking[0] = -1; 7243 } 7244 this.current.lineWidth = width; 7245 this.ctx.lineWidth = width; 7246 } 7247 setLineCap(style) { 7248 this.ctx.lineCap = LINE_CAP_STYLES[style]; 7249 } 7250 setLineJoin(style) { 7251 this.ctx.lineJoin = LINE_JOIN_STYLES[style]; 7252 } 7253 setMiterLimit(limit) { 7254 this.ctx.miterLimit = limit; 7255 } 7256 setDash(dashArray, dashPhase) { 7257 const ctx = this.ctx; 7258 if (ctx.setLineDash !== undefined) { 7259 ctx.setLineDash(dashArray); 7260 ctx.lineDashOffset = dashPhase; 7261 } 7262 } 7263 setRenderingIntent(intent) {} 7264 setFlatness(flatness) {} 7265 setGState(states) { 7266 for (const [key, value] of states) { 7267 switch (key) { 7268 case "LW": 7269 this.setLineWidth(value); 7270 break; 7271 case "LC": 7272 this.setLineCap(value); 7273 break; 7274 case "LJ": 7275 this.setLineJoin(value); 7276 break; 7277 case "ML": 7278 this.setMiterLimit(value); 7279 break; 7280 case "D": 7281 this.setDash(value[0], value[1]); 7282 break; 7283 case "RI": 7284 this.setRenderingIntent(value); 7285 break; 7286 case "FL": 7287 this.setFlatness(value); 7288 break; 7289 case "Font": 7290 this.setFont(value[0], value[1]); 7291 break; 7292 case "CA": 7293 this.current.strokeAlpha = value; 7294 break; 7295 case "ca": 7296 this.current.fillAlpha = value; 7297 this.ctx.globalAlpha = value; 7298 break; 7299 case "BM": 7300 this.ctx.globalCompositeOperation = value; 7301 break; 7302 case "SMask": 7303 this.current.activeSMask = value ? this.tempSMask : null; 7304 this.tempSMask = null; 7305 this.checkSMaskState(); 7306 break; 7307 case "TR": 7308 this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value); 7309 break; 7310 } 7311 } 7312 } 7313 get inSMaskMode() { 7314 return !!this.suspendedCtx; 7315 } 7316 checkSMaskState() { 7317 const inSMaskMode = this.inSMaskMode; 7318 if (this.current.activeSMask && !inSMaskMode) { 7319 this.beginSMaskMode(); 7320 } else if (!this.current.activeSMask && inSMaskMode) { 7321 this.endSMaskMode(); 7322 } 7323 } 7324 beginSMaskMode() { 7325 if (this.inSMaskMode) { 7326 throw new Error("beginSMaskMode called while already in smask mode"); 7327 } 7328 const drawnWidth = this.ctx.canvas.width; 7329 const drawnHeight = this.ctx.canvas.height; 7330 const cacheId = "smaskGroupAt" + this.groupLevel; 7331 const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); 7332 this.suspendedCtx = this.ctx; 7333 this.ctx = scratchCanvas.context; 7334 const ctx = this.ctx; 7335 ctx.setTransform(...getCurrentTransform(this.suspendedCtx)); 7336 copyCtxState(this.suspendedCtx, ctx); 7337 mirrorContextOperations(ctx, this.suspendedCtx); 7338 this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]); 7339 } 7340 endSMaskMode() { 7341 if (!this.inSMaskMode) { 7342 throw new Error("endSMaskMode called while not in smask mode"); 7343 } 7344 this.ctx._removeMirroring(); 7345 copyCtxState(this.ctx, this.suspendedCtx); 7346 this.ctx = this.suspendedCtx; 7347 this.suspendedCtx = null; 7348 } 7349 compose(dirtyBox) { 7350 if (!this.current.activeSMask) { 7351 return; 7352 } 7353 if (!dirtyBox) { 7354 dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height]; 7355 } else { 7356 dirtyBox[0] = Math.floor(dirtyBox[0]); 7357 dirtyBox[1] = Math.floor(dirtyBox[1]); 7358 dirtyBox[2] = Math.ceil(dirtyBox[2]); 7359 dirtyBox[3] = Math.ceil(dirtyBox[3]); 7360 } 7361 const smask = this.current.activeSMask; 7362 const suspendedCtx = this.suspendedCtx; 7363 this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox); 7364 this.ctx.save(); 7365 this.ctx.setTransform(1, 0, 0, 1, 0, 0); 7366 this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 7367 this.ctx.restore(); 7368 } 7369 composeSMask(ctx, smask, layerCtx, layerBox) { 7370 const layerOffsetX = layerBox[0]; 7371 const layerOffsetY = layerBox[1]; 7372 const layerWidth = layerBox[2] - layerOffsetX; 7373 const layerHeight = layerBox[3] - layerOffsetY; 7374 if (layerWidth === 0 || layerHeight === 0) { 7375 return; 7376 } 7377 this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY); 7378 ctx.save(); 7379 ctx.globalAlpha = 1; 7380 ctx.globalCompositeOperation = "source-over"; 7381 ctx.setTransform(1, 0, 0, 1, 0, 0); 7382 ctx.drawImage(layerCtx.canvas, 0, 0);
7383 ctx.restore(); 7384 } 7385 genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) { 7386 let maskCanvas = maskCtx.canvas; 7387 let maskX = layerOffsetX - maskOffsetX; 7388 let maskY = layerOffsetY - maskOffsetY; 7389 if (backdrop) { 7390 const backdropRGB = Util.makeHexColor(...backdrop); 7391 if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) { 7392 const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height); 7393 const ctx = canvas.context; 7394 ctx.drawImage(maskCanvas, -maskX, -maskY); 7395 ctx.globalCompositeOperation = "destination-atop"; 7396 ctx.fillStyle = backdropRGB; 7397 ctx.fillRect(0, 0, width, height); 7398 ctx.globalCompositeOperation = "source-over"; 7399 maskCanvas = canvas.canvas; 7400 maskX = maskY = 0; 7401 } else { 7402 maskCtx.save(); 7403 maskCtx.globalAlpha = 1; 7404 maskCtx.setTransform(1, 0, 0, 1, 0, 0); 7405 const clip = new Path2D(); 7406 clip.rect(maskX, maskY, width, height); 7407 maskCtx.clip(clip); 7408 maskCtx.globalCompositeOperation = "destination-atop"; 7409 maskCtx.fillStyle = backdropRGB; 7410 maskCtx.fillRect(maskX, maskY, width, height); 7411 maskCtx.restore(); 7412 } 7413 } 7414 layerCtx.save(); 7415 layerCtx.globalAlpha = 1; 7416 layerCtx.setTransform(1, 0, 0, 1, 0, 0); 7417 if (subtype === "Alpha" && transferMap) { 7418 layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap); 7419 } else if (subtype === "Luminosity") { 7420 layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap); 7421 } 7422 const clip = new Path2D(); 7423 clip.rect(layerOffsetX, layerOffsetY, width, height); 7424 layerCtx.clip(clip); 7425 layerCtx.globalCompositeOperation = "destination-in"; 7426 layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height); 7427 layerCtx.restore(); 7428 } 7429 save() { 7430 if (this.inSMaskMode) { 7431 copyCtxState(this.ctx, this.suspendedCtx); 7432 this.suspendedCtx.save(); 7433 } else { 7434 this.ctx.save(); 7435 } 7436 const old = this.current; 7437 this.stateStack.push(old); 7438 this.current = old.clone(); 7439 } 7440 restore() { 7441 if (this.stateStack.length === 0 && this.inSMaskMode) { 7442 this.endSMaskMode(); 7443 } 7444 if (this.stateStack.length !== 0) { 7445 this.current = this.stateStack.pop(); 7446 if (this.inSMaskMode) { 7447 this.suspendedCtx.restore(); 7448 copyCtxState(this.suspendedCtx, this.ctx); 7449 } else { 7450 this.ctx.restore(); 7451 } 7452 this.checkSMaskState(); 7453 this.pendingClip = null; 7454 this._cachedScaleForStroking[0] = -1; 7455 this._cachedGetSinglePixelWidth = null; 7456 } 7457 } 7458 transform(a, b, c, d, e, f) { 7459 this.ctx.transform(a, b, c, d, e, f); 7460 this._cachedScaleForStroking[0] = -1; 7461 this._cachedGetSinglePixelWidth = null; 7462 } 7463 constructPath(ops, args, minMax) { 7464 const ctx = this.ctx; 7465 const current = this.current; 7466 let x = current.x, 7467 y = current.y; 7468 let startX, startY; 7469 const currentTransform = getCurrentTransform(ctx); 7470 const isScalingMatrix = currentTransform[0] === 0 && currentTransform[3] === 0 || currentTransform[1] === 0 && currentTransform[2] === 0; 7471 const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null; 7472 for (let i = 0, j = 0, ii = ops.length; i < ii; i++) { 7473 switch (ops[i] | 0) { 7474 case OPS.rectangle: 7475 x = args[j++]; 7476 y = args[j++]; 7477 const width = args[j++]; 7478 const height = args[j++]; 7479 const xw = x + width; 7480 const yh = y + height; 7481 ctx.moveTo(x, y); 7482 if (width === 0 || height === 0) { 7483 ctx.lineTo(xw, yh); 7484 } else { 7485 ctx.lineTo(xw, y); 7486 ctx.lineTo(xw, yh); 7487 ctx.lineTo(x, yh); 7488 } 7489 if (!isScalingMatrix) { 7490 current.updateRectMinMax(currentTransform, [x, y, xw, yh]); 7491 } 7492 ctx.closePath(); 7493 break; 7494 case OPS.moveTo: 7495 x = args[j++]; 7496 y = args[j++]; 7497 ctx.moveTo(x, y); 7498 if (!isScalingMatrix) { 7499 current.updatePathMinMax(currentTransform, x, y); 7500 } 7501 break; 7502 case OPS.lineTo: 7503 x = args[j++]; 7504 y = args[j++]; 7505 ctx.lineTo(x, y); 7506 if (!isScalingMatrix) { 7507 current.updatePathMinMax(currentTransform, x, y); 7508 } 7509 break; 7510 case OPS.curveTo: 7511 startX = x; 7512 startY = y; 7513 x = args[j + 4]; 7514 y = args[j + 5]; 7515 ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y); 7516 current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y, minMaxForBezier); 7517 j += 6; 7518 break; 7519 case OPS.curveTo2: 7520 startX = x; 7521 startY = y; 7522 ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]); 7523 current.updateCurvePathMinMax(currentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3], minMaxForBezier); 7524 x = args[j + 2]; 7525 y = args[j + 3]; 7526 j += 4; 7527 break; 7528 case OPS.curveTo3: 7529 startX = x; 7530 startY = y; 7531 x = args[j + 2]; 7532 y = args[j + 3]; 7533 ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y); 7534 current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], x, y, x, y, minMaxForBezier); 7535 j += 4; 7536 break; 7537 case OPS.closePath: 7538 ctx.closePath(); 7539 break; 7540 } 7541 } 7542 if (isScalingMatrix) { 7543 current.updateScalingPathMinMax(currentTransform, minMaxForBezier); 7544 } 7545 current.setCurrentPoint(x, y); 7546 } 7547 closePath() { 7548 this.ctx.closePath(); 7549 }
7550 stroke(consumePath = true) { 7551 const ctx = this.ctx; 7552 const strokeColor = this.current.strokeColor; 7553 ctx.globalAlpha = this.current.strokeAlpha; 7554 if (this.contentVisible) { 7555 if (typeof strokeColor === "object" && strokeColor?.getPattern) { 7556 ctx.save(); 7557 ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE); 7558 this.rescaleAndStroke(false); 7559 ctx.restore(); 7560 } else { 7561 this.rescaleAndStroke(true); 7562 } 7563 } 7564 if (consumePath) { 7565 this.consumePath(this.current.getClippedPathBoundingBox()); 7566 } 7567 ctx.globalAlpha = this.current.fillAlpha; 7568 } 7569 closeStroke() { 7570 this.closePath(); 7571 this.stroke(); 7572 } 7573 fill(consumePath = true) { 7574 const ctx = this.ctx; 7575 const fillColor = this.current.fillColor; 7576 const isPatternFill = this.current.patternFill; 7577 let needRestore = false; 7578 if (isPatternFill) { 7579 ctx.save(); 7580 ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL); 7581 needRestore = true; 7582 } 7583 const intersect = this.current.getClippedPathBoundingBox(); 7584 if (this.contentVisible && intersect !== null) { 7585 if (this.pendingEOFill) { 7586 ctx.fill("evenodd"); 7587 this.pendingEOFill = false; 7588 } else { 7589 ctx.fill(); 7590 } 7591 } 7592 if (needRestore) { 7593 ctx.restore(); 7594 } 7595 if (consumePath) { 7596 this.consumePath(intersect); 7597 } 7598 } 7599 eoFill() { 7600 this.pendingEOFill = true; 7601 this.fill(); 7602 } 7603 fillStroke() { 7604 this.fill(false); 7605 this.stroke(false); 7606 this.consumePath(); 7607 } 7608 eoFillStroke() { 7609 this.pendingEOFill = true; 7610 this.fillStroke(); 7611 } 7612 closeFillStroke() { 7613 this.closePath(); 7614 this.fillStroke(); 7615 } 7616 closeEOFillStroke() { 7617 this.pendingEOFill = true; 7618 this.closePath(); 7619 this.fillStroke(); 7620 } 7621 endPath() { 7622 this.consumePath(); 7623 } 7624 clip() { 7625 this.pendingClip = NORMAL_CLIP; 7626 } 7627 eoClip() { 7628 this.pendingClip = EO_CLIP; 7629 } 7630 beginText() { 7631 this.current.textMatrix = IDENTITY_MATRIX; 7632 this.current.textMatrixScale = 1; 7633 this.current.x = this.current.lineX = 0; 7634 this.current.y = this.current.lineY = 0; 7635 } 7636 endText() { 7637 const paths = this.pendingTextPaths; 7638 const ctx = this.ctx; 7639 if (paths === undefined) { 7640 ctx.beginPath(); 7641 return; 7642 } 7643 ctx.save(); 7644 ctx.beginPath(); 7645 for (const path of paths) { 7646 ctx.setTransform(...path.transform); 7647 ctx.translate(path.x, path.y); 7648 path.addToPath(ctx, path.fontSize); 7649 } 7650 ctx.restore(); 7651 ctx.clip(); 7652 ctx.beginPath(); 7653 delete this.pendingTextPaths; 7654 } 7655 setCharSpacing(spacing) { 7656 this.current.charSpacing = spacing; 7657 } 7658 setWordSpacing(spacing) { 7659 this.current.wordSpacing = spacing; 7660 } 7661 setHScale(scale) { 7662 this.current.textHScale = scale / 100; 7663 } 7664 setLeading(leading) { 7665 this.current.leading = -leading; 7666 } 7667 setFont(fontRefName, size) { 7668 const fontObj = this.commonObjs.get(fontRefName); 7669 const current = this.current; 7670 if (!fontObj) { 7671 throw new Error(`Can't find font for ${fontRefName}`); 7672 } 7673 current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX; 7674 if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) { 7675 warn("Invalid font matrix for font " + fontRefName); 7676 } 7677 if (size < 0) { 7678 size = -size; 7679 current.fontDirection = -1; 7680 } else { 7681 current.fontDirection = 1; 7682 } 7683 this.current.font = fontObj; 7684 this.current.fontSize = size; 7685 if (fontObj.isType3Font) { 7686 return; 7687 } 7688 const name = fontObj.loadedName || "sans-serif"; 7689 const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`; 7690 let bold = "normal"; 7691 if (fontObj.black) { 7692 bold = "900"; 7693 } else if (fontObj.bold) { 7694 bold = "bold"; 7695 } 7696 const italic = fontObj.italic ? "italic" : "normal"; 7697 let browserFontSize = size; 7698 if (size < MIN_FONT_SIZE) { 7699 browserFontSize = MIN_FONT_SIZE; 7700 } else if (size > MAX_FONT_SIZE) { 7701 browserFontSize = MAX_FONT_SIZE; 7702 } 7703 this.current.fontSizeScale = size / browserFontSize; 7704 this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`; 7705 } 7706 setTextRenderingMode(mode) { 7707 this.current.textRenderingMode = mode; 7708 } 7709 setTextRise(rise) { 7710 this.current.textRise = rise; 7711 } 7712 moveText(x, y) { 7713 this.current.x = this.current.lineX += x; 7714 this.current.y = this.current.lineY += y; 7715 } 7716 setLeadingMoveText(x, y) { 7717 this.setLeading(-y); 7718 this.moveText(x, y); 7719 } 7720 setTextMatrix(a, b, c, d, e, f) { 7721 this.current.textMatrix = [a, b, c, d, e, f]; 7722 this.current.textMatrixScale = Math.hypot(a, b); 7723 this.current.x = this.current.lineX = 0; 7724 this.current.y = this.current.lineY = 0; 7725 } 7726 nextLine() { 7727 this.moveText(0, this.current.leading); 7728 } 7729 paintChar(character, x, y, patternTransform) { 7730 const ctx = this.ctx; 7731 const current = this.current; 7732 const font = current.font; 7733 const textRenderingMode = current.textRenderingMode; 7734 const fontSize = current.fontSize / current.fontSizeScale; 7735 const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; 7736 const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); 7737 const patternFill = current.patternFill && !font.missingFile; 7738 let addToPath; 7739 if (font.disableFontFace || isAddToPathSet || patternFill) { 7740 addToPath = font.getPathGenerator(this.commonObjs, character); 7741 } 7742 if (font.disableFontFace || patternFill) { 7743 ctx.save(); 7744 ctx.translate(x, y); 7745 ctx.beginPath(); 7746 addToPath(ctx, fontSize); 7747 if (patternTransform) { 7748 ctx.setTransform(...patternTransform); 7749 } 7750 if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 7751 ctx.fill(); 7752 } 7753 if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 7754 ctx.stroke(); 7755 }
vendor: 3,502 bytes, lines 7756-7861
7756 ctx.restore(); 7757 } else { 7758 if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 7759 ctx.fillText(character, x, y); 7760 } 7761 if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 7762 ctx.strokeText(character, x, y); 7763 } 7764 } 7765 if (isAddToPathSet) { 7766 const paths = this.pendingTextPaths ||= []; 7767 paths.push({ 7768 transform: getCurrentTransform(ctx), 7769 x, 7770 y, 7771 fontSize, 7772 addToPath 7773 }); 7774 } 7775 } 7776 get isFontSubpixelAAEnabled() { 7777 const { 7778 context: ctx 7779 } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10); 7780 ctx.scale(1.5, 1); 7781 ctx.fillText("I", 0, 10); 7782 const data = ctx.getImageData(0, 0, 10, 10).data; 7783 let enabled = false; 7784 for (let i = 3; i < data.length; i += 4) { 7785 if (data[i] > 0 && data[i] < 255) { 7786 enabled = true; 7787 break; 7788 } 7789 } 7790 return shadow(this, "isFontSubpixelAAEnabled", enabled); 7791 } 7792 showText(glyphs) { 7793 const current = this.current; 7794 const font = current.font; 7795 if (font.isType3Font) { 7796 return this.showType3Text(glyphs); 7797 } 7798 const fontSize = current.fontSize; 7799 if (fontSize === 0) { 7800 return undefined; 7801 } 7802 const ctx = this.ctx; 7803 const fontSizeScale = current.fontSizeScale; 7804 const charSpacing = current.charSpacing; 7805 const wordSpacing = current.wordSpacing; 7806 const fontDirection = current.fontDirection; 7807 const textHScale = current.textHScale * fontDirection; 7808 const glyphsLength = glyphs.length; 7809 const vertical = font.vertical; 7810 const spacingDir = vertical ? 1 : -1; 7811 const defaultVMetrics = font.defaultVMetrics; 7812 const widthAdvanceScale = fontSize * current.fontMatrix[0]; 7813 const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill; 7814 ctx.save(); 7815 ctx.transform(...current.textMatrix); 7816 ctx.translate(current.x, current.y + current.textRise); 7817 if (fontDirection > 0) { 7818 ctx.scale(textHScale, -1); 7819 } else { 7820 ctx.scale(textHScale, 1); 7821 } 7822 let patternTransform; 7823 if (current.patternFill) { 7824 ctx.save(); 7825 const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL); 7826 patternTransform = getCurrentTransform(ctx); 7827 ctx.restore(); 7828 ctx.fillStyle = pattern; 7829 } 7830 let lineWidth = current.lineWidth; 7831 const scale = current.textMatrixScale; 7832 if (scale === 0 || lineWidth === 0) { 7833 const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; 7834 if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 7835 lineWidth = this.getSinglePixelWidth(); 7836 } 7837 } else { 7838 lineWidth /= scale; 7839 } 7840 if (fontSizeScale !== 1.0) { 7841 ctx.scale(fontSizeScale, fontSizeScale); 7842 lineWidth /= fontSizeScale; 7843 } 7844 ctx.lineWidth = lineWidth; 7845 if (font.isInvalidPDFjsFont) { 7846 const chars = []; 7847 let width = 0; 7848 for (const glyph of glyphs) { 7849 chars.push(glyph.unicode); 7850 width += glyph.width; 7851 } 7852 ctx.fillText(chars.join(""), 0, 0); 7853 current.x += width * widthAdvanceScale * textHScale; 7854 ctx.restore(); 7855 this.compose(); 7856 return undefined; 7857 } 7858 let x = 0, 7859 i; 7860 for (i = 0; i < glyphsLength; ++i) { 7861 const glyph = glyphs[i];
vendor: 3,283 bytes, lines 7862-7946
7862 if (typeof glyph === "number") { 7863 x += spacingDir * glyph * fontSize / 1000; 7864 continue; 7865 } 7866 let restoreNeeded = false; 7867 const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; 7868 const character = glyph.fontChar; 7869 const accent = glyph.accent; 7870 let scaledX, scaledY; 7871 let width = glyph.width; 7872 if (vertical) { 7873 const vmetric = glyph.vmetric || defaultVMetrics; 7874 const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale; 7875 const vy = vmetric[2] * widthAdvanceScale; 7876 width = vmetric ? -vmetric[0] : width; 7877 scaledX = vx / fontSizeScale; 7878 scaledY = (x + vy) / fontSizeScale; 7879 } else { 7880 scaledX = x / fontSizeScale; 7881 scaledY = 0; 7882 } 7883 if (font.remeasure && width > 0) { 7884 const measuredWidth = ctx.measureText(character).width * 1000 / fontSize * fontSizeScale; 7885 if (width < measuredWidth && this.isFontSubpixelAAEnabled) { 7886 const characterScaleX = width / measuredWidth; 7887 restoreNeeded = true; 7888 ctx.save(); 7889 ctx.scale(characterScaleX, 1); 7890 scaledX /= characterScaleX; 7891 } else if (width !== measuredWidth) { 7892 scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale; 7893 } 7894 } 7895 if (this.contentVisible && (glyph.isInFont || font.missingFile)) { 7896 if (simpleFillText && !accent) { 7897 ctx.fillText(character, scaledX, scaledY); 7898 } else { 7899 this.paintChar(character, scaledX, scaledY, patternTransform); 7900 if (accent) { 7901 const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale; 7902 const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale; 7903 this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY, patternTransform); 7904 } 7905 } 7906 } 7907 const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection; 7908 x += charWidth; 7909 if (restoreNeeded) { 7910 ctx.restore(); 7911 } 7912 } 7913 if (vertical) { 7914 current.y -= x; 7915 } else { 7916 current.x += x * textHScale; 7917 } 7918 ctx.restore(); 7919 this.compose(); 7920 return undefined; 7921 } 7922 showType3Text(glyphs) { 7923 const ctx = this.ctx; 7924 const current = this.current; 7925 const font = current.font; 7926 const fontSize = current.fontSize; 7927 const fontDirection = current.fontDirection; 7928 const spacingDir = font.vertical ? 1 : -1; 7929 const charSpacing = current.charSpacing; 7930 const wordSpacing = current.wordSpacing; 7931 const textHScale = current.textHScale * fontDirection; 7932 const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; 7933 const glyphsLength = glyphs.length; 7934 const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE; 7935 let i, glyph, width, spacingLength; 7936 if (isTextInvisible || fontSize === 0) { 7937 return; 7938 } 7939 this._cachedScaleForStroking[0] = -1; 7940 this._cachedGetSinglePixelWidth = null; 7941 ctx.save(); 7942 ctx.transform(...current.textMatrix); 7943 ctx.translate(current.x, current.y); 7944 ctx.scale(textHScale, fontDirection); 7945 for (i = 0; i < glyphsLength; ++i) { 7946 glyph = glyphs[i];
7947 if (typeof glyph === "number") { 7948 spacingLength = spacingDir * glyph * fontSize / 1000; 7949 this.ctx.translate(spacingLength, 0); 7950 current.x += spacingLength * textHScale; 7951 continue; 7952 } 7953 const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; 7954 const operatorList = font.charProcOperatorList[glyph.operatorListId]; 7955 if (!operatorList) { 7956 warn(`Type3 character "${glyph.operatorListId}" is not available.`); 7957 continue; 7958 } 7959 if (this.contentVisible) { 7960 this.processingType3 = glyph; 7961 this.save(); 7962 ctx.scale(fontSize, fontSize); 7963 ctx.transform(...fontMatrix); 7964 this.executeOperatorList(operatorList); 7965 this.restore(); 7966 } 7967 const transformed = Util.applyTransform([glyph.width, 0], fontMatrix); 7968 width = transformed[0] * fontSize + spacing; 7969 ctx.translate(width, 0); 7970 current.x += width * textHScale; 7971 } 7972 ctx.restore(); 7973 this.processingType3 = null; 7974 } 7975 setCharWidth(xWidth, yWidth) {} 7976 setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) { 7977 this.ctx.rect(llx, lly, urx - llx, ury - lly); 7978 this.ctx.clip(); 7979 this.endPath(); 7980 } 7981 getColorN_Pattern(IR) { 7982 let pattern; 7983 if (IR[0] === "TilingPattern") { 7984 const color = IR[1]; 7985 const baseTransform = this.baseTransform || getCurrentTransform(this.ctx); 7986 const canvasGraphicsFactory = { 7987 createCanvasGraphics: ctx => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { 7988 optionalContentConfig: this.optionalContentConfig, 7989 markedContentStack: this.markedContentStack 7990 }) 7991 }; 7992 pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform); 7993 } else { 7994 pattern = this._getPattern(IR[1], IR[2]); 7995 } 7996 return pattern; 7997 } 7998 setStrokeColorN() { 7999 this.current.strokeColor = this.getColorN_Pattern(arguments); 8000 } 8001 setFillColorN() { 8002 this.current.fillColor = this.getColorN_Pattern(arguments); 8003 this.current.patternFill = true; 8004 } 8005 setStrokeRGBColor(r, g, b) { 8006 this.ctx.strokeStyle = this.current.strokeColor = Util.makeHexColor(r, g, b); 8007 }
8008 setStrokeTransparent() { 8009 this.ctx.strokeStyle = this.current.strokeColor = "transparent"; 8010 } 8011 setFillRGBColor(r, g, b) { 8012 this.ctx.fillStyle = this.current.fillColor = Util.makeHexColor(r, g, b); 8013 this.current.patternFill = false; 8014 } 8015 setFillTransparent() { 8016 this.ctx.fillStyle = this.current.fillColor = "transparent"; 8017 this.current.patternFill = false; 8018 } 8019 _getPattern(objId, matrix = null) { 8020 let pattern; 8021 if (this.cachedPatterns.has(objId)) { 8022 pattern = this.cachedPatterns.get(objId); 8023 } else { 8024 pattern = getShadingPattern(this.getObject(objId)); 8025 this.cachedPatterns.set(objId, pattern); 8026 } 8027 if (matrix) { 8028 pattern.matrix = matrix; 8029 } 8030 return pattern; 8031 } 8032 shadingFill(objId) { 8033 if (!this.contentVisible) { 8034 return; 8035 } 8036 const ctx = this.ctx; 8037 this.save(); 8038 const pattern = this._getPattern(objId); 8039 ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING); 8040 const inv = getCurrentTransformInverse(ctx); 8041 if (inv) { 8042 const { 8043 width, 8044 height 8045 } = ctx.canvas; 8046 const [x0, y0, x1, y1] = Util.getAxialAlignedBoundingBox([0, 0, width, height], inv); 8047 this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0); 8048 } else { 8049 this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); 8050 } 8051 this.compose(this.current.getClippedPathBoundingBox()); 8052 this.restore(); 8053 } 8054 beginInlineImage() { 8055 unreachable("Should not call beginInlineImage"); 8056 } 8057 beginImageData() { 8058 unreachable("Should not call beginImageData"); 8059 } 8060 paintFormXObjectBegin(matrix, bbox) { 8061 if (!this.contentVisible) { 8062 return; 8063 } 8064 this.save(); 8065 this.baseTransformStack.push(this.baseTransform); 8066 if (matrix) { 8067 this.transform(...matrix); 8068 } 8069 this.baseTransform = getCurrentTransform(this.ctx); 8070 if (bbox) { 8071 const width = bbox[2] - bbox[0]; 8072 const height = bbox[3] - bbox[1]; 8073 this.ctx.rect(bbox[0], bbox[1], width, height); 8074 this.current.updateRectMinMax(getCurrentTransform(this.ctx), bbox); 8075 this.clip(); 8076 this.endPath(); 8077 } 8078 } 8079 paintFormXObjectEnd() { 8080 if (!this.contentVisible) { 8081 return; 8082 } 8083 this.restore(); 8084 this.baseTransform = this.baseTransformStack.pop(); 8085 } 8086 beginGroup(group) { 8087 if (!this.contentVisible) { 8088 return; 8089 } 8090 this.save(); 8091 if (this.inSMaskMode) { 8092 this.endSMaskMode(); 8093 this.current.activeSMask = null; 8094 } 8095 const currentCtx = this.ctx; 8096 if (!group.isolated) { 8097 info("TODO: Support non-isolated groups."); 8098 } 8099 if (group.knockout) { 8100 warn("Knockout groups not supported."); 8101 } 8102 const currentTransform = getCurrentTransform(currentCtx); 8103 if (group.matrix) { 8104 currentCtx.transform(...group.matrix); 8105 } 8106 if (!group.bbox) { 8107 throw new Error("Bounding box is required."); 8108 } 8109 let bounds = Util.getAxialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx)); 8110 const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height]; 8111 bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; 8112 const offsetX = Math.floor(bounds[0]); 8113 const offsetY = Math.floor(bounds[1]); 8114 const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1); 8115 const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1); 8116 this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]); 8117 let cacheId = "groupAt" + this.groupLevel; 8118 if (group.smask) { 8119 cacheId += "_smask_" + this.smaskCounter++ % 2; 8120 } 8121 const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); 8122 const groupCtx = scratchCanvas.context; 8123 groupCtx.translate(-offsetX, -offsetY); 8124 groupCtx.transform(...currentTransform); 8125 if (group.smask) { 8126 this.smaskStack.push({ 8127 canvas: scratchCanvas.canvas, 8128 context: groupCtx, 8129 offsetX, 8130 offsetY, 8131 subtype: group.smask.subtype, 8132 backdrop: group.smask.backdrop, 8133 transferMap: group.smask.transferMap || null, 8134 startTransformInverse: null 8135 }); 8136 } else { 8137 currentCtx.setTransform(1, 0, 0, 1, 0, 0); 8138 currentCtx.translate(offsetX, offsetY); 8139 currentCtx.save(); 8140 } 8141 copyCtxState(currentCtx, groupCtx); 8142 this.ctx = groupCtx; 8143 this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]); 8144 this.groupStack.push(currentCtx); 8145 this.groupLevel++; 8146 } 8147 endGroup(group) { 8148 if (!this.contentVisible) { 8149 return; 8150 } 8151 this.groupLevel--; 8152 const groupCtx = this.ctx; 8153 const ctx = this.groupStack.pop(); 8154 this.ctx = ctx; 8155 this.ctx.imageSmoothingEnabled = false;
8156 if (group.smask) { 8157 this.tempSMask = this.smaskStack.pop(); 8158 this.restore(); 8159 } else { 8160 this.ctx.restore(); 8161 const currentMtx = getCurrentTransform(this.ctx); 8162 this.restore(); 8163 this.ctx.save(); 8164 this.ctx.setTransform(...currentMtx); 8165 const dirtyBox = Util.getAxialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx); 8166 this.ctx.drawImage(groupCtx.canvas, 0, 0); 8167 this.ctx.restore(); 8168 this.compose(dirtyBox); 8169 } 8170 } 8171 beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) { 8172 this.#restoreInitialState(); 8173 resetCtxToDefault(this.ctx); 8174 this.ctx.save(); 8175 this.save(); 8176 if (this.baseTransform) { 8177 this.ctx.setTransform(...this.baseTransform); 8178 } 8179 if (rect) { 8180 const width = rect[2] - rect[0]; 8181 const height = rect[3] - rect[1]; 8182 if (hasOwnCanvas && this.annotationCanvasMap) { 8183 transform = transform.slice(); 8184 transform[4] -= rect[0]; 8185 transform[5] -= rect[1]; 8186 rect = rect.slice(); 8187 rect[0] = rect[1] = 0; 8188 rect[2] = width; 8189 rect[3] = height; 8190 const [scaleX, scaleY] = Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx)); 8191 const { 8192 viewportScale 8193 } = this; 8194 const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale); 8195 const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale); 8196 this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight); 8197 const { 8198 canvas, 8199 context 8200 } = this.annotationCanvas; 8201 this.annotationCanvasMap.set(id, canvas); 8202 this.annotationCanvas.savedCtx = this.ctx; 8203 this.ctx = context; 8204 this.ctx.save(); 8205 this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY); 8206 resetCtxToDefault(this.ctx); 8207 } else { 8208 resetCtxToDefault(this.ctx); 8209 this.endPath(); 8210 this.ctx.rect(rect[0], rect[1], width, height); 8211 this.ctx.clip(); 8212 this.ctx.beginPath(); 8213 } 8214 } 8215 this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); 8216 this.transform(...transform); 8217 this.transform(...matrix); 8218 } 8219 endAnnotation() { 8220 if (this.annotationCanvas) { 8221 this.ctx.restore(); 8222 this.#drawFilter(); 8223 this.ctx = this.annotationCanvas.savedCtx; 8224 delete this.annotationCanvas.savedCtx; 8225 delete this.annotationCanvas; 8226 } 8227 } 8228 paintImageMaskXObject(img) { 8229 if (!this.contentVisible) { 8230 return; 8231 } 8232 const count = img.count; 8233 img = this.getObject(img.data, img); 8234 img.count = count; 8235 const ctx = this.ctx; 8236 const glyph = this.processingType3; 8237 if (glyph) { 8238 if (glyph.compiled === undefined) { 8239 glyph.compiled = compileType3Glyph(img); 8240 } 8241 if (glyph.compiled) { 8242 glyph.compiled(ctx); 8243 return; 8244 } 8245 } 8246 const mask = this._createMaskCanvas(img); 8247 const maskCanvas = mask.canvas; 8248 ctx.save(); 8249 ctx.setTransform(1, 0, 0, 1, 0, 0); 8250 ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY); 8251 ctx.restore(); 8252 this.compose(); 8253 } 8254 paintImageMaskXObjectRepeat(img, scaleX, skewX = 0, skewY = 0, scaleY, positions) { 8255 if (!this.contentVisible) { 8256 return; 8257 } 8258 img = this.getObject(img.data, img); 8259 const ctx = this.ctx; 8260 ctx.save(); 8261 const currentTransform = getCurrentTransform(ctx); 8262 ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0); 8263 const mask = this._createMaskCanvas(img); 8264 ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]); 8265 for (let i = 0, ii = positions.length; i < ii; i += 2) { 8266 const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]); 8267 const [x, y] = Util.applyTransform([0, 0], trans); 8268 ctx.drawImage(mask.canvas, x, y); 8269 } 8270 ctx.restore(); 8271 this.compose(); 8272 } 8273 paintImageMaskXObjectGroup(images) { 8274 if (!this.contentVisible) { 8275 return; 8276 } 8277 const ctx = this.ctx; 8278 const fillColor = this.current.fillColor; 8279 const isPatternFill = this.current.patternFill; 8280 for (const image of images) { 8281 const { 8282 data, 8283 width, 8284 height, 8285 transform 8286 } = image; 8287 const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); 8288 const maskCtx = maskCanvas.context; 8289 maskCtx.save(); 8290 const img = this.getObject(data, image); 8291 putBinaryImageMask(maskCtx, img); 8292 maskCtx.globalCompositeOperation = "source-in"; 8293 maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL) : fillColor; 8294 maskCtx.fillRect(0, 0, width, height);
vendor: 4,749 bytes, lines 8295-8458
8295 maskCtx.restore(); 8296 ctx.save(); 8297 ctx.transform(...transform); 8298 ctx.scale(1, -1); 8299 drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); 8300 ctx.restore(); 8301 } 8302 this.compose(); 8303 } 8304 paintImageXObject(objId) { 8305 if (!this.contentVisible) { 8306 return; 8307 } 8308 const imgData = this.getObject(objId); 8309 if (!imgData) { 8310 warn("Dependent image isn't ready yet"); 8311 return; 8312 } 8313 this.paintInlineImageXObject(imgData); 8314 } 8315 paintImageXObjectRepeat(objId, scaleX, scaleY, positions) { 8316 if (!this.contentVisible) { 8317 return; 8318 } 8319 const imgData = this.getObject(objId); 8320 if (!imgData) { 8321 warn("Dependent image isn't ready yet"); 8322 return; 8323 } 8324 const width = imgData.width; 8325 const height = imgData.height; 8326 const map = []; 8327 for (let i = 0, ii = positions.length; i < ii; i += 2) { 8328 map.push({ 8329 transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]], 8330 x: 0, 8331 y: 0, 8332 w: width, 8333 h: height 8334 }); 8335 } 8336 this.paintInlineImageXObjectGroup(imgData, map); 8337 } 8338 applyTransferMapsToCanvas(ctx) { 8339 if (this.current.transferMaps !== "none") { 8340 ctx.filter = this.current.transferMaps; 8341 ctx.drawImage(ctx.canvas, 0, 0); 8342 ctx.filter = "none"; 8343 } 8344 return ctx.canvas; 8345 } 8346 applyTransferMapsToBitmap(imgData) { 8347 if (this.current.transferMaps === "none") { 8348 return imgData.bitmap; 8349 } 8350 const { 8351 bitmap, 8352 width, 8353 height 8354 } = imgData; 8355 const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); 8356 const tmpCtx = tmpCanvas.context; 8357 tmpCtx.filter = this.current.transferMaps; 8358 tmpCtx.drawImage(bitmap, 0, 0); 8359 tmpCtx.filter = "none"; 8360 return tmpCanvas.canvas; 8361 } 8362 paintInlineImageXObject(imgData) { 8363 if (!this.contentVisible) { 8364 return; 8365 } 8366 const width = imgData.width; 8367 const height = imgData.height; 8368 const ctx = this.ctx; 8369 this.save(); 8370 if (!isNodeJS) { 8371 const { 8372 filter 8373 } = ctx; 8374 if (filter !== "none" && filter !== "") { 8375 ctx.filter = "none"; 8376 } 8377 } 8378 ctx.scale(1 / width, -1 / height); 8379 let imgToPaint; 8380 if (imgData.bitmap) { 8381 imgToPaint = this.applyTransferMapsToBitmap(imgData); 8382 } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) { 8383 imgToPaint = imgData; 8384 } else { 8385 const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); 8386 const tmpCtx = tmpCanvas.context; 8387 putBinaryImageData(tmpCtx, imgData); 8388 imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); 8389 } 8390 const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx)); 8391 ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate); 8392 drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height); 8393 this.compose(); 8394 this.restore(); 8395 } 8396 paintInlineImageXObjectGroup(imgData, map) { 8397 if (!this.contentVisible) { 8398 return; 8399 } 8400 const ctx = this.ctx; 8401 let imgToPaint; 8402 if (imgData.bitmap) { 8403 imgToPaint = imgData.bitmap; 8404 } else { 8405 const w = imgData.width; 8406 const h = imgData.height; 8407 const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h); 8408 const tmpCtx = tmpCanvas.context; 8409 putBinaryImageData(tmpCtx, imgData); 8410 imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); 8411 } 8412 for (const entry of map) { 8413 ctx.save(); 8414 ctx.transform(...entry.transform); 8415 ctx.scale(1, -1); 8416 drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); 8417 ctx.restore(); 8418 } 8419 this.compose(); 8420 } 8421 paintSolidColorImageMask() { 8422 if (!this.contentVisible) { 8423 return; 8424 } 8425 this.ctx.fillRect(0, 0, 1, 1); 8426 this.compose(); 8427 } 8428 markPoint(tag) {} 8429 markPointProps(tag, properties) {} 8430 beginMarkedContent(tag) { 8431 this.markedContentStack.push({ 8432 visible: true 8433 }); 8434 } 8435 beginMarkedContentProps(tag, properties) { 8436 if (tag === "OC") { 8437 this.markedContentStack.push({ 8438 visible: this.optionalContentConfig.isVisible(properties) 8439 }); 8440 } else { 8441 this.markedContentStack.push({ 8442 visible: true 8443 }); 8444 } 8445 this.contentVisible = this.isContentVisible(); 8446 } 8447 endMarkedContent() { 8448 this.markedContentStack.pop(); 8449 this.contentVisible = this.isContentVisible(); 8450 } 8451 beginCompat() {} 8452 endCompat() {} 8453 consumePath(clipBox) { 8454 const isEmpty = this.current.isEmptyClip(); 8455 if (this.pendingClip) { 8456 this.current.updateClipFromPath(); 8457 } 8458 if (!this.pendingClip) {
vendor: 16,943 bytes, lines 8459-9045
8459 this.compose(clipBox); 8460 } 8461 const ctx = this.ctx; 8462 if (this.pendingClip) { 8463 if (!isEmpty) { 8464 if (this.pendingClip === EO_CLIP) { 8465 ctx.clip("evenodd"); 8466 } else { 8467 ctx.clip(); 8468 } 8469 } 8470 this.pendingClip = null; 8471 } 8472 this.current.startNewPathAndClipBox(this.current.clipBox); 8473 ctx.beginPath(); 8474 } 8475 getSinglePixelWidth() { 8476 if (!this._cachedGetSinglePixelWidth) { 8477 const m = getCurrentTransform(this.ctx); 8478 if (m[1] === 0 && m[2] === 0) { 8479 this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3])); 8480 } else { 8481 const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]); 8482 const normX = Math.hypot(m[0], m[2]); 8483 const normY = Math.hypot(m[1], m[3]); 8484 this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet; 8485 } 8486 } 8487 return this._cachedGetSinglePixelWidth; 8488 } 8489 getScaleForStroking() { 8490 if (this._cachedScaleForStroking[0] === -1) { 8491 const { 8492 lineWidth 8493 } = this.current; 8494 const { 8495 a, 8496 b, 8497 c, 8498 d 8499 } = this.ctx.getTransform(); 8500 let scaleX, scaleY; 8501 if (b === 0 && c === 0) { 8502 const normX = Math.abs(a); 8503 const normY = Math.abs(d); 8504 if (normX === normY) { 8505 if (lineWidth === 0) { 8506 scaleX = scaleY = 1 / normX; 8507 } else { 8508 const scaledLineWidth = normX * lineWidth; 8509 scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1; 8510 } 8511 } else if (lineWidth === 0) { 8512 scaleX = 1 / normX; 8513 scaleY = 1 / normY; 8514 } else { 8515 const scaledXLineWidth = normX * lineWidth; 8516 const scaledYLineWidth = normY * lineWidth; 8517 scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1; 8518 scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1; 8519 } 8520 } else { 8521 const absDet = Math.abs(a * d - b * c); 8522 const normX = Math.hypot(a, b); 8523 const normY = Math.hypot(c, d); 8524 if (lineWidth === 0) { 8525 scaleX = normY / absDet; 8526 scaleY = normX / absDet; 8527 } else { 8528 const baseArea = lineWidth * absDet; 8529 scaleX = normY > baseArea ? normY / baseArea : 1; 8530 scaleY = normX > baseArea ? normX / baseArea : 1; 8531 } 8532 } 8533 this._cachedScaleForStroking[0] = scaleX; 8534 this._cachedScaleForStroking[1] = scaleY; 8535 } 8536 return this._cachedScaleForStroking; 8537 } 8538 rescaleAndStroke(saveRestore) { 8539 const { 8540 ctx 8541 } = this; 8542 const { 8543 lineWidth 8544 } = this.current; 8545 const [scaleX, scaleY] = this.getScaleForStroking(); 8546 ctx.lineWidth = lineWidth || 1; 8547 if (scaleX === 1 && scaleY === 1) { 8548 ctx.stroke(); 8549 return; 8550 } 8551 const dashes = ctx.getLineDash(); 8552 if (saveRestore) { 8553 ctx.save(); 8554 } 8555 ctx.scale(scaleX, scaleY); 8556 if (dashes.length > 0) { 8557 const scale = Math.max(scaleX, scaleY); 8558 ctx.setLineDash(dashes.map(x => x / scale)); 8559 ctx.lineDashOffset /= scale; 8560 } 8561 ctx.stroke(); 8562 if (saveRestore) { 8563 ctx.restore(); 8564 } 8565 } 8566 isContentVisible() { 8567 for (let i = this.markedContentStack.length - 1; i >= 0; i--) { 8568 if (!this.markedContentStack[i].visible) { 8569 return false; 8570 } 8571 } 8572 return true; 8573 } 8574} 8575for (const op in OPS) { 8576 if (CanvasGraphics.prototype[op] !== undefined) { 8577 CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; 8578 } 8579} 8580 8581;// ./src/display/worker_options.js 8582class GlobalWorkerOptions { 8583 static #port = null; 8584 static #src = ""; 8585 static get workerPort() { 8586 return this.#port; 8587 } 8588 static set workerPort(val) { 8589 if (!(typeof Worker !== "undefined" && val instanceof Worker) && val !== null) { 8590 throw new Error("Invalid `workerPort` type."); 8591 } 8592 this.#port = val; 8593 } 8594 static get workerSrc() { 8595 return this.#src; 8596 } 8597 static set workerSrc(val) { 8598 if (typeof val !== "string") { 8599 throw new Error("Invalid `workerSrc` type."); 8600 } 8601 this.#src = val; 8602 } 8603} 8604 8605;// ./src/shared/message_handler.js 8606 8607const CallbackKind = { 8608 UNKNOWN: 0, 8609 DATA: 1, 8610 ERROR: 2 8611}; 8612const StreamKind = { 8613 UNKNOWN: 0, 8614 CANCEL: 1, 8615 CANCEL_COMPLETE: 2, 8616 CLOSE: 3, 8617 ENQUEUE: 4, 8618 ERROR: 5, 8619 PULL: 6, 8620 PULL_COMPLETE: 7, 8621 START_COMPLETE: 8 8622}; 8623function wrapReason(reason) { 8624 if (!(reason instanceof Error || typeof reason === "object" && reason !== null)) { 8625 unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.'); 8626 } 8627 switch (reason.name) { 8628 case "AbortException": 8629 return new AbortException(reason.message); 8630 case "MissingPDFException": 8631 return new MissingPDFException(reason.message); 8632 case "PasswordException": 8633 return new PasswordException(reason.message, reason.code); 8634 case "UnexpectedResponseException": 8635 return new UnexpectedResponseException(reason.message, reason.status); 8636 case "UnknownErrorException": 8637 return new UnknownErrorException(reason.message, reason.details); 8638 default: 8639 return new UnknownErrorException(reason.message, reason.toString()); 8640 } 8641} 8642class MessageHandler { 8643 #messageAC = new AbortController(); 8644 constructor(sourceName, targetName, comObj) { 8645 this.sourceName = sourceName; 8646 this.targetName = targetName; 8647 this.comObj = comObj; 8648 this.callbackId = 1; 8649 this.streamId = 1; 8650 this.streamSinks = Object.create(null); 8651 this.streamControllers = Object.create(null); 8652 this.callbackCapabilities = Object.create(null); 8653 this.actionHandler = Object.create(null); 8654 comObj.addEventListener("message", this.#onMessage.bind(this), { 8655 signal: this.#messageAC.signal 8656 }); 8657 } 8658 #onMessage({ 8659 data 8660 }) { 8661 if (data.targetName !== this.sourceName) { 8662 return; 8663 } 8664 if (data.stream) { 8665 this.#processStreamMessage(data); 8666 return; 8667 } 8668 if (data.callback) { 8669 const callbackId = data.callbackId; 8670 const capability = this.callbackCapabilities[callbackId]; 8671 if (!capability) { 8672 throw new Error(`Cannot resolve callback ${callbackId}`); 8673 } 8674 delete this.callbackCapabilities[callbackId]; 8675 if (data.callback === CallbackKind.DATA) { 8676 capability.resolve(data.data); 8677 } else if (data.callback === CallbackKind.ERROR) { 8678 capability.reject(wrapReason(data.reason)); 8679 } else { 8680 throw new Error("Unexpected callback case"); 8681 } 8682 return; 8683 } 8684 const action = this.actionHandler[data.action]; 8685 if (!action) { 8686 throw new Error(`Unknown action from worker: ${data.action}`); 8687 } 8688 if (data.callbackId) { 8689 const sourceName = this.sourceName, 8690 targetName = data.sourceName, 8691 comObj = this.comObj; 8692 new Promise(function (resolve) { 8693 resolve(action(data.data)); 8694 }).then(function (result) { 8695 comObj.postMessage({ 8696 sourceName, 8697 targetName, 8698 callback: CallbackKind.DATA, 8699 callbackId: data.callbackId, 8700 data: result 8701 }); 8702 }, function (reason) { 8703 comObj.postMessage({ 8704 sourceName, 8705 targetName, 8706 callback: CallbackKind.ERROR, 8707 callbackId: data.callbackId, 8708 reason: wrapReason(reason) 8709 }); 8710 }); 8711 return; 8712 } 8713 if (data.streamId) { 8714 this.#createStreamSink(data); 8715 return; 8716 } 8717 action(data.data); 8718 } 8719 on(actionName, handler) { 8720 const ah = this.actionHandler; 8721 if (ah[actionName]) { 8722 throw new Error(`There is already an actionName called "${actionName}"`); 8723 } 8724 ah[actionName] = handler; 8725 } 8726 send(actionName, data, transfers) { 8727 this.comObj.postMessage({ 8728 sourceName: this.sourceName, 8729 targetName: this.targetName, 8730 action: actionName, 8731 data 8732 }, transfers); 8733 } 8734 sendWithPromise(actionName, data, transfers) { 8735 const callbackId = this.callbackId++; 8736 const capability = Promise.withResolvers(); 8737 this.callbackCapabilities[callbackId] = capability; 8738 try { 8739 this.comObj.postMessage({ 8740 sourceName: this.sourceName, 8741 targetName: this.targetName, 8742 action: actionName, 8743 callbackId, 8744 data 8745 }, transfers); 8746 } catch (ex) { 8747 capability.reject(ex); 8748 } 8749 return capability.promise; 8750 } 8751 sendWithStream(actionName, data, queueingStrategy, transfers) { 8752 const streamId = this.streamId++, 8753 sourceName = this.sourceName, 8754 targetName = this.targetName, 8755 comObj = this.comObj; 8756 return new ReadableStream({ 8757 start: controller => { 8758 const startCapability = Promise.withResolvers(); 8759 this.streamControllers[streamId] = { 8760 controller, 8761 startCall: startCapability, 8762 pullCall: null, 8763 cancelCall: null, 8764 isClosed: false 8765 }; 8766 comObj.postMessage({ 8767 sourceName, 8768 targetName, 8769 action: actionName, 8770 streamId, 8771 data, 8772 desiredSize: controller.desiredSize 8773 }, transfers); 8774 return startCapability.promise; 8775 }, 8776 pull: controller => { 8777 const pullCapability = Promise.withResolvers(); 8778 this.streamControllers[streamId].pullCall = pullCapability; 8779 comObj.postMessage({ 8780 sourceName, 8781 targetName, 8782 stream: StreamKind.PULL, 8783 streamId, 8784 desiredSize: controller.desiredSize 8785 }); 8786 return pullCapability.promise; 8787 }, 8788 cancel: reason => { 8789 assert(reason instanceof Error, "cancel must have a valid reason"); 8790 const cancelCapability = Promise.withResolvers(); 8791 this.streamControllers[streamId].cancelCall = cancelCapability; 8792 this.streamControllers[streamId].isClosed = true; 8793 comObj.postMessage({ 8794 sourceName, 8795 targetName, 8796 stream: StreamKind.CANCEL, 8797 streamId, 8798 reason: wrapReason(reason) 8799 }); 8800 return cancelCapability.promise; 8801 } 8802 }, queueingStrategy); 8803 } 8804 #createStreamSink(data) { 8805 const streamId = data.streamId, 8806 sourceName = this.sourceName, 8807 targetName = data.sourceName, 8808 comObj = this.comObj; 8809 const self = this, 8810 action = this.actionHandler[data.action]; 8811 const streamSink = { 8812 enqueue(chunk, size = 1, transfers) { 8813 if (this.isCancelled) { 8814 return; 8815 } 8816 const lastDesiredSize = this.desiredSize; 8817 this.desiredSize -= size; 8818 if (lastDesiredSize > 0 && this.desiredSize <= 0) { 8819 this.sinkCapability = Promise.withResolvers(); 8820 this.ready = this.sinkCapability.promise; 8821 } 8822 comObj.postMessage({ 8823 sourceName, 8824 targetName, 8825 stream: StreamKind.ENQUEUE, 8826 streamId, 8827 chunk 8828 }, transfers); 8829 }, 8830 close() { 8831 if (this.isCancelled) { 8832 return; 8833 } 8834 this.isCancelled = true; 8835 comObj.postMessage({ 8836 sourceName, 8837 targetName, 8838 stream: StreamKind.CLOSE, 8839 streamId 8840 }); 8841 delete self.streamSinks[streamId]; 8842 }, 8843 error(reason) { 8844 assert(reason instanceof Error, "error must have a valid reason"); 8845 if (this.isCancelled) { 8846 return; 8847 } 8848 this.isCancelled = true; 8849 comObj.postMessage({ 8850 sourceName, 8851 targetName, 8852 stream: StreamKind.ERROR, 8853 streamId, 8854 reason: wrapReason(reason) 8855 }); 8856 }, 8857 sinkCapability: Promise.withResolvers(), 8858 onPull: null, 8859 onCancel: null, 8860 isCancelled: false, 8861 desiredSize: data.desiredSize, 8862 ready: null 8863 }; 8864 streamSink.sinkCapability.resolve(); 8865 streamSink.ready = streamSink.sinkCapability.promise; 8866 this.streamSinks[streamId] = streamSink; 8867 new Promise(function (resolve) { 8868 resolve(action(data.data, streamSink)); 8869 }).then(function () { 8870 comObj.postMessage({ 8871 sourceName, 8872 targetName, 8873 stream: StreamKind.START_COMPLETE, 8874 streamId, 8875 success: true 8876 }); 8877 }, function (reason) { 8878 comObj.postMessage({ 8879 sourceName, 8880 targetName, 8881 stream: StreamKind.START_COMPLETE, 8882 streamId, 8883 reason: wrapReason(reason) 8884 }); 8885 }); 8886 } 8887 #processStreamMessage(data) { 8888 const streamId = data.streamId, 8889 sourceName = this.sourceName, 8890 targetName = data.sourceName, 8891 comObj = this.comObj; 8892 const streamController = this.streamControllers[streamId], 8893 streamSink = this.streamSinks[streamId]; 8894 switch (data.stream) { 8895 case StreamKind.START_COMPLETE: 8896 if (data.success) { 8897 streamController.startCall.resolve(); 8898 } else { 8899 streamController.startCall.reject(wrapReason(data.reason)); 8900 } 8901 break; 8902 case StreamKind.PULL_COMPLETE: 8903 if (data.success) { 8904 streamController.pullCall.resolve(); 8905 } else { 8906 streamController.pullCall.reject(wrapReason(data.reason)); 8907 } 8908 break; 8909 case StreamKind.PULL: 8910 if (!streamSink) { 8911 comObj.postMessage({ 8912 sourceName, 8913 targetName, 8914 stream: StreamKind.PULL_COMPLETE, 8915 streamId, 8916 success: true 8917 }); 8918 break; 8919 } 8920 if (streamSink.desiredSize <= 0 && data.desiredSize > 0) { 8921 streamSink.sinkCapability.resolve(); 8922 } 8923 streamSink.desiredSize = data.desiredSize; 8924 new Promise(function (resolve) { 8925 resolve(streamSink.onPull?.()); 8926 }).then(function () { 8927 comObj.postMessage({ 8928 sourceName, 8929 targetName, 8930 stream: StreamKind.PULL_COMPLETE, 8931 streamId, 8932 success: true 8933 }); 8934 }, function (reason) { 8935 comObj.postMessage({ 8936 sourceName, 8937 targetName, 8938 stream: StreamKind.PULL_COMPLETE, 8939 streamId, 8940 reason: wrapReason(reason) 8941 }); 8942 }); 8943 break; 8944 case StreamKind.ENQUEUE: 8945 assert(streamController, "enqueue should have stream controller"); 8946 if (streamController.isClosed) { 8947 break; 8948 } 8949 streamController.controller.enqueue(data.chunk); 8950 break; 8951 case StreamKind.CLOSE: 8952 assert(streamController, "close should have stream controller"); 8953 if (streamController.isClosed) { 8954 break; 8955 } 8956 streamController.isClosed = true; 8957 streamController.controller.close(); 8958 this.#deleteStreamController(streamController, streamId); 8959 break; 8960 case StreamKind.ERROR: 8961 assert(streamController, "error should have stream controller"); 8962 streamController.controller.error(wrapReason(data.reason)); 8963 this.#deleteStreamController(streamController, streamId); 8964 break; 8965 case StreamKind.CANCEL_COMPLETE: 8966 if (data.success) { 8967 streamController.cancelCall.resolve(); 8968 } else { 8969 streamController.cancelCall.reject(wrapReason(data.reason)); 8970 } 8971 this.#deleteStreamController(streamController, streamId); 8972 break; 8973 case StreamKind.CANCEL: 8974 if (!streamSink) { 8975 break; 8976 } 8977 new Promise(function (resolve) { 8978 resolve(streamSink.onCancel?.(wrapReason(data.reason))); 8979 }).then(function () { 8980 comObj.postMessage({ 8981 sourceName, 8982 targetName, 8983 stream: StreamKind.CANCEL_COMPLETE, 8984 streamId, 8985 success: true 8986 }); 8987 }, function (reason) { 8988 comObj.postMessage({ 8989 sourceName, 8990 targetName, 8991 stream: StreamKind.CANCEL_COMPLETE, 8992 streamId, 8993 reason: wrapReason(reason) 8994 }); 8995 }); 8996 streamSink.sinkCapability.reject(wrapReason(data.reason)); 8997 streamSink.isCancelled = true; 8998 delete this.streamSinks[streamId]; 8999 break; 9000 default: 9001 throw new Error("Unexpected stream case"); 9002 } 9003 } 9004 async #deleteStreamController(streamController, streamId) { 9005 await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]); 9006 delete this.streamControllers[streamId]; 9007 } 9008 destroy() { 9009 this.#messageAC?.abort(); 9010 this.#messageAC = null; 9011 } 9012} 9013 9014;// ./src/display/metadata.js 9015 9016class Metadata { 9017 #metadataMap; 9018 #data; 9019 constructor({ 9020 parsedData, 9021 rawData 9022 }) { 9023 this.#metadataMap = parsedData; 9024 this.#data = rawData; 9025 } 9026 getRaw() { 9027 return this.#data; 9028 } 9029 get(name) { 9030 return this.#metadataMap.get(name) ?? null; 9031 } 9032 getAll() { 9033 return objectFromMap(this.#metadataMap); 9034 } 9035 has(name) { 9036 return this.#metadataMap.has(name); 9037 } 9038} 9039 9040;// ./src/display/optional_content_config.js 9041 9042 9043const INTERNAL = Symbol("INTERNAL"); 9044class OptionalContentGroup { 9045 #isDisplay = false;
vendor: 13,465 bytes, lines 9046-9537
9046 #isPrint = false; 9047 #userSet = false; 9048 #visible = true; 9049 constructor(renderingIntent, { 9050 name, 9051 intent, 9052 usage, 9053 rbGroups 9054 }) { 9055 this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY); 9056 this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); 9057 this.name = name; 9058 this.intent = intent; 9059 this.usage = usage; 9060 this.rbGroups = rbGroups; 9061 } 9062 get visible() { 9063 if (this.#userSet) { 9064 return this.#visible; 9065 } 9066 if (!this.#visible) { 9067 return false; 9068 } 9069 const { 9070 print, 9071 view 9072 } = this.usage; 9073 if (this.#isDisplay) { 9074 return view?.viewState !== "OFF"; 9075 } else if (this.#isPrint) { 9076 return print?.printState !== "OFF"; 9077 } 9078 return true; 9079 } 9080 _setVisible(internal, visible, userSet = false) { 9081 if (internal !== INTERNAL) { 9082 unreachable("Internal method `_setVisible` called."); 9083 } 9084 this.#userSet = userSet; 9085 this.#visible = visible; 9086 } 9087} 9088class OptionalContentConfig { 9089 #cachedGetHash = null; 9090 #groups = new Map(); 9091 #initialHash = null; 9092 #order = null; 9093 constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) { 9094 this.renderingIntent = renderingIntent; 9095 this.name = null; 9096 this.creator = null; 9097 if (data === null) { 9098 return; 9099 } 9100 this.name = data.name; 9101 this.creator = data.creator; 9102 this.#order = data.order; 9103 for (const group of data.groups) { 9104 this.#groups.set(group.id, new OptionalContentGroup(renderingIntent, group)); 9105 } 9106 if (data.baseState === "OFF") { 9107 for (const group of this.#groups.values()) { 9108 group._setVisible(INTERNAL, false); 9109 } 9110 } 9111 for (const on of data.on) { 9112 this.#groups.get(on)._setVisible(INTERNAL, true); 9113 } 9114 for (const off of data.off) { 9115 this.#groups.get(off)._setVisible(INTERNAL, false); 9116 } 9117 this.#initialHash = this.getHash(); 9118 } 9119 #evaluateVisibilityExpression(array) { 9120 const length = array.length; 9121 if (length < 2) { 9122 return true; 9123 } 9124 const operator = array[0]; 9125 for (let i = 1; i < length; i++) { 9126 const element = array[i]; 9127 let state; 9128 if (Array.isArray(element)) { 9129 state = this.#evaluateVisibilityExpression(element); 9130 } else if (this.#groups.has(element)) { 9131 state = this.#groups.get(element).visible; 9132 } else { 9133 warn(`Optional content group not found: ${element}`); 9134 return true; 9135 } 9136 switch (operator) { 9137 case "And": 9138 if (!state) { 9139 return false; 9140 } 9141 break; 9142 case "Or": 9143 if (state) { 9144 return true; 9145 } 9146 break; 9147 case "Not": 9148 return !state; 9149 default: 9150 return true; 9151 } 9152 } 9153 return operator === "And"; 9154 } 9155 isVisible(group) { 9156 if (this.#groups.size === 0) { 9157 return true; 9158 } 9159 if (!group) { 9160 info("Optional content group not defined."); 9161 return true; 9162 } 9163 if (group.type === "OCG") { 9164 if (!this.#groups.has(group.id)) { 9165 warn(`Optional content group not found: ${group.id}`); 9166 return true; 9167 } 9168 return this.#groups.get(group.id).visible; 9169 } else if (group.type === "OCMD") { 9170 if (group.expression) { 9171 return this.#evaluateVisibilityExpression(group.expression); 9172 } 9173 if (!group.policy || group.policy === "AnyOn") { 9174 for (const id of group.ids) { 9175 if (!this.#groups.has(id)) { 9176 warn(`Optional content group not found: ${id}`); 9177 return true; 9178 } 9179 if (this.#groups.get(id).visible) { 9180 return true; 9181 } 9182 } 9183 return false; 9184 } else if (group.policy === "AllOn") { 9185 for (const id of group.ids) { 9186 if (!this.#groups.has(id)) { 9187 warn(`Optional content group not found: ${id}`); 9188 return true; 9189 } 9190 if (!this.#groups.get(id).visible) { 9191 return false; 9192 } 9193 } 9194 return true; 9195 } else if (group.policy === "AnyOff") { 9196 for (const id of group.ids) { 9197 if (!this.#groups.has(id)) { 9198 warn(`Optional content group not found: ${id}`); 9199 return true; 9200 } 9201 if (!this.#groups.get(id).visible) { 9202 return true; 9203 } 9204 } 9205 return false; 9206 } else if (group.policy === "AllOff") { 9207 for (const id of group.ids) { 9208 if (!this.#groups.has(id)) { 9209 warn(`Optional content group not found: ${id}`); 9210 return true; 9211 } 9212 if (this.#groups.get(id).visible) { 9213 return false; 9214 } 9215 } 9216 return true; 9217 } 9218 warn(`Unknown optional content policy ${group.policy}.`); 9219 return true; 9220 } 9221 warn(`Unknown group type ${group.type}.`); 9222 return true; 9223 } 9224 setVisibility(id, visible = true, preserveRB = true) { 9225 const group = this.#groups.get(id); 9226 if (!group) { 9227 warn(`Optional content group not found: ${id}`); 9228 return; 9229 } 9230 if (preserveRB && visible && group.rbGroups.length) { 9231 for (const rbGroup of group.rbGroups) { 9232 for (const otherId of rbGroup) { 9233 if (otherId !== id) { 9234 this.#groups.get(otherId)?._setVisible(INTERNAL, false, true); 9235 } 9236 } 9237 } 9238 } 9239 group._setVisible(INTERNAL, !!visible, true); 9240 this.#cachedGetHash = null; 9241 } 9242 setOCGState({ 9243 state, 9244 preserveRB 9245 }) { 9246 let operator; 9247 for (const elem of state) { 9248 switch (elem) { 9249 case "ON": 9250 case "OFF": 9251 case "Toggle": 9252 operator = elem; 9253 continue; 9254 } 9255 const group = this.#groups.get(elem); 9256 if (!group) { 9257 continue; 9258 } 9259 switch (operator) { 9260 case "ON": 9261 this.setVisibility(elem, true, preserveRB); 9262 break; 9263 case "OFF": 9264 this.setVisibility(elem, false, preserveRB); 9265 break; 9266 case "Toggle": 9267 this.setVisibility(elem, !group.visible, preserveRB); 9268 break; 9269 } 9270 } 9271 this.#cachedGetHash = null; 9272 } 9273 get hasInitialVisibility() { 9274 return this.#initialHash === null || this.getHash() === this.#initialHash; 9275 } 9276 getOrder() { 9277 if (!this.#groups.size) { 9278 return null; 9279 } 9280 if (this.#order) { 9281 return this.#order.slice(); 9282 } 9283 return [...this.#groups.keys()]; 9284 } 9285 getGroups() { 9286 return this.#groups.size > 0 ? objectFromMap(this.#groups) : null; 9287 } 9288 getGroup(id) { 9289 return this.#groups.get(id) || null; 9290 } 9291 getHash() { 9292 if (this.#cachedGetHash !== null) { 9293 return this.#cachedGetHash; 9294 } 9295 const hash = new MurmurHash3_64(); 9296 for (const [id, group] of this.#groups) { 9297 hash.update(`${id}:${group.visible}`); 9298 } 9299 return this.#cachedGetHash = hash.hexdigest(); 9300 } 9301} 9302 9303;// ./src/display/transport_stream.js 9304 9305 9306class PDFDataTransportStream { 9307 constructor(pdfDataRangeTransport, { 9308 disableRange = false, 9309 disableStream = false 9310 }) { 9311 assert(pdfDataRangeTransport, 'PDFDataTransportStream - missing required "pdfDataRangeTransport" argument.'); 9312 const { 9313 length, 9314 initialData, 9315 progressiveDone, 9316 contentDispositionFilename 9317 } = pdfDataRangeTransport; 9318 this._queuedChunks = []; 9319 this._progressiveDone = progressiveDone; 9320 this._contentDispositionFilename = contentDispositionFilename; 9321 if (initialData?.length > 0) { 9322 const buffer = initialData instanceof Uint8Array && initialData.byteLength === initialData.buffer.byteLength ? initialData.buffer : new Uint8Array(initialData).buffer; 9323 this._queuedChunks.push(buffer); 9324 } 9325 this._pdfDataRangeTransport = pdfDataRangeTransport; 9326 this._isStreamingSupported = !disableStream; 9327 this._isRangeSupported = !disableRange; 9328 this._contentLength = length; 9329 this._fullRequestReader = null; 9330 this._rangeReaders = []; 9331 pdfDataRangeTransport.addRangeListener((begin, chunk) => { 9332 this._onReceiveData({ 9333 begin, 9334 chunk 9335 }); 9336 }); 9337 pdfDataRangeTransport.addProgressListener((loaded, total) => { 9338 this._onProgress({ 9339 loaded, 9340 total 9341 }); 9342 }); 9343 pdfDataRangeTransport.addProgressiveReadListener(chunk => { 9344 this._onReceiveData({ 9345 chunk 9346 }); 9347 }); 9348 pdfDataRangeTransport.addProgressiveDoneListener(() => { 9349 this._onProgressiveDone(); 9350 }); 9351 pdfDataRangeTransport.transportReady(); 9352 } 9353 _onReceiveData({ 9354 begin, 9355 chunk 9356 }) { 9357 const buffer = chunk instanceof Uint8Array && chunk.byteLength === chunk.buffer.byteLength ? chunk.buffer : new Uint8Array(chunk).buffer; 9358 if (begin === undefined) { 9359 if (this._fullRequestReader) { 9360 this._fullRequestReader._enqueue(buffer); 9361 } else { 9362 this._queuedChunks.push(buffer); 9363 } 9364 } else { 9365 const found = this._rangeReaders.some(function (rangeReader) { 9366 if (rangeReader._begin !== begin) { 9367 return false; 9368 } 9369 rangeReader._enqueue(buffer); 9370 return true; 9371 }); 9372 assert(found, "_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found."); 9373 } 9374 } 9375 get _progressiveDataLength() { 9376 return this._fullRequestReader?._loaded ?? 0; 9377 } 9378 _onProgress(evt) { 9379 if (evt.total === undefined) { 9380 this._rangeReaders[0]?.onProgress?.({ 9381 loaded: evt.loaded 9382 }); 9383 } else { 9384 this._fullRequestReader?.onProgress?.({ 9385 loaded: evt.loaded, 9386 total: evt.total 9387 }); 9388 } 9389 } 9390 _onProgressiveDone() { 9391 this._fullRequestReader?.progressiveDone(); 9392 this._progressiveDone = true; 9393 } 9394 _removeRangeReader(reader) { 9395 const i = this._rangeReaders.indexOf(reader); 9396 if (i >= 0) { 9397 this._rangeReaders.splice(i, 1); 9398 } 9399 } 9400 getFullReader() { 9401 assert(!this._fullRequestReader, "PDFDataTransportStream.getFullReader can only be called once."); 9402 const queuedChunks = this._queuedChunks; 9403 this._queuedChunks = null; 9404 return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone, this._contentDispositionFilename); 9405 } 9406 getRangeReader(begin, end) { 9407 if (end <= this._progressiveDataLength) { 9408 return null; 9409 } 9410 const reader = new PDFDataTransportStreamRangeReader(this, begin, end); 9411 this._pdfDataRangeTransport.requestDataRange(begin, end); 9412 this._rangeReaders.push(reader); 9413 return reader; 9414 } 9415 cancelAllRequests(reason) { 9416 this._fullRequestReader?.cancel(reason); 9417 for (const reader of this._rangeReaders.slice(0)) { 9418 reader.cancel(reason); 9419 } 9420 this._pdfDataRangeTransport.abort(); 9421 } 9422} 9423class PDFDataTransportStreamReader { 9424 constructor(stream, queuedChunks, progressiveDone = false, contentDispositionFilename = null) { 9425 this._stream = stream; 9426 this._done = progressiveDone || false; 9427 this._filename = isPdfFile(contentDispositionFilename) ? contentDispositionFilename : null; 9428 this._queuedChunks = queuedChunks || []; 9429 this._loaded = 0; 9430 for (const chunk of this._queuedChunks) { 9431 this._loaded += chunk.byteLength; 9432 } 9433 this._requests = []; 9434 this._headersReady = Promise.resolve(); 9435 stream._fullRequestReader = this; 9436 this.onProgress = null; 9437 } 9438 _enqueue(chunk) { 9439 if (this._done) { 9440 return; 9441 } 9442 if (this._requests.length > 0) { 9443 const requestCapability = this._requests.shift(); 9444 requestCapability.resolve({ 9445 value: chunk, 9446 done: false 9447 }); 9448 } else { 9449 this._queuedChunks.push(chunk); 9450 } 9451 this._loaded += chunk.byteLength; 9452 } 9453 get headersReady() { 9454 return this._headersReady; 9455 } 9456 get filename() { 9457 return this._filename; 9458 } 9459 get isRangeSupported() { 9460 return this._stream._isRangeSupported; 9461 } 9462 get isStreamingSupported() { 9463 return this._stream._isStreamingSupported; 9464 } 9465 get contentLength() { 9466 return this._stream._contentLength; 9467 } 9468 async read() { 9469 if (this._queuedChunks.length > 0) { 9470 const chunk = this._queuedChunks.shift(); 9471 return { 9472 value: chunk, 9473 done: false 9474 }; 9475 } 9476 if (this._done) { 9477 return { 9478 value: undefined, 9479 done: true 9480 }; 9481 } 9482 const requestCapability = Promise.withResolvers(); 9483 this._requests.push(requestCapability); 9484 return requestCapability.promise; 9485 } 9486 cancel(reason) { 9487 this._done = true; 9488 for (const requestCapability of this._requests) { 9489 requestCapability.resolve({ 9490 value: undefined, 9491 done: true 9492 }); 9493 } 9494 this._requests.length = 0; 9495 } 9496 progressiveDone() { 9497 if (this._done) { 9498 return; 9499 } 9500 this._done = true; 9501 } 9502} 9503class PDFDataTransportStreamRangeReader { 9504 constructor(stream, begin, end) { 9505 this._stream = stream; 9506 this._begin = begin; 9507 this._end = end; 9508 this._queuedChunk = null; 9509 this._requests = []; 9510 this._done = false; 9511 this.onProgress = null; 9512 } 9513 _enqueue(chunk) { 9514 if (this._done) { 9515 return; 9516 } 9517 if (this._requests.length === 0) { 9518 this._queuedChunk = chunk; 9519 } else { 9520 const requestsCapability = this._requests.shift(); 9521 requestsCapability.resolve({ 9522 value: chunk, 9523 done: false 9524 }); 9525 for (const requestCapability of this._requests) { 9526 requestCapability.resolve({ 9527 value: undefined, 9528 done: true 9529 }); 9530 } 9531 this._requests.length = 0; 9532 } 9533 this._done = true; 9534 this._stream._removeRangeReader(this); 9535 } 9536 get isStreamingSupported() { 9537 return false;
vendor: 4,523 bytes, lines 9538-9692
9538 } 9539 async read() { 9540 if (this._queuedChunk) { 9541 const chunk = this._queuedChunk; 9542 this._queuedChunk = null; 9543 return { 9544 value: chunk, 9545 done: false 9546 }; 9547 } 9548 if (this._done) { 9549 return { 9550 value: undefined, 9551 done: true 9552 }; 9553 } 9554 const requestCapability = Promise.withResolvers(); 9555 this._requests.push(requestCapability); 9556 return requestCapability.promise; 9557 } 9558 cancel(reason) { 9559 this._done = true; 9560 for (const requestCapability of this._requests) { 9561 requestCapability.resolve({ 9562 value: undefined, 9563 done: true 9564 }); 9565 } 9566 this._requests.length = 0; 9567 this._stream._removeRangeReader(this); 9568 } 9569} 9570 9571;// ./src/display/content_disposition.js 9572 9573function getFilenameFromContentDispositionHeader(contentDisposition) { 9574 let needsEncodingFixup = true; 9575 let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition); 9576 if (tmp) { 9577 tmp = tmp[1]; 9578 let filename = rfc2616unquote(tmp); 9579 filename = unescape(filename); 9580 filename = rfc5987decode(filename); 9581 filename = rfc2047decode(filename); 9582 return fixupEncoding(filename); 9583 } 9584 tmp = rfc2231getparam(contentDisposition); 9585 if (tmp) { 9586 const filename = rfc2047decode(tmp); 9587 return fixupEncoding(filename); 9588 } 9589 tmp = toParamRegExp("filename", "i").exec(contentDisposition); 9590 if (tmp) { 9591 tmp = tmp[1]; 9592 let filename = rfc2616unquote(tmp); 9593 filename = rfc2047decode(filename); 9594 return fixupEncoding(filename); 9595 } 9596 function toParamRegExp(attributePattern, flags) { 9597 return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags); 9598 } 9599 function textdecode(encoding, value) { 9600 if (encoding) { 9601 if (!/^[\x00-\xFF]+$/.test(value)) { 9602 return value; 9603 } 9604 try { 9605 const decoder = new TextDecoder(encoding, { 9606 fatal: true 9607 }); 9608 const buffer = stringToBytes(value); 9609 value = decoder.decode(buffer); 9610 needsEncodingFixup = false; 9611 } catch {} 9612 } 9613 return value; 9614 } 9615 function fixupEncoding(value) { 9616 if (needsEncodingFixup && /[\x80-\xff]/.test(value)) { 9617 value = textdecode("utf-8", value); 9618 if (needsEncodingFixup) { 9619 value = textdecode("iso-8859-1", value); 9620 } 9621 } 9622 return value; 9623 } 9624 function rfc2231getparam(contentDispositionStr) { 9625 const matches = []; 9626 let match; 9627 const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig"); 9628 while ((match = iter.exec(contentDispositionStr)) !== null) { 9629 let [, n, quot, part] = match; 9630 n = parseInt(n, 10); 9631 if (n in matches) { 9632 if (n === 0) { 9633 break; 9634 } 9635 continue; 9636 } 9637 matches[n] = [quot, part]; 9638 } 9639 const parts = []; 9640 for (let n = 0; n < matches.length; ++n) { 9641 if (!(n in matches)) { 9642 break; 9643 } 9644 let [quot, part] = matches[n]; 9645 part = rfc2616unquote(part); 9646 if (quot) { 9647 part = unescape(part); 9648 if (n === 0) { 9649 part = rfc5987decode(part); 9650 } 9651 } 9652 parts.push(part); 9653 } 9654 return parts.join(""); 9655 } 9656 function rfc2616unquote(value) { 9657 if (value.startsWith('"')) { 9658 const parts = value.slice(1).split('\\"'); 9659 for (let i = 0; i < parts.length; ++i) { 9660 const quotindex = parts[i].indexOf('"'); 9661 if (quotindex !== -1) { 9662 parts[i] = parts[i].slice(0, quotindex); 9663 parts.length = i + 1; 9664 } 9665 parts[i] = parts[i].replaceAll(/\\(.)/g, "$1"); 9666 } 9667 value = parts.join('"'); 9668 } 9669 return value; 9670 } 9671 function rfc5987decode(extvalue) { 9672 const encodingend = extvalue.indexOf("'"); 9673 if (encodingend === -1) { 9674 return extvalue; 9675 } 9676 const encoding = extvalue.slice(0, encodingend); 9677 const langvalue = extvalue.slice(encodingend + 1); 9678 const value = langvalue.replace(/^[^']*'/, ""); 9679 return textdecode(encoding, value); 9680 } 9681 function rfc2047decode(value) { 9682 if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) { 9683 return value; 9684 } 9685 return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) { 9686 if (encoding === "q" || encoding === "Q") { 9687 text = text.replaceAll("_", " "); 9688 text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) { 9689 return String.fromCharCode(parseInt(hex, 16)); 9690 }); 9691 return textdecode(charset, text); 9692 }
vendor: 6,807 bytes, lines 9693-9927
9693 try { 9694 text = atob(text); 9695 } catch {} 9696 return textdecode(charset, text); 9697 }); 9698 } 9699 return ""; 9700} 9701 9702;// ./src/display/network_utils.js 9703 9704 9705 9706function createHeaders(isHttp, httpHeaders) { 9707 const headers = new Headers(); 9708 if (!isHttp || !httpHeaders || typeof httpHeaders !== "object") { 9709 return headers; 9710 } 9711 for (const key in httpHeaders) { 9712 const val = httpHeaders[key]; 9713 if (val !== undefined) { 9714 headers.append(key, val); 9715 } 9716 } 9717 return headers; 9718} 9719function validateRangeRequestCapabilities({ 9720 responseHeaders, 9721 isHttp, 9722 rangeChunkSize, 9723 disableRange 9724}) { 9725 const returnValues = { 9726 allowRangeRequests: false, 9727 suggestedLength: undefined 9728 }; 9729 const length = parseInt(responseHeaders.get("Content-Length"), 10); 9730 if (!Number.isInteger(length)) { 9731 return returnValues; 9732 } 9733 returnValues.suggestedLength = length; 9734 if (length <= 2 * rangeChunkSize) { 9735 return returnValues; 9736 } 9737 if (disableRange || !isHttp) { 9738 return returnValues; 9739 } 9740 if (responseHeaders.get("Accept-Ranges") !== "bytes") { 9741 return returnValues; 9742 } 9743 const contentEncoding = responseHeaders.get("Content-Encoding") || "identity"; 9744 if (contentEncoding !== "identity") { 9745 return returnValues; 9746 } 9747 returnValues.allowRangeRequests = true; 9748 return returnValues; 9749} 9750function extractFilenameFromHeader(responseHeaders) { 9751 const contentDisposition = responseHeaders.get("Content-Disposition"); 9752 if (contentDisposition) { 9753 let filename = getFilenameFromContentDispositionHeader(contentDisposition); 9754 if (filename.includes("%")) { 9755 try { 9756 filename = decodeURIComponent(filename); 9757 } catch {} 9758 } 9759 if (isPdfFile(filename)) { 9760 return filename; 9761 } 9762 } 9763 return null; 9764} 9765function createResponseStatusError(status, url) { 9766 if (status === 404 || status === 0 && url.startsWith("file:")) { 9767 return new MissingPDFException('Missing PDF "' + url + '".'); 9768 } 9769 return new UnexpectedResponseException(`Unexpected server response (${status}) while retrieving PDF "${url}".`, status); 9770} 9771function validateResponseStatus(status) { 9772 return status === 200 || status === 206; 9773} 9774 9775;// ./src/display/fetch_stream.js 9776 9777 9778function createFetchOptions(headers, withCredentials, abortController) { 9779 return { 9780 method: "GET", 9781 headers, 9782 signal: abortController.signal, 9783 mode: "cors", 9784 credentials: withCredentials ? "include" : "same-origin", 9785 redirect: "follow" 9786 }; 9787} 9788function getArrayBuffer(val) { 9789 if (val instanceof Uint8Array) { 9790 return val.buffer; 9791 } 9792 if (val instanceof ArrayBuffer) { 9793 return val; 9794 } 9795 warn(`getArrayBuffer - unexpected data format: ${val}`); 9796 return new Uint8Array(val).buffer; 9797} 9798class PDFFetchStream { 9799 constructor(source) { 9800 this.source = source; 9801 this.isHttp = /^https?:/i.test(source.url); 9802 this.headers = createHeaders(this.isHttp, source.httpHeaders); 9803 this._fullRequestReader = null; 9804 this._rangeRequestReaders = []; 9805 } 9806 get _progressiveDataLength() { 9807 return this._fullRequestReader?._loaded ?? 0; 9808 } 9809 getFullReader() { 9810 assert(!this._fullRequestReader, "PDFFetchStream.getFullReader can only be called once."); 9811 this._fullRequestReader = new PDFFetchStreamReader(this); 9812 return this._fullRequestReader; 9813 } 9814 getRangeReader(begin, end) { 9815 if (end <= this._progressiveDataLength) { 9816 return null; 9817 } 9818 const reader = new PDFFetchStreamRangeReader(this, begin, end); 9819 this._rangeRequestReaders.push(reader); 9820 return reader; 9821 } 9822 cancelAllRequests(reason) { 9823 this._fullRequestReader?.cancel(reason); 9824 for (const reader of this._rangeRequestReaders.slice(0)) { 9825 reader.cancel(reason); 9826 } 9827 } 9828} 9829class PDFFetchStreamReader { 9830 constructor(stream) { 9831 this._stream = stream; 9832 this._reader = null; 9833 this._loaded = 0; 9834 this._filename = null; 9835 const source = stream.source; 9836 this._withCredentials = source.withCredentials || false; 9837 this._contentLength = source.length; 9838 this._headersCapability = Promise.withResolvers(); 9839 this._disableRange = source.disableRange || false; 9840 this._rangeChunkSize = source.rangeChunkSize; 9841 if (!this._rangeChunkSize && !this._disableRange) { 9842 this._disableRange = true; 9843 } 9844 this._abortController = new AbortController(); 9845 this._isStreamingSupported = !source.disableStream; 9846 this._isRangeSupported = !source.disableRange; 9847 const headers = new Headers(stream.headers); 9848 const url = source.url; 9849 fetch(url, createFetchOptions(headers, this._withCredentials, this._abortController)).then(response => { 9850 if (!validateResponseStatus(response.status)) { 9851 throw createResponseStatusError(response.status, url); 9852 } 9853 this._reader = response.body.getReader(); 9854 this._headersCapability.resolve(); 9855 const responseHeaders = response.headers; 9856 const { 9857 allowRangeRequests, 9858 suggestedLength 9859 } = validateRangeRequestCapabilities({ 9860 responseHeaders, 9861 isHttp: stream.isHttp, 9862 rangeChunkSize: this._rangeChunkSize, 9863 disableRange: this._disableRange 9864 }); 9865 this._isRangeSupported = allowRangeRequests; 9866 this._contentLength = suggestedLength || this._contentLength; 9867 this._filename = extractFilenameFromHeader(responseHeaders); 9868 if (!this._isStreamingSupported && this._isRangeSupported) { 9869 this.cancel(new AbortException("Streaming is disabled.")); 9870 } 9871 }).catch(this._headersCapability.reject); 9872 this.onProgress = null; 9873 } 9874 get headersReady() { 9875 return this._headersCapability.promise; 9876 } 9877 get filename() { 9878 return this._filename; 9879 } 9880 get contentLength() { 9881 return this._contentLength; 9882 } 9883 get isRangeSupported() { 9884 return this._isRangeSupported; 9885 } 9886 get isStreamingSupported() { 9887 return this._isStreamingSupported; 9888 } 9889 async read() { 9890 await this._headersCapability.promise; 9891 const { 9892 value, 9893 done 9894 } = await this._reader.read(); 9895 if (done) { 9896 return { 9897 value, 9898 done 9899 }; 9900 } 9901 this._loaded += value.byteLength; 9902 this.onProgress?.({ 9903 loaded: this._loaded, 9904 total: this._contentLength 9905 }); 9906 return { 9907 value: getArrayBuffer(value), 9908 done: false 9909 }; 9910 } 9911 cancel(reason) { 9912 this._reader?.cancel(reason); 9913 this._abortController.abort(); 9914 } 9915} 9916class PDFFetchStreamRangeReader { 9917 constructor(stream, begin, end) { 9918 this._stream = stream; 9919 this._reader = null; 9920 this._loaded = 0; 9921 const source = stream.source; 9922 this._withCredentials = source.withCredentials || false; 9923 this._readCapability = Promise.withResolvers(); 9924 this._isStreamingSupported = !source.disableStream; 9925 this._abortController = new AbortController(); 9926 const headers = new Headers(stream.headers); 9927 headers.append("Range", `bytes=${begin}
9927-${end - 1}`); 9928 const url = source.url; 9929 fetch(url, createFetchOptions(headers, this._withCredentials, this._abortController)).then(response => { 9930 if (!validateResponseStatus(response.status)) { 9931 throw createResponseStatusError(response.status, url); 9932 } 9933 this._readCapability.resolve(); 9934 this._reader = response.body.getReader(); 9935 }).catch(this._readCapability.reject); 9936 this.onProgress = null; 9937 } 9938 get isStreamingSupported() { 9939 return this._isStreamingSupported; 9940 } 9941 async read() { 9942 await this._readCapability.promise; 9943 const { 9944 value, 9945 done 9946 } = await this._reader.read(); 9947 if (done) { 9948 return { 9949 value, 9950 done 9951 }; 9952 } 9953 this._loaded += value.byteLength; 9954 this.onProgress?.({ 9955 loaded: this._loaded 9956 }); 9957 return { 9958 value: getArrayBuffer(value), 9959 done: false 9960 }; 9961 } 9962 cancel(reason) { 9963 this._reader?.cancel(reason); 9964 this._abortController.abort(); 9965 } 9966} 9967 9968;// ./src/display/network.js 9969 9970 9971const OK_RESPONSE = 200; 9972const PARTIAL_CONTENT_RESPONSE = 206; 9973function network_getArrayBuffer(xhr) { 9974 const data = xhr.response; 9975 if (typeof data !== "string") { 9976 return data; 9977 } 9978 return stringToBytes(data).buffer; 9979} 9980class NetworkManager { 9981 constructor({ 9982 url, 9983 httpHeaders, 9984 withCredentials 9985 }) { 9986 this.url = url; 9987 this.isHttp = /^https?:/i.test(url); 9988 this.headers = createHeaders(this.isHttp, httpHeaders); 9989 this.withCredentials = withCredentials || false; 9990 this.currXhrId = 0; 9991 this.pendingRequests = Object.create(null); 9992 } 9993 requestRange(begin, end, listeners) { 9994 const args = { 9995 begin, 9996 end 9997 }; 9998 for (const prop in listeners) { 9999 args[prop] = listeners[prop]; 10000 } 10001 return this.request(args); 10002 } 10003 requestFull(listeners) { 10004 return this.request(listeners); 10005 } 10006 request(args) { 10007 const xhr = new XMLHttpRequest(); 10008 const xhrId = this.currXhrId++; 10009 const pendingRequest = this.pendingRequests[xhrId] = { 10010 xhr 10011 }; 10012 xhr.open("GET", this.url); 10013 xhr.withCredentials = this.withCredentials; 10014 for (const [key, val] of this.headers) { 10015 xhr.setRequestHeader(key, val); 10016 } 10017 if (this.isHttp && "begin" in args && "end" in args) { 10018 xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`); 10019 pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE; 10020 } else { 10021 pendingRequest.expectedStatus = OK_RESPONSE; 10022 } 10023 xhr.responseType = "arraybuffer"; 10024 if (args.onError) { 10025 xhr.onerror = function (evt) { 10026 args.onError(xhr.status); 10027 }; 10028 } 10029 xhr.onreadystatechange = this.onStateChange.bind(this, xhrId); 10030 xhr.onprogress = this.onProgress.bind(this, xhrId); 10031 pendingRequest.onHeadersReceived = args.onHeadersReceived; 10032 pendingRequest.onDone = args.onDone; 10033 pendingRequest.onError = args.onError; 10034 pendingRequest.onProgress = args.onProgress; 10035 xhr.send(null); 10036 return xhrId; 10037 } 10038 onProgress(xhrId, evt) { 10039 const pendingRequest = this.pendingRequests[xhrId]; 10040 if (!pendingRequest) { 10041 return; 10042 } 10043 pendingRequest.onProgress?.(evt); 10044 } 10045 onStateChange(xhrId, evt) { 10046 const pendingRequest = this.pendingRequests[xhrId]; 10047 if (!pendingRequest) { 10048 return; 10049 } 10050 const xhr = pendingRequest.xhr; 10051 if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { 10052 pendingRequest.onHeadersReceived(); 10053 delete pendingRequest.onHeadersReceived; 10054 } 10055 if (xhr.readyState !== 4) { 10056 return; 10057 } 10058 if (!(xhrId in this.pendingRequests)) { 10059 return; 10060 } 10061 delete this.pendingRequests[xhrId]; 10062 if (xhr.status === 0 && this.isHttp) { 10063 pendingRequest.onError?.(xhr.status); 10064 return; 10065 } 10066 const xhrStatus = xhr.status || OK_RESPONSE; 10067 const ok_response_on_range_request = xhrStatus === OK_RESPONSE && pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE; 10068 if (!ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus) { 10069 pendingRequest.onError?.(xhr.status); 10070 return; 10071 } 10072 const chunk = network_getArrayBuffer(xhr); 10073 if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { 10074 const rangeHeader = xhr.getResponseHeader("Content-Range"); 10075 const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader); 10076 pendingRequest.onDone({ 10077 begin: parseInt(matches[1], 10), 10078 chunk 10079 }); 10080 } else if (chunk) { 10081 pendingRequest.onDone({ 10082 begin: 0, 10083 chunk 10084 }); 10085 } else { 10086 pendingRequest.onError?.(xhr.status); 10087 } 10088 } 10089 getRequestXhr(xhrId) { 10090 return this.pendingRequests[xhrId].xhr; 10091 } 10092 isPendingRequest(xhrId) { 10093 return xhrId in this.pendingRequests; 10094 }
vendor: 5,309 bytes, lines 10095-10271
10095 abortRequest(xhrId) { 10096 const xhr = this.pendingRequests[xhrId].xhr; 10097 delete this.pendingRequests[xhrId]; 10098 xhr.abort(); 10099 } 10100} 10101class PDFNetworkStream { 10102 constructor(source) { 10103 this._source = source; 10104 this._manager = new NetworkManager(source); 10105 this._rangeChunkSize = source.rangeChunkSize; 10106 this._fullRequestReader = null; 10107 this._rangeRequestReaders = []; 10108 } 10109 _onRangeRequestReaderClosed(reader) { 10110 const i = this._rangeRequestReaders.indexOf(reader); 10111 if (i >= 0) { 10112 this._rangeRequestReaders.splice(i, 1); 10113 } 10114 } 10115 getFullReader() { 10116 assert(!this._fullRequestReader, "PDFNetworkStream.getFullReader can only be called once."); 10117 this._fullRequestReader = new PDFNetworkStreamFullRequestReader(this._manager, this._source); 10118 return this._fullRequestReader; 10119 } 10120 getRangeReader(begin, end) { 10121 const reader = new PDFNetworkStreamRangeRequestReader(this._manager, begin, end); 10122 reader.onClosed = this._onRangeRequestReaderClosed.bind(this); 10123 this._rangeRequestReaders.push(reader); 10124 return reader; 10125 } 10126 cancelAllRequests(reason) { 10127 this._fullRequestReader?.cancel(reason); 10128 for (const reader of this._rangeRequestReaders.slice(0)) { 10129 reader.cancel(reason); 10130 } 10131 } 10132} 10133class PDFNetworkStreamFullRequestReader { 10134 constructor(manager, source) { 10135 this._manager = manager; 10136 const args = { 10137 onHeadersReceived: this._onHeadersReceived.bind(this), 10138 onDone: this._onDone.bind(this), 10139 onError: this._onError.bind(this), 10140 onProgress: this._onProgress.bind(this) 10141 }; 10142 this._url = source.url; 10143 this._fullRequestId = manager.requestFull(args); 10144 this._headersCapability = Promise.withResolvers(); 10145 this._disableRange = source.disableRange || false; 10146 this._contentLength = source.length; 10147 this._rangeChunkSize = source.rangeChunkSize; 10148 if (!this._rangeChunkSize && !this._disableRange) { 10149 this._disableRange = true; 10150 } 10151 this._isStreamingSupported = false; 10152 this._isRangeSupported = false; 10153 this._cachedChunks = []; 10154 this._requests = []; 10155 this._done = false; 10156 this._storedError = undefined; 10157 this._filename = null; 10158 this.onProgress = null; 10159 } 10160 _onHeadersReceived() { 10161 const fullRequestXhrId = this._fullRequestId; 10162 const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId); 10163 const responseHeaders = new Headers(fullRequestXhr.getAllResponseHeaders().trim().split(/[\r\n]+/).map(x => { 10164 const [key, ...val] = x.split(": "); 10165 return [key, val.join(": ")]; 10166 })); 10167 const { 10168 allowRangeRequests, 10169 suggestedLength 10170 } = validateRangeRequestCapabilities({ 10171 responseHeaders, 10172 isHttp: this._manager.isHttp, 10173 rangeChunkSize: this._rangeChunkSize, 10174 disableRange: this._disableRange 10175 }); 10176 if (allowRangeRequests) { 10177 this._isRangeSupported = true; 10178 } 10179 this._contentLength = suggestedLength || this._contentLength; 10180 this._filename = extractFilenameFromHeader(responseHeaders); 10181 if (this._isRangeSupported) { 10182 this._manager.abortRequest(fullRequestXhrId); 10183 } 10184 this._headersCapability.resolve(); 10185 } 10186 _onDone(data) { 10187 if (data) { 10188 if (this._requests.length > 0) { 10189 const requestCapability = this._requests.shift(); 10190 requestCapability.resolve({ 10191 value: data.chunk, 10192 done: false 10193 }); 10194 } else { 10195 this._cachedChunks.push(data.chunk); 10196 } 10197 } 10198 this._done = true; 10199 if (this._cachedChunks.length > 0) { 10200 return; 10201 } 10202 for (const requestCapability of this._requests) { 10203 requestCapability.resolve({ 10204 value: undefined, 10205 done: true 10206 }); 10207 } 10208 this._requests.length = 0; 10209 } 10210 _onError(status) { 10211 this._storedError = createResponseStatusError(status, this._url); 10212 this._headersCapability.reject(this._storedError); 10213 for (const requestCapability of this._requests) { 10214 requestCapability.reject(this._storedError); 10215 } 10216 this._requests.length = 0; 10217 this._cachedChunks.length = 0; 10218 } 10219 _onProgress(evt) { 10220 this.onProgress?.({ 10221 loaded: evt.loaded, 10222 total: evt.lengthComputable ? evt.total : this._contentLength 10223 }); 10224 } 10225 get filename() { 10226 return this._filename; 10227 } 10228 get isRangeSupported() { 10229 return this._isRangeSupported; 10230 } 10231 get isStreamingSupported() { 10232 return this._isStreamingSupported; 10233 } 10234 get contentLength() { 10235 return this._contentLength; 10236 } 10237 get headersReady() { 10238 return this._headersCapability.promise; 10239 } 10240 async read() { 10241 if (this._storedError) { 10242 throw this._storedError; 10243 } 10244 if (this._cachedChunks.length > 0) { 10245 const chunk = this._cachedChunks.shift(); 10246 return { 10247 value: chunk, 10248 done: false 10249 }; 10250 } 10251 if (this._done) { 10252 return { 10253 value: undefined, 10254 done: true 10255 }; 10256 } 10257 const requestCapability = Promise.withResolvers(); 10258 this._requests.push(requestCapability); 10259 return requestCapability.promise; 10260 } 10261 cancel(reason) { 10262 this._done = true; 10263 this._headersCapability.reject(reason); 10264 for (const requestCapability of this._requests) { 10265 requestCapability.resolve({ 10266 value: undefined, 10267 done: true 10268 }); 10269 } 10270 this._requests.length = 0; 10271 if (this._manager.isPendingRequest(this._fullRequestId)) {
10272 this._manager.abortRequest(this._fullRequestId); 10273 } 10274 this._fullRequestReader = null; 10275 } 10276} 10277class PDFNetworkStreamRangeRequestReader { 10278 constructor(manager, begin, end) { 10279 this._manager = manager; 10280 const args = { 10281 onDone: this._onDone.bind(this), 10282 onError: this._onError.bind(this), 10283 onProgress: this._onProgress.bind(this) 10284 }; 10285 this._url = manager.url; 10286 this._requestId = manager.requestRange(begin, end, args); 10287 this._requests = []; 10288 this._queuedChunk = null; 10289 this._done = false; 10290 this._storedError = undefined; 10291 this.onProgress = null; 10292 this.onClosed = null; 10293 } 10294 _close() { 10295 this.onClosed?.(this); 10296 } 10297 _onDone(data) { 10298 const chunk = data.chunk; 10299 if (this._requests.length > 0) { 10300 const requestCapability = this._requests.shift(); 10301 requestCapability.resolve({ 10302 value: chunk, 10303 done: false 10304 }); 10305 } else { 10306 this._queuedChunk = chunk; 10307 } 10308 this._done = true; 10309 for (const requestCapability of this._requests) { 10310 requestCapability.resolve({ 10311 value: undefined, 10312 done: true 10313 }); 10314 } 10315 this._requests.length = 0; 10316 this._close(); 10317 } 10318 _onError(status) { 10319 this._storedError = createResponseStatusError(status, this._url); 10320 for (const requestCapability of this._requests) { 10321 requestCapability.reject(this._storedError); 10322 } 10323 this._requests.length = 0; 10324 this._queuedChunk = null; 10325 } 10326 _onProgress(evt) { 10327 if (!this.isStreamingSupported) { 10328 this.onProgress?.({ 10329 loaded: evt.loaded 10330 }); 10331 } 10332 } 10333 get isStreamingSupported() { 10334 return false; 10335 } 10336 async read() { 10337 if (this._storedError) { 10338 throw this._storedError; 10339 } 10340 if (this._queuedChunk !== null) { 10341 const chunk = this._queuedChunk; 10342 this._queuedChunk = null; 10343 return { 10344 value: chunk, 10345 done: false 10346 }; 10347 } 10348 if (this._done) { 10349 return { 10350 value: undefined, 10351 done: true 10352 }; 10353 } 10354 const requestCapability = Promise.withResolvers(); 10355 this._requests.push(requestCapability); 10356 return requestCapability.promise; 10357 } 10358 cancel(reason) { 10359 this._done = true; 10360 for (const requestCapability of this._requests) { 10361 requestCapability.resolve({ 10362 value: undefined, 10363 done: true 10364 }); 10365 } 10366 this._requests.length = 0; 10367 if (this._manager.isPendingRequest(this._requestId)) { 10368 this._manager.abortRequest(this._requestId); 10369 } 10370 this._close(); 10371 } 10372} 10373 10374;// ./src/display/node_stream.js 10375 10376 10377 10378const urlRegex = /^[a-z][a-z0-9\-+.]+:/i; 10379function parseUrlOrPath(sourceUrl) { 10380 if (urlRegex.test(sourceUrl)) { 10381 return new URL(sourceUrl); 10382 } 10383 const url = NodePackages.get("url"); 10384 return new URL(url.pathToFileURL(sourceUrl)); 10385} 10386function createRequest(url, headers, callback) { 10387 if (url.protocol === "http:") { 10388 const http = NodePackages.get("http"); 10389 return http.request(url, { 10390 headers 10391 }, callback); 10392 } 10393 const https = NodePackages.get("https"); 10394 return https.request(url, { 10395 headers 10396 }, callback); 10397} 10398class PDFNodeStream { 10399 constructor(source) { 10400 this.source = source; 10401 this.url = parseUrlOrPath(source.url); 10402 this.isHttp = this.url.protocol === "http:" || this.url.protocol === "https:"; 10403 this.isFsUrl = this.url.protocol === "file:"; 10404 this.headers = createHeaders(this.isHttp, source.httpHeaders); 10405 this._fullRequestReader = null; 10406 this._rangeRequestReaders = []; 10407 } 10408 get _progressiveDataLength() { 10409 return this._fullRequestReader?._loaded ?? 0; 10410 } 10411 getFullReader() { 10412 assert(!this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once."); 10413 this._fullRequestReader = this.isFsUrl ? new PDFNodeStreamFsFullReader(this) : new PDFNodeStreamFullReader(this); 10414 return this._fullRequestReader; 10415 } 10416 getRangeReader(start, end) { 10417 if (end <= this._progressiveDataLength) { 10418 return null; 10419 } 10420 const rangeReader = this.isFsUrl ? new PDFNodeStreamFsRangeReader(this, start, end) : new PDFNodeStreamRangeReader(this, start, end); 10421 this._rangeRequestReaders.push(rangeReader); 10422 return rangeReader; 10423 } 10424 cancelAllRequests(reason) { 10425 this._fullRequestReader?.cancel(reason); 10426 for (const reader of this._rangeRequestReaders.slice(0)) { 10427 reader.cancel(reason); 10428 } 10429 } 10430} 10431class BaseFullReader { 10432 constructor(stream) { 10433 this._url = stream.url; 10434 this._done = false;
10435 this._storedError = null; 10436 this.onProgress = null; 10437 const source = stream.source; 10438 this._contentLength = source.length; 10439 this._loaded = 0; 10440 this._filename = null; 10441 this._disableRange = source.disableRange || false; 10442 this._rangeChunkSize = source.rangeChunkSize; 10443 if (!this._rangeChunkSize && !this._disableRange) { 10444 this._disableRange = true; 10445 } 10446 this._isStreamingSupported = !source.disableStream; 10447 this._isRangeSupported = !source.disableRange; 10448 this._readableStream = null; 10449 this._readCapability = Promise.withResolvers(); 10450 this._headersCapability = Promise.withResolvers(); 10451 } 10452 get headersReady() { 10453 return this._headersCapability.promise; 10454 } 10455 get filename() { 10456 return this._filename; 10457 } 10458 get contentLength() { 10459 return this._contentLength; 10460 } 10461 get isRangeSupported() { 10462 return this._isRangeSupported; 10463 } 10464 get isStreamingSupported() { 10465 return this._isStreamingSupported; 10466 } 10467 async read() { 10468 await this._readCapability.promise; 10469 if (this._done) { 10470 return { 10471 value: undefined, 10472 done: true 10473 }; 10474 } 10475 if (this._storedError) { 10476 throw this._storedError; 10477 } 10478 const chunk = this._readableStream.read(); 10479 if (chunk === null) { 10480 this._readCapability = Promise.withResolvers(); 10481 return this.read(); 10482 } 10483 this._loaded += chunk.length; 10484 this.onProgress?.({ 10485 loaded: this._loaded, 10486 total: this._contentLength 10487 }); 10488 const buffer = new Uint8Array(chunk).buffer; 10489 return { 10490 value: buffer, 10491 done: false 10492 }; 10493 } 10494 cancel(reason) { 10495 if (!this._readableStream) { 10496 this._error(reason); 10497 return; 10498 } 10499 this._readableStream.destroy(reason); 10500 } 10501 _error(reason) { 10502 this._storedError = reason; 10503 this._readCapability.resolve(); 10504 } 10505 _setReadableStream(readableStream) { 10506 this._readableStream = readableStream; 10507 readableStream.on("readable", () => { 10508 this._readCapability.resolve(); 10509 }); 10510 readableStream.on("end", () => { 10511 readableStream.destroy(); 10512 this._done = true; 10513 this._readCapability.resolve(); 10514 }); 10515 readableStream.on("error", reason => { 10516 this._error(reason); 10517 }); 10518 if (!this._isStreamingSupported && this._isRangeSupported) { 10519 this._error(new AbortException("streaming is disabled")); 10520 } 10521 if (this._storedError) { 10522 this._readableStream.destroy(this._storedError); 10523 } 10524 } 10525} 10526class BaseRangeReader { 10527 constructor(stream) { 10528 this._url = stream.url; 10529 this._done = false; 10530 this._storedError = null; 10531 this.onProgress = null; 10532 this._loaded = 0; 10533 this._readableStream = null; 10534 this._readCapability = Promise.withResolvers(); 10535 const source = stream.source; 10536 this._isStreamingSupported = !source.disableStream; 10537 } 10538 get isStreamingSupported() { 10539 return this._isStreamingSupported; 10540 } 10541 async read() { 10542 await this._readCapability.promise; 10543 if (this._done) { 10544 return { 10545 value: undefined, 10546 done: true 10547 }; 10548 } 10549 if (this._storedError) { 10550 throw this._storedError; 10551 } 10552 const chunk = this._readableStream.read(); 10553 if (chunk === null) { 10554 this._readCapability = Promise.withResolvers(); 10555 return this.read(); 10556 } 10557 this._loaded += chunk.length; 10558 this.onProgress?.({ 10559 loaded: this._loaded 10560 }); 10561 const buffer = new Uint8Array(chunk).buffer; 10562 return { 10563 value: buffer, 10564 done: false 10565 }; 10566 } 10567 cancel(reason) { 10568 if (!this._readableStream) { 10569 this._error(reason); 10570 return; 10571 } 10572 this._readableStream.destroy(reason); 10573 } 10574 _error(reason) { 10575 this._storedError = reason; 10576 this._readCapability.resolve(); 10577 } 10578 _setReadableStream(readableStream) { 10579 this._readableStream = readableStream; 10580 readableStream.on("readable", () => { 10581 this._readCapability.resolve(); 10582 }); 10583 readableStream.on("end", () => { 10584 readableStream.destroy(); 10585 this._done = true; 10586 this._readCapability.resolve(); 10587 }); 10588 readableStream.on("error", reason => { 10589 this._error(reason); 10590 }); 10591 if (this._storedError) { 10592 this._readableStream.destroy(this._storedError); 10593 } 10594 } 10595} 10596class PDFNodeStreamFullReader extends BaseFullReader { 10597 constructor(stream) { 10598 super(stream); 10599 const headers = Object.fromEntries(stream.headers); 10600 const handleResponse = response => { 10601 if (response.statusCode === 404) { 10602 const error = new MissingPDFException(`Missing PDF "${this._url}".`); 10603 this._storedError = error; 10604 this._headersCapability.reject(error); 10605 return; 10606 } 10607 this._headersCapability.resolve(); 10608 this._setReadableStream(response); 10609 const responseHeaders = new Headers(this._readableStream.headers); 10610 const { 10611 allowRangeRequests, 10612 suggestedLength 10613 } = validateRangeRequestCapabilities({ 10614 responseHeaders, 10615 isHttp: stream.isHttp, 10616 rangeChunkSize: this._rangeChunkSize, 10617 disableRange: this._disableRange 10618 }); 10619 this._isRangeSupported = allowRangeRequests; 10620 this._contentLength = suggestedLength || this._contentLength; 10621 this._filename = extractFilenameFromHeader(responseHeaders); 10622 }; 10623 this._request = createRequest(this._url, headers, handleResponse); 10624 this._request.on("error", reason => { 10625 this._storedError = reason; 10626 this._headersCapability.reject(reason); 10627 }); 10628 this._request.end(); 10629 } 10630} 10631class PDFNodeStreamRangeReader extends BaseRangeReader { 10632 constructor(stream, start, end) { 10633 super(stream); 10634 const headers = Object.fromEntries(stream.headers); 10635 headers.Range = `bytes=${start}-${end - 1}`; 10636 const handleResponse = response => { 10637 if (response.statusCode === 404) { 10638 const error = new MissingPDFException(`Missing PDF "${this._url}".`); 10639 this._storedError = error; 10640 return; 10641 } 10642 this._setReadableStream(response); 10643 }; 10644 this._request = createRequest(this._url, headers, handleResponse); 10645 this._request.on("error", reason => { 10646 this._storedError = reason; 10647 }); 10648 this._request.end(); 10649 } 10650} 10651class PDFNodeStreamFsFullReader extends BaseFullReader { 10652 constructor(stream) { 10653 super(stream); 10654 const fs = NodePackages.get("fs"); 10655 fs.promises.lstat(this._url).then(stat => { 10656 this._contentLength = stat.size; 10657 this._setReadableStream(fs.createReadStream(this._url)); 10658 this._headersCapability.resolve(); 10659 }, error => { 10660 if (error.code === "ENOENT") { 10661 error = new MissingPDFException(`Missing PDF "${this._url}".`); 10662 } 10663 this._storedError = error; 10664 this._headersCapability.reject(error); 10665 }); 10666 } 10667} 10668class PDFNodeStreamFsRangeReader extends BaseRangeReader { 10669 constructor(stream, start, end) { 10670 super(stream); 10671 const fs = NodePackages.get("fs"); 10672 this._setReadableStream(fs.createReadStream(this._url, { 10673 start, 10674 end: end - 1 10675 })); 10676 } 10677} 10678 10679;// ./src/display/text_layer.js 10680 10681 10682const MAX_TEXT_DIVS_TO_RENDER = 100000; 10683const DEFAULT_FONT_SIZE = 30; 10684const DEFAULT_FONT_ASCENT = 0.8; 10685class TextLayer { 10686 #capability = Promise.withResolvers(); 10687 #container = null; 10688 #disableProcessItems = false;
vendor: 8,985 bytes, lines 10689-10973
10689 #fontInspectorEnabled = !!globalThis.FontInspector?.enabled; 10690 #lang = null; 10691 #layoutTextParams = null; 10692 #pageHeight = 0; 10693 #pageWidth = 0; 10694 #reader = null; 10695 #rootContainer = null; 10696 #rotation = 0; 10697 #scale = 0; 10698 #styleCache = Object.create(null); 10699 #textContentItemsStr = []; 10700 #textContentSource = null; 10701 #textDivs = []; 10702 #textDivProperties = new WeakMap(); 10703 #transform = null; 10704 static #ascentCache = new Map(); 10705 static #canvasContexts = new Map(); 10706 static #canvasCtxFonts = new WeakMap(); 10707 static #minFontSize = null; 10708 static #pendingTextLayers = new Set(); 10709 constructor({ 10710 textContentSource, 10711 container, 10712 viewport 10713 }) { 10714 if (textContentSource instanceof ReadableStream) { 10715 this.#textContentSource = textContentSource; 10716 } else if (typeof textContentSource === "object") { 10717 this.#textContentSource = new ReadableStream({ 10718 start(controller) { 10719 controller.enqueue(textContentSource); 10720 controller.close(); 10721 } 10722 }); 10723 } else { 10724 throw new Error('No "textContentSource" parameter specified.'); 10725 } 10726 this.#container = this.#rootContainer = container; 10727 this.#scale = viewport.scale * (globalThis.devicePixelRatio || 1); 10728 this.#rotation = viewport.rotation; 10729 this.#layoutTextParams = { 10730 div: null, 10731 properties: null, 10732 ctx: null 10733 }; 10734 const { 10735 pageWidth, 10736 pageHeight, 10737 pageX, 10738 pageY 10739 } = viewport.rawDims; 10740 this.#transform = [1, 0, 0, -1, -pageX, pageY + pageHeight]; 10741 this.#pageWidth = pageWidth; 10742 this.#pageHeight = pageHeight; 10743 TextLayer.#ensureMinFontSizeComputed(); 10744 setLayerDimensions(container, viewport); 10745 this.#capability.promise.finally(() => { 10746 TextLayer.#pendingTextLayers.delete(this); 10747 this.#layoutTextParams = null; 10748 this.#styleCache = null; 10749 }).catch(() => {}); 10750 } 10751 static get fontFamilyMap() { 10752 const { 10753 isWindows, 10754 isFirefox 10755 } = util_FeatureTest.platform; 10756 return shadow(this, "fontFamilyMap", new Map([["sans-serif", `${isWindows && isFirefox ? "Calibri, " : ""}sans-serif`], ["monospace", `${isWindows && isFirefox ? "Lucida Console, " : ""}monospace`]])); 10757 } 10758 render() { 10759 const pump = () => { 10760 this.#reader.read().then(({ 10761 value, 10762 done 10763 }) => { 10764 if (done) { 10765 this.#capability.resolve(); 10766 return; 10767 } 10768 this.#lang ??= value.lang; 10769 Object.assign(this.#styleCache, value.styles); 10770 this.#processItems(value.items); 10771 pump(); 10772 }, this.#capability.reject); 10773 }; 10774 this.#reader = this.#textContentSource.getReader(); 10775 TextLayer.#pendingTextLayers.add(this); 10776 pump(); 10777 return this.#capability.promise; 10778 } 10779 update({ 10780 viewport, 10781 onBefore = null 10782 }) { 10783 const scale = viewport.scale * (globalThis.devicePixelRatio || 1); 10784 const rotation = viewport.rotation; 10785 if (rotation !== this.#rotation) { 10786 onBefore?.(); 10787 this.#rotation = rotation; 10788 setLayerDimensions(this.#rootContainer, { 10789 rotation 10790 }); 10791 } 10792 if (scale !== this.#scale) { 10793 onBefore?.(); 10794 this.#scale = scale; 10795 const params = { 10796 div: null, 10797 properties: null, 10798 ctx: TextLayer.#getCtx(this.#lang) 10799 }; 10800 for (const div of this.#textDivs) { 10801 params.properties = this.#textDivProperties.get(div); 10802 params.div = div; 10803 this.#layout(params); 10804 } 10805 } 10806 } 10807 cancel() { 10808 const abortEx = new AbortException("TextLayer task cancelled."); 10809 this.#reader?.cancel(abortEx).catch(() => {}); 10810 this.#reader = null; 10811 this.#capability.reject(abortEx); 10812 } 10813 get textDivs() { 10814 return this.#textDivs; 10815 } 10816 get textContentItemsStr() { 10817 return this.#textContentItemsStr; 10818 } 10819 #processItems(items) { 10820 if (this.#disableProcessItems) { 10821 return; 10822 } 10823 this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang); 10824 const textDivs = this.#textDivs, 10825 textContentItemsStr = this.#textContentItemsStr; 10826 for (const item of items) { 10827 if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) { 10828 warn("Ignoring additional textDivs for performance reasons."); 10829 this.#disableProcessItems = true; 10830 return; 10831 } 10832 if (item.str === undefined) { 10833 if (item.type === "beginMarkedContentProps" || item.type === "beginMarkedContent") { 10834 const parent = this.#container; 10835 this.#container = document.createElement("span"); 10836 this.#container.classList.add("markedContent"); 10837 if (item.id !== null) { 10838 this.#container.setAttribute("id", `${item.id}`); 10839 } 10840 parent.append(this.#container); 10841 } else if (item.type === "endMarkedContent") { 10842 this.#container = this.#container.parentNode; 10843 } 10844 continue; 10845 } 10846 textContentItemsStr.push(item.str); 10847 this.#appendText(item); 10848 } 10849 } 10850 #appendText(geom) { 10851 const textDiv = document.createElement("span"); 10852 const textDivProperties = { 10853 angle: 0, 10854 canvasWidth: 0, 10855 hasText: geom.str !== "", 10856 hasEOL: geom.hasEOL, 10857 fontSize: 0 10858 }; 10859 this.#textDivs.push(textDiv); 10860 const tx = Util.transform(this.#transform, geom.transform); 10861 let angle = Math.atan2(tx[1], tx[0]); 10862 const style = this.#styleCache[geom.fontName]; 10863 if (style.vertical) { 10864 angle += Math.PI / 2; 10865 } 10866 let fontFamily = this.#fontInspectorEnabled && style.fontSubstitution || style.fontFamily; 10867 fontFamily = TextLayer.fontFamilyMap.get(fontFamily) || fontFamily; 10868 const fontHeight = Math.hypot(tx[2], tx[3]); 10869 const fontAscent = fontHeight * TextLayer.#getAscent(fontFamily, this.#lang); 10870 let left, top; 10871 if (angle === 0) { 10872 left = tx[4]; 10873 top = tx[5] - fontAscent; 10874 } else { 10875 left = tx[4] + fontAscent * Math.sin(angle); 10876 top = tx[5] - fontAscent * Math.cos(angle); 10877 } 10878 const scaleFactorStr = "calc(var(--scale-factor)*"; 10879 const divStyle = textDiv.style; 10880 if (this.#container === this.#rootContainer) { 10881 divStyle.left = `${(100 * left / this.#pageWidth).toFixed(2)}%`; 10882 divStyle.top = `${(100 * top / this.#pageHeight).toFixed(2)}%`; 10883 } else { 10884 divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`; 10885 divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`; 10886 } 10887 divStyle.fontSize = `${scaleFactorStr}${(TextLayer.#minFontSize * fontHeight).toFixed(2)}px)`; 10888 divStyle.fontFamily = fontFamily; 10889 textDivProperties.fontSize = fontHeight; 10890 textDiv.setAttribute("role", "presentation"); 10891 textDiv.textContent = geom.str; 10892 textDiv.dir = geom.dir; 10893 if (this.#fontInspectorEnabled) { 10894 textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName; 10895 } 10896 if (angle !== 0) { 10897 textDivProperties.angle = angle * (180 / Math.PI); 10898 } 10899 let shouldScaleText = false; 10900 if (geom.str.length > 1) { 10901 shouldScaleText = true; 10902 } else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) { 10903 const absScaleX = Math.abs(geom.transform[0]), 10904 absScaleY = Math.abs(geom.transform[3]); 10905 if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) { 10906 shouldScaleText = true; 10907 } 10908 } 10909 if (shouldScaleText) { 10910 textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width; 10911 } 10912 this.#textDivProperties.set(textDiv, textDivProperties); 10913 this.#layoutTextParams.div = textDiv; 10914 this.#layoutTextParams.properties = textDivProperties; 10915 this.#layout(this.#layoutTextParams); 10916 if (textDivProperties.hasText) { 10917 this.#container.append(textDiv); 10918 } 10919 if (textDivProperties.hasEOL) { 10920 const br = document.createElement("br"); 10921 br.setAttribute("role", "presentation"); 10922 this.#container.append(br); 10923 } 10924 } 10925 #layout(params) { 10926 const { 10927 div, 10928 properties, 10929 ctx 10930 } = params; 10931 const { 10932 style 10933 } = div; 10934 let transform = ""; 10935 if (TextLayer.#minFontSize > 1) { 10936 transform = `scale(${1 / TextLayer.#minFontSize})`; 10937 } 10938 if (properties.canvasWidth !== 0 && properties.hasText) { 10939 const { 10940 fontFamily 10941 } = style; 10942 const { 10943 canvasWidth, 10944 fontSize 10945 } = properties; 10946 TextLayer.#ensureCtxFont(ctx, fontSize * this.#scale, fontFamily); 10947 const { 10948 width 10949 } = ctx.measureText(div.textContent); 10950 if (width > 0) { 10951 transform = `scaleX(${canvasWidth * this.#scale / width}) ${transform}`; 10952 } 10953 } 10954 if (properties.angle !== 0) { 10955 transform = `rotate(${properties.angle}deg) ${transform}`; 10956 } 10957 if (transform.length > 0) { 10958 style.transform = transform; 10959 } 10960 } 10961 static cleanup() { 10962 if (this.#pendingTextLayers.size > 0) { 10963 return; 10964 } 10965 this.#ascentCache.clear(); 10966 for (const { 10967 canvas 10968 } of this.#canvasContexts.values()) { 10969 canvas.remove(); 10970 } 10971 this.#canvasContexts.clear(); 10972 } 10973 static #getCtx(lang = null) {
10974 let ctx = this.#canvasContexts.get(lang ||= ""); 10975 if (!ctx) { 10976 const canvas = document.createElement("canvas"); 10977 canvas.className = "hiddenCanvasElement"; 10978 canvas.lang = lang; 10979 document.body.append(canvas); 10980 ctx = canvas.getContext("2d", { 10981 alpha: false, 10982 willReadFrequently: true 10983 }); 10984 this.#canvasContexts.set(lang, ctx); 10985 this.#canvasCtxFonts.set(ctx, { 10986 size: 0, 10987 family: "" 10988 }); 10989 } 10990 return ctx; 10991 } 10992 static #ensureCtxFont(ctx, size, family) { 10993 const cached = this.#canvasCtxFonts.get(ctx); 10994 if (size === cached.size && family === cached.family) { 10995 return; 10996 } 10997 ctx.font = `${size}px ${family}`; 10998 cached.size = size; 10999 cached.family = family; 11000 } 11001 static #ensureMinFontSizeComputed() { 11002 if (this.#minFontSize !== null) { 11003 return; 11004 } 11005 const div = document.createElement("div"); 11006 div.style.opacity = 0; 11007 div.style.lineHeight = 1; 11008 div.style.fontSize = "1px"; 11009 div.style.position = "absolute"; 11010 div.textContent = "X"; 11011 document.body.append(div); 11012 this.#minFontSize = div.getBoundingClientRect().height; 11013 div.remove(); 11014 } 11015 static #getAscent(fontFamily, lang) { 11016 const cachedAscent = this.#ascentCache.get(fontFamily); 11017 if (cachedAscent) { 11018 return cachedAscent; 11019 } 11020 const ctx = this.#getCtx(lang); 11021 ctx.canvas.width = ctx.canvas.height = DEFAULT_FONT_SIZE; 11022 this.#ensureCtxFont(ctx, DEFAULT_FONT_SIZE, fontFamily); 11023 const metrics = ctx.measureText(""); 11024 let ascent = metrics.fontBoundingBoxAscent; 11025 let descent = Math.abs(metrics.fontBoundingBoxDescent); 11026 if (ascent) { 11027 const ratio = ascent / (ascent + descent); 11028 this.#ascentCache.set(fontFamily, ratio); 11029 ctx.canvas.width = ctx.canvas.height = 0; 11030 return ratio; 11031 } 11032 ctx.strokeStyle = "red"; 11033 ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE); 11034 ctx.strokeText("g", 0, 0); 11035 let pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data; 11036 descent = 0; 11037 for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) { 11038 if (pixels[i] > 0) { 11039 descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE); 11040 break; 11041 } 11042 } 11043 ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE); 11044 ctx.strokeText("A", 0, DEFAULT_FONT_SIZE); 11045 pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data; 11046 ascent = 0; 11047 for (let i = 0, ii = pixels.length; i < ii; i += 4) { 11048 if (pixels[i] > 0) { 11049 ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE); 11050 break; 11051 } 11052 } 11053 ctx.canvas.width = ctx.canvas.height = 0; 11054 const ratio = ascent ? ascent / (ascent + descent) : DEFAULT_FONT_ASCENT; 11055 this.#ascentCache.set(fontFamily, ratio); 11056 return ratio; 11057 } 11058} 11059 11060;// ./src/display/xfa_text.js 11061class XfaText { 11062 static textContent(xfa) { 11063 const items = []; 11064 const output = { 11065 items, 11066 styles: Object.create(null) 11067 }; 11068 function walk(node) { 11069 if (!node) { 11070 return; 11071 } 11072 let str = null; 11073 const name = node.name; 11074 if (name === "#text") { 11075 str = node.value; 11076 } else if (!XfaText.shouldBuildText(name)) { 11077 return; 11078 } else if (node?.attributes?.textContent) { 11079 str = node.attributes.textContent; 11080 } else if (node.value) { 11081 str = node.value; 11082 } 11083 if (str !== null) { 11084 items.push({ 11085 str 11086 }); 11087 } 11088 if (!node.children) { 11089 return; 11090 } 11091 for (const child of node.children) { 11092 walk(child); 11093 } 11094 } 11095 walk(xfa); 11096 return output; 11097 } 11098 static shouldBuildText(name) { 11099 return !(name === "textarea" || name === "input" || name === "option" || name === "select"); 11100 } 11101} 11102 11103;// ./src/display/api.js 11104 11105 11106 11107 11108 11109 11110 11111 11112 11113 11114 11115 11116 11117 11118 11119 11120 11121 11122 11123 11124const DEFAULT_RANGE_CHUNK_SIZE = 65536; 11125const RENDERING_CANCELLED_TIMEOUT = 100; 11126const DELAYED_CLEANUP_TIMEOUT = 5000; 11127const DefaultCanvasFactory = isNodeJS ? NodeCanvasFactory : DOMCanvasFactory; 11128const DefaultCMapReaderFactory = isNodeJS ? NodeCMapReaderFactory : DOMCMapReaderFactory; 11129const DefaultFilterFactory = isNodeJS ? NodeFilterFactory : DOMFilterFactory; 11130const DefaultStandardFontDataFactory = isNodeJS ? NodeStandardFontDataFactory : DOMStandardFontDataFactory; 11131function getDocument(src = {}) { 11132 if (typeof src === "string" || src instanceof URL) { 11133 src = { 11134 url: src 11135 }; 11136 } else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) { 11137 src = { 11138 data: src 11139 }; 11140 } 11141 const task = new PDFDocumentLoadingTask(); 11142 const { 11143 docId 11144 } = task; 11145 const url = src.url ? getUrlProp(src.url) : null; 11146 const data = src.data ? getDataProp(src.data) : null; 11147 const httpHeaders = src.httpHeaders || null; 11148 const withCredentials = src.withCredentials === true; 11149 const password = src.password ?? null; 11150 const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null; 11151 const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : DEFAULT_RANGE_CHUNK_SIZE; 11152 let worker = src.worker instanceof PDFWorker ? src.worker : null; 11153 const verbosity = src.verbosity; 11154 const docBaseUrl = typeof src.docBaseUrl === "string" && !isDataScheme(src.docBaseUrl) ? src.docBaseUrl : null; 11155 const cMapUrl = typeof src.cMapUrl === "string" ? src.cMapUrl : null; 11156 const cMapPacked = src.cMapPacked !== false; 11157 const CMapReaderFactory = src.CMapReaderFactory || DefaultCMapReaderFactory; 11158 const standardFontDataUrl = typeof src.standardFontDataUrl === "string" ? src.standardFontDataUrl : null; 11159 const StandardFontDataFactory = src.StandardFontDataFactory || DefaultStandardFontDataFactory; 11160 const ignoreErrors = src.stopAtErrors !== true; 11161 const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1; 11162 const isEvalSupported = src.isEvalSupported !== false; 11163 const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !isNodeJS; 11164 const isChrome = typeof src.isChrome === "boolean" ? src.isChrome : !util_FeatureTest.platform.isFirefox && typeof window !== "undefined" && !!window?.chrome; 11165 const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes :
11165-1; 11166 const disableFontFace = typeof src.disableFontFace === "boolean" ? src.disableFontFace : isNodeJS; 11167 const fontExtraProperties = src.fontExtraProperties === true; 11168 const enableXfa = src.enableXfa === true; 11169 const ownerDocument = src.ownerDocument || globalThis.document; 11170 const disableRange = src.disableRange === true; 11171 const disableStream = src.disableStream === true; 11172 const disableAutoFetch = src.disableAutoFetch === true; 11173 const pdfBug = src.pdfBug === true; 11174 const CanvasFactory = src.CanvasFactory || DefaultCanvasFactory; 11175 const FilterFactory = src.FilterFactory || DefaultFilterFactory; 11176 const enableHWA = src.enableHWA === true; 11177 const length = rangeTransport ? rangeTransport.length : src.length ?? NaN; 11178 const useSystemFonts = typeof src.useSystemFonts === "boolean" ? src.useSystemFonts : !isNodeJS && !disableFontFace; 11179 const useWorkerFetch = typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && cMapUrl && standardFontDataUrl && isValidFetchUrl(cMapUrl, document.baseURI) && isValidFetchUrl(standardFontDataUrl, document.baseURI); 11180 if (src.canvasFactory) { 11181 deprecated("`canvasFactory`-instance option, please use `CanvasFactory` instead."); 11182 } 11183 if (src.filterFactory) { 11184 deprecated("`filterFactory`-instance option, please use `FilterFactory` instead."); 11185 } 11186 const styleElement = null; 11187 setVerbosityLevel(verbosity); 11188 const transportFactory = { 11189 canvasFactory: new CanvasFactory({ 11190 ownerDocument, 11191 enableHWA 11192 }), 11193 filterFactory: new FilterFactory({ 11194 docId, 11195 ownerDocument 11196 }), 11197 cMapReaderFactory: useWorkerFetch ? null : new CMapReaderFactory({ 11198 baseUrl: cMapUrl, 11199 isCompressed: cMapPacked 11200 }), 11201 standardFontDataFactory: useWorkerFetch ? null : new StandardFontDataFactory({ 11202 baseUrl: standardFontDataUrl 11203 }) 11204 }; 11205 if (!worker) { 11206 const workerParams = { 11207 verbosity, 11208 port: GlobalWorkerOptions.workerPort 11209 }; 11210 worker = workerParams.port ? PDFWorker.fromPort(workerParams) : new PDFWorker(workerParams); 11211 task._worker = worker; 11212 } 11213 const docParams = { 11214 docId, 11215 apiVersion: "4.8.69", 11216 data, 11217 password, 11218 disableAutoFetch, 11219 rangeChunkSize, 11220 length, 11221 docBaseUrl, 11222 enableXfa, 11223 evaluatorOptions: { 11224 maxImageSize, 11225 disableFontFace, 11226 ignoreErrors, 11227 isEvalSupported, 11228 isOffscreenCanvasSupported, 11229 isChrome, 11230 canvasMaxAreaInBytes, 11231 fontExtraProperties, 11232 useSystemFonts, 11233 cMapUrl: useWorkerFetch ? cMapUrl : null, 11234 standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null 11235 } 11236 }; 11237 const transportParams = { 11238 disableFontFace, 11239 fontExtraProperties, 11240 ownerDocument, 11241 pdfBug, 11242 styleElement, 11243 loadingParams: { 11244 disableAutoFetch, 11245 enableXfa 11246 } 11247 }; 11248 worker.promise.then(function () { 11249 if (task.destroyed) { 11250 throw new Error("Loading aborted"); 11251 } 11252 if (worker.destroyed) { 11253 throw new Error("Worker was destroyed"); 11254 } 11255 const workerIdPromise = worker.messageHandler.sendWithPromise("GetDocRequest", docParams, data ? [data.buffer] : null); 11256 let networkStream; 11257 if (rangeTransport) { 11258 networkStream = new PDFDataTransportStream(rangeTransport, { 11259 disableRange, 11260 disableStream 11261 }); 11262 } else if (!data) { 11263 if (!url) { 11264 throw new Error("getDocument - no `url` parameter provided."); 11265 } 11266 let NetworkStream; 11267 if (isNodeJS) { 11268 const isFetchSupported = typeof fetch !== "undefined" && typeof Response !== "undefined" && "body" in Response.prototype; 11269 NetworkStream = isFetchSupported && isValidFetchUrl(url) ? PDFFetchStream : PDFNodeStream; 11270 } else { 11271 NetworkStream = isValidFetchUrl(url) ? PDFFetchStream : PDFNetworkStream; 11272 } 11273 networkStream = new NetworkStream({ 11274 url, 11275 length, 11276 httpHeaders, 11277 withCredentials, 11278 rangeChunkSize, 11279 disableRange, 11280 disableStream 11281 }); 11282 } 11283 return workerIdPromise.then(workerId => { 11284 if (task.destroyed) { 11285 throw new Error("Loading aborted"); 11286 } 11287 if (worker.destroyed) { 11288 throw new Error("Worker was destroyed"); 11289 } 11290 const messageHandler = new MessageHandler(docId, workerId, worker.port); 11291 const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory); 11292 task._transport = transport; 11293 messageHandler.send("Ready", null); 11294 }); 11295 }).catch(task._capability.reject); 11296 return task; 11297} 11298function getUrlProp(val) { 11299 if (val instanceof URL) { 11300 return val.href; 11301 } 11302 try { 11303 return new URL(val, window.location).href; 11304 } catch { 11305 if (isNodeJS && typeof val === "string") { 11306 return val; 11307 } 11308 } 11309 throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property."); 11310} 11311function getDataProp(val) { 11312 if (isNodeJS && typeof Buffer !== "undefined" && val instanceof Buffer) { 11313 throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`."); 11314 } 11315 if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) { 11316 return val; 11317 } 11318 if (typeof val === "string") { 11319 return stringToBytes(val); 11320 } 11321 if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) { 11322 return new Uint8Array(val); 11323 } 11324 throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property."); 11325} 11326function isRefProxy(ref) { 11327 return typeof ref === "object" && Number.isInteger(ref?.num) && ref.num >= 0 && Number.isInteger(ref?.gen) && ref.gen >= 0; 11328} 11329class PDFDocumentLoadingTask { 11330 static #docId = 0; 11331 constructor() { 11332 this._capability = Promise.withResolvers(); 11333 this._transport = null; 11334 this._worker = null; 11335 this.docId = `d${PDFDocumentLoadingTask.#docId++}`; 11336 this.destroyed = false;
vendor: 13,882 bytes, lines 11337-11821
11337 this.onPassword = null; 11338 this.onProgress = null; 11339 } 11340 get promise() { 11341 return this._capability.promise; 11342 } 11343 async destroy() { 11344 this.destroyed = true; 11345 try { 11346 if (this._worker?.port) { 11347 this._worker._pendingDestroy = true; 11348 } 11349 await this._transport?.destroy(); 11350 } catch (ex) { 11351 if (this._worker?.port) { 11352 delete this._worker._pendingDestroy; 11353 } 11354 throw ex; 11355 } 11356 this._transport = null; 11357 if (this._worker) { 11358 this._worker.destroy(); 11359 this._worker = null; 11360 } 11361 } 11362} 11363class PDFDataRangeTransport { 11364 constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) { 11365 this.length = length; 11366 this.initialData = initialData; 11367 this.progressiveDone = progressiveDone; 11368 this.contentDispositionFilename = contentDispositionFilename; 11369 this._rangeListeners = []; 11370 this._progressListeners = []; 11371 this._progressiveReadListeners = []; 11372 this._progressiveDoneListeners = []; 11373 this._readyCapability = Promise.withResolvers(); 11374 } 11375 addRangeListener(listener) { 11376 this._rangeListeners.push(listener); 11377 } 11378 addProgressListener(listener) { 11379 this._progressListeners.push(listener); 11380 } 11381 addProgressiveReadListener(listener) { 11382 this._progressiveReadListeners.push(listener); 11383 } 11384 addProgressiveDoneListener(listener) { 11385 this._progressiveDoneListeners.push(listener); 11386 } 11387 onDataRange(begin, chunk) { 11388 for (const listener of this._rangeListeners) { 11389 listener(begin, chunk); 11390 } 11391 } 11392 onDataProgress(loaded, total) { 11393 this._readyCapability.promise.then(() => { 11394 for (const listener of this._progressListeners) { 11395 listener(loaded, total); 11396 } 11397 }); 11398 } 11399 onDataProgressiveRead(chunk) { 11400 this._readyCapability.promise.then(() => { 11401 for (const listener of this._progressiveReadListeners) { 11402 listener(chunk); 11403 } 11404 }); 11405 } 11406 onDataProgressiveDone() { 11407 this._readyCapability.promise.then(() => { 11408 for (const listener of this._progressiveDoneListeners) { 11409 listener(); 11410 } 11411 }); 11412 } 11413 transportReady() { 11414 this._readyCapability.resolve(); 11415 } 11416 requestDataRange(begin, end) { 11417 unreachable("Abstract method PDFDataRangeTransport.requestDataRange"); 11418 } 11419 abort() {} 11420} 11421class PDFDocumentProxy { 11422 constructor(pdfInfo, transport) { 11423 this._pdfInfo = pdfInfo; 11424 this._transport = transport; 11425 } 11426 get annotationStorage() { 11427 return this._transport.annotationStorage; 11428 } 11429 get canvasFactory() { 11430 return this._transport.canvasFactory; 11431 } 11432 get filterFactory() { 11433 return this._transport.filterFactory; 11434 } 11435 get numPages() { 11436 return this._pdfInfo.numPages; 11437 } 11438 get fingerprints() { 11439 return this._pdfInfo.fingerprints; 11440 } 11441 get isPureXfa() { 11442 return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); 11443 } 11444 get allXfaHtml() { 11445 return this._transport._htmlForXfa; 11446 } 11447 getPage(pageNumber) { 11448 return this._transport.getPage(pageNumber); 11449 } 11450 getPageIndex(ref) { 11451 return this._transport.getPageIndex(ref); 11452 } 11453 getDestinations() { 11454 return this._transport.getDestinations(); 11455 } 11456 getDestination(id) { 11457 return this._transport.getDestination(id); 11458 } 11459 getPageLabels() { 11460 return this._transport.getPageLabels(); 11461 } 11462 getPageLayout() { 11463 return this._transport.getPageLayout(); 11464 } 11465 getPageMode() { 11466 return this._transport.getPageMode(); 11467 } 11468 getViewerPreferences() { 11469 return this._transport.getViewerPreferences(); 11470 } 11471 getOpenAction() { 11472 return this._transport.getOpenAction(); 11473 } 11474 getAttachments() { 11475 return this._transport.getAttachments(); 11476 } 11477 getJSActions() { 11478 return this._transport.getDocJSActions(); 11479 } 11480 getOutline() { 11481 return this._transport.getOutline(); 11482 } 11483 getOptionalContentConfig({ 11484 intent = "display" 11485 } = {}) { 11486 const { 11487 renderingIntent 11488 } = this._transport.getRenderingIntent(intent); 11489 return this._transport.getOptionalContentConfig(renderingIntent); 11490 } 11491 getPermissions() { 11492 return this._transport.getPermissions(); 11493 } 11494 getMetadata() { 11495 return this._transport.getMetadata(); 11496 } 11497 getMarkInfo() { 11498 return this._transport.getMarkInfo(); 11499 } 11500 getData() { 11501 return this._transport.getData(); 11502 } 11503 saveDocument() { 11504 return this._transport.saveDocument(); 11505 } 11506 getDownloadInfo() { 11507 return this._transport.downloadInfoCapability.promise; 11508 } 11509 cleanup(keepLoadedFonts = false) { 11510 return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa); 11511 } 11512 destroy() { 11513 return this.loadingTask.destroy(); 11514 } 11515 cachedPageNumber(ref) { 11516 return this._transport.cachedPageNumber(ref); 11517 } 11518 get loadingParams() { 11519 return this._transport.loadingParams; 11520 } 11521 get loadingTask() { 11522 return this._transport.loadingTask; 11523 } 11524 getFieldObjects() { 11525 return this._transport.getFieldObjects(); 11526 } 11527 hasJSActions() { 11528 return this._transport.hasJSActions(); 11529 } 11530 getCalculationOrderIds() { 11531 return this._transport.getCalculationOrderIds(); 11532 } 11533} 11534class PDFPageProxy { 11535 #delayedCleanupTimeout = null; 11536 #pendingCleanup = false; 11537 constructor(pageIndex, pageInfo, transport, pdfBug = false) { 11538 this._pageIndex = pageIndex; 11539 this._pageInfo = pageInfo; 11540 this._transport = transport; 11541 this._stats = pdfBug ? new StatTimer() : null; 11542 this._pdfBug = pdfBug; 11543 this.commonObjs = transport.commonObjs; 11544 this.objs = new PDFObjects(); 11545 this._maybeCleanupAfterRender = false; 11546 this._intentStates = new Map(); 11547 this.destroyed = false; 11548 } 11549 get pageNumber() { 11550 return this._pageIndex + 1; 11551 } 11552 get rotate() { 11553 return this._pageInfo.rotate; 11554 } 11555 get ref() { 11556 return this._pageInfo.ref; 11557 } 11558 get userUnit() { 11559 return this._pageInfo.userUnit; 11560 } 11561 get view() { 11562 return this._pageInfo.view; 11563 } 11564 getViewport({ 11565 scale, 11566 rotation = this.rotate, 11567 offsetX = 0, 11568 offsetY = 0, 11569 dontFlip = false 11570 } = {}) { 11571 return new PageViewport({ 11572 viewBox: this.view, 11573 scale, 11574 rotation, 11575 offsetX, 11576 offsetY, 11577 dontFlip 11578 }); 11579 } 11580 getAnnotations({ 11581 intent = "display" 11582 } = {}) { 11583 const { 11584 renderingIntent 11585 } = this._transport.getRenderingIntent(intent); 11586 return this._transport.getAnnotations(this._pageIndex, renderingIntent); 11587 } 11588 getJSActions() { 11589 return this._transport.getPageJSActions(this._pageIndex); 11590 } 11591 get filterFactory() { 11592 return this._transport.filterFactory; 11593 } 11594 get isPureXfa() { 11595 return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); 11596 } 11597 async getXfa() { 11598 return this._transport._htmlForXfa?.children[this._pageIndex] || null; 11599 } 11600 render({ 11601 canvasContext, 11602 viewport, 11603 intent = "display", 11604 annotationMode = AnnotationMode.ENABLE, 11605 transform = null, 11606 background = null, 11607 optionalContentConfigPromise = null, 11608 annotationCanvasMap = null, 11609 pageColors = null, 11610 printAnnotationStorage = null, 11611 isEditing = false 11612 }) { 11613 this._stats?.time("Overall"); 11614 const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing); 11615 const { 11616 renderingIntent, 11617 cacheKey 11618 } = intentArgs; 11619 this.#pendingCleanup = false; 11620 this.#abortDelayedCleanup(); 11621 optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent); 11622 let intentState = this._intentStates.get(cacheKey); 11623 if (!intentState) { 11624 intentState = Object.create(null); 11625 this._intentStates.set(cacheKey, intentState); 11626 } 11627 if (intentState.streamReaderCancelTimeout) { 11628 clearTimeout(intentState.streamReaderCancelTimeout); 11629 intentState.streamReaderCancelTimeout = null; 11630 } 11631 const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); 11632 if (!intentState.displayReadyCapability) { 11633 intentState.displayReadyCapability = Promise.withResolvers(); 11634 intentState.operatorList = { 11635 fnArray: [], 11636 argsArray: [], 11637 lastChunk: false, 11638 separateAnnots: null 11639 }; 11640 this._stats?.time("Page Request"); 11641 this._pumpOperatorList(intentArgs); 11642 } 11643 const complete = error => { 11644 intentState.renderTasks.delete(internalRenderTask); 11645 if (this._maybeCleanupAfterRender || intentPrint) { 11646 this.#pendingCleanup = true; 11647 } 11648 this.#tryCleanup(!intentPrint); 11649 if (error) { 11650 internalRenderTask.capability.reject(error); 11651 this._abortOperatorList({ 11652 intentState, 11653 reason: error instanceof Error ? error : new Error(error) 11654 }); 11655 } else { 11656 internalRenderTask.capability.resolve(); 11657 } 11658 if (this._stats) { 11659 this._stats.timeEnd("Rendering"); 11660 this._stats.timeEnd("Overall"); 11661 if (globalThis.Stats?.enabled) { 11662 globalThis.Stats.add(this.pageNumber, this._stats); 11663 } 11664 } 11665 }; 11666 const internalRenderTask = new InternalRenderTask({ 11667 callback: complete, 11668 params: { 11669 canvasContext, 11670 viewport, 11671 transform, 11672 background 11673 }, 11674 objs: this.objs, 11675 commonObjs: this.commonObjs, 11676 annotationCanvasMap, 11677 operatorList: intentState.operatorList, 11678 pageIndex: this._pageIndex, 11679 canvasFactory: this._transport.canvasFactory, 11680 filterFactory: this._transport.filterFactory, 11681 useRequestAnimationFrame: !intentPrint, 11682 pdfBug: this._pdfBug, 11683 pageColors 11684 }); 11685 (intentState.renderTasks ||= new Set()).add(internalRenderTask); 11686 const renderTask = internalRenderTask.task; 11687 Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => { 11688 if (this.destroyed) { 11689 complete(); 11690 return; 11691 } 11692 this._stats?.time("Rendering"); 11693 if (!(optionalContentConfig.renderingIntent & renderingIntent)) { 11694 throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` " + "and `PDFDocumentProxy.getOptionalContentConfig` methods."); 11695 } 11696 internalRenderTask.initializeGraphics({ 11697 transparency, 11698 optionalContentConfig 11699 }); 11700 internalRenderTask.operatorListChanged(); 11701 }).catch(complete); 11702 return renderTask; 11703 } 11704 getOperatorList({ 11705 intent = "display", 11706 annotationMode = AnnotationMode.ENABLE, 11707 printAnnotationStorage = null, 11708 isEditing = false 11709 } = {}) { 11710 function operatorListChanged() { 11711 if (intentState.operatorList.lastChunk) { 11712 intentState.opListReadCapability.resolve(intentState.operatorList); 11713 intentState.renderTasks.delete(opListTask); 11714 } 11715 } 11716 const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing, true); 11717 let intentState = this._intentStates.get(intentArgs.cacheKey); 11718 if (!intentState) { 11719 intentState = Object.create(null); 11720 this._intentStates.set(intentArgs.cacheKey, intentState); 11721 } 11722 let opListTask; 11723 if (!intentState.opListReadCapability) { 11724 opListTask = Object.create(null); 11725 opListTask.operatorListChanged = operatorListChanged; 11726 intentState.opListReadCapability = Promise.withResolvers(); 11727 (intentState.renderTasks ||= new Set()).add(opListTask); 11728 intentState.operatorList = { 11729 fnArray: [], 11730 argsArray: [], 11731 lastChunk: false, 11732 separateAnnots: null 11733 }; 11734 this._stats?.time("Page Request"); 11735 this._pumpOperatorList(intentArgs); 11736 } 11737 return intentState.opListReadCapability.promise; 11738 } 11739 streamTextContent({ 11740 includeMarkedContent = false, 11741 disableNormalization = false 11742 } = {}) { 11743 const TEXT_CONTENT_CHUNK_SIZE = 100; 11744 return this._transport.messageHandler.sendWithStream("GetTextContent", { 11745 pageIndex: this._pageIndex, 11746 includeMarkedContent: includeMarkedContent === true, 11747 disableNormalization: disableNormalization === true 11748 }, { 11749 highWaterMark: TEXT_CONTENT_CHUNK_SIZE, 11750 size(textContent) { 11751 return textContent.items.length; 11752 } 11753 }); 11754 } 11755 getTextContent(params = {}) { 11756 if (this._transport._htmlForXfa) { 11757 return this.getXfa().then(xfa => XfaText.textContent(xfa)); 11758 } 11759 const readableStream = this.streamTextContent(params); 11760 return new Promise(function (resolve, reject) { 11761 function pump() { 11762 reader.read().then(function ({ 11763 value, 11764 done 11765 }) { 11766 if (done) { 11767 resolve(textContent); 11768 return; 11769 } 11770 textContent.lang ??= value.lang; 11771 Object.assign(textContent.styles, value.styles); 11772 textContent.items.push(...value.items); 11773 pump(); 11774 }, reject); 11775 } 11776 const reader = readableStream.getReader(); 11777 const textContent = { 11778 items: [], 11779 styles: Object.create(null), 11780 lang: null 11781 }; 11782 pump(); 11783 }); 11784 } 11785 getStructTree() { 11786 return this._transport.getStructTree(this._pageIndex); 11787 } 11788 _destroy() { 11789 this.destroyed = true; 11790 const waitOn = []; 11791 for (const intentState of this._intentStates.values()) { 11792 this._abortOperatorList({ 11793 intentState, 11794 reason: new Error("Page was destroyed."), 11795 force: true 11796 }); 11797 if (intentState.opListReadCapability) { 11798 continue; 11799 } 11800 for (const internalRenderTask of intentState.renderTasks) { 11801 waitOn.push(internalRenderTask.completed); 11802 internalRenderTask.cancel(); 11803 } 11804 } 11805 this.objs.clear(); 11806 this.#pendingCleanup = false; 11807 this.#abortDelayedCleanup(); 11808 return Promise.all(waitOn); 11809 } 11810 cleanup(resetStats = false) { 11811 this.#pendingCleanup = true; 11812 const success = this.#tryCleanup(false); 11813 if (resetStats && success) { 11814 this._stats &&= new StatTimer(); 11815 } 11816 return success; 11817 } 11818 #tryCleanup(delayed = false) { 11819 this.#abortDelayedCleanup(); 11820 if (!this.#pendingCleanup || this.destroyed) { 11821 return false;
vendor: 5,769 bytes, lines 11822-12022
11822 } 11823 if (delayed) { 11824 this.#delayedCleanupTimeout = setTimeout(() => { 11825 this.#delayedCleanupTimeout = null; 11826 this.#tryCleanup(false); 11827 }, DELAYED_CLEANUP_TIMEOUT); 11828 return false; 11829 } 11830 for (const { 11831 renderTasks, 11832 operatorList 11833 } of this._intentStates.values()) { 11834 if (renderTasks.size > 0 || !operatorList.lastChunk) { 11835 return false; 11836 } 11837 } 11838 this._intentStates.clear(); 11839 this.objs.clear(); 11840 this.#pendingCleanup = false; 11841 return true; 11842 } 11843 #abortDelayedCleanup() { 11844 if (this.#delayedCleanupTimeout) { 11845 clearTimeout(this.#delayedCleanupTimeout); 11846 this.#delayedCleanupTimeout = null; 11847 } 11848 } 11849 _startRenderPage(transparency, cacheKey) { 11850 const intentState = this._intentStates.get(cacheKey); 11851 if (!intentState) { 11852 return; 11853 } 11854 this._stats?.timeEnd("Page Request"); 11855 intentState.displayReadyCapability?.resolve(transparency); 11856 } 11857 _renderPageChunk(operatorListChunk, intentState) { 11858 for (let i = 0, ii = operatorListChunk.length; i < ii; i++) { 11859 intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); 11860 intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]); 11861 } 11862 intentState.operatorList.lastChunk = operatorListChunk.lastChunk; 11863 intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots; 11864 for (const internalRenderTask of intentState.renderTasks) { 11865 internalRenderTask.operatorListChanged(); 11866 } 11867 if (operatorListChunk.lastChunk) { 11868 this.#tryCleanup(true); 11869 } 11870 } 11871 _pumpOperatorList({ 11872 renderingIntent, 11873 cacheKey, 11874 annotationStorageSerializable, 11875 modifiedIds 11876 }) { 11877 const { 11878 map, 11879 transfer 11880 } = annotationStorageSerializable; 11881 const readableStream = this._transport.messageHandler.sendWithStream("GetOperatorList", { 11882 pageIndex: this._pageIndex, 11883 intent: renderingIntent, 11884 cacheKey, 11885 annotationStorage: map, 11886 modifiedIds 11887 }, transfer); 11888 const reader = readableStream.getReader(); 11889 const intentState = this._intentStates.get(cacheKey); 11890 intentState.streamReader = reader; 11891 const pump = () => { 11892 reader.read().then(({ 11893 value, 11894 done 11895 }) => { 11896 if (done) { 11897 intentState.streamReader = null; 11898 return; 11899 } 11900 if (this._transport.destroyed) { 11901 return; 11902 } 11903 this._renderPageChunk(value, intentState); 11904 pump(); 11905 }, reason => { 11906 intentState.streamReader = null; 11907 if (this._transport.destroyed) { 11908 return; 11909 } 11910 if (intentState.operatorList) { 11911 intentState.operatorList.lastChunk = true; 11912 for (const internalRenderTask of intentState.renderTasks) { 11913 internalRenderTask.operatorListChanged(); 11914 } 11915 this.#tryCleanup(true); 11916 } 11917 if (intentState.displayReadyCapability) { 11918 intentState.displayReadyCapability.reject(reason); 11919 } else if (intentState.opListReadCapability) { 11920 intentState.opListReadCapability.reject(reason); 11921 } else { 11922 throw reason; 11923 } 11924 }); 11925 }; 11926 pump(); 11927 } 11928 _abortOperatorList({ 11929 intentState, 11930 reason, 11931 force = false 11932 }) { 11933 if (!intentState.streamReader) { 11934 return; 11935 } 11936 if (intentState.streamReaderCancelTimeout) { 11937 clearTimeout(intentState.streamReaderCancelTimeout); 11938 intentState.streamReaderCancelTimeout = null; 11939 } 11940 if (!force) { 11941 if (intentState.renderTasks.size > 0) { 11942 return; 11943 } 11944 if (reason instanceof RenderingCancelledException) { 11945 let delay = RENDERING_CANCELLED_TIMEOUT; 11946 if (reason.extraDelay > 0 && reason.extraDelay < 1000) { 11947 delay += reason.extraDelay; 11948 } 11949 intentState.streamReaderCancelTimeout = setTimeout(() => { 11950 intentState.streamReaderCancelTimeout = null; 11951 this._abortOperatorList({ 11952 intentState, 11953 reason, 11954 force: true 11955 }); 11956 }, delay); 11957 return; 11958 } 11959 } 11960 intentState.streamReader.cancel(new AbortException(reason.message)).catch(() => {}); 11961 intentState.streamReader = null; 11962 if (this._transport.destroyed) { 11963 return; 11964 } 11965 for (const [curCacheKey, curIntentState] of this._intentStates) { 11966 if (curIntentState === intentState) { 11967 this._intentStates.delete(curCacheKey); 11968 break; 11969 } 11970 } 11971 this.cleanup(); 11972 } 11973 get stats() { 11974 return this._stats; 11975 } 11976} 11977class LoopbackPort { 11978 #listeners = new Map(); 11979 #deferred = Promise.resolve(); 11980 postMessage(obj, transfer) { 11981 const event = { 11982 data: structuredClone(obj, transfer ? { 11983 transfer 11984 } : null) 11985 }; 11986 this.#deferred.then(() => { 11987 for (const [listener] of this.#listeners) { 11988 listener.call(this, event); 11989 } 11990 }); 11991 } 11992 addEventListener(name, listener, options = null) { 11993 let rmAbort = null; 11994 if (options?.signal instanceof AbortSignal) { 11995 const { 11996 signal 11997 } = options; 11998 if (signal.aborted) { 11999 warn("LoopbackPort - cannot use an `aborted` signal."); 12000 return; 12001 } 12002 const onAbort = () => this.removeEventListener(name, listener); 12003 rmAbort = () => signal.removeEventListener("abort", onAbort); 12004 signal.addEventListener("abort", onAbort); 12005 } 12006 this.#listeners.set(listener, rmAbort); 12007 } 12008 removeEventListener(name, listener) { 12009 const rmAbort = this.#listeners.get(listener); 12010 rmAbort?.(); 12011 this.#listeners.delete(listener); 12012 } 12013 terminate() { 12014 for (const [, rmAbort] of this.#listeners) { 12015 rmAbort?.(); 12016 } 12017 this.#listeners.clear(); 12018 } 12019} 12020class PDFWorker { 12021 static #fakeWorkerId = 0; 12022 static #isWorkerDisabled = false;
12023 static #workerPorts; 12024 static { 12025 if (isNodeJS) { 12026 this.#isWorkerDisabled = true; 12027 GlobalWorkerOptions.workerSrc ||= "./pdf.worker.js"; 12028 } 12029 this._isSameOrigin = (baseUrl, otherUrl) => { 12030 let base; 12031 try { 12032 base = new URL(baseUrl); 12033 if (!base.origin || base.origin === "null") { 12034 return false; 12035 } 12036 } catch { 12037 return false; 12038 } 12039 const other = new URL(otherUrl, base); 12040 return base.origin === other.origin; 12041 }; 12042 this._createCDNWrapper = url => { 12043 const wrapper = `await import("${url}");`; 12044 return URL.createObjectURL(new Blob([wrapper], { 12045 type: "text/javascript" 12046 })); 12047 }; 12048 } 12049 constructor({ 12050 name = null, 12051 port = null, 12052 verbosity = getVerbosityLevel() 12053 } = {}) { 12054 this.name = name; 12055 this.destroyed = false; 12056 this.verbosity = verbosity; 12057 this._readyCapability = Promise.withResolvers(); 12058 this._port = null; 12059 this._webWorker = null; 12060 this._messageHandler = null; 12061 if (port) { 12062 if (PDFWorker.#workerPorts?.has(port)) { 12063 throw new Error("Cannot use more than one PDFWorker per port."); 12064 } 12065 (PDFWorker.#workerPorts ||= new WeakMap()).set(port, this); 12066 this._initializeFromPort(port); 12067 return; 12068 } 12069 this._initialize(); 12070 } 12071 get promise() { 12072 if (isNodeJS) { 12073 return Promise.all([NodePackages.promise, this._readyCapability.promise]); 12074 } 12075 return this._readyCapability.promise; 12076 } 12077 #resolve() { 12078 this._readyCapability.resolve(); 12079 this._messageHandler.send("configure", { 12080 verbosity: this.verbosity 12081 }); 12082 } 12083 get port() { 12084 return this._port; 12085 } 12086 get messageHandler() { 12087 return this._messageHandler; 12088 } 12089 _initializeFromPort(port) { 12090 this._port = port; 12091 this._messageHandler = new MessageHandler("main", "worker", port); 12092 this._messageHandler.on("ready", function () {}); 12093 this.#resolve(); 12094 } 12095 _initialize() { 12096 if (PDFWorker.#isWorkerDisabled || PDFWorker.#mainThreadWorkerMessageHandler) { 12097 this._setupFakeWorker(); 12098 return; 12099 } 12100 let { 12101 workerSrc 12102 } = PDFWorker; 12103 try { 12104 if (!PDFWorker._isSameOrigin(window.location.href, workerSrc)) { 12105 workerSrc = PDFWorker._createCDNWrapper(new URL(workerSrc, window.location).href); 12106 } 12107 const worker = new Worker(workerSrc, { 12108 type: "module" 12109 }); 12110 const messageHandler = new MessageHandler("main", "worker", worker); 12111 const terminateEarly = () => { 12112 ac.abort(); 12113 messageHandler.destroy(); 12114 worker.terminate(); 12115 if (this.destroyed) { 12116 this._readyCapability.reject(new Error("Worker was destroyed")); 12117 } else { 12118 this._setupFakeWorker(); 12119 } 12120 }; 12121 const ac = new AbortController(); 12122 worker.addEventListener("error", () => { 12123 if (!this._webWorker) { 12124 terminateEarly(); 12125 } 12126 }, { 12127 signal: ac.signal 12128 }); 12129 messageHandler.on("test", data => { 12130 ac.abort(); 12131 if (this.destroyed || !data) { 12132 terminateEarly(); 12133 return; 12134 } 12135 this._messageHandler = messageHandler; 12136 this._port = worker; 12137 this._webWorker = worker; 12138 this.#resolve(); 12139 }); 12140 messageHandler.on("ready", data => { 12141 ac.abort(); 12142 if (this.destroyed) { 12143 terminateEarly(); 12144 return; 12145 } 12146 try { 12147 sendTest(); 12148 } catch { 12149 this._setupFakeWorker(); 12150 } 12151 }); 12152 const sendTest = () => { 12153 const testObj = new Uint8Array(); 12154 messageHandler.send("test", testObj, [testObj.buffer]); 12155 }; 12156 sendTest(); 12157 return; 12158 } catch { 12159 info("The worker has been disabled."); 12160 } 12161 this._setupFakeWorker(); 12162 } 12163 _setupFakeWorker() { 12164 if (!PDFWorker.#isWorkerDisabled) { 12165 warn("Setting up fake worker."); 12166 PDFWorker.#isWorkerDisabled = true; 12167 } 12168 PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => { 12169 if (this.destroyed) { 12170 this._readyCapability.reject(new Error("Worker was destroyed")); 12171 return; 12172 } 12173 const port = new LoopbackPort(); 12174 this._port = port;
vendor: 5,740 bytes, lines 12175-12336
12175 const id = `fake${PDFWorker.#fakeWorkerId++}`; 12176 const workerHandler = new MessageHandler(id + "_worker", id, port); 12177 WorkerMessageHandler.setup(workerHandler, port); 12178 this._messageHandler = new MessageHandler(id, id + "_worker", port); 12179 this.#resolve(); 12180 }).catch(reason => { 12181 this._readyCapability.reject(new Error(`Setting up fake worker failed: "${reason.message}".`)); 12182 }); 12183 } 12184 destroy() { 12185 this.destroyed = true; 12186 if (this._webWorker) { 12187 this._webWorker.terminate(); 12188 this._webWorker = null; 12189 } 12190 PDFWorker.#workerPorts?.delete(this._port); 12191 this._port = null; 12192 if (this._messageHandler) { 12193 this._messageHandler.destroy(); 12194 this._messageHandler = null; 12195 } 12196 } 12197 static fromPort(params) { 12198 if (!params?.port) { 12199 throw new Error("PDFWorker.fromPort - invalid method signature."); 12200 } 12201 const cachedPort = this.#workerPorts?.get(params.port); 12202 if (cachedPort) { 12203 if (cachedPort._pendingDestroy) { 12204 throw new Error("PDFWorker.fromPort - the worker is being destroyed.\n" + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls."); 12205 } 12206 return cachedPort; 12207 } 12208 return new PDFWorker(params); 12209 } 12210 static get workerSrc() { 12211 if (GlobalWorkerOptions.workerSrc) { 12212 return GlobalWorkerOptions.workerSrc; 12213 } 12214 throw new Error('No "GlobalWorkerOptions.workerSrc" specified.'); 12215 } 12216 static get #mainThreadWorkerMessageHandler() { 12217 try { 12218 return globalThis.pdfjsWorker?.WorkerMessageHandler || null; 12219 } catch { 12220 return null; 12221 } 12222 } 12223 static get _setupFakeWorkerGlobal() { 12224 const loader = async () => { 12225 if (this.#mainThreadWorkerMessageHandler) { 12226 return this.#mainThreadWorkerMessageHandler; 12227 } 12228 const worker = await import(/*webpackIgnore: true*/this.workerSrc); 12229 return worker.WorkerMessageHandler; 12230 }; 12231 return shadow(this, "_setupFakeWorkerGlobal", loader()); 12232 } 12233} 12234class WorkerTransport { 12235 #methodPromises = new Map(); 12236 #pageCache = new Map(); 12237 #pagePromises = new Map(); 12238 #pageRefCache = new Map(); 12239 #passwordCapability = null; 12240 constructor(messageHandler, loadingTask, networkStream, params, factory) { 12241 this.messageHandler = messageHandler; 12242 this.loadingTask = loadingTask; 12243 this.commonObjs = new PDFObjects(); 12244 this.fontLoader = new FontLoader({ 12245 ownerDocument: params.ownerDocument, 12246 styleElement: params.styleElement 12247 }); 12248 this.loadingParams = params.loadingParams; 12249 this._params = params; 12250 this.canvasFactory = factory.canvasFactory; 12251 this.filterFactory = factory.filterFactory; 12252 this.cMapReaderFactory = factory.cMapReaderFactory; 12253 this.standardFontDataFactory = factory.standardFontDataFactory; 12254 this.destroyed = false; 12255 this.destroyCapability = null; 12256 this._networkStream = networkStream; 12257 this._fullReader = null; 12258 this._lastProgress = null; 12259 this.downloadInfoCapability = Promise.withResolvers(); 12260 this.setupMessageHandler(); 12261 } 12262 #cacheSimpleMethod(name, data = null) { 12263 const cachedPromise = this.#methodPromises.get(name); 12264 if (cachedPromise) { 12265 return cachedPromise; 12266 } 12267 const promise = this.messageHandler.sendWithPromise(name, data); 12268 this.#methodPromises.set(name, promise); 12269 return promise; 12270 } 12271 get annotationStorage() { 12272 return shadow(this, "annotationStorage", new AnnotationStorage()); 12273 } 12274 getRenderingIntent(intent, annotationMode = AnnotationMode.ENABLE, printAnnotationStorage = null, isEditing = false, isOpList = false) { 12275 let renderingIntent = RenderingIntentFlag.DISPLAY; 12276 let annotationStorageSerializable = SerializableEmpty; 12277 switch (intent) { 12278 case "any": 12279 renderingIntent = RenderingIntentFlag.ANY; 12280 break; 12281 case "display": 12282 break; 12283 case "print": 12284 renderingIntent = RenderingIntentFlag.PRINT; 12285 break; 12286 default: 12287 warn(`getRenderingIntent - invalid intent: ${intent}`); 12288 } 12289 const annotationStorage = renderingIntent & RenderingIntentFlag.PRINT && printAnnotationStorage instanceof PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage; 12290 switch (annotationMode) { 12291 case AnnotationMode.DISABLE: 12292 renderingIntent += RenderingIntentFlag.ANNOTATIONS_DISABLE; 12293 break; 12294 case AnnotationMode.ENABLE: 12295 break; 12296 case AnnotationMode.ENABLE_FORMS: 12297 renderingIntent += RenderingIntentFlag.ANNOTATIONS_FORMS; 12298 break; 12299 case AnnotationMode.ENABLE_STORAGE: 12300 renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE; 12301 annotationStorageSerializable = annotationStorage.serializable; 12302 break; 12303 default: 12304 warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`); 12305 } 12306 if (isEditing) { 12307 renderingIntent += RenderingIntentFlag.IS_EDITING; 12308 } 12309 if (isOpList) { 12310 renderingIntent += RenderingIntentFlag.OPLIST; 12311 } 12312 const { 12313 ids: modifiedIds, 12314 hash: modifiedIdsHash 12315 } = annotationStorage.modifiedIds; 12316 const cacheKeyBuf = [renderingIntent, annotationStorageSerializable.hash, modifiedIdsHash]; 12317 return { 12318 renderingIntent, 12319 cacheKey: cacheKeyBuf.join("_"), 12320 annotationStorageSerializable, 12321 modifiedIds 12322 }; 12323 } 12324 destroy() { 12325 if (this.destroyCapability) { 12326 return this.destroyCapability.promise; 12327 } 12328 this.destroyed = true; 12329 this.destroyCapability = Promise.withResolvers(); 12330 this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback")); 12331 const waitOn = []; 12332 for (const page of this.#pageCache.values()) { 12333 waitOn.push(page._destroy()); 12334 } 12335 this.#pageCache.clear(); 12336 this.#pagePromises.clear();
12337 this.#pageRefCache.clear(); 12338 if (this.hasOwnProperty("annotationStorage")) { 12339 this.annotationStorage.resetModified(); 12340 } 12341 const terminated = this.messageHandler.sendWithPromise("Terminate", null); 12342 waitOn.push(terminated); 12343 Promise.all(waitOn).then(() => { 12344 this.commonObjs.clear(); 12345 this.fontLoader.clear(); 12346 this.#methodPromises.clear(); 12347 this.filterFactory.destroy(); 12348 TextLayer.cleanup(); 12349 this._networkStream?.cancelAllRequests(new AbortException("Worker was terminated.")); 12350 if (this.messageHandler) { 12351 this.messageHandler.destroy(); 12352 this.messageHandler = null; 12353 } 12354 this.destroyCapability.resolve(); 12355 }, this.destroyCapability.reject); 12356 return this.destroyCapability.promise; 12357 } 12358 setupMessageHandler() { 12359 const { 12360 messageHandler, 12361 loadingTask 12362 } = this; 12363 messageHandler.on("GetReader", (data, sink) => { 12364 assert(this._networkStream, "GetReader - no `IPDFStream` instance available."); 12365 this._fullReader = this._networkStream.getFullReader(); 12366 this._fullReader.onProgress = evt => { 12367 this._lastProgress = { 12368 loaded: evt.loaded, 12369 total: evt.total 12370 }; 12371 }; 12372 sink.onPull = () => { 12373 this._fullReader.read().then(function ({ 12374 value, 12375 done 12376 }) { 12377 if (done) { 12378 sink.close(); 12379 return; 12380 } 12381 assert(value instanceof ArrayBuffer, "GetReader - expected an ArrayBuffer."); 12382 sink.enqueue(new Uint8Array(value), 1, [value]); 12383 }).catch(reason => { 12384 sink.error(reason); 12385 }); 12386 }; 12387 sink.onCancel = reason => { 12388 this._fullReader.cancel(reason); 12389 sink.ready.catch(readyReason => { 12390 if (this.destroyed) { 12391 return; 12392 } 12393 throw readyReason; 12394 }); 12395 }; 12396 }); 12397 messageHandler.on("ReaderHeadersReady", async data => { 12398 await this._fullReader.headersReady; 12399 const { 12400 isStreamingSupported, 12401 isRangeSupported, 12402 contentLength 12403 } = this._fullReader; 12404 if (!isStreamingSupported || !isRangeSupported) { 12405 if (this._lastProgress) { 12406 loadingTask.onProgress?.(this._lastProgress); 12407 } 12408 this._fullReader.onProgress = evt => { 12409 loadingTask.onProgress?.({ 12410 loaded: evt.loaded, 12411 total: evt.total 12412 }); 12413 }; 12414 } 12415 return { 12416 isStreamingSupported, 12417 isRangeSupported, 12418 contentLength 12419 }; 12420 }); 12421 messageHandler.on("GetRangeReader", (data, sink) => { 12422 assert(this._networkStream, "GetRangeReader - no `IPDFStream` instance available."); 12423 const rangeReader = this._networkStream.getRangeReader(data.begin, data.end); 12424 if (!rangeReader) { 12425 sink.close(); 12426 return; 12427 } 12428 sink.onPull = () => { 12429 rangeReader.read().then(function ({ 12430 value, 12431 done 12432 }) { 12433 if (done) { 12434 sink.close(); 12435 return; 12436 } 12437 assert(value instanceof ArrayBuffer, "GetRangeReader - expected an ArrayBuffer."); 12438 sink.enqueue(new Uint8Array(value), 1, [value]); 12439 }).catch(reason => { 12440 sink.error(reason); 12441 }); 12442 }; 12443 sink.onCancel = reason => { 12444 rangeReader.cancel(reason); 12445 sink.ready.catch(readyReason => { 12446 if (this.destroyed) { 12447 return; 12448 } 12449 throw readyReason; 12450 }); 12451 }; 12452 }); 12453 messageHandler.on("GetDoc", ({ 12454 pdfInfo 12455 }) => { 12456 this._numPages = pdfInfo.numPages; 12457 this._htmlForXfa = pdfInfo.htmlForXfa; 12458 delete pdfInfo.htmlForXfa; 12459 loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this)); 12460 }); 12461 messageHandler.on("DocException", function (ex) { 12462 let reason; 12463 switch (ex.name) { 12464 case "PasswordException": 12465 reason = new PasswordException(ex.message, ex.code); 12466 break; 12467 case "InvalidPDFException": 12468 reason = new InvalidPDFException(ex.message); 12469 break; 12470 case "MissingPDFException": 12471 reason = new MissingPDFException(ex.message); 12472 break; 12473 case "UnexpectedResponseException": 12474 reason = new UnexpectedResponseException(ex.message, ex.status); 12475 break; 12476 case "UnknownErrorException": 12477 reason = new UnknownErrorException(ex.message, ex.details); 12478 break; 12479 default: 12480 unreachable("DocException - expected a valid Error."); 12481 } 12482 loadingTask._capability.reject(reason); 12483 }); 12484 messageHandler.on("PasswordRequest", exception => { 12485 this.#passwordCapability = Promise.withResolvers(); 12486 if (loadingTask.onPassword) { 12487 const updatePassword = password => { 12488 if (password instanceof Error) { 12489 this.#passwordCapability.reject(password); 12490 } else { 12491 this.#passwordCapability.resolve({ 12492 password 12493 }); 12494 } 12495 }; 12496 try { 12497 loadingTask.onPassword(updatePassword, exception.code); 12498 } catch (ex) { 12499 this.#passwordCapability.reject(ex); 12500 } 12501 } else { 12502 this.#passwordCapability.reject(new PasswordException(exception.message, exception.code)); 12503 } 12504 return this.#passwordCapability.promise; 12505 }); 12506 messageHandler.on("DataLoaded", data => { 12507 loadingTask.onProgress?.({ 12508 loaded: data.length, 12509 total: data.length 12510 }); 12511 this.downloadInfoCapability.resolve(data); 12512 }); 12513 messageHandler.on("StartRenderPage", data => { 12514 if (this.destroyed) { 12515 return; 12516 } 12517 const page = this.#pageCache.get(data.pageIndex); 12518 page._startRenderPage(data.transparency, data.cacheKey); 12519 }); 12520 messageHandler.on("commonobj", ([id, type, exportedData]) => { 12521 if (this.destroyed) { 12522 return null; 12523 } 12524 if (this.commonObjs.has(id)) { 12525 return null; 12526 } 12527 switch (type) { 12528 case "Font": 12529 const { 12530 disableFontFace, 12531 fontExtraProperties, 12532 pdfBug 12533 } = this._params; 12534 if ("error" in exportedData) { 12535 const exportedError = exportedData.error; 12536 warn(`Error during font loading: ${exportedError}`); 12537 this.commonObjs.resolve(id, exportedError); 12538 break; 12539 } 12540 const inspectFont = pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null; 12541 const font = new FontFaceObject(exportedData, { 12542 disableFontFace, 12543 inspectFont 12544 });
12545 this.fontLoader.bind(font).catch(() => messageHandler.sendWithPromise("FontFallback", { 12546 id 12547 })).finally(() => { 12548 if (!fontExtraProperties && font.data) { 12549 font.data = null; 12550 } 12551 this.commonObjs.resolve(id, font); 12552 }); 12553 break; 12554 case "CopyLocalImage": 12555 const { 12556 imageRef 12557 } = exportedData; 12558 assert(imageRef, "The imageRef must be defined."); 12559 for (const pageProxy of this.#pageCache.values()) { 12560 for (const [, data] of pageProxy.objs) { 12561 if (data?.ref !== imageRef) { 12562 continue; 12563 } 12564 if (!data.dataLen) { 12565 return null; 12566 } 12567 this.commonObjs.resolve(id, structuredClone(data)); 12568 return data.dataLen; 12569 } 12570 } 12571 break; 12572 case "FontPath": 12573 case "Image": 12574 case "Pattern": 12575 this.commonObjs.resolve(id, exportedData); 12576 break; 12577 default: 12578 throw new Error(`Got unknown common object type ${type}`); 12579 } 12580 return null; 12581 }); 12582 messageHandler.on("obj", ([id, pageIndex, type, imageData]) => { 12583 if (this.destroyed) { 12584 return; 12585 } 12586 const pageProxy = this.#pageCache.get(pageIndex); 12587 if (pageProxy.objs.has(id)) { 12588 return; 12589 } 12590 if (pageProxy._intentStates.size === 0) { 12591 imageData?.bitmap?.close(); 12592 return; 12593 } 12594 switch (type) { 12595 case "Image": 12596 pageProxy.objs.resolve(id, imageData); 12597 if (imageData?.dataLen > MAX_IMAGE_SIZE_TO_CACHE) { 12598 pageProxy._maybeCleanupAfterRender = true; 12599 } 12600 break; 12601 case "Pattern": 12602 pageProxy.objs.resolve(id, imageData); 12603 break; 12604 default: 12605 throw new Error(`Got unknown object type ${type}`); 12606 } 12607 }); 12608 messageHandler.on("DocProgress", data => { 12609 if (this.destroyed) { 12610 return; 12611 } 12612 loadingTask.onProgress?.({ 12613 loaded: data.loaded, 12614 total: data.total 12615 }); 12616 }); 12617 messageHandler.on("FetchBuiltInCMap", async data => { 12618 if (this.destroyed) { 12619 throw new Error("Worker was destroyed."); 12620 } 12621 if (!this.cMapReaderFactory) { 12622 throw new Error("CMapReaderFactory not initialized, see the `useWorkerFetch` parameter."); 12623 } 12624 return this.cMapReaderFactory.fetch(data); 12625 }); 12626 messageHandler.on("FetchStandardFontData", async data => { 12627 if (this.destroyed) { 12628 throw new Error("Worker was destroyed."); 12629 } 12630 if (!this.standardFontDataFactory) { 12631 throw new Error("StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter."); 12632 } 12633 return this.standardFontDataFactory.fetch(data); 12634 }); 12635 } 12636 getData() { 12637 return this.messageHandler.sendWithPromise("GetData", null); 12638 } 12639 saveDocument() { 12640 if (this.annotationStorage.size <= 0) { 12641 warn("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead."); 12642 } 12643 const { 12644 map, 12645 transfer 12646 } = this.annotationStorage.serializable; 12647 return this.messageHandler.sendWithPromise("SaveDocument", { 12648 isPureXfa: !!this._htmlForXfa, 12649 numPages: this._numPages, 12650 annotationStorage: map, 12651 filename: this._fullReader?.filename ?? null 12652 }, transfer).finally(() => { 12653 this.annotationStorage.resetModified(); 12654 }); 12655 } 12656 getPage(pageNumber) { 12657 if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this._numPages) { 12658 return Promise.reject(new Error("Invalid page request.")); 12659 } 12660 const pageIndex = pageNumber - 1, 12661 cachedPromise = this.#pagePromises.get(pageIndex); 12662 if (cachedPromise) { 12663 return cachedPromise; 12664 } 12665 const promise = this.messageHandler.sendWithPromise("GetPage", { 12666 pageIndex 12667 }).then(pageInfo => { 12668 if (this.destroyed) { 12669 throw new Error("Transport destroyed"); 12670 } 12671 if (pageInfo.refStr) {
vendor: 19,015 bytes, lines 12672-13320
12672 this.#pageRefCache.set(pageInfo.refStr, pageNumber); 12673 } 12674 const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.pdfBug); 12675 this.#pageCache.set(pageIndex, page); 12676 return page; 12677 }); 12678 this.#pagePromises.set(pageIndex, promise); 12679 return promise; 12680 } 12681 getPageIndex(ref) { 12682 if (!isRefProxy(ref)) { 12683 return Promise.reject(new Error("Invalid pageIndex request.")); 12684 } 12685 return this.messageHandler.sendWithPromise("GetPageIndex", { 12686 num: ref.num, 12687 gen: ref.gen 12688 }); 12689 } 12690 getAnnotations(pageIndex, intent) { 12691 return this.messageHandler.sendWithPromise("GetAnnotations", { 12692 pageIndex, 12693 intent 12694 }); 12695 } 12696 getFieldObjects() { 12697 return this.#cacheSimpleMethod("GetFieldObjects"); 12698 } 12699 hasJSActions() { 12700 return this.#cacheSimpleMethod("HasJSActions"); 12701 } 12702 getCalculationOrderIds() { 12703 return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null); 12704 } 12705 getDestinations() { 12706 return this.messageHandler.sendWithPromise("GetDestinations", null); 12707 } 12708 getDestination(id) { 12709 if (typeof id !== "string") { 12710 return Promise.reject(new Error("Invalid destination request.")); 12711 } 12712 return this.messageHandler.sendWithPromise("GetDestination", { 12713 id 12714 }); 12715 } 12716 getPageLabels() { 12717 return this.messageHandler.sendWithPromise("GetPageLabels", null); 12718 } 12719 getPageLayout() { 12720 return this.messageHandler.sendWithPromise("GetPageLayout", null); 12721 } 12722 getPageMode() { 12723 return this.messageHandler.sendWithPromise("GetPageMode", null); 12724 } 12725 getViewerPreferences() { 12726 return this.messageHandler.sendWithPromise("GetViewerPreferences", null); 12727 } 12728 getOpenAction() { 12729 return this.messageHandler.sendWithPromise("GetOpenAction", null); 12730 } 12731 getAttachments() { 12732 return this.messageHandler.sendWithPromise("GetAttachments", null); 12733 } 12734 getDocJSActions() { 12735 return this.#cacheSimpleMethod("GetDocJSActions"); 12736 } 12737 getPageJSActions(pageIndex) { 12738 return this.messageHandler.sendWithPromise("GetPageJSActions", { 12739 pageIndex 12740 }); 12741 } 12742 getStructTree(pageIndex) { 12743 return this.messageHandler.sendWithPromise("GetStructTree", { 12744 pageIndex 12745 }); 12746 } 12747 getOutline() { 12748 return this.messageHandler.sendWithPromise("GetOutline", null); 12749 } 12750 getOptionalContentConfig(renderingIntent) { 12751 return this.#cacheSimpleMethod("GetOptionalContentConfig").then(data => new OptionalContentConfig(data, renderingIntent)); 12752 } 12753 getPermissions() { 12754 return this.messageHandler.sendWithPromise("GetPermissions", null); 12755 } 12756 getMetadata() { 12757 const name = "GetMetadata", 12758 cachedPromise = this.#methodPromises.get(name); 12759 if (cachedPromise) { 12760 return cachedPromise; 12761 } 12762 const promise = this.messageHandler.sendWithPromise(name, null).then(results => ({ 12763 info: results[0], 12764 metadata: results[1] ? new Metadata(results[1]) : null, 12765 contentDispositionFilename: this._fullReader?.filename ?? null, 12766 contentLength: this._fullReader?.contentLength ?? null 12767 })); 12768 this.#methodPromises.set(name, promise); 12769 return promise; 12770 } 12771 getMarkInfo() { 12772 return this.messageHandler.sendWithPromise("GetMarkInfo", null); 12773 } 12774 async startCleanup(keepLoadedFonts = false) { 12775 if (this.destroyed) { 12776 return; 12777 } 12778 await this.messageHandler.sendWithPromise("Cleanup", null); 12779 for (const page of this.#pageCache.values()) { 12780 const cleanupSuccessful = page.cleanup(); 12781 if (!cleanupSuccessful) { 12782 throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`); 12783 } 12784 } 12785 this.commonObjs.clear(); 12786 if (!keepLoadedFonts) { 12787 this.fontLoader.clear(); 12788 } 12789 this.#methodPromises.clear(); 12790 this.filterFactory.destroy(true); 12791 TextLayer.cleanup(); 12792 } 12793 cachedPageNumber(ref) { 12794 if (!isRefProxy(ref)) { 12795 return null; 12796 } 12797 const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`; 12798 return this.#pageRefCache.get(refStr) ?? null; 12799 } 12800} 12801const INITIAL_DATA = Symbol("INITIAL_DATA"); 12802class PDFObjects { 12803 #objs = Object.create(null); 12804 #ensureObj(objId) { 12805 return this.#objs[objId] ||= { 12806 ...Promise.withResolvers(), 12807 data: INITIAL_DATA 12808 }; 12809 } 12810 get(objId, callback = null) { 12811 if (callback) { 12812 const obj = this.#ensureObj(objId); 12813 obj.promise.then(() => callback(obj.data)); 12814 return null; 12815 } 12816 const obj = this.#objs[objId]; 12817 if (!obj || obj.data === INITIAL_DATA) { 12818 throw new Error(`Requesting object that isn't resolved yet ${objId}.`); 12819 } 12820 return obj.data; 12821 } 12822 has(objId) { 12823 const obj = this.#objs[objId]; 12824 return !!obj && obj.data !== INITIAL_DATA; 12825 } 12826 resolve(objId, data = null) { 12827 const obj = this.#ensureObj(objId); 12828 obj.data = data; 12829 obj.resolve(); 12830 } 12831 clear() { 12832 for (const objId in this.#objs) { 12833 const { 12834 data 12835 } = this.#objs[objId]; 12836 data?.bitmap?.close(); 12837 } 12838 this.#objs = Object.create(null); 12839 } 12840 *[Symbol.iterator]() { 12841 for (const objId in this.#objs) { 12842 const { 12843 data 12844 } = this.#objs[objId]; 12845 if (data === INITIAL_DATA) { 12846 continue; 12847 } 12848 yield [objId, data]; 12849 } 12850 } 12851} 12852class RenderTask { 12853 #internalRenderTask = null; 12854 constructor(internalRenderTask) { 12855 this.#internalRenderTask = internalRenderTask; 12856 this.onContinue = null; 12857 } 12858 get promise() { 12859 return this.#internalRenderTask.capability.promise; 12860 } 12861 cancel(extraDelay = 0) { 12862 this.#internalRenderTask.cancel(null, extraDelay); 12863 } 12864 get separateAnnots() { 12865 const { 12866 separateAnnots 12867 } = this.#internalRenderTask.operatorList; 12868 if (!separateAnnots) { 12869 return false; 12870 } 12871 const { 12872 annotationCanvasMap 12873 } = this.#internalRenderTask; 12874 return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0; 12875 } 12876} 12877class InternalRenderTask { 12878 #rAF = null; 12879 static #canvasInUse = new WeakSet(); 12880 constructor({ 12881 callback, 12882 params, 12883 objs, 12884 commonObjs, 12885 annotationCanvasMap, 12886 operatorList, 12887 pageIndex, 12888 canvasFactory, 12889 filterFactory, 12890 useRequestAnimationFrame = false, 12891 pdfBug = false, 12892 pageColors = null 12893 }) { 12894 this.callback = callback; 12895 this.params = params; 12896 this.objs = objs; 12897 this.commonObjs = commonObjs; 12898 this.annotationCanvasMap = annotationCanvasMap; 12899 this.operatorListIdx = null; 12900 this.operatorList = operatorList; 12901 this._pageIndex = pageIndex; 12902 this.canvasFactory = canvasFactory; 12903 this.filterFactory = filterFactory; 12904 this._pdfBug = pdfBug; 12905 this.pageColors = pageColors; 12906 this.running = false; 12907 this.graphicsReadyCallback = null; 12908 this.graphicsReady = false; 12909 this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined"; 12910 this.cancelled = false; 12911 this.capability = Promise.withResolvers(); 12912 this.task = new RenderTask(this); 12913 this._cancelBound = this.cancel.bind(this); 12914 this._continueBound = this._continue.bind(this); 12915 this._scheduleNextBound = this._scheduleNext.bind(this); 12916 this._nextBound = this._next.bind(this); 12917 this._canvas = params.canvasContext.canvas; 12918 } 12919 get completed() { 12920 return this.capability.promise.catch(function () {}); 12921 } 12922 initializeGraphics({ 12923 transparency = false, 12924 optionalContentConfig 12925 }) { 12926 if (this.cancelled) { 12927 return; 12928 } 12929 if (this._canvas) { 12930 if (InternalRenderTask.#canvasInUse.has(this._canvas)) { 12931 throw new Error("Cannot use the same canvas during multiple render() operations. " + "Use different canvas or ensure previous operations were " + "cancelled or completed."); 12932 } 12933 InternalRenderTask.#canvasInUse.add(this._canvas); 12934 } 12935 if (this._pdfBug && globalThis.StepperManager?.enabled) { 12936 this.stepper = globalThis.StepperManager.create(this._pageIndex); 12937 this.stepper.init(this.operatorList); 12938 this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); 12939 } 12940 const { 12941 canvasContext, 12942 viewport, 12943 transform, 12944 background 12945 } = this.params; 12946 this.gfx = new CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { 12947 optionalContentConfig 12948 }, this.annotationCanvasMap, this.pageColors); 12949 this.gfx.beginDrawing({ 12950 transform, 12951 viewport, 12952 transparency, 12953 background 12954 }); 12955 this.operatorListIdx = 0; 12956 this.graphicsReady = true; 12957 this.graphicsReadyCallback?.(); 12958 } 12959 cancel(error = null, extraDelay = 0) { 12960 this.running = false; 12961 this.cancelled = true; 12962 this.gfx?.endDrawing(); 12963 if (this.#rAF) { 12964 window.cancelAnimationFrame(this.#rAF); 12965 this.#rAF = null; 12966 } 12967 InternalRenderTask.#canvasInUse.delete(this._canvas); 12968 this.callback(error || new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay)); 12969 } 12970 operatorListChanged() { 12971 if (!this.graphicsReady) { 12972 this.graphicsReadyCallback ||= this._continueBound; 12973 return; 12974 } 12975 this.stepper?.updateOperatorList(this.operatorList); 12976 if (this.running) { 12977 return; 12978 } 12979 this._continue(); 12980 } 12981 _continue() { 12982 this.running = true; 12983 if (this.cancelled) { 12984 return; 12985 } 12986 if (this.task.onContinue) { 12987 this.task.onContinue(this._scheduleNextBound); 12988 } else { 12989 this._scheduleNext(); 12990 } 12991 } 12992 _scheduleNext() { 12993 if (this._useRequestAnimationFrame) { 12994 this.#rAF = window.requestAnimationFrame(() => { 12995 this.#rAF = null; 12996 this._nextBound().catch(this._cancelBound); 12997 }); 12998 } else { 12999 Promise.resolve().then(this._nextBound).catch(this._cancelBound); 13000 } 13001 } 13002 async _next() { 13003 if (this.cancelled) { 13004 return; 13005 } 13006 this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper); 13007 if (this.operatorListIdx === this.operatorList.argsArray.length) { 13008 this.running = false; 13009 if (this.operatorList.lastChunk) { 13010 this.gfx.endDrawing(); 13011 InternalRenderTask.#canvasInUse.delete(this._canvas); 13012 this.callback(); 13013 } 13014 } 13015 } 13016} 13017const version = "4.8.69"; 13018const build = "3634dab10"; 13019 13020;// ./src/shared/scripting_utils.js 13021function makeColorComp(n) { 13022 return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0"); 13023} 13024function scaleAndClamp(x) { 13025 return Math.max(0, Math.min(255, 255 * x)); 13026} 13027class ColorConverters { 13028 static CMYK_G([c, y, m, k]) { 13029 return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)]; 13030 } 13031 static G_CMYK([g]) { 13032 return ["CMYK", 0, 0, 0, 1 - g]; 13033 } 13034 static G_RGB([g]) { 13035 return ["RGB", g, g, g]; 13036 } 13037 static G_rgb([g]) { 13038 g = scaleAndClamp(g); 13039 return [g, g, g]; 13040 } 13041 static G_HTML([g]) { 13042 const G = makeColorComp(g); 13043 return `#${G}${G}${G}`; 13044 } 13045 static RGB_G([r, g, b]) { 13046 return ["G", 0.3 * r + 0.59 * g + 0.11 * b]; 13047 } 13048 static RGB_rgb(color) { 13049 return color.map(scaleAndClamp); 13050 } 13051 static RGB_HTML(color) { 13052 return `#${color.map(makeColorComp).join("")}`; 13053 } 13054 static T_HTML() { 13055 return "#00000000"; 13056 } 13057 static T_rgb() { 13058 return [null]; 13059 } 13060 static CMYK_RGB([c, y, m, k]) { 13061 return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)]; 13062 } 13063 static CMYK_rgb([c, y, m, k]) { 13064 return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))]; 13065 } 13066 static CMYK_HTML(components) { 13067 const rgb = this.CMYK_RGB(components).slice(1); 13068 return this.RGB_HTML(rgb); 13069 } 13070 static RGB_CMYK([r, g, b]) { 13071 const c = 1 - r; 13072 const m = 1 - g; 13073 const y = 1 - b; 13074 const k = Math.min(c, m, y); 13075 return ["CMYK", c, m, y, k]; 13076 } 13077} 13078 13079;// ./src/display/svg_factory.js 13080 13081 13082class BaseSVGFactory { 13083 create(width, height, skipDimensions = false) { 13084 if (width <= 0 || height <= 0) { 13085 throw new Error("Invalid SVG dimensions"); 13086 } 13087 const svg = this._createSVG("svg:svg"); 13088 svg.setAttribute("version", "1.1"); 13089 if (!skipDimensions) { 13090 svg.setAttribute("width", `${width}px`); 13091 svg.setAttribute("height", `${height}px`); 13092 } 13093 svg.setAttribute("preserveAspectRatio", "none"); 13094 svg.setAttribute("viewBox", `0 0 ${width} ${height}`); 13095 return svg; 13096 } 13097 createElement(type) { 13098 if (typeof type !== "string") { 13099 throw new Error("Invalid SVG element type"); 13100 } 13101 return this._createSVG(type); 13102 } 13103 _createSVG(type) { 13104 unreachable("Abstract method `_createSVG` called."); 13105 } 13106} 13107class DOMSVGFactory extends BaseSVGFactory { 13108 _createSVG(type) { 13109 return document.createElementNS(SVG_NS, type); 13110 } 13111} 13112 13113;// ./src/display/xfa_layer.js 13114 13115class XfaLayer { 13116 static setupStorage(html, id, element, storage, intent) { 13117 const storedData = storage.getValue(id, { 13118 value: null 13119 }); 13120 switch (element.name) { 13121 case "textarea": 13122 if (storedData.value !== null) { 13123 html.textContent = storedData.value; 13124 } 13125 if (intent === "print") { 13126 break; 13127 } 13128 html.addEventListener("input", event => { 13129 storage.setValue(id, { 13130 value: event.target.value 13131 }); 13132 }); 13133 break; 13134 case "input": 13135 if (element.attributes.type === "radio" || element.attributes.type === "checkbox") { 13136 if (storedData.value === element.attributes.xfaOn) { 13137 html.setAttribute("checked", true); 13138 } else if (storedData.value === element.attributes.xfaOff) { 13139 html.removeAttribute("checked"); 13140 } 13141 if (intent === "print") { 13142 break; 13143 } 13144 html.addEventListener("change", event => { 13145 storage.setValue(id, { 13146 value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff") 13147 }); 13148 }); 13149 } else { 13150 if (storedData.value !== null) { 13151 html.setAttribute("value", storedData.value); 13152 } 13153 if (intent === "print") { 13154 break; 13155 } 13156 html.addEventListener("input", event => { 13157 storage.setValue(id, { 13158 value: event.target.value 13159 }); 13160 }); 13161 } 13162 break; 13163 case "select": 13164 if (storedData.value !== null) { 13165 html.setAttribute("value", storedData.value); 13166 for (const option of element.children) { 13167 if (option.attributes.value === storedData.value) { 13168 option.attributes.selected = true; 13169 } else if (option.attributes.hasOwnProperty("selected")) { 13170 delete option.attributes.selected; 13171 } 13172 } 13173 } 13174 html.addEventListener("input", event => { 13175 const options = event.target.options; 13176 const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value; 13177 storage.setValue(id, { 13178 value 13179 }); 13180 }); 13181 break; 13182 } 13183 } 13184 static setAttributes({ 13185 html, 13186 element, 13187 storage = null, 13188 intent, 13189 linkService 13190 }) { 13191 const { 13192 attributes 13193 } = element; 13194 const isHTMLAnchorElement = html instanceof HTMLAnchorElement; 13195 if (attributes.type === "radio") { 13196 attributes.name = `${attributes.name}-${intent}`; 13197 } 13198 for (const [key, value] of Object.entries(attributes)) { 13199 if (value === null || value === undefined) { 13200 continue; 13201 } 13202 switch (key) { 13203 case "class": 13204 if (value.length) { 13205 html.setAttribute(key, value.join(" ")); 13206 } 13207 break; 13208 case "dataId": 13209 break; 13210 case "id": 13211 html.setAttribute("data-element-id", value); 13212 break; 13213 case "style": 13214 Object.assign(html.style, value); 13215 break; 13216 case "textContent": 13217 html.textContent = value; 13218 break; 13219 default: 13220 if (!isHTMLAnchorElement || key !== "href" && key !== "newWindow") { 13221 html.setAttribute(key, value); 13222 } 13223 } 13224 } 13225 if (isHTMLAnchorElement) { 13226 linkService.addLinkAttributes(html, attributes.href, attributes.newWindow); 13227 } 13228 if (storage && attributes.dataId) { 13229 this.setupStorage(html, attributes.dataId, element, storage); 13230 } 13231 } 13232 static render(parameters) { 13233 const storage = parameters.annotationStorage; 13234 const linkService = parameters.linkService; 13235 const root = parameters.xfaHtml; 13236 const intent = parameters.intent || "display"; 13237 const rootHtml = document.createElement(root.name); 13238 if (root.attributes) { 13239 this.setAttributes({ 13240 html: rootHtml, 13241 element: root, 13242 intent, 13243 linkService 13244 }); 13245 } 13246 const isNotForRichText = intent !== "richText"; 13247 const rootDiv = parameters.div; 13248 rootDiv.append(rootHtml); 13249 if (parameters.viewport) { 13250 const transform = `matrix(${parameters.viewport.transform.join(",")})`; 13251 rootDiv.style.transform = transform; 13252 } 13253 if (isNotForRichText) { 13254 rootDiv.setAttribute("class", "xfaLayer xfaFont"); 13255 } 13256 const textDivs = []; 13257 if (root.children.length === 0) { 13258 if (root.value) { 13259 const node = document.createTextNode(root.value); 13260 rootHtml.append(node); 13261 if (isNotForRichText && XfaText.shouldBuildText(root.name)) { 13262 textDivs.push(node); 13263 } 13264 } 13265 return { 13266 textDivs 13267 }; 13268 } 13269 const stack = [[root, -1, rootHtml]]; 13270 while (stack.length > 0) { 13271 const [parent, i, html] = stack.at(-1); 13272 if (i + 1 === parent.children.length) { 13273 stack.pop(); 13274 continue; 13275 } 13276 const child = parent.children[++stack.at(-1)[1]]; 13277 if (child === null) { 13278 continue; 13279 } 13280 const { 13281 name 13282 } = child; 13283 if (name === "#text") { 13284 const node = document.createTextNode(child.value); 13285 textDivs.push(node); 13286 html.append(node); 13287 continue; 13288 } 13289 const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name); 13290 html.append(childHtml); 13291 if (child.attributes) { 13292 this.setAttributes({ 13293 html: childHtml, 13294 element: child, 13295 storage, 13296 intent, 13297 linkService 13298 }); 13299 } 13300 if (child.children?.length > 0) { 13301 stack.push([child, -1, childHtml]); 13302 } else if (child.value) { 13303 const node = document.createTextNode(child.value); 13304 if (isNotForRichText && XfaText.shouldBuildText(name)) { 13305 textDivs.push(node); 13306 } 13307 childHtml.append(node); 13308 } 13309 } 13310 for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) { 13311 el.setAttribute("readOnly", true); 13312 } 13313 return { 13314 textDivs 13315 }; 13316 } 13317 static update(parameters) { 13318 const transform = `matrix(${parameters.viewport.transform.join(",")})`; 13319 parameters.div.style.transform = transform; 13320 parameters.div.hidden = false;
vendor: 11,731 bytes, lines 13321-13697
13321 } 13322} 13323 13324;// ./src/display/annotation_layer.js 13325 13326 13327 13328 13329 13330 13331const DEFAULT_TAB_INDEX = 1000; 13332const annotation_layer_DEFAULT_FONT_SIZE = 9; 13333const GetElementsByNameSet = new WeakSet(); 13334function getRectDims(rect) { 13335 return { 13336 width: rect[2] - rect[0], 13337 height: rect[3] - rect[1] 13338 }; 13339} 13340class AnnotationElementFactory { 13341 static create(parameters) { 13342 const subtype = parameters.data.annotationType; 13343 switch (subtype) { 13344 case AnnotationType.LINK: 13345 return new LinkAnnotationElement(parameters); 13346 case AnnotationType.TEXT: 13347 return new TextAnnotationElement(parameters); 13348 case AnnotationType.WIDGET: 13349 const fieldType = parameters.data.fieldType; 13350 switch (fieldType) { 13351 case "Tx": 13352 return new TextWidgetAnnotationElement(parameters); 13353 case "Btn": 13354 if (parameters.data.radioButton) { 13355 return new RadioButtonWidgetAnnotationElement(parameters); 13356 } else if (parameters.data.checkBox) { 13357 return new CheckboxWidgetAnnotationElement(parameters); 13358 } 13359 return new PushButtonWidgetAnnotationElement(parameters); 13360 case "Ch": 13361 return new ChoiceWidgetAnnotationElement(parameters); 13362 case "Sig": 13363 return new SignatureWidgetAnnotationElement(parameters); 13364 } 13365 return new WidgetAnnotationElement(parameters); 13366 case AnnotationType.POPUP: 13367 return new PopupAnnotationElement(parameters); 13368 case AnnotationType.FREETEXT: 13369 return new FreeTextAnnotationElement(parameters); 13370 case AnnotationType.LINE: 13371 return new LineAnnotationElement(parameters); 13372 case AnnotationType.SQUARE: 13373 return new SquareAnnotationElement(parameters); 13374 case AnnotationType.CIRCLE: 13375 return new CircleAnnotationElement(parameters); 13376 case AnnotationType.POLYLINE: 13377 return new PolylineAnnotationElement(parameters); 13378 case AnnotationType.CARET: 13379 return new CaretAnnotationElement(parameters); 13380 case AnnotationType.INK: 13381 return new InkAnnotationElement(parameters); 13382 case AnnotationType.POLYGON: 13383 return new PolygonAnnotationElement(parameters); 13384 case AnnotationType.HIGHLIGHT: 13385 return new HighlightAnnotationElement(parameters); 13386 case AnnotationType.UNDERLINE: 13387 return new UnderlineAnnotationElement(parameters); 13388 case AnnotationType.SQUIGGLY: 13389 return new SquigglyAnnotationElement(parameters); 13390 case AnnotationType.STRIKEOUT: 13391 return new StrikeOutAnnotationElement(parameters); 13392 case AnnotationType.STAMP: 13393 return new StampAnnotationElement(parameters); 13394 case AnnotationType.FILEATTACHMENT: 13395 return new FileAttachmentAnnotationElement(parameters); 13396 default: 13397 return new AnnotationElement(parameters); 13398 } 13399 } 13400} 13401class AnnotationElement { 13402 #updates = null; 13403 #hasBorder = false; 13404 #popupElement = null; 13405 constructor(parameters, { 13406 isRenderable = false, 13407 ignoreBorder = false, 13408 createQuadrilaterals = false 13409 } = {}) { 13410 this.isRenderable = isRenderable; 13411 this.data = parameters.data; 13412 this.layer = parameters.layer; 13413 this.linkService = parameters.linkService; 13414 this.downloadManager = parameters.downloadManager; 13415 this.imageResourcesPath = parameters.imageResourcesPath; 13416 this.renderForms = parameters.renderForms; 13417 this.svgFactory = parameters.svgFactory; 13418 this.annotationStorage = parameters.annotationStorage; 13419 this.enableScripting = parameters.enableScripting; 13420 this.hasJSActions = parameters.hasJSActions; 13421 this._fieldObjects = parameters.fieldObjects; 13422 this.parent = parameters.parent; 13423 if (isRenderable) { 13424 this.container = this._createContainer(ignoreBorder); 13425 } 13426 if (createQuadrilaterals) { 13427 this._createQuadrilaterals(); 13428 } 13429 } 13430 static _hasPopupData({ 13431 titleObj, 13432 contentsObj, 13433 richText 13434 }) { 13435 return !!(titleObj?.str || contentsObj?.str || richText?.str); 13436 } 13437 get _isEditable() { 13438 return this.data.isEditable; 13439 } 13440 get hasPopupData() { 13441 return AnnotationElement._hasPopupData(this.data); 13442 } 13443 updateEdited(params) { 13444 if (!this.container) { 13445 return; 13446 } 13447 this.#updates ||= { 13448 rect: this.data.rect.slice(0) 13449 }; 13450 const { 13451 rect 13452 } = params; 13453 if (rect) { 13454 this.#setRectEdited(rect); 13455 } 13456 this.#popupElement?.popup.updateEdited(params); 13457 } 13458 resetEdited() { 13459 if (!this.#updates) { 13460 return; 13461 } 13462 this.#setRectEdited(this.#updates.rect); 13463 this.#popupElement?.popup.resetEdited(); 13464 this.#updates = null; 13465 } 13466 #setRectEdited(rect) { 13467 const { 13468 container: { 13469 style 13470 }, 13471 data: { 13472 rect: currentRect, 13473 rotation 13474 }, 13475 parent: { 13476 viewport: { 13477 rawDims: { 13478 pageWidth, 13479 pageHeight, 13480 pageX, 13481 pageY 13482 } 13483 } 13484 } 13485 } = this; 13486 currentRect?.splice(0, 4, ...rect); 13487 const { 13488 width, 13489 height 13490 } = getRectDims(rect); 13491 style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; 13492 style.top = `${100 * (pageHeight - rect[3] + pageY) / pageHeight}%`; 13493 if (rotation === 0) { 13494 style.width = `${100 * width / pageWidth}%`; 13495 style.height = `${100 * height / pageHeight}%`; 13496 } else { 13497 this.setRotation(rotation); 13498 } 13499 } 13500 _createContainer(ignoreBorder) { 13501 const { 13502 data, 13503 parent: { 13504 page, 13505 viewport 13506 } 13507 } = this; 13508 const container = document.createElement("section"); 13509 container.setAttribute("data-annotation-id", data.id); 13510 if (!(this instanceof WidgetAnnotationElement)) { 13511 container.tabIndex = DEFAULT_TAB_INDEX; 13512 } 13513 const { 13514 style 13515 } = container; 13516 style.zIndex = this.parent.zIndex++; 13517 if (data.alternativeText) { 13518 container.title = data.alternativeText; 13519 } 13520 if (data.noRotate) { 13521 container.classList.add("norotate"); 13522 } 13523 if (!data.rect || this instanceof PopupAnnotationElement) { 13524 const { 13525 rotation 13526 } = data; 13527 if (!data.hasOwnCanvas && rotation !== 0) { 13528 this.setRotation(rotation, container); 13529 } 13530 return container; 13531 } 13532 const { 13533 width, 13534 height 13535 } = getRectDims(data.rect); 13536 if (!ignoreBorder && data.borderStyle.width > 0) { 13537 style.borderWidth = `${data.borderStyle.width}px`; 13538 const horizontalRadius = data.borderStyle.horizontalCornerRadius; 13539 const verticalRadius = data.borderStyle.verticalCornerRadius; 13540 if (horizontalRadius > 0 || verticalRadius > 0) { 13541 const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`; 13542 style.borderRadius = radius; 13543 } else if (this instanceof RadioButtonWidgetAnnotationElement) { 13544 const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`; 13545 style.borderRadius = radius; 13546 } 13547 switch (data.borderStyle.style) { 13548 case AnnotationBorderStyleType.SOLID: 13549 style.borderStyle = "solid"; 13550 break; 13551 case AnnotationBorderStyleType.DASHED: 13552 style.borderStyle = "dashed"; 13553 break; 13554 case AnnotationBorderStyleType.BEVELED: 13555 warn("Unimplemented border style: beveled"); 13556 break; 13557 case AnnotationBorderStyleType.INSET: 13558 warn("Unimplemented border style: inset"); 13559 break; 13560 case AnnotationBorderStyleType.UNDERLINE: 13561 style.borderBottomStyle = "solid"; 13562 break; 13563 default: 13564 break; 13565 } 13566 const borderColor = data.borderColor || null; 13567 if (borderColor) { 13568 this.#hasBorder = true; 13569 style.borderColor = Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0); 13570 } else { 13571 style.borderWidth = 0; 13572 } 13573 } 13574 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]]); 13575 const { 13576 pageWidth, 13577 pageHeight, 13578 pageX, 13579 pageY 13580 } = viewport.rawDims; 13581 style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; 13582 style.top = `${100 * (rect[1] - pageY) / pageHeight}%`; 13583 const { 13584 rotation 13585 } = data; 13586 if (data.hasOwnCanvas || rotation === 0) { 13587 style.width = `${100 * width / pageWidth}%`; 13588 style.height = `${100 * height / pageHeight}%`; 13589 } else { 13590 this.setRotation(rotation, container); 13591 } 13592 return container; 13593 } 13594 setRotation(angle, container = this.container) { 13595 if (!this.data.rect) { 13596 return; 13597 } 13598 const { 13599 pageWidth, 13600 pageHeight 13601 } = this.parent.viewport.rawDims; 13602 const { 13603 width, 13604 height 13605 } = getRectDims(this.data.rect); 13606 let elementWidth, elementHeight; 13607 if (angle % 180 === 0) { 13608 elementWidth = 100 * width / pageWidth; 13609 elementHeight = 100 * height / pageHeight; 13610 } else { 13611 elementWidth = 100 * height / pageWidth; 13612 elementHeight = 100 * width / pageHeight; 13613 } 13614 container.style.width = `${elementWidth}%`; 13615 container.style.height = `${elementHeight}%`; 13616 container.setAttribute("data-main-rotation", (360 - angle) % 360); 13617 } 13618 get _commonActions() { 13619 const setColor = (jsName, styleName, event) => { 13620 const color = event.detail[jsName]; 13621 const colorType = color[0]; 13622 const colorArray = color.slice(1); 13623 event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray); 13624 this.annotationStorage.setValue(this.data.id, { 13625 [styleName]: ColorConverters[`${colorType}_rgb`](colorArray) 13626 }); 13627 }; 13628 return shadow(this, "_commonActions", { 13629 display: event => { 13630 const { 13631 display 13632 } = event.detail; 13633 const hidden = display % 2 === 1; 13634 this.container.style.visibility = hidden ? "hidden" : "visible"; 13635 this.annotationStorage.setValue(this.data.id, { 13636 noView: hidden, 13637 noPrint: display === 1 || display === 2 13638 }); 13639 }, 13640 print: event => { 13641 this.annotationStorage.setValue(this.data.id, { 13642 noPrint: !event.detail.print 13643 }); 13644 }, 13645 hidden: event => { 13646 const { 13647 hidden 13648 } = event.detail; 13649 this.container.style.visibility = hidden ? "hidden" : "visible"; 13650 this.annotationStorage.setValue(this.data.id, { 13651 noPrint: hidden, 13652 noView: hidden 13653 }); 13654 }, 13655 focus: event => { 13656 setTimeout(() => event.target.focus({ 13657 preventScroll: false 13658 }), 0); 13659 }, 13660 userName: event => { 13661 event.target.title = event.detail.userName; 13662 }, 13663 readonly: event => { 13664 event.target.disabled = event.detail.readonly; 13665 }, 13666 required: event => { 13667 this._setRequired(event.target, event.detail.required); 13668 }, 13669 bgColor: event => { 13670 setColor("bgColor", "backgroundColor", event); 13671 }, 13672 fillColor: event => { 13673 setColor("fillColor", "backgroundColor", event); 13674 }, 13675 fgColor: event => { 13676 setColor("fgColor", "color", event); 13677 }, 13678 textColor: event => { 13679 setColor("textColor", "color", event); 13680 }, 13681 borderColor: event => { 13682 setColor("borderColor", "borderColor", event); 13683 }, 13684 strokeColor: event => { 13685 setColor("strokeColor", "borderColor", event); 13686 }, 13687 rotation: event => { 13688 const angle = event.detail.rotation; 13689 this.setRotation(angle); 13690 this.annotationStorage.setValue(this.data.id, { 13691 rotation: angle 13692 }); 13693 } 13694 }); 13695 } 13696 _dispatchEventFromSandbox(actions, jsEvent) { 13697 const commonActions = this._commonActions;
vendor: 16,194 bytes, lines 13698-14238
13698 for (const name of Object.keys(jsEvent.detail)) { 13699 const action = actions[name] || commonActions[name]; 13700 action?.(jsEvent); 13701 } 13702 } 13703 _setDefaultPropertiesFromJS(element) { 13704 if (!this.enableScripting) { 13705 return; 13706 } 13707 const storedData = this.annotationStorage.getRawValue(this.data.id); 13708 if (!storedData) { 13709 return; 13710 } 13711 const commonActions = this._commonActions; 13712 for (const [actionName, detail] of Object.entries(storedData)) { 13713 const action = commonActions[actionName]; 13714 if (action) { 13715 const eventProxy = { 13716 detail: { 13717 [actionName]: detail 13718 }, 13719 target: element 13720 }; 13721 action(eventProxy); 13722 delete storedData[actionName]; 13723 } 13724 } 13725 } 13726 _createQuadrilaterals() { 13727 if (!this.container) { 13728 return; 13729 } 13730 const { 13731 quadPoints 13732 } = this.data; 13733 if (!quadPoints) { 13734 return; 13735 } 13736 const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect.map(x => Math.fround(x)); 13737 if (quadPoints.length === 8) { 13738 const [trX, trY, blX, blY] = quadPoints.subarray(2, 6); 13739 if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) { 13740 return; 13741 } 13742 } 13743 const { 13744 style 13745 } = this.container; 13746 let svgBuffer; 13747 if (this.#hasBorder) { 13748 const { 13749 borderColor, 13750 borderWidth 13751 } = style; 13752 style.borderWidth = 0; 13753 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}">`]; 13754 this.container.classList.add("hasBorder"); 13755 } 13756 const width = rectTrX - rectBlX; 13757 const height = rectTrY - rectBlY; 13758 const { 13759 svgFactory 13760 } = this; 13761 const svg = svgFactory.createElement("svg"); 13762 svg.classList.add("quadrilateralsContainer"); 13763 svg.setAttribute("width", 0); 13764 svg.setAttribute("height", 0); 13765 const defs = svgFactory.createElement("defs"); 13766 svg.append(defs); 13767 const clipPath = svgFactory.createElement("clipPath"); 13768 const id = `clippath_${this.data.id}`; 13769 clipPath.setAttribute("id", id); 13770 clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); 13771 defs.append(clipPath); 13772 for (let i = 2, ii = quadPoints.length; i < ii; i += 8) { 13773 const trX = quadPoints[i]; 13774 const trY = quadPoints[i + 1]; 13775 const blX = quadPoints[i + 2]; 13776 const blY = quadPoints[i + 3]; 13777 const rect = svgFactory.createElement("rect"); 13778 const x = (blX - rectBlX) / width; 13779 const y = (rectTrY - trY) / height; 13780 const rectWidth = (trX - blX) / width; 13781 const rectHeight = (trY - blY) / height; 13782 rect.setAttribute("x", x); 13783 rect.setAttribute("y", y); 13784 rect.setAttribute("width", rectWidth); 13785 rect.setAttribute("height", rectHeight); 13786 clipPath.append(rect); 13787 svgBuffer?.push(`<rect vector-effect="non-scaling-stroke" x="${x}" y="${y}" width="${rectWidth}" height="${rectHeight}"/>`); 13788 } 13789 if (this.#hasBorder) { 13790 svgBuffer.push(`</g></svg>')`); 13791 style.backgroundImage = svgBuffer.join(""); 13792 } 13793 this.container.append(svg); 13794 this.container.style.clipPath = `url(#${id})`; 13795 } 13796 _createPopup() { 13797 const { 13798 data 13799 } = this; 13800 const popup = this.#popupElement = new PopupAnnotationElement({ 13801 data: { 13802 color: data.color, 13803 titleObj: data.titleObj, 13804 modificationDate: data.modificationDate, 13805 contentsObj: data.contentsObj, 13806 richText: data.richText, 13807 parentRect: data.rect, 13808 borderStyle: 0, 13809 id: `popup_${data.id}`, 13810 rotation: data.rotation 13811 }, 13812 parent: this.parent, 13813 elements: [this] 13814 }); 13815 this.parent.div.append(popup.render()); 13816 } 13817 render() { 13818 unreachable("Abstract method `AnnotationElement.render` called"); 13819 } 13820 _getElementsByName(name, skipId = null) { 13821 const fields = []; 13822 if (this._fieldObjects) { 13823 const fieldObj = this._fieldObjects[name]; 13824 if (fieldObj) { 13825 for (const { 13826 page, 13827 id, 13828 exportValues 13829 } of fieldObj) { 13830 if (page === -1) { 13831 continue; 13832 } 13833 if (id === skipId) { 13834 continue; 13835 } 13836 const exportValue = typeof exportValues === "string" ? exportValues : null; 13837 const domElement = document.querySelector(`[data-element-id="${id}"]`); 13838 if (domElement && !GetElementsByNameSet.has(domElement)) { 13839 warn(`_getElementsByName - element not allowed: ${id}`); 13840 continue; 13841 } 13842 fields.push({ 13843 id, 13844 exportValue, 13845 domElement 13846 }); 13847 } 13848 } 13849 return fields; 13850 } 13851 for (const domElement of document.getElementsByName(name)) { 13852 const { 13853 exportValue 13854 } = domElement; 13855 const id = domElement.getAttribute("data-element-id"); 13856 if (id === skipId) { 13857 continue; 13858 } 13859 if (!GetElementsByNameSet.has(domElement)) { 13860 continue; 13861 } 13862 fields.push({ 13863 id, 13864 exportValue, 13865 domElement 13866 }); 13867 } 13868 return fields; 13869 } 13870 show() { 13871 if (this.container) { 13872 this.container.hidden = false; 13873 } 13874 this.popup?.maybeShow(); 13875 } 13876 hide() { 13877 if (this.container) { 13878 this.container.hidden = true; 13879 } 13880 this.popup?.forceHide(); 13881 } 13882 getElementsToTriggerPopup() { 13883 return this.container; 13884 } 13885 addHighlightArea() { 13886 const triggers = this.getElementsToTriggerPopup(); 13887 if (Array.isArray(triggers)) { 13888 for (const element of triggers) { 13889 element.classList.add("highlightArea"); 13890 } 13891 } else { 13892 triggers.classList.add("highlightArea"); 13893 } 13894 } 13895 _editOnDoubleClick() { 13896 if (!this._isEditable) { 13897 return; 13898 } 13899 const { 13900 annotationEditorType: mode, 13901 data: { 13902 id: editId 13903 } 13904 } = this; 13905 this.container.addEventListener("dblclick", () => { 13906 this.linkService.eventBus?.dispatch("switchannotationeditormode", { 13907 source: this, 13908 mode, 13909 editId 13910 }); 13911 }); 13912 } 13913} 13914class LinkAnnotationElement extends AnnotationElement { 13915 constructor(parameters, options = null) { 13916 super(parameters, { 13917 isRenderable: true, 13918 ignoreBorder: !!options?.ignoreBorder, 13919 createQuadrilaterals: true 13920 }); 13921 this.isTooltipOnly = parameters.data.isTooltipOnly; 13922 } 13923 render() { 13924 const { 13925 data, 13926 linkService 13927 } = this; 13928 const link = document.createElement("a"); 13929 link.setAttribute("data-element-id", data.id); 13930 let isBound = false; 13931 if (data.url) { 13932 linkService.addLinkAttributes(link, data.url, data.newWindow); 13933 isBound = true; 13934 } else if (data.action) { 13935 this._bindNamedAction(link, data.action); 13936 isBound = true; 13937 } else if (data.attachment) { 13938 this.#bindAttachment(link, data.attachment, data.attachmentDest); 13939 isBound = true; 13940 } else if (data.setOCGState) { 13941 this.#bindSetOCGState(link, data.setOCGState); 13942 isBound = true; 13943 } else if (data.dest) { 13944 this._bindLink(link, data.dest); 13945 isBound = true; 13946 } else { 13947 if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) { 13948 this._bindJSAction(link, data); 13949 isBound = true; 13950 } 13951 if (data.resetForm) { 13952 this._bindResetFormAction(link, data.resetForm); 13953 isBound = true; 13954 } else if (this.isTooltipOnly && !isBound) { 13955 this._bindLink(link, ""); 13956 isBound = true; 13957 } 13958 } 13959 this.container.classList.add("linkAnnotation"); 13960 if (isBound) { 13961 this.container.append(link); 13962 } 13963 return this.container; 13964 } 13965 #setInternalLink() { 13966 this.container.setAttribute("data-internal-link", ""); 13967 } 13968 _bindLink(link, destination) { 13969 link.href = this.linkService.getDestinationHash(destination); 13970 link.onclick = () => { 13971 if (destination) { 13972 this.linkService.goToDestination(destination); 13973 } 13974 return false; 13975 }; 13976 if (destination || destination === "") { 13977 this.#setInternalLink(); 13978 } 13979 } 13980 _bindNamedAction(link, action) { 13981 link.href = this.linkService.getAnchorUrl(""); 13982 link.onclick = () => { 13983 this.linkService.executeNamedAction(action); 13984 return false; 13985 }; 13986 this.#setInternalLink(); 13987 } 13988 #bindAttachment(link, attachment, dest = null) { 13989 link.href = this.linkService.getAnchorUrl(""); 13990 if (attachment.description) { 13991 link.title = attachment.description; 13992 } 13993 link.onclick = () => { 13994 this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest); 13995 return false; 13996 }; 13997 this.#setInternalLink(); 13998 } 13999 #bindSetOCGState(link, action) { 14000 link.href = this.linkService.getAnchorUrl(""); 14001 link.onclick = () => { 14002 this.linkService.executeSetOCGState(action); 14003 return false; 14004 }; 14005 this.#setInternalLink(); 14006 } 14007 _bindJSAction(link, data) { 14008 link.href = this.linkService.getAnchorUrl(""); 14009 const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]); 14010 for (const name of Object.keys(data.actions)) { 14011 const jsName = map.get(name); 14012 if (!jsName) { 14013 continue; 14014 } 14015 link[jsName] = () => { 14016 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14017 source: this, 14018 detail: { 14019 id: data.id, 14020 name 14021 } 14022 }); 14023 return false; 14024 }; 14025 } 14026 if (!link.onclick) { 14027 link.onclick = () => false; 14028 } 14029 this.#setInternalLink(); 14030 } 14031 _bindResetFormAction(link, resetForm) { 14032 const otherClickAction = link.onclick; 14033 if (!otherClickAction) { 14034 link.href = this.linkService.getAnchorUrl(""); 14035 } 14036 this.#setInternalLink(); 14037 if (!this._fieldObjects) { 14038 warn(`_bindResetFormAction - "resetForm" action not supported, ` + "ensure that the `fieldObjects` parameter is provided."); 14039 if (!otherClickAction) { 14040 link.onclick = () => false; 14041 } 14042 return; 14043 } 14044 link.onclick = () => { 14045 otherClickAction?.(); 14046 const { 14047 fields: resetFormFields, 14048 refs: resetFormRefs, 14049 include 14050 } = resetForm; 14051 const allFields = []; 14052 if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) { 14053 const fieldIds = new Set(resetFormRefs); 14054 for (const fieldName of resetFormFields) { 14055 const fields = this._fieldObjects[fieldName] || []; 14056 for (const { 14057 id 14058 } of fields) { 14059 fieldIds.add(id); 14060 } 14061 } 14062 for (const fields of Object.values(this._fieldObjects)) { 14063 for (const field of fields) { 14064 if (fieldIds.has(field.id) === include) { 14065 allFields.push(field); 14066 } 14067 } 14068 } 14069 } else { 14070 for (const fields of Object.values(this._fieldObjects)) { 14071 allFields.push(...fields); 14072 } 14073 } 14074 const storage = this.annotationStorage; 14075 const allIds = []; 14076 for (const field of allFields) { 14077 const { 14078 id 14079 } = field; 14080 allIds.push(id); 14081 switch (field.type) { 14082 case "text": 14083 { 14084 const value = field.defaultValue || ""; 14085 storage.setValue(id, { 14086 value 14087 }); 14088 break; 14089 } 14090 case "checkbox": 14091 case "radiobutton": 14092 { 14093 const value = field.defaultValue === field.exportValues; 14094 storage.setValue(id, { 14095 value 14096 }); 14097 break; 14098 } 14099 case "combobox": 14100 case "listbox": 14101 { 14102 const value = field.defaultValue || ""; 14103 storage.setValue(id, { 14104 value 14105 }); 14106 break; 14107 } 14108 default: 14109 continue; 14110 } 14111 const domElement = document.querySelector(`[data-element-id="${id}"]`); 14112 if (!domElement) { 14113 continue; 14114 } else if (!GetElementsByNameSet.has(domElement)) { 14115 warn(`_bindResetFormAction - element not allowed: ${id}`); 14116 continue; 14117 } 14118 domElement.dispatchEvent(new Event("resetform")); 14119 } 14120 if (this.enableScripting) { 14121 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14122 source: this, 14123 detail: { 14124 id: "app", 14125 ids: allIds, 14126 name: "ResetForm" 14127 } 14128 }); 14129 } 14130 return false; 14131 }; 14132 } 14133} 14134class TextAnnotationElement extends AnnotationElement { 14135 constructor(parameters) { 14136 super(parameters, { 14137 isRenderable: true 14138 }); 14139 } 14140 render() { 14141 this.container.classList.add("textAnnotation"); 14142 const image = document.createElement("img"); 14143 image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg"; 14144 image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type"); 14145 image.setAttribute("data-l10n-args", JSON.stringify({ 14146 type: this.data.name 14147 })); 14148 if (!this.data.popupRef && this.hasPopupData) { 14149 this._createPopup(); 14150 } 14151 this.container.append(image); 14152 return this.container; 14153 } 14154} 14155class WidgetAnnotationElement extends AnnotationElement { 14156 render() { 14157 return this.container; 14158 } 14159 showElementAndHideCanvas(element) { 14160 if (this.data.hasOwnCanvas) { 14161 if (element.previousSibling?.nodeName === "CANVAS") { 14162 element.previousSibling.hidden = true; 14163 } 14164 element.hidden = false; 14165 } 14166 } 14167 _getKeyModifier(event) { 14168 return util_FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey; 14169 } 14170 _setEventListener(element, elementData, baseName, eventName, valueGetter) { 14171 if (baseName.includes("mouse")) { 14172 element.addEventListener(baseName, event => { 14173 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14174 source: this, 14175 detail: { 14176 id: this.data.id, 14177 name: eventName, 14178 value: valueGetter(event), 14179 shift: event.shiftKey, 14180 modifier: this._getKeyModifier(event) 14181 } 14182 }); 14183 }); 14184 } else { 14185 element.addEventListener(baseName, event => { 14186 if (baseName === "blur") { 14187 if (!elementData.focused || !event.relatedTarget) { 14188 return; 14189 } 14190 elementData.focused = false; 14191 } else if (baseName === "focus") { 14192 if (elementData.focused) { 14193 return; 14194 } 14195 elementData.focused = true; 14196 } 14197 if (!valueGetter) { 14198 return; 14199 } 14200 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14201 source: this, 14202 detail: { 14203 id: this.data.id, 14204 name: eventName, 14205 value: valueGetter(event) 14206 } 14207 }); 14208 }); 14209 } 14210 } 14211 _setEventListeners(element, elementData, names, getter) { 14212 for (const [baseName, eventName] of names) { 14213 if (eventName === "Action" || this.data.actions?.[eventName]) { 14214 if (eventName === "Focus" || eventName === "Blur") { 14215 elementData ||= { 14216 focused: false 14217 }; 14218 } 14219 this._setEventListener(element, elementData, baseName, eventName, getter); 14220 if (eventName === "Focus" && !this.data.actions?.Blur) { 14221 this._setEventListener(element, elementData, "blur", "Blur", null); 14222 } else if (eventName === "Blur" && !this.data.actions?.Focus) { 14223 this._setEventListener(element, elementData, "focus", "Focus", null); 14224 } 14225 } 14226 } 14227 } 14228 _setBackgroundColor(element) { 14229 const color = this.data.backgroundColor || null; 14230 element.style.backgroundColor = color === null ? "transparent" : Util.makeHexColor(color[0], color[1], color[2]); 14231 } 14232 _setTextStyle(element) { 14233 const TEXT_ALIGNMENT = ["left", "center", "right"]; 14234 const { 14235 fontColor 14236 } = this.data.defaultAppearanceData; 14237 const fontSize = this.data.defaultAppearanceData.fontSize || annotation_layer_DEFAULT_FONT_SIZE; 14238 const style = element.style;
vendor: 26,419 bytes, lines 14239-14982
14239 let computedFontSize; 14240 const BORDER_SIZE = 2; 14241 const roundToOneDecimal = x => Math.round(10 * x) / 10; 14242 if (this.data.multiLine) { 14243 const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); 14244 const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; 14245 const lineHeight = height / numberOfLines; 14246 computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / LINE_FACTOR)); 14247 } else { 14248 const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); 14249 computedFontSize = Math.min(fontSize, roundToOneDecimal(height / LINE_FACTOR)); 14250 } 14251 style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`; 14252 style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); 14253 if (this.data.textAlignment !== null) { 14254 style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; 14255 } 14256 } 14257 _setRequired(element, isRequired) { 14258 if (isRequired) { 14259 element.setAttribute("required", true); 14260 } else { 14261 element.removeAttribute("required"); 14262 } 14263 element.setAttribute("aria-required", isRequired); 14264 } 14265} 14266class TextWidgetAnnotationElement extends WidgetAnnotationElement { 14267 constructor(parameters) { 14268 const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue; 14269 super(parameters, { 14270 isRenderable 14271 }); 14272 } 14273 setPropertyOnSiblings(base, key, value, keyInStorage) { 14274 const storage = this.annotationStorage; 14275 for (const element of this._getElementsByName(base.name, base.id)) { 14276 if (element.domElement) { 14277 element.domElement[key] = value; 14278 } 14279 storage.setValue(element.id, { 14280 [keyInStorage]: value 14281 }); 14282 } 14283 } 14284 render() { 14285 const storage = this.annotationStorage; 14286 const id = this.data.id; 14287 this.container.classList.add("textWidgetAnnotation"); 14288 let element = null; 14289 if (this.renderForms) { 14290 const storedData = storage.getValue(id, { 14291 value: this.data.fieldValue 14292 }); 14293 let textContent = storedData.value || ""; 14294 const maxLen = storage.getValue(id, { 14295 charLimit: this.data.maxLen 14296 }).charLimit; 14297 if (maxLen && textContent.length > maxLen) { 14298 textContent = textContent.slice(0, maxLen); 14299 } 14300 let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join("\n") || null; 14301 if (fieldFormattedValues && this.data.comb) { 14302 fieldFormattedValues = fieldFormattedValues.replaceAll(/\s+/g, ""); 14303 } 14304 const elementData = { 14305 userValue: textContent, 14306 formattedValue: fieldFormattedValues, 14307 lastCommittedValue: null, 14308 commitKey: 1, 14309 focused: false 14310 }; 14311 if (this.data.multiLine) { 14312 element = document.createElement("textarea"); 14313 element.textContent = fieldFormattedValues ?? textContent; 14314 if (this.data.doNotScroll) { 14315 element.style.overflowY = "hidden"; 14316 } 14317 } else { 14318 element = document.createElement("input"); 14319 element.type = "text"; 14320 element.setAttribute("value", fieldFormattedValues ?? textContent); 14321 if (this.data.doNotScroll) { 14322 element.style.overflowX = "hidden"; 14323 } 14324 } 14325 if (this.data.hasOwnCanvas) { 14326 element.hidden = true; 14327 } 14328 GetElementsByNameSet.add(element); 14329 element.setAttribute("data-element-id", id); 14330 element.disabled = this.data.readOnly; 14331 element.name = this.data.fieldName; 14332 element.tabIndex = DEFAULT_TAB_INDEX; 14333 this._setRequired(element, this.data.required); 14334 if (maxLen) { 14335 element.maxLength = maxLen; 14336 } 14337 element.addEventListener("input", event => { 14338 storage.setValue(id, { 14339 value: event.target.value 14340 }); 14341 this.setPropertyOnSiblings(element, "value", event.target.value, "value"); 14342 elementData.formattedValue = null; 14343 }); 14344 element.addEventListener("resetform", event => { 14345 const defaultValue = this.data.defaultFieldValue ?? ""; 14346 element.value = elementData.userValue = defaultValue; 14347 elementData.formattedValue = null; 14348 }); 14349 let blurListener = event => { 14350 const { 14351 formattedValue 14352 } = elementData; 14353 if (formattedValue !== null && formattedValue !== undefined) { 14354 event.target.value = formattedValue; 14355 } 14356 event.target.scrollLeft = 0; 14357 }; 14358 if (this.enableScripting && this.hasJSActions) { 14359 element.addEventListener("focus", event => { 14360 if (elementData.focused) { 14361 return; 14362 } 14363 const { 14364 target 14365 } = event; 14366 if (elementData.userValue) { 14367 target.value = elementData.userValue; 14368 } 14369 elementData.lastCommittedValue = target.value; 14370 elementData.commitKey = 1; 14371 if (!this.data.actions?.Focus) { 14372 elementData.focused = true; 14373 } 14374 }); 14375 element.addEventListener("updatefromsandbox", jsEvent => { 14376 this.showElementAndHideCanvas(jsEvent.target); 14377 const actions = { 14378 value(event) { 14379 elementData.userValue = event.detail.value ?? ""; 14380 storage.setValue(id, { 14381 value: elementData.userValue.toString() 14382 }); 14383 event.target.value = elementData.userValue; 14384 }, 14385 formattedValue(event) { 14386 const { 14387 formattedValue 14388 } = event.detail; 14389 elementData.formattedValue = formattedValue; 14390 if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) { 14391 event.target.value = formattedValue; 14392 } 14393 storage.setValue(id, { 14394 formattedValue 14395 }); 14396 }, 14397 selRange(event) { 14398 event.target.setSelectionRange(...event.detail.selRange); 14399 }, 14400 charLimit: event => { 14401 const { 14402 charLimit 14403 } = event.detail; 14404 const { 14405 target 14406 } = event; 14407 if (charLimit === 0) { 14408 target.removeAttribute("maxLength"); 14409 return; 14410 } 14411 target.setAttribute("maxLength", charLimit); 14412 let value = elementData.userValue; 14413 if (!value || value.length <= charLimit) { 14414 return; 14415 } 14416 value = value.slice(0, charLimit); 14417 target.value = elementData.userValue = value; 14418 storage.setValue(id, { 14419 value 14420 }); 14421 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14422 source: this, 14423 detail: { 14424 id, 14425 name: "Keystroke", 14426 value, 14427 willCommit: true, 14428 commitKey: 1, 14429 selStart: target.selectionStart, 14430 selEnd: target.selectionEnd 14431 } 14432 }); 14433 } 14434 }; 14435 this._dispatchEventFromSandbox(actions, jsEvent); 14436 }); 14437 element.addEventListener("keydown", event => { 14438 elementData.commitKey = 1; 14439 let commitKey = -1; 14440 if (event.key === "Escape") { 14441 commitKey = 0; 14442 } else if (event.key === "Enter" && !this.data.multiLine) { 14443 commitKey = 2; 14444 } else if (event.key === "Tab") { 14445 elementData.commitKey = 3; 14446 } 14447 if (commitKey === -1) { 14448 return; 14449 } 14450 const { 14451 value 14452 } = event.target; 14453 if (elementData.lastCommittedValue === value) { 14454 return; 14455 } 14456 elementData.lastCommittedValue = value; 14457 elementData.userValue = value; 14458 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14459 source: this, 14460 detail: { 14461 id, 14462 name: "Keystroke", 14463 value, 14464 willCommit: true, 14465 commitKey, 14466 selStart: event.target.selectionStart, 14467 selEnd: event.target.selectionEnd 14468 } 14469 }); 14470 }); 14471 const _blurListener = blurListener; 14472 blurListener = null; 14473 element.addEventListener("blur", event => { 14474 if (!elementData.focused || !event.relatedTarget) { 14475 return; 14476 } 14477 if (!this.data.actions?.Blur) { 14478 elementData.focused = false; 14479 } 14480 const { 14481 value 14482 } = event.target; 14483 elementData.userValue = value; 14484 if (elementData.lastCommittedValue !== value) { 14485 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14486 source: this, 14487 detail: { 14488 id, 14489 name: "Keystroke", 14490 value, 14491 willCommit: true, 14492 commitKey: elementData.commitKey, 14493 selStart: event.target.selectionStart, 14494 selEnd: event.target.selectionEnd 14495 } 14496 }); 14497 } 14498 _blurListener(event); 14499 }); 14500 if (this.data.actions?.Keystroke) { 14501 element.addEventListener("beforeinput", event => { 14502 elementData.lastCommittedValue = null; 14503 const { 14504 data, 14505 target 14506 } = event; 14507 const { 14508 value, 14509 selectionStart, 14510 selectionEnd 14511 } = target; 14512 let selStart = selectionStart, 14513 selEnd = selectionEnd; 14514 switch (event.inputType) { 14515 case "deleteWordBackward": 14516 { 14517 const match = value.substring(0, selectionStart).match(/\w*[^\w]*$/); 14518 if (match) { 14519 selStart -= match[0].length; 14520 } 14521 break; 14522 } 14523 case "deleteWordForward": 14524 { 14525 const match = value.substring(selectionStart).match(/^[^\w]*\w*/); 14526 if (match) { 14527 selEnd += match[0].length; 14528 } 14529 break; 14530 } 14531 case "deleteContentBackward": 14532 if (selectionStart === selectionEnd) { 14533 selStart -= 1; 14534 } 14535 break; 14536 case "deleteContentForward": 14537 if (selectionStart === selectionEnd) { 14538 selEnd += 1; 14539 } 14540 break; 14541 } 14542 event.preventDefault(); 14543 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14544 source: this, 14545 detail: { 14546 id, 14547 name: "Keystroke", 14548 value, 14549 change: data || "", 14550 willCommit: false, 14551 selStart, 14552 selEnd 14553 } 14554 }); 14555 }); 14556 } 14557 this._setEventListeners(element, elementData, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value); 14558 } 14559 if (blurListener) { 14560 element.addEventListener("blur", blurListener); 14561 } 14562 if (this.data.comb) { 14563 const fieldWidth = this.data.rect[2] - this.data.rect[0]; 14564 const combWidth = fieldWidth / maxLen; 14565 element.classList.add("comb"); 14566 element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`; 14567 } 14568 } else { 14569 element = document.createElement("div"); 14570 element.textContent = this.data.fieldValue; 14571 element.style.verticalAlign = "middle"; 14572 element.style.display = "table-cell"; 14573 if (this.data.hasOwnCanvas) { 14574 element.hidden = true; 14575 } 14576 } 14577 this._setTextStyle(element); 14578 this._setBackgroundColor(element); 14579 this._setDefaultPropertiesFromJS(element); 14580 this.container.append(element); 14581 return this.container; 14582 } 14583} 14584class SignatureWidgetAnnotationElement extends WidgetAnnotationElement { 14585 constructor(parameters) { 14586 super(parameters, { 14587 isRenderable: !!parameters.data.hasOwnCanvas 14588 }); 14589 } 14590} 14591class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { 14592 constructor(parameters) { 14593 super(parameters, { 14594 isRenderable: parameters.renderForms 14595 }); 14596 } 14597 render() { 14598 const storage = this.annotationStorage; 14599 const data = this.data; 14600 const id = data.id; 14601 let value = storage.getValue(id, { 14602 value: data.exportValue === data.fieldValue 14603 }).value; 14604 if (typeof value === "string") { 14605 value = value !== "Off"; 14606 storage.setValue(id, { 14607 value 14608 }); 14609 } 14610 this.container.classList.add("buttonWidgetAnnotation", "checkBox"); 14611 const element = document.createElement("input"); 14612 GetElementsByNameSet.add(element); 14613 element.setAttribute("data-element-id", id); 14614 element.disabled = data.readOnly; 14615 this._setRequired(element, this.data.required); 14616 element.type = "checkbox"; 14617 element.name = data.fieldName; 14618 if (value) { 14619 element.setAttribute("checked", true); 14620 } 14621 element.setAttribute("exportValue", data.exportValue); 14622 element.tabIndex = DEFAULT_TAB_INDEX; 14623 element.addEventListener("change", event => { 14624 const { 14625 name, 14626 checked 14627 } = event.target; 14628 for (const checkbox of this._getElementsByName(name, id)) { 14629 const curChecked = checked && checkbox.exportValue === data.exportValue; 14630 if (checkbox.domElement) { 14631 checkbox.domElement.checked = curChecked; 14632 } 14633 storage.setValue(checkbox.id, { 14634 value: curChecked 14635 }); 14636 } 14637 storage.setValue(id, { 14638 value: checked 14639 }); 14640 }); 14641 element.addEventListener("resetform", event => { 14642 const defaultValue = data.defaultFieldValue || "Off"; 14643 event.target.checked = defaultValue === data.exportValue; 14644 }); 14645 if (this.enableScripting && this.hasJSActions) { 14646 element.addEventListener("updatefromsandbox", jsEvent => { 14647 const actions = { 14648 value(event) { 14649 event.target.checked = event.detail.value !== "Off"; 14650 storage.setValue(id, { 14651 value: event.target.checked 14652 }); 14653 } 14654 }; 14655 this._dispatchEventFromSandbox(actions, jsEvent); 14656 }); 14657 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); 14658 } 14659 this._setBackgroundColor(element); 14660 this._setDefaultPropertiesFromJS(element); 14661 this.container.append(element); 14662 return this.container; 14663 } 14664} 14665class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { 14666 constructor(parameters) { 14667 super(parameters, { 14668 isRenderable: parameters.renderForms 14669 }); 14670 } 14671 render() { 14672 this.container.classList.add("buttonWidgetAnnotation", "radioButton"); 14673 const storage = this.annotationStorage; 14674 const data = this.data; 14675 const id = data.id; 14676 let value = storage.getValue(id, { 14677 value: data.fieldValue === data.buttonValue 14678 }).value; 14679 if (typeof value === "string") { 14680 value = value !== data.buttonValue; 14681 storage.setValue(id, { 14682 value 14683 }); 14684 } 14685 if (value) { 14686 for (const radio of this._getElementsByName(data.fieldName, id)) { 14687 storage.setValue(radio.id, { 14688 value: false 14689 }); 14690 } 14691 } 14692 const element = document.createElement("input"); 14693 GetElementsByNameSet.add(element); 14694 element.setAttribute("data-element-id", id); 14695 element.disabled = data.readOnly; 14696 this._setRequired(element, this.data.required); 14697 element.type = "radio"; 14698 element.name = data.fieldName; 14699 if (value) { 14700 element.setAttribute("checked", true); 14701 } 14702 element.tabIndex = DEFAULT_TAB_INDEX; 14703 element.addEventListener("change", event => { 14704 const { 14705 name, 14706 checked 14707 } = event.target; 14708 for (const radio of this._getElementsByName(name, id)) { 14709 storage.setValue(radio.id, { 14710 value: false 14711 }); 14712 } 14713 storage.setValue(id, { 14714 value: checked 14715 }); 14716 }); 14717 element.addEventListener("resetform", event => { 14718 const defaultValue = data.defaultFieldValue; 14719 event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue; 14720 }); 14721 if (this.enableScripting && this.hasJSActions) { 14722 const pdfButtonValue = data.buttonValue; 14723 element.addEventListener("updatefromsandbox", jsEvent => { 14724 const actions = { 14725 value: event => { 14726 const checked = pdfButtonValue === event.detail.value; 14727 for (const radio of this._getElementsByName(event.target.name)) { 14728 const curChecked = checked && radio.id === id; 14729 if (radio.domElement) { 14730 radio.domElement.checked = curChecked; 14731 } 14732 storage.setValue(radio.id, { 14733 value: curChecked 14734 }); 14735 } 14736 } 14737 }; 14738 this._dispatchEventFromSandbox(actions, jsEvent); 14739 }); 14740 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); 14741 } 14742 this._setBackgroundColor(element); 14743 this._setDefaultPropertiesFromJS(element); 14744 this.container.append(element); 14745 return this.container; 14746 } 14747} 14748class PushButtonWidgetAnnotationElement extends LinkAnnotationElement { 14749 constructor(parameters) { 14750 super(parameters, { 14751 ignoreBorder: parameters.data.hasAppearance 14752 }); 14753 } 14754 render() { 14755 const container = super.render(); 14756 container.classList.add("buttonWidgetAnnotation", "pushButton"); 14757 const linkElement = container.lastChild; 14758 if (this.enableScripting && this.hasJSActions && linkElement) { 14759 this._setDefaultPropertiesFromJS(linkElement); 14760 linkElement.addEventListener("updatefromsandbox", jsEvent => { 14761 this._dispatchEventFromSandbox({}, jsEvent); 14762 }); 14763 } 14764 return container; 14765 } 14766} 14767class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { 14768 constructor(parameters) { 14769 super(parameters, { 14770 isRenderable: parameters.renderForms 14771 }); 14772 } 14773 render() { 14774 this.container.classList.add("choiceWidgetAnnotation"); 14775 const storage = this.annotationStorage; 14776 const id = this.data.id; 14777 const storedData = storage.getValue(id, { 14778 value: this.data.fieldValue 14779 }); 14780 const selectElement = document.createElement("select"); 14781 GetElementsByNameSet.add(selectElement); 14782 selectElement.setAttribute("data-element-id", id); 14783 selectElement.disabled = this.data.readOnly; 14784 this._setRequired(selectElement, this.data.required); 14785 selectElement.name = this.data.fieldName; 14786 selectElement.tabIndex = DEFAULT_TAB_INDEX; 14787 let addAnEmptyEntry = this.data.combo && this.data.options.length > 0; 14788 if (!this.data.combo) { 14789 selectElement.size = this.data.options.length; 14790 if (this.data.multiSelect) { 14791 selectElement.multiple = true; 14792 } 14793 } 14794 selectElement.addEventListener("resetform", event => { 14795 const defaultValue = this.data.defaultFieldValue; 14796 for (const option of selectElement.options) { 14797 option.selected = option.value === defaultValue; 14798 } 14799 }); 14800 for (const option of this.data.options) { 14801 const optionElement = document.createElement("option"); 14802 optionElement.textContent = option.displayValue; 14803 optionElement.value = option.exportValue; 14804 if (storedData.value.includes(option.exportValue)) { 14805 optionElement.setAttribute("selected", true); 14806 addAnEmptyEntry = false; 14807 } 14808 selectElement.append(optionElement); 14809 } 14810 let removeEmptyEntry = null; 14811 if (addAnEmptyEntry) { 14812 const noneOptionElement = document.createElement("option"); 14813 noneOptionElement.value = " "; 14814 noneOptionElement.setAttribute("hidden", true); 14815 noneOptionElement.setAttribute("selected", true); 14816 selectElement.prepend(noneOptionElement); 14817 removeEmptyEntry = () => { 14818 noneOptionElement.remove(); 14819 selectElement.removeEventListener("input", removeEmptyEntry); 14820 removeEmptyEntry = null; 14821 }; 14822 selectElement.addEventListener("input", removeEmptyEntry); 14823 } 14824 const getValue = isExport => { 14825 const name = isExport ? "value" : "textContent"; 14826 const { 14827 options, 14828 multiple 14829 } = selectElement; 14830 if (!multiple) { 14831 return options.selectedIndex === -1 ? null : options[options.selectedIndex][name]; 14832 } 14833 return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]); 14834 }; 14835 let selectedValues = getValue(false); 14836 const getItems = event => { 14837 const options = event.target.options; 14838 return Array.prototype.map.call(options, option => ({ 14839 displayValue: option.textContent, 14840 exportValue: option.value 14841 })); 14842 }; 14843 if (this.enableScripting && this.hasJSActions) { 14844 selectElement.addEventListener("updatefromsandbox", jsEvent => { 14845 const actions = { 14846 value(event) { 14847 removeEmptyEntry?.(); 14848 const value = event.detail.value; 14849 const values = new Set(Array.isArray(value) ? value : [value]); 14850 for (const option of selectElement.options) { 14851 option.selected = values.has(option.value); 14852 } 14853 storage.setValue(id, { 14854 value: getValue(true) 14855 }); 14856 selectedValues = getValue(false); 14857 }, 14858 multipleSelection(event) { 14859 selectElement.multiple = true; 14860 }, 14861 remove(event) { 14862 const options = selectElement.options; 14863 const index = event.detail.remove; 14864 options[index].selected = false; 14865 selectElement.remove(index); 14866 if (options.length > 0) { 14867 const i = Array.prototype.findIndex.call(options, option => option.selected); 14868 if (i === -1) { 14869 options[0].selected = true; 14870 } 14871 } 14872 storage.setValue(id, { 14873 value: getValue(true), 14874 items: getItems(event) 14875 }); 14876 selectedValues = getValue(false); 14877 }, 14878 clear(event) { 14879 while (selectElement.length !== 0) { 14880 selectElement.remove(0); 14881 } 14882 storage.setValue(id, { 14883 value: null, 14884 items: [] 14885 }); 14886 selectedValues = getValue(false); 14887 }, 14888 insert(event) { 14889 const { 14890 index, 14891 displayValue, 14892 exportValue 14893 } = event.detail.insert; 14894 const selectChild = selectElement.children[index]; 14895 const optionElement = document.createElement("option"); 14896 optionElement.textContent = displayValue; 14897 optionElement.value = exportValue; 14898 if (selectChild) { 14899 selectChild.before(optionElement); 14900 } else { 14901 selectElement.append(optionElement); 14902 } 14903 storage.setValue(id, { 14904 value: getValue(true), 14905 items: getItems(event) 14906 }); 14907 selectedValues = getValue(false); 14908 }, 14909 items(event) { 14910 const { 14911 items 14912 } = event.detail; 14913 while (selectElement.length !== 0) { 14914 selectElement.remove(0); 14915 } 14916 for (const item of items) { 14917 const { 14918 displayValue, 14919 exportValue 14920 } = item; 14921 const optionElement = document.createElement("option"); 14922 optionElement.textContent = displayValue; 14923 optionElement.value = exportValue; 14924 selectElement.append(optionElement); 14925 } 14926 if (selectElement.options.length > 0) { 14927 selectElement.options[0].selected = true; 14928 } 14929 storage.setValue(id, { 14930 value: getValue(true), 14931 items: getItems(event) 14932 }); 14933 selectedValues = getValue(false); 14934 }, 14935 indices(event) { 14936 const indices = new Set(event.detail.indices); 14937 for (const option of event.target.options) { 14938 option.selected = indices.has(option.index); 14939 } 14940 storage.setValue(id, { 14941 value: getValue(true) 14942 }); 14943 selectedValues = getValue(false); 14944 }, 14945 editable(event) { 14946 event.target.disabled = !event.detail.editable; 14947 } 14948 }; 14949 this._dispatchEventFromSandbox(actions, jsEvent); 14950 }); 14951 selectElement.addEventListener("input", event => { 14952 const exportValue = getValue(true); 14953 const change = getValue(false); 14954 storage.setValue(id, { 14955 value: exportValue 14956 }); 14957 event.preventDefault(); 14958 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 14959 source: this, 14960 detail: { 14961 id, 14962 name: "Keystroke", 14963 value: selectedValues, 14964 change, 14965 changeEx: exportValue, 14966 willCommit: false, 14967 commitKey: 1, 14968 keyDown: false 14969 } 14970 }); 14971 }); 14972 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); 14973 } else { 14974 selectElement.addEventListener("input", function (event) { 14975 storage.setValue(id, { 14976 value: getValue(true) 14977 }); 14978 }); 14979 } 14980 if (this.data.combo) { 14981 this._setTextStyle(selectElement); 14982 } else {}
14983 this._setBackgroundColor(selectElement); 14984 this._setDefaultPropertiesFromJS(selectElement); 14985 this.container.append(selectElement); 14986 return this.container; 14987 } 14988} 14989class PopupAnnotationElement extends AnnotationElement { 14990 constructor(parameters) { 14991 const { 14992 data, 14993 elements 14994 } = parameters; 14995 super(parameters, { 14996 isRenderable: AnnotationElement._hasPopupData(data) 14997 }); 14998 this.elements = elements; 14999 this.popup = null; 15000 } 15001 render() { 15002 this.container.classList.add("popupAnnotation"); 15003 const popup = this.popup = new PopupElement({ 15004 container: this.container, 15005 color: this.data.color, 15006 titleObj: this.data.titleObj, 15007 modificationDate: this.data.modificationDate, 15008 contentsObj: this.data.contentsObj, 15009 richText: this.data.richText, 15010 rect: this.data.rect, 15011 parentRect: this.data.parentRect || null, 15012 parent: this.parent, 15013 elements: this.elements, 15014 open: this.data.open 15015 }); 15016 const elementIds = []; 15017 for (const element of this.elements) { 15018 element.popup = popup; 15019 element.container.ariaHasPopup = "dialog"; 15020 elementIds.push(element.data.id); 15021 element.addHighlightArea(); 15022 } 15023 this.container.setAttribute("aria-controls", elementIds.map(id => `${AnnotationPrefix}${id}`).join(",")); 15024 return this.container; 15025 } 15026} 15027class PopupElement { 15028 #boundKeyDown = this.#keyDown.bind(this); 15029 #boundHide = this.#hide.bind(this); 15030 #boundShow = this.#show.bind(this); 15031 #boundToggle = this.#toggle.bind(this); 15032 #color = null; 15033 #container = null; 15034 #contentsObj = null; 15035 #dateObj = null; 15036 #elements = null; 15037 #parent = null; 15038 #parentRect = null; 15039 #pinned = false; 15040 #popup = null; 15041 #position = null; 15042 #rect = null; 15043 #richText = null; 15044 #titleObj = null; 15045 #updates = null; 15046 #wasVisible = false; 15047 constructor({ 15048 container, 15049 color, 15050 elements, 15051 titleObj, 15052 modificationDate, 15053 contentsObj, 15054 richText, 15055 parent, 15056 rect, 15057 parentRect, 15058 open 15059 }) { 15060 this.#container = container; 15061 this.#titleObj = titleObj; 15062 this.#contentsObj = contentsObj; 15063 this.#richText = richText; 15064 this.#parent = parent; 15065 this.#color = color; 15066 this.#rect = rect; 15067 this.#parentRect = parentRect; 15068 this.#elements = elements; 15069 this.#dateObj = PDFDateString.toDateObject(modificationDate); 15070 this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup()); 15071 for (const element of this.trigger) { 15072 element.addEventListener("click", this.#boundToggle); 15073 element.addEventListener("mouseenter", this.#boundShow); 15074 element.addEventListener("mouseleave", this.#boundHide); 15075 element.classList.add("popupTriggerArea"); 15076 } 15077 for (const element of elements) { 15078 element.container?.addEventListener("keydown", this.#boundKeyDown); 15079 } 15080 this.#container.hidden = true; 15081 if (open) { 15082 this.#toggle(); 15083 } 15084 } 15085 render() { 15086 if (this.#popup) { 15087 return; 15088 } 15089 const popup = this.#popup = document.createElement("div"); 15090 popup.className = "popup"; 15091 if (this.#color) { 15092 const baseColor = popup.style.outlineColor = Util.makeHexColor(...this.#color); 15093 if (CSS.supports("background-color", "color-mix(in srgb, red 30%, white)")) { 15094 popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`; 15095 } else { 15096 const BACKGROUND_ENLIGHT = 0.7; 15097 popup.style.backgroundColor = Util.makeHexColor(...this.#color.map(c => Math.floor(BACKGROUND_ENLIGHT * (255 - c) + c))); 15098 } 15099 } 15100 const header = document.createElement("span"); 15101 header.className = "header"; 15102 const title = document.createElement("h1"); 15103 header.append(title); 15104 ({ 15105 dir: title.dir, 15106 str: title.textContent 15107 } = this.#titleObj); 15108 popup.append(header); 15109 if (this.#dateObj) { 15110 const modificationDate = document.createElement("span"); 15111 modificationDate.classList.add("popupDate"); 15112 modificationDate.setAttribute("data-l10n-id", "pdfjs-annotation-date-time-string"); 15113 modificationDate.setAttribute("data-l10n-args", JSON.stringify({ 15114 dateObj: this.#dateObj.valueOf() 15115 })); 15116 header.append(modificationDate); 15117 } 15118 const html = this.#html; 15119 if (html) { 15120 XfaLayer.render({ 15121 xfaHtml: html, 15122 intent: "richText", 15123 div: popup 15124 }); 15125 popup.lastChild.classList.add("richText", "popupContent"); 15126 } else { 15127 const contents = this._formatContents(this.#contentsObj); 15128 popup.append(contents); 15129 } 15130 this.#container.append(popup); 15131 } 15132 get #html() { 15133 const richText = this.#richText; 15134 const contentsObj = this.#contentsObj; 15135 if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) { 15136 return this.#richText.html || null; 15137 } 15138 return null; 15139 } 15140 get #fontSize() { 15141 return this.#html?.attributes?.style?.fontSize || 0; 15142 } 15143 get #fontColor() { 15144 return this.#html?.attributes?.style?.color || null; 15145 } 15146 #makePopupContent(text) { 15147 const popupLines = []; 15148 const popupContent = { 15149 str: text, 15150 html: { 15151 name: "div", 15152 attributes: { 15153 dir: "auto" 15154 }, 15155 children: [{ 15156 name: "p", 15157 children: popupLines 15158 }] 15159 } 15160 }; 15161 const lineAttributes = { 15162 style: { 15163 color: this.#fontColor, 15164 fontSize: this.#fontSize ? `calc(${this.#fontSize}px * var(--scale-factor))` : "" 15165 } 15166 }; 15167 for (const line of text.split("\n")) { 15168 popupLines.push({ 15169 name: "span", 15170 value: line, 15171 attributes: lineAttributes 15172 }); 15173 } 15174 return popupContent; 15175 } 15176 _formatContents({ 15177 str, 15178 dir 15179 }) { 15180 const p = document.createElement("p"); 15181 p.classList.add("popupContent"); 15182 p.dir = dir; 15183 const lines = str.split(/(?:\r\n?|\n)/); 15184 for (let i = 0, ii = lines.length; i < ii; ++i) { 15185 const line = lines[i]; 15186 p.append(document.createTextNode(line)); 15187 if (i < ii - 1) { 15188 p.append(document.createElement("br")); 15189 } 15190 } 15191 return p; 15192 } 15193 #keyDown(event) { 15194 if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) { 15195 return; 15196 } 15197 if (event.key === "Enter" || event.key === "Escape" && this.#pinned) {
vendor: 16,979 bytes, lines 15198-15789
15198 this.#toggle(); 15199 } 15200 } 15201 updateEdited({ 15202 rect, 15203 popupContent 15204 }) { 15205 this.#updates ||= { 15206 contentsObj: this.#contentsObj, 15207 richText: this.#richText 15208 }; 15209 if (rect) { 15210 this.#position = null; 15211 } 15212 if (popupContent) { 15213 this.#richText = this.#makePopupContent(popupContent); 15214 this.#contentsObj = null; 15215 } 15216 this.#popup?.remove(); 15217 this.#popup = null; 15218 } 15219 resetEdited() { 15220 if (!this.#updates) { 15221 return; 15222 } 15223 ({ 15224 contentsObj: this.#contentsObj, 15225 richText: this.#richText 15226 } = this.#updates); 15227 this.#updates = null; 15228 this.#popup?.remove(); 15229 this.#popup = null; 15230 this.#position = null; 15231 } 15232 #setPosition() { 15233 if (this.#position !== null) { 15234 return; 15235 } 15236 const { 15237 page: { 15238 view 15239 }, 15240 viewport: { 15241 rawDims: { 15242 pageWidth, 15243 pageHeight, 15244 pageX, 15245 pageY 15246 } 15247 } 15248 } = this.#parent; 15249 let useParentRect = !!this.#parentRect; 15250 let rect = useParentRect ? this.#parentRect : this.#rect; 15251 for (const element of this.#elements) { 15252 if (!rect || Util.intersect(element.data.rect, rect) !== null) { 15253 rect = element.data.rect; 15254 useParentRect = true; 15255 break; 15256 } 15257 } 15258 const normalizedRect = Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]); 15259 const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5; 15260 const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0; 15261 const popupLeft = normalizedRect[0] + parentWidth; 15262 const popupTop = normalizedRect[1]; 15263 this.#position = [100 * (popupLeft - pageX) / pageWidth, 100 * (popupTop - pageY) / pageHeight]; 15264 const { 15265 style 15266 } = this.#container; 15267 style.left = `${this.#position[0]}%`; 15268 style.top = `${this.#position[1]}%`; 15269 } 15270 #toggle() { 15271 this.#pinned = !this.#pinned; 15272 if (this.#pinned) { 15273 this.#show(); 15274 this.#container.addEventListener("click", this.#boundToggle); 15275 this.#container.addEventListener("keydown", this.#boundKeyDown); 15276 } else { 15277 this.#hide(); 15278 this.#container.removeEventListener("click", this.#boundToggle); 15279 this.#container.removeEventListener("keydown", this.#boundKeyDown); 15280 } 15281 } 15282 #show() { 15283 if (!this.#popup) { 15284 this.render(); 15285 } 15286 if (!this.isVisible) { 15287 this.#setPosition(); 15288 this.#container.hidden = false; 15289 this.#container.style.zIndex = parseInt(this.#container.style.zIndex) + 1000; 15290 } else if (this.#pinned) { 15291 this.#container.classList.add("focused"); 15292 } 15293 } 15294 #hide() { 15295 this.#container.classList.remove("focused"); 15296 if (this.#pinned || !this.isVisible) { 15297 return; 15298 } 15299 this.#container.hidden = true; 15300 this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000; 15301 } 15302 forceHide() { 15303 this.#wasVisible = this.isVisible; 15304 if (!this.#wasVisible) { 15305 return; 15306 } 15307 this.#container.hidden = true; 15308 } 15309 maybeShow() { 15310 if (!this.#wasVisible) { 15311 return; 15312 } 15313 if (!this.#popup) { 15314 this.#show(); 15315 } 15316 this.#wasVisible = false; 15317 this.#container.hidden = false; 15318 } 15319 get isVisible() { 15320 return this.#container.hidden === false; 15321 } 15322} 15323class FreeTextAnnotationElement extends AnnotationElement { 15324 constructor(parameters) { 15325 super(parameters, { 15326 isRenderable: true, 15327 ignoreBorder: true 15328 }); 15329 this.textContent = parameters.data.textContent; 15330 this.textPosition = parameters.data.textPosition; 15331 this.annotationEditorType = AnnotationEditorType.FREETEXT; 15332 } 15333 render() { 15334 this.container.classList.add("freeTextAnnotation"); 15335 if (this.textContent) { 15336 const content = document.createElement("div"); 15337 content.classList.add("annotationTextContent"); 15338 content.setAttribute("role", "comment"); 15339 for (const line of this.textContent) { 15340 const lineSpan = document.createElement("span"); 15341 lineSpan.textContent = line; 15342 content.append(lineSpan); 15343 } 15344 this.container.append(content); 15345 } 15346 if (!this.data.popupRef && this.hasPopupData) { 15347 this._createPopup(); 15348 } 15349 this._editOnDoubleClick(); 15350 return this.container; 15351 } 15352} 15353class LineAnnotationElement extends AnnotationElement { 15354 #line = null; 15355 constructor(parameters) { 15356 super(parameters, { 15357 isRenderable: true, 15358 ignoreBorder: true 15359 }); 15360 } 15361 render() { 15362 this.container.classList.add("lineAnnotation"); 15363 const data = this.data; 15364 const { 15365 width, 15366 height 15367 } = getRectDims(data.rect); 15368 const svg = this.svgFactory.create(width, height, true); 15369 const line = this.#line = this.svgFactory.createElement("svg:line"); 15370 line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]); 15371 line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]); 15372 line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]); 15373 line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]); 15374 line.setAttribute("stroke-width", data.borderStyle.width || 1); 15375 line.setAttribute("stroke", "transparent"); 15376 line.setAttribute("fill", "transparent"); 15377 svg.append(line); 15378 this.container.append(svg); 15379 if (!data.popupRef && this.hasPopupData) { 15380 this._createPopup(); 15381 } 15382 return this.container; 15383 } 15384 getElementsToTriggerPopup() { 15385 return this.#line; 15386 } 15387 addHighlightArea() { 15388 this.container.classList.add("highlightArea"); 15389 } 15390} 15391class SquareAnnotationElement extends AnnotationElement { 15392 #square = null; 15393 constructor(parameters) { 15394 super(parameters, { 15395 isRenderable: true, 15396 ignoreBorder: true 15397 }); 15398 } 15399 render() { 15400 this.container.classList.add("squareAnnotation"); 15401 const data = this.data; 15402 const { 15403 width, 15404 height 15405 } = getRectDims(data.rect); 15406 const svg = this.svgFactory.create(width, height, true); 15407 const borderWidth = data.borderStyle.width; 15408 const square = this.#square = this.svgFactory.createElement("svg:rect"); 15409 square.setAttribute("x", borderWidth / 2); 15410 square.setAttribute("y", borderWidth / 2); 15411 square.setAttribute("width", width - borderWidth); 15412 square.setAttribute("height", height - borderWidth); 15413 square.setAttribute("stroke-width", borderWidth || 1); 15414 square.setAttribute("stroke", "transparent"); 15415 square.setAttribute("fill", "transparent"); 15416 svg.append(square); 15417 this.container.append(svg); 15418 if (!data.popupRef && this.hasPopupData) { 15419 this._createPopup(); 15420 } 15421 return this.container; 15422 } 15423 getElementsToTriggerPopup() { 15424 return this.#square; 15425 } 15426 addHighlightArea() { 15427 this.container.classList.add("highlightArea"); 15428 } 15429} 15430class CircleAnnotationElement extends AnnotationElement { 15431 #circle = null; 15432 constructor(parameters) { 15433 super(parameters, { 15434 isRenderable: true, 15435 ignoreBorder: true 15436 }); 15437 } 15438 render() { 15439 this.container.classList.add("circleAnnotation"); 15440 const data = this.data; 15441 const { 15442 width, 15443 height 15444 } = getRectDims(data.rect); 15445 const svg = this.svgFactory.create(width, height, true); 15446 const borderWidth = data.borderStyle.width; 15447 const circle = this.#circle = this.svgFactory.createElement("svg:ellipse"); 15448 circle.setAttribute("cx", width / 2); 15449 circle.setAttribute("cy", height / 2); 15450 circle.setAttribute("rx", width / 2 - borderWidth / 2); 15451 circle.setAttribute("ry", height / 2 - borderWidth / 2); 15452 circle.setAttribute("stroke-width", borderWidth || 1); 15453 circle.setAttribute("stroke", "transparent"); 15454 circle.setAttribute("fill", "transparent"); 15455 svg.append(circle); 15456 this.container.append(svg); 15457 if (!data.popupRef && this.hasPopupData) { 15458 this._createPopup(); 15459 } 15460 return this.container; 15461 } 15462 getElementsToTriggerPopup() { 15463 return this.#circle; 15464 } 15465 addHighlightArea() { 15466 this.container.classList.add("highlightArea"); 15467 } 15468} 15469class PolylineAnnotationElement extends AnnotationElement { 15470 #polyline = null; 15471 constructor(parameters) { 15472 super(parameters, { 15473 isRenderable: true, 15474 ignoreBorder: true 15475 }); 15476 this.containerClassName = "polylineAnnotation"; 15477 this.svgElementName = "svg:polyline"; 15478 } 15479 render() { 15480 this.container.classList.add(this.containerClassName); 15481 const { 15482 data: { 15483 rect, 15484 vertices, 15485 borderStyle, 15486 popupRef 15487 } 15488 } = this; 15489 if (!vertices) { 15490 return this.container; 15491 } 15492 const { 15493 width, 15494 height 15495 } = getRectDims(rect); 15496 const svg = this.svgFactory.create(width, height, true); 15497 let points = []; 15498 for (let i = 0, ii = vertices.length; i < ii; i += 2) { 15499 const x = vertices[i] - rect[0]; 15500 const y = rect[3] - vertices[i + 1]; 15501 points.push(`${x},${y}`); 15502 } 15503 points = points.join(" "); 15504 const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName); 15505 polyline.setAttribute("points", points); 15506 polyline.setAttribute("stroke-width", borderStyle.width || 1); 15507 polyline.setAttribute("stroke", "transparent"); 15508 polyline.setAttribute("fill", "transparent"); 15509 svg.append(polyline); 15510 this.container.append(svg); 15511 if (!popupRef && this.hasPopupData) { 15512 this._createPopup(); 15513 } 15514 return this.container; 15515 } 15516 getElementsToTriggerPopup() { 15517 return this.#polyline; 15518 } 15519 addHighlightArea() { 15520 this.container.classList.add("highlightArea"); 15521 } 15522} 15523class PolygonAnnotationElement extends PolylineAnnotationElement { 15524 constructor(parameters) { 15525 super(parameters); 15526 this.containerClassName = "polygonAnnotation"; 15527 this.svgElementName = "svg:polygon"; 15528 } 15529} 15530class CaretAnnotationElement extends AnnotationElement { 15531 constructor(parameters) { 15532 super(parameters, { 15533 isRenderable: true, 15534 ignoreBorder: true 15535 }); 15536 } 15537 render() { 15538 this.container.classList.add("caretAnnotation"); 15539 if (!this.data.popupRef && this.hasPopupData) { 15540 this._createPopup(); 15541 } 15542 return this.container; 15543 } 15544} 15545class InkAnnotationElement extends AnnotationElement { 15546 #polylines = []; 15547 constructor(parameters) { 15548 super(parameters, { 15549 isRenderable: true, 15550 ignoreBorder: true 15551 }); 15552 this.containerClassName = "inkAnnotation"; 15553 this.svgElementName = "svg:polyline"; 15554 this.annotationEditorType = this.data.it === "InkHighlight" ? AnnotationEditorType.HIGHLIGHT : AnnotationEditorType.INK; 15555 } 15556 render() { 15557 this.container.classList.add(this.containerClassName); 15558 const { 15559 data: { 15560 rect, 15561 inkLists, 15562 borderStyle, 15563 popupRef 15564 } 15565 } = this; 15566 const { 15567 width, 15568 height 15569 } = getRectDims(rect); 15570 const svg = this.svgFactory.create(width, height, true); 15571 for (const inkList of inkLists) { 15572 let points = []; 15573 for (let i = 0, ii = inkList.length; i < ii; i += 2) { 15574 const x = inkList[i] - rect[0]; 15575 const y = rect[3] - inkList[i + 1]; 15576 points.push(`${x},${y}`); 15577 } 15578 points = points.join(" "); 15579 const polyline = this.svgFactory.createElement(this.svgElementName); 15580 this.#polylines.push(polyline); 15581 polyline.setAttribute("points", points); 15582 polyline.setAttribute("stroke-width", borderStyle.width || 1); 15583 polyline.setAttribute("stroke", "transparent"); 15584 polyline.setAttribute("fill", "transparent"); 15585 svg.append(polyline); 15586 } 15587 if (!popupRef && this.hasPopupData) { 15588 this._createPopup(); 15589 } 15590 this.container.append(svg); 15591 this._editOnDoubleClick(); 15592 return this.container; 15593 } 15594 getElementsToTriggerPopup() { 15595 return this.#polylines; 15596 } 15597 addHighlightArea() { 15598 this.container.classList.add("highlightArea"); 15599 } 15600} 15601class HighlightAnnotationElement extends AnnotationElement { 15602 constructor(parameters) { 15603 super(parameters, { 15604 isRenderable: true, 15605 ignoreBorder: true, 15606 createQuadrilaterals: true 15607 }); 15608 this.annotationEditorType = AnnotationEditorType.HIGHLIGHT; 15609 } 15610 render() { 15611 if (!this.data.popupRef && this.hasPopupData) { 15612 this._createPopup(); 15613 } 15614 this.container.classList.add("highlightAnnotation"); 15615 this._editOnDoubleClick(); 15616 return this.container; 15617 } 15618} 15619class UnderlineAnnotationElement extends AnnotationElement { 15620 constructor(parameters) { 15621 super(parameters, { 15622 isRenderable: true, 15623 ignoreBorder: true, 15624 createQuadrilaterals: true 15625 }); 15626 } 15627 render() { 15628 if (!this.data.popupRef && this.hasPopupData) { 15629 this._createPopup(); 15630 } 15631 this.container.classList.add("underlineAnnotation"); 15632 return this.container; 15633 } 15634} 15635class SquigglyAnnotationElement extends AnnotationElement { 15636 constructor(parameters) { 15637 super(parameters, { 15638 isRenderable: true, 15639 ignoreBorder: true, 15640 createQuadrilaterals: true 15641 }); 15642 } 15643 render() { 15644 if (!this.data.popupRef && this.hasPopupData) { 15645 this._createPopup(); 15646 } 15647 this.container.classList.add("squigglyAnnotation"); 15648 return this.container; 15649 } 15650} 15651class StrikeOutAnnotationElement extends AnnotationElement { 15652 constructor(parameters) { 15653 super(parameters, { 15654 isRenderable: true, 15655 ignoreBorder: true, 15656 createQuadrilaterals: true 15657 }); 15658 } 15659 render() { 15660 if (!this.data.popupRef && this.hasPopupData) { 15661 this._createPopup(); 15662 } 15663 this.container.classList.add("strikeoutAnnotation"); 15664 return this.container; 15665 } 15666} 15667class StampAnnotationElement extends AnnotationElement { 15668 constructor(parameters) { 15669 super(parameters, { 15670 isRenderable: true, 15671 ignoreBorder: true 15672 }); 15673 this.annotationEditorType = AnnotationEditorType.STAMP; 15674 } 15675 render() { 15676 this.container.classList.add("stampAnnotation"); 15677 this.container.setAttribute("role", "img"); 15678 if (!this.data.popupRef && this.hasPopupData) { 15679 this._createPopup(); 15680 } 15681 this._editOnDoubleClick(); 15682 return this.container; 15683 } 15684} 15685class FileAttachmentAnnotationElement extends AnnotationElement { 15686 #trigger = null; 15687 constructor(parameters) { 15688 super(parameters, { 15689 isRenderable: true 15690 }); 15691 const { 15692 file 15693 } = this.data; 15694 this.filename = file.filename; 15695 this.content = file.content; 15696 this.linkService.eventBus?.dispatch("fileattachmentannotation", { 15697 source: this, 15698 ...file 15699 }); 15700 } 15701 render() { 15702 this.container.classList.add("fileAttachmentAnnotation"); 15703 const { 15704 container, 15705 data 15706 } = this; 15707 let trigger; 15708 if (data.hasAppearance || data.fillAlpha === 0) { 15709 trigger = document.createElement("div"); 15710 } else { 15711 trigger = document.createElement("img"); 15712 trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`; 15713 if (data.fillAlpha && data.fillAlpha < 1) { 15714 trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`; 15715 } 15716 } 15717 trigger.addEventListener("dblclick", this.#download.bind(this)); 15718 this.#trigger = trigger; 15719 const { 15720 isMac 15721 } = util_FeatureTest.platform; 15722 container.addEventListener("keydown", evt => { 15723 if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) { 15724 this.#download(); 15725 } 15726 }); 15727 if (!data.popupRef && this.hasPopupData) { 15728 this._createPopup(); 15729 } else { 15730 trigger.classList.add("popupTriggerArea"); 15731 } 15732 container.append(trigger); 15733 return container; 15734 } 15735 getElementsToTriggerPopup() { 15736 return this.#trigger; 15737 } 15738 addHighlightArea() { 15739 this.container.classList.add("highlightArea"); 15740 } 15741 #download() { 15742 this.downloadManager?.openOrDownloadData(this.content, this.filename); 15743 } 15744} 15745class AnnotationLayer { 15746 #accessibilityManager = null; 15747 #annotationCanvasMap = null; 15748 #editableAnnotations = new Map(); 15749 #structTreeLayer = null; 15750 constructor({ 15751 div, 15752 accessibilityManager, 15753 annotationCanvasMap, 15754 annotationEditorUIManager, 15755 page, 15756 viewport, 15757 structTreeLayer 15758 }) { 15759 this.div = div; 15760 this.#accessibilityManager = accessibilityManager; 15761 this.#annotationCanvasMap = annotationCanvasMap; 15762 this.#structTreeLayer = structTreeLayer || null; 15763 this.page = page; 15764 this.viewport = viewport; 15765 this.zIndex = 0; 15766 this._annotationEditorUIManager = annotationEditorUIManager; 15767 } 15768 hasEditableAnnotations() { 15769 return this.#editableAnnotations.size > 0; 15770 } 15771 async #appendElement(element, id) { 15772 const contentElement = element.firstChild || element; 15773 const annotationId = contentElement.id = `${AnnotationPrefix}${id}`; 15774 const ariaAttributes = await this.#structTreeLayer?.getAriaAttributes(annotationId); 15775 if (ariaAttributes) { 15776 for (const [key, value] of ariaAttributes) { 15777 contentElement.setAttribute(key, value); 15778 } 15779 } 15780 this.div.append(element); 15781 this.#accessibilityManager?.moveElementInDOM(this.div, element, contentElement, false); 15782 } 15783 async render(params) { 15784 const { 15785 annotations 15786 } = params; 15787 const layer = this.div; 15788 setLayerDimensions(layer, this.viewport); 15789 const popupToElements = new Map();
15790 const elementParams = { 15791 data: null, 15792 layer, 15793 linkService: params.linkService, 15794 downloadManager: params.downloadManager, 15795 imageResourcesPath: params.imageResourcesPath || "", 15796 renderForms: params.renderForms !== false, 15797 svgFactory: new DOMSVGFactory(), 15798 annotationStorage: params.annotationStorage || new AnnotationStorage(), 15799 enableScripting: params.enableScripting === true, 15800 hasJSActions: params.hasJSActions, 15801 fieldObjects: params.fieldObjects, 15802 parent: this, 15803 elements: null 15804 }; 15805 for (const data of annotations) { 15806 if (data.noHTML) { 15807 continue; 15808 } 15809 const isPopupAnnotation = data.annotationType === AnnotationType.POPUP; 15810 if (!isPopupAnnotation) { 15811 const { 15812 width, 15813 height 15814 } = getRectDims(data.rect); 15815 if (width <= 0 || height <= 0) { 15816 continue; 15817 } 15818 } else { 15819 const elements = popupToElements.get(data.id); 15820 if (!elements) { 15821 continue; 15822 }
vendor: 4,533 bytes, lines 15823-15960
15823 elementParams.elements = elements; 15824 } 15825 elementParams.data = data; 15826 const element = AnnotationElementFactory.create(elementParams); 15827 if (!element.isRenderable) { 15828 continue; 15829 } 15830 if (!isPopupAnnotation && data.popupRef) { 15831 const elements = popupToElements.get(data.popupRef); 15832 if (!elements) { 15833 popupToElements.set(data.popupRef, [element]); 15834 } else { 15835 elements.push(element); 15836 } 15837 } 15838 const rendered = element.render(); 15839 if (data.hidden) { 15840 rendered.style.visibility = "hidden"; 15841 } 15842 await this.#appendElement(rendered, data.id); 15843 if (element._isEditable) { 15844 this.#editableAnnotations.set(element.data.id, element); 15845 this._annotationEditorUIManager?.renderAnnotationElement(element); 15846 } 15847 } 15848 this.#setAnnotationCanvasMap(); 15849 } 15850 update({ 15851 viewport 15852 }) { 15853 const layer = this.div; 15854 this.viewport = viewport; 15855 setLayerDimensions(layer, { 15856 rotation: viewport.rotation 15857 }); 15858 this.#setAnnotationCanvasMap(); 15859 layer.hidden = false; 15860 } 15861 #setAnnotationCanvasMap() { 15862 if (!this.#annotationCanvasMap) { 15863 return; 15864 } 15865 const layer = this.div; 15866 for (const [id, canvas] of this.#annotationCanvasMap) { 15867 const element = layer.querySelector(`[data-annotation-id="${id}"]`); 15868 if (!element) { 15869 continue; 15870 } 15871 canvas.className = "annotationContent"; 15872 const { 15873 firstChild 15874 } = element; 15875 if (!firstChild) { 15876 element.append(canvas); 15877 } else if (firstChild.nodeName === "CANVAS") { 15878 firstChild.replaceWith(canvas); 15879 } else if (!firstChild.classList.contains("annotationContent")) { 15880 firstChild.before(canvas); 15881 } else { 15882 firstChild.after(canvas); 15883 } 15884 } 15885 this.#annotationCanvasMap.clear(); 15886 } 15887 getEditableAnnotations() { 15888 return Array.from(this.#editableAnnotations.values()); 15889 } 15890 getEditableAnnotation(id) { 15891 return this.#editableAnnotations.get(id); 15892 } 15893} 15894 15895;// ./src/display/editor/freetext.js 15896 15897 15898 15899 15900const EOL_PATTERN = /\r\n?|\n/g; 15901class FreeTextEditor extends AnnotationEditor { 15902 #color; 15903 #content = ""; 15904 #editorDivId = `${this.id}-editor`; 15905 #editModeAC = null; 15906 #fontSize; 15907 static _freeTextDefaultContent = ""; 15908 static _internalPadding = 0; 15909 static _defaultColor = null; 15910 static _defaultFontSize = 10; 15911 static get _keyboardManager() { 15912 const proto = FreeTextEditor.prototype; 15913 const arrowChecker = self => self.isEmpty(); 15914 const small = AnnotationEditorUIManager.TRANSLATE_SMALL; 15915 const big = AnnotationEditorUIManager.TRANSLATE_BIG; 15916 return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, { 15917 bubbles: true 15918 }], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, { 15919 args: [-small, 0], 15920 checker: arrowChecker 15921 }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, { 15922 args: [-big, 0], 15923 checker: arrowChecker 15924 }], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, { 15925 args: [small, 0], 15926 checker: arrowChecker 15927 }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, { 15928 args: [big, 0], 15929 checker: arrowChecker 15930 }], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, { 15931 args: [0, -small], 15932 checker: arrowChecker 15933 }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, { 15934 args: [0, -big], 15935 checker: arrowChecker 15936 }], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, { 15937 args: [0, small], 15938 checker: arrowChecker 15939 }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, { 15940 args: [0, big], 15941 checker: arrowChecker 15942 }]])); 15943 } 15944 static _type = "freetext"; 15945 static _editorType = AnnotationEditorType.FREETEXT; 15946 constructor(params) { 15947 super({ 15948 ...params, 15949 name: "freeTextEditor" 15950 }); 15951 this.#color = params.color || FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor; 15952 this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize; 15953 } 15954 static initialize(l10n, uiManager) { 15955 AnnotationEditor.initialize(l10n, uiManager); 15956 const style = getComputedStyle(document.documentElement); 15957 this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding")); 15958 } 15959 static updateDefaultParams(type, value) { 15960 switch (type) {
vendor: 10,244 bytes, lines 15961-16290
15961 case AnnotationEditorParamsType.FREETEXT_SIZE: 15962 FreeTextEditor._defaultFontSize = value; 15963 break; 15964 case AnnotationEditorParamsType.FREETEXT_COLOR: 15965 FreeTextEditor._defaultColor = value; 15966 break; 15967 } 15968 } 15969 updateParams(type, value) { 15970 switch (type) { 15971 case AnnotationEditorParamsType.FREETEXT_SIZE: 15972 this.#updateFontSize(value); 15973 break; 15974 case AnnotationEditorParamsType.FREETEXT_COLOR: 15975 this.#updateColor(value); 15976 break; 15977 } 15978 } 15979 static get defaultPropertiesToUpdate() { 15980 return [[AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor]]; 15981 } 15982 get propertiesToUpdate() { 15983 return [[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, this.#color]]; 15984 } 15985 #updateFontSize(fontSize) { 15986 const setFontsize = size => { 15987 this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`; 15988 this.translate(0, -(size - this.#fontSize) * this.parentScale); 15989 this.#fontSize = size; 15990 this.#setEditorDimensions(); 15991 }; 15992 const savedFontsize = this.#fontSize; 15993 this.addCommands({ 15994 cmd: setFontsize.bind(this, fontSize), 15995 undo: setFontsize.bind(this, savedFontsize), 15996 post: this._uiManager.updateUI.bind(this._uiManager, this), 15997 mustExec: true, 15998 type: AnnotationEditorParamsType.FREETEXT_SIZE, 15999 overwriteIfSameType: true, 16000 keepUndo: true 16001 }); 16002 } 16003 #updateColor(color) { 16004 const setColor = col => { 16005 this.#color = this.editorDiv.style.color = col; 16006 }; 16007 const savedColor = this.#color; 16008 this.addCommands({ 16009 cmd: setColor.bind(this, color), 16010 undo: setColor.bind(this, savedColor), 16011 post: this._uiManager.updateUI.bind(this._uiManager, this), 16012 mustExec: true, 16013 type: AnnotationEditorParamsType.FREETEXT_COLOR, 16014 overwriteIfSameType: true, 16015 keepUndo: true 16016 }); 16017 } 16018 _translateEmpty(x, y) { 16019 this._uiManager.translateSelectedEditors(x, y, true); 16020 } 16021 getInitialTranslation() { 16022 const scale = this.parentScale; 16023 return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale]; 16024 } 16025 rebuild() { 16026 if (!this.parent) { 16027 return; 16028 } 16029 super.rebuild(); 16030 if (this.div === null) { 16031 return; 16032 } 16033 if (!this.isAttachedToDOM) { 16034 this.parent.add(this); 16035 } 16036 } 16037 enableEditMode() { 16038 if (this.isInEditMode()) { 16039 return; 16040 } 16041 this.parent.setEditingState(false); 16042 this.parent.updateToolbar(AnnotationEditorType.FREETEXT); 16043 super.enableEditMode(); 16044 this.overlayDiv.classList.remove("enabled"); 16045 this.editorDiv.contentEditable = true; 16046 this._isDraggable = false; 16047 this.div.removeAttribute("aria-activedescendant"); 16048 this.#editModeAC = new AbortController(); 16049 const signal = this._uiManager.combinedSignal(this.#editModeAC); 16050 this.editorDiv.addEventListener("keydown", this.editorDivKeydown.bind(this), { 16051 signal 16052 }); 16053 this.editorDiv.addEventListener("focus", this.editorDivFocus.bind(this), { 16054 signal 16055 }); 16056 this.editorDiv.addEventListener("blur", this.editorDivBlur.bind(this), { 16057 signal 16058 }); 16059 this.editorDiv.addEventListener("input", this.editorDivInput.bind(this), { 16060 signal 16061 }); 16062 this.editorDiv.addEventListener("paste", this.editorDivPaste.bind(this), { 16063 signal 16064 }); 16065 } 16066 disableEditMode() { 16067 if (!this.isInEditMode()) { 16068 return; 16069 } 16070 this.parent.setEditingState(true); 16071 super.disableEditMode(); 16072 this.overlayDiv.classList.add("enabled"); 16073 this.editorDiv.contentEditable = false; 16074 this.div.setAttribute("aria-activedescendant", this.#editorDivId); 16075 this._isDraggable = true; 16076 this.#editModeAC?.abort(); 16077 this.#editModeAC = null; 16078 this.div.focus({ 16079 preventScroll: true 16080 }); 16081 this.isEditing = false; 16082 this.parent.div.classList.add("freetextEditing"); 16083 } 16084 focusin(event) { 16085 if (!this._focusEventsAllowed) { 16086 return; 16087 } 16088 super.focusin(event); 16089 if (event.target !== this.editorDiv) { 16090 this.editorDiv.focus(); 16091 } 16092 } 16093 onceAdded() { 16094 if (this.width) { 16095 return; 16096 } 16097 this.enableEditMode(); 16098 this.editorDiv.focus(); 16099 if (this._initialOptions?.isCentered) { 16100 this.center(); 16101 } 16102 this._initialOptions = null; 16103 } 16104 isEmpty() { 16105 return !this.editorDiv || this.editorDiv.innerText.trim() === ""; 16106 } 16107 remove() { 16108 this.isEditing = false; 16109 if (this.parent) { 16110 this.parent.setEditingState(true); 16111 this.parent.div.classList.add("freetextEditing"); 16112 } 16113 super.remove(); 16114 } 16115 #extractText() { 16116 const buffer = []; 16117 this.editorDiv.normalize(); 16118 let prevChild = null; 16119 for (const child of this.editorDiv.childNodes) { 16120 if (prevChild?.nodeType === Node.TEXT_NODE && child.nodeName === "BR") { 16121 continue; 16122 } 16123 buffer.push(FreeTextEditor.#getNodeContent(child)); 16124 prevChild = child; 16125 } 16126 return buffer.join("\n"); 16127 } 16128 #setEditorDimensions() { 16129 const [parentWidth, parentHeight] = this.parentDimensions; 16130 let rect; 16131 if (this.isAttachedToDOM) { 16132 rect = this.div.getBoundingClientRect(); 16133 } else { 16134 const { 16135 currentLayer, 16136 div 16137 } = this; 16138 const savedDisplay = div.style.display; 16139 const savedVisibility = div.classList.contains("hidden"); 16140 div.classList.remove("hidden"); 16141 div.style.display = "hidden"; 16142 currentLayer.div.append(this.div); 16143 rect = div.getBoundingClientRect(); 16144 div.remove(); 16145 div.style.display = savedDisplay; 16146 div.classList.toggle("hidden", savedVisibility); 16147 } 16148 if (this.rotation % 180 === this.parentRotation % 180) { 16149 this.width = rect.width / parentWidth; 16150 this.height = rect.height / parentHeight; 16151 } else { 16152 this.width = rect.height / parentWidth; 16153 this.height = rect.width / parentHeight; 16154 } 16155 this.fixAndSetPosition(); 16156 } 16157 commit() { 16158 if (!this.isInEditMode()) { 16159 return; 16160 } 16161 super.commit(); 16162 this.disableEditMode(); 16163 const savedText = this.#content; 16164 const newText = this.#content = this.#extractText().trimEnd(); 16165 if (savedText === newText) { 16166 return; 16167 } 16168 const setText = text => { 16169 this.#content = text; 16170 if (!text) { 16171 this.remove(); 16172 return; 16173 } 16174 this.#setContent(); 16175 this._uiManager.rebuild(this); 16176 this.#setEditorDimensions(); 16177 }; 16178 this.addCommands({ 16179 cmd: () => { 16180 setText(newText); 16181 }, 16182 undo: () => { 16183 setText(savedText); 16184 }, 16185 mustExec: false 16186 }); 16187 this.#setEditorDimensions(); 16188 } 16189 shouldGetKeyboardEvents() { 16190 return this.isInEditMode(); 16191 } 16192 enterInEditMode() { 16193 this.enableEditMode(); 16194 this.editorDiv.focus(); 16195 } 16196 dblclick(event) { 16197 this.enterInEditMode(); 16198 } 16199 keydown(event) { 16200 if (event.target === this.div && event.key === "Enter") { 16201 this.enterInEditMode(); 16202 event.preventDefault(); 16203 } 16204 } 16205 editorDivKeydown(event) { 16206 FreeTextEditor._keyboardManager.exec(this, event); 16207 } 16208 editorDivFocus(event) { 16209 this.isEditing = true; 16210 } 16211 editorDivBlur(event) { 16212 this.isEditing = false; 16213 } 16214 editorDivInput(event) { 16215 this.parent.div.classList.toggle("freetextEditing", this.isEmpty()); 16216 } 16217 disableEditing() { 16218 this.editorDiv.setAttribute("role", "comment"); 16219 this.editorDiv.removeAttribute("aria-multiline"); 16220 } 16221 enableEditing() { 16222 this.editorDiv.setAttribute("role", "textbox"); 16223 this.editorDiv.setAttribute("aria-multiline", true); 16224 } 16225 render() { 16226 if (this.div) { 16227 return this.div; 16228 } 16229 let baseX, baseY; 16230 if (this.width) { 16231 baseX = this.x; 16232 baseY = this.y; 16233 } 16234 super.render(); 16235 this.editorDiv = document.createElement("div"); 16236 this.editorDiv.className = "internal"; 16237 this.editorDiv.setAttribute("id", this.#editorDivId); 16238 this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text2"); 16239 this.editorDiv.setAttribute("data-l10n-attrs", "default-content"); 16240 this.enableEditing(); 16241 this.editorDiv.contentEditable = true; 16242 const { 16243 style 16244 } = this.editorDiv; 16245 style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; 16246 style.color = this.#color; 16247 this.div.append(this.editorDiv); 16248 this.overlayDiv = document.createElement("div"); 16249 this.overlayDiv.classList.add("overlay", "enabled"); 16250 this.div.append(this.overlayDiv); 16251 bindEvents(this, this.div, ["dblclick", "keydown"]); 16252 if (this.width) { 16253 const [parentWidth, parentHeight] = this.parentDimensions; 16254 if (this.annotationElementId) { 16255 const { 16256 position 16257 } = this._initialData; 16258 let [tx, ty] = this.getInitialTranslation(); 16259 [tx, ty] = this.pageTranslationToScreen(tx, ty); 16260 const [pageWidth, pageHeight] = this.pageDimensions; 16261 const [pageX, pageY] = this.pageTranslation; 16262 let posX, posY; 16263 switch (this.rotation) { 16264 case 0: 16265 posX = baseX + (position[0] - pageX) / pageWidth; 16266 posY = baseY + this.height - (position[1] - pageY) / pageHeight; 16267 break; 16268 case 90: 16269 posX = baseX + (position[0] - pageX) / pageWidth; 16270 posY = baseY - (position[1] - pageY) / pageHeight; 16271 [tx, ty] = [ty, -tx]; 16272 break; 16273 case 180: 16274 posX = baseX - this.width + (position[0] - pageX) / pageWidth; 16275 posY = baseY - (position[1] - pageY) / pageHeight; 16276 [tx, ty] = [-tx, -ty]; 16277 break; 16278 case 270: 16279 posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth; 16280 posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight; 16281 [tx, ty] = [-ty, tx]; 16282 break; 16283 } 16284 this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); 16285 } else { 16286 this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); 16287 } 16288 this.#setContent(); 16289 this._isDraggable = true; 16290 this.editorDiv.contentEditable = false;
vendor: 46,341 bytes, lines 16291-17718
16291 } else { 16292 this._isDraggable = false; 16293 this.editorDiv.contentEditable = true; 16294 } 16295 return this.div; 16296 } 16297 static #getNodeContent(node) { 16298 return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, ""); 16299 } 16300 editorDivPaste(event) { 16301 const clipboardData = event.clipboardData || window.clipboardData; 16302 const { 16303 types 16304 } = clipboardData; 16305 if (types.length === 1 && types[0] === "text/plain") { 16306 return; 16307 } 16308 event.preventDefault(); 16309 const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n"); 16310 if (!paste) { 16311 return; 16312 } 16313 const selection = window.getSelection(); 16314 if (!selection.rangeCount) { 16315 return; 16316 } 16317 this.editorDiv.normalize(); 16318 selection.deleteFromDocument(); 16319 const range = selection.getRangeAt(0); 16320 if (!paste.includes("\n")) { 16321 range.insertNode(document.createTextNode(paste)); 16322 this.editorDiv.normalize(); 16323 selection.collapseToStart(); 16324 return; 16325 } 16326 const { 16327 startContainer, 16328 startOffset 16329 } = range; 16330 const bufferBefore = []; 16331 const bufferAfter = []; 16332 if (startContainer.nodeType === Node.TEXT_NODE) { 16333 const parent = startContainer.parentElement; 16334 bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, "")); 16335 if (parent !== this.editorDiv) { 16336 let buffer = bufferBefore; 16337 for (const child of this.editorDiv.childNodes) { 16338 if (child === parent) { 16339 buffer = bufferAfter; 16340 continue; 16341 } 16342 buffer.push(FreeTextEditor.#getNodeContent(child)); 16343 } 16344 } 16345 bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, "")); 16346 } else if (startContainer === this.editorDiv) { 16347 let buffer = bufferBefore; 16348 let i = 0; 16349 for (const child of this.editorDiv.childNodes) { 16350 if (i++ === startOffset) { 16351 buffer = bufferAfter; 16352 } 16353 buffer.push(FreeTextEditor.#getNodeContent(child)); 16354 } 16355 } 16356 this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`; 16357 this.#setContent(); 16358 const newRange = new Range(); 16359 let beforeLength = bufferBefore.reduce((acc, line) => acc + line.length, 0); 16360 for (const { 16361 firstChild 16362 } of this.editorDiv.childNodes) { 16363 if (firstChild.nodeType === Node.TEXT_NODE) { 16364 const length = firstChild.nodeValue.length; 16365 if (beforeLength <= length) { 16366 newRange.setStart(firstChild, beforeLength); 16367 newRange.setEnd(firstChild, beforeLength); 16368 break; 16369 } 16370 beforeLength -= length; 16371 } 16372 } 16373 selection.removeAllRanges(); 16374 selection.addRange(newRange); 16375 } 16376 #setContent() { 16377 this.editorDiv.replaceChildren(); 16378 if (!this.#content) { 16379 return; 16380 } 16381 for (const line of this.#content.split("\n")) { 16382 const div = document.createElement("div"); 16383 div.append(line ? document.createTextNode(line) : document.createElement("br")); 16384 this.editorDiv.append(div); 16385 } 16386 } 16387 #serializeContent() { 16388 return this.#content.replaceAll("\xa0", " "); 16389 } 16390 static #deserializeContent(content) { 16391 return content.replaceAll(" ", "\xa0"); 16392 } 16393 get contentDiv() { 16394 return this.editorDiv; 16395 } 16396 static async deserialize(data, parent, uiManager) { 16397 let initialData = null; 16398 if (data instanceof FreeTextAnnotationElement) { 16399 const { 16400 data: { 16401 defaultAppearanceData: { 16402 fontSize, 16403 fontColor 16404 }, 16405 rect, 16406 rotation, 16407 id, 16408 popupRef 16409 }, 16410 textContent, 16411 textPosition, 16412 parent: { 16413 page: { 16414 pageNumber 16415 } 16416 } 16417 } = data; 16418 if (!textContent || textContent.length === 0) { 16419 return null; 16420 } 16421 initialData = data = { 16422 annotationType: AnnotationEditorType.FREETEXT, 16423 color: Array.from(fontColor), 16424 fontSize, 16425 value: textContent.join("\n"), 16426 position: textPosition, 16427 pageIndex: pageNumber - 1, 16428 rect: rect.slice(0), 16429 rotation, 16430 id, 16431 deleted: false, 16432 popupRef 16433 }; 16434 } 16435 const editor = await super.deserialize(data, parent, uiManager); 16436 editor.#fontSize = data.fontSize; 16437 editor.#color = Util.makeHexColor(...data.color); 16438 editor.#content = FreeTextEditor.#deserializeContent(data.value); 16439 editor.annotationElementId = data.id || null; 16440 editor._initialData = initialData; 16441 return editor; 16442 } 16443 serialize(isForCopying = false) { 16444 if (this.isEmpty()) { 16445 return null; 16446 } 16447 if (this.deleted) { 16448 return this.serializeDeleted(); 16449 } 16450 const padding = FreeTextEditor._internalPadding * this.parentScale; 16451 const rect = this.getRect(padding, padding); 16452 const color = AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.#color); 16453 const serialized = { 16454 annotationType: AnnotationEditorType.FREETEXT, 16455 color, 16456 fontSize: this.#fontSize, 16457 value: this.#serializeContent(), 16458 pageIndex: this.pageIndex, 16459 rect, 16460 rotation: this.rotation, 16461 structTreeParentId: this._structTreeParentId 16462 }; 16463 if (isForCopying) { 16464 return serialized; 16465 } 16466 if (this.annotationElementId && !this.#hasElementChanged(serialized)) { 16467 return null; 16468 } 16469 serialized.id = this.annotationElementId; 16470 return serialized; 16471 } 16472 #hasElementChanged(serialized) { 16473 const { 16474 value, 16475 fontSize, 16476 color, 16477 pageIndex 16478 } = this._initialData; 16479 return this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex; 16480 } 16481 renderAnnotationElement(annotation) { 16482 const content = super.renderAnnotationElement(annotation); 16483 if (this.deleted) { 16484 return content; 16485 } 16486 const { 16487 style 16488 } = content; 16489 style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`; 16490 style.color = this.#color; 16491 content.replaceChildren(); 16492 for (const line of this.#content.split("\n")) { 16493 const div = document.createElement("div"); 16494 div.append(line ? document.createTextNode(line) : document.createElement("br")); 16495 content.append(div); 16496 } 16497 const padding = FreeTextEditor._internalPadding * this.parentScale; 16498 annotation.updateEdited({ 16499 rect: this.getRect(padding, padding), 16500 popupContent: this.#content 16501 }); 16502 return content; 16503 } 16504 resetAnnotationElement(annotation) { 16505 super.resetAnnotationElement(annotation); 16506 annotation.resetEdited(); 16507 } 16508} 16509 16510;// ./src/display/editor/drawers/outline.js 16511 16512class Outline { 16513 toSVGPath() { 16514 unreachable("Abstract method `toSVGPath` must be implemented."); 16515 } 16516 get box() { 16517 unreachable("Abstract getter `box` must be implemented."); 16518 } 16519 serialize(_bbox, _rotation) { 16520 unreachable("Abstract method `serialize` must be implemented."); 16521 } 16522 get classNamesForDrawing() { 16523 unreachable("Abstract getter `classNamesForDrawing` must be implemented."); 16524 } 16525 get classNamesForOutlining() { 16526 unreachable("Abstract getter `classNamesForOutlining` must be implemented."); 16527 } 16528 get mustRemoveSelfIntersections() { 16529 return false; 16530 } 16531} 16532 16533;// ./src/display/editor/drawers/freedraw.js 16534 16535 16536class FreeDrawOutliner { 16537 #box; 16538 #bottom = []; 16539 #innerMargin; 16540 #isLTR; 16541 #top = []; 16542 #last = new Float64Array(18); 16543 #lastX; 16544 #lastY; 16545 #min; 16546 #min_dist; 16547 #scaleFactor; 16548 #thickness; 16549 #points = []; 16550 static #MIN_DIST = 8; 16551 static #MIN_DIFF = 2; 16552 static #MIN = FreeDrawOutliner.#MIN_DIST + FreeDrawOutliner.#MIN_DIFF; 16553 constructor({ 16554 x, 16555 y 16556 }, box, scaleFactor, thickness, isLTR, innerMargin = 0) { 16557 this.#box = box; 16558 this.#thickness = thickness * scaleFactor; 16559 this.#isLTR = isLTR; 16560 this.#last.set([NaN, NaN, NaN, NaN, x, y], 6); 16561 this.#innerMargin = innerMargin; 16562 this.#min_dist = FreeDrawOutliner.#MIN_DIST * scaleFactor; 16563 this.#min = FreeDrawOutliner.#MIN * scaleFactor; 16564 this.#scaleFactor = scaleFactor; 16565 this.#points.push(x, y); 16566 } 16567 isEmpty() { 16568 return isNaN(this.#last[8]); 16569 } 16570 #getLastCoords() { 16571 const lastTop = this.#last.subarray(4, 6); 16572 const lastBottom = this.#last.subarray(16, 18); 16573 const [x, y, width, height] = this.#box; 16574 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]; 16575 } 16576 add({ 16577 x, 16578 y 16579 }) { 16580 this.#lastX = x; 16581 this.#lastY = y; 16582 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 16583 let [x1, y1, x2, y2] = this.#last.subarray(8, 12); 16584 const diffX = x - x2; 16585 const diffY = y - y2; 16586 const d = Math.hypot(diffX, diffY); 16587 if (d < this.#min) { 16588 return false; 16589 } 16590 const diffD = d - this.#min_dist; 16591 const K = diffD / d; 16592 const shiftX = K * diffX; 16593 const shiftY = K * diffY; 16594 let x0 = x1; 16595 let y0 = y1; 16596 x1 = x2; 16597 y1 = y2; 16598 x2 += shiftX; 16599 y2 += shiftY; 16600 this.#points?.push(x, y); 16601 const nX = -shiftY / diffD; 16602 const nY = shiftX / diffD; 16603 const thX = nX * this.#thickness; 16604 const thY = nY * this.#thickness; 16605 this.#last.set(this.#last.subarray(2, 8), 0); 16606 this.#last.set([x2 + thX, y2 + thY], 4); 16607 this.#last.set(this.#last.subarray(14, 18), 12); 16608 this.#last.set([x2 - thX, y2 - thY], 16); 16609 if (isNaN(this.#last[6])) { 16610 if (this.#top.length === 0) { 16611 this.#last.set([x1 + thX, y1 + thY], 2); 16612 this.#top.push(NaN, NaN, NaN, NaN, (x1 + thX - layerX) / layerWidth, (y1 + thY - layerY) / layerHeight); 16613 this.#last.set([x1 - thX, y1 - thY], 14); 16614 this.#bottom.push(NaN, NaN, NaN, NaN, (x1 - thX - layerX) / layerWidth, (y1 - thY - layerY) / layerHeight); 16615 } 16616 this.#last.set([x0, y0, x1, y1, x2, y2], 6); 16617 return !this.isEmpty(); 16618 } 16619 this.#last.set([x0, y0, x1, y1, x2, y2], 6); 16620 const angle = Math.abs(Math.atan2(y0 - y1, x0 - x1) - Math.atan2(shiftY, shiftX)); 16621 if (angle < Math.PI / 2) { 16622 [x1, y1, x2, y2] = this.#last.subarray(2, 6); 16623 this.#top.push(NaN, NaN, NaN, NaN, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); 16624 [x1, y1, x0, y0] = this.#last.subarray(14, 18); 16625 this.#bottom.push(NaN, NaN, NaN, NaN, ((x0 + x1) / 2 - layerX) / layerWidth, ((y0 + y1) / 2 - layerY) / layerHeight); 16626 return true; 16627 } 16628 [x0, y0, x1, y1, x2, y2] = this.#last.subarray(0, 6); 16629 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); 16630 [x2, y2, x1, y1, x0, y0] = this.#last.subarray(12, 18); 16631 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); 16632 return true; 16633 } 16634 toSVGPath() { 16635 if (this.isEmpty()) { 16636 return ""; 16637 } 16638 const top = this.#top; 16639 const bottom = this.#bottom; 16640 if (isNaN(this.#last[6]) && !this.isEmpty()) { 16641 return this.#toSVGPathTwoPoints(); 16642 } 16643 const buffer = []; 16644 buffer.push(`M${top[4]} ${top[5]}`); 16645 for (let i = 6; i < top.length; i += 6) { 16646 if (isNaN(top[i])) { 16647 buffer.push(`L${top[i + 4]} ${top[i + 5]}`); 16648 } else { 16649 buffer.push(`C${top[i]} ${top[i + 1]} ${top[i + 2]} ${top[i + 3]} ${top[i + 4]} ${top[i + 5]}`); 16650 } 16651 } 16652 this.#toSVGPathEnd(buffer); 16653 for (let i = bottom.length - 6; i >= 6; i -= 6) { 16654 if (isNaN(bottom[i])) { 16655 buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`); 16656 } else { 16657 buffer.push(`C${bottom[i]} ${bottom[i + 1]} ${bottom[i + 2]} ${bottom[i + 3]} ${bottom[i + 4]} ${bottom[i + 5]}`); 16658 } 16659 } 16660 this.#toSVGPathStart(buffer); 16661 return buffer.join(" "); 16662 } 16663 #toSVGPathTwoPoints() { 16664 const [x, y, width, height] = this.#box; 16665 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 16666 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`; 16667 } 16668 #toSVGPathStart(buffer) { 16669 const bottom = this.#bottom; 16670 buffer.push(`L${bottom[4]} ${bottom[5]} Z`); 16671 } 16672 #toSVGPathEnd(buffer) { 16673 const [x, y, width, height] = this.#box; 16674 const lastTop = this.#last.subarray(4, 6); 16675 const lastBottom = this.#last.subarray(16, 18); 16676 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 16677 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}`); 16678 } 16679 newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { 16680 return new FreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR); 16681 } 16682 getOutlines() { 16683 const top = this.#top; 16684 const bottom = this.#bottom; 16685 const last = this.#last; 16686 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 16687 const points = new Float64Array((this.#points?.length ?? 0) + 2); 16688 for (let i = 0, ii = points.length - 2; i < ii; i += 2) { 16689 points[i] = (this.#points[i] - layerX) / layerWidth; 16690 points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight; 16691 } 16692 points[points.length - 2] = (this.#lastX - layerX) / layerWidth; 16693 points[points.length - 1] = (this.#lastY - layerY) / layerHeight; 16694 if (isNaN(last[6]) && !this.isEmpty()) { 16695 return this.#getOutlineTwoPoints(points); 16696 } 16697 const outline = new Float64Array(this.#top.length + 24 + this.#bottom.length); 16698 let N = top.length; 16699 for (let i = 0; i < N; i += 2) { 16700 if (isNaN(top[i])) { 16701 outline[i] = outline[i + 1] = NaN; 16702 continue; 16703 } 16704 outline[i] = top[i]; 16705 outline[i + 1] = top[i + 1]; 16706 } 16707 N = this.#getOutlineEnd(outline, N); 16708 for (let i = bottom.length - 6; i >= 6; i -= 6) { 16709 for (let j = 0; j < 6; j += 2) { 16710 if (isNaN(bottom[i + j])) { 16711 outline[N] = outline[N + 1] = NaN; 16712 N += 2; 16713 continue; 16714 } 16715 outline[N] = bottom[i + j]; 16716 outline[N + 1] = bottom[i + j + 1]; 16717 N += 2; 16718 } 16719 } 16720 this.#getOutlineStart(outline, N); 16721 return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); 16722 } 16723 #getOutlineTwoPoints(points) { 16724 const last = this.#last; 16725 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 16726 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 16727 const outline = new Float64Array(36); 16728 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); 16729 return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); 16730 } 16731 #getOutlineStart(outline, pos) { 16732 const bottom = this.#bottom; 16733 outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], pos); 16734 return pos += 6; 16735 } 16736 #getOutlineEnd(outline, pos) { 16737 const lastTop = this.#last.subarray(4, 6); 16738 const lastBottom = this.#last.subarray(16, 18); 16739 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 16740 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 16741 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); 16742 return pos += 24; 16743 } 16744} 16745class FreeDrawOutline extends Outline { 16746 #box; 16747 #bbox = null; 16748 #innerMargin; 16749 #isLTR; 16750 #points; 16751 #scaleFactor; 16752 #outline; 16753 constructor(outline, points, box, scaleFactor, innerMargin, isLTR) { 16754 super(); 16755 this.#outline = outline; 16756 this.#points = points; 16757 this.#box = box; 16758 this.#scaleFactor = scaleFactor; 16759 this.#innerMargin = innerMargin; 16760 this.#isLTR = isLTR; 16761 this.#computeMinMax(isLTR); 16762 const { 16763 x, 16764 y, 16765 width, 16766 height 16767 } = this.#bbox; 16768 for (let i = 0, ii = outline.length; i < ii; i += 2) { 16769 outline[i] = (outline[i] - x) / width; 16770 outline[i + 1] = (outline[i + 1] - y) / height; 16771 } 16772 for (let i = 0, ii = points.length; i < ii; i += 2) { 16773 points[i] = (points[i] - x) / width; 16774 points[i + 1] = (points[i + 1] - y) / height; 16775 } 16776 } 16777 toSVGPath() { 16778 const buffer = [`M${this.#outline[4]} ${this.#outline[5]}`]; 16779 for (let i = 6, ii = this.#outline.length; i < ii; i += 6) { 16780 if (isNaN(this.#outline[i])) { 16781 buffer.push(`L${this.#outline[i + 4]} ${this.#outline[i + 5]}`); 16782 continue; 16783 } 16784 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]}`); 16785 } 16786 buffer.push("Z"); 16787 return buffer.join(" "); 16788 } 16789 serialize([blX, blY, trX, trY], rotation) { 16790 const width = trX - blX; 16791 const height = trY - blY; 16792 let outline; 16793 let points; 16794 switch (rotation) { 16795 case 0: 16796 outline = this.#rescale(this.#outline, blX, trY, width, -height); 16797 points = this.#rescale(this.#points, blX, trY, width, -height); 16798 break; 16799 case 90: 16800 outline = this.#rescaleAndSwap(this.#outline, blX, blY, width, height); 16801 points = this.#rescaleAndSwap(this.#points, blX, blY, width, height); 16802 break; 16803 case 180: 16804 outline = this.#rescale(this.#outline, trX, blY, -width, height); 16805 points = this.#rescale(this.#points, trX, blY, -width, height); 16806 break; 16807 case 270: 16808 outline = this.#rescaleAndSwap(this.#outline, trX, trY, -width, -height); 16809 points = this.#rescaleAndSwap(this.#points, trX, trY, -width, -height); 16810 break; 16811 } 16812 return { 16813 outline: Array.from(outline), 16814 points: [Array.from(points)] 16815 }; 16816 } 16817 #rescale(src, tx, ty, sx, sy) { 16818 const dest = new Float64Array(src.length); 16819 for (let i = 0, ii = src.length; i < ii; i += 2) { 16820 dest[i] = tx + src[i] * sx; 16821 dest[i + 1] = ty + src[i + 1] * sy; 16822 } 16823 return dest; 16824 } 16825 #rescaleAndSwap(src, tx, ty, sx, sy) { 16826 const dest = new Float64Array(src.length); 16827 for (let i = 0, ii = src.length; i < ii; i += 2) { 16828 dest[i] = tx + src[i + 1] * sx; 16829 dest[i + 1] = ty + src[i] * sy; 16830 } 16831 return dest; 16832 } 16833 #computeMinMax(isLTR) { 16834 const outline = this.#outline; 16835 let lastX = outline[4]; 16836 let lastY = outline[5]; 16837 let minX = lastX; 16838 let minY = lastY; 16839 let maxX = lastX; 16840 let maxY = lastY; 16841 let lastPointX = lastX; 16842 let lastPointY = lastY; 16843 const ltrCallback = isLTR ? Math.max : Math.min; 16844 for (let i = 6, ii = outline.length; i < ii; i += 6) { 16845 if (isNaN(outline[i])) { 16846 minX = Math.min(minX, outline[i + 4]); 16847 minY = Math.min(minY, outline[i + 5]); 16848 maxX = Math.max(maxX, outline[i + 4]); 16849 maxY = Math.max(maxY, outline[i + 5]); 16850 if (lastPointY < outline[i + 5]) { 16851 lastPointX = outline[i + 4]; 16852 lastPointY = outline[i + 5]; 16853 } else if (lastPointY === outline[i + 5]) { 16854 lastPointX = ltrCallback(lastPointX, outline[i + 4]); 16855 } 16856 } else { 16857 const bbox = Util.bezierBoundingBox(lastX, lastY, ...outline.slice(i, i + 6)); 16858 minX = Math.min(minX, bbox[0]); 16859 minY = Math.min(minY, bbox[1]); 16860 maxX = Math.max(maxX, bbox[2]); 16861 maxY = Math.max(maxY, bbox[3]); 16862 if (lastPointY < bbox[3]) { 16863 lastPointX = bbox[2]; 16864 lastPointY = bbox[3]; 16865 } else if (lastPointY === bbox[3]) { 16866 lastPointX = ltrCallback(lastPointX, bbox[2]); 16867 } 16868 } 16869 lastX = outline[i + 4]; 16870 lastY = outline[i + 5]; 16871 } 16872 const x = minX - this.#innerMargin, 16873 y = minY - this.#innerMargin, 16874 width = maxX - minX + 2 * this.#innerMargin, 16875 height = maxY - minY + 2 * this.#innerMargin; 16876 this.#bbox = { 16877 x, 16878 y, 16879 width, 16880 height, 16881 lastPoint: [lastPointX, lastPointY] 16882 }; 16883 } 16884 get box() { 16885 return this.#bbox; 16886 } 16887 newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { 16888 return new FreeDrawOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); 16889 } 16890 getNewOutline(thickness, innerMargin) { 16891 const { 16892 x, 16893 y, 16894 width, 16895 height 16896 } = this.#bbox; 16897 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 16898 const sx = width * layerWidth; 16899 const sy = height * layerHeight; 16900 const tx = x * layerWidth + layerX; 16901 const ty = y * layerHeight + layerY; 16902 const outliner = this.newOutliner({ 16903 x: this.#points[0] * sx + tx, 16904 y: this.#points[1] * sy + ty 16905 }, this.#box, this.#scaleFactor, thickness, this.#isLTR, innerMargin ?? this.#innerMargin); 16906 for (let i = 2; i < this.#points.length; i += 2) { 16907 outliner.add({ 16908 x: this.#points[i] * sx + tx, 16909 y: this.#points[i + 1] * sy + ty 16910 }); 16911 } 16912 return outliner.getOutlines(); 16913 } 16914 get mustRemoveSelfIntersections() { 16915 return true; 16916 } 16917} 16918 16919;// ./src/display/editor/drawers/highlight.js 16920 16921 16922class HighlightOutliner { 16923 #box; 16924 #verticalEdges = []; 16925 #intervals = []; 16926 constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) { 16927 let minX = Infinity; 16928 let maxX = -Infinity; 16929 let minY = Infinity; 16930 let maxY = -Infinity; 16931 const NUMBER_OF_DIGITS = 4; 16932 const EPSILON = 10 ** -NUMBER_OF_DIGITS; 16933 for (const { 16934 x, 16935 y, 16936 width, 16937 height 16938 } of boxes) { 16939 const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON; 16940 const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON; 16941 const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON; 16942 const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON; 16943 const left = [x1, y1, y2, true]; 16944 const right = [x2, y1, y2, false]; 16945 this.#verticalEdges.push(left, right); 16946 minX = Math.min(minX, x1); 16947 maxX = Math.max(maxX, x2); 16948 minY = Math.min(minY, y1); 16949 maxY = Math.max(maxY, y2); 16950 } 16951 const bboxWidth = maxX - minX + 2 * innerMargin; 16952 const bboxHeight = maxY - minY + 2 * innerMargin; 16953 const shiftedMinX = minX - innerMargin; 16954 const shiftedMinY = minY - innerMargin; 16955 const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2); 16956 const lastPoint = [lastEdge[0], lastEdge[2]]; 16957 for (const edge of this.#verticalEdges) { 16958 const [x, y1, y2] = edge; 16959 edge[0] = (x - shiftedMinX) / bboxWidth; 16960 edge[1] = (y1 - shiftedMinY) / bboxHeight; 16961 edge[2] = (y2 - shiftedMinY) / bboxHeight; 16962 } 16963 this.#box = { 16964 x: shiftedMinX, 16965 y: shiftedMinY, 16966 width: bboxWidth, 16967 height: bboxHeight, 16968 lastPoint 16969 }; 16970 } 16971 getOutlines() { 16972 this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]); 16973 const outlineVerticalEdges = []; 16974 for (const edge of this.#verticalEdges) { 16975 if (edge[3]) { 16976 outlineVerticalEdges.push(...this.#breakEdge(edge)); 16977 this.#insert(edge); 16978 } else { 16979 this.#remove(edge); 16980 outlineVerticalEdges.push(...this.#breakEdge(edge)); 16981 } 16982 } 16983 return this.#getOutlines(outlineVerticalEdges); 16984 } 16985 #getOutlines(outlineVerticalEdges) { 16986 const edges = []; 16987 const allEdges = new Set(); 16988 for (const edge of outlineVerticalEdges) { 16989 const [x, y1, y2] = edge; 16990 edges.push([x, y1, edge], [x, y2, edge]); 16991 } 16992 edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]); 16993 for (let i = 0, ii = edges.length; i < ii; i += 2) { 16994 const edge1 = edges[i][2]; 16995 const edge2 = edges[i + 1][2]; 16996 edge1.push(edge2); 16997 edge2.push(edge1); 16998 allEdges.add(edge1); 16999 allEdges.add(edge2); 17000 } 17001 const outlines = []; 17002 let outline; 17003 while (allEdges.size > 0) { 17004 const edge = allEdges.values().next().value; 17005 let [x, y1, y2, edge1, edge2] = edge; 17006 allEdges.delete(edge); 17007 let lastPointX = x; 17008 let lastPointY = y1; 17009 outline = [x, y2]; 17010 outlines.push(outline); 17011 while (true) { 17012 let e; 17013 if (allEdges.has(edge1)) { 17014 e = edge1; 17015 } else if (allEdges.has(edge2)) { 17016 e = edge2; 17017 } else { 17018 break; 17019 } 17020 allEdges.delete(e); 17021 [x, y1, y2, edge1, edge2] = e; 17022 if (lastPointX !== x) { 17023 outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2); 17024 lastPointX = x; 17025 } 17026 lastPointY = lastPointY === y1 ? y2 : y1; 17027 } 17028 outline.push(lastPointX, lastPointY); 17029 } 17030 return new HighlightOutline(outlines, this.#box); 17031 } 17032 #binarySearch(y) { 17033 const array = this.#intervals; 17034 let start = 0; 17035 let end = array.length - 1; 17036 while (start <= end) { 17037 const middle = start + end >> 1; 17038 const y1 = array[middle][0]; 17039 if (y1 === y) { 17040 return middle; 17041 } 17042 if (y1 < y) { 17043 start = middle + 1; 17044 } else { 17045 end = middle - 1; 17046 } 17047 } 17048 return end + 1; 17049 } 17050 #insert([, y1, y2]) { 17051 const index = this.#binarySearch(y1); 17052 this.#intervals.splice(index, 0, [y1, y2]); 17053 } 17054 #remove([, y1, y2]) { 17055 const index = this.#binarySearch(y1); 17056 for (let i = index; i < this.#intervals.length; i++) { 17057 const [start, end] = this.#intervals[i]; 17058 if (start !== y1) { 17059 break; 17060 } 17061 if (start === y1 && end === y2) { 17062 this.#intervals.splice(i, 1); 17063 return; 17064 } 17065 } 17066 for (let i = index - 1; i >= 0; i--) { 17067 const [start, end] = this.#intervals[i]; 17068 if (start !== y1) { 17069 break; 17070 } 17071 if (start === y1 && end === y2) { 17072 this.#intervals.splice(i, 1); 17073 return; 17074 } 17075 } 17076 } 17077 #breakEdge(edge) { 17078 const [x, y1, y2] = edge; 17079 const results = [[x, y1, y2]]; 17080 const index = this.#binarySearch(y2); 17081 for (let i = 0; i < index; i++) { 17082 const [start, end] = this.#intervals[i]; 17083 for (let j = 0, jj = results.length; j < jj; j++) { 17084 const [, y3, y4] = results[j]; 17085 if (end <= y3 || y4 <= start) { 17086 continue; 17087 } 17088 if (y3 >= start) { 17089 if (y4 > end) { 17090 results[j][1] = end; 17091 } else { 17092 if (jj === 1) { 17093 return []; 17094 } 17095 results.splice(j, 1); 17096 j--; 17097 jj--; 17098 } 17099 continue; 17100 } 17101 results[j][2] = start; 17102 if (y4 > end) { 17103 results.push([x, end, y4]); 17104 } 17105 } 17106 } 17107 return results; 17108 } 17109} 17110class HighlightOutline extends Outline { 17111 #box; 17112 #outlines; 17113 constructor(outlines, box) { 17114 super(); 17115 this.#outlines = outlines; 17116 this.#box = box; 17117 } 17118 toSVGPath() { 17119 const buffer = []; 17120 for (const polygon of this.#outlines) { 17121 let [prevX, prevY] = polygon; 17122 buffer.push(`M${prevX} ${prevY}`); 17123 for (let i = 2; i < polygon.length; i += 2) { 17124 const x = polygon[i]; 17125 const y = polygon[i + 1]; 17126 if (x === prevX) { 17127 buffer.push(`V${y}`); 17128 prevY = y; 17129 } else if (y === prevY) { 17130 buffer.push(`H${x}`); 17131 prevX = x; 17132 } 17133 } 17134 buffer.push("Z"); 17135 } 17136 return buffer.join(" "); 17137 } 17138 serialize([blX, blY, trX, trY], _rotation) { 17139 const outlines = []; 17140 const width = trX - blX; 17141 const height = trY - blY; 17142 for (const outline of this.#outlines) { 17143 const points = new Array(outline.length); 17144 for (let i = 0; i < outline.length; i += 2) { 17145 points[i] = blX + outline[i] * width; 17146 points[i + 1] = trY - outline[i + 1] * height; 17147 } 17148 outlines.push(points); 17149 } 17150 return outlines; 17151 } 17152 get box() { 17153 return this.#box; 17154 } 17155 get classNamesForDrawing() { 17156 return ["highlight"]; 17157 } 17158 get classNamesForOutlining() { 17159 return ["highlightOutline"]; 17160 } 17161} 17162class FreeHighlightOutliner extends FreeDrawOutliner { 17163 newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { 17164 return new FreeHighlightOutline(outline, points, box, scaleFactor, innerMargin, isLTR); 17165 } 17166 get classNamesForDrawing() { 17167 return ["highlight", "free"]; 17168 } 17169} 17170class FreeHighlightOutline extends FreeDrawOutline { 17171 get classNamesForDrawing() { 17172 return ["highlight", "free"]; 17173 } 17174 get classNamesForOutlining() { 17175 return ["highlightOutline", "free"]; 17176 } 17177 newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { 17178 return new FreeHighlightOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); 17179 } 17180} 17181 17182;// ./src/display/editor/color_picker.js 17183 17184 17185 17186class ColorPicker { 17187 #button = null; 17188 #buttonSwatch = null; 17189 #defaultColor; 17190 #dropdown = null; 17191 #dropdownWasFromKeyboard = false; 17192 #isMainColorPicker = false; 17193 #editor = null; 17194 #eventBus; 17195 #openDropdownAC = null; 17196 #uiManager = null; 17197 #type; 17198 static #l10nColor = null; 17199 static get _keyboardManager() { 17200 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]])); 17201 } 17202 constructor({ 17203 editor = null, 17204 uiManager = null 17205 }) { 17206 if (editor) { 17207 this.#isMainColorPicker = false; 17208 this.#type = AnnotationEditorParamsType.HIGHLIGHT_COLOR; 17209 this.#editor = editor; 17210 } else { 17211 this.#isMainColorPicker = true; 17212 this.#type = AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR; 17213 } 17214 this.#uiManager = editor?._uiManager || uiManager; 17215 this.#eventBus = this.#uiManager._eventBus; 17216 this.#defaultColor = editor?.color || this.#uiManager?.highlightColors.values().next().value || "#FFFF98"; 17217 ColorPicker.#l10nColor ||= Object.freeze({ 17218 blue: "pdfjs-editor-colorpicker-blue", 17219 green: "pdfjs-editor-colorpicker-green", 17220 pink: "pdfjs-editor-colorpicker-pink", 17221 red: "pdfjs-editor-colorpicker-red", 17222 yellow: "pdfjs-editor-colorpicker-yellow" 17223 }); 17224 } 17225 renderButton() { 17226 const button = this.#button = document.createElement("button"); 17227 button.className = "colorPicker"; 17228 button.tabIndex = "0"; 17229 button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button"); 17230 button.setAttribute("aria-haspopup", true); 17231 const signal = this.#uiManager._signal; 17232 button.addEventListener("click", this.#openDropdown.bind(this), { 17233 signal 17234 }); 17235 button.addEventListener("keydown", this.#keyDown.bind(this), { 17236 signal 17237 }); 17238 const swatch = this.#buttonSwatch = document.createElement("span"); 17239 swatch.className = "swatch"; 17240 swatch.setAttribute("aria-hidden", true); 17241 swatch.style.backgroundColor = this.#defaultColor; 17242 button.append(swatch); 17243 return button; 17244 } 17245 renderMainDropdown() { 17246 const dropdown = this.#dropdown = this.#getDropdownRoot(); 17247 dropdown.setAttribute("aria-orientation", "horizontal"); 17248 dropdown.setAttribute("aria-labelledby", "highlightColorPickerLabel"); 17249 return dropdown; 17250 } 17251 #getDropdownRoot() { 17252 const div = document.createElement("div"); 17253 const signal = this.#uiManager._signal; 17254 div.addEventListener("contextmenu", noContextMenu, { 17255 signal 17256 }); 17257 div.className = "dropdown"; 17258 div.role = "listbox"; 17259 div.setAttribute("aria-multiselectable", false); 17260 div.setAttribute("aria-orientation", "vertical"); 17261 div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown"); 17262 for (const [name, color] of this.#uiManager.highlightColors) { 17263 const button = document.createElement("button"); 17264 button.tabIndex = "0"; 17265 button.role = "option"; 17266 button.setAttribute("data-color", color); 17267 button.title = name; 17268 button.setAttribute("data-l10n-id", ColorPicker.#l10nColor[name]); 17269 const swatch = document.createElement("span"); 17270 button.append(swatch); 17271 swatch.className = "swatch"; 17272 swatch.style.backgroundColor = color; 17273 button.setAttribute("aria-selected", color === this.#defaultColor); 17274 button.addEventListener("click", this.#colorSelect.bind(this, color), { 17275 signal 17276 }); 17277 div.append(button); 17278 } 17279 div.addEventListener("keydown", this.#keyDown.bind(this), { 17280 signal 17281 }); 17282 return div; 17283 } 17284 #colorSelect(color, event) { 17285 event.stopPropagation(); 17286 this.#eventBus.dispatch("switchannotationeditorparams", { 17287 source: this, 17288 type: this.#type, 17289 value: color 17290 }); 17291 } 17292 _colorSelectFromKeyboard(event) { 17293 if (event.target === this.#button) { 17294 this.#openDropdown(event); 17295 return; 17296 } 17297 const color = event.target.getAttribute("data-color"); 17298 if (!color) { 17299 return; 17300 } 17301 this.#colorSelect(color, event); 17302 } 17303 _moveToNext(event) { 17304 if (!this.#isDropdownVisible) { 17305 this.#openDropdown(event); 17306 return; 17307 } 17308 if (event.target === this.#button) { 17309 this.#dropdown.firstChild?.focus(); 17310 return; 17311 } 17312 event.target.nextSibling?.focus(); 17313 } 17314 _moveToPrevious(event) { 17315 if (event.target === this.#dropdown?.firstChild || event.target === this.#button) { 17316 if (this.#isDropdownVisible) { 17317 this._hideDropdownFromKeyboard(); 17318 } 17319 return; 17320 } 17321 if (!this.#isDropdownVisible) { 17322 this.#openDropdown(event); 17323 } 17324 event.target.previousSibling?.focus(); 17325 } 17326 _moveToBeginning(event) { 17327 if (!this.#isDropdownVisible) { 17328 this.#openDropdown(event); 17329 return; 17330 } 17331 this.#dropdown.firstChild?.focus(); 17332 } 17333 _moveToEnd(event) { 17334 if (!this.#isDropdownVisible) { 17335 this.#openDropdown(event); 17336 return; 17337 } 17338 this.#dropdown.lastChild?.focus(); 17339 } 17340 #keyDown(event) { 17341 ColorPicker._keyboardManager.exec(this, event); 17342 } 17343 #openDropdown(event) { 17344 if (this.#isDropdownVisible) { 17345 this.hideDropdown(); 17346 return; 17347 } 17348 this.#dropdownWasFromKeyboard = event.detail === 0; 17349 if (!this.#openDropdownAC) { 17350 this.#openDropdownAC = new AbortController(); 17351 window.addEventListener("pointerdown", this.#pointerDown.bind(this), { 17352 signal: this.#uiManager.combinedSignal(this.#openDropdownAC) 17353 }); 17354 } 17355 if (this.#dropdown) { 17356 this.#dropdown.classList.remove("hidden"); 17357 return; 17358 } 17359 const root = this.#dropdown = this.#getDropdownRoot(); 17360 this.#button.append(root); 17361 } 17362 #pointerDown(event) { 17363 if (this.#dropdown?.contains(event.target)) { 17364 return; 17365 } 17366 this.hideDropdown(); 17367 } 17368 hideDropdown() { 17369 this.#dropdown?.classList.add("hidden"); 17370 this.#openDropdownAC?.abort(); 17371 this.#openDropdownAC = null; 17372 } 17373 get #isDropdownVisible() { 17374 return this.#dropdown && !this.#dropdown.classList.contains("hidden"); 17375 } 17376 _hideDropdownFromKeyboard() { 17377 if (this.#isMainColorPicker) { 17378 return; 17379 } 17380 if (!this.#isDropdownVisible) { 17381 this.#editor?.unselect(); 17382 return; 17383 } 17384 this.hideDropdown(); 17385 this.#button.focus({ 17386 preventScroll: true, 17387 focusVisible: this.#dropdownWasFromKeyboard 17388 }); 17389 } 17390 updateColor(color) { 17391 if (this.#buttonSwatch) { 17392 this.#buttonSwatch.style.backgroundColor = color; 17393 } 17394 if (!this.#dropdown) { 17395 return; 17396 } 17397 const i = this.#uiManager.highlightColors.values(); 17398 for (const child of this.#dropdown.children) { 17399 child.setAttribute("aria-selected", i.next().value === color); 17400 } 17401 } 17402 destroy() { 17403 this.#button?.remove(); 17404 this.#button = null; 17405 this.#buttonSwatch = null; 17406 this.#dropdown?.remove(); 17407 this.#dropdown = null; 17408 } 17409} 17410 17411;// ./src/display/editor/highlight.js 17412 17413 17414 17415 17416 17417 17418 17419class HighlightEditor extends AnnotationEditor { 17420 #anchorNode = null; 17421 #anchorOffset = 0; 17422 #boxes; 17423 #clipPathId = null; 17424 #colorPicker = null; 17425 #focusOutlines = null; 17426 #focusNode = null; 17427 #focusOffset = 0; 17428 #highlightDiv = null; 17429 #highlightOutlines = null; 17430 #id = null; 17431 #isFreeHighlight = false; 17432 #lastPoint = null; 17433 #opacity; 17434 #outlineId = null; 17435 #text = ""; 17436 #thickness; 17437 #methodOfCreation = ""; 17438 static _defaultColor = null; 17439 static _defaultOpacity = 1; 17440 static _defaultThickness = 12; 17441 static _type = "highlight"; 17442 static _editorType = AnnotationEditorType.HIGHLIGHT; 17443 static _freeHighlightId = -1; 17444 static _freeHighlight = null; 17445 static _freeHighlightClipId = ""; 17446 static get _keyboardManager() { 17447 const proto = HighlightEditor.prototype; 17448 return shadow(this, "_keyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, { 17449 args: [0] 17450 }], [["ArrowRight", "mac+ArrowRight"], proto._moveCaret, { 17451 args: [1] 17452 }], [["ArrowUp", "mac+ArrowUp"], proto._moveCaret, { 17453 args: [2] 17454 }], [["ArrowDown", "mac+ArrowDown"], proto._moveCaret, { 17455 args: [3] 17456 }]])); 17457 } 17458 constructor(params) { 17459 super({ 17460 ...params, 17461 name: "highlightEditor" 17462 }); 17463 this.color = params.color || HighlightEditor._defaultColor; 17464 this.#thickness = params.thickness || HighlightEditor._defaultThickness; 17465 this.#opacity = params.opacity || HighlightEditor._defaultOpacity; 17466 this.#boxes = params.boxes || null; 17467 this.#methodOfCreation = params.methodOfCreation || ""; 17468 this.#text = params.text || ""; 17469 this._isDraggable = false; 17470 if (params.highlightId > -1) { 17471 this.#isFreeHighlight = true; 17472 this.#createFreeOutlines(params); 17473 this.#addToDrawLayer(); 17474 } else if (this.#boxes) { 17475 this.#anchorNode = params.anchorNode; 17476 this.#anchorOffset = params.anchorOffset; 17477 this.#focusNode = params.focusNode; 17478 this.#focusOffset = params.focusOffset; 17479 this.#createOutlines(); 17480 this.#addToDrawLayer(); 17481 this.rotate(this.rotation); 17482 } 17483 } 17484 get telemetryInitialData() { 17485 return { 17486 action: "added", 17487 type: this.#isFreeHighlight ? "free_highlight" : "highlight", 17488 color: this._uiManager.highlightColorNames.get(this.color), 17489 thickness: this.#thickness, 17490 methodOfCreation: this.#methodOfCreation 17491 }; 17492 } 17493 get telemetryFinalData() { 17494 return { 17495 type: "highlight", 17496 color: this._uiManager.highlightColorNames.get(this.color) 17497 }; 17498 } 17499 static computeTelemetryFinalData(data) { 17500 return { 17501 numberOfColors: data.get("color").size 17502 }; 17503 } 17504 #createOutlines() { 17505 const outliner = new HighlightOutliner(this.#boxes, 0.001); 17506 this.#highlightOutlines = outliner.getOutlines(); 17507 ({ 17508 x: this.x, 17509 y: this.y, 17510 width: this.width, 17511 height: this.height 17512 } = this.#highlightOutlines.box); 17513 const outlinerForOutline = new HighlightOutliner(this.#boxes, 0.0025, 0.001, this._uiManager.direction === "ltr"); 17514 this.#focusOutlines = outlinerForOutline.getOutlines(); 17515 const { 17516 lastPoint 17517 } = this.#focusOutlines.box; 17518 this.#lastPoint = [(lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height]; 17519 } 17520 #createFreeOutlines({ 17521 highlightOutlines, 17522 highlightId, 17523 clipPathId 17524 }) { 17525 this.#highlightOutlines = highlightOutlines; 17526 const extraThickness = 1.5; 17527 this.#focusOutlines = highlightOutlines.getNewOutline(this.#thickness / 2 + extraThickness, 0.0025); 17528 if (highlightId >= 0) { 17529 this.#id = highlightId; 17530 this.#clipPathId = clipPathId; 17531 this.parent.drawLayer.finalizeLine(highlightId, highlightOutlines); 17532 this.#outlineId = this.parent.drawLayer.drawOutline(this.#focusOutlines); 17533 } else if (this.parent) { 17534 const angle = this.parent.viewport.rotation; 17535 this.parent.drawLayer.updateLine(this.#id, highlightOutlines); 17536 this.parent.drawLayer.updateBox(this.#id, HighlightEditor.#rotateBbox(this.#highlightOutlines.box, (angle - this.rotation + 360) % 360)); 17537 this.parent.drawLayer.updateLine(this.#outlineId, this.#focusOutlines); 17538 this.parent.drawLayer.updateBox(this.#outlineId, HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle)); 17539 } 17540 const { 17541 x, 17542 y, 17543 width, 17544 height 17545 } = highlightOutlines.box; 17546 switch (this.rotation) { 17547 case 0: 17548 this.x = x; 17549 this.y = y; 17550 this.width = width; 17551 this.height = height; 17552 break; 17553 case 90: 17554 { 17555 const [pageWidth, pageHeight] = this.parentDimensions; 17556 this.x = y; 17557 this.y = 1 - x; 17558 this.width = width * pageHeight / pageWidth; 17559 this.height = height * pageWidth / pageHeight; 17560 break; 17561 } 17562 case 180: 17563 this.x = 1 - x; 17564 this.y = 1 - y; 17565 this.width = width; 17566 this.height = height; 17567 break; 17568 case 270: 17569 { 17570 const [pageWidth, pageHeight] = this.parentDimensions; 17571 this.x = 1 - y; 17572 this.y = x; 17573 this.width = width * pageHeight / pageWidth; 17574 this.height = height * pageWidth / pageHeight; 17575 break; 17576 } 17577 } 17578 const { 17579 lastPoint 17580 } = this.#focusOutlines.box; 17581 this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height]; 17582 } 17583 static initialize(l10n, uiManager) { 17584 AnnotationEditor.initialize(l10n, uiManager); 17585 HighlightEditor._defaultColor ||= uiManager.highlightColors?.values().next().value || "#fff066"; 17586 } 17587 static updateDefaultParams(type, value) { 17588 switch (type) { 17589 case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR: 17590 HighlightEditor._defaultColor = value; 17591 break; 17592 case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: 17593 HighlightEditor._defaultThickness = value; 17594 break; 17595 } 17596 } 17597 translateInPage(x, y) {} 17598 get toolbarPosition() { 17599 return this.#lastPoint; 17600 } 17601 updateParams(type, value) { 17602 switch (type) { 17603 case AnnotationEditorParamsType.HIGHLIGHT_COLOR: 17604 this.#updateColor(value); 17605 break; 17606 case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: 17607 this.#updateThickness(value); 17608 break; 17609 } 17610 } 17611 static get defaultPropertiesToUpdate() { 17612 return [[AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR, HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, HighlightEditor._defaultThickness]]; 17613 } 17614 get propertiesToUpdate() { 17615 return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, this.color || HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, this.#thickness || HighlightEditor._defaultThickness], [AnnotationEditorParamsType.HIGHLIGHT_FREE, this.#isFreeHighlight]]; 17616 } 17617 #updateColor(color) { 17618 const setColorAndOpacity = (col, opa) => { 17619 this.color = col; 17620 this.parent?.drawLayer.changeColor(this.#id, col); 17621 this.#colorPicker?.updateColor(col); 17622 this.#opacity = opa; 17623 this.parent?.drawLayer.changeOpacity(this.#id, opa); 17624 }; 17625 const savedColor = this.color; 17626 const savedOpacity = this.#opacity; 17627 this.addCommands({ 17628 cmd: setColorAndOpacity.bind(this, color, HighlightEditor._defaultOpacity), 17629 undo: setColorAndOpacity.bind(this, savedColor, savedOpacity), 17630 post: this._uiManager.updateUI.bind(this._uiManager, this), 17631 mustExec: true, 17632 type: AnnotationEditorParamsType.HIGHLIGHT_COLOR, 17633 overwriteIfSameType: true, 17634 keepUndo: true 17635 }); 17636 this._reportTelemetry({ 17637 action: "color_changed", 17638 color: this._uiManager.highlightColorNames.get(color) 17639 }, true); 17640 } 17641 #updateThickness(thickness) { 17642 const savedThickness = this.#thickness; 17643 const setThickness = th => { 17644 this.#thickness = th; 17645 this.#changeThickness(th); 17646 }; 17647 this.addCommands({ 17648 cmd: setThickness.bind(this, thickness), 17649 undo: setThickness.bind(this, savedThickness), 17650 post: this._uiManager.updateUI.bind(this._uiManager, this), 17651 mustExec: true, 17652 type: AnnotationEditorParamsType.INK_THICKNESS, 17653 overwriteIfSameType: true, 17654 keepUndo: true 17655 }); 17656 this._reportTelemetry({ 17657 action: "thickness_changed", 17658 thickness 17659 }, true); 17660 } 17661 async addEditToolbar() { 17662 const toolbar = await super.addEditToolbar(); 17663 if (!toolbar) { 17664 return null; 17665 } 17666 if (this._uiManager.highlightColors) { 17667 this.#colorPicker = new ColorPicker({ 17668 editor: this 17669 }); 17670 toolbar.addColorPicker(this.#colorPicker); 17671 } 17672 return toolbar; 17673 } 17674 disableEditing() { 17675 super.disableEditing(); 17676 this.div.classList.toggle("disabled", true); 17677 } 17678 enableEditing() { 17679 super.enableEditing(); 17680 this.div.classList.toggle("disabled", false); 17681 } 17682 fixAndSetPosition() { 17683 return super.fixAndSetPosition(this.#getRotation()); 17684 } 17685 getBaseTranslation() { 17686 return [0, 0]; 17687 } 17688 getRect(tx, ty) { 17689 return super.getRect(tx, ty, this.#getRotation()); 17690 } 17691 onceAdded() { 17692 if (!this.annotationElementId) { 17693 this.parent.addUndoableEditor(this); 17694 } 17695 this.div.focus(); 17696 } 17697 remove() { 17698 this.#cleanDrawLayer(); 17699 this._reportTelemetry({ 17700 action: "deleted" 17701 }); 17702 super.remove(); 17703 } 17704 rebuild() { 17705 if (!this.parent) { 17706 return; 17707 } 17708 super.rebuild(); 17709 if (this.div === null) { 17710 return; 17711 } 17712 this.#addToDrawLayer(); 17713 if (!this.isAttachedToDOM) { 17714 this.parent.add(this); 17715 } 17716 } 17717 setParent(parent) { 17718 let mustBeSelected = false;
17719 if (this.parent && !parent) { 17720 this.#cleanDrawLayer(); 17721 } else if (parent) { 17722 this.#addToDrawLayer(parent); 17723 mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); 17724 } 17725 super.setParent(parent); 17726 this.show(this._isVisible); 17727 if (mustBeSelected) { 17728 this.select(); 17729 } 17730 } 17731 #changeThickness(thickness) { 17732 if (!this.#isFreeHighlight) { 17733 return; 17734 } 17735 this.#createFreeOutlines({ 17736 highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2) 17737 }); 17738 this.fixAndSetPosition(); 17739 const [parentWidth, parentHeight] = this.parentDimensions; 17740 this.setDims(this.width * parentWidth, this.height * parentHeight); 17741 } 17742 #cleanDrawLayer() { 17743 if (this.#id === null || !this.parent) { 17744 return; 17745 } 17746 this.parent.drawLayer.remove(this.#id); 17747 this.#id = null; 17748 this.parent.drawLayer.remove(this.#outlineId); 17749 this.#outlineId = null; 17750 } 17751 #addToDrawLayer(parent = this.parent) { 17752 if (this.#id !== null) { 17753 return; 17754 } 17755 ({ 17756 id: this.#id, 17757 clipPathId: this.#clipPathId 17758 } = parent.drawLayer.draw(this.#highlightOutlines, this.color, this.#opacity)); 17759 this.#outlineId = parent.drawLayer.drawOutline(this.#focusOutlines); 17760 if (this.#highlightDiv) { 17761 this.#highlightDiv.style.clipPath = this.#clipPathId; 17762 } 17763 } 17764 static #rotateBbox({ 17765 x, 17766 y, 17767 width, 17768 height 17769 }, angle) { 17770 switch (angle) { 17771 case 90: 17772 return { 17773 x: 1 - y - height, 17774 y: x, 17775 width: height, 17776 height: width 17777 }; 17778 case 180: 17779 return { 17780 x: 1 - x - width, 17781 y: 1 - y - height, 17782 width, 17783 height 17784 }; 17785 case 270: 17786 return { 17787 x: y, 17788 y: 1 - x - width, 17789 width: height, 17790 height: width 17791 }; 17792 } 17793 return { 17794 x, 17795 y, 17796 width, 17797 height 17798 }; 17799 } 17800 rotate(angle) { 17801 const { 17802 drawLayer 17803 } = this.parent; 17804 let box; 17805 if (this.#isFreeHighlight) { 17806 angle = (angle - this.rotation + 360) % 360; 17807 box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle); 17808 } else { 17809 box = HighlightEditor.#rotateBbox(this, angle); 17810 } 17811 drawLayer.rotate(this.#id, angle); 17812 drawLayer.rotate(this.#outlineId, angle); 17813 drawLayer.updateBox(this.#id, box); 17814 drawLayer.updateBox(this.#outlineId, HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle)); 17815 } 17816 render() { 17817 if (this.div) { 17818 return this.div; 17819 } 17820 const div = super.render(); 17821 if (this.#text) { 17822 div.setAttribute("aria-label", this.#text); 17823 div.setAttribute("role", "mark"); 17824 } 17825 if (this.#isFreeHighlight) { 17826 div.classList.add("free"); 17827 } else { 17828 this.div.addEventListener("keydown", this.#keydown.bind(this), { 17829 signal: this._uiManager._signal 17830 }); 17831 } 17832 const highlightDiv = this.#highlightDiv = document.createElement("div"); 17833 div.append(highlightDiv); 17834 highlightDiv.setAttribute("aria-hidden", "true"); 17835 highlightDiv.className = "internal"; 17836 highlightDiv.style.clipPath = this.#clipPathId; 17837 const [parentWidth, parentHeight] = this.parentDimensions; 17838 this.setDims(this.width * parentWidth, this.height * parentHeight); 17839 bindEvents(this, this.#highlightDiv, ["pointerover", "pointerleave"]); 17840 this.enableEditing(); 17841 return div; 17842 } 17843 pointerover() { 17844 if (!this.isSelected) { 17845 this.parent.drawLayer.addClass(this.#outlineId, "hovered"); 17846 } 17847 } 17848 pointerleave() { 17849 if (!this.isSelected) { 17850 this.parent.drawLayer.removeClass(this.#outlineId, "hovered"); 17851 } 17852 } 17853 #keydown(event) { 17854 HighlightEditor._keyboardManager.exec(this, event); 17855 } 17856 _moveCaret(direction) { 17857 this.parent.unselect(this); 17858 switch (direction) { 17859 case 0: 17860 case 2: 17861 this.#setCaret(true); 17862 break; 17863 case 1: 17864 case 3: 17865 this.#setCaret(false); 17866 break; 17867 } 17868 } 17869 #setCaret(start) { 17870 if (!this.#anchorNode) { 17871 return; 17872 } 17873 const selection = window.getSelection(); 17874 if (start) { 17875 selection.setPosition(this.#anchorNode, this.#anchorOffset); 17876 } else { 17877 selection.setPosition(this.#focusNode, this.#focusOffset); 17878 } 17879 } 17880 select() { 17881 super.select(); 17882 if (!this.#outlineId) { 17883 return; 17884 } 17885 this.parent?.drawLayer.removeClass(this.#outlineId, "hovered"); 17886 this.parent?.drawLayer.addClass(this.#outlineId, "selected"); 17887 } 17888 unselect() { 17889 super.unselect(); 17890 if (!this.#outlineId) { 17891 return; 17892 } 17893 this.parent?.drawLayer.removeClass(this.#outlineId, "selected"); 17894 if (!this.#isFreeHighlight) { 17895 this.#setCaret(false); 17896 } 17897 } 17898 get _mustFixPosition() { 17899 return !this.#isFreeHighlight; 17900 } 17901 show(visible = this._isVisible) { 17902 super.show(visible); 17903 if (this.parent) { 17904 this.parent.drawLayer.show(this.#id, visible); 17905 this.parent.drawLayer.show(this.#outlineId, visible); 17906 } 17907 } 17908 #getRotation() { 17909 return this.#isFreeHighlight ? this.rotation : 0; 17910 } 17911 #serializeBoxes() { 17912 if (this.#isFreeHighlight) { 17913 return null; 17914 } 17915 const [pageWidth, pageHeight] = this.pageDimensions; 17916 const [pageX, pageY] = this.pageTranslation; 17917 const boxes = this.#boxes; 17918 const quadPoints = new Float32Array(boxes.length * 8); 17919 let i = 0; 17920 for (const { 17921 x, 17922 y, 17923 width, 17924 height 17925 } of boxes) { 17926 const sx = x * pageWidth + pageX; 17927 const sy = (1 - y - height) * pageHeight + pageY; 17928 quadPoints[i] = quadPoints[i + 4] = sx; 17929 quadPoints[i + 1] = quadPoints[i + 3] = sy; 17930 quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth; 17931 quadPoints[i + 5] = quadPoints[i + 7] = sy + height * pageHeight; 17932 i += 8; 17933 } 17934 return quadPoints; 17935 } 17936 #serializeOutlines(rect) { 17937 return this.#highlightOutlines.serialize(rect, this.#getRotation()); 17938 } 17939 static startHighlighting(parent, isLTR, { 17940 target: textLayer, 17941 x, 17942 y 17943 }) { 17944 const { 17945 x: layerX, 17946 y: layerY, 17947 width: parentWidth, 17948 height: parentHeight 17949 } = textLayer.getBoundingClientRect(); 17950 const ac = new AbortController(); 17951 const signal = parent.combinedSignal(ac); 17952 const pointerDown = e => { 17953 e.preventDefault(); 17954 e.stopPropagation(); 17955 }; 17956 const pointerUpCallback = e => { 17957 ac.abort(); 17958 this.#endHighlight(parent, e); 17959 }; 17960 window.addEventListener("blur", pointerUpCallback, { 17961 signal 17962 }); 17963 window.addEventListener("pointerup", pointerUpCallback, { 17964 signal 17965 }); 17966 window.addEventListener("pointerdown", pointerDown, {
vendor: 4,479 bytes, lines 17967-18118
17967 capture: true, 17968 passive: false, 17969 signal 17970 }); 17971 window.addEventListener("contextmenu", noContextMenu, { 17972 signal 17973 }); 17974 textLayer.addEventListener("pointermove", this.#highlightMove.bind(this, parent), { 17975 signal 17976 }); 17977 this._freeHighlight = new FreeHighlightOutliner({ 17978 x, 17979 y 17980 }, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001); 17981 ({ 17982 id: this._freeHighlightId, 17983 clipPathId: this._freeHighlightClipId 17984 } = parent.drawLayer.draw(this._freeHighlight, this._defaultColor, this._defaultOpacity, true)); 17985 } 17986 static #highlightMove(parent, event) { 17987 if (this._freeHighlight.add(event)) { 17988 parent.drawLayer.updatePath(this._freeHighlightId, this._freeHighlight); 17989 } 17990 } 17991 static #endHighlight(parent, event) { 17992 if (!this._freeHighlight.isEmpty()) { 17993 parent.createAndAddNewEditor(event, false, { 17994 highlightId: this._freeHighlightId, 17995 highlightOutlines: this._freeHighlight.getOutlines(), 17996 clipPathId: this._freeHighlightClipId, 17997 methodOfCreation: "main_toolbar" 17998 }); 17999 } else { 18000 parent.drawLayer.remove(this._freeHighlightId); 18001 } 18002 this._freeHighlightId = -1; 18003 this._freeHighlight = null; 18004 this._freeHighlightClipId = ""; 18005 } 18006 static async deserialize(data, parent, uiManager) { 18007 let initialData = null; 18008 if (data instanceof HighlightAnnotationElement) { 18009 const { 18010 data: { 18011 quadPoints, 18012 rect, 18013 rotation, 18014 id, 18015 color, 18016 opacity, 18017 popupRef 18018 }, 18019 parent: { 18020 page: { 18021 pageNumber 18022 } 18023 } 18024 } = data; 18025 initialData = data = { 18026 annotationType: AnnotationEditorType.HIGHLIGHT, 18027 color: Array.from(color), 18028 opacity, 18029 quadPoints, 18030 boxes: null, 18031 pageIndex: pageNumber - 1, 18032 rect: rect.slice(0), 18033 rotation, 18034 id, 18035 deleted: false, 18036 popupRef 18037 }; 18038 } else if (data instanceof InkAnnotationElement) { 18039 const { 18040 data: { 18041 inkLists, 18042 rect, 18043 rotation, 18044 id, 18045 color, 18046 borderStyle: { 18047 rawWidth: thickness 18048 }, 18049 popupRef 18050 }, 18051 parent: { 18052 page: { 18053 pageNumber 18054 } 18055 } 18056 } = data; 18057 initialData = data = { 18058 annotationType: AnnotationEditorType.HIGHLIGHT, 18059 color: Array.from(color), 18060 thickness, 18061 inkLists, 18062 boxes: null, 18063 pageIndex: pageNumber - 1, 18064 rect: rect.slice(0), 18065 rotation, 18066 id, 18067 deleted: false, 18068 popupRef 18069 }; 18070 } 18071 const { 18072 color, 18073 quadPoints, 18074 inkLists, 18075 opacity 18076 } = data; 18077 const editor = await super.deserialize(data, parent, uiManager); 18078 editor.color = Util.makeHexColor(...color); 18079 editor.#opacity = opacity || 1; 18080 if (inkLists) { 18081 editor.#thickness = data.thickness; 18082 } 18083 editor.annotationElementId = data.id || null; 18084 editor._initialData = initialData; 18085 const [pageWidth, pageHeight] = editor.pageDimensions; 18086 const [pageX, pageY] = editor.pageTranslation; 18087 if (quadPoints) { 18088 const boxes = editor.#boxes = []; 18089 for (let i = 0; i < quadPoints.length; i += 8) { 18090 boxes.push({ 18091 x: (quadPoints[i] - pageX) / pageWidth, 18092 y: 1 - (quadPoints[i + 1] - pageY) / pageHeight, 18093 width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth, 18094 height: (quadPoints[i + 1] - quadPoints[i + 5]) / pageHeight 18095 }); 18096 } 18097 editor.#createOutlines(); 18098 editor.#addToDrawLayer(); 18099 editor.rotate(editor.rotation); 18100 } else if (inkLists) { 18101 editor.#isFreeHighlight = true; 18102 const points = inkLists[0]; 18103 const point = { 18104 x: points[0] - pageX, 18105 y: pageHeight - (points[1] - pageY) 18106 }; 18107 const outliner = new FreeHighlightOutliner(point, [0, 0, pageWidth, pageHeight], 1, editor.#thickness / 2, true, 0.001); 18108 for (let i = 0, ii = points.length; i < ii; i += 2) { 18109 point.x = points[i] - pageX; 18110 point.y = pageHeight - (points[i + 1] - pageY); 18111 outliner.add(point); 18112 } 18113 const { 18114 id, 18115 clipPathId 18116 } = parent.drawLayer.draw(outliner, editor.color, editor._defaultOpacity, true); 18117 editor.#createFreeOutlines({ 18118 highlightOutlines: outliner.getOutlines(),
18119 highlightId: id, 18120 clipPathId 18121 }); 18122 editor.#addToDrawLayer(); 18123 } 18124 return editor; 18125 } 18126 serialize(isForCopying = false) { 18127 if (this.isEmpty() || isForCopying) { 18128 return null; 18129 } 18130 if (this.deleted) { 18131 return this.serializeDeleted(); 18132 } 18133 const rect = this.getRect(0, 0); 18134 const color = AnnotationEditor._colorManager.convert(this.color); 18135 const serialized = { 18136 annotationType: AnnotationEditorType.HIGHLIGHT, 18137 color, 18138 opacity: this.#opacity, 18139 thickness: this.#thickness, 18140 quadPoints: this.#serializeBoxes(), 18141 outlines: this.#serializeOutlines(rect), 18142 pageIndex: this.pageIndex, 18143 rect, 18144 rotation: this.#getRotation(), 18145 structTreeParentId: this._structTreeParentId 18146 }; 18147 if (this.annotationElementId && !this.#hasElementChanged(serialized)) { 18148 return null; 18149 } 18150 serialized.id = this.annotationElementId; 18151 return serialized; 18152 } 18153 #hasElementChanged(serialized) { 18154 const { 18155 color 18156 } = this._initialData; 18157 return serialized.color.some((c, i) => c !== color[i]); 18158 } 18159 renderAnnotationElement(annotation) { 18160 annotation.updateEdited({ 18161 rect: this.getRect(0, 0) 18162 }); 18163 return null; 18164 } 18165 static canCreateNewEmptyEditor() { 18166 return false; 18167 } 18168} 18169 18170;// ./src/display/editor/ink.js 18171 18172 18173 18174 18175 18176class InkEditor extends AnnotationEditor { 18177 #baseHeight = 0; 18178 #baseWidth = 0; 18179 #canvasContextMenuTimeoutId = null; 18180 #currentPath2D = new Path2D(); 18181 #disableEditing = false; 18182 #drawingAC = null; 18183 #hasSomethingToDraw = false; 18184 #isCanvasInitialized = false; 18185 #observer = null; 18186 #pointerdownAC = null; 18187 #realWidth = 0; 18188 #realHeight = 0; 18189 #requestFrameCallback = null; 18190 static _defaultColor = null; 18191 static _defaultOpacity = 1; 18192 static _defaultThickness = 1; 18193 static _type = "ink"; 18194 static _editorType = AnnotationEditorType.INK; 18195 constructor(params) { 18196 super({ 18197 ...params, 18198 name: "inkEditor" 18199 }); 18200 this.color = params.color || null; 18201 this.thickness = params.thickness || null; 18202 this.opacity = params.opacity || null; 18203 this.paths = []; 18204 this.bezierPath2D = []; 18205 this.allRawPaths = []; 18206 this.currentPath = []; 18207 this.scaleFactor = 1; 18208 this.translationX = this.translationY = 0; 18209 this.x = 0; 18210 this.y = 0; 18211 this._willKeepAspectRatio = true; 18212 } 18213 static initialize(l10n, uiManager) { 18214 AnnotationEditor.initialize(l10n, uiManager); 18215 } 18216 static updateDefaultParams(type, value) { 18217 switch (type) { 18218 case AnnotationEditorParamsType.INK_THICKNESS: 18219 InkEditor._defaultThickness = value; 18220 break; 18221 case AnnotationEditorParamsType.INK_COLOR: 18222 InkEditor._defaultColor = value; 18223 break; 18224 case AnnotationEditorParamsType.INK_OPACITY: 18225 InkEditor._defaultOpacity = value / 100; 18226 break; 18227 } 18228 } 18229 updateParams(type, value) { 18230 switch (type) { 18231 case AnnotationEditorParamsType.INK_THICKNESS: 18232 this.#updateThickness(value); 18233 break; 18234 case AnnotationEditorParamsType.INK_COLOR: 18235 this.#updateColor(value); 18236 break; 18237 case AnnotationEditorParamsType.INK_OPACITY: 18238 this.#updateOpacity(value); 18239 break; 18240 } 18241 } 18242 static get defaultPropertiesToUpdate() { 18243 return [[AnnotationEditorParamsType.INK_THICKNESS, InkEditor._defaultThickness], [AnnotationEditorParamsType.INK_COLOR, InkEditor._defaultColor || AnnotationEditor._defaultLineColor], [AnnotationEditorParamsType.INK_OPACITY, Math.round(InkEditor._defaultOpacity * 100)]]; 18244 } 18245 get propertiesToUpdate() { 18246 return [[AnnotationEditorParamsType.INK_THICKNESS, this.thickness || InkEditor._defaultThickness], [AnnotationEditorParamsType.INK_COLOR, this.color || InkEditor._defaultColor || AnnotationEditor._defaultLineColor], [AnnotationEditorParamsType.INK_OPACITY, Math.round(100 * (this.opacity ?? InkEditor._defaultOpacity))]]; 18247 } 18248 #updateThickness(thickness) { 18249 const setThickness = th => { 18250 this.thickness = th; 18251 this.#fitToContent(); 18252 }; 18253 const savedThickness = this.thickness; 18254 this.addCommands({ 18255 cmd: setThickness.bind(this, thickness), 18256 undo: setThickness.bind(this, savedThickness), 18257 post: this._uiManager.updateUI.bind(this._uiManager, this), 18258 mustExec: true, 18259 type: AnnotationEditorParamsType.INK_THICKNESS, 18260 overwriteIfSameType: true, 18261 keepUndo: true 18262 }); 18263 } 18264 #updateColor(color) { 18265 const setColor = col => { 18266 this.color = col; 18267 this.#redraw(); 18268 }; 18269 const savedColor = this.color; 18270 this.addCommands({ 18271 cmd: setColor.bind(this, color), 18272 undo: setColor.bind(this, savedColor), 18273 post: this._uiManager.updateUI.bind(this._uiManager, this), 18274 mustExec: true, 18275 type: AnnotationEditorParamsType.INK_COLOR,
18276 overwriteIfSameType: true, 18277 keepUndo: true 18278 }); 18279 } 18280 #updateOpacity(opacity) { 18281 const setOpacity = op => { 18282 this.opacity = op; 18283 this.#redraw(); 18284 }; 18285 opacity /= 100; 18286 const savedOpacity = this.opacity; 18287 this.addCommands({ 18288 cmd: setOpacity.bind(this, opacity), 18289 undo: setOpacity.bind(this, savedOpacity), 18290 post: this._uiManager.updateUI.bind(this._uiManager, this), 18291 mustExec: true, 18292 type: AnnotationEditorParamsType.INK_OPACITY, 18293 overwriteIfSameType: true, 18294 keepUndo: true 18295 }); 18296 } 18297 rebuild() { 18298 if (!this.parent) { 18299 return; 18300 } 18301 super.rebuild(); 18302 if (this.div === null) { 18303 return; 18304 } 18305 if (!this.canvas) { 18306 this.#createCanvas(); 18307 this.#createObserver(); 18308 } 18309 if (!this.isAttachedToDOM) { 18310 this.parent.add(this); 18311 this.#setCanvasDims(); 18312 } 18313 this.#fitToContent(); 18314 } 18315 remove() { 18316 if (this.canvas === null) { 18317 return; 18318 } 18319 if (!this.isEmpty()) { 18320 this.commit(); 18321 } 18322 this.canvas.width = this.canvas.height = 0; 18323 this.canvas.remove(); 18324 this.canvas = null; 18325 if (this.#canvasContextMenuTimeoutId) { 18326 clearTimeout(this.#canvasContextMenuTimeoutId); 18327 this.#canvasContextMenuTimeoutId = null; 18328 } 18329 this.#observer?.disconnect(); 18330 this.#observer = null; 18331 super.remove(); 18332 } 18333 setParent(parent) { 18334 if (!this.parent && parent) { 18335 this._uiManager.removeShouldRescale(this); 18336 } else if (this.parent && parent === null) { 18337 this._uiManager.addShouldRescale(this); 18338 } 18339 super.setParent(parent); 18340 } 18341 onScaleChanging() { 18342 const [parentWidth, parentHeight] = this.parentDimensions; 18343 const width = this.width * parentWidth; 18344 const height = this.height * parentHeight; 18345 this.setDimensions(width, height); 18346 } 18347 enableEditMode() { 18348 if (this.#disableEditing || this.canvas === null) { 18349 return; 18350 } 18351 super.enableEditMode(); 18352 this._isDraggable = false; 18353 this.#addPointerdownListener(); 18354 } 18355 disableEditMode() { 18356 if (!this.isInEditMode() || this.canvas === null) { 18357 return; 18358 } 18359 super.disableEditMode(); 18360 this._isDraggable = !this.isEmpty(); 18361 this.div.classList.remove("editing"); 18362 this.#removePointerdownListener(); 18363 } 18364 onceAdded() { 18365 this._isDraggable = !this.isEmpty(); 18366 } 18367 isEmpty() { 18368 return this.paths.length === 0 || this.paths.length === 1 && this.paths[0].length === 0; 18369 } 18370 #getInitialBBox() { 18371 const { 18372 parentRotation, 18373 parentDimensions: [width, height] 18374 } = this; 18375 switch (parentRotation) { 18376 case 90: 18377 return [0, height, height, width]; 18378 case 180: 18379 return [width, height, width, height]; 18380 case 270: 18381 return [width, 0, height, width]; 18382 default: 18383 return [0, 0, width, height]; 18384 } 18385 } 18386 #setStroke() { 18387 const { 18388 ctx, 18389 color, 18390 opacity, 18391 thickness, 18392 parentScale, 18393 scaleFactor 18394 } = this; 18395 ctx.lineWidth = thickness * parentScale / scaleFactor; 18396 ctx.lineCap = "round"; 18397 ctx.lineJoin = "round"; 18398 ctx.miterLimit = 10; 18399 ctx.strokeStyle = `${color}${opacityToHex(opacity)}`; 18400 } 18401 #startDrawing(x, y) { 18402 this.canvas.addEventListener("contextmenu", noContextMenu, { 18403 signal: this._uiManager._signal 18404 }); 18405 this.#removePointerdownListener(); 18406 this.#drawingAC = new AbortController(); 18407 const signal = this._uiManager.combinedSignal(this.#drawingAC); 18408 this.canvas.addEventListener("pointerleave", this.canvasPointerleave.bind(this), { 18409 signal 18410 }); 18411 this.canvas.addEventListener("pointermove", this.canvasPointermove.bind(this), { 18412 signal 18413 }); 18414 this.canvas.addEventListener("pointerup", this.canvasPointerup.bind(this), { 18415 signal 18416 }); 18417 this.isEditing = true; 18418 if (!this.#isCanvasInitialized) { 18419 this.#isCanvasInitialized = true; 18420 this.#setCanvasDims(); 18421 this.thickness ||= InkEditor._defaultThickness; 18422 this.color ||= InkEditor._defaultColor || AnnotationEditor._defaultLineColor; 18423 this.opacity ??= InkEditor._defaultOpacity; 18424 } 18425 this.currentPath.push([x, y]); 18426 this.#hasSomethingToDraw = false;
18427 this.#setStroke(); 18428 this.#requestFrameCallback = () => { 18429 this.#drawPoints(); 18430 if (this.#requestFrameCallback) { 18431 window.requestAnimationFrame(this.#requestFrameCallback); 18432 } 18433 }; 18434 window.requestAnimationFrame(this.#requestFrameCallback); 18435 } 18436 #draw(x, y) { 18437 const [lastX, lastY] = this.currentPath.at(-1); 18438 if (this.currentPath.length > 1 && x === lastX && y === lastY) { 18439 return; 18440 } 18441 const currentPath = this.currentPath; 18442 let path2D = this.#currentPath2D; 18443 currentPath.push([x, y]); 18444 this.#hasSomethingToDraw = true; 18445 if (currentPath.length <= 2) { 18446 path2D.moveTo(...currentPath[0]); 18447 path2D.lineTo(x, y); 18448 return; 18449 } 18450 if (currentPath.length === 3) { 18451 this.#currentPath2D = path2D = new Path2D(); 18452 path2D.moveTo(...currentPath[0]); 18453 } 18454 this.#makeBezierCurve(path2D, ...currentPath.at(-3), ...currentPath.at(-2), x, y); 18455 } 18456 #endPath() { 18457 if (this.currentPath.length === 0) { 18458 return; 18459 } 18460 const lastPoint = this.currentPath.at(-1); 18461 this.#currentPath2D.lineTo(...lastPoint); 18462 } 18463 #stopDrawing(x, y) { 18464 this.#requestFrameCallback = null; 18465 x = Math.min(Math.max(x, 0), this.canvas.width); 18466 y = Math.min(Math.max(y, 0), this.canvas.height); 18467 this.#draw(x, y); 18468 this.#endPath(); 18469 let bezier; 18470 if (this.currentPath.length !== 1) { 18471 bezier = this.#generateBezierPoints(); 18472 } else { 18473 const xy = [x, y]; 18474 bezier = [[xy, xy.slice(), xy.slice(), xy]]; 18475 } 18476 const path2D = this.#currentPath2D; 18477 const currentPath = this.currentPath; 18478 this.currentPath = []; 18479 this.#currentPath2D = new Path2D(); 18480 const cmd = () => { 18481 this.allRawPaths.push(currentPath); 18482 this.paths.push(bezier); 18483 this.bezierPath2D.push(path2D); 18484 this._uiManager.rebuild(this); 18485 }; 18486 const undo = () => { 18487 this.allRawPaths.pop(); 18488 this.paths.pop(); 18489 this.bezierPath2D.pop(); 18490 if (this.paths.length === 0) { 18491 this.remove(); 18492 } else { 18493 if (!this.canvas) { 18494 this.#createCanvas(); 18495 this.#createObserver(); 18496 } 18497 this.#fitToContent(); 18498 } 18499 }; 18500 this.addCommands({ 18501 cmd, 18502 undo, 18503 mustExec: true 18504 }); 18505 } 18506 #drawPoints() { 18507 if (!this.#hasSomethingToDraw) { 18508 return; 18509 } 18510 this.#hasSomethingToDraw = false; 18511 const thickness = Math.ceil(this.thickness * this.parentScale); 18512 const lastPoints = this.currentPath.slice(-3); 18513 const x = lastPoints.map(xy => xy[0]); 18514 const y = lastPoints.map(xy => xy[1]); 18515 const xMin = Math.min(...x) - thickness; 18516 const xMax = Math.max(...x) + thickness; 18517 const yMin = Math.min(...y) - thickness; 18518 const yMax = Math.max(...y) + thickness; 18519 const { 18520 ctx 18521 } = this; 18522 ctx.save(); 18523 ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 18524 for (const path of this.bezierPath2D) { 18525 ctx.stroke(path); 18526 } 18527 ctx.stroke(this.#currentPath2D); 18528 ctx.restore(); 18529 } 18530 #makeBezierCurve(path2D, x0, y0, x1, y1, x2, y2) { 18531 const prevX = (x0 + x1) / 2; 18532 const prevY = (y0 + y1) / 2; 18533 const x3 = (x1 + x2) / 2; 18534 const y3 = (y1 + y2) / 2; 18535 path2D.bezierCurveTo(prevX + 2 * (x1 - prevX) / 3, prevY + 2 * (y1 - prevY) / 3, x3 + 2 * (x1 - x3) / 3, y3 + 2 * (y1 - y3) / 3, x3, y3); 18536 } 18537 #generateBezierPoints() { 18538 const path = this.currentPath; 18539 if (path.length <= 2) { 18540 return [[path[0], path[0], path.at(-1), path.at(-1)]]; 18541 } 18542 const bezierPoints = []; 18543 let i; 18544 let [x0, y0] = path[0]; 18545 for (i = 1; i < path.length - 2; i++) { 18546 const [x1, y1] = path[i]; 18547 const [x2, y2] = path[i + 1]; 18548 const x3 = (x1 + x2) / 2; 18549 const y3 = (y1 + y2) / 2; 18550 const control1 = [x0 + 2 * (x1 - x0) / 3, y0 + 2 * (y1 - y0) / 3]; 18551 const control2 = [x3 + 2 * (x1 - x3) / 3, y3 + 2 * (y1 - y3) / 3]; 18552 bezierPoints.push([[x0, y0], control1, control2, [x3, y3]]); 18553 [x0, y0] = [x3, y3]; 18554 } 18555 const [x1, y1] = path[i]; 18556 const [x2, y2] = path[i + 1]; 18557 const control1 = [x0 + 2 * (x1 - x0) / 3, y0 + 2 * (y1 - y0) / 3]; 18558 const control2 = [x2 + 2 * (x1 - x2) / 3, y2 + 2 * (y1 - y2) / 3]; 18559 bezierPoints.push([[x0, y0], control1, control2, [x2, y2]]); 18560 return bezierPoints; 18561 } 18562 #redraw() { 18563 if (this.isEmpty()) { 18564 this.#updateTransform(); 18565 return; 18566 } 18567 this.#setStroke(); 18568 const { 18569 canvas, 18570 ctx 18571 } = this; 18572 ctx.setTransform(1, 0, 0, 1, 0, 0); 18573 ctx.clearRect(0, 0, canvas.width, canvas.height); 18574 this.#updateTransform(); 18575 for (const path of this.bezierPath2D) { 18576 ctx.stroke(path); 18577 } 18578 } 18579 commit() { 18580 if (this.#disableEditing) { 18581 return; 18582 } 18583 super.commit(); 18584 this.isEditing = false;
18585 this.disableEditMode(); 18586 this.setInForeground(); 18587 this.#disableEditing = true; 18588 this.div.classList.add("disabled"); 18589 this.#fitToContent(true); 18590 this.select(); 18591 this.parent.addInkEditorIfNeeded(true); 18592 this.moveInDOM(); 18593 this.div.focus({ 18594 preventScroll: true 18595 }); 18596 } 18597 focusin(event) { 18598 if (!this._focusEventsAllowed) { 18599 return; 18600 } 18601 super.focusin(event); 18602 this.enableEditMode(); 18603 } 18604 #addPointerdownListener() { 18605 if (this.#pointerdownAC) { 18606 return; 18607 } 18608 this.#pointerdownAC = new AbortController(); 18609 const signal = this._uiManager.combinedSignal(this.#pointerdownAC); 18610 this.canvas.addEventListener("pointerdown", this.canvasPointerdown.bind(this), { 18611 signal 18612 }); 18613 } 18614 #removePointerdownListener() { 18615 this.pointerdownAC?.abort(); 18616 this.pointerdownAC = null; 18617 } 18618 canvasPointerdown(event) { 18619 if (event.button !== 0 || !this.isInEditMode() || this.#disableEditing) { 18620 return; 18621 } 18622 this.setInForeground(); 18623 event.preventDefault(); 18624 if (!this.div.contains(document.activeElement)) { 18625 this.div.focus({ 18626 preventScroll: true 18627 }); 18628 } 18629 this.#startDrawing(event.offsetX, event.offsetY); 18630 } 18631 canvasPointermove(event) { 18632 event.preventDefault(); 18633 this.#draw(event.offsetX, event.offsetY); 18634 } 18635 canvasPointerup(event) { 18636 event.preventDefault(); 18637 this.#endDrawing(event); 18638 } 18639 canvasPointerleave(event) { 18640 this.#endDrawing(event); 18641 } 18642 #endDrawing(event) { 18643 this.#drawingAC?.abort(); 18644 this.#drawingAC = null; 18645 this.#addPointerdownListener(); 18646 if (this.#canvasContextMenuTimeoutId) { 18647 clearTimeout(this.#canvasContextMenuTimeoutId); 18648 } 18649 this.#canvasContextMenuTimeoutId = setTimeout(() => { 18650 this.#canvasContextMenuTimeoutId = null; 18651 this.canvas.removeEventListener("contextmenu", noContextMenu); 18652 }, 10); 18653 this.#stopDrawing(event.offsetX, event.offsetY); 18654 this.addToAnnotationStorage(); 18655 this.setInBackground(); 18656 } 18657 #createCanvas() { 18658 this.canvas = document.createElement("canvas"); 18659 this.canvas.width = this.canvas.height = 0; 18660 this.canvas.className = "inkEditorCanvas"; 18661 this.canvas.setAttribute("data-l10n-id", "pdfjs-ink-canvas"); 18662 this.div.append(this.canvas); 18663 this.ctx = this.canvas.getContext("2d"); 18664 } 18665 #createObserver() { 18666 this.#observer = new ResizeObserver(entries => { 18667 const rect = entries[0].contentRect; 18668 if (rect.width && rect.height) { 18669 this.setDimensions(rect.width, rect.height); 18670 } 18671 }); 18672 this.#observer.observe(this.div); 18673 this._uiManager._signal.addEventListener("abort", () => { 18674 this.#observer?.disconnect(); 18675 this.#observer = null; 18676 }, { 18677 once: true 18678 }); 18679 } 18680 get isResizable() { 18681 return !this.isEmpty() && this.#disableEditing; 18682 } 18683 render() { 18684 if (this.div) { 18685 return this.div; 18686 } 18687 let baseX, baseY; 18688 if (this.width) { 18689 baseX = this.x; 18690 baseY = this.y; 18691 } 18692 super.render(); 18693 this.div.setAttribute("data-l10n-id", "pdfjs-ink"); 18694 const [x, y, w, h] = this.#getInitialBBox(); 18695 this.setAt(x, y, 0, 0); 18696 this.setDims(w, h); 18697 this.#createCanvas(); 18698 if (this.width) { 18699 const [parentWidth, parentHeight] = this.parentDimensions; 18700 this.setAspectRatio(this.width * parentWidth, this.height * parentHeight); 18701 this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); 18702 this.#isCanvasInitialized = true; 18703 this.#setCanvasDims(); 18704 this.setDims(this.width * parentWidth, this.height * parentHeight); 18705 this.#redraw(); 18706 this.div.classList.add("disabled"); 18707 } else { 18708 this.div.classList.add("editing"); 18709 this.enableEditMode(); 18710 } 18711 this.#createObserver(); 18712 return this.div; 18713 } 18714 #setCanvasDims() { 18715 if (!this.#isCanvasInitialized) { 18716 return; 18717 } 18718 const [parentWidth, parentHeight] = this.parentDimensions; 18719 this.canvas.width = Math.ceil(this.width * parentWidth); 18720 this.canvas.height = Math.ceil(this.height * parentHeight); 18721 this.#updateTransform(); 18722 } 18723 setDimensions(width, height) { 18724 const roundedWidth = Math.round(width); 18725 const roundedHeight = Math.round(height); 18726 if (this.#realWidth === roundedWidth && this.#realHeight === rounded
18726Height) { 18727 return; 18728 } 18729 this.#realWidth = roundedWidth; 18730 this.#realHeight = roundedHeight; 18731 this.canvas.style.visibility = "hidden"; 18732 const [parentWidth, parentHeight] = this.parentDimensions; 18733 this.width = width / parentWidth; 18734 this.height = height / parentHeight; 18735 this.fixAndSetPosition(); 18736 if (this.#disableEditing) { 18737 this.#setScaleFactor(width, height); 18738 } 18739 this.#setCanvasDims(); 18740 this.#redraw(); 18741 this.canvas.style.visibility = "visible"; 18742 this.fixDims(); 18743 } 18744 #setScaleFactor(width, height) { 18745 const padding = this.#getPadding(); 18746 const scaleFactorW = (width - padding) / this.#baseWidth; 18747 const scaleFactorH = (height - padding) / this.#baseHeight; 18748 this.scaleFactor = Math.min(scaleFactorW, scaleFactorH); 18749 } 18750 #updateTransform() { 18751 const padding = this.#getPadding() / 2; 18752 this.ctx.setTransform(this.scaleFactor, 0, 0, this.scaleFactor, this.translationX * this.scaleFactor + padding, this.translationY * this.scaleFactor + padding); 18753 } 18754 static #buildPath2D(bezier) { 18755 const path2D = new Path2D(); 18756 for (let i = 0, ii = bezier.length; i < ii; i++) { 18757 const [first, control1, control2, second] = bezier[i]; 18758 if (i === 0) { 18759 path2D.moveTo(...first); 18760 } 18761 path2D.bezierCurveTo(control1[0], control1[1], control2[0], control2[1], second[0], second[1]); 18762 } 18763 return path2D; 18764 } 18765 static #toPDFCoordinates(points, rect, rotation) { 18766 const [blX, blY, trX, trY] = rect; 18767 switch (rotation) { 18768 case 0: 18769 for (let i = 0, ii = points.length; i < ii; i += 2) { 18770 points[i] += blX; 18771 points[i + 1] = trY - points[i + 1]; 18772 } 18773 break; 18774 case 90: 18775 for (let i = 0, ii = points.length; i < ii; i += 2) { 18776 const x = points[i]; 18777 points[i] = points[i + 1] + blX; 18778 points[i + 1] = x + blY; 18779 } 18780 break; 18781 case 180: 18782 for (let i = 0, ii = points.length; i < ii; i += 2) { 18783 points[i] = trX - points[i]; 18784 points[i + 1] += blY; 18785 } 18786 break; 18787 case 270: 18788 for (let i = 0, ii = points.length; i < ii; i += 2) { 18789 const x = points[i]; 18790 points[i] = trX - points[i + 1]; 18791 points[i + 1] = trY - x; 18792 } 18793 break; 18794 default: 18795 throw new Error("Invalid rotation"); 18796 } 18797 return points; 18798 } 18799 static #fromPDFCoordinates(points, rect, rotation) { 18800 const [blX, blY, trX, trY] = rect; 18801 switch (rotation) { 18802 case 0: 18803 for (let i = 0, ii = points.length; i < ii; i += 2) { 18804 points[i] -= blX; 18805 points[i + 1] = trY - points[i + 1]; 18806 } 18807 break; 18808 case 90: 18809 for (let i = 0, ii = points.length; i < ii; i += 2) { 18810 const x = points[i]; 18811 points[i] = points[i + 1] - blY; 18812 points[i + 1] = x - blX; 18813 } 18814 break; 18815 case 180: 18816 for (let i = 0, ii = points.length; i < ii; i += 2) { 18817 points[i] = trX - points[i]; 18818 points[i + 1] -= blY; 18819 } 18820 break; 18821 case 270: 18822 for (let i = 0, ii = points.length; i < ii; i += 2) { 18823 const x = points[i]; 18824 points[i] = trY - points[i + 1]; 18825 points[i + 1] = trX - x; 18826 } 18827 break; 18828 default: 18829 throw new Error("Invalid rotation"); 18830 } 18831 return points; 18832 } 18833 #serializePaths(s, tx, ty, rect) { 18834 const paths = []; 18835 const padding = this.thickness / 2; 18836 const shiftX = s * tx + padding; 18837 const shiftY = s * ty + padding; 18838 for (const bezier of this.paths) { 18839 const buffer = []; 18840 const points = []; 18841 for (let j = 0, jj = bezier.length; j < jj; j++) { 18842 const [first, control1, control2, second] = bezier[j]; 18843 if (first[0] === second[0] && first[1] === second[1] && jj === 1) { 18844 const p0 = s * first[0] + shiftX; 18845 const p1 = s * first[1] + shiftY; 18846 buffer.push(p0, p1); 18847 points.push(p0, p1); 18848 break; 18849 } 18850 const p10 = s * first[0] + shiftX; 18851 const p11 = s * first[1] + shiftY; 18852 const p20 = s * control1[0] + shiftX; 18853 const p21 = s * control1[1] + shiftY; 18854 const p30 = s * control2[0] + shiftX; 18855 const p31 = s * control2[1] + shiftY; 18856 const p40 = s * second[0] + shiftX; 18857 const p41 = s * second[1] + shiftY; 18858 if (j === 0) { 18859 buffer.push(p10, p11); 18860 points.push(p10, p11); 18861 } 18862 buffer.push(p20, p21, p30, p31, p40, p41); 18863 points.push(p20, p21); 18864 if (j === jj - 1) { 18865 points.push(p40, p41); 18866 } 18867 } 18868 paths.push({ 18869 bezier: InkEditor.#toPDFCoordinates(buffer, rect, this.rotation), 18870 points: InkEditor.#toPDFCoordinates(points, rect, this.rotation) 18871 }); 18872 } 18873 return paths; 18874 } 18875 #getBbox() { 18876 let xMin = Infinity; 18877 let xMax = -Infinity; 18878 let yMin = Infinity; 18879 let yMax = -Infinity; 18880 for (const path of this.paths) { 18881 for (const [first, control1, control2, second] of path) { 18882 const bbox = Util.bezierBoundingBox(...first, ...control1, ...control2, ...second); 18883 xMin = Math.min(xMin, bbox[0]); 18884 yMin = Math.min(yMin, bbox[1]); 18885 xMax = Math.max(xMax, bbox[2]); 18886 yMax = Math.max(yMax, bbox[3]); 18887 } 18888 } 18889 return [xMin, yMin, xMax, yMax]; 18890 } 18891 #getPadding() { 18892 return this.#disableEditing ? Math.ceil(this.thickness * this.parentScale) : 0; 18893 } 18894 #fitToContent(firstTime = false) { 18895 if (this.isEmpty()) { 18896 return; 18897 } 18898 if (!this.#disableEditing) { 18899 this.#redraw(); 18900 return; 18901 } 18902 const bbox = this.#getBbox(); 18903 const padding = this.#getPadding();
18904 this.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]); 18905 this.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]); 18906 const width = Math.ceil(padding + this.#baseWidth * this.scaleFactor); 18907 const height = Math.ceil(padding + this.#baseHeight * this.scaleFactor); 18908 const [parentWidth, parentHeight] = this.parentDimensions; 18909 this.width = width / parentWidth; 18910 this.height = height / parentHeight; 18911 this.setAspectRatio(width, height); 18912 const prevTranslationX = this.translationX; 18913 const prevTranslationY = this.translationY; 18914 this.translationX = -bbox[0]; 18915 this.translationY = -bbox[1]; 18916 this.#setCanvasDims(); 18917 this.#redraw(); 18918 this.#realWidth = width; 18919 this.#realHeight = height; 18920 this.setDims(width, height); 18921 const unscaledPadding = firstTime ? padding / this.scaleFactor / 2 : 0; 18922 this.translate(prevTranslationX - this.translationX - unscaledPadding, prevTranslationY - this.translationY - unscaledPadding); 18923 } 18924 static async deserialize(data, parent, uiManager) { 18925 if (data instanceof InkAnnotationElement) { 18926 return null; 18927 } 18928 const editor = await super.deserialize(data, parent, uiManager); 18929 editor.thickness = data.thickness; 18930 editor.color = Util.makeHexColor(...data.color); 18931 editor.opacity = data.opacity; 18932 const [pageWidth, pageHeight] = editor.pageDimensions; 18933 const width = editor.width * pageWidth; 18934 const height = editor.height * pageHeight; 18935 const scaleFactor = editor.parentScale; 18936 const padding = data.thickness / 2; 18937 editor.#disableEditing = true; 18938 editor.#realWidth = Math.round(width); 18939 editor.#realHeight = Math.round(height); 18940 const { 18941 paths, 18942 rect, 18943 rotation 18944 } = data; 18945 for (let { 18946 bezier 18947 } of paths) { 18948 bezier = InkEditor.#fromPDFCoordinates(bezier, rect, rotation); 18949 const path = []; 18950 editor.paths.push(path); 18951 let p0 = scaleFactor * (bezier[0] - padding); 18952 let p1 = scaleFactor * (bezier[1] - padding); 18953 for (let i = 2, ii = bezier.length; i < ii; i += 6) { 18954 const p10 = scaleFactor * (bezier[i] - padding); 18955 const p11 = scaleFactor * (bezier[i + 1] - padding); 18956 const p20 = scaleFactor * (bezier[i + 2] - padding); 18957 const p21 = scaleFactor * (bezier[i + 3] - padding); 18958 const p30 = scaleFactor * (bezier[i + 4] - padding); 18959 const p31 = scaleFactor * (bezier[i + 5] - padding); 18960 path.push([[p0, p1], [p10, p11], [p20, p21], [p30, p31]]); 18961 p0 = p30; 18962 p1 = p31; 18963 } 18964 const path2D = this.#buildPath2D(path); 18965 editor.bezierPath2D.push(path2D); 18966 } 18967 const bbox = editor.#getBbox(); 18968 editor.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]); 18969 editor.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]); 18970 editor.#setScaleFactor(width, height); 18971 return editor; 18972 } 18973 serialize() { 18974 if (this.isEmpty()) { 18975 return null; 18976 } 18977 const rect = this.getRect(0, 0); 18978 const color = AnnotationEditor._colorManager.convert(this.ctx.strokeStyle); 18979 return { 18980 annotationType: AnnotationEditorType.INK, 18981 color, 18982 thickness: this.thickness, 18983 opacity: this.opacity, 18984 paths: this.#serializePaths(this.scaleFactor / this.parentScale, this.translationX, this.translationY, rect), 18985 pageIndex: this.pageIndex, 18986 rect, 18987 rotation: this.rotation, 18988 structTreeParentId: this._structTreeParentId 18989 }; 18990 } 18991} 18992 18993;// ./src/display/editor/stamp.js 18994 18995 18996 18997 18998class StampEditor extends AnnotationEditor { 18999 #bitmap = null; 19000 #bitmapId = null; 19001 #bitmapPromise = null; 19002 #bitmapUrl = null; 19003 #bitmapFile = null; 19004 #bitmapFileName = ""; 19005 #canvas = null; 19006 #observer = null; 19007 #resizeTimeoutId = null; 19008 #isSvg = false; 19009 #hasBeenAddedInUndoStack = false; 19010 static _type = "stamp"; 19011 static _editorType = AnnotationEditorType.STAMP; 19012 constructor(params) { 19013 super({ 19014 ...params, 19015 name: "stampEditor" 19016 }); 19017 this.#bitmapUrl = params.bitmapUrl; 19018 this.#bitmapFile = params.bitmapFile; 19019 } 19020 static initialize(l10n, uiManager) { 19021 AnnotationEditor.initialize(l10n, uiManager); 19022 } 19023 static get supportedTypes() { 19024 const types = ["apng", "avif", "bmp", "gif", "jpeg", "png", "svg+xml", "webp", "x-icon"]; 19025 return shadow(this, "supportedTypes", types.map(type => `image/${type}`)); 19026 } 19027 static get supportedTypesStr() { 19028 return shadow(this, "supportedTypesStr", this.supportedTypes.join(",")); 19029 } 19030 static isHandlingMimeForPasting(mime) { 19031 return this.supportedTypes.includes(mime); 19032 } 19033 static paste(item, parent) { 19034 parent.pasteEditor(AnnotationEditorType.STAMP, { 19035 bitmapFile: item.getAsFile() 19036 }); 19037 } 19038 altTextFinish() { 19039 if (this._uiManager.useNewAltTextFlow) { 19040 this.div.hidden = false;
vendor: 7,184 bytes, lines 19041-19290
19041 } 19042 super.altTextFinish(); 19043 } 19044 get telemetryFinalData() { 19045 return { 19046 type: "stamp", 19047 hasAltText: !!this.altTextData?.altText 19048 }; 19049 } 19050 static computeTelemetryFinalData(data) { 19051 const hasAltTextStats = data.get("hasAltText"); 19052 return { 19053 hasAltText: hasAltTextStats.get(true) ?? 0, 19054 hasNoAltText: hasAltTextStats.get(false) ?? 0 19055 }; 19056 } 19057 #getBitmapFetched(data, fromId = false) { 19058 if (!data) { 19059 this.remove(); 19060 return; 19061 } 19062 this.#bitmap = data.bitmap; 19063 if (!fromId) { 19064 this.#bitmapId = data.id; 19065 this.#isSvg = data.isSvg; 19066 } 19067 if (data.file) { 19068 this.#bitmapFileName = data.file.name; 19069 } 19070 this.#createCanvas(); 19071 } 19072 #getBitmapDone() { 19073 this.#bitmapPromise = null; 19074 this._uiManager.enableWaiting(false); 19075 if (!this.#canvas) { 19076 return; 19077 } 19078 if (this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { 19079 this._editToolbar.hide(); 19080 this._uiManager.editAltText(this, true); 19081 return; 19082 } 19083 if (!this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { 19084 this._reportTelemetry({ 19085 action: "pdfjs.image.image_added", 19086 data: { 19087 alt_text_modal: false, 19088 alt_text_type: "empty" 19089 } 19090 }); 19091 try { 19092 this.mlGuessAltText(); 19093 } catch {} 19094 } 19095 this.div.focus(); 19096 } 19097 async mlGuessAltText(imageData = null, updateAltTextData = true) { 19098 if (this.hasAltTextData()) { 19099 return null; 19100 } 19101 const { 19102 mlManager 19103 } = this._uiManager; 19104 if (!mlManager) { 19105 throw new Error("No ML."); 19106 } 19107 if (!(await mlManager.isEnabledFor("altText"))) { 19108 throw new Error("ML isn't enabled for alt text."); 19109 } 19110 const { 19111 data, 19112 width, 19113 height 19114 } = imageData || this.copyCanvas(null, null, true).imageData; 19115 const response = await mlManager.guess({ 19116 name: "altText", 19117 request: { 19118 data, 19119 width, 19120 height, 19121 channels: data.length / (width * height) 19122 } 19123 }); 19124 if (!response) { 19125 throw new Error("No response from the AI service."); 19126 } 19127 if (response.error) { 19128 throw new Error("Error from the AI service."); 19129 } 19130 if (response.cancel) { 19131 return null; 19132 } 19133 if (!response.output) { 19134 throw new Error("No valid response from the AI service."); 19135 } 19136 const altText = response.output; 19137 await this.setGuessedAltText(altText); 19138 if (updateAltTextData && !this.hasAltTextData()) { 19139 this.altTextData = { 19140 alt: altText, 19141 decorative: false 19142 }; 19143 } 19144 return altText; 19145 } 19146 #getBitmap() { 19147 if (this.#bitmapId) { 19148 this._uiManager.enableWaiting(true); 19149 this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone()); 19150 return; 19151 } 19152 if (this.#bitmapUrl) { 19153 const url = this.#bitmapUrl; 19154 this.#bitmapUrl = null; 19155 this._uiManager.enableWaiting(true); 19156 this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); 19157 return; 19158 } 19159 if (this.#bitmapFile) { 19160 const file = this.#bitmapFile; 19161 this.#bitmapFile = null; 19162 this._uiManager.enableWaiting(true); 19163 this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); 19164 return; 19165 } 19166 const input = document.createElement("input"); 19167 input.type = "file"; 19168 input.accept = StampEditor.supportedTypesStr; 19169 const signal = this._uiManager._signal; 19170 this.#bitmapPromise = new Promise(resolve => { 19171 input.addEventListener("change", async () => { 19172 if (!input.files || input.files.length === 0) { 19173 this.remove(); 19174 } else { 19175 this._uiManager.enableWaiting(true); 19176 const data = await this._uiManager.imageManager.getFromFile(input.files[0]); 19177 this._reportTelemetry({ 19178 action: "pdfjs.image.image_selected", 19179 data: { 19180 alt_text_modal: this._uiManager.useNewAltTextFlow 19181 } 19182 }); 19183 this.#getBitmapFetched(data); 19184 } 19185 resolve(); 19186 }, { 19187 signal 19188 }); 19189 input.addEventListener("cancel", () => { 19190 this.remove(); 19191 resolve(); 19192 }, { 19193 signal 19194 }); 19195 }).finally(() => this.#getBitmapDone()); 19196 input.click(); 19197 } 19198 remove() { 19199 if (this.#bitmapId) { 19200 this.#bitmap = null; 19201 this._uiManager.imageManager.deleteId(this.#bitmapId); 19202 this.#canvas?.remove(); 19203 this.#canvas = null; 19204 this.#observer?.disconnect(); 19205 this.#observer = null; 19206 if (this.#resizeTimeoutId) { 19207 clearTimeout(this.#resizeTimeoutId); 19208 this.#resizeTimeoutId = null; 19209 } 19210 } 19211 super.remove(); 19212 } 19213 rebuild() { 19214 if (!this.parent) { 19215 if (this.#bitmapId) { 19216 this.#getBitmap(); 19217 } 19218 return; 19219 } 19220 super.rebuild(); 19221 if (this.div === null) { 19222 return; 19223 } 19224 if (this.#bitmapId && this.#canvas === null) { 19225 this.#getBitmap(); 19226 } 19227 if (!this.isAttachedToDOM) { 19228 this.parent.add(this); 19229 } 19230 } 19231 onceAdded() { 19232 this._isDraggable = true; 19233 this.div.focus(); 19234 } 19235 isEmpty() { 19236 return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId); 19237 } 19238 get isResizable() { 19239 return true; 19240 } 19241 render() { 19242 if (this.div) { 19243 return this.div; 19244 } 19245 let baseX, baseY; 19246 if (this.width) { 19247 baseX = this.x; 19248 baseY = this.y; 19249 } 19250 super.render(); 19251 this.div.hidden = true; 19252 this.div.setAttribute("role", "figure"); 19253 this.addAltTextButton(); 19254 if (this.#bitmap) { 19255 this.#createCanvas(); 19256 } else { 19257 this.#getBitmap(); 19258 } 19259 if (this.width && !this.annotationElementId) { 19260 const [parentWidth, parentHeight] = this.parentDimensions; 19261 this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); 19262 } 19263 return this.div; 19264 } 19265 #createCanvas() { 19266 const { 19267 div 19268 } = this; 19269 let { 19270 width, 19271 height 19272 } = this.#bitmap; 19273 const [pageWidth, pageHeight] = this.pageDimensions; 19274 const MAX_RATIO = 0.75; 19275 if (this.width) { 19276 width = this.width * pageWidth; 19277 height = this.height * pageHeight; 19278 } else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) { 19279 const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height); 19280 width *= factor; 19281 height *= factor; 19282 } 19283 const [parentWidth, parentHeight] = this.parentDimensions; 19284 this.setDims(width * parentWidth / pageWidth, height * parentHeight / pageHeight); 19285 this._uiManager.enableWaiting(false); 19286 const canvas = this.#canvas = document.createElement("canvas"); 19287 canvas.setAttribute("role", "img"); 19288 this.addContainer(canvas); 19289 if (!this._uiManager.useNewAltTextWhenAddingImage || !this._uiManager.useNewAltTextFlow || this.annotationElementId) { 19290 div.hidden = false;
vendor: 5,372 bytes, lines 19291-19434
19291 } 19292 this.#drawBitmap(width, height); 19293 this.#createObserver(); 19294 if (!this.#hasBeenAddedInUndoStack) { 19295 this.parent.addUndoableEditor(this); 19296 this.#hasBeenAddedInUndoStack = true; 19297 } 19298 this._reportTelemetry({ 19299 action: "inserted_image" 19300 }); 19301 if (this.#bitmapFileName) { 19302 canvas.setAttribute("aria-label", this.#bitmapFileName); 19303 } 19304 } 19305 copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) { 19306 if (!maxDataDimension) { 19307 maxDataDimension = 224; 19308 } 19309 const { 19310 width: bitmapWidth, 19311 height: bitmapHeight 19312 } = this.#bitmap; 19313 const outputScale = new OutputScale(); 19314 let bitmap = this.#bitmap; 19315 let width = bitmapWidth, 19316 height = bitmapHeight; 19317 let canvas = null; 19318 if (maxPreviewDimension) { 19319 if (bitmapWidth > maxPreviewDimension || bitmapHeight > maxPreviewDimension) { 19320 const ratio = Math.min(maxPreviewDimension / bitmapWidth, maxPreviewDimension / bitmapHeight); 19321 width = Math.floor(bitmapWidth * ratio); 19322 height = Math.floor(bitmapHeight * ratio); 19323 } 19324 canvas = document.createElement("canvas"); 19325 const scaledWidth = canvas.width = Math.ceil(width * outputScale.sx); 19326 const scaledHeight = canvas.height = Math.ceil(height * outputScale.sy); 19327 if (!this.#isSvg) { 19328 bitmap = this.#scaleBitmap(scaledWidth, scaledHeight); 19329 } 19330 const ctx = canvas.getContext("2d"); 19331 ctx.filter = this._uiManager.hcmFilter; 19332 let white = "white", 19333 black = "#cfcfd8"; 19334 if (this._uiManager.hcmFilter !== "none") { 19335 black = "black"; 19336 } else if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) { 19337 white = "#8f8f9d"; 19338 black = "#42414d"; 19339 } 19340 const boxDim = 15; 19341 const boxDimWidth = boxDim * outputScale.sx; 19342 const boxDimHeight = boxDim * outputScale.sy; 19343 const pattern = new OffscreenCanvas(boxDimWidth * 2, boxDimHeight * 2); 19344 const patternCtx = pattern.getContext("2d"); 19345 patternCtx.fillStyle = white; 19346 patternCtx.fillRect(0, 0, boxDimWidth * 2, boxDimHeight * 2); 19347 patternCtx.fillStyle = black; 19348 patternCtx.fillRect(0, 0, boxDimWidth, boxDimHeight); 19349 patternCtx.fillRect(boxDimWidth, boxDimHeight, boxDimWidth, boxDimHeight); 19350 ctx.fillStyle = ctx.createPattern(pattern, "repeat"); 19351 ctx.fillRect(0, 0, scaledWidth, scaledHeight); 19352 ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); 19353 } 19354 let imageData = null; 19355 if (createImageData) { 19356 let dataWidth, dataHeight; 19357 if (outputScale.symmetric && bitmap.width < maxDataDimension && bitmap.height < maxDataDimension) { 19358 dataWidth = bitmap.width; 19359 dataHeight = bitmap.height; 19360 } else { 19361 bitmap = this.#bitmap; 19362 if (bitmapWidth > maxDataDimension || bitmapHeight > maxDataDimension) { 19363 const ratio = Math.min(maxDataDimension / bitmapWidth, maxDataDimension / bitmapHeight); 19364 dataWidth = Math.floor(bitmapWidth * ratio); 19365 dataHeight = Math.floor(bitmapHeight * ratio); 19366 if (!this.#isSvg) { 19367 bitmap = this.#scaleBitmap(dataWidth, dataHeight); 19368 } 19369 } 19370 } 19371 const offscreen = new OffscreenCanvas(dataWidth, dataHeight); 19372 const offscreenCtx = offscreen.getContext("2d", { 19373 willReadFrequently: true 19374 }); 19375 offscreenCtx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, dataWidth, dataHeight); 19376 imageData = { 19377 width: dataWidth, 19378 height: dataHeight, 19379 data: offscreenCtx.getImageData(0, 0, dataWidth, dataHeight).data 19380 }; 19381 } 19382 return { 19383 canvas, 19384 width, 19385 height, 19386 imageData 19387 }; 19388 } 19389 #setDimensions(width, height) { 19390 const [parentWidth, parentHeight] = this.parentDimensions; 19391 this.width = width / parentWidth; 19392 this.height = height / parentHeight; 19393 if (this._initialOptions?.isCentered) { 19394 this.center(); 19395 } else { 19396 this.fixAndSetPosition(); 19397 } 19398 this._initialOptions = null; 19399 if (this.#resizeTimeoutId !== null) { 19400 clearTimeout(this.#resizeTimeoutId); 19401 } 19402 const TIME_TO_WAIT = 200; 19403 this.#resizeTimeoutId = setTimeout(() => { 19404 this.#resizeTimeoutId = null; 19405 this.#drawBitmap(width, height); 19406 }, TIME_TO_WAIT); 19407 } 19408 #scaleBitmap(width, height) { 19409 const { 19410 width: bitmapWidth, 19411 height: bitmapHeight 19412 } = this.#bitmap; 19413 let newWidth = bitmapWidth; 19414 let newHeight = bitmapHeight; 19415 let bitmap = this.#bitmap; 19416 while (newWidth > 2 * width || newHeight > 2 * height) { 19417 const prevWidth = newWidth; 19418 const prevHeight = newHeight; 19419 if (newWidth > 2 * width) { 19420 newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2); 19421 } 19422 if (newHeight > 2 * height) { 19423 newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2); 19424 } 19425 const offscreen = new OffscreenCanvas(newWidth, newHeight); 19426 const ctx = offscreen.getContext("2d"); 19427 ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); 19428 bitmap = offscreen.transferToImageBitmap(); 19429 } 19430 return bitmap; 19431 } 19432 #drawBitmap(width, height) { 19433 const outputScale = new OutputScale(); 19434 const scaledWidth = Math.ceil(width * outputScale.sx);
vendor: 9,717 bytes, lines 19435-19759
19435 const scaledHeight = Math.ceil(height * outputScale.sy); 19436 const canvas = this.#canvas; 19437 if (!canvas || canvas.width === scaledWidth && canvas.height === scaledHeight) { 19438 return; 19439 } 19440 canvas.width = scaledWidth; 19441 canvas.height = scaledHeight; 19442 const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(scaledWidth, scaledHeight); 19443 const ctx = canvas.getContext("2d"); 19444 ctx.filter = this._uiManager.hcmFilter; 19445 ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); 19446 } 19447 getImageForAltText() { 19448 return this.#canvas; 19449 } 19450 #serializeBitmap(toUrl) { 19451 if (toUrl) { 19452 if (this.#isSvg) { 19453 const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId); 19454 if (url) { 19455 return url; 19456 } 19457 } 19458 const canvas = document.createElement("canvas"); 19459 ({ 19460 width: canvas.width, 19461 height: canvas.height 19462 } = this.#bitmap); 19463 const ctx = canvas.getContext("2d"); 19464 ctx.drawImage(this.#bitmap, 0, 0); 19465 return canvas.toDataURL(); 19466 } 19467 if (this.#isSvg) { 19468 const [pageWidth, pageHeight] = this.pageDimensions; 19469 const width = Math.round(this.width * pageWidth * PixelsPerInch.PDF_TO_CSS_UNITS); 19470 const height = Math.round(this.height * pageHeight * PixelsPerInch.PDF_TO_CSS_UNITS); 19471 const offscreen = new OffscreenCanvas(width, height); 19472 const ctx = offscreen.getContext("2d"); 19473 ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height); 19474 return offscreen.transferToImageBitmap(); 19475 } 19476 return structuredClone(this.#bitmap); 19477 } 19478 #createObserver() { 19479 if (!this._uiManager._signal) { 19480 return; 19481 } 19482 this.#observer = new ResizeObserver(entries => { 19483 const rect = entries[0].contentRect; 19484 if (rect.width && rect.height) { 19485 this.#setDimensions(rect.width, rect.height); 19486 } 19487 }); 19488 this.#observer.observe(this.div); 19489 this._uiManager._signal.addEventListener("abort", () => { 19490 this.#observer?.disconnect(); 19491 this.#observer = null; 19492 }, { 19493 once: true 19494 }); 19495 } 19496 static async deserialize(data, parent, uiManager) { 19497 let initialData = null; 19498 if (data instanceof StampAnnotationElement) { 19499 const { 19500 data: { 19501 rect, 19502 rotation, 19503 id, 19504 structParent, 19505 popupRef 19506 }, 19507 container, 19508 parent: { 19509 page: { 19510 pageNumber 19511 } 19512 } 19513 } = data; 19514 const canvas = container.querySelector("canvas"); 19515 const imageData = uiManager.imageManager.getFromCanvas(container.id, canvas); 19516 canvas.remove(); 19517 const altText = (await parent._structTree.getAriaAttributes(`${AnnotationPrefix}${id}`))?.get("aria-label") || ""; 19518 initialData = data = { 19519 annotationType: AnnotationEditorType.STAMP, 19520 bitmapId: imageData.id, 19521 bitmap: imageData.bitmap, 19522 pageIndex: pageNumber - 1, 19523 rect: rect.slice(0), 19524 rotation, 19525 id, 19526 deleted: false, 19527 accessibilityData: { 19528 decorative: false, 19529 altText 19530 }, 19531 isSvg: false, 19532 structParent, 19533 popupRef 19534 }; 19535 } 19536 const editor = await super.deserialize(data, parent, uiManager); 19537 const { 19538 rect, 19539 bitmap, 19540 bitmapUrl, 19541 bitmapId, 19542 isSvg, 19543 accessibilityData 19544 } = data; 19545 if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { 19546 editor.#bitmapId = bitmapId; 19547 if (bitmap) { 19548 editor.#bitmap = bitmap; 19549 } 19550 } else { 19551 editor.#bitmapUrl = bitmapUrl; 19552 } 19553 editor.#isSvg = isSvg; 19554 const [parentWidth, parentHeight] = editor.pageDimensions; 19555 editor.width = (rect[2] - rect[0]) / parentWidth; 19556 editor.height = (rect[3] - rect[1]) / parentHeight; 19557 editor.annotationElementId = data.id || null; 19558 if (accessibilityData) { 19559 editor.altTextData = accessibilityData; 19560 } 19561 editor._initialData = initialData; 19562 editor.#hasBeenAddedInUndoStack = !!initialData; 19563 return editor; 19564 } 19565 serialize(isForCopying = false, context = null) { 19566 if (this.isEmpty()) { 19567 return null; 19568 } 19569 if (this.deleted) { 19570 return this.serializeDeleted(); 19571 } 19572 const serialized = { 19573 annotationType: AnnotationEditorType.STAMP, 19574 bitmapId: this.#bitmapId, 19575 pageIndex: this.pageIndex, 19576 rect: this.getRect(0, 0), 19577 rotation: this.rotation, 19578 isSvg: this.#isSvg, 19579 structTreeParentId: this._structTreeParentId 19580 }; 19581 if (isForCopying) { 19582 serialized.bitmapUrl = this.#serializeBitmap(true); 19583 serialized.accessibilityData = this.serializeAltText(true); 19584 return serialized; 19585 } 19586 const { 19587 decorative, 19588 altText 19589 } = this.serializeAltText(false); 19590 if (!decorative && altText) { 19591 serialized.accessibilityData = { 19592 type: "Figure", 19593 alt: altText 19594 }; 19595 } 19596 if (this.annotationElementId) { 19597 const changes = this.#hasElementChanged(serialized); 19598 if (changes.isSame) { 19599 return null; 19600 } 19601 if (changes.isSameAltText) { 19602 delete serialized.accessibilityData; 19603 } else { 19604 serialized.accessibilityData.structParent = this._initialData.structParent ?? -1; 19605 } 19606 } 19607 serialized.id = this.annotationElementId; 19608 if (context === null) { 19609 return serialized; 19610 } 19611 context.stamps ||= new Map(); 19612 const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null; 19613 if (!context.stamps.has(this.#bitmapId)) { 19614 context.stamps.set(this.#bitmapId, { 19615 area, 19616 serialized 19617 }); 19618 serialized.bitmap = this.#serializeBitmap(false); 19619 } else if (this.#isSvg) { 19620 const prevData = context.stamps.get(this.#bitmapId); 19621 if (area > prevData.area) { 19622 prevData.area = area; 19623 prevData.serialized.bitmap.close(); 19624 prevData.serialized.bitmap = this.#serializeBitmap(false); 19625 } 19626 } 19627 return serialized; 19628 } 19629 #hasElementChanged(serialized) { 19630 const { 19631 rect, 19632 pageIndex, 19633 accessibilityData: { 19634 altText 19635 } 19636 } = this._initialData; 19637 const isSameRect = serialized.rect.every((x, i) => Math.abs(x - rect[i]) < 1); 19638 const isSamePageIndex = serialized.pageIndex === pageIndex; 19639 const isSameAltText = (serialized.accessibilityData?.alt || "") === altText; 19640 return { 19641 isSame: isSameRect && isSamePageIndex && isSameAltText, 19642 isSameAltText 19643 }; 19644 } 19645 renderAnnotationElement(annotation) { 19646 annotation.updateEdited({ 19647 rect: this.getRect(0, 0) 19648 }); 19649 return null; 19650 } 19651} 19652 19653;// ./src/display/editor/annotation_editor_layer.js 19654 19655 19656 19657 19658 19659 19660 19661class AnnotationEditorLayer { 19662 #accessibilityManager; 19663 #allowClick = false; 19664 #annotationLayer = null; 19665 #clickAC = null; 19666 #editorFocusTimeoutId = null; 19667 #editors = new Map(); 19668 #hadPointerDown = false; 19669 #isCleaningUp = false; 19670 #isDisabling = false; 19671 #textLayer = null; 19672 #textSelectionAC = null; 19673 #uiManager; 19674 static _initialized = false; 19675 static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor].map(type => [type._editorType, type])); 19676 constructor({ 19677 uiManager, 19678 pageIndex, 19679 div, 19680 structTreeLayer, 19681 accessibilityManager, 19682 annotationLayer, 19683 drawLayer, 19684 textLayer, 19685 viewport, 19686 l10n 19687 }) { 19688 const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()]; 19689 if (!AnnotationEditorLayer._initialized) { 19690 AnnotationEditorLayer._initialized = true; 19691 for (const editorType of editorTypes) { 19692 editorType.initialize(l10n, uiManager); 19693 } 19694 } 19695 uiManager.registerEditorTypes(editorTypes); 19696 this.#uiManager = uiManager; 19697 this.pageIndex = pageIndex; 19698 this.div = div; 19699 this.#accessibilityManager = accessibilityManager; 19700 this.#annotationLayer = annotationLayer; 19701 this.viewport = viewport; 19702 this.#textLayer = textLayer; 19703 this.drawLayer = drawLayer; 19704 this._structTree = structTreeLayer; 19705 this.#uiManager.addLayer(this); 19706 } 19707 get isEmpty() { 19708 return this.#editors.size === 0; 19709 } 19710 get isInvisible() { 19711 return this.isEmpty && this.#uiManager.getMode() === AnnotationEditorType.NONE; 19712 } 19713 updateToolbar(mode) { 19714 this.#uiManager.updateToolbar(mode); 19715 } 19716 updateMode(mode = this.#uiManager.getMode()) { 19717 this.#cleanup(); 19718 switch (mode) { 19719 case AnnotationEditorType.NONE: 19720 this.disableTextSelection(); 19721 this.togglePointerEvents(false); 19722 this.toggleAnnotationLayerPointerEvents(true); 19723 this.disableClick(); 19724 return; 19725 case AnnotationEditorType.INK: 19726 this.addInkEditorIfNeeded(false); 19727 this.disableTextSelection(); 19728 this.togglePointerEvents(true); 19729 this.disableClick(); 19730 break; 19731 case AnnotationEditorType.HIGHLIGHT: 19732 this.enableTextSelection(); 19733 this.togglePointerEvents(false); 19734 this.disableClick(); 19735 break; 19736 default: 19737 this.disableTextSelection(); 19738 this.togglePointerEvents(true); 19739 this.enableClick(); 19740 } 19741 this.toggleAnnotationLayerPointerEvents(false); 19742 const { 19743 classList 19744 } = this.div; 19745 for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { 19746 classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType); 19747 } 19748 this.div.hidden = false; 19749 } 19750 hasTextLayer(textLayer) { 19751 return textLayer === this.#textLayer?.div; 19752 } 19753 addInkEditorIfNeeded(isCommitting) { 19754 if (this.#uiManager.getMode() !== AnnotationEditorType.INK) { 19755 return; 19756 } 19757 if (!isCommitting) { 19758 for (const editor of this.#editors.values()) { 19759 if (editor.isEmpty()) {
vendor: 6,583 bytes, lines 19760-19974
19760 editor.setInBackground(); 19761 return; 19762 } 19763 } 19764 } 19765 const editor = this.createAndAddNewEditor({ 19766 offsetX: 0, 19767 offsetY: 0 19768 }, false); 19769 editor.setInBackground(); 19770 } 19771 setEditingState(isEditing) { 19772 this.#uiManager.setEditingState(isEditing); 19773 } 19774 addCommands(params) { 19775 this.#uiManager.addCommands(params); 19776 } 19777 toggleDrawing(enabled = false) { 19778 this.div.classList.toggle("drawing", !enabled); 19779 } 19780 togglePointerEvents(enabled = false) { 19781 this.div.classList.toggle("disabled", !enabled); 19782 } 19783 toggleAnnotationLayerPointerEvents(enabled = false) { 19784 this.#annotationLayer?.div.classList.toggle("disabled", !enabled); 19785 } 19786 async enable() { 19787 this.div.tabIndex = 0; 19788 this.togglePointerEvents(true); 19789 const annotationElementIds = new Set(); 19790 for (const editor of this.#editors.values()) { 19791 editor.enableEditing(); 19792 editor.show(true); 19793 if (editor.annotationElementId) { 19794 this.#uiManager.removeChangedExistingAnnotation(editor); 19795 annotationElementIds.add(editor.annotationElementId); 19796 } 19797 } 19798 if (!this.#annotationLayer) { 19799 return; 19800 } 19801 const editables = this.#annotationLayer.getEditableAnnotations(); 19802 for (const editable of editables) { 19803 editable.hide(); 19804 if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) { 19805 continue; 19806 } 19807 if (annotationElementIds.has(editable.data.id)) { 19808 continue; 19809 } 19810 const editor = await this.deserialize(editable); 19811 if (!editor) { 19812 continue; 19813 } 19814 this.addOrRebuild(editor); 19815 editor.enableEditing(); 19816 } 19817 } 19818 disable() { 19819 this.#isDisabling = true; 19820 this.div.tabIndex = -1; 19821 this.togglePointerEvents(false); 19822 const changedAnnotations = new Map(); 19823 const resetAnnotations = new Map(); 19824 for (const editor of this.#editors.values()) { 19825 editor.disableEditing(); 19826 if (!editor.annotationElementId) { 19827 continue; 19828 } 19829 if (editor.serialize() !== null) { 19830 changedAnnotations.set(editor.annotationElementId, editor); 19831 continue; 19832 } else { 19833 resetAnnotations.set(editor.annotationElementId, editor); 19834 } 19835 this.getEditableAnnotation(editor.annotationElementId)?.show(); 19836 editor.remove(); 19837 } 19838 if (this.#annotationLayer) { 19839 const editables = this.#annotationLayer.getEditableAnnotations(); 19840 for (const editable of editables) { 19841 const { 19842 id 19843 } = editable.data; 19844 if (this.#uiManager.isDeletedAnnotationElement(id)) { 19845 continue; 19846 } 19847 let editor = resetAnnotations.get(id); 19848 if (editor) { 19849 editor.resetAnnotationElement(editable); 19850 editor.show(false); 19851 editable.show(); 19852 continue; 19853 } 19854 editor = changedAnnotations.get(id); 19855 if (editor) { 19856 this.#uiManager.addChangedExistingAnnotation(editor); 19857 if (editor.renderAnnotationElement(editable)) { 19858 editor.show(false); 19859 } 19860 } 19861 editable.show(); 19862 } 19863 } 19864 this.#cleanup(); 19865 if (this.isEmpty) { 19866 this.div.hidden = true; 19867 } 19868 const { 19869 classList 19870 } = this.div; 19871 for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { 19872 classList.remove(`${editorType._type}Editing`); 19873 } 19874 this.disableTextSelection(); 19875 this.toggleAnnotationLayerPointerEvents(true); 19876 this.#isDisabling = false; 19877 } 19878 getEditableAnnotation(id) { 19879 return this.#annotationLayer?.getEditableAnnotation(id) || null; 19880 } 19881 setActiveEditor(editor) { 19882 const currentActive = this.#uiManager.getActive(); 19883 if (currentActive === editor) { 19884 return; 19885 } 19886 this.#uiManager.setActiveEditor(editor); 19887 } 19888 enableTextSelection() { 19889 this.div.tabIndex = -1; 19890 if (this.#textLayer?.div && !this.#textSelectionAC) { 19891 this.#textSelectionAC = new AbortController(); 19892 const signal = this.#uiManager.combinedSignal(this.#textSelectionAC); 19893 this.#textLayer.div.addEventListener("pointerdown", this.#textLayerPointerDown.bind(this), { 19894 signal 19895 }); 19896 this.#textLayer.div.classList.add("highlighting"); 19897 } 19898 } 19899 disableTextSelection() { 19900 this.div.tabIndex = 0; 19901 if (this.#textLayer?.div && this.#textSelectionAC) { 19902 this.#textSelectionAC.abort(); 19903 this.#textSelectionAC = null; 19904 this.#textLayer.div.classList.remove("highlighting"); 19905 } 19906 } 19907 #textLayerPointerDown(event) { 19908 this.#uiManager.unselectAll(); 19909 const { 19910 target 19911 } = event; 19912 if (target === this.#textLayer.div || (target.getAttribute("role") === "img" || target.classList.contains("endOfContent")) && this.#textLayer.div.contains(target)) { 19913 const { 19914 isMac 19915 } = util_FeatureTest.platform; 19916 if (event.button !== 0 || event.ctrlKey && isMac) { 19917 return; 19918 } 19919 this.#uiManager.showAllEditors("highlight", true, true); 19920 this.#textLayer.div.classList.add("free"); 19921 this.toggleDrawing(); 19922 HighlightEditor.startHighlighting(this, this.#uiManager.direction === "ltr", { 19923 target: this.#textLayer.div, 19924 x: event.x, 19925 y: event.y 19926 }); 19927 this.#textLayer.div.addEventListener("pointerup", () => { 19928 this.#textLayer.div.classList.remove("free"); 19929 this.toggleDrawing(true); 19930 }, { 19931 once: true, 19932 signal: this.#uiManager._signal 19933 }); 19934 event.preventDefault(); 19935 } 19936 } 19937 enableClick() { 19938 if (this.#clickAC) { 19939 return; 19940 } 19941 this.#clickAC = new AbortController(); 19942 const signal = this.#uiManager.combinedSignal(this.#clickAC); 19943 this.div.addEventListener("pointerdown", this.pointerdown.bind(this), { 19944 signal 19945 }); 19946 this.div.addEventListener("pointerup", this.pointerup.bind(this), { 19947 signal 19948 }); 19949 } 19950 disableClick() { 19951 this.#clickAC?.abort(); 19952 this.#clickAC = null; 19953 } 19954 attach(editor) { 19955 this.#editors.set(editor.id, editor); 19956 const { 19957 annotationElementId 19958 } = editor; 19959 if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) { 19960 this.#uiManager.removeDeletedAnnotationElement(editor); 19961 } 19962 } 19963 detach(editor) { 19964 this.#editors.delete(editor.id); 19965 this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); 19966 if (!this.#isDisabling && editor.annotationElementId) { 19967 this.#uiManager.addDeletedAnnotationElement(editor); 19968 } 19969 } 19970 remove(editor) { 19971 this.detach(editor); 19972 this.#uiManager.removeEditor(editor); 19973 editor.div.remove(); 19974 editor.isAttachedToDOM = false;
vendor: 5,011 bytes, lines 19975-20159
19975 if (!this.#isCleaningUp) { 19976 this.addInkEditorIfNeeded(false); 19977 } 19978 } 19979 changeParent(editor) { 19980 if (editor.parent === this) { 19981 return; 19982 } 19983 if (editor.parent && editor.annotationElementId) { 19984 this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId); 19985 AnnotationEditor.deleteAnnotationElement(editor); 19986 editor.annotationElementId = null; 19987 } 19988 this.attach(editor); 19989 editor.parent?.detach(editor); 19990 editor.setParent(this); 19991 if (editor.div && editor.isAttachedToDOM) { 19992 editor.div.remove(); 19993 this.div.append(editor.div); 19994 } 19995 } 19996 add(editor) { 19997 if (editor.parent === this && editor.isAttachedToDOM) { 19998 return; 19999 } 20000 this.changeParent(editor); 20001 this.#uiManager.addEditor(editor); 20002 this.attach(editor); 20003 if (!editor.isAttachedToDOM) { 20004 const div = editor.render(); 20005 this.div.append(div); 20006 editor.isAttachedToDOM = true; 20007 } 20008 editor.fixAndSetPosition(); 20009 editor.onceAdded(); 20010 this.#uiManager.addToAnnotationStorage(editor); 20011 editor._reportTelemetry(editor.telemetryInitialData); 20012 } 20013 moveEditorInDOM(editor) { 20014 if (!editor.isAttachedToDOM) { 20015 return; 20016 } 20017 const { 20018 activeElement 20019 } = document; 20020 if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) { 20021 editor._focusEventsAllowed = false; 20022 this.#editorFocusTimeoutId = setTimeout(() => { 20023 this.#editorFocusTimeoutId = null; 20024 if (!editor.div.contains(document.activeElement)) { 20025 editor.div.addEventListener("focusin", () => { 20026 editor._focusEventsAllowed = true; 20027 }, { 20028 once: true, 20029 signal: this.#uiManager._signal 20030 }); 20031 activeElement.focus(); 20032 } else { 20033 editor._focusEventsAllowed = true; 20034 } 20035 }, 0); 20036 } 20037 editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true); 20038 } 20039 addOrRebuild(editor) { 20040 if (editor.needsToBeRebuilt()) { 20041 editor.parent ||= this; 20042 editor.rebuild(); 20043 editor.show(); 20044 } else { 20045 this.add(editor); 20046 } 20047 } 20048 addUndoableEditor(editor) { 20049 const cmd = () => editor._uiManager.rebuild(editor); 20050 const undo = () => { 20051 editor.remove(); 20052 }; 20053 this.addCommands({ 20054 cmd, 20055 undo, 20056 mustExec: false 20057 }); 20058 } 20059 getNextId() { 20060 return this.#uiManager.getId(); 20061 } 20062 get #currentEditorType() { 20063 return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode()); 20064 } 20065 combinedSignal(ac) { 20066 return this.#uiManager.combinedSignal(ac); 20067 } 20068 #createNewEditor(params) { 20069 const editorType = this.#currentEditorType; 20070 return editorType ? new editorType.prototype.constructor(params) : null; 20071 } 20072 canCreateNewEmptyEditor() { 20073 return this.#currentEditorType?.canCreateNewEmptyEditor(); 20074 } 20075 pasteEditor(mode, params) { 20076 this.#uiManager.updateToolbar(mode); 20077 this.#uiManager.updateMode(mode); 20078 const { 20079 offsetX, 20080 offsetY 20081 } = this.#getCenterPoint(); 20082 const id = this.getNextId(); 20083 const editor = this.#createNewEditor({ 20084 parent: this, 20085 id, 20086 x: offsetX, 20087 y: offsetY, 20088 uiManager: this.#uiManager, 20089 isCentered: true, 20090 ...params 20091 }); 20092 if (editor) { 20093 this.add(editor); 20094 } 20095 } 20096 async deserialize(data) { 20097 return (await AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager)) || null; 20098 } 20099 createAndAddNewEditor(event, isCentered, data = {}) { 20100 const id = this.getNextId(); 20101 const editor = this.#createNewEditor({ 20102 parent: this, 20103 id, 20104 x: event.offsetX, 20105 y: event.offsetY, 20106 uiManager: this.#uiManager, 20107 isCentered, 20108 ...data 20109 }); 20110 if (editor) { 20111 this.add(editor); 20112 } 20113 return editor; 20114 } 20115 #getCenterPoint() { 20116 const { 20117 x, 20118 y, 20119 width, 20120 height 20121 } = this.div.getBoundingClientRect(); 20122 const tlX = Math.max(0, x); 20123 const tlY = Math.max(0, y); 20124 const brX = Math.min(window.innerWidth, x + width); 20125 const brY = Math.min(window.innerHeight, y + height); 20126 const centerX = (tlX + brX) / 2 - x; 20127 const centerY = (tlY + brY) / 2 - y; 20128 const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX]; 20129 return { 20130 offsetX, 20131 offsetY 20132 }; 20133 } 20134 addNewEditor() { 20135 this.createAndAddNewEditor(this.#getCenterPoint(), true); 20136 } 20137 setSelected(editor) { 20138 this.#uiManager.setSelected(editor); 20139 } 20140 toggleSelected(editor) { 20141 this.#uiManager.toggleSelected(editor); 20142 } 20143 unselect(editor) { 20144 this.#uiManager.unselect(editor); 20145 } 20146 pointerup(event) { 20147 const { 20148 isMac 20149 } = util_FeatureTest.platform; 20150 if (event.button !== 0 || event.ctrlKey && isMac) { 20151 return; 20152 } 20153 if (event.target !== this.div) { 20154 return; 20155 } 20156 if (!this.#hadPointerDown) { 20157 return; 20158 } 20159 this.#hadPointerDown = false;
vendor: 14,263 bytes, lines 20160-20521
20160 if (!this.#allowClick) { 20161 this.#allowClick = true; 20162 return; 20163 } 20164 if (this.#uiManager.getMode() === AnnotationEditorType.STAMP) { 20165 this.#uiManager.unselectAll(); 20166 return; 20167 } 20168 this.createAndAddNewEditor(event, false); 20169 } 20170 pointerdown(event) { 20171 if (this.#uiManager.getMode() === AnnotationEditorType.HIGHLIGHT) { 20172 this.enableTextSelection(); 20173 } 20174 if (this.#hadPointerDown) { 20175 this.#hadPointerDown = false; 20176 return; 20177 } 20178 const { 20179 isMac 20180 } = util_FeatureTest.platform; 20181 if (event.button !== 0 || event.ctrlKey && isMac) { 20182 return; 20183 } 20184 if (event.target !== this.div) { 20185 return; 20186 } 20187 this.#hadPointerDown = true; 20188 const editor = this.#uiManager.getActive(); 20189 this.#allowClick = !editor || editor.isEmpty(); 20190 } 20191 findNewParent(editor, x, y) { 20192 const layer = this.#uiManager.findParent(x, y); 20193 if (layer === null || layer === this) { 20194 return false; 20195 } 20196 layer.changeParent(editor); 20197 return true; 20198 } 20199 destroy() { 20200 if (this.#uiManager.getActive()?.parent === this) { 20201 this.#uiManager.commitOrRemove(); 20202 this.#uiManager.setActiveEditor(null); 20203 } 20204 if (this.#editorFocusTimeoutId) { 20205 clearTimeout(this.#editorFocusTimeoutId); 20206 this.#editorFocusTimeoutId = null; 20207 } 20208 for (const editor of this.#editors.values()) { 20209 this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); 20210 editor.setParent(null); 20211 editor.isAttachedToDOM = false; 20212 editor.div.remove(); 20213 } 20214 this.div = null; 20215 this.#editors.clear(); 20216 this.#uiManager.removeLayer(this); 20217 } 20218 #cleanup() { 20219 this.#isCleaningUp = true; 20220 for (const editor of this.#editors.values()) { 20221 if (editor.isEmpty()) { 20222 editor.remove(); 20223 } 20224 } 20225 this.#isCleaningUp = false; 20226 } 20227 render({ 20228 viewport 20229 }) { 20230 this.viewport = viewport; 20231 setLayerDimensions(this.div, viewport); 20232 for (const editor of this.#uiManager.getEditors(this.pageIndex)) { 20233 this.add(editor); 20234 editor.rebuild(); 20235 } 20236 this.updateMode(); 20237 } 20238 update({ 20239 viewport 20240 }) { 20241 this.#uiManager.commitOrRemove(); 20242 this.#cleanup(); 20243 const oldRotation = this.viewport.rotation; 20244 const rotation = viewport.rotation; 20245 this.viewport = viewport; 20246 setLayerDimensions(this.div, { 20247 rotation 20248 }); 20249 if (oldRotation !== rotation) { 20250 for (const editor of this.#editors.values()) { 20251 editor.rotate(rotation); 20252 } 20253 } 20254 this.addInkEditorIfNeeded(false); 20255 } 20256 get pageDimensions() { 20257 const { 20258 pageWidth, 20259 pageHeight 20260 } = this.viewport.rawDims; 20261 return [pageWidth, pageHeight]; 20262 } 20263 get scale() { 20264 return this.#uiManager.viewParameters.realScale; 20265 } 20266} 20267 20268;// ./src/display/draw_layer.js 20269 20270 20271class DrawLayer { 20272 #parent = null; 20273 #id = 0; 20274 #mapping = new Map(); 20275 #toUpdate = new Map(); 20276 constructor({ 20277 pageIndex 20278 }) { 20279 this.pageIndex = pageIndex; 20280 } 20281 setParent(parent) { 20282 if (!this.#parent) { 20283 this.#parent = parent; 20284 return; 20285 } 20286 if (this.#parent !== parent) { 20287 if (this.#mapping.size > 0) { 20288 for (const root of this.#mapping.values()) { 20289 root.remove(); 20290 parent.append(root); 20291 } 20292 } 20293 this.#parent = parent; 20294 } 20295 } 20296 static get _svgFactory() { 20297 return shadow(this, "_svgFactory", new DOMSVGFactory()); 20298 } 20299 static #setBox(element, { 20300 x = 0, 20301 y = 0, 20302 width = 1, 20303 height = 1 20304 } = {}) { 20305 const { 20306 style 20307 } = element; 20308 style.top = `${100 * y}%`; 20309 style.left = `${100 * x}%`; 20310 style.width = `${100 * width}%`; 20311 style.height = `${100 * height}%`; 20312 } 20313 #createSVG(box) { 20314 const svg = DrawLayer._svgFactory.create(1, 1, true); 20315 this.#parent.append(svg); 20316 svg.setAttribute("aria-hidden", true); 20317 DrawLayer.#setBox(svg, box); 20318 return svg; 20319 } 20320 #createClipPath(defs, pathId) { 20321 const clipPath = DrawLayer._svgFactory.createElement("clipPath"); 20322 defs.append(clipPath); 20323 const clipPathId = `clip_${pathId}`; 20324 clipPath.setAttribute("id", clipPathId); 20325 clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); 20326 const clipPathUse = DrawLayer._svgFactory.createElement("use"); 20327 clipPath.append(clipPathUse); 20328 clipPathUse.setAttribute("href", `#${pathId}`); 20329 clipPathUse.classList.add("clip"); 20330 return clipPathId; 20331 } 20332 draw(outlines, color, opacity, isPathUpdatable = false) { 20333 const id = this.#id++; 20334 const root = this.#createSVG(outlines.box); 20335 root.classList.add(...outlines.classNamesForDrawing); 20336 const defs = DrawLayer._svgFactory.createElement("defs"); 20337 root.append(defs); 20338 const path = DrawLayer._svgFactory.createElement("path"); 20339 defs.append(path); 20340 const pathId = `path_p${this.pageIndex}_${id}`; 20341 path.setAttribute("id", pathId); 20342 path.setAttribute("d", outlines.toSVGPath()); 20343 if (isPathUpdatable) { 20344 this.#toUpdate.set(id, path); 20345 } 20346 const clipPathId = this.#createClipPath(defs, pathId); 20347 const use = DrawLayer._svgFactory.createElement("use"); 20348 root.append(use); 20349 root.setAttribute("fill", color); 20350 root.setAttribute("fill-opacity", opacity); 20351 use.setAttribute("href", `#${pathId}`); 20352 this.#mapping.set(id, root); 20353 return { 20354 id, 20355 clipPathId: `url(#${clipPathId})` 20356 }; 20357 } 20358 drawOutline(outlines) { 20359 const id = this.#id++; 20360 const root = this.#createSVG(outlines.box); 20361 root.classList.add(...outlines.classNamesForOutlining); 20362 const defs = DrawLayer._svgFactory.createElement("defs"); 20363 root.append(defs); 20364 const path = DrawLayer._svgFactory.createElement("path"); 20365 defs.append(path); 20366 const pathId = `path_p${this.pageIndex}_${id}`; 20367 path.setAttribute("id", pathId); 20368 path.setAttribute("d", outlines.toSVGPath()); 20369 path.setAttribute("vector-effect", "non-scaling-stroke"); 20370 let maskId; 20371 if (outlines.mustRemoveSelfIntersections) { 20372 const mask = DrawLayer._svgFactory.createElement("mask"); 20373 defs.append(mask); 20374 maskId = `mask_p${this.pageIndex}_${id}`; 20375 mask.setAttribute("id", maskId); 20376 mask.setAttribute("maskUnits", "objectBoundingBox"); 20377 const rect = DrawLayer._svgFactory.createElement("rect"); 20378 mask.append(rect); 20379 rect.setAttribute("width", "1"); 20380 rect.setAttribute("height", "1"); 20381 rect.setAttribute("fill", "white"); 20382 const use = DrawLayer._svgFactory.createElement("use"); 20383 mask.append(use); 20384 use.setAttribute("href", `#${pathId}`); 20385 use.setAttribute("stroke", "none"); 20386 use.setAttribute("fill", "black"); 20387 use.setAttribute("fill-rule", "nonzero"); 20388 use.classList.add("mask"); 20389 } 20390 const use1 = DrawLayer._svgFactory.createElement("use"); 20391 root.append(use1); 20392 use1.setAttribute("href", `#${pathId}`); 20393 if (maskId) { 20394 use1.setAttribute("mask", `url(#${maskId})`); 20395 } 20396 const use2 = use1.cloneNode(); 20397 root.append(use2); 20398 use1.classList.add("mainOutline"); 20399 use2.classList.add("secondaryOutline"); 20400 this.#mapping.set(id, root); 20401 return id; 20402 } 20403 finalizeLine(id, line) { 20404 const path = this.#toUpdate.get(id); 20405 this.#toUpdate.delete(id); 20406 this.updateBox(id, line.box); 20407 path.setAttribute("d", line.toSVGPath()); 20408 } 20409 updateLine(id, line) { 20410 const root = this.#mapping.get(id); 20411 const defs = root.firstChild; 20412 const path = defs.firstChild; 20413 path.setAttribute("d", line.toSVGPath()); 20414 } 20415 updatePath(id, line) { 20416 this.#toUpdate.get(id).setAttribute("d", line.toSVGPath()); 20417 } 20418 updateBox(id, box) { 20419 DrawLayer.#setBox(this.#mapping.get(id), box); 20420 } 20421 show(id, visible) { 20422 this.#mapping.get(id).classList.toggle("hidden", !visible); 20423 } 20424 rotate(id, angle) { 20425 this.#mapping.get(id).setAttribute("data-main-rotation", angle); 20426 } 20427 changeColor(id, color) { 20428 this.#mapping.get(id).setAttribute("fill", color); 20429 } 20430 changeOpacity(id, opacity) { 20431 this.#mapping.get(id).setAttribute("fill-opacity", opacity); 20432 } 20433 addClass(id, className) { 20434 this.#mapping.get(id).classList.add(className); 20435 } 20436 removeClass(id, className) { 20437 this.#mapping.get(id).classList.remove(className); 20438 } 20439 getSVGRoot(id) { 20440 return this.#mapping.get(id); 20441 } 20442 remove(id) { 20443 this.#toUpdate.delete(id); 20444 if (this.#parent === null) { 20445 return; 20446 } 20447 this.#mapping.get(id).remove(); 20448 this.#mapping.delete(id); 20449 } 20450 destroy() { 20451 this.#parent = null; 20452 for (const root of this.#mapping.values()) { 20453 root.remove(); 20454 } 20455 this.#mapping.clear(); 20456 this.#toUpdate.clear(); 20457 } 20458} 20459 20460;// ./src/pdf.js 20461 20462 20463 20464 20465 20466 20467 20468 20469 20470 20471 20472 20473 20474const pdfjsVersion = "4.8.69"; 20475const pdfjsBuild = "3634dab10"; 20476 20477var __webpack_exports__AbortException = __webpack_exports__.AbortException; 20478var __webpack_exports__AnnotationEditorLayer = __webpack_exports__.AnnotationEditorLayer; 20479var __webpack_exports__AnnotationEditorParamsType = __webpack_exports__.AnnotationEditorParamsType; 20480var __webpack_exports__AnnotationEditorType = __webpack_exports__.AnnotationEditorType; 20481var __webpack_exports__AnnotationEditorUIManager = __webpack_exports__.AnnotationEditorUIManager; 20482var __webpack_exports__AnnotationLayer = __webpack_exports__.AnnotationLayer; 20483var __webpack_exports__AnnotationMode = __webpack_exports__.AnnotationMode; 20484var __webpack_exports__ColorPicker = __webpack_exports__.ColorPicker; 20485var __webpack_exports__DOMSVGFactory = __webpack_exports__.DOMSVGFactory; 20486var __webpack_exports__DrawLayer = __webpack_exports__.DrawLayer; 20487var __webpack_exports__FeatureTest = __webpack_exports__.FeatureTest; 20488var __webpack_exports__GlobalWorkerOptions = __webpack_exports__.GlobalWorkerOptions; 20489var __webpack_exports__ImageKind = __webpack_exports__.ImageKind; 20490var __webpack_exports__InvalidPDFException = __webpack_exports__.InvalidPDFException; 20491var __webpack_exports__MissingPDFException = __webpack_exports__.MissingPDFException; 20492var __webpack_exports__OPS = __webpack_exports__.OPS; 20493var __webpack_exports__OutputScale = __webpack_exports__.OutputScale; 20494var __webpack_exports__PDFDataRangeTransport = __webpack_exports__.PDFDataRangeTransport; 20495var __webpack_exports__PDFDateString = __webpack_exports__.PDFDateString; 20496var __webpack_exports__PDFWorker = __webpack_exports__.PDFWorker; 20497var __webpack_exports__PasswordResponses = __webpack_exports__.PasswordResponses; 20498var __webpack_exports__PermissionFlag = __webpack_exports__.PermissionFlag; 20499var __webpack_exports__PixelsPerInch = __webpack_exports__.PixelsPerInch; 20500var __webpack_exports__RenderingCancelledException = __webpack_exports__.RenderingCancelledException; 20501var __webpack_exports__TextLayer = __webpack_exports__.TextLayer; 20502var __webpack_exports__UnexpectedResponseException = __webpack_exports__.UnexpectedResponseException; 20503var __webpack_exports__Util = __webpack_exports__.Util; 20504var __webpack_exports__VerbosityLevel = __webpack_exports__.VerbosityLevel; 20505var __webpack_exports__XfaLayer = __webpack_exports__.XfaLayer; 20506var __webpack_exports__build = __webpack_exports__.build; 20507var __webpack_exports__createValidAbsoluteUrl = __webpack_exports__.createValidAbsoluteUrl; 20508var __webpack_exports__fetchData = __webpack_exports__.fetchData; 20509var __webpack_exports__getDocument = __webpack_exports__.getDocument; 20510var __webpack_exports__getFilenameFromUrl = __webpack_exports__.getFilenameFromUrl; 20511var __webpack_exports__getPdfFilenameFromUrl = __webpack_exports__.getPdfFilenameFromUrl; 20512var __webpack_exports__getXfaPageViewport = __webpack_exports__.getXfaPageViewport; 20513var __webpack_exports__isDataScheme = __webpack_exports__.isDataScheme; 20514var __webpack_exports__isPdfFile = __webpack_exports__.isPdfFile; 20515var __webpack_exports__noContextMenu = __webpack_exports__.noContextMenu; 20516var __webpack_exports__normalizeUnicode = __webpack_exports__.normalizeUnicode; 20517var __webpack_exports__setLayerDimensions = __webpack_exports__.setLayerDimensions; 20518var __webpack_exports__shadow = __webpack_exports__.shadow; 20519var __webpack_exports__version = __webpack_exports__.version; 20520export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__ColorPicker as ColorPicker, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__OutputScale as OutputScale, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__TextLayer as TextLayer, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__version as version }; 20521
20522//# sourceMappingURL=pdf.mjs.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.