vendor: 4,177 bytes, lines 1-169
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/** 24 * pdfjsVersion = 5.4.624 25 * pdfjsBuild = 384c6208b 26 */ 27/******/ // The require scope 28/******/ var __webpack_require__ = {}; 29/******/ 30/************************************************************************/ 31/******/ /* webpack/runtime/define property getters */ 32/******/ (() => { 33/******/ // define getter functions for harmony exports 34/******/ __webpack_require__.d = (exports, definition) => { 35/******/ for(var key in definition) { 36/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 37/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 38/******/ } 39/******/ } 40/******/ }; 41/******/ })(); 42/******/ 43/******/ /* webpack/runtime/hasOwnProperty shorthand */ 44/******/ (() => { 45/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 46/******/ })(); 47/******/ 48/************************************************************************/ 49var __webpack_exports__ = {}; 50 51;// ./src/shared/util.js 52const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); 53const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; 54const LINE_FACTOR = 1.35; 55const LINE_DESCENT_FACTOR = 0.35; 56const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR; 57const RenderingIntentFlag = { 58 ANY: 0x01, 59 DISPLAY: 0x02, 60 PRINT: 0x04, 61 SAVE: 0x08, 62 ANNOTATIONS_FORMS: 0x10, 63 ANNOTATIONS_STORAGE: 0x20, 64 ANNOTATIONS_DISABLE: 0x40, 65 IS_EDITING: 0x80, 66 OPLIST: 0x100 67}; 68const AnnotationMode = { 69 DISABLE: 0, 70 ENABLE: 1, 71 ENABLE_FORMS: 2, 72 ENABLE_STORAGE: 3 73}; 74const AnnotationEditorPrefix = "pdfjs_internal_editor_"; 75const AnnotationEditorType = { 76 DISABLE: -1, 77 NONE: 0, 78 FREETEXT: 3, 79 HIGHLIGHT: 9, 80 STAMP: 13, 81 INK: 15, 82 POPUP: 16, 83 SIGNATURE: 101, 84 COMMENT: 102 85}; 86const AnnotationEditorParamsType = { 87 RESIZE: 1, 88 CREATE: 2, 89 FREETEXT_SIZE: 11, 90 FREETEXT_COLOR: 12, 91 FREETEXT_OPACITY: 13, 92 INK_COLOR: 21, 93 INK_THICKNESS: 22, 94 INK_OPACITY: 23, 95 HIGHLIGHT_COLOR: 31, 96 HIGHLIGHT_THICKNESS: 32, 97 HIGHLIGHT_FREE: 33, 98 HIGHLIGHT_SHOW_ALL: 34, 99 DRAW_STEP: 41 100}; 101const PermissionFlag = { 102 PRINT: 0x04, 103 MODIFY_CONTENTS: 0x08, 104 COPY: 0x10, 105 MODIFY_ANNOTATIONS: 0x20, 106 FILL_INTERACTIVE_FORMS: 0x100, 107 COPY_FOR_ACCESSIBILITY: 0x200, 108 ASSEMBLE: 0x400, 109 PRINT_HIGH_QUALITY: 0x800 110}; 111const MeshFigureType = { 112 TRIANGLES: 1, 113 LATTICE: 2, 114 PATCH: 3 115}; 116const TextRenderingMode = { 117 FILL: 0, 118 STROKE: 1, 119 FILL_STROKE: 2, 120 INVISIBLE: 3, 121 FILL_ADD_TO_PATH: 4, 122 STROKE_ADD_TO_PATH: 5, 123 FILL_STROKE_ADD_TO_PATH: 6, 124 ADD_TO_PATH: 7, 125 FILL_STROKE_MASK: 3, 126 ADD_TO_PATH_FLAG: 4 127}; 128const util_ImageKind = { 129 GRAYSCALE_1BPP: 1, 130 RGB_24BPP: 2, 131 RGBA_32BPP: 3 132}; 133const AnnotationType = { 134 TEXT: 1, 135 LINK: 2, 136 FREETEXT: 3, 137 LINE: 4, 138 SQUARE: 5, 139 CIRCLE: 6, 140 POLYGON: 7, 141 POLYLINE: 8, 142 HIGHLIGHT: 9, 143 UNDERLINE: 10, 144 SQUIGGLY: 11, 145 STRIKEOUT: 12, 146 STAMP: 13, 147 CARET: 14, 148 INK: 15, 149 POPUP: 16, 150 FILEATTACHMENT: 17, 151 SOUND: 18, 152 MOVIE: 19, 153 WIDGET: 20, 154 SCREEN: 21, 155 PRINTERMARK: 22, 156 TRAPNET: 23, 157 WATERMARK: 24, 158 THREED: 25, 159 REDACT: 26 160}; 161const AnnotationReplyType = { 162 GROUP: "Group", 163 REPLY: "R" 164}; 165const AnnotationFlag = { 166 INVISIBLE: 0x01, 167 HIDDEN: 0x02, 168 PRINT: 0x04, 169 NOZOOM: 0x08,
170 NOROTATE: 0x10, 171 NOVIEW: 0x20, 172 READONLY: 0x40, 173 LOCKED: 0x80, 174 TOGGLENOVIEW: 0x100, 175 LOCKEDCONTENTS: 0x200 176}; 177const AnnotationFieldFlag = { 178 READONLY: 0x0000001, 179 REQUIRED: 0x0000002, 180 NOEXPORT: 0x0000004, 181 MULTILINE: 0x0001000, 182 PASSWORD: 0x0002000, 183 NOTOGGLETOOFF: 0x0004000, 184 RADIO: 0x0008000, 185 PUSHBUTTON: 0x0010000, 186 COMBO: 0x0020000, 187 EDIT: 0x0040000, 188 SORT: 0x0080000, 189 FILESELECT: 0x0100000, 190 MULTISELECT: 0x0200000, 191 DONOTSPELLCHECK: 0x0400000, 192 DONOTSCROLL: 0x0800000, 193 COMB: 0x1000000, 194 RICHTEXT: 0x2000000, 195 RADIOSINUNISON: 0x2000000, 196 COMMITONSELCHANGE: 0x4000000 197}; 198const AnnotationBorderStyleType = { 199 SOLID: 1, 200 DASHED: 2, 201 BEVELED: 3, 202 INSET: 4, 203 UNDERLINE: 5 204}; 205const AnnotationActionEventType = { 206 E: "Mouse Enter", 207 X: "Mouse Exit", 208 D: "Mouse Down", 209 U: "Mouse Up", 210 Fo: "Focus", 211 Bl: "Blur", 212 PO: "PageOpen", 213 PC: "PageClose", 214 PV: "PageVisible", 215 PI: "PageInvisible", 216 K: "Keystroke", 217 F: "Format", 218 V: "Validate", 219 C: "Calculate" 220}; 221const DocumentActionEventType = { 222 WC: "WillClose", 223 WS: "WillSave", 224 DS: "DidSave", 225 WP: "WillPrint", 226 DP: "DidPrint" 227}; 228const PageActionEventType = { 229 O: "PageOpen", 230 C: "PageClose" 231}; 232const VerbosityLevel = { 233 ERRORS: 0, 234 WARNINGS: 1, 235 INFOS: 5 236}; 237const OPS = { 238 dependency: 1, 239 setLineWidth: 2, 240 setLineCap: 3, 241 setLineJoin: 4, 242 setMiterLimit: 5, 243 setDash: 6, 244 setRenderingIntent: 7, 245 setFlatness: 8, 246 setGState: 9, 247 save: 10, 248 restore: 11, 249 transform: 12, 250 moveTo: 13, 251 lineTo: 14, 252 curveTo: 15, 253 curveTo2: 16,
vendor: 5,587 bytes, lines 254-487
254 curveTo3: 17, 255 closePath: 18, 256 rectangle: 19, 257 stroke: 20, 258 closeStroke: 21, 259 fill: 22, 260 eoFill: 23, 261 fillStroke: 24, 262 eoFillStroke: 25, 263 closeFillStroke: 26, 264 closeEOFillStroke: 27, 265 endPath: 28, 266 clip: 29, 267 eoClip: 30, 268 beginText: 31, 269 endText: 32, 270 setCharSpacing: 33, 271 setWordSpacing: 34, 272 setHScale: 35, 273 setLeading: 36, 274 setFont: 37, 275 setTextRenderingMode: 38, 276 setTextRise: 39, 277 moveText: 40, 278 setLeadingMoveText: 41, 279 setTextMatrix: 42, 280 nextLine: 43, 281 showText: 44, 282 showSpacedText: 45, 283 nextLineShowText: 46, 284 nextLineSetSpacingShowText: 47, 285 setCharWidth: 48, 286 setCharWidthAndBounds: 49, 287 setStrokeColorSpace: 50, 288 setFillColorSpace: 51, 289 setStrokeColor: 52, 290 setStrokeColorN: 53, 291 setFillColor: 54, 292 setFillColorN: 55, 293 setStrokeGray: 56, 294 setFillGray: 57, 295 setStrokeRGBColor: 58, 296 setFillRGBColor: 59, 297 setStrokeCMYKColor: 60, 298 setFillCMYKColor: 61, 299 shadingFill: 62, 300 beginInlineImage: 63, 301 beginImageData: 64, 302 endInlineImage: 65, 303 paintXObject: 66, 304 markPoint: 67, 305 markPointProps: 68, 306 beginMarkedContent: 69, 307 beginMarkedContentProps: 70, 308 endMarkedContent: 71, 309 beginCompat: 72, 310 endCompat: 73, 311 paintFormXObjectBegin: 74, 312 paintFormXObjectEnd: 75, 313 beginGroup: 76, 314 endGroup: 77, 315 beginAnnotation: 80, 316 endAnnotation: 81, 317 paintImageMaskXObject: 83, 318 paintImageMaskXObjectGroup: 84, 319 paintImageXObject: 85, 320 paintInlineImageXObject: 86, 321 paintInlineImageXObjectGroup: 87, 322 paintImageXObjectRepeat: 88, 323 paintImageMaskXObjectRepeat: 89, 324 paintSolidColorImageMask: 90, 325 constructPath: 91, 326 setStrokeTransparent: 92, 327 setFillTransparent: 93, 328 rawFillPath: 94 329}; 330const DrawOPS = { 331 moveTo: 0, 332 lineTo: 1, 333 curveTo: 2, 334 quadraticCurveTo: 3, 335 closePath: 4 336}; 337const PasswordResponses = { 338 NEED_PASSWORD: 1, 339 INCORRECT_PASSWORD: 2 340}; 341let verbosity = VerbosityLevel.WARNINGS; 342function setVerbosityLevel(level) { 343 if (Number.isInteger(level)) { 344 verbosity = level; 345 } 346} 347function getVerbosityLevel() { 348 return verbosity; 349} 350function info(msg) { 351 if (verbosity >= VerbosityLevel.INFOS) { 352 console.info(`Info: ${msg}`); 353 } 354} 355function warn(msg) { 356 if (verbosity >= VerbosityLevel.WARNINGS) { 357 console.warn(`Warning: ${msg}`); 358 } 359} 360function unreachable(msg) { 361 throw new Error(msg); 362} 363function assert(cond, msg) { 364 if (!cond) { 365 unreachable(msg); 366 } 367} 368function _isValidProtocol(url) { 369 switch (url?.protocol) { 370 case "http:": 371 case "https:": 372 case "ftp:": 373 case "mailto:": 374 case "tel:": 375 return true; 376 default: 377 return false; 378 } 379} 380function createValidAbsoluteUrl(url, baseUrl = null, options = null) { 381 if (!url) { 382 return null; 383 } 384 if (options && typeof url === "string") { 385 if (options.addDefaultProtocol && url.startsWith("www.")) { 386 const dots = url.match(/\./g); 387 if (dots?.length >= 2) { 388 url = `http://${url}`; 389 } 390 } 391 if (options.tryConvertEncoding) { 392 try { 393 url = stringToUTF8String(url); 394 } catch {} 395 } 396 } 397 const absoluteUrl = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url); 398 return _isValidProtocol(absoluteUrl) ? absoluteUrl : null; 399} 400function updateUrlHash(url, hash, allowRel = false) { 401 const res = URL.parse(url); 402 if (res) { 403 res.hash = hash; 404 return res.href; 405 } 406 if (allowRel && createValidAbsoluteUrl(url, "http://example.com")) { 407 return url.split("#", 1)[0] + `${hash ? `#${hash}` : ""}`; 408 } 409 return ""; 410} 411function shadow(obj, prop, value, nonSerializable = false) { 412 Object.defineProperty(obj, prop, { 413 value, 414 enumerable: !nonSerializable, 415 configurable: true, 416 writable: false 417 }); 418 return value; 419} 420const BaseException = function BaseExceptionClosure() { 421 function BaseException(message, name) { 422 this.message = message; 423 this.name = name; 424 } 425 BaseException.prototype = new Error(); 426 BaseException.constructor = BaseException; 427 return BaseException; 428}(); 429class PasswordException extends BaseException { 430 constructor(msg, code) { 431 super(msg, "PasswordException"); 432 this.code = code; 433 } 434} 435class UnknownErrorException extends BaseException { 436 constructor(msg, details) { 437 super(msg, "UnknownErrorException"); 438 this.details = details; 439 } 440} 441class InvalidPDFException extends BaseException { 442 constructor(msg) { 443 super(msg, "InvalidPDFException"); 444 } 445} 446class ResponseException extends BaseException { 447 constructor(msg, status, missing) { 448 super(msg, "ResponseException"); 449 this.status = status; 450 this.missing = missing; 451 } 452} 453class FormatError extends BaseException { 454 constructor(msg) { 455 super(msg, "FormatError"); 456 } 457} 458class AbortException extends BaseException { 459 constructor(msg) { 460 super(msg, "AbortException"); 461 } 462} 463function bytesToString(bytes) { 464 if (typeof bytes !== "object" || bytes?.length === undefined) { 465 unreachable("Invalid argument for bytesToString"); 466 } 467 const length = bytes.length; 468 const MAX_ARGUMENT_COUNT = 8192; 469 if (length < MAX_ARGUMENT_COUNT) { 470 return String.fromCharCode.apply(null, bytes); 471 } 472 const strBuf = []; 473 for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) { 474 const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); 475 const chunk = bytes.subarray(i, chunkEnd); 476 strBuf.push(String.fromCharCode.apply(null, chunk)); 477 } 478 return strBuf.join(""); 479} 480function stringToBytes(str) { 481 if (typeof str !== "string") { 482 unreachable("Invalid argument for stringToBytes"); 483 } 484 const length = str.length; 485 const bytes = new Uint8Array(length); 486 for (let i = 0; i < length; ++i) { 487 bytes[i] = str.charCodeAt(i) & 0xff;
vendor: 10,311 bytes, lines 488-780
488 } 489 return bytes; 490} 491function string32(value) { 492 return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff); 493} 494function objectSize(obj) { 495 return Object.keys(obj).length; 496} 497function isLittleEndian() { 498 const buffer8 = new Uint8Array(4); 499 buffer8[0] = 1; 500 const view32 = new Uint32Array(buffer8.buffer, 0, 1); 501 return view32[0] === 1; 502} 503function isEvalSupported() { 504 try { 505 new Function(""); 506 return true; 507 } catch { 508 return false; 509 } 510} 511class util_FeatureTest { 512 static get isLittleEndian() { 513 return shadow(this, "isLittleEndian", isLittleEndian()); 514 } 515 static get isEvalSupported() { 516 return shadow(this, "isEvalSupported", isEvalSupported()); 517 } 518 static get isOffscreenCanvasSupported() { 519 return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined"); 520 } 521 static get isImageDecoderSupported() { 522 return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined"); 523 } 524 static get isFloat16ArraySupported() { 525 return shadow(this, "isFloat16ArraySupported", typeof Float16Array !== "undefined"); 526 } 527 static get isSanitizerSupported() { 528 return shadow(this, "isSanitizerSupported", typeof Sanitizer !== "undefined"); 529 } 530 static get platform() { 531 const { 532 platform, 533 userAgent 534 } = navigator; 535 return shadow(this, "platform", { 536 isAndroid: userAgent.includes("Android"), 537 isLinux: platform.includes("Linux"), 538 isMac: platform.includes("Mac"), 539 isWindows: platform.includes("Win"), 540 isFirefox: userAgent.includes("Firefox") 541 }); 542 } 543 static get isCSSRoundSupported() { 544 return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)")); 545 } 546} 547const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0")); 548class Util { 549 static makeHexColor(r, g, b) { 550 return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; 551 } 552 static domMatrixToTransform(dm) { 553 return [dm.a, dm.b, dm.c, dm.d, dm.e, dm.f]; 554 } 555 static scaleMinMax(transform, minMax) { 556 let temp; 557 if (transform[0]) { 558 if (transform[0] < 0) { 559 temp = minMax[0]; 560 minMax[0] = minMax[2]; 561 minMax[2] = temp; 562 } 563 minMax[0] *= transform[0]; 564 minMax[2] *= transform[0]; 565 if (transform[3] < 0) { 566 temp = minMax[1]; 567 minMax[1] = minMax[3]; 568 minMax[3] = temp; 569 } 570 minMax[1] *= transform[3]; 571 minMax[3] *= transform[3]; 572 } else { 573 temp = minMax[0]; 574 minMax[0] = minMax[1]; 575 minMax[1] = temp; 576 temp = minMax[2]; 577 minMax[2] = minMax[3]; 578 minMax[3] = temp; 579 if (transform[1] < 0) { 580 temp = minMax[1]; 581 minMax[1] = minMax[3]; 582 minMax[3] = temp; 583 } 584 minMax[1] *= transform[1]; 585 minMax[3] *= transform[1]; 586 if (transform[2] < 0) { 587 temp = minMax[0]; 588 minMax[0] = minMax[2]; 589 minMax[2] = temp; 590 } 591 minMax[0] *= transform[2]; 592 minMax[2] *= transform[2]; 593 } 594 minMax[0] += transform[4]; 595 minMax[1] += transform[5]; 596 minMax[2] += transform[4]; 597 minMax[3] += transform[5]; 598 } 599 static transform(m1, m2) { 600 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]]; 601 } 602 static multiplyByDOMMatrix(m, md) { 603 return [m[0] * md.a + m[2] * md.b, m[1] * md.a + m[3] * md.b, m[0] * md.c + m[2] * md.d, m[1] * md.c + m[3] * md.d, m[0] * md.e + m[2] * md.f + m[4], m[1] * md.e + m[3] * md.f + m[5]]; 604 } 605 static applyTransform(p, m, pos = 0) { 606 const p0 = p[pos]; 607 const p1 = p[pos + 1]; 608 p[pos] = p0 * m[0] + p1 * m[2] + m[4]; 609 p[pos + 1] = p0 * m[1] + p1 * m[3] + m[5]; 610 } 611 static applyTransformToBezier(p, transform, pos = 0) { 612 const m0 = transform[0]; 613 const m1 = transform[1]; 614 const m2 = transform[2]; 615 const m3 = transform[3]; 616 const m4 = transform[4]; 617 const m5 = transform[5]; 618 for (let i = 0; i < 6; i += 2) { 619 const pI = p[pos + i]; 620 const pI1 = p[pos + i + 1]; 621 p[pos + i] = pI * m0 + pI1 * m2 + m4; 622 p[pos + i + 1] = pI * m1 + pI1 * m3 + m5; 623 } 624 } 625 static applyInverseTransform(p, m) { 626 const p0 = p[0]; 627 const p1 = p[1]; 628 const d = m[0] * m[3] - m[1] * m[2]; 629 p[0] = (p0 * m[3] - p1 * m[2] + m[2] * m[5] - m[4] * m[3]) / d; 630 p[1] = (-p0 * m[1] + p1 * m[0] + m[4] * m[1] - m[5] * m[0]) / d; 631 } 632 static axialAlignedBoundingBox(rect, transform, output) { 633 const m0 = transform[0]; 634 const m1 = transform[1]; 635 const m2 = transform[2]; 636 const m3 = transform[3]; 637 const m4 = transform[4]; 638 const m5 = transform[5]; 639 const r0 = rect[0]; 640 const r1 = rect[1]; 641 const r2 = rect[2]; 642 const r3 = rect[3]; 643 let a0 = m0 * r0 + m4; 644 let a2 = a0; 645 let a1 = m0 * r2 + m4; 646 let a3 = a1; 647 let b0 = m3 * r1 + m5; 648 let b2 = b0; 649 let b1 = m3 * r3 + m5; 650 let b3 = b1; 651 if (m1 !== 0 || m2 !== 0) { 652 const m1r0 = m1 * r0; 653 const m1r2 = m1 * r2; 654 const m2r1 = m2 * r1; 655 const m2r3 = m2 * r3; 656 a0 += m2r1; 657 a3 += m2r1; 658 a1 += m2r3; 659 a2 += m2r3; 660 b0 += m1r0; 661 b3 += m1r0; 662 b1 += m1r2; 663 b2 += m1r2; 664 } 665 output[0] = Math.min(output[0], a0, a1, a2, a3); 666 output[1] = Math.min(output[1], b0, b1, b2, b3); 667 output[2] = Math.max(output[2], a0, a1, a2, a3); 668 output[3] = Math.max(output[3], b0, b1, b2, b3); 669 } 670 static inverseTransform(m) { 671 const d = m[0] * m[3] - m[1] * m[2]; 672 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]; 673 } 674 static singularValueDecompose2dScale(matrix, output) { 675 const m0 = matrix[0]; 676 const m1 = matrix[1]; 677 const m2 = matrix[2]; 678 const m3 = matrix[3]; 679 const a = m0 ** 2 + m1 ** 2; 680 const b = m0 * m2 + m1 * m3; 681 const c = m2 ** 2 + m3 ** 2; 682 const first = (a + c) / 2; 683 const second = Math.sqrt(first ** 2 - (a * c - b ** 2)); 684 output[0] = Math.sqrt(first + second || 1); 685 output[1] = Math.sqrt(first - second || 1); 686 } 687 static normalizeRect(rect) { 688 const r = rect.slice(0); 689 if (rect[0] > rect[2]) { 690 r[0] = rect[2]; 691 r[2] = rect[0]; 692 } 693 if (rect[1] > rect[3]) { 694 r[1] = rect[3]; 695 r[3] = rect[1]; 696 } 697 return r; 698 } 699 static intersect(rect1, rect2) { 700 const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2])); 701 const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2])); 702 if (xLow > xHigh) { 703 return null; 704 } 705 const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3])); 706 const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3])); 707 if (yLow > yHigh) { 708 return null; 709 } 710 return [xLow, yLow, xHigh, yHigh]; 711 } 712 static pointBoundingBox(x, y, minMax) { 713 minMax[0] = Math.min(minMax[0], x); 714 minMax[1] = Math.min(minMax[1], y); 715 minMax[2] = Math.max(minMax[2], x); 716 minMax[3] = Math.max(minMax[3], y); 717 } 718 static rectBoundingBox(x0, y0, x1, y1, minMax) { 719 minMax[0] = Math.min(minMax[0], x0, x1); 720 minMax[1] = Math.min(minMax[1], y0, y1); 721 minMax[2] = Math.max(minMax[2], x0, x1); 722 minMax[3] = Math.max(minMax[3], y0, y1); 723 } 724 static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) { 725 if (t <= 0 || t >= 1) { 726 return; 727 } 728 const mt = 1 - t; 729 const tt = t * t; 730 const ttt = tt * t; 731 const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3; 732 const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3; 733 minMax[0] = Math.min(minMax[0], x); 734 minMax[1] = Math.min(minMax[1], y); 735 minMax[2] = Math.max(minMax[2], x); 736 minMax[3] = Math.max(minMax[3], y); 737 } 738 static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) { 739 if (Math.abs(a) < 1e-12) { 740 if (Math.abs(b) >= 1e-12) { 741 this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax); 742 } 743 return; 744 } 745 const delta = b ** 2 - 4 * c * a; 746 if (delta < 0) { 747 return; 748 } 749 const sqrtDelta = Math.sqrt(delta); 750 const a2 = 2 * a; 751 this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax); 752 this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax); 753 } 754 static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) { 755 minMax[0] = Math.min(minMax[0], x0, x3); 756 minMax[1] = Math.min(minMax[1], y0, y3); 757 minMax[2] = Math.max(minMax[2], x0, x3); 758 minMax[3] = Math.max(minMax[3], y0, y3); 759 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); 760 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); 761 } 762} 763const 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])); 764function stringToPDFString(str, keepEscapeSequence = false) { 765 if (str[0] >= "\xEF") { 766 let encoding; 767 if (str[0] === "\xFE" && str[1] === "\xFF") { 768 encoding = "utf-16be"; 769 if (str.length % 2 === 1) { 770 str = str.slice(0, -1); 771 } 772 } else if (str[0] === "\xFF" && str[1] === "\xFE") { 773 encoding = "utf-16le"; 774 if (str.length % 2 === 1) { 775 str = str.slice(0, -1); 776 } 777 } else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") { 778 encoding = "utf-8"; 779 } 780 if (encoding) {
vendor: 18,792 bytes, lines 781-1454
781 try { 782 const decoder = new TextDecoder(encoding, { 783 fatal: true 784 }); 785 const buffer = stringToBytes(str); 786 const decoded = decoder.decode(buffer); 787 if (keepEscapeSequence || !decoded.includes("\x1b")) { 788 return decoded; 789 } 790 return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, ""); 791 } catch (ex) { 792 warn(`stringToPDFString: "${ex}".`); 793 } 794 } 795 } 796 const strBuf = []; 797 for (let i = 0, ii = str.length; i < ii; i++) { 798 const charCode = str.charCodeAt(i); 799 if (!keepEscapeSequence && charCode === 0x1b) { 800 while (++i < ii && str.charCodeAt(i) !== 0x1b) {} 801 continue; 802 } 803 const code = PDFStringTranslateTable[charCode]; 804 strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); 805 } 806 return strBuf.join(""); 807} 808function stringToUTF8String(str) { 809 return decodeURIComponent(escape(str)); 810} 811function utf8StringToString(str) { 812 return unescape(encodeURIComponent(str)); 813} 814function isArrayEqual(arr1, arr2) { 815 if (arr1.length !== arr2.length) { 816 return false; 817 } 818 for (let i = 0, ii = arr1.length; i < ii; i++) { 819 if (arr1[i] !== arr2[i]) { 820 return false; 821 } 822 } 823 return true; 824} 825function getModificationDate(date = new Date()) { 826 if (!(date instanceof Date)) { 827 date = new Date(date); 828 } 829 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")]; 830 return buffer.join(""); 831} 832let NormalizeRegex = null; 833let NormalizationMap = null; 834function normalizeUnicode(str) { 835 if (!NormalizeRegex) { 836 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; 837 NormalizationMap = new Map([["ſt", "ſt"]]); 838 } 839 return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2)); 840} 841function getUuid() { 842 if (typeof crypto.randomUUID === "function") { 843 return crypto.randomUUID(); 844 } 845 const buf = new Uint8Array(32); 846 crypto.getRandomValues(buf); 847 return bytesToString(buf); 848} 849const AnnotationPrefix = "pdfjs_internal_id_"; 850function _isValidExplicitDest(validRef, validName, dest) { 851 if (!Array.isArray(dest) || dest.length < 2) { 852 return false; 853 } 854 const [page, zoom, ...args] = dest; 855 if (!validRef(page) && !Number.isInteger(page)) { 856 return false; 857 } 858 if (!validName(zoom)) { 859 return false; 860 } 861 const argsLen = args.length; 862 let allowNull = true; 863 switch (zoom.name) { 864 case "XYZ": 865 if (argsLen < 2 || argsLen > 3) { 866 return false; 867 } 868 break; 869 case "Fit": 870 case "FitB": 871 return argsLen === 0; 872 case "FitH": 873 case "FitBH": 874 case "FitV": 875 case "FitBV": 876 if (argsLen > 1) { 877 return false; 878 } 879 break; 880 case "FitR": 881 if (argsLen !== 4) { 882 return false; 883 } 884 allowNull = false; 885 break; 886 default: 887 return false; 888 } 889 for (const arg of args) { 890 if (typeof arg === "number" || allowNull && arg === null) { 891 continue; 892 } 893 return false; 894 } 895 return true; 896} 897function MathClamp(v, min, max) { 898 return Math.min(Math.max(v, min), max); 899} 900if (typeof Math.sumPrecise !== "function") { 901 Math.sumPrecise = function (numbers) { 902 return numbers.reduce((a, b) => a + b, 0); 903 }; 904} 905 906;// ./src/display/xfa_text.js 907class XfaText { 908 static textContent(xfa) { 909 const items = []; 910 const output = { 911 items, 912 styles: Object.create(null) 913 }; 914 function walk(node) { 915 if (!node) { 916 return; 917 } 918 let str = null; 919 const name = node.name; 920 if (name === "#text") { 921 str = node.value; 922 } else if (!XfaText.shouldBuildText(name)) { 923 return; 924 } else if (node?.attributes?.textContent) { 925 str = node.attributes.textContent; 926 } else if (node.value) { 927 str = node.value; 928 } 929 if (str !== null) { 930 items.push({ 931 str 932 }); 933 } 934 if (!node.children) { 935 return; 936 } 937 for (const child of node.children) { 938 walk(child); 939 } 940 } 941 walk(xfa); 942 return output; 943 } 944 static shouldBuildText(name) { 945 return !(name === "textarea" || name === "input" || name === "option" || name === "select"); 946 } 947} 948 949;// ./src/display/xfa_layer.js 950 951class XfaLayer { 952 static setupStorage(html, id, element, storage, intent) { 953 const storedData = storage.getValue(id, { 954 value: null 955 }); 956 switch (element.name) { 957 case "textarea": 958 if (storedData.value !== null) { 959 html.textContent = storedData.value; 960 } 961 if (intent === "print") { 962 break; 963 } 964 html.addEventListener("input", event => { 965 storage.setValue(id, { 966 value: event.target.value 967 }); 968 }); 969 break; 970 case "input": 971 if (element.attributes.type === "radio" || element.attributes.type === "checkbox") { 972 if (storedData.value === element.attributes.xfaOn) { 973 html.setAttribute("checked", true); 974 } else if (storedData.value === element.attributes.xfaOff) { 975 html.removeAttribute("checked"); 976 } 977 if (intent === "print") { 978 break; 979 } 980 html.addEventListener("change", event => { 981 storage.setValue(id, { 982 value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff") 983 }); 984 }); 985 } else { 986 if (storedData.value !== null) { 987 html.setAttribute("value", storedData.value); 988 } 989 if (intent === "print") { 990 break; 991 } 992 html.addEventListener("input", event => { 993 storage.setValue(id, { 994 value: event.target.value 995 }); 996 }); 997 } 998 break; 999 case "select": 1000 if (storedData.value !== null) { 1001 html.setAttribute("value", storedData.value); 1002 for (const option of element.children) { 1003 if (option.attributes.value === storedData.value) { 1004 option.attributes.selected = true; 1005 } else if (option.attributes.hasOwnProperty("selected")) { 1006 delete option.attributes.selected; 1007 } 1008 } 1009 } 1010 html.addEventListener("input", event => { 1011 const options = event.target.options; 1012 const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value; 1013 storage.setValue(id, { 1014 value 1015 }); 1016 }); 1017 break; 1018 } 1019 } 1020 static setAttributes({ 1021 html, 1022 element, 1023 storage = null, 1024 intent, 1025 linkService 1026 }) { 1027 const { 1028 attributes 1029 } = element; 1030 const isHTMLAnchorElement = html instanceof HTMLAnchorElement; 1031 if (attributes.type === "radio") { 1032 attributes.name = `${attributes.name}-${intent}`; 1033 } 1034 for (const [key, value] of Object.entries(attributes)) { 1035 if (value === null || value === undefined) { 1036 continue; 1037 } 1038 switch (key) { 1039 case "class": 1040 if (value.length) { 1041 html.setAttribute(key, value.join(" ")); 1042 } 1043 break; 1044 case "dataId": 1045 break; 1046 case "id": 1047 html.setAttribute("data-element-id", value); 1048 break; 1049 case "style": 1050 Object.assign(html.style, value); 1051 break; 1052 case "textContent": 1053 html.textContent = value; 1054 break; 1055 default: 1056 if (!isHTMLAnchorElement || key !== "href" && key !== "newWindow") { 1057 html.setAttribute(key, value); 1058 } 1059 } 1060 } 1061 if (isHTMLAnchorElement) { 1062 linkService.addLinkAttributes(html, attributes.href, attributes.newWindow); 1063 } 1064 if (storage && attributes.dataId) { 1065 this.setupStorage(html, attributes.dataId, element, storage); 1066 } 1067 } 1068 static render(parameters) { 1069 const storage = parameters.annotationStorage; 1070 const linkService = parameters.linkService; 1071 const root = parameters.xfaHtml; 1072 const intent = parameters.intent || "display"; 1073 const rootHtml = document.createElement(root.name); 1074 if (root.attributes) { 1075 this.setAttributes({ 1076 html: rootHtml, 1077 element: root, 1078 intent, 1079 linkService 1080 }); 1081 } 1082 const isNotForRichText = intent !== "richText"; 1083 const rootDiv = parameters.div; 1084 rootDiv.append(rootHtml); 1085 if (parameters.viewport) { 1086 const transform = `matrix(${parameters.viewport.transform.join(",")})`; 1087 rootDiv.style.transform = transform; 1088 } 1089 if (isNotForRichText) { 1090 rootDiv.setAttribute("class", "xfaLayer xfaFont"); 1091 } 1092 const textDivs = []; 1093 if (root.children.length === 0) { 1094 if (root.value) { 1095 const node = document.createTextNode(root.value); 1096 rootHtml.append(node); 1097 if (isNotForRichText && XfaText.shouldBuildText(root.name)) { 1098 textDivs.push(node); 1099 } 1100 } 1101 return { 1102 textDivs 1103 }; 1104 } 1105 const stack = [[root, -1, rootHtml]]; 1106 while (stack.length > 0) { 1107 const [parent, i, html] = stack.at(-1); 1108 if (i + 1 === parent.children.length) { 1109 stack.pop(); 1110 continue; 1111 } 1112 const child = parent.children[++stack.at(-1)[1]]; 1113 if (child === null) { 1114 continue; 1115 } 1116 const { 1117 name 1118 } = child; 1119 if (name === "#text") { 1120 const node = document.createTextNode(child.value); 1121 textDivs.push(node); 1122 html.append(node); 1123 continue; 1124 } 1125 const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name); 1126 html.append(childHtml); 1127 if (child.attributes) { 1128 this.setAttributes({ 1129 html: childHtml, 1130 element: child, 1131 storage, 1132 intent, 1133 linkService 1134 }); 1135 } 1136 if (child.children?.length > 0) { 1137 stack.push([child, -1, childHtml]); 1138 } else if (child.value) { 1139 const node = document.createTextNode(child.value); 1140 if (isNotForRichText && XfaText.shouldBuildText(name)) { 1141 textDivs.push(node); 1142 } 1143 childHtml.append(node); 1144 } 1145 } 1146 for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) { 1147 el.setAttribute("readOnly", true); 1148 } 1149 return { 1150 textDivs 1151 }; 1152 } 1153 static update(parameters) { 1154 const transform = `matrix(${parameters.viewport.transform.join(",")})`; 1155 parameters.div.style.transform = transform; 1156 parameters.div.hidden = false; 1157 } 1158} 1159 1160;// ./src/display/display_utils.js 1161 1162 1163const SVG_NS = "http://www.w3.org/2000/svg"; 1164class PixelsPerInch { 1165 static CSS = 96.0; 1166 static PDF = 72.0; 1167 static PDF_TO_CSS_UNITS = this.CSS / this.PDF; 1168} 1169async function fetchData(url, type = "text") { 1170 if (isValidFetchUrl(url, document.baseURI)) { 1171 const response = await fetch(url); 1172 if (!response.ok) { 1173 throw new Error(response.statusText); 1174 } 1175 switch (type) { 1176 case "arraybuffer": 1177 return response.arrayBuffer(); 1178 case "blob": 1179 return response.blob(); 1180 case "json": 1181 return response.json(); 1182 } 1183 return response.text(); 1184 } 1185 return new Promise((resolve, reject) => { 1186 const request = new XMLHttpRequest(); 1187 request.open("GET", url, true); 1188 request.responseType = type; 1189 request.onreadystatechange = () => { 1190 if (request.readyState !== XMLHttpRequest.DONE) { 1191 return; 1192 } 1193 if (request.status === 200 || request.status === 0) { 1194 switch (type) { 1195 case "arraybuffer": 1196 case "blob": 1197 case "json": 1198 resolve(request.response); 1199 return; 1200 } 1201 resolve(request.responseText); 1202 return; 1203 } 1204 reject(new Error(request.statusText)); 1205 }; 1206 request.send(null); 1207 }); 1208} 1209class PageViewport { 1210 constructor({ 1211 viewBox, 1212 userUnit, 1213 scale, 1214 rotation, 1215 offsetX = 0, 1216 offsetY = 0, 1217 dontFlip = false 1218 }) { 1219 this.viewBox = viewBox; 1220 this.userUnit = userUnit; 1221 this.scale = scale; 1222 this.rotation = rotation; 1223 this.offsetX = offsetX; 1224 this.offsetY = offsetY; 1225 scale *= userUnit; 1226 const centerX = (viewBox[2] + viewBox[0]) / 2; 1227 const centerY = (viewBox[3] + viewBox[1]) / 2; 1228 let rotateA, rotateB, rotateC, rotateD; 1229 rotation %= 360; 1230 if (rotation < 0) { 1231 rotation += 360; 1232 } 1233 switch (rotation) { 1234 case 180: 1235 rotateA = -1; 1236 rotateB = 0; 1237 rotateC = 0; 1238 rotateD = 1; 1239 break; 1240 case 90: 1241 rotateA = 0; 1242 rotateB = 1; 1243 rotateC = 1; 1244 rotateD = 0; 1245 break; 1246 case 270: 1247 rotateA = 0; 1248 rotateB = -1; 1249 rotateC = -1; 1250 rotateD = 0; 1251 break; 1252 case 0: 1253 rotateA = 1; 1254 rotateB = 0; 1255 rotateC = 0; 1256 rotateD = -1; 1257 break; 1258 default: 1259 throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees."); 1260 } 1261 if (dontFlip) { 1262 rotateC = -rotateC; 1263 rotateD = -rotateD; 1264 } 1265 let offsetCanvasX, offsetCanvasY; 1266 let width, height; 1267 if (rotateA === 0) { 1268 offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; 1269 offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; 1270 width = (viewBox[3] - viewBox[1]) * scale; 1271 height = (viewBox[2] - viewBox[0]) * scale; 1272 } else { 1273 offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; 1274 offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; 1275 width = (viewBox[2] - viewBox[0]) * scale; 1276 height = (viewBox[3] - viewBox[1]) * scale; 1277 } 1278 this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY]; 1279 this.width = width; 1280 this.height = height; 1281 } 1282 get rawDims() { 1283 const dims = this.viewBox; 1284 return shadow(this, "rawDims", { 1285 pageWidth: dims[2] - dims[0], 1286 pageHeight: dims[3] - dims[1], 1287 pageX: dims[0], 1288 pageY: dims[1] 1289 }); 1290 } 1291 clone({ 1292 scale = this.scale, 1293 rotation = this.rotation, 1294 offsetX = this.offsetX, 1295 offsetY = this.offsetY, 1296 dontFlip = false 1297 } = {}) { 1298 return new PageViewport({ 1299 viewBox: this.viewBox.slice(), 1300 userUnit: this.userUnit, 1301 scale, 1302 rotation, 1303 offsetX, 1304 offsetY, 1305 dontFlip 1306 }); 1307 } 1308 convertToViewportPoint(x, y) { 1309 const p = [x, y]; 1310 Util.applyTransform(p, this.transform); 1311 return p; 1312 } 1313 convertToViewportRectangle(rect) { 1314 const topLeft = [rect[0], rect[1]]; 1315 Util.applyTransform(topLeft, this.transform); 1316 const bottomRight = [rect[2], rect[3]]; 1317 Util.applyTransform(bottomRight, this.transform); 1318 return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]]; 1319 } 1320 convertToPdfPoint(x, y) { 1321 const p = [x, y]; 1322 Util.applyInverseTransform(p, this.transform); 1323 return p; 1324 } 1325} 1326class RenderingCancelledException extends BaseException { 1327 constructor(msg, extraDelay = 0) { 1328 super(msg, "RenderingCancelledException"); 1329 this.extraDelay = extraDelay; 1330 } 1331} 1332function isDataScheme(url) { 1333 const ii = url.length; 1334 let i = 0; 1335 while (i < ii && url[i].trim() === "") { 1336 i++; 1337 } 1338 return url.substring(i, i + 5).toLowerCase() === "data:"; 1339} 1340function isPdfFile(filename) { 1341 return typeof filename === "string" && /\.pdf$/i.test(filename); 1342} 1343function getFilenameFromUrl(url) { 1344 [url] = url.split(/[#?]/, 1); 1345 return url.substring(url.lastIndexOf("/") + 1); 1346} 1347function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") { 1348 if (typeof url !== "string") { 1349 return defaultFilename; 1350 } 1351 if (isDataScheme(url)) { 1352 warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.'); 1353 return defaultFilename; 1354 } 1355 const getURL = urlString => { 1356 try { 1357 return new URL(urlString); 1358 } catch { 1359 try { 1360 return new URL(decodeURIComponent(urlString)); 1361 } catch { 1362 try { 1363 return new URL(urlString, "https://foo.bar"); 1364 } catch { 1365 try { 1366 return new URL(decodeURIComponent(urlString), "https://foo.bar"); 1367 } catch { 1368 return null; 1369 } 1370 } 1371 } 1372 } 1373 }; 1374 const newURL = getURL(url); 1375 if (!newURL) { 1376 return defaultFilename; 1377 } 1378 const decode = name => { 1379 try { 1380 let decoded = decodeURIComponent(name); 1381 if (decoded.includes("/")) { 1382 decoded = decoded.split("/").at(-1); 1383 if (decoded.test(/^\.pdf$/i)) { 1384 return decoded; 1385 } 1386 return name; 1387 } 1388 return decoded; 1389 } catch { 1390 return name; 1391 } 1392 }; 1393 const pdfRegex = /\.pdf$/i; 1394 const filename = newURL.pathname.split("/").at(-1); 1395 if (pdfRegex.test(filename)) { 1396 return decode(filename); 1397 } 1398 if (newURL.searchParams.size > 0) { 1399 const values = Array.from(newURL.searchParams.values()).reverse(); 1400 for (const value of values) { 1401 if (pdfRegex.test(value)) { 1402 return decode(value); 1403 } 1404 } 1405 const keys = Array.from(newURL.searchParams.keys()).reverse(); 1406 for (const key of keys) { 1407 if (pdfRegex.test(key)) { 1408 return decode(key); 1409 } 1410 } 1411 } 1412 if (newURL.hash) { 1413 const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i; 1414 const hashFilename = reFilename.exec(newURL.hash); 1415 if (hashFilename) { 1416 return decode(hashFilename[0]); 1417 } 1418 } 1419 return defaultFilename; 1420} 1421class StatTimer { 1422 started = Object.create(null); 1423 times = []; 1424 time(name) { 1425 if (name in this.started) { 1426 warn(`Timer is already running for ${name}`); 1427 } 1428 this.started[name] = Date.now(); 1429 } 1430 timeEnd(name) { 1431 if (!(name in this.started)) { 1432 warn(`Timer has not been started for ${name}`); 1433 } 1434 this.times.push({ 1435 name, 1436 start: this.started[name], 1437 end: Date.now() 1438 }); 1439 delete this.started[name]; 1440 } 1441 toString() { 1442 const outBuf = []; 1443 let longest = 0; 1444 for (const { 1445 name 1446 } of this.times) { 1447 longest = Math.max(name.length, longest); 1448 } 1449 for (const { 1450 name, 1451 start, 1452 end 1453 } of this.times) { 1454 outBuf.push(`${name.padEnd(longest)} ${end - start}
1454ms\n`); 1455 } 1456 return outBuf.join(""); 1457 } 1458} 1459function isValidFetchUrl(url, baseUrl) { 1460 const res = baseUrl ? URL.parse(url, baseUrl) : URL.parse(url); 1461 return res?.protocol === "http:" || res?.protocol === "https:"; 1462} 1463function noContextMenu(e) { 1464 e.preventDefault(); 1465} 1466function stopEvent(e) { 1467 e.preventDefault(); 1468 e.stopPropagation(); 1469} 1470function deprecated(details) { 1471 console.log("Deprecated API usage: " + details); 1472} 1473class PDFDateString { 1474 static #regex; 1475 static toDateObject(input) { 1476 if (input instanceof Date) { 1477 return input; 1478 } 1479 if (!input || typeof input !== "string") { 1480 return null; 1481 } 1482 this.#regex ||= new RegExp("^D:" + "(\\d{4})" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "([Z|+|-])?" + "(\\d{2})?" + "'?" + "(\\d{2})?" + "'?"); 1483 const matches = this.#regex.exec(input); 1484 if (!matches) { 1485 return null; 1486 } 1487 const year = parseInt(matches[1], 10); 1488 let month = parseInt(matches[2], 10); 1489 month = month >= 1 && month <= 12 ? month - 1 : 0; 1490 let day = parseInt(matches[3], 10); 1491 day = day >= 1 && day <= 31 ? day : 1; 1492 let hour = parseInt(matches[4], 10); 1493 hour = hour >= 0 && hour <= 23 ? hour : 0; 1494 let minute = parseInt(matches[5], 10); 1495 minute = minute >= 0 && minute <= 59 ? minute : 0; 1496 let second = parseInt(matches[6], 10); 1497 second = second >= 0 && second <= 59 ? second : 0; 1498 const universalTimeRelation = matches[7] || "Z"; 1499 let offsetHour = parseInt(matches[8], 10); 1500 offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0; 1501 let offsetMinute = parseInt(matches[9], 10) || 0; 1502 offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0; 1503 if (universalTimeRelation === "-") { 1504 hour += offsetHour; 1505 minute += offsetMinute; 1506 } else if (universalTimeRelation === "+") { 1507 hour -= offsetHour; 1508 minute -= offsetMinute; 1509 } 1510 return new Date(Date.UTC(year, month, day, hour, minute, second)); 1511 } 1512} 1513function getXfaPageViewport(xfaPage, { 1514 scale = 1, 1515 rotation = 0 1516}) { 1517 const { 1518 width, 1519 height 1520 } = xfaPage.attributes.style; 1521 const viewBox = [0, 0, parseInt(width), parseInt(height)]; 1522 return new PageViewport({ 1523 viewBox, 1524 userUnit: 1, 1525 scale, 1526 rotation 1527 }); 1528} 1529function getRGB(color) { 1530 if (color.startsWith("#")) { 1531 const colorRGB = parseInt(color.slice(1), 16); 1532 return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff]; 1533 } 1534 if (color.startsWith("rgb(")) { 1535 return color.slice(4, -1).split(",").map(x => parseInt(x)); 1536 } 1537 if (color.startsWith("rgba(")) { 1538 return color.slice(5, -1).split(",").map(x => parseInt(x)).slice(0, 3); 1539 } 1540 warn(`Not a valid color format: "${color}"`); 1541 return [0, 0, 0]; 1542} 1543function getColorValues(colors) { 1544 const span = document.createElement("span"); 1545 span.style.visibility = "hidden"; 1546 span.style.colorScheme = "only light"; 1547 document.body.append(span); 1548 for (const name of colors.keys()) { 1549 span.style.color = name; 1550 const computedColor = window.getComputedStyle(span).color; 1551 colors.set(name, getRGB(computedColor)); 1552 } 1553 span.remove(); 1554} 1555function getCurrentTransform(ctx) { 1556 const { 1557 a, 1558 b, 1559 c, 1560 d, 1561 e, 1562 f 1563 } = ctx.getTransform(); 1564 return [a, b, c, d, e, f]; 1565} 1566function getCurrentTransformInverse(ctx) { 1567 const { 1568 a, 1569 b, 1570 c, 1571 d, 1572 e, 1573 f 1574 } = ctx.getTransform().invertSelf(); 1575 return [a, b, c, d, e, f]; 1576} 1577function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) { 1578 if (viewport instanceof PageViewport) { 1579 const { 1580 pageWidth, 1581 pageHeight 1582 } = viewport.rawDims; 1583 const { 1584 style 1585 } = div; 1586 const useRound = util_FeatureTest.isCSSRoundSupported; 1587 const w = `var(--total-scale-factor) * ${pageWidth}px`, 1588 h = `var(--total-scale-factor) * ${pageHeight}px`; 1589 const widthStr = useRound ? `round(down, ${w}, var(--scale-round-x))` : `calc(${w})`, 1590 heightStr = useRound ? `round(down, ${h}, var(--scale-round-y))` : `calc(${h})`; 1591 if (!mustFlip || viewport.rotation % 180 === 0) { 1592 style.width = widthStr; 1593 style.height = heightStr; 1594 } else { 1595 style.width = heightStr; 1596 style.height = widthStr; 1597 } 1598 } 1599 if (mustRotate) { 1600 div.setAttribute("data-main-rotation", viewport.rotation); 1601 } 1602} 1603class OutputScale { 1604 constructor() { 1605 const { 1606 pixelRatio 1607 } = OutputScale; 1608 this.sx = pixelRatio; 1609 this.sy = pixelRatio; 1610 } 1611 get scaled() { 1612 return this.sx !== 1 || this.sy !== 1; 1613 } 1614 get symmetric() { 1615 return this.sx === this.sy; 1616 } 1617 limitCanvas(width, height, maxPixels, maxDim, capAreaFactor = -1) { 1618 let maxAreaScale = Infinity, 1619 maxWidthScale = Infinity, 1620 maxHeightScale = Infinity; 1621 maxPixels = OutputScale.capPixels(maxPixels, capAreaFactor); 1622 if (maxPixels > 0) { 1623 maxAreaScale = Math.sqrt(maxPixels / (width * height)); 1624 } 1625 if (maxDim !== -1) { 1626 maxWidthScale = maxDim / width; 1627 maxHeightScale = maxDim / height; 1628 } 1629 const maxScale = Math.min(maxAreaScale, maxWidthScale, maxHeightScale); 1630 if (this.sx > maxScale || this.sy > maxScale) { 1631 this.sx = maxScale; 1632 this.sy = maxScale; 1633 return true; 1634 } 1635 return false;
vendor: 6,348 bytes, lines 1636-1847
1636 } 1637 static get pixelRatio() { 1638 return globalThis.devicePixelRatio || 1; 1639 } 1640 static capPixels(maxPixels, capAreaFactor) { 1641 if (capAreaFactor >= 0) { 1642 const winPixels = Math.ceil(window.screen.availWidth * window.screen.availHeight * this.pixelRatio ** 2 * (1 + capAreaFactor / 100)); 1643 return maxPixels > 0 ? Math.min(maxPixels, winPixels) : winPixels; 1644 } 1645 return maxPixels; 1646 } 1647} 1648const SupportedImageMimeTypes = ["image/apng", "image/avif", "image/bmp", "image/gif", "image/jpeg", "image/png", "image/svg+xml", "image/webp", "image/x-icon"]; 1649class ColorScheme { 1650 static get isDarkMode() { 1651 return shadow(this, "isDarkMode", !!window?.matchMedia?.("(prefers-color-scheme: dark)").matches); 1652 } 1653} 1654class CSSConstants { 1655 static get commentForegroundColor() { 1656 const element = document.createElement("span"); 1657 element.classList.add("comment", "sidebar"); 1658 const { 1659 style 1660 } = element; 1661 style.width = style.height = "0"; 1662 style.display = "none"; 1663 style.color = "var(--comment-fg-color)"; 1664 document.body.append(element); 1665 const { 1666 color 1667 } = window.getComputedStyle(element); 1668 element.remove(); 1669 return shadow(this, "commentForegroundColor", getRGB(color)); 1670 } 1671} 1672function applyOpacity(r, g, b, opacity) { 1673 opacity = Math.min(Math.max(opacity ?? 1, 0), 1); 1674 const white = 255 * (1 - opacity); 1675 r = Math.round(r * opacity + white); 1676 g = Math.round(g * opacity + white); 1677 b = Math.round(b * opacity + white); 1678 return [r, g, b]; 1679} 1680function RGBToHSL(rgb, output) { 1681 const r = rgb[0] / 255; 1682 const g = rgb[1] / 255; 1683 const b = rgb[2] / 255; 1684 const max = Math.max(r, g, b); 1685 const min = Math.min(r, g, b); 1686 const l = (max + min) / 2; 1687 if (max === min) { 1688 output[0] = output[1] = 0; 1689 } else { 1690 const d = max - min; 1691 output[1] = l < 0.5 ? d / (max + min) : d / (2 - max - min); 1692 switch (max) { 1693 case r: 1694 output[0] = ((g - b) / d + (g < b ? 6 : 0)) * 60; 1695 break; 1696 case g: 1697 output[0] = ((b - r) / d + 2) * 60; 1698 break; 1699 case b: 1700 output[0] = ((r - g) / d + 4) * 60; 1701 break; 1702 } 1703 } 1704 output[2] = l; 1705} 1706function HSLToRGB(hsl, output) { 1707 const h = hsl[0]; 1708 const s = hsl[1]; 1709 const l = hsl[2]; 1710 const c = (1 - Math.abs(2 * l - 1)) * s; 1711 const x = c * (1 - Math.abs(h / 60 % 2 - 1)); 1712 const m = l - c / 2; 1713 switch (Math.floor(h / 60)) { 1714 case 0: 1715 output[0] = c + m; 1716 output[1] = x + m; 1717 output[2] = m; 1718 break; 1719 case 1: 1720 output[0] = x + m; 1721 output[1] = c + m; 1722 output[2] = m; 1723 break; 1724 case 2: 1725 output[0] = m; 1726 output[1] = c + m; 1727 output[2] = x + m; 1728 break; 1729 case 3: 1730 output[0] = m; 1731 output[1] = x + m; 1732 output[2] = c + m; 1733 break; 1734 case 4: 1735 output[0] = x + m; 1736 output[1] = m; 1737 output[2] = c + m; 1738 break; 1739 case 5: 1740 case 6: 1741 output[0] = c + m; 1742 output[1] = m; 1743 output[2] = x + m; 1744 break; 1745 } 1746} 1747function computeLuminance(x) { 1748 return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; 1749} 1750function contrastRatio(hsl1, hsl2, output) { 1751 HSLToRGB(hsl1, output); 1752 output.map(computeLuminance); 1753 const lum1 = 0.2126 * output[0] + 0.7152 * output[1] + 0.0722 * output[2]; 1754 HSLToRGB(hsl2, output); 1755 output.map(computeLuminance); 1756 const lum2 = 0.2126 * output[0] + 0.7152 * output[1] + 0.0722 * output[2]; 1757 return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05); 1758} 1759const contrastCache = new Map(); 1760function findContrastColor(baseColor, fixedColor) { 1761 const key = baseColor[0] + baseColor[1] * 0x100 + baseColor[2] * 0x10000 + fixedColor[0] * 0x1000000 + fixedColor[1] * 0x100000000 + fixedColor[2] * 0x10000000000; 1762 let cachedValue = contrastCache.get(key); 1763 if (cachedValue) { 1764 return cachedValue; 1765 } 1766 const array = new Float32Array(9); 1767 const output = array.subarray(0, 3); 1768 const baseHSL = array.subarray(3, 6); 1769 RGBToHSL(baseColor, baseHSL); 1770 const fixedHSL = array.subarray(6, 9); 1771 RGBToHSL(fixedColor, fixedHSL); 1772 const isFixedColorDark = fixedHSL[2] < 0.5; 1773 const minContrast = isFixedColorDark ? 12 : 4.5; 1774 baseHSL[2] = isFixedColorDark ? Math.sqrt(baseHSL[2]) : 1 - Math.sqrt(1 - baseHSL[2]); 1775 if (contrastRatio(baseHSL, fixedHSL, output) < minContrast) { 1776 let start, end; 1777 if (isFixedColorDark) { 1778 start = baseHSL[2]; 1779 end = 1; 1780 } else { 1781 start = 0; 1782 end = baseHSL[2]; 1783 } 1784 const PRECISION = 0.005; 1785 while (end - start > PRECISION) { 1786 const mid = baseHSL[2] = (start + end) / 2; 1787 if (isFixedColorDark === contrastRatio(baseHSL, fixedHSL, output) < minContrast) { 1788 start = mid; 1789 } else { 1790 end = mid; 1791 } 1792 } 1793 baseHSL[2] = isFixedColorDark ? end : start; 1794 } 1795 HSLToRGB(baseHSL, output); 1796 cachedValue = Util.makeHexColor(Math.round(output[0] * 255), Math.round(output[1] * 255), Math.round(output[2] * 255)); 1797 contrastCache.set(key, cachedValue); 1798 return cachedValue; 1799} 1800function renderRichText({ 1801 html, 1802 dir, 1803 className 1804}, container) { 1805 const fragment = document.createDocumentFragment(); 1806 if (typeof html === "string") { 1807 const p = document.createElement("p"); 1808 p.dir = dir || "auto"; 1809 const lines = html.split(/(?:\r\n?|\n)/); 1810 for (let i = 0, ii = lines.length; i < ii; ++i) { 1811 const line = lines[i]; 1812 p.append(document.createTextNode(line)); 1813 if (i < ii - 1) { 1814 p.append(document.createElement("br")); 1815 } 1816 } 1817 fragment.append(p); 1818 } else { 1819 XfaLayer.render({ 1820 xfaHtml: html, 1821 div: fragment, 1822 intent: "richText" 1823 }); 1824 } 1825 fragment.firstElementChild.classList.add("richText", className); 1826 container.append(fragment); 1827} 1828function makePathFromDrawOPS(data) { 1829 const path = new Path2D(); 1830 if (!data) { 1831 return path; 1832 } 1833 for (let i = 0, ii = data.length; i < ii;) { 1834 switch (data[i++]) { 1835 case DrawOPS.moveTo: 1836 path.moveTo(data[i++], data[i++]); 1837 break; 1838 case DrawOPS.lineTo: 1839 path.lineTo(data[i++], data[i++]); 1840 break; 1841 case DrawOPS.curveTo: 1842 path.bezierCurveTo(data[i++], data[i++], data[i++], data[i++], data[i++], data[i++]); 1843 break; 1844 case DrawOPS.quadraticCurveTo: 1845 path.quadraticCurveTo(data[i++], data[i++], data[i++], data[i++]); 1846 break; 1847 case DrawOPS.closePath:
1848 path.closePath(); 1849 break; 1850 default: 1851 warn(`Unrecognized drawing path operator: ${data[i - 1]}`); 1852 break; 1853 } 1854 } 1855 return path; 1856} 1857class PagesMapper { 1858 static #idToPageNumber = null; 1859 static #pageNumberToId = null; 1860 static #prevIdToPageNumber = null; 1861 static #pagesNumber = 0; 1862 static #listeners = []; 1863 get pagesNumber() { 1864 return PagesMapper.#pagesNumber; 1865 } 1866 set pagesNumber(n) { 1867 if (PagesMapper.#pagesNumber === n) { 1868 return; 1869 } 1870 PagesMapper.#pagesNumber = n; 1871 if (n === 0) { 1872 PagesMapper.#pageNumberToId = null; 1873 PagesMapper.#idToPageNumber = null; 1874 } 1875 } 1876 addListener(listener) { 1877 PagesMapper.#listeners.push(listener); 1878 } 1879 removeListener(listener) { 1880 const index = PagesMapper.#listeners.indexOf(listener); 1881 if (index >= 0) { 1882 PagesMapper.#listeners.splice(index, 1); 1883 } 1884 } 1885 #updateListeners() { 1886 for (const listener of PagesMapper.#listeners) { 1887 listener(); 1888 } 1889 } 1890 #init(mustInit) { 1891 if (PagesMapper.#pageNumberToId) { 1892 return; 1893 } 1894 const n = PagesMapper.#pagesNumber; 1895 const array = new Uint32Array(3 * n); 1896 const pageNumberToId = PagesMapper.#pageNumberToId = array.subarray(0, n); 1897 const idToPageNumber = PagesMapper.#idToPageNumber = array.subarray(n, 2 * n); 1898 if (mustInit) { 1899 for (let i = 0; i < n; i++) { 1900 pageNumberToId[i] = idToPageNumber[i] = i + 1; 1901 } 1902 } 1903 PagesMapper.#prevIdToPageNumber = array.subarray(2 * n); 1904 } 1905 movePages(selectedPages, pagesToMove, index) { 1906 this.#init(true); 1907 const pageNumberToId = PagesMapper.#pageNumberToId; 1908 const idToPageNumber = PagesMapper.#idToPageNumber; 1909 PagesMapper.#prevIdToPageNumber.set(idToPageNumber); 1910 const movedCount = pagesToMove.length; 1911 const mappedPagesToMove = new Uint32Array(movedCount); 1912 let removedBeforeTarget = 0; 1913 for (let i = 0; i < movedCount; i++) { 1914 const pageIndex = pagesToMove[i] - 1; 1915 mappedPagesToMove[i] = pageNumberToId[pageIndex]; 1916 if (pageIndex < index) { 1917 removedBeforeTarget += 1; 1918 } 1919 } 1920 const pagesNumber = PagesMapper.#pagesNumber; 1921 let adjustedTarget = index - removedBeforeTarget; 1922 const remainingLen = pagesNumber - movedCount; 1923 adjustedTarget = MathClamp(adjustedTarget, 0, remainingLen); 1924 for (let i = 0, r = 0; i < pagesNumber; i++) { 1925 if (!selectedPages.has(i + 1)) { 1926 pageNumberToId[r++] = pageNumberToId[i]; 1927 } 1928 } 1929 pageNumberToId.copyWithin(adjustedTarget + movedCount, adjustedTarget, remainingLen); 1930 pageNumberToId.set(mappedPagesToMove, adjustedTarget); 1931 let hasChanged = false; 1932 for (let i = 0, ii = pagesNumber; i < ii; i++) { 1933 const id = pageNumberToId[i]; 1934 hasChanged ||= id !== i + 1; 1935 idToPageNumber[id - 1] = i + 1; 1936 } 1937 this.#updateListeners(); 1938 if (!hasChanged) { 1939 this.pagesNumber = 0; 1940 } 1941 } 1942 hasBeenAltered() { 1943 return PagesMapper.#pageNumberToId !== null; 1944 } 1945 getPageMappingForSaving() { 1946 return { 1947 pageIndices: PagesMapper.#idToPageNumber ? PagesMapper.#idToPageNumber.map(x => x - 1) : null 1948 }; 1949 } 1950 getPrevPageNumber(pageNumber) { 1951 return PagesMapper.#prevIdToPageNumber[PagesMapper.#pageNumberToId[pageNumber - 1] - 1]; 1952 } 1953 getPageNumber(id) { 1954 return PagesMapper.#idToPageNumber?.[id - 1] ?? id; 1955 } 1956 getPageId(pageNumber) { 1957 return PagesMapper.#pageNumberToId?.[pageNumber - 1] ?? pageNumber; 1958 } 1959 static get instance() { 1960 return shadow(this, "instance", new PagesMapper()); 1961 } 1962 getMapping() { 1963 return PagesMapper.#pageNumberToId.subarray(0, this.pagesNumber); 1964 } 1965} 1966 1967;// ./src/display/editor/toolbar.js 1968 1969class EditorToolbar { 1970 #toolbar = null; 1971 #colorPicker = null; 1972 #editor; 1973 #buttons = null; 1974 #altText = null; 1975 #comment = null; 1976 #commentButtonDivider = null; 1977 #signatureDescriptionButton = null; 1978 static #l10nRemove = null; 1979 constructor(editor) { 1980 this.#editor = editor; 1981 EditorToolbar.#l10nRemove ||= Object.freeze({ 1982 freetext: "pdfjs-editor-remove-freetext-button", 1983 highlight: "pdfjs-editor-remove-highlight-button", 1984 ink: "pdfjs-editor-remove-ink-button", 1985 stamp: "pdfjs-editor-remove-stamp-button", 1986 signature: "pdfjs-editor-remove-signature-button" 1987 }); 1988 } 1989 render() { 1990 const editToolbar = this.#toolbar = document.createElement("div"); 1991 editToolbar.classList.add("editToolbar", "hidden"); 1992 editToolbar.setAttribute("role", "toolbar"); 1993 const signal = this.#editor._uiManager._signal; 1994 if (signal instanceof AbortSignal && !signal.aborted) { 1995 editToolbar.addEventListener("contextmenu", noContextMenu, { 1996 signal 1997 }); 1998 editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, {
vendor: 14,026 bytes, lines 1999-2499
1999 signal 2000 }); 2001 } 2002 const buttons = this.#buttons = document.createElement("div"); 2003 buttons.className = "buttons"; 2004 editToolbar.append(buttons); 2005 const position = this.#editor.toolbarPosition; 2006 if (position) { 2007 const { 2008 style 2009 } = editToolbar; 2010 const x = this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0]; 2011 style.insetInlineEnd = `${100 * x}%`; 2012 style.top = `calc(${100 * position[1]}% + var(--editor-toolbar-vert-offset))`; 2013 } 2014 return editToolbar; 2015 } 2016 get div() { 2017 return this.#toolbar; 2018 } 2019 static #pointerDown(e) { 2020 e.stopPropagation(); 2021 } 2022 #focusIn(e) { 2023 this.#editor._focusEventsAllowed = false; 2024 stopEvent(e); 2025 } 2026 #focusOut(e) { 2027 this.#editor._focusEventsAllowed = true; 2028 stopEvent(e); 2029 } 2030 #addListenersToElement(element) { 2031 const signal = this.#editor._uiManager._signal; 2032 if (!(signal instanceof AbortSignal) || signal.aborted) { 2033 return false; 2034 } 2035 element.addEventListener("focusin", this.#focusIn.bind(this), { 2036 capture: true, 2037 signal 2038 }); 2039 element.addEventListener("focusout", this.#focusOut.bind(this), { 2040 capture: true, 2041 signal 2042 }); 2043 element.addEventListener("contextmenu", noContextMenu, { 2044 signal 2045 }); 2046 return true; 2047 } 2048 hide() { 2049 this.#toolbar.classList.add("hidden"); 2050 this.#colorPicker?.hideDropdown(); 2051 } 2052 show() { 2053 this.#toolbar.classList.remove("hidden"); 2054 this.#altText?.shown(); 2055 this.#comment?.shown(); 2056 } 2057 addDeleteButton() { 2058 const { 2059 editorType, 2060 _uiManager 2061 } = this.#editor; 2062 const button = document.createElement("button"); 2063 button.classList.add("basic", "deleteButton"); 2064 button.tabIndex = 0; 2065 button.setAttribute("data-l10n-id", EditorToolbar.#l10nRemove[editorType]); 2066 if (this.#addListenersToElement(button)) { 2067 button.addEventListener("click", e => { 2068 _uiManager.delete(); 2069 }, { 2070 signal: _uiManager._signal 2071 }); 2072 } 2073 this.#buttons.append(button); 2074 } 2075 get #divider() { 2076 const divider = document.createElement("div"); 2077 divider.className = "divider"; 2078 return divider; 2079 } 2080 async addAltText(altText) { 2081 const button = await altText.render(); 2082 this.#addListenersToElement(button); 2083 this.#buttons.append(button, this.#divider); 2084 this.#altText = altText; 2085 } 2086 addComment(comment, beforeElement = null) { 2087 if (this.#comment) { 2088 return; 2089 } 2090 const button = comment.renderForToolbar(); 2091 if (!button) { 2092 return; 2093 } 2094 this.#addListenersToElement(button); 2095 const divider = this.#commentButtonDivider = this.#divider; 2096 if (!beforeElement) { 2097 this.#buttons.append(button, divider); 2098 } else { 2099 this.#buttons.insertBefore(button, beforeElement); 2100 this.#buttons.insertBefore(divider, beforeElement); 2101 } 2102 this.#comment = comment; 2103 comment.toolbar = this; 2104 } 2105 addColorPicker(colorPicker) { 2106 if (this.#colorPicker) { 2107 return; 2108 } 2109 this.#colorPicker = colorPicker; 2110 const button = colorPicker.renderButton(); 2111 this.#addListenersToElement(button); 2112 this.#buttons.append(button, this.#divider); 2113 } 2114 async addEditSignatureButton(signatureManager) { 2115 const button = this.#signatureDescriptionButton = await signatureManager.renderEditButton(this.#editor); 2116 this.#addListenersToElement(button); 2117 this.#buttons.append(button, this.#divider); 2118 } 2119 removeButton(name) { 2120 switch (name) { 2121 case "comment": 2122 this.#comment?.removeToolbarCommentButton(); 2123 this.#comment = null; 2124 this.#commentButtonDivider?.remove(); 2125 this.#commentButtonDivider = null; 2126 break; 2127 } 2128 } 2129 async addButton(name, tool) { 2130 switch (name) { 2131 case "colorPicker": 2132 if (tool) { 2133 this.addColorPicker(tool); 2134 } 2135 break; 2136 case "altText": 2137 if (tool) { 2138 await this.addAltText(tool); 2139 } 2140 break; 2141 case "editSignature": 2142 if (tool) { 2143 await this.addEditSignatureButton(tool); 2144 } 2145 break; 2146 case "delete": 2147 this.addDeleteButton(); 2148 break; 2149 case "comment": 2150 if (tool) { 2151 this.addComment(tool); 2152 } 2153 break; 2154 } 2155 } 2156 async addButtonBefore(name, tool, beforeSelector) { 2157 if (!tool && name === "comment") { 2158 return; 2159 } 2160 const beforeElement = this.#buttons.querySelector(beforeSelector); 2161 if (!beforeElement) { 2162 return; 2163 } 2164 if (name === "comment") { 2165 this.addComment(tool, beforeElement); 2166 } 2167 } 2168 updateEditSignatureButton(description) { 2169 if (this.#signatureDescriptionButton) { 2170 this.#signatureDescriptionButton.title = description; 2171 } 2172 } 2173 remove() { 2174 this.#toolbar.remove(); 2175 this.#colorPicker?.destroy(); 2176 this.#colorPicker = null; 2177 } 2178} 2179class FloatingToolbar { 2180 #buttons = null; 2181 #toolbar = null; 2182 #uiManager; 2183 constructor(uiManager) { 2184 this.#uiManager = uiManager; 2185 } 2186 #render() { 2187 const editToolbar = this.#toolbar = document.createElement("div"); 2188 editToolbar.className = "editToolbar"; 2189 editToolbar.setAttribute("role", "toolbar"); 2190 const signal = this.#uiManager._signal; 2191 if (signal instanceof AbortSignal && !signal.aborted) { 2192 editToolbar.addEventListener("contextmenu", noContextMenu, { 2193 signal 2194 }); 2195 } 2196 const buttons = this.#buttons = document.createElement("div"); 2197 buttons.className = "buttons"; 2198 editToolbar.append(buttons); 2199 if (this.#uiManager.hasCommentManager()) { 2200 this.#makeButton("commentButton", `pdfjs-comment-floating-button`, "pdfjs-comment-floating-button-label", () => { 2201 this.#uiManager.commentSelection("floating_button"); 2202 }); 2203 } 2204 this.#makeButton("highlightButton", `pdfjs-highlight-floating-button1`, "pdfjs-highlight-floating-button-label", () => { 2205 this.#uiManager.highlightSelection("floating_button"); 2206 }); 2207 return editToolbar; 2208 } 2209 #getLastPoint(boxes, isLTR) { 2210 let lastY = 0; 2211 let lastX = 0; 2212 for (const box of boxes) { 2213 const y = box.y + box.height; 2214 if (y < lastY) { 2215 continue; 2216 } 2217 const x = box.x + (isLTR ? box.width : 0); 2218 if (y > lastY) { 2219 lastX = x; 2220 lastY = y; 2221 continue; 2222 } 2223 if (isLTR) { 2224 if (x > lastX) { 2225 lastX = x; 2226 } 2227 } else if (x < lastX) { 2228 lastX = x; 2229 } 2230 } 2231 return [isLTR ? 1 - lastX : lastX, lastY]; 2232 } 2233 show(parent, boxes, isLTR) { 2234 const [x, y] = this.#getLastPoint(boxes, isLTR); 2235 const { 2236 style 2237 } = this.#toolbar ||= this.#render(); 2238 parent.append(this.#toolbar); 2239 style.insetInlineEnd = `${100 * x}%`; 2240 style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`; 2241 } 2242 hide() { 2243 this.#toolbar.remove(); 2244 } 2245 #makeButton(buttonClass, l10nId, labelL10nId, clickHandler) { 2246 const button = document.createElement("button"); 2247 button.classList.add("basic", buttonClass); 2248 button.tabIndex = 0; 2249 button.setAttribute("data-l10n-id", l10nId); 2250 const span = document.createElement("span"); 2251 button.append(span); 2252 span.className = "visuallyHidden"; 2253 span.setAttribute("data-l10n-id", labelL10nId); 2254 const signal = this.#uiManager._signal; 2255 if (signal instanceof AbortSignal && !signal.aborted) { 2256 button.addEventListener("contextmenu", noContextMenu, { 2257 signal 2258 }); 2259 button.addEventListener("click", clickHandler, { 2260 signal 2261 }); 2262 } 2263 this.#buttons.append(button); 2264 } 2265} 2266 2267;// ./src/display/editor/tools.js 2268 2269 2270 2271function bindEvents(obj, element, names) { 2272 for (const name of names) { 2273 element.addEventListener(name, obj[name].bind(obj)); 2274 } 2275} 2276class CurrentPointers { 2277 static #pointerId = NaN; 2278 static #pointerIds = null; 2279 static #moveTimestamp = NaN; 2280 static #pointerType = null; 2281 static initializeAndAddPointerId(pointerId) { 2282 (CurrentPointers.#pointerIds ||= new Set()).add(pointerId); 2283 } 2284 static setPointer(pointerType, pointerId) { 2285 CurrentPointers.#pointerId ||= pointerId; 2286 CurrentPointers.#pointerType ??= pointerType; 2287 } 2288 static setTimeStamp(timeStamp) { 2289 CurrentPointers.#moveTimestamp = timeStamp; 2290 } 2291 static isSamePointerId(pointerId) { 2292 return CurrentPointers.#pointerId === pointerId; 2293 } 2294 static isSamePointerIdOrRemove(pointerId) { 2295 if (CurrentPointers.#pointerId === pointerId) { 2296 return true; 2297 } 2298 CurrentPointers.#pointerIds?.delete(pointerId); 2299 return false; 2300 } 2301 static isSamePointerType(pointerType) { 2302 return CurrentPointers.#pointerType === pointerType; 2303 } 2304 static isInitializedAndDifferentPointerType(pointerType) { 2305 return CurrentPointers.#pointerType !== null && !CurrentPointers.isSamePointerType(pointerType); 2306 } 2307 static isSameTimeStamp(timeStamp) { 2308 return CurrentPointers.#moveTimestamp === timeStamp; 2309 } 2310 static isUsingMultiplePointers() { 2311 return CurrentPointers.#pointerIds?.size >= 1; 2312 } 2313 static clearPointerType() { 2314 CurrentPointers.#pointerType = null; 2315 } 2316 static clearPointerIds() { 2317 CurrentPointers.#pointerId = NaN; 2318 CurrentPointers.#pointerIds = null; 2319 } 2320 static clearTimeStamp() { 2321 CurrentPointers.#moveTimestamp = NaN; 2322 } 2323} 2324class IdManager { 2325 #id = 0; 2326 get id() { 2327 return `${AnnotationEditorPrefix}${this.#id++}`; 2328 } 2329} 2330class ImageManager { 2331 #baseId = getUuid(); 2332 #id = 0; 2333 #cache = null; 2334 static get _isSVGFittingCanvas() { 2335 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>`; 2336 const canvas = new OffscreenCanvas(1, 3); 2337 const ctx = canvas.getContext("2d", { 2338 willReadFrequently: true 2339 }); 2340 const image = new Image(); 2341 image.src = svg; 2342 const promise = image.decode().then(() => { 2343 ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3); 2344 return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0; 2345 }); 2346 return shadow(this, "_isSVGFittingCanvas", promise); 2347 } 2348 async #get(key, rawData) { 2349 this.#cache ||= new Map(); 2350 let data = this.#cache.get(key); 2351 if (data === null) { 2352 return null; 2353 } 2354 if (data?.bitmap) { 2355 data.refCounter += 1; 2356 return data; 2357 } 2358 try { 2359 data ||= { 2360 bitmap: null, 2361 id: `image_${this.#baseId}_${this.#id++}`, 2362 refCounter: 0, 2363 isSvg: false 2364 }; 2365 let image; 2366 if (typeof rawData === "string") { 2367 data.url = rawData; 2368 image = await fetchData(rawData, "blob"); 2369 } else if (rawData instanceof File) { 2370 image = data.file = rawData; 2371 } else if (rawData instanceof Blob) { 2372 image = rawData; 2373 } 2374 if (image.type === "image/svg+xml") { 2375 const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas; 2376 const fileReader = new FileReader(); 2377 const imageElement = new Image(); 2378 const imagePromise = new Promise((resolve, reject) => { 2379 imageElement.onload = () => { 2380 data.bitmap = imageElement; 2381 data.isSvg = true; 2382 resolve(); 2383 }; 2384 fileReader.onload = async () => { 2385 const url = data.svgUrl = fileReader.result; 2386 imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url; 2387 }; 2388 imageElement.onerror = fileReader.onerror = reject; 2389 }); 2390 fileReader.readAsDataURL(image); 2391 await imagePromise; 2392 } else { 2393 data.bitmap = await createImageBitmap(image); 2394 } 2395 data.refCounter = 1; 2396 } catch (e) { 2397 warn(e); 2398 data = null; 2399 } 2400 this.#cache.set(key, data); 2401 if (data) { 2402 this.#cache.set(data.id, data); 2403 } 2404 return data; 2405 } 2406 async getFromFile(file) { 2407 const { 2408 lastModified, 2409 name, 2410 size, 2411 type 2412 } = file; 2413 return this.#get(`${lastModified}_${name}_${size}_${type}`, file); 2414 } 2415 async getFromUrl(url) { 2416 return this.#get(url, url); 2417 } 2418 async getFromBlob(id, blobPromise) { 2419 const blob = await blobPromise; 2420 return this.#get(id, blob); 2421 } 2422 async getFromId(id) { 2423 this.#cache ||= new Map(); 2424 const data = this.#cache.get(id); 2425 if (!data) { 2426 return null; 2427 } 2428 if (data.bitmap) { 2429 data.refCounter += 1; 2430 return data; 2431 } 2432 if (data.file) { 2433 return this.getFromFile(data.file); 2434 } 2435 if (data.blobPromise) { 2436 const { 2437 blobPromise 2438 } = data; 2439 delete data.blobPromise; 2440 return this.getFromBlob(data.id, blobPromise); 2441 } 2442 return this.getFromUrl(data.url); 2443 } 2444 getFromCanvas(id, canvas) { 2445 this.#cache ||= new Map(); 2446 let data = this.#cache.get(id); 2447 if (data?.bitmap) { 2448 data.refCounter += 1; 2449 return data; 2450 } 2451 const offscreen = new OffscreenCanvas(canvas.width, canvas.height); 2452 const ctx = offscreen.getContext("2d"); 2453 ctx.drawImage(canvas, 0, 0); 2454 data = { 2455 bitmap: offscreen.transferToImageBitmap(), 2456 id: `image_${this.#baseId}_${this.#id++}`, 2457 refCounter: 1, 2458 isSvg: false 2459 }; 2460 this.#cache.set(id, data); 2461 this.#cache.set(data.id, data); 2462 return data; 2463 } 2464 getSvgUrl(id) { 2465 const data = this.#cache.get(id); 2466 if (!data?.isSvg) { 2467 return null; 2468 } 2469 return data.svgUrl; 2470 } 2471 deleteId(id) { 2472 this.#cache ||= new Map(); 2473 const data = this.#cache.get(id); 2474 if (!data) { 2475 return; 2476 } 2477 data.refCounter -= 1; 2478 if (data.refCounter !== 0) { 2479 return; 2480 } 2481 const { 2482 bitmap 2483 } = data; 2484 if (!data.url && !data.file) { 2485 const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); 2486 const ctx = canvas.getContext("bitmaprenderer"); 2487 ctx.transferFromImageBitmap(bitmap); 2488 data.blobPromise = canvas.convertToBlob(); 2489 } 2490 bitmap.close?.(); 2491 data.bitmap = null; 2492 } 2493 isValidId(id) { 2494 return id.startsWith(`image_${this.#baseId}_`); 2495 } 2496} 2497class CommandManager { 2498 #commands = []; 2499 #locked = false;
vendor: 10,298 bytes, lines 2500-2851
2500 #maxSize; 2501 #position = -1; 2502 constructor(maxSize = 128) { 2503 this.#maxSize = maxSize; 2504 } 2505 add({ 2506 cmd, 2507 undo, 2508 post, 2509 mustExec, 2510 type = NaN, 2511 overwriteIfSameType = false, 2512 keepUndo = false 2513 }) { 2514 if (mustExec) { 2515 cmd(); 2516 } 2517 if (this.#locked) { 2518 return; 2519 } 2520 const save = { 2521 cmd, 2522 undo, 2523 post, 2524 type 2525 }; 2526 if (this.#position === -1) { 2527 if (this.#commands.length > 0) { 2528 this.#commands.length = 0; 2529 } 2530 this.#position = 0; 2531 this.#commands.push(save); 2532 return; 2533 } 2534 if (overwriteIfSameType && this.#commands[this.#position].type === type) { 2535 if (keepUndo) { 2536 save.undo = this.#commands[this.#position].undo; 2537 } 2538 this.#commands[this.#position] = save; 2539 return; 2540 } 2541 const next = this.#position + 1; 2542 if (next === this.#maxSize) { 2543 this.#commands.splice(0, 1); 2544 } else { 2545 this.#position = next; 2546 if (next < this.#commands.length) { 2547 this.#commands.splice(next); 2548 } 2549 } 2550 this.#commands.push(save); 2551 } 2552 undo() { 2553 if (this.#position === -1) { 2554 return; 2555 } 2556 this.#locked = true; 2557 const { 2558 undo, 2559 post 2560 } = this.#commands[this.#position]; 2561 undo(); 2562 post?.(); 2563 this.#locked = false; 2564 this.#position -= 1; 2565 } 2566 redo() { 2567 if (this.#position < this.#commands.length - 1) { 2568 this.#position += 1; 2569 this.#locked = true; 2570 const { 2571 cmd, 2572 post 2573 } = this.#commands[this.#position]; 2574 cmd(); 2575 post?.(); 2576 this.#locked = false; 2577 } 2578 } 2579 hasSomethingToUndo() { 2580 return this.#position !== -1; 2581 } 2582 hasSomethingToRedo() { 2583 return this.#position < this.#commands.length - 1; 2584 } 2585 cleanType(type) { 2586 if (this.#position === -1) { 2587 return; 2588 } 2589 for (let i = this.#position; i >= 0; i--) { 2590 if (this.#commands[i].type !== type) { 2591 this.#commands.splice(i + 1, this.#position - i); 2592 this.#position = i; 2593 return; 2594 } 2595 } 2596 this.#commands.length = 0; 2597 this.#position = -1; 2598 } 2599 destroy() { 2600 this.#commands = null; 2601 } 2602} 2603class KeyboardManager { 2604 constructor(callbacks) { 2605 this.buffer = []; 2606 this.callbacks = new Map(); 2607 this.allKeys = new Set(); 2608 const { 2609 isMac 2610 } = util_FeatureTest.platform; 2611 for (const [keys, callback, options = {}] of callbacks) { 2612 for (const key of keys) { 2613 const isMacKey = key.startsWith("mac+"); 2614 if (isMac && isMacKey) { 2615 this.callbacks.set(key.slice(4), { 2616 callback, 2617 options 2618 }); 2619 this.allKeys.add(key.split("+").at(-1)); 2620 } else if (!isMac && !isMacKey) { 2621 this.callbacks.set(key, { 2622 callback, 2623 options 2624 }); 2625 this.allKeys.add(key.split("+").at(-1)); 2626 } 2627 } 2628 } 2629 } 2630 #serialize(event) { 2631 if (event.altKey) { 2632 this.buffer.push("alt"); 2633 } 2634 if (event.ctrlKey) { 2635 this.buffer.push("ctrl"); 2636 } 2637 if (event.metaKey) { 2638 this.buffer.push("meta"); 2639 } 2640 if (event.shiftKey) { 2641 this.buffer.push("shift"); 2642 } 2643 this.buffer.push(event.key); 2644 const str = this.buffer.join("+"); 2645 this.buffer.length = 0; 2646 return str; 2647 } 2648 exec(self, event) { 2649 if (!this.allKeys.has(event.key)) { 2650 return; 2651 } 2652 const info = this.callbacks.get(this.#serialize(event)); 2653 if (!info) { 2654 return; 2655 } 2656 const { 2657 callback, 2658 options: { 2659 bubbles = false, 2660 args = [], 2661 checker = null 2662 } 2663 } = info; 2664 if (checker && !checker(self, event)) { 2665 return; 2666 } 2667 callback.bind(self, ...args, event)(); 2668 if (!bubbles) { 2669 stopEvent(event); 2670 } 2671 } 2672} 2673class ColorManager { 2674 static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]); 2675 get _colors() { 2676 const colors = new Map([["CanvasText", null], ["Canvas", null]]); 2677 getColorValues(colors); 2678 return shadow(this, "_colors", colors); 2679 } 2680 convert(color) { 2681 const rgb = getRGB(color); 2682 if (!window.matchMedia("(forced-colors: active)").matches) { 2683 return rgb; 2684 } 2685 for (const [name, RGB] of this._colors) { 2686 if (RGB.every((x, i) => x === rgb[i])) { 2687 return ColorManager._colorsMapping.get(name); 2688 } 2689 } 2690 return rgb; 2691 } 2692 getHexCode(name) { 2693 const rgb = this._colors.get(name); 2694 if (!rgb) { 2695 return name; 2696 } 2697 return Util.makeHexColor(...rgb); 2698 } 2699} 2700class AnnotationEditorUIManager { 2701 #abortController = new AbortController(); 2702 #activeEditor = null; 2703 #allEditableAnnotations = null; 2704 #allEditors = new Map(); 2705 #allLayers = new Map(); 2706 #altTextManager = null; 2707 #annotationStorage = null; 2708 #changedExistingAnnotations = null; 2709 #commandManager = new CommandManager(); 2710 #commentManager = null; 2711 #copyPasteAC = null; 2712 #currentDrawingSession = null; 2713 #currentPageIndex = 0; 2714 #deletedAnnotationsElementIds = new Set(); 2715 #draggingEditors = null; 2716 #editorTypes = null; 2717 #editorsToRescale = new Set(); 2718 _editorUndoBar = null; 2719 #enableHighlightFloatingButton = false; 2720 #enableUpdatedAddImage = false; 2721 #enableNewAltTextWhenAddingImage = false; 2722 #filterFactory = null; 2723 #focusMainContainerTimeoutId = null; 2724 #focusManagerAC = null; 2725 #highlightColors = null; 2726 #highlightWhenShiftUp = false; 2727 #floatingToolbar = null; 2728 #idManager = new IdManager(); 2729 #isEnabled = false; 2730 #isPointerDown = false; 2731 #isWaiting = false; 2732 #keyboardManagerAC = null; 2733 #lastActiveElement = null; 2734 #mainHighlightColorPicker = null; 2735 #missingCanvases = null; 2736 #mlManager = null; 2737 #mode = AnnotationEditorType.NONE; 2738 #selectedEditors = new Set(); 2739 #selectedTextNode = null; 2740 #signatureManager = null; 2741 #pageColors = null; 2742 #showAllStates = null; 2743 #pdfDocument = null; 2744 #previousStates = { 2745 isEditing: false, 2746 isEmpty: true, 2747 hasSomethingToUndo: false, 2748 hasSomethingToRedo: false, 2749 hasSelectedEditor: false, 2750 hasSelectedText: false 2751 }; 2752 #translation = [0, 0]; 2753 #translationTimeoutId = null; 2754 #container = null; 2755 #viewer = null; 2756 #viewerAlert = null; 2757 #updateModeCapability = null; 2758 static TRANSLATE_SMALL = 1; 2759 static TRANSLATE_BIG = 10; 2760 static get _keyboardManager() { 2761 const proto = AnnotationEditorUIManager.prototype; 2762 const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl(); 2763 const textInputChecker = (_self, { 2764 target: el 2765 }) => { 2766 if (el instanceof HTMLInputElement) { 2767 const { 2768 type 2769 } = el; 2770 return type !== "text" && type !== "number"; 2771 } 2772 return true; 2773 }; 2774 const small = this.TRANSLATE_SMALL; 2775 const big = this.TRANSLATE_BIG; 2776 return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, { 2777 checker: textInputChecker 2778 }], [["ctrl+z", "mac+meta+z"], proto.undo, { 2779 checker: textInputChecker 2780 }], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, { 2781 checker: textInputChecker 2782 }], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, { 2783 checker: textInputChecker 2784 }], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, { 2785 checker: (self, { 2786 target: el 2787 }) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled 2788 }], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, { 2789 checker: (self, { 2790 target: el 2791 }) => !(el instanceof HTMLButtonElement) && self.#container.contains(document.activeElement) 2792 }], [["Escape", "mac+Escape"], proto.unselectAll], [["ArrowLeft", "mac+ArrowLeft"], proto.translateSelectedEditors, { 2793 args: [-small, 0], 2794 checker: arrowChecker 2795 }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto.translateSelectedEditors, { 2796 args: [-big, 0], 2797 checker: arrowChecker 2798 }], [["ArrowRight", "mac+ArrowRight"], proto.translateSelectedEditors, { 2799 args: [small, 0], 2800 checker: arrowChecker 2801 }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto.translateSelectedEditors, { 2802 args: [big, 0], 2803 checker: arrowChecker 2804 }], [["ArrowUp", "mac+ArrowUp"], proto.translateSelectedEditors, { 2805 args: [0, -small], 2806 checker: arrowChecker 2807 }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto.translateSelectedEditors, { 2808 args: [0, -big], 2809 checker: arrowChecker 2810 }], [["ArrowDown", "mac+ArrowDown"], proto.translateSelectedEditors, { 2811 args: [0, small], 2812 checker: arrowChecker 2813 }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto.translateSelectedEditors, { 2814 args: [0, big], 2815 checker: arrowChecker 2816 }]])); 2817 } 2818 constructor(container, viewer, viewerAlert, altTextManager, commentManager, signatureManager, eventBus, pdfDocument, pageColors, highlightColors, enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, mlManager, editorUndoBar, supportsPinchToZoom) { 2819 const signal = this._signal = this.#abortController.signal; 2820 this.#container = container; 2821 this.#viewer = viewer; 2822 this.#viewerAlert = viewerAlert; 2823 this.#altTextManager = altTextManager; 2824 this.#commentManager = commentManager; 2825 this.#signatureManager = signatureManager; 2826 this.#pdfDocument = pdfDocument; 2827 this._eventBus = eventBus; 2828 eventBus._on("editingaction", this.onEditingAction.bind(this), { 2829 signal 2830 }); 2831 eventBus._on("pagechanging", this.onPageChanging.bind(this), { 2832 signal 2833 }); 2834 eventBus._on("scalechanging", this.onScaleChanging.bind(this), { 2835 signal 2836 }); 2837 eventBus._on("rotationchanging", this.onRotationChanging.bind(this), { 2838 signal 2839 }); 2840 eventBus._on("setpreference", this.onSetPreference.bind(this), { 2841 signal 2842 }); 2843 eventBus._on("switchannotationeditorparams", evt => this.updateParams(evt.type, evt.value), { 2844 signal 2845 }); 2846 eventBus._on("pagesedited", this.onPagesEdited.bind(this), { 2847 signal 2848 }); 2849 window.addEventListener("pointerdown", () => { 2850 this.#isPointerDown = true; 2851 }, {
vendor: 10,609 bytes, lines 2852-3219
2852 capture: true, 2853 signal 2854 }); 2855 window.addEventListener("pointerup", () => { 2856 this.#isPointerDown = false; 2857 }, { 2858 capture: true, 2859 signal 2860 }); 2861 this.#addSelectionListener(); 2862 this.#addDragAndDropListeners(); 2863 this.#addKeyboardManager(); 2864 this.#annotationStorage = pdfDocument.annotationStorage; 2865 this.#filterFactory = pdfDocument.filterFactory; 2866 this.#pageColors = pageColors; 2867 this.#highlightColors = highlightColors || null; 2868 this.#enableHighlightFloatingButton = enableHighlightFloatingButton; 2869 this.#enableUpdatedAddImage = enableUpdatedAddImage; 2870 this.#enableNewAltTextWhenAddingImage = enableNewAltTextWhenAddingImage; 2871 this.#mlManager = mlManager || null; 2872 this.viewParameters = { 2873 realScale: PixelsPerInch.PDF_TO_CSS_UNITS, 2874 rotation: 0 2875 }; 2876 this.isShiftKeyDown = false; 2877 this._editorUndoBar = editorUndoBar || null; 2878 this._supportsPinchToZoom = supportsPinchToZoom !== false; 2879 commentManager?.setSidebarUiManager(this); 2880 } 2881 destroy() { 2882 this.#updateModeCapability?.resolve(); 2883 this.#updateModeCapability = null; 2884 this.#abortController?.abort(); 2885 this.#abortController = null; 2886 this._signal = null; 2887 for (const layer of this.#allLayers.values()) { 2888 layer.destroy(); 2889 } 2890 this.#allLayers.clear(); 2891 this.#allEditors.clear(); 2892 this.#editorsToRescale.clear(); 2893 this.#missingCanvases?.clear(); 2894 this.#activeEditor = null; 2895 this.#selectedEditors.clear(); 2896 this.#commandManager.destroy(); 2897 this.#altTextManager?.destroy(); 2898 this.#commentManager?.destroy(); 2899 this.#signatureManager?.destroy(); 2900 this.#floatingToolbar?.hide(); 2901 this.#floatingToolbar = null; 2902 this.#mainHighlightColorPicker?.destroy(); 2903 this.#mainHighlightColorPicker = null; 2904 this.#allEditableAnnotations = null; 2905 if (this.#focusMainContainerTimeoutId) { 2906 clearTimeout(this.#focusMainContainerTimeoutId); 2907 this.#focusMainContainerTimeoutId = null; 2908 } 2909 if (this.#translationTimeoutId) { 2910 clearTimeout(this.#translationTimeoutId); 2911 this.#translationTimeoutId = null; 2912 } 2913 this._editorUndoBar?.destroy(); 2914 this.#pdfDocument = null; 2915 } 2916 combinedSignal(ac) { 2917 return AbortSignal.any([this._signal, ac.signal]); 2918 } 2919 get mlManager() { 2920 return this.#mlManager; 2921 } 2922 get useNewAltTextFlow() { 2923 return this.#enableUpdatedAddImage; 2924 } 2925 get useNewAltTextWhenAddingImage() { 2926 return this.#enableNewAltTextWhenAddingImage; 2927 } 2928 get hcmFilter() { 2929 return shadow(this, "hcmFilter", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : "none"); 2930 } 2931 get direction() { 2932 return shadow(this, "direction", getComputedStyle(this.#container).direction); 2933 } 2934 get _highlightColors() { 2935 return shadow(this, "_highlightColors", this.#highlightColors ? new Map(this.#highlightColors.split(",").map(pair => { 2936 pair = pair.split("=").map(x => x.trim()); 2937 pair[1] = pair[1].toUpperCase(); 2938 return pair; 2939 })) : null); 2940 } 2941 get highlightColors() { 2942 const { 2943 _highlightColors 2944 } = this; 2945 if (!_highlightColors) { 2946 return shadow(this, "highlightColors", null); 2947 } 2948 const map = new Map(); 2949 const hasHCM = !!this.#pageColors; 2950 for (const [name, color] of _highlightColors) { 2951 const isNameForHCM = name.endsWith("_HCM"); 2952 if (hasHCM && isNameForHCM) { 2953 map.set(name.replace("_HCM", ""), color); 2954 continue; 2955 } 2956 if (!hasHCM && !isNameForHCM) { 2957 map.set(name, color); 2958 } 2959 } 2960 return shadow(this, "highlightColors", map); 2961 } 2962 get highlightColorNames() { 2963 return shadow(this, "highlightColorNames", this.highlightColors ? new Map(Array.from(this.highlightColors, e => e.reverse())) : null); 2964 } 2965 getNonHCMColor(color) { 2966 if (!this._highlightColors) { 2967 return color; 2968 } 2969 const colorName = this.highlightColorNames.get(color); 2970 return this._highlightColors.get(colorName) || color; 2971 } 2972 getNonHCMColorName(color) { 2973 return this.highlightColorNames.get(color) || color; 2974 } 2975 setCurrentDrawingSession(layer) { 2976 if (layer) { 2977 this.unselectAll(); 2978 this.disableUserSelect(true); 2979 } else { 2980 this.disableUserSelect(false); 2981 } 2982 this.#currentDrawingSession = layer; 2983 } 2984 setMainHighlightColorPicker(colorPicker) { 2985 this.#mainHighlightColorPicker = colorPicker; 2986 } 2987 editAltText(editor, firstTime = false) { 2988 this.#altTextManager?.editAltText(this, editor, firstTime); 2989 } 2990 hasCommentManager() { 2991 return !!this.#commentManager; 2992 } 2993 editComment(editor, posX, posY, options) { 2994 this.#commentManager?.showDialog(this, editor, posX, posY, options); 2995 } 2996 selectComment(pageIndex, uid) { 2997 const layer = this.#allLayers.get(pageIndex); 2998 const editor = layer?.getEditorByUID(uid); 2999 editor?.toggleComment(true, true); 3000 } 3001 updateComment(editor) { 3002 this.#commentManager?.updateComment(editor.getData()); 3003 } 3004 updatePopupColor(editor) { 3005 this.#commentManager?.updatePopupColor(editor); 3006 } 3007 removeComment(editor) { 3008 this.#commentManager?.removeComments([editor.uid]); 3009 } 3010 deleteComment(editor, savedData) { 3011 const undo = () => { 3012 editor.comment = savedData; 3013 }; 3014 const cmd = () => { 3015 this._editorUndoBar?.show(undo, "comment"); 3016 this.toggleComment(null); 3017 editor.comment = null; 3018 }; 3019 this.addCommands({ 3020 cmd, 3021 undo, 3022 mustExec: true 3023 }); 3024 } 3025 toggleComment(editor, isSelected, visibility = undefined) { 3026 this.#commentManager?.toggleCommentPopup(editor, isSelected, visibility); 3027 } 3028 makeCommentColor(color, opacity) { 3029 return color && this.#commentManager?.makeCommentColor(color, opacity) || null; 3030 } 3031 getCommentDialogElement() { 3032 return this.#commentManager?.dialogElement || null; 3033 } 3034 async waitForEditorsRendered(pageNumber) { 3035 if (this.#allLayers.has(pageNumber - 1)) { 3036 return; 3037 } 3038 const { 3039 resolve, 3040 promise 3041 } = Promise.withResolvers(); 3042 const onEditorsRendered = evt => { 3043 if (evt.pageNumber === pageNumber) { 3044 this._eventBus._off("editorsrendered", onEditorsRendered); 3045 resolve(); 3046 } 3047 }; 3048 this._eventBus.on("editorsrendered", onEditorsRendered); 3049 await promise; 3050 } 3051 getSignature(editor) { 3052 this.#signatureManager?.getSignature({ 3053 uiManager: this, 3054 editor 3055 }); 3056 } 3057 get signatureManager() { 3058 return this.#signatureManager; 3059 } 3060 switchToMode(mode, callback) { 3061 this._eventBus.on("annotationeditormodechanged", callback, { 3062 once: true, 3063 signal: this._signal 3064 }); 3065 this._eventBus.dispatch("showannotationeditorui", { 3066 source: this, 3067 mode 3068 }); 3069 } 3070 setPreference(name, value) { 3071 this._eventBus.dispatch("setpreference", { 3072 source: this, 3073 name, 3074 value 3075 }); 3076 } 3077 onSetPreference({ 3078 name, 3079 value 3080 }) { 3081 switch (name) { 3082 case "enableNewAltTextWhenAddingImage": 3083 this.#enableNewAltTextWhenAddingImage = value; 3084 break; 3085 } 3086 } 3087 onPagesEdited({ 3088 pagesMapper 3089 }) { 3090 for (const editor of this.#allEditors.values()) { 3091 editor.updatePageIndex(pagesMapper.getPrevPageNumber(editor.pageIndex + 1) - 1); 3092 } 3093 const allLayers = this.#allLayers; 3094 const newAllLayers = this.#allLayers = new Map(); 3095 for (const [pageIndex, layer] of allLayers) { 3096 const prevPageIndex = pagesMapper.getPrevPageNumber(pageIndex + 1) - 1; 3097 if (prevPageIndex === -1) { 3098 layer.destroy(); 3099 continue; 3100 } 3101 newAllLayers.set(prevPageIndex, layer); 3102 layer.updatePageIndex(prevPageIndex); 3103 } 3104 } 3105 onPageChanging({ 3106 pageNumber 3107 }) { 3108 this.#currentPageIndex = pageNumber - 1; 3109 } 3110 focusMainContainer() { 3111 this.#container.focus(); 3112 } 3113 findParent(x, y) { 3114 for (const layer of this.#allLayers.values()) { 3115 const { 3116 x: layerX, 3117 y: layerY, 3118 width, 3119 height 3120 } = layer.div.getBoundingClientRect(); 3121 if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) { 3122 return layer; 3123 } 3124 } 3125 return null; 3126 } 3127 disableUserSelect(value = false) { 3128 this.#viewer.classList.toggle("noUserSelect", value); 3129 } 3130 addShouldRescale(editor) { 3131 this.#editorsToRescale.add(editor); 3132 } 3133 removeShouldRescale(editor) { 3134 this.#editorsToRescale.delete(editor); 3135 } 3136 onScaleChanging({ 3137 scale 3138 }) { 3139 this.commitOrRemove(); 3140 this.viewParameters.realScale = scale * PixelsPerInch.PDF_TO_CSS_UNITS; 3141 for (const editor of this.#editorsToRescale) { 3142 editor.onScaleChanging(); 3143 } 3144 this.#currentDrawingSession?.onScaleChanging(); 3145 } 3146 onRotationChanging({ 3147 pagesRotation 3148 }) { 3149 this.commitOrRemove(); 3150 this.viewParameters.rotation = pagesRotation; 3151 } 3152 #getAnchorElementForSelection({ 3153 anchorNode 3154 }) { 3155 return anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode; 3156 } 3157 #getLayerForTextLayer(textLayer) { 3158 const { 3159 currentLayer 3160 } = this; 3161 if (currentLayer.hasTextLayer(textLayer)) { 3162 return currentLayer; 3163 } 3164 for (const layer of this.#allLayers.values()) { 3165 if (layer.hasTextLayer(textLayer)) { 3166 return layer; 3167 } 3168 } 3169 return null; 3170 } 3171 highlightSelection(methodOfCreation = "", comment = false) { 3172 const selection = document.getSelection(); 3173 if (!selection || selection.isCollapsed) { 3174 return; 3175 } 3176 const { 3177 anchorNode, 3178 anchorOffset, 3179 focusNode, 3180 focusOffset 3181 } = selection; 3182 const text = selection.toString(); 3183 const anchorElement = this.#getAnchorElementForSelection(selection); 3184 const textLayer = anchorElement.closest(".textLayer"); 3185 const boxes = this.getSelectionBoxes(textLayer); 3186 if (!boxes) { 3187 return; 3188 } 3189 selection.empty(); 3190 const layer = this.#getLayerForTextLayer(textLayer); 3191 const isNoneMode = this.#mode === AnnotationEditorType.NONE; 3192 const callback = () => { 3193 const editor = layer?.createAndAddNewEditor({ 3194 x: 0, 3195 y: 0 3196 }, false, { 3197 methodOfCreation, 3198 boxes, 3199 anchorNode, 3200 anchorOffset, 3201 focusNode, 3202 focusOffset, 3203 text 3204 }); 3205 if (isNoneMode) { 3206 this.showAllEditors("highlight", true, true); 3207 } 3208 if (comment) { 3209 editor?.editComment(); 3210 } 3211 }; 3212 if (isNoneMode) { 3213 this.switchToMode(AnnotationEditorType.HIGHLIGHT, callback); 3214 return; 3215 } 3216 callback(); 3217 } 3218 commentSelection(methodOfCreation = "") { 3219 this.highlightSelection(methodOfCreation, tru
vendor: 10,203 bytes, lines 3219-3588
3219e); 3220 } 3221 #displayFloatingToolbar() { 3222 const selection = document.getSelection(); 3223 if (!selection || selection.isCollapsed) { 3224 return; 3225 } 3226 const anchorElement = this.#getAnchorElementForSelection(selection); 3227 const textLayer = anchorElement.closest(".textLayer"); 3228 const boxes = this.getSelectionBoxes(textLayer); 3229 if (!boxes) { 3230 return; 3231 } 3232 this.#floatingToolbar ||= new FloatingToolbar(this); 3233 this.#floatingToolbar.show(textLayer, boxes, this.direction === "ltr"); 3234 } 3235 getAndRemoveDataFromAnnotationStorage(annotationId) { 3236 if (!this.#annotationStorage) { 3237 return null; 3238 } 3239 const key = `${AnnotationEditorPrefix}${annotationId}`; 3240 const storedValue = this.#annotationStorage.getRawValue(key); 3241 if (storedValue) { 3242 this.#annotationStorage.remove(key); 3243 } 3244 return storedValue; 3245 } 3246 addToAnnotationStorage(editor) { 3247 if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) { 3248 this.#annotationStorage.setValue(editor.id, editor); 3249 } 3250 } 3251 a11yAlert(messageId, args = null) { 3252 const viewerAlert = this.#viewerAlert; 3253 if (!viewerAlert) { 3254 return; 3255 } 3256 viewerAlert.setAttribute("data-l10n-id", messageId); 3257 if (args) { 3258 viewerAlert.setAttribute("data-l10n-args", JSON.stringify(args)); 3259 } else { 3260 viewerAlert.removeAttribute("data-l10n-args"); 3261 } 3262 } 3263 #selectionChange() { 3264 const selection = document.getSelection(); 3265 if (!selection || selection.isCollapsed) { 3266 if (this.#selectedTextNode) { 3267 this.#floatingToolbar?.hide(); 3268 this.#selectedTextNode = null; 3269 this.#dispatchUpdateStates({ 3270 hasSelectedText: false 3271 }); 3272 } 3273 return; 3274 } 3275 const { 3276 anchorNode 3277 } = selection; 3278 if (anchorNode === this.#selectedTextNode) { 3279 return; 3280 } 3281 const anchorElement = this.#getAnchorElementForSelection(selection); 3282 const textLayer = anchorElement.closest(".textLayer"); 3283 if (!textLayer) { 3284 if (this.#selectedTextNode) { 3285 this.#floatingToolbar?.hide(); 3286 this.#selectedTextNode = null; 3287 this.#dispatchUpdateStates({ 3288 hasSelectedText: false 3289 }); 3290 } 3291 return; 3292 } 3293 this.#floatingToolbar?.hide(); 3294 this.#selectedTextNode = anchorNode; 3295 this.#dispatchUpdateStates({ 3296 hasSelectedText: true 3297 }); 3298 if (this.#mode !== AnnotationEditorType.HIGHLIGHT && this.#mode !== AnnotationEditorType.NONE) { 3299 return; 3300 } 3301 if (this.#mode === AnnotationEditorType.HIGHLIGHT) { 3302 this.showAllEditors("highlight", true, true); 3303 } 3304 this.#highlightWhenShiftUp = this.isShiftKeyDown; 3305 if (!this.isShiftKeyDown) { 3306 const activeLayer = this.#mode === AnnotationEditorType.HIGHLIGHT ? this.#getLayerForTextLayer(textLayer) : null; 3307 activeLayer?.toggleDrawing(); 3308 if (this.#isPointerDown) { 3309 const ac = new AbortController(); 3310 const signal = this.combinedSignal(ac); 3311 const pointerup = e => { 3312 if (e.type === "pointerup" && e.button !== 0) { 3313 return; 3314 } 3315 ac.abort(); 3316 activeLayer?.toggleDrawing(true); 3317 if (e.type === "pointerup") { 3318 this.#onSelectEnd("main_toolbar"); 3319 } 3320 }; 3321 window.addEventListener("pointerup", pointerup, { 3322 signal 3323 }); 3324 window.addEventListener("blur", pointerup, { 3325 signal 3326 }); 3327 } else { 3328 activeLayer?.toggleDrawing(true); 3329 this.#onSelectEnd("main_toolbar"); 3330 } 3331 } 3332 } 3333 #onSelectEnd(methodOfCreation = "") { 3334 if (this.#mode === AnnotationEditorType.HIGHLIGHT) { 3335 this.highlightSelection(methodOfCreation); 3336 } else if (this.#enableHighlightFloatingButton) { 3337 this.#displayFloatingToolbar(); 3338 } 3339 } 3340 #addSelectionListener() { 3341 document.addEventListener("selectionchange", this.#selectionChange.bind(this), { 3342 signal: this._signal 3343 }); 3344 } 3345 #addFocusManager() { 3346 if (this.#focusManagerAC) { 3347 return; 3348 } 3349 this.#focusManagerAC = new AbortController(); 3350 const signal = this.combinedSignal(this.#focusManagerAC); 3351 window.addEventListener("focus", this.focus.bind(this), { 3352 signal 3353 }); 3354 window.addEventListener("blur", this.blur.bind(this), { 3355 signal 3356 }); 3357 } 3358 #removeFocusManager() { 3359 this.#focusManagerAC?.abort(); 3360 this.#focusManagerAC = null; 3361 } 3362 blur() { 3363 this.isShiftKeyDown = false; 3364 if (this.#highlightWhenShiftUp) { 3365 this.#highlightWhenShiftUp = false; 3366 this.#onSelectEnd("main_toolbar"); 3367 } 3368 if (!this.hasSelection) { 3369 return; 3370 } 3371 const { 3372 activeElement 3373 } = document; 3374 for (const editor of this.#selectedEditors) { 3375 if (editor.div.contains(activeElement)) { 3376 this.#lastActiveElement = [editor, activeElement]; 3377 editor._focusEventsAllowed = false; 3378 break; 3379 } 3380 } 3381 } 3382 focus() { 3383 if (!this.#lastActiveElement) { 3384 return; 3385 } 3386 const [lastEditor, lastActiveElement] = this.#lastActiveElement; 3387 this.#lastActiveElement = null; 3388 lastActiveElement.addEventListener("focusin", () => { 3389 lastEditor._focusEventsAllowed = true; 3390 }, { 3391 once: true, 3392 signal: this._signal 3393 }); 3394 lastActiveElement.focus(); 3395 } 3396 #addKeyboardManager() { 3397 if (this.#keyboardManagerAC) { 3398 return; 3399 } 3400 this.#keyboardManagerAC = new AbortController(); 3401 const signal = this.combinedSignal(this.#keyboardManagerAC); 3402 window.addEventListener("keydown", this.keydown.bind(this), { 3403 signal 3404 }); 3405 window.addEventListener("keyup", this.keyup.bind(this), { 3406 signal 3407 }); 3408 } 3409 #removeKeyboardManager() { 3410 this.#keyboardManagerAC?.abort(); 3411 this.#keyboardManagerAC = null; 3412 } 3413 #addCopyPasteListeners() { 3414 if (this.#copyPasteAC) { 3415 return; 3416 } 3417 this.#copyPasteAC = new AbortController(); 3418 const signal = this.combinedSignal(this.#copyPasteAC); 3419 document.addEventListener("copy", this.copy.bind(this), { 3420 signal 3421 }); 3422 document.addEventListener("cut", this.cut.bind(this), { 3423 signal 3424 }); 3425 document.addEventListener("paste", this.paste.bind(this), { 3426 signal 3427 }); 3428 } 3429 #removeCopyPasteListeners() { 3430 this.#copyPasteAC?.abort(); 3431 this.#copyPasteAC = null; 3432 } 3433 #addDragAndDropListeners() { 3434 const signal = this._signal; 3435 document.addEventListener("dragover", this.dragOver.bind(this), { 3436 signal 3437 }); 3438 document.addEventListener("drop", this.drop.bind(this), { 3439 signal 3440 }); 3441 } 3442 addEditListeners() { 3443 this.#addKeyboardManager(); 3444 this.setEditingState(true); 3445 } 3446 removeEditListeners() { 3447 this.#removeKeyboardManager(); 3448 this.setEditingState(false); 3449 } 3450 dragOver(event) { 3451 for (const { 3452 type 3453 } of event.dataTransfer.items) { 3454 for (const editorType of this.#editorTypes) { 3455 if (editorType.isHandlingMimeForPasting(type)) { 3456 event.dataTransfer.dropEffect = "copy"; 3457 event.preventDefault(); 3458 return; 3459 } 3460 } 3461 } 3462 } 3463 drop(event) { 3464 for (const item of event.dataTransfer.items) { 3465 for (const editorType of this.#editorTypes) { 3466 if (editorType.isHandlingMimeForPasting(item.type)) { 3467 editorType.paste(item, this.currentLayer); 3468 event.preventDefault(); 3469 return; 3470 } 3471 } 3472 } 3473 } 3474 copy(event) { 3475 event.preventDefault(); 3476 this.#activeEditor?.commitOrRemove(); 3477 if (!this.hasSelection) { 3478 return; 3479 } 3480 const editors = []; 3481 for (const editor of this.#selectedEditors) { 3482 const serialized = editor.serialize(true); 3483 if (serialized) { 3484 editors.push(serialized); 3485 } 3486 } 3487 if (editors.length === 0) { 3488 return; 3489 } 3490 event.clipboardData.setData("application/pdfjs", JSON.stringify(editors)); 3491 } 3492 cut(event) { 3493 this.copy(event); 3494 this.delete(); 3495 } 3496 async paste(event) { 3497 event.preventDefault(); 3498 const { 3499 clipboardData 3500 } = event; 3501 for (const item of clipboardData.items) { 3502 for (const editorType of this.#editorTypes) { 3503 if (editorType.isHandlingMimeForPasting(item.type)) { 3504 editorType.paste(item, this.currentLayer); 3505 return; 3506 } 3507 } 3508 } 3509 let data = clipboardData.getData("application/pdfjs"); 3510 if (!data) { 3511 return; 3512 } 3513 try { 3514 data = JSON.parse(data); 3515 } catch (ex) { 3516 warn(`paste: "${ex.message}".`); 3517 return; 3518 } 3519 if (!Array.isArray(data)) { 3520 return; 3521 } 3522 this.unselectAll(); 3523 const layer = this.currentLayer; 3524 try { 3525 const newEditors = []; 3526 for (const editor of data) { 3527 const deserializedEditor = await layer.deserialize(editor); 3528 if (!deserializedEditor) { 3529 return; 3530 } 3531 newEditors.push(deserializedEditor); 3532 } 3533 const cmd = () => { 3534 for (const editor of newEditors) { 3535 this.#addEditorToLayer(editor); 3536 } 3537 this.#selectEditors(newEditors); 3538 }; 3539 const undo = () => { 3540 for (const editor of newEditors) { 3541 editor.remove(); 3542 } 3543 }; 3544 this.addCommands({ 3545 cmd, 3546 undo, 3547 mustExec: true 3548 }); 3549 } catch (ex) { 3550 warn(`paste: "${ex.message}".`); 3551 } 3552 } 3553 keydown(event) { 3554 if (!this.isShiftKeyDown && event.key === "Shift") { 3555 this.isShiftKeyDown = true; 3556 } 3557 if (this.#mode !== AnnotationEditorType.NONE && !this.isEditorHandlingKeyboard) { 3558 AnnotationEditorUIManager._keyboardManager.exec(this, event); 3559 } 3560 } 3561 keyup(event) { 3562 if (this.isShiftKeyDown && event.key === "Shift") { 3563 this.isShiftKeyDown = false; 3564 if (this.#highlightWhenShiftUp) { 3565 this.#highlightWhenShiftUp = false; 3566 this.#onSelectEnd("main_toolbar"); 3567 } 3568 } 3569 } 3570 onEditingAction({ 3571 name 3572 }) { 3573 switch (name) { 3574 case "undo": 3575 case "redo": 3576 case "delete": 3577 case "selectAll": 3578 this[name](); 3579 break; 3580 case "highlightSelection": 3581 this.highlightSelection("context_menu"); 3582 break; 3583 case "commentSelection": 3584 this.commentSelection("context_menu"); 3585 break; 3586 } 3587 } 3588
vendor: 7,533 bytes, lines 3588-3841
3588#dispatchUpdateStates(details) { 3589 const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value); 3590 if (hasChanged) { 3591 this._eventBus.dispatch("annotationeditorstateschanged", { 3592 source: this, 3593 details: Object.assign(this.#previousStates, details) 3594 }); 3595 if (this.#mode === AnnotationEditorType.HIGHLIGHT && details.hasSelectedEditor === false) { 3596 this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_FREE, true]]); 3597 } 3598 } 3599 } 3600 #dispatchUpdateUI(details) { 3601 this._eventBus.dispatch("annotationeditorparamschanged", { 3602 source: this, 3603 details 3604 }); 3605 } 3606 setEditingState(isEditing) { 3607 if (isEditing) { 3608 this.#addFocusManager(); 3609 this.#addCopyPasteListeners(); 3610 this.#dispatchUpdateStates({ 3611 isEditing: this.#mode !== AnnotationEditorType.NONE, 3612 isEmpty: this.#isEmpty(), 3613 hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), 3614 hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), 3615 hasSelectedEditor: false 3616 }); 3617 } else { 3618 this.#removeFocusManager(); 3619 this.#removeCopyPasteListeners(); 3620 this.#dispatchUpdateStates({ 3621 isEditing: false 3622 }); 3623 this.disableUserSelect(false); 3624 } 3625 } 3626 registerEditorTypes(types) { 3627 if (this.#editorTypes) { 3628 return; 3629 } 3630 this.#editorTypes = types; 3631 for (const editorType of this.#editorTypes) { 3632 this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); 3633 } 3634 } 3635 getId() { 3636 return this.#idManager.id; 3637 } 3638 get currentLayer() { 3639 return this.#allLayers.get(this.#currentPageIndex); 3640 } 3641 getLayer(pageIndex) { 3642 return this.#allLayers.get(pageIndex); 3643 } 3644 get currentPageIndex() { 3645 return this.#currentPageIndex; 3646 } 3647 addLayer(layer) { 3648 this.#allLayers.set(layer.pageIndex, layer); 3649 if (this.#isEnabled) { 3650 layer.enable(); 3651 } else { 3652 layer.disable(); 3653 } 3654 } 3655 removeLayer(layer) { 3656 this.#allLayers.delete(layer.pageIndex); 3657 } 3658 async updateMode(mode, editId = null, isFromUser = false, isFromKeyboard = false, mustEnterInEditMode = false, editComment = false) { 3659 if (this.#mode === mode) { 3660 return; 3661 } 3662 if (this.#updateModeCapability) { 3663 await this.#updateModeCapability.promise; 3664 if (!this.#updateModeCapability) { 3665 return; 3666 } 3667 } 3668 this.#updateModeCapability = Promise.withResolvers(); 3669 this.#currentDrawingSession?.commitOrRemove(); 3670 if (this.#mode === AnnotationEditorType.POPUP) { 3671 this.#commentManager?.hideSidebar(); 3672 } 3673 this.#commentManager?.destroyPopup(); 3674 this.#mode = mode; 3675 if (mode === AnnotationEditorType.NONE) { 3676 this.setEditingState(false); 3677 this.#disableAll(); 3678 for (const editor of this.#allEditors.values()) { 3679 editor.hideStandaloneCommentButton(); 3680 } 3681 this._editorUndoBar?.hide(); 3682 this.toggleComment(null); 3683 this.#updateModeCapability.resolve(); 3684 return; 3685 } 3686 for (const editor of this.#allEditors.values()) { 3687 editor.addStandaloneCommentButton(); 3688 } 3689 if (mode === AnnotationEditorType.SIGNATURE) { 3690 await this.#signatureManager?.loadSignatures(); 3691 } 3692 if (isFromUser) { 3693 CurrentPointers.clearPointerType(); 3694 } 3695 this.setEditingState(true); 3696 await this.#enableAll(); 3697 this.unselectAll(); 3698 for (const layer of this.#allLayers.values()) { 3699 layer.updateMode(mode); 3700 } 3701 if (mode === AnnotationEditorType.POPUP) { 3702 this.#allEditableAnnotations ||= await this.#pdfDocument.getAnnotationsByType(new Set(this.#editorTypes.map(editorClass => editorClass._editorType))); 3703 const elementIds = new Set(); 3704 const allComments = []; 3705 for (const editor of this.#allEditors.values()) { 3706 const { 3707 annotationElementId, 3708 hasComment, 3709 deleted 3710 } = editor; 3711 if (annotationElementId) { 3712 elementIds.add(annotationElementId); 3713 } 3714 if (hasComment && !deleted) { 3715 allComments.push(editor.getData()); 3716 } 3717 } 3718 for (const annotation of this.#allEditableAnnotations) { 3719 const { 3720 id, 3721 popupRef, 3722 contentsObj 3723 } = annotation; 3724 if (popupRef && contentsObj?.str && !elementIds.has(id) && !this.#deletedAnnotationsElementIds.has(id)) { 3725 allComments.push(annotation); 3726 } 3727 } 3728 this.#commentManager?.showSidebar(allComments); 3729 } 3730 if (!editId) { 3731 if (isFromKeyboard) { 3732 this.addNewEditorFromKeyboard(); 3733 } 3734 this.#updateModeCapability.resolve(); 3735 return; 3736 } 3737 for (const editor of this.#allEditors.values()) { 3738 if (editor.uid === editId) { 3739 this.setSelected(editor); 3740 if (editComment) { 3741 editor.editComment(); 3742 } else if (mustEnterInEditMode) { 3743 editor.enterInEditMode(); 3744 } else { 3745 editor.focus(); 3746 } 3747 } else { 3748 editor.unselect(); 3749 } 3750 } 3751 this.#updateModeCapability.resolve(); 3752 } 3753 addNewEditorFromKeyboard() { 3754 if (this.currentLayer.canCreateNewEmptyEditor()) { 3755 this.currentLayer.addNewEditor(); 3756 } 3757 } 3758 updateToolbar(options) { 3759 if (options.mode === this.#mode) { 3760 return; 3761 } 3762 this._eventBus.dispatch("switchannotationeditormode", { 3763 source: this, 3764 ...options 3765 }); 3766 } 3767 updateParams(type, value) { 3768 if (!this.#editorTypes) { 3769 return; 3770 } 3771 switch (type) { 3772 case AnnotationEditorParamsType.CREATE: 3773 this.currentLayer.addNewEditor(value); 3774 return; 3775 case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL: 3776 this._eventBus.dispatch("reporttelemetry", { 3777 source: this, 3778 details: { 3779 type: "editing", 3780 data: { 3781 type: "highlight", 3782 action: "toggle_visibility" 3783 } 3784 } 3785 }); 3786 (this.#showAllStates ||= new Map()).set(type, value); 3787 this.showAllEditors("highlight", value); 3788 break; 3789 } 3790 if (this.hasSelection) { 3791 for (const editor of this.#selectedEditors) { 3792 editor.updateParams(type, value); 3793 } 3794 } else { 3795 for (const editorType of this.#editorTypes) { 3796 editorType.updateDefaultParams(type, value); 3797 } 3798 } 3799 } 3800 showAllEditors(type, visible, updateButton = false) { 3801 for (const editor of this.#allEditors.values()) { 3802 if (editor.editorType === type) { 3803 editor.show(visible); 3804 } 3805 } 3806 const state = this.#showAllStates?.get(AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL) ?? true; 3807 if (state !== visible) { 3808 this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL, visible]]); 3809 } 3810 } 3811 enableWaiting(mustWait = false) { 3812 if (this.#isWaiting === mustWait) { 3813 return; 3814 } 3815 this.#isWaiting = mustWait; 3816 for (const layer of this.#allLayers.values()) { 3817 if (mustWait) { 3818 layer.disableClick(); 3819 } else { 3820 layer.enableClick(); 3821 } 3822 layer.div.classList.toggle("waiting", mustWait); 3823 } 3824 } 3825 async #enableAll() { 3826 if (!this.#isEnabled) { 3827 this.#isEnabled = true; 3828 const promises = []; 3829 for (const layer of this.#allLayers.values()) { 3830 promises.push(layer.enable()); 3831 } 3832 await Promise.all(promises); 3833 for (const editor of this.#allEditors.values()) { 3834 editor.enable(); 3835 } 3836 } 3837 } 3838 #disableAll() { 3839 this.unselectAll(); 3840 if (this.#isEnabled) { 3841 this.#isEnabled = false;
vendor: 4,951 bytes, lines 3842-4017
3842 for (const layer of this.#allLayers.values()) { 3843 layer.disable(); 3844 } 3845 for (const editor of this.#allEditors.values()) { 3846 editor.disable(); 3847 } 3848 } 3849 } 3850 *getEditors(pageIndex) { 3851 for (const editor of this.#allEditors.values()) { 3852 if (editor.pageIndex === pageIndex) { 3853 yield editor; 3854 } 3855 } 3856 } 3857 getEditor(id) { 3858 return this.#allEditors.get(id); 3859 } 3860 addEditor(editor) { 3861 this.#allEditors.set(editor.id, editor); 3862 } 3863 removeEditor(editor) { 3864 if (editor.div.contains(document.activeElement)) { 3865 if (this.#focusMainContainerTimeoutId) { 3866 clearTimeout(this.#focusMainContainerTimeoutId); 3867 } 3868 this.#focusMainContainerTimeoutId = setTimeout(() => { 3869 this.focusMainContainer(); 3870 this.#focusMainContainerTimeoutId = null; 3871 }, 0); 3872 } 3873 this.#allEditors.delete(editor.id); 3874 if (editor.annotationElementId) { 3875 this.#missingCanvases?.delete(editor.annotationElementId); 3876 } 3877 this.unselect(editor); 3878 if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) { 3879 this.#annotationStorage?.remove(editor.id); 3880 } 3881 } 3882 addDeletedAnnotationElement(editor) { 3883 this.#deletedAnnotationsElementIds.add(editor.annotationElementId); 3884 this.addChangedExistingAnnotation(editor); 3885 editor.deleted = true; 3886 } 3887 isDeletedAnnotationElement(annotationElementId) { 3888 return this.#deletedAnnotationsElementIds.has(annotationElementId); 3889 } 3890 removeDeletedAnnotationElement(editor) { 3891 this.#deletedAnnotationsElementIds.delete(editor.annotationElementId); 3892 this.removeChangedExistingAnnotation(editor); 3893 editor.deleted = false; 3894 } 3895 #addEditorToLayer(editor) { 3896 const layer = this.#allLayers.get(editor.pageIndex); 3897 if (layer) { 3898 layer.addOrRebuild(editor); 3899 } else { 3900 this.addEditor(editor); 3901 this.addToAnnotationStorage(editor); 3902 } 3903 } 3904 setActiveEditor(editor) { 3905 if (this.#activeEditor === editor) { 3906 return; 3907 } 3908 this.#activeEditor = editor; 3909 if (editor) { 3910 this.#dispatchUpdateUI(editor.propertiesToUpdate); 3911 } 3912 } 3913 get #lastSelectedEditor() { 3914 let ed = null; 3915 for (ed of this.#selectedEditors) {} 3916 return ed; 3917 } 3918 updateUI(editor) { 3919 if (this.#lastSelectedEditor === editor) { 3920 this.#dispatchUpdateUI(editor.propertiesToUpdate); 3921 } 3922 } 3923 updateUIForDefaultProperties(editorType) { 3924 this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate); 3925 } 3926 toggleSelected(editor) { 3927 if (this.#selectedEditors.has(editor)) { 3928 this.#selectedEditors.delete(editor); 3929 editor.unselect(); 3930 this.#dispatchUpdateStates({ 3931 hasSelectedEditor: this.hasSelection 3932 }); 3933 return; 3934 } 3935 this.#selectedEditors.add(editor); 3936 editor.select(); 3937 this.#dispatchUpdateUI(editor.propertiesToUpdate); 3938 this.#dispatchUpdateStates({ 3939 hasSelectedEditor: true 3940 }); 3941 } 3942 setSelected(editor) { 3943 this.updateToolbar({ 3944 mode: editor.mode, 3945 editId: editor.uid 3946 }); 3947 this.#currentDrawingSession?.commitOrRemove(); 3948 for (const ed of this.#selectedEditors) { 3949 if (ed !== editor) { 3950 ed.unselect(); 3951 } 3952 } 3953 this.#selectedEditors.clear(); 3954 this.#selectedEditors.add(editor); 3955 editor.select(); 3956 this.#dispatchUpdateUI(editor.propertiesToUpdate); 3957 this.#dispatchUpdateStates({ 3958 hasSelectedEditor: true 3959 }); 3960 } 3961 isSelected(editor) { 3962 return this.#selectedEditors.has(editor); 3963 } 3964 get firstSelectedEditor() { 3965 return this.#selectedEditors.values().next().value; 3966 } 3967 unselect(editor) { 3968 editor.unselect(); 3969 this.#selectedEditors.delete(editor); 3970 this.#dispatchUpdateStates({ 3971 hasSelectedEditor: this.hasSelection 3972 }); 3973 } 3974 get hasSelection() { 3975 return this.#selectedEditors.size !== 0; 3976 } 3977 get isEnterHandled() { 3978 return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled; 3979 } 3980 undo() { 3981 this.#commandManager.undo(); 3982 this.#dispatchUpdateStates({ 3983 hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(), 3984 hasSomethingToRedo: true, 3985 isEmpty: this.#isEmpty() 3986 }); 3987 this._editorUndoBar?.hide(); 3988 } 3989 redo() { 3990 this.#commandManager.redo(); 3991 this.#dispatchUpdateStates({ 3992 hasSomethingToUndo: true, 3993 hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(), 3994 isEmpty: this.#isEmpty() 3995 }); 3996 } 3997 addCommands(params) { 3998 this.#commandManager.add(params); 3999 this.#dispatchUpdateStates({ 4000 hasSomethingToUndo: true, 4001 hasSomethingToRedo: false, 4002 isEmpty: this.#isEmpty() 4003 }); 4004 } 4005 cleanUndoStack(type) { 4006 this.#commandManager.cleanType(type); 4007 } 4008 #isEmpty() { 4009 if (this.#allEditors.size === 0) { 4010 return true; 4011 } 4012 if (this.#allEditors.size === 1) { 4013 for (const editor of this.#allEditors.values()) { 4014 return editor.isEmpty(); 4015 } 4016 } 4017 return false;
vendor: 1,612 bytes, lines 4018-4080
4018 } 4019 delete() { 4020 this.commitOrRemove(); 4021 const drawingEditor = this.currentLayer?.endDrawingSession(true); 4022 if (!this.hasSelection && !drawingEditor) { 4023 return; 4024 } 4025 const editors = drawingEditor ? [drawingEditor] : [...this.#selectedEditors]; 4026 const cmd = () => { 4027 this._editorUndoBar?.show(undo, editors.length === 1 ? editors[0].editorType : editors.length); 4028 for (const editor of editors) { 4029 editor.remove(); 4030 } 4031 }; 4032 const undo = () => { 4033 for (const editor of editors) { 4034 this.#addEditorToLayer(editor); 4035 } 4036 }; 4037 this.addCommands({ 4038 cmd, 4039 undo, 4040 mustExec: true 4041 }); 4042 } 4043 commitOrRemove() { 4044 this.#activeEditor?.commitOrRemove(); 4045 } 4046 hasSomethingToControl() { 4047 return this.#activeEditor || this.hasSelection; 4048 } 4049 #selectEditors(editors) { 4050 for (const editor of this.#selectedEditors) { 4051 editor.unselect(); 4052 } 4053 this.#selectedEditors.clear(); 4054 for (const editor of editors) { 4055 if (editor.isEmpty()) { 4056 continue; 4057 } 4058 this.#selectedEditors.add(editor); 4059 editor.select(); 4060 } 4061 this.#dispatchUpdateStates({ 4062 hasSelectedEditor: this.hasSelection 4063 }); 4064 } 4065 selectAll() { 4066 for (const editor of this.#selectedEditors) { 4067 editor.commit(); 4068 } 4069 this.#selectEditors(this.#allEditors.values()); 4070 } 4071 unselectAll() { 4072 if (this.#activeEditor) { 4073 this.#activeEditor.commitOrRemove(); 4074 if (this.#mode !== AnnotationEditorType.NONE) { 4075 return; 4076 } 4077 } 4078 if (this.#currentDrawingSession?.commitOrRemove()) { 4079 return; 4080 }
vendor: 10,497 bytes, lines 4080-4451
4080 4081 if (!this.hasSelection) { 4082 return; 4083 } 4084 for (const editor of this.#selectedEditors) { 4085 editor.unselect(); 4086 } 4087 this.#selectedEditors.clear(); 4088 this.#dispatchUpdateStates({ 4089 hasSelectedEditor: false 4090 }); 4091 } 4092 translateSelectedEditors(x, y, noCommit = false) { 4093 if (!noCommit) { 4094 this.commitOrRemove(); 4095 } 4096 if (!this.hasSelection) { 4097 return; 4098 } 4099 this.#translation[0] += x; 4100 this.#translation[1] += y; 4101 const [totalX, totalY] = this.#translation; 4102 const editors = [...this.#selectedEditors]; 4103 const TIME_TO_WAIT = 1000; 4104 if (this.#translationTimeoutId) { 4105 clearTimeout(this.#translationTimeoutId); 4106 } 4107 this.#translationTimeoutId = setTimeout(() => { 4108 this.#translationTimeoutId = null; 4109 this.#translation[0] = this.#translation[1] = 0; 4110 this.addCommands({ 4111 cmd: () => { 4112 for (const editor of editors) { 4113 if (this.#allEditors.has(editor.id)) { 4114 editor.translateInPage(totalX, totalY); 4115 editor.translationDone(); 4116 } 4117 } 4118 }, 4119 undo: () => { 4120 for (const editor of editors) { 4121 if (this.#allEditors.has(editor.id)) { 4122 editor.translateInPage(-totalX, -totalY); 4123 editor.translationDone(); 4124 } 4125 } 4126 }, 4127 mustExec: false 4128 }); 4129 }, TIME_TO_WAIT); 4130 for (const editor of editors) { 4131 editor.translateInPage(x, y); 4132 editor.translationDone(); 4133 } 4134 } 4135 setUpDragSession() { 4136 if (!this.hasSelection) { 4137 return; 4138 } 4139 this.disableUserSelect(true); 4140 this.#draggingEditors = new Map(); 4141 for (const editor of this.#selectedEditors) { 4142 this.#draggingEditors.set(editor, { 4143 savedX: editor.x, 4144 savedY: editor.y, 4145 savedPageIndex: editor.pageIndex, 4146 newX: 0, 4147 newY: 0, 4148 newPageIndex: -1 4149 }); 4150 } 4151 } 4152 endDragSession() { 4153 if (!this.#draggingEditors) { 4154 return false; 4155 } 4156 this.disableUserSelect(false); 4157 const map = this.#draggingEditors; 4158 this.#draggingEditors = null; 4159 let mustBeAddedInUndoStack = false; 4160 for (const [{ 4161 x, 4162 y, 4163 pageIndex 4164 }, value] of map) { 4165 value.newX = x; 4166 value.newY = y; 4167 value.newPageIndex = pageIndex; 4168 mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex; 4169 } 4170 if (!mustBeAddedInUndoStack) { 4171 return false; 4172 } 4173 const move = (editor, x, y, pageIndex) => { 4174 if (this.#allEditors.has(editor.id)) { 4175 const parent = this.#allLayers.get(pageIndex); 4176 if (parent) { 4177 editor._setParentAndPosition(parent, x, y); 4178 } else { 4179 editor.pageIndex = pageIndex; 4180 editor.x = x; 4181 editor.y = y; 4182 } 4183 } 4184 }; 4185 this.addCommands({ 4186 cmd: () => { 4187 for (const [editor, { 4188 newX, 4189 newY, 4190 newPageIndex 4191 }] of map) { 4192 move(editor, newX, newY, newPageIndex); 4193 } 4194 }, 4195 undo: () => { 4196 for (const [editor, { 4197 savedX, 4198 savedY, 4199 savedPageIndex 4200 }] of map) { 4201 move(editor, savedX, savedY, savedPageIndex); 4202 } 4203 }, 4204 mustExec: true 4205 }); 4206 return true; 4207 } 4208 dragSelectedEditors(tx, ty) { 4209 if (!this.#draggingEditors) { 4210 return; 4211 } 4212 for (const editor of this.#draggingEditors.keys()) { 4213 editor.drag(tx, ty); 4214 } 4215 } 4216 rebuild(editor) { 4217 if (editor.parent === null) { 4218 const parent = this.getLayer(editor.pageIndex); 4219 if (parent) { 4220 parent.changeParent(editor); 4221 parent.addOrRebuild(editor); 4222 } else { 4223 this.addEditor(editor); 4224 this.addToAnnotationStorage(editor); 4225 editor.rebuild(); 4226 } 4227 } else { 4228 editor.parent.addOrRebuild(editor); 4229 } 4230 } 4231 get isEditorHandlingKeyboard() { 4232 return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents(); 4233 } 4234 isActive(editor) { 4235 return this.#activeEditor === editor; 4236 } 4237 getActive() { 4238 return this.#activeEditor; 4239 } 4240 getMode() { 4241 return this.#mode; 4242 } 4243 isEditingMode() { 4244 return this.#mode !== AnnotationEditorType.NONE; 4245 } 4246 get imageManager() { 4247 return shadow(this, "imageManager", new ImageManager()); 4248 } 4249 getSelectionBoxes(textLayer) { 4250 if (!textLayer) { 4251 return null; 4252 } 4253 const selection = document.getSelection(); 4254 for (let i = 0, ii = selection.rangeCount; i < ii; i++) { 4255 if (!textLayer.contains(selection.getRangeAt(i).commonAncestorContainer)) { 4256 return null; 4257 } 4258 } 4259 const { 4260 x: layerX, 4261 y: layerY, 4262 width: parentWidth, 4263 height: parentHeight 4264 } = textLayer.getBoundingClientRect(); 4265 let rotator; 4266 switch (textLayer.getAttribute("data-main-rotation")) { 4267 case "90": 4268 rotator = (x, y, w, h) => ({ 4269 x: (y - layerY) / parentHeight, 4270 y: 1 - (x + w - layerX) / parentWidth, 4271 width: h / parentHeight, 4272 height: w / parentWidth 4273 }); 4274 break; 4275 case "180": 4276 rotator = (x, y, w, h) => ({ 4277 x: 1 - (x + w - layerX) / parentWidth, 4278 y: 1 - (y + h - layerY) / parentHeight, 4279 width: w / parentWidth, 4280 height: h / parentHeight 4281 }); 4282 break; 4283 case "270": 4284 rotator = (x, y, w, h) => ({ 4285 x: 1 - (y + h - layerY) / parentHeight, 4286 y: (x - layerX) / parentWidth, 4287 width: h / parentHeight, 4288 height: w / parentWidth 4289 }); 4290 break; 4291 default: 4292 rotator = (x, y, w, h) => ({ 4293 x: (x - layerX) / parentWidth, 4294 y: (y - layerY) / parentHeight, 4295 width: w / parentWidth, 4296 height: h / parentHeight 4297 }); 4298 break; 4299 } 4300 const boxes = []; 4301 for (let i = 0, ii = selection.rangeCount; i < ii; i++) { 4302 const range = selection.getRangeAt(i); 4303 if (range.collapsed) { 4304 continue; 4305 } 4306 for (const { 4307 x, 4308 y, 4309 width, 4310 height 4311 } of range.getClientRects()) { 4312 if (width === 0 || height === 0) { 4313 continue; 4314 } 4315 boxes.push(rotator(x, y, width, height)); 4316 } 4317 } 4318 return boxes.length === 0 ? null : boxes; 4319 } 4320 addChangedExistingAnnotation({ 4321 annotationElementId, 4322 id 4323 }) { 4324 (this.#changedExistingAnnotations ||= new Map()).set(annotationElementId, id); 4325 } 4326 removeChangedExistingAnnotation({ 4327 annotationElementId 4328 }) { 4329 this.#changedExistingAnnotations?.delete(annotationElementId); 4330 } 4331 renderAnnotationElement(annotation) { 4332 const editorId = this.#changedExistingAnnotations?.get(annotation.data.id); 4333 if (!editorId) { 4334 return; 4335 } 4336 const editor = this.#annotationStorage.getRawValue(editorId); 4337 if (!editor) { 4338 return; 4339 } 4340 if (this.#mode === AnnotationEditorType.NONE && !editor.hasBeenModified) { 4341 return; 4342 } 4343 editor.renderAnnotationElement(annotation); 4344 } 4345 setMissingCanvas(annotationId, annotationElementId, canvas) { 4346 const editor = this.#missingCanvases?.get(annotationId); 4347 if (!editor) { 4348 return; 4349 } 4350 editor.setCanvas(annotationElementId, canvas); 4351 this.#missingCanvases.delete(annotationId); 4352 } 4353 addMissingCanvas(annotationId, editor) { 4354 (this.#missingCanvases ||= new Map()).set(annotationId, editor); 4355 } 4356} 4357 4358;// ./src/display/editor/alt_text.js 4359 4360class AltText { 4361 #altText = null; 4362 #altTextDecorative = false; 4363 #altTextButton = null; 4364 #altTextButtonLabel = null; 4365 #altTextTooltip = null; 4366 #altTextTooltipTimeout = null; 4367 #altTextWasFromKeyBoard = false; 4368 #badge = null; 4369 #editor = null; 4370 #guessedText = null; 4371 #textWithDisclaimer = null; 4372 #useNewAltTextFlow = false; 4373 static #l10nNewButton = null; 4374 static _l10n = null; 4375 constructor(editor) { 4376 this.#editor = editor; 4377 this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow; 4378 AltText.#l10nNewButton ||= Object.freeze({ 4379 added: "pdfjs-editor-new-alt-text-added-button", 4380 "added-label": "pdfjs-editor-new-alt-text-added-button-label", 4381 missing: "pdfjs-editor-new-alt-text-missing-button", 4382 "missing-label": "pdfjs-editor-new-alt-text-missing-button-label", 4383 review: "pdfjs-editor-new-alt-text-to-review-button", 4384 "review-label": "pdfjs-editor-new-alt-text-to-review-button-label" 4385 }); 4386 } 4387 static initialize(l10n) { 4388 AltText._l10n ??= l10n; 4389 } 4390 async render() { 4391 const altText = this.#altTextButton = document.createElement("button"); 4392 altText.className = "altText"; 4393 altText.tabIndex = "0"; 4394 const label = this.#altTextButtonLabel = document.createElement("span"); 4395 altText.append(label); 4396 if (this.#useNewAltTextFlow) { 4397 altText.classList.add("new"); 4398 altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing); 4399 label.setAttribute("data-l10n-id", AltText.#l10nNewButton["missing-label"]); 4400 } else { 4401 altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button"); 4402 label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label"); 4403 } 4404 const signal = this.#editor._uiManager._signal; 4405 altText.addEventListener("contextmenu", noContextMenu, { 4406 signal 4407 }); 4408 altText.addEventListener("pointerdown", event => event.stopPropagation(), { 4409 signal 4410 }); 4411 const onClick = event => { 4412 event.preventDefault(); 4413 this.#editor._uiManager.editAltText(this.#editor); 4414 if (this.#useNewAltTextFlow) { 4415 this.#editor._reportTelemetry({ 4416 action: "pdfjs.image.alt_text.image_status_label_clicked", 4417 data: { 4418 label: this.#label 4419 } 4420 }); 4421 } 4422 }; 4423 altText.addEventListener("click", onClick, { 4424 capture: true, 4425 signal 4426 }); 4427 altText.addEventListener("keydown", event => { 4428 if (event.target === altText && event.key === "Enter") { 4429 this.#altTextWasFromKeyBoard = true; 4430 onClick(event); 4431 } 4432 }, { 4433 signal 4434 }); 4435 await this.#setState(); 4436 return altText; 4437 } 4438 get #label() { 4439 return this.#altText && "added" || this.#altText === null && this.guessedText && "review" || "missing"; 4440 } 4441 finish() { 4442 if (!this.#altTextButton) { 4443 return; 4444 } 4445 this.#altTextButton.focus({ 4446 focusVisible: this.#altTextWasFromKeyBoard 4447 }); 4448 this.#altTextWasFromKeyBoard = false; 4449 } 4450 isEmpty() { 4451
4451if (this.#useNewAltTextFlow) { 4452 return this.#altText === null; 4453 } 4454 return !this.#altText && !this.#altTextDecorative; 4455 } 4456 hasData() { 4457 if (this.#useNewAltTextFlow) { 4458 return this.#altText !== null || !!this.#guessedText; 4459 } 4460 return
vendor: 12,118 bytes, lines 4460-4896
4460 this.isEmpty(); 4461 } 4462 get guessedText() { 4463 return this.#guessedText; 4464 } 4465 async setGuessedText(guessedText) { 4466 if (this.#altText !== null) { 4467 return; 4468 } 4469 this.#guessedText = guessedText; 4470 this.#textWithDisclaimer = await AltText._l10n.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", { 4471 generatedAltText: guessedText 4472 }); 4473 this.#setState(); 4474 } 4475 toggleAltTextBadge(visibility = false) { 4476 if (!this.#useNewAltTextFlow || this.#altText) { 4477 this.#badge?.remove(); 4478 this.#badge = null; 4479 return; 4480 } 4481 if (!this.#badge) { 4482 const badge = this.#badge = document.createElement("div"); 4483 badge.className = "noAltTextBadge"; 4484 this.#editor.div.append(badge); 4485 } 4486 this.#badge.classList.toggle("hidden", !visibility); 4487 } 4488 serialize(isForCopying) { 4489 let altText = this.#altText; 4490 if (!isForCopying && this.#guessedText === altText) { 4491 altText = this.#textWithDisclaimer; 4492 } 4493 return { 4494 altText, 4495 decorative: this.#altTextDecorative, 4496 guessedText: this.#guessedText, 4497 textWithDisclaimer: this.#textWithDisclaimer 4498 }; 4499 } 4500 get data() { 4501 return { 4502 altText: this.#altText, 4503 decorative: this.#altTextDecorative 4504 }; 4505 } 4506 set data({ 4507 altText, 4508 decorative, 4509 guessedText, 4510 textWithDisclaimer, 4511 cancel = false 4512 }) { 4513 if (guessedText) { 4514 this.#guessedText = guessedText; 4515 this.#textWithDisclaimer = textWithDisclaimer; 4516 } 4517 if (this.#altText === altText && this.#altTextDecorative === decorative) { 4518 return; 4519 } 4520 if (!cancel) { 4521 this.#altText = altText; 4522 this.#altTextDecorative = decorative; 4523 } 4524 this.#setState(); 4525 } 4526 toggle(enabled = false) { 4527 if (!this.#altTextButton) { 4528 return; 4529 } 4530 if (!enabled && this.#altTextTooltipTimeout) { 4531 clearTimeout(this.#altTextTooltipTimeout); 4532 this.#altTextTooltipTimeout = null; 4533 } 4534 this.#altTextButton.disabled = !enabled; 4535 } 4536 shown() { 4537 this.#editor._reportTelemetry({ 4538 action: "pdfjs.image.alt_text.image_status_label_displayed", 4539 data: { 4540 label: this.#label 4541 } 4542 }); 4543 } 4544 destroy() { 4545 this.#altTextButton?.remove(); 4546 this.#altTextButton = null; 4547 this.#altTextButtonLabel = null; 4548 this.#altTextTooltip = null; 4549 this.#badge?.remove(); 4550 this.#badge = null; 4551 } 4552 async #setState() { 4553 const button = this.#altTextButton; 4554 if (!button) { 4555 return; 4556 } 4557 if (this.#useNewAltTextFlow) { 4558 button.classList.toggle("done", !!this.#altText); 4559 button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]); 4560 this.#altTextButtonLabel?.setAttribute("data-l10n-id", AltText.#l10nNewButton[`${this.#label}-label`]); 4561 if (!this.#altText) { 4562 this.#altTextTooltip?.remove(); 4563 return; 4564 } 4565 } else { 4566 if (!this.#altText && !this.#altTextDecorative) { 4567 button.classList.remove("done"); 4568 this.#altTextTooltip?.remove(); 4569 return; 4570 } 4571 button.classList.add("done"); 4572 button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button"); 4573 } 4574 let tooltip = this.#altTextTooltip; 4575 if (!tooltip) { 4576 this.#altTextTooltip = tooltip = document.createElement("span"); 4577 tooltip.className = "tooltip"; 4578 tooltip.setAttribute("role", "tooltip"); 4579 tooltip.id = `alt-text-tooltip-${this.#editor.id}`; 4580 const DELAY_TO_SHOW_TOOLTIP = 100; 4581 const signal = this.#editor._uiManager._signal; 4582 signal.addEventListener("abort", () => { 4583 clearTimeout(this.#altTextTooltipTimeout); 4584 this.#altTextTooltipTimeout = null; 4585 }, { 4586 once: true 4587 }); 4588 button.addEventListener("mouseenter", () => { 4589 this.#altTextTooltipTimeout = setTimeout(() => { 4590 this.#altTextTooltipTimeout = null; 4591 this.#altTextTooltip.classList.add("show"); 4592 this.#editor._reportTelemetry({ 4593 action: "alt_text_tooltip" 4594 }); 4595 }, DELAY_TO_SHOW_TOOLTIP); 4596 }, { 4597 signal 4598 }); 4599 button.addEventListener("mouseleave", () => { 4600 if (this.#altTextTooltipTimeout) { 4601 clearTimeout(this.#altTextTooltipTimeout); 4602 this.#altTextTooltipTimeout = null; 4603 } 4604 this.#altTextTooltip?.classList.remove("show"); 4605 }, { 4606 signal 4607 }); 4608 } 4609 if (this.#altTextDecorative) { 4610 tooltip.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-decorative-tooltip"); 4611 } else { 4612 tooltip.removeAttribute("data-l10n-id"); 4613 tooltip.textContent = this.#altText; 4614 } 4615 if (!tooltip.parentNode) { 4616 button.append(tooltip); 4617 } 4618 const element = this.#editor.getElementForAltText(); 4619 element?.setAttribute("aria-describedby", tooltip.id); 4620 } 4621} 4622 4623;// ./src/display/editor/comment.js 4624 4625class Comment { 4626 #commentStandaloneButton = null; 4627 #commentToolbarButton = null; 4628 #commentWasFromKeyBoard = false; 4629 #editor = null; 4630 #initialText = null; 4631 #richText = null; 4632 #text = null; 4633 #date = null; 4634 #deleted = false; 4635 #popupPosition = null; 4636 constructor(editor) { 4637 this.#editor = editor; 4638 } 4639 renderForToolbar() { 4640 const button = this.#commentToolbarButton = document.createElement("button"); 4641 button.className = "comment"; 4642 return this.#render(button, false); 4643 } 4644 renderForStandalone() { 4645 const button = this.#commentStandaloneButton = document.createElement("button"); 4646 button.className = "annotationCommentButton"; 4647 const position = this.#editor.commentButtonPosition; 4648 if (position) { 4649 const { 4650 style 4651 } = button; 4652 style.insetInlineEnd = `calc(${100 * (this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0])}% - var(--comment-button-dim))`; 4653 style.top = `calc(${100 * position[1]}% - var(--comment-button-dim))`; 4654 const color = this.#editor.commentButtonColor; 4655 if (color) { 4656 style.backgroundColor = color; 4657 } 4658 } 4659 return this.#render(button, true); 4660 } 4661 focusButton() { 4662 setTimeout(() => { 4663 (this.#commentStandaloneButton ?? this.#commentToolbarButton)?.focus(); 4664 }, 0); 4665 } 4666 onUpdatedColor() { 4667 if (!this.#commentStandaloneButton) { 4668 return; 4669 } 4670 const color = this.#editor.commentButtonColor; 4671 if (color) { 4672 this.#commentStandaloneButton.style.backgroundColor = color; 4673 } 4674 this.#editor._uiManager.updatePopupColor(this.#editor); 4675 } 4676 get commentButtonWidth() { 4677 return (this.#commentStandaloneButton?.getBoundingClientRect().width ?? 0) / this.#editor.parent.boundingClientRect.width; 4678 } 4679 get commentPopupPositionInLayer() { 4680 if (this.#popupPosition) { 4681 return this.#popupPosition; 4682 } 4683 if (!this.#commentStandaloneButton) { 4684 return null; 4685 } 4686 const { 4687 x, 4688 y, 4689 height 4690 } = this.#commentStandaloneButton.getBoundingClientRect(); 4691 const { 4692 x: parentX, 4693 y: parentY, 4694 width: parentWidth, 4695 height: parentHeight 4696 } = this.#editor.parent.boundingClientRect; 4697 return [(x - parentX) / parentWidth, (y + height - parentY) / parentHeight]; 4698 } 4699 set commentPopupPositionInLayer(pos) { 4700 this.#popupPosition = pos; 4701 } 4702 hasDefaultPopupPosition() { 4703 return this.#popupPosition === null; 4704 } 4705 removeStandaloneCommentButton() { 4706 this.#commentStandaloneButton?.remove(); 4707 this.#commentStandaloneButton = null; 4708 } 4709 removeToolbarCommentButton() { 4710 this.#commentToolbarButton?.remove(); 4711 this.#commentToolbarButton = null; 4712 } 4713 setCommentButtonStates({ 4714 selected, 4715 hasPopup 4716 }) { 4717 if (!this.#commentStandaloneButton) { 4718 return; 4719 } 4720 this.#commentStandaloneButton.classList.toggle("selected", selected); 4721 this.#commentStandaloneButton.ariaExpanded = hasPopup; 4722 } 4723 #render(comment, isStandalone) { 4724 if (!this.#editor._uiManager.hasCommentManager()) { 4725 return null; 4726 } 4727 comment.tabIndex = "0"; 4728 comment.ariaHasPopup = "dialog"; 4729 if (isStandalone) { 4730 comment.ariaControls = "commentPopup"; 4731 comment.setAttribute("data-l10n-id", "pdfjs-show-comment-button"); 4732 } else { 4733 comment.ariaControlsElements = [this.#editor._uiManager.getCommentDialogElement()]; 4734 comment.setAttribute("data-l10n-id", "pdfjs-editor-add-comment-button"); 4735 } 4736 const signal = this.#editor._uiManager._signal; 4737 if (!(signal instanceof AbortSignal) || signal.aborted) { 4738 return comment; 4739 } 4740 comment.addEventListener("contextmenu", noContextMenu, { 4741 signal 4742 }); 4743 if (isStandalone) { 4744 comment.addEventListener("focusin", e => { 4745 this.#editor._focusEventsAllowed = false; 4746 stopEvent(e); 4747 }, { 4748 capture: true, 4749 signal 4750 }); 4751 comment.addEventListener("focusout", e => { 4752 this.#editor._focusEventsAllowed = true; 4753 stopEvent(e); 4754 }, { 4755 capture: true, 4756 signal 4757 }); 4758 } 4759 comment.addEventListener("pointerdown", event => event.stopPropagation(), { 4760 signal 4761 }); 4762 const onClick = event => { 4763 event.preventDefault(); 4764 if (comment === this.#commentToolbarButton) { 4765 this.edit(); 4766 } else { 4767 this.#editor.toggleComment(true); 4768 } 4769 }; 4770 comment.addEventListener("click", onClick, { 4771 capture: true, 4772 signal 4773 }); 4774 comment.addEventListener("keydown", event => { 4775 if (event.target === comment && event.key === "Enter") { 4776 this.#commentWasFromKeyBoard = true; 4777 onClick(event); 4778 } 4779 }, { 4780 signal 4781 }); 4782 comment.addEventListener("pointerenter", () => { 4783 this.#editor.toggleComment(false, true); 4784 }, { 4785 signal 4786 }); 4787 comment.addEventListener("pointerleave", () => { 4788 this.#editor.toggleComment(false, false); 4789 }, { 4790 signal 4791 }); 4792 return comment; 4793 } 4794 edit(options) { 4795 const position = this.commentPopupPositionInLayer; 4796 let posX, posY; 4797 if (position) { 4798 [posX, posY] = position; 4799 } else { 4800 [posX, posY] = this.#editor.commentButtonPosition; 4801 const { 4802 width, 4803 height, 4804 x, 4805 y 4806 } = this.#editor; 4807 posX = x + posX * width; 4808 posY = y + posY * height; 4809 } 4810 const parentDimensions = this.#editor.parent.boundingClientRect; 4811 const { 4812 x: parentX, 4813 y: parentY, 4814 width: parentWidth, 4815 height: parentHeight 4816 } = parentDimensions; 4817 this.#editor._uiManager.editComment(this.#editor, parentX + posX * parentWidth, parentY + posY * parentHeight, { 4818 ...options, 4819 parentDimensions 4820 }); 4821 } 4822 finish() { 4823 if (!this.#commentToolbarButton) { 4824 return; 4825 } 4826 this.#commentToolbarButton.focus({ 4827 focusVisible: this.#commentWasFromKeyBoard 4828 }); 4829 this.#commentWasFromKeyBoard = false; 4830 } 4831 isDeleted() { 4832 return this.#deleted || this.#text === ""; 4833 } 4834 isEmpty() { 4835 return this.#text === null; 4836 } 4837 hasBeenEdited() { 4838 return this.isDeleted() || this.#text !== this.#initialText; 4839 } 4840 serialize() { 4841 return this.data; 4842 } 4843 get data() { 4844 return { 4845 text: this.#text, 4846 richText: this.#richText, 4847 date: this.#date, 4848 deleted: this.isDeleted() 4849 }; 4850 } 4851 set data(text) { 4852 if (text !== this.#text) { 4853 this.#richText = null; 4854 } 4855 if (text === null) { 4856 this.#text = ""; 4857 this.#deleted = true; 4858 return; 4859 } 4860 this.#text = text; 4861 this.#date = new Date(); 4862 this.#deleted = false; 4863 } 4864 restoreData({ 4865 text, 4866 richText, 4867 date 4868 }) { 4869 this.#text = text; 4870 this.#richText = richText; 4871 this.#date = date; 4872 this.#deleted = false; 4873 } 4874 setInitialText(text, richText = null) { 4875 this.#initialText = text; 4876 this.data = text; 4877 this.#date = null; 4878 this.#richText = richText; 4879 } 4880 shown() {} 4881 destroy() { 4882 this.#commentToolbarButton?.remove(); 4883 this.#commentToolbarButton = null; 4884 this.#commentStandaloneButton?.remove(); 4885 this.#commentStandaloneButton = null; 4886 this.#text = ""; 4887 this.#richText = null; 4888 this.#date = null; 4889 this.#editor = null; 4890 this.#commentWasFromKeyBoard = false; 4891 this.#deleted = false; 4892 } 4893} 4894 4895;// ./src/display/touch_manager.js 4896
vendor: 8,488 bytes, lines 4897-5174
4897class TouchManager { 4898 #container; 4899 #isPinching = false; 4900 #isPinchingStopped = null; 4901 #isPinchingDisabled; 4902 #onPinchStart; 4903 #onPinching; 4904 #onPinchEnd; 4905 #pointerDownAC = null; 4906 #signal; 4907 #touchInfo = null; 4908 #touchManagerAC; 4909 #touchMoveAC = null; 4910 constructor({ 4911 container, 4912 isPinchingDisabled = null, 4913 isPinchingStopped = null, 4914 onPinchStart = null, 4915 onPinching = null, 4916 onPinchEnd = null, 4917 signal 4918 }) { 4919 this.#container = container; 4920 this.#isPinchingStopped = isPinchingStopped; 4921 this.#isPinchingDisabled = isPinchingDisabled; 4922 this.#onPinchStart = onPinchStart; 4923 this.#onPinching = onPinching; 4924 this.#onPinchEnd = onPinchEnd; 4925 this.#touchManagerAC = new AbortController(); 4926 this.#signal = AbortSignal.any([signal, this.#touchManagerAC.signal]); 4927 container.addEventListener("touchstart", this.#onTouchStart.bind(this), { 4928 passive: false, 4929 signal: this.#signal 4930 }); 4931 } 4932 get MIN_TOUCH_DISTANCE_TO_PINCH() { 4933 return 35 / OutputScale.pixelRatio; 4934 } 4935 #onTouchStart(evt) { 4936 if (this.#isPinchingDisabled?.()) { 4937 return; 4938 } 4939 if (evt.touches.length === 1) { 4940 if (this.#pointerDownAC) { 4941 return; 4942 } 4943 const pointerDownAC = this.#pointerDownAC = new AbortController(); 4944 const signal = AbortSignal.any([this.#signal, pointerDownAC.signal]); 4945 const container = this.#container; 4946 const opts = { 4947 capture: true, 4948 signal, 4949 passive: false 4950 }; 4951 const cancelPointerDown = e => { 4952 if (e.pointerType === "touch") { 4953 this.#pointerDownAC?.abort(); 4954 this.#pointerDownAC = null; 4955 } 4956 }; 4957 container.addEventListener("pointerdown", e => { 4958 if (e.pointerType === "touch") { 4959 stopEvent(e); 4960 cancelPointerDown(e); 4961 } 4962 }, opts); 4963 container.addEventListener("pointerup", cancelPointerDown, opts); 4964 container.addEventListener("pointercancel", cancelPointerDown, opts); 4965 return; 4966 } 4967 if (!this.#touchMoveAC) { 4968 this.#touchMoveAC = new AbortController(); 4969 const signal = AbortSignal.any([this.#signal, this.#touchMoveAC.signal]); 4970 const container = this.#container; 4971 const opt = { 4972 signal, 4973 capture: false, 4974 passive: false 4975 }; 4976 container.addEventListener("touchmove", this.#onTouchMove.bind(this), opt); 4977 const onTouchEnd = this.#onTouchEnd.bind(this); 4978 container.addEventListener("touchend", onTouchEnd, opt); 4979 container.addEventListener("touchcancel", onTouchEnd, opt); 4980 opt.capture = true; 4981 container.addEventListener("pointerdown", stopEvent, opt); 4982 container.addEventListener("pointermove", stopEvent, opt); 4983 container.addEventListener("pointercancel", stopEvent, opt); 4984 container.addEventListener("pointerup", stopEvent, opt); 4985 this.#onPinchStart?.(); 4986 } 4987 stopEvent(evt); 4988 if (evt.touches.length !== 2 || this.#isPinchingStopped?.()) { 4989 this.#touchInfo = null; 4990 return; 4991 } 4992 let [touch0, touch1] = evt.touches; 4993 if (touch0.identifier > touch1.identifier) { 4994 [touch0, touch1] = [touch1, touch0]; 4995 } 4996 this.#touchInfo = { 4997 touch0X: touch0.screenX, 4998 touch0Y: touch0.screenY, 4999 touch1X: touch1.screenX, 5000 touch1Y: touch1.screenY 5001 }; 5002 } 5003 #onTouchMove(evt) { 5004 if (!this.#touchInfo || evt.touches.length !== 2) { 5005 return; 5006 } 5007 stopEvent(evt); 5008 let [touch0, touch1] = evt.touches; 5009 if (touch0.identifier > touch1.identifier) { 5010 [touch0, touch1] = [touch1, touch0]; 5011 } 5012 const { 5013 screenX: screen0X, 5014 screenY: screen0Y 5015 } = touch0; 5016 const { 5017 screenX: screen1X, 5018 screenY: screen1Y 5019 } = touch1; 5020 const touchInfo = this.#touchInfo; 5021 const { 5022 touch0X: pTouch0X, 5023 touch0Y: pTouch0Y, 5024 touch1X: pTouch1X, 5025 touch1Y: pTouch1Y 5026 } = touchInfo; 5027 const prevGapX = pTouch1X - pTouch0X; 5028 const prevGapY = pTouch1Y - pTouch0Y; 5029 const currGapX = screen1X - screen0X; 5030 const currGapY = screen1Y - screen0Y; 5031 const distance = Math.hypot(currGapX, currGapY) || 1; 5032 const pDistance = Math.hypot(prevGapX, prevGapY) || 1; 5033 if (!this.#isPinching && Math.abs(pDistance - distance) <= TouchManager.MIN_TOUCH_DISTANCE_TO_PINCH) { 5034 return; 5035 } 5036 touchInfo.touch0X = screen0X; 5037 touchInfo.touch0Y = screen0Y; 5038 touchInfo.touch1X = screen1X; 5039 touchInfo.touch1Y = screen1Y; 5040 if (!this.#isPinching) { 5041 this.#isPinching = true; 5042 return; 5043 } 5044 const origin = [(screen0X + screen1X) / 2, (screen0Y + screen1Y) / 2]; 5045 this.#onPinching?.(origin, pDistance, distance); 5046 } 5047 #onTouchEnd(evt) { 5048 if (evt.touches.length >= 2) { 5049 return; 5050 } 5051 if (this.#touchMoveAC) { 5052 this.#touchMoveAC.abort(); 5053 this.#touchMoveAC = null; 5054 this.#onPinchEnd?.(); 5055 } 5056 if (!this.#touchInfo) { 5057 return; 5058 } 5059 stopEvent(evt); 5060 this.#touchInfo = null; 5061 this.#isPinching = false; 5062 } 5063 destroy() { 5064 this.#touchManagerAC?.abort(); 5065 this.#touchManagerAC = null; 5066 this.#pointerDownAC?.abort(); 5067 this.#pointerDownAC = null; 5068 } 5069} 5070 5071;// ./src/display/editor/editor.js 5072 5073 5074 5075 5076 5077 5078 5079class AnnotationEditor { 5080 #accessibilityData = null; 5081 #allResizerDivs = null; 5082 #altText = null; 5083 #comment = null; 5084 #commentStandaloneButton = null; 5085 #disabled = false; 5086 #dragPointerId = null; 5087 #dragPointerType = ""; 5088 #resizersDiv = null; 5089 #lastPointerCoords = null; 5090 #savedDimensions = null; 5091 #fakeAnnotation = null; 5092 #focusAC = null; 5093 #focusedResizerName = ""; 5094 #hasBeenClicked = false; 5095 #initialRect = null; 5096 #isEditing = false; 5097 #isInEditMode = false; 5098 #isResizerEnabledForKeyboard = false; 5099 #moveInDOMTimeout = null; 5100 #prevDragX = 0; 5101 #prevDragY = 0; 5102 #telemetryTimeouts = null; 5103 #touchManager = null; 5104 isSelected = false; 5105 _isCopy = false; 5106 _editToolbar = null; 5107 _initialOptions = Object.create(null); 5108 _initialData = null; 5109 _isVisible = true; 5110 _uiManager = null; 5111 _focusEventsAllowed = true; 5112 static _l10n = null; 5113 static _l10nResizer = null; 5114 #isDraggable = false; 5115 #zIndex = AnnotationEditor._zIndex++; 5116 static _borderLineWidth = -1; 5117 static _colorManager = new ColorManager(); 5118 static _zIndex = 1; 5119 static _telemetryTimeout = 1000; 5120 static get _resizerKeyboardManager() { 5121 const resize = AnnotationEditor.prototype._resizeWithKeyboard; 5122 const small = AnnotationEditorUIManager.TRANSLATE_SMALL; 5123 const big = AnnotationEditorUIManager.TRANSLATE_BIG; 5124 return shadow(this, "_resizerKeyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], resize, { 5125 args: [-small, 0] 5126 }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], resize, { 5127 args: [-big, 0] 5128 }], [["ArrowRight", "mac+ArrowRight"], resize, { 5129 args: [small, 0] 5130 }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], resize, { 5131 args: [big, 0] 5132 }], [["ArrowUp", "mac+ArrowUp"], resize, { 5133 args: [0, -small] 5134 }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], resize, { 5135 args: [0, -big] 5136 }], [["ArrowDown", "mac+ArrowDown"], resize, { 5137 args: [0, small] 5138 }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], resize, { 5139 args: [0, big] 5140 }], [["Escape", "mac+Escape"], AnnotationEditor.prototype._stopResizingWithKeyboard]])); 5141 } 5142 constructor(parameters) { 5143 this.parent = parameters.parent; 5144 this.id = parameters.id; 5145 this.width = this.height = null; 5146 this.pageIndex = parameters.parent.pageIndex; 5147 this.name = parameters.name; 5148 this.div = null; 5149 this._uiManager = parameters.uiManager; 5150 this.annotationElementId = null; 5151 this._willKeepAspectRatio = false; 5152 this._initialOptions.isCentered = parameters.isCentered; 5153 this._structTreeParentId = null; 5154 this.annotationElementId = parameters.annotationElementId || null; 5155 this.creationDate = parameters.creationDate || new Date(); 5156 this.modificationDate = parameters.modificationDate || null; 5157 this.canAddComment = true; 5158 const { 5159 rotation, 5160 rawDims: { 5161 pageWidth, 5162 pageHeight, 5163 pageX, 5164 pageY 5165 } 5166 } = this.parent.viewport; 5167 this.rotation = rotation; 5168 this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360; 5169 this.pageDimensions = [pageWidth, pageHeight]; 5170 this.pageTranslation = [pageX, pageY]; 5171 const [width, height] = this.parentDimensions; 5172 this.x = parameters.x / width; 5173 this.y = parameters.y / height; 5174 this.isAttachedToDOM = false;
vendor: 35,507 bytes, lines 5175-6378
5175 this.deleted = false; 5176 } 5177 updatePageIndex(newPageIndex) { 5178 this.pageIndex = newPageIndex; 5179 } 5180 get editorType() { 5181 return Object.getPrototypeOf(this).constructor._type; 5182 } 5183 get mode() { 5184 return Object.getPrototypeOf(this).constructor._editorType; 5185 } 5186 static get isDrawer() { 5187 return false; 5188 } 5189 static get _defaultLineColor() { 5190 return shadow(this, "_defaultLineColor", this._colorManager.getHexCode("CanvasText")); 5191 } 5192 static deleteAnnotationElement(editor) { 5193 const fakeEditor = new FakeEditor({ 5194 id: editor.parent.getNextId(), 5195 parent: editor.parent, 5196 uiManager: editor._uiManager 5197 }); 5198 fakeEditor.annotationElementId = editor.annotationElementId; 5199 fakeEditor.deleted = true; 5200 fakeEditor._uiManager.addToAnnotationStorage(fakeEditor); 5201 } 5202 static initialize(l10n, _uiManager) { 5203 AnnotationEditor._l10n ??= l10n; 5204 AnnotationEditor._l10nResizer ||= Object.freeze({ 5205 topLeft: "pdfjs-editor-resizer-top-left", 5206 topMiddle: "pdfjs-editor-resizer-top-middle", 5207 topRight: "pdfjs-editor-resizer-top-right", 5208 middleRight: "pdfjs-editor-resizer-middle-right", 5209 bottomRight: "pdfjs-editor-resizer-bottom-right", 5210 bottomMiddle: "pdfjs-editor-resizer-bottom-middle", 5211 bottomLeft: "pdfjs-editor-resizer-bottom-left", 5212 middleLeft: "pdfjs-editor-resizer-middle-left" 5213 }); 5214 if (AnnotationEditor._borderLineWidth !== -1) { 5215 return; 5216 } 5217 const style = getComputedStyle(document.documentElement); 5218 AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue("--outline-width")) || 0; 5219 } 5220 static updateDefaultParams(_type, _value) {} 5221 static get defaultPropertiesToUpdate() { 5222 return []; 5223 } 5224 static isHandlingMimeForPasting(mime) { 5225 return false; 5226 } 5227 static paste(item, parent) { 5228 unreachable("Not implemented"); 5229 } 5230 get propertiesToUpdate() { 5231 return []; 5232 } 5233 get _isDraggable() { 5234 return this.#isDraggable; 5235 } 5236 set _isDraggable(value) { 5237 this.#isDraggable = value; 5238 this.div?.classList.toggle("draggable", value); 5239 } 5240 get uid() { 5241 return this.annotationElementId || this.id; 5242 } 5243 get isEnterHandled() { 5244 return true; 5245 } 5246 center() { 5247 const [pageWidth, pageHeight] = this.pageDimensions; 5248 switch (this.parentRotation) { 5249 case 90: 5250 this.x -= this.height * pageHeight / (pageWidth * 2); 5251 this.y += this.width * pageWidth / (pageHeight * 2); 5252 break; 5253 case 180: 5254 this.x += this.width / 2; 5255 this.y += this.height / 2; 5256 break; 5257 case 270: 5258 this.x += this.height * pageHeight / (pageWidth * 2); 5259 this.y -= this.width * pageWidth / (pageHeight * 2); 5260 break; 5261 default: 5262 this.x -= this.width / 2; 5263 this.y -= this.height / 2; 5264 break; 5265 } 5266 this.fixAndSetPosition(); 5267 } 5268 addCommands(params) { 5269 this._uiManager.addCommands(params); 5270 } 5271 get currentLayer() { 5272 return this._uiManager.currentLayer; 5273 } 5274 setInBackground() { 5275 this.div.style.zIndex = 0; 5276 } 5277 setInForeground() { 5278 this.div.style.zIndex = this.#zIndex; 5279 } 5280 setParent(parent) { 5281 if (parent !== null) { 5282 this.pageIndex = parent.pageIndex; 5283 this.pageDimensions = parent.pageDimensions; 5284 } else { 5285 this.#stopResizing(); 5286 this.#fakeAnnotation?.remove(); 5287 this.#fakeAnnotation = null; 5288 } 5289 this.parent = parent; 5290 } 5291 focusin(event) { 5292 if (!this._focusEventsAllowed) { 5293 return; 5294 } 5295 if (!this.#hasBeenClicked) { 5296 this.parent.setSelected(this); 5297 } else { 5298 this.#hasBeenClicked = false; 5299 } 5300 } 5301 focusout(event) { 5302 if (!this._focusEventsAllowed) { 5303 return; 5304 } 5305 if (!this.isAttachedToDOM) { 5306 return; 5307 } 5308 const target = event.relatedTarget; 5309 if (target?.closest(`#${this.id}`)) { 5310 return; 5311 } 5312 event.preventDefault(); 5313 if (!this.parent?.isMultipleSelection) { 5314 this.commitOrRemove(); 5315 } 5316 } 5317 commitOrRemove() { 5318 if (this.isEmpty()) { 5319 this.remove(); 5320 } else { 5321 this.commit(); 5322 } 5323 } 5324 commit() { 5325 if (!this.isInEditMode()) { 5326 return; 5327 } 5328 this.addToAnnotationStorage(); 5329 } 5330 addToAnnotationStorage() { 5331 this._uiManager.addToAnnotationStorage(this); 5332 } 5333 setAt(x, y, tx, ty) { 5334 const [width, height] = this.parentDimensions; 5335 [tx, ty] = this.screenToPageTranslation(tx, ty); 5336 this.x = (x + tx) / width; 5337 this.y = (y + ty) / height; 5338 this.fixAndSetPosition(); 5339 } 5340 _moveAfterPaste(baseX, baseY) { 5341 const [parentWidth, parentHeight] = this.parentDimensions; 5342 this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight); 5343 this._onTranslated(); 5344 } 5345 #translate([width, height], x, y) { 5346 [x, y] = this.screenToPageTranslation(x, y); 5347 this.x += x / width; 5348 this.y += y / height; 5349 this._onTranslating(this.x, this.y); 5350 this.fixAndSetPosition(); 5351 } 5352 translate(x, y) { 5353 this.#translate(this.parentDimensions, x, y); 5354 } 5355 translateInPage(x, y) { 5356 this.#initialRect ||= [this.x, this.y, this.width, this.height]; 5357 this.#translate(this.pageDimensions, x, y); 5358 this.div.scrollIntoView({ 5359 block: "nearest" 5360 }); 5361 } 5362 translationDone() { 5363 this._onTranslated(this.x, this.y); 5364 } 5365 drag(tx, ty) { 5366 this.#initialRect ||= [this.x, this.y, this.width, this.height]; 5367 const { 5368 div, 5369 parentDimensions: [parentWidth, parentHeight] 5370 } = this; 5371 this.x += tx / parentWidth; 5372 this.y += ty / parentHeight; 5373 if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) { 5374 const { 5375 x, 5376 y 5377 } = this.div.getBoundingClientRect(); 5378 if (this.parent.findNewParent(this, x, y)) { 5379 this.x -= Math.floor(this.x); 5380 this.y -= Math.floor(this.y); 5381 } 5382 } 5383 let { 5384 x, 5385 y 5386 } = this; 5387 const [bx, by] = this.getBaseTranslation(); 5388 x += bx; 5389 y += by; 5390 const { 5391 style 5392 } = div; 5393 style.left = `${(100 * x).toFixed(2)}%`; 5394 style.top = `${(100 * y).toFixed(2)}%`; 5395 this._onTranslating(x, y); 5396 div.scrollIntoView({ 5397 block: "nearest" 5398 }); 5399 } 5400 _onTranslating(x, y) {} 5401 _onTranslated(x, y) {} 5402 get _hasBeenMoved() { 5403 return !!this.#initialRect && (this.#initialRect[0] !== this.x || this.#initialRect[1] !== this.y); 5404 } 5405 get _hasBeenResized() { 5406 return !!this.#initialRect && (this.#initialRect[2] !== this.width || this.#initialRect[3] !== this.height); 5407 } 5408 getBaseTranslation() { 5409 const [parentWidth, parentHeight] = this.parentDimensions; 5410 const { 5411 _borderLineWidth 5412 } = AnnotationEditor; 5413 const x = _borderLineWidth / parentWidth; 5414 const y = _borderLineWidth / parentHeight; 5415 switch (this.rotation) { 5416 case 90: 5417 return [-x, y]; 5418 case 180: 5419 return [x, y]; 5420 case 270: 5421 return [x, -y]; 5422 default: 5423 return [-x, -y]; 5424 } 5425 } 5426 get _mustFixPosition() { 5427 return true; 5428 } 5429 fixAndSetPosition(rotation = this.rotation) { 5430 const { 5431 div: { 5432 style 5433 }, 5434 pageDimensions: [pageWidth, pageHeight] 5435 } = this; 5436 let { 5437 x, 5438 y, 5439 width, 5440 height 5441 } = this; 5442 width *= pageWidth; 5443 height *= pageHeight; 5444 x *= pageWidth; 5445 y *= pageHeight; 5446 if (this._mustFixPosition) { 5447 switch (rotation) { 5448 case 0: 5449 x = MathClamp(x, 0, pageWidth - width); 5450 y = MathClamp(y, 0, pageHeight - height); 5451 break; 5452 case 90: 5453 x = MathClamp(x, 0, pageWidth - height); 5454 y = MathClamp(y, width, pageHeight); 5455 break; 5456 case 180: 5457 x = MathClamp(x, width, pageWidth); 5458 y = MathClamp(y, height, pageHeight); 5459 break; 5460 case 270: 5461 x = MathClamp(x, height, pageWidth); 5462 y = MathClamp(y, 0, pageHeight - width); 5463 break; 5464 } 5465 } 5466 this.x = x /= pageWidth; 5467 this.y = y /= pageHeight; 5468 const [bx, by] = this.getBaseTranslation(); 5469 x += bx; 5470 y += by; 5471 style.left = `${(100 * x).toFixed(2)}%`; 5472 style.top = `${(100 * y).toFixed(2)}%`; 5473 this.moveInDOM(); 5474 } 5475 static #rotatePoint(x, y, angle) { 5476 switch (angle) { 5477 case 90: 5478 return [y, -x]; 5479 case 180: 5480 return [-x, -y]; 5481 case 270: 5482 return [-y, x]; 5483 default: 5484 return [x, y]; 5485 } 5486 } 5487 screenToPageTranslation(x, y) { 5488 return AnnotationEditor.#rotatePoint(x, y, this.parentRotation); 5489 } 5490 pageTranslationToScreen(x, y) { 5491 return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation); 5492 } 5493 #getRotationMatrix(rotation) { 5494 switch (rotation) { 5495 case 90: 5496 { 5497 const [pageWidth, pageHeight] = this.pageDimensions; 5498 return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0]; 5499 } 5500 case 180: 5501 return [-1, 0, 0, -1]; 5502 case 270: 5503 { 5504 const [pageWidth, pageHeight] = this.pageDimensions; 5505 return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0]; 5506 } 5507 default: 5508 return [1, 0, 0, 1]; 5509 } 5510 } 5511 get parentScale() { 5512 return this._uiManager.viewParameters.realScale; 5513 } 5514 get parentRotation() { 5515 return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360; 5516 } 5517 get parentDimensions() { 5518 const { 5519 parentScale, 5520 pageDimensions: [pageWidth, pageHeight] 5521 } = this; 5522 return [pageWidth * parentScale, pageHeight * parentScale]; 5523 } 5524 setDims() { 5525 const { 5526 div: { 5527 style 5528 }, 5529 width, 5530 height 5531 } = this; 5532 style.width = `${(100 * width).toFixed(2)}%`; 5533 style.height = `${(100 * height).toFixed(2)}%`; 5534 } 5535 getInitialTranslation() { 5536 return [0, 0]; 5537 } 5538 #createResizers() { 5539 if (this.#resizersDiv) { 5540 return; 5541 } 5542 this.#resizersDiv = document.createElement("div"); 5543 this.#resizersDiv.classList.add("resizers"); 5544 const classes = this._willKeepAspectRatio ? ["topLeft", "topRight", "bottomRight", "bottomLeft"] : ["topLeft", "topMiddle", "topRight", "middleRight", "bottomRight", "bottomMiddle", "bottomLeft", "middleLeft"]; 5545 const signal = this._uiManager._signal; 5546 for (const name of classes) { 5547 const div = document.createElement("div"); 5548 this.#resizersDiv.append(div); 5549 div.classList.add("resizer", name); 5550 div.setAttribute("data-resizer-name", name); 5551 div.addEventListener("pointerdown", this.#resizerPointerdown.bind(this, name), { 5552 signal 5553 }); 5554 div.addEventListener("contextmenu", noContextMenu, { 5555 signal 5556 }); 5557 div.tabIndex = -1; 5558 } 5559 this.div.prepend(this.#resizersDiv); 5560 } 5561 #resizerPointerdown(name, event) { 5562 event.preventDefault(); 5563 const { 5564 isMac 5565 } = util_FeatureTest.platform; 5566 if (event.button !== 0 || event.ctrlKey && isMac) { 5567 return; 5568 } 5569 this.#altText?.toggle(false); 5570 const savedDraggable = this._isDraggable; 5571 this._isDraggable = false; 5572 this.#lastPointerCoords = [event.screenX, event.screenY]; 5573 const ac = new AbortController(); 5574 const signal = this._uiManager.combinedSignal(ac); 5575 this.parent.togglePointerEvents(false); 5576 window.addEventListener("pointermove", this.#resizerPointermove.bind(this, name), { 5577 passive: true, 5578 capture: true, 5579 signal 5580 }); 5581 window.addEventListener("touchmove", stopEvent, { 5582 passive: false, 5583 signal 5584 }); 5585 window.addEventListener("contextmenu", noContextMenu, { 5586 signal 5587 }); 5588 this.#savedDimensions = { 5589 savedX: this.x, 5590 savedY: this.y, 5591 savedWidth: this.width, 5592 savedHeight: this.height 5593 }; 5594 const savedParentCursor = this.parent.div.style.cursor; 5595 const savedCursor = this.div.style.cursor; 5596 this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor; 5597 const pointerUpCallback = () => { 5598 ac.abort(); 5599 this.parent.togglePointerEvents(true); 5600 this.#altText?.toggle(true); 5601 this._isDraggable = savedDraggable; 5602 this.parent.div.style.cursor = savedParentCursor; 5603 this.div.style.cursor = savedCursor; 5604 this.#addResizeToUndoStack(); 5605 }; 5606 window.addEventListener("pointerup", pointerUpCallback, { 5607 signal 5608 }); 5609 window.addEventListener("blur", pointerUpCallback, { 5610 signal 5611 }); 5612 } 5613 #resize(x, y, width, height) { 5614 this.width = width; 5615 this.height = height; 5616 this.x = x; 5617 this.y = y; 5618 this.setDims(); 5619 this.fixAndSetPosition(); 5620 this._onResized(); 5621 } 5622 _onResized() {} 5623 #addResizeToUndoStack() { 5624 if (!this.#savedDimensions) { 5625 return; 5626 } 5627 const { 5628 savedX, 5629 savedY, 5630 savedWidth, 5631 savedHeight 5632 } = this.#savedDimensions; 5633 this.#savedDimensions = null; 5634 const newX = this.x; 5635 const newY = this.y; 5636 const newWidth = this.width; 5637 const newHeight = this.height; 5638 if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) { 5639 return; 5640 } 5641 this.addCommands({ 5642 cmd: this.#resize.bind(this, newX, newY, newWidth, newHeight), 5643 undo: this.#resize.bind(this, savedX, savedY, savedWidth, savedHeight), 5644 mustExec: true 5645 }); 5646 } 5647 static _round(x) { 5648 return Math.round(x * 10000) / 10000; 5649 } 5650 #resizerPointermove(name, event) { 5651 const [parentWidth, parentHeight] = this.parentDimensions; 5652 const savedX = this.x; 5653 const savedY = this.y; 5654 const savedWidth = this.width; 5655 const savedHeight = this.height; 5656 const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; 5657 const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; 5658 const rotationMatrix = this.#getRotationMatrix(this.rotation); 5659 const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; 5660 const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation); 5661 const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y]; 5662 let getPoint; 5663 let getOpposite; 5664 let isDiagonal = false; 5665 let isHorizontal = false; 5666 switch (name) { 5667 case "topLeft": 5668 isDiagonal = true; 5669 getPoint = (w, h) => [0, 0]; 5670 getOpposite = (w, h) => [w, h]; 5671 break; 5672 case "topMiddle": 5673 getPoint = (w, h) => [w / 2, 0]; 5674 getOpposite = (w, h) => [w / 2, h]; 5675 break; 5676 case "topRight": 5677 isDiagonal = true; 5678 getPoint = (w, h) => [w, 0]; 5679 getOpposite = (w, h) => [0, h]; 5680 break; 5681 case "middleRight": 5682 isHorizontal = true; 5683 getPoint = (w, h) => [w, h / 2]; 5684 getOpposite = (w, h) => [0, h / 2]; 5685 break; 5686 case "bottomRight": 5687 isDiagonal = true; 5688 getPoint = (w, h) => [w, h]; 5689 getOpposite = (w, h) => [0, 0]; 5690 break; 5691 case "bottomMiddle": 5692 getPoint = (w, h) => [w / 2, h]; 5693 getOpposite = (w, h) => [w / 2, 0]; 5694 break; 5695 case "bottomLeft": 5696 isDiagonal = true; 5697 getPoint = (w, h) => [0, h]; 5698 getOpposite = (w, h) => [w, 0]; 5699 break; 5700 case "middleLeft": 5701 isHorizontal = true; 5702 getPoint = (w, h) => [0, h / 2]; 5703 getOpposite = (w, h) => [w, h / 2]; 5704 break; 5705 } 5706 const point = getPoint(savedWidth, savedHeight); 5707 const oppositePoint = getOpposite(savedWidth, savedHeight); 5708 let transfOppositePoint = transf(...oppositePoint); 5709 const oppositeX = AnnotationEditor._round(savedX + transfOppositePoint[0]); 5710 const oppositeY = AnnotationEditor._round(savedY + transfOppositePoint[1]); 5711 let ratioX = 1; 5712 let ratioY = 1; 5713 let deltaX, deltaY; 5714 if (!event.fromKeyboard) { 5715 const { 5716 screenX, 5717 screenY 5718 } = event; 5719 const [lastScreenX, lastScreenY] = this.#lastPointerCoords; 5720 [deltaX, deltaY] = this.screenToPageTranslation(screenX - lastScreenX, screenY - lastScreenY); 5721 this.#lastPointerCoords[0] = screenX; 5722 this.#lastPointerCoords[1] = screenY; 5723 } else { 5724 ({ 5725 deltaX, 5726 deltaY 5727 } = event); 5728 } 5729 [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight); 5730 if (isDiagonal) { 5731 const oldDiag = Math.hypot(savedWidth, savedHeight); 5732 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); 5733 } else if (isHorizontal) { 5734 ratioX = MathClamp(Math.abs(oppositePoint[0] - point[0] - deltaX), minWidth, 1) / savedWidth; 5735 } else { 5736 ratioY = MathClamp(Math.abs(oppositePoint[1] - point[1] - deltaY), minHeight, 1) / savedHeight; 5737 } 5738 const newWidth = AnnotationEditor._round(savedWidth * ratioX); 5739 const newHeight = AnnotationEditor._round(savedHeight * ratioY); 5740 transfOppositePoint = transf(...getOpposite(newWidth, newHeight)); 5741 const newX = oppositeX - transfOppositePoint[0]; 5742 const newY = oppositeY - transfOppositePoint[1]; 5743 this.#initialRect ||= [this.x, this.y, this.width, this.height]; 5744 this.width = newWidth; 5745 this.height = newHeight; 5746 this.x = newX; 5747 this.y = newY; 5748 this.setDims(); 5749 this.fixAndSetPosition(); 5750 this._onResizing(); 5751 } 5752 _onResizing() {} 5753 altTextFinish() { 5754 this.#altText?.finish(); 5755 } 5756 get toolbarButtons() { 5757 return null; 5758 } 5759 async addEditToolbar() { 5760 if (this._editToolbar || this.#isInEditMode) { 5761 return this._editToolbar; 5762 } 5763 this._editToolbar = new EditorToolbar(this); 5764 this.div.append(this._editToolbar.render()); 5765 const { 5766 toolbarButtons 5767 } = this; 5768 if (toolbarButtons) { 5769 for (const [name, tool] of toolbarButtons) { 5770 await this._editToolbar.addButton(name, tool); 5771 } 5772 } 5773 if (!this.hasComment) { 5774 this._editToolbar.addButton("comment", this.addCommentButton()); 5775 } 5776 this._editToolbar.addButton("delete"); 5777 return this._editToolbar; 5778 } 5779 addCommentButtonInToolbar() { 5780 this._editToolbar?.addButtonBefore("comment", this.addCommentButton(), ".deleteButton"); 5781 } 5782 removeCommentButtonFromToolbar() { 5783 this._editToolbar?.removeButton("comment"); 5784 } 5785 removeEditToolbar() { 5786 this._editToolbar?.remove(); 5787 this._editToolbar = null; 5788 this.#altText?.destroy(); 5789 } 5790 addContainer(container) { 5791 const editToolbarDiv = this._editToolbar?.div; 5792 if (editToolbarDiv) { 5793 editToolbarDiv.before(container); 5794 } else { 5795 this.div.append(container); 5796 } 5797 } 5798 getClientDimensions() { 5799 return this.div.getBoundingClientRect(); 5800 } 5801 createAltText() { 5802 if (!this.#altText) { 5803 AltText.initialize(AnnotationEditor._l10n); 5804 this.#altText = new AltText(this); 5805 if (this.#accessibilityData) { 5806 this.#altText.data = this.#accessibilityData; 5807 this.#accessibilityData = null; 5808 } 5809 } 5810 return this.#altText; 5811 } 5812 get altTextData() { 5813 return this.#altText?.data; 5814 } 5815 set altTextData(data) { 5816 if (!this.#altText) { 5817 return; 5818 } 5819 this.#altText.data = data; 5820 } 5821 get guessedAltText() { 5822 return this.#altText?.guessedText; 5823 } 5824 async setGuessedAltText(text) { 5825 await this.#altText?.setGuessedText(text); 5826 } 5827 serializeAltText(isForCopying) { 5828 return this.#altText?.serialize(isForCopying); 5829 } 5830 hasAltText() { 5831 return !!this.#altText && !this.#altText.isEmpty(); 5832 } 5833 hasAltTextData() { 5834 return this.#altText?.hasData() ?? false; 5835 } 5836 focusCommentButton() { 5837 this.#comment?.focusButton(); 5838 } 5839 addCommentButton() { 5840 return this.canAddComment ? this.#comment ||= new Comment(this) : null; 5841 } 5842 addStandaloneCommentButton() { 5843 if (!this._uiManager.hasCommentManager()) { 5844 return; 5845 } 5846 if (this.#commentStandaloneButton) { 5847 if (this._uiManager.isEditingMode()) { 5848 this.#commentStandaloneButton.classList.remove("hidden"); 5849 } 5850 return; 5851 } 5852 if (!this.hasComment) { 5853 return; 5854 } 5855 this.#commentStandaloneButton = this.#comment.renderForStandalone(); 5856 this.div.append(this.#commentStandaloneButton); 5857 } 5858 removeStandaloneCommentButton() { 5859 this.#comment.removeStandaloneCommentButton(); 5860 this.#commentStandaloneButton = null; 5861 } 5862 hideStandaloneCommentButton() { 5863 this.#commentStandaloneButton?.classList.add("hidden"); 5864 } 5865 get comment() { 5866 if (!this.#comment) { 5867 return null; 5868 } 5869 const { 5870 data: { 5871 richText, 5872 text, 5873 date, 5874 deleted 5875 } 5876 } = this.#comment; 5877 return { 5878 text, 5879 richText, 5880 date, 5881 deleted, 5882 color: this.getNonHCMColor(), 5883 opacity: this.opacity ?? 1 5884 }; 5885 } 5886 set comment(value) { 5887 this.#comment ||= new Comment(this); 5888 if (typeof value === "object" && value !== null) { 5889 this.#comment.restoreData(value); 5890 } else { 5891 this.#comment.data = value; 5892 } 5893 if (this.hasComment) { 5894 this.removeCommentButtonFromToolbar(); 5895 this.addStandaloneCommentButton(); 5896 this._uiManager.updateComment(this); 5897 } else { 5898 this.addCommentButtonInToolbar(); 5899 this.removeStandaloneCommentButton(); 5900 this._uiManager.removeComment(this); 5901 } 5902 } 5903 setCommentData({ 5904 comment, 5905 popupRef, 5906 richText 5907 }) { 5908 if (!popupRef) { 5909 return; 5910 } 5911 this.#comment ||= new Comment(this); 5912 this.#comment.setInitialText(comment, richText); 5913 if (!this.annotationElementId) { 5914 return; 5915 } 5916 const storedData = this._uiManager.getAndRemoveDataFromAnnotationStorage(this.annotationElementId); 5917 if (storedData) { 5918 this.updateFromAnnotationLayer(storedData); 5919 } 5920 } 5921 get hasEditedComment() { 5922 return this.#comment?.hasBeenEdited(); 5923 } 5924 get hasDeletedComment() { 5925 return this.#comment?.isDeleted(); 5926 } 5927 get hasComment() { 5928 return !!this.#comment && !this.#comment.isEmpty() && !this.#comment.isDeleted(); 5929 } 5930 async editComment(options) { 5931 this.#comment ||= new Comment(this); 5932 this.#comment.edit(options); 5933 } 5934 toggleComment(isSelected, visibility = undefined) { 5935 if (this.hasComment) { 5936 this._uiManager.toggleComment(this, isSelected, visibility); 5937 } 5938 } 5939 setSelectedCommentButton(selected) { 5940 this.#comment.setSelectedButton(selected); 5941 } 5942 addComment(serialized) { 5943 if (this.hasEditedComment) { 5944 const DEFAULT_POPUP_WIDTH = 180; 5945 const DEFAULT_POPUP_HEIGHT = 100; 5946 const [,,, trY] = serialized.rect; 5947 const [pageWidth] = this.pageDimensions; 5948 const [pageX] = this.pageTranslation; 5949 const blX = pageX + pageWidth + 1; 5950 const blY = trY - DEFAULT_POPUP_HEIGHT; 5951 const trX = blX + DEFAULT_POPUP_WIDTH; 5952 serialized.popup = { 5953 contents: this.comment.text, 5954 deleted: this.comment.deleted, 5955 rect: [blX, blY, trX, trY] 5956 }; 5957 } 5958 } 5959 updateFromAnnotationLayer({ 5960 popup: { 5961 contents, 5962 deleted 5963 } 5964 }) { 5965 this.#comment.data = deleted ? null : contents; 5966 } 5967 get parentBoundingClientRect() { 5968 return this.parent.boundingClientRect; 5969 } 5970 render() { 5971 const div = this.div = document.createElement("div"); 5972 div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360); 5973 div.className = this.name; 5974 div.setAttribute("id", this.id); 5975 div.tabIndex = this.#disabled ? -1 : 0; 5976 div.setAttribute("role", "application"); 5977 if (this.defaultL10nId) { 5978 div.setAttribute("data-l10n-id", this.defaultL10nId); 5979 } 5980 if (!this._isVisible) { 5981 div.classList.add("hidden"); 5982 } 5983 this.setInForeground(); 5984 this.#addFocusListeners(); 5985 const [parentWidth, parentHeight] = this.parentDimensions; 5986 if (this.parentRotation % 180 !== 0) { 5987 div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`; 5988 div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`; 5989 } 5990 const [tx, ty] = this.getInitialTranslation(); 5991 this.translate(tx, ty); 5992 bindEvents(this, div, ["keydown", "pointerdown", "dblclick"]); 5993 if (this.isResizable && this._uiManager._supportsPinchToZoom) { 5994 this.#touchManager ||= new TouchManager({ 5995 container: div, 5996 isPinchingDisabled: () => !this.isSelected, 5997 onPinchStart: this.#touchPinchStartCallback.bind(this), 5998 onPinching: this.#touchPinchCallback.bind(this), 5999 onPinchEnd: this.#touchPinchEndCallback.bind(this), 6000 signal: this._uiManager._signal 6001 }); 6002 } 6003 this.addStandaloneCommentButton(); 6004 this._uiManager._editorUndoBar?.hide(); 6005 return div; 6006 } 6007 #touchPinchStartCallback() { 6008 this.#savedDimensions = { 6009 savedX: this.x, 6010 savedY: this.y, 6011 savedWidth: this.width, 6012 savedHeight: this.height 6013 }; 6014 this.#altText?.toggle(false); 6015 this.parent.togglePointerEvents(false); 6016 } 6017 #touchPinchCallback(_origin, prevDistance, distance) { 6018 const slowDownFactor = 0.7; 6019 let factor = slowDownFactor * (distance / prevDistance) + 1 - slowDownFactor; 6020 if (factor === 1) { 6021 return; 6022 } 6023 const rotationMatrix = this.#getRotationMatrix(this.rotation); 6024 const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y]; 6025 const [parentWidth, parentHeight] = this.parentDimensions; 6026 const savedX = this.x; 6027 const savedY = this.y; 6028 const savedWidth = this.width; 6029 const savedHeight = this.height; 6030 const minWidth = AnnotationEditor.MIN_SIZE / parentWidth; 6031 const minHeight = AnnotationEditor.MIN_SIZE / parentHeight; 6032 factor = Math.max(Math.min(factor, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight); 6033 const newWidth = AnnotationEditor._round(savedWidth * factor); 6034 const newHeight = AnnotationEditor._round(savedHeight * factor); 6035 if (newWidth === savedWidth && newHeight === savedHeight) { 6036 return; 6037 } 6038 this.#initialRect ||= [savedX, savedY, savedWidth, savedHeight]; 6039 const transfCenterPoint = transf(savedWidth / 2, savedHeight / 2); 6040 const centerX = AnnotationEditor._round(savedX + transfCenterPoint[0]); 6041 const centerY = AnnotationEditor._round(savedY + transfCenterPoint[1]); 6042 const newTransfCenterPoint = transf(newWidth / 2, newHeight / 2); 6043 this.x = centerX - newTransfCenterPoint[0]; 6044 this.y = centerY - newTransfCenterPoint[1]; 6045 this.width = newWidth; 6046 this.height = newHeight; 6047 this.setDims(); 6048 this.fixAndSetPosition(); 6049 this._onResizing(); 6050 } 6051 #touchPinchEndCallback() { 6052 this.#altText?.toggle(true); 6053 this.parent.togglePointerEvents(true); 6054 this.#addResizeToUndoStack(); 6055 } 6056 pointerdown(event) { 6057 const { 6058 isMac 6059 } = util_FeatureTest.platform; 6060 if (event.button !== 0 || event.ctrlKey && isMac) { 6061 event.preventDefault(); 6062 return; 6063 } 6064 this.#hasBeenClicked = true; 6065 if (this._isDraggable) { 6066 this.#setUpDragSession(event); 6067 return; 6068 } 6069 this.#selectOnPointerEvent(event); 6070 } 6071 #selectOnPointerEvent(event) { 6072 const { 6073 isMac 6074 } = util_FeatureTest.platform; 6075 if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) { 6076 this.parent.toggleSelected(this); 6077 } else { 6078 this.parent.setSelected(this); 6079 } 6080 } 6081 #setUpDragSession(event) { 6082 const { 6083 isSelected 6084 } = this; 6085 this._uiManager.setUpDragSession(); 6086 let hasDraggingStarted = false; 6087 const ac = new AbortController(); 6088 const signal = this._uiManager.combinedSignal(ac); 6089 const opts = { 6090 capture: true, 6091 passive: false, 6092 signal 6093 }; 6094 const cancelDrag = e => { 6095 ac.abort(); 6096 this.#dragPointerId = null; 6097 this.#hasBeenClicked = false; 6098 if (!this._uiManager.endDragSession()) { 6099 this.#selectOnPointerEvent(e); 6100 } 6101 if (hasDraggingStarted) { 6102 this._onStopDragging(); 6103 } 6104 }; 6105 if (isSelected) { 6106 this.#prevDragX = event.clientX; 6107 this.#prevDragY = event.clientY; 6108 this.#dragPointerId = event.pointerId; 6109 this.#dragPointerType = event.pointerType; 6110 window.addEventListener("pointermove", e => { 6111 if (!hasDraggingStarted) { 6112 hasDraggingStarted = true; 6113 this._uiManager.toggleComment(this, true, false); 6114 this._onStartDragging(); 6115 } 6116 const { 6117 clientX: x, 6118 clientY: y, 6119 pointerId 6120 } = e; 6121 if (pointerId !== this.#dragPointerId) { 6122 stopEvent(e); 6123 return; 6124 } 6125 const [tx, ty] = this.screenToPageTranslation(x - this.#prevDragX, y - this.#prevDragY); 6126 this.#prevDragX = x; 6127 this.#prevDragY = y; 6128 this._uiManager.dragSelectedEditors(tx, ty); 6129 }, opts); 6130 window.addEventListener("touchmove", stopEvent, opts); 6131 window.addEventListener("pointerdown", e => { 6132 if (e.pointerType === this.#dragPointerType) { 6133 if (this.#touchManager || e.isPrimary) { 6134 cancelDrag(e); 6135 } 6136 } 6137 stopEvent(e); 6138 }, opts); 6139 } 6140 const pointerUpCallback = e => { 6141 if (!this.#dragPointerId || this.#dragPointerId === e.pointerId) { 6142 cancelDrag(e); 6143 return; 6144 } 6145 stopEvent(e); 6146 }; 6147 window.addEventListener("pointerup", pointerUpCallback, { 6148 signal 6149 }); 6150 window.addEventListener("blur", pointerUpCallback, { 6151 signal 6152 }); 6153 } 6154 _onStartDragging() {} 6155 _onStopDragging() {} 6156 moveInDOM() { 6157 if (this.#moveInDOMTimeout) { 6158 clearTimeout(this.#moveInDOMTimeout); 6159 } 6160 this.#moveInDOMTimeout = setTimeout(() => { 6161 this.#moveInDOMTimeout = null; 6162 this.parent?.moveEditorInDOM(this); 6163 }, 0); 6164 } 6165 _setParentAndPosition(parent, x, y) { 6166 parent.changeParent(this); 6167 this.x = x; 6168 this.y = y; 6169 this.fixAndSetPosition(); 6170 this._onTranslated(); 6171 } 6172 getRect(tx, ty, rotation = this.rotation) { 6173 const scale = this.parentScale; 6174 const [pageWidth, pageHeight] = this.pageDimensions; 6175 const [pageX, pageY] = this.pageTranslation; 6176 const shiftX = tx / scale; 6177 const shiftY = ty / scale; 6178 const x = this.x * pageWidth; 6179 const y = this.y * pageHeight; 6180 const width = this.width * pageWidth; 6181 const height = this.height * pageHeight; 6182 switch (rotation) { 6183 case 0: 6184 return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY]; 6185 case 90: 6186 return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY]; 6187 case 180: 6188 return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY]; 6189 case 270: 6190 return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY]; 6191 default: 6192 throw new Error("Invalid rotation"); 6193 } 6194 } 6195 getRectInCurrentCoords(rect, pageHeight) { 6196 const [x1, y1, x2, y2] = rect; 6197 const width = x2 - x1; 6198 const height = y2 - y1; 6199 switch (this.rotation) { 6200 case 0: 6201 return [x1, pageHeight - y2, width, height]; 6202 case 90: 6203 return [x1, pageHeight - y1, height, width]; 6204 case 180: 6205 return [x2, pageHeight - y1, width, height]; 6206 case 270: 6207 return [x2, pageHeight - y2, height, width]; 6208 default: 6209 throw new Error("Invalid rotation"); 6210 } 6211 } 6212 getPDFRect() { 6213 return this.getRect(0, 0); 6214 } 6215 getNonHCMColor() { 6216 return this.color && AnnotationEditor._colorManager.convert(this._uiManager.getNonHCMColor(this.color)); 6217 } 6218 onUpdatedColor() { 6219 this.#comment?.onUpdatedColor(); 6220 } 6221 getData() { 6222 const { 6223 comment: { 6224 text: str, 6225 color, 6226 date, 6227 opacity, 6228 deleted, 6229 richText 6230 }, 6231 uid: id, 6232 pageIndex, 6233 creationDate, 6234 modificationDate 6235 } = this; 6236 return { 6237 id, 6238 pageIndex, 6239 rect: this.getPDFRect(), 6240 richText, 6241 contentsObj: { 6242 str 6243 }, 6244 creationDate, 6245 modificationDate: date || modificationDate, 6246 popupRef: !deleted, 6247 color, 6248 opacity 6249 }; 6250 } 6251 onceAdded(focus) {} 6252 isEmpty() { 6253 return false; 6254 } 6255 enableEditMode() { 6256 if (this.isInEditMode()) { 6257 return false; 6258 } 6259 this.parent.setEditingState(false); 6260 this.#isInEditMode = true; 6261 return true; 6262 } 6263 disableEditMode() { 6264 if (!this.isInEditMode()) { 6265 return false; 6266 } 6267 this.parent.setEditingState(true); 6268 this.#isInEditMode = false; 6269 return true; 6270 } 6271 isInEditMode() { 6272 return this.#isInEditMode; 6273 } 6274 shouldGetKeyboardEvents() { 6275 return this.#isResizerEnabledForKeyboard; 6276 } 6277 needsToBeRebuilt() { 6278 return this.div && !this.isAttachedToDOM; 6279 } 6280 get isOnScreen() { 6281 const { 6282 top, 6283 left, 6284 bottom, 6285 right 6286 } = this.getClientDimensions(); 6287 const { 6288 innerHeight, 6289 innerWidth 6290 } = window; 6291 return left < innerWidth && right > 0 && top < innerHeight && bottom > 0; 6292 } 6293 #addFocusListeners() { 6294 if (this.#focusAC || !this.div) { 6295 return; 6296 } 6297 this.#focusAC = new AbortController(); 6298 const signal = this._uiManager.combinedSignal(this.#focusAC); 6299 this.div.addEventListener("focusin", this.focusin.bind(this), { 6300 signal 6301 }); 6302 this.div.addEventListener("focusout", this.focusout.bind(this), { 6303 signal 6304 }); 6305 } 6306 rebuild() { 6307 this.#addFocusListeners(); 6308 } 6309 rotate(_angle) {} 6310 resize() {} 6311 serializeDeleted() { 6312 return { 6313 id: this.annotationElementId, 6314 deleted: true, 6315 pageIndex: this.pageIndex, 6316 popupRef: this._initialData?.popupRef || "" 6317 }; 6318 } 6319 serialize(isForCopying = false, context = null) { 6320 return { 6321 annotationType: this.mode, 6322 pageIndex: this.pageIndex, 6323 rect: this.getPDFRect(), 6324 rotation: this.rotation, 6325 structTreeParentId: this._structTreeParentId, 6326 popupRef: this._initialData?.popupRef || "" 6327 }; 6328 } 6329 static async deserialize(data, parent, uiManager) { 6330 const editor = new this.prototype.constructor({ 6331 parent, 6332 id: parent.getNextId(), 6333 uiManager, 6334 annotationElementId: data.annotationElementId, 6335 creationDate: data.creationDate, 6336 modificationDate: data.modificationDate 6337 }); 6338 editor.rotation = data.rotation; 6339 editor.#accessibilityData = data.accessibilityData; 6340 editor._isCopy = data.isCopy || false; 6341 const [pageWidth, pageHeight] = editor.pageDimensions; 6342 const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight); 6343 editor.x = x / pageWidth; 6344 editor.y = y / pageHeight; 6345 editor.width = width / pageWidth; 6346 editor.height = height / pageHeight; 6347 return editor; 6348 } 6349 get hasBeenModified() { 6350 return !!this.annotationElementId && (this.deleted || this.serialize() !== null); 6351 } 6352 remove() { 6353 this.#focusAC?.abort(); 6354 this.#focusAC = null; 6355 if (!this.isEmpty()) { 6356 this.commit(); 6357 } 6358 if (this.parent) { 6359 this.parent.remove(this); 6360 } else { 6361 this._uiManager.removeEditor(this); 6362 } 6363 this.hideCommentPopup(); 6364 if (this.#moveInDOMTimeout) { 6365 clearTimeout(this.#moveInDOMTimeout); 6366 this.#moveInDOMTimeout = null; 6367 } 6368 this.#stopResizing(); 6369 this.removeEditToolbar(); 6370 if (this.#telemetryTimeouts) { 6371 for (const timeout of this.#telemetryTimeouts.values()) { 6372 clearTimeout(timeout); 6373 } 6374 this.#telemetryTimeouts = null; 6375 } 6376 this.parent = null; 6377 this.#touchManager?.destroy(); 6378 this.#touchManager
vendor: 12,923 bytes, lines 6378-6841
6378 = null; 6379 this.#fakeAnnotation?.remove(); 6380 this.#fakeAnnotation = null; 6381 } 6382 get isResizable() { 6383 return false; 6384 } 6385 makeResizable() { 6386 if (this.isResizable) { 6387 this.#createResizers(); 6388 this.#resizersDiv.classList.remove("hidden"); 6389 } 6390 } 6391 get toolbarPosition() { 6392 return null; 6393 } 6394 get commentButtonPosition() { 6395 return this._uiManager.direction === "ltr" ? [1, 0] : [0, 0]; 6396 } 6397 get commentButtonPositionInPage() { 6398 const { 6399 commentButtonPosition: [posX, posY] 6400 } = this; 6401 const [blX, blY, trX, trY] = this.getPDFRect(); 6402 return [AnnotationEditor._round(blX + (trX - blX) * posX), AnnotationEditor._round(blY + (trY - blY) * (1 - posY))]; 6403 } 6404 get commentButtonColor() { 6405 return this._uiManager.makeCommentColor(this.getNonHCMColor(), this.opacity); 6406 } 6407 get commentPopupPosition() { 6408 return this.#comment.commentPopupPositionInLayer; 6409 } 6410 set commentPopupPosition(pos) { 6411 this.#comment.commentPopupPositionInLayer = pos; 6412 } 6413 hasDefaultPopupPosition() { 6414 return this.#comment.hasDefaultPopupPosition(); 6415 } 6416 get commentButtonWidth() { 6417 return this.#comment.commentButtonWidth; 6418 } 6419 get elementBeforePopup() { 6420 return this.div; 6421 } 6422 setCommentButtonStates(options) { 6423 this.#comment?.setCommentButtonStates(options); 6424 } 6425 keydown(event) { 6426 if (!this.isResizable || event.target !== this.div || event.key !== "Enter") { 6427 return; 6428 } 6429 this._uiManager.setSelected(this); 6430 this.#savedDimensions = { 6431 savedX: this.x, 6432 savedY: this.y, 6433 savedWidth: this.width, 6434 savedHeight: this.height 6435 }; 6436 const children = this.#resizersDiv.children; 6437 if (!this.#allResizerDivs) { 6438 this.#allResizerDivs = Array.from(children); 6439 const boundResizerKeydown = this.#resizerKeydown.bind(this); 6440 const boundResizerBlur = this.#resizerBlur.bind(this); 6441 const signal = this._uiManager._signal; 6442 for (const div of this.#allResizerDivs) { 6443 const name = div.getAttribute("data-resizer-name"); 6444 div.setAttribute("role", "spinbutton"); 6445 div.addEventListener("keydown", boundResizerKeydown, { 6446 signal 6447 }); 6448 div.addEventListener("blur", boundResizerBlur, { 6449 signal 6450 }); 6451 div.addEventListener("focus", this.#resizerFocus.bind(this, name), { 6452 signal 6453 }); 6454 div.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); 6455 } 6456 } 6457 const first = this.#allResizerDivs[0]; 6458 let firstPosition = 0; 6459 for (const div of children) { 6460 if (div === first) { 6461 break; 6462 } 6463 firstPosition++; 6464 } 6465 const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4); 6466 if (nextFirstPosition !== firstPosition) { 6467 if (nextFirstPosition < firstPosition) { 6468 for (let i = 0; i < firstPosition - nextFirstPosition; i++) { 6469 this.#resizersDiv.append(this.#resizersDiv.firstElementChild); 6470 } 6471 } else if (nextFirstPosition > firstPosition) { 6472 for (let i = 0; i < nextFirstPosition - firstPosition; i++) { 6473 this.#resizersDiv.firstElementChild.before(this.#resizersDiv.lastElementChild); 6474 } 6475 } 6476 let i = 0; 6477 for (const child of children) { 6478 const div = this.#allResizerDivs[i++]; 6479 const name = div.getAttribute("data-resizer-name"); 6480 child.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]); 6481 } 6482 } 6483 this.#setResizerTabIndex(0); 6484 this.#isResizerEnabledForKeyboard = true; 6485 this.#resizersDiv.firstElementChild.focus({ 6486 focusVisible: true 6487 }); 6488 event.preventDefault(); 6489 event.stopImmediatePropagation(); 6490 } 6491 #resizerKeydown(event) { 6492 AnnotationEditor._resizerKeyboardManager.exec(this, event); 6493 } 6494 #resizerBlur(event) { 6495 if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) { 6496 this.#stopResizing(); 6497 } 6498 } 6499 #resizerFocus(name) { 6500 this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : ""; 6501 } 6502 #setResizerTabIndex(value) { 6503 if (!this.#allResizerDivs) { 6504 return; 6505 } 6506 for (const div of this.#allResizerDivs) { 6507 div.tabIndex = value; 6508 } 6509 } 6510 _resizeWithKeyboard(x, y) { 6511 if (!this.#isResizerEnabledForKeyboard) { 6512 return; 6513 } 6514 this.#resizerPointermove(this.#focusedResizerName, { 6515 deltaX: x, 6516 deltaY: y, 6517 fromKeyboard: true 6518 }); 6519 } 6520 #stopResizing() { 6521 this.#isResizerEnabledForKeyboard = false; 6522 this.#setResizerTabIndex(-1); 6523 this.#addResizeToUndoStack(); 6524 } 6525 _stopResizingWithKeyboard() { 6526 this.#stopResizing(); 6527 this.div.focus(); 6528 } 6529 select() { 6530 if (this.isSelected && this._editToolbar) { 6531 this._editToolbar.show(); 6532 return; 6533 } 6534 this.isSelected = true; 6535 this.makeResizable(); 6536 this.div?.classList.add("selectedEditor"); 6537 if (!this._editToolbar) { 6538 this.addEditToolbar().then(() => { 6539 if (this.div?.classList.contains("selectedEditor")) { 6540 this._editToolbar?.show(); 6541 } 6542 }); 6543 return; 6544 } 6545 this._editToolbar?.show(); 6546 this.#altText?.toggleAltTextBadge(false); 6547 } 6548 focus() { 6549 if (this.div && !this.div.contains(document.activeElement)) { 6550 setTimeout(() => this.div?.focus({ 6551 preventScroll: true 6552 }), 0); 6553 } 6554 } 6555 unselect() { 6556 if (!this.isSelected) { 6557 return; 6558 } 6559 this.isSelected = false; 6560 this.#resizersDiv?.classList.add("hidden"); 6561 this.div?.classList.remove("selectedEditor"); 6562 if (this.div?.contains(document.activeElement)) { 6563 this._uiManager.currentLayer.div.focus({ 6564 preventScroll: true 6565 }); 6566 } 6567 this._editToolbar?.hide(); 6568 this.#altText?.toggleAltTextBadge(true); 6569 this.hideCommentPopup(); 6570 } 6571 hideCommentPopup() { 6572 if (this.hasComment) { 6573 this._uiManager.toggleComment(null); 6574 } 6575 } 6576 updateParams(type, value) {} 6577 disableEditing() {} 6578 enableEditing() {} 6579 get canChangeContent() { 6580 return false; 6581 } 6582 enterInEditMode() { 6583 if (!this.canChangeContent) { 6584 return; 6585 } 6586 this.enableEditMode(); 6587 this.div.focus(); 6588 } 6589 dblclick(event) { 6590 if (event.target.nodeName === "BUTTON") { 6591 return; 6592 } 6593 this.enterInEditMode(); 6594 this.parent.updateToolbar({ 6595 mode: this.constructor._editorType, 6596 editId: this.uid 6597 }); 6598 } 6599 getElementForAltText() { 6600 return this.div; 6601 } 6602 get contentDiv() { 6603 return this.div; 6604 } 6605 get isEditing() { 6606 return this.#isEditing; 6607 } 6608 set isEditing(value) { 6609 this.#isEditing = value; 6610 if (!this.parent) { 6611 return; 6612 } 6613 if (value) { 6614 this.parent.setSelected(this); 6615 this.parent.setActiveEditor(this); 6616 } else { 6617 this.parent.setActiveEditor(null); 6618 } 6619 } 6620 static get MIN_SIZE() { 6621 return 16; 6622 } 6623 static canCreateNewEmptyEditor() { 6624 return true; 6625 } 6626 get telemetryInitialData() { 6627 return { 6628 action: "added" 6629 }; 6630 } 6631 get telemetryFinalData() { 6632 return null; 6633 } 6634 _reportTelemetry(data, mustWait = false) { 6635 if (mustWait) { 6636 this.#telemetryTimeouts ||= new Map(); 6637 const { 6638 action 6639 } = data; 6640 let timeout = this.#telemetryTimeouts.get(action); 6641 if (timeout) { 6642 clearTimeout(timeout); 6643 } 6644 timeout = setTimeout(() => { 6645 this._reportTelemetry(data); 6646 this.#telemetryTimeouts.delete(action); 6647 if (this.#telemetryTimeouts.size === 0) { 6648 this.#telemetryTimeouts = null; 6649 } 6650 }, AnnotationEditor._telemetryTimeout); 6651 this.#telemetryTimeouts.set(action, timeout); 6652 return; 6653 } 6654 data.type ||= this.editorType; 6655 this._uiManager._eventBus.dispatch("reporttelemetry", { 6656 source: this, 6657 details: { 6658 type: "editing", 6659 data 6660 } 6661 }); 6662 } 6663 show(visible = this._isVisible) { 6664 this.div.classList.toggle("hidden", !visible); 6665 this._isVisible = visible; 6666 } 6667 enable() { 6668 if (this.div) { 6669 this.div.tabIndex = 0; 6670 } 6671 this.#disabled = false; 6672 } 6673 disable() { 6674 if (this.div) { 6675 this.div.tabIndex = -1; 6676 } 6677 this.#disabled = true; 6678 } 6679 updateFakeAnnotationElement(annotationLayer) { 6680 if (!this.#fakeAnnotation && !this.deleted) { 6681 this.#fakeAnnotation = annotationLayer.addFakeAnnotation(this); 6682 return; 6683 } 6684 if (this.deleted) { 6685 this.#fakeAnnotation.remove(); 6686 this.#fakeAnnotation = null; 6687 return; 6688 } 6689 if (this.hasEditedComment || this._hasBeenMoved || this._hasBeenResized) { 6690 this.#fakeAnnotation.updateEdited({ 6691 rect: this.getPDFRect(), 6692 popup: this.comment 6693 }); 6694 } 6695 } 6696 renderAnnotationElement(annotation) { 6697 if (this.deleted) { 6698 annotation.hide(); 6699 return null; 6700 } 6701 let content = annotation.container.querySelector(".annotationContent"); 6702 if (!content) { 6703 content = document.createElement("div"); 6704 content.classList.add("annotationContent", this.editorType); 6705 annotation.container.prepend(content); 6706 } else if (content.nodeName === "CANVAS") { 6707 const canvas = content; 6708 content = document.createElement("div"); 6709 content.classList.add("annotationContent", this.editorType); 6710 canvas.before(content); 6711 } 6712 return content; 6713 } 6714 resetAnnotationElement(annotation) { 6715 const { 6716 firstElementChild 6717 } = annotation.container; 6718 if (firstElementChild?.nodeName === "DIV" && firstElementChild.classList.contains("annotationContent")) { 6719 firstElementChild.remove(); 6720 } 6721 } 6722} 6723class FakeEditor extends AnnotationEditor { 6724 constructor(params) { 6725 super(params); 6726 this.annotationElementId = params.annotationElementId; 6727 this.deleted = true; 6728 } 6729 serialize() { 6730 return this.serializeDeleted(); 6731 } 6732} 6733 6734;// ./src/shared/murmurhash3.js 6735const SEED = 0xc3d2e1f0; 6736const MASK_HIGH = 0xffff0000; 6737const MASK_LOW = 0xffff; 6738class MurmurHash3_64 { 6739 constructor(seed) { 6740 this.h1 = seed ? seed & 0xffffffff : SEED; 6741 this.h2 = seed ? seed & 0xffffffff : SEED; 6742 } 6743 update(input) { 6744 let data, length; 6745 if (typeof input === "string") { 6746 data = new Uint8Array(input.length * 2); 6747 length = 0; 6748 for (let i = 0, ii = input.length; i < ii; i++) { 6749 const code = input.charCodeAt(i); 6750 if (code <= 0xff) { 6751 data[length++] = code; 6752 } else { 6753 data[length++] = code >>> 8; 6754 data[length++] = code & 0xff; 6755 } 6756 } 6757 } else if (ArrayBuffer.isView(input)) { 6758 data = input.slice(); 6759 length = data.byteLength; 6760 } else { 6761 throw new Error("Invalid data format, must be a string or TypedArray."); 6762 } 6763 const blockCounts = length >> 2; 6764 const tailLength = length - blockCounts * 4; 6765 const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts); 6766 let k1 = 0, 6767 k2 = 0; 6768 let h1 = this.h1, 6769 h2 = this.h2; 6770 const C1 = 0xcc9e2d51, 6771 C2 = 0x1b873593; 6772 const C1_LOW = C1 & MASK_LOW, 6773 C2_LOW = C2 & MASK_LOW; 6774 for (let i = 0; i < blockCounts; i++) { 6775 if (i & 1) { 6776 k1 = dataUint32[i]; 6777 k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; 6778 k1 = k1 << 15 | k1 >>> 17; 6779 k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; 6780 h1 ^= k1; 6781 h1 = h1 << 13 | h1 >>> 19; 6782 h1 = h1 * 5 + 0xe6546b64; 6783 } else { 6784 k2 = dataUint32[i]; 6785 k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW; 6786 k2 = k2 << 15 | k2 >>> 17; 6787 k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW; 6788 h2 ^= k2; 6789 h2 = h2 << 13 | h2 >>> 19; 6790 h2 = h2 * 5 + 0xe6546b64; 6791 } 6792 } 6793 k1 = 0; 6794 switch (tailLength) { 6795 case 3: 6796 k1 ^= data[blockCounts * 4 + 2] << 16; 6797 case 2: 6798 k1 ^= data[blockCounts * 4 + 1] << 8; 6799 case 1: 6800 k1 ^= data[blockCounts * 4]; 6801 k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW; 6802 k1 = k1 << 15 | k1 >>> 17; 6803 k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW; 6804 if (blockCounts & 1) { 6805 h1 ^= k1; 6806 } else { 6807 h2 ^= k1; 6808 } 6809 } 6810 this.h1 = h1; 6811 this.h2 = h2; 6812 } 6813 hexdigest() { 6814 let h1 = this.h1, 6815 h2 = this.h2; 6816 h1 ^= h2 >>> 1; 6817 h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW; 6818 h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16; 6819 h1 ^= h2 >>> 1; 6820 h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW; 6821 h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16; 6822 h1 ^= h2 >>> 1; 6823 return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0"); 6824 } 6825} 6826 6827;// ./src/display/annotation_storage.js 6828 6829 6830 6831const SerializableEmpty = Object.freeze({ 6832 map: null, 6833 hash: "", 6834 transfer: undefined 6835}); 6836class AnnotationStorage { 6837 #modified = false; 6838 #modifiedIds = null; 6839 #editorsMap = null; 6840 #storage = new Map(); 6841
vendor: 18,603 bytes, lines 6841-7451
6841constructor() { 6842 this.onSetModified = null; 6843 this.onResetModified = null; 6844 this.onAnnotationEditor = null; 6845 } 6846 getValue(key, defaultValue) { 6847 const value = this.#storage.get(key); 6848 if (value === undefined) { 6849 return defaultValue; 6850 } 6851 return Object.assign(defaultValue, value); 6852 } 6853 getRawValue(key) { 6854 return this.#storage.get(key); 6855 } 6856 remove(key) { 6857 const storedValue = this.#storage.get(key); 6858 if (storedValue === undefined) { 6859 return; 6860 } 6861 if (storedValue instanceof AnnotationEditor) { 6862 this.#editorsMap.delete(storedValue.annotationElementId); 6863 } 6864 this.#storage.delete(key); 6865 if (this.#storage.size === 0) { 6866 this.resetModified(); 6867 } 6868 if (typeof this.onAnnotationEditor === "function") { 6869 for (const value of this.#storage.values()) { 6870 if (value instanceof AnnotationEditor) { 6871 return; 6872 } 6873 } 6874 this.onAnnotationEditor(null); 6875 } 6876 } 6877 setValue(key, value) { 6878 const obj = this.#storage.get(key); 6879 let modified = false; 6880 if (obj !== undefined) { 6881 for (const [entry, val] of Object.entries(value)) { 6882 if (obj[entry] !== val) { 6883 modified = true; 6884 obj[entry] = val; 6885 } 6886 } 6887 } else { 6888 modified = true; 6889 this.#storage.set(key, value); 6890 } 6891 if (modified) { 6892 this.#setModified(); 6893 } 6894 if (value instanceof AnnotationEditor) { 6895 (this.#editorsMap ||= new Map()).set(value.annotationElementId, value); 6896 if (typeof this.onAnnotationEditor === "function") { 6897 this.onAnnotationEditor(value.constructor._type); 6898 } 6899 } 6900 } 6901 has(key) { 6902 return this.#storage.has(key); 6903 } 6904 get size() { 6905 return this.#storage.size; 6906 } 6907 #setModified() { 6908 if (!this.#modified) { 6909 this.#modified = true; 6910 if (typeof this.onSetModified === "function") { 6911 this.onSetModified(); 6912 } 6913 } 6914 } 6915 resetModified() { 6916 if (this.#modified) { 6917 this.#modified = false; 6918 if (typeof this.onResetModified === "function") { 6919 this.onResetModified(); 6920 } 6921 } 6922 } 6923 get print() { 6924 return new PrintAnnotationStorage(this); 6925 } 6926 get serializable() { 6927 if (this.#storage.size === 0) { 6928 return SerializableEmpty; 6929 } 6930 const map = new Map(), 6931 hash = new MurmurHash3_64(), 6932 transfer = []; 6933 const context = Object.create(null); 6934 let hasBitmap = false; 6935 for (const [key, val] of this.#storage) { 6936 const serialized = val instanceof AnnotationEditor ? val.serialize(false, context) : val; 6937 if (val.page) { 6938 val.pageIndex = val.page._pageIndex; 6939 delete val.page; 6940 } 6941 if (serialized) { 6942 map.set(key, serialized); 6943 hash.update(`${key}:${JSON.stringify(serialized)}`); 6944 hasBitmap ||= !!serialized.bitmap; 6945 } 6946 } 6947 if (hasBitmap) { 6948 for (const value of map.values()) { 6949 if (value.bitmap) { 6950 transfer.push(value.bitmap); 6951 } 6952 } 6953 } 6954 return map.size > 0 ? { 6955 map, 6956 hash: hash.hexdigest(), 6957 transfer 6958 } : SerializableEmpty; 6959 } 6960 get editorStats() { 6961 let stats = null; 6962 const typeToEditor = new Map(); 6963 let numberOfEditedComments = 0; 6964 let numberOfDeletedComments = 0; 6965 for (const value of this.#storage.values()) { 6966 if (!(value instanceof AnnotationEditor)) { 6967 if (value.popup) { 6968 if (value.popup.deleted) { 6969 numberOfDeletedComments += 1; 6970 } else { 6971 numberOfEditedComments += 1; 6972 } 6973 } 6974 continue; 6975 } 6976 if (value.isCommentDeleted) { 6977 numberOfDeletedComments += 1; 6978 } else if (value.hasEditedComment) { 6979 numberOfEditedComments += 1; 6980 } 6981 const editorStats = value.telemetryFinalData; 6982 if (!editorStats) { 6983 continue; 6984 } 6985 const { 6986 type 6987 } = editorStats; 6988 if (!typeToEditor.has(type)) { 6989 typeToEditor.set(type, Object.getPrototypeOf(value).constructor); 6990 } 6991 stats ||= Object.create(null); 6992 const map = stats[type] ||= new Map(); 6993 for (const [key, val] of Object.entries(editorStats)) { 6994 if (key === "type") { 6995 continue; 6996 } 6997 let counters = map.get(key); 6998 if (!counters) { 6999 counters = new Map(); 7000 map.set(key, counters); 7001 } 7002 const count = counters.get(val) ?? 0; 7003 counters.set(val, count + 1); 7004 } 7005 } 7006 if (numberOfDeletedComments > 0 || numberOfEditedComments > 0) { 7007 stats ||= Object.create(null); 7008 stats.comments = { 7009 deleted: numberOfDeletedComments, 7010 edited: numberOfEditedComments 7011 }; 7012 } 7013 if (!stats) { 7014 return null; 7015 } 7016 for (const [type, editor] of typeToEditor) { 7017 stats[type] = editor.computeTelemetryFinalData(stats[type]); 7018 } 7019 return stats; 7020 } 7021 resetModifiedIds() { 7022 this.#modifiedIds = null; 7023 } 7024 updateEditor(annotationId, data) { 7025 const value = this.#editorsMap?.get(annotationId); 7026 if (value) { 7027 value.updateFromAnnotationLayer(data); 7028 return true; 7029 } 7030 return false; 7031 } 7032 getEditor(annotationId) { 7033 return this.#editorsMap?.get(annotationId) || null; 7034 } 7035 get modifiedIds() { 7036 if (this.#modifiedIds) { 7037 return this.#modifiedIds; 7038 } 7039 const ids = []; 7040 if (this.#editorsMap) { 7041 for (const value of this.#editorsMap.values()) { 7042 if (!value.serialize()) { 7043 continue; 7044 } 7045 ids.push(value.annotationElementId); 7046 } 7047 } 7048 return this.#modifiedIds = { 7049 ids: new Set(ids), 7050 hash: ids.join(",") 7051 }; 7052 } 7053 [Symbol.iterator]() { 7054 return this.#storage.entries(); 7055 } 7056} 7057class PrintAnnotationStorage extends AnnotationStorage { 7058 #serializable; 7059 constructor(parent) { 7060 super(); 7061 const { 7062 map, 7063 hash, 7064 transfer 7065 } = parent.serializable; 7066 const clone = structuredClone(map, transfer ? { 7067 transfer 7068 } : null); 7069 this.#serializable = { 7070 map: clone, 7071 hash, 7072 transfer 7073 }; 7074 } 7075 get print() { 7076 unreachable("Should not call PrintAnnotationStorage.print"); 7077 } 7078 get serializable() { 7079 return this.#serializable; 7080 } 7081 get modifiedIds() { 7082 return shadow(this, "modifiedIds", { 7083 ids: new Set(), 7084 hash: "" 7085 }); 7086 } 7087} 7088 7089;// ./src/display/font_loader.js 7090 7091 7092class FontLoader { 7093 #systemFonts = new Set(); 7094 constructor({ 7095 ownerDocument = globalThis.document, 7096 styleElement = null 7097 }) { 7098 this._document = ownerDocument; 7099 this.nativeFontFaces = new Set(); 7100 this.styleElement = null; 7101 this.loadingRequests = []; 7102 this.loadTestFontId = 0; 7103 } 7104 addNativeFontFace(nativeFontFace) { 7105 this.nativeFontFaces.add(nativeFontFace); 7106 this._document.fonts.add(nativeFontFace); 7107 } 7108 removeNativeFontFace(nativeFontFace) { 7109 this.nativeFontFaces.delete(nativeFontFace); 7110 this._document.fonts.delete(nativeFontFace); 7111 } 7112 insertRule(rule) { 7113 if (!this.styleElement) { 7114 this.styleElement = this._document.createElement("style"); 7115 this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement); 7116 } 7117 const styleSheet = this.styleElement.sheet; 7118 styleSheet.insertRule(rule, styleSheet.cssRules.length); 7119 } 7120 clear() { 7121 for (const nativeFontFace of this.nativeFontFaces) { 7122 this._document.fonts.delete(nativeFontFace); 7123 } 7124 this.nativeFontFaces.clear(); 7125 this.#systemFonts.clear(); 7126 if (this.styleElement) { 7127 this.styleElement.remove(); 7128 this.styleElement = null; 7129 } 7130 } 7131 async loadSystemFont({ 7132 systemFontInfo: info, 7133 disableFontFace, 7134 _inspectFont 7135 }) { 7136 if (!info || this.#systemFonts.has(info.loadedName)) { 7137 return; 7138 } 7139 assert(!disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set."); 7140 if (this.isFontLoadingAPISupported) { 7141 const { 7142 loadedName, 7143 src, 7144 style 7145 } = info; 7146 const fontFace = new FontFace(loadedName, src, style); 7147 this.addNativeFontFace(fontFace); 7148 try { 7149 await fontFace.load(); 7150 this.#systemFonts.add(loadedName); 7151 _inspectFont?.(info); 7152 } catch { 7153 warn(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`); 7154 this.removeNativeFontFace(fontFace); 7155 } 7156 return; 7157 } 7158 unreachable("Not implemented: loadSystemFont without the Font Loading API."); 7159 } 7160 async bind(font) { 7161 if (font.attached || font.missingFile && !font.systemFontInfo) { 7162 return; 7163 } 7164 font.attached = true; 7165 if (font.systemFontInfo) { 7166 await this.loadSystemFont(font); 7167 return; 7168 } 7169 if (this.isFontLoadingAPISupported) { 7170 const nativeFontFace = font.createNativeFontFace(); 7171 if (nativeFontFace) { 7172 this.addNativeFontFace(nativeFontFace); 7173 try { 7174 await nativeFontFace.loaded; 7175 } catch (ex) { 7176 warn(`Failed to load font '${nativeFontFace.family}': '${ex}'.`); 7177 font.disableFontFace = true; 7178 throw ex; 7179 } 7180 } 7181 return; 7182 } 7183 const rule = font.createFontFaceRule(); 7184 if (rule) { 7185 this.insertRule(rule); 7186 if (this.isSyncFontLoadingSupported) { 7187 return; 7188 } 7189 await new Promise(resolve => { 7190 const request = this._queueLoadingCallback(resolve); 7191 this._prepareFontLoadEvent(font, request); 7192 }); 7193 } 7194 } 7195 get isFontLoadingAPISupported() { 7196 const hasFonts = !!this._document?.fonts; 7197 return shadow(this, "isFontLoadingAPISupported", hasFonts); 7198 } 7199 get isSyncFontLoadingSupported() { 7200 return shadow(this, "isSyncFontLoadingSupported", isNodeJS || util_FeatureTest.platform.isFirefox); 7201 } 7202 _queueLoadingCallback(callback) { 7203 function completeRequest() { 7204 assert(!request.done, "completeRequest() cannot be called twice."); 7205 request.done = true; 7206 while (loadingRequests.length > 0 && loadingRequests[0].done) { 7207 const otherRequest = loadingRequests.shift(); 7208 setTimeout(otherRequest.callback, 0); 7209 } 7210 } 7211 const { 7212 loadingRequests 7213 } = this; 7214 const request = { 7215 done: false, 7216 complete: completeRequest, 7217 callback 7218 }; 7219 loadingRequests.push(request); 7220 return request; 7221 } 7222 get _loadTestFont() { 7223 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=="); 7224 return shadow(this, "_loadTestFont", testFont); 7225 } 7226 _prepareFontLoadEvent(font, request) { 7227 function int32(data, offset) { 7228 return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff; 7229 } 7230 function spliceString(s, offset, remove, insert) { 7231 const chunk1 = s.substring(0, offset); 7232 const chunk2 = s.substring(offset + remove); 7233 return chunk1 + insert + chunk2; 7234 } 7235 let i, ii; 7236 const canvas = this._document.createElement("canvas"); 7237 canvas.width = 1; 7238 canvas.height = 1; 7239 const ctx = canvas.getContext("2d"); 7240 let called = 0; 7241 function isFontReady(name, callback) { 7242 if (++called > 30) { 7243 warn("Load test font never loaded."); 7244 callback(); 7245 return; 7246 } 7247 ctx.font = "30px " + name; 7248 ctx.fillText(".", 0, 20); 7249 const imageData = ctx.getImageData(0, 0, 1, 1); 7250 if (imageData.data[3] > 0) { 7251 callback(); 7252 return; 7253 } 7254 setTimeout(isFontReady.bind(null, name, callback)); 7255 } 7256 const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`; 7257 let data = this._loadTestFont; 7258 const COMMENT_OFFSET = 976; 7259 data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId); 7260 const CFF_CHECKSUM_OFFSET = 16; 7261 const XXXX_VALUE = 0x58585858; 7262 let checksum = int32(data, CFF_CHECKSUM_OFFSET); 7263 for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) { 7264 checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0; 7265 } 7266 if (i < loadTestFontId.length) { 7267 checksum = checksum - XXXX_VALUE + int32(loadTestFontId + "XXX", i) | 0; 7268 } 7269 data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum)); 7270 const url = `url(data:font/opentype;base64,${btoa(data)});`; 7271 const rule = `@font-face {font-family:"${loadTestFontId}";src:${url}}`; 7272 this.insertRule(rule); 7273 const div = this._document.createElement("div"); 7274 div.style.visibility = "hidden"; 7275 div.style.width = div.style.height = "10px"; 7276 div.style.position = "absolute"; 7277 div.style.top = div.style.left = "0px"; 7278 for (const name of [font.loadedName, loadTestFontId]) { 7279 const span = this._document.createElement("span"); 7280 span.textContent = "Hi"; 7281 span.style.fontFamily = name; 7282 div.append(span); 7283 } 7284 this._document.body.append(div); 7285 isFontReady(loadTestFontId, () => { 7286 div.remove(); 7287 request.complete(); 7288 }); 7289 } 7290} 7291class FontFaceObject { 7292 #fontData; 7293 constructor(translatedData, inspectFont = null, extra, charProcOperatorList) { 7294 this.compiledGlyphs = Object.create(null); 7295 this.#fontData = translatedData; 7296 this._inspectFont = inspectFont; 7297 if (extra) { 7298 Object.assign(this, extra); 7299 } 7300 if (charProcOperatorList) { 7301 this.charProcOperatorList = charProcOperatorList; 7302 } 7303 } 7304 createNativeFontFace() { 7305 if (!this.data || this.disableFontFace) { 7306 return null; 7307 } 7308 let nativeFontFace; 7309 if (!this.cssFontInfo) { 7310 nativeFontFace = new FontFace(this.loadedName, this.data, {}); 7311 } else { 7312 const css = { 7313 weight: this.cssFontInfo.fontWeight 7314 }; 7315 if (this.cssFontInfo.italicAngle) { 7316 css.style = `oblique ${this.cssFontInfo.italicAngle}deg`; 7317 } 7318 nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css); 7319 } 7320 this._inspectFont?.(this); 7321 return nativeFontFace; 7322 } 7323 createFontFaceRule() { 7324 if (!this.data || this.disableFontFace) { 7325 return null; 7326 } 7327 const url = `url(data:${this.mimetype};base64,${this.data.toBase64()});`; 7328 let rule; 7329 if (!this.cssFontInfo) { 7330 rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; 7331 } else { 7332 let css = `font-weight: ${this.cssFontInfo.fontWeight};`; 7333 if (this.cssFontInfo.italicAngle) { 7334 css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`; 7335 } 7336 rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`; 7337 } 7338 this._inspectFont?.(this, url); 7339 return rule; 7340 } 7341 getPathGenerator(objs, character) { 7342 if (this.compiledGlyphs[character] !== undefined) { 7343 return this.compiledGlyphs[character]; 7344 } 7345 const objId = this.loadedName + "_path_" + character; 7346 let cmds; 7347 try { 7348 cmds = objs.get(objId); 7349 } catch (ex) { 7350 warn(`getPathGenerator - ignoring character: "${ex}".`); 7351 } 7352 const path = makePathFromDrawOPS(cmds?.path); 7353 if (!this.fontExtraProperties) { 7354 objs.delete(objId); 7355 } 7356 return this.compiledGlyphs[character] = path; 7357 } 7358 get black() { 7359 return this.#fontData.black; 7360 } 7361 get bold() { 7362 return this.#fontData.bold; 7363 } 7364 get disableFontFace() { 7365 return this.#fontData.disableFontFace ?? false; 7366 } 7367 set disableFontFace(value) { 7368 shadow(this, "disableFontFace", !!value); 7369 } 7370 get fontExtraProperties() { 7371 return this.#fontData.fontExtraProperties ?? false; 7372 } 7373 get isInvalidPDFjsFont() { 7374 return this.#fontData.isInvalidPDFjsFont; 7375 } 7376 get isType3Font() { 7377 return this.#fontData.isType3Font; 7378 } 7379 get italic() { 7380 return this.#fontData.italic; 7381 } 7382 get missingFile() { 7383 return this.#fontData.missingFile; 7384 } 7385 get remeasure() { 7386 return this.#fontData.remeasure; 7387 } 7388 get vertical() { 7389 return this.#fontData.vertical; 7390 } 7391 get ascent() { 7392 return this.#fontData.ascent; 7393 } 7394 get defaultWidth() { 7395 return this.#fontData.defaultWidth; 7396 } 7397 get descent() { 7398 return this.#fontData.descent; 7399 } 7400 get bbox() { 7401 return this.#fontData.bbox; 7402 } 7403 set bbox(bbox) { 7404 shadow(this, "bbox", bbox); 7405 } 7406 get fontMatrix() { 7407 return this.#fontData.fontMatrix; 7408 } 7409 get fallbackName() { 7410 return this.#fontData.fallbackName; 7411 } 7412 get loadedName() { 7413 return this.#fontData.loadedName; 7414 } 7415 get mimetype() { 7416 return this.#fontData.mimetype; 7417 } 7418 get name() { 7419 return this.#fontData.name; 7420 } 7421 get data() { 7422 return this.#fontData.data; 7423 } 7424 clearData() { 7425 this.#fontData.clearData(); 7426 } 7427 get cssFontInfo() { 7428 return this.#fontData.cssFontInfo; 7429 } 7430 get systemFontInfo() { 7431 return this.#fontData.systemFontInfo; 7432 } 7433 get defaultVMetrics() { 7434 return this.#fontData.defaultVMetrics; 7435 } 7436} 7437 7438;// ./src/shared/obj-bin-transform.js 7439 7440class CssFontInfo { 7441 #buffer; 7442 #view; 7443 #decoder; 7444 static strings = ["fontFamily", "fontWeight", "italicAngle"]; 7445 static write(info) { 7446 const encoder = new TextEncoder(); 7447 const encodedStrings = {}; 7448 let stringsLength = 0; 7449 for (const prop of CssFontInfo.strings) { 7450 const encoded = encoder.encode(info[prop]); 7451 encodedStrings[prop] = encoded;
vendor: 10,728 bytes, lines 7452-7784
7452 stringsLength += 4 + encoded.length; 7453 } 7454 const buffer = new ArrayBuffer(stringsLength); 7455 const data = new Uint8Array(buffer); 7456 const view = new DataView(buffer); 7457 let offset = 0; 7458 for (const prop of CssFontInfo.strings) { 7459 const encoded = encodedStrings[prop]; 7460 const length = encoded.length; 7461 view.setUint32(offset, length); 7462 data.set(encoded, offset + 4); 7463 offset += 4 + length; 7464 } 7465 assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow"); 7466 return buffer; 7467 } 7468 constructor(buffer) { 7469 this.#buffer = buffer; 7470 this.#view = new DataView(this.#buffer); 7471 this.#decoder = new TextDecoder(); 7472 } 7473 #readString(index) { 7474 assert(index < CssFontInfo.strings.length, "Invalid string index"); 7475 let offset = 0; 7476 for (let i = 0; i < index; i++) { 7477 offset += this.#view.getUint32(offset) + 4; 7478 } 7479 const length = this.#view.getUint32(offset); 7480 return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length)); 7481 } 7482 get fontFamily() { 7483 return this.#readString(0); 7484 } 7485 get fontWeight() { 7486 return this.#readString(1); 7487 } 7488 get italicAngle() { 7489 return this.#readString(2); 7490 } 7491} 7492class SystemFontInfo { 7493 #buffer; 7494 #view; 7495 #decoder; 7496 static strings = ["css", "loadedName", "baseFontName", "src"]; 7497 static write(info) { 7498 const encoder = new TextEncoder(); 7499 const encodedStrings = {}; 7500 let stringsLength = 0; 7501 for (const prop of SystemFontInfo.strings) { 7502 const encoded = encoder.encode(info[prop]); 7503 encodedStrings[prop] = encoded; 7504 stringsLength += 4 + encoded.length; 7505 } 7506 stringsLength += 4; 7507 let encodedStyleStyle, 7508 encodedStyleWeight, 7509 lengthEstimate = 1 + stringsLength; 7510 if (info.style) { 7511 encodedStyleStyle = encoder.encode(info.style.style); 7512 encodedStyleWeight = encoder.encode(info.style.weight); 7513 lengthEstimate += 4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length; 7514 } 7515 const buffer = new ArrayBuffer(lengthEstimate); 7516 const data = new Uint8Array(buffer); 7517 const view = new DataView(buffer); 7518 let offset = 0; 7519 view.setUint8(offset++, info.guessFallback ? 1 : 0); 7520 view.setUint32(offset, 0); 7521 offset += 4; 7522 stringsLength = 0; 7523 for (const prop of SystemFontInfo.strings) { 7524 const encoded = encodedStrings[prop]; 7525 const length = encoded.length; 7526 stringsLength += 4 + length; 7527 view.setUint32(offset, length); 7528 data.set(encoded, offset + 4); 7529 offset += 4 + length; 7530 } 7531 view.setUint32(offset - stringsLength - 4, stringsLength); 7532 if (info.style) { 7533 view.setUint32(offset, encodedStyleStyle.length); 7534 data.set(encodedStyleStyle, offset + 4); 7535 offset += 4 + encodedStyleStyle.length; 7536 view.setUint32(offset, encodedStyleWeight.length); 7537 data.set(encodedStyleWeight, offset + 4); 7538 offset += 4 + encodedStyleWeight.length; 7539 } 7540 assert(offset <= buffer.byteLength, "SubstitionInfo.write: Buffer overflow"); 7541 return buffer.transferToFixedLength(offset); 7542 } 7543 constructor(buffer) { 7544 this.#buffer = buffer; 7545 this.#view = new DataView(this.#buffer); 7546 this.#decoder = new TextDecoder(); 7547 } 7548 get guessFallback() { 7549 return this.#view.getUint8(0) !== 0; 7550 } 7551 #readString(index) { 7552 assert(index < SystemFontInfo.strings.length, "Invalid string index"); 7553 let offset = 5; 7554 for (let i = 0; i < index; i++) { 7555 offset += this.#view.getUint32(offset) + 4; 7556 } 7557 const length = this.#view.getUint32(offset); 7558 return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length)); 7559 } 7560 get css() { 7561 return this.#readString(0); 7562 } 7563 get loadedName() { 7564 return this.#readString(1); 7565 } 7566 get baseFontName() { 7567 return this.#readString(2); 7568 } 7569 get src() { 7570 return this.#readString(3); 7571 } 7572 get style() { 7573 let offset = 1; 7574 offset += 4 + this.#view.getUint32(offset); 7575 const styleLength = this.#view.getUint32(offset); 7576 const style = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, styleLength)); 7577 offset += 4 + styleLength; 7578 const weightLength = this.#view.getUint32(offset); 7579 const weight = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, weightLength)); 7580 return { 7581 style, 7582 weight 7583 }; 7584 } 7585} 7586class FontInfo { 7587 static bools = ["black", "bold", "disableFontFace", "fontExtraProperties", "isInvalidPDFjsFont", "isType3Font", "italic", "missingFile", "remeasure", "vertical"]; 7588 static numbers = ["ascent", "defaultWidth", "descent"]; 7589 static strings = ["fallbackName", "loadedName", "mimetype", "name"]; 7590 static #OFFSET_NUMBERS = Math.ceil(this.bools.length * 2 / 8); 7591 static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8; 7592 static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4; 7593 static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6; 7594 static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3; 7595 #buffer; 7596 #decoder; 7597 #view; 7598 constructor({ 7599 data, 7600 extra 7601 }) { 7602 this.#buffer = data; 7603 this.#decoder = new TextDecoder(); 7604 this.#view = new DataView(this.#buffer); 7605 if (extra) { 7606 Object.assign(this, extra); 7607 } 7608 } 7609 #readBoolean(index) { 7610 assert(index < FontInfo.bools.length, "Invalid boolean index"); 7611 const byteOffset = Math.floor(index / 4); 7612 const bitOffset = index * 2 % 8; 7613 const value = this.#view.getUint8(byteOffset) >> bitOffset & 0x03; 7614 return value === 0x00 ? undefined : value === 0x02; 7615 } 7616 get black() { 7617 return this.#readBoolean(0); 7618 } 7619 get bold() { 7620 return this.#readBoolean(1); 7621 } 7622 get disableFontFace() { 7623 return this.#readBoolean(2); 7624 } 7625 get fontExtraProperties() { 7626 return this.#readBoolean(3); 7627 } 7628 get isInvalidPDFjsFont() { 7629 return this.#readBoolean(4); 7630 } 7631 get isType3Font() { 7632 return this.#readBoolean(5); 7633 } 7634 get italic() { 7635 return this.#readBoolean(6); 7636 } 7637 get missingFile() { 7638 return this.#readBoolean(7); 7639 } 7640 get remeasure() { 7641 return this.#readBoolean(8); 7642 } 7643 get vertical() { 7644 return this.#readBoolean(9); 7645 } 7646 #readNumber(index) { 7647 assert(index < FontInfo.numbers.length, "Invalid number index"); 7648 return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8); 7649 } 7650 get ascent() { 7651 return this.#readNumber(0); 7652 } 7653 get defaultWidth() { 7654 return this.#readNumber(1); 7655 } 7656 get descent() { 7657 return this.#readNumber(2); 7658 } 7659 get bbox() { 7660 let offset = FontInfo.#OFFSET_BBOX; 7661 const numCoords = this.#view.getUint8(offset); 7662 if (numCoords === 0) { 7663 return undefined; 7664 } 7665 offset += 1; 7666 const bbox = []; 7667 for (let i = 0; i < 4; i++) { 7668 bbox.push(this.#view.getInt16(offset, true)); 7669 offset += 2; 7670 } 7671 return bbox; 7672 } 7673 get fontMatrix() { 7674 let offset = FontInfo.#OFFSET_FONT_MATRIX; 7675 const numPoints = this.#view.getUint8(offset); 7676 if (numPoints === 0) { 7677 return undefined; 7678 } 7679 offset += 1; 7680 const fontMatrix = []; 7681 for (let i = 0; i < 6; i++) { 7682 fontMatrix.push(this.#view.getFloat64(offset, true)); 7683 offset += 8; 7684 } 7685 return fontMatrix; 7686 } 7687 get defaultVMetrics() { 7688 let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS; 7689 const numMetrics = this.#view.getUint8(offset); 7690 if (numMetrics === 0) { 7691 return undefined; 7692 } 7693 offset += 1; 7694 const defaultVMetrics = []; 7695 for (let i = 0; i < 3; i++) { 7696 defaultVMetrics.push(this.#view.getInt16(offset, true)); 7697 offset += 2; 7698 } 7699 return defaultVMetrics; 7700 } 7701 #readString(index) { 7702 assert(index < FontInfo.strings.length, "Invalid string index"); 7703 let offset = FontInfo.#OFFSET_STRINGS + 4; 7704 for (let i = 0; i < index; i++) { 7705 offset += this.#view.getUint32(offset) + 4; 7706 } 7707 const length = this.#view.getUint32(offset); 7708 const stringData = new Uint8Array(length); 7709 stringData.set(new Uint8Array(this.#buffer, offset + 4, length)); 7710 return this.#decoder.decode(stringData); 7711 } 7712 get fallbackName() { 7713 return this.#readString(0); 7714 } 7715 get loadedName() { 7716 return this.#readString(1); 7717 } 7718 get mimetype() { 7719 return this.#readString(2); 7720 } 7721 get name() { 7722 return this.#readString(3); 7723 } 7724 get data() { 7725 let offset = FontInfo.#OFFSET_STRINGS; 7726 const stringsLength = this.#view.getUint32(offset); 7727 offset += 4 + stringsLength; 7728 const systemFontInfoLength = this.#view.getUint32(offset); 7729 offset += 4 + systemFontInfoLength; 7730 const cssFontInfoLength = this.#view.getUint32(offset); 7731 offset += 4 + cssFontInfoLength; 7732 const length = this.#view.getUint32(offset); 7733 if (length === 0) { 7734 return undefined; 7735 } 7736 return new Uint8Array(this.#buffer, offset + 4, length); 7737 } 7738 clearData() { 7739 let offset = FontInfo.#OFFSET_STRINGS; 7740 const stringsLength = this.#view.getUint32(offset); 7741 offset += 4 + stringsLength; 7742 const systemFontInfoLength = this.#view.getUint32(offset); 7743 offset += 4 + systemFontInfoLength; 7744 const cssFontInfoLength = this.#view.getUint32(offset); 7745 offset += 4 + cssFontInfoLength; 7746 const length = this.#view.getUint32(offset); 7747 const data = new Uint8Array(this.#buffer, offset + 4, length); 7748 data.fill(0); 7749 this.#view.setUint32(offset, 0); 7750 } 7751 get cssFontInfo() { 7752 let offset = FontInfo.#OFFSET_STRINGS; 7753 const stringsLength = this.#view.getUint32(offset); 7754 offset += 4 + stringsLength; 7755 const systemFontInfoLength = this.#view.getUint32(offset); 7756 offset += 4 + systemFontInfoLength; 7757 const cssFontInfoLength = this.#view.getUint32(offset); 7758 if (cssFontInfoLength === 0) { 7759 return null; 7760 } 7761 const cssFontInfoData = new Uint8Array(cssFontInfoLength); 7762 cssFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength)); 7763 return new CssFontInfo(cssFontInfoData.buffer); 7764 } 7765 get systemFontInfo() { 7766 let offset = FontInfo.#OFFSET_STRINGS; 7767 const stringsLength = this.#view.getUint32(offset); 7768 offset += 4 + stringsLength; 7769 const systemFontInfoLength = this.#view.getUint32(offset); 7770 if (systemFontInfoLength === 0) { 7771 return null; 7772 } 7773 const systemFontInfoData = new Uint8Array(systemFontInfoLength); 7774 systemFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength)); 7775 return new SystemFontInfo(systemFontInfoData.buffer); 7776 } 7777 static write(font) { 7778 const systemFontInfoBuffer = font.systemFontInfo ? SystemFontInfo.write(font.systemFontInfo) : null; 7779 const cssFontInfoBuffer = font.cssFontInfo ? CssFontInfo.write(font.cssFontInfo) : null; 7780 const encoder = new TextEncoder(); 7781 const encodedStrings = {}; 7782 let stringsLength = 0; 7783 for (const prop of FontInfo.strings) { 7784 encodedStrings[prop] = encoder.encode(font[prop]);
vendor: 5,233 bytes, lines 7785-7938
7785 stringsLength += 4 + encodedStrings[prop].length; 7786 } 7787 const lengthEstimate = FontInfo.#OFFSET_STRINGS + 4 + stringsLength + 4 + (systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) + 4 + (cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) + 4 + (font.data ? font.data.length : 0); 7788 const buffer = new ArrayBuffer(lengthEstimate); 7789 const data = new Uint8Array(buffer); 7790 const view = new DataView(buffer); 7791 let offset = 0; 7792 const numBools = FontInfo.bools.length; 7793 let boolByte = 0, 7794 boolBit = 0; 7795 for (let i = 0; i < numBools; i++) { 7796 const value = font[FontInfo.bools[i]]; 7797 const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01; 7798 boolByte |= bits << boolBit; 7799 boolBit += 2; 7800 if (boolBit === 8 || i === numBools - 1) { 7801 view.setUint8(offset++, boolByte); 7802 boolByte = 0; 7803 boolBit = 0; 7804 } 7805 } 7806 assert(offset === FontInfo.#OFFSET_NUMBERS, "FontInfo.write: Boolean properties offset mismatch"); 7807 for (const prop of FontInfo.numbers) { 7808 view.setFloat64(offset, font[prop]); 7809 offset += 8; 7810 } 7811 assert(offset === FontInfo.#OFFSET_BBOX, "FontInfo.write: Number properties offset mismatch"); 7812 if (font.bbox) { 7813 view.setUint8(offset++, 4); 7814 for (const coord of font.bbox) { 7815 view.setInt16(offset, coord, true); 7816 offset += 2; 7817 } 7818 } else { 7819 view.setUint8(offset++, 0); 7820 offset += 2 * 4; 7821 } 7822 assert(offset === FontInfo.#OFFSET_FONT_MATRIX, "FontInfo.write: BBox properties offset mismatch"); 7823 if (font.fontMatrix) { 7824 view.setUint8(offset++, 6); 7825 for (const point of font.fontMatrix) { 7826 view.setFloat64(offset, point, true); 7827 offset += 8; 7828 } 7829 } else { 7830 view.setUint8(offset++, 0); 7831 offset += 8 * 6; 7832 } 7833 assert(offset === FontInfo.#OFFSET_DEFAULT_VMETRICS, "FontInfo.write: FontMatrix properties offset mismatch"); 7834 if (font.defaultVMetrics) { 7835 view.setUint8(offset++, 1); 7836 for (const metric of font.defaultVMetrics) { 7837 view.setInt16(offset, metric, true); 7838 offset += 2; 7839 } 7840 } else { 7841 view.setUint8(offset++, 0); 7842 offset += 3 * 2; 7843 } 7844 assert(offset === FontInfo.#OFFSET_STRINGS, "FontInfo.write: DefaultVMetrics properties offset mismatch"); 7845 view.setUint32(FontInfo.#OFFSET_STRINGS, 0); 7846 offset += 4; 7847 for (const prop of FontInfo.strings) { 7848 const encoded = encodedStrings[prop]; 7849 const length = encoded.length; 7850 view.setUint32(offset, length); 7851 data.set(encoded, offset + 4); 7852 offset += 4 + length; 7853 } 7854 view.setUint32(FontInfo.#OFFSET_STRINGS, offset - FontInfo.#OFFSET_STRINGS - 4); 7855 if (!systemFontInfoBuffer) { 7856 view.setUint32(offset, 0); 7857 offset += 4; 7858 } else { 7859 const length = systemFontInfoBuffer.byteLength; 7860 view.setUint32(offset, length); 7861 assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at systemFontInfo"); 7862 data.set(new Uint8Array(systemFontInfoBuffer), offset + 4); 7863 offset += 4 + length; 7864 } 7865 if (!cssFontInfoBuffer) { 7866 view.setUint32(offset, 0); 7867 offset += 4; 7868 } else { 7869 const length = cssFontInfoBuffer.byteLength; 7870 view.setUint32(offset, length); 7871 assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at cssFontInfo"); 7872 data.set(new Uint8Array(cssFontInfoBuffer), offset + 4); 7873 offset += 4 + length; 7874 } 7875 if (font.data === undefined) { 7876 view.setUint32(offset, 0); 7877 offset += 4; 7878 } else { 7879 view.setUint32(offset, font.data.length); 7880 data.set(font.data, offset + 4); 7881 offset += 4 + font.data.length; 7882 } 7883 assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow"); 7884 return buffer.transferToFixedLength(offset); 7885 } 7886} 7887class PatternInfo { 7888 static #KIND = 0; 7889 static #HAS_BBOX = 1; 7890 static #HAS_BACKGROUND = 2; 7891 static #SHADING_TYPE = 3; 7892 static #N_COORD = 4; 7893 static #N_COLOR = 8; 7894 static #N_STOP = 12; 7895 static #N_FIGURES = 16; 7896 constructor(buffer) { 7897 this.buffer = buffer; 7898 this.view = new DataView(buffer); 7899 this.data = new Uint8Array(buffer); 7900 } 7901 static write(ir) { 7902 let kind, 7903 bbox = null, 7904 coords = [], 7905 colors = [], 7906 colorStops = [], 7907 figures = [], 7908 shadingType = null, 7909 background = null; 7910 switch (ir[0]) { 7911 case "RadialAxial": 7912 kind = ir[1] === "axial" ? 1 : 2; 7913 bbox = ir[2]; 7914 colorStops = ir[3]; 7915 if (kind === 1) { 7916 coords.push(...ir[4], ...ir[5]); 7917 } else { 7918 coords.push(ir[4][0], ir[4][1], ir[6], ir[5][0], ir[5][1], ir[7]); 7919 } 7920 break; 7921 case "Mesh": 7922 kind = 3; 7923 shadingType = ir[1]; 7924 coords = ir[2]; 7925 colors = ir[3]; 7926 figures = ir[4] || []; 7927 bbox = ir[6]; 7928 background = ir[7]; 7929 break; 7930 default: 7931 throw new Error(`Unsupported pattern type: ${ir[0]}`); 7932 } 7933 const nCoord = Math.floor(coords.length / 2); 7934 const nColor = Math.floor(colors.length / 3); 7935 const nStop = colorStops.length; 7936 const nFigures = figures.length; 7937 let figuresSize = 0; 7938 for (const figure of figures) {
7939 figuresSize += 1; 7940 figuresSize = Math.ceil(figuresSize / 4) * 4; 7941 figuresSize += 4 + figure.coords.length * 4; 7942 figuresSize += 4 + figure.colors.length * 4; 7943 if (figure.verticesPerRow !== undefined) { 7944 figuresSize += 4; 7945 } 7946 } 7947 const byteLen = 20 + nCoord * 8 + nColor * 3 + nStop * 8 + (bbox ? 16 : 0) + (background ? 3 : 0) + figuresSize; 7948 const buffer = new ArrayBuffer(byteLen); 7949 const dataView = new DataView(buffer); 7950 const u8data = new Uint8Array(buffer); 7951 dataView.setUint8(PatternInfo.#KIND, kind); 7952 dataView.setUint8(PatternInfo.#HAS_BBOX, bbox ? 1 : 0); 7953 dataView.setUint8(PatternInfo.#HAS_BACKGROUND, background ? 1 : 0); 7954 dataView.setUint8(PatternInfo.#SHADING_TYPE, shadingType); 7955 dataView.setUint32(PatternInfo.#N_COORD, nCoord, true); 7956 dataView.setUint32(PatternInfo.#N_COLOR, nColor, true); 7957 dataView.setUint32(PatternInfo.#N_STOP, nStop, true); 7958 dataView.setUint32(PatternInfo.#N_FIGURES, nFigures, true); 7959 let offset = 20; 7960 const coordsView = new Float32Array(buffer, offset, nCoord * 2); 7961 coordsView.set(coords); 7962 offset += nCoord * 8; 7963 u8data.set(colors, offset); 7964 offset += nColor * 3; 7965 for (const [pos, hex] of colorStops) { 7966 dataView.setFloat32(offset, pos, true); 7967 offset += 4; 7968 dataView.setUint32(offset, parseInt(hex.slice(1), 16), true); 7969 offset += 4; 7970 } 7971 if (bbox) { 7972 for (const v of bbox) { 7973 dataView.setFloat32(offset, v, true); 7974 offset += 4; 7975 } 7976 } 7977 if (background) { 7978 u8data.set(background, offset); 7979 offset += 3; 7980 } 7981 for (let i = 0; i < figures.length; i++) { 7982 const figure = figures[i]; 7983 dataView.setUint8(offset, figure.type); 7984 offset += 1; 7985 offset = Math.ceil(offset / 4) * 4; 7986 dataView.setUint32(offset, figure.coords.length, true); 7987 offset += 4; 7988 const figureCoordsView = new Int32Array(buffer, offset, figure.coords.length); 7989 figureCoordsView.set(figure.coords); 7990 offset += figure.coords.length * 4; 7991 dataView.setUint32(offset, figure.colors.length, true); 7992 offset += 4; 7993 const colorsView = new Int32Array(buffer, offset, figure.colors.length); 7994 colorsView.set(figure.colors); 7995 offset += figure.colors.length * 4; 7996 if (figure.verticesPerRow !== undefined) { 7997 dataView.setUint32(offset, figure.verticesPerRow, true); 7998 offset += 4; 7999 } 8000 } 8001 return buffer; 8002 } 8003 getIR() { 8004 const dataView = this.view; 8005 const kind = this.data[PatternInfo.#KIND]; 8006 const hasBBox = !!this.data[PatternInfo.#HAS_BBOX]; 8007 const hasBackground = !!this.data[PatternInfo.#HAS_BACKGROUND]; 8008 const nCoord = dataView.getUint32(PatternInfo.#N_COORD, true); 8009 const nColor = dataView.getUint32(PatternInfo.#N_COLOR, true); 8010 const nStop = dataView.getUint32(PatternInfo.#N_STOP, true); 8011 const nFigures = dataView.getUint32(PatternInfo.#N_FIGURES, true); 8012 let offset = 20; 8013 const coords = new Float32Array(this.buffer, offset, nCoord * 2); 8014 offset += nCoord * 8; 8015 const colors = new Uint8Array(this.buffer, offset, nColor * 3); 8016 offset += nColor * 3; 8017 const stops = []; 8018 for (let i = 0; i < nStop; ++i) { 8019 const p = dataView.getFloat32(offset, true); 8020 offset += 4; 8021 const rgb = dataView.getUint32(offset, true); 8022 offset += 4; 8023 stops.push([p, `#${rgb.toString(16).padStart(6, "0")}`]); 8024 } 8025 let bbox = null; 8026 if (hasBBox) { 8027 bbox = []; 8028 for (let i = 0; i < 4; ++i) { 8029 bbox.push(dataView.getFloat32(offset, true)); 8030 offset += 4; 8031 } 8032 } 8033 let background = null; 8034 if (hasBackground) { 8035 background = new Uint8Array(this.buffer, offset, 3); 8036 offset += 3; 8037 } 8038 const figures = []; 8039 for (let i = 0; i < nFigures; ++i) { 8040 const type = dataView.getUint8(offset); 8041 offset += 1; 8042 offset = Math.ceil(offset / 4) * 4; 8043 const coordsLength = dataView.getUint32(offset, true); 8044 offset += 4; 8045 const figureCoords = new Int32Array(this.buffer, offset, coordsLength); 8046 offset += coordsLength * 4; 8047 const colorsLength = dataView.getUint32(offset, true); 8048 offset += 4; 8049 const figureColors = new Int32Array(this.buffer, offset, colorsLength); 8050 offset += colorsLength * 4; 8051 const figure = { 8052 type, 8053 coords: figureCoords, 8054 colors: figureColors 8055 }; 8056 if (type === MeshFigureType.LATTICE) { 8057 figure.verticesPerRow = dataView.getUint32(offset, true); 8058 offset += 4; 8059 } 8060 figures.push(figure); 8061 } 8062 if (kind === 1) { 8063 return ["RadialAxial", "axial", bbox, stops, Array.from(coords.slice(0, 2)), Array.from(coords.slice(2, 4)), null, null]; 8064 } 8065 if (kind === 2) { 8066 return ["RadialAxial", "radial", bbox, stops, [coords[0], coords[1]], [coords[3], c
vendor: 16,469 bytes, lines 8066-8611
8066oords[4]], coords[2], coords[5]]; 8067 } 8068 if (kind === 3) { 8069 const shadingType = this.data[PatternInfo.#SHADING_TYPE]; 8070 let bounds = null; 8071 if (coords.length > 0) { 8072 let minX = coords[0], 8073 maxX = coords[0]; 8074 let minY = coords[1], 8075 maxY = coords[1]; 8076 for (let i = 0; i < coords.length; i += 2) { 8077 const x = coords[i], 8078 y = coords[i + 1]; 8079 minX = minX > x ? x : minX; 8080 minY = minY > y ? y : minY; 8081 maxX = maxX < x ? x : maxX; 8082 maxY = maxY < y ? y : maxY; 8083 } 8084 bounds = [minX, minY, maxX, maxY]; 8085 } 8086 return ["Mesh", shadingType, coords, colors, figures, bounds, bbox, background]; 8087 } 8088 throw new Error(`Unsupported pattern kind: ${kind}`); 8089 } 8090} 8091class FontPathInfo { 8092 static write(path) { 8093 let data; 8094 let buffer; 8095 if (util_FeatureTest.isFloat16ArraySupported) { 8096 buffer = new ArrayBuffer(path.length * 2); 8097 data = new Float16Array(buffer); 8098 } else { 8099 buffer = new ArrayBuffer(path.length * 4); 8100 data = new Float32Array(buffer); 8101 } 8102 data.set(path); 8103 return buffer; 8104 } 8105 #buffer; 8106 constructor(buffer) { 8107 this.#buffer = buffer; 8108 } 8109 get path() { 8110 if (util_FeatureTest.isFloat16ArraySupported) { 8111 return new Float16Array(this.#buffer); 8112 } 8113 return new Float32Array(this.#buffer); 8114 } 8115} 8116 8117;// ./src/display/api_utils.js 8118 8119function getUrlProp(val) { 8120 if (val instanceof URL) { 8121 return val.href; 8122 } 8123 if (typeof val === "string") { 8124 if (isNodeJS) { 8125 return val; 8126 } 8127 const url = URL.parse(val, window.location); 8128 if (url) { 8129 return url.href; 8130 } 8131 } 8132 throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property."); 8133} 8134function getDataProp(val) { 8135 if (isNodeJS && typeof Buffer !== "undefined" && val instanceof Buffer) { 8136 throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`."); 8137 } 8138 if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) { 8139 return val; 8140 } 8141 if (typeof val === "string") { 8142 return stringToBytes(val); 8143 } 8144 if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) { 8145 return new Uint8Array(val); 8146 } 8147 throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property."); 8148} 8149function getFactoryUrlProp(val) { 8150 if (typeof val !== "string") { 8151 return null; 8152 } 8153 if (val.endsWith("/")) { 8154 return val; 8155 } 8156 throw new Error(`Invalid factory url: "${val}" must include trailing slash.`); 8157} 8158const isRefProxy = v => typeof v === "object" && Number.isInteger(v?.num) && v.num >= 0 && Number.isInteger(v?.gen) && v.gen >= 0; 8159const isNameProxy = v => typeof v === "object" && typeof v?.name === "string"; 8160const isValidExplicitDest = _isValidExplicitDest.bind(null, isRefProxy, isNameProxy); 8161class LoopbackPort { 8162 #listeners = new Map(); 8163 #deferred = Promise.resolve(); 8164 postMessage(obj, transfer) { 8165 const event = { 8166 data: structuredClone(obj, transfer ? { 8167 transfer 8168 } : null) 8169 }; 8170 this.#deferred.then(() => { 8171 for (const [listener] of this.#listeners) { 8172 listener.call(this, event); 8173 } 8174 }); 8175 } 8176 addEventListener(name, listener, options = null) { 8177 let rmAbort = null; 8178 if (options?.signal instanceof AbortSignal) { 8179 const { 8180 signal 8181 } = options; 8182 if (signal.aborted) { 8183 warn("LoopbackPort - cannot use an `aborted` signal."); 8184 return; 8185 } 8186 const onAbort = () => this.removeEventListener(name, listener); 8187 rmAbort = () => signal.removeEventListener("abort", onAbort); 8188 signal.addEventListener("abort", onAbort); 8189 } 8190 this.#listeners.set(listener, rmAbort); 8191 } 8192 removeEventListener(name, listener) { 8193 const rmAbort = this.#listeners.get(listener); 8194 rmAbort?.(); 8195 this.#listeners.delete(listener); 8196 } 8197 terminate() { 8198 for (const [, rmAbort] of this.#listeners) { 8199 rmAbort?.(); 8200 } 8201 this.#listeners.clear(); 8202 } 8203} 8204 8205;// ./src/shared/message_handler.js 8206 8207const CallbackKind = { 8208 DATA: 1, 8209 ERROR: 2 8210}; 8211const StreamKind = { 8212 CANCEL: 1, 8213 CANCEL_COMPLETE: 2, 8214 CLOSE: 3, 8215 ENQUEUE: 4, 8216 ERROR: 5, 8217 PULL: 6, 8218 PULL_COMPLETE: 7, 8219 START_COMPLETE: 8 8220}; 8221function onFn() {} 8222function wrapReason(ex) { 8223 if (ex instanceof AbortException || ex instanceof InvalidPDFException || ex instanceof PasswordException || ex instanceof ResponseException || ex instanceof UnknownErrorException) { 8224 return ex; 8225 } 8226 if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) { 8227 unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.'); 8228 } 8229 switch (ex.name) { 8230 case "AbortException": 8231 return new AbortException(ex.message); 8232 case "InvalidPDFException": 8233 return new InvalidPDFException(ex.message); 8234 case "PasswordException": 8235 return new PasswordException(ex.message, ex.code); 8236 case "ResponseException": 8237 return new ResponseException(ex.message, ex.status, ex.missing); 8238 case "UnknownErrorException": 8239 return new UnknownErrorException(ex.message, ex.details); 8240 } 8241 return new UnknownErrorException(ex.message, ex.toString()); 8242} 8243class MessageHandler { 8244 #messageAC = new AbortController(); 8245 constructor(sourceName, targetName, comObj) { 8246 this.sourceName = sourceName; 8247 this.targetName = targetName; 8248 this.comObj = comObj; 8249 this.callbackId = 1; 8250 this.streamId = 1; 8251 this.streamSinks = Object.create(null); 8252 this.streamControllers = Object.create(null); 8253 this.callbackCapabilities = Object.create(null); 8254 this.actionHandler = Object.create(null); 8255 comObj.addEventListener("message", this.#onMessage.bind(this), { 8256 signal: this.#messageAC.signal 8257 }); 8258 } 8259 #onMessage({ 8260 data 8261 }) { 8262 if (data.targetName !== this.sourceName) { 8263 return; 8264 } 8265 if (data.stream) { 8266 this.#processStreamMessage(data); 8267 return; 8268 } 8269 if (data.callback) { 8270 const callbackId = data.callbackId; 8271 const capability = this.callbackCapabilities[callbackId]; 8272 if (!capability) { 8273 throw new Error(`Cannot resolve callback ${callbackId}`); 8274 } 8275 delete this.callbackCapabilities[callbackId]; 8276 if (data.callback === CallbackKind.DATA) { 8277 capability.resolve(data.data); 8278 } else if (data.callback === CallbackKind.ERROR) { 8279 capability.reject(wrapReason(data.reason)); 8280 } else { 8281 throw new Error("Unexpected callback case"); 8282 } 8283 return; 8284 } 8285 const action = this.actionHandler[data.action]; 8286 if (!action) { 8287 throw new Error(`Unknown action from worker: ${data.action}`); 8288 } 8289 if (data.callbackId) { 8290 const sourceName = this.sourceName, 8291 targetName = data.sourceName, 8292 comObj = this.comObj; 8293 Promise.try(action, data.data).then(function (result) { 8294 comObj.postMessage({ 8295 sourceName, 8296 targetName, 8297 callback: CallbackKind.DATA, 8298 callbackId: data.callbackId, 8299 data: result 8300 }); 8301 }, function (reason) { 8302 comObj.postMessage({ 8303 sourceName, 8304 targetName, 8305 callback: CallbackKind.ERROR, 8306 callbackId: data.callbackId, 8307 reason: wrapReason(reason) 8308 }); 8309 }); 8310 return; 8311 } 8312 if (data.streamId) { 8313 this.#createStreamSink(data); 8314 return; 8315 } 8316 action(data.data); 8317 } 8318 on(actionName, handler) { 8319 const ah = this.actionHandler; 8320 if (ah[actionName]) { 8321 throw new Error(`There is already an actionName called "${actionName}"`); 8322 } 8323 ah[actionName] = handler; 8324 } 8325 send(actionName, data, transfers) { 8326 this.comObj.postMessage({ 8327 sourceName: this.sourceName, 8328 targetName: this.targetName, 8329 action: actionName, 8330 data 8331 }, transfers); 8332 } 8333 sendWithPromise(actionName, data, transfers) { 8334 const callbackId = this.callbackId++; 8335 const capability = Promise.withResolvers(); 8336 this.callbackCapabilities[callbackId] = capability; 8337 try { 8338 this.comObj.postMessage({ 8339 sourceName: this.sourceName, 8340 targetName: this.targetName, 8341 action: actionName, 8342 callbackId, 8343 data 8344 }, transfers); 8345 } catch (ex) { 8346 capability.reject(ex); 8347 } 8348 return capability.promise; 8349 } 8350 sendWithStream(actionName, data, queueingStrategy, transfers) { 8351 const streamId = this.streamId++, 8352 sourceName = this.sourceName, 8353 targetName = this.targetName, 8354 comObj = this.comObj; 8355 return new ReadableStream({ 8356 start: controller => { 8357 const startCapability = Promise.withResolvers(); 8358 this.streamControllers[streamId] = { 8359 controller, 8360 startCall: startCapability, 8361 pullCall: null, 8362 cancelCall: null, 8363 isClosed: false 8364 }; 8365 comObj.postMessage({ 8366 sourceName, 8367 targetName, 8368 action: actionName, 8369 streamId, 8370 data, 8371 desiredSize: controller.desiredSize 8372 }, transfers); 8373 return startCapability.promise; 8374 }, 8375 pull: controller => { 8376 const pullCapability = Promise.withResolvers(); 8377 this.streamControllers[streamId].pullCall = pullCapability; 8378 comObj.postMessage({ 8379 sourceName, 8380 targetName, 8381 stream: StreamKind.PULL, 8382 streamId, 8383 desiredSize: controller.desiredSize 8384 }); 8385 return pullCapability.promise; 8386 }, 8387 cancel: reason => { 8388 assert(reason instanceof Error, "cancel must have a valid reason"); 8389 const cancelCapability = Promise.withResolvers(); 8390 this.streamControllers[streamId].cancelCall = cancelCapability; 8391 this.streamControllers[streamId].isClosed = true; 8392 comObj.postMessage({ 8393 sourceName, 8394 targetName, 8395 stream: StreamKind.CANCEL, 8396 streamId, 8397 reason: wrapReason(reason) 8398 }); 8399 return cancelCapability.promise; 8400 } 8401 }, queueingStrategy); 8402 } 8403 #createStreamSink(data) { 8404 const streamId = data.streamId, 8405 sourceName = this.sourceName, 8406 targetName = data.sourceName, 8407 comObj = this.comObj; 8408 const self = this, 8409 action = this.actionHandler[data.action]; 8410 const streamSink = { 8411 enqueue(chunk, size = 1, transfers) { 8412 if (this.isCancelled) { 8413 return; 8414 } 8415 const lastDesiredSize = this.desiredSize; 8416 this.desiredSize -= size; 8417 if (lastDesiredSize > 0 && this.desiredSize <= 0) { 8418 this.sinkCapability = Promise.withResolvers(); 8419 this.ready = this.sinkCapability.promise; 8420 } 8421 comObj.postMessage({ 8422 sourceName, 8423 targetName, 8424 stream: StreamKind.ENQUEUE, 8425 streamId, 8426 chunk 8427 }, transfers); 8428 }, 8429 close() { 8430 if (this.isCancelled) { 8431 return; 8432 } 8433 this.isCancelled = true; 8434 comObj.postMessage({ 8435 sourceName, 8436 targetName, 8437 stream: StreamKind.CLOSE, 8438 streamId 8439 }); 8440 delete self.streamSinks[streamId]; 8441 }, 8442 error(reason) { 8443 assert(reason instanceof Error, "error must have a valid reason"); 8444 if (this.isCancelled) { 8445 return; 8446 } 8447 this.isCancelled = true; 8448 comObj.postMessage({ 8449 sourceName, 8450 targetName, 8451 stream: StreamKind.ERROR, 8452 streamId, 8453 reason: wrapReason(reason) 8454 }); 8455 }, 8456 sinkCapability: Promise.withResolvers(), 8457 onPull: null, 8458 onCancel: null, 8459 isCancelled: false, 8460 desiredSize: data.desiredSize, 8461 ready: null 8462 }; 8463 streamSink.sinkCapability.resolve(); 8464 streamSink.ready = streamSink.sinkCapability.promise; 8465 this.streamSinks[streamId] = streamSink; 8466 Promise.try(action, data.data, streamSink).then(function () { 8467 comObj.postMessage({ 8468 sourceName, 8469 targetName, 8470 stream: StreamKind.START_COMPLETE, 8471 streamId, 8472 success: true 8473 }); 8474 }, function (reason) { 8475 comObj.postMessage({ 8476 sourceName, 8477 targetName, 8478 stream: StreamKind.START_COMPLETE, 8479 streamId, 8480 reason: wrapReason(reason) 8481 }); 8482 }); 8483 } 8484 #processStreamMessage(data) { 8485 const streamId = data.streamId, 8486 sourceName = this.sourceName, 8487 targetName = data.sourceName, 8488 comObj = this.comObj; 8489 const streamController = this.streamControllers[streamId], 8490 streamSink = this.streamSinks[streamId]; 8491 switch (data.stream) { 8492 case StreamKind.START_COMPLETE: 8493 if (data.success) { 8494 streamController.startCall.resolve(); 8495 } else { 8496 streamController.startCall.reject(wrapReason(data.reason)); 8497 } 8498 break; 8499 case StreamKind.PULL_COMPLETE: 8500 if (data.success) { 8501 streamController.pullCall.resolve(); 8502 } else { 8503 streamController.pullCall.reject(wrapReason(data.reason)); 8504 } 8505 break; 8506 case StreamKind.PULL: 8507 if (!streamSink) { 8508 comObj.postMessage({ 8509 sourceName, 8510 targetName, 8511 stream: StreamKind.PULL_COMPLETE, 8512 streamId, 8513 success: true 8514 }); 8515 break; 8516 } 8517 if (streamSink.desiredSize <= 0 && data.desiredSize > 0) { 8518 streamSink.sinkCapability.resolve(); 8519 } 8520 streamSink.desiredSize = data.desiredSize; 8521 Promise.try(streamSink.onPull || onFn).then(function () { 8522 comObj.postMessage({ 8523 sourceName, 8524 targetName, 8525 stream: StreamKind.PULL_COMPLETE, 8526 streamId, 8527 success: true 8528 }); 8529 }, function (reason) { 8530 comObj.postMessage({ 8531 sourceName, 8532 targetName, 8533 stream: StreamKind.PULL_COMPLETE, 8534 streamId, 8535 reason: wrapReason(reason) 8536 }); 8537 }); 8538 break; 8539 case StreamKind.ENQUEUE: 8540 assert(streamController, "enqueue should have stream controller"); 8541 if (streamController.isClosed) { 8542 break; 8543 } 8544 streamController.controller.enqueue(data.chunk); 8545 break; 8546 case StreamKind.CLOSE: 8547 assert(streamController, "close should have stream controller"); 8548 if (streamController.isClosed) { 8549 break; 8550 } 8551 streamController.isClosed = true; 8552 streamController.controller.close(); 8553 this.#deleteStreamController(streamController, streamId); 8554 break; 8555 case StreamKind.ERROR: 8556 assert(streamController, "error should have stream controller"); 8557 streamController.controller.error(wrapReason(data.reason)); 8558 this.#deleteStreamController(streamController, streamId); 8559 break; 8560 case StreamKind.CANCEL_COMPLETE: 8561 if (data.success) { 8562 streamController.cancelCall.resolve(); 8563 } else { 8564 streamController.cancelCall.reject(wrapReason(data.reason)); 8565 } 8566 this.#deleteStreamController(streamController, streamId); 8567 break; 8568 case StreamKind.CANCEL: 8569 if (!streamSink) { 8570 break; 8571 } 8572 const dataReason = wrapReason(data.reason); 8573 Promise.try(streamSink.onCancel || onFn, dataReason).then(function () { 8574 comObj.postMessage({ 8575 sourceName, 8576 targetName, 8577 stream: StreamKind.CANCEL_COMPLETE, 8578 streamId, 8579 success: true 8580 }); 8581 }, function (reason) { 8582 comObj.postMessage({ 8583 sourceName, 8584 targetName, 8585 stream: StreamKind.CANCEL_COMPLETE, 8586 streamId, 8587 reason: wrapReason(reason) 8588 }); 8589 }); 8590 streamSink.sinkCapability.reject(dataReason); 8591 streamSink.isCancelled = true; 8592 delete this.streamSinks[streamId]; 8593 break; 8594 default: 8595 throw new Error("Unexpected stream case"); 8596 } 8597 } 8598 async #deleteStreamController(streamController, streamId) { 8599 await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]); 8600 delete this.streamControllers[streamId]; 8601 } 8602 destroy() { 8603 this.#messageAC?.abort(); 8604 this.#messageAC = null; 8605 } 8606} 8607 8608;// ./src/display/canvas_factory.js 8609 8610class BaseCanvasFactory { 8611 #enableHWA = false;
vendor: 34,720 bytes, lines 8612-9739
8612 constructor({ 8613 enableHWA = false 8614 }) { 8615 this.#enableHWA = enableHWA; 8616 } 8617 create(width, height) { 8618 if (width <= 0 || height <= 0) { 8619 throw new Error("Invalid canvas size"); 8620 } 8621 const canvas = this._createCanvas(width, height); 8622 return { 8623 canvas, 8624 context: canvas.getContext("2d", { 8625 willReadFrequently: !this.#enableHWA 8626 }) 8627 }; 8628 } 8629 reset(canvasAndContext, width, height) { 8630 if (!canvasAndContext.canvas) { 8631 throw new Error("Canvas is not specified"); 8632 } 8633 if (width <= 0 || height <= 0) { 8634 throw new Error("Invalid canvas size"); 8635 } 8636 canvasAndContext.canvas.width = width; 8637 canvasAndContext.canvas.height = height; 8638 } 8639 destroy(canvasAndContext) { 8640 if (!canvasAndContext.canvas) { 8641 throw new Error("Canvas is not specified"); 8642 } 8643 canvasAndContext.canvas.width = 0; 8644 canvasAndContext.canvas.height = 0; 8645 canvasAndContext.canvas = null; 8646 canvasAndContext.context = null; 8647 } 8648 _createCanvas(width, height) { 8649 unreachable("Abstract method `_createCanvas` called."); 8650 } 8651} 8652class DOMCanvasFactory extends BaseCanvasFactory { 8653 constructor({ 8654 ownerDocument = globalThis.document, 8655 enableHWA = false 8656 }) { 8657 super({ 8658 enableHWA 8659 }); 8660 this._document = ownerDocument; 8661 } 8662 _createCanvas(width, height) { 8663 const canvas = this._document.createElement("canvas"); 8664 canvas.width = width; 8665 canvas.height = height; 8666 return canvas; 8667 } 8668} 8669 8670;// ./src/display/cmap_reader_factory.js 8671 8672 8673class BaseCMapReaderFactory { 8674 constructor({ 8675 baseUrl = null, 8676 isCompressed = true 8677 }) { 8678 this.baseUrl = baseUrl; 8679 this.isCompressed = isCompressed; 8680 } 8681 async fetch({ 8682 name 8683 }) { 8684 if (!this.baseUrl) { 8685 throw new Error("Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided."); 8686 } 8687 if (!name) { 8688 throw new Error("CMap name must be specified."); 8689 } 8690 const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : ""); 8691 return this._fetch(url).then(cMapData => ({ 8692 cMapData, 8693 isCompressed: this.isCompressed 8694 })).catch(reason => { 8695 throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`); 8696 }); 8697 } 8698 async _fetch(url) { 8699 unreachable("Abstract method `_fetch` called."); 8700 } 8701} 8702class DOMCMapReaderFactory extends BaseCMapReaderFactory { 8703 async _fetch(url) { 8704 const data = await fetchData(url, this.isCompressed ? "arraybuffer" : "text"); 8705 return data instanceof ArrayBuffer ? new Uint8Array(data) : stringToBytes(data); 8706 } 8707} 8708 8709;// ./src/display/filter_factory.js 8710 8711 8712class BaseFilterFactory { 8713 addFilter(maps) { 8714 return "none"; 8715 } 8716 addHCMFilter(fgColor, bgColor) { 8717 return "none"; 8718 } 8719 addAlphaFilter(map) { 8720 return "none"; 8721 } 8722 addLuminosityFilter(map) { 8723 return "none"; 8724 } 8725 addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { 8726 return "none"; 8727 } 8728 destroy(keepHCM = false) {} 8729} 8730class DOMFilterFactory extends BaseFilterFactory { 8731 #baseUrl; 8732 #_cache; 8733 #_defs; 8734 #docId; 8735 #document; 8736 #_hcmCache; 8737 #id = 0; 8738 constructor({ 8739 docId, 8740 ownerDocument = globalThis.document 8741 }) { 8742 super(); 8743 this.#docId = docId; 8744 this.#document = ownerDocument; 8745 } 8746 get #cache() { 8747 return this.#_cache ||= new Map(); 8748 } 8749 get #hcmCache() { 8750 return this.#_hcmCache ||= new Map(); 8751 } 8752 get #defs() { 8753 if (!this.#_defs) { 8754 const div = this.#document.createElement("div"); 8755 const { 8756 style 8757 } = div; 8758 style.visibility = "hidden"; 8759 style.contain = "strict"; 8760 style.width = style.height = 0; 8761 style.position = "absolute"; 8762 style.top = style.left = 0; 8763 style.zIndex = -1; 8764 const svg = this.#document.createElementNS(SVG_NS, "svg"); 8765 svg.setAttribute("width", 0); 8766 svg.setAttribute("height", 0); 8767 this.#_defs = this.#document.createElementNS(SVG_NS, "defs"); 8768 div.append(svg); 8769 svg.append(this.#_defs); 8770 this.#document.body.append(div); 8771 } 8772 return this.#_defs; 8773 } 8774 #createTables(maps) { 8775 if (maps.length === 1) { 8776 const mapR = maps[0]; 8777 const buffer = new Array(256); 8778 for (let i = 0; i < 256; i++) { 8779 buffer[i] = mapR[i] / 255; 8780 } 8781 const table = buffer.join(","); 8782 return [table, table, table]; 8783 } 8784 const [mapR, mapG, mapB] = maps; 8785 const bufferR = new Array(256); 8786 const bufferG = new Array(256); 8787 const bufferB = new Array(256); 8788 for (let i = 0; i < 256; i++) { 8789 bufferR[i] = mapR[i] / 255; 8790 bufferG[i] = mapG[i] / 255; 8791 bufferB[i] = mapB[i] / 255; 8792 } 8793 return [bufferR.join(","), bufferG.join(","), bufferB.join(",")]; 8794 } 8795 #createUrl(id) { 8796 if (this.#baseUrl === undefined) { 8797 this.#baseUrl = ""; 8798 const url = this.#document.URL; 8799 if (url !== this.#document.baseURI) { 8800 if (isDataScheme(url)) { 8801 warn('#createUrl: ignore "data:"-URL for performance reasons.'); 8802 } else { 8803 this.#baseUrl = updateUrlHash(url, ""); 8804 } 8805 } 8806 } 8807 return `url(${this.#baseUrl}#${id})`; 8808 } 8809 addFilter(maps) { 8810 if (!maps) { 8811 return "none"; 8812 } 8813 let value = this.#cache.get(maps); 8814 if (value) { 8815 return value; 8816 } 8817 const [tableR, tableG, tableB] = this.#createTables(maps); 8818 const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`; 8819 value = this.#cache.get(key); 8820 if (value) { 8821 this.#cache.set(maps, value); 8822 return value; 8823 } 8824 const id = `g_${this.#docId}_transfer_map_${this.#id++}`; 8825 const url = this.#createUrl(id); 8826 this.#cache.set(maps, url); 8827 this.#cache.set(key, url); 8828 const filter = this.#createFilter(id); 8829 this.#addTransferMapConversion(tableR, tableG, tableB, filter); 8830 return url; 8831 } 8832 addHCMFilter(fgColor, bgColor) { 8833 const key = `${fgColor}-${bgColor}`; 8834 const filterName = "base"; 8835 let info = this.#hcmCache.get(filterName); 8836 if (info?.key === key) { 8837 return info.url; 8838 } 8839 if (info) { 8840 info.filter?.remove(); 8841 info.key = key; 8842 info.url = "none"; 8843 info.filter = null; 8844 } else { 8845 info = { 8846 key, 8847 url: "none", 8848 filter: null 8849 }; 8850 this.#hcmCache.set(filterName, info); 8851 } 8852 if (!fgColor || !bgColor) { 8853 return info.url; 8854 } 8855 const fgRGB = this.#getRGB(fgColor); 8856 fgColor = Util.makeHexColor(...fgRGB); 8857 const bgRGB = this.#getRGB(bgColor); 8858 bgColor = Util.makeHexColor(...bgRGB); 8859 this.#defs.style.color = ""; 8860 if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) { 8861 return info.url; 8862 } 8863 const map = new Array(256); 8864 for (let i = 0; i <= 255; i++) { 8865 const x = i / 255; 8866 map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; 8867 } 8868 const table = map.join(","); 8869 const id = `g_${this.#docId}_hcm_filter`; 8870 const filter = info.filter = this.#createFilter(id); 8871 this.#addTransferMapConversion(table, table, table, filter); 8872 this.#addGrayConversion(filter); 8873 const getSteps = (c, n) => { 8874 const start = fgRGB[c] / 255; 8875 const end = bgRGB[c] / 255; 8876 const arr = new Array(n + 1); 8877 for (let i = 0; i <= n; i++) { 8878 arr[i] = start + i / n * (end - start); 8879 } 8880 return arr.join(","); 8881 }; 8882 this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter); 8883 info.url = this.#createUrl(id); 8884 return info.url; 8885 } 8886 addAlphaFilter(map) { 8887 let value = this.#cache.get(map); 8888 if (value) { 8889 return value; 8890 } 8891 const [tableA] = this.#createTables([map]); 8892 const key = `alpha_${tableA}`; 8893 value = this.#cache.get(key); 8894 if (value) { 8895 this.#cache.set(map, value); 8896 return value; 8897 } 8898 const id = `g_${this.#docId}_alpha_map_${this.#id++}`; 8899 const url = this.#createUrl(id); 8900 this.#cache.set(map, url); 8901 this.#cache.set(key, url); 8902 const filter = this.#createFilter(id); 8903 this.#addTransferMapAlphaConversion(tableA, filter); 8904 return url; 8905 } 8906 addLuminosityFilter(map) { 8907 let value = this.#cache.get(map || "luminosity"); 8908 if (value) { 8909 return value; 8910 } 8911 let tableA, key; 8912 if (map) { 8913 [tableA] = this.#createTables([map]); 8914 key = `luminosity_${tableA}`; 8915 } else { 8916 key = "luminosity"; 8917 } 8918 value = this.#cache.get(key); 8919 if (value) { 8920 this.#cache.set(map, value); 8921 return value; 8922 } 8923 const id = `g_${this.#docId}_luminosity_map_${this.#id++}`; 8924 const url = this.#createUrl(id); 8925 this.#cache.set(map, url); 8926 this.#cache.set(key, url); 8927 const filter = this.#createFilter(id); 8928 this.#addLuminosityConversion(filter); 8929 if (map) { 8930 this.#addTransferMapAlphaConversion(tableA, filter); 8931 } 8932 return url; 8933 } 8934 addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) { 8935 const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`; 8936 let info = this.#hcmCache.get(filterName); 8937 if (info?.key === key) { 8938 return info.url; 8939 } 8940 if (info) { 8941 info.filter?.remove(); 8942 info.key = key; 8943 info.url = "none"; 8944 info.filter = null; 8945 } else { 8946 info = { 8947 key, 8948 url: "none", 8949 filter: null 8950 }; 8951 this.#hcmCache.set(filterName, info); 8952 } 8953 if (!fgColor || !bgColor) { 8954 return info.url; 8955 } 8956 const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this)); 8957 let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]); 8958 let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]); 8959 let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this)); 8960 if (bgGray < fgGray) { 8961 [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB]; 8962 } 8963 this.#defs.style.color = ""; 8964 const getSteps = (fg, bg, n) => { 8965 const arr = new Array(256); 8966 const step = (bgGray - fgGray) / n; 8967 const newStart = fg / 255; 8968 const newStep = (bg - fg) / (255 * n); 8969 let prev = 0; 8970 for (let i = 0; i <= n; i++) { 8971 const k = Math.round(fgGray + i * step); 8972 const value = newStart + i * newStep; 8973 for (let j = prev; j <= k; j++) { 8974 arr[j] = value; 8975 } 8976 prev = k + 1; 8977 } 8978 for (let i = prev; i < 256; i++) { 8979 arr[i] = arr[prev - 1]; 8980 } 8981 return arr.join(","); 8982 }; 8983 const id = `g_${this.#docId}_hcm_${filterName}_filter`; 8984 const filter = info.filter = this.#createFilter(id); 8985 this.#addGrayConversion(filter); 8986 this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter); 8987 info.url = this.#createUrl(id); 8988 return info.url; 8989 } 8990 destroy(keepHCM = false) { 8991 if (keepHCM && this.#_hcmCache?.size) { 8992 return; 8993 } 8994 this.#_defs?.parentNode.parentNode.remove(); 8995 this.#_defs = null; 8996 this.#_cache?.clear(); 8997 this.#_cache = null; 8998 this.#_hcmCache?.clear(); 8999 this.#_hcmCache = null; 9000 this.#id = 0; 9001 } 9002 #addLuminosityConversion(filter) { 9003 const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); 9004 feColorMatrix.setAttribute("type", "matrix"); 9005 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"); 9006 filter.append(feColorMatrix); 9007 } 9008 #addGrayConversion(filter) { 9009 const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix"); 9010 feColorMatrix.setAttribute("type", "matrix"); 9011 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"); 9012 filter.append(feColorMatrix); 9013 } 9014 #createFilter(id) { 9015 const filter = this.#document.createElementNS(SVG_NS, "filter"); 9016 filter.setAttribute("color-interpolation-filters", "sRGB"); 9017 filter.setAttribute("id", id); 9018 this.#defs.append(filter); 9019 return filter; 9020 } 9021 #appendFeFunc(feComponentTransfer, func, table) { 9022 const feFunc = this.#document.createElementNS(SVG_NS, func); 9023 feFunc.setAttribute("type", "discrete"); 9024 feFunc.setAttribute("tableValues", table); 9025 feComponentTransfer.append(feFunc); 9026 } 9027 #addTransferMapConversion(rTable, gTable, bTable, filter) { 9028 const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); 9029 filter.append(feComponentTransfer); 9030 this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable); 9031 this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable); 9032 this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable); 9033 } 9034 #addTransferMapAlphaConversion(aTable, filter) { 9035 const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer"); 9036 filter.append(feComponentTransfer); 9037 this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable); 9038 } 9039 #getRGB(color) { 9040 this.#defs.style.color = color; 9041 return getRGB(getComputedStyle(this.#defs).getPropertyValue("color")); 9042 } 9043} 9044 9045;// ./src/display/standard_fontdata_factory.js 9046 9047 9048class BaseStandardFontDataFactory { 9049 constructor({ 9050 baseUrl = null 9051 }) { 9052 this.baseUrl = baseUrl; 9053 } 9054 async fetch({ 9055 filename 9056 }) { 9057 if (!this.baseUrl) { 9058 throw new Error("Ensure that the `standardFontDataUrl` API parameter is provided."); 9059 } 9060 if (!filename) { 9061 throw new Error("Font filename must be specified."); 9062 } 9063 const url = `${this.baseUrl}${filename}`; 9064 return this._fetch(url).catch(reason => { 9065 throw new Error(`Unable to load font data at: ${url}`); 9066 }); 9067 } 9068 async _fetch(url) { 9069 unreachable("Abstract method `_fetch` called."); 9070 } 9071} 9072class DOMStandardFontDataFactory extends BaseStandardFontDataFactory { 9073 async _fetch(url) { 9074 const data = await fetchData(url, "arraybuffer"); 9075 return new Uint8Array(data); 9076 } 9077} 9078 9079;// ./src/display/wasm_factory.js 9080 9081 9082class BaseWasmFactory { 9083 constructor({ 9084 baseUrl = null 9085 }) { 9086 this.baseUrl = baseUrl; 9087 } 9088 async fetch({ 9089 filename 9090 }) { 9091 if (!this.baseUrl) { 9092 throw new Error("Ensure that the `wasmUrl` API parameter is provided."); 9093 } 9094 if (!filename) { 9095 throw new Error("Wasm filename must be specified."); 9096 } 9097 const url = `${this.baseUrl}${filename}`; 9098 return this._fetch(url).catch(reason => { 9099 throw new Error(`Unable to load wasm data at: ${url}`); 9100 }); 9101 } 9102 async _fetch(url) { 9103 unreachable("Abstract method `_fetch` called."); 9104 } 9105} 9106class DOMWasmFactory extends BaseWasmFactory { 9107 async _fetch(url) { 9108 const data = await fetchData(url, "arraybuffer"); 9109 return new Uint8Array(data); 9110 } 9111} 9112 9113;// ./src/display/node_utils.js 9114 9115 9116 9117 9118 9119 9120if (isNodeJS) { 9121 warn("Please use the `legacy` build in Node.js environments."); 9122} 9123async function node_utils_fetchData(url) { 9124 const fs = process.getBuiltinModule("fs"); 9125 const data = await fs.promises.readFile(url); 9126 return new Uint8Array(data); 9127} 9128class NodeFilterFactory extends BaseFilterFactory {} 9129class NodeCanvasFactory extends BaseCanvasFactory { 9130 _createCanvas(width, height) { 9131 const require = process.getBuiltinModule("module").createRequire(import.meta.url); 9132 const canvas = require("@napi-rs/canvas"); 9133 return canvas.createCanvas(width, height); 9134 } 9135} 9136class NodeCMapReaderFactory extends BaseCMapReaderFactory { 9137 async _fetch(url) { 9138 return node_utils_fetchData(url); 9139 } 9140} 9141class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { 9142 async _fetch(url) { 9143 return node_utils_fetchData(url); 9144 } 9145} 9146class NodeWasmFactory extends BaseWasmFactory { 9147 async _fetch(url) { 9148 return node_utils_fetchData(url); 9149 } 9150} 9151 9152;// ./src/display/canvas_dependency_tracker.js 9153 9154const FORCED_DEPENDENCY_LABEL = "__forcedDependency"; 9155const { 9156 floor, 9157 ceil 9158} = Math; 9159function expandBBox(array, index, minX, minY, maxX, maxY) { 9160 array[index * 4 + 0] = Math.min(array[index * 4 + 0], minX); 9161 array[index * 4 + 1] = Math.min(array[index * 4 + 1], minY); 9162 array[index * 4 + 2] = Math.max(array[index * 4 + 2], maxX); 9163 array[index * 4 + 3] = Math.max(array[index * 4 + 3], maxY); 9164} 9165const EMPTY_BBOX = new Uint32Array(new Uint8Array([255, 255, 0, 0]).buffer)[0]; 9166class BBoxReader { 9167 #bboxes; 9168 #coords; 9169 constructor(bboxes, coords) { 9170 this.#bboxes = bboxes; 9171 this.#coords = coords; 9172 } 9173 get length() { 9174 return this.#bboxes.length; 9175 } 9176 isEmpty(i) { 9177 return this.#bboxes[i] === EMPTY_BBOX; 9178 } 9179 minX(i) { 9180 return this.#coords[i * 4 + 0] / 256; 9181 } 9182 minY(i) { 9183 return this.#coords[i * 4 + 1] / 256; 9184 } 9185 maxX(i) { 9186 return (this.#coords[i * 4 + 2] + 1) / 256; 9187 } 9188 maxY(i) { 9189 return (this.#coords[i * 4 + 3] + 1) / 256; 9190 } 9191} 9192const ensureDebugMetadata = (map, key) => { 9193 if (!map) { 9194 return undefined; 9195 } 9196 let value = map.get(key); 9197 if (!value) { 9198 value = { 9199 dependencies: new Set(), 9200 isRenderingOperation: false 9201 }; 9202 map.set(key, value); 9203 } 9204 return value; 9205}; 9206class CanvasDependencyTracker { 9207 #simple = { 9208 __proto__: null 9209 }; 9210 #incremental = { 9211 __proto__: null, 9212 transform: [], 9213 moveText: [], 9214 sameLineText: [], 9215 [FORCED_DEPENDENCY_LABEL]: [] 9216 }; 9217 #namedDependencies = new Map(); 9218 #savesStack = []; 9219 #markedContentStack = []; 9220 #baseTransformStack = [[1, 0, 0, 1, 0, 0]]; 9221 #clipBox = [-Infinity, -Infinity, Infinity, Infinity]; 9222 #pendingBBox = new Float64Array([Infinity, Infinity, -Infinity, -Infinity]); 9223 #pendingBBoxIdx = -1; 9224 #pendingDependencies = new Set(); 9225 #operations = new Map(); 9226 #fontBBoxTrustworthy = new Map(); 9227 #canvasWidth; 9228 #canvasHeight; 9229 #bboxesCoords; 9230 #bboxes; 9231 #debugMetadata; 9232 constructor(canvas, operationsCount, recordDebugMetadata = false) { 9233 this.#canvasWidth = canvas.width; 9234 this.#canvasHeight = canvas.height; 9235 this.#initializeBBoxes(operationsCount); 9236 if (recordDebugMetadata) { 9237 this.#debugMetadata = new Map(); 9238 } 9239 } 9240 growOperationsCount(operationsCount) { 9241 if (operationsCount >= this.#bboxes.length) { 9242 this.#initializeBBoxes(operationsCount, this.#bboxes); 9243 } 9244 } 9245 #initializeBBoxes(operationsCount, oldBBoxes) { 9246 const buffer = new ArrayBuffer(operationsCount * 4); 9247 this.#bboxesCoords = new Uint8ClampedArray(buffer); 9248 this.#bboxes = new Uint32Array(buffer); 9249 if (oldBBoxes && oldBBoxes.length > 0) { 9250 this.#bboxes.set(oldBBoxes); 9251 this.#bboxes.fill(EMPTY_BBOX, oldBBoxes.length); 9252 } else { 9253 this.#bboxes.fill(EMPTY_BBOX); 9254 } 9255 } 9256 save(opIdx) { 9257 this.#simple = { 9258 __proto__: this.#simple 9259 }; 9260 this.#incremental = { 9261 __proto__: this.#incremental, 9262 transform: { 9263 __proto__: this.#incremental.transform 9264 }, 9265 moveText: { 9266 __proto__: this.#incremental.moveText 9267 }, 9268 sameLineText: { 9269 __proto__: this.#incremental.sameLineText 9270 }, 9271 [FORCED_DEPENDENCY_LABEL]: { 9272 __proto__: this.#incremental[FORCED_DEPENDENCY_LABEL] 9273 } 9274 }; 9275 this.#clipBox = { 9276 __proto__: this.#clipBox 9277 }; 9278 this.#savesStack.push(opIdx); 9279 return this; 9280 } 9281 restore(opIdx) { 9282 const previous = Object.getPrototypeOf(this.#simple); 9283 if (previous === null) { 9284 return this; 9285 } 9286 this.#simple = previous; 9287 this.#incremental = Object.getPrototypeOf(this.#incremental); 9288 this.#clipBox = Object.getPrototypeOf(this.#clipBox); 9289 const lastSave = this.#savesStack.pop(); 9290 if (lastSave !== undefined) { 9291 ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave); 9292 this.#bboxes[opIdx] = this.#bboxes[lastSave]; 9293 } 9294 return this; 9295 } 9296 recordOpenMarker(idx) { 9297 this.#savesStack.push(idx); 9298 return this; 9299 } 9300 getOpenMarker() { 9301 if (this.#savesStack.length === 0) { 9302 return null; 9303 } 9304 return this.#savesStack.at(-1); 9305 } 9306 recordCloseMarker(opIdx) { 9307 const lastSave = this.#savesStack.pop(); 9308 if (lastSave !== undefined) { 9309 ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave); 9310 this.#bboxes[opIdx] = this.#bboxes[lastSave]; 9311 } 9312 return this; 9313 } 9314 beginMarkedContent(opIdx) { 9315 this.#markedContentStack.push(opIdx); 9316 return this; 9317 } 9318 endMarkedContent(opIdx) { 9319 const lastSave = this.#markedContentStack.pop(); 9320 if (lastSave !== undefined) { 9321 ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave); 9322 this.#bboxes[opIdx] = this.#bboxes[lastSave]; 9323 } 9324 return this; 9325 } 9326 pushBaseTransform(ctx) { 9327 this.#baseTransformStack.push(Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform())); 9328 return this; 9329 } 9330 popBaseTransform() { 9331 if (this.#baseTransformStack.length > 1) { 9332 this.#baseTransformStack.pop(); 9333 } 9334 return this; 9335 } 9336 recordSimpleData(name, idx) { 9337 this.#simple[name] = idx; 9338 return this; 9339 } 9340 recordIncrementalData(name, idx) { 9341 this.#incremental[name].push(idx); 9342 return this; 9343 } 9344 resetIncrementalData(name, idx) { 9345 this.#incremental[name].length = 0; 9346 return this; 9347 } 9348 recordNamedData(name, idx) { 9349 this.#namedDependencies.set(name, idx); 9350 return this; 9351 } 9352 recordSimpleDataFromNamed(name, depName, fallbackIdx) { 9353 this.#simple[name] = this.#namedDependencies.get(depName) ?? fallbackIdx; 9354 } 9355 recordFutureForcedDependency(name, idx) { 9356 this.recordIncrementalData(FORCED_DEPENDENCY_LABEL, idx); 9357 return this; 9358 } 9359 inheritSimpleDataAsFutureForcedDependencies(names) { 9360 for (const name of names) { 9361 if (name in this.#simple) { 9362 this.recordFutureForcedDependency(name, this.#simple[name]); 9363 } 9364 } 9365 return this; 9366 } 9367 inheritPendingDependenciesAsFutureForcedDependencies() { 9368 for (const dep of this.#pendingDependencies) { 9369 this.recordFutureForcedDependency(FORCED_DEPENDENCY_LABEL, dep); 9370 } 9371 return this; 9372 } 9373 resetBBox(idx) { 9374 if (this.#pendingBBoxIdx !== idx) { 9375 this.#pendingBBoxIdx = idx; 9376 this.#pendingBBox[0] = Infinity; 9377 this.#pendingBBox[1] = Infinity; 9378 this.#pendingBBox[2] = -Infinity; 9379 this.#pendingBBox[3] = -Infinity; 9380 } 9381 return this; 9382 } 9383 recordClipBox(idx, ctx, minX, maxX, minY, maxY) { 9384 const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform()); 9385 const clipBox = [Infinity, Infinity, -Infinity, -Infinity]; 9386 Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, clipBox); 9387 const intersection = Util.intersect(this.#clipBox, clipBox); 9388 if (intersection) { 9389 this.#clipBox[0] = intersection[0]; 9390 this.#clipBox[1] = intersection[1]; 9391 this.#clipBox[2] = intersection[2]; 9392 this.#clipBox[3] = intersection[3]; 9393 } else { 9394 this.#clipBox[0] = this.#clipBox[1] = Infinity; 9395 this.#clipBox[2] = this.#clipBox[3] = -Infinity; 9396 } 9397 return this; 9398 } 9399 recordBBox(idx, ctx, minX, maxX, minY, maxY) { 9400 const clipBox = this.#clipBox; 9401 if (clipBox[0] === Infinity) { 9402 return this; 9403 } 9404 const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform()); 9405 if (clipBox[0] === -Infinity) { 9406 Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, this.#pendingBBox); 9407 return this; 9408 } 9409 const bbox = [Infinity, Infinity, -Infinity, -Infinity]; 9410 Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, bbox); 9411 this.#pendingBBox[0] = Math.min(this.#pendingBBox[0], Math.max(bbox[0], clipBox[0])); 9412 this.#pendingBBox[1] = Math.min(this.#pendingBBox[1], Math.max(bbox[1], clipBox[1])); 9413 this.#pendingBBox[2] = Math.max(this.#pendingBBox[2], Math.min(bbox[2], clipBox[2])); 9414 this.#pendingBBox[3] = Math.max(this.#pendingBBox[3], Math.min(bbox[3], clipBox[3])); 9415 return this; 9416 } 9417 recordCharacterBBox(idx, ctx, font, scale = 1, x = 0, y = 0, getMeasure) { 9418 const fontBBox = font.bbox; 9419 let isBBoxTrustworthy; 9420 let computedBBox; 9421 if (fontBBox) { 9422 isBBoxTrustworthy = fontBBox[2] !== fontBBox[0] && fontBBox[3] !== fontBBox[1] && this.#fontBBoxTrustworthy.get(font); 9423 if (isBBoxTrustworthy !== false) { 9424 computedBBox = [0, 0, 0, 0]; 9425 Util.axialAlignedBoundingBox(fontBBox, font.fontMatrix, computedBBox); 9426 if (scale !== 1 || x !== 0 || y !== 0) { 9427 Util.scaleMinMax([scale, 0, 0, -scale, x, y], computedBBox); 9428 } 9429 if (isBBoxTrustworthy) { 9430 return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]); 9431 } 9432 } 9433 } 9434 if (!getMeasure) { 9435 return this.recordFullPageBBox(idx); 9436 } 9437 const measure = getMeasure(); 9438 if (fontBBox && computedBBox && isBBoxTrustworthy === undefined) { 9439 isBBoxTrustworthy = computedBBox[0] <= x - measure.actualBoundingBoxLeft && computedBBox[2] >= x + measure.actualBoundingBoxRight && computedBBox[1] <= y - measure.actualBoundingBoxAscent && computedBBox[3] >= y + measure.actualBoundingBoxDescent; 9440 this.#fontBBoxTrustworthy.set(font, isBBoxTrustworthy); 9441 if (isBBoxTrustworthy) { 9442 return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]); 9443 } 9444 } 9445 return this.recordBBox(idx, ctx, x - measure.actualBoundingBoxLeft, x + measure.actualBoundingBoxRight, y - measure.actualBoundingBoxAscent, y + measure.actualBoundingBoxDescent); 9446 } 9447 recordFullPageBBox(idx) { 9448 this.#pendingBBox[0] = Math.max(0, this.#clipBox[0]); 9449 this.#pendingBBox[1] = Math.max(0, this.#clipBox[1]); 9450 this.#pendingBBox[2] = Math.min(this.#canvasWidth, this.#clipBox[2]); 9451 this.#pendingBBox[3] = Math.min(this.#canvasHeight, this.#clipBox[3]); 9452 return this; 9453 } 9454 getSimpleIndex(dependencyName) { 9455 return this.#simple[dependencyName]; 9456 } 9457 recordDependencies(idx, dependencyNames) { 9458 const pendingDependencies = this.#pendingDependencies; 9459 const simple = this.#simple; 9460 const incremental = this.#incremental; 9461 for (const name of dependencyNames) { 9462 if (name in this.#simple) { 9463 pendingDependencies.add(simple[name]); 9464 } else if (name in incremental) { 9465 incremental[name].forEach(pendingDependencies.add, pendingDependencies); 9466 } 9467 } 9468 return this; 9469 } 9470 recordNamedDependency(idx, name) { 9471 if (this.#namedDependencies.has(name)) { 9472 this.#pendingDependencies.add(this.#namedDependencies.get(name)); 9473 } 9474 return this; 9475 } 9476 recordOperation(idx, preserve = false) { 9477 this.recordDependencies(idx, [FORCED_DEPENDENCY_LABEL]); 9478 if (this.#debugMetadata) { 9479 const metadata = ensureDebugMetadata(this.#debugMetadata, idx); 9480 const { 9481 dependencies 9482 } = metadata; 9483 this.#pendingDependencies.forEach(dependencies.add, dependencies); 9484 this.#savesStack.forEach(dependencies.add, dependencies); 9485 this.#markedContentStack.forEach(dependencies.add, dependencies); 9486 dependencies.delete(idx); 9487 metadata.isRenderingOperation = true; 9488 } 9489 if (this.#pendingBBoxIdx === idx) { 9490 const minX = floor(this.#pendingBBox[0] * 256 / this.#canvasWidth); 9491 const minY = floor(this.#pendingBBox[1] * 256 / this.#canvasHeight); 9492 const maxX = ceil(this.#pendingBBox[2] * 256 / this.#canvasWidth); 9493 const maxY = ceil(this.#pendingBBox[3] * 256 / this.#canvasHeight); 9494 expandBBox(this.#bboxesCoords, idx, minX, minY, maxX, maxY); 9495 for (const depIdx of this.#pendingDependencies) { 9496 if (depIdx !== idx) { 9497 expandBBox(this.#bboxesCoords, depIdx, minX, minY, maxX, maxY); 9498 } 9499 } 9500 for (const saveIdx of this.#savesStack) { 9501 if (saveIdx !== idx) { 9502 expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY); 9503 } 9504 } 9505 for (const saveIdx of this.#markedContentStack) { 9506 if (saveIdx !== idx) { 9507 expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY); 9508 } 9509 } 9510 if (!preserve) { 9511 this.#pendingDependencies.clear(); 9512 this.#pendingBBoxIdx = -1; 9513 } 9514 } 9515 return this; 9516 } 9517 recordShowTextOperation(idx, preserve = false) { 9518 const deps = Array.from(this.#pendingDependencies); 9519 this.recordOperation(idx, preserve); 9520 this.recordIncrementalData("sameLineText", idx); 9521 for (const dep of deps) { 9522 this.recordIncrementalData("sameLineText", dep); 9523 } 9524 return this; 9525 } 9526 bboxToClipBoxDropOperation(idx, preserve = false) { 9527 if (this.#pendingBBoxIdx === idx) { 9528 this.#pendingBBoxIdx = -1; 9529 this.#clipBox[0] = Math.max(this.#clipBox[0], this.#pendingBBox[0]); 9530 this.#clipBox[1] = Math.max(this.#clipBox[1], this.#pendingBBox[1]); 9531 this.#clipBox[2] = Math.min(this.#clipBox[2], this.#pendingBBox[2]); 9532 this.#clipBox[3] = Math.min(this.#clipBox[3], this.#pendingBBox[3]); 9533 if (!preserve) { 9534 this.#pendingDependencies.clear(); 9535 } 9536 } 9537 return this; 9538 } 9539 _takePendingDependencies() { 9540 const pendingDependencies = this.#pendingDependencies; 9541 this.#pendingDependencies = new Set(); 9542 return pendingDependencies; 9543 } 9544 _extractOperation(idx) { 9545 const operation = this.#operations.get(idx); 9546 this.#operations.delete(idx); 9547 return operation; 9548 } 9549 _pushPendingDependencies(dependencies) { 9550 for (const dep of dependencies) { 9551 this.#pendingDependencies.add(dep); 9552 } 9553 } 9554 take() { 9555 this.#fontBBoxTrustworthy.clear(); 9556 return new BBoxReader(this.#bboxes, this.#bboxesCoords); 9557 } 9558 takeDebugMetadata() { 9559 return this.#debugMetadata; 9560 } 9561} 9562class CanvasNestedDependencyTracker { 9563 #dependencyTracker; 9564 #opIdx; 9565 #ignoreBBoxes; 9566 #nestingLevel = 0; 9567 #savesLevel = 0; 9568 constructor(dependencyTracker, opIdx, ignoreBBoxes) { 9569 if (dependencyTracker instanceof CanvasNestedDependencyTracker && dependencyTracker.#ignoreBBoxes === !!ignoreBBoxes) { 9570 return dependencyTracker; 9571 } 9572 this.#dependencyTracker = dependencyTracker; 9573 this.#opIdx = opIdx; 9574 this.#ignoreBBoxes = !!ignoreBBoxes; 9575 } 9576 growOperationsCount() { 9577 throw new Error("Unreachable"); 9578 } 9579 save(opIdx) { 9580 this.#savesLevel++; 9581 this.#dependencyTracker.save(this.#opIdx); 9582 return this; 9583 } 9584 restore(opIdx) { 9585 if (this.#savesLevel > 0) { 9586 this.#dependencyTracker.restore(this.#opIdx); 9587 this.#savesLevel--; 9588 } 9589 return this; 9590 } 9591 recordOpenMarker(idx) { 9592 this.#nestingLevel++; 9593 return this; 9594 } 9595 getOpenMarker() { 9596 return this.#nestingLevel > 0 ? this.#opIdx : this.#dependencyTracker.getOpenMarker(); 9597 } 9598 recordCloseMarker(idx) { 9599 this.#nestingLevel--; 9600 return this; 9601 } 9602 beginMarkedContent(opIdx) { 9603 return this; 9604 } 9605 endMarkedContent(opIdx) { 9606 return this; 9607 } 9608 pushBaseTransform(ctx) { 9609 this.#dependencyTracker.pushBaseTransform(ctx); 9610 return this; 9611 } 9612 popBaseTransform() { 9613 this.#dependencyTracker.popBaseTransform(); 9614 return this; 9615 } 9616 recordSimpleData(name, idx) { 9617 this.#dependencyTracker.recordSimpleData(name, this.#opIdx); 9618 return this; 9619 } 9620 recordIncrementalData(name, idx) { 9621 this.#dependencyTracker.recordIncrementalData(name, this.#opIdx); 9622 return this; 9623 } 9624 resetIncrementalData(name, idx) { 9625 this.#dependencyTracker.resetIncrementalData(name, this.#opIdx); 9626 return this; 9627 } 9628 recordNamedData(name, idx) { 9629 return this; 9630 } 9631 recordSimpleDataFromNamed(name, depName, fallbackIdx) { 9632 this.#dependencyTracker.recordSimpleDataFromNamed(name, depName, this.#opIdx); 9633 return this; 9634 } 9635 recordFutureForcedDependency(name, idx) { 9636 this.#dependencyTracker.recordFutureForcedDependency(name, this.#opIdx); 9637 return this; 9638 } 9639 inheritSimpleDataAsFutureForcedDependencies(names) { 9640 this.#dependencyTracker.inheritSimpleDataAsFutureForcedDependencies(names); 9641 return this; 9642 } 9643 inheritPendingDependenciesAsFutureForcedDependencies() { 9644 this.#dependencyTracker.inheritPendingDependenciesAsFutureForcedDependencies(); 9645 return this; 9646 } 9647 resetBBox(idx) { 9648 if (!this.#ignoreBBoxes) { 9649 this.#dependencyTracker.resetBBox(this.#opIdx); 9650 } 9651 return this; 9652 } 9653 recordClipBox(idx, ctx, minX, maxX, minY, maxY) { 9654 if (!this.#ignoreBBoxes) { 9655 this.#dependencyTracker.recordClipBox(this.#opIdx, ctx, minX, maxX, minY, maxY); 9656 } 9657 return this; 9658 } 9659 recordBBox(idx, ctx, minX, maxX, minY, maxY) { 9660 if (!this.#ignoreBBoxes) { 9661 this.#dependencyTracker.recordBBox(this.#opIdx, ctx, minX, maxX, minY, maxY); 9662 } 9663 return this; 9664 } 9665 recordCharacterBBox(idx, ctx, font, scale, x, y, getMeasure) { 9666 if (!this.#ignoreBBoxes) { 9667 this.#dependencyTracker.recordCharacterBBox(this.#opIdx, ctx, font, scale, x, y, getMeasure); 9668 } 9669 return this; 9670 } 9671 recordFullPageBBox(idx) { 9672 if (!this.#ignoreBBoxes) { 9673 this.#dependencyTracker.recordFullPageBBox(this.#opIdx); 9674 } 9675 return this; 9676 } 9677 getSimpleIndex(dependencyName) { 9678 return this.#dependencyTracker.getSimpleIndex(dependencyName); 9679 } 9680 recordDependencies(idx, dependencyNames) { 9681 this.#dependencyTracker.recordDependencies(this.#opIdx, dependencyNames); 9682 return this; 9683 } 9684 recordNamedDependency(idx, name) { 9685 this.#dependencyTracker.recordNamedDependency(this.#opIdx, name); 9686 return this; 9687 } 9688 recordOperation(idx) { 9689 this.#dependencyTracker.recordOperation(this.#opIdx, true); 9690 return this; 9691 } 9692 recordShowTextOperation(idx) { 9693 this.#dependencyTracker.recordShowTextOperation(this.#opIdx, true); 9694 return this; 9695 } 9696 bboxToClipBoxDropOperation(idx) { 9697 if (!this.#ignoreBBoxes) { 9698 this.#dependencyTracker.bboxToClipBoxDropOperation(this.#opIdx, true); 9699 } 9700 return this; 9701 } 9702 take() { 9703 throw new Error("Unreachable"); 9704 } 9705 takeDebugMetadata() { 9706 throw new Error("Unreachable"); 9707 } 9708} 9709const Dependencies = { 9710 stroke: ["path", "transform", "filter", "strokeColor", "strokeAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "dash"], 9711 fill: ["path", "transform", "filter", "fillColor", "fillAlpha", "globalCompositeOperation", "SMask"], 9712 imageXObject: ["transform", "SMask", "filter", "fillAlpha", "strokeAlpha", "globalCompositeOperation"], 9713 rawFillPath: ["filter", "fillColor", "fillAlpha"], 9714 showText: ["transform", "leading", "charSpacing", "wordSpacing", "hScale", "textRise", "moveText", "textMatrix", "font", "fontObj", "filter", "fillColor", "textRenderingMode", "SMask", "fillAlpha", "strokeAlpha", "globalCompositeOperation", "sameLineText"], 9715 transform: ["transform"], 9716 transformAndFill: ["transform", "fillColor"] 9717}; 9718 9719;// ./src/display/pattern_helper.js 9720 9721 9722const PathType = { 9723 FILL: "Fill", 9724 STROKE: "Stroke", 9725 SHADING: "Shading" 9726}; 9727function applyBoundingBox(ctx, bbox) { 9728 if (!bbox) { 9729 return; 9730 } 9731 const width = bbox[2] - bbox[0]; 9732 const height = bbox[3] - bbox[1]; 9733 const region = new Path2D(); 9734 region.rect(bbox[0], bbox[1], width, height); 9735 ctx.clip(region); 9736} 9737class BaseShadingPattern { 9738 isModifyingCurrentTransform() { 9739 return false;
vendor: 4,978 bytes, lines 9740-9893
9740 } 9741 getPattern() { 9742 unreachable("Abstract method `getPattern` called."); 9743 } 9744} 9745class RadialAxialShadingPattern extends BaseShadingPattern { 9746 constructor(IR) { 9747 super(); 9748 this._type = IR[1]; 9749 this._bbox = IR[2]; 9750 this._colorStops = IR[3]; 9751 this._p0 = IR[4]; 9752 this._p1 = IR[5]; 9753 this._r0 = IR[6]; 9754 this._r1 = IR[7]; 9755 this.matrix = null; 9756 } 9757 isOriginBased() { 9758 return this._p0[0] === 0 && this._p0[1] === 0 && (!this.isRadial() || this._p1[0] === 0 && this._p1[1] === 0); 9759 } 9760 isRadial() { 9761 return this._type === "radial"; 9762 } 9763 _createGradient(ctx, transform = null) { 9764 let grad; 9765 let firstPoint = this._p0; 9766 let secondPoint = this._p1; 9767 if (transform) { 9768 firstPoint = firstPoint.slice(); 9769 secondPoint = secondPoint.slice(); 9770 Util.applyTransform(firstPoint, transform); 9771 Util.applyTransform(secondPoint, transform); 9772 } 9773 if (this._type === "axial") { 9774 grad = ctx.createLinearGradient(firstPoint[0], firstPoint[1], secondPoint[0], secondPoint[1]); 9775 } else if (this._type === "radial") { 9776 let r0 = this._r0; 9777 let r1 = this._r1; 9778 if (transform) { 9779 const scale = new Float32Array(2); 9780 Util.singularValueDecompose2dScale(transform, scale); 9781 r0 *= scale[0]; 9782 r1 *= scale[0]; 9783 } 9784 grad = ctx.createRadialGradient(firstPoint[0], firstPoint[1], r0, secondPoint[0], secondPoint[1], r1); 9785 } 9786 for (const colorStop of this._colorStops) { 9787 grad.addColorStop(colorStop[0], colorStop[1]); 9788 } 9789 return grad; 9790 } 9791 getPattern(ctx, owner, inverse, pathType) { 9792 let pattern; 9793 if (pathType === PathType.STROKE || pathType === PathType.FILL) { 9794 if (this.isOriginBased()) { 9795 let transf = Util.transform(inverse, owner.baseTransform); 9796 if (this.matrix) { 9797 transf = Util.transform(transf, this.matrix); 9798 } 9799 const precision = 1e-3; 9800 const n1 = Math.hypot(transf[0], transf[1]); 9801 const n2 = Math.hypot(transf[2], transf[3]); 9802 const ps = (transf[0] * transf[2] + transf[1] * transf[3]) / (n1 * n2); 9803 if (Math.abs(ps) < precision) { 9804 if (this.isRadial()) { 9805 if (Math.abs(n1 - n2) < precision) { 9806 return this._createGradient(ctx, transf); 9807 } 9808 } else { 9809 return this._createGradient(ctx, transf); 9810 } 9811 } 9812 } 9813 const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0]; 9814 const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1; 9815 const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1; 9816 const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height); 9817 const tmpCtx = tmpCanvas.context; 9818 tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); 9819 tmpCtx.beginPath(); 9820 tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height); 9821 tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]); 9822 inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]); 9823 tmpCtx.transform(...owner.baseTransform); 9824 if (this.matrix) { 9825 tmpCtx.transform(...this.matrix); 9826 } 9827 applyBoundingBox(tmpCtx, this._bbox); 9828 tmpCtx.fillStyle = this._createGradient(tmpCtx); 9829 tmpCtx.fill(); 9830 pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat"); 9831 const domMatrix = new DOMMatrix(inverse); 9832 pattern.setTransform(domMatrix); 9833 } else { 9834 applyBoundingBox(ctx, this._bbox); 9835 pattern = this._createGradient(ctx); 9836 } 9837 return pattern; 9838 } 9839} 9840function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) { 9841 const coords = context.coords, 9842 colors = context.colors; 9843 const bytes = data.data, 9844 rowSize = data.width * 4; 9845 let tmp; 9846 if (coords[p1 + 1] > coords[p2 + 1]) { 9847 tmp = p1; 9848 p1 = p2; 9849 p2 = tmp; 9850 tmp = c1; 9851 c1 = c2; 9852 c2 = tmp; 9853 } 9854 if (coords[p2 + 1] > coords[p3 + 1]) { 9855 tmp = p2; 9856 p2 = p3; 9857 p3 = tmp; 9858 tmp = c2; 9859 c2 = c3; 9860 c3 = tmp; 9861 } 9862 if (coords[p1 + 1] > coords[p2 + 1]) { 9863 tmp = p1; 9864 p1 = p2; 9865 p2 = tmp; 9866 tmp = c1; 9867 c1 = c2; 9868 c2 = tmp; 9869 } 9870 const x1 = (coords[p1] + context.offsetX) * context.scaleX; 9871 const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY; 9872 const x2 = (coords[p2] + context.offsetX) * context.scaleX; 9873 const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY; 9874 const x3 = (coords[p3] + context.offsetX) * context.scaleX; 9875 const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY; 9876 if (y1 >= y3) { 9877 return; 9878 } 9879 const c1r = colors[c1], 9880 c1g = colors[c1 + 1], 9881 c1b = colors[c1 + 2]; 9882 const c2r = colors[c2], 9883 c2g = colors[c2 + 1], 9884 c2b = colors[c2 + 2]; 9885 const c3r = colors[c3], 9886 c3g = colors[c3 + 1], 9887 c3b = colors[c3 + 2]; 9888 const minY = Math.round(y1), 9889 maxY = Math.round(y3); 9890 let xa, car, cag, cab; 9891 let xb, cbr, cbg, cbb; 9892 for (let y = minY; y <= maxY; y++) { 9893 if (y < y2) {
vendor: 8,643 bytes, lines 9894-10150
9894 const k = y < y1 ? 0 : (y1 - y) / (y1 - y2); 9895 xa = x1 - (x1 - x2) * k; 9896 car = c1r - (c1r - c2r) * k; 9897 cag = c1g - (c1g - c2g) * k; 9898 cab = c1b - (c1b - c2b) * k; 9899 } else { 9900 let k; 9901 if (y > y3) { 9902 k = 1; 9903 } else if (y2 === y3) { 9904 k = 0; 9905 } else { 9906 k = (y2 - y) / (y2 - y3); 9907 } 9908 xa = x2 - (x2 - x3) * k; 9909 car = c2r - (c2r - c3r) * k; 9910 cag = c2g - (c2g - c3g) * k; 9911 cab = c2b - (c2b - c3b) * k; 9912 } 9913 let k; 9914 if (y < y1) { 9915 k = 0; 9916 } else if (y > y3) { 9917 k = 1; 9918 } else { 9919 k = (y1 - y) / (y1 - y3); 9920 } 9921 xb = x1 - (x1 - x3) * k; 9922 cbr = c1r - (c1r - c3r) * k; 9923 cbg = c1g - (c1g - c3g) * k; 9924 cbb = c1b - (c1b - c3b) * k; 9925 const x1_ = Math.round(Math.min(xa, xb)); 9926 const x2_ = Math.round(Math.max(xa, xb)); 9927 let j = rowSize * y + x1_ * 4; 9928 for (let x = x1_; x <= x2_; x++) { 9929 k = (xa - x) / (xa - xb); 9930 if (k < 0) { 9931 k = 0; 9932 } else if (k > 1) { 9933 k = 1; 9934 } 9935 bytes[j++] = car - (car - cbr) * k | 0; 9936 bytes[j++] = cag - (cag - cbg) * k | 0; 9937 bytes[j++] = cab - (cab - cbb) * k | 0; 9938 bytes[j++] = 255; 9939 } 9940 } 9941} 9942function drawFigure(data, figure, context) { 9943 const ps = figure.coords; 9944 const cs = figure.colors; 9945 let i, ii; 9946 switch (figure.type) { 9947 case MeshFigureType.LATTICE: 9948 const verticesPerRow = figure.verticesPerRow; 9949 const rows = Math.floor(ps.length / verticesPerRow) - 1; 9950 const cols = verticesPerRow - 1; 9951 for (i = 0; i < rows; i++) { 9952 let q = i * verticesPerRow; 9953 for (let j = 0; j < cols; j++, q++) { 9954 drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]); 9955 drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]); 9956 } 9957 } 9958 break; 9959 case MeshFigureType.TRIANGLES: 9960 for (i = 0, ii = ps.length; i < ii; i += 3) { 9961 drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]); 9962 } 9963 break; 9964 default: 9965 throw new Error("illegal figure"); 9966 } 9967} 9968class MeshShadingPattern extends BaseShadingPattern { 9969 constructor(IR) { 9970 super(); 9971 this._coords = IR[2]; 9972 this._colors = IR[3]; 9973 this._figures = IR[4]; 9974 this._bounds = IR[5]; 9975 this._bbox = IR[6]; 9976 this._background = IR[7]; 9977 this.matrix = null; 9978 } 9979 _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) { 9980 const EXPECTED_SCALE = 1.1; 9981 const MAX_PATTERN_SIZE = 3000; 9982 const BORDER_SIZE = 2; 9983 const offsetX = Math.floor(this._bounds[0]); 9984 const offsetY = Math.floor(this._bounds[1]); 9985 const boundsWidth = Math.ceil(this._bounds[2]) - offsetX; 9986 const boundsHeight = Math.ceil(this._bounds[3]) - offsetY; 9987 const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); 9988 const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); 9989 const scaleX = boundsWidth / width; 9990 const scaleY = boundsHeight / height; 9991 const context = { 9992 coords: this._coords, 9993 colors: this._colors, 9994 offsetX: -offsetX, 9995 offsetY: -offsetY, 9996 scaleX: 1 / scaleX, 9997 scaleY: 1 / scaleY 9998 }; 9999 const paddedWidth = width + BORDER_SIZE * 2; 10000 const paddedHeight = height + BORDER_SIZE * 2; 10001 const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight); 10002 const tmpCtx = tmpCanvas.context; 10003 const data = tmpCtx.createImageData(width, height); 10004 if (backgroundColor) { 10005 const bytes = data.data; 10006 for (let i = 0, ii = bytes.length; i < ii; i += 4) { 10007 bytes[i] = backgroundColor[0]; 10008 bytes[i + 1] = backgroundColor[1]; 10009 bytes[i + 2] = backgroundColor[2]; 10010 bytes[i + 3] = 255; 10011 } 10012 } 10013 for (const figure of this._figures) { 10014 drawFigure(data, figure, context); 10015 } 10016 tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE); 10017 const canvas = tmpCanvas.canvas; 10018 return { 10019 canvas, 10020 offsetX: offsetX - BORDER_SIZE * scaleX, 10021 offsetY: offsetY - BORDER_SIZE * scaleY, 10022 scaleX, 10023 scaleY 10024 }; 10025 } 10026 isModifyingCurrentTransform() { 10027 return true; 10028 } 10029 getPattern(ctx, owner, inverse, pathType) { 10030 applyBoundingBox(ctx, this._bbox); 10031 const scale = new Float32Array(2); 10032 if (pathType === PathType.SHADING) { 10033 Util.singularValueDecompose2dScale(getCurrentTransform(ctx), scale); 10034 } else if (this.matrix) { 10035 Util.singularValueDecompose2dScale(this.matrix, scale); 10036 const [matrixScaleX, matrixScaleY] = scale; 10037 Util.singularValueDecompose2dScale(owner.baseTransform, scale); 10038 scale[0] *= matrixScaleX; 10039 scale[1] *= matrixScaleY; 10040 } else { 10041 Util.singularValueDecompose2dScale(owner.baseTransform, scale); 10042 } 10043 const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases); 10044 if (pathType !== PathType.SHADING) { 10045 ctx.setTransform(...owner.baseTransform); 10046 if (this.matrix) { 10047 ctx.transform(...this.matrix); 10048 } 10049 } 10050 ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); 10051 ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY); 10052 return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat"); 10053 } 10054} 10055class DummyShadingPattern extends BaseShadingPattern { 10056 getPattern() { 10057 return "hotpink"; 10058 } 10059} 10060function getShadingPattern(IR) { 10061 switch (IR[0]) { 10062 case "RadialAxial": 10063 return new RadialAxialShadingPattern(IR); 10064 case "Mesh": 10065 return new MeshShadingPattern(IR); 10066 case "Dummy": 10067 return new DummyShadingPattern(); 10068 } 10069 throw new Error(`Unknown IR type: ${IR[0]}`); 10070} 10071const PaintType = { 10072 COLORED: 1, 10073 UNCOLORED: 2 10074}; 10075class TilingPattern { 10076 static MAX_PATTERN_SIZE = 3000; 10077 constructor(IR, ctx, canvasGraphicsFactory, baseTransform) { 10078 this.color = IR[1]; 10079 this.operatorList = IR[2]; 10080 this.matrix = IR[3]; 10081 this.bbox = IR[4]; 10082 this.xstep = IR[5]; 10083 this.ystep = IR[6]; 10084 this.paintType = IR[7]; 10085 this.tilingType = IR[8]; 10086 this.ctx = ctx; 10087 this.canvasGraphicsFactory = canvasGraphicsFactory; 10088 this.baseTransform = baseTransform; 10089 } 10090 createPatternCanvas(owner, opIdx) { 10091 const { 10092 bbox, 10093 operatorList, 10094 paintType, 10095 tilingType, 10096 color, 10097 canvasGraphicsFactory 10098 } = this; 10099 let { 10100 xstep, 10101 ystep 10102 } = this; 10103 xstep = Math.abs(xstep); 10104 ystep = Math.abs(ystep); 10105 info("TilingType: " + tilingType); 10106 const x0 = bbox[0], 10107 y0 = bbox[1], 10108 x1 = bbox[2], 10109 y1 = bbox[3]; 10110 const width = x1 - x0; 10111 const height = y1 - y0; 10112 const scale = new Float32Array(2); 10113 Util.singularValueDecompose2dScale(this.matrix, scale); 10114 const [matrixScaleX, matrixScaleY] = scale; 10115 Util.singularValueDecompose2dScale(this.baseTransform, scale); 10116 const combinedScaleX = matrixScaleX * scale[0]; 10117 const combinedScaleY = matrixScaleY * scale[1]; 10118 let canvasWidth = width, 10119 canvasHeight = height, 10120 redrawHorizontally = false, 10121 redrawVertically = false; 10122 const xScaledStep = Math.ceil(xstep * combinedScaleX); 10123 const yScaledStep = Math.ceil(ystep * combinedScaleY); 10124 const xScaledWidth = Math.ceil(width * combinedScaleX); 10125 const yScaledHeight = Math.ceil(height * combinedScaleY); 10126 if (xScaledStep >= xScaledWidth) { 10127 canvasWidth = xstep; 10128 } else { 10129 redrawHorizontally = true; 10130 } 10131 if (yScaledStep >= yScaledHeight) { 10132 canvasHeight = ystep; 10133 } else { 10134 redrawVertically = true; 10135 } 10136 const dimx = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); 10137 const dimy = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); 10138 const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size); 10139 const tmpCtx = tmpCanvas.context; 10140 const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx, opIdx); 10141 graphics.groupLevel = owner.groupLevel; 10142 this.setFillAndStrokeStyleToContext(graphics, paintType, color); 10143 tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0); 10144 graphics.transform(0, dimx.scale, 0, 0, dimy.scale, 0, 0); 10145 tmpCtx.save(); 10146 graphics.dependencyTracker?.save(); 10147 this.clipBbox(graphics, x0, y0, x1, y1); 10148 graphics.baseTransform = getCurrentTransform(graphics.ctx); 10149 graphics.executeOperatorList(operatorList); 10150 graphics.endDrawing();
vendor: 6,083 bytes, lines 10151-10334
10151 graphics.dependencyTracker?.restore(); 10152 tmpCtx.restore(); 10153 if (redrawHorizontally || redrawVertically) { 10154 const image = tmpCanvas.canvas; 10155 if (redrawHorizontally) { 10156 canvasWidth = xstep; 10157 } 10158 if (redrawVertically) { 10159 canvasHeight = ystep; 10160 } 10161 const dimx2 = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX); 10162 const dimy2 = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY); 10163 const xSize = dimx2.size; 10164 const ySize = dimy2.size; 10165 const tmpCanvas2 = owner.cachedCanvases.getCanvas("pattern-workaround", xSize, ySize); 10166 const tmpCtx2 = tmpCanvas2.context; 10167 const ii = redrawHorizontally ? Math.floor(width / xstep) : 0; 10168 const jj = redrawVertically ? Math.floor(height / ystep) : 0; 10169 for (let i = 0; i <= ii; i++) { 10170 for (let j = 0; j <= jj; j++) { 10171 tmpCtx2.drawImage(image, xSize * i, ySize * j, xSize, ySize, 0, 0, xSize, ySize); 10172 } 10173 } 10174 return { 10175 canvas: tmpCanvas2.canvas, 10176 scaleX: dimx2.scale, 10177 scaleY: dimy2.scale, 10178 offsetX: x0, 10179 offsetY: y0 10180 }; 10181 } 10182 return { 10183 canvas: tmpCanvas.canvas, 10184 scaleX: dimx.scale, 10185 scaleY: dimy.scale, 10186 offsetX: x0, 10187 offsetY: y0 10188 }; 10189 } 10190 getSizeAndScale(step, realOutputSize, scale) { 10191 const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize); 10192 let size = Math.ceil(step * scale); 10193 if (size >= maxSize) { 10194 size = maxSize; 10195 } else { 10196 scale = size / step; 10197 } 10198 return { 10199 scale, 10200 size 10201 }; 10202 } 10203 clipBbox(graphics, x0, y0, x1, y1) { 10204 const bboxWidth = x1 - x0; 10205 const bboxHeight = y1 - y0; 10206 graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight); 10207 Util.axialAlignedBoundingBox([x0, y0, x1, y1], getCurrentTransform(graphics.ctx), graphics.current.minMax); 10208 graphics.clip(); 10209 graphics.endPath(); 10210 } 10211 setFillAndStrokeStyleToContext(graphics, paintType, color) { 10212 const context = graphics.ctx, 10213 current = graphics.current; 10214 switch (paintType) { 10215 case PaintType.COLORED: 10216 const { 10217 fillStyle, 10218 strokeStyle 10219 } = this.ctx; 10220 context.fillStyle = current.fillColor = fillStyle; 10221 context.strokeStyle = current.strokeColor = strokeStyle; 10222 break; 10223 case PaintType.UNCOLORED: 10224 context.fillStyle = context.strokeStyle = color; 10225 current.fillColor = current.strokeColor = color; 10226 break; 10227 default: 10228 throw new FormatError(`Unsupported paint type: ${paintType}`); 10229 } 10230 } 10231 isModifyingCurrentTransform() { 10232 return false; 10233 } 10234 getPattern(ctx, owner, inverse, pathType, opIdx) { 10235 let matrix = inverse; 10236 if (pathType !== PathType.SHADING) { 10237 matrix = Util.transform(matrix, owner.baseTransform); 10238 if (this.matrix) { 10239 matrix = Util.transform(matrix, this.matrix); 10240 } 10241 } 10242 const temporaryPatternCanvas = this.createPatternCanvas(owner, opIdx); 10243 let domMatrix = new DOMMatrix(matrix); 10244 domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY); 10245 domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY); 10246 const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat"); 10247 pattern.setTransform(domMatrix); 10248 return pattern; 10249 } 10250} 10251 10252;// ./src/shared/image_utils.js 10253 10254function convertToRGBA(params) { 10255 switch (params.kind) { 10256 case ImageKind.GRAYSCALE_1BPP: 10257 return convertBlackAndWhiteToRGBA(params); 10258 case ImageKind.RGB_24BPP: 10259 return convertRGBToRGBA(params); 10260 } 10261 return null; 10262} 10263function convertBlackAndWhiteToRGBA({ 10264 src, 10265 srcPos = 0, 10266 dest, 10267 width, 10268 height, 10269 nonBlackColor = 0xffffffff, 10270 inverseDecode = false 10271}) { 10272 const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; 10273 const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor]; 10274 const widthInSource = width >> 3; 10275 const widthRemainder = width & 7; 10276 const srcLength = src.length; 10277 dest = new Uint32Array(dest.buffer); 10278 let destPos = 0; 10279 for (let i = 0; i < height; i++) { 10280 for (const max = srcPos + widthInSource; srcPos < max; srcPos++) { 10281 const elem = srcPos < srcLength ? src[srcPos] : 255; 10282 dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping; 10283 dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping; 10284 dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping; 10285 dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping; 10286 dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping; 10287 dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping; 10288 dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping; 10289 dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping; 10290 } 10291 if (widthRemainder === 0) { 10292 continue; 10293 } 10294 const elem = srcPos < srcLength ? src[srcPos++] : 255; 10295 for (let j = 0; j < widthRemainder; j++) { 10296 dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping; 10297 } 10298 } 10299 return { 10300 srcPos, 10301 destPos 10302 }; 10303} 10304function convertRGBToRGBA({ 10305 src, 10306 srcPos = 0, 10307 dest, 10308 destPos = 0, 10309 width, 10310 height 10311}) { 10312 let i = 0; 10313 const len = width * height * 3; 10314 const len32 = len >> 2; 10315 const src32 = new Uint32Array(src.buffer, srcPos, len32); 10316 if (FeatureTest.isLittleEndian) { 10317 for (; i < len32 - 2; i += 3, destPos += 4) { 10318 const s1 = src32[i]; 10319 const s2 = src32[i + 1]; 10320 const s3 = src32[i + 2]; 10321 dest[destPos] = s1 | 0xff000000; 10322 dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000; 10323 dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000; 10324 dest[destPos + 3] = s3 >>> 8 | 0xff000000; 10325 } 10326 for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { 10327 dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000; 10328 } 10329 } else { 10330 for (; i < len32 - 2; i += 3, destPos += 4) { 10331 const s1 = src32[i]; 10332 const s2 = src32[i + 1]; 10333 const s3 = src32[i + 2]; 10334 dest[destPos] = s1 | 0xff;
10335 dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff; 10336 dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff; 10337 dest[destPos + 3] = s3 << 8 | 0xff; 10338 } 10339 for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) { 10340 dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff; 10341 } 10342 } 10343 return { 10344 srcPos: srcPos + len, 10345 destPos 10346 }; 10347} 10348function grayToRGBA(src, dest) { 10349 if (FeatureTest.isLittleEndian) { 10350 for (let i = 0, ii = src.length; i < ii; i++) { 10351 dest[i] = src[i] * 0x10101 | 0xff000000; 10352 } 10353 } else { 10354 for (let i = 0, ii = src.length; i < ii; i++) { 10355 dest[i] = src[i] * 0x1010100 | 0x000000ff; 10356 } 10357 } 10358} 10359 10360;// ./src/display/canvas.js 10361 10362 10363 10364 10365 10366const MIN_FONT_SIZE = 16; 10367const MAX_FONT_SIZE = 100; 10368const EXECUTION_TIME = 15; 10369const EXECUTION_STEPS = 10; 10370const FULL_CHUNK_HEIGHT = 16; 10371const SCALE_MATRIX = new DOMMatrix(); 10372const XY = new Float32Array(2); 10373const MIN_MAX_INIT = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]); 10374function mirrorContextOperations(ctx, destCtx) { 10375 if (ctx._removeMirroring) { 10376 throw new Error("Context is already forwarding operations."); 10377 } 10378 ctx.__originalSave = ctx.save; 10379 ctx.__originalRestore = ctx.restore; 10380 ctx.__originalRotate = ctx.rotate; 10381 ctx.__originalScale = ctx.scale; 10382 ctx.__originalTranslate = ctx.translate; 10383 ctx.__originalTransform = ctx.transform; 10384 ctx.__originalSetTransform = ctx.setTransform; 10385 ctx.__originalResetTransform = ctx.resetTransform; 10386 ctx.__originalClip = ctx.clip; 10387 ctx.__originalMoveTo = ctx.moveTo; 10388 ctx.__originalLineTo = ctx.lineTo; 10389 ctx.__originalBezierCurveTo = ctx.bezierCurveTo; 10390 ctx.__originalRect = ctx.rect; 10391 ctx.__originalClosePath = ctx.closePath; 10392 ctx.__originalBeginPath = ctx.beginPath; 10393 ctx._removeMirroring = () => { 10394 ctx.save = ctx.__originalSave; 10395 ctx.restore = ctx.__originalRestore; 10396 ctx.rotate = ctx.__originalRotate; 10397 ctx.scale = ctx.__originalScale; 10398 ctx.translate = ctx.__originalTranslate; 10399 ctx.transform = ctx.__originalTransform; 10400 ctx.setTransform = ctx.__originalSetTransform; 10401 ctx.resetTransform = ctx.__originalResetTransform; 10402 ctx.clip = ctx.__originalClip; 10403 ctx.moveTo = ctx.__originalMoveTo; 10404 ctx.lineTo = ctx.__originalLineTo; 10405 ctx.bezierCurveTo = ctx.__originalBezierCurveTo; 10406 ctx.rect = ctx.__originalRect; 10407 ctx.closePath = ctx.__originalClosePath; 10408 ctx.beginPath = ctx.__originalBeginPath; 10409 delete ctx._removeMirroring; 10410 }; 10411 ctx.save = function () { 10412 destCtx.save(); 10413 this.__originalSave(); 10414 }; 10415 ctx.restore = function () { 10416 destCtx.restore(); 10417 this.__originalRestore(); 10418 }; 10419 ctx.translate = function (x, y) { 10420 destCtx.translate(x, y); 10421 this.__originalTranslate(x, y); 10422 }; 10423 ctx.scale = function (x, y) { 10424 destCtx.scale(x, y); 10425 this.__originalScale(x, y); 10426 }; 10427 ctx.transform = function (a, b, c, d, e, f) { 10428 destCtx.transform(a, b, c, d, e, f); 10429 this.__originalTransform(a, b, c, d, e, f); 10430 }; 10431 ctx.setTransform = function (a, b, c, d, e, f) { 10432 destCtx.setTransform(a, b, c, d, e, f); 10433 this.__originalSetTransform(a, b, c, d, e, f); 10434 }; 10435 ctx.resetTransform = function () { 10436 destCtx.resetTransform(); 10437 this.__originalResetTransform(); 10438 }; 10439 ctx.rotate = function (angle) { 10440 destCtx.rotate(angle); 10441 this.__originalRotate(angle); 10442 }; 10443 ctx.clip = function (rule) { 10444 destCtx.clip(rule); 10445 this.__originalClip(rule); 10446 }; 10447 ctx.moveTo = function (x, y) { 10448 destCtx.moveTo(x, y); 10449 this.__originalMoveTo(x, y); 10450 }; 10451 ctx.lineTo = function (x, y) { 10452 destCtx.lineTo(x, y); 10453 this.__originalLineTo(x, y); 10454 }; 10455 ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) { 10456 destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); 10457 this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); 10458 }; 10459 ctx.rect = function (x, y, width, height) { 10460 destCtx.rect(x, y, width, height); 10461 this.__originalRect(x, y, width, height); 10462 }; 10463 ctx.closePath = function () { 10464 destCtx.closePath(); 10465 this.__originalClosePath(); 10466 }; 10467 ctx.beginPath = function () { 10468 destCtx.beginPath(); 10469 this.__originalBeginPath(); 10470 }; 10471} 10472class CachedCanvases { 10473 constructor(canvasFactory) { 10474 this.canvasFactory = canvasFactory; 10475 this.cache = Object.create(null); 10476 } 10477 getCanvas(id, width, height) {
vendor: 36,726 bytes, lines 10478-11508
10478 let canvasEntry; 10479 if (this.cache[id] !== undefined) { 10480 canvasEntry = this.cache[id]; 10481 this.canvasFactory.reset(canvasEntry, width, height); 10482 } else { 10483 canvasEntry = this.canvasFactory.create(width, height); 10484 this.cache[id] = canvasEntry; 10485 } 10486 return canvasEntry; 10487 } 10488 delete(id) { 10489 delete this.cache[id]; 10490 } 10491 clear() { 10492 for (const id in this.cache) { 10493 const canvasEntry = this.cache[id]; 10494 this.canvasFactory.destroy(canvasEntry); 10495 delete this.cache[id]; 10496 } 10497 } 10498} 10499function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) { 10500 const [a, b, c, d, tx, ty] = getCurrentTransform(ctx); 10501 if (b === 0 && c === 0) { 10502 const tlX = destX * a + tx; 10503 const rTlX = Math.round(tlX); 10504 const tlY = destY * d + ty; 10505 const rTlY = Math.round(tlY); 10506 const brX = (destX + destW) * a + tx; 10507 const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; 10508 const brY = (destY + destH) * d + ty; 10509 const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; 10510 ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY); 10511 ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight); 10512 ctx.setTransform(a, b, c, d, tx, ty); 10513 return [rWidth, rHeight]; 10514 } 10515 if (a === 0 && d === 0) { 10516 const tlX = destY * c + tx; 10517 const rTlX = Math.round(tlX); 10518 const tlY = destX * b + ty; 10519 const rTlY = Math.round(tlY); 10520 const brX = (destY + destH) * c + tx; 10521 const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; 10522 const brY = (destX + destW) * b + ty; 10523 const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; 10524 ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY); 10525 ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth); 10526 ctx.setTransform(a, b, c, d, tx, ty); 10527 return [rHeight, rWidth]; 10528 } 10529 ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH); 10530 const scaleX = Math.hypot(a, b); 10531 const scaleY = Math.hypot(c, d); 10532 return [scaleX * destW, scaleY * destH]; 10533} 10534class CanvasExtraState { 10535 alphaIsShape = false; 10536 fontSize = 0; 10537 fontSizeScale = 1; 10538 textMatrix = null; 10539 textMatrixScale = 1; 10540 fontMatrix = FONT_IDENTITY_MATRIX; 10541 leading = 0; 10542 x = 0; 10543 y = 0; 10544 lineX = 0; 10545 lineY = 0; 10546 charSpacing = 0; 10547 wordSpacing = 0; 10548 textHScale = 1; 10549 textRenderingMode = TextRenderingMode.FILL; 10550 textRise = 0; 10551 fillColor = "#000000"; 10552 strokeColor = "#000000"; 10553 patternFill = false; 10554 patternStroke = false; 10555 fillAlpha = 1; 10556 strokeAlpha = 1; 10557 lineWidth = 1; 10558 activeSMask = null; 10559 transferMaps = "none"; 10560 constructor(width, height, preInit) { 10561 preInit?.(this); 10562 this.clipBox = new Float32Array([0, 0, width, height]); 10563 this.minMax = MIN_MAX_INIT.slice(); 10564 } 10565 clone() { 10566 const clone = Object.create(this); 10567 clone.clipBox = this.clipBox.slice(); 10568 clone.minMax = this.minMax.slice(); 10569 return clone; 10570 } 10571 getPathBoundingBox(pathType = PathType.FILL, transform = null) { 10572 const box = this.minMax.slice(); 10573 if (pathType === PathType.STROKE) { 10574 if (!transform) { 10575 unreachable("Stroke bounding box must include transform."); 10576 } 10577 Util.singularValueDecompose2dScale(transform, XY); 10578 const xStrokePad = XY[0] * this.lineWidth / 2; 10579 const yStrokePad = XY[1] * this.lineWidth / 2; 10580 box[0] -= xStrokePad; 10581 box[1] -= yStrokePad; 10582 box[2] += xStrokePad; 10583 box[3] += yStrokePad; 10584 } 10585 return box; 10586 } 10587 updateClipFromPath() { 10588 const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox()); 10589 this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]); 10590 } 10591 isEmptyClip() { 10592 return this.minMax[0] === Infinity; 10593 } 10594 startNewPathAndClipBox(box) { 10595 this.clipBox.set(box, 0); 10596 this.minMax.set(MIN_MAX_INIT, 0); 10597 } 10598 getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) { 10599 return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform)); 10600 } 10601} 10602function putBinaryImageData(ctx, imgData) { 10603 if (imgData instanceof ImageData) { 10604 ctx.putImageData(imgData, 0, 0); 10605 return; 10606 } 10607 const height = imgData.height, 10608 width = imgData.width; 10609 const partialChunkHeight = height % FULL_CHUNK_HEIGHT; 10610 const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; 10611 const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; 10612 const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); 10613 let srcPos = 0, 10614 destPos; 10615 const src = imgData.data; 10616 const dest = chunkImgData.data; 10617 let i, j, thisChunkHeight, elemsInThisChunk; 10618 if (imgData.kind === util_ImageKind.GRAYSCALE_1BPP) { 10619 const srcLength = src.byteLength; 10620 const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2); 10621 const dest32DataLength = dest32.length; 10622 const fullSrcDiff = width + 7 >> 3; 10623 const white = 0xffffffff; 10624 const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; 10625 for (i = 0; i < totalChunks; i++) { 10626 thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; 10627 destPos = 0; 10628 for (j = 0; j < thisChunkHeight; j++) { 10629 const srcDiff = srcLength - srcPos; 10630 let k = 0; 10631 const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7; 10632 const kEndUnrolled = kEnd & ~7; 10633 let mask = 0; 10634 let srcByte = 0; 10635 for (; k < kEndUnrolled; k += 8) { 10636 srcByte = src[srcPos++]; 10637 dest32[destPos++] = srcByte & 128 ? white : black; 10638 dest32[destPos++] = srcByte & 64 ? white : black; 10639 dest32[destPos++] = srcByte & 32 ? white : black; 10640 dest32[destPos++] = srcByte & 16 ? white : black; 10641 dest32[destPos++] = srcByte & 8 ? white : black; 10642 dest32[destPos++] = srcByte & 4 ? white : black; 10643 dest32[destPos++] = srcByte & 2 ? white : black; 10644 dest32[destPos++] = srcByte & 1 ? white : black; 10645 } 10646 for (; k < kEnd; k++) { 10647 if (mask === 0) { 10648 srcByte = src[srcPos++]; 10649 mask = 128; 10650 } 10651 dest32[destPos++] = srcByte & mask ? white : black; 10652 mask >>= 1; 10653 } 10654 } 10655 while (destPos < dest32DataLength) { 10656 dest32[destPos++] = 0; 10657 } 10658 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 10659 } 10660 } else if (imgData.kind === util_ImageKind.RGBA_32BPP) { 10661 j = 0; 10662 elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; 10663 for (i = 0; i < fullChunks; i++) { 10664 dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); 10665 srcPos += elemsInThisChunk; 10666 ctx.putImageData(chunkImgData, 0, j); 10667 j += FULL_CHUNK_HEIGHT; 10668 } 10669 if (i < totalChunks) { 10670 elemsInThisChunk = width * partialChunkHeight * 4; 10671 dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); 10672 ctx.putImageData(chunkImgData, 0, j); 10673 } 10674 } else if (imgData.kind === util_ImageKind.RGB_24BPP) { 10675 thisChunkHeight = FULL_CHUNK_HEIGHT; 10676 elemsInThisChunk = width * thisChunkHeight; 10677 for (i = 0; i < totalChunks; i++) { 10678 if (i >= fullChunks) { 10679 thisChunkHeight = partialChunkHeight; 10680 elemsInThisChunk = width * thisChunkHeight; 10681 } 10682 destPos = 0; 10683 for (j = elemsInThisChunk; j--;) { 10684 dest[destPos++] = src[srcPos++]; 10685 dest[destPos++] = src[srcPos++]; 10686 dest[destPos++] = src[srcPos++]; 10687 dest[destPos++] = 255; 10688 } 10689 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 10690 } 10691 } else { 10692 throw new Error(`bad image kind: ${imgData.kind}`); 10693 } 10694} 10695function putBinaryImageMask(ctx, imgData) { 10696 if (imgData.bitmap) { 10697 ctx.drawImage(imgData.bitmap, 0, 0); 10698 return; 10699 } 10700 const height = imgData.height, 10701 width = imgData.width; 10702 const partialChunkHeight = height % FULL_CHUNK_HEIGHT; 10703 const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; 10704 const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; 10705 const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); 10706 let srcPos = 0; 10707 const src = imgData.data; 10708 const dest = chunkImgData.data; 10709 for (let i = 0; i < totalChunks; i++) { 10710 const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; 10711 ({ 10712 srcPos 10713 } = convertBlackAndWhiteToRGBA({ 10714 src, 10715 srcPos, 10716 dest, 10717 width, 10718 height: thisChunkHeight, 10719 nonBlackColor: 0 10720 })); 10721 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); 10722 } 10723} 10724function copyCtxState(sourceCtx, destCtx) { 10725 const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"]; 10726 for (const property of properties) { 10727 if (sourceCtx[property] !== undefined) { 10728 destCtx[property] = sourceCtx[property]; 10729 } 10730 } 10731 if (sourceCtx.setLineDash !== undefined) { 10732 destCtx.setLineDash(sourceCtx.getLineDash()); 10733 destCtx.lineDashOffset = sourceCtx.lineDashOffset; 10734 } 10735} 10736function resetCtxToDefault(ctx) { 10737 ctx.strokeStyle = ctx.fillStyle = "#000000"; 10738 ctx.fillRule = "nonzero"; 10739 ctx.globalAlpha = 1; 10740 ctx.lineWidth = 1; 10741 ctx.lineCap = "butt"; 10742 ctx.lineJoin = "miter"; 10743 ctx.miterLimit = 10; 10744 ctx.globalCompositeOperation = "source-over"; 10745 ctx.font = "10px sans-serif"; 10746 if (ctx.setLineDash !== undefined) { 10747 ctx.setLineDash([]); 10748 ctx.lineDashOffset = 0; 10749 } 10750 const { 10751 filter 10752 } = ctx; 10753 if (filter !== "none" && filter !== "") { 10754 ctx.filter = "none"; 10755 } 10756} 10757function getImageSmoothingEnabled(transform, interpolate) { 10758 if (interpolate) { 10759 return true; 10760 } 10761 Util.singularValueDecompose2dScale(transform, XY); 10762 const actualScale = Math.fround(OutputScale.pixelRatio * PixelsPerInch.PDF_TO_CSS_UNITS); 10763 return XY[0] <= actualScale && XY[1] <= actualScale; 10764} 10765const LINE_CAP_STYLES = ["butt", "round", "square"]; 10766const LINE_JOIN_STYLES = ["miter", "round", "bevel"]; 10767const NORMAL_CLIP = {}; 10768const EO_CLIP = {}; 10769class CanvasGraphics { 10770 constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, { 10771 optionalContentConfig, 10772 markedContentStack = null 10773 }, annotationCanvasMap, pageColors, dependencyTracker) { 10774 this.ctx = canvasCtx; 10775 this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); 10776 this.stateStack = []; 10777 this.pendingClip = null; 10778 this.pendingEOFill = false; 10779 this.res = null; 10780 this.xobjs = null; 10781 this.commonObjs = commonObjs; 10782 this.objs = objs; 10783 this.canvasFactory = canvasFactory; 10784 this.filterFactory = filterFactory; 10785 this.groupStack = []; 10786 this.baseTransform = null; 10787 this.baseTransformStack = []; 10788 this.groupLevel = 0; 10789 this.smaskStack = []; 10790 this.smaskCounter = 0; 10791 this.tempSMask = null; 10792 this.suspendedCtx = null; 10793 this.contentVisible = true; 10794 this.markedContentStack = markedContentStack || []; 10795 this.optionalContentConfig = optionalContentConfig; 10796 this.cachedCanvases = new CachedCanvases(this.canvasFactory); 10797 this.cachedPatterns = new Map(); 10798 this.annotationCanvasMap = annotationCanvasMap; 10799 this.viewportScale = 1; 10800 this.outputScaleX = 1; 10801 this.outputScaleY = 1; 10802 this.pageColors = pageColors; 10803 this._cachedScaleForStroking = [-1, 0]; 10804 this._cachedGetSinglePixelWidth = null; 10805 this._cachedBitmapsMap = new Map(); 10806 this.dependencyTracker = dependencyTracker ?? null; 10807 } 10808 getObject(opIdx, data, fallback = null) { 10809 if (typeof data === "string") { 10810 this.dependencyTracker?.recordNamedDependency(opIdx, data); 10811 return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data); 10812 } 10813 return fallback; 10814 } 10815 beginDrawing({ 10816 transform, 10817 viewport, 10818 transparency = false, 10819 background = null 10820 }) { 10821 const width = this.ctx.canvas.width; 10822 const height = this.ctx.canvas.height; 10823 const savedFillStyle = this.ctx.fillStyle; 10824 this.ctx.fillStyle = background || "#ffffff"; 10825 this.ctx.fillRect(0, 0, width, height); 10826 this.ctx.fillStyle = savedFillStyle; 10827 if (transparency) { 10828 const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height); 10829 this.compositeCtx = this.ctx; 10830 this.transparentCanvas = transparentCanvas.canvas; 10831 this.ctx = transparentCanvas.context; 10832 this.ctx.save(); 10833 this.ctx.transform(...getCurrentTransform(this.compositeCtx)); 10834 } 10835 this.ctx.save(); 10836 resetCtxToDefault(this.ctx); 10837 if (transform) { 10838 this.ctx.transform(...transform); 10839 this.outputScaleX = transform[0]; 10840 this.outputScaleY = transform[0]; 10841 } 10842 this.ctx.transform(...viewport.transform); 10843 this.viewportScale = viewport.scale; 10844 this.baseTransform = getCurrentTransform(this.ctx); 10845 } 10846 executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper, operationsFilter) { 10847 const argsArray = operatorList.argsArray; 10848 const fnArray = operatorList.fnArray; 10849 let i = executionStartIdx || 0; 10850 const argsArrayLen = argsArray.length; 10851 if (argsArrayLen === i) { 10852 return i; 10853 } 10854 const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function"; 10855 const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0; 10856 let steps = 0; 10857 const commonObjs = this.commonObjs; 10858 const objs = this.objs; 10859 let fnId, fnArgs; 10860 while (true) { 10861 if (stepper !== undefined && i === stepper.nextBreakPoint) { 10862 stepper.breakIt(i, continueCallback); 10863 return i; 10864 } 10865 if (!operationsFilter || operationsFilter(i)) { 10866 fnId = fnArray[i]; 10867 fnArgs = argsArray[i] ?? null; 10868 if (fnId !== OPS.dependency) { 10869 if (fnArgs === null) { 10870 this[fnId](i); 10871 } else { 10872 this[fnId](i, ...fnArgs); 10873 } 10874 } else { 10875 for (const depObjId of fnArgs) { 10876 this.dependencyTracker?.recordNamedData(depObjId, i); 10877 const objsPool = depObjId.startsWith("g_") ? commonObjs : objs; 10878 if (!objsPool.has(depObjId)) { 10879 objsPool.get(depObjId, continueCallback); 10880 return i; 10881 } 10882 } 10883 } 10884 } 10885 i++; 10886 if (i === argsArrayLen) { 10887 return i; 10888 } 10889 if (chunkOperations && ++steps > EXECUTION_STEPS) { 10890 if (Date.now() > endTime) { 10891 continueCallback(); 10892 return i; 10893 } 10894 steps = 0; 10895 } 10896 } 10897 } 10898 #restoreInitialState() { 10899 while (this.stateStack.length || this.inSMaskMode) { 10900 this.restore(); 10901 } 10902 this.current.activeSMask = null; 10903 this.ctx.restore(); 10904 if (this.transparentCanvas) { 10905 this.ctx = this.compositeCtx; 10906 this.ctx.save(); 10907 this.ctx.setTransform(1, 0, 0, 1, 0, 0); 10908 this.ctx.drawImage(this.transparentCanvas, 0, 0); 10909 this.ctx.restore(); 10910 this.transparentCanvas = null; 10911 } 10912 } 10913 endDrawing() { 10914 this.#restoreInitialState(); 10915 this.cachedCanvases.clear(); 10916 this.cachedPatterns.clear(); 10917 for (const cache of this._cachedBitmapsMap.values()) { 10918 for (const canvas of cache.values()) { 10919 if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) { 10920 canvas.width = canvas.height = 0; 10921 } 10922 } 10923 cache.clear(); 10924 } 10925 this._cachedBitmapsMap.clear(); 10926 this.#drawFilter(); 10927 } 10928 #drawFilter() { 10929 if (this.pageColors) { 10930 const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background); 10931 if (hcmFilterId !== "none") { 10932 const savedFilter = this.ctx.filter; 10933 this.ctx.filter = hcmFilterId; 10934 this.ctx.drawImage(this.ctx.canvas, 0, 0); 10935 this.ctx.filter = savedFilter; 10936 } 10937 } 10938 } 10939 _scaleImage(img, inverseTransform) { 10940 const width = img.width ?? img.displayWidth; 10941 const height = img.height ?? img.displayHeight; 10942 let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1); 10943 let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1); 10944 let paintWidth = width, 10945 paintHeight = height; 10946 let tmpCanvasId = "prescale1"; 10947 let tmpCanvas, tmpCtx; 10948 while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) { 10949 let newWidth = paintWidth, 10950 newHeight = paintHeight; 10951 if (widthScale > 2 && paintWidth > 1) { 10952 newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2); 10953 widthScale /= paintWidth / newWidth; 10954 } 10955 if (heightScale > 2 && paintHeight > 1) { 10956 newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2; 10957 heightScale /= paintHeight / newHeight; 10958 } 10959 tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); 10960 tmpCtx = tmpCanvas.context; 10961 tmpCtx.clearRect(0, 0, newWidth, newHeight); 10962 tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight); 10963 img = tmpCanvas.canvas; 10964 paintWidth = newWidth; 10965 paintHeight = newHeight; 10966 tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1"; 10967 } 10968 return { 10969 img, 10970 paintWidth, 10971 paintHeight 10972 }; 10973 } 10974 _createMaskCanvas(opIdx, img) { 10975 const ctx = this.ctx; 10976 const { 10977 width, 10978 height 10979 } = img; 10980 const fillColor = this.current.fillColor; 10981 const isPatternFill = this.current.patternFill; 10982 const currentTransform = getCurrentTransform(ctx); 10983 let cache, cacheKey, scaled, maskCanvas; 10984 if ((img.bitmap || img.data) && img.count > 1) { 10985 const mainKey = img.bitmap || img.data.buffer; 10986 cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]); 10987 cache = this._cachedBitmapsMap.get(mainKey); 10988 if (!cache) { 10989 cache = new Map(); 10990 this._cachedBitmapsMap.set(mainKey, cache); 10991 } 10992 const cachedImage = cache.get(cacheKey); 10993 if (cachedImage && !isPatternFill) { 10994 const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]); 10995 const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]); 10996 this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill); 10997 return { 10998 canvas: cachedImage, 10999 offsetX, 11000 offsetY 11001 }; 11002 } 11003 scaled = cachedImage; 11004 } 11005 if (!scaled) { 11006 maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); 11007 putBinaryImageMask(maskCanvas.context, img); 11008 } 11009 let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]); 11010 maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]); 11011 const minMax = MIN_MAX_INIT.slice(); 11012 Util.axialAlignedBoundingBox([0, 0, width, height], maskToCanvas, minMax); 11013 const [minX, minY, maxX, maxY] = minMax; 11014 const drawnWidth = Math.round(maxX - minX) || 1; 11015 const drawnHeight = Math.round(maxY - minY) || 1; 11016 const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight); 11017 const fillCtx = fillCanvas.context; 11018 const offsetX = minX; 11019 const offsetY = minY; 11020 fillCtx.translate(-offsetX, -offsetY); 11021 fillCtx.transform(...maskToCanvas); 11022 if (!scaled) { 11023 scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx)); 11024 scaled = scaled.img; 11025 if (cache && isPatternFill) { 11026 cache.set(cacheKey, scaled); 11027 } 11028 } 11029 fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate); 11030 drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height); 11031 fillCtx.globalCompositeOperation = "source-in"; 11032 const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]); 11033 fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL, opIdx) : fillColor; 11034 fillCtx.fillRect(0, 0, width, height); 11035 if (cache && !isPatternFill) { 11036 this.cachedCanvases.delete("fillCanvas"); 11037 cache.set(cacheKey, fillCanvas.canvas); 11038 } 11039 this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill); 11040 return { 11041 canvas: fillCanvas.canvas, 11042 offsetX: Math.round(offsetX), 11043 offsetY: Math.round(offsetY) 11044 }; 11045 } 11046 setLineWidth(opIdx, width) { 11047 this.dependencyTracker?.recordSimpleData("lineWidth", opIdx); 11048 if (width !== this.current.lineWidth) { 11049 this._cachedScaleForStroking[0] = -1; 11050 } 11051 this.current.lineWidth = width; 11052 this.ctx.lineWidth = width; 11053 } 11054 setLineCap(opIdx, style) { 11055 this.dependencyTracker?.recordSimpleData("lineCap", opIdx); 11056 this.ctx.lineCap = LINE_CAP_STYLES[style]; 11057 } 11058 setLineJoin(opIdx, style) { 11059 this.dependencyTracker?.recordSimpleData("lineJoin", opIdx); 11060 this.ctx.lineJoin = LINE_JOIN_STYLES[style]; 11061 } 11062 setMiterLimit(opIdx, limit) { 11063 this.dependencyTracker?.recordSimpleData("miterLimit", opIdx); 11064 this.ctx.miterLimit = limit; 11065 } 11066 setDash(opIdx, dashArray, dashPhase) { 11067 this.dependencyTracker?.recordSimpleData("dash", opIdx); 11068 const ctx = this.ctx; 11069 if (ctx.setLineDash !== undefined) { 11070 ctx.setLineDash(dashArray); 11071 ctx.lineDashOffset = dashPhase; 11072 } 11073 } 11074 setRenderingIntent(opIdx, intent) {} 11075 setFlatness(opIdx, flatness) {} 11076 setGState(opIdx, states) { 11077 for (const [key, value] of states) { 11078 switch (key) { 11079 case "LW": 11080 this.setLineWidth(opIdx, value); 11081 break; 11082 case "LC": 11083 this.setLineCap(opIdx, value); 11084 break; 11085 case "LJ": 11086 this.setLineJoin(opIdx, value); 11087 break; 11088 case "ML": 11089 this.setMiterLimit(opIdx, value); 11090 break; 11091 case "D": 11092 this.setDash(opIdx, value[0], value[1]); 11093 break; 11094 case "RI": 11095 this.setRenderingIntent(opIdx, value); 11096 break; 11097 case "FL": 11098 this.setFlatness(opIdx, value); 11099 break; 11100 case "Font": 11101 this.setFont(opIdx, value[0], value[1]); 11102 break; 11103 case "CA": 11104 this.dependencyTracker?.recordSimpleData("strokeAlpha", opIdx); 11105 this.current.strokeAlpha = value; 11106 break; 11107 case "ca": 11108 this.dependencyTracker?.recordSimpleData("fillAlpha", opIdx); 11109 this.ctx.globalAlpha = this.current.fillAlpha = value; 11110 break; 11111 case "BM": 11112 this.dependencyTracker?.recordSimpleData("globalCompositeOperation", opIdx); 11113 this.ctx.globalCompositeOperation = value; 11114 break; 11115 case "SMask": 11116 this.dependencyTracker?.recordSimpleData("SMask", opIdx); 11117 this.current.activeSMask = value ? this.tempSMask : null; 11118 this.tempSMask = null; 11119 this.checkSMaskState(); 11120 break; 11121 case "TR": 11122 this.dependencyTracker?.recordSimpleData("filter", opIdx); 11123 this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value); 11124 break; 11125 } 11126 } 11127 } 11128 get inSMaskMode() { 11129 return !!this.suspendedCtx; 11130 } 11131 checkSMaskState() { 11132 const inSMaskMode = this.inSMaskMode; 11133 if (this.current.activeSMask && !inSMaskMode) { 11134 this.beginSMaskMode(); 11135 } else if (!this.current.activeSMask && inSMaskMode) { 11136 this.endSMaskMode(); 11137 } 11138 } 11139 beginSMaskMode(opIdx) { 11140 if (this.inSMaskMode) { 11141 throw new Error("beginSMaskMode called while already in smask mode"); 11142 } 11143 const drawnWidth = this.ctx.canvas.width; 11144 const drawnHeight = this.ctx.canvas.height; 11145 const cacheId = "smaskGroupAt" + this.groupLevel; 11146 const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); 11147 this.suspendedCtx = this.ctx; 11148 const ctx = this.ctx = scratchCanvas.context; 11149 ctx.setTransform(this.suspendedCtx.getTransform()); 11150 copyCtxState(this.suspendedCtx, ctx); 11151 mirrorContextOperations(ctx, this.suspendedCtx); 11152 this.setGState(opIdx, [["BM", "source-over"]]); 11153 } 11154 endSMaskMode() { 11155 if (!this.inSMaskMode) { 11156 throw new Error("endSMaskMode called while not in smask mode"); 11157 } 11158 this.ctx._removeMirroring(); 11159 copyCtxState(this.ctx, this.suspendedCtx); 11160 this.ctx = this.suspendedCtx; 11161 this.suspendedCtx = null; 11162 } 11163 compose(dirtyBox) { 11164 if (!this.current.activeSMask) { 11165 return; 11166 } 11167 if (!dirtyBox) { 11168 dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height]; 11169 } else { 11170 dirtyBox[0] = Math.floor(dirtyBox[0]); 11171 dirtyBox[1] = Math.floor(dirtyBox[1]); 11172 dirtyBox[2] = Math.ceil(dirtyBox[2]); 11173 dirtyBox[3] = Math.ceil(dirtyBox[3]); 11174 } 11175 const smask = this.current.activeSMask; 11176 const suspendedCtx = this.suspendedCtx; 11177 this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox); 11178 this.ctx.save(); 11179 this.ctx.setTransform(1, 0, 0, 1, 0, 0); 11180 this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 11181 this.ctx.restore(); 11182 } 11183 composeSMask(ctx, smask, layerCtx, layerBox) { 11184 const layerOffsetX = layerBox[0]; 11185 const layerOffsetY = layerBox[1]; 11186 const layerWidth = layerBox[2] - layerOffsetX; 11187 const layerHeight = layerBox[3] - layerOffsetY; 11188 if (layerWidth === 0 || layerHeight === 0) { 11189 return; 11190 } 11191 this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY); 11192 ctx.save(); 11193 ctx.globalAlpha = 1; 11194 ctx.globalCompositeOperation = "source-over"; 11195 ctx.setTransform(1, 0, 0, 1, 0, 0); 11196 ctx.drawImage(layerCtx.canvas, 0, 0); 11197 ctx.restore(); 11198 } 11199 genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) { 11200 let maskCanvas = maskCtx.canvas; 11201 let maskX = layerOffsetX - maskOffsetX; 11202 let maskY = layerOffsetY - maskOffsetY; 11203 if (backdrop) { 11204 if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) { 11205 const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height); 11206 const ctx = canvas.context; 11207 ctx.drawImage(maskCanvas, -maskX, -maskY); 11208 ctx.globalCompositeOperation = "destination-atop"; 11209 ctx.fillStyle = backdrop; 11210 ctx.fillRect(0, 0, width, height); 11211 ctx.globalCompositeOperation = "source-over"; 11212 maskCanvas = canvas.canvas; 11213 maskX = maskY = 0; 11214 } else { 11215 maskCtx.save(); 11216 maskCtx.globalAlpha = 1; 11217 maskCtx.setTransform(1, 0, 0, 1, 0, 0); 11218 const clip = new Path2D(); 11219 clip.rect(maskX, maskY, width, height); 11220 maskCtx.clip(clip); 11221 maskCtx.globalCompositeOperation = "destination-atop"; 11222 maskCtx.fillStyle = backdrop; 11223 maskCtx.fillRect(maskX, maskY, width, height); 11224 maskCtx.restore(); 11225 } 11226 } 11227 layerCtx.save(); 11228 layerCtx.globalAlpha = 1; 11229 layerCtx.setTransform(1, 0, 0, 1, 0, 0); 11230 if (subtype === "Alpha" && transferMap) { 11231 layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap); 11232 } else if (subtype === "Luminosity") { 11233 layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap); 11234 } 11235 const clip = new Path2D(); 11236 clip.rect(layerOffsetX, layerOffsetY, width, height); 11237 layerCtx.clip(clip); 11238 layerCtx.globalCompositeOperation = "destination-in"; 11239 layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height); 11240 layerCtx.restore(); 11241 } 11242 save(opIdx) { 11243 if (this.inSMaskMode) { 11244 copyCtxState(this.ctx, this.suspendedCtx); 11245 } 11246 this.ctx.save(); 11247 const old = this.current; 11248 this.stateStack.push(old); 11249 this.current = old.clone(); 11250 this.dependencyTracker?.save(opIdx); 11251 } 11252 restore(opIdx) { 11253 this.dependencyTracker?.restore(opIdx); 11254 if (this.stateStack.length === 0) { 11255 if (this.inSMaskMode) { 11256 this.endSMaskMode(); 11257 } 11258 return; 11259 } 11260 this.current = this.stateStack.pop(); 11261 this.ctx.restore(); 11262 if (this.inSMaskMode) { 11263 copyCtxState(this.suspendedCtx, this.ctx); 11264 } 11265 this.checkSMaskState(); 11266 this.pendingClip = null; 11267 this._cachedScaleForStroking[0] = -1; 11268 this._cachedGetSinglePixelWidth = null; 11269 } 11270 transform(opIdx, a, b, c, d, e, f) { 11271 this.dependencyTracker?.recordIncrementalData("transform", opIdx); 11272 this.ctx.transform(a, b, c, d, e, f); 11273 this._cachedScaleForStroking[0] = -1; 11274 this._cachedGetSinglePixelWidth = null; 11275 } 11276 constructPath(opIdx, op, data, minMax) { 11277 let [path] = data; 11278 if (!minMax) { 11279 path ||= data[0] = new Path2D(); 11280 this[op](opIdx, path); 11281 return; 11282 } 11283 if (this.dependencyTracker !== null) { 11284 const outerExtraSize = op === OPS.stroke ? this.current.lineWidth / 2 : 0; 11285 this.dependencyTracker.resetBBox(opIdx).recordBBox(opIdx, this.ctx, minMax[0] - outerExtraSize, minMax[2] + outerExtraSize, minMax[1] - outerExtraSize, minMax[3] + outerExtraSize).recordDependencies(opIdx, ["transform"]); 11286 } 11287 if (!(path instanceof Path2D)) { 11288 path = data[0] = makePathFromDrawOPS(path); 11289 } 11290 Util.axialAlignedBoundingBox(minMax, getCurrentTransform(this.ctx), this.current.minMax); 11291 this[op](opIdx, path); 11292 this._pathStartIdx = opIdx; 11293 } 11294 closePath(opIdx) { 11295 this.ctx.closePath(); 11296 } 11297 stroke(opIdx, path, consumePath = true) { 11298 const ctx = this.ctx; 11299 const strokeColor = this.current.strokeColor; 11300 ctx.globalAlpha = this.current.strokeAlpha; 11301 if (this.contentVisible) { 11302 if (typeof strokeColor === "object" && strokeColor?.getPattern) { 11303 const baseTransform = strokeColor.isModifyingCurrentTransform() ? ctx.getTransform() : null; 11304 ctx.save(); 11305 ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx); 11306 if (baseTransform) { 11307 const newPath = new Path2D(); 11308 newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform)); 11309 path = newPath; 11310 } 11311 this.rescaleAndStroke(path, false); 11312 ctx.restore(); 11313 } else { 11314 this.rescaleAndStroke(path, true); 11315 } 11316 } 11317 this.dependencyTracker?.recordDependencies(opIdx, Dependencies.stroke); 11318 if (consumePath) { 11319 this.consumePath(opIdx, path, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx))); 11320 } 11321 ctx.globalAlpha = this.current.fillAlpha; 11322 } 11323 closeStroke(opIdx, path) { 11324 this.stroke(opIdx, path); 11325 } 11326 fill(opIdx, path, consumePath = true) { 11327 const ctx = this.ctx; 11328 const fillColor = this.current.fillColor; 11329 const isPatternFill = this.current.patternFill; 11330 let needRestore = false; 11331 if (isPatternFill) { 11332 const baseTransform = fillColor.isModifyingCurrentTransform() ? ctx.getTransform() : null; 11333 this.dependencyTracker?.save(opIdx); 11334 ctx.save(); 11335 ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx); 11336 if (baseTransform) { 11337 const newPath = new Path2D(); 11338 newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform)); 11339 path = newPath; 11340 } 11341 needRestore = true; 11342 } 11343 const intersect = this.current.getClippedPathBoundingBox(); 11344 if (this.contentVisible && intersect !== null) { 11345 if (this.pendingEOFill) { 11346 ctx.fill(path, "evenodd"); 11347 this.pendingEOFill = false; 11348 } else { 11349 ctx.fill(path); 11350 } 11351 } 11352 this.dependencyTracker?.recordDependencies(opIdx, Dependencies.fill); 11353 if (needRestore) { 11354 ctx.restore(); 11355 this.dependencyTracker?.restore(opIdx); 11356 } 11357 if (consumePath) { 11358 this.consumePath(opIdx, path, intersect); 11359 } 11360 } 11361 eoFill(opIdx, path) { 11362 this.pendingEOFill = true; 11363 this.fill(opIdx, path); 11364 } 11365 fillStroke(opIdx, path) { 11366 this.fill(opIdx, path, false); 11367 this.stroke(opIdx, path, false); 11368 this.consumePath(opIdx, path); 11369 } 11370 eoFillStroke(opIdx, path) { 11371 this.pendingEOFill = true; 11372 this.fillStroke(opIdx, path); 11373 } 11374 closeFillStroke(opIdx, path) { 11375 this.fillStroke(opIdx, path); 11376 } 11377 closeEOFillStroke(opIdx, path) { 11378 this.pendingEOFill = true; 11379 this.fillStroke(opIdx, path); 11380 } 11381 endPath(opIdx, path) { 11382 this.consumePath(opIdx, path); 11383 } 11384 rawFillPath(opIdx, path) { 11385 this.ctx.fill(path); 11386 this.dependencyTracker?.recordDependencies(opIdx, Dependencies.rawFillPath).recordOperation(opIdx); 11387 } 11388 clip(opIdx) { 11389 this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx); 11390 this.pendingClip = NORMAL_CLIP; 11391 } 11392 eoClip(opIdx) { 11393 this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx); 11394 this.pendingClip = EO_CLIP; 11395 } 11396 beginText(opIdx) { 11397 this.current.textMatrix = null; 11398 this.current.textMatrixScale = 1; 11399 this.current.x = this.current.lineX = 0; 11400 this.current.y = this.current.lineY = 0; 11401 this.dependencyTracker?.recordOpenMarker(opIdx).resetIncrementalData("sameLineText").resetIncrementalData("moveText", opIdx); 11402 } 11403 endText(opIdx) { 11404 const paths = this.pendingTextPaths; 11405 const ctx = this.ctx; 11406 if (this.dependencyTracker) { 11407 const { 11408 dependencyTracker 11409 } = this; 11410 if (paths !== undefined) { 11411 dependencyTracker.recordFutureForcedDependency("textClip", dependencyTracker.getOpenMarker()).recordFutureForcedDependency("textClip", opIdx); 11412 } 11413 dependencyTracker.recordCloseMarker(opIdx); 11414 } 11415 if (paths !== undefined) { 11416 const newPath = new Path2D(); 11417 const invTransf = ctx.getTransform().invertSelf(); 11418 for (const { 11419 transform, 11420 x, 11421 y, 11422 fontSize, 11423 path 11424 } of paths) { 11425 if (!path) { 11426 continue; 11427 } 11428 newPath.addPath(path, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x, y).scale(fontSize, -fontSize)); 11429 } 11430 ctx.clip(newPath); 11431 } 11432 delete this.pendingTextPaths; 11433 } 11434 setCharSpacing(opIdx, spacing) { 11435 this.dependencyTracker?.recordSimpleData("charSpacing", opIdx); 11436 this.current.charSpacing = spacing; 11437 } 11438 setWordSpacing(opIdx, spacing) { 11439 this.dependencyTracker?.recordSimpleData("wordSpacing", opIdx); 11440 this.current.wordSpacing = spacing; 11441 } 11442 setHScale(opIdx, scale) { 11443 this.dependencyTracker?.recordSimpleData("hScale", opIdx); 11444 this.current.textHScale = scale / 100; 11445 } 11446 setLeading(opIdx, leading) { 11447 this.dependencyTracker?.recordSimpleData("leading", opIdx); 11448 this.current.leading = -leading; 11449 } 11450 setFont(opIdx, fontRefName, size) { 11451 this.dependencyTracker?.recordSimpleData("font", opIdx).recordSimpleDataFromNamed("fontObj", fontRefName, opIdx); 11452 const fontObj = this.commonObjs.get(fontRefName); 11453 const current = this.current; 11454 if (!fontObj) { 11455 throw new Error(`Can't find font for ${fontRefName}`); 11456 } 11457 current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX; 11458 if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) { 11459 warn("Invalid font matrix for font " + fontRefName); 11460 } 11461 if (size < 0) { 11462 size = -size; 11463 current.fontDirection = -1; 11464 } else { 11465 current.fontDirection = 1; 11466 } 11467 this.current.font = fontObj; 11468 this.current.fontSize = size; 11469 if (fontObj.isType3Font) { 11470 return; 11471 } 11472 const name = fontObj.loadedName || "sans-serif"; 11473 const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`; 11474 let bold = "normal"; 11475 if (fontObj.black) { 11476 bold = "900"; 11477 } else if (fontObj.bold) { 11478 bold = "bold"; 11479 } 11480 const italic = fontObj.italic ? "italic" : "normal"; 11481 let browserFontSize = size; 11482 if (size < MIN_FONT_SIZE) { 11483 browserFontSize = MIN_FONT_SIZE; 11484 } else if (size > MAX_FONT_SIZE) { 11485 browserFontSize = MAX_FONT_SIZE; 11486 } 11487 this.current.fontSizeScale = size / browserFontSize; 11488 this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`; 11489 } 11490 setTextRenderingMode(opIdx, mode) { 11491 this.dependencyTracker?.recordSimpleData("textRenderingMode", opIdx); 11492 this.current.textRenderingMode = mode; 11493 } 11494 setTextRise(opIdx, rise) { 11495 this.dependencyTracker?.recordSimpleData("textRise", opIdx); 11496 this.current.textRise = rise; 11497 } 11498 moveText(opIdx, x, y) { 11499 this.dependencyTracker?.resetIncrementalData("sameLineText").recordIncrementalData("moveText", opIdx); 11500 this.current.x = this.current.lineX += x; 11501 this.current.y = this.current.lineY += y; 11502 } 11503 setLeadingMoveText(opIdx, x, y) { 11504 this.setLeading(opIdx, -y); 11505 this.moveText(opIdx, x, y); 11506 } 11507 setTextMatrix(opIdx, matrix) { 11508 this.dependencyTracker?.resetIncrementalData("sameLineText")
vendor: 5,039 bytes, lines 11508-11629
11508.recordSimpleData("textMatrix", opIdx); 11509 const { 11510 current 11511 } = this; 11512 current.textMatrix = matrix; 11513 current.textMatrixScale = Math.hypot(matrix[0], matrix[1]); 11514 current.x = current.lineX = 0; 11515 current.y = current.lineY = 0; 11516 } 11517 nextLine(opIdx) { 11518 this.moveText(opIdx, 0, this.current.leading); 11519 this.dependencyTracker?.recordIncrementalData("moveText", this.dependencyTracker.getSimpleIndex("leading") ?? opIdx); 11520 } 11521 #getScaledPath(path, currentTransform, transform) { 11522 const newPath = new Path2D(); 11523 newPath.addPath(path, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform)); 11524 return newPath; 11525 } 11526 paintChar(opIdx, character, x, y, patternFillTransform, patternStrokeTransform) { 11527 const ctx = this.ctx; 11528 const current = this.current; 11529 const font = current.font; 11530 const textRenderingMode = current.textRenderingMode; 11531 const fontSize = current.fontSize / current.fontSizeScale; 11532 const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; 11533 const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); 11534 const patternFill = current.patternFill && !font.missingFile; 11535 const patternStroke = current.patternStroke && !font.missingFile; 11536 let path; 11537 if ((font.disableFontFace || isAddToPathSet || patternFill || patternStroke) && !font.missingFile) { 11538 path = font.getPathGenerator(this.commonObjs, character); 11539 } 11540 if (path && (font.disableFontFace || patternFill || patternStroke)) { 11541 ctx.save(); 11542 ctx.translate(x, y); 11543 ctx.scale(fontSize, -fontSize); 11544 this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font); 11545 let currentTransform; 11546 if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 11547 if (patternFillTransform) { 11548 currentTransform = ctx.getTransform(); 11549 ctx.setTransform(...patternFillTransform); 11550 const scaledPath = this.#getScaledPath(path, currentTransform, patternFillTransform); 11551 ctx.fill(scaledPath); 11552 } else { 11553 ctx.fill(path); 11554 } 11555 } 11556 if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 11557 if (patternStrokeTransform) { 11558 currentTransform ||= ctx.getTransform(); 11559 ctx.setTransform(...patternStrokeTransform); 11560 const { 11561 a, 11562 b, 11563 c, 11564 d 11565 } = currentTransform; 11566 const invPatternTransform = Util.inverseTransform(patternStrokeTransform); 11567 const transf = Util.transform([a, b, c, d, 0, 0], invPatternTransform); 11568 Util.singularValueDecompose2dScale(transf, XY); 11569 ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize; 11570 ctx.stroke(this.#getScaledPath(path, currentTransform, patternStrokeTransform)); 11571 } else { 11572 ctx.lineWidth /= fontSize; 11573 ctx.stroke(path); 11574 } 11575 } 11576 ctx.restore(); 11577 } else { 11578 if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 11579 ctx.fillText(character, x, y); 11580 this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character)); 11581 } 11582 if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { 11583 if (this.dependencyTracker) { 11584 this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character)).recordDependencies(opIdx, Dependencies.stroke); 11585 } 11586 ctx.strokeText(character, x, y); 11587 } 11588 } 11589 if (isAddToPathSet) { 11590 const paths = this.pendingTextPaths ||= []; 11591 paths.push({ 11592 transform: getCurrentTransform(ctx), 11593 x, 11594 y, 11595 fontSize, 11596 path 11597 }); 11598 this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y); 11599 } 11600 } 11601 get isFontSubpixelAAEnabled() { 11602 const { 11603 context: ctx 11604 } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10); 11605 ctx.scale(1.5, 1); 11606 ctx.fillText("I", 0, 10); 11607 const data = ctx.getImageData(0, 0, 10, 10).data; 11608 let enabled = false; 11609 for (let i = 3; i < data.length; i += 4) { 11610 if (data[i] > 0 && data[i] < 255) { 11611 enabled = true; 11612 break; 11613 } 11614 } 11615 return shadow(this, "isFontSubpixelAAEnabled", enabled); 11616 } 11617 showText(opIdx, glyphs) { 11618 if (this.dependencyTracker) { 11619 this.dependencyTracker.recordDependencies(opIdx, Dependencies.showText).resetBBox(opIdx); 11620 if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) { 11621 this.dependencyTracker.recordFutureForcedDependency("textClip", opIdx).inheritPendingDependenciesAsFutureForcedDependencies(); 11622 } 11623 } 11624 const current = this.current; 11625 const font = current.font; 11626 if (font.isType3Font) { 11627 this.showType3Text(opIdx, glyphs); 11628 this.dependencyTracker?.recordShowTextOperation(opIdx); 11629 return
vendor: 7,185 bytes, lines 11629-11806
11629undefined; 11630 } 11631 const fontSize = current.fontSize; 11632 if (fontSize === 0) { 11633 this.dependencyTracker?.recordOperation(opIdx); 11634 return undefined; 11635 } 11636 const ctx = this.ctx; 11637 const fontSizeScale = current.fontSizeScale; 11638 const charSpacing = current.charSpacing; 11639 const wordSpacing = current.wordSpacing; 11640 const fontDirection = current.fontDirection; 11641 const textHScale = current.textHScale * fontDirection; 11642 const glyphsLength = glyphs.length; 11643 const vertical = font.vertical; 11644 const spacingDir = vertical ? 1 : -1; 11645 const defaultVMetrics = font.defaultVMetrics; 11646 const widthAdvanceScale = fontSize * current.fontMatrix[0]; 11647 const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill; 11648 ctx.save(); 11649 if (current.textMatrix) { 11650 ctx.transform(...current.textMatrix); 11651 } 11652 ctx.translate(current.x, current.y + current.textRise); 11653 if (fontDirection > 0) { 11654 ctx.scale(textHScale, -1); 11655 } else { 11656 ctx.scale(textHScale, 1); 11657 } 11658 let patternFillTransform, patternStrokeTransform; 11659 const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; 11660 const needsFill = fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE; 11661 const needsStroke = fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE; 11662 if (needsFill && current.patternFill) { 11663 ctx.save(); 11664 const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx); 11665 patternFillTransform = getCurrentTransform(ctx); 11666 ctx.restore(); 11667 ctx.fillStyle = pattern; 11668 } 11669 if (needsStroke && current.patternStroke) { 11670 ctx.save(); 11671 const pattern = current.strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx); 11672 patternStrokeTransform = getCurrentTransform(ctx); 11673 ctx.restore(); 11674 ctx.strokeStyle = pattern; 11675 } 11676 let lineWidth = current.lineWidth; 11677 const scale = current.textMatrixScale; 11678 if (scale === 0 || lineWidth === 0) { 11679 if (needsStroke) { 11680 lineWidth = this.getSinglePixelWidth(); 11681 } 11682 } else { 11683 lineWidth /= scale; 11684 } 11685 if (fontSizeScale !== 1.0) { 11686 ctx.scale(fontSizeScale, fontSizeScale); 11687 lineWidth /= fontSizeScale; 11688 } 11689 ctx.lineWidth = lineWidth; 11690 if (font.isInvalidPDFjsFont) { 11691 const chars = []; 11692 let width = 0; 11693 for (const glyph of glyphs) { 11694 chars.push(glyph.unicode); 11695 width += glyph.width; 11696 } 11697 const joinedChars = chars.join(""); 11698 ctx.fillText(joinedChars, 0, 0); 11699 if (this.dependencyTracker !== null) { 11700 const measure = ctx.measureText(joinedChars); 11701 this.dependencyTracker.recordBBox(opIdx, this.ctx, -measure.actualBoundingBoxLeft, measure.actualBoundingBoxRight, -measure.actualBoundingBoxAscent, measure.actualBoundingBoxDescent).recordShowTextOperation(opIdx); 11702 } 11703 current.x += width * widthAdvanceScale * textHScale; 11704 ctx.restore(); 11705 this.compose(); 11706 return undefined; 11707 } 11708 let x = 0, 11709 i; 11710 for (i = 0; i < glyphsLength; ++i) { 11711 const glyph = glyphs[i]; 11712 if (typeof glyph === "number") { 11713 x += spacingDir * glyph * fontSize / 1000; 11714 continue; 11715 } 11716 let restoreNeeded = false; 11717 const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; 11718 const character = glyph.fontChar; 11719 const accent = glyph.accent; 11720 let scaledX, scaledY; 11721 let width = glyph.width; 11722 if (vertical) { 11723 const vmetric = glyph.vmetric || defaultVMetrics; 11724 const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale; 11725 const vy = vmetric[2] * widthAdvanceScale; 11726 width = vmetric ? -vmetric[0] : width; 11727 scaledX = vx / fontSizeScale; 11728 scaledY = (x + vy) / fontSizeScale; 11729 } else { 11730 scaledX = x / fontSizeScale; 11731 scaledY = 0; 11732 } 11733 let measure; 11734 if (font.remeasure && width > 0) { 11735 measure = ctx.measureText(character); 11736 const measuredWidth = measure.width * 1000 / fontSize * fontSizeScale; 11737 if (width < measuredWidth && this.isFontSubpixelAAEnabled) { 11738 const characterScaleX = width / measuredWidth; 11739 restoreNeeded = true; 11740 ctx.save(); 11741 ctx.scale(characterScaleX, 1); 11742 scaledX /= characterScaleX; 11743 } else if (width !== measuredWidth) { 11744 scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale; 11745 } 11746 } 11747 if (this.contentVisible && (glyph.isInFont || font.missingFile)) { 11748 if (simpleFillText && !accent) { 11749 ctx.fillText(character, scaledX, scaledY); 11750 this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, measure ? { 11751 bbox: null 11752 } : font, fontSize / fontSizeScale, scaledX, scaledY, () => measure ?? ctx.measureText(character)); 11753 } else { 11754 this.paintChar(opIdx, character, scaledX, scaledY, patternFillTransform, patternStrokeTransform); 11755 if (accent) { 11756 const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale; 11757 const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale; 11758 this.paintChar(opIdx, accent.fontChar, scaledAccentX, scaledAccentY, patternFillTransform, patternStrokeTransform); 11759 } 11760 } 11761 } 11762 const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection; 11763 x += charWidth; 11764 if (restoreNeeded) { 11765 ctx.restore(); 11766 } 11767 } 11768 if (vertical) { 11769 current.y -= x; 11770 } else { 11771 current.x += x * textHScale; 11772 } 11773 ctx.restore(); 11774 this.compose(); 11775 this.dependencyTracker?.recordShowTextOperation(opIdx); 11776 return undefined; 11777 } 11778 showType3Text(opIdx, glyphs) { 11779 const ctx = this.ctx; 11780 const current = this.current; 11781 const font = current.font; 11782 const fontSize = current.fontSize; 11783 const fontDirection = current.fontDirection; 11784 const spacingDir = font.vertical ? 1 : -1; 11785 const charSpacing = current.charSpacing; 11786 const wordSpacing = current.wordSpacing; 11787 const textHScale = current.textHScale * fontDirection; 11788 const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; 11789 const glyphsLength = glyphs.length; 11790 const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE; 11791 let i, glyph, width, spacingLength; 11792 if (isTextInvisible || fontSize === 0) { 11793 return; 11794 } 11795 this._cachedScaleForStroking[0] = -1; 11796 this._cachedGetSinglePixelWidth = null; 11797 ctx.save(); 11798 if (current.textMatrix) { 11799 ctx.transform(...current.textMatrix); 11800 } 11801 ctx.translate(current.x, current.y + current.textRise); 11802 ctx.scale(textHScale, fontDirection); 11803 const dependencyTracker = this.dependencyTracker; 11804 this.dependencyTracker = dependencyTracker ? new CanvasNestedDependencyTracker(dependencyTracker, opIdx) : null; 11805 for (i = 0; i < glyphsLength; ++i) { 11806 glyph = glyphs[i];
vendor: 1,863 bytes, lines 11807-11851
11807 if (typeof glyph === "number") { 11808 spacingLength = spacingDir * glyph * fontSize / 1000; 11809 this.ctx.translate(spacingLength, 0); 11810 current.x += spacingLength * textHScale; 11811 continue; 11812 } 11813 const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; 11814 const operatorList = font.charProcOperatorList[glyph.operatorListId]; 11815 if (!operatorList) { 11816 warn(`Type3 character "${glyph.operatorListId}" is not available.`); 11817 } else if (this.contentVisible) { 11818 this.save(); 11819 ctx.scale(fontSize, fontSize); 11820 ctx.transform(...fontMatrix); 11821 this.executeOperatorList(operatorList); 11822 this.restore(); 11823 } 11824 const p = [glyph.width, 0]; 11825 Util.applyTransform(p, fontMatrix); 11826 width = p[0] * fontSize + spacing; 11827 ctx.translate(width, 0); 11828 current.x += width * textHScale; 11829 } 11830 ctx.restore(); 11831 if (dependencyTracker) { 11832 this.dependencyTracker = dependencyTracker; 11833 } 11834 } 11835 setCharWidth(opIdx, xWidth, yWidth) {} 11836 setCharWidthAndBounds(opIdx, xWidth, yWidth, llx, lly, urx, ury) { 11837 const clip = new Path2D(); 11838 clip.rect(llx, lly, urx - llx, ury - lly); 11839 this.ctx.clip(clip); 11840 this.dependencyTracker?.recordBBox(opIdx, this.ctx, llx, urx, lly, ury).recordClipBox(opIdx, this.ctx, llx, urx, lly, ury); 11841 this.endPath(opIdx); 11842 } 11843 getColorN_Pattern(opIdx, IR) { 11844 let pattern; 11845 if (IR[0] === "TilingPattern") { 11846 const baseTransform = this.baseTransform || getCurrentTransform(this.ctx); 11847 const canvasGraphicsFactory = { 11848 createCanvasGraphics: (ctx, renderingOpIdx) => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { 11849 optionalContentConfig: this.optionalContentConfig, 11850 markedContentStack: this.markedContentStack 11851 }, undefined, undefined
vendor: 16,709 bytes, lines 11851-12318
11851, this.dependencyTracker ? new CanvasNestedDependencyTracker(this.dependencyTracker, renderingOpIdx, true) : null) 11852 }; 11853 pattern = new TilingPattern(IR, this.ctx, canvasGraphicsFactory, baseTransform); 11854 } else { 11855 pattern = this._getPattern(opIdx, IR[1], IR[2]); 11856 } 11857 return pattern; 11858 } 11859 setStrokeColorN(opIdx, ...args) { 11860 this.dependencyTracker?.recordSimpleData("strokeColor", opIdx); 11861 this.current.strokeColor = this.getColorN_Pattern(opIdx, args); 11862 this.current.patternStroke = true; 11863 } 11864 setFillColorN(opIdx, ...args) { 11865 this.dependencyTracker?.recordSimpleData("fillColor", opIdx); 11866 this.current.fillColor = this.getColorN_Pattern(opIdx, args); 11867 this.current.patternFill = true; 11868 } 11869 setStrokeRGBColor(opIdx, color) { 11870 this.dependencyTracker?.recordSimpleData("strokeColor", opIdx); 11871 this.ctx.strokeStyle = this.current.strokeColor = color; 11872 this.current.patternStroke = false; 11873 } 11874 setStrokeTransparent(opIdx) { 11875 this.dependencyTracker?.recordSimpleData("strokeColor", opIdx); 11876 this.ctx.strokeStyle = this.current.strokeColor = "transparent"; 11877 this.current.patternStroke = false; 11878 } 11879 setFillRGBColor(opIdx, color) { 11880 this.dependencyTracker?.recordSimpleData("fillColor", opIdx); 11881 this.ctx.fillStyle = this.current.fillColor = color; 11882 this.current.patternFill = false; 11883 } 11884 setFillTransparent(opIdx) { 11885 this.dependencyTracker?.recordSimpleData("fillColor", opIdx); 11886 this.ctx.fillStyle = this.current.fillColor = "transparent"; 11887 this.current.patternFill = false; 11888 } 11889 _getPattern(opIdx, objId, matrix = null) { 11890 let pattern; 11891 if (this.cachedPatterns.has(objId)) { 11892 pattern = this.cachedPatterns.get(objId); 11893 } else { 11894 pattern = getShadingPattern(this.getObject(opIdx, objId)); 11895 this.cachedPatterns.set(objId, pattern); 11896 } 11897 if (matrix) { 11898 pattern.matrix = matrix; 11899 } 11900 return pattern; 11901 } 11902 shadingFill(opIdx, objId) { 11903 if (!this.contentVisible) { 11904 return; 11905 } 11906 const ctx = this.ctx; 11907 this.save(opIdx); 11908 const pattern = this._getPattern(opIdx, objId); 11909 ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING, opIdx); 11910 const inv = getCurrentTransformInverse(ctx); 11911 if (inv) { 11912 const { 11913 width, 11914 height 11915 } = ctx.canvas; 11916 const minMax = MIN_MAX_INIT.slice(); 11917 Util.axialAlignedBoundingBox([0, 0, width, height], inv, minMax); 11918 const [x0, y0, x1, y1] = minMax; 11919 this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0); 11920 } else { 11921 this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); 11922 } 11923 this.dependencyTracker?.resetBBox(opIdx).recordFullPageBBox(opIdx).recordDependencies(opIdx, Dependencies.transform).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx); 11924 this.compose(this.current.getClippedPathBoundingBox()); 11925 this.restore(opIdx); 11926 } 11927 beginInlineImage() { 11928 unreachable("Should not call beginInlineImage"); 11929 } 11930 beginImageData() { 11931 unreachable("Should not call beginImageData"); 11932 } 11933 paintFormXObjectBegin(opIdx, matrix, bbox) { 11934 if (!this.contentVisible) { 11935 return; 11936 } 11937 this.save(opIdx); 11938 this.baseTransformStack.push(this.baseTransform); 11939 if (matrix) { 11940 this.transform(opIdx, ...matrix); 11941 } 11942 this.baseTransform = getCurrentTransform(this.ctx); 11943 if (bbox) { 11944 Util.axialAlignedBoundingBox(bbox, this.baseTransform, this.current.minMax); 11945 const [x0, y0, x1, y1] = bbox; 11946 const clip = new Path2D(); 11947 clip.rect(x0, y0, x1 - x0, y1 - y0); 11948 this.ctx.clip(clip); 11949 this.dependencyTracker?.recordClipBox(opIdx, this.ctx, x0, x1, y0, y1); 11950 this.endPath(opIdx); 11951 } 11952 } 11953 paintFormXObjectEnd(opIdx) { 11954 if (!this.contentVisible) { 11955 return; 11956 } 11957 this.restore(opIdx); 11958 this.baseTransform = this.baseTransformStack.pop(); 11959 } 11960 beginGroup(opIdx, group) { 11961 if (!this.contentVisible) { 11962 return; 11963 } 11964 this.save(opIdx); 11965 if (this.inSMaskMode) { 11966 this.endSMaskMode(); 11967 this.current.activeSMask = null; 11968 } 11969 const currentCtx = this.ctx; 11970 if (!group.isolated) { 11971 info("TODO: Support non-isolated groups."); 11972 } 11973 if (group.knockout) { 11974 warn("Knockout groups not supported."); 11975 } 11976 const currentTransform = getCurrentTransform(currentCtx); 11977 if (group.matrix) { 11978 currentCtx.transform(...group.matrix); 11979 } 11980 if (!group.bbox) { 11981 throw new Error("Bounding box is required."); 11982 } 11983 let bounds = MIN_MAX_INIT.slice(); 11984 Util.axialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx), bounds); 11985 const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height]; 11986 bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; 11987 const offsetX = Math.floor(bounds[0]); 11988 const offsetY = Math.floor(bounds[1]); 11989 const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1); 11990 const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1); 11991 this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]); 11992 let cacheId = "groupAt" + this.groupLevel; 11993 if (group.smask) { 11994 cacheId += "_smask_" + this.smaskCounter++ % 2; 11995 } 11996 const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight); 11997 const groupCtx = scratchCanvas.context; 11998 groupCtx.translate(-offsetX, -offsetY); 11999 groupCtx.transform(...currentTransform); 12000 let clip = new Path2D(); 12001 const [x0, y0, x1, y1] = group.bbox; 12002 clip.rect(x0, y0, x1 - x0, y1 - y0); 12003 if (group.matrix) { 12004 const path = new Path2D(); 12005 path.addPath(clip, new DOMMatrix(group.matrix)); 12006 clip = path; 12007 } 12008 groupCtx.clip(clip); 12009 if (group.smask) { 12010 this.smaskStack.push({ 12011 canvas: scratchCanvas.canvas, 12012 context: groupCtx, 12013 offsetX, 12014 offsetY, 12015 subtype: group.smask.subtype, 12016 backdrop: group.smask.backdrop, 12017 transferMap: group.smask.transferMap || null, 12018 startTransformInverse: null 12019 }); 12020 } 12021 if (!group.smask || this.dependencyTracker) { 12022 currentCtx.setTransform(1, 0, 0, 1, 0, 0); 12023 currentCtx.translate(offsetX, offsetY); 12024 currentCtx.save(); 12025 } 12026 copyCtxState(currentCtx, groupCtx); 12027 this.ctx = groupCtx; 12028 this.dependencyTracker?.inheritSimpleDataAsFutureForcedDependencies(["fillAlpha", "strokeAlpha", "globalCompositeOperation"]).pushBaseTransform(currentCtx); 12029 this.setGState(opIdx, [["BM", "source-over"], ["ca", 1], ["CA", 1]]); 12030 this.groupStack.push(currentCtx); 12031 this.groupLevel++; 12032 } 12033 endGroup(opIdx, group) { 12034 if (!this.contentVisible) { 12035 return; 12036 } 12037 this.groupLevel--; 12038 const groupCtx = this.ctx; 12039 const ctx = this.groupStack.pop(); 12040 this.ctx = ctx; 12041 this.ctx.imageSmoothingEnabled = false; 12042 this.dependencyTracker?.popBaseTransform(); 12043 if (group.smask) { 12044 this.tempSMask = this.smaskStack.pop(); 12045 this.restore(opIdx); 12046 if (this.dependencyTracker) { 12047 this.ctx.restore(); 12048 } 12049 } else { 12050 this.ctx.restore(); 12051 const currentMtx = getCurrentTransform(this.ctx); 12052 this.restore(opIdx); 12053 this.ctx.save(); 12054 this.ctx.setTransform(...currentMtx); 12055 const dirtyBox = MIN_MAX_INIT.slice(); 12056 Util.axialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx, dirtyBox); 12057 this.ctx.drawImage(groupCtx.canvas, 0, 0); 12058 this.ctx.restore(); 12059 this.compose(dirtyBox); 12060 } 12061 } 12062 beginAnnotation(opIdx, id, rect, transform, matrix, hasOwnCanvas) { 12063 this.#restoreInitialState(); 12064 resetCtxToDefault(this.ctx); 12065 this.ctx.save(); 12066 this.save(opIdx); 12067 if (this.baseTransform) { 12068 this.ctx.setTransform(...this.baseTransform); 12069 } 12070 if (rect) { 12071 const width = rect[2] - rect[0]; 12072 const height = rect[3] - rect[1]; 12073 if (hasOwnCanvas && this.annotationCanvasMap) { 12074 transform = transform.slice(); 12075 transform[4] -= rect[0]; 12076 transform[5] -= rect[1]; 12077 rect = rect.slice(); 12078 rect[0] = rect[1] = 0; 12079 rect[2] = width; 12080 rect[3] = height; 12081 Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx), XY); 12082 const { 12083 viewportScale 12084 } = this; 12085 const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale); 12086 const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale); 12087 this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight); 12088 const { 12089 canvas, 12090 context 12091 } = this.annotationCanvas; 12092 this.annotationCanvasMap.set(id, canvas); 12093 this.annotationCanvas.savedCtx = this.ctx; 12094 this.ctx = context; 12095 this.ctx.save(); 12096 this.ctx.setTransform(XY[0], 0, 0, -XY[1], 0, height * XY[1]); 12097 resetCtxToDefault(this.ctx); 12098 } else { 12099 resetCtxToDefault(this.ctx); 12100 this.endPath(opIdx); 12101 const clip = new Path2D(); 12102 clip.rect(rect[0], rect[1], width, height); 12103 this.ctx.clip(clip); 12104 } 12105 } 12106 this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height); 12107 this.transform(opIdx, ...transform); 12108 this.transform(opIdx, ...matrix); 12109 } 12110 endAnnotation(opIdx) { 12111 if (this.annotationCanvas) { 12112 this.ctx.restore(); 12113 this.#drawFilter(); 12114 this.ctx = this.annotationCanvas.savedCtx; 12115 delete this.annotationCanvas.savedCtx; 12116 delete this.annotationCanvas; 12117 } 12118 } 12119 paintImageMaskXObject(opIdx, img) { 12120 if (!this.contentVisible) { 12121 return; 12122 } 12123 const count = img.count; 12124 img = this.getObject(opIdx, img.data, img); 12125 img.count = count; 12126 const ctx = this.ctx; 12127 const mask = this._createMaskCanvas(opIdx, img); 12128 const maskCanvas = mask.canvas; 12129 ctx.save(); 12130 ctx.setTransform(1, 0, 0, 1, 0, 0); 12131 ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY); 12132 this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, mask.offsetX, mask.offsetX + maskCanvas.width, mask.offsetY, mask.offsetY + maskCanvas.height).recordOperation(opIdx); 12133 ctx.restore(); 12134 this.compose(); 12135 } 12136 paintImageMaskXObjectRepeat(opIdx, img, scaleX, skewX = 0, skewY = 0, scaleY, positions) { 12137 if (!this.contentVisible) { 12138 return; 12139 } 12140 img = this.getObject(opIdx, img.data, img); 12141 const ctx = this.ctx; 12142 ctx.save(); 12143 const currentTransform = getCurrentTransform(ctx); 12144 ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0); 12145 const mask = this._createMaskCanvas(opIdx, img); 12146 ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]); 12147 this.dependencyTracker?.resetBBox(opIdx); 12148 for (let i = 0, ii = positions.length; i < ii; i += 2) { 12149 const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]); 12150 ctx.drawImage(mask.canvas, trans[4], trans[5]); 12151 this.dependencyTracker?.recordBBox(opIdx, this.ctx, trans[4], trans[4] + mask.canvas.width, trans[5], trans[5] + mask.canvas.height); 12152 } 12153 ctx.restore(); 12154 this.compose(); 12155 this.dependencyTracker?.recordOperation(opIdx); 12156 } 12157 paintImageMaskXObjectGroup(opIdx, images) { 12158 if (!this.contentVisible) { 12159 return; 12160 } 12161 const ctx = this.ctx; 12162 const fillColor = this.current.fillColor; 12163 const isPatternFill = this.current.patternFill; 12164 this.dependencyTracker?.resetBBox(opIdx).recordDependencies(opIdx, Dependencies.transformAndFill); 12165 for (const image of images) { 12166 const { 12167 data, 12168 width, 12169 height, 12170 transform 12171 } = image; 12172 const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height); 12173 const maskCtx = maskCanvas.context; 12174 maskCtx.save(); 12175 const img = this.getObject(opIdx, data, image); 12176 putBinaryImageMask(maskCtx, img); 12177 maskCtx.globalCompositeOperation = "source-in"; 12178 maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx) : fillColor; 12179 maskCtx.fillRect(0, 0, width, height); 12180 maskCtx.restore(); 12181 ctx.save(); 12182 ctx.transform(...transform); 12183 ctx.scale(1, -1); 12184 drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); 12185 this.dependencyTracker?.recordBBox(opIdx, ctx, 0, width, 0, height); 12186 ctx.restore(); 12187 } 12188 this.compose(); 12189 this.dependencyTracker?.recordOperation(opIdx); 12190 } 12191 paintImageXObject(opIdx, objId) { 12192 if (!this.contentVisible) { 12193 return; 12194 } 12195 const imgData = this.getObject(opIdx, objId); 12196 if (!imgData) { 12197 warn("Dependent image isn't ready yet"); 12198 return; 12199 } 12200 this.paintInlineImageXObject(opIdx, imgData); 12201 } 12202 paintImageXObjectRepeat(opIdx, objId, scaleX, scaleY, positions) { 12203 if (!this.contentVisible) { 12204 return; 12205 } 12206 const imgData = this.getObject(opIdx, objId); 12207 if (!imgData) { 12208 warn("Dependent image isn't ready yet"); 12209 return; 12210 } 12211 const width = imgData.width; 12212 const height = imgData.height; 12213 const map = []; 12214 for (let i = 0, ii = positions.length; i < ii; i += 2) { 12215 map.push({ 12216 transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]], 12217 x: 0, 12218 y: 0, 12219 w: width, 12220 h: height 12221 }); 12222 } 12223 this.paintInlineImageXObjectGroup(opIdx, imgData, map); 12224 } 12225 applyTransferMapsToCanvas(ctx) { 12226 if (this.current.transferMaps !== "none") { 12227 ctx.filter = this.current.transferMaps; 12228 ctx.drawImage(ctx.canvas, 0, 0); 12229 ctx.filter = "none"; 12230 } 12231 return ctx.canvas; 12232 } 12233 applyTransferMapsToBitmap(imgData) { 12234 if (this.current.transferMaps === "none") { 12235 return imgData.bitmap; 12236 } 12237 const { 12238 bitmap, 12239 width, 12240 height 12241 } = imgData; 12242 const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); 12243 const tmpCtx = tmpCanvas.context; 12244 tmpCtx.filter = this.current.transferMaps; 12245 tmpCtx.drawImage(bitmap, 0, 0); 12246 tmpCtx.filter = "none"; 12247 return tmpCanvas.canvas; 12248 } 12249 paintInlineImageXObject(opIdx, imgData) { 12250 if (!this.contentVisible) { 12251 return; 12252 } 12253 const width = imgData.width; 12254 const height = imgData.height; 12255 const ctx = this.ctx; 12256 this.save(opIdx); 12257 const { 12258 filter 12259 } = ctx; 12260 if (filter !== "none" && filter !== "") { 12261 ctx.filter = "none"; 12262 } 12263 ctx.scale(1 / width, -1 / height); 12264 let imgToPaint; 12265 if (imgData.bitmap) { 12266 imgToPaint = this.applyTransferMapsToBitmap(imgData); 12267 } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) { 12268 imgToPaint = imgData; 12269 } else { 12270 const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height); 12271 const tmpCtx = tmpCanvas.context; 12272 putBinaryImageData(tmpCtx, imgData); 12273 imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); 12274 } 12275 const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx)); 12276 ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate); 12277 this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, ctx, 0, width, -height, 0).recordDependencies(opIdx, Dependencies.imageXObject).recordOperation(opIdx); 12278 drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height); 12279 this.compose(); 12280 this.restore(opIdx); 12281 } 12282 paintInlineImageXObjectGroup(opIdx, imgData, map) { 12283 if (!this.contentVisible) { 12284 return; 12285 } 12286 const ctx = this.ctx; 12287 let imgToPaint; 12288 if (imgData.bitmap) { 12289 imgToPaint = imgData.bitmap; 12290 } else { 12291 const w = imgData.width; 12292 const h = imgData.height; 12293 const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h); 12294 const tmpCtx = tmpCanvas.context; 12295 putBinaryImageData(tmpCtx, imgData); 12296 imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); 12297 } 12298 this.dependencyTracker?.resetBBox(opIdx); 12299 for (const entry of map) { 12300 ctx.save(); 12301 ctx.transform(...entry.transform); 12302 ctx.scale(1, -1); 12303 drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); 12304 this.dependencyTracker?.recordBBox(opIdx, ctx, 0, 1, -1, 0); 12305 ctx.restore(); 12306 } 12307 this.dependencyTracker?.recordOperation(opIdx); 12308 this.compose(); 12309 } 12310 paintSolidColorImageMask(opIdx) { 12311 if (!this.contentVisible) { 12312 return; 12313 } 12314 this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, 0, 1, 0, 1).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx); 12315 this.ctx.fillRect(0, 0, 1, 1); 12316 this.compose(); 12317 } 12318 markPoint(opIdx, tag) {
vendor: 29,675 bytes, lines 12318-13409
12318} 12319 markPointProps(opIdx, tag, properties) {} 12320 beginMarkedContent(opIdx, tag) { 12321 this.dependencyTracker?.beginMarkedContent(opIdx); 12322 this.markedContentStack.push({ 12323 visible: true 12324 }); 12325 } 12326 beginMarkedContentProps(opIdx, tag, properties) { 12327 this.dependencyTracker?.beginMarkedContent(opIdx); 12328 if (tag === "OC") { 12329 this.markedContentStack.push({ 12330 visible: this.optionalContentConfig.isVisible(properties) 12331 }); 12332 } else { 12333 this.markedContentStack.push({ 12334 visible: true 12335 }); 12336 } 12337 this.contentVisible = this.isContentVisible(); 12338 } 12339 endMarkedContent(opIdx) { 12340 this.dependencyTracker?.endMarkedContent(opIdx); 12341 this.markedContentStack.pop(); 12342 this.contentVisible = this.isContentVisible(); 12343 } 12344 beginCompat(opIdx) {} 12345 endCompat(opIdx) {} 12346 consumePath(opIdx, path, clipBox) { 12347 const isEmpty = this.current.isEmptyClip(); 12348 if (this.pendingClip) { 12349 this.current.updateClipFromPath(); 12350 } 12351 if (!this.pendingClip) { 12352 this.compose(clipBox); 12353 } 12354 const ctx = this.ctx; 12355 if (this.pendingClip) { 12356 if (!isEmpty) { 12357 if (this.pendingClip === EO_CLIP) { 12358 ctx.clip(path, "evenodd"); 12359 } else { 12360 ctx.clip(path); 12361 } 12362 } 12363 this.pendingClip = null; 12364 this.dependencyTracker?.bboxToClipBoxDropOperation(opIdx).recordFutureForcedDependency("clipPath", opIdx); 12365 } else { 12366 this.dependencyTracker?.recordOperation(opIdx); 12367 } 12368 this.current.startNewPathAndClipBox(this.current.clipBox); 12369 } 12370 getSinglePixelWidth() { 12371 if (!this._cachedGetSinglePixelWidth) { 12372 const m = getCurrentTransform(this.ctx); 12373 if (m[1] === 0 && m[2] === 0) { 12374 this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3])); 12375 } else { 12376 const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]); 12377 const normX = Math.hypot(m[0], m[2]); 12378 const normY = Math.hypot(m[1], m[3]); 12379 this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet; 12380 } 12381 } 12382 return this._cachedGetSinglePixelWidth; 12383 } 12384 getScaleForStroking() { 12385 if (this._cachedScaleForStroking[0] === -1) { 12386 const { 12387 lineWidth 12388 } = this.current; 12389 const { 12390 a, 12391 b, 12392 c, 12393 d 12394 } = this.ctx.getTransform(); 12395 let scaleX, scaleY; 12396 if (b === 0 && c === 0) { 12397 const normX = Math.abs(a); 12398 const normY = Math.abs(d); 12399 if (normX === normY) { 12400 if (lineWidth === 0) { 12401 scaleX = scaleY = 1 / normX; 12402 } else { 12403 const scaledLineWidth = normX * lineWidth; 12404 scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1; 12405 } 12406 } else if (lineWidth === 0) { 12407 scaleX = 1 / normX; 12408 scaleY = 1 / normY; 12409 } else { 12410 const scaledXLineWidth = normX * lineWidth; 12411 const scaledYLineWidth = normY * lineWidth; 12412 scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1; 12413 scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1; 12414 } 12415 } else { 12416 const absDet = Math.abs(a * d - b * c); 12417 const normX = Math.hypot(a, b); 12418 const normY = Math.hypot(c, d); 12419 if (lineWidth === 0) { 12420 scaleX = normY / absDet; 12421 scaleY = normX / absDet; 12422 } else { 12423 const baseArea = lineWidth * absDet; 12424 scaleX = normY > baseArea ? normY / baseArea : 1; 12425 scaleY = normX > baseArea ? normX / baseArea : 1; 12426 } 12427 } 12428 this._cachedScaleForStroking[0] = scaleX; 12429 this._cachedScaleForStroking[1] = scaleY; 12430 } 12431 return this._cachedScaleForStroking; 12432 } 12433 rescaleAndStroke(path, saveRestore) { 12434 const { 12435 ctx, 12436 current: { 12437 lineWidth 12438 } 12439 } = this; 12440 const [scaleX, scaleY] = this.getScaleForStroking(); 12441 if (scaleX === scaleY) { 12442 ctx.lineWidth = (lineWidth || 1) * scaleX; 12443 ctx.stroke(path); 12444 return; 12445 } 12446 const dashes = ctx.getLineDash(); 12447 if (saveRestore) { 12448 ctx.save(); 12449 } 12450 ctx.scale(scaleX, scaleY); 12451 SCALE_MATRIX.a = 1 / scaleX; 12452 SCALE_MATRIX.d = 1 / scaleY; 12453 const newPath = new Path2D(); 12454 newPath.addPath(path, SCALE_MATRIX); 12455 if (dashes.length > 0) { 12456 const scale = Math.max(scaleX, scaleY); 12457 ctx.setLineDash(dashes.map(x => x / scale)); 12458 ctx.lineDashOffset /= scale; 12459 } 12460 ctx.lineWidth = lineWidth || 1; 12461 ctx.stroke(newPath); 12462 if (saveRestore) { 12463 ctx.restore(); 12464 } 12465 } 12466 isContentVisible() { 12467 for (let i = this.markedContentStack.length - 1; i >= 0; i--) { 12468 if (!this.markedContentStack[i].visible) { 12469 return false; 12470 } 12471 } 12472 return true; 12473 } 12474} 12475for (const op in OPS) { 12476 if (CanvasGraphics.prototype[op] !== undefined) { 12477 CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; 12478 } 12479} 12480 12481;// ./src/display/worker_options.js 12482class GlobalWorkerOptions { 12483 static #port = null; 12484 static #src = ""; 12485 static get workerPort() { 12486 return this.#port; 12487 } 12488 static set workerPort(val) { 12489 if (!(typeof Worker !== "undefined" && val instanceof Worker) && val !== null) { 12490 throw new Error("Invalid `workerPort` type."); 12491 } 12492 this.#port = val; 12493 } 12494 static get workerSrc() { 12495 return this.#src; 12496 } 12497 static set workerSrc(val) { 12498 if (typeof val !== "string") { 12499 throw new Error("Invalid `workerSrc` type."); 12500 } 12501 this.#src = val; 12502 } 12503} 12504 12505;// ./src/display/metadata.js 12506class Metadata { 12507 #map; 12508 #data; 12509 constructor({ 12510 parsedData, 12511 rawData 12512 }) { 12513 this.#map = parsedData; 12514 this.#data = rawData; 12515 } 12516 getRaw() { 12517 return this.#data; 12518 } 12519 get(name) { 12520 return this.#map.get(name) ?? null; 12521 } 12522 [Symbol.iterator]() { 12523 return this.#map.entries(); 12524 } 12525} 12526 12527;// ./src/display/optional_content_config.js 12528 12529 12530const INTERNAL = Symbol("INTERNAL"); 12531class OptionalContentGroup { 12532 #isDisplay = false; 12533 #isPrint = false; 12534 #userSet = false; 12535 #visible = true; 12536 constructor(renderingIntent, { 12537 name, 12538 intent, 12539 usage, 12540 rbGroups 12541 }) { 12542 this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY); 12543 this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); 12544 this.name = name; 12545 this.intent = intent; 12546 this.usage = usage; 12547 this.rbGroups = rbGroups; 12548 } 12549 get visible() { 12550 if (this.#userSet) { 12551 return this.#visible; 12552 } 12553 if (!this.#visible) { 12554 return false; 12555 } 12556 const { 12557 print, 12558 view 12559 } = this.usage; 12560 if (this.#isDisplay) { 12561 return view?.viewState !== "OFF"; 12562 } else if (this.#isPrint) { 12563 return print?.printState !== "OFF"; 12564 } 12565 return true; 12566 } 12567 _setVisible(internal, visible, userSet = false) { 12568 if (internal !== INTERNAL) { 12569 unreachable("Internal method `_setVisible` called."); 12570 } 12571 this.#userSet = userSet; 12572 this.#visible = visible; 12573 } 12574} 12575class OptionalContentConfig { 12576 #cachedGetHash = null; 12577 #groups = new Map(); 12578 #initialHash = null; 12579 #order = null; 12580 constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) { 12581 this.renderingIntent = renderingIntent; 12582 this.name = null; 12583 this.creator = null; 12584 if (data === null) { 12585 return; 12586 } 12587 this.name = data.name; 12588 this.creator = data.creator; 12589 this.#order = data.order; 12590 for (const group of data.groups) { 12591 this.#groups.set(group.id, new OptionalContentGroup(renderingIntent, group)); 12592 } 12593 if (data.baseState === "OFF") { 12594 for (const group of this.#groups.values()) { 12595 group._setVisible(INTERNAL, false); 12596 } 12597 } 12598 for (const on of data.on) { 12599 this.#groups.get(on)._setVisible(INTERNAL, true); 12600 } 12601 for (const off of data.off) { 12602 this.#groups.get(off)._setVisible(INTERNAL, false); 12603 } 12604 this.#initialHash = this.getHash(); 12605 } 12606 #evaluateVisibilityExpression(array) { 12607 const length = array.length; 12608 if (length < 2) { 12609 return true; 12610 } 12611 const operator = array[0]; 12612 for (let i = 1; i < length; i++) { 12613 const element = array[i]; 12614 let state; 12615 if (Array.isArray(element)) { 12616 state = this.#evaluateVisibilityExpression(element); 12617 } else if (this.#groups.has(element)) { 12618 state = this.#groups.get(element).visible; 12619 } else { 12620 warn(`Optional content group not found: ${element}`); 12621 return true; 12622 } 12623 switch (operator) { 12624 case "And": 12625 if (!state) { 12626 return false; 12627 } 12628 break; 12629 case "Or": 12630 if (state) { 12631 return true; 12632 } 12633 break; 12634 case "Not": 12635 return !state; 12636 default: 12637 return true; 12638 } 12639 } 12640 return operator === "And"; 12641 } 12642 isVisible(group) { 12643 if (this.#groups.size === 0) { 12644 return true; 12645 } 12646 if (!group) { 12647 info("Optional content group not defined."); 12648 return true; 12649 } 12650 if (group.type === "OCG") { 12651 if (!this.#groups.has(group.id)) { 12652 warn(`Optional content group not found: ${group.id}`); 12653 return true; 12654 } 12655 return this.#groups.get(group.id).visible; 12656 } else if (group.type === "OCMD") { 12657 if (group.expression) { 12658 return this.#evaluateVisibilityExpression(group.expression); 12659 } 12660 if (!group.policy || group.policy === "AnyOn") { 12661 for (const id of group.ids) { 12662 if (!this.#groups.has(id)) { 12663 warn(`Optional content group not found: ${id}`); 12664 return true; 12665 } 12666 if (this.#groups.get(id).visible) { 12667 return true; 12668 } 12669 } 12670 return false; 12671 } else if (group.policy === "AllOn") { 12672 for (const id of group.ids) { 12673 if (!this.#groups.has(id)) { 12674 warn(`Optional content group not found: ${id}`); 12675 return true; 12676 } 12677 if (!this.#groups.get(id).visible) { 12678 return false; 12679 } 12680 } 12681 return true; 12682 } else if (group.policy === "AnyOff") { 12683 for (const id of group.ids) { 12684 if (!this.#groups.has(id)) { 12685 warn(`Optional content group not found: ${id}`); 12686 return true; 12687 } 12688 if (!this.#groups.get(id).visible) { 12689 return true; 12690 } 12691 } 12692 return false; 12693 } else if (group.policy === "AllOff") { 12694 for (const id of group.ids) { 12695 if (!this.#groups.has(id)) { 12696 warn(`Optional content group not found: ${id}`); 12697 return true; 12698 } 12699 if (this.#groups.get(id).visible) { 12700 return false; 12701 } 12702 } 12703 return true; 12704 } 12705 warn(`Unknown optional content policy ${group.policy}.`); 12706 return true; 12707 } 12708 warn(`Unknown group type ${group.type}.`); 12709 return true; 12710 } 12711 setVisibility(id, visible = true, preserveRB = true) { 12712 const group = this.#groups.get(id); 12713 if (!group) { 12714 warn(`Optional content group not found: ${id}`); 12715 return; 12716 } 12717 if (preserveRB && visible && group.rbGroups.length) { 12718 for (const rbGroup of group.rbGroups) { 12719 for (const otherId of rbGroup) { 12720 if (otherId !== id) { 12721 this.#groups.get(otherId)?._setVisible(INTERNAL, false, true); 12722 } 12723 } 12724 } 12725 } 12726 group._setVisible(INTERNAL, !!visible, true); 12727 this.#cachedGetHash = null; 12728 } 12729 setOCGState({ 12730 state, 12731 preserveRB 12732 }) { 12733 let operator; 12734 for (const elem of state) { 12735 switch (elem) { 12736 case "ON": 12737 case "OFF": 12738 case "Toggle": 12739 operator = elem; 12740 continue; 12741 } 12742 const group = this.#groups.get(elem); 12743 if (!group) { 12744 continue; 12745 } 12746 switch (operator) { 12747 case "ON": 12748 this.setVisibility(elem, true, preserveRB); 12749 break; 12750 case "OFF": 12751 this.setVisibility(elem, false, preserveRB); 12752 break; 12753 case "Toggle": 12754 this.setVisibility(elem, !group.visible, preserveRB); 12755 break; 12756 } 12757 } 12758 this.#cachedGetHash = null; 12759 } 12760 get hasInitialVisibility() { 12761 return this.#initialHash === null || this.getHash() === this.#initialHash; 12762 } 12763 getOrder() { 12764 if (!this.#groups.size) { 12765 return null; 12766 } 12767 if (this.#order) { 12768 return this.#order.slice(); 12769 } 12770 return [...this.#groups.keys()]; 12771 } 12772 getGroup(id) { 12773 return this.#groups.get(id) || null; 12774 } 12775 getHash() { 12776 if (this.#cachedGetHash !== null) { 12777 return this.#cachedGetHash; 12778 } 12779 const hash = new MurmurHash3_64(); 12780 for (const [id, group] of this.#groups) { 12781 hash.update(`${id}:${group.visible}`); 12782 } 12783 return this.#cachedGetHash = hash.hexdigest(); 12784 } 12785 [Symbol.iterator]() { 12786 return this.#groups.entries(); 12787 } 12788} 12789 12790;// ./src/shared/base_pdf_stream.js 12791 12792class BasePDFStream { 12793 #PDFStreamReader = null; 12794 #PDFStreamRangeReader = null; 12795 _fullReader = null; 12796 _rangeReaders = new Set(); 12797 _source = null; 12798 constructor(source, PDFStreamReader, PDFStreamRangeReader) { 12799 this._source = source; 12800 this.#PDFStreamReader = PDFStreamReader; 12801 this.#PDFStreamRangeReader = PDFStreamRangeReader; 12802 } 12803 get _progressiveDataLength() { 12804 return this._fullReader?._loaded ?? 0; 12805 } 12806 getFullReader() { 12807 assert(!this._fullReader, "BasePDFStream.getFullReader can only be called once."); 12808 return this._fullReader = new this.#PDFStreamReader(this); 12809 } 12810 getRangeReader(begin, end) { 12811 if (end <= this._progressiveDataLength) { 12812 return null; 12813 } 12814 const reader = new this.#PDFStreamRangeReader(this, begin, end); 12815 this._rangeReaders.add(reader); 12816 return reader; 12817 } 12818 cancelAllRequests(reason) { 12819 this._fullReader?.cancel(reason); 12820 for (const reader of new Set(this._rangeReaders)) { 12821 reader.cancel(reason); 12822 } 12823 } 12824} 12825class BasePDFStreamReader { 12826 onProgress = null; 12827 _contentLength = 0; 12828 _filename = null; 12829 _headersCapability = Promise.withResolvers(); 12830 _isRangeSupported = false; 12831 _isStreamingSupported = false; 12832 _loaded = 0; 12833 _stream = null; 12834 constructor(stream) { 12835 this._stream = stream; 12836 } 12837 get headersReady() { 12838 return this._headersCapability.promise; 12839 } 12840 get filename() { 12841 return this._filename; 12842 } 12843 get contentLength() { 12844 return this._contentLength; 12845 } 12846 get isRangeSupported() { 12847 return this._isRangeSupported; 12848 } 12849 get isStreamingSupported() { 12850 return this._isStreamingSupported; 12851 } 12852 async read() { 12853 unreachable("Abstract method `read` called"); 12854 } 12855 cancel(reason) { 12856 unreachable("Abstract method `cancel` called"); 12857 } 12858} 12859class BasePDFStreamRangeReader { 12860 _stream = null; 12861 constructor(stream, begin, end) { 12862 this._stream = stream; 12863 } 12864 async read() { 12865 unreachable("Abstract method `read` called"); 12866 } 12867 cancel(reason) { 12868 unreachable("Abstract method `cancel` called"); 12869 } 12870} 12871 12872;// ./src/display/transport_stream.js 12873 12874 12875 12876function getArrayBuffer(val) { 12877 return val instanceof Uint8Array && val.byteLength === val.buffer.byteLength ? val.buffer : new Uint8Array(val).buffer; 12878} 12879class PDFDataTransportStream extends BasePDFStream { 12880 _progressiveDone = false; 12881 _queuedChunks = []; 12882 constructor(source) { 12883 super(source, PDFDataTransportStreamReader, PDFDataTransportStreamRangeReader); 12884 const { 12885 pdfDataRangeTransport 12886 } = source; 12887 const { 12888 initialData, 12889 progressiveDone 12890 } = pdfDataRangeTransport; 12891 if (initialData?.length > 0) { 12892 const buffer = getArrayBuffer(initialData); 12893 this._queuedChunks.push(buffer); 12894 } 12895 this._progressiveDone = progressiveDone; 12896 pdfDataRangeTransport.addRangeListener((begin, chunk) => { 12897 this.#onReceiveData(begin, chunk); 12898 }); 12899 pdfDataRangeTransport.addProgressListener((loaded, total) => { 12900 if (total !== undefined) { 12901 this._fullReader?.onProgress?.({ 12902 loaded, 12903 total 12904 }); 12905 } 12906 }); 12907 pdfDataRangeTransport.addProgressiveReadListener(chunk => { 12908 this.#onReceiveData(undefined, chunk); 12909 }); 12910 pdfDataRangeTransport.addProgressiveDoneListener(() => { 12911 this._fullReader?.progressiveDone(); 12912 this._progressiveDone = true; 12913 }); 12914 pdfDataRangeTransport.transportReady(); 12915 } 12916 #onReceiveData(begin, chunk) { 12917 const buffer = getArrayBuffer(chunk); 12918 if (begin === undefined) { 12919 if (this._fullReader) { 12920 this._fullReader._enqueue(buffer); 12921 } else { 12922 this._queuedChunks.push(buffer); 12923 } 12924 } else { 12925 const rangeReader = this._rangeReaders.keys().find(r => r._begin === begin); 12926 assert(rangeReader, "#onReceiveData - no `PDFDataTransportStreamRangeReader` instance found."); 12927 rangeReader._enqueue(buffer); 12928 } 12929 } 12930 getFullReader() { 12931 const reader = super.getFullReader(); 12932 this._queuedChunks = null; 12933 return reader; 12934 } 12935 getRangeReader(begin, end) { 12936 const reader = super.getRangeReader(begin, end); 12937 if (reader) { 12938 reader.onDone = () => this._rangeReaders.delete(reader); 12939 this._source.pdfDataRangeTransport.requestDataRange(begin, end); 12940 } 12941 return reader; 12942 } 12943 cancelAllRequests(reason) { 12944 super.cancelAllRequests(reason); 12945 this._source.pdfDataRangeTransport.abort(); 12946 } 12947} 12948class PDFDataTransportStreamReader extends BasePDFStreamReader { 12949 _done = false; 12950 _queuedChunks = null; 12951 _requests = []; 12952 constructor(stream) { 12953 super(stream); 12954 const { 12955 pdfDataRangeTransport, 12956 disableRange, 12957 disableStream 12958 } = stream._source; 12959 const { 12960 length, 12961 contentDispositionFilename 12962 } = pdfDataRangeTransport; 12963 this._queuedChunks = stream._queuedChunks || []; 12964 for (const chunk of this._queuedChunks) { 12965 this._loaded += chunk.byteLength; 12966 } 12967 this._done = stream._progressiveDone; 12968 this._contentLength = length; 12969 this._isStreamingSupported = !disableStream; 12970 this._isRangeSupported = !disableRange; 12971 if (isPdfFile(contentDispositionFilename)) { 12972 this._filename = contentDispositionFilename; 12973 } 12974 this._headersCapability.resolve(); 12975 } 12976 _enqueue(chunk) { 12977 if (this._done) { 12978 return; 12979 } 12980 if (this._requests.length > 0) { 12981 const capability = this._requests.shift(); 12982 capability.resolve({ 12983 value: chunk, 12984 done: false 12985 }); 12986 } else { 12987 this._queuedChunks.push(chunk); 12988 } 12989 this._loaded += chunk.byteLength; 12990 } 12991 async read() { 12992 if (this._queuedChunks.length > 0) { 12993 const chunk = this._queuedChunks.shift(); 12994 return { 12995 value: chunk, 12996 done: false 12997 }; 12998 } 12999 if (this._done) { 13000 return { 13001 value: undefined, 13002 done: true 13003 }; 13004 } 13005 const capability = Promise.withResolvers(); 13006 this._requests.push(capability); 13007 return capability.promise; 13008 } 13009 cancel(reason) { 13010 this._done = true; 13011 for (const capability of this._requests) { 13012 capability.resolve({ 13013 value: undefined, 13014 done: true 13015 }); 13016 } 13017 this._requests.length = 0; 13018 } 13019 progressiveDone() { 13020 this._done ||= true; 13021 } 13022} 13023class PDFDataTransportStreamRangeReader extends BasePDFStreamRangeReader { 13024 onDone = null; 13025 _begin = -1; 13026 _done = false; 13027 _queuedChunk = null; 13028 _requests = []; 13029 constructor(stream, begin, end) { 13030 super(stream, begin, end); 13031 this._begin = begin; 13032 } 13033 _enqueue(chunk) { 13034 if (this._done) { 13035 return; 13036 } 13037 if (this._requests.length === 0) { 13038 this._queuedChunk = chunk; 13039 } else { 13040 const firstCapability = this._requests.shift(); 13041 firstCapability.resolve({ 13042 value: chunk, 13043 done: false 13044 }); 13045 for (const capability of this._requests) { 13046 capability.resolve({ 13047 value: undefined, 13048 done: true 13049 }); 13050 } 13051 this._requests.length = 0; 13052 } 13053 this._done = true; 13054 this.onDone?.(); 13055 } 13056 async read() { 13057 if (this._queuedChunk) { 13058 const chunk = this._queuedChunk; 13059 this._queuedChunk = null; 13060 return { 13061 value: chunk, 13062 done: false 13063 }; 13064 } 13065 if (this._done) { 13066 return { 13067 value: undefined, 13068 done: true 13069 }; 13070 } 13071 const capability = Promise.withResolvers(); 13072 this._requests.push(capability); 13073 return capability.promise; 13074 } 13075 cancel(reason) { 13076 this._done = true; 13077 for (const capability of this._requests) { 13078 capability.resolve({ 13079 value: undefined, 13080 done: true 13081 }); 13082 } 13083 this._requests.length = 0; 13084 this.onDone?.(); 13085 } 13086} 13087 13088;// ./src/display/content_disposition.js 13089 13090function getFilenameFromContentDispositionHeader(contentDisposition) { 13091 let needsEncodingFixup = true; 13092 let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition); 13093 if (tmp) { 13094 tmp = tmp[1]; 13095 let filename = rfc2616unquote(tmp); 13096 filename = unescape(filename); 13097 filename = rfc5987decode(filename); 13098 filename = rfc2047decode(filename); 13099 return fixupEncoding(filename); 13100 } 13101 tmp = rfc2231getparam(contentDisposition); 13102 if (tmp) { 13103 const filename = rfc2047decode(tmp); 13104 return fixupEncoding(filename); 13105 } 13106 tmp = toParamRegExp("filename", "i").exec(contentDisposition); 13107 if (tmp) { 13108 tmp = tmp[1]; 13109 let filename = rfc2616unquote(tmp); 13110 filename = rfc2047decode(filename); 13111 return fixupEncoding(filename); 13112 } 13113 function toParamRegExp(attributePattern, flags) { 13114 return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags); 13115 } 13116 function textdecode(encoding, value) { 13117 if (encoding) { 13118 if (!/^[\x00-\xFF]+$/.test(value)) { 13119 return value; 13120 } 13121 try { 13122 const decoder = new TextDecoder(encoding, { 13123 fatal: true 13124 }); 13125 const buffer = stringToBytes(value); 13126 value = decoder.decode(buffer); 13127 needsEncodingFixup = false; 13128 } catch {} 13129 } 13130 return value; 13131 } 13132 function fixupEncoding(value) { 13133 if (needsEncodingFixup && /[\x80-\xff]/.test(value)) { 13134 value = textdecode("utf-8", value); 13135 if (needsEncodingFixup) { 13136 value = textdecode("iso-8859-1", value); 13137 } 13138 } 13139 return value; 13140 } 13141 function rfc2231getparam(contentDispositionStr) { 13142 const matches = []; 13143 let match; 13144 const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig"); 13145 while ((match = iter.exec(contentDispositionStr)) !== null) { 13146 let [, n, quot, part] = match; 13147 n = parseInt(n, 10); 13148 if (n in matches) { 13149 if (n === 0) { 13150 break; 13151 } 13152 continue; 13153 } 13154 matches[n] = [quot, part]; 13155 } 13156 const parts = []; 13157 for (let n = 0; n < matches.length; ++n) { 13158 if (!(n in matches)) { 13159 break; 13160 } 13161 let [quot, part] = matches[n]; 13162 part = rfc2616unquote(part); 13163 if (quot) { 13164 part = unescape(part); 13165 if (n === 0) { 13166 part = rfc5987decode(part); 13167 } 13168 } 13169 parts.push(part); 13170 } 13171 return parts.join(""); 13172 } 13173 function rfc2616unquote(value) { 13174 if (value.startsWith('"')) { 13175 const parts = value.slice(1).split('\\"'); 13176 for (let i = 0; i < parts.length; ++i) { 13177 const quotindex = parts[i].indexOf('"'); 13178 if (quotindex !== -1) { 13179 parts[i] = parts[i].slice(0, quotindex); 13180 parts.length = i + 1; 13181 } 13182 parts[i] = parts[i].replaceAll(/\\(.)/g, "$1"); 13183 } 13184 value = parts.join('"'); 13185 } 13186 return value; 13187 } 13188 function rfc5987decode(extvalue) { 13189 const encodingend = extvalue.indexOf("'"); 13190 if (encodingend === -1) { 13191 return extvalue; 13192 } 13193 const encoding = extvalue.slice(0, encodingend); 13194 const langvalue = extvalue.slice(encodingend + 1); 13195 const value = langvalue.replace(/^[^']*'/, ""); 13196 return textdecode(encoding, value); 13197 } 13198 function rfc2047decode(value) { 13199 if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) { 13200 return value; 13201 } 13202 return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) { 13203 if (encoding === "q" || encoding === "Q") { 13204 text = text.replaceAll("_", " "); 13205 text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) { 13206 return String.fromCharCode(parseInt(hex, 16)); 13207 }); 13208 return textdecode(charset, text); 13209 } 13210 try { 13211 text = atob(text); 13212 } catch {} 13213 return textdecode(charset, text); 13214 }); 13215 } 13216 return ""; 13217} 13218 13219;// ./src/display/network_utils.js 13220 13221 13222 13223function createHeaders(isHttp, httpHeaders) { 13224 const headers = new Headers(); 13225 if (!isHttp || !httpHeaders || typeof httpHeaders !== "object") { 13226 return headers; 13227 } 13228 for (const key in httpHeaders) { 13229 const val = httpHeaders[key]; 13230 if (val !== undefined) { 13231 headers.append(key, val); 13232 } 13233 } 13234 return headers; 13235} 13236function getResponseOrigin(url) { 13237 return URL.parse(url)?.origin ?? null; 13238} 13239function validateRangeRequestCapabilities({ 13240 responseHeaders, 13241 isHttp, 13242 rangeChunkSize, 13243 disableRange 13244}) { 13245 const returnValues = { 13246 allowRangeRequests: false, 13247 suggestedLength: undefined 13248 }; 13249 const length = parseInt(responseHeaders.get("Content-Length"), 10); 13250 if (!Number.isInteger(length)) { 13251 return returnValues; 13252 } 13253 returnValues.suggestedLength = length; 13254 if (length <= 2 * rangeChunkSize) { 13255 return returnValues; 13256 } 13257 if (disableRange || !isHttp) { 13258 return returnValues; 13259 } 13260 if (responseHeaders.get("Accept-Ranges") !== "bytes") { 13261 return returnValues; 13262 } 13263 const contentEncoding = responseHeaders.get("Content-Encoding") || "identity"; 13264 if (contentEncoding !== "identity") { 13265 return returnValues; 13266 } 13267 returnValues.allowRangeRequests = true; 13268 return returnValues; 13269} 13270function extractFilenameFromHeader(responseHeaders) { 13271 const contentDisposition = responseHeaders.get("Content-Disposition"); 13272 if (contentDisposition) { 13273 let filename = getFilenameFromContentDispositionHeader(contentDisposition); 13274 if (filename.includes("%")) { 13275 try { 13276 filename = decodeURIComponent(filename); 13277 } catch {} 13278 } 13279 if (isPdfFile(filename)) { 13280 return filename; 13281 } 13282 } 13283 return null; 13284} 13285function createResponseError(status, url) { 13286 return new ResponseException(`Unexpected server response (${status}) while retrieving PDF "${url}".`, status, status === 404 || status === 0 && url.startsWith("file:")); 13287} 13288function ensureResponseOrigin(rangeOrigin, origin) { 13289 if (rangeOrigin !== origin) { 13290 throw new Error(`Expected range response-origin "${rangeOrigin}" to match "${origin}".`); 13291 } 13292} 13293 13294;// ./src/display/fetch_stream.js 13295 13296 13297 13298function fetchUrl(url, headers, withCredentials, abortController) { 13299 return fetch(url, { 13300 method: "GET", 13301 headers, 13302 signal: abortController.signal, 13303 mode: "cors", 13304 credentials: withCredentials ? "include" : "same-origin", 13305 redirect: "follow" 13306 }); 13307} 13308function ensureResponseStatus(status, url) { 13309 if (status !== 200 && status !== 206) { 13310 throw createResponseError(status, url); 13311 } 13312} 13313function fetch_stream_getArrayBuffer(val) { 13314 if (val instanceof Uint8Array) { 13315 return val.buffer; 13316 } 13317 if (val instanceof ArrayBuffer) { 13318 return val; 13319 } 13320 warn(`getArrayBuffer - unexpected data format: ${val}`); 13321 return new Uint8Array(val).buffer; 13322} 13323class PDFFetchStream extends BasePDFStream { 13324 _responseOrigin = null; 13325 constructor(source) { 13326 super(source, PDFFetchStreamReader, PDFFetchStreamRangeReader); 13327 this.isHttp = /^https?:/i.test(source.url); 13328 this.headers = createHeaders(this.isHttp, source.httpHeaders); 13329 } 13330} 13331class PDFFetchStreamReader extends BasePDFStreamReader { 13332 _abortController = new AbortController(); 13333 _reader = null; 13334 constructor(stream) { 13335 super(stream); 13336 const { 13337 disableRange, 13338 disableStream, 13339 length, 13340 rangeChunkSize, 13341 url, 13342 withCredentials 13343 } = stream._source; 13344 this._contentLength = length; 13345 this._isStreamingSupported = !disableStream; 13346 this._isRangeSupported = !disableRange; 13347 const headers = new Headers(stream.headers); 13348 fetchUrl(url, headers, withCredentials, this._abortController).then(response => { 13349 stream._responseOrigin = getResponseOrigin(response.url); 13350 ensureResponseStatus(response.status, url); 13351 this._reader = response.body.getReader(); 13352 const responseHeaders = response.headers; 13353 const { 13354 allowRangeRequests, 13355 suggestedLength 13356 } = validateRangeRequestCapabilities({ 13357 responseHeaders, 13358 isHttp: stream.isHttp, 13359 rangeChunkSize, 13360 disableRange 13361 }); 13362 this._isRangeSupported = allowRangeRequests; 13363 this._contentLength = suggestedLength || this._contentLength; 13364 this._filename = extractFilenameFromHeader(responseHeaders); 13365 if (!this._isStreamingSupported && this._isRangeSupported) { 13366 this.cancel(new AbortException("Streaming is disabled.")); 13367 } 13368 this._headersCapability.resolve(); 13369 }).catch(this._headersCapability.reject); 13370 } 13371 async read() { 13372 await this._headersCapability.promise; 13373 const { 13374 value, 13375 done 13376 } = await this._reader.read(); 13377 if (done) { 13378 return { 13379 value, 13380 done 13381 }; 13382 } 13383 this._loaded += value.byteLength; 13384 this.onProgress?.({ 13385 loaded: this._loaded, 13386 total: this._contentLength 13387 }); 13388 return { 13389 value: fetch_stream_getArrayBuffer(value), 13390 done: false 13391 }; 13392 } 13393 cancel(reason) { 13394 this._reader?.cancel(reason); 13395 this._abortController.abort(); 13396 } 13397} 13398class PDFFetchStreamRangeReader extends BasePDFStreamRangeReader { 13399 _abortController = new AbortController(); 13400 _readCapability = Promise.withResolvers(); 13401 _reader = null; 13402 constructor(stream, begin, end) { 13403 super(stream, begin, end); 13404 const { 13405 url, 13406 withCredentials 13407 } = stream._source; 13408 const headers = new Headers(stream.headers); 13409 headers.append("Range", `bytes=${begin}
13409-${end - 1}`); 13410 fetchUrl(url, headers, withCredentials, this._abortController).then(response => { 13411 const responseOrigin = getResponseOrigin(response.url); 13412 ensureResponseOrigin(responseOrigin, stream._responseOrigin); 13413 ensureResponseStatus(response.status, url); 13414 this._reader = response.body.getReader(); 13415 this._readCapability.resolve(); 13416 }).catch(this._readCapability.reject); 13417 } 13418 async read() { 13419 await this._readCapability.promise; 13420 const { 13421 value, 13422 done 13423 } = await this._reader.read(); 13424 if (done) { 13425 return { 13426 value, 13427 done 13428 }; 13429 } 13430 return { 13431 value: fetch_stream_getArrayBuffer(value), 13432 done: false 13433 }; 13434 } 13435 cancel(reason) { 13436 this._reader?.cancel(reason); 13437 this._abortController.abort(); 13438 } 13439} 13440 13441;// ./src/display/network.js 13442 13443 13444 13445const OK_RESPONSE = 200; 13446const PARTIAL_CONTENT_RESPONSE = 206; 13447function network_getArrayBuffer(val) { 13448 return typeof val !== "string" ? val : stringToBytes(val).buffer; 13449} 13450class PDFNetworkStream extends BasePDFStream { 13451 #pendingRequests = new WeakMap(); 13452 _responseOrigin = null; 13453 constructor(source) { 13454 super(source, PDFNetworkStreamReader, PDFNetworkStreamRangeReader); 13455 this.url = source.url; 13456 this.isHttp = /^https?:/i.test(this.url); 13457 this.headers = createHeaders(this.isHttp, source.httpHeaders); 13458 } 13459 _request(args) { 13460 const xhr = new XMLHttpRequest(); 13461 const pendingRequest = { 13462 validateStatus: null, 13463 onHeadersReceived: args.onHeadersReceived, 13464 onDone: args.onDone, 13465 onError: args.onError, 13466 onProgress: args.onProgress 13467 }; 13468 this.#pendingRequests.set(xhr, pendingRequest); 13469 xhr.open("GET", this.url); 13470 xhr.withCredentials = this._source.withCredentials; 13471 for (const [key, val] of this.headers) { 13472 xhr.setRequestHeader(key, val); 13473 } 13474 if (this.isHttp && "begin" in args && "end" in args) { 13475 xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`); 13476 pendingRequest.validateStatus = status => status === PARTIAL_CONTENT_RESPONSE || status === OK_RESPONSE; 13477 } else { 13478 pendingRequest.validateStatus = status => status === OK_RESPONSE; 13479 } 13480 xhr.responseType = "arraybuffer"; 13481 assert(args.onError, "Expected `onError` callback to be provided."); 13482 xhr.onerror = () => args.onError(xhr.status); 13483 xhr.onreadystatechange = this.#onStateChange.bind(this, xhr); 13484 xhr.onprogress = this.#onProgress.bind(this, xhr); 13485 xhr.send(null); 13486 return xhr; 13487 } 13488 #onProgress(xhr, evt) { 13489 const pendingRequest = this.#pendingRequests.get(xhr); 13490 pendingRequest?.onProgress?.(evt); 13491 } 13492 #onStateChange(xhr, evt) { 13493 const pendingRequest = this.#pendingRequests.get(xhr); 13494 if (!pendingRequest) { 13495 return; 13496 } 13497 if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { 13498 pendingRequest.onHeadersReceived(); 13499 delete pendingRequest.onHeadersReceived; 13500 } 13501 if (xhr.readyState !== 4) { 13502 return; 13503 } 13504 if (!this.#pendingRequests.has(xhr)) { 13505 return; 13506 } 13507 this.#pendingRequests.delete(xhr); 13508 if (xhr.status === 0 && this.isHttp) { 13509 pendingRequest.onError(xhr.status); 13510 return; 13511 } 13512 const xhrStatus = xhr.status || OK_RESPONSE; 13513 if (!pendingRequest.validateStatus(xhrStatus)) { 13514 pendingRequest.onError(xhr.status); 13515 return; 13516 } 13517 const chunk = network_getArrayBuffer(xhr.response); 13518 if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { 13519 const rangeHeader = xhr.getResponseHeader("Content-Range"); 13520 if (/bytes (\d+)-(\d+)\/(\d+)/.test(rangeHeader)) { 13521 pendingRequest.onDone(chunk); 13522 } else { 13523 warn(`Missing or invalid "Content-Range" header.`); 13524 pendingRequest.onError(0); 13525 } 13526 } else if (chunk) { 13527 pendingRequest.onDone(chunk); 13528 } else { 13529 pendingRequest.onError(xhr.status); 13530 } 13531 } 13532 _abortRequest(xhr) { 13533 if (this.#pendingRequests.has(xhr)) { 13534 this.#pendingRequests.delete(xhr); 13535 xhr.abort(); 13536 } 13537 } 13538 getRangeReader(begin, end) { 13539 const reader = super.getRangeReader(begin, end); 13540 if (reader) { 13541 reader.onClosed = () => this._rangeReaders.delete(reader); 13542 } 13543 return reader; 13544 } 13545} 13546class PDFNetworkStreamReader extends BasePDFStreamReader { 13547 _cachedChunks = []; 13548 _done = false;
vendor: 5,466 bytes, lines 13549-13753
13549 _requests = []; 13550 _storedError = null; 13551 constructor(stream) { 13552 super(stream); 13553 const { 13554 length 13555 } = stream._source; 13556 this._contentLength = length; 13557 this._fullRequestXhr = stream._request({ 13558 onHeadersReceived: this.#onHeadersReceived.bind(this), 13559 onDone: this.#onDone.bind(this), 13560 onError: this.#onError.bind(this), 13561 onProgress: this.#onProgress.bind(this) 13562 }); 13563 } 13564 #onHeadersReceived() { 13565 const stream = this._stream; 13566 const { 13567 disableRange, 13568 rangeChunkSize 13569 } = stream._source; 13570 const fullRequestXhr = this._fullRequestXhr; 13571 stream._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL); 13572 const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders(); 13573 const responseHeaders = new Headers(rawResponseHeaders ? rawResponseHeaders.trimStart().replace(/[^\S ]+$/, "").split(/[\r\n]+/).map(x => { 13574 const [key, ...val] = x.split(": "); 13575 return [key, val.join(": ")]; 13576 }) : []); 13577 const { 13578 allowRangeRequests, 13579 suggestedLength 13580 } = validateRangeRequestCapabilities({ 13581 responseHeaders, 13582 isHttp: stream.isHttp, 13583 rangeChunkSize, 13584 disableRange 13585 }); 13586 if (allowRangeRequests) { 13587 this._isRangeSupported = true; 13588 } 13589 this._contentLength = suggestedLength || this._contentLength; 13590 this._filename = extractFilenameFromHeader(responseHeaders); 13591 if (this._isRangeSupported) { 13592 stream._abortRequest(fullRequestXhr); 13593 } 13594 this._headersCapability.resolve(); 13595 } 13596 #onDone(chunk) { 13597 if (this._requests.length > 0) { 13598 const capability = this._requests.shift(); 13599 capability.resolve({ 13600 value: chunk, 13601 done: false 13602 }); 13603 } else { 13604 this._cachedChunks.push(chunk); 13605 } 13606 this._done = true; 13607 if (this._cachedChunks.length > 0) { 13608 return; 13609 } 13610 for (const capability of this._requests) { 13611 capability.resolve({ 13612 value: undefined, 13613 done: true 13614 }); 13615 } 13616 this._requests.length = 0; 13617 } 13618 #onError(status) { 13619 this._storedError = createResponseError(status, this._stream.url); 13620 this._headersCapability.reject(this._storedError); 13621 for (const capability of this._requests) { 13622 capability.reject(this._storedError); 13623 } 13624 this._requests.length = 0; 13625 this._cachedChunks.length = 0; 13626 } 13627 #onProgress(evt) { 13628 this.onProgress?.({ 13629 loaded: evt.loaded, 13630 total: evt.lengthComputable ? evt.total : this._contentLength 13631 }); 13632 } 13633 async read() { 13634 await this._headersCapability.promise; 13635 if (this._storedError) { 13636 throw this._storedError; 13637 } 13638 if (this._cachedChunks.length > 0) { 13639 const chunk = this._cachedChunks.shift(); 13640 return { 13641 value: chunk, 13642 done: false 13643 }; 13644 } 13645 if (this._done) { 13646 return { 13647 value: undefined, 13648 done: true 13649 }; 13650 } 13651 const capability = Promise.withResolvers(); 13652 this._requests.push(capability); 13653 return capability.promise; 13654 } 13655 cancel(reason) { 13656 this._done = true; 13657 this._headersCapability.reject(reason); 13658 for (const capability of this._requests) { 13659 capability.resolve({ 13660 value: undefined, 13661 done: true 13662 }); 13663 } 13664 this._requests.length = 0; 13665 this._stream._abortRequest(this._fullRequestXhr); 13666 this._fullRequestXhr = null; 13667 } 13668} 13669class PDFNetworkStreamRangeReader extends BasePDFStreamRangeReader { 13670 onClosed = null; 13671 _done = false; 13672 _queuedChunk = null; 13673 _requests = []; 13674 _storedError = null; 13675 constructor(stream, begin, end) { 13676 super(stream, begin, end); 13677 this._requestXhr = stream._request({ 13678 begin, 13679 end, 13680 onHeadersReceived: this.#onHeadersReceived.bind(this), 13681 onDone: this.#onDone.bind(this), 13682 onError: this.#onError.bind(this), 13683 onProgress: null 13684 }); 13685 } 13686 #onHeadersReceived() { 13687 const responseOrigin = getResponseOrigin(this._requestXhr?.responseURL); 13688 try { 13689 ensureResponseOrigin(responseOrigin, this._stream._responseOrigin); 13690 } catch (ex) { 13691 this._storedError = ex; 13692 this.#onError(0); 13693 } 13694 } 13695 #onDone(chunk) { 13696 if (this._requests.length > 0) { 13697 const capability = this._requests.shift(); 13698 capability.resolve({ 13699 value: chunk, 13700 done: false 13701 }); 13702 } else { 13703 this._queuedChunk = chunk; 13704 } 13705 this._done = true; 13706 for (const capability of this._requests) { 13707 capability.resolve({ 13708 value: undefined, 13709 done: true 13710 }); 13711 } 13712 this._requests.length = 0; 13713 this.onClosed?.(); 13714 } 13715 #onError(status) { 13716 this._storedError ??= createResponseError(status, this._stream.url); 13717 for (const capability of this._requests) { 13718 capability.reject(this._storedError); 13719 } 13720 this._requests.length = 0; 13721 this._queuedChunk = null; 13722 } 13723 async read() { 13724 if (this._storedError) { 13725 throw this._storedError; 13726 } 13727 if (this._queuedChunk !== null) { 13728 const chunk = this._queuedChunk; 13729 this._queuedChunk = null; 13730 return { 13731 value: chunk, 13732 done: false 13733 }; 13734 } 13735 if (this._done) { 13736 return { 13737 value: undefined, 13738 done: true 13739 }; 13740 } 13741 const capability = Promise.withResolvers(); 13742 this._requests.push(capability); 13743 return capability.promise; 13744 } 13745 cancel(reason) { 13746 this._done = true; 13747 for (const capability of this._requests) { 13748 capability.resolve({ 13749 value: undefined, 13750 done: true 13751 }); 13752 } 13753 this._requests.length = 0;
13754 this._stream._abortRequest(this._requestXhr); 13755 this.onClosed?.(); 13756 } 13757} 13758 13759;// ./src/display/node_stream.js 13760 13761 13762 13763const urlRegex = /^[a-z][a-z0-9\-+.]+:/i; 13764function parseUrlOrPath(sourceUrl) { 13765 if (urlRegex.test(sourceUrl)) { 13766 return new URL(sourceUrl); 13767 } 13768 const url = process.getBuiltinModule("url"); 13769 return new URL(url.pathToFileURL(sourceUrl)); 13770} 13771function getReadableStream(readStream) { 13772 const { 13773 Readable 13774 } = process.getBuiltinModule("stream"); 13775 if (typeof Readable.toWeb === "function") { 13776 return Readable.toWeb(readStream); 13777 } 13778 const require = process.getBuiltinModule("module").createRequire(import.meta.url); 13779 const polyfill = require("node-readable-to-web-readable-stream"); 13780 return polyfill.makeDefaultReadableStreamFromNodeReadable(readStream); 13781} 13782function node_stream_getArrayBuffer(val) { 13783 if (val instanceof Uint8Array) { 13784 return val.buffer; 13785 } 13786 if (val instanceof ArrayBuffer) { 13787 return val; 13788 } 13789 warn(`getArrayBuffer - unexpected data format: ${val}`); 13790 return new Uint8Array(val).buffer; 13791} 13792class PDFNodeStream extends BasePDFStream { 13793 constructor(source) { 13794 super(source, PDFNodeStreamReader, PDFNodeStreamRangeReader); 13795 this.url = parseUrlOrPath(source.url); 13796 assert(this.url.protocol === "file:", "PDFNodeStream only supports file:// URLs."); 13797 } 13798} 13799class PDFNodeStreamReader extends BasePDFStreamReader { 13800 _reader = null; 13801 constructor(stream) { 13802 super(stream); 13803 const { 13804 disableRange, 13805 disableStream, 13806 length, 13807 rangeChunkSize 13808 } = stream._source; 13809 this._contentLength = length; 13810 this._isStreamingSupported = !disableStream; 13811 this._isRangeSupported = !disableRange; 13812 const url = stream.url; 13813 const fs = process.getBuiltinModule("fs"); 13814 fs.promises.lstat(url).then(stat => { 13815 const readStream = fs.createReadStream(url); 13816 const readableStream = getReadableStream(readStream); 13817 this._reader = readableStream.getReader(); 13818 const { 13819 size 13820 } = stat; 13821 if (size <= 2 * rangeChunkSize) { 13822 this._isRangeSupported = false; 13823 } 13824 this._contentLength = size; 13825 if (!this._isStreamingSupported && this._isRangeSupported) { 13826 this.cancel(new AbortException("Streaming is disabled.")); 13827 } 13828 this._headersCapability.resolve(); 13829 }).catch(error => { 13830 if (error.code === "ENOENT") { 13831 error = createResponseError(0, url.href); 13832 } 13833 this._headersCapability.reject(error); 13834 }); 13835 } 13836 async read() { 13837 await this._headersCapability.promise; 13838 const { 13839 value, 13840 done 13841 } = await this._reader.read(); 13842 if (done) { 13843 return { 13844 value, 13845 done 13846 }; 13847 } 13848 this._loaded += value.length; 13849 this.onProgress?.({ 13850 loaded: this._loaded, 13851 total: this._contentLength 13852 }); 13853 return { 13854 value: node_stream_getArrayBuffer(value), 13855 done: false 13856 }; 13857 } 13858 cancel(reason) { 13859 this._reader?.cancel(reason); 13860 } 13861} 13862class PDFNodeStreamRangeReader extends BasePDFStreamRangeReader { 13863 _readCapability = Promise.withResolvers(); 13864 _reader = null; 13865 constructor(stream, begin, end) { 13866 super(stream, begin, end); 13867 const url = stream.url; 13868 const fs = process.getBuiltinModule("fs"); 13869 try { 13870 const readStream = fs.createReadStream(url, { 13871 start: begin, 13872 end: end - 1 13873 }); 13874 const readableStream = getReadableStream(readStream); 13875 this._reader = readableStream.getReader(); 13876 this._readCapability.resolve(); 13877 } catch (error) { 13878 this._readCapability.reject(error); 13879 } 13880 } 13881 async read() { 13882 await this._readCapability.promise; 13883 const { 13884 value, 13885 done 13886 } = await this._reader.read(); 13887 if (done) { 13888 return { 13889 value, 13890 done 13891 }; 13892 } 13893 return { 13894 value: node_stream_getArrayBuffer(value), 13895 done: false 13896 }; 13897 } 13898 cancel(reason) { 13899 this._reader?.cancel(reason); 13900 } 13901} 13902 13903;// ./src/display/pdf_objects.js 13904const INITIAL_DATA = Symbol("INITIAL_DATA"); 13905class PDFObjects { 13906 #objs = Object.create(null); 13907 #ensureObj(objId) { 13908 return this.#objs[objId] ||= { 13909 ...Promise.withResolvers(), 13910 data: INITIAL_DATA 13911 }; 13912 } 13913 get(objId, callback = null) { 13914 if (callback) { 13915 const obj = this.#ensureObj(objId); 13916 obj.promise.then(() => callback(obj.data)); 13917 return null; 13918 } 13919 const obj = this.#objs[objId]; 13920 if (!obj || obj.data === INITIAL_DATA) { 13921 throw new Error(`Requesting object that isn't resolved yet ${objId}.`); 13922 } 13923 return obj.data; 13924 }
vendor: 5,997 bytes, lines 13925-14134
13925 has(objId) { 13926 const obj = this.#objs[objId]; 13927 return !!obj && obj.data !== INITIAL_DATA; 13928 } 13929 delete(objId) { 13930 const obj = this.#objs[objId]; 13931 if (!obj || obj.data === INITIAL_DATA) { 13932 return false; 13933 } 13934 delete this.#objs[objId]; 13935 return true; 13936 } 13937 resolve(objId, data = null) { 13938 const obj = this.#ensureObj(objId); 13939 obj.data = data; 13940 obj.resolve(); 13941 } 13942 clear() { 13943 for (const objId in this.#objs) { 13944 const { 13945 data 13946 } = this.#objs[objId]; 13947 data?.bitmap?.close(); 13948 } 13949 this.#objs = Object.create(null); 13950 } 13951 *[Symbol.iterator]() { 13952 for (const objId in this.#objs) { 13953 const { 13954 data 13955 } = this.#objs[objId]; 13956 if (data === INITIAL_DATA) { 13957 continue; 13958 } 13959 yield [objId, data]; 13960 } 13961 } 13962} 13963 13964;// ./src/display/text_layer.js 13965 13966 13967const MAX_TEXT_DIVS_TO_RENDER = 100000; 13968const DEFAULT_FONT_SIZE = 30; 13969class TextLayer { 13970 #capability = Promise.withResolvers(); 13971 #container = null; 13972 #disableProcessItems = false; 13973 #fontInspectorEnabled = !!globalThis.FontInspector?.enabled; 13974 #lang = null; 13975 #layoutTextParams = null; 13976 #pageHeight = 0; 13977 #pageWidth = 0; 13978 #reader = null; 13979 #rootContainer = null; 13980 #rotation = 0; 13981 #scale = 0; 13982 #styleCache = Object.create(null); 13983 #textContentItemsStr = []; 13984 #textContentSource = null; 13985 #textDivs = []; 13986 #textDivProperties = new WeakMap(); 13987 #transform = null; 13988 static #ascentCache = new Map(); 13989 static #canvasContexts = new Map(); 13990 static #canvasCtxFonts = new WeakMap(); 13991 static #minFontSize = null; 13992 static #pendingTextLayers = new Set(); 13993 constructor({ 13994 textContentSource, 13995 container, 13996 viewport 13997 }) { 13998 if (textContentSource instanceof ReadableStream) { 13999 this.#textContentSource = textContentSource; 14000 } else if (typeof textContentSource === "object") { 14001 this.#textContentSource = new ReadableStream({ 14002 start(controller) { 14003 controller.enqueue(textContentSource); 14004 controller.close(); 14005 } 14006 }); 14007 } else { 14008 throw new Error('No "textContentSource" parameter specified.'); 14009 } 14010 this.#container = this.#rootContainer = container; 14011 this.#scale = viewport.scale * OutputScale.pixelRatio; 14012 this.#rotation = viewport.rotation; 14013 this.#layoutTextParams = { 14014 div: null, 14015 properties: null, 14016 ctx: null 14017 }; 14018 const { 14019 pageWidth, 14020 pageHeight, 14021 pageX, 14022 pageY 14023 } = viewport.rawDims; 14024 this.#transform = [1, 0, 0, -1, -pageX, pageY + pageHeight]; 14025 this.#pageWidth = pageWidth; 14026 this.#pageHeight = pageHeight; 14027 TextLayer.#ensureMinFontSizeComputed(); 14028 container.style.setProperty("--min-font-size", TextLayer.#minFontSize); 14029 setLayerDimensions(container, viewport); 14030 this.#capability.promise.finally(() => { 14031 TextLayer.#pendingTextLayers.delete(this); 14032 this.#layoutTextParams = null; 14033 this.#styleCache = null; 14034 }).catch(() => {}); 14035 } 14036 static get fontFamilyMap() { 14037 const { 14038 isWindows, 14039 isFirefox 14040 } = util_FeatureTest.platform; 14041 return shadow(this, "fontFamilyMap", new Map([["sans-serif", `${isWindows && isFirefox ? "Calibri, " : ""}sans-serif`], ["monospace", `${isWindows && isFirefox ? "Lucida Console, " : ""}monospace`]])); 14042 } 14043 render() { 14044 const pump = () => { 14045 this.#reader.read().then(({ 14046 value, 14047 done 14048 }) => { 14049 if (done) { 14050 this.#capability.resolve(); 14051 return; 14052 } 14053 this.#lang ??= value.lang; 14054 Object.assign(this.#styleCache, value.styles); 14055 this.#processItems(value.items); 14056 pump(); 14057 }, this.#capability.reject); 14058 }; 14059 this.#reader = this.#textContentSource.getReader(); 14060 TextLayer.#pendingTextLayers.add(this); 14061 pump(); 14062 return this.#capability.promise; 14063 } 14064 update({ 14065 viewport, 14066 onBefore = null 14067 }) { 14068 const scale = viewport.scale * OutputScale.pixelRatio; 14069 const rotation = viewport.rotation; 14070 if (rotation !== this.#rotation) { 14071 onBefore?.(); 14072 this.#rotation = rotation; 14073 setLayerDimensions(this.#rootContainer, { 14074 rotation 14075 }); 14076 } 14077 if (scale !== this.#scale) { 14078 onBefore?.(); 14079 this.#scale = scale; 14080 const params = { 14081 div: null, 14082 properties: null, 14083 ctx: TextLayer.#getCtx(this.#lang) 14084 }; 14085 for (const div of this.#textDivs) { 14086 params.properties = this.#textDivProperties.get(div); 14087 params.div = div; 14088 this.#layout(params); 14089 } 14090 } 14091 } 14092 cancel() { 14093 const abortEx = new AbortException("TextLayer task cancelled."); 14094 this.#reader?.cancel(abortEx).catch(() => {}); 14095 this.#reader = null; 14096 this.#capability.reject(abortEx); 14097 } 14098 get textDivs() { 14099 return this.#textDivs; 14100 } 14101 get textContentItemsStr() { 14102 return this.#textContentItemsStr; 14103 } 14104 #processItems(items) { 14105 if (this.#disableProcessItems) { 14106 return; 14107 } 14108 this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang); 14109 const textDivs = this.#textDivs, 14110 textContentItemsStr = this.#textContentItemsStr; 14111 for (const item of items) { 14112 if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) { 14113 warn("Ignoring additional textDivs for performance reasons."); 14114 this.#disableProcessItems = true; 14115 return; 14116 } 14117 if (item.str === undefined) { 14118 if (item.type === "beginMarkedContentProps" || item.type === "beginMarkedContent") { 14119 const parent = this.#container; 14120 this.#container = document.createElement("span"); 14121 this.#container.classList.add("markedContent"); 14122 if (item.id) { 14123 this.#container.setAttribute("id", `${item.id}`); 14124 } 14125 if (item.tag === "Artifact") { 14126 this.#container.ariaHidden = true; 14127 } 14128 parent.append(this.#container); 14129 } else if (item.type === "endMarkedContent") { 14130 this.#container = this.#container.parentNode; 14131 } 14132 continue; 14133 } 14134 textContentItemsStr.push(item.str);
vendor: 5,703 bytes, lines 14135-14311
14135 this.#appendText(item); 14136 } 14137 } 14138 #appendText(geom) { 14139 const textDiv = document.createElement("span"); 14140 const textDivProperties = { 14141 angle: 0, 14142 canvasWidth: 0, 14143 hasText: geom.str !== "", 14144 hasEOL: geom.hasEOL, 14145 fontSize: 0 14146 }; 14147 this.#textDivs.push(textDiv); 14148 const tx = Util.transform(this.#transform, geom.transform); 14149 let angle = Math.atan2(tx[1], tx[0]); 14150 const style = this.#styleCache[geom.fontName]; 14151 if (style.vertical) { 14152 angle += Math.PI / 2; 14153 } 14154 let fontFamily = this.#fontInspectorEnabled && style.fontSubstitution || style.fontFamily; 14155 fontFamily = TextLayer.fontFamilyMap.get(fontFamily) || fontFamily; 14156 const fontHeight = Math.hypot(tx[2], tx[3]); 14157 const fontAscent = fontHeight * TextLayer.#getAscent(fontFamily, style, this.#lang); 14158 let left, top; 14159 if (angle === 0) { 14160 left = tx[4]; 14161 top = tx[5] - fontAscent; 14162 } else { 14163 left = tx[4] + fontAscent * Math.sin(angle); 14164 top = tx[5] - fontAscent * Math.cos(angle); 14165 } 14166 const divStyle = textDiv.style; 14167 divStyle.left = `${(100 * left / this.#pageWidth).toFixed(2)}%`; 14168 divStyle.top = `${(100 * top / this.#pageHeight).toFixed(2)}%`; 14169 divStyle.setProperty("--font-height", `${fontHeight.toFixed(2)}px`); 14170 divStyle.fontFamily = fontFamily; 14171 textDivProperties.fontSize = fontHeight; 14172 textDiv.setAttribute("role", "presentation"); 14173 textDiv.textContent = geom.str; 14174 textDiv.dir = geom.dir; 14175 if (this.#fontInspectorEnabled) { 14176 textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName; 14177 } 14178 if (angle !== 0) { 14179 textDivProperties.angle = angle * (180 / Math.PI); 14180 } 14181 let shouldScaleText = false; 14182 if (geom.str.length > 1) { 14183 shouldScaleText = true; 14184 } else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) { 14185 const absScaleX = Math.abs(geom.transform[0]), 14186 absScaleY = Math.abs(geom.transform[3]); 14187 if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) { 14188 shouldScaleText = true; 14189 } 14190 } 14191 if (shouldScaleText) { 14192 textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width; 14193 } 14194 this.#textDivProperties.set(textDiv, textDivProperties); 14195 this.#layoutTextParams.div = textDiv; 14196 this.#layoutTextParams.properties = textDivProperties; 14197 this.#layout(this.#layoutTextParams); 14198 if (textDivProperties.hasText) { 14199 this.#container.append(textDiv); 14200 } 14201 if (textDivProperties.hasEOL) { 14202 const br = document.createElement("br"); 14203 br.setAttribute("role", "presentation"); 14204 this.#container.append(br); 14205 } 14206 } 14207 #layout(params) { 14208 const { 14209 div, 14210 properties, 14211 ctx 14212 } = params; 14213 const { 14214 style 14215 } = div; 14216 if (properties.canvasWidth !== 0 && properties.hasText) { 14217 const { 14218 fontFamily 14219 } = style; 14220 const { 14221 canvasWidth, 14222 fontSize 14223 } = properties; 14224 TextLayer.#ensureCtxFont(ctx, fontSize * this.#scale, fontFamily); 14225 const { 14226 width 14227 } = ctx.measureText(div.textContent); 14228 if (width > 0) { 14229 style.setProperty("--scale-x", canvasWidth * this.#scale / width); 14230 } 14231 } 14232 if (properties.angle !== 0) { 14233 style.setProperty("--rotate", `${properties.angle}deg`); 14234 } 14235 } 14236 static cleanup() { 14237 if (this.#pendingTextLayers.size > 0) { 14238 return; 14239 } 14240 this.#ascentCache.clear(); 14241 for (const { 14242 canvas 14243 } of this.#canvasContexts.values()) { 14244 canvas.remove(); 14245 } 14246 this.#canvasContexts.clear(); 14247 } 14248 static #getCtx(lang = null) { 14249 let ctx = this.#canvasContexts.get(lang ||= ""); 14250 if (!ctx) { 14251 const canvas = document.createElement("canvas"); 14252 canvas.className = "hiddenCanvasElement"; 14253 canvas.lang = lang; 14254 document.body.append(canvas); 14255 ctx = canvas.getContext("2d", { 14256 alpha: false, 14257 willReadFrequently: true 14258 }); 14259 this.#canvasContexts.set(lang, ctx); 14260 this.#canvasCtxFonts.set(ctx, { 14261 size: 0, 14262 family: "" 14263 }); 14264 } 14265 return ctx; 14266 } 14267 static #ensureCtxFont(ctx, size, family) { 14268 const cached = this.#canvasCtxFonts.get(ctx); 14269 if (size === cached.size && family === cached.family) { 14270 return; 14271 } 14272 ctx.font = `${size}px ${family}`; 14273 cached.size = size; 14274 cached.family = family; 14275 } 14276 static #ensureMinFontSizeComputed() { 14277 if (this.#minFontSize !== null) { 14278 return; 14279 } 14280 const div = document.createElement("div"); 14281 div.style.opacity = 0; 14282 div.style.lineHeight = 1; 14283 div.style.fontSize = "1px"; 14284 div.style.position = "absolute"; 14285 div.textContent = "X"; 14286 document.body.append(div); 14287 this.#minFontSize = div.getBoundingClientRect().height; 14288 div.remove(); 14289 } 14290 static #getAscent(fontFamily, style, lang) { 14291 const cachedAscent = this.#ascentCache.get(fontFamily); 14292 if (cachedAscent) { 14293 return cachedAscent; 14294 } 14295 const ctx = this.#getCtx(lang); 14296 ctx.canvas.width = ctx.canvas.height = DEFAULT_FONT_SIZE; 14297 this.#ensureCtxFont(ctx, DEFAULT_FONT_SIZE, fontFamily); 14298 const metrics = ctx.measureText(""); 14299 const ascent = metrics.fontBoundingBoxAscent; 14300 const descent = Math.abs(metrics.fontBoundingBoxDescent); 14301 ctx.canvas.width = ctx.canvas.height = 0; 14302 let ratio = 0.8; 14303 if (ascent) { 14304 ratio = ascent / (ascent + descent); 14305 } else { 14306 if (util_FeatureTest.platform.isFirefox) { 14307 warn("Enable the `dom.textMetrics.fontBoundingBox.enabled` preference " + "in `about:config` to improve TextLayer rendering."); 14308 } 14309 if (style.ascent) { 14310 ratio = style.ascent; 14311 }
vendor: 22,013 bytes, lines 14311-15031
14311 else if (style.descent) { 14312 ratio = 1 + style.descent; 14313 } 14314 } 14315 this.#ascentCache.set(fontFamily, ratio); 14316 return ratio; 14317 } 14318} 14319 14320;// ./src/display/api.js 14321 14322 14323 14324 14325 14326 14327 14328 14329 14330 14331 14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 14344 14345 14346const RENDERING_CANCELLED_TIMEOUT = 100; 14347function getDocument(src = {}) { 14348 if (typeof src === "string" || src instanceof URL) { 14349 src = { 14350 url: src 14351 }; 14352 } else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) { 14353 src = { 14354 data: src 14355 }; 14356 } 14357 const task = new PDFDocumentLoadingTask(); 14358 const { 14359 docId 14360 } = task; 14361 const url = src.url ? getUrlProp(src.url) : null; 14362 const data = src.data ? getDataProp(src.data) : null; 14363 const httpHeaders = src.httpHeaders || null; 14364 const withCredentials = src.withCredentials === true; 14365 const password = src.password ?? null; 14366 const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null; 14367 const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : 2 ** 16; 14368 let worker = src.worker instanceof PDFWorker ? src.worker : null; 14369 const verbosity = src.verbosity; 14370 const docBaseUrl = typeof src.docBaseUrl === "string" && !isDataScheme(src.docBaseUrl) ? src.docBaseUrl : null; 14371 const cMapUrl = getFactoryUrlProp(src.cMapUrl); 14372 const cMapPacked = src.cMapPacked !== false; 14373 const CMapReaderFactory = src.CMapReaderFactory || (isNodeJS ? NodeCMapReaderFactory : DOMCMapReaderFactory); 14374 const iccUrl = getFactoryUrlProp(src.iccUrl); 14375 const standardFontDataUrl = getFactoryUrlProp(src.standardFontDataUrl); 14376 const StandardFontDataFactory = src.StandardFontDataFactory || (isNodeJS ? NodeStandardFontDataFactory : DOMStandardFontDataFactory); 14377 const wasmUrl = getFactoryUrlProp(src.wasmUrl); 14378 const WasmFactory = src.WasmFactory || (isNodeJS ? NodeWasmFactory : DOMWasmFactory); 14379 const ignoreErrors = src.stopAtErrors !== true; 14380 const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1; 14381 const isEvalSupported = src.isEvalSupported !== false; 14382 const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !isNodeJS; 14383 const isImageDecoderSupported = typeof src.isImageDecoderSupported === "boolean" ? src.isImageDecoderSupported : !isNodeJS && (util_FeatureTest.platform.isFirefox || !globalThis.chrome); 14384 const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1; 14385 const disableFontFace = typeof src.disableFontFace === "boolean" ? src.disableFontFace : isNodeJS; 14386 const fontExtraProperties = src.fontExtraProperties === true; 14387 const enableXfa = src.enableXfa === true; 14388 const ownerDocument = src.ownerDocument || globalThis.document; 14389 const disableRange = src.disableRange === true; 14390 const disableStream = src.disableStream === true; 14391 const disableAutoFetch = src.disableAutoFetch === true; 14392 const pdfBug = src.pdfBug === true; 14393 const CanvasFactory = src.CanvasFactory || (isNodeJS ? NodeCanvasFactory : DOMCanvasFactory); 14394 const FilterFactory = src.FilterFactory || (isNodeJS ? NodeFilterFactory : DOMFilterFactory); 14395 const enableHWA = src.enableHWA === true; 14396 const useWasm = src.useWasm !== false; 14397 const length = rangeTransport ? rangeTransport.length : src.length ?? NaN; 14398 const useSystemFonts = typeof src.useSystemFonts === "boolean" ? src.useSystemFonts : !isNodeJS && !disableFontFace; 14399 const useWorkerFetch = typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : !!(CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && WasmFactory === DOMWasmFactory && cMapUrl && standardFontDataUrl && wasmUrl && isValidFetchUrl(cMapUrl, document.baseURI) && isValidFetchUrl(standardFontDataUrl, document.baseURI) && isValidFetchUrl(wasmUrl, document.baseURI)); 14400 const styleElement = null; 14401 setVerbosityLevel(verbosity); 14402 const transportFactory = { 14403 canvasFactory: new CanvasFactory({ 14404 ownerDocument, 14405 enableHWA 14406 }), 14407 filterFactory: new FilterFactory({ 14408 docId, 14409 ownerDocument 14410 }), 14411 cMapReaderFactory: useWorkerFetch ? null : new CMapReaderFactory({ 14412 baseUrl: cMapUrl, 14413 isCompressed: cMapPacked 14414 }), 14415 standardFontDataFactory: useWorkerFetch ? null : new StandardFontDataFactory({ 14416 baseUrl: standardFontDataUrl 14417 }), 14418 wasmFactory: useWorkerFetch ? null : new WasmFactory({ 14419 baseUrl: wasmUrl 14420 }) 14421 }; 14422 if (!worker) { 14423 worker = PDFWorker.create({ 14424 verbosity, 14425 port: GlobalWorkerOptions.workerPort 14426 }); 14427 task._worker = worker; 14428 } 14429 const docParams = { 14430 docId, 14431 apiVersion: "5.4.624", 14432 data, 14433 password, 14434 disableAutoFetch, 14435 rangeChunkSize, 14436 length, 14437 docBaseUrl, 14438 enableXfa, 14439 evaluatorOptions: { 14440 maxImageSize, 14441 disableFontFace, 14442 ignoreErrors, 14443 isEvalSupported, 14444 isOffscreenCanvasSupported, 14445 isImageDecoderSupported, 14446 canvasMaxAreaInBytes, 14447 fontExtraProperties, 14448 useSystemFonts, 14449 useWasm, 14450 useWorkerFetch, 14451 cMapUrl, 14452 iccUrl, 14453 standardFontDataUrl, 14454 wasmUrl 14455 } 14456 }; 14457 const transportParams = { 14458 ownerDocument, 14459 pdfBug, 14460 styleElement, 14461 enableHWA, 14462 loadingParams: { 14463 disableAutoFetch, 14464 enableXfa 14465 } 14466 }; 14467 worker.promise.then(function () { 14468 if (task.destroyed) { 14469 throw new Error("Loading aborted"); 14470 } 14471 if (worker.destroyed) { 14472 throw new Error("Worker was destroyed"); 14473 } 14474 const workerIdPromise = worker.messageHandler.sendWithPromise("GetDocRequest", docParams, data ? [data.buffer] : null); 14475 let networkStream; 14476 if (rangeTransport) { 14477 networkStream = new PDFDataTransportStream({ 14478 pdfDataRangeTransport: rangeTransport, 14479 disableRange, 14480 disableStream 14481 }); 14482 } else if (!data) { 14483 if (!url) { 14484 throw new Error("getDocument - no `url` parameter provided."); 14485 } 14486 const NetworkStream = isValidFetchUrl(url) ? PDFFetchStream : isNodeJS ? PDFNodeStream : PDFNetworkStream; 14487 networkStream = new NetworkStream({ 14488 url, 14489 length, 14490 httpHeaders, 14491 withCredentials, 14492 rangeChunkSize, 14493 disableRange, 14494 disableStream 14495 }); 14496 } 14497 return workerIdPromise.then(workerId => { 14498 if (task.destroyed) { 14499 throw new Error("Loading aborted"); 14500 } 14501 if (worker.destroyed) { 14502 throw new Error("Worker was destroyed"); 14503 } 14504 const messageHandler = new MessageHandler(docId, workerId, worker.port); 14505 const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory); 14506 task._transport = transport; 14507 messageHandler.send("Ready", null); 14508 }); 14509 }).catch(task._capability.reject); 14510 return task; 14511} 14512class PDFDocumentLoadingTask { 14513 static #docId = 0; 14514 _capability = Promise.withResolvers(); 14515 _transport = null; 14516 _worker = null; 14517 docId = `d${PDFDocumentLoadingTask.#docId++}`; 14518 destroyed = false; 14519 onPassword = null; 14520 onProgress = null; 14521 get promise() { 14522 return this._capability.promise; 14523 } 14524 async destroy() { 14525 this.destroyed = true; 14526 try { 14527 if (this._worker?.port) { 14528 this._worker._pendingDestroy = true; 14529 } 14530 await this._transport?.destroy(); 14531 } catch (ex) { 14532 if (this._worker?.port) { 14533 delete this._worker._pendingDestroy; 14534 } 14535 throw ex; 14536 } 14537 this._transport = null; 14538 this._worker?.destroy(); 14539 this._worker = null; 14540 } 14541 async getData() { 14542 return this._transport.getData(); 14543 } 14544} 14545class PDFDataRangeTransport { 14546 #capability = Promise.withResolvers(); 14547 #progressiveDoneListeners = []; 14548 #progressiveReadListeners = []; 14549 #progressListeners = []; 14550 #rangeListeners = []; 14551 constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) { 14552 this.length = length; 14553 this.initialData = initialData; 14554 this.progressiveDone = progressiveDone; 14555 this.contentDispositionFilename = contentDispositionFilename; 14556 } 14557 addRangeListener(listener) { 14558 this.#rangeListeners.push(listener); 14559 } 14560 addProgressListener(listener) { 14561 this.#progressListeners.push(listener); 14562 } 14563 addProgressiveReadListener(listener) { 14564 this.#progressiveReadListeners.push(listener); 14565 } 14566 addProgressiveDoneListener(listener) { 14567 this.#progressiveDoneListeners.push(listener); 14568 } 14569 onDataRange(begin, chunk) { 14570 for (const listener of this.#rangeListeners) { 14571 listener(begin, chunk); 14572 } 14573 } 14574 onDataProgress(loaded, total) { 14575 this.#capability.promise.then(() => { 14576 for (const listener of this.#progressListeners) { 14577 listener(loaded, total); 14578 } 14579 }); 14580 } 14581 onDataProgressiveRead(chunk) { 14582 this.#capability.promise.then(() => { 14583 for (const listener of this.#progressiveReadListeners) { 14584 listener(chunk); 14585 } 14586 }); 14587 } 14588 onDataProgressiveDone() { 14589 this.#capability.promise.then(() => { 14590 for (const listener of this.#progressiveDoneListeners) { 14591 listener(); 14592 } 14593 }); 14594 } 14595 transportReady() { 14596 this.#capability.resolve(); 14597 } 14598 requestDataRange(begin, end) { 14599 unreachable("Abstract method PDFDataRangeTransport.requestDataRange"); 14600 } 14601 abort() {} 14602} 14603class PDFDocumentProxy { 14604 constructor(pdfInfo, transport) { 14605 this._pdfInfo = pdfInfo; 14606 this._transport = transport; 14607 } 14608 get annotationStorage() { 14609 return this._transport.annotationStorage; 14610 } 14611 get canvasFactory() { 14612 return this._transport.canvasFactory; 14613 } 14614 get filterFactory() { 14615 return this._transport.filterFactory; 14616 } 14617 get numPages() { 14618 return this._pdfInfo.numPages; 14619 } 14620 get fingerprints() { 14621 return this._pdfInfo.fingerprints; 14622 } 14623 get isPureXfa() { 14624 return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); 14625 } 14626 get allXfaHtml() { 14627 return this._transport._htmlForXfa; 14628 } 14629 getPage(pageNumber) { 14630 return this._transport.getPage(pageNumber); 14631 } 14632 getPageIndex(ref) { 14633 return this._transport.getPageIndex(ref); 14634 } 14635 getDestinations() { 14636 return this._transport.getDestinations(); 14637 } 14638 getDestination(id) { 14639 return this._transport.getDestination(id); 14640 } 14641 getPageLabels() { 14642 return this._transport.getPageLabels(); 14643 } 14644 getPageLayout() { 14645 return this._transport.getPageLayout(); 14646 } 14647 getPageMode() { 14648 return this._transport.getPageMode(); 14649 } 14650 getViewerPreferences() { 14651 return this._transport.getViewerPreferences(); 14652 } 14653 getOpenAction() { 14654 return this._transport.getOpenAction(); 14655 } 14656 getAttachments() { 14657 return this._transport.getAttachments(); 14658 } 14659 getAnnotationsByType(types, pageIndexesToSkip) { 14660 return this._transport.getAnnotationsByType(types, pageIndexesToSkip); 14661 } 14662 getJSActions() { 14663 return this._transport.getDocJSActions(); 14664 } 14665 getOutline() { 14666 return this._transport.getOutline(); 14667 } 14668 getOptionalContentConfig({ 14669 intent = "display" 14670 } = {}) { 14671 const { 14672 renderingIntent 14673 } = this._transport.getRenderingIntent(intent); 14674 return this._transport.getOptionalContentConfig(renderingIntent); 14675 } 14676 getPermissions() { 14677 return this._transport.getPermissions(); 14678 } 14679 getMetadata() { 14680 return this._transport.getMetadata(); 14681 } 14682 getMarkInfo() { 14683 return this._transport.getMarkInfo(); 14684 } 14685 getData() { 14686 return this._transport.getData(); 14687 } 14688 saveDocument() { 14689 return this._transport.saveDocument(); 14690 } 14691 extractPages(pageInfos) { 14692 return this._transport.extractPages(pageInfos); 14693 } 14694 getDownloadInfo() { 14695 return this._transport.downloadInfoCapability.promise; 14696 } 14697 cleanup(keepLoadedFonts = false) { 14698 return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa); 14699 } 14700 destroy() { 14701 return this.loadingTask.destroy(); 14702 } 14703 cachedPageNumber(ref) { 14704 return this._transport.cachedPageNumber(ref); 14705 } 14706 get loadingParams() { 14707 return this._transport.loadingParams; 14708 } 14709 get loadingTask() { 14710 return this._transport.loadingTask; 14711 } 14712 getFieldObjects() { 14713 return this._transport.getFieldObjects(); 14714 } 14715 hasJSActions() { 14716 return this._transport.hasJSActions(); 14717 } 14718 getCalculationOrderIds() { 14719 return this._transport.getCalculationOrderIds(); 14720 } 14721} 14722class PDFPageProxy { 14723 #pendingCleanup = false; 14724 #pagesMapper = PagesMapper.instance; 14725 constructor(pageIndex, pageInfo, transport, pdfBug = false) { 14726 this._pageIndex = pageIndex; 14727 this._pageInfo = pageInfo; 14728 this._transport = transport; 14729 this._stats = pdfBug ? new StatTimer() : null; 14730 this._pdfBug = pdfBug; 14731 this.commonObjs = transport.commonObjs; 14732 this.objs = new PDFObjects(); 14733 this._intentStates = new Map(); 14734 this.destroyed = false; 14735 this.recordedBBoxes = null; 14736 } 14737 get pageNumber() { 14738 return this._pageIndex + 1; 14739 } 14740 set pageNumber(value) { 14741 this._pageIndex = value - 1; 14742 } 14743 get rotate() { 14744 return this._pageInfo.rotate; 14745 } 14746 get ref() { 14747 return this._pageInfo.ref; 14748 } 14749 get userUnit() { 14750 return this._pageInfo.userUnit; 14751 } 14752 get view() { 14753 return this._pageInfo.view; 14754 } 14755 getViewport({ 14756 scale, 14757 rotation = this.rotate, 14758 offsetX = 0, 14759 offsetY = 0, 14760 dontFlip = false 14761 } = {}) { 14762 return new PageViewport({ 14763 viewBox: this.view, 14764 userUnit: this.userUnit, 14765 scale, 14766 rotation, 14767 offsetX, 14768 offsetY, 14769 dontFlip 14770 }); 14771 } 14772 getAnnotations({ 14773 intent = "display" 14774 } = {}) { 14775 const { 14776 renderingIntent 14777 } = this._transport.getRenderingIntent(intent); 14778 return this._transport.getAnnotations(this._pageIndex, renderingIntent); 14779 } 14780 getJSActions() { 14781 return this._transport.getPageJSActions(this._pageIndex); 14782 } 14783 get filterFactory() { 14784 return this._transport.filterFactory; 14785 } 14786 get isPureXfa() { 14787 return shadow(this, "isPureXfa", !!this._transport._htmlForXfa); 14788 } 14789 async getXfa() { 14790 return this._transport._htmlForXfa?.children[this._pageIndex] || null; 14791 } 14792 render({ 14793 canvasContext, 14794 canvas = canvasContext.canvas, 14795 viewport, 14796 intent = "display", 14797 annotationMode = AnnotationMode.ENABLE, 14798 transform = null, 14799 background = null, 14800 optionalContentConfigPromise = null, 14801 annotationCanvasMap = null, 14802 pageColors = null, 14803 printAnnotationStorage = null, 14804 isEditing = false, 14805 recordOperations = false, 14806 operationsFilter = null 14807 }) { 14808 this._stats?.time("Overall"); 14809 const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing); 14810 const { 14811 renderingIntent, 14812 cacheKey 14813 } = intentArgs; 14814 this.#pendingCleanup = false; 14815 optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent); 14816 let intentState = this._intentStates.get(cacheKey); 14817 if (!intentState) { 14818 intentState = Object.create(null); 14819 this._intentStates.set(cacheKey, intentState); 14820 } 14821 if (intentState.streamReaderCancelTimeout) { 14822 clearTimeout(intentState.streamReaderCancelTimeout); 14823 intentState.streamReaderCancelTimeout = null; 14824 } 14825 const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); 14826 if (!intentState.displayReadyCapability) { 14827 intentState.displayReadyCapability = Promise.withResolvers(); 14828 intentState.operatorList = { 14829 fnArray: [], 14830 argsArray: [], 14831 lastChunk: false, 14832 separateAnnots: null 14833 }; 14834 this._stats?.time("Page Request"); 14835 this._pumpOperatorList(intentArgs); 14836 } 14837 const recordForDebugger = Boolean(this._pdfBug && globalThis.StepperManager?.enabled); 14838 const shouldRecordOperations = !this.recordedBBoxes && (recordOperations || recordForDebugger); 14839 const complete = error => { 14840 intentState.renderTasks.delete(internalRenderTask); 14841 if (shouldRecordOperations) { 14842 const recordedBBoxes = internalRenderTask.gfx?.dependencyTracker.take(); 14843 if (recordedBBoxes) { 14844 if (internalRenderTask.stepper) { 14845 internalRenderTask.stepper.setOperatorBBoxes(recordedBBoxes, internalRenderTask.gfx.dependencyTracker.takeDebugMetadata()); 14846 } 14847 if (recordOperations) { 14848 this.recordedBBoxes = recordedBBoxes; 14849 } 14850 } 14851 } 14852 if (intentPrint) { 14853 this.#pendingCleanup = true; 14854 } 14855 this.#tryCleanup(); 14856 if (error) { 14857 internalRenderTask.capability.reject(error); 14858 this._abortOperatorList({ 14859 intentState, 14860 reason: error instanceof Error ? error : new Error(error) 14861 }); 14862 } else { 14863 internalRenderTask.capability.resolve(); 14864 } 14865 if (this._stats) { 14866 this._stats.timeEnd("Rendering"); 14867 this._stats.timeEnd("Overall"); 14868 if (globalThis.Stats?.enabled) { 14869 globalThis.Stats.add(this.pageNumber, this._stats); 14870 } 14871 } 14872 }; 14873 const internalRenderTask = new InternalRenderTask({ 14874 callback: complete, 14875 params: { 14876 canvas, 14877 canvasContext, 14878 dependencyTracker: shouldRecordOperations ? new CanvasDependencyTracker(canvas, intentState.operatorList.length, recordForDebugger) : null, 14879 viewport, 14880 transform, 14881 background 14882 }, 14883 objs: this.objs, 14884 commonObjs: this.commonObjs, 14885 annotationCanvasMap, 14886 operatorList: intentState.operatorList, 14887 pageIndex: this._pageIndex, 14888 canvasFactory: this._transport.canvasFactory, 14889 filterFactory: this._transport.filterFactory, 14890 useRequestAnimationFrame: !intentPrint, 14891 pdfBug: this._pdfBug, 14892 pageColors, 14893 enableHWA: this._transport.enableHWA, 14894 operationsFilter 14895 }); 14896 (intentState.renderTasks ||= new Set()).add(internalRenderTask); 14897 const renderTask = internalRenderTask.task; 14898 Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => { 14899 if (this.destroyed) { 14900 complete(); 14901 return; 14902 } 14903 this._stats?.time("Rendering"); 14904 if (!(optionalContentConfig.renderingIntent & renderingIntent)) { 14905 throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` " + "and `PDFDocumentProxy.getOptionalContentConfig` methods."); 14906 } 14907 internalRenderTask.initializeGraphics({ 14908 transparency, 14909 optionalContentConfig 14910 }); 14911 internalRenderTask.operatorListChanged(); 14912 }).catch(complete); 14913 return renderTask; 14914 } 14915 getOperatorList({ 14916 intent = "display", 14917 annotationMode = AnnotationMode.ENABLE, 14918 printAnnotationStorage = null, 14919 isEditing = false 14920 } = {}) { 14921 function operatorListChanged() { 14922 if (intentState.operatorList.lastChunk) { 14923 intentState.opListReadCapability.resolve(intentState.operatorList); 14924 intentState.renderTasks.delete(opListTask); 14925 } 14926 } 14927 const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing, true); 14928 let intentState = this._intentStates.get(intentArgs.cacheKey); 14929 if (!intentState) { 14930 intentState = Object.create(null); 14931 this._intentStates.set(intentArgs.cacheKey, intentState); 14932 } 14933 let opListTask; 14934 if (!intentState.opListReadCapability) { 14935 opListTask = Object.create(null); 14936 opListTask.operatorListChanged = operatorListChanged; 14937 intentState.opListReadCapability = Promise.withResolvers(); 14938 (intentState.renderTasks ||= new Set()).add(opListTask); 14939 intentState.operatorList = { 14940 fnArray: [], 14941 argsArray: [], 14942 lastChunk: false, 14943 separateAnnots: null 14944 }; 14945 this._stats?.time("Page Request"); 14946 this._pumpOperatorList(intentArgs); 14947 } 14948 return intentState.opListReadCapability.promise; 14949 } 14950 streamTextContent({ 14951 includeMarkedContent = false, 14952 disableNormalization = false 14953 } = {}) { 14954 const TEXT_CONTENT_CHUNK_SIZE = 100; 14955 return this._transport.messageHandler.sendWithStream("GetTextContent", { 14956 pageId: this.#pagesMapper.getPageId(this._pageIndex + 1) - 1, 14957 pageIndex: this._pageIndex, 14958 includeMarkedContent: includeMarkedContent === true, 14959 disableNormalization: disableNormalization === true 14960 }, { 14961 highWaterMark: TEXT_CONTENT_CHUNK_SIZE, 14962 size(textContent) { 14963 return textContent.items.length; 14964 } 14965 }); 14966 } 14967 getTextContent(params = {}) { 14968 if (this._transport._htmlForXfa) { 14969 return this.getXfa().then(xfa => XfaText.textContent(xfa)); 14970 } 14971 const readableStream = this.streamTextContent(params); 14972 return new Promise(function (resolve, reject) { 14973 function pump() { 14974 reader.read().then(function ({ 14975 value, 14976 done 14977 }) { 14978 if (done) { 14979 resolve(textContent); 14980 return; 14981 } 14982 textContent.lang ??= value.lang; 14983 Object.assign(textContent.styles, value.styles); 14984 textContent.items.push(...value.items); 14985 pump(); 14986 }, reject); 14987 } 14988 const reader = readableStream.getReader(); 14989 const textContent = { 14990 items: [], 14991 styles: Object.create(null), 14992 lang: null 14993 }; 14994 pump(); 14995 }); 14996 } 14997 getStructTree() { 14998 return this._transport.getStructTree(this._pageIndex); 14999 } 15000 _destroy() { 15001 this.destroyed = true; 15002 const waitOn = []; 15003 for (const intentState of this._intentStates.values()) { 15004 this._abortOperatorList({ 15005 intentState, 15006 reason: new Error("Page was destroyed."), 15007 force: true 15008 }); 15009 if (intentState.opListReadCapability) { 15010 continue; 15011 } 15012 for (const internalRenderTask of intentState.renderTasks) { 15013 waitOn.push(internalRenderTask.completed); 15014 internalRenderTask.cancel(); 15015 } 15016 } 15017 this.objs.clear(); 15018 this.#pendingCleanup = false; 15019 return Promise.all(waitOn); 15020 } 15021 cleanup(resetStats = false) { 15022 this.#pendingCleanup = true; 15023 const success = this.#tryCleanup(); 15024 if (resetStats && success) { 15025 this._stats &&= new StatTimer(); 15026 } 15027 return success; 15028 } 15029 #tryCleanup() { 15030 if (!this.#pendingCleanup || this.destroyed) { 15031 return false;
vendor: 32,691 bytes, lines 15032-16063
15032 } 15033 for (const { 15034 renderTasks, 15035 operatorList 15036 } of this._intentStates.values()) { 15037 if (renderTasks.size > 0 || !operatorList.lastChunk) { 15038 return false; 15039 } 15040 } 15041 this._intentStates.clear(); 15042 this.objs.clear(); 15043 this.#pendingCleanup = false; 15044 return true; 15045 } 15046 _startRenderPage(transparency, cacheKey) { 15047 const intentState = this._intentStates.get(cacheKey); 15048 if (!intentState) { 15049 return; 15050 } 15051 this._stats?.timeEnd("Page Request"); 15052 intentState.displayReadyCapability?.resolve(transparency); 15053 } 15054 _renderPageChunk(operatorListChunk, intentState) { 15055 for (let i = 0, ii = operatorListChunk.length; i < ii; i++) { 15056 intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); 15057 intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]); 15058 } 15059 intentState.operatorList.lastChunk = operatorListChunk.lastChunk; 15060 intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots; 15061 for (const internalRenderTask of intentState.renderTasks) { 15062 internalRenderTask.operatorListChanged(); 15063 } 15064 if (operatorListChunk.lastChunk) { 15065 this.#tryCleanup(); 15066 } 15067 } 15068 _pumpOperatorList({ 15069 renderingIntent, 15070 cacheKey, 15071 annotationStorageSerializable, 15072 modifiedIds 15073 }) { 15074 const { 15075 map, 15076 transfer 15077 } = annotationStorageSerializable; 15078 const readableStream = this._transport.messageHandler.sendWithStream("GetOperatorList", { 15079 pageId: this.#pagesMapper.getPageId(this._pageIndex + 1) - 1, 15080 pageIndex: this._pageIndex, 15081 intent: renderingIntent, 15082 cacheKey, 15083 annotationStorage: map, 15084 modifiedIds 15085 }, transfer); 15086 const reader = readableStream.getReader(); 15087 const intentState = this._intentStates.get(cacheKey); 15088 intentState.streamReader = reader; 15089 const pump = () => { 15090 reader.read().then(({ 15091 value, 15092 done 15093 }) => { 15094 if (done) { 15095 intentState.streamReader = null; 15096 return; 15097 } 15098 if (this._transport.destroyed) { 15099 return; 15100 } 15101 this._renderPageChunk(value, intentState); 15102 pump(); 15103 }, reason => { 15104 intentState.streamReader = null; 15105 if (this._transport.destroyed) { 15106 return; 15107 } 15108 if (intentState.operatorList) { 15109 intentState.operatorList.lastChunk = true; 15110 for (const internalRenderTask of intentState.renderTasks) { 15111 internalRenderTask.operatorListChanged(); 15112 } 15113 this.#tryCleanup(); 15114 } 15115 if (intentState.displayReadyCapability) { 15116 intentState.displayReadyCapability.reject(reason); 15117 } else if (intentState.opListReadCapability) { 15118 intentState.opListReadCapability.reject(reason); 15119 } else { 15120 throw reason; 15121 } 15122 }); 15123 }; 15124 pump(); 15125 } 15126 _abortOperatorList({ 15127 intentState, 15128 reason, 15129 force = false 15130 }) { 15131 if (!intentState.streamReader) { 15132 return; 15133 } 15134 if (intentState.streamReaderCancelTimeout) { 15135 clearTimeout(intentState.streamReaderCancelTimeout); 15136 intentState.streamReaderCancelTimeout = null; 15137 } 15138 if (!force) { 15139 if (intentState.renderTasks.size > 0) { 15140 return; 15141 } 15142 if (reason instanceof RenderingCancelledException) { 15143 let delay = RENDERING_CANCELLED_TIMEOUT; 15144 if (reason.extraDelay > 0 && reason.extraDelay < 1000) { 15145 delay += reason.extraDelay; 15146 } 15147 intentState.streamReaderCancelTimeout = setTimeout(() => { 15148 intentState.streamReaderCancelTimeout = null; 15149 this._abortOperatorList({ 15150 intentState, 15151 reason, 15152 force: true 15153 }); 15154 }, delay); 15155 return; 15156 } 15157 } 15158 intentState.streamReader.cancel(new AbortException(reason.message)).catch(() => {}); 15159 intentState.streamReader = null; 15160 if (this._transport.destroyed) { 15161 return; 15162 } 15163 for (const [curCacheKey, curIntentState] of this._intentStates) { 15164 if (curIntentState === intentState) { 15165 this._intentStates.delete(curCacheKey); 15166 break; 15167 } 15168 } 15169 this.cleanup(); 15170 } 15171 get stats() { 15172 return this._stats; 15173 } 15174} 15175class PDFWorker { 15176 #capability = Promise.withResolvers(); 15177 #messageHandler = null; 15178 #port = null; 15179 #webWorker = null; 15180 static #fakeWorkerId = 0; 15181 static #isWorkerDisabled = false; 15182 static #workerPorts = new WeakMap(); 15183 static { 15184 if (isNodeJS) { 15185 this.#isWorkerDisabled = true; 15186 GlobalWorkerOptions.workerSrc ||= "./pdf.worker.mjs"; 15187 } 15188 this._isSameOrigin = (baseUrl, otherUrl) => { 15189 const base = URL.parse(baseUrl); 15190 if (!base?.origin || base.origin === "null") { 15191 return false; 15192 } 15193 const other = new URL(otherUrl, base); 15194 return base.origin === other.origin; 15195 }; 15196 this._createCDNWrapper = url => { 15197 const wrapper = `await import("${url}");`; 15198 return URL.createObjectURL(new Blob([wrapper], { 15199 type: "text/javascript" 15200 })); 15201 }; 15202 this.fromPort = params => { 15203 deprecated("`PDFWorker.fromPort` - please use `PDFWorker.create` instead."); 15204 if (!params?.port) { 15205 throw new Error("PDFWorker.fromPort - invalid method signature."); 15206 } 15207 return this.create(params); 15208 }; 15209 } 15210 constructor({ 15211 name = null, 15212 port = null, 15213 verbosity = getVerbosityLevel() 15214 } = {}) { 15215 this.name = name; 15216 this.destroyed = false; 15217 this.verbosity = verbosity; 15218 if (port) { 15219 if (PDFWorker.#workerPorts.has(port)) { 15220 throw new Error("Cannot use more than one PDFWorker per port."); 15221 } 15222 PDFWorker.#workerPorts.set(port, this); 15223 this.#initializeFromPort(port); 15224 } else { 15225 this.#initialize(); 15226 } 15227 } 15228 get promise() { 15229 return this.#capability.promise; 15230 } 15231 #resolve() { 15232 this.#capability.resolve(); 15233 this.#messageHandler.send("configure", { 15234 verbosity: this.verbosity 15235 }); 15236 } 15237 get port() { 15238 return this.#port; 15239 } 15240 get messageHandler() { 15241 return this.#messageHandler; 15242 } 15243 #initializeFromPort(port) { 15244 this.#port = port; 15245 this.#messageHandler = new MessageHandler("main", "worker", port); 15246 this.#messageHandler.on("ready", () => {}); 15247 this.#resolve(); 15248 } 15249 #initialize() { 15250 if (PDFWorker.#isWorkerDisabled || PDFWorker.#mainThreadWorkerMessageHandler) { 15251 this.#setupFakeWorker(); 15252 return; 15253 } 15254 let { 15255 workerSrc 15256 } = PDFWorker; 15257 try { 15258 if (!PDFWorker._isSameOrigin(window.location, workerSrc)) { 15259 workerSrc = PDFWorker._createCDNWrapper(new URL(workerSrc, window.location).href); 15260 } 15261 const worker = new Worker(workerSrc, { 15262 type: "module" 15263 }); 15264 const messageHandler = new MessageHandler("main", "worker", worker); 15265 const terminateEarly = () => { 15266 ac.abort(); 15267 messageHandler.destroy(); 15268 worker.terminate(); 15269 if (this.destroyed) { 15270 this.#capability.reject(new Error("Worker was destroyed")); 15271 } else { 15272 this.#setupFakeWorker(); 15273 } 15274 }; 15275 const ac = new AbortController(); 15276 worker.addEventListener("error", () => { 15277 if (!this.#webWorker) { 15278 terminateEarly(); 15279 } 15280 }, { 15281 signal: ac.signal 15282 }); 15283 messageHandler.on("test", data => { 15284 ac.abort(); 15285 if (this.destroyed || !data) { 15286 terminateEarly(); 15287 return; 15288 } 15289 this.#messageHandler = messageHandler; 15290 this.#port = worker; 15291 this.#webWorker = worker; 15292 this.#resolve(); 15293 }); 15294 messageHandler.on("ready", data => { 15295 ac.abort(); 15296 if (this.destroyed) { 15297 terminateEarly(); 15298 return; 15299 } 15300 try { 15301 sendTest(); 15302 } catch { 15303 this.#setupFakeWorker(); 15304 } 15305 }); 15306 const sendTest = () => { 15307 const testObj = new Uint8Array(); 15308 messageHandler.send("test", testObj, [testObj.buffer]); 15309 }; 15310 sendTest(); 15311 return; 15312 } catch { 15313 info("The worker has been disabled."); 15314 } 15315 this.#setupFakeWorker(); 15316 } 15317 #setupFakeWorker() { 15318 if (!PDFWorker.#isWorkerDisabled) { 15319 warn("Setting up fake worker."); 15320 PDFWorker.#isWorkerDisabled = true; 15321 } 15322 PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => { 15323 if (this.destroyed) { 15324 this.#capability.reject(new Error("Worker was destroyed")); 15325 return; 15326 } 15327 const port = new LoopbackPort(); 15328 this.#port = port; 15329 const id = `fake${PDFWorker.#fakeWorkerId++}`; 15330 const workerHandler = new MessageHandler(id + "_worker", id, port); 15331 WorkerMessageHandler.setup(workerHandler, port); 15332 this.#messageHandler = new MessageHandler(id, id + "_worker", port); 15333 this.#resolve(); 15334 }).catch(reason => { 15335 this.#capability.reject(new Error(`Setting up fake worker failed: "${reason.message}".`)); 15336 }); 15337 } 15338 destroy() { 15339 this.destroyed = true; 15340 this.#webWorker?.terminate(); 15341 this.#webWorker = null; 15342 PDFWorker.#workerPorts.delete(this.#port); 15343 this.#port = null; 15344 this.#messageHandler?.destroy(); 15345 this.#messageHandler = null; 15346 } 15347 static create(params) { 15348 const cachedPort = this.#workerPorts.get(params?.port); 15349 if (cachedPort) { 15350 if (cachedPort._pendingDestroy) { 15351 throw new Error("PDFWorker.create - the worker is being destroyed.\n" + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls."); 15352 } 15353 return cachedPort; 15354 } 15355 return new PDFWorker(params); 15356 } 15357 static get workerSrc() { 15358 if (GlobalWorkerOptions.workerSrc) { 15359 return GlobalWorkerOptions.workerSrc; 15360 } 15361 throw new Error('No "GlobalWorkerOptions.workerSrc" specified.'); 15362 } 15363 static get #mainThreadWorkerMessageHandler() { 15364 try { 15365 return globalThis.pdfjsWorker?.WorkerMessageHandler || null; 15366 } catch { 15367 return null; 15368 } 15369 } 15370 static get _setupFakeWorkerGlobal() { 15371 const loader = async () => { 15372 if (this.#mainThreadWorkerMessageHandler) { 15373 return this.#mainThreadWorkerMessageHandler; 15374 } 15375 const worker = await import( 15376 /*webpackIgnore: true*/ 15377 /*@vite-ignore*/ 15378 this.workerSrc); 15379 return worker.WorkerMessageHandler; 15380 }; 15381 return shadow(this, "_setupFakeWorkerGlobal", loader()); 15382 } 15383} 15384class WorkerTransport { 15385 downloadInfoCapability = Promise.withResolvers(); 15386 #fullReader = null; 15387 #methodPromises = new Map(); 15388 #networkStream = null; 15389 #pageCache = new Map(); 15390 #pagePromises = new Map(); 15391 #pageRefCache = new Map(); 15392 #passwordCapability = null; 15393 #pagesMapper = PagesMapper.instance; 15394 constructor(messageHandler, loadingTask, networkStream, params, factory) { 15395 this.messageHandler = messageHandler; 15396 this.loadingTask = loadingTask; 15397 this.#networkStream = networkStream; 15398 this.commonObjs = new PDFObjects(); 15399 this.fontLoader = new FontLoader({ 15400 ownerDocument: params.ownerDocument, 15401 styleElement: params.styleElement 15402 }); 15403 this.enableHWA = params.enableHWA; 15404 this.loadingParams = params.loadingParams; 15405 this._params = params; 15406 this.canvasFactory = factory.canvasFactory; 15407 this.filterFactory = factory.filterFactory; 15408 this.cMapReaderFactory = factory.cMapReaderFactory; 15409 this.standardFontDataFactory = factory.standardFontDataFactory; 15410 this.wasmFactory = factory.wasmFactory; 15411 this.destroyed = false; 15412 this.destroyCapability = null; 15413 this.setupMessageHandler(); 15414 this.#pagesMapper.addListener(this.#updateCaches.bind(this)); 15415 } 15416 #updateCaches() { 15417 const newPageCache = new Map(); 15418 const newPromiseCache = new Map(); 15419 for (let i = 0, ii = this.#pagesMapper.pagesNumber; i < ii; i++) { 15420 const prevPageIndex = this.#pagesMapper.getPrevPageNumber(i + 1) - 1; 15421 const page = this.#pageCache.get(prevPageIndex); 15422 if (page) { 15423 newPageCache.set(i, page); 15424 } 15425 const promise = this.#pagePromises.get(prevPageIndex); 15426 if (promise) { 15427 newPromiseCache.set(i, promise); 15428 } 15429 } 15430 this.#pageCache = newPageCache; 15431 this.#pagePromises = newPromiseCache; 15432 } 15433 #cacheSimpleMethod(name, data = null) { 15434 const cachedPromise = this.#methodPromises.get(name); 15435 if (cachedPromise) { 15436 return cachedPromise; 15437 } 15438 const promise = this.messageHandler.sendWithPromise(name, data); 15439 this.#methodPromises.set(name, promise); 15440 return promise; 15441 } 15442 #onProgress({ 15443 loaded, 15444 total 15445 }) { 15446 this.loadingTask.onProgress?.({ 15447 loaded, 15448 total, 15449 percent: MathClamp(Math.round(loaded / total * 100), 0, 100) 15450 }); 15451 } 15452 get annotationStorage() { 15453 return shadow(this, "annotationStorage", new AnnotationStorage()); 15454 } 15455 getRenderingIntent(intent, annotationMode = AnnotationMode.ENABLE, printAnnotationStorage = null, isEditing = false, isOpList = false) { 15456 let renderingIntent = RenderingIntentFlag.DISPLAY; 15457 let annotationStorageSerializable = SerializableEmpty; 15458 switch (intent) { 15459 case "any": 15460 renderingIntent = RenderingIntentFlag.ANY; 15461 break; 15462 case "display": 15463 break; 15464 case "print": 15465 renderingIntent = RenderingIntentFlag.PRINT; 15466 break; 15467 default: 15468 warn(`getRenderingIntent - invalid intent: ${intent}`); 15469 } 15470 const annotationStorage = renderingIntent & RenderingIntentFlag.PRINT && printAnnotationStorage instanceof PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage; 15471 switch (annotationMode) { 15472 case AnnotationMode.DISABLE: 15473 renderingIntent += RenderingIntentFlag.ANNOTATIONS_DISABLE; 15474 break; 15475 case AnnotationMode.ENABLE: 15476 break; 15477 case AnnotationMode.ENABLE_FORMS: 15478 renderingIntent += RenderingIntentFlag.ANNOTATIONS_FORMS; 15479 break; 15480 case AnnotationMode.ENABLE_STORAGE: 15481 renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE; 15482 annotationStorageSerializable = annotationStorage.serializable; 15483 break; 15484 default: 15485 warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`); 15486 } 15487 if (isEditing) { 15488 renderingIntent += RenderingIntentFlag.IS_EDITING; 15489 } 15490 if (isOpList) { 15491 renderingIntent += RenderingIntentFlag.OPLIST; 15492 } 15493 const { 15494 ids: modifiedIds, 15495 hash: modifiedIdsHash 15496 } = annotationStorage.modifiedIds; 15497 const cacheKeyBuf = [renderingIntent, annotationStorageSerializable.hash, modifiedIdsHash]; 15498 return { 15499 renderingIntent, 15500 cacheKey: cacheKeyBuf.join("_"), 15501 annotationStorageSerializable, 15502 modifiedIds 15503 }; 15504 } 15505 destroy() { 15506 if (this.destroyCapability) { 15507 return this.destroyCapability.promise; 15508 } 15509 this.destroyed = true; 15510 this.destroyCapability = Promise.withResolvers(); 15511 this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback")); 15512 const waitOn = []; 15513 for (const page of this.#pageCache.values()) { 15514 waitOn.push(page._destroy()); 15515 } 15516 this.#pageCache.clear(); 15517 this.#pagePromises.clear(); 15518 this.#pageRefCache.clear(); 15519 if (this.hasOwnProperty("annotationStorage")) { 15520 this.annotationStorage.resetModified(); 15521 } 15522 const terminated = this.messageHandler.sendWithPromise("Terminate", null); 15523 waitOn.push(terminated); 15524 Promise.all(waitOn).then(() => { 15525 this.commonObjs.clear(); 15526 this.fontLoader.clear(); 15527 this.#methodPromises.clear(); 15528 this.filterFactory.destroy(); 15529 TextLayer.cleanup(); 15530 this.#networkStream?.cancelAllRequests(new AbortException("Worker was terminated.")); 15531 this.messageHandler?.destroy(); 15532 this.messageHandler = null; 15533 this.destroyCapability.resolve(); 15534 }, this.destroyCapability.reject); 15535 return this.destroyCapability.promise; 15536 } 15537 setupMessageHandler() { 15538 const { 15539 messageHandler, 15540 loadingTask 15541 } = this; 15542 messageHandler.on("GetReader", (data, sink) => { 15543 assert(this.#networkStream, "GetReader - no `BasePDFStream` instance available."); 15544 this.#fullReader = this.#networkStream.getFullReader(); 15545 this.#fullReader.onProgress = evt => this.#onProgress(evt); 15546 sink.onPull = () => { 15547 this.#fullReader.read().then(function ({ 15548 value, 15549 done 15550 }) { 15551 if (done) { 15552 sink.close(); 15553 return; 15554 } 15555 assert(value instanceof ArrayBuffer, "GetReader - expected an ArrayBuffer."); 15556 sink.enqueue(new Uint8Array(value), 1, [value]); 15557 }).catch(reason => { 15558 sink.error(reason); 15559 }); 15560 }; 15561 sink.onCancel = reason => { 15562 this.#fullReader.cancel(reason); 15563 sink.ready.catch(readyReason => { 15564 if (this.destroyed) { 15565 return; 15566 } 15567 throw readyReason; 15568 }); 15569 }; 15570 }); 15571 messageHandler.on("ReaderHeadersReady", async data => { 15572 await this.#fullReader.headersReady; 15573 const { 15574 isStreamingSupported, 15575 isRangeSupported, 15576 contentLength 15577 } = this.#fullReader; 15578 if (isStreamingSupported && isRangeSupported) { 15579 this.#fullReader.onProgress = null; 15580 } 15581 return { 15582 isStreamingSupported, 15583 isRangeSupported, 15584 contentLength 15585 }; 15586 }); 15587 messageHandler.on("GetRangeReader", (data, sink) => { 15588 assert(this.#networkStream, "GetRangeReader - no `BasePDFStream` instance available."); 15589 const rangeReader = this.#networkStream.getRangeReader(data.begin, data.end); 15590 if (!rangeReader) { 15591 sink.close(); 15592 return; 15593 } 15594 sink.onPull = () => { 15595 rangeReader.read().then(function ({ 15596 value, 15597 done 15598 }) { 15599 if (done) { 15600 sink.close(); 15601 return; 15602 } 15603 assert(value instanceof ArrayBuffer, "GetRangeReader - expected an ArrayBuffer."); 15604 sink.enqueue(new Uint8Array(value), 1, [value]); 15605 }).catch(reason => { 15606 sink.error(reason); 15607 }); 15608 }; 15609 sink.onCancel = reason => { 15610 rangeReader.cancel(reason); 15611 sink.ready.catch(readyReason => { 15612 if (this.destroyed) { 15613 return; 15614 } 15615 throw readyReason; 15616 }); 15617 }; 15618 }); 15619 messageHandler.on("GetDoc", ({ 15620 pdfInfo 15621 }) => { 15622 this.#pagesMapper.pagesNumber = pdfInfo.numPages; 15623 this._numPages = pdfInfo.numPages; 15624 this._htmlForXfa = pdfInfo.htmlForXfa; 15625 delete pdfInfo.htmlForXfa; 15626 loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this)); 15627 }); 15628 messageHandler.on("DocException", ex => { 15629 loadingTask._capability.reject(wrapReason(ex)); 15630 }); 15631 messageHandler.on("PasswordRequest", ex => { 15632 this.#passwordCapability = Promise.withResolvers(); 15633 try { 15634 if (!loadingTask.onPassword) { 15635 throw wrapReason(ex); 15636 } 15637 const updatePassword = password => { 15638 if (password instanceof Error) { 15639 this.#passwordCapability.reject(password); 15640 } else { 15641 this.#passwordCapability.resolve({ 15642 password 15643 }); 15644 } 15645 }; 15646 loadingTask.onPassword(updatePassword, ex.code); 15647 } catch (err) { 15648 this.#passwordCapability.reject(err); 15649 } 15650 return this.#passwordCapability.promise; 15651 }); 15652 messageHandler.on("DataLoaded", data => { 15653 this.#onProgress({ 15654 loaded: data.length, 15655 total: data.length 15656 }); 15657 this.downloadInfoCapability.resolve(data); 15658 }); 15659 messageHandler.on("StartRenderPage", data => { 15660 if (this.destroyed) { 15661 return; 15662 } 15663 const page = this.#pageCache.get(data.pageIndex); 15664 page._startRenderPage(data.transparency, data.cacheKey); 15665 }); 15666 messageHandler.on("commonobj", ([id, type, exportedData]) => { 15667 if (this.destroyed) { 15668 return null; 15669 } 15670 if (this.commonObjs.has(id)) { 15671 return null; 15672 } 15673 switch (type) { 15674 case "Font": 15675 if ("error" in exportedData) { 15676 const exportedError = exportedData.error; 15677 warn(`Error during font loading: ${exportedError}`); 15678 this.commonObjs.resolve(id, exportedError); 15679 break; 15680 } 15681 const fontData = new FontInfo(exportedData); 15682 const inspectFont = this._params.pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null; 15683 const font = new FontFaceObject(fontData, inspectFont, exportedData.extra, exportedData.charProcOperatorList); 15684 this.fontLoader.bind(font).catch(() => messageHandler.sendWithPromise("FontFallback", { 15685 id 15686 })).finally(() => { 15687 if (!font.fontExtraProperties && font.data) { 15688 font.clearData(); 15689 } 15690 this.commonObjs.resolve(id, font); 15691 }); 15692 break; 15693 case "CopyLocalImage": 15694 const { 15695 imageRef 15696 } = exportedData; 15697 assert(imageRef, "The imageRef must be defined."); 15698 for (const pageProxy of this.#pageCache.values()) { 15699 for (const [, data] of pageProxy.objs) { 15700 if (data?.ref !== imageRef) { 15701 continue; 15702 } 15703 if (!data.dataLen) { 15704 return null; 15705 } 15706 this.commonObjs.resolve(id, structuredClone(data)); 15707 return data.dataLen; 15708 } 15709 } 15710 break; 15711 case "FontPath": 15712 this.commonObjs.resolve(id, new FontPathInfo(exportedData)); 15713 break; 15714 case "Image": 15715 this.commonObjs.resolve(id, exportedData); 15716 break; 15717 case "Pattern": 15718 const pattern = new PatternInfo(exportedData); 15719 this.commonObjs.resolve(id, pattern.getIR()); 15720 break; 15721 default: 15722 throw new Error(`Got unknown common object type ${type}`); 15723 } 15724 return null; 15725 }); 15726 messageHandler.on("obj", ([id, pageIndex, type, imageData]) => { 15727 if (this.destroyed) { 15728 return; 15729 } 15730 const pageProxy = this.#pageCache.get(pageIndex); 15731 if (pageProxy.objs.has(id)) { 15732 return; 15733 } 15734 if (pageProxy._intentStates.size === 0) { 15735 imageData?.bitmap?.close(); 15736 return; 15737 } 15738 switch (type) { 15739 case "Image": 15740 case "Pattern": 15741 pageProxy.objs.resolve(id, imageData); 15742 break; 15743 default: 15744 throw new Error(`Got unknown object type ${type}`); 15745 } 15746 }); 15747 messageHandler.on("DocProgress", data => { 15748 if (this.destroyed) { 15749 return; 15750 } 15751 this.#onProgress(data); 15752 }); 15753 messageHandler.on("FetchBinaryData", async data => { 15754 if (this.destroyed) { 15755 throw new Error("Worker was destroyed."); 15756 } 15757 const factory = this[data.type]; 15758 if (!factory) { 15759 throw new Error(`${data.type} not initialized, see the \`useWorkerFetch\` parameter.`); 15760 } 15761 return factory.fetch(data); 15762 }); 15763 } 15764 getData() { 15765 return this.messageHandler.sendWithPromise("GetData", null); 15766 } 15767 saveDocument() { 15768 if (this.annotationStorage.size <= 0) { 15769 warn("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead."); 15770 } 15771 const { 15772 map, 15773 transfer 15774 } = this.annotationStorage.serializable; 15775 return this.messageHandler.sendWithPromise("SaveDocument", { 15776 isPureXfa: !!this._htmlForXfa, 15777 numPages: this._numPages, 15778 annotationStorage: map, 15779 filename: this.#fullReader?.filename ?? null 15780 }, transfer).finally(() => { 15781 this.annotationStorage.resetModified(); 15782 }); 15783 } 15784 extractPages(pageInfos) { 15785 return this.messageHandler.sendWithPromise("ExtractPages", { 15786 pageInfos 15787 }); 15788 } 15789 getPage(pageNumber) { 15790 if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this.#pagesMapper.pagesNumber) { 15791 return Promise.reject(new Error("Invalid page request.")); 15792 } 15793 const pageIndex = pageNumber - 1; 15794 const newPageIndex = this.#pagesMapper.getPageId(pageNumber) - 1; 15795 const cachedPromise = this.#pagePromises.get(pageIndex); 15796 if (cachedPromise) { 15797 return cachedPromise; 15798 } 15799 const promise = this.messageHandler.sendWithPromise("GetPage", { 15800 pageIndex: newPageIndex 15801 }).then(pageInfo => { 15802 if (this.destroyed) { 15803 throw new Error("Transport destroyed"); 15804 } 15805 if (pageInfo.refStr) { 15806 this.#pageRefCache.set(pageInfo.refStr, newPageIndex); 15807 } 15808 const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.pdfBug); 15809 this.#pageCache.set(pageIndex, page); 15810 return page; 15811 }); 15812 this.#pagePromises.set(pageIndex, promise); 15813 return promise; 15814 } 15815 async getPageIndex(ref) { 15816 if (!isRefProxy(ref)) { 15817 throw new Error("Invalid pageIndex request."); 15818 } 15819 const index = await this.messageHandler.sendWithPromise("GetPageIndex", { 15820 num: ref.num, 15821 gen: ref.gen 15822 }); 15823 return this.#pagesMapper.getPageNumber(index + 1) - 1; 15824 } 15825 getAnnotations(pageIndex, intent) { 15826 return this.messageHandler.sendWithPromise("GetAnnotations", { 15827 pageIndex: this.#pagesMapper.getPageId(pageIndex + 1) - 1, 15828 intent 15829 }); 15830 } 15831 getFieldObjects() { 15832 return this.#cacheSimpleMethod("GetFieldObjects"); 15833 } 15834 hasJSActions() { 15835 return this.#cacheSimpleMethod("HasJSActions"); 15836 } 15837 getCalculationOrderIds() { 15838 return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null); 15839 } 15840 getDestinations() { 15841 return this.messageHandler.sendWithPromise("GetDestinations", null); 15842 } 15843 getDestination(id) { 15844 if (typeof id !== "string") { 15845 return Promise.reject(new Error("Invalid destination request.")); 15846 } 15847 return this.messageHandler.sendWithPromise("GetDestination", { 15848 id 15849 }); 15850 } 15851 getPageLabels() { 15852 return this.messageHandler.sendWithPromise("GetPageLabels", null); 15853 } 15854 getPageLayout() { 15855 return this.messageHandler.sendWithPromise("GetPageLayout", null); 15856 } 15857 getPageMode() { 15858 return this.messageHandler.sendWithPromise("GetPageMode", null); 15859 } 15860 getViewerPreferences() { 15861 return this.messageHandler.sendWithPromise("GetViewerPreferences", null); 15862 } 15863 getOpenAction() { 15864 return this.messageHandler.sendWithPromise("GetOpenAction", null); 15865 } 15866 getAttachments() { 15867 return this.messageHandler.sendWithPromise("GetAttachments", null); 15868 } 15869 getAnnotationsByType(types, pageIndexesToSkip) { 15870 return this.messageHandler.sendWithPromise("GetAnnotationsByType", { 15871 types, 15872 pageIndexesToSkip 15873 }); 15874 } 15875 getDocJSActions() { 15876 return this.#cacheSimpleMethod("GetDocJSActions"); 15877 } 15878 getPageJSActions(pageIndex) { 15879 return this.messageHandler.sendWithPromise("GetPageJSActions", { 15880 pageIndex: this.#pagesMapper.getPageId(pageIndex + 1) - 1 15881 }); 15882 } 15883 getStructTree(pageIndex) { 15884 return this.messageHandler.sendWithPromise("GetStructTree", { 15885 pageIndex: this.#pagesMapper.getPageId(pageIndex + 1) - 1 15886 }); 15887 } 15888 getOutline() { 15889 return this.messageHandler.sendWithPromise("GetOutline", null); 15890 } 15891 getOptionalContentConfig(renderingIntent) { 15892 return this.#cacheSimpleMethod("GetOptionalContentConfig").then(data => new OptionalContentConfig(data, renderingIntent)); 15893 } 15894 getPermissions() { 15895 return this.messageHandler.sendWithPromise("GetPermissions", null); 15896 } 15897 getMetadata() { 15898 const name = "GetMetadata", 15899 cachedPromise = this.#methodPromises.get(name); 15900 if (cachedPromise) { 15901 return cachedPromise; 15902 } 15903 const promise = this.messageHandler.sendWithPromise(name, null).then(results => ({ 15904 info: results[0], 15905 metadata: results[1] ? new Metadata(results[1]) : null, 15906 contentDispositionFilename: this.#fullReader?.filename ?? null, 15907 contentLength: this.#fullReader?.contentLength ?? null, 15908 hasStructTree: results[2] 15909 })); 15910 this.#methodPromises.set(name, promise); 15911 return promise; 15912 } 15913 getMarkInfo() { 15914 return this.messageHandler.sendWithPromise("GetMarkInfo", null); 15915 } 15916 async startCleanup(keepLoadedFonts = false) { 15917 if (this.destroyed) { 15918 return; 15919 } 15920 await this.messageHandler.sendWithPromise("Cleanup", null); 15921 for (const page of this.#pageCache.values()) { 15922 const cleanupSuccessful = page.cleanup(); 15923 if (!cleanupSuccessful) { 15924 throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`); 15925 } 15926 } 15927 this.commonObjs.clear(); 15928 if (!keepLoadedFonts) { 15929 this.fontLoader.clear(); 15930 } 15931 this.#methodPromises.clear(); 15932 this.filterFactory.destroy(true); 15933 TextLayer.cleanup(); 15934 } 15935 cachedPageNumber(ref) { 15936 if (!isRefProxy(ref)) { 15937 return null; 15938 } 15939 const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`; 15940 const pageIndex = this.#pageRefCache.get(refStr); 15941 return pageIndex >= 0 ? this.#pagesMapper.getPageNumber(pageIndex + 1) : null; 15942 } 15943} 15944class RenderTask { 15945 _internalRenderTask = null; 15946 onContinue = null; 15947 onError = null; 15948 constructor(internalRenderTask) { 15949 this._internalRenderTask = internalRenderTask; 15950 } 15951 get promise() { 15952 return this._internalRenderTask.capability.promise; 15953 } 15954 cancel(extraDelay = 0) { 15955 this._internalRenderTask.cancel(null, extraDelay); 15956 } 15957 get separateAnnots() { 15958 const { 15959 separateAnnots 15960 } = this._internalRenderTask.operatorList; 15961 if (!separateAnnots) { 15962 return false; 15963 } 15964 const { 15965 annotationCanvasMap 15966 } = this._internalRenderTask; 15967 return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0; 15968 } 15969} 15970class InternalRenderTask { 15971 #rAF = null; 15972 static #canvasInUse = new WeakSet(); 15973 constructor({ 15974 callback, 15975 params, 15976 objs, 15977 commonObjs, 15978 annotationCanvasMap, 15979 operatorList, 15980 pageIndex, 15981 canvasFactory, 15982 filterFactory, 15983 useRequestAnimationFrame = false, 15984 pdfBug = false, 15985 pageColors = null, 15986 enableHWA = false, 15987 operationsFilter = null 15988 }) { 15989 this.callback = callback; 15990 this.params = params; 15991 this.objs = objs; 15992 this.commonObjs = commonObjs; 15993 this.annotationCanvasMap = annotationCanvasMap; 15994 this.operatorListIdx = null; 15995 this.operatorList = operatorList; 15996 this._pageIndex = pageIndex; 15997 this.canvasFactory = canvasFactory; 15998 this.filterFactory = filterFactory; 15999 this._pdfBug = pdfBug; 16000 this.pageColors = pageColors; 16001 this.running = false; 16002 this.graphicsReadyCallback = null; 16003 this.graphicsReady = false; 16004 this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined"; 16005 this.cancelled = false; 16006 this.capability = Promise.withResolvers(); 16007 this.task = new RenderTask(this); 16008 this._cancelBound = this.cancel.bind(this); 16009 this._continueBound = this._continue.bind(this); 16010 this._scheduleNextBound = this._scheduleNext.bind(this); 16011 this._nextBound = this._next.bind(this); 16012 this._canvas = params.canvas; 16013 this._canvasContext = params.canvas ? null : params.canvasContext; 16014 this._enableHWA = enableHWA; 16015 this._dependencyTracker = params.dependencyTracker; 16016 this._operationsFilter = operationsFilter; 16017 } 16018 get completed() { 16019 return this.capability.promise.catch(function () {}); 16020 } 16021 initializeGraphics({ 16022 transparency = false, 16023 optionalContentConfig 16024 }) { 16025 if (this.cancelled) { 16026 return; 16027 } 16028 if (this._canvas) { 16029 if (InternalRenderTask.#canvasInUse.has(this._canvas)) { 16030 throw new Error("Cannot use the same canvas during multiple render() operations. " + "Use different canvas or ensure previous operations were " + "cancelled or completed."); 16031 } 16032 InternalRenderTask.#canvasInUse.add(this._canvas); 16033 } 16034 if (this._pdfBug && globalThis.StepperManager?.enabled) { 16035 this.stepper = globalThis.StepperManager.create(this._pageIndex); 16036 this.stepper.init(this.operatorList); 16037 this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); 16038 } 16039 const { 16040 viewport, 16041 transform, 16042 background, 16043 dependencyTracker 16044 } = this.params; 16045 const canvasContext = this._canvasContext || this._canvas.getContext("2d", { 16046 alpha: false, 16047 willReadFrequently: !this._enableHWA 16048 }); 16049 this.gfx = new CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, { 16050 optionalContentConfig 16051 }, this.annotationCanvasMap, this.pageColors, dependencyTracker); 16052 this.gfx.beginDrawing({ 16053 transform, 16054 viewport, 16055 transparency, 16056 background 16057 }); 16058 this.operatorListIdx = 0; 16059 this.graphicsReady = true; 16060 this.graphicsReadyCallback?.(); 16061 } 16062 cancel(error = null, extraDelay = 0) { 16063 this.running = false;
vendor: 10,072 bytes, lines 16064-16390
16064 this.cancelled = true; 16065 this.gfx?.endDrawing(); 16066 if (this.#rAF) { 16067 window.cancelAnimationFrame(this.#rAF); 16068 this.#rAF = null; 16069 } 16070 InternalRenderTask.#canvasInUse.delete(this._canvas); 16071 error ||= new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay); 16072 this.callback(error); 16073 this.task.onError?.(error); 16074 } 16075 operatorListChanged() { 16076 if (!this.graphicsReady) { 16077 this.graphicsReadyCallback ||= this._continueBound; 16078 return; 16079 } 16080 this.gfx.dependencyTracker?.growOperationsCount(this.operatorList.fnArray.length); 16081 this.stepper?.updateOperatorList(this.operatorList); 16082 if (this.running) { 16083 return; 16084 } 16085 this._continue(); 16086 } 16087 _continue() { 16088 this.running = true; 16089 if (this.cancelled) { 16090 return; 16091 } 16092 if (this.task.onContinue) { 16093 this.task.onContinue(this._scheduleNextBound); 16094 } else { 16095 this._scheduleNext(); 16096 } 16097 } 16098 _scheduleNext() { 16099 if (this._useRequestAnimationFrame) { 16100 this.#rAF = window.requestAnimationFrame(() => { 16101 this.#rAF = null; 16102 this._nextBound().catch(this._cancelBound); 16103 }); 16104 } else { 16105 Promise.resolve().then(this._nextBound).catch(this._cancelBound); 16106 } 16107 } 16108 async _next() { 16109 if (this.cancelled) { 16110 return; 16111 } 16112 this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper, this._operationsFilter); 16113 if (this.operatorListIdx === this.operatorList.argsArray.length) { 16114 this.running = false; 16115 if (this.operatorList.lastChunk) { 16116 this.gfx.endDrawing(); 16117 InternalRenderTask.#canvasInUse.delete(this._canvas); 16118 this.callback(); 16119 } 16120 } 16121 } 16122} 16123const version = "5.4.624"; 16124const build = "384c6208b"; 16125 16126;// ./src/display/editor/color_picker.js 16127 16128 16129 16130class ColorPicker { 16131 #button = null; 16132 #buttonSwatch = null; 16133 #defaultColor; 16134 #dropdown = null; 16135 #dropdownWasFromKeyboard = false; 16136 #isMainColorPicker = false; 16137 #editor = null; 16138 #eventBus; 16139 #openDropdownAC = null; 16140 #uiManager = null; 16141 static #l10nColor = null; 16142 static get _keyboardManager() { 16143 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]])); 16144 } 16145 constructor({ 16146 editor = null, 16147 uiManager = null 16148 }) { 16149 if (editor) { 16150 this.#isMainColorPicker = false; 16151 this.#editor = editor; 16152 } else { 16153 this.#isMainColorPicker = true; 16154 } 16155 this.#uiManager = editor?._uiManager || uiManager; 16156 this.#eventBus = this.#uiManager._eventBus; 16157 this.#defaultColor = editor?.color?.toUpperCase() || this.#uiManager?.highlightColors.values().next().value || "#FFFF98"; 16158 ColorPicker.#l10nColor ||= Object.freeze({ 16159 blue: "pdfjs-editor-colorpicker-blue", 16160 green: "pdfjs-editor-colorpicker-green", 16161 pink: "pdfjs-editor-colorpicker-pink", 16162 red: "pdfjs-editor-colorpicker-red", 16163 yellow: "pdfjs-editor-colorpicker-yellow" 16164 }); 16165 } 16166 renderButton() { 16167 const button = this.#button = document.createElement("button"); 16168 button.className = "colorPicker"; 16169 button.tabIndex = "0"; 16170 button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button"); 16171 button.ariaHasPopup = "true"; 16172 if (this.#editor) { 16173 button.ariaControls = `${this.#editor.id}_colorpicker_dropdown`; 16174 } 16175 const signal = this.#uiManager._signal; 16176 button.addEventListener("click", this.#openDropdown.bind(this), { 16177 signal 16178 }); 16179 button.addEventListener("keydown", this.#keyDown.bind(this), { 16180 signal 16181 }); 16182 const swatch = this.#buttonSwatch = document.createElement("span"); 16183 swatch.className = "swatch"; 16184 swatch.ariaHidden = "true"; 16185 swatch.style.backgroundColor = this.#defaultColor; 16186 button.append(swatch); 16187 return button; 16188 } 16189 renderMainDropdown() { 16190 const dropdown = this.#dropdown = this.#getDropdownRoot(); 16191 dropdown.ariaOrientation = "horizontal"; 16192 dropdown.ariaLabelledBy = "highlightColorPickerLabel"; 16193 return dropdown; 16194 } 16195 #getDropdownRoot() { 16196 const div = document.createElement("div"); 16197 const signal = this.#uiManager._signal; 16198 div.addEventListener("contextmenu", noContextMenu, { 16199 signal 16200 }); 16201 div.className = "dropdown"; 16202 div.role = "listbox"; 16203 div.ariaMultiSelectable = "false"; 16204 div.ariaOrientation = "vertical"; 16205 div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown"); 16206 if (this.#editor) { 16207 div.id = `${this.#editor.id}_colorpicker_dropdown`; 16208 } 16209 for (const [name, color] of this.#uiManager.highlightColors) { 16210 const button = document.createElement("button"); 16211 button.tabIndex = "0"; 16212 button.role = "option"; 16213 button.setAttribute("data-color", color); 16214 button.title = name; 16215 button.setAttribute("data-l10n-id", ColorPicker.#l10nColor[name]); 16216 const swatch = document.createElement("span"); 16217 button.append(swatch); 16218 swatch.className = "swatch"; 16219 swatch.style.backgroundColor = color; 16220 button.ariaSelected = color === this.#defaultColor; 16221 button.addEventListener("click", this.#colorSelect.bind(this, color), { 16222 signal 16223 }); 16224 div.append(button); 16225 } 16226 div.addEventListener("keydown", this.#keyDown.bind(this), { 16227 signal 16228 }); 16229 return div; 16230 } 16231 #colorSelect(color, event) { 16232 event.stopPropagation(); 16233 this.#eventBus.dispatch("switchannotationeditorparams", { 16234 source: this, 16235 type: AnnotationEditorParamsType.HIGHLIGHT_COLOR, 16236 value: color 16237 }); 16238 this.updateColor(color); 16239 } 16240 _colorSelectFromKeyboard(event) { 16241 if (event.target === this.#button) { 16242 this.#openDropdown(event); 16243 return; 16244 } 16245 const color = event.target.getAttribute("data-color"); 16246 if (!color) { 16247 return; 16248 } 16249 this.#colorSelect(color, event); 16250 } 16251 _moveToNext(event) { 16252 if (!this.#isDropdownVisible) { 16253 this.#openDropdown(event); 16254 return; 16255 } 16256 if (event.target === this.#button) { 16257 this.#dropdown.firstElementChild?.focus(); 16258 return; 16259 } 16260 event.target.nextSibling?.focus(); 16261 } 16262 _moveToPrevious(event) { 16263 if (event.target === this.#dropdown?.firstElementChild || event.target === this.#button) { 16264 if (this.#isDropdownVisible) { 16265 this._hideDropdownFromKeyboard(); 16266 } 16267 return; 16268 } 16269 if (!this.#isDropdownVisible) { 16270 this.#openDropdown(event); 16271 } 16272 event.target.previousSibling?.focus(); 16273 } 16274 _moveToBeginning(event) { 16275 if (!this.#isDropdownVisible) { 16276 this.#openDropdown(event); 16277 return; 16278 } 16279 this.#dropdown.firstElementChild?.focus(); 16280 } 16281 _moveToEnd(event) { 16282 if (!this.#isDropdownVisible) { 16283 this.#openDropdown(event); 16284 return; 16285 } 16286 this.#dropdown.lastElementChild?.focus(); 16287 } 16288 #keyDown(event) { 16289 ColorPicker._keyboardManager.exec(this, event); 16290 } 16291 #openDropdown(event) { 16292 if (this.#isDropdownVisible) { 16293 this.hideDropdown(); 16294 return; 16295 } 16296 this.#dropdownWasFromKeyboard = event.detail === 0; 16297 if (!this.#openDropdownAC) { 16298 this.#openDropdownAC = new AbortController(); 16299 window.addEventListener("pointerdown", this.#pointerDown.bind(this), { 16300 signal: this.#uiManager.combinedSignal(this.#openDropdownAC) 16301 }); 16302 } 16303 this.#button.ariaExpanded = "true"; 16304 if (this.#dropdown) { 16305 this.#dropdown.classList.remove("hidden"); 16306 return; 16307 } 16308 const root = this.#dropdown = this.#getDropdownRoot(); 16309 this.#button.append(root); 16310 } 16311 #pointerDown(event) { 16312 if (this.#dropdown?.contains(event.target)) { 16313 return; 16314 } 16315 this.hideDropdown(); 16316 } 16317 hideDropdown() { 16318 this.#dropdown?.classList.add("hidden"); 16319 this.#button.ariaExpanded = "false"; 16320 this.#openDropdownAC?.abort(); 16321 this.#openDropdownAC = null; 16322 } 16323 get #isDropdownVisible() { 16324 return this.#dropdown && !this.#dropdown.classList.contains("hidden"); 16325 } 16326 _hideDropdownFromKeyboard() { 16327 if (this.#isMainColorPicker) { 16328 return; 16329 } 16330 if (!this.#isDropdownVisible) { 16331 this.#editor?.unselect(); 16332 return; 16333 } 16334 this.hideDropdown(); 16335 this.#button.focus({ 16336 preventScroll: true, 16337 focusVisible: this.#dropdownWasFromKeyboard 16338 }); 16339 } 16340 updateColor(color) { 16341 if (this.#buttonSwatch) { 16342 this.#buttonSwatch.style.backgroundColor = color; 16343 } 16344 if (!this.#dropdown) { 16345 return; 16346 } 16347 const i = this.#uiManager.highlightColors.values(); 16348 for (const child of this.#dropdown.children) { 16349 child.ariaSelected = i.next().value === color.toUpperCase(); 16350 } 16351 } 16352 destroy() { 16353 this.#button?.remove(); 16354 this.#button = null; 16355 this.#buttonSwatch = null; 16356 this.#dropdown?.remove(); 16357 this.#dropdown = null; 16358 } 16359} 16360class BasicColorPicker { 16361 #input = null; 16362 #editor = null; 16363 #uiManager = null; 16364 static #l10nColor = null; 16365 constructor(editor) { 16366 this.#editor = editor; 16367 this.#uiManager = editor._uiManager; 16368 BasicColorPicker.#l10nColor ||= Object.freeze({ 16369 freetext: "pdfjs-editor-color-picker-free-text-input", 16370 ink: "pdfjs-editor-color-picker-ink-input" 16371 }); 16372 } 16373 renderButton() { 16374 if (this.#input) { 16375 return this.#input; 16376 } 16377 const { 16378 editorType, 16379 colorType, 16380 color 16381 } = this.#editor; 16382 const input = this.#input = document.createElement("input"); 16383 input.type = "color"; 16384 input.value = color || "#000000"; 16385 input.className = "basicColorPicker"; 16386 input.tabIndex = 0; 16387 input.setAttribute("data-l10n-id", BasicColorPicker.#l10nColor[editorType]); 16388 input.addEventListener("input", () => { 16389 this.#uiManager.updateParams(colorType, input.value); 16390 }, {
vendor: 17,756 bytes, lines 16391-16987
16391 signal: this.#uiManager._signal 16392 }); 16393 return input; 16394 } 16395 update(value) { 16396 if (!this.#input) { 16397 return; 16398 } 16399 this.#input.value = value; 16400 } 16401 destroy() { 16402 this.#input?.remove(); 16403 this.#input = null; 16404 } 16405 hideDropdown() {} 16406} 16407 16408;// ./src/shared/scripting_utils.js 16409function makeColorComp(n) { 16410 return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0"); 16411} 16412function scaleAndClamp(x) { 16413 return Math.max(0, Math.min(255, 255 * x)); 16414} 16415class ColorConverters { 16416 static CMYK_G([c, y, m, k]) { 16417 return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)]; 16418 } 16419 static G_CMYK([g]) { 16420 return ["CMYK", 0, 0, 0, 1 - g]; 16421 } 16422 static G_RGB([g]) { 16423 return ["RGB", g, g, g]; 16424 } 16425 static G_rgb([g]) { 16426 g = scaleAndClamp(g); 16427 return [g, g, g]; 16428 } 16429 static G_HTML([g]) { 16430 const G = makeColorComp(g); 16431 return `#${G}${G}${G}`; 16432 } 16433 static RGB_G([r, g, b]) { 16434 return ["G", 0.3 * r + 0.59 * g + 0.11 * b]; 16435 } 16436 static RGB_rgb(color) { 16437 return color.map(scaleAndClamp); 16438 } 16439 static RGB_HTML(color) { 16440 return `#${color.map(makeColorComp).join("")}`; 16441 } 16442 static T_HTML() { 16443 return "#00000000"; 16444 } 16445 static T_rgb() { 16446 return [null]; 16447 } 16448 static CMYK_RGB([c, y, m, k]) { 16449 return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)]; 16450 } 16451 static CMYK_rgb([c, y, m, k]) { 16452 return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))]; 16453 } 16454 static CMYK_HTML(components) { 16455 const rgb = this.CMYK_RGB(components).slice(1); 16456 return this.RGB_HTML(rgb); 16457 } 16458 static RGB_CMYK([r, g, b]) { 16459 const c = 1 - r; 16460 const m = 1 - g; 16461 const y = 1 - b; 16462 const k = Math.min(c, m, y); 16463 return ["CMYK", c, m, y, k]; 16464 } 16465} 16466const DateFormats = (/* unused pure expression or super */ null && (["m/d", "m/d/yy", "mm/dd/yy", "mm/yy", "d-mmm", "d-mmm-yy", "dd-mmm-yy", "yy-mm-dd", "mmm-yy", "mmmm-yy", "mmm d, yyyy", "mmmm d, yyyy", "m/d/yy h:MM tt", "m/d/yy HH:MM"])); 16467const TimeFormats = (/* unused pure expression or super */ null && (["HH:MM", "h:MM tt", "HH:MM:ss", "h:MM:ss tt"])); 16468 16469;// ./src/display/svg_factory.js 16470 16471 16472class BaseSVGFactory { 16473 create(width, height, skipDimensions = false) { 16474 if (width <= 0 || height <= 0) { 16475 throw new Error("Invalid SVG dimensions"); 16476 } 16477 const svg = this._createSVG("svg:svg"); 16478 svg.setAttribute("version", "1.1"); 16479 if (!skipDimensions) { 16480 svg.setAttribute("width", `${width}px`); 16481 svg.setAttribute("height", `${height}px`); 16482 } 16483 svg.setAttribute("preserveAspectRatio", "none"); 16484 svg.setAttribute("viewBox", `0 0 ${width} ${height}`); 16485 return svg; 16486 } 16487 createElement(type) { 16488 if (typeof type !== "string") { 16489 throw new Error("Invalid SVG element type"); 16490 } 16491 return this._createSVG(type); 16492 } 16493 _createSVG(type) { 16494 unreachable("Abstract method `_createSVG` called."); 16495 } 16496} 16497class DOMSVGFactory extends BaseSVGFactory { 16498 _createSVG(type) { 16499 return document.createElementNS(SVG_NS, type); 16500 } 16501} 16502 16503;// ./src/display/annotation_layer.js 16504 16505 16506 16507 16508 16509const annotation_layer_DEFAULT_FONT_SIZE = 9; 16510const GetElementsByNameSet = new WeakSet(); 16511const TIMEZONE_OFFSET = new Date().getTimezoneOffset() * 60 * 1000; 16512class AnnotationElementFactory { 16513 static create(parameters) { 16514 const subtype = parameters.data.annotationType; 16515 switch (subtype) { 16516 case AnnotationType.LINK: 16517 return new LinkAnnotationElement(parameters); 16518 case AnnotationType.TEXT: 16519 return new TextAnnotationElement(parameters); 16520 case AnnotationType.WIDGET: 16521 const fieldType = parameters.data.fieldType; 16522 switch (fieldType) { 16523 case "Tx": 16524 return new TextWidgetAnnotationElement(parameters); 16525 case "Btn": 16526 if (parameters.data.radioButton) { 16527 return new RadioButtonWidgetAnnotationElement(parameters); 16528 } else if (parameters.data.checkBox) { 16529 return new CheckboxWidgetAnnotationElement(parameters); 16530 } 16531 return new PushButtonWidgetAnnotationElement(parameters); 16532 case "Ch": 16533 return new ChoiceWidgetAnnotationElement(parameters); 16534 case "Sig": 16535 return new SignatureWidgetAnnotationElement(parameters); 16536 } 16537 return new WidgetAnnotationElement(parameters); 16538 case AnnotationType.POPUP: 16539 return new PopupAnnotationElement(parameters); 16540 case AnnotationType.FREETEXT: 16541 return new FreeTextAnnotationElement(parameters); 16542 case AnnotationType.LINE: 16543 return new LineAnnotationElement(parameters); 16544 case AnnotationType.SQUARE: 16545 return new SquareAnnotationElement(parameters); 16546 case AnnotationType.CIRCLE: 16547 return new CircleAnnotationElement(parameters); 16548 case AnnotationType.POLYLINE: 16549 return new PolylineAnnotationElement(parameters); 16550 case AnnotationType.CARET: 16551 return new CaretAnnotationElement(parameters); 16552 case AnnotationType.INK: 16553 return new InkAnnotationElement(parameters); 16554 case AnnotationType.POLYGON: 16555 return new PolygonAnnotationElement(parameters); 16556 case AnnotationType.HIGHLIGHT: 16557 return new HighlightAnnotationElement(parameters); 16558 case AnnotationType.UNDERLINE: 16559 return new UnderlineAnnotationElement(parameters); 16560 case AnnotationType.SQUIGGLY: 16561 return new SquigglyAnnotationElement(parameters); 16562 case AnnotationType.STRIKEOUT: 16563 return new StrikeOutAnnotationElement(parameters); 16564 case AnnotationType.STAMP: 16565 return new StampAnnotationElement(parameters); 16566 case AnnotationType.FILEATTACHMENT: 16567 return new FileAttachmentAnnotationElement(parameters); 16568 default: 16569 return new AnnotationElement(parameters); 16570 } 16571 } 16572} 16573class AnnotationElement { 16574 #updates = null; 16575 #hasBorder = false; 16576 #popupElement = null; 16577 constructor(parameters, { 16578 isRenderable = false, 16579 ignoreBorder = false, 16580 createQuadrilaterals = false 16581 } = {}) { 16582 this.isRenderable = isRenderable; 16583 this.data = parameters.data; 16584 this.layer = parameters.layer; 16585 this.linkService = parameters.linkService; 16586 this.downloadManager = parameters.downloadManager; 16587 this.imageResourcesPath = parameters.imageResourcesPath; 16588 this.renderForms = parameters.renderForms; 16589 this.svgFactory = parameters.svgFactory; 16590 this.annotationStorage = parameters.annotationStorage; 16591 this.enableComment = parameters.enableComment; 16592 this.enableScripting = parameters.enableScripting; 16593 this.hasJSActions = parameters.hasJSActions; 16594 this._fieldObjects = parameters.fieldObjects; 16595 this.parent = parameters.parent; 16596 this.hasOwnCommentButton = false; 16597 if (isRenderable) { 16598 this.contentElement = this.container = this._createContainer(ignoreBorder); 16599 } 16600 if (createQuadrilaterals) { 16601 this._createQuadrilaterals(); 16602 } 16603 } 16604 static _hasPopupData({ 16605 contentsObj, 16606 richText 16607 }) { 16608 return !!(contentsObj?.str || richText?.str); 16609 } 16610 get _isEditable() { 16611 return this.data.isEditable; 16612 } 16613 get hasPopupData() { 16614 return AnnotationElement._hasPopupData(this.data) || this.enableComment && !!this.commentText; 16615 } 16616 get commentData() { 16617 const { 16618 data 16619 } = this; 16620 const editor = this.annotationStorage?.getEditor(data.id); 16621 if (editor) { 16622 return editor.getData(); 16623 } 16624 return data; 16625 } 16626 get hasCommentButton() { 16627 return this.enableComment && this.hasPopupElement; 16628 } 16629 get commentButtonPosition() { 16630 const editor = this.annotationStorage?.getEditor(this.data.id); 16631 if (editor) { 16632 return editor.commentButtonPositionInPage; 16633 } 16634 const { 16635 quadPoints, 16636 inkLists, 16637 rect 16638 } = this.data; 16639 let maxX = -Infinity; 16640 let maxY = -Infinity; 16641 if (quadPoints?.length >= 8) { 16642 for (let i = 0; i < quadPoints.length; i += 8) { 16643 if (quadPoints[i + 1] > maxY) { 16644 maxY = quadPoints[i + 1]; 16645 maxX = quadPoints[i + 2]; 16646 } else if (quadPoints[i + 1] === maxY) { 16647 maxX = Math.max(maxX, quadPoints[i + 2]); 16648 } 16649 } 16650 return [maxX, maxY]; 16651 } 16652 if (inkLists?.length >= 1) { 16653 for (const inkList of inkLists) { 16654 for (let i = 0, ii = inkList.length; i < ii; i += 2) { 16655 if (inkList[i + 1] > maxY) { 16656 maxY = inkList[i + 1]; 16657 maxX = inkList[i]; 16658 } else if (inkList[i + 1] === maxY) { 16659 maxX = Math.max(maxX, inkList[i]); 16660 } 16661 } 16662 } 16663 if (maxX !== Infinity) { 16664 return [maxX, maxY]; 16665 } 16666 } 16667 if (rect) { 16668 return [rect[2], rect[3]]; 16669 } 16670 return null; 16671 } 16672 _normalizePoint(point) { 16673 const { 16674 page: { 16675 view 16676 }, 16677 viewport: { 16678 rawDims: { 16679 pageWidth, 16680 pageHeight, 16681 pageX, 16682 pageY 16683 } 16684 } 16685 } = this.parent; 16686 point[1] = view[3] - point[1] + view[1]; 16687 point[0] = 100 * (point[0] - pageX) / pageWidth; 16688 point[1] = 100 * (point[1] - pageY) / pageHeight; 16689 return point; 16690 } 16691 get commentText() { 16692 const { 16693 data 16694 } = this; 16695 return this.annotationStorage.getRawValue(`${AnnotationEditorPrefix}${data.id}`)?.popup?.contents || data.contentsObj?.str || ""; 16696 } 16697 set commentText(text) { 16698 const { 16699 data 16700 } = this; 16701 const popup = { 16702 deleted: !text, 16703 contents: text || "" 16704 }; 16705 if (!this.annotationStorage.updateEditor(data.id, { 16706 popup 16707 })) { 16708 this.annotationStorage.setValue(`${AnnotationEditorPrefix}${data.id}`, { 16709 id: data.id, 16710 annotationType: data.annotationType, 16711 page: this.parent.page, 16712 popup, 16713 popupRef: data.popupRef, 16714 modificationDate: new Date() 16715 }); 16716 } 16717 if (!text) { 16718 this.removePopup(); 16719 } 16720 } 16721 removePopup() { 16722 (this.#popupElement?.popup || this.popup)?.remove(); 16723 this.#popupElement = this.popup = null; 16724 } 16725 updateEdited(params) { 16726 if (!this.container) { 16727 return; 16728 } 16729 if (params.rect) { 16730 this.#updates ||= { 16731 rect: this.data.rect.slice(0) 16732 }; 16733 } 16734 const { 16735 rect, 16736 popup: newPopup 16737 } = params; 16738 if (rect) { 16739 this.#setRectEdited(rect); 16740 } 16741 let popup = this.#popupElement?.popup || this.popup; 16742 if (!popup && newPopup?.text) { 16743 this._createPopup(newPopup); 16744 popup = this.#popupElement.popup; 16745 } 16746 if (!popup) { 16747 return; 16748 } 16749 popup.updateEdited(params); 16750 if (newPopup?.deleted) { 16751 popup.remove(); 16752 this.#popupElement = null; 16753 this.popup = null; 16754 } 16755 } 16756 resetEdited() { 16757 if (!this.#updates) { 16758 return; 16759 } 16760 this.#setRectEdited(this.#updates.rect); 16761 this.#popupElement?.popup.resetEdited(); 16762 this.#updates = null; 16763 } 16764 #setRectEdited(rect) { 16765 const { 16766 container: { 16767 style 16768 }, 16769 data: { 16770 rect: currentRect, 16771 rotation 16772 }, 16773 parent: { 16774 viewport: { 16775 rawDims: { 16776 pageWidth, 16777 pageHeight, 16778 pageX, 16779 pageY 16780 } 16781 } 16782 } 16783 } = this; 16784 currentRect?.splice(0, 4, ...rect); 16785 style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; 16786 style.top = `${100 * (pageHeight - rect[3] + pageY) / pageHeight}%`; 16787 if (rotation === 0) { 16788 style.width = `${100 * (rect[2] - rect[0]) / pageWidth}%`; 16789 style.height = `${100 * (rect[3] - rect[1]) / pageHeight}%`; 16790 } else { 16791 this.setRotation(rotation); 16792 } 16793 } 16794 _createContainer(ignoreBorder) { 16795 const { 16796 data, 16797 parent: { 16798 page, 16799 viewport 16800 } 16801 } = this; 16802 const container = document.createElement("section"); 16803 container.setAttribute("data-annotation-id", data.id); 16804 if (!(this instanceof WidgetAnnotationElement) && !(this instanceof LinkAnnotationElement)) { 16805 container.tabIndex = 0; 16806 } 16807 const { 16808 style 16809 } = container; 16810 style.zIndex = this.parent.zIndex; 16811 this.parent.zIndex += 2; 16812 if (data.alternativeText) { 16813 container.title = data.alternativeText; 16814 } 16815 if (data.noRotate) { 16816 container.classList.add("norotate"); 16817 } 16818 if (!data.rect || this instanceof PopupAnnotationElement) { 16819 const { 16820 rotation 16821 } = data; 16822 if (!data.hasOwnCanvas && rotation !== 0) { 16823 this.setRotation(rotation, container); 16824 } 16825 return container; 16826 } 16827 const { 16828 width, 16829 height 16830 } = this; 16831 if (!ignoreBorder && data.borderStyle.width > 0) { 16832 style.borderWidth = `${data.borderStyle.width}px`; 16833 const horizontalRadius = data.borderStyle.horizontalCornerRadius; 16834 const verticalRadius = data.borderStyle.verticalCornerRadius; 16835 if (horizontalRadius > 0 || verticalRadius > 0) { 16836 const radius = `calc(${horizontalRadius}px * var(--total-scale-factor)) / calc(${verticalRadius}px * var(--total-scale-factor))`; 16837 style.borderRadius = radius; 16838 } else if (this instanceof RadioButtonWidgetAnnotationElement) { 16839 const radius = `calc(${width}px * var(--total-scale-factor)) / calc(${height}px * var(--total-scale-factor))`; 16840 style.borderRadius = radius; 16841 } 16842 switch (data.borderStyle.style) { 16843 case AnnotationBorderStyleType.SOLID: 16844 style.borderStyle = "solid"; 16845 break; 16846 case AnnotationBorderStyleType.DASHED: 16847 style.borderStyle = "dashed"; 16848 break; 16849 case AnnotationBorderStyleType.BEVELED: 16850 warn("Unimplemented border style: beveled"); 16851 break; 16852 case AnnotationBorderStyleType.INSET: 16853 warn("Unimplemented border style: inset"); 16854 break; 16855 case AnnotationBorderStyleType.UNDERLINE: 16856 style.borderBottomStyle = "solid"; 16857 break; 16858 default: 16859 break; 16860 } 16861 const borderColor = data.borderColor || null; 16862 if (borderColor) { 16863 this.#hasBorder = true; 16864 style.borderColor = Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0); 16865 } else { 16866 style.borderWidth = 0; 16867 } 16868 } 16869 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]]); 16870 const { 16871 pageWidth, 16872 pageHeight, 16873 pageX, 16874 pageY 16875 } = viewport.rawDims; 16876 style.left = `${100 * (rect[0] - pageX) / pageWidth}%`; 16877 style.top = `${100 * (rect[1] - pageY) / pageHeight}%`; 16878 const { 16879 rotation 16880 } = data; 16881 if (data.hasOwnCanvas || rotation === 0) { 16882 style.width = `${100 * width / pageWidth}%`; 16883 style.height = `${100 * height / pageHeight}%`; 16884 } else { 16885 this.setRotation(rotation, container); 16886 } 16887 return container; 16888 } 16889 setRotation(angle, container = this.container) { 16890 if (!this.data.rect) { 16891 return; 16892 } 16893 const { 16894 pageWidth, 16895 pageHeight 16896 } = this.parent.viewport.rawDims; 16897 let { 16898 width, 16899 height 16900 } = this; 16901 if (angle % 180 !== 0) { 16902 [width, height] = [height, width]; 16903 } 16904 container.style.width = `${100 * width / pageWidth}%`; 16905 container.style.height = `${100 * height / pageHeight}%`; 16906 container.setAttribute("data-main-rotation", (360 - angle) % 360); 16907 } 16908 get _commonActions() { 16909 const setColor = (jsName, styleName, event) => { 16910 const color = event.detail[jsName]; 16911 const colorType = color[0]; 16912 const colorArray = color.slice(1); 16913 event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray); 16914 this.annotationStorage.setValue(this.data.id, { 16915 [styleName]: ColorConverters[`${colorType}_rgb`](colorArray) 16916 }); 16917 }; 16918 return shadow(this, "_commonActions", { 16919 display: event => { 16920 const { 16921 display 16922 } = event.detail; 16923 const hidden = display % 2 === 1; 16924 this.container.style.visibility = hidden ? "hidden" : "visible"; 16925 this.annotationStorage.setValue(this.data.id, { 16926 noView: hidden, 16927 noPrint: display === 1 || display === 2 16928 }); 16929 }, 16930 print: event => { 16931 this.annotationStorage.setValue(this.data.id, { 16932 noPrint: !event.detail.print 16933 }); 16934 }, 16935 hidden: event => { 16936 const { 16937 hidden 16938 } = event.detail; 16939 this.container.style.visibility = hidden ? "hidden" : "visible"; 16940 this.annotationStorage.setValue(this.data.id, { 16941 noPrint: hidden, 16942 noView: hidden 16943 }); 16944 }, 16945 focus: event => { 16946 setTimeout(() => event.target.focus({ 16947 preventScroll: false 16948 }), 0); 16949 }, 16950 userName: event => { 16951 event.target.title = event.detail.userName; 16952 }, 16953 readonly: event => { 16954 event.target.disabled = event.detail.readonly; 16955 }, 16956 required: event => { 16957 this._setRequired(event.target, event.detail.required); 16958 }, 16959 bgColor: event => { 16960 setColor("bgColor", "backgroundColor", event); 16961 }, 16962 fillColor: event => { 16963 setColor("fillColor", "backgroundColor", event); 16964 }, 16965 fgColor: event => { 16966 setColor("fgColor", "color", event); 16967 }, 16968 textColor: event => { 16969 setColor("textColor", "color", event); 16970 }, 16971 borderColor: event => { 16972 setColor("borderColor", "borderColor", event); 16973 }, 16974 strokeColor: event => { 16975 setColor("strokeColor", "borderColor", event); 16976 }, 16977 rotation: event => { 16978 const angle = event.detail.rotation; 16979 this.setRotation(angle); 16980 this.annotationStorage.setValue(this.data.id, { 16981 rotation: angle 16982 }); 16983 } 16984 }); 16985 } 16986 _dispatchEventFromSandbox(actions, jsEvent) { 16987 const commonActions = this._commonActions;
vendor: 18,371 bytes, lines 16988-17615
16988 for (const name of Object.keys(jsEvent.detail)) { 16989 const action = actions[name] || commonActions[name]; 16990 action?.(jsEvent); 16991 } 16992 } 16993 _setDefaultPropertiesFromJS(element) { 16994 if (!this.enableScripting) { 16995 return; 16996 } 16997 const storedData = this.annotationStorage.getRawValue(this.data.id); 16998 if (!storedData) { 16999 return; 17000 } 17001 const commonActions = this._commonActions; 17002 for (const [actionName, detail] of Object.entries(storedData)) { 17003 const action = commonActions[actionName]; 17004 if (action) { 17005 const eventProxy = { 17006 detail: { 17007 [actionName]: detail 17008 }, 17009 target: element 17010 }; 17011 action(eventProxy); 17012 delete storedData[actionName]; 17013 } 17014 } 17015 } 17016 _createQuadrilaterals() { 17017 if (!this.container) { 17018 return; 17019 } 17020 const { 17021 quadPoints 17022 } = this.data; 17023 if (!quadPoints) { 17024 return; 17025 } 17026 const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect.map(x => Math.fround(x)); 17027 if (quadPoints.length === 8) { 17028 const [trX, trY, blX, blY] = quadPoints.subarray(2, 6); 17029 if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) { 17030 return; 17031 } 17032 } 17033 const { 17034 style 17035 } = this.container; 17036 let svgBuffer; 17037 if (this.#hasBorder) { 17038 const { 17039 borderColor, 17040 borderWidth 17041 } = style; 17042 style.borderWidth = 0; 17043 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}">`]; 17044 this.container.classList.add("hasBorder"); 17045 } 17046 const width = rectTrX - rectBlX; 17047 const height = rectTrY - rectBlY; 17048 const { 17049 svgFactory 17050 } = this; 17051 const svg = svgFactory.createElement("svg"); 17052 svg.classList.add("quadrilateralsContainer"); 17053 svg.setAttribute("width", 0); 17054 svg.setAttribute("height", 0); 17055 svg.role = "none"; 17056 const defs = svgFactory.createElement("defs"); 17057 svg.append(defs); 17058 const clipPath = svgFactory.createElement("clipPath"); 17059 const id = `clippath_${this.data.id}`; 17060 clipPath.setAttribute("id", id); 17061 clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); 17062 defs.append(clipPath); 17063 for (let i = 2, ii = quadPoints.length; i < ii; i += 8) { 17064 const trX = quadPoints[i]; 17065 const trY = quadPoints[i + 1]; 17066 const blX = quadPoints[i + 2]; 17067 const blY = quadPoints[i + 3]; 17068 const rect = svgFactory.createElement("rect"); 17069 const x = (blX - rectBlX) / width; 17070 const y = (rectTrY - trY) / height; 17071 const rectWidth = (trX - blX) / width; 17072 const rectHeight = (trY - blY) / height; 17073 rect.setAttribute("x", x); 17074 rect.setAttribute("y", y); 17075 rect.setAttribute("width", rectWidth); 17076 rect.setAttribute("height", rectHeight); 17077 clipPath.append(rect); 17078 svgBuffer?.push(`<rect vector-effect="non-scaling-stroke" x="${x}" y="${y}" width="${rectWidth}" height="${rectHeight}"/>`); 17079 } 17080 if (this.#hasBorder) { 17081 svgBuffer.push(`</g></svg>')`); 17082 style.backgroundImage = svgBuffer.join(""); 17083 } 17084 this.container.append(svg); 17085 this.container.style.clipPath = `url(#${id})`; 17086 } 17087 _createPopup(popupData = null) { 17088 const { 17089 data 17090 } = this; 17091 let contentsObj, modificationDate; 17092 if (popupData) { 17093 contentsObj = { 17094 str: popupData.text 17095 }; 17096 modificationDate = popupData.date; 17097 } else { 17098 contentsObj = data.contentsObj; 17099 modificationDate = data.modificationDate; 17100 } 17101 this.#popupElement = new PopupAnnotationElement({ 17102 data: { 17103 color: data.color, 17104 titleObj: data.titleObj, 17105 modificationDate, 17106 contentsObj, 17107 richText: data.richText, 17108 parentRect: data.rect, 17109 borderStyle: 0, 17110 id: `popup_${data.id}`, 17111 rotation: data.rotation, 17112 noRotate: true 17113 }, 17114 linkService: this.linkService, 17115 parent: this.parent, 17116 elements: [this] 17117 }); 17118 } 17119 get hasPopupElement() { 17120 return !!(this.#popupElement || this.popup || this.data.popupRef); 17121 } 17122 get extraPopupElement() { 17123 return this.#popupElement; 17124 } 17125 render() { 17126 unreachable("Abstract method `AnnotationElement.render` called"); 17127 } 17128 _getElementsByName(name, skipId = null) { 17129 const fields = []; 17130 if (this._fieldObjects) { 17131 const fieldObj = this._fieldObjects[name]; 17132 if (fieldObj) { 17133 for (const { 17134 page, 17135 id, 17136 exportValues 17137 } of fieldObj) { 17138 if (page === -1) { 17139 continue; 17140 } 17141 if (id === skipId) { 17142 continue; 17143 } 17144 const exportValue = typeof exportValues === "string" ? exportValues : null; 17145 const domElement = document.querySelector(`[data-element-id="${id}"]`); 17146 if (domElement && !GetElementsByNameSet.has(domElement)) { 17147 warn(`_getElementsByName - element not allowed: ${id}`); 17148 continue; 17149 } 17150 fields.push({ 17151 id, 17152 exportValue, 17153 domElement 17154 }); 17155 } 17156 } 17157 return fields; 17158 } 17159 for (const domElement of document.getElementsByName(name)) { 17160 const { 17161 exportValue 17162 } = domElement; 17163 const id = domElement.getAttribute("data-element-id"); 17164 if (id === skipId) { 17165 continue; 17166 } 17167 if (!GetElementsByNameSet.has(domElement)) { 17168 continue; 17169 } 17170 fields.push({ 17171 id, 17172 exportValue, 17173 domElement 17174 }); 17175 } 17176 return fields; 17177 } 17178 show() { 17179 if (this.container) { 17180 this.container.hidden = false; 17181 } 17182 this.popup?.maybeShow(); 17183 } 17184 hide() { 17185 if (this.container) { 17186 this.container.hidden = true; 17187 } 17188 this.popup?.forceHide(); 17189 } 17190 getElementsToTriggerPopup() { 17191 return this.container; 17192 } 17193 addHighlightArea() { 17194 const triggers = this.getElementsToTriggerPopup(); 17195 if (Array.isArray(triggers)) { 17196 for (const element of triggers) { 17197 element.classList.add("highlightArea"); 17198 } 17199 } else { 17200 triggers.classList.add("highlightArea"); 17201 } 17202 } 17203 _editOnDoubleClick() { 17204 if (!this._isEditable) { 17205 return; 17206 } 17207 const { 17208 annotationEditorType: mode, 17209 data: { 17210 id: editId 17211 } 17212 } = this; 17213 this.container.addEventListener("dblclick", () => { 17214 this.linkService.eventBus?.dispatch("switchannotationeditormode", { 17215 source: this, 17216 mode, 17217 editId, 17218 mustEnterInEditMode: true 17219 }); 17220 }); 17221 } 17222 get width() { 17223 return this.data.rect[2] - this.data.rect[0]; 17224 } 17225 get height() { 17226 return this.data.rect[3] - this.data.rect[1]; 17227 } 17228} 17229class EditorAnnotationElement extends AnnotationElement { 17230 constructor(parameters) { 17231 super(parameters, { 17232 isRenderable: true, 17233 ignoreBorder: true 17234 }); 17235 this.editor = parameters.editor; 17236 } 17237 render() { 17238 this.container.className = "editorAnnotation"; 17239 return this.container; 17240 } 17241 createOrUpdatePopup() { 17242 const { 17243 editor 17244 } = this; 17245 if (!editor.hasComment) { 17246 return; 17247 } 17248 this._createPopup(editor.comment); 17249 } 17250 get hasCommentButton() { 17251 return this.enableComment && this.editor.hasComment; 17252 } 17253 get commentButtonPosition() { 17254 return this.editor.commentButtonPositionInPage; 17255 } 17256 get commentText() { 17257 return this.editor.comment.text; 17258 } 17259 set commentText(text) { 17260 this.editor.comment = text; 17261 if (!text) { 17262 this.removePopup(); 17263 } 17264 } 17265 get commentData() { 17266 return this.editor.getData(); 17267 } 17268 remove() { 17269 this.parent.removeAnnotation(this.data.id); 17270 this.container.remove(); 17271 this.container = null; 17272 this.removePopup(); 17273 } 17274} 17275class LinkAnnotationElement extends AnnotationElement { 17276 constructor(parameters, options = null) { 17277 super(parameters, { 17278 isRenderable: true, 17279 ignoreBorder: !!options?.ignoreBorder, 17280 createQuadrilaterals: true 17281 }); 17282 this.isTooltipOnly = parameters.data.isTooltipOnly; 17283 } 17284 render() { 17285 const { 17286 data, 17287 linkService 17288 } = this; 17289 const link = document.createElement("a"); 17290 link.setAttribute("data-element-id", data.id); 17291 let isBound = false; 17292 if (data.url) { 17293 linkService.addLinkAttributes(link, data.url, data.newWindow); 17294 isBound = true; 17295 } else if (data.action) { 17296 this._bindNamedAction(link, data.action, data.overlaidText); 17297 isBound = true; 17298 } else if (data.attachment) { 17299 this.#bindAttachment(link, data.attachment, data.overlaidText, data.attachmentDest); 17300 isBound = true; 17301 } else if (data.setOCGState) { 17302 this.#bindSetOCGState(link, data.setOCGState, data.overlaidText); 17303 isBound = true; 17304 } else if (data.dest) { 17305 this._bindLink(link, data.dest, data.overlaidText); 17306 isBound = true; 17307 } else { 17308 if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) { 17309 this._bindJSAction(link, data); 17310 isBound = true; 17311 } 17312 if (data.resetForm) { 17313 this._bindResetFormAction(link, data.resetForm); 17314 isBound = true; 17315 } else if (this.isTooltipOnly && !isBound) { 17316 this._bindLink(link, ""); 17317 isBound = true; 17318 } 17319 } 17320 this.container.classList.add("linkAnnotation"); 17321 if (isBound) { 17322 this.contentElement = link; 17323 this.container.append(link); 17324 } 17325 return this.container; 17326 } 17327 #setInternalLink() { 17328 this.container.setAttribute("data-internal-link", ""); 17329 } 17330 _bindLink(link, destination, overlaidText = "") { 17331 link.href = this.linkService.getDestinationHash(destination); 17332 link.onclick = () => { 17333 if (destination) { 17334 this.linkService.goToDestination(destination); 17335 } 17336 return false; 17337 }; 17338 if (destination || destination === "") { 17339 this.#setInternalLink(); 17340 } 17341 if (overlaidText) { 17342 link.title = overlaidText; 17343 } 17344 } 17345 _bindNamedAction(link, action, overlaidText = "") { 17346 link.href = this.linkService.getAnchorUrl(""); 17347 link.onclick = () => { 17348 this.linkService.executeNamedAction(action); 17349 return false; 17350 }; 17351 if (overlaidText) { 17352 link.title = overlaidText; 17353 } 17354 this.#setInternalLink(); 17355 } 17356 #bindAttachment(link, attachment, overlaidText = "", dest = null) { 17357 link.href = this.linkService.getAnchorUrl(""); 17358 if (attachment.description) { 17359 link.title = attachment.description; 17360 } else if (overlaidText) { 17361 link.title = overlaidText; 17362 } 17363 link.onclick = () => { 17364 this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest); 17365 return false; 17366 }; 17367 this.#setInternalLink(); 17368 } 17369 #bindSetOCGState(link, action, overlaidText = "") { 17370 link.href = this.linkService.getAnchorUrl(""); 17371 link.onclick = () => { 17372 this.linkService.executeSetOCGState(action); 17373 return false; 17374 }; 17375 if (overlaidText) { 17376 link.title = overlaidText; 17377 } 17378 this.#setInternalLink(); 17379 } 17380 _bindJSAction(link, data) { 17381 link.href = this.linkService.getAnchorUrl(""); 17382 const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]); 17383 for (const name of Object.keys(data.actions)) { 17384 const jsName = map.get(name); 17385 if (!jsName) { 17386 continue; 17387 } 17388 link[jsName] = () => { 17389 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17390 source: this, 17391 detail: { 17392 id: data.id, 17393 name 17394 } 17395 }); 17396 return false; 17397 }; 17398 } 17399 if (data.overlaidText) { 17400 link.title = data.overlaidText; 17401 } 17402 if (!link.onclick) { 17403 link.onclick = () => false; 17404 } 17405 this.#setInternalLink(); 17406 } 17407 _bindResetFormAction(link, resetForm) { 17408 const otherClickAction = link.onclick; 17409 if (!otherClickAction) { 17410 link.href = this.linkService.getAnchorUrl(""); 17411 } 17412 this.#setInternalLink(); 17413 if (!this._fieldObjects) { 17414 warn(`_bindResetFormAction - "resetForm" action not supported, ` + "ensure that the `fieldObjects` parameter is provided."); 17415 if (!otherClickAction) { 17416 link.onclick = () => false; 17417 } 17418 return; 17419 } 17420 link.onclick = () => { 17421 otherClickAction?.(); 17422 const { 17423 fields: resetFormFields, 17424 refs: resetFormRefs, 17425 include 17426 } = resetForm; 17427 const allFields = []; 17428 if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) { 17429 const fieldIds = new Set(resetFormRefs); 17430 for (const fieldName of resetFormFields) { 17431 const fields = this._fieldObjects[fieldName] || []; 17432 for (const { 17433 id 17434 } of fields) { 17435 fieldIds.add(id); 17436 } 17437 } 17438 for (const fields of Object.values(this._fieldObjects)) { 17439 for (const field of fields) { 17440 if (fieldIds.has(field.id) === include) { 17441 allFields.push(field); 17442 } 17443 } 17444 } 17445 } else { 17446 for (const fields of Object.values(this._fieldObjects)) { 17447 allFields.push(...fields); 17448 } 17449 } 17450 const storage = this.annotationStorage; 17451 const allIds = []; 17452 for (const field of allFields) { 17453 const { 17454 id 17455 } = field; 17456 allIds.push(id); 17457 switch (field.type) { 17458 case "text": 17459 { 17460 const value = field.defaultValue || ""; 17461 storage.setValue(id, { 17462 value 17463 }); 17464 break; 17465 } 17466 case "checkbox": 17467 case "radiobutton": 17468 { 17469 const value = field.defaultValue === field.exportValues; 17470 storage.setValue(id, { 17471 value 17472 }); 17473 break; 17474 } 17475 case "combobox": 17476 case "listbox": 17477 { 17478 const value = field.defaultValue || ""; 17479 storage.setValue(id, { 17480 value 17481 }); 17482 break; 17483 } 17484 default: 17485 continue; 17486 } 17487 const domElement = document.querySelector(`[data-element-id="${id}"]`); 17488 if (!domElement) { 17489 continue; 17490 } else if (!GetElementsByNameSet.has(domElement)) { 17491 warn(`_bindResetFormAction - element not allowed: ${id}`); 17492 continue; 17493 } 17494 domElement.dispatchEvent(new Event("resetform")); 17495 } 17496 if (this.enableScripting) { 17497 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17498 source: this, 17499 detail: { 17500 id: "app", 17501 ids: allIds, 17502 name: "ResetForm" 17503 } 17504 }); 17505 } 17506 return false; 17507 }; 17508 } 17509} 17510class TextAnnotationElement extends AnnotationElement { 17511 constructor(parameters) { 17512 super(parameters, { 17513 isRenderable: true 17514 }); 17515 } 17516 render() { 17517 this.container.classList.add("textAnnotation"); 17518 const image = document.createElement("img"); 17519 image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg"; 17520 image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type"); 17521 image.setAttribute("data-l10n-args", JSON.stringify({ 17522 type: this.data.name 17523 })); 17524 if (!this.data.popupRef && this.hasPopupData) { 17525 this.hasOwnCommentButton = true; 17526 this._createPopup(); 17527 } 17528 this.container.append(image); 17529 return this.container; 17530 } 17531} 17532class WidgetAnnotationElement extends AnnotationElement { 17533 render() { 17534 return this.container; 17535 } 17536 showElementAndHideCanvas(element) { 17537 if (this.data.hasOwnCanvas) { 17538 if (element.previousSibling?.nodeName === "CANVAS") { 17539 element.previousSibling.hidden = true; 17540 } 17541 element.hidden = false; 17542 } 17543 } 17544 _getKeyModifier(event) { 17545 return util_FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey; 17546 } 17547 _setEventListener(element, elementData, baseName, eventName, valueGetter) { 17548 if (baseName.includes("mouse")) { 17549 element.addEventListener(baseName, event => { 17550 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17551 source: this, 17552 detail: { 17553 id: this.data.id, 17554 name: eventName, 17555 value: valueGetter(event), 17556 shift: event.shiftKey, 17557 modifier: this._getKeyModifier(event) 17558 } 17559 }); 17560 }); 17561 } else { 17562 element.addEventListener(baseName, event => { 17563 if (baseName === "blur") { 17564 if (!elementData.focused || !event.relatedTarget) { 17565 return; 17566 } 17567 elementData.focused = false; 17568 } else if (baseName === "focus") { 17569 if (elementData.focused) { 17570 return; 17571 } 17572 elementData.focused = true; 17573 } 17574 if (!valueGetter) { 17575 return; 17576 } 17577 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17578 source: this, 17579 detail: { 17580 id: this.data.id, 17581 name: eventName, 17582 value: valueGetter(event) 17583 } 17584 }); 17585 }); 17586 } 17587 } 17588 _setEventListeners(element, elementData, names, getter) { 17589 for (const [baseName, eventName] of names) { 17590 if (eventName === "Action" || this.data.actions?.[eventName]) { 17591 if (eventName === "Focus" || eventName === "Blur") { 17592 elementData ||= { 17593 focused: false 17594 }; 17595 } 17596 this._setEventListener(element, elementData, baseName, eventName, getter); 17597 if (eventName === "Focus" && !this.data.actions?.Blur) { 17598 this._setEventListener(element, elementData, "blur", "Blur", null); 17599 } else if (eventName === "Blur" && !this.data.actions?.Focus) { 17600 this._setEventListener(element, elementData, "focus", "Focus", null); 17601 } 17602 } 17603 } 17604 } 17605 _setBackgroundColor(element) { 17606 const color = this.data.backgroundColor || null; 17607 element.style.backgroundColor = color === null ? "transparent" : Util.makeHexColor(color[0], color[1], color[2]); 17608 } 17609 _setTextStyle(element) { 17610 const TEXT_ALIGNMENT = ["left", "center", "right"]; 17611 const { 17612 fontColor 17613 } = this.data.defaultAppearanceData; 17614 const fontSize = this.data.defaultAppearanceData.fontSize || annotation_layer_DEFAULT_FONT_SIZE; 17615 const style = element.style;
vendor: 33,566 bytes, lines 17616-18595
17616 let computedFontSize; 17617 const BORDER_SIZE = 2; 17618 const roundToOneDecimal = x => Math.round(10 * x) / 10; 17619 if (this.data.multiLine) { 17620 const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); 17621 const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; 17622 const lineHeight = height / numberOfLines; 17623 computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / LINE_FACTOR)); 17624 } else { 17625 const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE); 17626 computedFontSize = Math.min(fontSize, roundToOneDecimal(height / LINE_FACTOR)); 17627 } 17628 style.fontSize = `calc(${computedFontSize}px * var(--total-scale-factor))`; 17629 style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); 17630 if (this.data.textAlignment !== null) { 17631 style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; 17632 } 17633 } 17634 _setRequired(element, isRequired) { 17635 if (isRequired) { 17636 element.setAttribute("required", true); 17637 } else { 17638 element.removeAttribute("required"); 17639 } 17640 element.setAttribute("aria-required", isRequired); 17641 } 17642} 17643class TextWidgetAnnotationElement extends WidgetAnnotationElement { 17644 constructor(parameters) { 17645 const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue; 17646 super(parameters, { 17647 isRenderable 17648 }); 17649 } 17650 setPropertyOnSiblings(base, key, value, keyInStorage) { 17651 const storage = this.annotationStorage; 17652 for (const element of this._getElementsByName(base.name, base.id)) { 17653 if (element.domElement) { 17654 element.domElement[key] = value; 17655 } 17656 storage.setValue(element.id, { 17657 [keyInStorage]: value 17658 }); 17659 } 17660 } 17661 render() { 17662 const storage = this.annotationStorage; 17663 const id = this.data.id; 17664 this.container.classList.add("textWidgetAnnotation"); 17665 let element = null; 17666 if (this.renderForms) { 17667 const storedData = storage.getValue(id, { 17668 value: this.data.fieldValue 17669 }); 17670 let textContent = storedData.value || ""; 17671 const maxLen = storage.getValue(id, { 17672 charLimit: this.data.maxLen 17673 }).charLimit; 17674 if (maxLen && textContent.length > maxLen) { 17675 textContent = textContent.slice(0, maxLen); 17676 } 17677 let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join("\n") || null; 17678 if (fieldFormattedValues && this.data.comb) { 17679 fieldFormattedValues = fieldFormattedValues.replaceAll(/\s+/g, ""); 17680 } 17681 const elementData = { 17682 userValue: textContent, 17683 formattedValue: fieldFormattedValues, 17684 lastCommittedValue: null, 17685 commitKey: 1, 17686 focused: false 17687 }; 17688 if (this.data.multiLine) { 17689 element = document.createElement("textarea"); 17690 element.textContent = fieldFormattedValues ?? textContent; 17691 if (this.data.doNotScroll) { 17692 element.style.overflowY = "hidden"; 17693 } 17694 } else { 17695 element = document.createElement("input"); 17696 element.type = this.data.password ? "password" : "text"; 17697 element.setAttribute("value", fieldFormattedValues ?? textContent); 17698 if (this.data.doNotScroll) { 17699 element.style.overflowX = "hidden"; 17700 } 17701 } 17702 if (this.data.hasOwnCanvas) { 17703 element.hidden = true; 17704 } 17705 GetElementsByNameSet.add(element); 17706 this.contentElement = element; 17707 element.setAttribute("data-element-id", id); 17708 element.disabled = this.data.readOnly; 17709 element.name = this.data.fieldName; 17710 element.tabIndex = 0; 17711 const { 17712 datetimeFormat, 17713 datetimeType, 17714 timeStep 17715 } = this.data; 17716 const hasDateOrTime = !!datetimeType && this.enableScripting; 17717 if (datetimeFormat) { 17718 element.title = datetimeFormat; 17719 } 17720 this._setRequired(element, this.data.required); 17721 if (maxLen) { 17722 element.maxLength = maxLen; 17723 } 17724 element.addEventListener("input", event => { 17725 storage.setValue(id, { 17726 value: event.target.value 17727 }); 17728 this.setPropertyOnSiblings(element, "value", event.target.value, "value"); 17729 elementData.formattedValue = null; 17730 }); 17731 element.addEventListener("resetform", event => { 17732 const defaultValue = this.data.defaultFieldValue ?? ""; 17733 element.value = elementData.userValue = defaultValue; 17734 elementData.formattedValue = null; 17735 }); 17736 let blurListener = event => { 17737 const { 17738 formattedValue 17739 } = elementData; 17740 if (formattedValue !== null && formattedValue !== undefined) { 17741 event.target.value = formattedValue; 17742 } 17743 event.target.scrollLeft = 0; 17744 }; 17745 if (this.enableScripting && this.hasJSActions) { 17746 element.addEventListener("focus", event => { 17747 if (elementData.focused) { 17748 return; 17749 } 17750 const { 17751 target 17752 } = event; 17753 if (hasDateOrTime) { 17754 target.type = datetimeType; 17755 if (timeStep) { 17756 target.step = timeStep; 17757 } 17758 } 17759 if (elementData.userValue) { 17760 const value = elementData.userValue; 17761 if (hasDateOrTime) { 17762 if (datetimeType === "time") { 17763 const date = new Date(value); 17764 const parts = [date.getHours(), date.getMinutes(), date.getSeconds()]; 17765 target.value = parts.map(v => v.toString().padStart(2, "0")).join(":"); 17766 } else { 17767 target.value = new Date(value - TIMEZONE_OFFSET).toISOString().split(datetimeType === "date" ? "T" : ".", 1)[0]; 17768 } 17769 } else { 17770 target.value = value; 17771 } 17772 } 17773 elementData.lastCommittedValue = target.value; 17774 elementData.commitKey = 1; 17775 if (!this.data.actions?.Focus) { 17776 elementData.focused = true; 17777 } 17778 }); 17779 element.addEventListener("updatefromsandbox", jsEvent => { 17780 this.showElementAndHideCanvas(jsEvent.target); 17781 const actions = { 17782 value(event) { 17783 elementData.userValue = event.detail.value ?? ""; 17784 if (!hasDateOrTime) { 17785 storage.setValue(id, { 17786 value: elementData.userValue.toString() 17787 }); 17788 } 17789 event.target.value = elementData.userValue; 17790 }, 17791 formattedValue(event) { 17792 const { 17793 formattedValue 17794 } = event.detail; 17795 elementData.formattedValue = formattedValue; 17796 if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) { 17797 event.target.value = formattedValue; 17798 } 17799 const data = { 17800 formattedValue 17801 }; 17802 if (hasDateOrTime) { 17803 data.value = formattedValue; 17804 } 17805 storage.setValue(id, data); 17806 }, 17807 selRange(event) { 17808 event.target.setSelectionRange(...event.detail.selRange); 17809 }, 17810 charLimit: event => { 17811 const { 17812 charLimit 17813 } = event.detail; 17814 const { 17815 target 17816 } = event; 17817 if (charLimit === 0) { 17818 target.removeAttribute("maxLength"); 17819 return; 17820 } 17821 target.setAttribute("maxLength", charLimit); 17822 let value = elementData.userValue; 17823 if (!value || value.length <= charLimit) { 17824 return; 17825 } 17826 value = value.slice(0, charLimit); 17827 target.value = elementData.userValue = value; 17828 storage.setValue(id, { 17829 value 17830 }); 17831 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17832 source: this, 17833 detail: { 17834 id, 17835 name: "Keystroke", 17836 value, 17837 willCommit: true, 17838 commitKey: 1, 17839 selStart: target.selectionStart, 17840 selEnd: target.selectionEnd 17841 } 17842 }); 17843 } 17844 }; 17845 this._dispatchEventFromSandbox(actions, jsEvent); 17846 }); 17847 element.addEventListener("keydown", event => { 17848 elementData.commitKey = 1; 17849 let commitKey = -1; 17850 if (event.key === "Escape") { 17851 commitKey = 0; 17852 } else if (event.key === "Enter" && !this.data.multiLine) { 17853 commitKey = 2; 17854 } else if (event.key === "Tab") { 17855 elementData.commitKey = 3; 17856 } 17857 if (commitKey === -1) { 17858 return; 17859 } 17860 const { 17861 value 17862 } = event.target; 17863 if (elementData.lastCommittedValue === value) { 17864 return; 17865 } 17866 elementData.lastCommittedValue = value; 17867 elementData.userValue = value; 17868 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17869 source: this, 17870 detail: { 17871 id, 17872 name: "Keystroke", 17873 value, 17874 willCommit: true, 17875 commitKey, 17876 selStart: event.target.selectionStart, 17877 selEnd: event.target.selectionEnd 17878 } 17879 }); 17880 }); 17881 const _blurListener = blurListener; 17882 blurListener = null; 17883 element.addEventListener("blur", event => { 17884 if (!elementData.focused || !event.relatedTarget) { 17885 return; 17886 } 17887 if (!this.data.actions?.Blur) { 17888 elementData.focused = false; 17889 } 17890 const { 17891 target 17892 } = event; 17893 let { 17894 value 17895 } = target; 17896 if (hasDateOrTime) { 17897 if (value && datetimeType === "time") { 17898 const parts = value.split(":").map(v => parseInt(v, 10)); 17899 value = new Date(2000, 0, 1, parts[0], parts[1], parts[2] || 0).valueOf(); 17900 target.step = ""; 17901 } else { 17902 if (!value.includes("T")) { 17903 value = `${value}T00:00`; 17904 } 17905 value = new Date(value).valueOf(); 17906 } 17907 target.type = "text"; 17908 } 17909 elementData.userValue = value; 17910 if (elementData.lastCommittedValue !== value) { 17911 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17912 source: this, 17913 detail: { 17914 id, 17915 name: "Keystroke", 17916 value, 17917 willCommit: true, 17918 commitKey: elementData.commitKey, 17919 selStart: event.target.selectionStart, 17920 selEnd: event.target.selectionEnd 17921 } 17922 }); 17923 } 17924 _blurListener(event); 17925 }); 17926 if (this.data.actions?.Keystroke) { 17927 element.addEventListener("beforeinput", event => { 17928 elementData.lastCommittedValue = null; 17929 const { 17930 data, 17931 target 17932 } = event; 17933 const { 17934 value, 17935 selectionStart, 17936 selectionEnd 17937 } = target; 17938 let selStart = selectionStart, 17939 selEnd = selectionEnd; 17940 switch (event.inputType) { 17941 case "deleteWordBackward": 17942 { 17943 const match = value.substring(0, selectionStart).match(/\w*[^\w]*$/); 17944 if (match) { 17945 selStart -= match[0].length; 17946 } 17947 break; 17948 } 17949 case "deleteWordForward": 17950 { 17951 const match = value.substring(selectionStart).match(/^[^\w]*\w*/); 17952 if (match) { 17953 selEnd += match[0].length; 17954 } 17955 break; 17956 } 17957 case "deleteContentBackward": 17958 if (selectionStart === selectionEnd) { 17959 selStart -= 1; 17960 } 17961 break; 17962 case "deleteContentForward": 17963 if (selectionStart === selectionEnd) { 17964 selEnd += 1; 17965 } 17966 break; 17967 } 17968 event.preventDefault(); 17969 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 17970 source: this, 17971 detail: { 17972 id, 17973 name: "Keystroke", 17974 value, 17975 change: data || "", 17976 willCommit: false, 17977 selStart, 17978 selEnd 17979 } 17980 }); 17981 }); 17982 } 17983 this._setEventListeners(element, elementData, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value); 17984 } 17985 if (blurListener) { 17986 element.addEventListener("blur", blurListener); 17987 } 17988 if (this.data.comb) { 17989 const fieldWidth = this.data.rect[2] - this.data.rect[0]; 17990 const combWidth = fieldWidth / maxLen; 17991 element.classList.add("comb"); 17992 element.style.letterSpacing = `calc(${combWidth}px * var(--total-scale-factor) - 1ch)`; 17993 } 17994 } else { 17995 element = document.createElement("div"); 17996 element.textContent = this.data.fieldValue; 17997 element.style.verticalAlign = "middle"; 17998 element.style.display = "table-cell"; 17999 if (this.data.hasOwnCanvas) { 18000 element.hidden = true; 18001 } 18002 } 18003 this._setTextStyle(element); 18004 this._setBackgroundColor(element); 18005 this._setDefaultPropertiesFromJS(element); 18006 this.container.append(element); 18007 return this.container; 18008 } 18009} 18010class SignatureWidgetAnnotationElement extends WidgetAnnotationElement { 18011 constructor(parameters) { 18012 super(parameters, { 18013 isRenderable: !!parameters.data.hasOwnCanvas 18014 }); 18015 } 18016} 18017class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { 18018 constructor(parameters) { 18019 super(parameters, { 18020 isRenderable: parameters.renderForms 18021 }); 18022 } 18023 render() { 18024 const storage = this.annotationStorage; 18025 const data = this.data; 18026 const id = data.id; 18027 let value = storage.getValue(id, { 18028 value: data.exportValue === data.fieldValue 18029 }).value; 18030 if (typeof value === "string") { 18031 value = value !== "Off"; 18032 storage.setValue(id, { 18033 value 18034 }); 18035 } 18036 this.container.classList.add("buttonWidgetAnnotation", "checkBox"); 18037 const element = document.createElement("input"); 18038 GetElementsByNameSet.add(element); 18039 element.setAttribute("data-element-id", id); 18040 element.disabled = data.readOnly; 18041 this._setRequired(element, this.data.required); 18042 element.type = "checkbox"; 18043 element.name = data.fieldName; 18044 if (value) { 18045 element.setAttribute("checked", true); 18046 } 18047 element.setAttribute("exportValue", data.exportValue); 18048 element.tabIndex = 0; 18049 element.addEventListener("change", event => { 18050 const { 18051 name, 18052 checked 18053 } = event.target; 18054 for (const checkbox of this._getElementsByName(name, id)) { 18055 const curChecked = checked && checkbox.exportValue === data.exportValue; 18056 if (checkbox.domElement) { 18057 checkbox.domElement.checked = curChecked; 18058 } 18059 storage.setValue(checkbox.id, { 18060 value: curChecked 18061 }); 18062 } 18063 storage.setValue(id, { 18064 value: checked 18065 }); 18066 }); 18067 element.addEventListener("resetform", event => { 18068 const defaultValue = data.defaultFieldValue || "Off"; 18069 event.target.checked = defaultValue === data.exportValue; 18070 }); 18071 if (this.enableScripting && this.hasJSActions) { 18072 element.addEventListener("updatefromsandbox", jsEvent => { 18073 const actions = { 18074 value(event) { 18075 event.target.checked = event.detail.value !== "Off"; 18076 storage.setValue(id, { 18077 value: event.target.checked 18078 }); 18079 } 18080 }; 18081 this._dispatchEventFromSandbox(actions, jsEvent); 18082 }); 18083 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); 18084 } 18085 this._setBackgroundColor(element); 18086 this._setDefaultPropertiesFromJS(element); 18087 this.container.append(element); 18088 return this.container; 18089 } 18090} 18091class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { 18092 constructor(parameters) { 18093 super(parameters, { 18094 isRenderable: parameters.renderForms 18095 }); 18096 } 18097 render() { 18098 this.container.classList.add("buttonWidgetAnnotation", "radioButton"); 18099 const storage = this.annotationStorage; 18100 const data = this.data; 18101 const id = data.id; 18102 let value = storage.getValue(id, { 18103 value: data.fieldValue === data.buttonValue 18104 }).value; 18105 if (typeof value === "string") { 18106 value = value !== data.buttonValue; 18107 storage.setValue(id, { 18108 value 18109 }); 18110 } 18111 if (value) { 18112 for (const radio of this._getElementsByName(data.fieldName, id)) { 18113 storage.setValue(radio.id, { 18114 value: false 18115 }); 18116 } 18117 } 18118 const element = document.createElement("input"); 18119 GetElementsByNameSet.add(element); 18120 element.setAttribute("data-element-id", id); 18121 element.disabled = data.readOnly; 18122 this._setRequired(element, this.data.required); 18123 element.type = "radio"; 18124 element.name = data.fieldName; 18125 if (value) { 18126 element.setAttribute("checked", true); 18127 } 18128 element.tabIndex = 0; 18129 element.addEventListener("change", event => { 18130 const { 18131 name, 18132 checked 18133 } = event.target; 18134 for (const radio of this._getElementsByName(name, id)) { 18135 storage.setValue(radio.id, { 18136 value: false 18137 }); 18138 } 18139 storage.setValue(id, { 18140 value: checked 18141 }); 18142 }); 18143 element.addEventListener("resetform", event => { 18144 const defaultValue = data.defaultFieldValue; 18145 event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue; 18146 }); 18147 if (this.enableScripting && this.hasJSActions) { 18148 const pdfButtonValue = data.buttonValue; 18149 element.addEventListener("updatefromsandbox", jsEvent => { 18150 const actions = { 18151 value: event => { 18152 const checked = pdfButtonValue === event.detail.value; 18153 for (const radio of this._getElementsByName(event.target.name)) { 18154 const curChecked = checked && radio.id === id; 18155 if (radio.domElement) { 18156 radio.domElement.checked = curChecked; 18157 } 18158 storage.setValue(radio.id, { 18159 value: curChecked 18160 }); 18161 } 18162 } 18163 }; 18164 this._dispatchEventFromSandbox(actions, jsEvent); 18165 }); 18166 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); 18167 } 18168 this._setBackgroundColor(element); 18169 this._setDefaultPropertiesFromJS(element); 18170 this.container.append(element); 18171 return this.container; 18172 } 18173} 18174class PushButtonWidgetAnnotationElement extends LinkAnnotationElement { 18175 constructor(parameters) { 18176 super(parameters, { 18177 ignoreBorder: parameters.data.hasAppearance 18178 }); 18179 } 18180 render() { 18181 const container = super.render(); 18182 container.classList.add("buttonWidgetAnnotation", "pushButton"); 18183 const linkElement = container.lastChild; 18184 if (this.enableScripting && this.hasJSActions && linkElement) { 18185 this._setDefaultPropertiesFromJS(linkElement); 18186 linkElement.addEventListener("updatefromsandbox", jsEvent => { 18187 this._dispatchEventFromSandbox({}, jsEvent); 18188 }); 18189 } 18190 return container; 18191 } 18192} 18193class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { 18194 constructor(parameters) { 18195 super(parameters, { 18196 isRenderable: parameters.renderForms 18197 }); 18198 } 18199 render() { 18200 this.container.classList.add("choiceWidgetAnnotation"); 18201 const storage = this.annotationStorage; 18202 const id = this.data.id; 18203 const storedData = storage.getValue(id, { 18204 value: this.data.fieldValue 18205 }); 18206 const selectElement = document.createElement("select"); 18207 GetElementsByNameSet.add(selectElement); 18208 selectElement.setAttribute("data-element-id", id); 18209 selectElement.disabled = this.data.readOnly; 18210 this._setRequired(selectElement, this.data.required); 18211 selectElement.name = this.data.fieldName; 18212 selectElement.tabIndex = 0; 18213 let addAnEmptyEntry = this.data.combo && this.data.options.length > 0; 18214 if (!this.data.combo) { 18215 selectElement.size = this.data.options.length; 18216 if (this.data.multiSelect) { 18217 selectElement.multiple = true; 18218 } 18219 } 18220 selectElement.addEventListener("resetform", event => { 18221 const defaultValue = this.data.defaultFieldValue; 18222 for (const option of selectElement.options) { 18223 option.selected = option.value === defaultValue; 18224 } 18225 }); 18226 for (const option of this.data.options) { 18227 const optionElement = document.createElement("option"); 18228 optionElement.textContent = option.displayValue; 18229 optionElement.value = option.exportValue; 18230 if (storedData.value.includes(option.exportValue)) { 18231 optionElement.setAttribute("selected", true); 18232 addAnEmptyEntry = false; 18233 } 18234 selectElement.append(optionElement); 18235 } 18236 let removeEmptyEntry = null; 18237 if (addAnEmptyEntry) { 18238 const noneOptionElement = document.createElement("option"); 18239 noneOptionElement.value = " "; 18240 noneOptionElement.setAttribute("hidden", true); 18241 noneOptionElement.setAttribute("selected", true); 18242 selectElement.prepend(noneOptionElement); 18243 removeEmptyEntry = () => { 18244 noneOptionElement.remove(); 18245 selectElement.removeEventListener("input", removeEmptyEntry); 18246 removeEmptyEntry = null; 18247 }; 18248 selectElement.addEventListener("input", removeEmptyEntry); 18249 } 18250 const getValue = isExport => { 18251 const name = isExport ? "value" : "textContent"; 18252 const { 18253 options, 18254 multiple 18255 } = selectElement; 18256 if (!multiple) { 18257 return options.selectedIndex === -1 ? null : options[options.selectedIndex][name]; 18258 } 18259 return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]); 18260 }; 18261 let selectedValues = getValue(false); 18262 const getItems = event => { 18263 const options = event.target.options; 18264 return Array.prototype.map.call(options, option => ({ 18265 displayValue: option.textContent, 18266 exportValue: option.value 18267 })); 18268 }; 18269 if (this.enableScripting && this.hasJSActions) { 18270 selectElement.addEventListener("updatefromsandbox", jsEvent => { 18271 const actions = { 18272 value(event) { 18273 removeEmptyEntry?.(); 18274 const value = event.detail.value; 18275 const values = new Set(Array.isArray(value) ? value : [value]); 18276 for (const option of selectElement.options) { 18277 option.selected = values.has(option.value); 18278 } 18279 storage.setValue(id, { 18280 value: getValue(true) 18281 }); 18282 selectedValues = getValue(false); 18283 }, 18284 multipleSelection(event) { 18285 selectElement.multiple = true; 18286 }, 18287 remove(event) { 18288 const options = selectElement.options; 18289 const index = event.detail.remove; 18290 options[index].selected = false; 18291 selectElement.remove(index); 18292 if (options.length > 0) { 18293 const i = Array.prototype.findIndex.call(options, option => option.selected); 18294 if (i === -1) { 18295 options[0].selected = true; 18296 } 18297 } 18298 storage.setValue(id, { 18299 value: getValue(true), 18300 items: getItems(event) 18301 }); 18302 selectedValues = getValue(false); 18303 }, 18304 clear(event) { 18305 while (selectElement.length !== 0) { 18306 selectElement.remove(0); 18307 } 18308 storage.setValue(id, { 18309 value: null, 18310 items: [] 18311 }); 18312 selectedValues = getValue(false); 18313 }, 18314 insert(event) { 18315 const { 18316 index, 18317 displayValue, 18318 exportValue 18319 } = event.detail.insert; 18320 const selectChild = selectElement.children[index]; 18321 const optionElement = document.createElement("option"); 18322 optionElement.textContent = displayValue; 18323 optionElement.value = exportValue; 18324 if (selectChild) { 18325 selectChild.before(optionElement); 18326 } else { 18327 selectElement.append(optionElement); 18328 } 18329 storage.setValue(id, { 18330 value: getValue(true), 18331 items: getItems(event) 18332 }); 18333 selectedValues = getValue(false); 18334 }, 18335 items(event) { 18336 const { 18337 items 18338 } = event.detail; 18339 while (selectElement.length !== 0) { 18340 selectElement.remove(0); 18341 } 18342 for (const item of items) { 18343 const { 18344 displayValue, 18345 exportValue 18346 } = item; 18347 const optionElement = document.createElement("option"); 18348 optionElement.textContent = displayValue; 18349 optionElement.value = exportValue; 18350 selectElement.append(optionElement); 18351 } 18352 if (selectElement.options.length > 0) { 18353 selectElement.options[0].selected = true; 18354 } 18355 storage.setValue(id, { 18356 value: getValue(true), 18357 items: getItems(event) 18358 }); 18359 selectedValues = getValue(false); 18360 }, 18361 indices(event) { 18362 const indices = new Set(event.detail.indices); 18363 for (const option of event.target.options) { 18364 option.selected = indices.has(option.index); 18365 } 18366 storage.setValue(id, { 18367 value: getValue(true) 18368 }); 18369 selectedValues = getValue(false); 18370 }, 18371 editable(event) { 18372 event.target.disabled = !event.detail.editable; 18373 } 18374 }; 18375 this._dispatchEventFromSandbox(actions, jsEvent); 18376 }); 18377 selectElement.addEventListener("input", event => { 18378 const exportValue = getValue(true); 18379 const change = getValue(false); 18380 storage.setValue(id, { 18381 value: exportValue 18382 }); 18383 event.preventDefault(); 18384 this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { 18385 source: this, 18386 detail: { 18387 id, 18388 name: "Keystroke", 18389 value: selectedValues, 18390 change, 18391 changeEx: exportValue, 18392 willCommit: false, 18393 commitKey: 1, 18394 keyDown: false 18395 } 18396 }); 18397 }); 18398 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); 18399 } else { 18400 selectElement.addEventListener("input", function (event) { 18401 storage.setValue(id, { 18402 value: getValue(true) 18403 }); 18404 }); 18405 } 18406 if (this.data.combo) { 18407 this._setTextStyle(selectElement); 18408 } else {} 18409 this._setBackgroundColor(selectElement); 18410 this._setDefaultPropertiesFromJS(selectElement); 18411 this.container.append(selectElement); 18412 return this.container; 18413 } 18414} 18415class PopupAnnotationElement extends AnnotationElement { 18416 constructor(parameters) { 18417 const { 18418 data, 18419 elements, 18420 parent 18421 } = parameters; 18422 const hasCommentManager = !!parent._commentManager; 18423 super(parameters, { 18424 isRenderable: !hasCommentManager && AnnotationElement._hasPopupData(data) 18425 }); 18426 this.elements = elements; 18427 if (hasCommentManager && AnnotationElement._hasPopupData(data)) { 18428 const popup = this.popup = this.#createPopup(); 18429 for (const element of elements) { 18430 element.popup = popup; 18431 } 18432 } else { 18433 this.popup = null; 18434 } 18435 } 18436 #createPopup() { 18437 return new PopupElement({ 18438 container: this.container, 18439 color: this.data.color, 18440 titleObj: this.data.titleObj, 18441 modificationDate: this.data.modificationDate || this.data.creationDate, 18442 contentsObj: this.data.contentsObj, 18443 richText: this.data.richText, 18444 rect: this.data.rect, 18445 parentRect: this.data.parentRect || null, 18446 parent: this.parent, 18447 elements: this.elements, 18448 open: this.data.open, 18449 commentManager: this.parent._commentManager 18450 }); 18451 } 18452 render() { 18453 const { 18454 container 18455 } = this; 18456 container.classList.add("popupAnnotation"); 18457 container.role = "comment"; 18458 const popup = this.popup = this.#createPopup(); 18459 const elementIds = []; 18460 for (const element of this.elements) { 18461 element.popup = popup; 18462 element.container.ariaHasPopup = "dialog"; 18463 elementIds.push(element.data.id); 18464 element.addHighlightArea(); 18465 } 18466 this.container.setAttribute("aria-controls", elementIds.map(id => `${AnnotationPrefix}${id}`).join(",")); 18467 return this.container; 18468 } 18469} 18470class PopupElement { 18471 #commentManager = null; 18472 #boundKeyDown = this.#keyDown.bind(this); 18473 #boundHide = this.#hide.bind(this); 18474 #boundShow = this.#show.bind(this); 18475 #boundToggle = this.#toggle.bind(this); 18476 #color = null; 18477 #container = null; 18478 #contentsObj = null; 18479 #dateObj = null; 18480 #elements = null; 18481 #parent = null; 18482 #parentRect = null; 18483 #pinned = false; 18484 #popup = null; 18485 #popupAbortController = null; 18486 #position = null; 18487 #commentButton = null; 18488 #commentButtonPosition = null; 18489 #popupPosition = null; 18490 #rect = null; 18491 #richText = null; 18492 #titleObj = null; 18493 #updates = null; 18494 #wasVisible = false; 18495 #firstElement = null; 18496 #commentText = null; 18497 constructor({ 18498 container, 18499 color, 18500 elements, 18501 titleObj, 18502 modificationDate, 18503 contentsObj, 18504 richText, 18505 parent, 18506 rect, 18507 parentRect, 18508 open, 18509 commentManager = null 18510 }) { 18511 this.#container = container; 18512 this.#titleObj = titleObj; 18513 this.#contentsObj = contentsObj; 18514 this.#richText = richText; 18515 this.#parent = parent; 18516 this.#color = color; 18517 this.#rect = rect; 18518 this.#parentRect = parentRect; 18519 this.#elements = elements; 18520 this.#commentManager = commentManager; 18521 this.#firstElement = elements[0]; 18522 this.#dateObj = PDFDateString.toDateObject(modificationDate); 18523 this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup()); 18524 if (!commentManager) { 18525 this.#addEventListeners(); 18526 this.#container.hidden = true; 18527 if (open) { 18528 this.#toggle(); 18529 } 18530 } 18531 } 18532 #addEventListeners() { 18533 if (this.#popupAbortController) { 18534 return; 18535 } 18536 this.#popupAbortController = new AbortController(); 18537 const { 18538 signal 18539 } = this.#popupAbortController; 18540 for (const element of this.trigger) { 18541 element.addEventListener("click", this.#boundToggle, { 18542 signal 18543 }); 18544 element.addEventListener("pointerenter", this.#boundShow, { 18545 signal 18546 }); 18547 element.addEventListener("pointerleave", this.#boundHide, { 18548 signal 18549 }); 18550 element.classList.add("popupTriggerArea"); 18551 } 18552 for (const element of this.#elements) { 18553 element.container?.addEventListener("keydown", this.#boundKeyDown, { 18554 signal 18555 }); 18556 } 18557 } 18558 #setCommentButtonPosition() { 18559 const element = this.#elements.find(e => e.hasCommentButton); 18560 if (!element) { 18561 return; 18562 } 18563 this.#commentButtonPosition = element._normalizePoint(element.commentButtonPosition); 18564 } 18565 renderCommentButton() { 18566 if (this.#commentButton) { 18567 if (!this.#commentButton.parentNode) { 18568 this.#firstElement.container.after(this.#commentButton); 18569 } 18570 return; 18571 } 18572 if (!this.#commentButtonPosition) { 18573 this.#setCommentButtonPosition(); 18574 } 18575 if (!this.#commentButtonPosition) { 18576 return; 18577 } 18578 const { 18579 signal 18580 } = this.#popupAbortController = new AbortController(); 18581 const hasOwnButton = this.#firstElement.hasOwnCommentButton; 18582 const togglePopup = () => { 18583 this.#commentManager.toggleCommentPopup(this, true, undefined, !hasOwnButton); 18584 }; 18585 const showPopup = () => { 18586 this.#commentManager.toggleCommentPopup(this, false, true, !hasOwnButton); 18587 }; 18588 const hidePopup = () => { 18589 this.#commentManager.toggleCommentPopup(this, false, false); 18590 }; 18591 if (!hasOwnButton) { 18592 const button = this.#commentButton = document.createElement("button"); 18593 button.className = "annotationCommentButton"; 18594 const parentContainer = this.#firstElement.container; 18595 button.style.zIndex = parentContainer.style.zIndex
vendor: 10,950 bytes, lines 18595-18992
18595 + 1; 18596 button.tabIndex = 0; 18597 button.ariaHasPopup = "dialog"; 18598 button.ariaControls = "commentPopup"; 18599 button.setAttribute("data-l10n-id", "pdfjs-show-comment-button"); 18600 this.#updateColor(); 18601 this.#updateCommentButtonPosition(); 18602 button.addEventListener("keydown", this.#boundKeyDown, { 18603 signal 18604 }); 18605 button.addEventListener("click", togglePopup, { 18606 signal 18607 }); 18608 button.addEventListener("pointerenter", showPopup, { 18609 signal 18610 }); 18611 button.addEventListener("pointerleave", hidePopup, { 18612 signal 18613 }); 18614 parentContainer.after(button); 18615 } else { 18616 this.#commentButton = this.#firstElement.container; 18617 for (const element of this.trigger) { 18618 element.ariaHasPopup = "dialog"; 18619 element.ariaControls = "commentPopup"; 18620 element.addEventListener("keydown", this.#boundKeyDown, { 18621 signal 18622 }); 18623 element.addEventListener("click", togglePopup, { 18624 signal 18625 }); 18626 element.addEventListener("pointerenter", showPopup, { 18627 signal 18628 }); 18629 element.addEventListener("pointerleave", hidePopup, { 18630 signal 18631 }); 18632 element.classList.add("popupTriggerArea"); 18633 } 18634 } 18635 } 18636 #updateCommentButtonPosition() { 18637 if (this.#firstElement.extraPopupElement && !this.#firstElement.editor) { 18638 return; 18639 } 18640 if (!this.#commentButton) { 18641 this.renderCommentButton(); 18642 } 18643 const [x, y] = this.#commentButtonPosition; 18644 const { 18645 style 18646 } = this.#commentButton; 18647 style.left = `calc(${x}%)`; 18648 style.top = `calc(${y}% - var(--comment-button-dim))`; 18649 } 18650 #updateColor() { 18651 if (this.#firstElement.extraPopupElement) { 18652 return; 18653 } 18654 if (!this.#commentButton) { 18655 this.renderCommentButton(); 18656 } 18657 this.#commentButton.style.backgroundColor = this.commentButtonColor || ""; 18658 } 18659 get commentButtonColor() { 18660 const { 18661 color, 18662 opacity 18663 } = this.#firstElement.commentData; 18664 if (!color) { 18665 return null; 18666 } 18667 return this.#parent._commentManager.makeCommentColor(color, opacity); 18668 } 18669 focusCommentButton() { 18670 setTimeout(() => { 18671 this.#commentButton?.focus(); 18672 }, 0); 18673 } 18674 getData() { 18675 const { 18676 richText, 18677 color, 18678 opacity, 18679 creationDate, 18680 modificationDate 18681 } = this.#firstElement.commentData; 18682 return { 18683 contentsObj: { 18684 str: this.comment 18685 }, 18686 richText, 18687 color, 18688 opacity, 18689 creationDate, 18690 modificationDate 18691 }; 18692 } 18693 get elementBeforePopup() { 18694 return this.#commentButton; 18695 } 18696 get comment() { 18697 this.#commentText ||= this.#firstElement.commentText; 18698 return this.#commentText; 18699 } 18700 set comment(text) { 18701 if (text === this.comment) { 18702 return; 18703 } 18704 this.#firstElement.commentText = this.#commentText = text; 18705 } 18706 focus() { 18707 this.#firstElement.container?.focus(); 18708 } 18709 get parentBoundingClientRect() { 18710 return this.#firstElement.layer.getBoundingClientRect(); 18711 } 18712 setCommentButtonStates({ 18713 selected, 18714 hasPopup 18715 }) { 18716 if (!this.#commentButton) { 18717 return; 18718 } 18719 this.#commentButton.classList.toggle("selected", selected); 18720 this.#commentButton.ariaExpanded = hasPopup; 18721 } 18722 setSelectedCommentButton(selected) { 18723 this.#commentButton.classList.toggle("selected", selected); 18724 } 18725 get commentPopupPosition() { 18726 if (this.#popupPosition) { 18727 return this.#popupPosition; 18728 } 18729 const { 18730 x, 18731 y, 18732 height 18733 } = this.#commentButton.getBoundingClientRect(); 18734 const { 18735 x: parentX, 18736 y: parentY, 18737 width: parentWidth, 18738 height: parentHeight 18739 } = this.#firstElement.layer.getBoundingClientRect(); 18740 return [(x - parentX) / parentWidth, (y + height - parentY) / parentHeight]; 18741 } 18742 set commentPopupPosition(pos) { 18743 this.#popupPosition = pos; 18744 } 18745 hasDefaultPopupPosition() { 18746 return this.#popupPosition === null; 18747 } 18748 get commentButtonPosition() { 18749 return this.#commentButtonPosition; 18750 } 18751 get commentButtonWidth() { 18752 return this.#commentButton.getBoundingClientRect().width / this.parentBoundingClientRect.width; 18753 } 18754 editComment(options) { 18755 const [posX, posY] = this.#popupPosition || this.commentButtonPosition.map(x => x / 100); 18756 const parentDimensions = this.parentBoundingClientRect; 18757 const { 18758 x: parentX, 18759 y: parentY, 18760 width: parentWidth, 18761 height: parentHeight 18762 } = parentDimensions; 18763 this.#commentManager.showDialog(null, this, parentX + posX * parentWidth, parentY + posY * parentHeight, { 18764 ...options, 18765 parentDimensions 18766 }); 18767 } 18768 render() { 18769 if (this.#popup) { 18770 return; 18771 } 18772 const popup = this.#popup = document.createElement("div"); 18773 popup.className = "popup"; 18774 if (this.#color) { 18775 const baseColor = popup.style.outlineColor = Util.makeHexColor(...this.#color); 18776 popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`; 18777 } 18778 const header = document.createElement("span"); 18779 header.className = "header"; 18780 if (this.#titleObj?.str) { 18781 const title = document.createElement("span"); 18782 title.className = "title"; 18783 header.append(title); 18784 ({ 18785 dir: title.dir, 18786 str: title.textContent 18787 } = this.#titleObj); 18788 } 18789 popup.append(header); 18790 if (this.#dateObj) { 18791 const modificationDate = document.createElement("time"); 18792 modificationDate.className = "popupDate"; 18793 modificationDate.setAttribute("data-l10n-id", "pdfjs-annotation-date-time-string"); 18794 modificationDate.setAttribute("data-l10n-args", JSON.stringify({ 18795 dateObj: this.#dateObj.valueOf() 18796 })); 18797 modificationDate.dateTime = this.#dateObj.toISOString(); 18798 header.append(modificationDate); 18799 } 18800 renderRichText({ 18801 html: this.#html || this.#contentsObj.str, 18802 dir: this.#contentsObj?.dir, 18803 className: "popupContent" 18804 }, popup); 18805 this.#container.append(popup); 18806 } 18807 get #html() { 18808 const richText = this.#richText; 18809 const contentsObj = this.#contentsObj; 18810 if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) { 18811 return this.#richText.html || null; 18812 } 18813 return null; 18814 } 18815 get #fontSize() { 18816 return this.#html?.attributes?.style?.fontSize || 0; 18817 } 18818 get #fontColor() { 18819 return this.#html?.attributes?.style?.color || null; 18820 } 18821 #makePopupContent(text) { 18822 const popupLines = []; 18823 const popupContent = { 18824 str: text, 18825 html: { 18826 name: "div", 18827 attributes: { 18828 dir: "auto" 18829 }, 18830 children: [{ 18831 name: "p", 18832 children: popupLines 18833 }] 18834 } 18835 }; 18836 const lineAttributes = { 18837 style: { 18838 color: this.#fontColor, 18839 fontSize: this.#fontSize ? `calc(${this.#fontSize}px * var(--total-scale-factor))` : "" 18840 } 18841 }; 18842 for (const line of text.split("\n")) { 18843 popupLines.push({ 18844 name: "span", 18845 value: line, 18846 attributes: lineAttributes 18847 }); 18848 } 18849 return popupContent; 18850 } 18851 #keyDown(event) { 18852 if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) { 18853 return; 18854 } 18855 if (event.key === "Enter" || event.key === "Escape" && this.#pinned) { 18856 this.#toggle(); 18857 } 18858 } 18859 updateEdited({ 18860 rect, 18861 popup, 18862 deleted 18863 }) { 18864 if (this.#commentManager) { 18865 if (deleted) { 18866 this.remove(); 18867 this.#commentText = null; 18868 } else if (popup) { 18869 if (popup.deleted) { 18870 this.remove(); 18871 } else { 18872 this.#updateColor(); 18873 this.#commentText = popup.text; 18874 } 18875 } 18876 if (rect) { 18877 this.#commentButtonPosition = null; 18878 this.#setCommentButtonPosition(); 18879 this.#updateCommentButtonPosition(); 18880 } 18881 return; 18882 } 18883 if (deleted || popup?.deleted) { 18884 this.remove(); 18885 return; 18886 } 18887 this.#addEventListeners(); 18888 this.#updates ||= { 18889 contentsObj: this.#contentsObj, 18890 richText: this.#richText 18891 }; 18892 if (rect) { 18893 this.#position = null; 18894 } 18895 if (popup && popup.text) { 18896 this.#richText = this.#makePopupContent(popup.text); 18897 this.#dateObj = PDFDateString.toDateObject(popup.date); 18898 this.#contentsObj = null; 18899 } 18900 this.#popup?.remove(); 18901 this.#popup = null; 18902 } 18903 resetEdited() { 18904 if (!this.#updates) { 18905 return; 18906 } 18907 ({ 18908 contentsObj: this.#contentsObj, 18909 richText: this.#richText 18910 } = this.#updates); 18911 this.#updates = null; 18912 this.#popup?.remove(); 18913 this.#popup = null; 18914 this.#position = null; 18915 } 18916 remove() { 18917 this.#popupAbortController?.abort(); 18918 this.#popupAbortController = null; 18919 this.#popup?.remove(); 18920 this.#popup = null; 18921 this.#wasVisible = false; 18922 this.#pinned = false; 18923 this.#commentButton?.remove(); 18924 this.#commentButton = null; 18925 if (this.trigger) { 18926 for (const element of this.trigger) { 18927 element.classList.remove("popupTriggerArea"); 18928 } 18929 } 18930 } 18931 #setPosition() { 18932 if (this.#position !== null) { 18933 return; 18934 } 18935 const { 18936 page: { 18937 view 18938 }, 18939 viewport: { 18940 rawDims: { 18941 pageWidth, 18942 pageHeight, 18943 pageX, 18944 pageY 18945 } 18946 } 18947 } = this.#parent; 18948 let useParentRect = !!this.#parentRect; 18949 let rect = useParentRect ? this.#parentRect : this.#rect; 18950 for (const element of this.#elements) { 18951 if (!rect || Util.intersect(element.data.rect, rect) !== null) { 18952 rect = element.data.rect; 18953 useParentRect = true; 18954 break; 18955 } 18956 } 18957 const normalizedRect = Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]); 18958 const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5; 18959 const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0; 18960 const popupLeft = normalizedRect[0] + parentWidth; 18961 const popupTop = normalizedRect[1]; 18962 this.#position = [100 * (popupLeft - pageX) / pageWidth, 100 * (popupTop - pageY) / pageHeight]; 18963 const { 18964 style 18965 } = this.#container; 18966 style.left = `${this.#position[0]}%`; 18967 style.top = `${this.#position[1]}%`; 18968 } 18969 #toggle() { 18970 if (this.#commentManager) { 18971 this.#commentManager.toggleCommentPopup(this, false); 18972 return; 18973 } 18974 this.#pinned = !this.#pinned; 18975 if (this.#pinned) { 18976 this.#show(); 18977 this.#container.addEventListener("click", this.#boundToggle); 18978 this.#container.addEventListener("keydown", this.#boundKeyDown); 18979 } else { 18980 this.#hide(); 18981 this.#container.removeEventListener("click", this.#boundToggle); 18982 this.#container.removeEventListener("keydown", this.#boundKeyDown); 18983 } 18984 } 18985 #show() { 18986 if (!this.#popup) { 18987 this.render(); 18988 } 18989 if (!this.isVisible) { 18990 this.#setPosition(); 18991 this.#container.hidden = false; 18992 this.#container.style.zIndex = parseInt(this.#container.style.zIndex
vendor: 715 bytes, lines 18992-19023
18992) + 1000; 18993 } else if (this.#pinned) { 18994 this.#container.classList.add("focused"); 18995 } 18996 } 18997 #hide() { 18998 this.#container.classList.remove("focused"); 18999 if (this.#pinned || !this.isVisible) { 19000 return; 19001 } 19002 this.#container.hidden = true; 19003 this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000; 19004 } 19005 forceHide() { 19006 this.#wasVisible = this.isVisible; 19007 if (!this.#wasVisible) { 19008 return; 19009 } 19010 this.#container.hidden = true; 19011 } 19012 maybeShow() { 19013 if (this.#commentManager) { 19014 return; 19015 } 19016 this.#addEventListeners(); 19017 if (!this.#wasVisible) { 19018 return; 19019 } 19020 if (!this.#popup) { 19021 this.#show(); 19022 } 19023 this.#wasVisible = false;
vendor: 13,084 bytes, lines 19024-19480
19024 this.#container.hidden = false; 19025 } 19026 get isVisible() { 19027 if (this.#commentManager) { 19028 return false; 19029 } 19030 return this.#container.hidden === false; 19031 } 19032} 19033class FreeTextAnnotationElement extends AnnotationElement { 19034 constructor(parameters) { 19035 super(parameters, { 19036 isRenderable: true, 19037 ignoreBorder: true 19038 }); 19039 this.textContent = parameters.data.textContent; 19040 this.textPosition = parameters.data.textPosition; 19041 this.annotationEditorType = AnnotationEditorType.FREETEXT; 19042 } 19043 render() { 19044 this.container.classList.add("freeTextAnnotation"); 19045 if (this.textContent) { 19046 const content = this.contentElement = document.createElement("div"); 19047 content.classList.add("annotationTextContent"); 19048 content.setAttribute("role", "comment"); 19049 for (const line of this.textContent) { 19050 const lineSpan = document.createElement("span"); 19051 lineSpan.textContent = line; 19052 content.append(lineSpan); 19053 } 19054 this.container.append(content); 19055 } 19056 if (!this.data.popupRef && this.hasPopupData) { 19057 this.hasOwnCommentButton = true; 19058 this._createPopup(); 19059 } 19060 this._editOnDoubleClick(); 19061 return this.container; 19062 } 19063} 19064class LineAnnotationElement extends AnnotationElement { 19065 #line = null; 19066 constructor(parameters) { 19067 super(parameters, { 19068 isRenderable: true, 19069 ignoreBorder: true 19070 }); 19071 } 19072 render() { 19073 this.container.classList.add("lineAnnotation"); 19074 const { 19075 data, 19076 width, 19077 height 19078 } = this; 19079 const svg = this.svgFactory.create(width, height, true); 19080 const line = this.#line = this.svgFactory.createElement("svg:line"); 19081 line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]); 19082 line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]); 19083 line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]); 19084 line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]); 19085 line.setAttribute("stroke-width", data.borderStyle.width || 1); 19086 line.setAttribute("stroke", "transparent"); 19087 line.setAttribute("fill", "transparent"); 19088 svg.append(line); 19089 this.container.append(svg); 19090 if (!data.popupRef && this.hasPopupData) { 19091 this.hasOwnCommentButton = true; 19092 this._createPopup(); 19093 } 19094 return this.container; 19095 } 19096 getElementsToTriggerPopup() { 19097 return this.#line; 19098 } 19099 addHighlightArea() { 19100 this.container.classList.add("highlightArea"); 19101 } 19102} 19103class SquareAnnotationElement extends AnnotationElement { 19104 #square = null; 19105 constructor(parameters) { 19106 super(parameters, { 19107 isRenderable: true, 19108 ignoreBorder: true 19109 }); 19110 } 19111 render() { 19112 this.container.classList.add("squareAnnotation"); 19113 const { 19114 data, 19115 width, 19116 height 19117 } = this; 19118 const svg = this.svgFactory.create(width, height, true); 19119 const borderWidth = data.borderStyle.width; 19120 const square = this.#square = this.svgFactory.createElement("svg:rect"); 19121 square.setAttribute("x", borderWidth / 2); 19122 square.setAttribute("y", borderWidth / 2); 19123 square.setAttribute("width", width - borderWidth); 19124 square.setAttribute("height", height - borderWidth); 19125 square.setAttribute("stroke-width", borderWidth || 1); 19126 square.setAttribute("stroke", "transparent"); 19127 square.setAttribute("fill", "transparent"); 19128 svg.append(square); 19129 this.container.append(svg); 19130 if (!data.popupRef && this.hasPopupData) { 19131 this.hasOwnCommentButton = true; 19132 this._createPopup(); 19133 } 19134 return this.container; 19135 } 19136 getElementsToTriggerPopup() { 19137 return this.#square; 19138 } 19139 addHighlightArea() { 19140 this.container.classList.add("highlightArea"); 19141 } 19142} 19143class CircleAnnotationElement extends AnnotationElement { 19144 #circle = null; 19145 constructor(parameters) { 19146 super(parameters, { 19147 isRenderable: true, 19148 ignoreBorder: true 19149 }); 19150 } 19151 render() { 19152 this.container.classList.add("circleAnnotation"); 19153 const { 19154 data, 19155 width, 19156 height 19157 } = this; 19158 const svg = this.svgFactory.create(width, height, true); 19159 const borderWidth = data.borderStyle.width; 19160 const circle = this.#circle = this.svgFactory.createElement("svg:ellipse"); 19161 circle.setAttribute("cx", width / 2); 19162 circle.setAttribute("cy", height / 2); 19163 circle.setAttribute("rx", width / 2 - borderWidth / 2); 19164 circle.setAttribute("ry", height / 2 - borderWidth / 2); 19165 circle.setAttribute("stroke-width", borderWidth || 1); 19166 circle.setAttribute("stroke", "transparent"); 19167 circle.setAttribute("fill", "transparent"); 19168 svg.append(circle); 19169 this.container.append(svg); 19170 if (!data.popupRef && this.hasPopupData) { 19171 this.hasOwnCommentButton = true; 19172 this._createPopup(); 19173 } 19174 return this.container; 19175 } 19176 getElementsToTriggerPopup() { 19177 return this.#circle; 19178 } 19179 addHighlightArea() { 19180 this.container.classList.add("highlightArea"); 19181 } 19182} 19183class PolylineAnnotationElement extends AnnotationElement { 19184 #polyline = null; 19185 constructor(parameters) { 19186 super(parameters, { 19187 isRenderable: true, 19188 ignoreBorder: true 19189 }); 19190 this.containerClassName = "polylineAnnotation"; 19191 this.svgElementName = "svg:polyline"; 19192 } 19193 render() { 19194 this.container.classList.add(this.containerClassName); 19195 const { 19196 data: { 19197 rect, 19198 vertices, 19199 borderStyle, 19200 popupRef 19201 }, 19202 width, 19203 height 19204 } = this; 19205 if (!vertices) { 19206 return this.container; 19207 } 19208 const svg = this.svgFactory.create(width, height, true); 19209 let points = []; 19210 for (let i = 0, ii = vertices.length; i < ii; i += 2) { 19211 const x = vertices[i] - rect[0]; 19212 const y = rect[3] - vertices[i + 1]; 19213 points.push(`${x},${y}`); 19214 } 19215 points = points.join(" "); 19216 const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName); 19217 polyline.setAttribute("points", points); 19218 polyline.setAttribute("stroke-width", borderStyle.width || 1); 19219 polyline.setAttribute("stroke", "transparent"); 19220 polyline.setAttribute("fill", "transparent"); 19221 svg.append(polyline); 19222 this.container.append(svg); 19223 if (!popupRef && this.hasPopupData) { 19224 this.hasOwnCommentButton = true; 19225 this._createPopup(); 19226 } 19227 return this.container; 19228 } 19229 getElementsToTriggerPopup() { 19230 return this.#polyline; 19231 } 19232 addHighlightArea() { 19233 this.container.classList.add("highlightArea"); 19234 } 19235} 19236class PolygonAnnotationElement extends PolylineAnnotationElement { 19237 constructor(parameters) { 19238 super(parameters); 19239 this.containerClassName = "polygonAnnotation"; 19240 this.svgElementName = "svg:polygon"; 19241 } 19242} 19243class CaretAnnotationElement extends AnnotationElement { 19244 constructor(parameters) { 19245 super(parameters, { 19246 isRenderable: true, 19247 ignoreBorder: true 19248 }); 19249 } 19250 render() { 19251 this.container.classList.add("caretAnnotation"); 19252 if (!this.data.popupRef && this.hasPopupData) { 19253 this.hasOwnCommentButton = true; 19254 this._createPopup(); 19255 } 19256 return this.container; 19257 } 19258} 19259class InkAnnotationElement extends AnnotationElement { 19260 #polylinesGroupElement = null; 19261 #polylines = []; 19262 constructor(parameters) { 19263 super(parameters, { 19264 isRenderable: true, 19265 ignoreBorder: true 19266 }); 19267 this.containerClassName = "inkAnnotation"; 19268 this.svgElementName = "svg:polyline"; 19269 this.annotationEditorType = this.data.it === "InkHighlight" ? AnnotationEditorType.HIGHLIGHT : AnnotationEditorType.INK; 19270 } 19271 #getTransform(rotation, rect) { 19272 switch (rotation) { 19273 case 90: 19274 return { 19275 transform: `rotate(90) translate(${-rect[0]},${rect[1]}) scale(1,-1)`, 19276 width: rect[3] - rect[1], 19277 height: rect[2] - rect[0] 19278 }; 19279 case 180: 19280 return { 19281 transform: `rotate(180) translate(${-rect[2]},${rect[1]}) scale(1,-1)`, 19282 width: rect[2] - rect[0], 19283 height: rect[3] - rect[1] 19284 }; 19285 case 270: 19286 return { 19287 transform: `rotate(270) translate(${-rect[2]},${rect[3]}) scale(1,-1)`, 19288 width: rect[3] - rect[1], 19289 height: rect[2] - rect[0] 19290 }; 19291 default: 19292 return { 19293 transform: `translate(${-rect[0]},${rect[3]}) scale(1,-1)`, 19294 width: rect[2] - rect[0], 19295 height: rect[3] - rect[1] 19296 }; 19297 } 19298 } 19299 render() { 19300 this.container.classList.add(this.containerClassName); 19301 const { 19302 data: { 19303 rect, 19304 rotation, 19305 inkLists, 19306 borderStyle, 19307 popupRef 19308 } 19309 } = this; 19310 const { 19311 transform, 19312 width, 19313 height 19314 } = this.#getTransform(rotation, rect); 19315 const svg = this.svgFactory.create(width, height, true); 19316 const g = this.#polylinesGroupElement = this.svgFactory.createElement("svg:g"); 19317 svg.append(g); 19318 g.setAttribute("stroke-width", borderStyle.width || 1); 19319 g.setAttribute("stroke-linecap", "round"); 19320 g.setAttribute("stroke-linejoin", "round"); 19321 g.setAttribute("stroke-miterlimit", 10); 19322 g.setAttribute("stroke", "transparent"); 19323 g.setAttribute("fill", "transparent"); 19324 g.setAttribute("transform", transform); 19325 for (let i = 0, ii = inkLists.length; i < ii; i++) { 19326 const polyline = this.svgFactory.createElement(this.svgElementName); 19327 this.#polylines.push(polyline); 19328 polyline.setAttribute("points", inkLists[i].join(",")); 19329 g.append(polyline); 19330 } 19331 if (!popupRef && this.hasPopupData) { 19332 this.hasOwnCommentButton = true; 19333 this._createPopup(); 19334 } 19335 this.container.append(svg); 19336 this._editOnDoubleClick(); 19337 return this.container; 19338 } 19339 updateEdited(params) { 19340 super.updateEdited(params); 19341 const { 19342 thickness, 19343 points, 19344 rect 19345 } = params; 19346 const g = this.#polylinesGroupElement; 19347 if (thickness >= 0) { 19348 g.setAttribute("stroke-width", thickness || 1); 19349 } 19350 if (points) { 19351 for (let i = 0, ii = this.#polylines.length; i < ii; i++) { 19352 this.#polylines[i].setAttribute("points", points[i].join(",")); 19353 } 19354 } 19355 if (rect) { 19356 const { 19357 transform, 19358 width, 19359 height 19360 } = this.#getTransform(this.data.rotation, rect); 19361 const root = g.parentElement; 19362 root.setAttribute("viewBox", `0 0 ${width} ${height}`); 19363 g.setAttribute("transform", transform); 19364 } 19365 } 19366 getElementsToTriggerPopup() { 19367 return this.#polylines; 19368 } 19369 addHighlightArea() { 19370 this.container.classList.add("highlightArea"); 19371 } 19372} 19373class HighlightAnnotationElement extends AnnotationElement { 19374 constructor(parameters) { 19375 super(parameters, { 19376 isRenderable: true, 19377 ignoreBorder: true, 19378 createQuadrilaterals: true 19379 }); 19380 this.annotationEditorType = AnnotationEditorType.HIGHLIGHT; 19381 } 19382 render() { 19383 const { 19384 data: { 19385 overlaidText, 19386 popupRef 19387 } 19388 } = this; 19389 if (!popupRef && this.hasPopupData) { 19390 this.hasOwnCommentButton = true; 19391 this._createPopup(); 19392 } 19393 this.container.classList.add("highlightAnnotation"); 19394 this._editOnDoubleClick(); 19395 if (overlaidText) { 19396 const mark = document.createElement("mark"); 19397 mark.classList.add("overlaidText"); 19398 mark.textContent = overlaidText; 19399 this.container.append(mark); 19400 } 19401 return this.container; 19402 } 19403} 19404class UnderlineAnnotationElement extends AnnotationElement { 19405 constructor(parameters) { 19406 super(parameters, { 19407 isRenderable: true, 19408 ignoreBorder: true, 19409 createQuadrilaterals: true 19410 }); 19411 } 19412 render() { 19413 const { 19414 data: { 19415 overlaidText, 19416 popupRef 19417 } 19418 } = this; 19419 if (!popupRef && this.hasPopupData) { 19420 this.hasOwnCommentButton = true; 19421 this._createPopup(); 19422 } 19423 this.container.classList.add("underlineAnnotation"); 19424 if (overlaidText) { 19425 const underline = document.createElement("u"); 19426 underline.classList.add("overlaidText"); 19427 underline.textContent = overlaidText; 19428 this.container.append(underline); 19429 } 19430 return this.container; 19431 } 19432} 19433class SquigglyAnnotationElement extends AnnotationElement { 19434 constructor(parameters) { 19435 super(parameters, { 19436 isRenderable: true, 19437 ignoreBorder: true, 19438 createQuadrilaterals: true 19439 }); 19440 } 19441 render() { 19442 const { 19443 data: { 19444 overlaidText, 19445 popupRef 19446 } 19447 } = this; 19448 if (!popupRef && this.hasPopupData) { 19449 this.hasOwnCommentButton = true; 19450 this._createPopup(); 19451 } 19452 this.container.classList.add("squigglyAnnotation"); 19453 if (overlaidText) { 19454 const underline = document.createElement("u"); 19455 underline.classList.add("overlaidText"); 19456 underline.textContent = overlaidText; 19457 this.container.append(underline); 19458 } 19459 return this.container; 19460 } 19461} 19462class StrikeOutAnnotationElement extends AnnotationElement { 19463 constructor(parameters) { 19464 super(parameters, { 19465 isRenderable: true, 19466 ignoreBorder: true, 19467 createQuadrilaterals: true 19468 }); 19469 } 19470 render() { 19471 const { 19472 data: { 19473 overlaidText, 19474 popupRef 19475 } 19476 } = this; 19477 if (!popupRef && this.hasPopupData) { 19478 this.hasOwnCommentButton = true; 19479 this._createPopup(); 19480 }
vendor: 3,717 bytes, lines 19481-19609
19481 this.container.classList.add("strikeoutAnnotation"); 19482 if (overlaidText) { 19483 const strikeout = document.createElement("s"); 19484 strikeout.classList.add("overlaidText"); 19485 strikeout.textContent = overlaidText; 19486 this.container.append(strikeout); 19487 } 19488 return this.container; 19489 } 19490} 19491class StampAnnotationElement extends AnnotationElement { 19492 constructor(parameters) { 19493 super(parameters, { 19494 isRenderable: true, 19495 ignoreBorder: true 19496 }); 19497 this.annotationEditorType = AnnotationEditorType.STAMP; 19498 } 19499 render() { 19500 this.container.classList.add("stampAnnotation"); 19501 this.container.setAttribute("role", "img"); 19502 if (!this.data.popupRef && this.hasPopupData) { 19503 this.hasOwnCommentButton = true; 19504 this._createPopup(); 19505 } 19506 this._editOnDoubleClick(); 19507 return this.container; 19508 } 19509} 19510class FileAttachmentAnnotationElement extends AnnotationElement { 19511 #trigger = null; 19512 constructor(parameters) { 19513 super(parameters, { 19514 isRenderable: true 19515 }); 19516 const { 19517 file 19518 } = this.data; 19519 this.filename = file.filename; 19520 this.content = file.content; 19521 this.linkService.eventBus?.dispatch("fileattachmentannotation", { 19522 source: this, 19523 ...file 19524 }); 19525 } 19526 render() { 19527 this.container.classList.add("fileAttachmentAnnotation"); 19528 const { 19529 container, 19530 data 19531 } = this; 19532 let trigger; 19533 if (data.hasAppearance || data.fillAlpha === 0) { 19534 trigger = document.createElement("div"); 19535 } else { 19536 trigger = document.createElement("img"); 19537 trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`; 19538 if (data.fillAlpha && data.fillAlpha < 1) { 19539 trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`; 19540 } 19541 } 19542 trigger.addEventListener("dblclick", this.#download.bind(this)); 19543 this.#trigger = trigger; 19544 const { 19545 isMac 19546 } = util_FeatureTest.platform; 19547 container.addEventListener("keydown", evt => { 19548 if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) { 19549 this.#download(); 19550 } 19551 }); 19552 if (!data.popupRef && this.hasPopupData) { 19553 this.hasOwnCommentButton = true; 19554 this._createPopup(); 19555 } else { 19556 trigger.classList.add("popupTriggerArea"); 19557 } 19558 container.append(trigger); 19559 return container; 19560 } 19561 getElementsToTriggerPopup() { 19562 return this.#trigger; 19563 } 19564 addHighlightArea() { 19565 this.container.classList.add("highlightArea"); 19566 } 19567 #download() { 19568 this.downloadManager?.openOrDownloadData(this.content, this.filename); 19569 } 19570} 19571class AnnotationLayer { 19572 #accessibilityManager = null; 19573 #annotationCanvasMap = null; 19574 #annotationStorage = null; 19575 #editableAnnotations = new Map(); 19576 #structTreeLayer = null; 19577 #linkService = null; 19578 #elements = []; 19579 #hasAriaAttributesFromStructTree = false; 19580 constructor({ 19581 div, 19582 accessibilityManager, 19583 annotationCanvasMap, 19584 annotationEditorUIManager, 19585 page, 19586 viewport, 19587 structTreeLayer, 19588 commentManager, 19589 linkService, 19590 annotationStorage 19591 }) { 19592 this.div = div; 19593 this.#accessibilityManager = accessibilityManager; 19594 this.#annotationCanvasMap = annotationCanvasMap; 19595 this.#structTreeLayer = structTreeLayer || null; 19596 this.#linkService = linkService || null; 19597 this.#annotationStorage = annotationStorage || new AnnotationStorage(); 19598 this.page = page; 19599 this.viewport = viewport; 19600 this.zIndex = 0; 19601 this._annotationEditorUIManager = annotationEditorUIManager; 19602 this._commentManager = commentManager || null; 19603 } 19604 hasEditableAnnotations() { 19605 return this.#editableAnnotations.size > 0; 19606 } 19607 async render(params) { 19608 const { 19609 annotations
vendor: 1,604 bytes, lines 19609-19659
19609 19610 } = params; 19611 const layer = this.div; 19612 setLayerDimensions(layer, this.viewport); 19613 const popupToElements = new Map(); 19614 const popupAnnotations = []; 19615 const elementParams = { 19616 data: null, 19617 layer, 19618 linkService: this.#linkService, 19619 downloadManager: params.downloadManager, 19620 imageResourcesPath: params.imageResourcesPath || "", 19621 renderForms: params.renderForms !== false, 19622 svgFactory: new DOMSVGFactory(), 19623 annotationStorage: this.#annotationStorage, 19624 enableComment: params.enableComment === true, 19625 enableScripting: params.enableScripting === true, 19626 hasJSActions: params.hasJSActions, 19627 fieldObjects: params.fieldObjects, 19628 parent: this, 19629 elements: null 19630 }; 19631 for (const data of annotations) { 19632 if (data.noHTML) { 19633 continue; 19634 } 19635 const isPopupAnnotation = data.annotationType === AnnotationType.POPUP; 19636 if (!isPopupAnnotation) { 19637 if (data.rect[2] === data.rect[0] || data.rect[3] === data.rect[1]) { 19638 continue; 19639 } 19640 } else { 19641 const elements = popupToElements.get(data.id); 19642 if (!elements) { 19643 continue; 19644 } 19645 if (!this._commentManager) { 19646 popupAnnotations.push(data); 19647 continue; 19648 } 19649 elementParams.elements = elements; 19650 } 19651 elementParams.data = data; 19652 const element = AnnotationElementFactory.create(elementParams); 19653 if (!element.isRenderable) { 19654 continue; 19655 } 19656 if (!isPopupAnnotation) { 19657 this.#elements.push(element); 19658 if (data.popupRef) { 19659
vendor: 9,178 bytes, lines 19659-19958
19659const elements = popupToElements.get(data.popupRef); 19660 if (!elements) { 19661 popupToElements.set(data.popupRef, [element]); 19662 } else { 19663 elements.push(element); 19664 } 19665 } 19666 } 19667 const rendered = element.render(); 19668 if (data.hidden) { 19669 rendered.style.visibility = "hidden"; 19670 } 19671 if (element._isEditable) { 19672 this.#editableAnnotations.set(element.data.id, element); 19673 this._annotationEditorUIManager?.renderAnnotationElement(element); 19674 } 19675 } 19676 await this.#addElementsToDOM(); 19677 for (const data of popupAnnotations) { 19678 const elements = elementParams.elements = popupToElements.get(data.id); 19679 elementParams.data = data; 19680 const element = AnnotationElementFactory.create(elementParams); 19681 if (!element.isRenderable) { 19682 continue; 19683 } 19684 const rendered = element.render(); 19685 element.contentElement.id = `${AnnotationPrefix}${data.id}`; 19686 if (data.hidden) { 19687 rendered.style.visibility = "hidden"; 19688 } 19689 elements.at(-1).container.after(rendered); 19690 } 19691 this.#setAnnotationCanvasMap(); 19692 } 19693 async #addElementsToDOM() { 19694 if (this.#elements.length === 0) { 19695 return; 19696 } 19697 this.div.replaceChildren(); 19698 const promises = []; 19699 if (!this.#hasAriaAttributesFromStructTree) { 19700 this.#hasAriaAttributesFromStructTree = true; 19701 for (const { 19702 contentElement, 19703 data: { 19704 id 19705 } 19706 } of this.#elements) { 19707 const annotationId = contentElement.id = `${AnnotationPrefix}${id}`; 19708 promises.push(this.#structTreeLayer?.getAriaAttributes(annotationId).then(ariaAttributes => { 19709 if (ariaAttributes) { 19710 for (const [key, value] of ariaAttributes) { 19711 contentElement.setAttribute(key, value); 19712 } 19713 } 19714 })); 19715 } 19716 } 19717 this.#elements.sort(({ 19718 data: { 19719 rect: [a0, a1, a2, a3] 19720 } 19721 }, { 19722 data: { 19723 rect: [b0, b1, b2, b3] 19724 } 19725 }) => { 19726 if (a0 === a2 && a1 === a3) { 19727 return +1; 19728 } 19729 if (b0 === b2 && b1 === b3) { 19730 return -1; 19731 } 19732 const top1 = a3; 19733 const bot1 = a1; 19734 const mid1 = (a1 + a3) / 2; 19735 const top2 = b3; 19736 const bot2 = b1; 19737 const mid2 = (b1 + b3) / 2; 19738 if (mid1 >= top2 && mid2 <= bot1) { 19739 return -1; 19740 } 19741 if (mid2 >= top1 && mid1 <= bot2) { 19742 return +1; 19743 } 19744 const centerX1 = (a0 + a2) / 2; 19745 const centerX2 = (b0 + b2) / 2; 19746 return centerX1 - centerX2; 19747 }); 19748 const fragment = document.createDocumentFragment(); 19749 for (const element of this.#elements) { 19750 fragment.append(element.container); 19751 if (this._commentManager) { 19752 (element.extraPopupElement?.popup || element.popup)?.renderCommentButton(); 19753 } else if (element.extraPopupElement) { 19754 fragment.append(element.extraPopupElement.render()); 19755 } 19756 } 19757 this.div.append(fragment); 19758 await Promise.all(promises); 19759 if (this.#accessibilityManager) { 19760 for (const element of this.#elements) { 19761 this.#accessibilityManager.addPointerInTextLayer(element.contentElement, false); 19762 } 19763 } 19764 } 19765 async addLinkAnnotations(annotations) { 19766 const elementParams = { 19767 data: null, 19768 layer: this.div, 19769 linkService: this.#linkService, 19770 svgFactory: new DOMSVGFactory(), 19771 parent: this 19772 }; 19773 for (const data of annotations) { 19774 data.borderStyle ||= AnnotationLayer._defaultBorderStyle; 19775 elementParams.data = data; 19776 const element = AnnotationElementFactory.create(elementParams); 19777 if (!element.isRenderable) { 19778 continue; 19779 } 19780 element.render(); 19781 element.contentElement.id = `${AnnotationPrefix}${data.id}`; 19782 this.#elements.push(element); 19783 } 19784 await this.#addElementsToDOM(); 19785 } 19786 update({ 19787 viewport 19788 }) { 19789 const layer = this.div; 19790 this.viewport = viewport; 19791 setLayerDimensions(layer, { 19792 rotation: viewport.rotation 19793 }); 19794 this.#setAnnotationCanvasMap(); 19795 layer.hidden = false; 19796 } 19797 #setAnnotationCanvasMap() { 19798 if (!this.#annotationCanvasMap) { 19799 return; 19800 } 19801 const layer = this.div; 19802 for (const [id, canvas] of this.#annotationCanvasMap) { 19803 const element = layer.querySelector(`[data-annotation-id="${id}"]`); 19804 if (!element) { 19805 continue; 19806 } 19807 canvas.className = "annotationContent"; 19808 const { 19809 firstChild 19810 } = element; 19811 if (!firstChild) { 19812 element.append(canvas); 19813 } else if (firstChild.nodeName === "CANVAS") { 19814 firstChild.replaceWith(canvas); 19815 } else if (!firstChild.classList.contains("annotationContent")) { 19816 firstChild.before(canvas); 19817 } else { 19818 firstChild.after(canvas); 19819 } 19820 const editableAnnotation = this.#editableAnnotations.get(id); 19821 if (!editableAnnotation) { 19822 continue; 19823 } 19824 if (editableAnnotation._hasNoCanvas) { 19825 this._annotationEditorUIManager?.setMissingCanvas(id, element.id, canvas); 19826 editableAnnotation._hasNoCanvas = false; 19827 } else { 19828 editableAnnotation.canvas = canvas; 19829 } 19830 } 19831 this.#annotationCanvasMap.clear(); 19832 } 19833 getEditableAnnotations() { 19834 return Array.from(this.#editableAnnotations.values()); 19835 } 19836 getEditableAnnotation(id) { 19837 return this.#editableAnnotations.get(id); 19838 } 19839 addFakeAnnotation(editor) { 19840 const { 19841 div 19842 } = this; 19843 const { 19844 id, 19845 rotation 19846 } = editor; 19847 const element = new EditorAnnotationElement({ 19848 data: { 19849 id, 19850 rect: editor.getPDFRect(), 19851 rotation 19852 }, 19853 editor, 19854 layer: div, 19855 parent: this, 19856 enableComment: !!this._commentManager, 19857 linkService: this.#linkService, 19858 annotationStorage: this.#annotationStorage 19859 }); 19860 element.render(); 19861 element.contentElement.id = `${AnnotationPrefix}${id}`; 19862 element.createOrUpdatePopup(); 19863 this.#elements.push(element); 19864 return element; 19865 } 19866 removeAnnotation(id) { 19867 const index = this.#elements.findIndex(el => el.data.id === id); 19868 if (index < 0) { 19869 return; 19870 } 19871 const [element] = this.#elements.splice(index, 1); 19872 this.#accessibilityManager?.removePointerInTextLayer(element.contentElement); 19873 } 19874 updateFakeAnnotations(editors) { 19875 if (editors.length === 0) { 19876 return; 19877 } 19878 for (const editor of editors) { 19879 editor.updateFakeAnnotationElement(this); 19880 } 19881 this.#addElementsToDOM(); 19882 } 19883 togglePointerEvents(enabled = false) { 19884 this.div.classList.toggle("disabled", !enabled); 19885 } 19886 static get _defaultBorderStyle() { 19887 return shadow(this, "_defaultBorderStyle", Object.freeze({ 19888 width: 1, 19889 rawWidth: 1, 19890 style: AnnotationBorderStyleType.SOLID, 19891 dashArray: [3], 19892 horizontalCornerRadius: 0, 19893 verticalCornerRadius: 0 19894 })); 19895 } 19896} 19897 19898;// ./src/display/editor/freetext.js 19899 19900 19901 19902 19903 19904const EOL_PATTERN = /\r\n?|\n/g; 19905class FreeTextEditor extends AnnotationEditor { 19906 #content = ""; 19907 #editorDivId = `${this.id}-editor`; 19908 #editModeAC = null; 19909 #fontSize; 19910 _colorPicker = null; 19911 static _freeTextDefaultContent = ""; 19912 static _internalPadding = 0; 19913 static _defaultColor = null; 19914 static _defaultFontSize = 10; 19915 static get _keyboardManager() { 19916 const proto = FreeTextEditor.prototype; 19917 const arrowChecker = self => self.isEmpty(); 19918 const small = AnnotationEditorUIManager.TRANSLATE_SMALL; 19919 const big = AnnotationEditorUIManager.TRANSLATE_BIG; 19920 return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, { 19921 bubbles: true 19922 }], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, { 19923 args: [-small, 0], 19924 checker: arrowChecker 19925 }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, { 19926 args: [-big, 0], 19927 checker: arrowChecker 19928 }], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, { 19929 args: [small, 0], 19930 checker: arrowChecker 19931 }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, { 19932 args: [big, 0], 19933 checker: arrowChecker 19934 }], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, { 19935 args: [0, -small], 19936 checker: arrowChecker 19937 }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, { 19938 args: [0, -big], 19939 checker: arrowChecker 19940 }], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, { 19941 args: [0, small], 19942 checker: arrowChecker 19943 }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, { 19944 args: [0, big], 19945 checker: arrowChecker 19946 }]])); 19947 } 19948 static _type = "freetext"; 19949 static _editorType = AnnotationEditorType.FREETEXT; 19950 constructor(params) { 19951 super({ 19952 ...params, 19953 name: "freeTextEditor" 19954 }); 19955 this.color = params.color || FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor; 19956 this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize; 19957 if (!this.annotationElementId) { 19958 this._uiManager.a11yAlert("pdfjs-editor-freetext-added-alert"
vendor: 14,846 bytes, lines 19958-20445
19958); 19959 } 19960 this.canAddComment = false; 19961 } 19962 static initialize(l10n, uiManager) { 19963 AnnotationEditor.initialize(l10n, uiManager); 19964 const style = getComputedStyle(document.documentElement); 19965 this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding")); 19966 } 19967 static updateDefaultParams(type, value) { 19968 switch (type) { 19969 case AnnotationEditorParamsType.FREETEXT_SIZE: 19970 FreeTextEditor._defaultFontSize = value; 19971 break; 19972 case AnnotationEditorParamsType.FREETEXT_COLOR: 19973 FreeTextEditor._defaultColor = value; 19974 break; 19975 } 19976 } 19977 updateParams(type, value) { 19978 switch (type) { 19979 case AnnotationEditorParamsType.FREETEXT_SIZE: 19980 this.#updateFontSize(value); 19981 break; 19982 case AnnotationEditorParamsType.FREETEXT_COLOR: 19983 this.#updateColor(value); 19984 break; 19985 } 19986 } 19987 static get defaultPropertiesToUpdate() { 19988 return [[AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor]]; 19989 } 19990 get propertiesToUpdate() { 19991 return [[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, this.color]]; 19992 } 19993 get toolbarButtons() { 19994 this._colorPicker ||= new BasicColorPicker(this); 19995 return [["colorPicker", this._colorPicker]]; 19996 } 19997 get colorType() { 19998 return AnnotationEditorParamsType.FREETEXT_COLOR; 19999 } 20000 #updateFontSize(fontSize) { 20001 const setFontsize = size => { 20002 this.editorDiv.style.fontSize = `calc(${size}px * var(--total-scale-factor))`; 20003 this.translate(0, -(size - this.#fontSize) * this.parentScale); 20004 this.#fontSize = size; 20005 this.#setEditorDimensions(); 20006 }; 20007 const savedFontsize = this.#fontSize; 20008 this.addCommands({ 20009 cmd: setFontsize.bind(this, fontSize), 20010 undo: setFontsize.bind(this, savedFontsize), 20011 post: this._uiManager.updateUI.bind(this._uiManager, this), 20012 mustExec: true, 20013 type: AnnotationEditorParamsType.FREETEXT_SIZE, 20014 overwriteIfSameType: true, 20015 keepUndo: true 20016 }); 20017 } 20018 onUpdatedColor() { 20019 this.editorDiv.style.color = this.color; 20020 this._colorPicker?.update(this.color); 20021 super.onUpdatedColor(); 20022 } 20023 #updateColor(color) { 20024 const setColor = col => { 20025 this.color = col; 20026 this.onUpdatedColor(); 20027 }; 20028 const savedColor = this.color; 20029 this.addCommands({ 20030 cmd: setColor.bind(this, color), 20031 undo: setColor.bind(this, savedColor), 20032 post: this._uiManager.updateUI.bind(this._uiManager, this), 20033 mustExec: true, 20034 type: AnnotationEditorParamsType.FREETEXT_COLOR, 20035 overwriteIfSameType: true, 20036 keepUndo: true 20037 }); 20038 } 20039 _translateEmpty(x, y) { 20040 this._uiManager.translateSelectedEditors(x, y, true); 20041 } 20042 getInitialTranslation() { 20043 const scale = this.parentScale; 20044 return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale]; 20045 } 20046 rebuild() { 20047 if (!this.parent) { 20048 return; 20049 } 20050 super.rebuild(); 20051 if (this.div === null) { 20052 return; 20053 } 20054 if (!this.isAttachedToDOM) { 20055 this.parent.add(this); 20056 } 20057 } 20058 enableEditMode() { 20059 if (!super.enableEditMode()) { 20060 return false; 20061 } 20062 this.overlayDiv.classList.remove("enabled"); 20063 this.editorDiv.contentEditable = true; 20064 this._isDraggable = false; 20065 this.div.removeAttribute("aria-activedescendant"); 20066 this.#editModeAC = new AbortController(); 20067 const signal = this._uiManager.combinedSignal(this.#editModeAC); 20068 this.editorDiv.addEventListener("keydown", this.editorDivKeydown.bind(this), { 20069 signal 20070 }); 20071 this.editorDiv.addEventListener("focus", this.editorDivFocus.bind(this), { 20072 signal 20073 }); 20074 this.editorDiv.addEventListener("blur", this.editorDivBlur.bind(this), { 20075 signal 20076 }); 20077 this.editorDiv.addEventListener("input", this.editorDivInput.bind(this), { 20078 signal 20079 }); 20080 this.editorDiv.addEventListener("paste", this.editorDivPaste.bind(this), { 20081 signal 20082 }); 20083 return true; 20084 } 20085 disableEditMode() { 20086 if (!super.disableEditMode()) { 20087 return false; 20088 } 20089 this.overlayDiv.classList.add("enabled"); 20090 this.editorDiv.contentEditable = false; 20091 this.div.setAttribute("aria-activedescendant", this.#editorDivId); 20092 this._isDraggable = true; 20093 this.#editModeAC?.abort(); 20094 this.#editModeAC = null; 20095 this.div.focus({ 20096 preventScroll: true 20097 }); 20098 this.isEditing = false; 20099 this.parent.div.classList.add("freetextEditing"); 20100 return true; 20101 } 20102 focusin(event) { 20103 if (!this._focusEventsAllowed) { 20104 return; 20105 } 20106 super.focusin(event); 20107 if (event.target !== this.editorDiv) { 20108 this.editorDiv.focus(); 20109 } 20110 } 20111 onceAdded(focus) { 20112 if (this.width) { 20113 return; 20114 } 20115 this.enableEditMode(); 20116 if (focus) { 20117 this.editorDiv.focus(); 20118 } 20119 if (this._initialOptions?.isCentered) { 20120 this.center(); 20121 } 20122 this._initialOptions = null; 20123 } 20124 isEmpty() { 20125 return !this.editorDiv || this.editorDiv.innerText.trim() === ""; 20126 } 20127 remove() { 20128 this.isEditing = false; 20129 if (this.parent) { 20130 this.parent.setEditingState(true); 20131 this.parent.div.classList.add("freetextEditing"); 20132 } 20133 super.remove(); 20134 } 20135 #extractText() { 20136 const buffer = []; 20137 this.editorDiv.normalize(); 20138 let prevChild = null; 20139 for (const child of this.editorDiv.childNodes) { 20140 if (prevChild?.nodeType === Node.TEXT_NODE && child.nodeName === "BR") { 20141 continue; 20142 } 20143 buffer.push(FreeTextEditor.#getNodeContent(child)); 20144 prevChild = child; 20145 } 20146 return buffer.join("\n"); 20147 } 20148 #setEditorDimensions() { 20149 const [parentWidth, parentHeight] = this.parentDimensions; 20150 let rect; 20151 if (this.isAttachedToDOM) { 20152 rect = this.div.getBoundingClientRect(); 20153 } else { 20154 const { 20155 currentLayer, 20156 div 20157 } = this; 20158 const savedDisplay = div.style.display; 20159 const savedVisibility = div.classList.contains("hidden"); 20160 div.classList.remove("hidden"); 20161 div.style.display = "hidden"; 20162 currentLayer.div.append(this.div); 20163 rect = div.getBoundingClientRect(); 20164 div.remove(); 20165 div.style.display = savedDisplay; 20166 div.classList.toggle("hidden", savedVisibility); 20167 } 20168 if (this.rotation % 180 === this.parentRotation % 180) { 20169 this.width = rect.width / parentWidth; 20170 this.height = rect.height / parentHeight; 20171 } else { 20172 this.width = rect.height / parentWidth; 20173 this.height = rect.width / parentHeight; 20174 } 20175 this.fixAndSetPosition(); 20176 } 20177 commit() { 20178 if (!this.isInEditMode()) { 20179 return; 20180 } 20181 super.commit(); 20182 this.disableEditMode(); 20183 const savedText = this.#content; 20184 const newText = this.#content = this.#extractText().trimEnd(); 20185 if (savedText === newText) { 20186 return; 20187 } 20188 const setText = text => { 20189 this.#content = text; 20190 if (!text) { 20191 this.remove(); 20192 return; 20193 } 20194 this.#setContent(); 20195 this._uiManager.rebuild(this); 20196 this.#setEditorDimensions(); 20197 }; 20198 this.addCommands({ 20199 cmd: () => { 20200 setText(newText); 20201 }, 20202 undo: () => { 20203 setText(savedText); 20204 }, 20205 mustExec: false 20206 }); 20207 this.#setEditorDimensions(); 20208 } 20209 shouldGetKeyboardEvents() { 20210 return this.isInEditMode(); 20211 } 20212 enterInEditMode() { 20213 this.enableEditMode(); 20214 this.editorDiv.focus(); 20215 } 20216 keydown(event) { 20217 if (event.target === this.div && event.key === "Enter") { 20218 this.enterInEditMode(); 20219 event.preventDefault(); 20220 } 20221 } 20222 editorDivKeydown(event) { 20223 FreeTextEditor._keyboardManager.exec(this, event); 20224 } 20225 editorDivFocus(event) { 20226 this.isEditing = true; 20227 } 20228 editorDivBlur(event) { 20229 this.isEditing = false; 20230 } 20231 editorDivInput(event) { 20232 this.parent.div.classList.toggle("freetextEditing", this.isEmpty()); 20233 } 20234 disableEditing() { 20235 this.editorDiv.setAttribute("role", "comment"); 20236 this.editorDiv.removeAttribute("aria-multiline"); 20237 } 20238 enableEditing() { 20239 this.editorDiv.setAttribute("role", "textbox"); 20240 this.editorDiv.setAttribute("aria-multiline", true); 20241 } 20242 get canChangeContent() { 20243 return true; 20244 } 20245 render() { 20246 if (this.div) { 20247 return this.div; 20248 } 20249 let baseX, baseY; 20250 if (this._isCopy || this.annotationElementId) { 20251 baseX = this.x; 20252 baseY = this.y; 20253 } 20254 super.render(); 20255 this.editorDiv = document.createElement("div"); 20256 this.editorDiv.className = "internal"; 20257 this.editorDiv.setAttribute("id", this.#editorDivId); 20258 this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text2"); 20259 this.editorDiv.setAttribute("data-l10n-attrs", "default-content"); 20260 this.enableEditing(); 20261 this.editorDiv.contentEditable = true; 20262 const { 20263 style 20264 } = this.editorDiv; 20265 style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; 20266 style.color = this.color; 20267 this.div.append(this.editorDiv); 20268 this.overlayDiv = document.createElement("div"); 20269 this.overlayDiv.classList.add("overlay", "enabled"); 20270 this.div.append(this.overlayDiv); 20271 if (this._isCopy || this.annotationElementId) { 20272 const [parentWidth, parentHeight] = this.parentDimensions; 20273 if (this.annotationElementId) { 20274 const { 20275 position 20276 } = this._initialData; 20277 let [tx, ty] = this.getInitialTranslation(); 20278 [tx, ty] = this.pageTranslationToScreen(tx, ty); 20279 const [pageWidth, pageHeight] = this.pageDimensions; 20280 const [pageX, pageY] = this.pageTranslation; 20281 let posX, posY; 20282 switch (this.rotation) { 20283 case 0: 20284 posX = baseX + (position[0] - pageX) / pageWidth; 20285 posY = baseY + this.height - (position[1] - pageY) / pageHeight; 20286 break; 20287 case 90: 20288 posX = baseX + (position[0] - pageX) / pageWidth; 20289 posY = baseY - (position[1] - pageY) / pageHeight; 20290 [tx, ty] = [ty, -tx]; 20291 break; 20292 case 180: 20293 posX = baseX - this.width + (position[0] - pageX) / pageWidth; 20294 posY = baseY - (position[1] - pageY) / pageHeight; 20295 [tx, ty] = [-tx, -ty]; 20296 break; 20297 case 270: 20298 posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth; 20299 posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight; 20300 [tx, ty] = [-ty, tx]; 20301 break; 20302 } 20303 this.setAt(posX * parentWidth, posY * parentHeight, tx, ty); 20304 } else { 20305 this._moveAfterPaste(baseX, baseY); 20306 } 20307 this.#setContent(); 20308 this._isDraggable = true; 20309 this.editorDiv.contentEditable = false; 20310 } else { 20311 this._isDraggable = false; 20312 this.editorDiv.contentEditable = true; 20313 } 20314 return this.div; 20315 } 20316 static #getNodeContent(node) { 20317 return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, ""); 20318 } 20319 editorDivPaste(event) { 20320 const clipboardData = event.clipboardData || window.clipboardData; 20321 const { 20322 types 20323 } = clipboardData; 20324 if (types.length === 1 && types[0] === "text/plain") { 20325 return; 20326 } 20327 event.preventDefault(); 20328 const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n"); 20329 if (!paste) { 20330 return; 20331 } 20332 const selection = window.getSelection(); 20333 if (!selection.rangeCount) { 20334 return; 20335 } 20336 this.editorDiv.normalize(); 20337 selection.deleteFromDocument(); 20338 const range = selection.getRangeAt(0); 20339 if (!paste.includes("\n")) { 20340 range.insertNode(document.createTextNode(paste)); 20341 this.editorDiv.normalize(); 20342 selection.collapseToStart(); 20343 return; 20344 } 20345 const { 20346 startContainer, 20347 startOffset 20348 } = range; 20349 const bufferBefore = []; 20350 const bufferAfter = []; 20351 if (startContainer.nodeType === Node.TEXT_NODE) { 20352 const parent = startContainer.parentElement; 20353 bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, "")); 20354 if (parent !== this.editorDiv) { 20355 let buffer = bufferBefore; 20356 for (const child of this.editorDiv.childNodes) { 20357 if (child === parent) { 20358 buffer = bufferAfter; 20359 continue; 20360 } 20361 buffer.push(FreeTextEditor.#getNodeContent(child)); 20362 } 20363 } 20364 bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, "")); 20365 } else if (startContainer === this.editorDiv) { 20366 let buffer = bufferBefore; 20367 let i = 0; 20368 for (const child of this.editorDiv.childNodes) { 20369 if (i++ === startOffset) { 20370 buffer = bufferAfter; 20371 } 20372 buffer.push(FreeTextEditor.#getNodeContent(child)); 20373 } 20374 } 20375 this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`; 20376 this.#setContent(); 20377 const newRange = new Range(); 20378 let beforeLength = Math.sumPrecise(bufferBefore.map(line => line.length)); 20379 for (const { 20380 firstChild 20381 } of this.editorDiv.childNodes) { 20382 if (firstChild.nodeType === Node.TEXT_NODE) { 20383 const length = firstChild.nodeValue.length; 20384 if (beforeLength <= length) { 20385 newRange.setStart(firstChild, beforeLength); 20386 newRange.setEnd(firstChild, beforeLength); 20387 break; 20388 } 20389 beforeLength -= length; 20390 } 20391 } 20392 selection.removeAllRanges(); 20393 selection.addRange(newRange); 20394 } 20395 #setContent() { 20396 this.editorDiv.replaceChildren(); 20397 if (!this.#content) { 20398 return; 20399 } 20400 for (const line of this.#content.split("\n")) { 20401 const div = document.createElement("div"); 20402 div.append(line ? document.createTextNode(line) : document.createElement("br")); 20403 this.editorDiv.append(div); 20404 } 20405 } 20406 #serializeContent() { 20407 return this.#content.replaceAll("\xa0", " "); 20408 } 20409 static #deserializeContent(content) { 20410 return content.replaceAll(" ", "\xa0"); 20411 } 20412 get contentDiv() { 20413 return this.editorDiv; 20414 } 20415 getPDFRect() { 20416 const padding = FreeTextEditor._internalPadding * this.parentScale; 20417 return this.getRect(padding, padding); 20418 } 20419 static async deserialize(data, parent, uiManager) { 20420 let initialData = null; 20421 if (data instanceof FreeTextAnnotationElement) { 20422 const { 20423 data: { 20424 defaultAppearanceData: { 20425 fontSize, 20426 fontColor 20427 }, 20428 rect, 20429 rotation, 20430 id, 20431 popupRef, 20432 richText, 20433 contentsObj, 20434 creationDate, 20435 modificationDate 20436 }, 20437 textContent, 20438 textPosition, 20439 parent: { 20440 page: { 20441 pageNumber 20442 } 20443 } 20444 } = data; 20445 if (!textContent
vendor: 6,223 bytes, lines 20445-20655
20445 || textContent.length === 0) { 20446 return null; 20447 } 20448 initialData = data = { 20449 annotationType: AnnotationEditorType.FREETEXT, 20450 color: Array.from(fontColor), 20451 fontSize, 20452 value: textContent.join("\n"), 20453 position: textPosition, 20454 pageIndex: pageNumber - 1, 20455 rect: rect.slice(0), 20456 rotation, 20457 annotationElementId: id, 20458 id, 20459 deleted: false, 20460 popupRef, 20461 comment: contentsObj?.str || null, 20462 richText, 20463 creationDate, 20464 modificationDate 20465 }; 20466 } 20467 const editor = await super.deserialize(data, parent, uiManager); 20468 editor.#fontSize = data.fontSize; 20469 editor.color = Util.makeHexColor(...data.color); 20470 editor.#content = FreeTextEditor.#deserializeContent(data.value); 20471 editor._initialData = initialData; 20472 if (data.comment) { 20473 editor.setCommentData(data); 20474 } 20475 return editor; 20476 } 20477 serialize(isForCopying = false) { 20478 if (this.isEmpty()) { 20479 return null; 20480 } 20481 if (this.deleted) { 20482 return this.serializeDeleted(); 20483 } 20484 const color = AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.color); 20485 const serialized = Object.assign(super.serialize(isForCopying), { 20486 color, 20487 fontSize: this.#fontSize, 20488 value: this.#serializeContent() 20489 }); 20490 this.addComment(serialized); 20491 if (isForCopying) { 20492 serialized.isCopy = true; 20493 return serialized; 20494 } 20495 if (this.annotationElementId && !this.#hasElementChanged(serialized)) { 20496 return null; 20497 } 20498 serialized.id = this.annotationElementId; 20499 return serialized; 20500 } 20501 #hasElementChanged(serialized) { 20502 const { 20503 value, 20504 fontSize, 20505 color, 20506 pageIndex 20507 } = this._initialData; 20508 return this.hasEditedComment || this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex; 20509 } 20510 renderAnnotationElement(annotation) { 20511 const content = super.renderAnnotationElement(annotation); 20512 if (!content) { 20513 return null; 20514 } 20515 const { 20516 style 20517 } = content; 20518 style.fontSize = `calc(${this.#fontSize}px * var(--total-scale-factor))`; 20519 style.color = this.color; 20520 content.replaceChildren(); 20521 for (const line of this.#content.split("\n")) { 20522 const div = document.createElement("div"); 20523 div.append(line ? document.createTextNode(line) : document.createElement("br")); 20524 content.append(div); 20525 } 20526 annotation.updateEdited({ 20527 rect: this.getPDFRect(), 20528 popup: this._uiManager.hasCommentManager() || this.hasEditedComment ? this.comment : { 20529 text: this.#content 20530 } 20531 }); 20532 return content; 20533 } 20534 resetAnnotationElement(annotation) { 20535 super.resetAnnotationElement(annotation); 20536 annotation.resetEdited(); 20537 } 20538} 20539 20540;// ./src/display/editor/drawers/outline.js 20541 20542class Outline { 20543 static PRECISION = 1e-4; 20544 toSVGPath() { 20545 unreachable("Abstract method `toSVGPath` must be implemented."); 20546 } 20547 get box() { 20548 unreachable("Abstract getter `box` must be implemented."); 20549 } 20550 serialize(_bbox, _rotation) { 20551 unreachable("Abstract method `serialize` must be implemented."); 20552 } 20553 static _rescale(src, tx, ty, sx, sy, dest) { 20554 dest ||= new Float32Array(src.length); 20555 for (let i = 0, ii = src.length; i < ii; i += 2) { 20556 dest[i] = tx + src[i] * sx; 20557 dest[i + 1] = ty + src[i + 1] * sy; 20558 } 20559 return dest; 20560 } 20561 static _rescaleAndSwap(src, tx, ty, sx, sy, dest) { 20562 dest ||= new Float32Array(src.length); 20563 for (let i = 0, ii = src.length; i < ii; i += 2) { 20564 dest[i] = tx + src[i + 1] * sx; 20565 dest[i + 1] = ty + src[i] * sy; 20566 } 20567 return dest; 20568 } 20569 static _translate(src, tx, ty, dest) { 20570 dest ||= new Float32Array(src.length); 20571 for (let i = 0, ii = src.length; i < ii; i += 2) { 20572 dest[i] = tx + src[i]; 20573 dest[i + 1] = ty + src[i + 1]; 20574 } 20575 return dest; 20576 } 20577 static svgRound(x) { 20578 return Math.round(x * 10000); 20579 } 20580 static _normalizePoint(x, y, parentWidth, parentHeight, rotation) { 20581 switch (rotation) { 20582 case 90: 20583 return [1 - y / parentWidth, x / parentHeight]; 20584 case 180: 20585 return [1 - x / parentWidth, 1 - y / parentHeight]; 20586 case 270: 20587 return [y / parentWidth, 1 - x / parentHeight]; 20588 default: 20589 return [x / parentWidth, y / parentHeight]; 20590 } 20591 } 20592 static _normalizePagePoint(x, y, rotation) { 20593 switch (rotation) { 20594 case 90: 20595 return [1 - y, x]; 20596 case 180: 20597 return [1 - x, 1 - y]; 20598 case 270: 20599 return [y, 1 - x]; 20600 default: 20601 return [x, y]; 20602 } 20603 } 20604 static createBezierPoints(x1, y1, x2, y2, x3, y3) { 20605 return [(x1 + 5 * x2) / 6, (y1 + 5 * y2) / 6, (5 * x2 + x3) / 6, (5 * y2 + y3) / 6, (x2 + x3) / 2, (y2 + y3) / 2]; 20606 } 20607} 20608 20609;// ./src/display/editor/drawers/freedraw.js 20610 20611 20612class FreeDrawOutliner { 20613 #box; 20614 #bottom = []; 20615 #innerMargin; 20616 #isLTR; 20617 #top = []; 20618 #last = new Float32Array(18); 20619 #lastX; 20620 #lastY; 20621 #min; 20622 #min_dist; 20623 #scaleFactor; 20624 #thickness; 20625 #points = []; 20626 static #MIN_DIST = 8; 20627 static #MIN_DIFF = 2; 20628 static #MIN = FreeDrawOutliner.#MIN_DIST + FreeDrawOutliner.#MIN_DIFF; 20629 constructor({ 20630 x, 20631 y 20632 }, box, scaleFactor, thickness, isLTR, innerMargin = 0) { 20633 this.#box = box; 20634 this.#thickness = thickness * scaleFactor; 20635 this.#isLTR = isLTR; 20636 this.#last.set([NaN, NaN, NaN, NaN, x, y], 6); 20637 this.#innerMargin = innerMargin; 20638 this.#min_dist = FreeDrawOutliner.#MIN_DIST * scaleFactor; 20639 this.#min = FreeDrawOutliner.#MIN * scaleFactor; 20640 this.#scaleFactor = scaleFactor; 20641 this.#points.push(x, y); 20642 } 20643 isEmpty() { 20644 return isNaN(this.#last[8]); 20645 } 20646 #getLastCoords() { 20647 const lastTop = this.#last.subarray(4, 6); 20648 const lastBottom = this.#last.subarray(16, 18); 20649 const [x, y, width, height] = this.#box; 20650 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]; 20651 } 20652 add({ 20653 x, 20654 y 20655 }
vendor: 11,269 bytes, lines 20655-20919
20655) { 20656 this.#lastX = x; 20657 this.#lastY = y; 20658 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 20659 let [x1, y1, x2, y2] = this.#last.subarray(8, 12); 20660 const diffX = x - x2; 20661 const diffY = y - y2; 20662 const d = Math.hypot(diffX, diffY); 20663 if (d < this.#min) { 20664 return false; 20665 } 20666 const diffD = d - this.#min_dist; 20667 const K = diffD / d; 20668 const shiftX = K * diffX; 20669 const shiftY = K * diffY; 20670 let x0 = x1; 20671 let y0 = y1; 20672 x1 = x2; 20673 y1 = y2; 20674 x2 += shiftX; 20675 y2 += shiftY; 20676 this.#points?.push(x, y); 20677 const nX = -shiftY / diffD; 20678 const nY = shiftX / diffD; 20679 const thX = nX * this.#thickness; 20680 const thY = nY * this.#thickness; 20681 this.#last.set(this.#last.subarray(2, 8), 0); 20682 this.#last.set([x2 + thX, y2 + thY], 4); 20683 this.#last.set(this.#last.subarray(14, 18), 12); 20684 this.#last.set([x2 - thX, y2 - thY], 16); 20685 if (isNaN(this.#last[6])) { 20686 if (this.#top.length === 0) { 20687 this.#last.set([x1 + thX, y1 + thY], 2); 20688 this.#top.push(NaN, NaN, NaN, NaN, (x1 + thX - layerX) / layerWidth, (y1 + thY - layerY) / layerHeight); 20689 this.#last.set([x1 - thX, y1 - thY], 14); 20690 this.#bottom.push(NaN, NaN, NaN, NaN, (x1 - thX - layerX) / layerWidth, (y1 - thY - layerY) / layerHeight); 20691 } 20692 this.#last.set([x0, y0, x1, y1, x2, y2], 6); 20693 return !this.isEmpty(); 20694 } 20695 this.#last.set([x0, y0, x1, y1, x2, y2], 6); 20696 const angle = Math.abs(Math.atan2(y0 - y1, x0 - x1) - Math.atan2(shiftY, shiftX)); 20697 if (angle < Math.PI / 2) { 20698 [x1, y1, x2, y2] = this.#last.subarray(2, 6); 20699 this.#top.push(NaN, NaN, NaN, NaN, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight); 20700 [x1, y1, x0, y0] = this.#last.subarray(14, 18); 20701 this.#bottom.push(NaN, NaN, NaN, NaN, ((x0 + x1) / 2 - layerX) / layerWidth, ((y0 + y1) / 2 - layerY) / layerHeight); 20702 return true; 20703 } 20704 [x0, y0, x1, y1, x2, y2] = this.#last.subarray(0, 6); 20705 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); 20706 [x2, y2, x1, y1, x0, y0] = this.#last.subarray(12, 18); 20707 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); 20708 return true; 20709 } 20710 toSVGPath() { 20711 if (this.isEmpty()) { 20712 return ""; 20713 } 20714 const top = this.#top; 20715 const bottom = this.#bottom; 20716 if (isNaN(this.#last[6]) && !this.isEmpty()) { 20717 return this.#toSVGPathTwoPoints(); 20718 } 20719 const buffer = []; 20720 buffer.push(`M${top[4]} ${top[5]}`); 20721 for (let i = 6; i < top.length; i += 6) { 20722 if (isNaN(top[i])) { 20723 buffer.push(`L${top[i + 4]} ${top[i + 5]}`); 20724 } else { 20725 buffer.push(`C${top[i]} ${top[i + 1]} ${top[i + 2]} ${top[i + 3]} ${top[i + 4]} ${top[i + 5]}`); 20726 } 20727 } 20728 this.#toSVGPathEnd(buffer); 20729 for (let i = bottom.length - 6; i >= 6; i -= 6) { 20730 if (isNaN(bottom[i])) { 20731 buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`); 20732 } else { 20733 buffer.push(`C${bottom[i]} ${bottom[i + 1]} ${bottom[i + 2]} ${bottom[i + 3]} ${bottom[i + 4]} ${bottom[i + 5]}`); 20734 } 20735 } 20736 this.#toSVGPathStart(buffer); 20737 return buffer.join(" "); 20738 } 20739 #toSVGPathTwoPoints() { 20740 const [x, y, width, height] = this.#box; 20741 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 20742 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`; 20743 } 20744 #toSVGPathStart(buffer) { 20745 const bottom = this.#bottom; 20746 buffer.push(`L${bottom[4]} ${bottom[5]} Z`); 20747 } 20748 #toSVGPathEnd(buffer) { 20749 const [x, y, width, height] = this.#box; 20750 const lastTop = this.#last.subarray(4, 6); 20751 const lastBottom = this.#last.subarray(16, 18); 20752 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 20753 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}`); 20754 } 20755 newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { 20756 return new FreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR); 20757 } 20758 getOutlines() { 20759 const top = this.#top; 20760 const bottom = this.#bottom; 20761 const last = this.#last; 20762 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 20763 const points = new Float32Array((this.#points?.length ?? 0) + 2); 20764 for (let i = 0, ii = points.length - 2; i < ii; i += 2) { 20765 points[i] = (this.#points[i] - layerX) / layerWidth; 20766 points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight; 20767 } 20768 points[points.length - 2] = (this.#lastX - layerX) / layerWidth; 20769 points[points.length - 1] = (this.#lastY - layerY) / layerHeight; 20770 if (isNaN(last[6]) && !this.isEmpty()) { 20771 return this.#getOutlineTwoPoints(points); 20772 } 20773 const outline = new Float32Array(this.#top.length + 24 + this.#bottom.length); 20774 let N = top.length; 20775 for (let i = 0; i < N; i += 2) { 20776 if (isNaN(top[i])) { 20777 outline[i] = outline[i + 1] = NaN; 20778 continue; 20779 } 20780 outline[i] = top[i]; 20781 outline[i + 1] = top[i + 1]; 20782 } 20783 N = this.#getOutlineEnd(outline, N); 20784 for (let i = bottom.length - 6; i >= 6; i -= 6) { 20785 for (let j = 0; j < 6; j += 2) { 20786 if (isNaN(bottom[i + j])) { 20787 outline[N] = outline[N + 1] = NaN; 20788 N += 2; 20789 continue; 20790 } 20791 outline[N] = bottom[i + j]; 20792 outline[N + 1] = bottom[i + j + 1]; 20793 N += 2; 20794 } 20795 } 20796 this.#getOutlineStart(outline, N); 20797 return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); 20798 } 20799 #getOutlineTwoPoints(points) { 20800 const last = this.#last; 20801 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 20802 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 20803 const outline = new Float32Array(36); 20804 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); 20805 return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR); 20806 } 20807 #getOutlineStart(outline, pos) { 20808 const bottom = this.#bottom; 20809 outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], pos); 20810 return pos += 6; 20811 } 20812 #getOutlineEnd(outline, pos) { 20813 const lastTop = this.#last.subarray(4, 6); 20814 const lastBottom = this.#last.subarray(16, 18); 20815 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 20816 const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords(); 20817 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); 20818 return pos += 24; 20819 } 20820} 20821class FreeDrawOutline extends Outline { 20822 #box; 20823 #bbox = new Float32Array(4); 20824 #innerMargin; 20825 #isLTR; 20826 #points; 20827 #scaleFactor; 20828 #outline; 20829 constructor(outline, points, box, scaleFactor, innerMargin, isLTR) { 20830 super(); 20831 this.#outline = outline; 20832 this.#points = points; 20833 this.#box = box; 20834 this.#scaleFactor = scaleFactor; 20835 this.#innerMargin = innerMargin; 20836 this.#isLTR = isLTR; 20837 this.firstPoint = [NaN, NaN]; 20838 this.lastPoint = [NaN, NaN]; 20839 this.#computeMinMax(isLTR); 20840 const [x, y, width, height] = this.#bbox; 20841 for (let i = 0, ii = outline.length; i < ii; i += 2) { 20842 outline[i] = (outline[i] - x) / width; 20843 outline[i + 1] = (outline[i + 1] - y) / height; 20844 } 20845 for (let i = 0, ii = points.length; i < ii; i += 2) { 20846 points[i] = (points[i] - x) / width; 20847 points[i + 1] = (points[i + 1] - y) / height; 20848 } 20849 } 20850 toSVGPath() { 20851 const buffer = [`M${this.#outline[4]} ${this.#outline[5]}`]; 20852 for (let i = 6, ii = this.#outline.length; i < ii; i += 6) { 20853 if (isNaN(this.#outline[i])) { 20854 buffer.push(`L${this.#outline[i + 4]} ${this.#outline[i + 5]}`); 20855 continue; 20856 } 20857 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]}`); 20858 } 20859 buffer.push("Z"); 20860 return buffer.join(" "); 20861 } 20862 serialize([blX, blY, trX, trY], rotation) { 20863 const width = trX - blX; 20864 const height = trY - blY; 20865 let outline; 20866 let points; 20867 switch (rotation) { 20868 case 0: 20869 outline = Outline._rescale(this.#outline, blX, trY, width, -height); 20870 points = Outline._rescale(this.#points, blX, trY, width, -height); 20871 break; 20872 case 90: 20873 outline = Outline._rescaleAndSwap(this.#outline, blX, blY, width, height); 20874 points = Outline._rescaleAndSwap(this.#points, blX, blY, width, height); 20875 break; 20876 case 180: 20877 outline = Outline._rescale(this.#outline, trX, blY, -width, height); 20878 points = Outline._rescale(this.#points, trX, blY, -width, height); 20879 break; 20880 case 270: 20881 outline = Outline._rescaleAndSwap(this.#outline, trX, trY, -width, -height); 20882 points = Outline._rescaleAndSwap(this.#points, trX, trY, -width, -height); 20883 break; 20884 } 20885 return { 20886 outline: Array.from(outline), 20887 points: [Array.from(points)] 20888 }; 20889 } 20890 #computeMinMax(isLTR) { 20891 const outline = this.#outline; 20892 let lastX = outline[4]; 20893 let lastY = outline[5]; 20894 const minMax = [lastX, lastY, lastX, lastY]; 20895 let firstPointX = lastX; 20896 let firstPointY = lastY; 20897 let lastPointX = lastX; 20898 let lastPointY = lastY; 20899 const ltrCallback = isLTR ? Math.max : Math.min; 20900 const bezierBbox = new Float32Array(4); 20901 for (let i = 6, ii = outline.length; i < ii; i += 6) { 20902 const x = outline[i + 4], 20903 y = outline[i + 5]; 20904 if (isNaN(outline[i])) { 20905 Util.pointBoundingBox(x, y, minMax); 20906 if (firstPointY > y) { 20907 firstPointX = x; 20908 firstPointY = y; 20909 } else if (firstPointY === y) { 20910 firstPointX = ltrCallback(firstPointX, x); 20911 } 20912 if (lastPointY < y) { 20913 lastPointX = x; 20914 lastPointY = y; 20915 } else if (lastPointY === y) { 20916 lastPointX = ltrCallback(lastPointX, x); 20917 } 20918 } else { 20919 bezierBbox
vendor: 46,763 bytes, lines 20919-22555
20919[0] = bezierBbox[1] = Infinity; 20920 bezierBbox[2] = bezierBbox[3] = -Infinity; 20921 Util.bezierBoundingBox(lastX, lastY, ...outline.slice(i, i + 6), bezierBbox); 20922 Util.rectBoundingBox(bezierBbox[0], bezierBbox[1], bezierBbox[2], bezierBbox[3], minMax); 20923 if (firstPointY > bezierBbox[1]) { 20924 firstPointX = bezierBbox[0]; 20925 firstPointY = bezierBbox[1]; 20926 } else if (firstPointY === bezierBbox[1]) { 20927 firstPointX = ltrCallback(firstPointX, bezierBbox[0]); 20928 } 20929 if (lastPointY < bezierBbox[3]) { 20930 lastPointX = bezierBbox[2]; 20931 lastPointY = bezierBbox[3]; 20932 } else if (lastPointY === bezierBbox[3]) { 20933 lastPointX = ltrCallback(lastPointX, bezierBbox[2]); 20934 } 20935 } 20936 lastX = x; 20937 lastY = y; 20938 } 20939 const bbox = this.#bbox; 20940 bbox[0] = minMax[0] - this.#innerMargin; 20941 bbox[1] = minMax[1] - this.#innerMargin; 20942 bbox[2] = minMax[2] - minMax[0] + 2 * this.#innerMargin; 20943 bbox[3] = minMax[3] - minMax[1] + 2 * this.#innerMargin; 20944 this.firstPoint = [firstPointX, firstPointY]; 20945 this.lastPoint = [lastPointX, lastPointY]; 20946 } 20947 get box() { 20948 return this.#bbox; 20949 } 20950 newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { 20951 return new FreeDrawOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); 20952 } 20953 getNewOutline(thickness, innerMargin) { 20954 const [x, y, width, height] = this.#bbox; 20955 const [layerX, layerY, layerWidth, layerHeight] = this.#box; 20956 const sx = width * layerWidth; 20957 const sy = height * layerHeight; 20958 const tx = x * layerWidth + layerX; 20959 const ty = y * layerHeight + layerY; 20960 const outliner = this.newOutliner({ 20961 x: this.#points[0] * sx + tx, 20962 y: this.#points[1] * sy + ty 20963 }, this.#box, this.#scaleFactor, thickness, this.#isLTR, innerMargin ?? this.#innerMargin); 20964 for (let i = 2; i < this.#points.length; i += 2) { 20965 outliner.add({ 20966 x: this.#points[i] * sx + tx, 20967 y: this.#points[i + 1] * sy + ty 20968 }); 20969 } 20970 return outliner.getOutlines(); 20971 } 20972} 20973 20974;// ./src/display/editor/drawers/highlight.js 20975 20976 20977 20978class HighlightOutliner { 20979 #box; 20980 #firstPoint; 20981 #lastPoint; 20982 #verticalEdges = []; 20983 #intervals = []; 20984 constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) { 20985 const minMax = [Infinity, Infinity, -Infinity, -Infinity]; 20986 const NUMBER_OF_DIGITS = 4; 20987 const EPSILON = 10 ** -NUMBER_OF_DIGITS; 20988 for (const { 20989 x, 20990 y, 20991 width, 20992 height 20993 } of boxes) { 20994 const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON; 20995 const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON; 20996 const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON; 20997 const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON; 20998 const left = [x1, y1, y2, true]; 20999 const right = [x2, y1, y2, false]; 21000 this.#verticalEdges.push(left, right); 21001 Util.rectBoundingBox(x1, y1, x2, y2, minMax); 21002 } 21003 const bboxWidth = minMax[2] - minMax[0] + 2 * innerMargin; 21004 const bboxHeight = minMax[3] - minMax[1] + 2 * innerMargin; 21005 const shiftedMinX = minMax[0] - innerMargin; 21006 const shiftedMinY = minMax[1] - innerMargin; 21007 let firstPointX = isLTR ? -Infinity : Infinity; 21008 let firstPointY = Infinity; 21009 const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2); 21010 const lastPoint = [lastEdge[0], lastEdge[2]]; 21011 for (const edge of this.#verticalEdges) { 21012 const [x, y1, y2, left] = edge; 21013 if (!left && isLTR) { 21014 if (y1 < firstPointY) { 21015 firstPointY = y1; 21016 firstPointX = x; 21017 } else if (y1 === firstPointY) { 21018 firstPointX = Math.max(firstPointX, x); 21019 } 21020 } else if (left && !isLTR) { 21021 if (y1 < firstPointY) { 21022 firstPointY = y1; 21023 firstPointX = x; 21024 } else if (y1 === firstPointY) { 21025 firstPointX = Math.min(firstPointX, x); 21026 } 21027 } 21028 edge[0] = (x - shiftedMinX) / bboxWidth; 21029 edge[1] = (y1 - shiftedMinY) / bboxHeight; 21030 edge[2] = (y2 - shiftedMinY) / bboxHeight; 21031 } 21032 this.#box = new Float32Array([shiftedMinX, shiftedMinY, bboxWidth, bboxHeight]); 21033 this.#firstPoint = [firstPointX, firstPointY]; 21034 this.#lastPoint = lastPoint; 21035 } 21036 getOutlines() { 21037 this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]); 21038 const outlineVerticalEdges = []; 21039 for (const edge of this.#verticalEdges) { 21040 if (edge[3]) { 21041 outlineVerticalEdges.push(...this.#breakEdge(edge)); 21042 this.#insert(edge); 21043 } else { 21044 this.#remove(edge); 21045 outlineVerticalEdges.push(...this.#breakEdge(edge)); 21046 } 21047 } 21048 return this.#getOutlines(outlineVerticalEdges); 21049 } 21050 #getOutlines(outlineVerticalEdges) { 21051 const edges = []; 21052 const allEdges = new Set(); 21053 for (const edge of outlineVerticalEdges) { 21054 const [x, y1, y2] = edge; 21055 edges.push([x, y1, edge], [x, y2, edge]); 21056 } 21057 edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]); 21058 for (let i = 0, ii = edges.length; i < ii; i += 2) { 21059 const edge1 = edges[i][2]; 21060 const edge2 = edges[i + 1][2]; 21061 edge1.push(edge2); 21062 edge2.push(edge1); 21063 allEdges.add(edge1); 21064 allEdges.add(edge2); 21065 } 21066 const outlines = []; 21067 let outline; 21068 while (allEdges.size > 0) { 21069 const edge = allEdges.values().next().value; 21070 let [x, y1, y2, edge1, edge2] = edge; 21071 allEdges.delete(edge); 21072 let lastPointX = x; 21073 let lastPointY = y1; 21074 outline = [x, y2]; 21075 outlines.push(outline); 21076 while (true) { 21077 let e; 21078 if (allEdges.has(edge1)) { 21079 e = edge1; 21080 } else if (allEdges.has(edge2)) { 21081 e = edge2; 21082 } else { 21083 break; 21084 } 21085 allEdges.delete(e); 21086 [x, y1, y2, edge1, edge2] = e; 21087 if (lastPointX !== x) { 21088 outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2); 21089 lastPointX = x; 21090 } 21091 lastPointY = lastPointY === y1 ? y2 : y1; 21092 } 21093 outline.push(lastPointX, lastPointY); 21094 } 21095 return new HighlightOutline(outlines, this.#box, this.#firstPoint, this.#lastPoint); 21096 } 21097 #binarySearch(y) { 21098 const array = this.#intervals; 21099 let start = 0; 21100 let end = array.length - 1; 21101 while (start <= end) { 21102 const middle = start + end >> 1; 21103 const y1 = array[middle][0]; 21104 if (y1 === y) { 21105 return middle; 21106 } 21107 if (y1 < y) { 21108 start = middle + 1; 21109 } else { 21110 end = middle - 1; 21111 } 21112 } 21113 return end + 1; 21114 } 21115 #insert([, y1, y2]) { 21116 const index = this.#binarySearch(y1); 21117 this.#intervals.splice(index, 0, [y1, y2]); 21118 } 21119 #remove([, y1, y2]) { 21120 const index = this.#binarySearch(y1); 21121 for (let i = index; i < this.#intervals.length; i++) { 21122 const [start, end] = this.#intervals[i]; 21123 if (start !== y1) { 21124 break; 21125 } 21126 if (start === y1 && end === y2) { 21127 this.#intervals.splice(i, 1); 21128 return; 21129 } 21130 } 21131 for (let i = index - 1; i >= 0; i--) { 21132 const [start, end] = this.#intervals[i]; 21133 if (start !== y1) { 21134 break; 21135 } 21136 if (start === y1 && end === y2) { 21137 this.#intervals.splice(i, 1); 21138 return; 21139 } 21140 } 21141 } 21142 #breakEdge(edge) { 21143 const [x, y1, y2] = edge; 21144 const results = [[x, y1, y2]]; 21145 const index = this.#binarySearch(y2); 21146 for (let i = 0; i < index; i++) { 21147 const [start, end] = this.#intervals[i]; 21148 for (let j = 0, jj = results.length; j < jj; j++) { 21149 const [, y3, y4] = results[j]; 21150 if (end <= y3 || y4 <= start) { 21151 continue; 21152 } 21153 if (y3 >= start) { 21154 if (y4 > end) { 21155 results[j][1] = end; 21156 } else { 21157 if (jj === 1) { 21158 return []; 21159 } 21160 results.splice(j, 1); 21161 j--; 21162 jj--; 21163 } 21164 continue; 21165 } 21166 results[j][2] = start; 21167 if (y4 > end) { 21168 results.push([x, end, y4]); 21169 } 21170 } 21171 } 21172 return results; 21173 } 21174} 21175class HighlightOutline extends Outline { 21176 #box; 21177 #outlines; 21178 constructor(outlines, box, firstPoint, lastPoint) { 21179 super(); 21180 this.#outlines = outlines; 21181 this.#box = box; 21182 this.firstPoint = firstPoint; 21183 this.lastPoint = lastPoint; 21184 } 21185 toSVGPath() { 21186 const buffer = []; 21187 for (const polygon of this.#outlines) { 21188 let [prevX, prevY] = polygon; 21189 buffer.push(`M${prevX} ${prevY}`); 21190 for (let i = 2; i < polygon.length; i += 2) { 21191 const x = polygon[i]; 21192 const y = polygon[i + 1]; 21193 if (x === prevX) { 21194 buffer.push(`V${y}`); 21195 prevY = y; 21196 } else if (y === prevY) { 21197 buffer.push(`H${x}`); 21198 prevX = x; 21199 } 21200 } 21201 buffer.push("Z"); 21202 } 21203 return buffer.join(" "); 21204 } 21205 serialize([blX, blY, trX, trY], _rotation) { 21206 const outlines = []; 21207 const width = trX - blX; 21208 const height = trY - blY; 21209 for (const outline of this.#outlines) { 21210 const points = new Array(outline.length); 21211 for (let i = 0; i < outline.length; i += 2) { 21212 points[i] = blX + outline[i] * width; 21213 points[i + 1] = trY - outline[i + 1] * height; 21214 } 21215 outlines.push(points); 21216 } 21217 return outlines; 21218 } 21219 get box() { 21220 return this.#box; 21221 } 21222 get classNamesForOutlining() { 21223 return ["highlightOutline"]; 21224 } 21225} 21226class FreeHighlightOutliner extends FreeDrawOutliner { 21227 newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) { 21228 return new FreeHighlightOutline(outline, points, box, scaleFactor, innerMargin, isLTR); 21229 } 21230} 21231class FreeHighlightOutline extends FreeDrawOutline { 21232 newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) { 21233 return new FreeHighlightOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin); 21234 } 21235} 21236 21237;// ./src/display/editor/highlight.js 21238 21239 21240 21241 21242 21243 21244 21245class HighlightEditor extends AnnotationEditor { 21246 #anchorNode = null; 21247 #anchorOffset = 0; 21248 #boxes; 21249 #clipPathId = null; 21250 #colorPicker = null; 21251 #focusOutlines = null; 21252 #focusNode = null; 21253 #focusOffset = 0; 21254 #highlightDiv = null; 21255 #highlightOutlines = null; 21256 #id = null; 21257 #isFreeHighlight = false; 21258 #firstPoint = null; 21259 #lastPoint = null; 21260 #outlineId = null; 21261 #text = ""; 21262 #thickness; 21263 #methodOfCreation = ""; 21264 static _defaultColor = null; 21265 static _defaultOpacity = 1; 21266 static _defaultThickness = 12; 21267 static _type = "highlight"; 21268 static _editorType = AnnotationEditorType.HIGHLIGHT; 21269 static _freeHighlightId = -1; 21270 static _freeHighlight = null; 21271 static _freeHighlightClipId = ""; 21272 static get _keyboardManager() { 21273 const proto = HighlightEditor.prototype; 21274 return shadow(this, "_keyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, { 21275 args: [0] 21276 }], [["ArrowRight", "mac+ArrowRight"], proto._moveCaret, { 21277 args: [1] 21278 }], [["ArrowUp", "mac+ArrowUp"], proto._moveCaret, { 21279 args: [2] 21280 }], [["ArrowDown", "mac+ArrowDown"], proto._moveCaret, { 21281 args: [3] 21282 }]])); 21283 } 21284 constructor(params) { 21285 super({ 21286 ...params, 21287 name: "highlightEditor" 21288 }); 21289 this.color = params.color || HighlightEditor._defaultColor; 21290 this.#thickness = params.thickness || HighlightEditor._defaultThickness; 21291 this.opacity = params.opacity || HighlightEditor._defaultOpacity; 21292 this.#boxes = params.boxes || null; 21293 this.#methodOfCreation = params.methodOfCreation || ""; 21294 this.#text = params.text || ""; 21295 this._isDraggable = false; 21296 this.defaultL10nId = "pdfjs-editor-highlight-editor"; 21297 if (params.highlightId > -1) { 21298 this.#isFreeHighlight = true; 21299 this.#createFreeOutlines(params); 21300 this.#addToDrawLayer(); 21301 } else if (this.#boxes) { 21302 this.#anchorNode = params.anchorNode; 21303 this.#anchorOffset = params.anchorOffset; 21304 this.#focusNode = params.focusNode; 21305 this.#focusOffset = params.focusOffset; 21306 this.#createOutlines(); 21307 this.#addToDrawLayer(); 21308 this.rotate(this.rotation); 21309 } 21310 if (!this.annotationElementId) { 21311 this._uiManager.a11yAlert("pdfjs-editor-highlight-added-alert"); 21312 } 21313 } 21314 get telemetryInitialData() { 21315 return { 21316 action: "added", 21317 type: this.#isFreeHighlight ? "free_highlight" : "highlight", 21318 color: this._uiManager.getNonHCMColorName(this.color), 21319 thickness: this.#thickness, 21320 methodOfCreation: this.#methodOfCreation 21321 }; 21322 } 21323 get telemetryFinalData() { 21324 return { 21325 type: "highlight", 21326 color: this._uiManager.getNonHCMColorName(this.color) 21327 }; 21328 } 21329 static computeTelemetryFinalData(data) { 21330 return { 21331 numberOfColors: data.get("color").size 21332 }; 21333 } 21334 #createOutlines() { 21335 const outliner = new HighlightOutliner(this.#boxes, 0.001); 21336 this.#highlightOutlines = outliner.getOutlines(); 21337 [this.x, this.y, this.width, this.height] = this.#highlightOutlines.box; 21338 const outlinerForOutline = new HighlightOutliner(this.#boxes, 0.0025, 0.001, this._uiManager.direction === "ltr"); 21339 this.#focusOutlines = outlinerForOutline.getOutlines(); 21340 const { 21341 firstPoint 21342 } = this.#highlightOutlines; 21343 this.#firstPoint = [(firstPoint[0] - this.x) / this.width, (firstPoint[1] - this.y) / this.height]; 21344 const { 21345 lastPoint 21346 } = this.#focusOutlines; 21347 this.#lastPoint = [(lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height]; 21348 } 21349 #createFreeOutlines({ 21350 highlightOutlines, 21351 highlightId, 21352 clipPathId 21353 }) { 21354 this.#highlightOutlines = highlightOutlines; 21355 const extraThickness = 1.5; 21356 this.#focusOutlines = highlightOutlines.getNewOutline(this.#thickness / 2 + extraThickness, 0.0025); 21357 if (highlightId >= 0) { 21358 this.#id = highlightId; 21359 this.#clipPathId = clipPathId; 21360 this.parent.drawLayer.finalizeDraw(highlightId, { 21361 bbox: highlightOutlines.box, 21362 path: { 21363 d: highlightOutlines.toSVGPath() 21364 } 21365 }); 21366 this.#outlineId = this.parent.drawLayer.drawOutline({ 21367 rootClass: { 21368 highlightOutline: true, 21369 free: true 21370 }, 21371 bbox: this.#focusOutlines.box, 21372 path: { 21373 d: this.#focusOutlines.toSVGPath() 21374 } 21375 }, true); 21376 } else if (this.parent) { 21377 const angle = this.parent.viewport.rotation; 21378 this.parent.drawLayer.updateProperties(this.#id, { 21379 bbox: HighlightEditor.#rotateBbox(this.#highlightOutlines.box, (angle - this.rotation + 360) % 360), 21380 path: { 21381 d: highlightOutlines.toSVGPath() 21382 } 21383 }); 21384 this.parent.drawLayer.updateProperties(this.#outlineId, { 21385 bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), 21386 path: { 21387 d: this.#focusOutlines.toSVGPath() 21388 } 21389 }); 21390 } 21391 const [x, y, width, height] = highlightOutlines.box; 21392 switch (this.rotation) { 21393 case 0: 21394 this.x = x; 21395 this.y = y; 21396 this.width = width; 21397 this.height = height; 21398 break; 21399 case 90: 21400 { 21401 const [pageWidth, pageHeight] = this.parentDimensions; 21402 this.x = y; 21403 this.y = 1 - x; 21404 this.width = width * pageHeight / pageWidth; 21405 this.height = height * pageWidth / pageHeight; 21406 break; 21407 } 21408 case 180: 21409 this.x = 1 - x; 21410 this.y = 1 - y; 21411 this.width = width; 21412 this.height = height; 21413 break; 21414 case 270: 21415 { 21416 const [pageWidth, pageHeight] = this.parentDimensions; 21417 this.x = 1 - y; 21418 this.y = x; 21419 this.width = width * pageHeight / pageWidth; 21420 this.height = height * pageWidth / pageHeight; 21421 break; 21422 } 21423 } 21424 const { 21425 firstPoint 21426 } = highlightOutlines; 21427 this.#firstPoint = [(firstPoint[0] - x) / width, (firstPoint[1] - y) / height]; 21428 const { 21429 lastPoint 21430 } = this.#focusOutlines; 21431 this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height]; 21432 } 21433 static initialize(l10n, uiManager) { 21434 AnnotationEditor.initialize(l10n, uiManager); 21435 HighlightEditor._defaultColor ||= uiManager.highlightColors?.values().next().value || "#fff066"; 21436 } 21437 static updateDefaultParams(type, value) { 21438 switch (type) { 21439 case AnnotationEditorParamsType.HIGHLIGHT_COLOR: 21440 HighlightEditor._defaultColor = value; 21441 break; 21442 case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: 21443 HighlightEditor._defaultThickness = value; 21444 break; 21445 } 21446 } 21447 translateInPage(x, y) {} 21448 get toolbarPosition() { 21449 return this.#lastPoint; 21450 } 21451 get commentButtonPosition() { 21452 return this.#firstPoint; 21453 } 21454 updateParams(type, value) { 21455 switch (type) { 21456 case AnnotationEditorParamsType.HIGHLIGHT_COLOR: 21457 this.#updateColor(value); 21458 break; 21459 case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS: 21460 this.#updateThickness(value); 21461 break; 21462 } 21463 } 21464 static get defaultPropertiesToUpdate() { 21465 return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, HighlightEditor._defaultThickness]]; 21466 } 21467 get propertiesToUpdate() { 21468 return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, this.color || HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, this.#thickness || HighlightEditor._defaultThickness], [AnnotationEditorParamsType.HIGHLIGHT_FREE, this.#isFreeHighlight]]; 21469 } 21470 onUpdatedColor() { 21471 this.parent?.drawLayer.updateProperties(this.#id, { 21472 root: { 21473 fill: this.color, 21474 "fill-opacity": this.opacity 21475 } 21476 }); 21477 this.#colorPicker?.updateColor(this.color); 21478 super.onUpdatedColor(); 21479 } 21480 #updateColor(color) { 21481 const setColorAndOpacity = (col, opa) => { 21482 this.color = col; 21483 this.opacity = opa; 21484 this.onUpdatedColor(); 21485 }; 21486 const savedColor = this.color; 21487 const savedOpacity = this.opacity; 21488 this.addCommands({ 21489 cmd: setColorAndOpacity.bind(this, color, HighlightEditor._defaultOpacity), 21490 undo: setColorAndOpacity.bind(this, savedColor, savedOpacity), 21491 post: this._uiManager.updateUI.bind(this._uiManager, this), 21492 mustExec: true, 21493 type: AnnotationEditorParamsType.HIGHLIGHT_COLOR, 21494 overwriteIfSameType: true, 21495 keepUndo: true 21496 }); 21497 this._reportTelemetry({ 21498 action: "color_changed", 21499 color: this._uiManager.getNonHCMColorName(color) 21500 }, true); 21501 } 21502 #updateThickness(thickness) { 21503 const savedThickness = this.#thickness; 21504 const setThickness = th => { 21505 this.#thickness = th; 21506 this.#changeThickness(th); 21507 }; 21508 this.addCommands({ 21509 cmd: setThickness.bind(this, thickness), 21510 undo: setThickness.bind(this, savedThickness), 21511 post: this._uiManager.updateUI.bind(this._uiManager, this), 21512 mustExec: true, 21513 type: AnnotationEditorParamsType.INK_THICKNESS, 21514 overwriteIfSameType: true, 21515 keepUndo: true 21516 }); 21517 this._reportTelemetry({ 21518 action: "thickness_changed", 21519 thickness 21520 }, true); 21521 } 21522 get toolbarButtons() { 21523 if (this._uiManager.highlightColors) { 21524 const colorPicker = this.#colorPicker = new ColorPicker({ 21525 editor: this 21526 }); 21527 return [["colorPicker", colorPicker]]; 21528 } 21529 return super.toolbarButtons; 21530 } 21531 disableEditing() { 21532 super.disableEditing(); 21533 this.div.classList.toggle("disabled", true); 21534 } 21535 enableEditing() { 21536 super.enableEditing(); 21537 this.div.classList.toggle("disabled", false); 21538 } 21539 fixAndSetPosition() { 21540 return super.fixAndSetPosition(this.#getRotation()); 21541 } 21542 getBaseTranslation() { 21543 return [0, 0]; 21544 } 21545 getRect(tx, ty) { 21546 return super.getRect(tx, ty, this.#getRotation()); 21547 } 21548 onceAdded(focus) { 21549 if (!this.annotationElementId) { 21550 this.parent.addUndoableEditor(this); 21551 } 21552 if (focus) { 21553 this.div.focus(); 21554 } 21555 } 21556 remove() { 21557 this.#cleanDrawLayer(); 21558 this._reportTelemetry({ 21559 action: "deleted" 21560 }); 21561 super.remove(); 21562 } 21563 rebuild() { 21564 if (!this.parent) { 21565 return; 21566 } 21567 super.rebuild(); 21568 if (this.div === null) { 21569 return; 21570 } 21571 this.#addToDrawLayer(); 21572 if (!this.isAttachedToDOM) { 21573 this.parent.add(this); 21574 } 21575 } 21576 setParent(parent) { 21577 let mustBeSelected = false; 21578 if (this.parent && !parent) { 21579 this.#cleanDrawLayer(); 21580 } else if (parent) { 21581 this.#addToDrawLayer(parent); 21582 mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); 21583 } 21584 super.setParent(parent); 21585 this.show(this._isVisible); 21586 if (mustBeSelected) { 21587 this.select(); 21588 } 21589 } 21590 #changeThickness(thickness) { 21591 if (!this.#isFreeHighlight) { 21592 return; 21593 } 21594 this.#createFreeOutlines({ 21595 highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2) 21596 }); 21597 this.fixAndSetPosition(); 21598 this.setDims(); 21599 } 21600 #cleanDrawLayer() { 21601 if (this.#id === null || !this.parent) { 21602 return; 21603 } 21604 this.parent.drawLayer.remove(this.#id); 21605 this.#id = null; 21606 this.parent.drawLayer.remove(this.#outlineId); 21607 this.#outlineId = null; 21608 } 21609 #addToDrawLayer(parent = this.parent) { 21610 if (this.#id !== null) { 21611 return; 21612 } 21613 ({ 21614 id: this.#id, 21615 clipPathId: this.#clipPathId 21616 } = parent.drawLayer.draw({ 21617 bbox: this.#highlightOutlines.box, 21618 root: { 21619 viewBox: "0 0 1 1", 21620 fill: this.color, 21621 "fill-opacity": this.opacity 21622 }, 21623 rootClass: { 21624 highlight: true, 21625 free: this.#isFreeHighlight 21626 }, 21627 path: { 21628 d: this.#highlightOutlines.toSVGPath() 21629 } 21630 }, false, true)); 21631 this.#outlineId = parent.drawLayer.drawOutline({ 21632 rootClass: { 21633 highlightOutline: true, 21634 free: this.#isFreeHighlight 21635 }, 21636 bbox: this.#focusOutlines.box, 21637 path: { 21638 d: this.#focusOutlines.toSVGPath() 21639 } 21640 }, this.#isFreeHighlight); 21641 if (this.#highlightDiv) { 21642 this.#highlightDiv.style.clipPath = this.#clipPathId; 21643 } 21644 } 21645 static #rotateBbox([x, y, width, height], angle) { 21646 switch (angle) { 21647 case 90: 21648 return [1 - y - height, x, height, width]; 21649 case 180: 21650 return [1 - x - width, 1 - y - height, width, height]; 21651 case 270: 21652 return [y, 1 - x - width, height, width]; 21653 } 21654 return [x, y, width, height]; 21655 } 21656 rotate(angle) { 21657 const { 21658 drawLayer 21659 } = this.parent; 21660 let box; 21661 if (this.#isFreeHighlight) { 21662 angle = (angle - this.rotation + 360) % 360; 21663 box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle); 21664 } else { 21665 box = HighlightEditor.#rotateBbox([this.x, this.y, this.width, this.height], angle); 21666 } 21667 drawLayer.updateProperties(this.#id, { 21668 bbox: box, 21669 root: { 21670 "data-main-rotation": angle 21671 } 21672 }); 21673 drawLayer.updateProperties(this.#outlineId, { 21674 bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle), 21675 root: { 21676 "data-main-rotation": angle 21677 } 21678 }); 21679 } 21680 render() { 21681 if (this.div) { 21682 return this.div; 21683 } 21684 const div = super.render(); 21685 if (this.#text) { 21686 div.setAttribute("aria-label", this.#text); 21687 div.setAttribute("role", "mark"); 21688 } 21689 if (this.#isFreeHighlight) { 21690 div.classList.add("free"); 21691 } else { 21692 this.div.addEventListener("keydown", this.#keydown.bind(this), { 21693 signal: this._uiManager._signal 21694 }); 21695 } 21696 const highlightDiv = this.#highlightDiv = document.createElement("div"); 21697 div.append(highlightDiv); 21698 highlightDiv.setAttribute("aria-hidden", "true"); 21699 highlightDiv.className = "internal"; 21700 highlightDiv.style.clipPath = this.#clipPathId; 21701 this.setDims(); 21702 bindEvents(this, this.#highlightDiv, ["pointerover", "pointerleave"]); 21703 this.enableEditing(); 21704 return div; 21705 } 21706 pointerover() { 21707 if (!this.isSelected) { 21708 this.parent?.drawLayer.updateProperties(this.#outlineId, { 21709 rootClass: { 21710 hovered: true 21711 } 21712 }); 21713 } 21714 } 21715 pointerleave() { 21716 if (!this.isSelected) { 21717 this.parent?.drawLayer.updateProperties(this.#outlineId, { 21718 rootClass: { 21719 hovered: false 21720 } 21721 }); 21722 } 21723 } 21724 #keydown(event) { 21725 HighlightEditor._keyboardManager.exec(this, event); 21726 } 21727 _moveCaret(direction) { 21728 this.parent.unselect(this); 21729 switch (direction) { 21730 case 0: 21731 case 2: 21732 this.#setCaret(true); 21733 break; 21734 case 1: 21735 case 3: 21736 this.#setCaret(false); 21737 break; 21738 } 21739 } 21740 #setCaret(start) { 21741 if (!this.#anchorNode) { 21742 return; 21743 } 21744 const selection = window.getSelection(); 21745 if (start) { 21746 selection.setPosition(this.#anchorNode, this.#anchorOffset); 21747 } else { 21748 selection.setPosition(this.#focusNode, this.#focusOffset); 21749 } 21750 } 21751 select() { 21752 super.select(); 21753 if (!this.#outlineId) { 21754 return; 21755 } 21756 this.parent?.drawLayer.updateProperties(this.#outlineId, { 21757 rootClass: { 21758 hovered: false, 21759 selected: true 21760 } 21761 }); 21762 } 21763 unselect() { 21764 super.unselect(); 21765 if (!this.#outlineId) { 21766 return; 21767 } 21768 this.parent?.drawLayer.updateProperties(this.#outlineId, { 21769 rootClass: { 21770 selected: false 21771 } 21772 }); 21773 if (!this.#isFreeHighlight) { 21774 this.#setCaret(false); 21775 } 21776 } 21777 get _mustFixPosition() { 21778 return !this.#isFreeHighlight; 21779 } 21780 show(visible = this._isVisible) { 21781 super.show(visible); 21782 if (this.parent) { 21783 this.parent.drawLayer.updateProperties(this.#id, { 21784 rootClass: { 21785 hidden: !visible 21786 } 21787 }); 21788 this.parent.drawLayer.updateProperties(this.#outlineId, { 21789 rootClass: { 21790 hidden: !visible 21791 } 21792 }); 21793 } 21794 } 21795 #getRotation() { 21796 return this.#isFreeHighlight ? this.rotation : 0; 21797 } 21798 #serializeBoxes() { 21799 if (this.#isFreeHighlight) { 21800 return null; 21801 } 21802 const [pageWidth, pageHeight] = this.pageDimensions; 21803 const [pageX, pageY] = this.pageTranslation; 21804 const boxes = this.#boxes; 21805 const quadPoints = new Float32Array(boxes.length * 8); 21806 let i = 0; 21807 for (const { 21808 x, 21809 y, 21810 width, 21811 height 21812 } of boxes) { 21813 const sx = x * pageWidth + pageX; 21814 const sy = (1 - y) * pageHeight + pageY; 21815 quadPoints[i] = quadPoints[i + 4] = sx; 21816 quadPoints[i + 1] = quadPoints[i + 3] = sy; 21817 quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth; 21818 quadPoints[i + 5] = quadPoints[i + 7] = sy - height * pageHeight; 21819 i += 8; 21820 } 21821 return quadPoints; 21822 } 21823 #serializeOutlines(rect) { 21824 return this.#highlightOutlines.serialize(rect, this.#getRotation()); 21825 } 21826 static startHighlighting(parent, isLTR, { 21827 target: textLayer, 21828 x, 21829 y 21830 }) { 21831 const { 21832 x: layerX, 21833 y: layerY, 21834 width: parentWidth, 21835 height: parentHeight 21836 } = textLayer.getBoundingClientRect(); 21837 const ac = new AbortController(); 21838 const signal = parent.combinedSignal(ac); 21839 const pointerUpCallback = e => { 21840 ac.abort(); 21841 this.#endHighlight(parent, e); 21842 }; 21843 window.addEventListener("blur", pointerUpCallback, { 21844 signal 21845 }); 21846 window.addEventListener("pointerup", pointerUpCallback, { 21847 signal 21848 }); 21849 window.addEventListener("pointerdown", stopEvent, { 21850 capture: true, 21851 passive: false, 21852 signal 21853 }); 21854 window.addEventListener("contextmenu", noContextMenu, { 21855 signal 21856 }); 21857 textLayer.addEventListener("pointermove", this.#highlightMove.bind(this, parent), { 21858 signal 21859 }); 21860 this._freeHighlight = new FreeHighlightOutliner({ 21861 x, 21862 y 21863 }, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001); 21864 ({ 21865 id: this._freeHighlightId, 21866 clipPathId: this._freeHighlightClipId 21867 } = parent.drawLayer.draw({ 21868 bbox: [0, 0, 1, 1], 21869 root: { 21870 viewBox: "0 0 1 1", 21871 fill: this._defaultColor, 21872 "fill-opacity": this._defaultOpacity 21873 }, 21874 rootClass: { 21875 highlight: true, 21876 free: true 21877 }, 21878 path: { 21879 d: this._freeHighlight.toSVGPath() 21880 } 21881 }, true, true)); 21882 } 21883 static #highlightMove(parent, event) { 21884 if (this._freeHighlight.add(event)) { 21885 parent.drawLayer.updateProperties(this._freeHighlightId, { 21886 path: { 21887 d: this._freeHighlight.toSVGPath() 21888 } 21889 }); 21890 } 21891 } 21892 static #endHighlight(parent, event) { 21893 if (!this._freeHighlight.isEmpty()) { 21894 parent.createAndAddNewEditor(event, false, { 21895 highlightId: this._freeHighlightId, 21896 highlightOutlines: this._freeHighlight.getOutlines(), 21897 clipPathId: this._freeHighlightClipId, 21898 methodOfCreation: "main_toolbar" 21899 }); 21900 } else { 21901 parent.drawLayer.remove(this._freeHighlightId); 21902 } 21903 this._freeHighlightId = -1; 21904 this._freeHighlight = null; 21905 this._freeHighlightClipId = ""; 21906 } 21907 static async deserialize(data, parent, uiManager) { 21908 let initialData = null; 21909 if (data instanceof HighlightAnnotationElement) { 21910 const { 21911 data: { 21912 quadPoints, 21913 rect, 21914 rotation, 21915 id, 21916 color, 21917 opacity, 21918 popupRef, 21919 richText, 21920 contentsObj, 21921 creationDate, 21922 modificationDate 21923 }, 21924 parent: { 21925 page: { 21926 pageNumber 21927 } 21928 } 21929 } = data; 21930 initialData = data = { 21931 annotationType: AnnotationEditorType.HIGHLIGHT, 21932 color: Array.from(color), 21933 opacity, 21934 quadPoints, 21935 boxes: null, 21936 pageIndex: pageNumber - 1, 21937 rect: rect.slice(0), 21938 rotation, 21939 annotationElementId: id, 21940 id, 21941 deleted: false, 21942 popupRef, 21943 richText, 21944 comment: contentsObj?.str || null, 21945 creationDate, 21946 modificationDate 21947 }; 21948 } else if (data instanceof InkAnnotationElement) { 21949 const { 21950 data: { 21951 inkLists, 21952 rect, 21953 rotation, 21954 id, 21955 color, 21956 borderStyle: { 21957 rawWidth: thickness 21958 }, 21959 popupRef, 21960 richText, 21961 contentsObj, 21962 creationDate, 21963 modificationDate 21964 }, 21965 parent: { 21966 page: { 21967 pageNumber 21968 } 21969 } 21970 } = data; 21971 initialData = data = { 21972 annotationType: AnnotationEditorType.HIGHLIGHT, 21973 color: Array.from(color), 21974 thickness, 21975 inkLists, 21976 boxes: null, 21977 pageIndex: pageNumber - 1, 21978 rect: rect.slice(0), 21979 rotation, 21980 annotationElementId: id, 21981 id, 21982 deleted: false, 21983 popupRef, 21984 richText, 21985 comment: contentsObj?.str || null, 21986 creationDate, 21987 modificationDate 21988 }; 21989 } 21990 const { 21991 color, 21992 quadPoints, 21993 inkLists, 21994 opacity 21995 } = data; 21996 const editor = await super.deserialize(data, parent, uiManager); 21997 editor.color = Util.makeHexColor(...color); 21998 editor.opacity = opacity || 1; 21999 if (inkLists) { 22000 editor.#thickness = data.thickness; 22001 } 22002 editor._initialData = initialData; 22003 if (data.comment) { 22004 editor.setCommentData(data); 22005 } 22006 const [pageWidth, pageHeight] = editor.pageDimensions; 22007 const [pageX, pageY] = editor.pageTranslation; 22008 if (quadPoints) { 22009 const boxes = editor.#boxes = []; 22010 for (let i = 0; i < quadPoints.length; i += 8) { 22011 boxes.push({ 22012 x: (quadPoints[i] - pageX) / pageWidth, 22013 y: 1 - (quadPoints[i + 1] - pageY) / pageHeight, 22014 width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth, 22015 height: (quadPoints[i + 1] - quadPoints[i + 5]) / pageHeight 22016 }); 22017 } 22018 editor.#createOutlines(); 22019 editor.#addToDrawLayer(); 22020 editor.rotate(editor.rotation); 22021 } else if (inkLists) { 22022 editor.#isFreeHighlight = true; 22023 const points = inkLists[0]; 22024 const point = { 22025 x: points[0] - pageX, 22026 y: pageHeight - (points[1] - pageY) 22027 }; 22028 const outliner = new FreeHighlightOutliner(point, [0, 0, pageWidth, pageHeight], 1, editor.#thickness / 2, true, 0.001); 22029 for (let i = 0, ii = points.length; i < ii; i += 2) { 22030 point.x = points[i] - pageX; 22031 point.y = pageHeight - (points[i + 1] - pageY); 22032 outliner.add(point); 22033 } 22034 const { 22035 id, 22036 clipPathId 22037 } = parent.drawLayer.draw({ 22038 bbox: [0, 0, 1, 1], 22039 root: { 22040 viewBox: "0 0 1 1", 22041 fill: editor.color, 22042 "fill-opacity": editor._defaultOpacity 22043 }, 22044 rootClass: { 22045 highlight: true, 22046 free: true 22047 }, 22048 path: { 22049 d: outliner.toSVGPath() 22050 } 22051 }, true, true); 22052 editor.#createFreeOutlines({ 22053 highlightOutlines: outliner.getOutlines(), 22054 highlightId: id, 22055 clipPathId 22056 }); 22057 editor.#addToDrawLayer(); 22058 editor.rotate(editor.parentRotation); 22059 } 22060 return editor; 22061 } 22062 serialize(isForCopying = false) { 22063 if (this.isEmpty() || isForCopying) { 22064 return null; 22065 } 22066 if (this.deleted) { 22067 return this.serializeDeleted(); 22068 } 22069 const color = AnnotationEditor._colorManager.convert(this._uiManager.getNonHCMColor(this.color)); 22070 const serialized = super.serialize(isForCopying); 22071 Object.assign(serialized, { 22072 color, 22073 opacity: this.opacity, 22074 thickness: this.#thickness, 22075 quadPoints: this.#serializeBoxes(), 22076 outlines: this.#serializeOutlines(serialized.rect) 22077 }); 22078 this.addComment(serialized); 22079 if (this.annotationElementId && !this.#hasElementChanged(serialized)) { 22080 return null; 22081 } 22082 serialized.id = this.annotationElementId; 22083 return serialized; 22084 } 22085 #hasElementChanged(serialized) { 22086 const { 22087 color 22088 } = this._initialData; 22089 return this.hasEditedComment || serialized.color.some((c, i) => c !== color[i]); 22090 } 22091 renderAnnotationElement(annotation) { 22092 if (this.deleted) { 22093 annotation.hide(); 22094 return null; 22095 } 22096 annotation.updateEdited({ 22097 rect: this.getPDFRect(), 22098 popup: this.comment 22099 }); 22100 return null; 22101 } 22102 static canCreateNewEmptyEditor() { 22103 return false; 22104 } 22105} 22106 22107;// ./src/display/editor/draw.js 22108 22109 22110 22111 22112class DrawingOptions { 22113 #svgProperties = Object.create(null); 22114 updateProperty(name, value) { 22115 this[name] = value; 22116 this.updateSVGProperty(name, value); 22117 } 22118 updateProperties(properties) { 22119 if (!properties) { 22120 return; 22121 } 22122 for (const [name, value] of Object.entries(properties)) { 22123 if (!name.startsWith("_")) { 22124 this.updateProperty(name, value); 22125 } 22126 } 22127 } 22128 updateSVGProperty(name, value) { 22129 this.#svgProperties[name] = value; 22130 } 22131 toSVGProperties() { 22132 const root = this.#svgProperties; 22133 this.#svgProperties = Object.create(null); 22134 return { 22135 root 22136 }; 22137 } 22138 reset() { 22139 this.#svgProperties = Object.create(null); 22140 } 22141 updateAll(options = this) { 22142 this.updateProperties(options); 22143 } 22144 clone() { 22145 unreachable("Not implemented"); 22146 } 22147} 22148class DrawingEditor extends AnnotationEditor { 22149 #drawOutlines = null; 22150 #mustBeCommitted; 22151 _colorPicker = null; 22152 _drawId = null; 22153 static _currentDrawId = -1; 22154 static _currentParent = null; 22155 static #currentDraw = null; 22156 static #currentDrawingAC = null; 22157 static #currentDrawingOptions = null; 22158 static _INNER_MARGIN = 3; 22159 constructor(params) { 22160 super(params); 22161 this.#mustBeCommitted = params.mustBeCommitted || false; 22162 this._addOutlines(params); 22163 } 22164 onUpdatedColor() { 22165 this._colorPicker?.update(this.color); 22166 super.onUpdatedColor(); 22167 } 22168 _addOutlines(params) { 22169 if (params.drawOutlines) { 22170 this.#createDrawOutlines(params); 22171 this.#addToDrawLayer(); 22172 } 22173 } 22174 #createDrawOutlines({ 22175 drawOutlines, 22176 drawId, 22177 drawingOptions 22178 }) { 22179 this.#drawOutlines = drawOutlines; 22180 this._drawingOptions ||= drawingOptions; 22181 if (!this.annotationElementId) { 22182 this._uiManager.a11yAlert(`pdfjs-editor-${this.editorType}-added-alert`); 22183 } 22184 if (drawId >= 0) { 22185 this._drawId = drawId; 22186 this.parent.drawLayer.finalizeDraw(drawId, drawOutlines.defaultProperties); 22187 } else { 22188 this._drawId = this.#createDrawing(drawOutlines, this.parent); 22189 } 22190 this.#updateBbox(drawOutlines.box); 22191 } 22192 #createDrawing(drawOutlines, parent) { 22193 const { 22194 id 22195 } = parent.drawLayer.draw(DrawingEditor._mergeSVGProperties(this._drawingOptions.toSVGProperties(), drawOutlines.defaultSVGProperties), false, false); 22196 return id; 22197 } 22198 static _mergeSVGProperties(p1, p2) { 22199 const p1Keys = new Set(Object.keys(p1)); 22200 for (const [key, value] of Object.entries(p2)) { 22201 if (p1Keys.has(key)) { 22202 Object.assign(p1[key], value); 22203 } else { 22204 p1[key] = value; 22205 } 22206 } 22207 return p1; 22208 } 22209 static getDefaultDrawingOptions(_options) { 22210 unreachable("Not implemented"); 22211 } 22212 static get typesMap() { 22213 unreachable("Not implemented"); 22214 } 22215 static get isDrawer() { 22216 return true; 22217 } 22218 static get supportMultipleDrawings() { 22219 return false; 22220 } 22221 static updateDefaultParams(type, value) { 22222 const propertyName = this.typesMap.get(type); 22223 if (propertyName) { 22224 this._defaultDrawingOptions.updateProperty(propertyName, value); 22225 } 22226 if (this._currentParent) { 22227 DrawingEditor.#currentDraw.updateProperty(propertyName, value); 22228 this._currentParent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties()); 22229 } 22230 } 22231 updateParams(type, value) { 22232 const propertyName = this.constructor.typesMap.get(type); 22233 if (propertyName) { 22234 this._updateProperty(type, propertyName, value); 22235 } 22236 } 22237 static get defaultPropertiesToUpdate() { 22238 const properties = []; 22239 const options = this._defaultDrawingOptions; 22240 for (const [type, name] of this.typesMap) { 22241 properties.push([type, options[name]]); 22242 } 22243 return properties; 22244 } 22245 get propertiesToUpdate() { 22246 const properties = []; 22247 const { 22248 _drawingOptions 22249 } = this; 22250 for (const [type, name] of this.constructor.typesMap) { 22251 properties.push([type, _drawingOptions[name]]); 22252 } 22253 return properties; 22254 } 22255 _updateProperty(type, name, value) { 22256 const options = this._drawingOptions; 22257 const savedValue = options[name]; 22258 const setter = val => { 22259 options.updateProperty(name, val); 22260 const bbox = this.#drawOutlines.updateProperty(name, val); 22261 if (bbox) { 22262 this.#updateBbox(bbox); 22263 } 22264 this.parent?.drawLayer.updateProperties(this._drawId, options.toSVGProperties()); 22265 if (type === this.colorType) { 22266 this.onUpdatedColor(); 22267 } 22268 }; 22269 this.addCommands({ 22270 cmd: setter.bind(this, value), 22271 undo: setter.bind(this, savedValue), 22272 post: this._uiManager.updateUI.bind(this._uiManager, this), 22273 mustExec: true, 22274 type, 22275 overwriteIfSameType: true, 22276 keepUndo: true 22277 }); 22278 } 22279 _onResizing() { 22280 this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizingSVGProperties(this.#convertToDrawSpace()), { 22281 bbox: this.#rotateBox() 22282 })); 22283 } 22284 _onResized() { 22285 this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizedSVGProperties(this.#convertToDrawSpace()), { 22286 bbox: this.#rotateBox() 22287 })); 22288 } 22289 _onTranslating(_x, _y) { 22290 this.parent?.drawLayer.updateProperties(this._drawId, { 22291 bbox: this.#rotateBox() 22292 }); 22293 } 22294 _onTranslated() { 22295 this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathTranslatedSVGProperties(this.#convertToDrawSpace(), this.parentDimensions), { 22296 bbox: this.#rotateBox() 22297 })); 22298 } 22299 _onStartDragging() { 22300 this.parent?.drawLayer.updateProperties(this._drawId, { 22301 rootClass: { 22302 moving: true 22303 } 22304 }); 22305 } 22306 _onStopDragging() { 22307 this.parent?.drawLayer.updateProperties(this._drawId, { 22308 rootClass: { 22309 moving: false 22310 } 22311 }); 22312 } 22313 commit() { 22314 super.commit(); 22315 this.disableEditMode(); 22316 this.disableEditing(); 22317 } 22318 disableEditing() { 22319 super.disableEditing(); 22320 this.div.classList.toggle("disabled", true); 22321 } 22322 enableEditing() { 22323 super.enableEditing(); 22324 this.div.classList.toggle("disabled", false); 22325 } 22326 getBaseTranslation() { 22327 return [0, 0]; 22328 } 22329 get isResizable() { 22330 return true; 22331 } 22332 onceAdded(focus) { 22333 if (!this.annotationElementId) { 22334 this.parent.addUndoableEditor(this); 22335 } 22336 this._isDraggable = true; 22337 if (this.#mustBeCommitted) { 22338 this.#mustBeCommitted = false; 22339 this.commit(); 22340 this.parent.setSelected(this); 22341 if (focus && this.isOnScreen) { 22342 this.div.focus(); 22343 } 22344 } 22345 } 22346 remove() { 22347 this.#cleanDrawLayer(); 22348 super.remove(); 22349 } 22350 rebuild() { 22351 if (!this.parent) { 22352 return; 22353 } 22354 super.rebuild(); 22355 if (this.div === null) { 22356 return; 22357 } 22358 this.#addToDrawLayer(); 22359 this.#updateBbox(this.#drawOutlines.box); 22360 if (!this.isAttachedToDOM) { 22361 this.parent.add(this); 22362 } 22363 } 22364 setParent(parent) { 22365 let mustBeSelected = false; 22366 if (this.parent && !parent) { 22367 this._uiManager.removeShouldRescale(this); 22368 this.#cleanDrawLayer(); 22369 } else if (parent) { 22370 this._uiManager.addShouldRescale(this); 22371 this.#addToDrawLayer(parent); 22372 mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor"); 22373 } 22374 super.setParent(parent); 22375 if (mustBeSelected) { 22376 this.select(); 22377 } 22378 } 22379 #cleanDrawLayer() { 22380 if (this._drawId === null || !this.parent) { 22381 return; 22382 } 22383 this.parent.drawLayer.remove(this._drawId); 22384 this._drawId = null; 22385 this._drawingOptions.reset(); 22386 } 22387 #addToDrawLayer(parent = this.parent) { 22388 if (this._drawId !== null && this.parent === parent) { 22389 return; 22390 } 22391 if (this._drawId !== null) { 22392 this.parent.drawLayer.updateParent(this._drawId, parent.drawLayer); 22393 return; 22394 } 22395 this._drawingOptions.updateAll(); 22396 this._drawId = this.#createDrawing(this.#drawOutlines, parent); 22397 } 22398 #convertToParentSpace([x, y, width, height]) { 22399 const { 22400 parentDimensions: [pW, pH], 22401 rotation 22402 } = this; 22403 switch (rotation) { 22404 case 90: 22405 return [y, 1 - x, width * (pH / pW), height * (pW / pH)]; 22406 case 180: 22407 return [1 - x, 1 - y, width, height]; 22408 case 270: 22409 return [1 - y, x, width * (pH / pW), height * (pW / pH)]; 22410 default: 22411 return [x, y, width, height]; 22412 } 22413 } 22414 #convertToDrawSpace() { 22415 const { 22416 x, 22417 y, 22418 width, 22419 height, 22420 parentDimensions: [pW, pH], 22421 rotation 22422 } = this; 22423 switch (rotation) { 22424 case 90: 22425 return [1 - y, x, width * (pW / pH), height * (pH / pW)]; 22426 case 180: 22427 return [1 - x, 1 - y, width, height]; 22428 case 270: 22429 return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; 22430 default: 22431 return [x, y, width, height]; 22432 } 22433 } 22434 #updateBbox(bbox) { 22435 [this.x, this.y, this.width, this.height] = this.#convertToParentSpace(bbox); 22436 if (this.div) { 22437 this.fixAndSetPosition(); 22438 this.setDims(); 22439 } 22440 this._onResized(); 22441 } 22442 #rotateBox() { 22443 const { 22444 x, 22445 y, 22446 width, 22447 height, 22448 rotation, 22449 parentRotation, 22450 parentDimensions: [pW, pH] 22451 } = this; 22452 switch ((rotation * 4 + parentRotation) / 90) { 22453 case 1: 22454 return [1 - y - height, x, height, width]; 22455 case 2: 22456 return [1 - x - width, 1 - y - height, width, height]; 22457 case 3: 22458 return [y, 1 - x - width, height, width]; 22459 case 4: 22460 return [x, y - width * (pW / pH), height * (pH / pW), width * (pW / pH)]; 22461 case 5: 22462 return [1 - y, x, width * (pW / pH), height * (pH / pW)]; 22463 case 6: 22464 return [1 - x - height * (pH / pW), 1 - y, height * (pH / pW), width * (pW / pH)]; 22465 case 7: 22466 return [y - width * (pW / pH), 1 - x - height * (pH / pW), width * (pW / pH), height * (pH / pW)]; 22467 case 8: 22468 return [x - width, y - height, width, height]; 22469 case 9: 22470 return [1 - y, x - width, height, width]; 22471 case 10: 22472 return [1 - x, 1 - y, width, height]; 22473 case 11: 22474 return [y - height, 1 - x, height, width]; 22475 case 12: 22476 return [x - height * (pH / pW), y, height * (pH / pW), width * (pW / pH)]; 22477 case 13: 22478 return [1 - y - width * (pW / pH), x - height * (pH / pW), width * (pW / pH), height * (pH / pW)]; 22479 case 14: 22480 return [1 - x, 1 - y - width * (pW / pH), height * (pH / pW), width * (pW / pH)]; 22481 case 15: 22482 return [y, 1 - x, width * (pW / pH), height * (pH / pW)]; 22483 default: 22484 return [x, y, width, height]; 22485 } 22486 } 22487 rotate() { 22488 if (!this.parent) { 22489 return; 22490 } 22491 this.parent.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties({ 22492 bbox: this.#rotateBox() 22493 }, this.#drawOutlines.updateRotation((this.parentRotation - this.rotation + 360) % 360))); 22494 } 22495 onScaleChanging() { 22496 if (!this.parent) { 22497 return; 22498 } 22499 this.#updateBbox(this.#drawOutlines.updateParentDimensions(this.parentDimensions, this.parent.scale)); 22500 } 22501 static onScaleChangingWhenDrawing() {} 22502 render() { 22503 if (this.div) { 22504 return this.div; 22505 } 22506 let baseX, baseY; 22507 if (this._isCopy) { 22508 baseX = this.x; 22509 baseY = this.y; 22510 } 22511 const div = super.render(); 22512 div.classList.add("draw"); 22513 const drawDiv = document.createElement("div"); 22514 div.append(drawDiv); 22515 drawDiv.setAttribute("aria-hidden", "true"); 22516 drawDiv.className = "internal"; 22517 this.setDims(); 22518 this._uiManager.addShouldRescale(this); 22519 this.disableEditing(); 22520 if (this._isCopy) { 22521 this._moveAfterPaste(baseX, baseY); 22522 } 22523 return div; 22524 } 22525 static createDrawerInstance(_x, _y, _parentWidth, _parentHeight, _rotation) { 22526 unreachable("Not implemented"); 22527 } 22528 static startDrawing(parent, uiManager, _isLTR, event) { 22529 const { 22530 target, 22531 offsetX: x, 22532 offsetY: y, 22533 pointerId, 22534 pointerType 22535 } = event; 22536 if (CurrentPointers.isInitializedAndDifferentPointerType(pointerType)) { 22537 return; 22538 } 22539 const { 22540 viewport: { 22541 rotation 22542 } 22543 } = parent; 22544 const { 22545 width: parentWidth, 22546 height: parentHeight 22547 } = target.getBoundingClientRect(); 22548 const ac = DrawingEditor.#currentDrawingAC = new AbortController(); 22549 const signal = parent.combinedSignal(ac); 22550 CurrentPointers.setPointer(pointerType, pointerId); 22551 window.addEventListener("pointerup", e => { 22552 if (CurrentPointers.isSamePointerIdOrRemove(e.pointerId)) { 22553 this._endDraw(e); 22554 } 22555 }, {
vendor: 5,835 bytes, lines 22556-22737
22556 signal 22557 }); 22558 window.addEventListener("pointercancel", e => { 22559 if (CurrentPointers.isSamePointerIdOrRemove(e.pointerId)) { 22560 this._currentParent.endDrawingSession(); 22561 } 22562 }, { 22563 signal 22564 }); 22565 window.addEventListener("pointerdown", e => { 22566 if (!CurrentPointers.isSamePointerType(e.pointerType)) { 22567 return; 22568 } 22569 CurrentPointers.initializeAndAddPointerId(e.pointerId); 22570 if (DrawingEditor.#currentDraw.isCancellable()) { 22571 DrawingEditor.#currentDraw.removeLastElement(); 22572 if (DrawingEditor.#currentDraw.isEmpty()) { 22573 this._currentParent.endDrawingSession(true); 22574 } else { 22575 this._endDraw(null); 22576 } 22577 } 22578 }, { 22579 capture: true, 22580 passive: false, 22581 signal 22582 }); 22583 window.addEventListener("contextmenu", noContextMenu, { 22584 signal 22585 }); 22586 target.addEventListener("pointermove", this._drawMove.bind(this), { 22587 signal 22588 }); 22589 target.addEventListener("touchmove", e => { 22590 if (CurrentPointers.isSameTimeStamp(e.timeStamp)) { 22591 stopEvent(e); 22592 } 22593 }, { 22594 signal 22595 }); 22596 parent.toggleDrawing(); 22597 uiManager._editorUndoBar?.hide(); 22598 if (DrawingEditor.#currentDraw) { 22599 parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.startNew(x, y, parentWidth, parentHeight, rotation)); 22600 return; 22601 } 22602 uiManager.updateUIForDefaultProperties(this); 22603 DrawingEditor.#currentDraw = this.createDrawerInstance(x, y, parentWidth, parentHeight, rotation); 22604 DrawingEditor.#currentDrawingOptions = this.getDefaultDrawingOptions(); 22605 this._currentParent = parent; 22606 ({ 22607 id: this._currentDrawId 22608 } = parent.drawLayer.draw(this._mergeSVGProperties(DrawingEditor.#currentDrawingOptions.toSVGProperties(), DrawingEditor.#currentDraw.defaultSVGProperties), true, false)); 22609 } 22610 static _drawMove(event) { 22611 CurrentPointers.isSameTimeStamp(event.timeStamp); 22612 if (!DrawingEditor.#currentDraw) { 22613 return; 22614 } 22615 const { 22616 offsetX, 22617 offsetY, 22618 pointerId 22619 } = event; 22620 if (!CurrentPointers.isSamePointerId(pointerId)) { 22621 return; 22622 } 22623 if (CurrentPointers.isUsingMultiplePointers()) { 22624 this._endDraw(event); 22625 return; 22626 } 22627 this._currentParent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.add(offsetX, offsetY)); 22628 CurrentPointers.setTimeStamp(event.timeStamp); 22629 stopEvent(event); 22630 } 22631 static _cleanup(all) { 22632 if (all) { 22633 this._currentDrawId = -1; 22634 this._currentParent = null; 22635 DrawingEditor.#currentDraw = null; 22636 DrawingEditor.#currentDrawingOptions = null; 22637 CurrentPointers.clearTimeStamp(); 22638 } 22639 if (DrawingEditor.#currentDrawingAC) { 22640 DrawingEditor.#currentDrawingAC.abort(); 22641 DrawingEditor.#currentDrawingAC = null; 22642 CurrentPointers.clearPointerIds(); 22643 } 22644 } 22645 static _endDraw(event) { 22646 const parent = this._currentParent; 22647 if (!parent) { 22648 return; 22649 } 22650 parent.toggleDrawing(true); 22651 this._cleanup(false); 22652 if (event?.target === parent.div) { 22653 parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.end(event.offsetX, event.offsetY)); 22654 } 22655 if (this.supportMultipleDrawings) { 22656 const draw = DrawingEditor.#currentDraw; 22657 const drawId = this._currentDrawId; 22658 const lastElement = draw.getLastElement(); 22659 parent.addCommands({ 22660 cmd: () => { 22661 parent.drawLayer.updateProperties(drawId, draw.setLastElement(lastElement)); 22662 }, 22663 undo: () => { 22664 parent.drawLayer.updateProperties(drawId, draw.removeLastElement()); 22665 }, 22666 mustExec: false, 22667 type: AnnotationEditorParamsType.DRAW_STEP 22668 }); 22669 return; 22670 } 22671 this.endDrawing(false); 22672 } 22673 static endDrawing(isAborted) { 22674 const parent = this._currentParent; 22675 if (!parent) { 22676 return null; 22677 } 22678 parent.toggleDrawing(true); 22679 parent.cleanUndoStack(AnnotationEditorParamsType.DRAW_STEP); 22680 if (!DrawingEditor.#currentDraw.isEmpty()) { 22681 const { 22682 pageDimensions: [pageWidth, pageHeight], 22683 scale 22684 } = parent; 22685 const editor = parent.createAndAddNewEditor({ 22686 offsetX: 0, 22687 offsetY: 0 22688 }, false, { 22689 drawId: this._currentDrawId, 22690 drawOutlines: DrawingEditor.#currentDraw.getOutlines(pageWidth * scale, pageHeight * scale, scale, this._INNER_MARGIN), 22691 drawingOptions: DrawingEditor.#currentDrawingOptions, 22692 mustBeCommitted: !isAborted 22693 }); 22694 this._cleanup(true); 22695 return editor; 22696 } 22697 parent.drawLayer.remove(this._currentDrawId); 22698 this._cleanup(true); 22699 return null; 22700 } 22701 createDrawingOptions(_data) {} 22702 static deserializeDraw(_pageX, _pageY, _pageWidth, _pageHeight, _innerWidth, _data) { 22703 unreachable("Not implemented"); 22704 } 22705 static async deserialize(data, parent, uiManager) { 22706 const { 22707 rawDims: { 22708 pageWidth, 22709 pageHeight, 22710 pageX, 22711 pageY 22712 } 22713 } = parent.viewport; 22714 const drawOutlines = this.deserializeDraw(pageX, pageY, pageWidth, pageHeight, this._INNER_MARGIN, data); 22715 const editor = await super.deserialize(data, parent, uiManager); 22716 editor.createDrawingOptions(data); 22717 editor.#createDrawOutlines({ 22718 drawOutlines 22719 }); 22720 editor.#addToDrawLayer(); 22721 editor.onScaleChanging(); 22722 editor.rotate(); 22723 return editor; 22724 } 22725 serializeDraw(isForCopying) { 22726 const [pageX, pageY] = this.pageTranslation; 22727 const [pageWidth, pageHeight] = this.pageDimensions; 22728 return this.#drawOutlines.serialize([pageX, pageY, pageWidth, pageHeight], isForCopying); 22729 } 22730 renderAnnotationElement(annotation) { 22731 annotation.updateEdited({ 22732 rect: this.getPDFRect() 22733 }); 22734 return null; 22735 } 22736 static canCreateNewEmptyEditor() { 22737 return false;
22738 } 22739} 22740 22741;// ./src/display/editor/drawers/inkdraw.js 22742
vendor: 10,994 bytes, lines 22743-23133
22743 22744class InkDrawOutliner { 22745 #last = new Float64Array(6); 22746 #line; 22747 #lines; 22748 #rotation; 22749 #thickness; 22750 #points; 22751 #lastSVGPath = ""; 22752 #lastIndex = 0; 22753 #outlines = new InkDrawOutline(); 22754 #parentWidth; 22755 #parentHeight; 22756 constructor(x, y, parentWidth, parentHeight, rotation, thickness) { 22757 this.#parentWidth = parentWidth; 22758 this.#parentHeight = parentHeight; 22759 this.#rotation = rotation; 22760 this.#thickness = thickness; 22761 [x, y] = this.#normalizePoint(x, y); 22762 const line = this.#line = [NaN, NaN, NaN, NaN, x, y]; 22763 this.#points = [x, y]; 22764 this.#lines = [{ 22765 line, 22766 points: this.#points 22767 }]; 22768 this.#last.set(line, 0); 22769 } 22770 updateProperty(name, value) { 22771 if (name === "stroke-width") { 22772 this.#thickness = value; 22773 } 22774 } 22775 #normalizePoint(x, y) { 22776 return Outline._normalizePoint(x, y, this.#parentWidth, this.#parentHeight, this.#rotation); 22777 } 22778 isEmpty() { 22779 return !this.#lines || this.#lines.length === 0; 22780 } 22781 isCancellable() { 22782 return this.#points.length <= 10; 22783 } 22784 add(x, y) { 22785 [x, y] = this.#normalizePoint(x, y); 22786 const [x1, y1, x2, y2] = this.#last.subarray(2, 6); 22787 const diffX = x - x2; 22788 const diffY = y - y2; 22789 const d = Math.hypot(this.#parentWidth * diffX, this.#parentHeight * diffY); 22790 if (d <= 2) { 22791 return null; 22792 } 22793 this.#points.push(x, y); 22794 if (isNaN(x1)) { 22795 this.#last.set([x2, y2, x, y], 2); 22796 this.#line.push(NaN, NaN, NaN, NaN, x, y); 22797 return { 22798 path: { 22799 d: this.toSVGPath() 22800 } 22801 }; 22802 } 22803 if (isNaN(this.#last[0])) { 22804 this.#line.splice(6, 6); 22805 } 22806 this.#last.set([x1, y1, x2, y2, x, y], 0); 22807 this.#line.push(...Outline.createBezierPoints(x1, y1, x2, y2, x, y)); 22808 return { 22809 path: { 22810 d: this.toSVGPath() 22811 } 22812 }; 22813 } 22814 end(x, y) { 22815 const change = this.add(x, y); 22816 if (change) { 22817 return change; 22818 } 22819 if (this.#points.length === 2) { 22820 return { 22821 path: { 22822 d: this.toSVGPath() 22823 } 22824 }; 22825 } 22826 return null; 22827 } 22828 startNew(x, y, parentWidth, parentHeight, rotation) { 22829 this.#parentWidth = parentWidth; 22830 this.#parentHeight = parentHeight; 22831 this.#rotation = rotation; 22832 [x, y] = this.#normalizePoint(x, y); 22833 const line = this.#line = [NaN, NaN, NaN, NaN, x, y]; 22834 this.#points = [x, y]; 22835 const last = this.#lines.at(-1); 22836 if (last) { 22837 last.line = new Float32Array(last.line); 22838 last.points = new Float32Array(last.points); 22839 } 22840 this.#lines.push({ 22841 line, 22842 points: this.#points 22843 }); 22844 this.#last.set(line, 0); 22845 this.#lastIndex = 0; 22846 this.toSVGPath(); 22847 return null; 22848 } 22849 getLastElement() { 22850 return this.#lines.at(-1); 22851 } 22852 setLastElement(element) { 22853 if (!this.#lines) { 22854 return this.#outlines.setLastElement(element); 22855 } 22856 this.#lines.push(element); 22857 this.#line = element.line; 22858 this.#points = element.points; 22859 this.#lastIndex = 0; 22860 return { 22861 path: { 22862 d: this.toSVGPath() 22863 } 22864 }; 22865 } 22866 removeLastElement() { 22867 if (!this.#lines) { 22868 return this.#outlines.removeLastElement(); 22869 } 22870 this.#lines.pop(); 22871 this.#lastSVGPath = ""; 22872 for (let i = 0, ii = this.#lines.length; i < ii; i++) { 22873 const { 22874 line, 22875 points 22876 } = this.#lines[i]; 22877 this.#line = line; 22878 this.#points = points; 22879 this.#lastIndex = 0; 22880 this.toSVGPath(); 22881 } 22882 return { 22883 path: { 22884 d: this.#lastSVGPath 22885 } 22886 }; 22887 } 22888 toSVGPath() { 22889 const firstX = Outline.svgRound(this.#line[4]); 22890 const firstY = Outline.svgRound(this.#line[5]); 22891 if (this.#points.length === 2) { 22892 this.#lastSVGPath = `${this.#lastSVGPath} M ${firstX} ${firstY} Z`; 22893 return this.#lastSVGPath; 22894 } 22895 if (this.#points.length <= 6) { 22896 const i = this.#lastSVGPath.lastIndexOf("M"); 22897 this.#lastSVGPath = `${this.#lastSVGPath.slice(0, i)} M ${firstX} ${firstY}`; 22898 this.#lastIndex = 6; 22899 } 22900 if (this.#points.length === 4) { 22901 const secondX = Outline.svgRound(this.#line[10]); 22902 const secondY = Outline.svgRound(this.#line[11]); 22903 this.#lastSVGPath = `${this.#lastSVGPath} L ${secondX} ${secondY}`; 22904 this.#lastIndex = 12; 22905 return this.#lastSVGPath; 22906 } 22907 const buffer = []; 22908 if (this.#lastIndex === 0) { 22909 buffer.push(`M ${firstX} ${firstY}`); 22910 this.#lastIndex = 6; 22911 } 22912 for (let i = this.#lastIndex, ii = this.#line.length; i < ii; i += 6) { 22913 const [c1x, c1y, c2x, c2y, x, y] = this.#line.slice(i, i + 6).map(Outline.svgRound); 22914 buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); 22915 } 22916 this.#lastSVGPath += buffer.join(" "); 22917 this.#lastIndex = this.#line.length; 22918 return this.#lastSVGPath; 22919 } 22920 getOutlines(parentWidth, parentHeight, scale, innerMargin) { 22921 const last = this.#lines.at(-1); 22922 last.line = new Float32Array(last.line); 22923 last.points = new Float32Array(last.points); 22924 this.#outlines.build(this.#lines, parentWidth, parentHeight, scale, this.#rotation, this.#thickness, innerMargin); 22925 this.#last = null; 22926 this.#line = null; 22927 this.#lines = null; 22928 this.#lastSVGPath = null; 22929 return this.#outlines; 22930 } 22931 get defaultSVGProperties() { 22932 return { 22933 root: { 22934 viewBox: "0 0 10000 10000" 22935 }, 22936 rootClass: { 22937 draw: true 22938 }, 22939 bbox: [0, 0, 1, 1] 22940 }; 22941 } 22942} 22943class InkDrawOutline extends Outline { 22944 #bbox; 22945 #currentRotation = 0; 22946 #innerMargin; 22947 #lines; 22948 #parentWidth; 22949 #parentHeight; 22950 #parentScale; 22951 #rotation; 22952 #thickness; 22953 build(lines, parentWidth, parentHeight, parentScale, rotation, thickness, innerMargin) { 22954 this.#parentWidth = parentWidth; 22955 this.#parentHeight = parentHeight; 22956 this.#parentScale = parentScale; 22957 this.#rotation = rotation; 22958 this.#thickness = thickness; 22959 this.#innerMargin = innerMargin ?? 0; 22960 this.#lines = lines; 22961 this.#computeBbox(); 22962 } 22963 get thickness() { 22964 return this.#thickness; 22965 } 22966 setLastElement(element) { 22967 this.#lines.push(element); 22968 return { 22969 path: { 22970 d: this.toSVGPath() 22971 } 22972 }; 22973 } 22974 removeLastElement() { 22975 this.#lines.pop(); 22976 return { 22977 path: { 22978 d: this.toSVGPath() 22979 } 22980 }; 22981 } 22982 toSVGPath() { 22983 const buffer = []; 22984 for (const { 22985 line 22986 } of this.#lines) { 22987 buffer.push(`M${Outline.svgRound(line[4])} ${Outline.svgRound(line[5])}`); 22988 if (line.length === 6) { 22989 buffer.push("Z"); 22990 continue; 22991 } 22992 if (line.length === 12 && isNaN(line[6])) { 22993 buffer.push(`L${Outline.svgRound(line[10])} ${Outline.svgRound(line[11])}`); 22994 continue; 22995 } 22996 for (let i = 6, ii = line.length; i < ii; i += 6) { 22997 const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6).map(Outline.svgRound); 22998 buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`); 22999 } 23000 } 23001 return buffer.join(""); 23002 } 23003 serialize([pageX, pageY, pageWidth, pageHeight], isForCopying) { 23004 const serializedLines = []; 23005 const serializedPoints = []; 23006 const [x, y, width, height] = this.#getBBoxWithNoMargin(); 23007 let tx, ty, sx, sy, x1, y1, x2, y2, rescaleFn; 23008 switch (this.#rotation) { 23009 case 0: 23010 rescaleFn = Outline._rescale; 23011 tx = pageX; 23012 ty = pageY + pageHeight; 23013 sx = pageWidth; 23014 sy = -pageHeight; 23015 x1 = pageX + x * pageWidth; 23016 y1 = pageY + (1 - y - height) * pageHeight; 23017 x2 = pageX + (x + width) * pageWidth; 23018 y2 = pageY + (1 - y) * pageHeight; 23019 break; 23020 case 90: 23021 rescaleFn = Outline._rescaleAndSwap; 23022 tx = pageX; 23023 ty = pageY; 23024 sx = pageWidth; 23025 sy = pageHeight; 23026 x1 = pageX + y * pageWidth; 23027 y1 = pageY + x * pageHeight; 23028 x2 = pageX + (y + height) * pageWidth; 23029 y2 = pageY + (x + width) * pageHeight; 23030 break; 23031 case 180: 23032 rescaleFn = Outline._rescale; 23033 tx = pageX + pageWidth; 23034 ty = pageY; 23035 sx = -pageWidth; 23036 sy = pageHeight; 23037 x1 = pageX + (1 - x - width) * pageWidth; 23038 y1 = pageY + y * pageHeight; 23039 x2 = pageX + (1 - x) * pageWidth; 23040 y2 = pageY + (y + height) * pageHeight; 23041 break; 23042 case 270: 23043 rescaleFn = Outline._rescaleAndSwap; 23044 tx = pageX + pageWidth; 23045 ty = pageY + pageHeight; 23046 sx = -pageWidth; 23047 sy = -pageHeight; 23048 x1 = pageX + (1 - y - height) * pageWidth; 23049 y1 = pageY + (1 - x - width) * pageHeight; 23050 x2 = pageX + (1 - y) * pageWidth; 23051 y2 = pageY + (1 - x) * pageHeight; 23052 break; 23053 } 23054 for (const { 23055 line, 23056 points 23057 } of this.#lines) { 23058 serializedLines.push(rescaleFn(line, tx, ty, sx, sy, isForCopying ? new Array(line.length) : null)); 23059 serializedPoints.push(rescaleFn(points, tx, ty, sx, sy, isForCopying ? new Array(points.length) : null)); 23060 } 23061 return { 23062 lines: serializedLines, 23063 points: serializedPoints, 23064 rect: [x1, y1, x2, y2] 23065 }; 23066 } 23067 static deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, { 23068 paths: { 23069 lines, 23070 points 23071 }, 23072 rotation, 23073 thickness 23074 }) { 23075 const newLines = []; 23076 let tx, ty, sx, sy, rescaleFn; 23077 switch (rotation) { 23078 case 0: 23079 rescaleFn = Outline._rescale; 23080 tx = -pageX / pageWidth; 23081 ty = pageY / pageHeight + 1; 23082 sx = 1 / pageWidth; 23083 sy = -1 / pageHeight; 23084 break; 23085 case 90: 23086 rescaleFn = Outline._rescaleAndSwap; 23087 tx = -pageY / pageHeight; 23088 ty = -pageX / pageWidth; 23089 sx = 1 / pageHeight; 23090 sy = 1 / pageWidth; 23091 break; 23092 case 180: 23093 rescaleFn = Outline._rescale; 23094 tx = pageX / pageWidth + 1; 23095 ty = -pageY / pageHeight; 23096 sx = -1 / pageWidth; 23097 sy = 1 / pageHeight; 23098 break; 23099 case 270: 23100 rescaleFn = Outline._rescaleAndSwap; 23101 tx = pageY / pageHeight + 1; 23102 ty = pageX / pageWidth + 1; 23103 sx = -1 / pageHeight; 23104 sy = -1 / pageWidth; 23105 break; 23106 } 23107 if (!lines) { 23108 lines = []; 23109 for (const point of points) { 23110 const len = point.length; 23111 if (len === 2) { 23112 lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1]])); 23113 continue; 23114 } 23115 if (len === 4) { 23116 lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1], NaN, NaN, NaN, NaN, point[2], point[3]])); 23117 continue; 23118 } 23119 const line = new Float32Array(3 * (len - 2)); 23120 lines.push(line); 23121 let [x1, y1, x2, y2] = point.subarray(0, 4); 23122 line.set([NaN, NaN, NaN, NaN, x1, y1], 0); 23123 for (let i = 4; i < len; i += 2) { 23124 const x = point[i]; 23125 const y = point[i + 1]; 23126 line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3); 23127 [x1, y1, x2, y2] = [x2, y2, x, y]; 23128 } 23129 } 23130 } 23131 for (let i = 0, ii = lines.length; i < ii; i++) { 23132 newLines.push({ 23133 line: rescaleFn(lines[i].map(
vendor: 1,940 bytes, lines 23133-23185
23133x => x ?? NaN), tx, ty, sx, sy), 23134 points: rescaleFn(points[i].map(x => x ?? NaN), tx, ty, sx, sy) 23135 }); 23136 } 23137 const outlines = new this.prototype.constructor(); 23138 outlines.build(newLines, pageWidth, pageHeight, 1, rotation, thickness, innerMargin); 23139 return outlines; 23140 } 23141 #getMarginComponents(thickness = this.#thickness) { 23142 const margin = this.#innerMargin + thickness / 2 * this.#parentScale; 23143 return this.#rotation % 180 === 0 ? [margin / this.#parentWidth, margin / this.#parentHeight] : [margin / this.#parentHeight, margin / this.#parentWidth]; 23144 } 23145 #getBBoxWithNoMargin() { 23146 const [x, y, width, height] = this.#bbox; 23147 const [marginX, marginY] = this.#getMarginComponents(0); 23148 return [x + marginX, y + marginY, width - 2 * marginX, height - 2 * marginY]; 23149 } 23150 #computeBbox() { 23151 const bbox = this.#bbox = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]); 23152 for (const { 23153 line 23154 } of this.#lines) { 23155 if (line.length <= 12) { 23156 for (let i = 4, ii = line.length; i < ii; i += 6) { 23157 Util.pointBoundingBox(line[i], line[i + 1], bbox); 23158 } 23159 continue; 23160 } 23161 let lastX = line[4], 23162 lastY = line[5]; 23163 for (let i = 6, ii = line.length; i < ii; i += 6) { 23164 const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6); 23165 Util.bezierBoundingBox(lastX, lastY, c1x, c1y, c2x, c2y, x, y, bbox); 23166 lastX = x; 23167 lastY = y; 23168 } 23169 } 23170 const [marginX, marginY] = this.#getMarginComponents(); 23171 bbox[0] = MathClamp(bbox[0] - marginX, 0, 1); 23172 bbox[1] = MathClamp(bbox[1] - marginY, 0, 1); 23173 bbox[2] = MathClamp(bbox[2] + marginX, 0, 1); 23174 bbox[3] = MathClamp(bbox[3] + marginY, 0, 1); 23175 bbox[2] -= bbox[0]; 23176 bbox[3] -= bbox[1]; 23177 } 23178 get box() { 23179 return this.#bbox; 23180 } 23181 updateProperty(name, value) { 23182 if (name === "stroke-width") { 23183 return this.#updateThickness(value); 23184 } 23185 return
vendor: 4,014 bytes, lines 23185-23309
23185 null; 23186 } 23187 #updateThickness(thickness) { 23188 const [oldMarginX, oldMarginY] = this.#getMarginComponents(); 23189 this.#thickness = thickness; 23190 const [newMarginX, newMarginY] = this.#getMarginComponents(); 23191 const [diffMarginX, diffMarginY] = [newMarginX - oldMarginX, newMarginY - oldMarginY]; 23192 const bbox = this.#bbox; 23193 bbox[0] -= diffMarginX; 23194 bbox[1] -= diffMarginY; 23195 bbox[2] += 2 * diffMarginX; 23196 bbox[3] += 2 * diffMarginY; 23197 return bbox; 23198 } 23199 updateParentDimensions([width, height], scale) { 23200 const [oldMarginX, oldMarginY] = this.#getMarginComponents(); 23201 this.#parentWidth = width; 23202 this.#parentHeight = height; 23203 this.#parentScale = scale; 23204 const [newMarginX, newMarginY] = this.#getMarginComponents(); 23205 const diffMarginX = newMarginX - oldMarginX; 23206 const diffMarginY = newMarginY - oldMarginY; 23207 const bbox = this.#bbox; 23208 bbox[0] -= diffMarginX; 23209 bbox[1] -= diffMarginY; 23210 bbox[2] += 2 * diffMarginX; 23211 bbox[3] += 2 * diffMarginY; 23212 return bbox; 23213 } 23214 updateRotation(rotation) { 23215 this.#currentRotation = rotation; 23216 return { 23217 path: { 23218 transform: this.rotationTransform 23219 } 23220 }; 23221 } 23222 get viewBox() { 23223 return this.#bbox.map(Outline.svgRound).join(" "); 23224 } 23225 get defaultProperties() { 23226 const [x, y] = this.#bbox; 23227 return { 23228 root: { 23229 viewBox: this.viewBox 23230 }, 23231 path: { 23232 "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}` 23233 } 23234 }; 23235 } 23236 get rotationTransform() { 23237 const [,, width, height] = this.#bbox; 23238 let a = 0, 23239 b = 0, 23240 c = 0, 23241 d = 0, 23242 e = 0, 23243 f = 0; 23244 switch (this.#currentRotation) { 23245 case 90: 23246 b = height / width; 23247 c = -width / height; 23248 e = width; 23249 break; 23250 case 180: 23251 a = -1; 23252 d = -1; 23253 e = width; 23254 f = height; 23255 break; 23256 case 270: 23257 b = -height / width; 23258 c = width / height; 23259 f = height; 23260 break; 23261 default: 23262 return ""; 23263 } 23264 return `matrix(${a} ${b} ${c} ${d} ${Outline.svgRound(e)} ${Outline.svgRound(f)})`; 23265 } 23266 getPathResizingSVGProperties([newX, newY, newWidth, newHeight]) { 23267 const [marginX, marginY] = this.#getMarginComponents(); 23268 const [x, y, width, height] = this.#bbox; 23269 if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) { 23270 const tx = newX + newWidth / 2 - (x + width / 2); 23271 const ty = newY + newHeight / 2 - (y + height / 2); 23272 return { 23273 path: { 23274 "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, 23275 transform: `${this.rotationTransform} translate(${tx} ${ty})` 23276 } 23277 }; 23278 } 23279 const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); 23280 const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); 23281 const s2x = width / newWidth; 23282 const s2y = height / newHeight; 23283 return { 23284 path: { 23285 "transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`, 23286 transform: `${this.rotationTransform} scale(${s2x} ${s2y}) ` + `translate(${Outline.svgRound(marginX)} ${Outline.svgRound(marginY)}) scale(${s1x} ${s1y}) ` + `translate(${Outline.svgRound(-marginX)} ${Outline.svgRound(-marginY)})` 23287 } 23288 }; 23289 } 23290 getPathResizedSVGProperties([newX, newY, newWidth, newHeight]) { 23291 const [marginX, marginY] = this.#getMarginComponents(); 23292 const bbox = this.#bbox; 23293 const [x, y, width, height] = bbox; 23294 bbox[0] = newX; 23295 bbox[1] = newY; 23296 bbox[2] = newWidth; 23297 bbox[3] = newHeight; 23298 if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) { 23299 const tx = newX + newWidth / 2 - (x + width / 2); 23300 const ty = newY + newHeight / 2 - (y + height / 2); 23301 for (const { 23302 line, 23303 points 23304 } of this.#lines) { 23305 Outline._translate(line, tx, ty, line); 23306 Outline._translate(points, tx, ty, points); 23307 } 23308 return { 23309 root: {
vendor: 2,598 bytes, lines 23310-23400
23310 viewBox: this.viewBox 23311 }, 23312 path: { 23313 "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, 23314 transform: this.rotationTransform || null, 23315 d: this.toSVGPath() 23316 } 23317 }; 23318 } 23319 const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX); 23320 const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY); 23321 const tx = -s1x * (x + marginX) + newX + marginX; 23322 const ty = -s1y * (y + marginY) + newY + marginY; 23323 if (s1x !== 1 || s1y !== 1 || tx !== 0 || ty !== 0) { 23324 for (const { 23325 line, 23326 points 23327 } of this.#lines) { 23328 Outline._rescale(line, tx, ty, s1x, s1y, line); 23329 Outline._rescale(points, tx, ty, s1x, s1y, points); 23330 } 23331 } 23332 return { 23333 root: { 23334 viewBox: this.viewBox 23335 }, 23336 path: { 23337 "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`, 23338 transform: this.rotationTransform || null, 23339 d: this.toSVGPath() 23340 } 23341 }; 23342 } 23343 getPathTranslatedSVGProperties([newX, newY], parentDimensions) { 23344 const [newParentWidth, newParentHeight] = parentDimensions; 23345 const bbox = this.#bbox; 23346 const tx = newX - bbox[0]; 23347 const ty = newY - bbox[1]; 23348 if (this.#parentWidth === newParentWidth && this.#parentHeight === newParentHeight) { 23349 for (const { 23350 line, 23351 points 23352 } of this.#lines) { 23353 Outline._translate(line, tx, ty, line); 23354 Outline._translate(points, tx, ty, points); 23355 } 23356 } else { 23357 const sx = this.#parentWidth / newParentWidth; 23358 const sy = this.#parentHeight / newParentHeight; 23359 this.#parentWidth = newParentWidth; 23360 this.#parentHeight = newParentHeight; 23361 for (const { 23362 line, 23363 points 23364 } of this.#lines) { 23365 Outline._rescale(line, tx, ty, sx, sy, line); 23366 Outline._rescale(points, tx, ty, sx, sy, points); 23367 } 23368 bbox[2] *= sx; 23369 bbox[3] *= sy; 23370 } 23371 bbox[0] = newX; 23372 bbox[1] = newY; 23373 return { 23374 root: { 23375 viewBox: this.viewBox 23376 }, 23377 path: { 23378 d: this.toSVGPath(), 23379 "transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}` 23380 } 23381 }; 23382 } 23383 get defaultSVGProperties() { 23384 const bbox = this.#bbox; 23385 return { 23386 root: { 23387 viewBox: this.viewBox 23388 }, 23389 rootClass: { 23390 draw: true 23391 }, 23392 path: { 23393 d: this.toSVGPath(), 23394 "transform-origin": `${Outline.svgRound(bbox[0])} ${Outline.svgRound(bbox[1])}`, 23395 transform: this.rotationTransform || null 23396 }, 23397 bbox 23398 }; 23399 } 23400}
vendor: 6,555 bytes, lines 23401-23651
23401 23402;// ./src/display/editor/ink.js 23403 23404 23405 23406 23407 23408 23409class InkDrawingOptions extends DrawingOptions { 23410 constructor(viewerParameters) { 23411 super(); 23412 this._viewParameters = viewerParameters; 23413 super.updateProperties({ 23414 fill: "none", 23415 stroke: AnnotationEditor._defaultLineColor, 23416 "stroke-opacity": 1, 23417 "stroke-width": 1, 23418 "stroke-linecap": "round", 23419 "stroke-linejoin": "round", 23420 "stroke-miterlimit": 10 23421 }); 23422 } 23423 updateSVGProperty(name, value) { 23424 if (name === "stroke-width") { 23425 value ??= this["stroke-width"]; 23426 value *= this._viewParameters.realScale; 23427 } 23428 super.updateSVGProperty(name, value); 23429 } 23430 clone() { 23431 const clone = new InkDrawingOptions(this._viewParameters); 23432 clone.updateAll(this); 23433 return clone; 23434 } 23435} 23436class InkEditor extends DrawingEditor { 23437 static _type = "ink"; 23438 static _editorType = AnnotationEditorType.INK; 23439 static _defaultDrawingOptions = null; 23440 constructor(params) { 23441 super({ 23442 ...params, 23443 name: "inkEditor" 23444 }); 23445 this._willKeepAspectRatio = true; 23446 this.defaultL10nId = "pdfjs-editor-ink-editor"; 23447 } 23448 static initialize(l10n, uiManager) { 23449 AnnotationEditor.initialize(l10n, uiManager); 23450 this._defaultDrawingOptions = new InkDrawingOptions(uiManager.viewParameters); 23451 } 23452 static getDefaultDrawingOptions(options) { 23453 const clone = this._defaultDrawingOptions.clone(); 23454 clone.updateProperties(options); 23455 return clone; 23456 } 23457 static get supportMultipleDrawings() { 23458 return true; 23459 } 23460 static get typesMap() { 23461 return shadow(this, "typesMap", new Map([[AnnotationEditorParamsType.INK_THICKNESS, "stroke-width"], [AnnotationEditorParamsType.INK_COLOR, "stroke"], [AnnotationEditorParamsType.INK_OPACITY, "stroke-opacity"]])); 23462 } 23463 static createDrawerInstance(x, y, parentWidth, parentHeight, rotation) { 23464 return new InkDrawOutliner(x, y, parentWidth, parentHeight, rotation, this._defaultDrawingOptions["stroke-width"]); 23465 } 23466 static deserializeDraw(pageX, pageY, pageWidth, pageHeight, innerMargin, data) { 23467 return InkDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); 23468 } 23469 static async deserialize(data, parent, uiManager) { 23470 let initialData = null; 23471 if (data instanceof InkAnnotationElement) { 23472 const { 23473 data: { 23474 inkLists, 23475 rect, 23476 rotation, 23477 id, 23478 color, 23479 opacity, 23480 borderStyle: { 23481 rawWidth: thickness 23482 }, 23483 popupRef, 23484 richText, 23485 contentsObj, 23486 creationDate, 23487 modificationDate 23488 }, 23489 parent: { 23490 page: { 23491 pageNumber 23492 } 23493 } 23494 } = data; 23495 initialData = data = { 23496 annotationType: AnnotationEditorType.INK, 23497 color: Array.from(color), 23498 thickness, 23499 opacity, 23500 paths: { 23501 points: inkLists 23502 }, 23503 boxes: null, 23504 pageIndex: pageNumber - 1, 23505 rect: rect.slice(0), 23506 rotation, 23507 annotationElementId: id, 23508 id, 23509 deleted: false, 23510 popupRef, 23511 richText, 23512 comment: contentsObj?.str || null, 23513 creationDate, 23514 modificationDate 23515 }; 23516 } 23517 const editor = await super.deserialize(data, parent, uiManager); 23518 editor._initialData = initialData; 23519 if (data.comment) { 23520 editor.setCommentData(data); 23521 } 23522 return editor; 23523 } 23524 get toolbarButtons() { 23525 this._colorPicker ||= new BasicColorPicker(this); 23526 return [["colorPicker", this._colorPicker]]; 23527 } 23528 get colorType() { 23529 return AnnotationEditorParamsType.INK_COLOR; 23530 } 23531 get color() { 23532 return this._drawingOptions.stroke; 23533 } 23534 get opacity() { 23535 return this._drawingOptions["stroke-opacity"]; 23536 } 23537 onScaleChanging() { 23538 if (!this.parent) { 23539 return; 23540 } 23541 super.onScaleChanging(); 23542 const { 23543 _drawId, 23544 _drawingOptions, 23545 parent 23546 } = this; 23547 _drawingOptions.updateSVGProperty("stroke-width"); 23548 parent.drawLayer.updateProperties(_drawId, _drawingOptions.toSVGProperties()); 23549 } 23550 static onScaleChangingWhenDrawing() { 23551 const parent = this._currentParent; 23552 if (!parent) { 23553 return; 23554 } 23555 super.onScaleChangingWhenDrawing(); 23556 this._defaultDrawingOptions.updateSVGProperty("stroke-width"); 23557 parent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties()); 23558 } 23559 createDrawingOptions({ 23560 color, 23561 thickness, 23562 opacity 23563 }) { 23564 this._drawingOptions = InkEditor.getDefaultDrawingOptions({ 23565 stroke: Util.makeHexColor(...color), 23566 "stroke-width": thickness, 23567 "stroke-opacity": opacity 23568 }); 23569 } 23570 serialize(isForCopying = false) { 23571 if (this.isEmpty()) { 23572 return null; 23573 } 23574 if (this.deleted) { 23575 return this.serializeDeleted(); 23576 } 23577 const { 23578 lines, 23579 points 23580 } = this.serializeDraw(isForCopying); 23581 const { 23582 _drawingOptions: { 23583 stroke, 23584 "stroke-opacity": opacity, 23585 "stroke-width": thickness 23586 } 23587 } = this; 23588 const serialized = Object.assign(super.serialize(isForCopying), { 23589 color: AnnotationEditor._colorManager.convert(stroke), 23590 opacity, 23591 thickness, 23592 paths: { 23593 lines, 23594 points 23595 } 23596 }); 23597 this.addComment(serialized); 23598 if (isForCopying) { 23599 serialized.isCopy = true; 23600 return serialized; 23601 } 23602 if (this.annotationElementId && !this.#hasElementChanged(serialized)) { 23603 return null; 23604 } 23605 serialized.id = this.annotationElementId; 23606 return serialized; 23607 } 23608 #hasElementChanged(serialized) { 23609 const { 23610 color, 23611 thickness, 23612 opacity, 23613 pageIndex 23614 } = this._initialData; 23615 return this.hasEditedComment || this._hasBeenMoved || this._hasBeenResized || serialized.color.some((c, i) => c !== color[i]) || serialized.thickness !== thickness || serialized.opacity !== opacity || serialized.pageIndex !== pageIndex; 23616 } 23617 renderAnnotationElement(annotation) { 23618 if (this.deleted) { 23619 annotation.hide(); 23620 return null; 23621 } 23622 const { 23623 points, 23624 rect 23625 } = this.serializeDraw(false); 23626 annotation.updateEdited({ 23627 rect, 23628 thickness: this._drawingOptions["stroke-width"], 23629 points, 23630 popup: this.comment 23631 }); 23632 return null; 23633 } 23634} 23635 23636;// ./src/display/editor/drawers/contour.js 23637 23638class ContourDrawOutline extends InkDrawOutline { 23639 toSVGPath() { 23640 let path = super.toSVGPath(); 23641 if (!path.endsWith("Z")) { 23642 path += "Z"; 23643 } 23644 return path; 23645 } 23646} 23647 23648;// ./src/display/editor/drawers/signaturedraw.js 23649 23650 23651
vendor: 4,991 bytes, lines 23652-23824
23652 23653const BASE_HEADER_LENGTH = 8; 23654const POINTS_PROPERTIES_NUMBER = 3; 23655class SignatureExtractor { 23656 static #PARAMETERS = { 23657 maxDim: 512, 23658 sigmaSFactor: 0.02, 23659 sigmaR: 25, 23660 kernelSize: 16 23661 }; 23662 static #neighborIndexToId(i0, j0, i, j) { 23663 i -= i0; 23664 j -= j0; 23665 if (i === 0) { 23666 return j > 0 ? 0 : 4; 23667 } 23668 if (i === 1) { 23669 return j + 6; 23670 } 23671 return 2 - j; 23672 } 23673 static #neighborIdToIndex = new Int32Array([0, 1, -1, 1, -1, 0, -1, -1, 0, -1, 1, -1, 1, 0, 1, 1]); 23674 static #clockwiseNonZero(buf, width, i0, j0, i, j, offset) { 23675 const id = this.#neighborIndexToId(i0, j0, i, j); 23676 for (let k = 0; k < 8; k++) { 23677 const kk = (-k + id - offset + 16) % 8; 23678 const shiftI = this.#neighborIdToIndex[2 * kk]; 23679 const shiftJ = this.#neighborIdToIndex[2 * kk + 1]; 23680 if (buf[(i0 + shiftI) * width + (j0 + shiftJ)] !== 0) { 23681 return kk; 23682 } 23683 } 23684 return -1; 23685 } 23686 static #counterClockwiseNonZero(buf, width, i0, j0, i, j, offset) { 23687 const id = this.#neighborIndexToId(i0, j0, i, j); 23688 for (let k = 0; k < 8; k++) { 23689 const kk = (k + id + offset + 16) % 8; 23690 const shiftI = this.#neighborIdToIndex[2 * kk]; 23691 const shiftJ = this.#neighborIdToIndex[2 * kk + 1]; 23692 if (buf[(i0 + shiftI) * width + (j0 + shiftJ)] !== 0) { 23693 return kk; 23694 } 23695 } 23696 return -1; 23697 } 23698 static #findContours(buf, width, height, threshold) { 23699 const N = buf.length; 23700 const types = new Int32Array(N); 23701 for (let i = 0; i < N; i++) { 23702 types[i] = buf[i] <= threshold ? 1 : 0; 23703 } 23704 for (let i = 1; i < height - 1; i++) { 23705 types[i * width] = types[i * width + width - 1] = 0; 23706 } 23707 for (let i = 0; i < width; i++) { 23708 types[i] = types[width * height - 1 - i] = 0; 23709 } 23710 let nbd = 1; 23711 let lnbd; 23712 const contours = []; 23713 for (let i = 1; i < height - 1; i++) { 23714 lnbd = 1; 23715 for (let j = 1; j < width - 1; j++) { 23716 const ij = i * width + j; 23717 const pix = types[ij]; 23718 if (pix === 0) { 23719 continue; 23720 } 23721 let i2 = i; 23722 let j2 = j; 23723 if (pix === 1 && types[ij - 1] === 0) { 23724 nbd += 1; 23725 j2 -= 1; 23726 } else if (pix >= 1 && types[ij + 1] === 0) { 23727 nbd += 1; 23728 j2 += 1; 23729 if (pix > 1) { 23730 lnbd = pix; 23731 } 23732 } else { 23733 if (pix !== 1) { 23734 lnbd = Math.abs(pix); 23735 } 23736 continue; 23737 } 23738 const points = [j, i]; 23739 const isHole = j2 === j + 1; 23740 const contour = { 23741 isHole, 23742 points, 23743 id: nbd, 23744 parent: 0 23745 }; 23746 contours.push(contour); 23747 let contour0; 23748 for (const c of contours) { 23749 if (c.id === lnbd) { 23750 contour0 = c; 23751 break; 23752 } 23753 } 23754 if (!contour0) { 23755 contour.parent = isHole ? lnbd : 0; 23756 } else if (contour0.isHole) { 23757 contour.parent = isHole ? contour0.parent : lnbd; 23758 } else { 23759 contour.parent = isHole ? lnbd : contour0.parent; 23760 } 23761 const k = this.#clockwiseNonZero(types, width, i, j, i2, j2, 0); 23762 if (k === -1) { 23763 types[ij] = -nbd; 23764 if (types[ij] !== 1) { 23765 lnbd = Math.abs(types[ij]); 23766 } 23767 continue; 23768 } 23769 let shiftI = this.#neighborIdToIndex[2 * k]; 23770 let shiftJ = this.#neighborIdToIndex[2 * k + 1]; 23771 const i1 = i + shiftI; 23772 const j1 = j + shiftJ; 23773 i2 = i1; 23774 j2 = j1; 23775 let i3 = i; 23776 let j3 = j; 23777 while (true) { 23778 const kk = this.#counterClockwiseNonZero(types, width, i3, j3, i2, j2, 1); 23779 shiftI = this.#neighborIdToIndex[2 * kk]; 23780 shiftJ = this.#neighborIdToIndex[2 * kk + 1]; 23781 const i4 = i3 + shiftI; 23782 const j4 = j3 + shiftJ; 23783 points.push(j4, i4); 23784 const ij3 = i3 * width + j3; 23785 if (types[ij3 + 1] === 0) { 23786 types[ij3] = -nbd; 23787 } else if (types[ij3] === 1) { 23788 types[ij3] = nbd; 23789 } 23790 if (i4 === i && j4 === j && i3 === i1 && j3 === j1) { 23791 if (types[ij] !== 1) { 23792 lnbd = Math.abs(types[ij]); 23793 } 23794 break; 23795 } else { 23796 i2 = i3; 23797 j2 = j3; 23798 i3 = i4; 23799 j3 = j4; 23800 } 23801 } 23802 } 23803 } 23804 return contours; 23805 } 23806 static #douglasPeuckerHelper(points, start, end, output) { 23807 if (end - start <= 4) { 23808 for (let i = start; i < end - 2; i += 2) { 23809 output.push(points[i], points[i + 1]); 23810 } 23811 return; 23812 } 23813 const ax = points[start]; 23814 const ay = points[start + 1]; 23815 const abx = points[end - 4] - ax; 23816 const aby = points[end - 3] - ay; 23817 const dist = Math.hypot(abx, aby); 23818 const nabx = abx / dist; 23819 const naby = aby / dist; 23820 const aa = nabx * ay - naby * ax; 23821 const m = aby / abx; 23822 const invS = 1 / dist; 23823 const phi = Math.atan(m); 23824 const cosPhi = Math.cos(phi);
vendor: 12,104 bytes, lines 23825-24191
23825 const sinPhi = Math.sin(phi); 23826 const tmax = invS * (Math.abs(cosPhi) + Math.abs(sinPhi)); 23827 const poly = invS * (1 - tmax + tmax ** 2); 23828 const partialPhi = Math.max(Math.atan(Math.abs(sinPhi + cosPhi) * poly), Math.atan(Math.abs(sinPhi - cosPhi) * poly)); 23829 let dmax = 0; 23830 let index = start; 23831 for (let i = start + 2; i < end - 2; i += 2) { 23832 const d = Math.abs(aa - nabx * points[i + 1] + naby * points[i]); 23833 if (d > dmax) { 23834 index = i; 23835 dmax = d; 23836 } 23837 } 23838 if (dmax > (dist * partialPhi) ** 2) { 23839 this.#douglasPeuckerHelper(points, start, index + 2, output); 23840 this.#douglasPeuckerHelper(points, index, end, output); 23841 } else { 23842 output.push(ax, ay); 23843 } 23844 } 23845 static #douglasPeucker(points) { 23846 const output = []; 23847 const len = points.length; 23848 this.#douglasPeuckerHelper(points, 0, len, output); 23849 output.push(points[len - 2], points[len - 1]); 23850 return output.length <= 4 ? null : output; 23851 } 23852 static #bilateralFilter(buf, width, height, sigmaS, sigmaR, kernelSize) { 23853 const kernel = new Float32Array(kernelSize ** 2); 23854 const sigmaS2 = -2 * sigmaS ** 2; 23855 const halfSize = kernelSize >> 1; 23856 for (let i = 0; i < kernelSize; i++) { 23857 const x = (i - halfSize) ** 2; 23858 for (let j = 0; j < kernelSize; j++) { 23859 kernel[i * kernelSize + j] = Math.exp((x + (j - halfSize) ** 2) / sigmaS2); 23860 } 23861 } 23862 const rangeValues = new Float32Array(256); 23863 const sigmaR2 = -2 * sigmaR ** 2; 23864 for (let i = 0; i < 256; i++) { 23865 rangeValues[i] = Math.exp(i ** 2 / sigmaR2); 23866 } 23867 const N = buf.length; 23868 const out = new Uint8Array(N); 23869 const histogram = new Uint32Array(256); 23870 for (let i = 0; i < height; i++) { 23871 for (let j = 0; j < width; j++) { 23872 const ij = i * width + j; 23873 const center = buf[ij]; 23874 let sum = 0; 23875 let norm = 0; 23876 for (let k = 0; k < kernelSize; k++) { 23877 const y = i + k - halfSize; 23878 if (y < 0 || y >= height) { 23879 continue; 23880 } 23881 for (let l = 0; l < kernelSize; l++) { 23882 const x = j + l - halfSize; 23883 if (x < 0 || x >= width) { 23884 continue; 23885 } 23886 const neighbour = buf[y * width + x]; 23887 const w = kernel[k * kernelSize + l] * rangeValues[Math.abs(neighbour - center)]; 23888 sum += neighbour * w; 23889 norm += w; 23890 } 23891 } 23892 const pix = out[ij] = Math.round(sum / norm); 23893 histogram[pix]++; 23894 } 23895 } 23896 return [out, histogram]; 23897 } 23898 static #getHistogram(buf) { 23899 const histogram = new Uint32Array(256); 23900 for (const g of buf) { 23901 histogram[g]++; 23902 } 23903 return histogram; 23904 } 23905 static #toUint8(buf) { 23906 const N = buf.length; 23907 const out = new Uint8ClampedArray(N >> 2); 23908 let max = -Infinity; 23909 let min = Infinity; 23910 for (let i = 0, ii = out.length; i < ii; i++) { 23911 const pix = out[i] = buf[i << 2]; 23912 max = Math.max(max, pix); 23913 min = Math.min(min, pix); 23914 } 23915 const ratio = 255 / (max - min); 23916 for (let i = 0, ii = out.length; i < ii; i++) { 23917 out[i] = (out[i] - min) * ratio; 23918 } 23919 return out; 23920 } 23921 static #guessThreshold(histogram) { 23922 let i; 23923 let M = -Infinity; 23924 let L = -Infinity; 23925 const min = histogram.findIndex(v => v !== 0); 23926 let pos = min; 23927 let spos = min; 23928 for (i = min; i < 256; i++) { 23929 const v = histogram[i]; 23930 if (v > M) { 23931 if (i - pos > L) { 23932 L = i - pos; 23933 spos = i - 1; 23934 } 23935 M = v; 23936 pos = i; 23937 } 23938 } 23939 for (i = spos - 1; i >= 0; i--) { 23940 if (histogram[i] > histogram[i + 1]) { 23941 break; 23942 } 23943 } 23944 return i; 23945 } 23946 static #getGrayPixels(bitmap) { 23947 const originalBitmap = bitmap; 23948 const { 23949 width, 23950 height 23951 } = bitmap; 23952 const { 23953 maxDim 23954 } = this.#PARAMETERS; 23955 let newWidth = width; 23956 let newHeight = height; 23957 if (width > maxDim || height > maxDim) { 23958 let prevWidth = width; 23959 let prevHeight = height; 23960 let steps = Math.log2(Math.max(width, height) / maxDim); 23961 const isteps = Math.floor(steps); 23962 steps = steps === isteps ? isteps - 1 : isteps; 23963 for (let i = 0; i < steps; i++) { 23964 newWidth = Math.ceil(prevWidth / 2); 23965 newHeight = Math.ceil(prevHeight / 2); 23966 const offscreen = new OffscreenCanvas(newWidth, newHeight); 23967 const ctx = offscreen.getContext("2d"); 23968 ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); 23969 prevWidth = newWidth; 23970 prevHeight = newHeight; 23971 if (bitmap !== originalBitmap) { 23972 bitmap.close(); 23973 } 23974 bitmap = offscreen.transferToImageBitmap(); 23975 } 23976 const ratio = Math.min(maxDim / newWidth, maxDim / newHeight); 23977 newWidth = Math.round(newWidth * ratio); 23978 newHeight = Math.round(newHeight * ratio); 23979 } 23980 const offscreen = new OffscreenCanvas(newWidth, newHeight); 23981 const ctx = offscreen.getContext("2d", { 23982 willReadFrequently: true 23983 }); 23984 ctx.fillStyle = "white"; 23985 ctx.fillRect(0, 0, newWidth, newHeight); 23986 ctx.filter = "grayscale(1)"; 23987 ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, newWidth, newHeight); 23988 const grayImage = ctx.getImageData(0, 0, newWidth, newHeight).data; 23989 const uint8Buf = this.#toUint8(grayImage); 23990 return [uint8Buf, newWidth, newHeight]; 23991 } 23992 static extractContoursFromText(text, { 23993 fontFamily, 23994 fontStyle, 23995 fontWeight 23996 }, pageWidth, pageHeight, rotation, innerMargin) { 23997 let canvas = new OffscreenCanvas(1, 1); 23998 let ctx = canvas.getContext("2d", { 23999 alpha: false 24000 }); 24001 const fontSize = 200; 24002 const font = ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; 24003 const { 24004 actualBoundingBoxLeft, 24005 actualBoundingBoxRight, 24006 actualBoundingBoxAscent, 24007 actualBoundingBoxDescent, 24008 fontBoundingBoxAscent, 24009 fontBoundingBoxDescent, 24010 width 24011 } = ctx.measureText(text); 24012 const SCALE = 1.5; 24013 const canvasWidth = Math.ceil(Math.max(Math.abs(actualBoundingBoxLeft) + Math.abs(actualBoundingBoxRight) || 0, width) * SCALE); 24014 const canvasHeight = Math.ceil(Math.max(Math.abs(actualBoundingBoxAscent) + Math.abs(actualBoundingBoxDescent) || fontSize, Math.abs(fontBoundingBoxAscent) + Math.abs(fontBoundingBoxDescent) || fontSize) * SCALE); 24015 canvas = new OffscreenCanvas(canvasWidth, canvasHeight); 24016 ctx = canvas.getContext("2d", { 24017 alpha: true, 24018 willReadFrequently: true 24019 }); 24020 ctx.font = font; 24021 ctx.filter = "grayscale(1)"; 24022 ctx.fillStyle = "white"; 24023 ctx.fillRect(0, 0, canvasWidth, canvasHeight); 24024 ctx.fillStyle = "black"; 24025 ctx.fillText(text, canvasWidth * (SCALE - 1) / 2, canvasHeight * (3 - SCALE) / 2); 24026 const uint8Buf = this.#toUint8(ctx.getImageData(0, 0, canvasWidth, canvasHeight).data); 24027 const histogram = this.#getHistogram(uint8Buf); 24028 const threshold = this.#guessThreshold(histogram); 24029 const contourList = this.#findContours(uint8Buf, canvasWidth, canvasHeight, threshold); 24030 return this.processDrawnLines({ 24031 lines: { 24032 curves: contourList, 24033 width: canvasWidth, 24034 height: canvasHeight 24035 }, 24036 pageWidth, 24037 pageHeight, 24038 rotation, 24039 innerMargin, 24040 mustSmooth: true, 24041 areContours: true 24042 }); 24043 } 24044 static process(bitmap, pageWidth, pageHeight, rotation, innerMargin) { 24045 const [uint8Buf, width, height] = this.#getGrayPixels(bitmap); 24046 const [buffer, histogram] = this.#bilateralFilter(uint8Buf, width, height, Math.hypot(width, height) * this.#PARAMETERS.sigmaSFactor, this.#PARAMETERS.sigmaR, this.#PARAMETERS.kernelSize); 24047 const threshold = this.#guessThreshold(histogram); 24048 const contourList = this.#findContours(buffer, width, height, threshold); 24049 return this.processDrawnLines({ 24050 lines: { 24051 curves: contourList, 24052 width, 24053 height 24054 }, 24055 pageWidth, 24056 pageHeight, 24057 rotation, 24058 innerMargin, 24059 mustSmooth: true, 24060 areContours: true 24061 }); 24062 } 24063 static processDrawnLines({ 24064 lines, 24065 pageWidth, 24066 pageHeight, 24067 rotation, 24068 innerMargin, 24069 mustSmooth, 24070 areContours 24071 }) { 24072 if (rotation % 180 !== 0) { 24073 [pageWidth, pageHeight] = [pageHeight, pageWidth]; 24074 } 24075 const { 24076 curves, 24077 width, 24078 height 24079 } = lines; 24080 const thickness = lines.thickness ?? 0; 24081 const linesAndPoints = []; 24082 const ratio = Math.min(pageWidth / width, pageHeight / height); 24083 const xScale = ratio / pageWidth; 24084 const yScale = ratio / pageHeight; 24085 const newCurves = []; 24086 for (const { 24087 points 24088 } of curves) { 24089 const reducedPoints = mustSmooth ? this.#douglasPeucker(points) : points; 24090 if (!reducedPoints) { 24091 continue; 24092 } 24093 newCurves.push(reducedPoints); 24094 const len = reducedPoints.length; 24095 const newPoints = new Float32Array(len); 24096 const line = new Float32Array(3 * (len === 2 ? 2 : len - 2)); 24097 linesAndPoints.push({ 24098 line, 24099 points: newPoints 24100 }); 24101 if (len === 2) { 24102 newPoints[0] = reducedPoints[0] * xScale; 24103 newPoints[1] = reducedPoints[1] * yScale; 24104 line.set([NaN, NaN, NaN, NaN, newPoints[0], newPoints[1]], 0); 24105 continue; 24106 } 24107 let [x1, y1, x2, y2] = reducedPoints; 24108 x1 *= xScale; 24109 y1 *= yScale; 24110 x2 *= xScale; 24111 y2 *= yScale; 24112 newPoints.set([x1, y1, x2, y2], 0); 24113 line.set([NaN, NaN, NaN, NaN, x1, y1], 0); 24114 for (let i = 4; i < len; i += 2) { 24115 const x = newPoints[i] = reducedPoints[i] * xScale; 24116 const y = newPoints[i + 1] = reducedPoints[i + 1] * yScale; 24117 line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3); 24118 [x1, y1, x2, y2] = [x2, y2, x, y]; 24119 } 24120 } 24121 if (linesAndPoints.length === 0) { 24122 return null; 24123 } 24124 const outline = areContours ? new ContourDrawOutline() : new InkDrawOutline(); 24125 outline.build(linesAndPoints, pageWidth, pageHeight, 1, rotation, areContours ? 0 : thickness, innerMargin); 24126 return { 24127 outline, 24128 newCurves, 24129 areContours, 24130 thickness, 24131 width, 24132 height 24133 }; 24134 } 24135 static async compressSignature({ 24136 outlines, 24137 areContours, 24138 thickness, 24139 width, 24140 height 24141 }) { 24142 let minDiff = Infinity; 24143 let maxDiff = -Infinity; 24144 let outlinesLength = 0; 24145 for (const points of outlines) { 24146 outlinesLength += points.length; 24147 for (let i = 2, ii = points.length; i < ii; i++) { 24148 const dx = points[i] - points[i - 2]; 24149 minDiff = Math.min(minDiff, dx); 24150 maxDiff = Math.max(maxDiff, dx); 24151 } 24152 } 24153 let bufferType; 24154 if (minDiff >= -128 && maxDiff <= 127) { 24155 bufferType = Int8Array; 24156 } else if (minDiff >= -32768 && maxDiff <= 32767) { 24157 bufferType = Int16Array; 24158 } else { 24159 bufferType = Int32Array; 24160 } 24161 const len = outlines.length; 24162 const headerLength = BASE_HEADER_LENGTH + POINTS_PROPERTIES_NUMBER * len; 24163 const header = new Uint32Array(headerLength); 24164 let offset = 0; 24165 header[offset++] = headerLength * Uint32Array.BYTES_PER_ELEMENT + (outlinesLength - 2 * len) * bufferType.BYTES_PER_ELEMENT; 24166 header[offset++] = 0; 24167 header[offset++] = width; 24168 header[offset++] = height; 24169 header[offset++] = areContours ? 0 : 1; 24170 header[offset++] = Math.max(0, Math.floor(thickness ?? 0)); 24171 header[offset++] = len; 24172 header[offset++] = bufferType.BYTES_PER_ELEMENT; 24173 for (const points of outlines) { 24174 header[offset++] = points.length - 2; 24175 header[offset++] = points[0]; 24176 header[offset++] = points[1]; 24177 } 24178 const cs = new CompressionStream("deflate-raw"); 24179 const writer = cs.writable.getWriter(); 24180 await writer.ready; 24181 writer.write(header); 24182 const BufferCtor = bufferType.prototype.constructor; 24183 for (const points of outlines) { 24184 const diffs = new BufferCtor(points.length - 2); 24185 for (let i = 2, ii = points.length; i < ii; i++) { 24186 diffs[i - 2] = points[i] - points[i - 2]; 24187 } 24188 writer.write(diffs); 24189 } 24190 writer.close(); 24191 const b
vendor: 7,528 bytes, lines 24191-24477
24191uf = await new Response(cs.readable).arrayBuffer(); 24192 const bytes = new Uint8Array(buf); 24193 return bytes.toBase64(); 24194 } 24195 static async decompressSignature(signatureData) { 24196 try { 24197 const bytes = Uint8Array.fromBase64(signatureData); 24198 const { 24199 readable, 24200 writable 24201 } = new DecompressionStream("deflate-raw"); 24202 const writer = writable.getWriter(); 24203 await writer.ready; 24204 writer.write(bytes).then(async () => { 24205 await writer.ready; 24206 await writer.close(); 24207 }).catch(() => {}); 24208 let data = null; 24209 let offset = 0; 24210 for await (const chunk of readable) { 24211 data ||= new Uint8Array(new Uint32Array(chunk.buffer, 0, 4)[0]); 24212 data.set(chunk, offset); 24213 offset += chunk.length; 24214 } 24215 const header = new Uint32Array(data.buffer, 0, data.length >> 2); 24216 const version = header[1]; 24217 if (version !== 0) { 24218 throw new Error(`Invalid version: ${version}`); 24219 } 24220 const width = header[2]; 24221 const height = header[3]; 24222 const areContours = header[4] === 0; 24223 const thickness = header[5]; 24224 const numberOfDrawings = header[6]; 24225 const bufferType = header[7]; 24226 const outlines = []; 24227 const diffsOffset = (BASE_HEADER_LENGTH + POINTS_PROPERTIES_NUMBER * numberOfDrawings) * Uint32Array.BYTES_PER_ELEMENT; 24228 let diffs; 24229 switch (bufferType) { 24230 case Int8Array.BYTES_PER_ELEMENT: 24231 diffs = new Int8Array(data.buffer, diffsOffset); 24232 break; 24233 case Int16Array.BYTES_PER_ELEMENT: 24234 diffs = new Int16Array(data.buffer, diffsOffset); 24235 break; 24236 case Int32Array.BYTES_PER_ELEMENT: 24237 diffs = new Int32Array(data.buffer, diffsOffset); 24238 break; 24239 } 24240 offset = 0; 24241 for (let i = 0; i < numberOfDrawings; i++) { 24242 const len = header[POINTS_PROPERTIES_NUMBER * i + BASE_HEADER_LENGTH]; 24243 const points = new Float32Array(len + 2); 24244 outlines.push(points); 24245 for (let j = 0; j < POINTS_PROPERTIES_NUMBER - 1; j++) { 24246 points[j] = header[POINTS_PROPERTIES_NUMBER * i + BASE_HEADER_LENGTH + j + 1]; 24247 } 24248 for (let j = 0; j < len; j++) { 24249 points[j + 2] = points[j] + diffs[offset++]; 24250 } 24251 } 24252 return { 24253 areContours, 24254 thickness, 24255 outlines, 24256 width, 24257 height 24258 }; 24259 } catch (e) { 24260 warn(`decompressSignature: ${e}`); 24261 return null; 24262 } 24263 } 24264} 24265 24266;// ./src/display/editor/signature.js 24267 24268 24269 24270 24271 24272 24273 24274class SignatureOptions extends DrawingOptions { 24275 constructor() { 24276 super(); 24277 super.updateProperties({ 24278 fill: AnnotationEditor._defaultLineColor, 24279 "stroke-width": 0 24280 }); 24281 } 24282 clone() { 24283 const clone = new SignatureOptions(); 24284 clone.updateAll(this); 24285 return clone; 24286 } 24287} 24288class DrawnSignatureOptions extends InkDrawingOptions { 24289 constructor(viewerParameters) { 24290 super(viewerParameters); 24291 super.updateProperties({ 24292 stroke: AnnotationEditor._defaultLineColor, 24293 "stroke-width": 1 24294 }); 24295 } 24296 clone() { 24297 const clone = new DrawnSignatureOptions(this._viewParameters); 24298 clone.updateAll(this); 24299 return clone; 24300 } 24301} 24302class SignatureEditor extends DrawingEditor { 24303 #isExtracted = false; 24304 #description = null; 24305 #signatureData = null; 24306 #signatureUUID = null; 24307 static _type = "signature"; 24308 static _editorType = AnnotationEditorType.SIGNATURE; 24309 static _defaultDrawingOptions = null; 24310 constructor(params) { 24311 super({ 24312 ...params, 24313 mustBeCommitted: true, 24314 name: "signatureEditor" 24315 }); 24316 this._willKeepAspectRatio = true; 24317 this.#signatureData = params.signatureData || null; 24318 this.#description = null; 24319 this.defaultL10nId = "pdfjs-editor-signature-editor1"; 24320 } 24321 static initialize(l10n, uiManager) { 24322 AnnotationEditor.initialize(l10n, uiManager); 24323 this._defaultDrawingOptions = new SignatureOptions(); 24324 this._defaultDrawnSignatureOptions = new DrawnSignatureOptions(uiManager.viewParameters); 24325 } 24326 static getDefaultDrawingOptions(options) { 24327 const clone = this._defaultDrawingOptions.clone(); 24328 clone.updateProperties(options); 24329 return clone; 24330 } 24331 static get supportMultipleDrawings() { 24332 return false; 24333 } 24334 static get typesMap() { 24335 return shadow(this, "typesMap", new Map()); 24336 } 24337 static get isDrawer() { 24338 return false; 24339 } 24340 get telemetryFinalData() { 24341 return { 24342 type: "signature", 24343 hasDescription: !!this.#description 24344 }; 24345 } 24346 static computeTelemetryFinalData(data) { 24347 const hasDescriptionStats = data.get("hasDescription"); 24348 return { 24349 hasAltText: hasDescriptionStats.get(true) ?? 0, 24350 hasNoAltText: hasDescriptionStats.get(false) ?? 0 24351 }; 24352 } 24353 get isResizable() { 24354 return true; 24355 } 24356 onScaleChanging() { 24357 if (this._drawId === null) { 24358 return; 24359 } 24360 super.onScaleChanging(); 24361 } 24362 render() { 24363 if (this.div) { 24364 return this.div; 24365 } 24366 let baseX, baseY; 24367 const { 24368 _isCopy 24369 } = this; 24370 if (_isCopy) { 24371 this._isCopy = false; 24372 baseX = this.x; 24373 baseY = this.y; 24374 } 24375 super.render(); 24376 if (this._drawId === null) { 24377 if (this.#signatureData) { 24378 const { 24379 lines, 24380 mustSmooth, 24381 areContours, 24382 description, 24383 uuid, 24384 heightInPage 24385 } = this.#signatureData; 24386 const { 24387 rawDims: { 24388 pageWidth, 24389 pageHeight 24390 }, 24391 rotation 24392 } = this.parent.viewport; 24393 const outline = SignatureExtractor.processDrawnLines({ 24394 lines, 24395 pageWidth, 24396 pageHeight, 24397 rotation, 24398 innerMargin: SignatureEditor._INNER_MARGIN, 24399 mustSmooth, 24400 areContours 24401 }); 24402 this.addSignature(outline, heightInPage, description, uuid); 24403 } else { 24404 this.div.setAttribute("data-l10n-args", JSON.stringify({ 24405 description: "" 24406 })); 24407 this.div.hidden = true; 24408 this._uiManager.getSignature(this); 24409 } 24410 } else { 24411 this.div.setAttribute("data-l10n-args", JSON.stringify({ 24412 description: this.#description || "" 24413 })); 24414 } 24415 if (_isCopy) { 24416 this._isCopy = true; 24417 this._moveAfterPaste(baseX, baseY); 24418 } 24419 return this.div; 24420 } 24421 setUuid(uuid) { 24422 this.#signatureUUID = uuid; 24423 this.addEditToolbar(); 24424 } 24425 getUuid() { 24426 return this.#signatureUUID; 24427 } 24428 get description() { 24429 return this.#description; 24430 } 24431 set description(description) { 24432 this.#description = description; 24433 if (!this.div) { 24434 return; 24435 } 24436 this.div.setAttribute("data-l10n-args", JSON.stringify({ 24437 description 24438 })); 24439 super.addEditToolbar().then(toolbar => { 24440 toolbar?.updateEditSignatureButton(description); 24441 }); 24442 } 24443 getSignaturePreview() { 24444 const { 24445 newCurves, 24446 areContours, 24447 thickness, 24448 width, 24449 height 24450 } = this.#signatureData; 24451 const maxDim = Math.max(width, height); 24452 const outlineData = SignatureExtractor.processDrawnLines({ 24453 lines: { 24454 curves: newCurves.map(points => ({ 24455 points 24456 })), 24457 thickness, 24458 width, 24459 height 24460 }, 24461 pageWidth: maxDim, 24462 pageHeight: maxDim, 24463 rotation: 0, 24464 innerMargin: 0, 24465 mustSmooth: false, 24466 areContours 24467 }); 24468 return { 24469 areContours, 24470 outline: outlineData.outline 24471 }; 24472 } 24473 get toolbarButtons() { 24474 if (this._uiManager.signatureManager) { 24475 return [["editSignature", this._uiManager.signatureManager]]; 24476 } 24477 return
vendor: 13,796 bytes, lines 24477-24984
24477 super.toolbarButtons; 24478 } 24479 addSignature(data, heightInPage, description, uuid) { 24480 const { 24481 x: savedX, 24482 y: savedY 24483 } = this; 24484 const { 24485 outline 24486 } = this.#signatureData = data; 24487 this.#isExtracted = outline instanceof ContourDrawOutline; 24488 this.description = description; 24489 let drawingOptions; 24490 if (this.#isExtracted) { 24491 drawingOptions = SignatureEditor.getDefaultDrawingOptions(); 24492 } else { 24493 drawingOptions = SignatureEditor._defaultDrawnSignatureOptions.clone(); 24494 drawingOptions.updateProperties({ 24495 "stroke-width": outline.thickness 24496 }); 24497 } 24498 this._addOutlines({ 24499 drawOutlines: outline, 24500 drawingOptions 24501 }); 24502 const [, pageHeight] = this.pageDimensions; 24503 let newHeight = heightInPage / pageHeight; 24504 newHeight = newHeight >= 1 ? 0.5 : newHeight; 24505 this.width *= newHeight / this.height; 24506 if (this.width >= 1) { 24507 newHeight *= 0.9 / this.width; 24508 this.width = 0.9; 24509 } 24510 this.height = newHeight; 24511 this.setDims(); 24512 this.x = savedX; 24513 this.y = savedY; 24514 this.center(); 24515 this._onResized(); 24516 this.onScaleChanging(); 24517 this.rotate(); 24518 this._uiManager.addToAnnotationStorage(this); 24519 this.setUuid(uuid); 24520 this._reportTelemetry({ 24521 action: "pdfjs.signature.inserted", 24522 data: { 24523 hasBeenSaved: !!uuid, 24524 hasDescription: !!description 24525 } 24526 }); 24527 this.div.hidden = false; 24528 } 24529 getFromImage(bitmap) { 24530 const { 24531 rawDims: { 24532 pageWidth, 24533 pageHeight 24534 }, 24535 rotation 24536 } = this.parent.viewport; 24537 return SignatureExtractor.process(bitmap, pageWidth, pageHeight, rotation, SignatureEditor._INNER_MARGIN); 24538 } 24539 getFromText(text, fontInfo) { 24540 const { 24541 rawDims: { 24542 pageWidth, 24543 pageHeight 24544 }, 24545 rotation 24546 } = this.parent.viewport; 24547 return SignatureExtractor.extractContoursFromText(text, fontInfo, pageWidth, pageHeight, rotation, SignatureEditor._INNER_MARGIN); 24548 } 24549 getDrawnSignature(curves) { 24550 const { 24551 rawDims: { 24552 pageWidth, 24553 pageHeight 24554 }, 24555 rotation 24556 } = this.parent.viewport; 24557 return SignatureExtractor.processDrawnLines({ 24558 lines: curves, 24559 pageWidth, 24560 pageHeight, 24561 rotation, 24562 innerMargin: SignatureEditor._INNER_MARGIN, 24563 mustSmooth: false, 24564 areContours: false 24565 }); 24566 } 24567 createDrawingOptions({ 24568 areContours, 24569 thickness 24570 }) { 24571 if (areContours) { 24572 this._drawingOptions = SignatureEditor.getDefaultDrawingOptions(); 24573 } else { 24574 this._drawingOptions = SignatureEditor._defaultDrawnSignatureOptions.clone(); 24575 this._drawingOptions.updateProperties({ 24576 "stroke-width": thickness 24577 }); 24578 } 24579 } 24580 serialize(isForCopying = false) { 24581 if (this.isEmpty()) { 24582 return null; 24583 } 24584 const { 24585 lines, 24586 points 24587 } = this.serializeDraw(isForCopying); 24588 const { 24589 _drawingOptions: { 24590 "stroke-width": thickness 24591 } 24592 } = this; 24593 const serialized = Object.assign(super.serialize(isForCopying), { 24594 isSignature: true, 24595 areContours: this.#isExtracted, 24596 color: [0, 0, 0], 24597 thickness: this.#isExtracted ? 0 : thickness 24598 }); 24599 this.addComment(serialized); 24600 if (isForCopying) { 24601 serialized.paths = { 24602 lines, 24603 points 24604 }; 24605 serialized.uuid = this.#signatureUUID; 24606 serialized.isCopy = true; 24607 } else { 24608 serialized.lines = lines; 24609 } 24610 if (this.#description) { 24611 serialized.accessibilityData = { 24612 type: "Figure", 24613 alt: this.#description 24614 }; 24615 } 24616 return serialized; 24617 } 24618 static deserializeDraw(pageX, pageY, pageWidth, pageHeight, innerMargin, data) { 24619 if (data.areContours) { 24620 return ContourDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); 24621 } 24622 return InkDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data); 24623 } 24624 static async deserialize(data, parent, uiManager) { 24625 const editor = await super.deserialize(data, parent, uiManager); 24626 editor.#isExtracted = data.areContours; 24627 editor.description = data.accessibilityData?.alt || ""; 24628 editor.#signatureUUID = data.uuid; 24629 return editor; 24630 } 24631} 24632 24633;// ./src/display/editor/stamp.js 24634 24635 24636 24637 24638class StampEditor extends AnnotationEditor { 24639 #bitmap = null; 24640 #bitmapId = null; 24641 #bitmapPromise = null; 24642 #bitmapUrl = null; 24643 #bitmapFile = null; 24644 #bitmapFileName = ""; 24645 #canvas = null; 24646 #missingCanvas = false; 24647 #resizeTimeoutId = null; 24648 #isSvg = false; 24649 #hasBeenAddedInUndoStack = false; 24650 static _type = "stamp"; 24651 static _editorType = AnnotationEditorType.STAMP; 24652 constructor(params) { 24653 super({ 24654 ...params, 24655 name: "stampEditor" 24656 }); 24657 this.#bitmapUrl = params.bitmapUrl; 24658 this.#bitmapFile = params.bitmapFile; 24659 this.defaultL10nId = "pdfjs-editor-stamp-editor"; 24660 } 24661 static initialize(l10n, uiManager) { 24662 AnnotationEditor.initialize(l10n, uiManager); 24663 } 24664 static isHandlingMimeForPasting(mime) { 24665 return SupportedImageMimeTypes.includes(mime); 24666 } 24667 static paste(item, parent) { 24668 parent.pasteEditor({ 24669 mode: AnnotationEditorType.STAMP 24670 }, { 24671 bitmapFile: item.getAsFile() 24672 }); 24673 } 24674 altTextFinish() { 24675 if (this._uiManager.useNewAltTextFlow) { 24676 this.div.hidden = false; 24677 } 24678 super.altTextFinish(); 24679 } 24680 get telemetryFinalData() { 24681 return { 24682 type: "stamp", 24683 hasAltText: !!this.altTextData?.altText 24684 }; 24685 } 24686 static computeTelemetryFinalData(data) { 24687 const hasAltTextStats = data.get("hasAltText"); 24688 return { 24689 hasAltText: hasAltTextStats.get(true) ?? 0, 24690 hasNoAltText: hasAltTextStats.get(false) ?? 0 24691 }; 24692 } 24693 #getBitmapFetched(data, fromId = false) { 24694 if (!data) { 24695 this.remove(); 24696 return; 24697 } 24698 this.#bitmap = data.bitmap; 24699 if (!fromId) { 24700 this.#bitmapId = data.id; 24701 this.#isSvg = data.isSvg; 24702 } 24703 if (data.file) { 24704 this.#bitmapFileName = data.file.name; 24705 } 24706 this.#createCanvas(); 24707 } 24708 #getBitmapDone() { 24709 this.#bitmapPromise = null; 24710 this._uiManager.enableWaiting(false); 24711 if (!this.#canvas) { 24712 return; 24713 } 24714 if (this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { 24715 this.addEditToolbar().then(() => { 24716 this._editToolbar.hide(); 24717 this._uiManager.editAltText(this, true); 24718 }); 24719 return; 24720 } 24721 if (!this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) { 24722 this._reportTelemetry({ 24723 action: "pdfjs.image.image_added", 24724 data: { 24725 alt_text_modal: false, 24726 alt_text_type: "empty" 24727 } 24728 }); 24729 try { 24730 this.mlGuessAltText(); 24731 } catch {} 24732 } 24733 this.div.focus(); 24734 } 24735 async mlGuessAltText(imageData = null, updateAltTextData = true) { 24736 if (this.hasAltTextData()) { 24737 return null; 24738 } 24739 const { 24740 mlManager 24741 } = this._uiManager; 24742 if (!mlManager) { 24743 throw new Error("No ML."); 24744 } 24745 if (!(await mlManager.isEnabledFor("altText"))) { 24746 throw new Error("ML isn't enabled for alt text."); 24747 } 24748 const { 24749 data, 24750 width, 24751 height 24752 } = imageData || this.copyCanvas(null, null, true).imageData; 24753 const response = await mlManager.guess({ 24754 name: "altText", 24755 request: { 24756 data, 24757 width, 24758 height, 24759 channels: data.length / (width * height) 24760 } 24761 }); 24762 if (!response) { 24763 throw new Error("No response from the AI service."); 24764 } 24765 if (response.error) { 24766 throw new Error("Error from the AI service."); 24767 } 24768 if (response.cancel) { 24769 return null; 24770 } 24771 if (!response.output) { 24772 throw new Error("No valid response from the AI service."); 24773 } 24774 const altText = response.output; 24775 await this.setGuessedAltText(altText); 24776 if (updateAltTextData && !this.hasAltTextData()) { 24777 this.altTextData = { 24778 alt: altText, 24779 decorative: false 24780 }; 24781 } 24782 return altText; 24783 } 24784 #getBitmap() { 24785 if (this.#bitmapId) { 24786 this._uiManager.enableWaiting(true); 24787 this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone()); 24788 return; 24789 } 24790 if (this.#bitmapUrl) { 24791 const url = this.#bitmapUrl; 24792 this.#bitmapUrl = null; 24793 this._uiManager.enableWaiting(true); 24794 this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); 24795 return; 24796 } 24797 if (this.#bitmapFile) { 24798 const file = this.#bitmapFile; 24799 this.#bitmapFile = null; 24800 this._uiManager.enableWaiting(true); 24801 this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone()); 24802 return; 24803 } 24804 const input = document.createElement("input"); 24805 input.type = "file"; 24806 input.accept = SupportedImageMimeTypes.join(","); 24807 const signal = this._uiManager._signal; 24808 this.#bitmapPromise = new Promise(resolve => { 24809 input.addEventListener("change", async () => { 24810 if (!input.files || input.files.length === 0) { 24811 this.remove(); 24812 } else { 24813 this._uiManager.enableWaiting(true); 24814 const data = await this._uiManager.imageManager.getFromFile(input.files[0]); 24815 this._reportTelemetry({ 24816 action: "pdfjs.image.image_selected", 24817 data: { 24818 alt_text_modal: this._uiManager.useNewAltTextFlow 24819 } 24820 }); 24821 this.#getBitmapFetched(data); 24822 } 24823 resolve(); 24824 }, { 24825 signal 24826 }); 24827 input.addEventListener("cancel", () => { 24828 this.remove(); 24829 resolve(); 24830 }, { 24831 signal 24832 }); 24833 }).finally(() => this.#getBitmapDone()); 24834 input.click(); 24835 } 24836 remove() { 24837 if (this.#bitmapId) { 24838 this.#bitmap = null; 24839 this._uiManager.imageManager.deleteId(this.#bitmapId); 24840 this.#canvas?.remove(); 24841 this.#canvas = null; 24842 if (this.#resizeTimeoutId) { 24843 clearTimeout(this.#resizeTimeoutId); 24844 this.#resizeTimeoutId = null; 24845 } 24846 } 24847 super.remove(); 24848 } 24849 rebuild() { 24850 if (!this.parent) { 24851 if (this.#bitmapId) { 24852 this.#getBitmap(); 24853 } 24854 return; 24855 } 24856 super.rebuild(); 24857 if (this.div === null) { 24858 return; 24859 } 24860 if (this.#bitmapId && this.#canvas === null) { 24861 this.#getBitmap(); 24862 } 24863 if (!this.isAttachedToDOM) { 24864 this.parent.add(this); 24865 } 24866 } 24867 onceAdded(focus) { 24868 this._isDraggable = true; 24869 if (focus) { 24870 this.div.focus(); 24871 } 24872 } 24873 isEmpty() { 24874 return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId || this.#missingCanvas); 24875 } 24876 get toolbarButtons() { 24877 return [["altText", this.createAltText()]]; 24878 } 24879 get isResizable() { 24880 return true; 24881 } 24882 render() { 24883 if (this.div) { 24884 return this.div; 24885 } 24886 let baseX, baseY; 24887 if (this._isCopy) { 24888 baseX = this.x; 24889 baseY = this.y; 24890 } 24891 super.render(); 24892 this.div.hidden = true; 24893 this.createAltText(); 24894 if (!this.#missingCanvas) { 24895 if (this.#bitmap) { 24896 this.#createCanvas(); 24897 } else { 24898 this.#getBitmap(); 24899 } 24900 } 24901 if (this._isCopy) { 24902 this._moveAfterPaste(baseX, baseY); 24903 } 24904 this._uiManager.addShouldRescale(this); 24905 return this.div; 24906 } 24907 setCanvas(annotationElementId, canvas) { 24908 const { 24909 id: bitmapId, 24910 bitmap 24911 } = this._uiManager.imageManager.getFromCanvas(annotationElementId, canvas); 24912 canvas.remove(); 24913 if (bitmapId && this._uiManager.imageManager.isValidId(bitmapId)) { 24914 this.#bitmapId = bitmapId; 24915 if (bitmap) { 24916 this.#bitmap = bitmap; 24917 } 24918 this.#missingCanvas = false; 24919 this.#createCanvas(); 24920 } 24921 } 24922 _onResized() { 24923 this.onScaleChanging(); 24924 } 24925 onScaleChanging() { 24926 if (!this.parent) { 24927 return; 24928 } 24929 if (this.#resizeTimeoutId !== null) { 24930 clearTimeout(this.#resizeTimeoutId); 24931 } 24932 const TIME_TO_WAIT = 200; 24933 this.#resizeTimeoutId = setTimeout(() => { 24934 this.#resizeTimeoutId = null; 24935 this.#drawBitmap(); 24936 }, TIME_TO_WAIT); 24937 } 24938 #createCanvas() { 24939 const { 24940 div 24941 } = this; 24942 let { 24943 width, 24944 height 24945 } = this.#bitmap; 24946 const [pageWidth, pageHeight] = this.pageDimensions; 24947 const MAX_RATIO = 0.75; 24948 if (this.width) { 24949 width = this.width * pageWidth; 24950 height = this.height * pageHeight; 24951 } else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) { 24952 const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height); 24953 width *= factor; 24954 height *= factor; 24955 } 24956 this._uiManager.enableWaiting(false); 24957 const canvas = this.#canvas = document.createElement("canvas"); 24958 canvas.setAttribute("role", "img"); 24959 this.addContainer(canvas); 24960 this.width = width / pageWidth; 24961 this.height = height / pageHeight; 24962 this.setDims(); 24963 if (this._initialOptions?.isCentered) { 24964 this.center(); 24965 } else { 24966 this.fixAndSetPosition(); 24967 } 24968 this._initialOptions = null; 24969 if (!this._uiManager.useNewAltTextWhenAddingImage || !this._uiManager.useNewAltTextFlow || this.annotationElementId) { 24970 div.hidden = false; 24971 } 24972 this.#drawBitmap(); 24973 if (!this.#hasBeenAddedInUndoStack) { 24974 this.parent.addUndoableEditor(this); 24975 this.#hasBeenAddedInUndoStack = true; 24976 } 24977 this._reportTelemetry({ 24978 action: "inserted_image" 24979 }); 24980 if (this.#bitmapFileName) { 24981 this.div.setAttribute("aria-description", this.#bitmapFileName); 24982 } 24983 if (!this.annotationElementId) { 24984 this._uiManager.a11yAlert(
vendor: 4,524 bytes, lines 24984-25102
24984"pdfjs-editor-stamp-added-alert"); 24985 } 24986 } 24987 copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) { 24988 if (!maxDataDimension) { 24989 maxDataDimension = 224; 24990 } 24991 const { 24992 width: bitmapWidth, 24993 height: bitmapHeight 24994 } = this.#bitmap; 24995 const outputScale = new OutputScale(); 24996 let bitmap = this.#bitmap; 24997 let width = bitmapWidth, 24998 height = bitmapHeight; 24999 let canvas = null; 25000 if (maxPreviewDimension) { 25001 if (bitmapWidth > maxPreviewDimension || bitmapHeight > maxPreviewDimension) { 25002 const ratio = Math.min(maxPreviewDimension / bitmapWidth, maxPreviewDimension / bitmapHeight); 25003 width = Math.floor(bitmapWidth * ratio); 25004 height = Math.floor(bitmapHeight * ratio); 25005 } 25006 canvas = document.createElement("canvas"); 25007 const scaledWidth = canvas.width = Math.ceil(width * outputScale.sx); 25008 const scaledHeight = canvas.height = Math.ceil(height * outputScale.sy); 25009 if (!this.#isSvg) { 25010 bitmap = this.#scaleBitmap(scaledWidth, scaledHeight); 25011 } 25012 const ctx = canvas.getContext("2d"); 25013 ctx.filter = this._uiManager.hcmFilter; 25014 let white = "white", 25015 black = "#cfcfd8"; 25016 if (this._uiManager.hcmFilter !== "none") { 25017 black = "black"; 25018 } else if (ColorScheme.isDarkMode) { 25019 white = "#8f8f9d"; 25020 black = "#42414d"; 25021 } 25022 const boxDim = 15; 25023 const boxDimWidth = boxDim * outputScale.sx; 25024 const boxDimHeight = boxDim * outputScale.sy; 25025 const pattern = new OffscreenCanvas(boxDimWidth * 2, boxDimHeight * 2); 25026 const patternCtx = pattern.getContext("2d"); 25027 patternCtx.fillStyle = white; 25028 patternCtx.fillRect(0, 0, boxDimWidth * 2, boxDimHeight * 2); 25029 patternCtx.fillStyle = black; 25030 patternCtx.fillRect(0, 0, boxDimWidth, boxDimHeight); 25031 patternCtx.fillRect(boxDimWidth, boxDimHeight, boxDimWidth, boxDimHeight); 25032 ctx.fillStyle = ctx.createPattern(pattern, "repeat"); 25033 ctx.fillRect(0, 0, scaledWidth, scaledHeight); 25034 ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); 25035 } 25036 let imageData = null; 25037 if (createImageData) { 25038 let dataWidth, dataHeight; 25039 if (outputScale.symmetric && bitmap.width < maxDataDimension && bitmap.height < maxDataDimension) { 25040 dataWidth = bitmap.width; 25041 dataHeight = bitmap.height; 25042 } else { 25043 bitmap = this.#bitmap; 25044 if (bitmapWidth > maxDataDimension || bitmapHeight > maxDataDimension) { 25045 const ratio = Math.min(maxDataDimension / bitmapWidth, maxDataDimension / bitmapHeight); 25046 dataWidth = Math.floor(bitmapWidth * ratio); 25047 dataHeight = Math.floor(bitmapHeight * ratio); 25048 if (!this.#isSvg) { 25049 bitmap = this.#scaleBitmap(dataWidth, dataHeight); 25050 } 25051 } 25052 } 25053 const offscreen = new OffscreenCanvas(dataWidth, dataHeight); 25054 const offscreenCtx = offscreen.getContext("2d", { 25055 willReadFrequently: true 25056 }); 25057 offscreenCtx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, dataWidth, dataHeight); 25058 imageData = { 25059 width: dataWidth, 25060 height: dataHeight, 25061 data: offscreenCtx.getImageData(0, 0, dataWidth, dataHeight).data 25062 }; 25063 } 25064 return { 25065 canvas, 25066 width, 25067 height, 25068 imageData 25069 }; 25070 } 25071 #scaleBitmap(width, height) { 25072 const { 25073 width: bitmapWidth, 25074 height: bitmapHeight 25075 } = this.#bitmap; 25076 let newWidth = bitmapWidth; 25077 let newHeight = bitmapHeight; 25078 let bitmap = this.#bitmap; 25079 while (newWidth > 2 * width || newHeight > 2 * height) { 25080 const prevWidth = newWidth; 25081 const prevHeight = newHeight; 25082 if (newWidth > 2 * width) { 25083 newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2); 25084 } 25085 if (newHeight > 2 * height) { 25086 newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2); 25087 } 25088 const offscreen = new OffscreenCanvas(newWidth, newHeight); 25089 const ctx = offscreen.getContext("2d"); 25090 ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight); 25091 bitmap = offscreen.transferToImageBitmap(); 25092 } 25093 return bitmap; 25094 } 25095 #drawBitmap() { 25096 const [parentWidth, parentHeight] = this.parentDimensions; 25097 const { 25098 width, 25099 height 25100 } = this; 25101 const outputScale = new OutputScale(); 25102 const scaledWidth = Math.ceil(width * parentWidth * outputScale.sx);
vendor: 15,596 bytes, lines 25103-25615
25103 const scaledHeight = Math.ceil(height * parentHeight * outputScale.sy); 25104 const canvas = this.#canvas; 25105 if (!canvas || canvas.width === scaledWidth && canvas.height === scaledHeight) { 25106 return; 25107 } 25108 canvas.width = scaledWidth; 25109 canvas.height = scaledHeight; 25110 const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(scaledWidth, scaledHeight); 25111 const ctx = canvas.getContext("2d"); 25112 ctx.filter = this._uiManager.hcmFilter; 25113 ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight); 25114 } 25115 #serializeBitmap(toUrl) { 25116 if (toUrl) { 25117 if (this.#isSvg) { 25118 const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId); 25119 if (url) { 25120 return url; 25121 } 25122 } 25123 const canvas = document.createElement("canvas"); 25124 ({ 25125 width: canvas.width, 25126 height: canvas.height 25127 } = this.#bitmap); 25128 const ctx = canvas.getContext("2d"); 25129 ctx.drawImage(this.#bitmap, 0, 0); 25130 return canvas.toDataURL(); 25131 } 25132 if (this.#isSvg) { 25133 const [pageWidth, pageHeight] = this.pageDimensions; 25134 const width = Math.round(this.width * pageWidth * PixelsPerInch.PDF_TO_CSS_UNITS); 25135 const height = Math.round(this.height * pageHeight * PixelsPerInch.PDF_TO_CSS_UNITS); 25136 const offscreen = new OffscreenCanvas(width, height); 25137 const ctx = offscreen.getContext("2d"); 25138 ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height); 25139 return offscreen.transferToImageBitmap(); 25140 } 25141 return structuredClone(this.#bitmap); 25142 } 25143 static async deserialize(data, parent, uiManager) { 25144 let initialData = null; 25145 let missingCanvas = false; 25146 if (data instanceof StampAnnotationElement) { 25147 const { 25148 data: { 25149 rect, 25150 rotation, 25151 id, 25152 structParent, 25153 popupRef, 25154 richText, 25155 contentsObj, 25156 creationDate, 25157 modificationDate 25158 }, 25159 container, 25160 parent: { 25161 page: { 25162 pageNumber 25163 } 25164 }, 25165 canvas 25166 } = data; 25167 let bitmapId, bitmap; 25168 if (canvas) { 25169 delete data.canvas; 25170 ({ 25171 id: bitmapId, 25172 bitmap 25173 } = uiManager.imageManager.getFromCanvas(container.id, canvas)); 25174 canvas.remove(); 25175 } else { 25176 missingCanvas = true; 25177 data._hasNoCanvas = true; 25178 } 25179 const altText = (await parent._structTree.getAriaAttributes(`${AnnotationPrefix}${id}`))?.get("aria-label") || ""; 25180 initialData = data = { 25181 annotationType: AnnotationEditorType.STAMP, 25182 bitmapId, 25183 bitmap, 25184 pageIndex: pageNumber - 1, 25185 rect: rect.slice(0), 25186 rotation, 25187 annotationElementId: id, 25188 id, 25189 deleted: false, 25190 accessibilityData: { 25191 decorative: false, 25192 altText 25193 }, 25194 isSvg: false, 25195 structParent, 25196 popupRef, 25197 richText, 25198 comment: contentsObj?.str || null, 25199 creationDate, 25200 modificationDate 25201 }; 25202 } 25203 const editor = await super.deserialize(data, parent, uiManager); 25204 const { 25205 rect, 25206 bitmap, 25207 bitmapUrl, 25208 bitmapId, 25209 isSvg, 25210 accessibilityData 25211 } = data; 25212 if (missingCanvas) { 25213 uiManager.addMissingCanvas(data.id, editor); 25214 editor.#missingCanvas = true; 25215 } else if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) { 25216 editor.#bitmapId = bitmapId; 25217 if (bitmap) { 25218 editor.#bitmap = bitmap; 25219 } 25220 } else { 25221 editor.#bitmapUrl = bitmapUrl; 25222 } 25223 editor.#isSvg = isSvg; 25224 const [parentWidth, parentHeight] = editor.pageDimensions; 25225 editor.width = (rect[2] - rect[0]) / parentWidth; 25226 editor.height = (rect[3] - rect[1]) / parentHeight; 25227 if (accessibilityData) { 25228 editor.altTextData = accessibilityData; 25229 } 25230 editor._initialData = initialData; 25231 if (data.comment) { 25232 editor.setCommentData(data); 25233 } 25234 editor.#hasBeenAddedInUndoStack = !!initialData; 25235 return editor; 25236 } 25237 serialize(isForCopying = false, context = null) { 25238 if (this.isEmpty()) { 25239 return null; 25240 } 25241 if (this.deleted) { 25242 return this.serializeDeleted(); 25243 } 25244 const serialized = Object.assign(super.serialize(isForCopying), { 25245 bitmapId: this.#bitmapId, 25246 isSvg: this.#isSvg 25247 }); 25248 this.addComment(serialized); 25249 if (isForCopying) { 25250 serialized.bitmapUrl = this.#serializeBitmap(true); 25251 serialized.accessibilityData = this.serializeAltText(true); 25252 serialized.isCopy = true; 25253 return serialized; 25254 } 25255 const { 25256 decorative, 25257 altText 25258 } = this.serializeAltText(false); 25259 if (!decorative && altText) { 25260 serialized.accessibilityData = { 25261 type: "Figure", 25262 alt: altText 25263 }; 25264 } 25265 if (this.annotationElementId) { 25266 const changes = this.#hasElementChanged(serialized); 25267 if (changes.isSame) { 25268 return null; 25269 } 25270 if (changes.isSameAltText) { 25271 delete serialized.accessibilityData; 25272 } else { 25273 serialized.accessibilityData.structParent = this._initialData.structParent ?? -1; 25274 } 25275 serialized.id = this.annotationElementId; 25276 delete serialized.bitmapId; 25277 return serialized; 25278 } 25279 if (context === null) { 25280 return serialized; 25281 } 25282 context.stamps ||= new Map(); 25283 const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null; 25284 if (!context.stamps.has(this.#bitmapId)) { 25285 context.stamps.set(this.#bitmapId, { 25286 area, 25287 serialized 25288 }); 25289 serialized.bitmap = this.#serializeBitmap(false); 25290 } else if (this.#isSvg) { 25291 const prevData = context.stamps.get(this.#bitmapId); 25292 if (area > prevData.area) { 25293 prevData.area = area; 25294 prevData.serialized.bitmap.close(); 25295 prevData.serialized.bitmap = this.#serializeBitmap(false); 25296 } 25297 } 25298 return serialized; 25299 } 25300 #hasElementChanged(serialized) { 25301 const { 25302 pageIndex, 25303 accessibilityData: { 25304 altText 25305 } 25306 } = this._initialData; 25307 const isSamePageIndex = serialized.pageIndex === pageIndex; 25308 const isSameAltText = (serialized.accessibilityData?.alt || "") === altText; 25309 return { 25310 isSame: !this.hasEditedComment && !this._hasBeenMoved && !this._hasBeenResized && isSamePageIndex && isSameAltText, 25311 isSameAltText 25312 }; 25313 } 25314 renderAnnotationElement(annotation) { 25315 if (this.deleted) { 25316 annotation.hide(); 25317 return null; 25318 } 25319 annotation.updateEdited({ 25320 rect: this.getPDFRect(), 25321 popup: this.comment 25322 }); 25323 return null; 25324 } 25325} 25326 25327;// ./src/display/editor/annotation_editor_layer.js 25328 25329 25330 25331 25332 25333 25334 25335 25336class AnnotationEditorLayer { 25337 #accessibilityManager; 25338 #allowClick = false; 25339 #annotationLayer = null; 25340 #clickAC = null; 25341 #editorFocusTimeoutId = null; 25342 #editors = new Map(); 25343 #hadPointerDown = false; 25344 #isDisabling = false; 25345 #isEnabling = false; 25346 #drawingAC = null; 25347 #focusedElement = null; 25348 #textLayer = null; 25349 #textSelectionAC = null; 25350 #textLayerDblClickAC = null; 25351 #lastPointerDownTimestamp = -1; 25352 #uiManager; 25353 static _initialized = false; 25354 static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor, SignatureEditor].map(type => [type._editorType, type])); 25355 constructor({ 25356 uiManager, 25357 pageIndex, 25358 div, 25359 structTreeLayer, 25360 accessibilityManager, 25361 annotationLayer, 25362 drawLayer, 25363 textLayer, 25364 viewport, 25365 l10n 25366 }) { 25367 const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()]; 25368 if (!AnnotationEditorLayer._initialized) { 25369 AnnotationEditorLayer._initialized = true; 25370 for (const editorType of editorTypes) { 25371 editorType.initialize(l10n, uiManager); 25372 } 25373 } 25374 uiManager.registerEditorTypes(editorTypes); 25375 this.#uiManager = uiManager; 25376 this.pageIndex = pageIndex; 25377 this.div = div; 25378 this.#accessibilityManager = accessibilityManager; 25379 this.#annotationLayer = annotationLayer; 25380 this.viewport = viewport; 25381 this.#textLayer = textLayer; 25382 this.drawLayer = drawLayer; 25383 this._structTree = structTreeLayer; 25384 this.#uiManager.addLayer(this); 25385 } 25386 updatePageIndex(newPageIndex) { 25387 this.pageIndex = newPageIndex; 25388 } 25389 get isEmpty() { 25390 return this.#editors.size === 0; 25391 } 25392 get isInvisible() { 25393 return this.isEmpty && this.#uiManager.getMode() === AnnotationEditorType.NONE; 25394 } 25395 updateToolbar(options) { 25396 this.#uiManager.updateToolbar(options); 25397 } 25398 updateMode(mode = this.#uiManager.getMode()) { 25399 this.#cleanup(); 25400 switch (mode) { 25401 case AnnotationEditorType.NONE: 25402 this.div.classList.toggle("nonEditing", true); 25403 this.disableTextSelection(); 25404 this.togglePointerEvents(false); 25405 this.toggleAnnotationLayerPointerEvents(true); 25406 this.disableClick(); 25407 return; 25408 case AnnotationEditorType.INK: 25409 this.disableTextSelection(); 25410 this.togglePointerEvents(true); 25411 this.enableClick(); 25412 break; 25413 case AnnotationEditorType.HIGHLIGHT: 25414 this.enableTextSelection(); 25415 this.togglePointerEvents(false); 25416 this.disableClick(); 25417 break; 25418 default: 25419 this.disableTextSelection(); 25420 this.togglePointerEvents(true); 25421 this.enableClick(); 25422 } 25423 this.toggleAnnotationLayerPointerEvents(false); 25424 const { 25425 classList 25426 } = this.div; 25427 classList.toggle("nonEditing", false); 25428 if (mode === AnnotationEditorType.POPUP) { 25429 classList.toggle("commentEditing", true); 25430 } else { 25431 classList.toggle("commentEditing", false); 25432 for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { 25433 classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType); 25434 } 25435 } 25436 this.div.hidden = false; 25437 } 25438 hasTextLayer(textLayer) { 25439 return textLayer === this.#textLayer?.div; 25440 } 25441 setEditingState(isEditing) { 25442 this.#uiManager.setEditingState(isEditing); 25443 } 25444 addCommands(params) { 25445 this.#uiManager.addCommands(params); 25446 } 25447 cleanUndoStack(type) { 25448 this.#uiManager.cleanUndoStack(type); 25449 } 25450 toggleDrawing(enabled = false) { 25451 this.div.classList.toggle("drawing", !enabled); 25452 } 25453 togglePointerEvents(enabled = false) { 25454 this.div.classList.toggle("disabled", !enabled); 25455 } 25456 toggleAnnotationLayerPointerEvents(enabled = false) { 25457 this.#annotationLayer?.togglePointerEvents(enabled); 25458 } 25459 get #allEditorsIterator() { 25460 return this.#editors.size !== 0 ? this.#editors.values() : this.#uiManager.getEditors(this.pageIndex); 25461 } 25462 async enable() { 25463 this.#isEnabling = true; 25464 this.div.tabIndex = 0; 25465 this.togglePointerEvents(true); 25466 this.div.classList.toggle("nonEditing", false); 25467 this.#textLayerDblClickAC?.abort(); 25468 this.#textLayerDblClickAC = null; 25469 const annotationElementIds = new Set(); 25470 for (const editor of this.#allEditorsIterator) { 25471 editor.enableEditing(); 25472 editor.show(true); 25473 if (editor.annotationElementId) { 25474 this.#uiManager.removeChangedExistingAnnotation(editor); 25475 annotationElementIds.add(editor.annotationElementId); 25476 } 25477 } 25478 const annotationLayer = this.#annotationLayer; 25479 if (annotationLayer) { 25480 for (const editable of annotationLayer.getEditableAnnotations()) { 25481 editable.hide(); 25482 if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) { 25483 continue; 25484 } 25485 if (annotationElementIds.has(editable.data.id)) { 25486 continue; 25487 } 25488 const editor = await this.deserialize(editable); 25489 if (!editor) { 25490 continue; 25491 } 25492 this.addOrRebuild(editor); 25493 editor.enableEditing(); 25494 } 25495 } 25496 this.#isEnabling = false; 25497 this.#uiManager._eventBus.dispatch("editorsrendered", { 25498 source: this, 25499 pageNumber: this.pageIndex + 1 25500 }); 25501 } 25502 disable() { 25503 this.#isDisabling = true; 25504 this.div.tabIndex = -1; 25505 this.togglePointerEvents(false); 25506 this.div.classList.toggle("nonEditing", true); 25507 if (this.#textLayer && !this.#textLayerDblClickAC) { 25508 this.#textLayerDblClickAC = new AbortController(); 25509 const signal = this.#uiManager.combinedSignal(this.#textLayerDblClickAC); 25510 this.#textLayer.div.addEventListener("pointerdown", e => { 25511 const DBL_CLICK_THRESHOLD = 500; 25512 const { 25513 clientX, 25514 clientY, 25515 timeStamp 25516 } = e; 25517 const lastPointerDownTimestamp = this.#lastPointerDownTimestamp; 25518 if (timeStamp - lastPointerDownTimestamp > DBL_CLICK_THRESHOLD) { 25519 this.#lastPointerDownTimestamp = timeStamp; 25520 return; 25521 } 25522 this.#lastPointerDownTimestamp = -1; 25523 const { 25524 classList 25525 } = this.div; 25526 classList.toggle("getElements", true); 25527 const elements = document.elementsFromPoint(clientX, clientY); 25528 classList.toggle("getElements", false); 25529 if (!this.div.contains(elements[0])) { 25530 return; 25531 } 25532 let id; 25533 const regex = new RegExp(`^${AnnotationEditorPrefix}[0-9]+$`); 25534 for (const element of elements) { 25535 if (regex.test(element.id)) { 25536 id = element.id; 25537 break; 25538 } 25539 } 25540 if (!id) { 25541 return; 25542 } 25543 const editor = this.#editors.get(id); 25544 if (editor?.annotationElementId === null) { 25545 e.stopPropagation(); 25546 e.preventDefault(); 25547 editor.dblclick(e); 25548 } 25549 }, { 25550 signal, 25551 capture: true 25552 }); 25553 } 25554 const annotationLayer = this.#annotationLayer; 25555 const needFakeAnnotation = []; 25556 if (annotationLayer) { 25557 const changedAnnotations = new Map(); 25558 const resetAnnotations = new Map(); 25559 for (const editor of this.#allEditorsIterator) { 25560 editor.disableEditing(); 25561 if (!editor.annotationElementId) { 25562 needFakeAnnotation.push(editor); 25563 continue; 25564 } 25565 if (editor.serialize() !== null) { 25566 changedAnnotations.set(editor.annotationElementId, editor); 25567 continue; 25568 } else { 25569 resetAnnotations.set(editor.annotationElementId, editor); 25570 } 25571 this.getEditableAnnotation(editor.annotationElementId)?.show(); 25572 editor.remove(); 25573 } 25574 const editables = annotationLayer.getEditableAnnotations(); 25575 for (const editable of editables) { 25576 const { 25577 id 25578 } = editable.data; 25579 if (this.#uiManager.isDeletedAnnotationElement(id)) { 25580 editable.updateEdited({ 25581 deleted: true 25582 }); 25583 continue; 25584 } 25585 let editor = resetAnnotations.get(id); 25586 if (editor) { 25587 editor.resetAnnotationElement(editable); 25588 editor.show(false); 25589 editable.show(); 25590 continue; 25591 } 25592 editor = changedAnnotations.get(id); 25593 if (editor) { 25594 this.#uiManager.addChangedExistingAnnotation(editor); 25595 if (editor.renderAnnotationElement(editable)) { 25596 editor.show(false); 25597 } 25598 } 25599 editable.show(); 25600 } 25601 } 25602 this.#cleanup(); 25603 if (this.isEmpty) { 25604 this.div.hidden = true; 25605 } 25606 const { 25607 classList 25608 } = this.div; 25609 for (const editorType of AnnotationEditorLayer.#editorTypes.values()) { 25610 classList.remove(`${editorType._type}Editing`); 25611 } 25612 this.disableTextSelection(); 25613 this.toggleAnnotationLayerPointerEvents(true); 25614 annotationLayer?.updateFakeAnnotations(needFakeAnnotation); 25615 this.#isDisabling = false;
vendor: 17,553 bytes, lines 25616-26231
25616 } 25617 getEditableAnnotation(id) { 25618 return this.#annotationLayer?.getEditableAnnotation(id) || null; 25619 } 25620 setActiveEditor(editor) { 25621 const currentActive = this.#uiManager.getActive(); 25622 if (currentActive === editor) { 25623 return; 25624 } 25625 this.#uiManager.setActiveEditor(editor); 25626 } 25627 enableTextSelection() { 25628 this.div.tabIndex = -1; 25629 if (this.#textLayer?.div && !this.#textSelectionAC) { 25630 this.#textSelectionAC = new AbortController(); 25631 const signal = this.#uiManager.combinedSignal(this.#textSelectionAC); 25632 this.#textLayer.div.addEventListener("pointerdown", this.#textLayerPointerDown.bind(this), { 25633 signal 25634 }); 25635 this.#textLayer.div.classList.add("highlighting"); 25636 } 25637 } 25638 disableTextSelection() { 25639 this.div.tabIndex = 0; 25640 if (this.#textLayer?.div && this.#textSelectionAC) { 25641 this.#textSelectionAC.abort(); 25642 this.#textSelectionAC = null; 25643 this.#textLayer.div.classList.remove("highlighting"); 25644 } 25645 } 25646 #textLayerPointerDown(event) { 25647 this.#uiManager.unselectAll(); 25648 const { 25649 target 25650 } = event; 25651 if (target === this.#textLayer.div || (target.getAttribute("role") === "img" || target.classList.contains("endOfContent")) && this.#textLayer.div.contains(target)) { 25652 const { 25653 isMac 25654 } = util_FeatureTest.platform; 25655 if (event.button !== 0 || event.ctrlKey && isMac) { 25656 return; 25657 } 25658 this.#uiManager.showAllEditors("highlight", true, true); 25659 this.#textLayer.div.classList.add("free"); 25660 this.toggleDrawing(); 25661 HighlightEditor.startHighlighting(this, this.#uiManager.direction === "ltr", { 25662 target: this.#textLayer.div, 25663 x: event.x, 25664 y: event.y 25665 }); 25666 this.#textLayer.div.addEventListener("pointerup", () => { 25667 this.#textLayer.div.classList.remove("free"); 25668 this.toggleDrawing(true); 25669 }, { 25670 once: true, 25671 signal: this.#uiManager._signal 25672 }); 25673 event.preventDefault(); 25674 } 25675 } 25676 enableClick() { 25677 if (this.#clickAC) { 25678 return; 25679 } 25680 this.#clickAC = new AbortController(); 25681 const signal = this.#uiManager.combinedSignal(this.#clickAC); 25682 this.div.addEventListener("pointerdown", this.pointerdown.bind(this), { 25683 signal 25684 }); 25685 const pointerup = this.pointerup.bind(this); 25686 this.div.addEventListener("pointerup", pointerup, { 25687 signal 25688 }); 25689 this.div.addEventListener("pointercancel", pointerup, { 25690 signal 25691 }); 25692 } 25693 disableClick() { 25694 this.#clickAC?.abort(); 25695 this.#clickAC = null; 25696 } 25697 attach(editor) { 25698 this.#editors.set(editor.id, editor); 25699 const { 25700 annotationElementId 25701 } = editor; 25702 if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) { 25703 this.#uiManager.removeDeletedAnnotationElement(editor); 25704 } 25705 } 25706 detach(editor) { 25707 this.#editors.delete(editor.id); 25708 this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); 25709 if (!this.#isDisabling && editor.annotationElementId) { 25710 this.#uiManager.addDeletedAnnotationElement(editor); 25711 } 25712 } 25713 remove(editor) { 25714 this.detach(editor); 25715 this.#uiManager.removeEditor(editor); 25716 editor.div.remove(); 25717 editor.isAttachedToDOM = false; 25718 } 25719 changeParent(editor) { 25720 if (editor.parent === this) { 25721 return; 25722 } 25723 if (editor.parent && editor.annotationElementId) { 25724 this.#uiManager.addDeletedAnnotationElement(editor); 25725 AnnotationEditor.deleteAnnotationElement(editor); 25726 editor.annotationElementId = null; 25727 } 25728 this.attach(editor); 25729 editor.parent?.detach(editor); 25730 editor.setParent(this); 25731 if (editor.div && editor.isAttachedToDOM) { 25732 editor.div.remove(); 25733 this.div.append(editor.div); 25734 } 25735 } 25736 add(editor) { 25737 if (editor.parent === this && editor.isAttachedToDOM) { 25738 return; 25739 } 25740 this.changeParent(editor); 25741 this.#uiManager.addEditor(editor); 25742 this.attach(editor); 25743 if (!editor.isAttachedToDOM) { 25744 const div = editor.render(); 25745 this.div.append(div); 25746 editor.isAttachedToDOM = true; 25747 } 25748 editor.fixAndSetPosition(); 25749 editor.onceAdded(!this.#isEnabling); 25750 this.#uiManager.addToAnnotationStorage(editor); 25751 editor._reportTelemetry(editor.telemetryInitialData); 25752 } 25753 moveEditorInDOM(editor) { 25754 if (!editor.isAttachedToDOM) { 25755 return; 25756 } 25757 const { 25758 activeElement 25759 } = document; 25760 if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) { 25761 editor._focusEventsAllowed = false; 25762 this.#editorFocusTimeoutId = setTimeout(() => { 25763 this.#editorFocusTimeoutId = null; 25764 if (!editor.div.contains(document.activeElement)) { 25765 editor.div.addEventListener("focusin", () => { 25766 editor._focusEventsAllowed = true; 25767 }, { 25768 once: true, 25769 signal: this.#uiManager._signal 25770 }); 25771 activeElement.focus(); 25772 } else { 25773 editor._focusEventsAllowed = true; 25774 } 25775 }, 0); 25776 } 25777 editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true); 25778 } 25779 addOrRebuild(editor) { 25780 if (editor.needsToBeRebuilt()) { 25781 editor.parent ||= this; 25782 editor.rebuild(); 25783 editor.show(); 25784 } else { 25785 this.add(editor); 25786 } 25787 } 25788 addUndoableEditor(editor) { 25789 const cmd = () => editor._uiManager.rebuild(editor); 25790 const undo = () => { 25791 editor.remove(); 25792 }; 25793 this.addCommands({ 25794 cmd, 25795 undo, 25796 mustExec: false 25797 }); 25798 } 25799 getEditorByUID(uid) { 25800 for (const editor of this.#editors.values()) { 25801 if (editor.uid === uid) { 25802 return editor; 25803 } 25804 } 25805 return null; 25806 } 25807 getNextId() { 25808 return this.#uiManager.getId(); 25809 } 25810 get #currentEditorType() { 25811 return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode()); 25812 } 25813 combinedSignal(ac) { 25814 return this.#uiManager.combinedSignal(ac); 25815 } 25816 #createNewEditor(params) { 25817 const editorType = this.#currentEditorType; 25818 return editorType ? new editorType.prototype.constructor(params) : null; 25819 } 25820 canCreateNewEmptyEditor() { 25821 return this.#currentEditorType?.canCreateNewEmptyEditor(); 25822 } 25823 async pasteEditor(options, params) { 25824 this.updateToolbar(options); 25825 await this.#uiManager.updateMode(options.mode); 25826 const { 25827 offsetX, 25828 offsetY 25829 } = this.#getCenterPoint(); 25830 const id = this.getNextId(); 25831 const editor = this.#createNewEditor({ 25832 parent: this, 25833 id, 25834 x: offsetX, 25835 y: offsetY, 25836 uiManager: this.#uiManager, 25837 isCentered: true, 25838 ...params 25839 }); 25840 if (editor) { 25841 this.add(editor); 25842 } 25843 } 25844 async deserialize(data) { 25845 return (await AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager)) || null; 25846 } 25847 createAndAddNewEditor(event, isCentered, data = {}) { 25848 const id = this.getNextId(); 25849 const editor = this.#createNewEditor({ 25850 parent: this, 25851 id, 25852 x: event.offsetX, 25853 y: event.offsetY, 25854 uiManager: this.#uiManager, 25855 isCentered, 25856 ...data 25857 }); 25858 if (editor) { 25859 this.add(editor); 25860 } 25861 return editor; 25862 } 25863 get boundingClientRect() { 25864 return this.div.getBoundingClientRect(); 25865 } 25866 #getCenterPoint() { 25867 const { 25868 x, 25869 y, 25870 width, 25871 height 25872 } = this.boundingClientRect; 25873 const tlX = Math.max(0, x); 25874 const tlY = Math.max(0, y); 25875 const brX = Math.min(window.innerWidth, x + width); 25876 const brY = Math.min(window.innerHeight, y + height); 25877 const centerX = (tlX + brX) / 2 - x; 25878 const centerY = (tlY + brY) / 2 - y; 25879 const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX]; 25880 return { 25881 offsetX, 25882 offsetY 25883 }; 25884 } 25885 addNewEditor(data = {}) { 25886 this.createAndAddNewEditor(this.#getCenterPoint(), true, data); 25887 } 25888 setSelected(editor) { 25889 this.#uiManager.setSelected(editor); 25890 } 25891 toggleSelected(editor) { 25892 this.#uiManager.toggleSelected(editor); 25893 } 25894 unselect(editor) { 25895 this.#uiManager.unselect(editor); 25896 } 25897 pointerup(event) { 25898 const { 25899 isMac 25900 } = util_FeatureTest.platform; 25901 if (event.button !== 0 || event.ctrlKey && isMac) { 25902 return; 25903 } 25904 if (event.target !== this.div) { 25905 return; 25906 } 25907 if (!this.#hadPointerDown) { 25908 return; 25909 } 25910 this.#hadPointerDown = false; 25911 if (this.#currentEditorType?.isDrawer && this.#currentEditorType.supportMultipleDrawings) { 25912 return; 25913 } 25914 if (!this.#allowClick) { 25915 this.#allowClick = true; 25916 return; 25917 } 25918 const currentMode = this.#uiManager.getMode(); 25919 if (currentMode === AnnotationEditorType.STAMP || currentMode === AnnotationEditorType.SIGNATURE) { 25920 this.#uiManager.unselectAll(); 25921 return; 25922 } 25923 this.createAndAddNewEditor(event, false); 25924 } 25925 pointerdown(event) { 25926 if (this.#uiManager.getMode() === AnnotationEditorType.HIGHLIGHT) { 25927 this.enableTextSelection(); 25928 } 25929 if (this.#hadPointerDown) { 25930 this.#hadPointerDown = false; 25931 return; 25932 } 25933 const { 25934 isMac 25935 } = util_FeatureTest.platform; 25936 if (event.button !== 0 || event.ctrlKey && isMac) { 25937 return; 25938 } 25939 if (event.target !== this.div) { 25940 return; 25941 } 25942 this.#hadPointerDown = true; 25943 if (this.#currentEditorType?.isDrawer) { 25944 this.startDrawingSession(event); 25945 return; 25946 } 25947 const editor = this.#uiManager.getActive(); 25948 this.#allowClick = !editor || editor.isEmpty(); 25949 } 25950 startDrawingSession(event) { 25951 this.div.focus({ 25952 preventScroll: true 25953 }); 25954 if (this.#drawingAC) { 25955 this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); 25956 return; 25957 } 25958 this.#uiManager.setCurrentDrawingSession(this); 25959 this.#drawingAC = new AbortController(); 25960 const signal = this.#uiManager.combinedSignal(this.#drawingAC); 25961 this.div.addEventListener("blur", ({ 25962 relatedTarget 25963 }) => { 25964 if (relatedTarget && !this.div.contains(relatedTarget)) { 25965 this.#focusedElement = null; 25966 this.commitOrRemove(); 25967 } 25968 }, { 25969 signal 25970 }); 25971 this.#currentEditorType.startDrawing(this, this.#uiManager, false, event); 25972 } 25973 pause(on) { 25974 if (on) { 25975 const { 25976 activeElement 25977 } = document; 25978 if (this.div.contains(activeElement)) { 25979 this.#focusedElement = activeElement; 25980 } 25981 return; 25982 } 25983 if (this.#focusedElement) { 25984 setTimeout(() => { 25985 this.#focusedElement?.focus(); 25986 this.#focusedElement = null; 25987 }, 0); 25988 } 25989 } 25990 endDrawingSession(isAborted = false) { 25991 if (!this.#drawingAC) { 25992 return null; 25993 } 25994 this.#uiManager.setCurrentDrawingSession(null); 25995 this.#drawingAC.abort(); 25996 this.#drawingAC = null; 25997 this.#focusedElement = null; 25998 return this.#currentEditorType.endDrawing(isAborted); 25999 } 26000 findNewParent(editor, x, y) { 26001 const layer = this.#uiManager.findParent(x, y); 26002 if (layer === null || layer === this) { 26003 return false; 26004 } 26005 layer.changeParent(editor); 26006 return true; 26007 } 26008 commitOrRemove() { 26009 if (this.#drawingAC) { 26010 this.endDrawingSession(); 26011 return true; 26012 } 26013 return false; 26014 } 26015 onScaleChanging() { 26016 if (!this.#drawingAC) { 26017 return; 26018 } 26019 this.#currentEditorType.onScaleChangingWhenDrawing(this); 26020 } 26021 destroy() { 26022 this.commitOrRemove(); 26023 if (this.#uiManager.getActive()?.parent === this) { 26024 this.#uiManager.commitOrRemove(); 26025 this.#uiManager.setActiveEditor(null); 26026 } 26027 if (this.#editorFocusTimeoutId) { 26028 clearTimeout(this.#editorFocusTimeoutId); 26029 this.#editorFocusTimeoutId = null; 26030 } 26031 for (const editor of this.#editors.values()) { 26032 this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv); 26033 editor.setParent(null); 26034 editor.isAttachedToDOM = false; 26035 editor.div.remove(); 26036 } 26037 this.div = null; 26038 this.#editors.clear(); 26039 this.#uiManager.removeLayer(this); 26040 } 26041 #cleanup() { 26042 for (const editor of this.#editors.values()) { 26043 if (editor.isEmpty()) { 26044 editor.remove(); 26045 } 26046 } 26047 } 26048 render({ 26049 viewport 26050 }) { 26051 this.viewport = viewport; 26052 setLayerDimensions(this.div, viewport); 26053 for (const editor of this.#uiManager.getEditors(this.pageIndex)) { 26054 this.add(editor); 26055 editor.rebuild(); 26056 } 26057 this.updateMode(); 26058 } 26059 update({ 26060 viewport 26061 }) { 26062 this.#uiManager.commitOrRemove(); 26063 this.#cleanup(); 26064 const oldRotation = this.viewport.rotation; 26065 const rotation = viewport.rotation; 26066 this.viewport = viewport; 26067 setLayerDimensions(this.div, { 26068 rotation 26069 }); 26070 if (oldRotation !== rotation) { 26071 for (const editor of this.#editors.values()) { 26072 editor.rotate(rotation); 26073 } 26074 } 26075 } 26076 get pageDimensions() { 26077 const { 26078 pageWidth, 26079 pageHeight 26080 } = this.viewport.rawDims; 26081 return [pageWidth, pageHeight]; 26082 } 26083 get scale() { 26084 return this.#uiManager.viewParameters.realScale; 26085 } 26086} 26087 26088;// ./src/display/draw_layer.js 26089 26090 26091class DrawLayer { 26092 #parent = null; 26093 #mapping = new Map(); 26094 #toUpdate = new Map(); 26095 static #id = 0; 26096 setParent(parent) { 26097 if (!this.#parent) { 26098 this.#parent = parent; 26099 return; 26100 } 26101 if (this.#parent !== parent) { 26102 if (this.#mapping.size > 0) { 26103 for (const root of this.#mapping.values()) { 26104 root.remove(); 26105 parent.append(root); 26106 } 26107 } 26108 this.#parent = parent; 26109 } 26110 } 26111 static get _svgFactory() { 26112 return shadow(this, "_svgFactory", new DOMSVGFactory()); 26113 } 26114 static #setBox(element, [x, y, width, height]) { 26115 const { 26116 style 26117 } = element; 26118 style.top = `${100 * y}%`; 26119 style.left = `${100 * x}%`; 26120 style.width = `${100 * width}%`; 26121 style.height = `${100 * height}%`; 26122 } 26123 #createSVG() { 26124 const svg = DrawLayer._svgFactory.create(1, 1, true); 26125 this.#parent.append(svg); 26126 svg.setAttribute("aria-hidden", true); 26127 return svg; 26128 } 26129 #createClipPath(defs, pathId) { 26130 const clipPath = DrawLayer._svgFactory.createElement("clipPath"); 26131 defs.append(clipPath); 26132 const clipPathId = `clip_${pathId}`; 26133 clipPath.setAttribute("id", clipPathId); 26134 clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); 26135 const clipPathUse = DrawLayer._svgFactory.createElement("use"); 26136 clipPath.append(clipPathUse); 26137 clipPathUse.setAttribute("href", `#${pathId}`); 26138 clipPathUse.classList.add("clip"); 26139 return clipPathId; 26140 } 26141 #updateProperties(element, properties) { 26142 for (const [key, value] of Object.entries(properties)) { 26143 if (value === null) { 26144 element.removeAttribute(key); 26145 } else { 26146 element.setAttribute(key, value); 26147 } 26148 } 26149 } 26150 draw(properties, isPathUpdatable = false, hasClip = false) { 26151 const id = DrawLayer.#id++; 26152 const root = this.#createSVG(); 26153 const defs = DrawLayer._svgFactory.createElement("defs"); 26154 root.append(defs); 26155 const path = DrawLayer._svgFactory.createElement("path"); 26156 defs.append(path); 26157 const pathId = `path_${id}`; 26158 path.setAttribute("id", pathId); 26159 path.setAttribute("vector-effect", "non-scaling-stroke"); 26160 if (isPathUpdatable) { 26161 this.#toUpdate.set(id, path); 26162 } 26163 const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null; 26164 const use = DrawLayer._svgFactory.createElement("use"); 26165 root.append(use); 26166 use.setAttribute("href", `#${pathId}`); 26167 this.updateProperties(root, properties); 26168 this.#mapping.set(id, root); 26169 return { 26170 id, 26171 clipPathId: `url(#${clipPathId})` 26172 }; 26173 } 26174 drawOutline(properties, mustRemoveSelfIntersections) { 26175 const id = DrawLayer.#id++; 26176 const root = this.#createSVG(); 26177 const defs = DrawLayer._svgFactory.createElement("defs"); 26178 root.append(defs); 26179 const path = DrawLayer._svgFactory.createElement("path"); 26180 defs.append(path); 26181 const pathId = `path_${id}`; 26182 path.setAttribute("id", pathId); 26183 path.setAttribute("vector-effect", "non-scaling-stroke"); 26184 let maskId; 26185 if (mustRemoveSelfIntersections) { 26186 const mask = DrawLayer._svgFactory.createElement("mask"); 26187 defs.append(mask); 26188 maskId = `mask_${id}`; 26189 mask.setAttribute("id", maskId); 26190 mask.setAttribute("maskUnits", "objectBoundingBox"); 26191 const rect = DrawLayer._svgFactory.createElement("rect"); 26192 mask.append(rect); 26193 rect.setAttribute("width", "1"); 26194 rect.setAttribute("height", "1"); 26195 rect.setAttribute("fill", "white"); 26196 const use = DrawLayer._svgFactory.createElement("use"); 26197 mask.append(use); 26198 use.setAttribute("href", `#${pathId}`); 26199 use.setAttribute("stroke", "none"); 26200 use.setAttribute("fill", "black"); 26201 use.setAttribute("fill-rule", "nonzero"); 26202 use.classList.add("mask"); 26203 } 26204 const use1 = DrawLayer._svgFactory.createElement("use"); 26205 root.append(use1); 26206 use1.setAttribute("href", `#${pathId}`); 26207 if (maskId) { 26208 use1.setAttribute("mask", `url(#${maskId})`); 26209 } 26210 const use2 = use1.cloneNode(); 26211 root.append(use2); 26212 use1.classList.add("mainOutline"); 26213 use2.classList.add("secondaryOutline"); 26214 this.updateProperties(root, properties); 26215 this.#mapping.set(id, root); 26216 return id; 26217 } 26218 finalizeDraw(id, properties) { 26219 this.#toUpdate.delete(id); 26220 this.updateProperties(id, properties); 26221 } 26222 updateProperties(elementOrId, properties) { 26223 if (!properties) { 26224 return; 26225 } 26226 const { 26227 root, 26228 bbox, 26229 rootClass, 26230 path 26231 } = properties;
vendor: 4,258 bytes, lines 26232-26369
26232 const element = typeof elementOrId === "number" ? this.#mapping.get(elementOrId) : elementOrId; 26233 if (!element) { 26234 return; 26235 } 26236 if (root) { 26237 this.#updateProperties(element, root); 26238 } 26239 if (bbox) { 26240 DrawLayer.#setBox(element, bbox); 26241 } 26242 if (rootClass) { 26243 const { 26244 classList 26245 } = element; 26246 for (const [className, value] of Object.entries(rootClass)) { 26247 classList.toggle(className, value); 26248 } 26249 } 26250 if (path) { 26251 const defs = element.firstElementChild; 26252 const pathElement = defs.firstElementChild; 26253 this.#updateProperties(pathElement, path); 26254 } 26255 } 26256 updateParent(id, layer) { 26257 if (layer === this) { 26258 return; 26259 } 26260 const root = this.#mapping.get(id); 26261 if (!root) { 26262 return; 26263 } 26264 layer.#parent.append(root); 26265 this.#mapping.delete(id); 26266 layer.#mapping.set(id, root); 26267 } 26268 remove(id) { 26269 this.#toUpdate.delete(id); 26270 if (this.#parent === null) { 26271 return; 26272 } 26273 this.#mapping.get(id).remove(); 26274 this.#mapping.delete(id); 26275 } 26276 destroy() { 26277 this.#parent = null; 26278 for (const root of this.#mapping.values()) { 26279 root.remove(); 26280 } 26281 this.#mapping.clear(); 26282 this.#toUpdate.clear(); 26283 } 26284} 26285 26286;// ./src/pdf.js 26287 26288 26289 26290 26291 26292 26293 26294 26295 26296 26297 26298 26299 26300 26301 26302 26303{ 26304 globalThis._pdfjsTestingUtils = { 26305 HighlightOutliner: HighlightOutliner 26306 }; 26307} 26308globalThis.pdfjsLib = { 26309 AbortException: AbortException, 26310 AnnotationEditorLayer: AnnotationEditorLayer, 26311 AnnotationEditorParamsType: AnnotationEditorParamsType, 26312 AnnotationEditorType: AnnotationEditorType, 26313 AnnotationEditorUIManager: AnnotationEditorUIManager, 26314 AnnotationLayer: AnnotationLayer, 26315 AnnotationMode: AnnotationMode, 26316 AnnotationType: AnnotationType, 26317 applyOpacity: applyOpacity, 26318 build: build, 26319 ColorPicker: ColorPicker, 26320 createValidAbsoluteUrl: createValidAbsoluteUrl, 26321 CSSConstants: CSSConstants, 26322 DOMSVGFactory: DOMSVGFactory, 26323 DrawLayer: DrawLayer, 26324 FeatureTest: util_FeatureTest, 26325 fetchData: fetchData, 26326 findContrastColor: findContrastColor, 26327 getDocument: getDocument, 26328 getFilenameFromUrl: getFilenameFromUrl, 26329 getPdfFilenameFromUrl: getPdfFilenameFromUrl, 26330 getRGB: getRGB, 26331 getUuid: getUuid, 26332 getXfaPageViewport: getXfaPageViewport, 26333 GlobalWorkerOptions: GlobalWorkerOptions, 26334 ImageKind: util_ImageKind, 26335 InvalidPDFException: InvalidPDFException, 26336 isDataScheme: isDataScheme, 26337 isPdfFile: isPdfFile, 26338 isValidExplicitDest: isValidExplicitDest, 26339 MathClamp: MathClamp, 26340 noContextMenu: noContextMenu, 26341 normalizeUnicode: normalizeUnicode, 26342 OPS: OPS, 26343 OutputScale: OutputScale, 26344 PagesMapper: PagesMapper, 26345 PasswordResponses: PasswordResponses, 26346 PDFDataRangeTransport: PDFDataRangeTransport, 26347 PDFDateString: PDFDateString, 26348 PDFWorker: PDFWorker, 26349 PermissionFlag: PermissionFlag, 26350 PixelsPerInch: PixelsPerInch, 26351 RenderingCancelledException: RenderingCancelledException, 26352 renderRichText: renderRichText, 26353 ResponseException: ResponseException, 26354 setLayerDimensions: setLayerDimensions, 26355 shadow: shadow, 26356 SignatureExtractor: SignatureExtractor, 26357 stopEvent: stopEvent, 26358 SupportedImageMimeTypes: SupportedImageMimeTypes, 26359 TextLayer: TextLayer, 26360 TouchManager: TouchManager, 26361 updateUrlHash: updateUrlHash, 26362 Util: Util, 26363 VerbosityLevel: VerbosityLevel, 26364 version: version, 26365 XfaLayer: XfaLayer 26366}; 26367 26368export { AbortException, AnnotationEditorLayer, AnnotationEditorParamsType, AnnotationEditorType, AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, AnnotationType, CSSConstants, ColorPicker, DOMSVGFactory, DrawLayer, util_FeatureTest as FeatureTest, GlobalWorkerOptions, util_ImageKind as ImageKind, InvalidPDFException, MathClamp, OPS, OutputScale, PDFDataRangeTransport, PDFDateString, PDFWorker, PagesMapper, PasswordResponses, PermissionFlag, PixelsPerInch, RenderingCancelledException, ResponseException, SignatureExtractor, SupportedImageMimeTypes, TextLayer, TouchManager, Util, VerbosityLevel, XfaLayer, applyOpacity, build, createValidAbsoluteUrl, fetchData, findContrastColor, getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, getRGB, getUuid, getXfaPageViewport, isDataScheme, isPdfFile, isValidExplicitDest, noContextMenu, normalizeUnicode, renderRichText, setLayerDimensions, shadow, stopEvent, updateUrlHash, version }; 26369
26370//# 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.