You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

19762 lines
595KB

  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. /******/ // The require scope
  23. /******/ var __webpack_require__ = {};
  24. /******/
  25. /************************************************************************/
  26. /******/ /* webpack/runtime/define property getters */
  27. /******/ (() => {
  28. /******/ // define getter functions for harmony exports
  29. /******/ __webpack_require__.d = (exports, definition) => {
  30. /******/ for(var key in definition) {
  31. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  32. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  33. /******/ }
  34. /******/ }
  35. /******/ };
  36. /******/ })();
  37. /******/
  38. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  39. /******/ (() => {
  40. /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
  41. /******/ })();
  42. /******/
  43. /************************************************************************/
  44. var __webpack_exports__ = globalThis.pdfjsLib = {};
  45. // EXPORTS
  46. __webpack_require__.d(__webpack_exports__, {
  47. AbortException: () => (/* reexport */ AbortException),
  48. AnnotationEditorLayer: () => (/* reexport */ AnnotationEditorLayer),
  49. AnnotationEditorParamsType: () => (/* reexport */ AnnotationEditorParamsType),
  50. AnnotationEditorType: () => (/* reexport */ AnnotationEditorType),
  51. AnnotationEditorUIManager: () => (/* reexport */ AnnotationEditorUIManager),
  52. AnnotationLayer: () => (/* reexport */ AnnotationLayer),
  53. AnnotationMode: () => (/* reexport */ AnnotationMode),
  54. CMapCompressionType: () => (/* reexport */ CMapCompressionType),
  55. ColorPicker: () => (/* reexport */ ColorPicker),
  56. DOMSVGFactory: () => (/* reexport */ DOMSVGFactory),
  57. DrawLayer: () => (/* reexport */ DrawLayer),
  58. FeatureTest: () => (/* reexport */ util_FeatureTest),
  59. GlobalWorkerOptions: () => (/* reexport */ GlobalWorkerOptions),
  60. ImageKind: () => (/* reexport */ util_ImageKind),
  61. InvalidPDFException: () => (/* reexport */ InvalidPDFException),
  62. MissingPDFException: () => (/* reexport */ MissingPDFException),
  63. OPS: () => (/* reexport */ OPS),
  64. PDFDataRangeTransport: () => (/* reexport */ PDFDataRangeTransport),
  65. PDFDateString: () => (/* reexport */ PDFDateString),
  66. PDFWorker: () => (/* reexport */ PDFWorker),
  67. PasswordResponses: () => (/* reexport */ PasswordResponses),
  68. PermissionFlag: () => (/* reexport */ PermissionFlag),
  69. PixelsPerInch: () => (/* reexport */ PixelsPerInch),
  70. RenderingCancelledException: () => (/* reexport */ RenderingCancelledException),
  71. TextLayer: () => (/* reexport */ TextLayer),
  72. UnexpectedResponseException: () => (/* reexport */ UnexpectedResponseException),
  73. Util: () => (/* reexport */ Util),
  74. VerbosityLevel: () => (/* reexport */ VerbosityLevel),
  75. XfaLayer: () => (/* reexport */ XfaLayer),
  76. build: () => (/* reexport */ build),
  77. createValidAbsoluteUrl: () => (/* reexport */ createValidAbsoluteUrl),
  78. fetchData: () => (/* reexport */ fetchData),
  79. getDocument: () => (/* reexport */ getDocument),
  80. getFilenameFromUrl: () => (/* reexport */ getFilenameFromUrl),
  81. getPdfFilenameFromUrl: () => (/* reexport */ getPdfFilenameFromUrl),
  82. getXfaPageViewport: () => (/* reexport */ getXfaPageViewport),
  83. isDataScheme: () => (/* reexport */ isDataScheme),
  84. isPdfFile: () => (/* reexport */ isPdfFile),
  85. noContextMenu: () => (/* reexport */ noContextMenu),
  86. normalizeUnicode: () => (/* reexport */ normalizeUnicode),
  87. setLayerDimensions: () => (/* reexport */ setLayerDimensions),
  88. shadow: () => (/* reexport */ shadow),
  89. version: () => (/* reexport */ version)
  90. });
  91. ;// CONCATENATED MODULE: ./src/shared/util.js
  92. const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser");
  93. const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
  94. const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
  95. const MAX_IMAGE_SIZE_TO_CACHE = 10e6;
  96. const LINE_FACTOR = 1.35;
  97. const LINE_DESCENT_FACTOR = 0.35;
  98. const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR;
  99. const RenderingIntentFlag = {
  100. ANY: 0x01,
  101. DISPLAY: 0x02,
  102. PRINT: 0x04,
  103. SAVE: 0x08,
  104. ANNOTATIONS_FORMS: 0x10,
  105. ANNOTATIONS_STORAGE: 0x20,
  106. ANNOTATIONS_DISABLE: 0x40,
  107. IS_EDITING: 0x80,
  108. OPLIST: 0x100
  109. };
  110. const AnnotationMode = {
  111. DISABLE: 0,
  112. ENABLE: 1,
  113. ENABLE_FORMS: 2,
  114. ENABLE_STORAGE: 3
  115. };
  116. const AnnotationEditorPrefix = "pdfjs_internal_editor_";
  117. const AnnotationEditorType = {
  118. DISABLE: -1,
  119. NONE: 0,
  120. FREETEXT: 3,
  121. HIGHLIGHT: 9,
  122. STAMP: 13,
  123. INK: 15
  124. };
  125. const AnnotationEditorParamsType = {
  126. RESIZE: 1,
  127. CREATE: 2,
  128. FREETEXT_SIZE: 11,
  129. FREETEXT_COLOR: 12,
  130. FREETEXT_OPACITY: 13,
  131. INK_COLOR: 21,
  132. INK_THICKNESS: 22,
  133. INK_OPACITY: 23,
  134. HIGHLIGHT_COLOR: 31,
  135. HIGHLIGHT_DEFAULT_COLOR: 32,
  136. HIGHLIGHT_THICKNESS: 33,
  137. HIGHLIGHT_FREE: 34,
  138. HIGHLIGHT_SHOW_ALL: 35
  139. };
  140. const PermissionFlag = {
  141. PRINT: 0x04,
  142. MODIFY_CONTENTS: 0x08,
  143. COPY: 0x10,
  144. MODIFY_ANNOTATIONS: 0x20,
  145. FILL_INTERACTIVE_FORMS: 0x100,
  146. COPY_FOR_ACCESSIBILITY: 0x200,
  147. ASSEMBLE: 0x400,
  148. PRINT_HIGH_QUALITY: 0x800
  149. };
  150. const TextRenderingMode = {
  151. FILL: 0,
  152. STROKE: 1,
  153. FILL_STROKE: 2,
  154. INVISIBLE: 3,
  155. FILL_ADD_TO_PATH: 4,
  156. STROKE_ADD_TO_PATH: 5,
  157. FILL_STROKE_ADD_TO_PATH: 6,
  158. ADD_TO_PATH: 7,
  159. FILL_STROKE_MASK: 3,
  160. ADD_TO_PATH_FLAG: 4
  161. };
  162. const util_ImageKind = {
  163. GRAYSCALE_1BPP: 1,
  164. RGB_24BPP: 2,
  165. RGBA_32BPP: 3
  166. };
  167. const AnnotationType = {
  168. TEXT: 1,
  169. LINK: 2,
  170. FREETEXT: 3,
  171. LINE: 4,
  172. SQUARE: 5,
  173. CIRCLE: 6,
  174. POLYGON: 7,
  175. POLYLINE: 8,
  176. HIGHLIGHT: 9,
  177. UNDERLINE: 10,
  178. SQUIGGLY: 11,
  179. STRIKEOUT: 12,
  180. STAMP: 13,
  181. CARET: 14,
  182. INK: 15,
  183. POPUP: 16,
  184. FILEATTACHMENT: 17,
  185. SOUND: 18,
  186. MOVIE: 19,
  187. WIDGET: 20,
  188. SCREEN: 21,
  189. PRINTERMARK: 22,
  190. TRAPNET: 23,
  191. WATERMARK: 24,
  192. THREED: 25,
  193. REDACT: 26
  194. };
  195. const AnnotationReplyType = {
  196. GROUP: "Group",
  197. REPLY: "R"
  198. };
  199. const AnnotationFlag = {
  200. INVISIBLE: 0x01,
  201. HIDDEN: 0x02,
  202. PRINT: 0x04,
  203. NOZOOM: 0x08,
  204. NOROTATE: 0x10,
  205. NOVIEW: 0x20,
  206. READONLY: 0x40,
  207. LOCKED: 0x80,
  208. TOGGLENOVIEW: 0x100,
  209. LOCKEDCONTENTS: 0x200
  210. };
  211. const AnnotationFieldFlag = {
  212. READONLY: 0x0000001,
  213. REQUIRED: 0x0000002,
  214. NOEXPORT: 0x0000004,
  215. MULTILINE: 0x0001000,
  216. PASSWORD: 0x0002000,
  217. NOTOGGLETOOFF: 0x0004000,
  218. RADIO: 0x0008000,
  219. PUSHBUTTON: 0x0010000,
  220. COMBO: 0x0020000,
  221. EDIT: 0x0040000,
  222. SORT: 0x0080000,
  223. FILESELECT: 0x0100000,
  224. MULTISELECT: 0x0200000,
  225. DONOTSPELLCHECK: 0x0400000,
  226. DONOTSCROLL: 0x0800000,
  227. COMB: 0x1000000,
  228. RICHTEXT: 0x2000000,
  229. RADIOSINUNISON: 0x2000000,
  230. COMMITONSELCHANGE: 0x4000000
  231. };
  232. const AnnotationBorderStyleType = {
  233. SOLID: 1,
  234. DASHED: 2,
  235. BEVELED: 3,
  236. INSET: 4,
  237. UNDERLINE: 5
  238. };
  239. const AnnotationActionEventType = {
  240. E: "Mouse Enter",
  241. X: "Mouse Exit",
  242. D: "Mouse Down",
  243. U: "Mouse Up",
  244. Fo: "Focus",
  245. Bl: "Blur",
  246. PO: "PageOpen",
  247. PC: "PageClose",
  248. PV: "PageVisible",
  249. PI: "PageInvisible",
  250. K: "Keystroke",
  251. F: "Format",
  252. V: "Validate",
  253. C: "Calculate"
  254. };
  255. const DocumentActionEventType = {
  256. WC: "WillClose",
  257. WS: "WillSave",
  258. DS: "DidSave",
  259. WP: "WillPrint",
  260. DP: "DidPrint"
  261. };
  262. const PageActionEventType = {
  263. O: "PageOpen",
  264. C: "PageClose"
  265. };
  266. const VerbosityLevel = {
  267. ERRORS: 0,
  268. WARNINGS: 1,
  269. INFOS: 5
  270. };
  271. const CMapCompressionType = {
  272. NONE: 0,
  273. BINARY: 1
  274. };
  275. const OPS = {
  276. dependency: 1,
  277. setLineWidth: 2,
  278. setLineCap: 3,
  279. setLineJoin: 4,
  280. setMiterLimit: 5,
  281. setDash: 6,
  282. setRenderingIntent: 7,
  283. setFlatness: 8,
  284. setGState: 9,
  285. save: 10,
  286. restore: 11,
  287. transform: 12,
  288. moveTo: 13,
  289. lineTo: 14,
  290. curveTo: 15,
  291. curveTo2: 16,
  292. curveTo3: 17,
  293. closePath: 18,
  294. rectangle: 19,
  295. stroke: 20,
  296. closeStroke: 21,
  297. fill: 22,
  298. eoFill: 23,
  299. fillStroke: 24,
  300. eoFillStroke: 25,
  301. closeFillStroke: 26,
  302. closeEOFillStroke: 27,
  303. endPath: 28,
  304. clip: 29,
  305. eoClip: 30,
  306. beginText: 31,
  307. endText: 32,
  308. setCharSpacing: 33,
  309. setWordSpacing: 34,
  310. setHScale: 35,
  311. setLeading: 36,
  312. setFont: 37,
  313. setTextRenderingMode: 38,
  314. setTextRise: 39,
  315. moveText: 40,
  316. setLeadingMoveText: 41,
  317. setTextMatrix: 42,
  318. nextLine: 43,
  319. showText: 44,
  320. showSpacedText: 45,
  321. nextLineShowText: 46,
  322. nextLineSetSpacingShowText: 47,
  323. setCharWidth: 48,
  324. setCharWidthAndBounds: 49,
  325. setStrokeColorSpace: 50,
  326. setFillColorSpace: 51,
  327. setStrokeColor: 52,
  328. setStrokeColorN: 53,
  329. setFillColor: 54,
  330. setFillColorN: 55,
  331. setStrokeGray: 56,
  332. setFillGray: 57,
  333. setStrokeRGBColor: 58,
  334. setFillRGBColor: 59,
  335. setStrokeCMYKColor: 60,
  336. setFillCMYKColor: 61,
  337. shadingFill: 62,
  338. beginInlineImage: 63,
  339. beginImageData: 64,
  340. endInlineImage: 65,
  341. paintXObject: 66,
  342. markPoint: 67,
  343. markPointProps: 68,
  344. beginMarkedContent: 69,
  345. beginMarkedContentProps: 70,
  346. endMarkedContent: 71,
  347. beginCompat: 72,
  348. endCompat: 73,
  349. paintFormXObjectBegin: 74,
  350. paintFormXObjectEnd: 75,
  351. beginGroup: 76,
  352. endGroup: 77,
  353. beginAnnotation: 80,
  354. endAnnotation: 81,
  355. paintImageMaskXObject: 83,
  356. paintImageMaskXObjectGroup: 84,
  357. paintImageXObject: 85,
  358. paintInlineImageXObject: 86,
  359. paintInlineImageXObjectGroup: 87,
  360. paintImageXObjectRepeat: 88,
  361. paintImageMaskXObjectRepeat: 89,
  362. paintSolidColorImageMask: 90,
  363. constructPath: 91,
  364. setStrokeTransparent: 92,
  365. setFillTransparent: 93
  366. };
  367. const PasswordResponses = {
  368. NEED_PASSWORD: 1,
  369. INCORRECT_PASSWORD: 2
  370. };
  371. let verbosity = VerbosityLevel.WARNINGS;
  372. function setVerbosityLevel(level) {
  373. if (Number.isInteger(level)) {
  374. verbosity = level;
  375. }
  376. }
  377. function getVerbosityLevel() {
  378. return verbosity;
  379. }
  380. function info(msg) {
  381. if (verbosity >= VerbosityLevel.INFOS) {
  382. console.log(`Info: ${msg}`);
  383. }
  384. }
  385. function warn(msg) {
  386. if (verbosity >= VerbosityLevel.WARNINGS) {
  387. console.log(`Warning: ${msg}`);
  388. }
  389. }
  390. function unreachable(msg) {
  391. throw new Error(msg);
  392. }
  393. function assert(cond, msg) {
  394. if (!cond) {
  395. unreachable(msg);
  396. }
  397. }
  398. function _isValidProtocol(url) {
  399. switch (url?.protocol) {
  400. case "http:":
  401. case "https:":
  402. case "ftp:":
  403. case "mailto:":
  404. case "tel:":
  405. return true;
  406. default:
  407. return false;
  408. }
  409. }
  410. function createValidAbsoluteUrl(url, baseUrl = null, options = null) {
  411. if (!url) {
  412. return null;
  413. }
  414. try {
  415. if (options && typeof url === "string") {
  416. if (options.addDefaultProtocol && url.startsWith("www.")) {
  417. const dots = url.match(/\./g);
  418. if (dots?.length >= 2) {
  419. url = `http://${url}`;
  420. }
  421. }
  422. if (options.tryConvertEncoding) {
  423. try {
  424. url = stringToUTF8String(url);
  425. } catch {}
  426. }
  427. }
  428. const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);
  429. if (_isValidProtocol(absoluteUrl)) {
  430. return absoluteUrl;
  431. }
  432. } catch {}
  433. return null;
  434. }
  435. function shadow(obj, prop, value, nonSerializable = false) {
  436. Object.defineProperty(obj, prop, {
  437. value,
  438. enumerable: !nonSerializable,
  439. configurable: true,
  440. writable: false
  441. });
  442. return value;
  443. }
  444. const BaseException = function BaseExceptionClosure() {
  445. function BaseException(message, name) {
  446. if (this.constructor === BaseException) {
  447. unreachable("Cannot initialize BaseException.");
  448. }
  449. this.message = message;
  450. this.name = name;
  451. }
  452. BaseException.prototype = new Error();
  453. BaseException.constructor = BaseException;
  454. return BaseException;
  455. }();
  456. class PasswordException extends BaseException {
  457. constructor(msg, code) {
  458. super(msg, "PasswordException");
  459. this.code = code;
  460. }
  461. }
  462. class UnknownErrorException extends BaseException {
  463. constructor(msg, details) {
  464. super(msg, "UnknownErrorException");
  465. this.details = details;
  466. }
  467. }
  468. class InvalidPDFException extends BaseException {
  469. constructor(msg) {
  470. super(msg, "InvalidPDFException");
  471. }
  472. }
  473. class MissingPDFException extends BaseException {
  474. constructor(msg) {
  475. super(msg, "MissingPDFException");
  476. }
  477. }
  478. class UnexpectedResponseException extends BaseException {
  479. constructor(msg, status) {
  480. super(msg, "UnexpectedResponseException");
  481. this.status = status;
  482. }
  483. }
  484. class FormatError extends BaseException {
  485. constructor(msg) {
  486. super(msg, "FormatError");
  487. }
  488. }
  489. class AbortException extends BaseException {
  490. constructor(msg) {
  491. super(msg, "AbortException");
  492. }
  493. }
  494. function bytesToString(bytes) {
  495. if (typeof bytes !== "object" || bytes?.length === undefined) {
  496. unreachable("Invalid argument for bytesToString");
  497. }
  498. const length = bytes.length;
  499. const MAX_ARGUMENT_COUNT = 8192;
  500. if (length < MAX_ARGUMENT_COUNT) {
  501. return String.fromCharCode.apply(null, bytes);
  502. }
  503. const strBuf = [];
  504. for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
  505. const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
  506. const chunk = bytes.subarray(i, chunkEnd);
  507. strBuf.push(String.fromCharCode.apply(null, chunk));
  508. }
  509. return strBuf.join("");
  510. }
  511. function stringToBytes(str) {
  512. if (typeof str !== "string") {
  513. unreachable("Invalid argument for stringToBytes");
  514. }
  515. const length = str.length;
  516. const bytes = new Uint8Array(length);
  517. for (let i = 0; i < length; ++i) {
  518. bytes[i] = str.charCodeAt(i) & 0xff;
  519. }
  520. return bytes;
  521. }
  522. function string32(value) {
  523. return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
  524. }
  525. function objectSize(obj) {
  526. return Object.keys(obj).length;
  527. }
  528. function objectFromMap(map) {
  529. const obj = Object.create(null);
  530. for (const [key, value] of map) {
  531. obj[key] = value;
  532. }
  533. return obj;
  534. }
  535. function isLittleEndian() {
  536. const buffer8 = new Uint8Array(4);
  537. buffer8[0] = 1;
  538. const view32 = new Uint32Array(buffer8.buffer, 0, 1);
  539. return view32[0] === 1;
  540. }
  541. function isEvalSupported() {
  542. try {
  543. new Function("");
  544. return true;
  545. } catch {
  546. return false;
  547. }
  548. }
  549. class util_FeatureTest {
  550. static get isLittleEndian() {
  551. return shadow(this, "isLittleEndian", isLittleEndian());
  552. }
  553. static get isEvalSupported() {
  554. return shadow(this, "isEvalSupported", isEvalSupported());
  555. }
  556. static get isOffscreenCanvasSupported() {
  557. return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined");
  558. }
  559. static get platform() {
  560. if (typeof navigator !== "undefined" && typeof navigator?.platform === "string") {
  561. return shadow(this, "platform", {
  562. isMac: navigator.platform.includes("Mac")
  563. });
  564. }
  565. return shadow(this, "platform", {
  566. isMac: false
  567. });
  568. }
  569. static get isCSSRoundSupported() {
  570. return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)"));
  571. }
  572. }
  573. const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0"));
  574. class Util {
  575. static makeHexColor(r, g, b) {
  576. return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;
  577. }
  578. static scaleMinMax(transform, minMax) {
  579. let temp;
  580. if (transform[0]) {
  581. if (transform[0] < 0) {
  582. temp = minMax[0];
  583. minMax[0] = minMax[2];
  584. minMax[2] = temp;
  585. }
  586. minMax[0] *= transform[0];
  587. minMax[2] *= transform[0];
  588. if (transform[3] < 0) {
  589. temp = minMax[1];
  590. minMax[1] = minMax[3];
  591. minMax[3] = temp;
  592. }
  593. minMax[1] *= transform[3];
  594. minMax[3] *= transform[3];
  595. } else {
  596. temp = minMax[0];
  597. minMax[0] = minMax[1];
  598. minMax[1] = temp;
  599. temp = minMax[2];
  600. minMax[2] = minMax[3];
  601. minMax[3] = temp;
  602. if (transform[1] < 0) {
  603. temp = minMax[1];
  604. minMax[1] = minMax[3];
  605. minMax[3] = temp;
  606. }
  607. minMax[1] *= transform[1];
  608. minMax[3] *= transform[1];
  609. if (transform[2] < 0) {
  610. temp = minMax[0];
  611. minMax[0] = minMax[2];
  612. minMax[2] = temp;
  613. }
  614. minMax[0] *= transform[2];
  615. minMax[2] *= transform[2];
  616. }
  617. minMax[0] += transform[4];
  618. minMax[1] += transform[5];
  619. minMax[2] += transform[4];
  620. minMax[3] += transform[5];
  621. }
  622. static transform(m1, m2) {
  623. 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]];
  624. }
  625. static applyTransform(p, m) {
  626. const xt = p[0] * m[0] + p[1] * m[2] + m[4];
  627. const yt = p[0] * m[1] + p[1] * m[3] + m[5];
  628. return [xt, yt];
  629. }
  630. static applyInverseTransform(p, m) {
  631. const d = m[0] * m[3] - m[1] * m[2];
  632. const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
  633. const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
  634. return [xt, yt];
  635. }
  636. static getAxialAlignedBoundingBox(r, m) {
  637. const p1 = this.applyTransform(r, m);
  638. const p2 = this.applyTransform(r.slice(2, 4), m);
  639. const p3 = this.applyTransform([r[0], r[3]], m);
  640. const p4 = this.applyTransform([r[2], r[1]], m);
  641. return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])];
  642. }
  643. static inverseTransform(m) {
  644. const d = m[0] * m[3] - m[1] * m[2];
  645. 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];
  646. }
  647. static singularValueDecompose2dScale(m) {
  648. const transpose = [m[0], m[2], m[1], m[3]];
  649. const a = m[0] * transpose[0] + m[1] * transpose[2];
  650. const b = m[0] * transpose[1] + m[1] * transpose[3];
  651. const c = m[2] * transpose[0] + m[3] * transpose[2];
  652. const d = m[2] * transpose[1] + m[3] * transpose[3];
  653. const first = (a + d) / 2;
  654. const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2;
  655. const sx = first + second || 1;
  656. const sy = first - second || 1;
  657. return [Math.sqrt(sx), Math.sqrt(sy)];
  658. }
  659. static normalizeRect(rect) {
  660. const r = rect.slice(0);
  661. if (rect[0] > rect[2]) {
  662. r[0] = rect[2];
  663. r[2] = rect[0];
  664. }
  665. if (rect[1] > rect[3]) {
  666. r[1] = rect[3];
  667. r[3] = rect[1];
  668. }
  669. return r;
  670. }
  671. static intersect(rect1, rect2) {
  672. const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2]));
  673. const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2]));
  674. if (xLow > xHigh) {
  675. return null;
  676. }
  677. const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3]));
  678. const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3]));
  679. if (yLow > yHigh) {
  680. return null;
  681. }
  682. return [xLow, yLow, xHigh, yHigh];
  683. }
  684. static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) {
  685. if (t <= 0 || t >= 1) {
  686. return;
  687. }
  688. const mt = 1 - t;
  689. const tt = t * t;
  690. const ttt = tt * t;
  691. const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3;
  692. const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3;
  693. minMax[0] = Math.min(minMax[0], x);
  694. minMax[1] = Math.min(minMax[1], y);
  695. minMax[2] = Math.max(minMax[2], x);
  696. minMax[3] = Math.max(minMax[3], y);
  697. }
  698. static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) {
  699. if (Math.abs(a) < 1e-12) {
  700. if (Math.abs(b) >= 1e-12) {
  701. this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax);
  702. }
  703. return;
  704. }
  705. const delta = b ** 2 - 4 * c * a;
  706. if (delta < 0) {
  707. return;
  708. }
  709. const sqrtDelta = Math.sqrt(delta);
  710. const a2 = 2 * a;
  711. this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax);
  712. this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax);
  713. }
  714. static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
  715. if (minMax) {
  716. minMax[0] = Math.min(minMax[0], x0, x3);
  717. minMax[1] = Math.min(minMax[1], y0, y3);
  718. minMax[2] = Math.max(minMax[2], x0, x3);
  719. minMax[3] = Math.max(minMax[3], y0, y3);
  720. } else {
  721. minMax = [Math.min(x0, x3), Math.min(y0, y3), Math.max(x0, x3), Math.max(y0, y3)];
  722. }
  723. 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);
  724. 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);
  725. return minMax;
  726. }
  727. }
  728. const PDFStringTranslateTable = (/* unused pure expression or super */ null && ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac]));
  729. function stringToPDFString(str) {
  730. if (str[0] >= "\xEF") {
  731. let encoding;
  732. if (str[0] === "\xFE" && str[1] === "\xFF") {
  733. encoding = "utf-16be";
  734. if (str.length % 2 === 1) {
  735. str = str.slice(0, -1);
  736. }
  737. } else if (str[0] === "\xFF" && str[1] === "\xFE") {
  738. encoding = "utf-16le";
  739. if (str.length % 2 === 1) {
  740. str = str.slice(0, -1);
  741. }
  742. } else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") {
  743. encoding = "utf-8";
  744. }
  745. if (encoding) {
  746. try {
  747. const decoder = new TextDecoder(encoding, {
  748. fatal: true
  749. });
  750. const buffer = stringToBytes(str);
  751. const decoded = decoder.decode(buffer);
  752. if (!decoded.includes("\x1b")) {
  753. return decoded;
  754. }
  755. return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, "");
  756. } catch (ex) {
  757. warn(`stringToPDFString: "${ex}".`);
  758. }
  759. }
  760. }
  761. const strBuf = [];
  762. for (let i = 0, ii = str.length; i < ii; i++) {
  763. const charCode = str.charCodeAt(i);
  764. if (charCode === 0x1b) {
  765. while (++i < ii && str.charCodeAt(i) !== 0x1b) {}
  766. continue;
  767. }
  768. const code = PDFStringTranslateTable[charCode];
  769. strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
  770. }
  771. return strBuf.join("");
  772. }
  773. function stringToUTF8String(str) {
  774. return decodeURIComponent(escape(str));
  775. }
  776. function utf8StringToString(str) {
  777. return unescape(encodeURIComponent(str));
  778. }
  779. function isArrayEqual(arr1, arr2) {
  780. if (arr1.length !== arr2.length) {
  781. return false;
  782. }
  783. for (let i = 0, ii = arr1.length; i < ii; i++) {
  784. if (arr1[i] !== arr2[i]) {
  785. return false;
  786. }
  787. }
  788. return true;
  789. }
  790. function getModificationDate(date = new Date()) {
  791. 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")];
  792. return buffer.join("");
  793. }
  794. let NormalizeRegex = null;
  795. let NormalizationMap = null;
  796. function normalizeUnicode(str) {
  797. if (!NormalizeRegex) {
  798. 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;
  799. NormalizationMap = new Map([["ſt", "ſt"]]);
  800. }
  801. return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2));
  802. }
  803. function getUuid() {
  804. if (typeof crypto !== "undefined" && typeof crypto?.randomUUID === "function") {
  805. return crypto.randomUUID();
  806. }
  807. const buf = new Uint8Array(32);
  808. if (typeof crypto !== "undefined" && typeof crypto?.getRandomValues === "function") {
  809. crypto.getRandomValues(buf);
  810. } else {
  811. for (let i = 0; i < 32; i++) {
  812. buf[i] = Math.floor(Math.random() * 255);
  813. }
  814. }
  815. return bytesToString(buf);
  816. }
  817. const AnnotationPrefix = "pdfjs_internal_id_";
  818. const FontRenderOps = {
  819. BEZIER_CURVE_TO: 0,
  820. MOVE_TO: 1,
  821. LINE_TO: 2,
  822. QUADRATIC_CURVE_TO: 3,
  823. RESTORE: 4,
  824. SAVE: 5,
  825. SCALE: 6,
  826. TRANSFORM: 7,
  827. TRANSLATE: 8
  828. };
  829. ;// CONCATENATED MODULE: ./src/display/base_factory.js
  830. class BaseFilterFactory {
  831. constructor() {
  832. if (this.constructor === BaseFilterFactory) {
  833. unreachable("Cannot initialize BaseFilterFactory.");
  834. }
  835. }
  836. addFilter(maps) {
  837. return "none";
  838. }
  839. addHCMFilter(fgColor, bgColor) {
  840. return "none";
  841. }
  842. addAlphaFilter(map) {
  843. return "none";
  844. }
  845. addLuminosityFilter(map) {
  846. return "none";
  847. }
  848. addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
  849. return "none";
  850. }
  851. destroy(keepHCM = false) {}
  852. }
  853. class BaseCanvasFactory {
  854. #enableHWA = false;
  855. constructor({
  856. enableHWA = false
  857. } = {}) {
  858. if (this.constructor === BaseCanvasFactory) {
  859. unreachable("Cannot initialize BaseCanvasFactory.");
  860. }
  861. this.#enableHWA = enableHWA;
  862. }
  863. create(width, height) {
  864. if (width <= 0 || height <= 0) {
  865. throw new Error("Invalid canvas size");
  866. }
  867. const canvas = this._createCanvas(width, height);
  868. return {
  869. canvas,
  870. context: canvas.getContext("2d", {
  871. willReadFrequently: !this.#enableHWA
  872. })
  873. };
  874. }
  875. reset(canvasAndContext, width, height) {
  876. if (!canvasAndContext.canvas) {
  877. throw new Error("Canvas is not specified");
  878. }
  879. if (width <= 0 || height <= 0) {
  880. throw new Error("Invalid canvas size");
  881. }
  882. canvasAndContext.canvas.width = width;
  883. canvasAndContext.canvas.height = height;
  884. }
  885. destroy(canvasAndContext) {
  886. if (!canvasAndContext.canvas) {
  887. throw new Error("Canvas is not specified");
  888. }
  889. canvasAndContext.canvas.width = 0;
  890. canvasAndContext.canvas.height = 0;
  891. canvasAndContext.canvas = null;
  892. canvasAndContext.context = null;
  893. }
  894. _createCanvas(width, height) {
  895. unreachable("Abstract method `_createCanvas` called.");
  896. }
  897. }
  898. class BaseCMapReaderFactory {
  899. constructor({
  900. baseUrl = null,
  901. isCompressed = true
  902. }) {
  903. if (this.constructor === BaseCMapReaderFactory) {
  904. unreachable("Cannot initialize BaseCMapReaderFactory.");
  905. }
  906. this.baseUrl = baseUrl;
  907. this.isCompressed = isCompressed;
  908. }
  909. async fetch({
  910. name
  911. }) {
  912. if (!this.baseUrl) {
  913. throw new Error('The CMap "baseUrl" parameter must be specified, ensure that ' + 'the "cMapUrl" and "cMapPacked" API parameters are provided.');
  914. }
  915. if (!name) {
  916. throw new Error("CMap name must be specified.");
  917. }
  918. const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : "");
  919. const compressionType = this.isCompressed ? CMapCompressionType.BINARY : CMapCompressionType.NONE;
  920. return this._fetchData(url, compressionType).catch(reason => {
  921. throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`);
  922. });
  923. }
  924. _fetchData(url, compressionType) {
  925. unreachable("Abstract method `_fetchData` called.");
  926. }
  927. }
  928. class BaseStandardFontDataFactory {
  929. constructor({
  930. baseUrl = null
  931. }) {
  932. if (this.constructor === BaseStandardFontDataFactory) {
  933. unreachable("Cannot initialize BaseStandardFontDataFactory.");
  934. }
  935. this.baseUrl = baseUrl;
  936. }
  937. async fetch({
  938. filename
  939. }) {
  940. if (!this.baseUrl) {
  941. throw new Error('The standard font "baseUrl" parameter must be specified, ensure that ' + 'the "standardFontDataUrl" API parameter is provided.');
  942. }
  943. if (!filename) {
  944. throw new Error("Font filename must be specified.");
  945. }
  946. const url = `${this.baseUrl}${filename}`;
  947. return this._fetchData(url).catch(reason => {
  948. throw new Error(`Unable to load font data at: ${url}`);
  949. });
  950. }
  951. _fetchData(url) {
  952. unreachable("Abstract method `_fetchData` called.");
  953. }
  954. }
  955. class BaseSVGFactory {
  956. constructor() {
  957. if (this.constructor === BaseSVGFactory) {
  958. unreachable("Cannot initialize BaseSVGFactory.");
  959. }
  960. }
  961. create(width, height, skipDimensions = false) {
  962. if (width <= 0 || height <= 0) {
  963. throw new Error("Invalid SVG dimensions");
  964. }
  965. const svg = this._createSVG("svg:svg");
  966. svg.setAttribute("version", "1.1");
  967. if (!skipDimensions) {
  968. svg.setAttribute("width", `${width}px`);
  969. svg.setAttribute("height", `${height}px`);
  970. }
  971. svg.setAttribute("preserveAspectRatio", "none");
  972. svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
  973. return svg;
  974. }
  975. createElement(type) {
  976. if (typeof type !== "string") {
  977. throw new Error("Invalid SVG element type");
  978. }
  979. return this._createSVG(type);
  980. }
  981. _createSVG(type) {
  982. unreachable("Abstract method `_createSVG` called.");
  983. }
  984. }
  985. ;// CONCATENATED MODULE: ./src/display/display_utils.js
  986. const SVG_NS = "http://www.w3.org/2000/svg";
  987. class PixelsPerInch {
  988. static CSS = 96.0;
  989. static PDF = 72.0;
  990. static PDF_TO_CSS_UNITS = this.CSS / this.PDF;
  991. }
  992. class DOMFilterFactory extends BaseFilterFactory {
  993. #baseUrl;
  994. #_cache;
  995. #_defs;
  996. #docId;
  997. #document;
  998. #_hcmCache;
  999. #id = 0;
  1000. constructor({
  1001. docId,
  1002. ownerDocument = globalThis.document
  1003. } = {}) {
  1004. super();
  1005. this.#docId = docId;
  1006. this.#document = ownerDocument;
  1007. }
  1008. get #cache() {
  1009. return this.#_cache ||= new Map();
  1010. }
  1011. get #hcmCache() {
  1012. return this.#_hcmCache ||= new Map();
  1013. }
  1014. get #defs() {
  1015. if (!this.#_defs) {
  1016. const div = this.#document.createElement("div");
  1017. const {
  1018. style
  1019. } = div;
  1020. style.visibility = "hidden";
  1021. style.contain = "strict";
  1022. style.width = style.height = 0;
  1023. style.position = "absolute";
  1024. style.top = style.left = 0;
  1025. style.zIndex = -1;
  1026. const svg = this.#document.createElementNS(SVG_NS, "svg");
  1027. svg.setAttribute("width", 0);
  1028. svg.setAttribute("height", 0);
  1029. this.#_defs = this.#document.createElementNS(SVG_NS, "defs");
  1030. div.append(svg);
  1031. svg.append(this.#_defs);
  1032. this.#document.body.append(div);
  1033. }
  1034. return this.#_defs;
  1035. }
  1036. #createTables(maps) {
  1037. if (maps.length === 1) {
  1038. const mapR = maps[0];
  1039. const buffer = new Array(256);
  1040. for (let i = 0; i < 256; i++) {
  1041. buffer[i] = mapR[i] / 255;
  1042. }
  1043. const table = buffer.join(",");
  1044. return [table, table, table];
  1045. }
  1046. const [mapR, mapG, mapB] = maps;
  1047. const bufferR = new Array(256);
  1048. const bufferG = new Array(256);
  1049. const bufferB = new Array(256);
  1050. for (let i = 0; i < 256; i++) {
  1051. bufferR[i] = mapR[i] / 255;
  1052. bufferG[i] = mapG[i] / 255;
  1053. bufferB[i] = mapB[i] / 255;
  1054. }
  1055. return [bufferR.join(","), bufferG.join(","), bufferB.join(",")];
  1056. }
  1057. #createUrl(id) {
  1058. if (this.#baseUrl === undefined) {
  1059. this.#baseUrl = "";
  1060. const url = this.#document.URL;
  1061. if (url !== this.#document.baseURI) {
  1062. if (isDataScheme(url)) {
  1063. warn('#createUrl: ignore "data:"-URL for performance reasons.');
  1064. } else {
  1065. this.#baseUrl = url.split("#", 1)[0];
  1066. }
  1067. }
  1068. }
  1069. return `url(${this.#baseUrl}#${id})`;
  1070. }
  1071. addFilter(maps) {
  1072. if (!maps) {
  1073. return "none";
  1074. }
  1075. let value = this.#cache.get(maps);
  1076. if (value) {
  1077. return value;
  1078. }
  1079. const [tableR, tableG, tableB] = this.#createTables(maps);
  1080. const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`;
  1081. value = this.#cache.get(key);
  1082. if (value) {
  1083. this.#cache.set(maps, value);
  1084. return value;
  1085. }
  1086. const id = `g_${this.#docId}_transfer_map_${this.#id++}`;
  1087. const url = this.#createUrl(id);
  1088. this.#cache.set(maps, url);
  1089. this.#cache.set(key, url);
  1090. const filter = this.#createFilter(id);
  1091. this.#addTransferMapConversion(tableR, tableG, tableB, filter);
  1092. return url;
  1093. }
  1094. addHCMFilter(fgColor, bgColor) {
  1095. const key = `${fgColor}-${bgColor}`;
  1096. const filterName = "base";
  1097. let info = this.#hcmCache.get(filterName);
  1098. if (info?.key === key) {
  1099. return info.url;
  1100. }
  1101. if (info) {
  1102. info.filter?.remove();
  1103. info.key = key;
  1104. info.url = "none";
  1105. info.filter = null;
  1106. } else {
  1107. info = {
  1108. key,
  1109. url: "none",
  1110. filter: null
  1111. };
  1112. this.#hcmCache.set(filterName, info);
  1113. }
  1114. if (!fgColor || !bgColor) {
  1115. return info.url;
  1116. }
  1117. const fgRGB = this.#getRGB(fgColor);
  1118. fgColor = Util.makeHexColor(...fgRGB);
  1119. const bgRGB = this.#getRGB(bgColor);
  1120. bgColor = Util.makeHexColor(...bgRGB);
  1121. this.#defs.style.color = "";
  1122. if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) {
  1123. return info.url;
  1124. }
  1125. const map = new Array(256);
  1126. for (let i = 0; i <= 255; i++) {
  1127. const x = i / 255;
  1128. map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
  1129. }
  1130. const table = map.join(",");
  1131. const id = `g_${this.#docId}_hcm_filter`;
  1132. const filter = info.filter = this.#createFilter(id);
  1133. this.#addTransferMapConversion(table, table, table, filter);
  1134. this.#addGrayConversion(filter);
  1135. const getSteps = (c, n) => {
  1136. const start = fgRGB[c] / 255;
  1137. const end = bgRGB[c] / 255;
  1138. const arr = new Array(n + 1);
  1139. for (let i = 0; i <= n; i++) {
  1140. arr[i] = start + i / n * (end - start);
  1141. }
  1142. return arr.join(",");
  1143. };
  1144. this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter);
  1145. info.url = this.#createUrl(id);
  1146. return info.url;
  1147. }
  1148. addAlphaFilter(map) {
  1149. let value = this.#cache.get(map);
  1150. if (value) {
  1151. return value;
  1152. }
  1153. const [tableA] = this.#createTables([map]);
  1154. const key = `alpha_${tableA}`;
  1155. value = this.#cache.get(key);
  1156. if (value) {
  1157. this.#cache.set(map, value);
  1158. return value;
  1159. }
  1160. const id = `g_${this.#docId}_alpha_map_${this.#id++}`;
  1161. const url = this.#createUrl(id);
  1162. this.#cache.set(map, url);
  1163. this.#cache.set(key, url);
  1164. const filter = this.#createFilter(id);
  1165. this.#addTransferMapAlphaConversion(tableA, filter);
  1166. return url;
  1167. }
  1168. addLuminosityFilter(map) {
  1169. let value = this.#cache.get(map || "luminosity");
  1170. if (value) {
  1171. return value;
  1172. }
  1173. let tableA, key;
  1174. if (map) {
  1175. [tableA] = this.#createTables([map]);
  1176. key = `luminosity_${tableA}`;
  1177. } else {
  1178. key = "luminosity";
  1179. }
  1180. value = this.#cache.get(key);
  1181. if (value) {
  1182. this.#cache.set(map, value);
  1183. return value;
  1184. }
  1185. const id = `g_${this.#docId}_luminosity_map_${this.#id++}`;
  1186. const url = this.#createUrl(id);
  1187. this.#cache.set(map, url);
  1188. this.#cache.set(key, url);
  1189. const filter = this.#createFilter(id);
  1190. this.#addLuminosityConversion(filter);
  1191. if (map) {
  1192. this.#addTransferMapAlphaConversion(tableA, filter);
  1193. }
  1194. return url;
  1195. }
  1196. addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
  1197. const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`;
  1198. let info = this.#hcmCache.get(filterName);
  1199. if (info?.key === key) {
  1200. return info.url;
  1201. }
  1202. if (info) {
  1203. info.filter?.remove();
  1204. info.key = key;
  1205. info.url = "none";
  1206. info.filter = null;
  1207. } else {
  1208. info = {
  1209. key,
  1210. url: "none",
  1211. filter: null
  1212. };
  1213. this.#hcmCache.set(filterName, info);
  1214. }
  1215. if (!fgColor || !bgColor) {
  1216. return info.url;
  1217. }
  1218. const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this));
  1219. let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]);
  1220. let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]);
  1221. let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this));
  1222. if (bgGray < fgGray) {
  1223. [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB];
  1224. }
  1225. this.#defs.style.color = "";
  1226. const getSteps = (fg, bg, n) => {
  1227. const arr = new Array(256);
  1228. const step = (bgGray - fgGray) / n;
  1229. const newStart = fg / 255;
  1230. const newStep = (bg - fg) / (255 * n);
  1231. let prev = 0;
  1232. for (let i = 0; i <= n; i++) {
  1233. const k = Math.round(fgGray + i * step);
  1234. const value = newStart + i * newStep;
  1235. for (let j = prev; j <= k; j++) {
  1236. arr[j] = value;
  1237. }
  1238. prev = k + 1;
  1239. }
  1240. for (let i = prev; i < 256; i++) {
  1241. arr[i] = arr[prev - 1];
  1242. }
  1243. return arr.join(",");
  1244. };
  1245. const id = `g_${this.#docId}_hcm_${filterName}_filter`;
  1246. const filter = info.filter = this.#createFilter(id);
  1247. this.#addGrayConversion(filter);
  1248. this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter);
  1249. info.url = this.#createUrl(id);
  1250. return info.url;
  1251. }
  1252. destroy(keepHCM = false) {
  1253. if (keepHCM && this.#hcmCache.size !== 0) {
  1254. return;
  1255. }
  1256. if (this.#_defs) {
  1257. this.#_defs.parentNode.parentNode.remove();
  1258. this.#_defs = null;
  1259. }
  1260. if (this.#_cache) {
  1261. this.#_cache.clear();
  1262. this.#_cache = null;
  1263. }
  1264. this.#id = 0;
  1265. }
  1266. #addLuminosityConversion(filter) {
  1267. const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
  1268. feColorMatrix.setAttribute("type", "matrix");
  1269. 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");
  1270. filter.append(feColorMatrix);
  1271. }
  1272. #addGrayConversion(filter) {
  1273. const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
  1274. feColorMatrix.setAttribute("type", "matrix");
  1275. 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");
  1276. filter.append(feColorMatrix);
  1277. }
  1278. #createFilter(id) {
  1279. const filter = this.#document.createElementNS(SVG_NS, "filter");
  1280. filter.setAttribute("color-interpolation-filters", "sRGB");
  1281. filter.setAttribute("id", id);
  1282. this.#defs.append(filter);
  1283. return filter;
  1284. }
  1285. #appendFeFunc(feComponentTransfer, func, table) {
  1286. const feFunc = this.#document.createElementNS(SVG_NS, func);
  1287. feFunc.setAttribute("type", "discrete");
  1288. feFunc.setAttribute("tableValues", table);
  1289. feComponentTransfer.append(feFunc);
  1290. }
  1291. #addTransferMapConversion(rTable, gTable, bTable, filter) {
  1292. const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
  1293. filter.append(feComponentTransfer);
  1294. this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable);
  1295. this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable);
  1296. this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable);
  1297. }
  1298. #addTransferMapAlphaConversion(aTable, filter) {
  1299. const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
  1300. filter.append(feComponentTransfer);
  1301. this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable);
  1302. }
  1303. #getRGB(color) {
  1304. this.#defs.style.color = color;
  1305. return getRGB(getComputedStyle(this.#defs).getPropertyValue("color"));
  1306. }
  1307. }
  1308. class DOMCanvasFactory extends BaseCanvasFactory {
  1309. constructor({
  1310. ownerDocument = globalThis.document,
  1311. enableHWA = false
  1312. } = {}) {
  1313. super({
  1314. enableHWA
  1315. });
  1316. this._document = ownerDocument;
  1317. }
  1318. _createCanvas(width, height) {
  1319. const canvas = this._document.createElement("canvas");
  1320. canvas.width = width;
  1321. canvas.height = height;
  1322. return canvas;
  1323. }
  1324. }
  1325. async function fetchData(url, type = "text") {
  1326. if (isValidFetchUrl(url, document.baseURI)) {
  1327. const response = await fetch(url);
  1328. if (!response.ok) {
  1329. throw new Error(response.statusText);
  1330. }
  1331. switch (type) {
  1332. case "arraybuffer":
  1333. return response.arrayBuffer();
  1334. case "blob":
  1335. return response.blob();
  1336. case "json":
  1337. return response.json();
  1338. }
  1339. return response.text();
  1340. }
  1341. return new Promise((resolve, reject) => {
  1342. const request = new XMLHttpRequest();
  1343. request.open("GET", url, true);
  1344. request.responseType = type;
  1345. request.onreadystatechange = () => {
  1346. if (request.readyState !== XMLHttpRequest.DONE) {
  1347. return;
  1348. }
  1349. if (request.status === 200 || request.status === 0) {
  1350. switch (type) {
  1351. case "arraybuffer":
  1352. case "blob":
  1353. case "json":
  1354. resolve(request.response);
  1355. return;
  1356. }
  1357. resolve(request.responseText);
  1358. return;
  1359. }
  1360. reject(new Error(request.statusText));
  1361. };
  1362. request.send(null);
  1363. });
  1364. }
  1365. class DOMCMapReaderFactory extends BaseCMapReaderFactory {
  1366. _fetchData(url, compressionType) {
  1367. return fetchData(url, this.isCompressed ? "arraybuffer" : "text").then(data => ({
  1368. cMapData: data instanceof ArrayBuffer ? new Uint8Array(data) : stringToBytes(data),
  1369. compressionType
  1370. }));
  1371. }
  1372. }
  1373. class DOMStandardFontDataFactory extends BaseStandardFontDataFactory {
  1374. _fetchData(url) {
  1375. return fetchData(url, "arraybuffer").then(data => new Uint8Array(data));
  1376. }
  1377. }
  1378. class DOMSVGFactory extends BaseSVGFactory {
  1379. _createSVG(type) {
  1380. return document.createElementNS(SVG_NS, type);
  1381. }
  1382. }
  1383. class PageViewport {
  1384. constructor({
  1385. viewBox,
  1386. scale,
  1387. rotation,
  1388. offsetX = 0,
  1389. offsetY = 0,
  1390. dontFlip = false
  1391. }) {
  1392. this.viewBox = viewBox;
  1393. this.scale = scale;
  1394. this.rotation = rotation;
  1395. this.offsetX = offsetX;
  1396. this.offsetY = offsetY;
  1397. const centerX = (viewBox[2] + viewBox[0]) / 2;
  1398. const centerY = (viewBox[3] + viewBox[1]) / 2;
  1399. let rotateA, rotateB, rotateC, rotateD;
  1400. rotation %= 360;
  1401. if (rotation < 0) {
  1402. rotation += 360;
  1403. }
  1404. switch (rotation) {
  1405. case 180:
  1406. rotateA = -1;
  1407. rotateB = 0;
  1408. rotateC = 0;
  1409. rotateD = 1;
  1410. break;
  1411. case 90:
  1412. rotateA = 0;
  1413. rotateB = 1;
  1414. rotateC = 1;
  1415. rotateD = 0;
  1416. break;
  1417. case 270:
  1418. rotateA = 0;
  1419. rotateB = -1;
  1420. rotateC = -1;
  1421. rotateD = 0;
  1422. break;
  1423. case 0:
  1424. rotateA = 1;
  1425. rotateB = 0;
  1426. rotateC = 0;
  1427. rotateD = -1;
  1428. break;
  1429. default:
  1430. throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees.");
  1431. }
  1432. if (dontFlip) {
  1433. rotateC = -rotateC;
  1434. rotateD = -rotateD;
  1435. }
  1436. let offsetCanvasX, offsetCanvasY;
  1437. let width, height;
  1438. if (rotateA === 0) {
  1439. offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
  1440. offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
  1441. width = (viewBox[3] - viewBox[1]) * scale;
  1442. height = (viewBox[2] - viewBox[0]) * scale;
  1443. } else {
  1444. offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
  1445. offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
  1446. width = (viewBox[2] - viewBox[0]) * scale;
  1447. height = (viewBox[3] - viewBox[1]) * scale;
  1448. }
  1449. this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY];
  1450. this.width = width;
  1451. this.height = height;
  1452. }
  1453. get rawDims() {
  1454. const {
  1455. viewBox
  1456. } = this;
  1457. return shadow(this, "rawDims", {
  1458. pageWidth: viewBox[2] - viewBox[0],
  1459. pageHeight: viewBox[3] - viewBox[1],
  1460. pageX: viewBox[0],
  1461. pageY: viewBox[1]
  1462. });
  1463. }
  1464. clone({
  1465. scale = this.scale,
  1466. rotation = this.rotation,
  1467. offsetX = this.offsetX,
  1468. offsetY = this.offsetY,
  1469. dontFlip = false
  1470. } = {}) {
  1471. return new PageViewport({
  1472. viewBox: this.viewBox.slice(),
  1473. scale,
  1474. rotation,
  1475. offsetX,
  1476. offsetY,
  1477. dontFlip
  1478. });
  1479. }
  1480. convertToViewportPoint(x, y) {
  1481. return Util.applyTransform([x, y], this.transform);
  1482. }
  1483. convertToViewportRectangle(rect) {
  1484. const topLeft = Util.applyTransform([rect[0], rect[1]], this.transform);
  1485. const bottomRight = Util.applyTransform([rect[2], rect[3]], this.transform);
  1486. return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]];
  1487. }
  1488. convertToPdfPoint(x, y) {
  1489. return Util.applyInverseTransform([x, y], this.transform);
  1490. }
  1491. }
  1492. class RenderingCancelledException extends BaseException {
  1493. constructor(msg, extraDelay = 0) {
  1494. super(msg, "RenderingCancelledException");
  1495. this.extraDelay = extraDelay;
  1496. }
  1497. }
  1498. function isDataScheme(url) {
  1499. const ii = url.length;
  1500. let i = 0;
  1501. while (i < ii && url[i].trim() === "") {
  1502. i++;
  1503. }
  1504. return url.substring(i, i + 5).toLowerCase() === "data:";
  1505. }
  1506. function isPdfFile(filename) {
  1507. return typeof filename === "string" && /\.pdf$/i.test(filename);
  1508. }
  1509. function getFilenameFromUrl(url) {
  1510. [url] = url.split(/[#?]/, 1);
  1511. return url.substring(url.lastIndexOf("/") + 1);
  1512. }
  1513. function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") {
  1514. if (typeof url !== "string") {
  1515. return defaultFilename;
  1516. }
  1517. if (isDataScheme(url)) {
  1518. warn('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.');
  1519. return defaultFilename;
  1520. }
  1521. const reURI = /^(?:(?:[^:]+:)?\/\/[^/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
  1522. const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
  1523. const splitURI = reURI.exec(url);
  1524. let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
  1525. if (suggestedFilename) {
  1526. suggestedFilename = suggestedFilename[0];
  1527. if (suggestedFilename.includes("%")) {
  1528. try {
  1529. suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];
  1530. } catch {}
  1531. }
  1532. }
  1533. return suggestedFilename || defaultFilename;
  1534. }
  1535. class StatTimer {
  1536. started = Object.create(null);
  1537. times = [];
  1538. time(name) {
  1539. if (name in this.started) {
  1540. warn(`Timer is already running for ${name}`);
  1541. }
  1542. this.started[name] = Date.now();
  1543. }
  1544. timeEnd(name) {
  1545. if (!(name in this.started)) {
  1546. warn(`Timer has not been started for ${name}`);
  1547. }
  1548. this.times.push({
  1549. name,
  1550. start: this.started[name],
  1551. end: Date.now()
  1552. });
  1553. delete this.started[name];
  1554. }
  1555. toString() {
  1556. const outBuf = [];
  1557. let longest = 0;
  1558. for (const {
  1559. name
  1560. } of this.times) {
  1561. longest = Math.max(name.length, longest);
  1562. }
  1563. for (const {
  1564. name,
  1565. start,
  1566. end
  1567. } of this.times) {
  1568. outBuf.push(`${name.padEnd(longest)} ${end - start}ms\n`);
  1569. }
  1570. return outBuf.join("");
  1571. }
  1572. }
  1573. function isValidFetchUrl(url, baseUrl) {
  1574. try {
  1575. const {
  1576. protocol
  1577. } = baseUrl ? new URL(url, baseUrl) : new URL(url);
  1578. return protocol === "http:" || protocol === "https:";
  1579. } catch {
  1580. return false;
  1581. }
  1582. }
  1583. function noContextMenu(e) {
  1584. e.preventDefault();
  1585. }
  1586. function deprecated(details) {
  1587. console.log("Deprecated API usage: " + details);
  1588. }
  1589. let pdfDateStringRegex;
  1590. class PDFDateString {
  1591. static toDateObject(input) {
  1592. if (!input || typeof input !== "string") {
  1593. return null;
  1594. }
  1595. pdfDateStringRegex ||= new RegExp("^D:" + "(\\d{4})" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "([Z|+|-])?" + "(\\d{2})?" + "'?" + "(\\d{2})?" + "'?");
  1596. const matches = pdfDateStringRegex.exec(input);
  1597. if (!matches) {
  1598. return null;
  1599. }
  1600. const year = parseInt(matches[1], 10);
  1601. let month = parseInt(matches[2], 10);
  1602. month = month >= 1 && month <= 12 ? month - 1 : 0;
  1603. let day = parseInt(matches[3], 10);
  1604. day = day >= 1 && day <= 31 ? day : 1;
  1605. let hour = parseInt(matches[4], 10);
  1606. hour = hour >= 0 && hour <= 23 ? hour : 0;
  1607. let minute = parseInt(matches[5], 10);
  1608. minute = minute >= 0 && minute <= 59 ? minute : 0;
  1609. let second = parseInt(matches[6], 10);
  1610. second = second >= 0 && second <= 59 ? second : 0;
  1611. const universalTimeRelation = matches[7] || "Z";
  1612. let offsetHour = parseInt(matches[8], 10);
  1613. offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0;
  1614. let offsetMinute = parseInt(matches[9], 10) || 0;
  1615. offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0;
  1616. if (universalTimeRelation === "-") {
  1617. hour += offsetHour;
  1618. minute += offsetMinute;
  1619. } else if (universalTimeRelation === "+") {
  1620. hour -= offsetHour;
  1621. minute -= offsetMinute;
  1622. }
  1623. return new Date(Date.UTC(year, month, day, hour, minute, second));
  1624. }
  1625. }
  1626. function getXfaPageViewport(xfaPage, {
  1627. scale = 1,
  1628. rotation = 0
  1629. }) {
  1630. const {
  1631. width,
  1632. height
  1633. } = xfaPage.attributes.style;
  1634. const viewBox = [0, 0, parseInt(width), parseInt(height)];
  1635. return new PageViewport({
  1636. viewBox,
  1637. scale,
  1638. rotation
  1639. });
  1640. }
  1641. function getRGB(color) {
  1642. if (color.startsWith("#")) {
  1643. const colorRGB = parseInt(color.slice(1), 16);
  1644. return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff];
  1645. }
  1646. if (color.startsWith("rgb(")) {
  1647. return color.slice(4, -1).split(",").map(x => parseInt(x));
  1648. }
  1649. if (color.startsWith("rgba(")) {
  1650. return color.slice(5, -1).split(",").map(x => parseInt(x)).slice(0, 3);
  1651. }
  1652. warn(`Not a valid color format: "${color}"`);
  1653. return [0, 0, 0];
  1654. }
  1655. function getColorValues(colors) {
  1656. const span = document.createElement("span");
  1657. span.style.visibility = "hidden";
  1658. document.body.append(span);
  1659. for (const name of colors.keys()) {
  1660. span.style.color = name;
  1661. const computedColor = window.getComputedStyle(span).color;
  1662. colors.set(name, getRGB(computedColor));
  1663. }
  1664. span.remove();
  1665. }
  1666. function getCurrentTransform(ctx) {
  1667. const {
  1668. a,
  1669. b,
  1670. c,
  1671. d,
  1672. e,
  1673. f
  1674. } = ctx.getTransform();
  1675. return [a, b, c, d, e, f];
  1676. }
  1677. function getCurrentTransformInverse(ctx) {
  1678. const {
  1679. a,
  1680. b,
  1681. c,
  1682. d,
  1683. e,
  1684. f
  1685. } = ctx.getTransform().invertSelf();
  1686. return [a, b, c, d, e, f];
  1687. }
  1688. function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) {
  1689. if (viewport instanceof PageViewport) {
  1690. const {
  1691. pageWidth,
  1692. pageHeight
  1693. } = viewport.rawDims;
  1694. const {
  1695. style
  1696. } = div;
  1697. const useRound = util_FeatureTest.isCSSRoundSupported;
  1698. const w = `var(--scale-factor) * ${pageWidth}px`,
  1699. h = `var(--scale-factor) * ${pageHeight}px`;
  1700. const widthStr = useRound ? `round(${w}, 1px)` : `calc(${w})`,
  1701. heightStr = useRound ? `round(${h}, 1px)` : `calc(${h})`;
  1702. if (!mustFlip || viewport.rotation % 180 === 0) {
  1703. style.width = widthStr;
  1704. style.height = heightStr;
  1705. } else {
  1706. style.width = heightStr;
  1707. style.height = widthStr;
  1708. }
  1709. }
  1710. if (mustRotate) {
  1711. div.setAttribute("data-main-rotation", viewport.rotation);
  1712. }
  1713. }
  1714. ;// CONCATENATED MODULE: ./src/display/editor/toolbar.js
  1715. class EditorToolbar {
  1716. #toolbar = null;
  1717. #colorPicker = null;
  1718. #editor;
  1719. #buttons = null;
  1720. constructor(editor) {
  1721. this.#editor = editor;
  1722. }
  1723. render() {
  1724. const editToolbar = this.#toolbar = document.createElement("div");
  1725. editToolbar.className = "editToolbar";
  1726. editToolbar.setAttribute("role", "toolbar");
  1727. const signal = this.#editor._uiManager._signal;
  1728. editToolbar.addEventListener("contextmenu", noContextMenu, {
  1729. signal
  1730. });
  1731. editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, {
  1732. signal
  1733. });
  1734. const buttons = this.#buttons = document.createElement("div");
  1735. buttons.className = "buttons";
  1736. editToolbar.append(buttons);
  1737. const position = this.#editor.toolbarPosition;
  1738. if (position) {
  1739. const {
  1740. style
  1741. } = editToolbar;
  1742. const x = this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0];
  1743. style.insetInlineEnd = `${100 * x}%`;
  1744. style.top = `calc(${100 * position[1]}% + var(--editor-toolbar-vert-offset))`;
  1745. }
  1746. this.#addDeleteButton();
  1747. return editToolbar;
  1748. }
  1749. static #pointerDown(e) {
  1750. e.stopPropagation();
  1751. }
  1752. #focusIn(e) {
  1753. this.#editor._focusEventsAllowed = false;
  1754. e.preventDefault();
  1755. e.stopPropagation();
  1756. }
  1757. #focusOut(e) {
  1758. this.#editor._focusEventsAllowed = true;
  1759. e.preventDefault();
  1760. e.stopPropagation();
  1761. }
  1762. #addListenersToElement(element) {
  1763. const signal = this.#editor._uiManager._signal;
  1764. element.addEventListener("focusin", this.#focusIn.bind(this), {
  1765. capture: true,
  1766. signal
  1767. });
  1768. element.addEventListener("focusout", this.#focusOut.bind(this), {
  1769. capture: true,
  1770. signal
  1771. });
  1772. element.addEventListener("contextmenu", noContextMenu, {
  1773. signal
  1774. });
  1775. }
  1776. hide() {
  1777. this.#toolbar.classList.add("hidden");
  1778. this.#colorPicker?.hideDropdown();
  1779. }
  1780. show() {
  1781. this.#toolbar.classList.remove("hidden");
  1782. }
  1783. #addDeleteButton() {
  1784. const button = document.createElement("button");
  1785. button.className = "delete";
  1786. button.tabIndex = 0;
  1787. button.setAttribute("data-l10n-id", `pdfjs-editor-remove-${this.#editor.editorType}-button`);
  1788. this.#addListenersToElement(button);
  1789. button.addEventListener("click", e => {
  1790. this.#editor._uiManager.delete();
  1791. }, {
  1792. signal: this.#editor._uiManager._signal
  1793. });
  1794. this.#buttons.append(button);
  1795. }
  1796. get #divider() {
  1797. const divider = document.createElement("div");
  1798. divider.className = "divider";
  1799. return divider;
  1800. }
  1801. addAltTextButton(button) {
  1802. this.#addListenersToElement(button);
  1803. this.#buttons.prepend(button, this.#divider);
  1804. }
  1805. addColorPicker(colorPicker) {
  1806. this.#colorPicker = colorPicker;
  1807. const button = colorPicker.renderButton();
  1808. this.#addListenersToElement(button);
  1809. this.#buttons.prepend(button, this.#divider);
  1810. }
  1811. remove() {
  1812. this.#toolbar.remove();
  1813. this.#colorPicker?.destroy();
  1814. this.#colorPicker = null;
  1815. }
  1816. }
  1817. class HighlightToolbar {
  1818. #buttons = null;
  1819. #toolbar = null;
  1820. #uiManager;
  1821. constructor(uiManager) {
  1822. this.#uiManager = uiManager;
  1823. }
  1824. #render() {
  1825. const editToolbar = this.#toolbar = document.createElement("div");
  1826. editToolbar.className = "editToolbar";
  1827. editToolbar.setAttribute("role", "toolbar");
  1828. editToolbar.addEventListener("contextmenu", noContextMenu, {
  1829. signal: this.#uiManager._signal
  1830. });
  1831. const buttons = this.#buttons = document.createElement("div");
  1832. buttons.className = "buttons";
  1833. editToolbar.append(buttons);
  1834. this.#addHighlightButton();
  1835. return editToolbar;
  1836. }
  1837. #getLastPoint(boxes, isLTR) {
  1838. let lastY = 0;
  1839. let lastX = 0;
  1840. for (const box of boxes) {
  1841. const y = box.y + box.height;
  1842. if (y < lastY) {
  1843. continue;
  1844. }
  1845. const x = box.x + (isLTR ? box.width : 0);
  1846. if (y > lastY) {
  1847. lastX = x;
  1848. lastY = y;
  1849. continue;
  1850. }
  1851. if (isLTR) {
  1852. if (x > lastX) {
  1853. lastX = x;
  1854. }
  1855. } else if (x < lastX) {
  1856. lastX = x;
  1857. }
  1858. }
  1859. return [isLTR ? 1 - lastX : lastX, lastY];
  1860. }
  1861. show(parent, boxes, isLTR) {
  1862. const [x, y] = this.#getLastPoint(boxes, isLTR);
  1863. const {
  1864. style
  1865. } = this.#toolbar ||= this.#render();
  1866. parent.append(this.#toolbar);
  1867. style.insetInlineEnd = `${100 * x}%`;
  1868. style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`;
  1869. }
  1870. hide() {
  1871. this.#toolbar.remove();
  1872. }
  1873. #addHighlightButton() {
  1874. const button = document.createElement("button");
  1875. button.className = "highlightButton";
  1876. button.tabIndex = 0;
  1877. button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button1`);
  1878. const span = document.createElement("span");
  1879. button.append(span);
  1880. span.className = "visuallyHidden";
  1881. span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label");
  1882. const signal = this.#uiManager._signal;
  1883. button.addEventListener("contextmenu", noContextMenu, {
  1884. signal
  1885. });
  1886. button.addEventListener("click", () => {
  1887. this.#uiManager.highlightSelection("floating_button");
  1888. }, {
  1889. signal
  1890. });
  1891. this.#buttons.append(button);
  1892. }
  1893. }
  1894. ;// CONCATENATED MODULE: ./src/display/editor/tools.js
  1895. function bindEvents(obj, element, names) {
  1896. for (const name of names) {
  1897. element.addEventListener(name, obj[name].bind(obj));
  1898. }
  1899. }
  1900. function opacityToHex(opacity) {
  1901. return Math.round(Math.min(255, Math.max(1, 255 * opacity))).toString(16).padStart(2, "0");
  1902. }
  1903. class IdManager {
  1904. #id = 0;
  1905. get id() {
  1906. return `${AnnotationEditorPrefix}${this.#id++}`;
  1907. }
  1908. }
  1909. class ImageManager {
  1910. #baseId = getUuid();
  1911. #id = 0;
  1912. #cache = null;
  1913. static get _isSVGFittingCanvas() {
  1914. 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>`;
  1915. const canvas = new OffscreenCanvas(1, 3);
  1916. const ctx = canvas.getContext("2d", {
  1917. willReadFrequently: true
  1918. });
  1919. const image = new Image();
  1920. image.src = svg;
  1921. const promise = image.decode().then(() => {
  1922. ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3);
  1923. return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0;
  1924. });
  1925. return shadow(this, "_isSVGFittingCanvas", promise);
  1926. }
  1927. async #get(key, rawData) {
  1928. this.#cache ||= new Map();
  1929. let data = this.#cache.get(key);
  1930. if (data === null) {
  1931. return null;
  1932. }
  1933. if (data?.bitmap) {
  1934. data.refCounter += 1;
  1935. return data;
  1936. }
  1937. try {
  1938. data ||= {
  1939. bitmap: null,
  1940. id: `image_${this.#baseId}_${this.#id++}`,
  1941. refCounter: 0,
  1942. isSvg: false
  1943. };
  1944. let image;
  1945. if (typeof rawData === "string") {
  1946. data.url = rawData;
  1947. image = await fetchData(rawData, "blob");
  1948. } else {
  1949. image = data.file = rawData;
  1950. }
  1951. if (image.type === "image/svg+xml") {
  1952. const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas;
  1953. const fileReader = new FileReader();
  1954. const imageElement = new Image();
  1955. const imagePromise = new Promise((resolve, reject) => {
  1956. imageElement.onload = () => {
  1957. data.bitmap = imageElement;
  1958. data.isSvg = true;
  1959. resolve();
  1960. };
  1961. fileReader.onload = async () => {
  1962. const url = data.svgUrl = fileReader.result;
  1963. imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url;
  1964. };
  1965. imageElement.onerror = fileReader.onerror = reject;
  1966. });
  1967. fileReader.readAsDataURL(image);
  1968. await imagePromise;
  1969. } else {
  1970. data.bitmap = await createImageBitmap(image);
  1971. }
  1972. data.refCounter = 1;
  1973. } catch (e) {
  1974. console.error(e);
  1975. data = null;
  1976. }
  1977. this.#cache.set(key, data);
  1978. if (data) {
  1979. this.#cache.set(data.id, data);
  1980. }
  1981. return data;
  1982. }
  1983. async getFromFile(file) {
  1984. const {
  1985. lastModified,
  1986. name,
  1987. size,
  1988. type
  1989. } = file;
  1990. return this.#get(`${lastModified}_${name}_${size}_${type}`, file);
  1991. }
  1992. async getFromUrl(url) {
  1993. return this.#get(url, url);
  1994. }
  1995. async getFromId(id) {
  1996. this.#cache ||= new Map();
  1997. const data = this.#cache.get(id);
  1998. if (!data) {
  1999. return null;
  2000. }
  2001. if (data.bitmap) {
  2002. data.refCounter += 1;
  2003. return data;
  2004. }
  2005. if (data.file) {
  2006. return this.getFromFile(data.file);
  2007. }
  2008. return this.getFromUrl(data.url);
  2009. }
  2010. getSvgUrl(id) {
  2011. const data = this.#cache.get(id);
  2012. if (!data?.isSvg) {
  2013. return null;
  2014. }
  2015. return data.svgUrl;
  2016. }
  2017. deleteId(id) {
  2018. this.#cache ||= new Map();
  2019. const data = this.#cache.get(id);
  2020. if (!data) {
  2021. return;
  2022. }
  2023. data.refCounter -= 1;
  2024. if (data.refCounter !== 0) {
  2025. return;
  2026. }
  2027. data.bitmap = null;
  2028. }
  2029. isValidId(id) {
  2030. return id.startsWith(`image_${this.#baseId}_`);
  2031. }
  2032. }
  2033. class CommandManager {
  2034. #commands = [];
  2035. #locked = false;
  2036. #maxSize;
  2037. #position = -1;
  2038. constructor(maxSize = 128) {
  2039. this.#maxSize = maxSize;
  2040. }
  2041. add({
  2042. cmd,
  2043. undo,
  2044. post,
  2045. mustExec,
  2046. type = NaN,
  2047. overwriteIfSameType = false,
  2048. keepUndo = false
  2049. }) {
  2050. if (mustExec) {
  2051. cmd();
  2052. }
  2053. if (this.#locked) {
  2054. return;
  2055. }
  2056. const save = {
  2057. cmd,
  2058. undo,
  2059. post,
  2060. type
  2061. };
  2062. if (this.#position === -1) {
  2063. if (this.#commands.length > 0) {
  2064. this.#commands.length = 0;
  2065. }
  2066. this.#position = 0;
  2067. this.#commands.push(save);
  2068. return;
  2069. }
  2070. if (overwriteIfSameType && this.#commands[this.#position].type === type) {
  2071. if (keepUndo) {
  2072. save.undo = this.#commands[this.#position].undo;
  2073. }
  2074. this.#commands[this.#position] = save;
  2075. return;
  2076. }
  2077. const next = this.#position + 1;
  2078. if (next === this.#maxSize) {
  2079. this.#commands.splice(0, 1);
  2080. } else {
  2081. this.#position = next;
  2082. if (next < this.#commands.length) {
  2083. this.#commands.splice(next);
  2084. }
  2085. }
  2086. this.#commands.push(save);
  2087. }
  2088. undo() {
  2089. if (this.#position === -1) {
  2090. return;
  2091. }
  2092. this.#locked = true;
  2093. const {
  2094. undo,
  2095. post
  2096. } = this.#commands[this.#position];
  2097. undo();
  2098. post?.();
  2099. this.#locked = false;
  2100. this.#position -= 1;
  2101. }
  2102. redo() {
  2103. if (this.#position < this.#commands.length - 1) {
  2104. this.#position += 1;
  2105. this.#locked = true;
  2106. const {
  2107. cmd,
  2108. post
  2109. } = this.#commands[this.#position];
  2110. cmd();
  2111. post?.();
  2112. this.#locked = false;
  2113. }
  2114. }
  2115. hasSomethingToUndo() {
  2116. return this.#position !== -1;
  2117. }
  2118. hasSomethingToRedo() {
  2119. return this.#position < this.#commands.length - 1;
  2120. }
  2121. destroy() {
  2122. this.#commands = null;
  2123. }
  2124. }
  2125. class KeyboardManager {
  2126. constructor(callbacks) {
  2127. this.buffer = [];
  2128. this.callbacks = new Map();
  2129. this.allKeys = new Set();
  2130. const {
  2131. isMac
  2132. } = util_FeatureTest.platform;
  2133. for (const [keys, callback, options = {}] of callbacks) {
  2134. for (const key of keys) {
  2135. const isMacKey = key.startsWith("mac+");
  2136. if (isMac && isMacKey) {
  2137. this.callbacks.set(key.slice(4), {
  2138. callback,
  2139. options
  2140. });
  2141. this.allKeys.add(key.split("+").at(-1));
  2142. } else if (!isMac && !isMacKey) {
  2143. this.callbacks.set(key, {
  2144. callback,
  2145. options
  2146. });
  2147. this.allKeys.add(key.split("+").at(-1));
  2148. }
  2149. }
  2150. }
  2151. }
  2152. #serialize(event) {
  2153. if (event.altKey) {
  2154. this.buffer.push("alt");
  2155. }
  2156. if (event.ctrlKey) {
  2157. this.buffer.push("ctrl");
  2158. }
  2159. if (event.metaKey) {
  2160. this.buffer.push("meta");
  2161. }
  2162. if (event.shiftKey) {
  2163. this.buffer.push("shift");
  2164. }
  2165. this.buffer.push(event.key);
  2166. const str = this.buffer.join("+");
  2167. this.buffer.length = 0;
  2168. return str;
  2169. }
  2170. exec(self, event) {
  2171. if (!this.allKeys.has(event.key)) {
  2172. return;
  2173. }
  2174. const info = this.callbacks.get(this.#serialize(event));
  2175. if (!info) {
  2176. return;
  2177. }
  2178. const {
  2179. callback,
  2180. options: {
  2181. bubbles = false,
  2182. args = [],
  2183. checker = null
  2184. }
  2185. } = info;
  2186. if (checker && !checker(self, event)) {
  2187. return;
  2188. }
  2189. callback.bind(self, ...args, event)();
  2190. if (!bubbles) {
  2191. event.stopPropagation();
  2192. event.preventDefault();
  2193. }
  2194. }
  2195. }
  2196. class ColorManager {
  2197. static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]);
  2198. get _colors() {
  2199. const colors = new Map([["CanvasText", null], ["Canvas", null]]);
  2200. getColorValues(colors);
  2201. return shadow(this, "_colors", colors);
  2202. }
  2203. convert(color) {
  2204. const rgb = getRGB(color);
  2205. if (!window.matchMedia("(forced-colors: active)").matches) {
  2206. return rgb;
  2207. }
  2208. for (const [name, RGB] of this._colors) {
  2209. if (RGB.every((x, i) => x === rgb[i])) {
  2210. return ColorManager._colorsMapping.get(name);
  2211. }
  2212. }
  2213. return rgb;
  2214. }
  2215. getHexCode(name) {
  2216. const rgb = this._colors.get(name);
  2217. if (!rgb) {
  2218. return name;
  2219. }
  2220. return Util.makeHexColor(...rgb);
  2221. }
  2222. }
  2223. class AnnotationEditorUIManager {
  2224. #abortController = new AbortController();
  2225. #activeEditor = null;
  2226. #allEditors = new Map();
  2227. #allLayers = new Map();
  2228. #altTextManager = null;
  2229. #annotationStorage = null;
  2230. #changedExistingAnnotations = null;
  2231. #commandManager = new CommandManager();
  2232. #currentPageIndex = 0;
  2233. #deletedAnnotationsElementIds = new Set();
  2234. #draggingEditors = null;
  2235. #editorTypes = null;
  2236. #editorsToRescale = new Set();
  2237. #enableHighlightFloatingButton = false;
  2238. #enableUpdatedAddImage = false;
  2239. #filterFactory = null;
  2240. #focusMainContainerTimeoutId = null;
  2241. #highlightColors = null;
  2242. #highlightWhenShiftUp = false;
  2243. #highlightToolbar = null;
  2244. #idManager = new IdManager();
  2245. #isEnabled = false;
  2246. #isWaiting = false;
  2247. #lastActiveElement = null;
  2248. #mainHighlightColorPicker = null;
  2249. #mlManager = null;
  2250. #mode = AnnotationEditorType.NONE;
  2251. #selectedEditors = new Set();
  2252. #selectedTextNode = null;
  2253. #pageColors = null;
  2254. #showAllStates = null;
  2255. #boundBlur = this.blur.bind(this);
  2256. #boundFocus = this.focus.bind(this);
  2257. #boundCopy = this.copy.bind(this);
  2258. #boundCut = this.cut.bind(this);
  2259. #boundPaste = this.paste.bind(this);
  2260. #boundKeydown = this.keydown.bind(this);
  2261. #boundKeyup = this.keyup.bind(this);
  2262. #boundOnEditingAction = this.onEditingAction.bind(this);
  2263. #boundOnPageChanging = this.onPageChanging.bind(this);
  2264. #boundOnScaleChanging = this.onScaleChanging.bind(this);
  2265. #boundOnRotationChanging = this.onRotationChanging.bind(this);
  2266. #previousStates = {
  2267. isEditing: false,
  2268. isEmpty: true,
  2269. hasSomethingToUndo: false,
  2270. hasSomethingToRedo: false,
  2271. hasSelectedEditor: false,
  2272. hasSelectedText: false
  2273. };
  2274. #translation = [0, 0];
  2275. #translationTimeoutId = null;
  2276. #container = null;
  2277. #viewer = null;
  2278. static TRANSLATE_SMALL = 1;
  2279. static TRANSLATE_BIG = 10;
  2280. static get _keyboardManager() {
  2281. const proto = AnnotationEditorUIManager.prototype;
  2282. const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl();
  2283. const textInputChecker = (_self, {
  2284. target: el
  2285. }) => {
  2286. if (el instanceof HTMLInputElement) {
  2287. const {
  2288. type
  2289. } = el;
  2290. return type !== "text" && type !== "number";
  2291. }
  2292. return true;
  2293. };
  2294. const small = this.TRANSLATE_SMALL;
  2295. const big = this.TRANSLATE_BIG;
  2296. return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, {
  2297. checker: textInputChecker
  2298. }], [["ctrl+z", "mac+meta+z"], proto.undo, {
  2299. checker: textInputChecker
  2300. }], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, {
  2301. checker: textInputChecker
  2302. }], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, {
  2303. checker: textInputChecker
  2304. }], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, {
  2305. checker: (self, {
  2306. target: el
  2307. }) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled
  2308. }], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, {
  2309. checker: (self, {
  2310. target: el
  2311. }) => !(el instanceof HTMLButtonElement) && self.#container.contains(document.activeElement)
  2312. }], [["Escape", "mac+Escape"], proto.unselectAll], [["ArrowLeft", "mac+ArrowLeft"], proto.translateSelectedEditors, {
  2313. args: [-small, 0],
  2314. checker: arrowChecker
  2315. }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto.translateSelectedEditors, {
  2316. args: [-big, 0],
  2317. checker: arrowChecker
  2318. }], [["ArrowRight", "mac+ArrowRight"], proto.translateSelectedEditors, {
  2319. args: [small, 0],
  2320. checker: arrowChecker
  2321. }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto.translateSelectedEditors, {
  2322. args: [big, 0],
  2323. checker: arrowChecker
  2324. }], [["ArrowUp", "mac+ArrowUp"], proto.translateSelectedEditors, {
  2325. args: [0, -small],
  2326. checker: arrowChecker
  2327. }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto.translateSelectedEditors, {
  2328. args: [0, -big],
  2329. checker: arrowChecker
  2330. }], [["ArrowDown", "mac+ArrowDown"], proto.translateSelectedEditors, {
  2331. args: [0, small],
  2332. checker: arrowChecker
  2333. }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto.translateSelectedEditors, {
  2334. args: [0, big],
  2335. checker: arrowChecker
  2336. }]]));
  2337. }
  2338. constructor(container, viewer, altTextManager, eventBus, pdfDocument, pageColors, highlightColors, enableHighlightFloatingButton, enableUpdatedAddImage, mlManager) {
  2339. this._signal = this.#abortController.signal;
  2340. this.#container = container;
  2341. this.#viewer = viewer;
  2342. this.#altTextManager = altTextManager;
  2343. this._eventBus = eventBus;
  2344. this._eventBus._on("editingaction", this.#boundOnEditingAction);
  2345. this._eventBus._on("pagechanging", this.#boundOnPageChanging);
  2346. this._eventBus._on("scalechanging", this.#boundOnScaleChanging);
  2347. this._eventBus._on("rotationchanging", this.#boundOnRotationChanging);
  2348. this.#addSelectionListener();
  2349. this.#addDragAndDropListeners();
  2350. this.#addKeyboardManager();
  2351. this.#annotationStorage = pdfDocument.annotationStorage;
  2352. this.#filterFactory = pdfDocument.filterFactory;
  2353. this.#pageColors = pageColors;
  2354. this.#highlightColors = highlightColors || null;
  2355. this.#enableHighlightFloatingButton = enableHighlightFloatingButton;
  2356. this.#enableUpdatedAddImage = enableUpdatedAddImage;
  2357. this.#mlManager = mlManager || null;
  2358. this.viewParameters = {
  2359. realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
  2360. rotation: 0
  2361. };
  2362. this.isShiftKeyDown = false;
  2363. }
  2364. destroy() {
  2365. this.#abortController?.abort();
  2366. this.#abortController = null;
  2367. this._signal = null;
  2368. this._eventBus._off("editingaction", this.#boundOnEditingAction);
  2369. this._eventBus._off("pagechanging", this.#boundOnPageChanging);
  2370. this._eventBus._off("scalechanging", this.#boundOnScaleChanging);
  2371. this._eventBus._off("rotationchanging", this.#boundOnRotationChanging);
  2372. for (const layer of this.#allLayers.values()) {
  2373. layer.destroy();
  2374. }
  2375. this.#allLayers.clear();
  2376. this.#allEditors.clear();
  2377. this.#editorsToRescale.clear();
  2378. this.#activeEditor = null;
  2379. this.#selectedEditors.clear();
  2380. this.#commandManager.destroy();
  2381. this.#altTextManager?.destroy();
  2382. this.#highlightToolbar?.hide();
  2383. this.#highlightToolbar = null;
  2384. if (this.#focusMainContainerTimeoutId) {
  2385. clearTimeout(this.#focusMainContainerTimeoutId);
  2386. this.#focusMainContainerTimeoutId = null;
  2387. }
  2388. if (this.#translationTimeoutId) {
  2389. clearTimeout(this.#translationTimeoutId);
  2390. this.#translationTimeoutId = null;
  2391. }
  2392. }
  2393. async mlGuess(data) {
  2394. return this.#mlManager?.guess(data) || null;
  2395. }
  2396. async isMLEnabledFor(name) {
  2397. return !!(await this.#mlManager?.isEnabledFor(name));
  2398. }
  2399. get useNewAltTextFlow() {
  2400. return this.#enableUpdatedAddImage;
  2401. }
  2402. get hcmFilter() {
  2403. return shadow(this, "hcmFilter", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : "none");
  2404. }
  2405. get direction() {
  2406. return shadow(this, "direction", getComputedStyle(this.#container).direction);
  2407. }
  2408. get highlightColors() {
  2409. return shadow(this, "highlightColors", this.#highlightColors ? new Map(this.#highlightColors.split(",").map(pair => pair.split("=").map(x => x.trim()))) : null);
  2410. }
  2411. get highlightColorNames() {
  2412. return shadow(this, "highlightColorNames", this.highlightColors ? new Map(Array.from(this.highlightColors, e => e.reverse())) : null);
  2413. }
  2414. setMainHighlightColorPicker(colorPicker) {
  2415. this.#mainHighlightColorPicker = colorPicker;
  2416. }
  2417. editAltText(editor) {
  2418. this.#altTextManager?.editAltText(this, editor);
  2419. }
  2420. switchToMode(mode, callback) {
  2421. this._eventBus.on("annotationeditormodechanged", callback, {
  2422. once: true,
  2423. signal: this._signal
  2424. });
  2425. this._eventBus.dispatch("showannotationeditorui", {
  2426. source: this,
  2427. mode
  2428. });
  2429. }
  2430. setPreference(name, value) {
  2431. this._eventBus.dispatch("setpreference", {
  2432. source: this,
  2433. name,
  2434. value
  2435. });
  2436. }
  2437. onPageChanging({
  2438. pageNumber
  2439. }) {
  2440. this.#currentPageIndex = pageNumber - 1;
  2441. }
  2442. focusMainContainer() {
  2443. this.#container.focus();
  2444. }
  2445. findParent(x, y) {
  2446. for (const layer of this.#allLayers.values()) {
  2447. const {
  2448. x: layerX,
  2449. y: layerY,
  2450. width,
  2451. height
  2452. } = layer.div.getBoundingClientRect();
  2453. if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) {
  2454. return layer;
  2455. }
  2456. }
  2457. return null;
  2458. }
  2459. disableUserSelect(value = false) {
  2460. this.#viewer.classList.toggle("noUserSelect", value);
  2461. }
  2462. addShouldRescale(editor) {
  2463. this.#editorsToRescale.add(editor);
  2464. }
  2465. removeShouldRescale(editor) {
  2466. this.#editorsToRescale.delete(editor);
  2467. }
  2468. onScaleChanging({
  2469. scale
  2470. }) {
  2471. this.commitOrRemove();
  2472. this.viewParameters.realScale = scale * PixelsPerInch.PDF_TO_CSS_UNITS;
  2473. for (const editor of this.#editorsToRescale) {
  2474. editor.onScaleChanging();
  2475. }
  2476. }
  2477. onRotationChanging({
  2478. pagesRotation
  2479. }) {
  2480. this.commitOrRemove();
  2481. this.viewParameters.rotation = pagesRotation;
  2482. }
  2483. #getAnchorElementForSelection({
  2484. anchorNode
  2485. }) {
  2486. return anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode;
  2487. }
  2488. #getLayerForTextLayer(textLayer) {
  2489. const {
  2490. currentLayer
  2491. } = this;
  2492. if (currentLayer.hasTextLayer(textLayer)) {
  2493. return currentLayer;
  2494. }
  2495. for (const layer of this.#allLayers.values()) {
  2496. if (layer.hasTextLayer(textLayer)) {
  2497. return layer;
  2498. }
  2499. }
  2500. return null;
  2501. }
  2502. highlightSelection(methodOfCreation = "") {
  2503. const selection = document.getSelection();
  2504. if (!selection || selection.isCollapsed) {
  2505. return;
  2506. }
  2507. const {
  2508. anchorNode,
  2509. anchorOffset,
  2510. focusNode,
  2511. focusOffset
  2512. } = selection;
  2513. const text = selection.toString();
  2514. const anchorElement = this.#getAnchorElementForSelection(selection);
  2515. const textLayer = anchorElement.closest(".textLayer");
  2516. const boxes = this.getSelectionBoxes(textLayer);
  2517. if (!boxes) {
  2518. return;
  2519. }
  2520. selection.empty();
  2521. const layer = this.#getLayerForTextLayer(textLayer);
  2522. const isNoneMode = this.#mode === AnnotationEditorType.NONE;
  2523. const callback = () => {
  2524. layer?.createAndAddNewEditor({
  2525. x: 0,
  2526. y: 0
  2527. }, false, {
  2528. methodOfCreation,
  2529. boxes,
  2530. anchorNode,
  2531. anchorOffset,
  2532. focusNode,
  2533. focusOffset,
  2534. text
  2535. });
  2536. if (isNoneMode) {
  2537. this.showAllEditors("highlight", true, true);
  2538. }
  2539. };
  2540. if (isNoneMode) {
  2541. this.switchToMode(AnnotationEditorType.HIGHLIGHT, callback);
  2542. return;
  2543. }
  2544. callback();
  2545. }
  2546. #displayHighlightToolbar() {
  2547. const selection = document.getSelection();
  2548. if (!selection || selection.isCollapsed) {
  2549. return;
  2550. }
  2551. const anchorElement = this.#getAnchorElementForSelection(selection);
  2552. const textLayer = anchorElement.closest(".textLayer");
  2553. const boxes = this.getSelectionBoxes(textLayer);
  2554. if (!boxes) {
  2555. return;
  2556. }
  2557. this.#highlightToolbar ||= new HighlightToolbar(this);
  2558. this.#highlightToolbar.show(textLayer, boxes, this.direction === "ltr");
  2559. }
  2560. addToAnnotationStorage(editor) {
  2561. if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) {
  2562. this.#annotationStorage.setValue(editor.id, editor);
  2563. }
  2564. }
  2565. #selectionChange() {
  2566. const selection = document.getSelection();
  2567. if (!selection || selection.isCollapsed) {
  2568. if (this.#selectedTextNode) {
  2569. this.#highlightToolbar?.hide();
  2570. this.#selectedTextNode = null;
  2571. this.#dispatchUpdateStates({
  2572. hasSelectedText: false
  2573. });
  2574. }
  2575. return;
  2576. }
  2577. const {
  2578. anchorNode
  2579. } = selection;
  2580. if (anchorNode === this.#selectedTextNode) {
  2581. return;
  2582. }
  2583. const anchorElement = this.#getAnchorElementForSelection(selection);
  2584. const textLayer = anchorElement.closest(".textLayer");
  2585. if (!textLayer) {
  2586. if (this.#selectedTextNode) {
  2587. this.#highlightToolbar?.hide();
  2588. this.#selectedTextNode = null;
  2589. this.#dispatchUpdateStates({
  2590. hasSelectedText: false
  2591. });
  2592. }
  2593. return;
  2594. }
  2595. this.#highlightToolbar?.hide();
  2596. this.#selectedTextNode = anchorNode;
  2597. this.#dispatchUpdateStates({
  2598. hasSelectedText: true
  2599. });
  2600. if (this.#mode !== AnnotationEditorType.HIGHLIGHT && this.#mode !== AnnotationEditorType.NONE) {
  2601. return;
  2602. }
  2603. if (this.#mode === AnnotationEditorType.HIGHLIGHT) {
  2604. this.showAllEditors("highlight", true, true);
  2605. }
  2606. this.#highlightWhenShiftUp = this.isShiftKeyDown;
  2607. if (!this.isShiftKeyDown) {
  2608. const activeLayer = this.#mode === AnnotationEditorType.HIGHLIGHT ? this.#getLayerForTextLayer(textLayer) : null;
  2609. activeLayer?.toggleDrawing();
  2610. const signal = this._signal;
  2611. const pointerup = e => {
  2612. if (e.type === "pointerup" && e.button !== 0) {
  2613. return;
  2614. }
  2615. activeLayer?.toggleDrawing(true);
  2616. window.removeEventListener("pointerup", pointerup);
  2617. window.removeEventListener("blur", pointerup);
  2618. if (e.type === "pointerup") {
  2619. this.#onSelectEnd("main_toolbar");
  2620. }
  2621. };
  2622. window.addEventListener("pointerup", pointerup, {
  2623. signal
  2624. });
  2625. window.addEventListener("blur", pointerup, {
  2626. signal
  2627. });
  2628. }
  2629. }
  2630. #onSelectEnd(methodOfCreation = "") {
  2631. if (this.#mode === AnnotationEditorType.HIGHLIGHT) {
  2632. this.highlightSelection(methodOfCreation);
  2633. } else if (this.#enableHighlightFloatingButton) {
  2634. this.#displayHighlightToolbar();
  2635. }
  2636. }
  2637. #addSelectionListener() {
  2638. document.addEventListener("selectionchange", this.#selectionChange.bind(this), {
  2639. signal: this._signal
  2640. });
  2641. }
  2642. #addFocusManager() {
  2643. const signal = this._signal;
  2644. window.addEventListener("focus", this.#boundFocus, {
  2645. signal
  2646. });
  2647. window.addEventListener("blur", this.#boundBlur, {
  2648. signal
  2649. });
  2650. }
  2651. #removeFocusManager() {
  2652. window.removeEventListener("focus", this.#boundFocus);
  2653. window.removeEventListener("blur", this.#boundBlur);
  2654. }
  2655. blur() {
  2656. this.isShiftKeyDown = false;
  2657. if (this.#highlightWhenShiftUp) {
  2658. this.#highlightWhenShiftUp = false;
  2659. this.#onSelectEnd("main_toolbar");
  2660. }
  2661. if (!this.hasSelection) {
  2662. return;
  2663. }
  2664. const {
  2665. activeElement
  2666. } = document;
  2667. for (const editor of this.#selectedEditors) {
  2668. if (editor.div.contains(activeElement)) {
  2669. this.#lastActiveElement = [editor, activeElement];
  2670. editor._focusEventsAllowed = false;
  2671. break;
  2672. }
  2673. }
  2674. }
  2675. focus() {
  2676. if (!this.#lastActiveElement) {
  2677. return;
  2678. }
  2679. const [lastEditor, lastActiveElement] = this.#lastActiveElement;
  2680. this.#lastActiveElement = null;
  2681. lastActiveElement.addEventListener("focusin", () => {
  2682. lastEditor._focusEventsAllowed = true;
  2683. }, {
  2684. once: true,
  2685. signal: this._signal
  2686. });
  2687. lastActiveElement.focus();
  2688. }
  2689. #addKeyboardManager() {
  2690. const signal = this._signal;
  2691. window.addEventListener("keydown", this.#boundKeydown, {
  2692. signal
  2693. });
  2694. window.addEventListener("keyup", this.#boundKeyup, {
  2695. signal
  2696. });
  2697. }
  2698. #removeKeyboardManager() {
  2699. window.removeEventListener("keydown", this.#boundKeydown);
  2700. window.removeEventListener("keyup", this.#boundKeyup);
  2701. }
  2702. #addCopyPasteListeners() {
  2703. const signal = this._signal;
  2704. document.addEventListener("copy", this.#boundCopy, {
  2705. signal
  2706. });
  2707. document.addEventListener("cut", this.#boundCut, {
  2708. signal
  2709. });
  2710. document.addEventListener("paste", this.#boundPaste, {
  2711. signal
  2712. });
  2713. }
  2714. #removeCopyPasteListeners() {
  2715. document.removeEventListener("copy", this.#boundCopy);
  2716. document.removeEventListener("cut", this.#boundCut);
  2717. document.removeEventListener("paste", this.#boundPaste);
  2718. }
  2719. #addDragAndDropListeners() {
  2720. const signal = this._signal;
  2721. document.addEventListener("dragover", this.dragOver.bind(this), {
  2722. signal
  2723. });
  2724. document.addEventListener("drop", this.drop.bind(this), {
  2725. signal
  2726. });
  2727. }
  2728. addEditListeners() {
  2729. this.#addKeyboardManager();
  2730. this.#addCopyPasteListeners();
  2731. }
  2732. removeEditListeners() {
  2733. this.#removeKeyboardManager();
  2734. this.#removeCopyPasteListeners();
  2735. }
  2736. dragOver(event) {
  2737. for (const {
  2738. type
  2739. } of event.dataTransfer.items) {
  2740. for (const editorType of this.#editorTypes) {
  2741. if (editorType.isHandlingMimeForPasting(type)) {
  2742. event.dataTransfer.dropEffect = "copy";
  2743. event.preventDefault();
  2744. return;
  2745. }
  2746. }
  2747. }
  2748. }
  2749. drop(event) {
  2750. for (const item of event.dataTransfer.items) {
  2751. for (const editorType of this.#editorTypes) {
  2752. if (editorType.isHandlingMimeForPasting(item.type)) {
  2753. editorType.paste(item, this.currentLayer);
  2754. event.preventDefault();
  2755. return;
  2756. }
  2757. }
  2758. }
  2759. }
  2760. copy(event) {
  2761. event.preventDefault();
  2762. this.#activeEditor?.commitOrRemove();
  2763. if (!this.hasSelection) {
  2764. return;
  2765. }
  2766. const editors = [];
  2767. for (const editor of this.#selectedEditors) {
  2768. const serialized = editor.serialize(true);
  2769. if (serialized) {
  2770. editors.push(serialized);
  2771. }
  2772. }
  2773. if (editors.length === 0) {
  2774. return;
  2775. }
  2776. event.clipboardData.setData("application/pdfjs", JSON.stringify(editors));
  2777. }
  2778. cut(event) {
  2779. this.copy(event);
  2780. this.delete();
  2781. }
  2782. paste(event) {
  2783. event.preventDefault();
  2784. const {
  2785. clipboardData
  2786. } = event;
  2787. for (const item of clipboardData.items) {
  2788. for (const editorType of this.#editorTypes) {
  2789. if (editorType.isHandlingMimeForPasting(item.type)) {
  2790. editorType.paste(item, this.currentLayer);
  2791. return;
  2792. }
  2793. }
  2794. }
  2795. let data = clipboardData.getData("application/pdfjs");
  2796. if (!data) {
  2797. return;
  2798. }
  2799. try {
  2800. data = JSON.parse(data);
  2801. } catch (ex) {
  2802. warn(`paste: "${ex.message}".`);
  2803. return;
  2804. }
  2805. if (!Array.isArray(data)) {
  2806. return;
  2807. }
  2808. this.unselectAll();
  2809. const layer = this.currentLayer;
  2810. try {
  2811. const newEditors = [];
  2812. for (const editor of data) {
  2813. const deserializedEditor = layer.deserialize(editor);
  2814. if (!deserializedEditor) {
  2815. return;
  2816. }
  2817. newEditors.push(deserializedEditor);
  2818. }
  2819. const cmd = () => {
  2820. for (const editor of newEditors) {
  2821. this.#addEditorToLayer(editor);
  2822. }
  2823. this.#selectEditors(newEditors);
  2824. };
  2825. const undo = () => {
  2826. for (const editor of newEditors) {
  2827. editor.remove();
  2828. }
  2829. };
  2830. this.addCommands({
  2831. cmd,
  2832. undo,
  2833. mustExec: true
  2834. });
  2835. } catch (ex) {
  2836. warn(`paste: "${ex.message}".`);
  2837. }
  2838. }
  2839. keydown(event) {
  2840. if (!this.isShiftKeyDown && event.key === "Shift") {
  2841. this.isShiftKeyDown = true;
  2842. }
  2843. if (this.#mode !== AnnotationEditorType.NONE && !this.isEditorHandlingKeyboard) {
  2844. AnnotationEditorUIManager._keyboardManager.exec(this, event);
  2845. }
  2846. }
  2847. keyup(event) {
  2848. if (this.isShiftKeyDown && event.key === "Shift") {
  2849. this.isShiftKeyDown = false;
  2850. if (this.#highlightWhenShiftUp) {
  2851. this.#highlightWhenShiftUp = false;
  2852. this.#onSelectEnd("main_toolbar");
  2853. }
  2854. }
  2855. }
  2856. onEditingAction({
  2857. name
  2858. }) {
  2859. switch (name) {
  2860. case "undo":
  2861. case "redo":
  2862. case "delete":
  2863. case "selectAll":
  2864. this[name]();
  2865. break;
  2866. case "highlightSelection":
  2867. this.highlightSelection("context_menu");
  2868. break;
  2869. }
  2870. }
  2871. #dispatchUpdateStates(details) {
  2872. const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value);
  2873. if (hasChanged) {
  2874. this._eventBus.dispatch("annotationeditorstateschanged", {
  2875. source: this,
  2876. details: Object.assign(this.#previousStates, details)
  2877. });
  2878. if (this.#mode === AnnotationEditorType.HIGHLIGHT && details.hasSelectedEditor === false) {
  2879. this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_FREE, true]]);
  2880. }
  2881. }
  2882. }
  2883. #dispatchUpdateUI(details) {
  2884. this._eventBus.dispatch("annotationeditorparamschanged", {
  2885. source: this,
  2886. details
  2887. });
  2888. }
  2889. setEditingState(isEditing) {
  2890. if (isEditing) {
  2891. this.#addFocusManager();
  2892. this.#addCopyPasteListeners();
  2893. this.#dispatchUpdateStates({
  2894. isEditing: this.#mode !== AnnotationEditorType.NONE,
  2895. isEmpty: this.#isEmpty(),
  2896. hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),
  2897. hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),
  2898. hasSelectedEditor: false
  2899. });
  2900. } else {
  2901. this.#removeFocusManager();
  2902. this.#removeCopyPasteListeners();
  2903. this.#dispatchUpdateStates({
  2904. isEditing: false
  2905. });
  2906. this.disableUserSelect(false);
  2907. }
  2908. }
  2909. registerEditorTypes(types) {
  2910. if (this.#editorTypes) {
  2911. return;
  2912. }
  2913. this.#editorTypes = types;
  2914. for (const editorType of this.#editorTypes) {
  2915. this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate);
  2916. }
  2917. }
  2918. getId() {
  2919. return this.#idManager.id;
  2920. }
  2921. get currentLayer() {
  2922. return this.#allLayers.get(this.#currentPageIndex);
  2923. }
  2924. getLayer(pageIndex) {
  2925. return this.#allLayers.get(pageIndex);
  2926. }
  2927. get currentPageIndex() {
  2928. return this.#currentPageIndex;
  2929. }
  2930. addLayer(layer) {
  2931. this.#allLayers.set(layer.pageIndex, layer);
  2932. if (this.#isEnabled) {
  2933. layer.enable();
  2934. } else {
  2935. layer.disable();
  2936. }
  2937. }
  2938. removeLayer(layer) {
  2939. this.#allLayers.delete(layer.pageIndex);
  2940. }
  2941. updateMode(mode, editId = null, isFromKeyboard = false) {
  2942. if (this.#mode === mode) {
  2943. return;
  2944. }
  2945. this.#mode = mode;
  2946. if (mode === AnnotationEditorType.NONE) {
  2947. this.setEditingState(false);
  2948. this.#disableAll();
  2949. return;
  2950. }
  2951. this.setEditingState(true);
  2952. this.#enableAll();
  2953. this.unselectAll();
  2954. for (const layer of this.#allLayers.values()) {
  2955. layer.updateMode(mode);
  2956. }
  2957. if (!editId && isFromKeyboard) {
  2958. this.addNewEditorFromKeyboard();
  2959. return;
  2960. }
  2961. if (!editId) {
  2962. return;
  2963. }
  2964. for (const editor of this.#allEditors.values()) {
  2965. if (editor.annotationElementId === editId) {
  2966. this.setSelected(editor);
  2967. editor.enterInEditMode();
  2968. break;
  2969. }
  2970. }
  2971. }
  2972. addNewEditorFromKeyboard() {
  2973. if (this.currentLayer.canCreateNewEmptyEditor()) {
  2974. this.currentLayer.addNewEditor();
  2975. }
  2976. }
  2977. updateToolbar(mode) {
  2978. if (mode === this.#mode) {
  2979. return;
  2980. }
  2981. this._eventBus.dispatch("switchannotationeditormode", {
  2982. source: this,
  2983. mode
  2984. });
  2985. }
  2986. updateParams(type, value) {
  2987. if (!this.#editorTypes) {
  2988. return;
  2989. }
  2990. switch (type) {
  2991. case AnnotationEditorParamsType.CREATE:
  2992. this.currentLayer.addNewEditor();
  2993. return;
  2994. case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
  2995. this.#mainHighlightColorPicker?.updateColor(value);
  2996. break;
  2997. case AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL:
  2998. this._eventBus.dispatch("reporttelemetry", {
  2999. source: this,
  3000. details: {
  3001. type: "editing",
  3002. data: {
  3003. type: "highlight",
  3004. action: "toggle_visibility"
  3005. }
  3006. }
  3007. });
  3008. (this.#showAllStates ||= new Map()).set(type, value);
  3009. this.showAllEditors("highlight", value);
  3010. break;
  3011. }
  3012. for (const editor of this.#selectedEditors) {
  3013. editor.updateParams(type, value);
  3014. }
  3015. for (const editorType of this.#editorTypes) {
  3016. editorType.updateDefaultParams(type, value);
  3017. }
  3018. }
  3019. showAllEditors(type, visible, updateButton = false) {
  3020. for (const editor of this.#allEditors.values()) {
  3021. if (editor.editorType === type) {
  3022. editor.show(visible);
  3023. }
  3024. }
  3025. const state = this.#showAllStates?.get(AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL) ?? true;
  3026. if (state !== visible) {
  3027. this.#dispatchUpdateUI([[AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL, visible]]);
  3028. }
  3029. }
  3030. enableWaiting(mustWait = false) {
  3031. if (this.#isWaiting === mustWait) {
  3032. return;
  3033. }
  3034. this.#isWaiting = mustWait;
  3035. for (const layer of this.#allLayers.values()) {
  3036. if (mustWait) {
  3037. layer.disableClick();
  3038. } else {
  3039. layer.enableClick();
  3040. }
  3041. layer.div.classList.toggle("waiting", mustWait);
  3042. }
  3043. }
  3044. #enableAll() {
  3045. if (!this.#isEnabled) {
  3046. this.#isEnabled = true;
  3047. for (const layer of this.#allLayers.values()) {
  3048. layer.enable();
  3049. }
  3050. for (const editor of this.#allEditors.values()) {
  3051. editor.enable();
  3052. }
  3053. }
  3054. }
  3055. #disableAll() {
  3056. this.unselectAll();
  3057. if (this.#isEnabled) {
  3058. this.#isEnabled = false;
  3059. for (const layer of this.#allLayers.values()) {
  3060. layer.disable();
  3061. }
  3062. for (const editor of this.#allEditors.values()) {
  3063. editor.disable();
  3064. }
  3065. }
  3066. }
  3067. getEditors(pageIndex) {
  3068. const editors = [];
  3069. for (const editor of this.#allEditors.values()) {
  3070. if (editor.pageIndex === pageIndex) {
  3071. editors.push(editor);
  3072. }
  3073. }
  3074. return editors;
  3075. }
  3076. getEditor(id) {
  3077. return this.#allEditors.get(id);
  3078. }
  3079. addEditor(editor) {
  3080. this.#allEditors.set(editor.id, editor);
  3081. }
  3082. removeEditor(editor) {
  3083. if (editor.div.contains(document.activeElement)) {
  3084. if (this.#focusMainContainerTimeoutId) {
  3085. clearTimeout(this.#focusMainContainerTimeoutId);
  3086. }
  3087. this.#focusMainContainerTimeoutId = setTimeout(() => {
  3088. this.focusMainContainer();
  3089. this.#focusMainContainerTimeoutId = null;
  3090. }, 0);
  3091. }
  3092. this.#allEditors.delete(editor.id);
  3093. this.unselect(editor);
  3094. if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) {
  3095. this.#annotationStorage?.remove(editor.id);
  3096. }
  3097. }
  3098. addDeletedAnnotationElement(editor) {
  3099. this.#deletedAnnotationsElementIds.add(editor.annotationElementId);
  3100. this.addChangedExistingAnnotation(editor);
  3101. editor.deleted = true;
  3102. }
  3103. isDeletedAnnotationElement(annotationElementId) {
  3104. return this.#deletedAnnotationsElementIds.has(annotationElementId);
  3105. }
  3106. removeDeletedAnnotationElement(editor) {
  3107. this.#deletedAnnotationsElementIds.delete(editor.annotationElementId);
  3108. this.removeChangedExistingAnnotation(editor);
  3109. editor.deleted = false;
  3110. }
  3111. #addEditorToLayer(editor) {
  3112. const layer = this.#allLayers.get(editor.pageIndex);
  3113. if (layer) {
  3114. layer.addOrRebuild(editor);
  3115. } else {
  3116. this.addEditor(editor);
  3117. this.addToAnnotationStorage(editor);
  3118. }
  3119. }
  3120. setActiveEditor(editor) {
  3121. if (this.#activeEditor === editor) {
  3122. return;
  3123. }
  3124. this.#activeEditor = editor;
  3125. if (editor) {
  3126. this.#dispatchUpdateUI(editor.propertiesToUpdate);
  3127. }
  3128. }
  3129. get #lastSelectedEditor() {
  3130. let ed = null;
  3131. for (ed of this.#selectedEditors) {}
  3132. return ed;
  3133. }
  3134. updateUI(editor) {
  3135. if (this.#lastSelectedEditor === editor) {
  3136. this.#dispatchUpdateUI(editor.propertiesToUpdate);
  3137. }
  3138. }
  3139. toggleSelected(editor) {
  3140. if (this.#selectedEditors.has(editor)) {
  3141. this.#selectedEditors.delete(editor);
  3142. editor.unselect();
  3143. this.#dispatchUpdateStates({
  3144. hasSelectedEditor: this.hasSelection
  3145. });
  3146. return;
  3147. }
  3148. this.#selectedEditors.add(editor);
  3149. editor.select();
  3150. this.#dispatchUpdateUI(editor.propertiesToUpdate);
  3151. this.#dispatchUpdateStates({
  3152. hasSelectedEditor: true
  3153. });
  3154. }
  3155. setSelected(editor) {
  3156. for (const ed of this.#selectedEditors) {
  3157. if (ed !== editor) {
  3158. ed.unselect();
  3159. }
  3160. }
  3161. this.#selectedEditors.clear();
  3162. this.#selectedEditors.add(editor);
  3163. editor.select();
  3164. this.#dispatchUpdateUI(editor.propertiesToUpdate);
  3165. this.#dispatchUpdateStates({
  3166. hasSelectedEditor: true
  3167. });
  3168. }
  3169. isSelected(editor) {
  3170. return this.#selectedEditors.has(editor);
  3171. }
  3172. get firstSelectedEditor() {
  3173. return this.#selectedEditors.values().next().value;
  3174. }
  3175. unselect(editor) {
  3176. editor.unselect();
  3177. this.#selectedEditors.delete(editor);
  3178. this.#dispatchUpdateStates({
  3179. hasSelectedEditor: this.hasSelection
  3180. });
  3181. }
  3182. get hasSelection() {
  3183. return this.#selectedEditors.size !== 0;
  3184. }
  3185. get isEnterHandled() {
  3186. return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled;
  3187. }
  3188. undo() {
  3189. this.#commandManager.undo();
  3190. this.#dispatchUpdateStates({
  3191. hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),
  3192. hasSomethingToRedo: true,
  3193. isEmpty: this.#isEmpty()
  3194. });
  3195. }
  3196. redo() {
  3197. this.#commandManager.redo();
  3198. this.#dispatchUpdateStates({
  3199. hasSomethingToUndo: true,
  3200. hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),
  3201. isEmpty: this.#isEmpty()
  3202. });
  3203. }
  3204. addCommands(params) {
  3205. this.#commandManager.add(params);
  3206. this.#dispatchUpdateStates({
  3207. hasSomethingToUndo: true,
  3208. hasSomethingToRedo: false,
  3209. isEmpty: this.#isEmpty()
  3210. });
  3211. }
  3212. #isEmpty() {
  3213. if (this.#allEditors.size === 0) {
  3214. return true;
  3215. }
  3216. if (this.#allEditors.size === 1) {
  3217. for (const editor of this.#allEditors.values()) {
  3218. return editor.isEmpty();
  3219. }
  3220. }
  3221. return false;
  3222. }
  3223. delete() {
  3224. this.commitOrRemove();
  3225. if (!this.hasSelection) {
  3226. return;
  3227. }
  3228. const editors = [...this.#selectedEditors];
  3229. const cmd = () => {
  3230. for (const editor of editors) {
  3231. editor.remove();
  3232. }
  3233. };
  3234. const undo = () => {
  3235. for (const editor of editors) {
  3236. this.#addEditorToLayer(editor);
  3237. }
  3238. };
  3239. this.addCommands({
  3240. cmd,
  3241. undo,
  3242. mustExec: true
  3243. });
  3244. }
  3245. commitOrRemove() {
  3246. this.#activeEditor?.commitOrRemove();
  3247. }
  3248. hasSomethingToControl() {
  3249. return this.#activeEditor || this.hasSelection;
  3250. }
  3251. #selectEditors(editors) {
  3252. for (const editor of this.#selectedEditors) {
  3253. editor.unselect();
  3254. }
  3255. this.#selectedEditors.clear();
  3256. for (const editor of editors) {
  3257. if (editor.isEmpty()) {
  3258. continue;
  3259. }
  3260. this.#selectedEditors.add(editor);
  3261. editor.select();
  3262. }
  3263. this.#dispatchUpdateStates({
  3264. hasSelectedEditor: this.hasSelection
  3265. });
  3266. }
  3267. selectAll() {
  3268. for (const editor of this.#selectedEditors) {
  3269. editor.commit();
  3270. }
  3271. this.#selectEditors(this.#allEditors.values());
  3272. }
  3273. unselectAll() {
  3274. if (this.#activeEditor) {
  3275. this.#activeEditor.commitOrRemove();
  3276. if (this.#mode !== AnnotationEditorType.NONE) {
  3277. return;
  3278. }
  3279. }
  3280. if (!this.hasSelection) {
  3281. return;
  3282. }
  3283. for (const editor of this.#selectedEditors) {
  3284. editor.unselect();
  3285. }
  3286. this.#selectedEditors.clear();
  3287. this.#dispatchUpdateStates({
  3288. hasSelectedEditor: false
  3289. });
  3290. }
  3291. translateSelectedEditors(x, y, noCommit = false) {
  3292. if (!noCommit) {
  3293. this.commitOrRemove();
  3294. }
  3295. if (!this.hasSelection) {
  3296. return;
  3297. }
  3298. this.#translation[0] += x;
  3299. this.#translation[1] += y;
  3300. const [totalX, totalY] = this.#translation;
  3301. const editors = [...this.#selectedEditors];
  3302. const TIME_TO_WAIT = 1000;
  3303. if (this.#translationTimeoutId) {
  3304. clearTimeout(this.#translationTimeoutId);
  3305. }
  3306. this.#translationTimeoutId = setTimeout(() => {
  3307. this.#translationTimeoutId = null;
  3308. this.#translation[0] = this.#translation[1] = 0;
  3309. this.addCommands({
  3310. cmd: () => {
  3311. for (const editor of editors) {
  3312. if (this.#allEditors.has(editor.id)) {
  3313. editor.translateInPage(totalX, totalY);
  3314. }
  3315. }
  3316. },
  3317. undo: () => {
  3318. for (const editor of editors) {
  3319. if (this.#allEditors.has(editor.id)) {
  3320. editor.translateInPage(-totalX, -totalY);
  3321. }
  3322. }
  3323. },
  3324. mustExec: false
  3325. });
  3326. }, TIME_TO_WAIT);
  3327. for (const editor of editors) {
  3328. editor.translateInPage(x, y);
  3329. }
  3330. }
  3331. setUpDragSession() {
  3332. if (!this.hasSelection) {
  3333. return;
  3334. }
  3335. this.disableUserSelect(true);
  3336. this.#draggingEditors = new Map();
  3337. for (const editor of this.#selectedEditors) {
  3338. this.#draggingEditors.set(editor, {
  3339. savedX: editor.x,
  3340. savedY: editor.y,
  3341. savedPageIndex: editor.pageIndex,
  3342. newX: 0,
  3343. newY: 0,
  3344. newPageIndex: -1
  3345. });
  3346. }
  3347. }
  3348. endDragSession() {
  3349. if (!this.#draggingEditors) {
  3350. return false;
  3351. }
  3352. this.disableUserSelect(false);
  3353. const map = this.#draggingEditors;
  3354. this.#draggingEditors = null;
  3355. let mustBeAddedInUndoStack = false;
  3356. for (const [{
  3357. x,
  3358. y,
  3359. pageIndex
  3360. }, value] of map) {
  3361. value.newX = x;
  3362. value.newY = y;
  3363. value.newPageIndex = pageIndex;
  3364. mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex;
  3365. }
  3366. if (!mustBeAddedInUndoStack) {
  3367. return false;
  3368. }
  3369. const move = (editor, x, y, pageIndex) => {
  3370. if (this.#allEditors.has(editor.id)) {
  3371. const parent = this.#allLayers.get(pageIndex);
  3372. if (parent) {
  3373. editor._setParentAndPosition(parent, x, y);
  3374. } else {
  3375. editor.pageIndex = pageIndex;
  3376. editor.x = x;
  3377. editor.y = y;
  3378. }
  3379. }
  3380. };
  3381. this.addCommands({
  3382. cmd: () => {
  3383. for (const [editor, {
  3384. newX,
  3385. newY,
  3386. newPageIndex
  3387. }] of map) {
  3388. move(editor, newX, newY, newPageIndex);
  3389. }
  3390. },
  3391. undo: () => {
  3392. for (const [editor, {
  3393. savedX,
  3394. savedY,
  3395. savedPageIndex
  3396. }] of map) {
  3397. move(editor, savedX, savedY, savedPageIndex);
  3398. }
  3399. },
  3400. mustExec: true
  3401. });
  3402. return true;
  3403. }
  3404. dragSelectedEditors(tx, ty) {
  3405. if (!this.#draggingEditors) {
  3406. return;
  3407. }
  3408. for (const editor of this.#draggingEditors.keys()) {
  3409. editor.drag(tx, ty);
  3410. }
  3411. }
  3412. rebuild(editor) {
  3413. if (editor.parent === null) {
  3414. const parent = this.getLayer(editor.pageIndex);
  3415. if (parent) {
  3416. parent.changeParent(editor);
  3417. parent.addOrRebuild(editor);
  3418. } else {
  3419. this.addEditor(editor);
  3420. this.addToAnnotationStorage(editor);
  3421. editor.rebuild();
  3422. }
  3423. } else {
  3424. editor.parent.addOrRebuild(editor);
  3425. }
  3426. }
  3427. get isEditorHandlingKeyboard() {
  3428. return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents();
  3429. }
  3430. isActive(editor) {
  3431. return this.#activeEditor === editor;
  3432. }
  3433. getActive() {
  3434. return this.#activeEditor;
  3435. }
  3436. getMode() {
  3437. return this.#mode;
  3438. }
  3439. get imageManager() {
  3440. return shadow(this, "imageManager", new ImageManager());
  3441. }
  3442. getSelectionBoxes(textLayer) {
  3443. if (!textLayer) {
  3444. return null;
  3445. }
  3446. const selection = document.getSelection();
  3447. for (let i = 0, ii = selection.rangeCount; i < ii; i++) {
  3448. if (!textLayer.contains(selection.getRangeAt(i).commonAncestorContainer)) {
  3449. return null;
  3450. }
  3451. }
  3452. const {
  3453. x: layerX,
  3454. y: layerY,
  3455. width: parentWidth,
  3456. height: parentHeight
  3457. } = textLayer.getBoundingClientRect();
  3458. let rotator;
  3459. switch (textLayer.getAttribute("data-main-rotation")) {
  3460. case "90":
  3461. rotator = (x, y, w, h) => ({
  3462. x: (y - layerY) / parentHeight,
  3463. y: 1 - (x + w - layerX) / parentWidth,
  3464. width: h / parentHeight,
  3465. height: w / parentWidth
  3466. });
  3467. break;
  3468. case "180":
  3469. rotator = (x, y, w, h) => ({
  3470. x: 1 - (x + w - layerX) / parentWidth,
  3471. y: 1 - (y + h - layerY) / parentHeight,
  3472. width: w / parentWidth,
  3473. height: h / parentHeight
  3474. });
  3475. break;
  3476. case "270":
  3477. rotator = (x, y, w, h) => ({
  3478. x: 1 - (y + h - layerY) / parentHeight,
  3479. y: (x - layerX) / parentWidth,
  3480. width: h / parentHeight,
  3481. height: w / parentWidth
  3482. });
  3483. break;
  3484. default:
  3485. rotator = (x, y, w, h) => ({
  3486. x: (x - layerX) / parentWidth,
  3487. y: (y - layerY) / parentHeight,
  3488. width: w / parentWidth,
  3489. height: h / parentHeight
  3490. });
  3491. break;
  3492. }
  3493. const boxes = [];
  3494. for (let i = 0, ii = selection.rangeCount; i < ii; i++) {
  3495. const range = selection.getRangeAt(i);
  3496. if (range.collapsed) {
  3497. continue;
  3498. }
  3499. for (const {
  3500. x,
  3501. y,
  3502. width,
  3503. height
  3504. } of range.getClientRects()) {
  3505. if (width === 0 || height === 0) {
  3506. continue;
  3507. }
  3508. boxes.push(rotator(x, y, width, height));
  3509. }
  3510. }
  3511. return boxes.length === 0 ? null : boxes;
  3512. }
  3513. addChangedExistingAnnotation({
  3514. annotationElementId,
  3515. id
  3516. }) {
  3517. (this.#changedExistingAnnotations ||= new Map()).set(annotationElementId, id);
  3518. }
  3519. removeChangedExistingAnnotation({
  3520. annotationElementId
  3521. }) {
  3522. this.#changedExistingAnnotations?.delete(annotationElementId);
  3523. }
  3524. renderAnnotationElement(annotation) {
  3525. const editorId = this.#changedExistingAnnotations?.get(annotation.data.id);
  3526. if (!editorId) {
  3527. return;
  3528. }
  3529. const editor = this.#annotationStorage.getRawValue(editorId);
  3530. if (!editor) {
  3531. return;
  3532. }
  3533. if (this.#mode === AnnotationEditorType.NONE && !editor.hasBeenModified) {
  3534. return;
  3535. }
  3536. editor.renderAnnotationElement(annotation);
  3537. }
  3538. }
  3539. ;// CONCATENATED MODULE: ./src/display/editor/alt_text.js
  3540. class AltText {
  3541. #altText = "";
  3542. #altTextDecorative = false;
  3543. #altTextButton = null;
  3544. #altTextTooltip = null;
  3545. #altTextTooltipTimeout = null;
  3546. #altTextWasFromKeyBoard = false;
  3547. #editor = null;
  3548. static _l10nPromise = null;
  3549. constructor(editor) {
  3550. this.#editor = editor;
  3551. }
  3552. static initialize(l10nPromise) {
  3553. AltText._l10nPromise ||= l10nPromise;
  3554. }
  3555. async render() {
  3556. const altText = this.#altTextButton = document.createElement("button");
  3557. altText.className = "altText";
  3558. const msg = await AltText._l10nPromise.get("pdfjs-editor-alt-text-button-label");
  3559. altText.textContent = msg;
  3560. altText.setAttribute("aria-label", msg);
  3561. altText.tabIndex = "0";
  3562. const signal = this.#editor._uiManager._signal;
  3563. altText.addEventListener("contextmenu", noContextMenu, {
  3564. signal
  3565. });
  3566. altText.addEventListener("pointerdown", event => event.stopPropagation(), {
  3567. signal
  3568. });
  3569. const onClick = event => {
  3570. event.preventDefault();
  3571. this.#editor._uiManager.editAltText(this.#editor);
  3572. };
  3573. altText.addEventListener("click", onClick, {
  3574. capture: true,
  3575. signal
  3576. });
  3577. altText.addEventListener("keydown", event => {
  3578. if (event.target === altText && event.key === "Enter") {
  3579. this.#altTextWasFromKeyBoard = true;
  3580. onClick(event);
  3581. }
  3582. }, {
  3583. signal
  3584. });
  3585. await this.#setState();
  3586. return altText;
  3587. }
  3588. finish() {
  3589. if (!this.#altTextButton) {
  3590. return;
  3591. }
  3592. this.#altTextButton.focus({
  3593. focusVisible: this.#altTextWasFromKeyBoard
  3594. });
  3595. this.#altTextWasFromKeyBoard = false;
  3596. }
  3597. isEmpty() {
  3598. return !this.#altText && !this.#altTextDecorative;
  3599. }
  3600. get data() {
  3601. return {
  3602. altText: this.#altText,
  3603. decorative: this.#altTextDecorative
  3604. };
  3605. }
  3606. set data({
  3607. altText,
  3608. decorative
  3609. }) {
  3610. if (this.#altText === altText && this.#altTextDecorative === decorative) {
  3611. return;
  3612. }
  3613. this.#altText = altText;
  3614. this.#altTextDecorative = decorative;
  3615. this.#setState();
  3616. }
  3617. toggle(enabled = false) {
  3618. if (!this.#altTextButton) {
  3619. return;
  3620. }
  3621. if (!enabled && this.#altTextTooltipTimeout) {
  3622. clearTimeout(this.#altTextTooltipTimeout);
  3623. this.#altTextTooltipTimeout = null;
  3624. }
  3625. this.#altTextButton.disabled = !enabled;
  3626. }
  3627. destroy() {
  3628. this.#altTextButton?.remove();
  3629. this.#altTextButton = null;
  3630. this.#altTextTooltip = null;
  3631. }
  3632. async #setState() {
  3633. const button = this.#altTextButton;
  3634. if (!button) {
  3635. return;
  3636. }
  3637. if (!this.#altText && !this.#altTextDecorative) {
  3638. button.classList.remove("done");
  3639. this.#altTextTooltip?.remove();
  3640. return;
  3641. }
  3642. button.classList.add("done");
  3643. AltText._l10nPromise.get("pdfjs-editor-alt-text-edit-button-label").then(msg => {
  3644. button.setAttribute("aria-label", msg);
  3645. });
  3646. let tooltip = this.#altTextTooltip;
  3647. if (!tooltip) {
  3648. this.#altTextTooltip = tooltip = document.createElement("span");
  3649. tooltip.className = "tooltip";
  3650. tooltip.setAttribute("role", "tooltip");
  3651. const id = tooltip.id = `alt-text-tooltip-${this.#editor.id}`;
  3652. button.setAttribute("aria-describedby", id);
  3653. const DELAY_TO_SHOW_TOOLTIP = 100;
  3654. const signal = this.#editor._uiManager._signal;
  3655. signal.addEventListener("abort", () => {
  3656. clearTimeout(this.#altTextTooltipTimeout);
  3657. this.#altTextTooltipTimeout = null;
  3658. }, {
  3659. once: true
  3660. });
  3661. button.addEventListener("mouseenter", () => {
  3662. this.#altTextTooltipTimeout = setTimeout(() => {
  3663. this.#altTextTooltipTimeout = null;
  3664. this.#altTextTooltip.classList.add("show");
  3665. this.#editor._reportTelemetry({
  3666. action: "alt_text_tooltip"
  3667. });
  3668. }, DELAY_TO_SHOW_TOOLTIP);
  3669. }, {
  3670. signal
  3671. });
  3672. button.addEventListener("mouseleave", () => {
  3673. if (this.#altTextTooltipTimeout) {
  3674. clearTimeout(this.#altTextTooltipTimeout);
  3675. this.#altTextTooltipTimeout = null;
  3676. }
  3677. this.#altTextTooltip?.classList.remove("show");
  3678. }, {
  3679. signal
  3680. });
  3681. }
  3682. tooltip.innerText = this.#altTextDecorative ? await AltText._l10nPromise.get("pdfjs-editor-alt-text-decorative-tooltip") : this.#altText;
  3683. if (!tooltip.parentNode) {
  3684. button.append(tooltip);
  3685. }
  3686. const element = this.#editor.getImageForAltText();
  3687. element?.setAttribute("aria-describedby", tooltip.id);
  3688. }
  3689. }
  3690. ;// CONCATENATED MODULE: ./src/display/editor/editor.js
  3691. class AnnotationEditor {
  3692. #accessibilityData = null;
  3693. #allResizerDivs = null;
  3694. #altText = null;
  3695. #disabled = false;
  3696. #keepAspectRatio = false;
  3697. #resizersDiv = null;
  3698. #savedDimensions = null;
  3699. #boundFocusin = this.focusin.bind(this);
  3700. #boundFocusout = this.focusout.bind(this);
  3701. #editToolbar = null;
  3702. #focusedResizerName = "";
  3703. #hasBeenClicked = false;
  3704. #initialPosition = null;
  3705. #isEditing = false;
  3706. #isInEditMode = false;
  3707. #isResizerEnabledForKeyboard = false;
  3708. #moveInDOMTimeout = null;
  3709. #prevDragX = 0;
  3710. #prevDragY = 0;
  3711. #telemetryTimeouts = null;
  3712. _initialOptions = Object.create(null);
  3713. _isVisible = true;
  3714. _uiManager = null;
  3715. _focusEventsAllowed = true;
  3716. _l10nPromise = null;
  3717. #isDraggable = false;
  3718. #zIndex = AnnotationEditor._zIndex++;
  3719. static _borderLineWidth = -1;
  3720. static _colorManager = new ColorManager();
  3721. static _zIndex = 1;
  3722. static _telemetryTimeout = 1000;
  3723. static get _resizerKeyboardManager() {
  3724. const resize = AnnotationEditor.prototype._resizeWithKeyboard;
  3725. const small = AnnotationEditorUIManager.TRANSLATE_SMALL;
  3726. const big = AnnotationEditorUIManager.TRANSLATE_BIG;
  3727. return shadow(this, "_resizerKeyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], resize, {
  3728. args: [-small, 0]
  3729. }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], resize, {
  3730. args: [-big, 0]
  3731. }], [["ArrowRight", "mac+ArrowRight"], resize, {
  3732. args: [small, 0]
  3733. }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], resize, {
  3734. args: [big, 0]
  3735. }], [["ArrowUp", "mac+ArrowUp"], resize, {
  3736. args: [0, -small]
  3737. }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], resize, {
  3738. args: [0, -big]
  3739. }], [["ArrowDown", "mac+ArrowDown"], resize, {
  3740. args: [0, small]
  3741. }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], resize, {
  3742. args: [0, big]
  3743. }], [["Escape", "mac+Escape"], AnnotationEditor.prototype._stopResizingWithKeyboard]]));
  3744. }
  3745. constructor(parameters) {
  3746. if (this.constructor === AnnotationEditor) {
  3747. unreachable("Cannot initialize AnnotationEditor.");
  3748. }
  3749. this.parent = parameters.parent;
  3750. this.id = parameters.id;
  3751. this.width = this.height = null;
  3752. this.pageIndex = parameters.parent.pageIndex;
  3753. this.name = parameters.name;
  3754. this.div = null;
  3755. this._uiManager = parameters.uiManager;
  3756. this.annotationElementId = null;
  3757. this._willKeepAspectRatio = false;
  3758. this._initialOptions.isCentered = parameters.isCentered;
  3759. this._structTreeParentId = null;
  3760. const {
  3761. rotation,
  3762. rawDims: {
  3763. pageWidth,
  3764. pageHeight,
  3765. pageX,
  3766. pageY
  3767. }
  3768. } = this.parent.viewport;
  3769. this.rotation = rotation;
  3770. this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360;
  3771. this.pageDimensions = [pageWidth, pageHeight];
  3772. this.pageTranslation = [pageX, pageY];
  3773. const [width, height] = this.parentDimensions;
  3774. this.x = parameters.x / width;
  3775. this.y = parameters.y / height;
  3776. this.isAttachedToDOM = false;
  3777. this.deleted = false;
  3778. }
  3779. get editorType() {
  3780. return Object.getPrototypeOf(this).constructor._type;
  3781. }
  3782. static get _defaultLineColor() {
  3783. return shadow(this, "_defaultLineColor", this._colorManager.getHexCode("CanvasText"));
  3784. }
  3785. static deleteAnnotationElement(editor) {
  3786. const fakeEditor = new FakeEditor({
  3787. id: editor.parent.getNextId(),
  3788. parent: editor.parent,
  3789. uiManager: editor._uiManager
  3790. });
  3791. fakeEditor.annotationElementId = editor.annotationElementId;
  3792. fakeEditor.deleted = true;
  3793. fakeEditor._uiManager.addToAnnotationStorage(fakeEditor);
  3794. }
  3795. static initialize(l10n, _uiManager, options) {
  3796. AnnotationEditor._l10nPromise ||= new Map(["pdfjs-editor-alt-text-button-label", "pdfjs-editor-alt-text-edit-button-label", "pdfjs-editor-alt-text-decorative-tooltip", "pdfjs-editor-resizer-label-topLeft", "pdfjs-editor-resizer-label-topMiddle", "pdfjs-editor-resizer-label-topRight", "pdfjs-editor-resizer-label-middleRight", "pdfjs-editor-resizer-label-bottomRight", "pdfjs-editor-resizer-label-bottomMiddle", "pdfjs-editor-resizer-label-bottomLeft", "pdfjs-editor-resizer-label-middleLeft"].map(str => [str, l10n.get(str.replaceAll(/([A-Z])/g, c => `-${c.toLowerCase()}`))]));
  3797. if (options?.strings) {
  3798. for (const str of options.strings) {
  3799. AnnotationEditor._l10nPromise.set(str, l10n.get(str));
  3800. }
  3801. }
  3802. if (AnnotationEditor._borderLineWidth !== -1) {
  3803. return;
  3804. }
  3805. const style = getComputedStyle(document.documentElement);
  3806. AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue("--outline-width")) || 0;
  3807. }
  3808. static updateDefaultParams(_type, _value) {}
  3809. static get defaultPropertiesToUpdate() {
  3810. return [];
  3811. }
  3812. static isHandlingMimeForPasting(mime) {
  3813. return false;
  3814. }
  3815. static paste(item, parent) {
  3816. unreachable("Not implemented");
  3817. }
  3818. get propertiesToUpdate() {
  3819. return [];
  3820. }
  3821. get _isDraggable() {
  3822. return this.#isDraggable;
  3823. }
  3824. set _isDraggable(value) {
  3825. this.#isDraggable = value;
  3826. this.div?.classList.toggle("draggable", value);
  3827. }
  3828. get isEnterHandled() {
  3829. return true;
  3830. }
  3831. center() {
  3832. const [pageWidth, pageHeight] = this.pageDimensions;
  3833. switch (this.parentRotation) {
  3834. case 90:
  3835. this.x -= this.height * pageHeight / (pageWidth * 2);
  3836. this.y += this.width * pageWidth / (pageHeight * 2);
  3837. break;
  3838. case 180:
  3839. this.x += this.width / 2;
  3840. this.y += this.height / 2;
  3841. break;
  3842. case 270:
  3843. this.x += this.height * pageHeight / (pageWidth * 2);
  3844. this.y -= this.width * pageWidth / (pageHeight * 2);
  3845. break;
  3846. default:
  3847. this.x -= this.width / 2;
  3848. this.y -= this.height / 2;
  3849. break;
  3850. }
  3851. this.fixAndSetPosition();
  3852. }
  3853. addCommands(params) {
  3854. this._uiManager.addCommands(params);
  3855. }
  3856. get currentLayer() {
  3857. return this._uiManager.currentLayer;
  3858. }
  3859. setInBackground() {
  3860. this.div.style.zIndex = 0;
  3861. }
  3862. setInForeground() {
  3863. this.div.style.zIndex = this.#zIndex;
  3864. }
  3865. setParent(parent) {
  3866. if (parent !== null) {
  3867. this.pageIndex = parent.pageIndex;
  3868. this.pageDimensions = parent.pageDimensions;
  3869. } else {
  3870. this.#stopResizing();
  3871. }
  3872. this.parent = parent;
  3873. }
  3874. focusin(event) {
  3875. if (!this._focusEventsAllowed) {
  3876. return;
  3877. }
  3878. if (!this.#hasBeenClicked) {
  3879. this.parent.setSelected(this);
  3880. } else {
  3881. this.#hasBeenClicked = false;
  3882. }
  3883. }
  3884. focusout(event) {
  3885. if (!this._focusEventsAllowed) {
  3886. return;
  3887. }
  3888. if (!this.isAttachedToDOM) {
  3889. return;
  3890. }
  3891. const target = event.relatedTarget;
  3892. if (target?.closest(`#${this.id}`)) {
  3893. return;
  3894. }
  3895. event.preventDefault();
  3896. if (!this.parent?.isMultipleSelection) {
  3897. this.commitOrRemove();
  3898. }
  3899. }
  3900. commitOrRemove() {
  3901. if (this.isEmpty()) {
  3902. this.remove();
  3903. } else {
  3904. this.commit();
  3905. }
  3906. }
  3907. commit() {
  3908. this.addToAnnotationStorage();
  3909. }
  3910. addToAnnotationStorage() {
  3911. this._uiManager.addToAnnotationStorage(this);
  3912. }
  3913. setAt(x, y, tx, ty) {
  3914. const [width, height] = this.parentDimensions;
  3915. [tx, ty] = this.screenToPageTranslation(tx, ty);
  3916. this.x = (x + tx) / width;
  3917. this.y = (y + ty) / height;
  3918. this.fixAndSetPosition();
  3919. }
  3920. #translate([width, height], x, y) {
  3921. [x, y] = this.screenToPageTranslation(x, y);
  3922. this.x += x / width;
  3923. this.y += y / height;
  3924. this.fixAndSetPosition();
  3925. }
  3926. translate(x, y) {
  3927. this.#translate(this.parentDimensions, x, y);
  3928. }
  3929. translateInPage(x, y) {
  3930. this.#initialPosition ||= [this.x, this.y];
  3931. this.#translate(this.pageDimensions, x, y);
  3932. this.div.scrollIntoView({
  3933. block: "nearest"
  3934. });
  3935. }
  3936. drag(tx, ty) {
  3937. this.#initialPosition ||= [this.x, this.y];
  3938. const [parentWidth, parentHeight] = this.parentDimensions;
  3939. this.x += tx / parentWidth;
  3940. this.y += ty / parentHeight;
  3941. if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) {
  3942. const {
  3943. x,
  3944. y
  3945. } = this.div.getBoundingClientRect();
  3946. if (this.parent.findNewParent(this, x, y)) {
  3947. this.x -= Math.floor(this.x);
  3948. this.y -= Math.floor(this.y);
  3949. }
  3950. }
  3951. let {
  3952. x,
  3953. y
  3954. } = this;
  3955. const [bx, by] = this.getBaseTranslation();
  3956. x += bx;
  3957. y += by;
  3958. this.div.style.left = `${(100 * x).toFixed(2)}%`;
  3959. this.div.style.top = `${(100 * y).toFixed(2)}%`;
  3960. this.div.scrollIntoView({
  3961. block: "nearest"
  3962. });
  3963. }
  3964. get _hasBeenMoved() {
  3965. return !!this.#initialPosition && (this.#initialPosition[0] !== this.x || this.#initialPosition[1] !== this.y);
  3966. }
  3967. getBaseTranslation() {
  3968. const [parentWidth, parentHeight] = this.parentDimensions;
  3969. const {
  3970. _borderLineWidth
  3971. } = AnnotationEditor;
  3972. const x = _borderLineWidth / parentWidth;
  3973. const y = _borderLineWidth / parentHeight;
  3974. switch (this.rotation) {
  3975. case 90:
  3976. return [-x, y];
  3977. case 180:
  3978. return [x, y];
  3979. case 270:
  3980. return [x, -y];
  3981. default:
  3982. return [-x, -y];
  3983. }
  3984. }
  3985. get _mustFixPosition() {
  3986. return true;
  3987. }
  3988. fixAndSetPosition(rotation = this.rotation) {
  3989. const [pageWidth, pageHeight] = this.pageDimensions;
  3990. let {
  3991. x,
  3992. y,
  3993. width,
  3994. height
  3995. } = this;
  3996. width *= pageWidth;
  3997. height *= pageHeight;
  3998. x *= pageWidth;
  3999. y *= pageHeight;
  4000. if (this._mustFixPosition) {
  4001. switch (rotation) {
  4002. case 0:
  4003. x = Math.max(0, Math.min(pageWidth - width, x));
  4004. y = Math.max(0, Math.min(pageHeight - height, y));
  4005. break;
  4006. case 90:
  4007. x = Math.max(0, Math.min(pageWidth - height, x));
  4008. y = Math.min(pageHeight, Math.max(width, y));
  4009. break;
  4010. case 180:
  4011. x = Math.min(pageWidth, Math.max(width, x));
  4012. y = Math.min(pageHeight, Math.max(height, y));
  4013. break;
  4014. case 270:
  4015. x = Math.min(pageWidth, Math.max(height, x));
  4016. y = Math.max(0, Math.min(pageHeight - width, y));
  4017. break;
  4018. }
  4019. }
  4020. this.x = x /= pageWidth;
  4021. this.y = y /= pageHeight;
  4022. const [bx, by] = this.getBaseTranslation();
  4023. x += bx;
  4024. y += by;
  4025. const {
  4026. style
  4027. } = this.div;
  4028. style.left = `${(100 * x).toFixed(2)}%`;
  4029. style.top = `${(100 * y).toFixed(2)}%`;
  4030. this.moveInDOM();
  4031. }
  4032. static #rotatePoint(x, y, angle) {
  4033. switch (angle) {
  4034. case 90:
  4035. return [y, -x];
  4036. case 180:
  4037. return [-x, -y];
  4038. case 270:
  4039. return [-y, x];
  4040. default:
  4041. return [x, y];
  4042. }
  4043. }
  4044. screenToPageTranslation(x, y) {
  4045. return AnnotationEditor.#rotatePoint(x, y, this.parentRotation);
  4046. }
  4047. pageTranslationToScreen(x, y) {
  4048. return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation);
  4049. }
  4050. #getRotationMatrix(rotation) {
  4051. switch (rotation) {
  4052. case 90:
  4053. {
  4054. const [pageWidth, pageHeight] = this.pageDimensions;
  4055. return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0];
  4056. }
  4057. case 180:
  4058. return [-1, 0, 0, -1];
  4059. case 270:
  4060. {
  4061. const [pageWidth, pageHeight] = this.pageDimensions;
  4062. return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0];
  4063. }
  4064. default:
  4065. return [1, 0, 0, 1];
  4066. }
  4067. }
  4068. get parentScale() {
  4069. return this._uiManager.viewParameters.realScale;
  4070. }
  4071. get parentRotation() {
  4072. return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360;
  4073. }
  4074. get parentDimensions() {
  4075. const {
  4076. parentScale,
  4077. pageDimensions: [pageWidth, pageHeight]
  4078. } = this;
  4079. const scaledWidth = pageWidth * parentScale;
  4080. const scaledHeight = pageHeight * parentScale;
  4081. return util_FeatureTest.isCSSRoundSupported ? [Math.round(scaledWidth), Math.round(scaledHeight)] : [scaledWidth, scaledHeight];
  4082. }
  4083. setDims(width, height) {
  4084. const [parentWidth, parentHeight] = this.parentDimensions;
  4085. this.div.style.width = `${(100 * width / parentWidth).toFixed(2)}%`;
  4086. if (!this.#keepAspectRatio) {
  4087. this.div.style.height = `${(100 * height / parentHeight).toFixed(2)}%`;
  4088. }
  4089. }
  4090. fixDims() {
  4091. const {
  4092. style
  4093. } = this.div;
  4094. const {
  4095. height,
  4096. width
  4097. } = style;
  4098. const widthPercent = width.endsWith("%");
  4099. const heightPercent = !this.#keepAspectRatio && height.endsWith("%");
  4100. if (widthPercent && heightPercent) {
  4101. return;
  4102. }
  4103. const [parentWidth, parentHeight] = this.parentDimensions;
  4104. if (!widthPercent) {
  4105. style.width = `${(100 * parseFloat(width) / parentWidth).toFixed(2)}%`;
  4106. }
  4107. if (!this.#keepAspectRatio && !heightPercent) {
  4108. style.height = `${(100 * parseFloat(height) / parentHeight).toFixed(2)}%`;
  4109. }
  4110. }
  4111. getInitialTranslation() {
  4112. return [0, 0];
  4113. }
  4114. #createResizers() {
  4115. if (this.#resizersDiv) {
  4116. return;
  4117. }
  4118. this.#resizersDiv = document.createElement("div");
  4119. this.#resizersDiv.classList.add("resizers");
  4120. const classes = this._willKeepAspectRatio ? ["topLeft", "topRight", "bottomRight", "bottomLeft"] : ["topLeft", "topMiddle", "topRight", "middleRight", "bottomRight", "bottomMiddle", "bottomLeft", "middleLeft"];
  4121. const signal = this._uiManager._signal;
  4122. for (const name of classes) {
  4123. const div = document.createElement("div");
  4124. this.#resizersDiv.append(div);
  4125. div.classList.add("resizer", name);
  4126. div.setAttribute("data-resizer-name", name);
  4127. div.addEventListener("pointerdown", this.#resizerPointerdown.bind(this, name), {
  4128. signal
  4129. });
  4130. div.addEventListener("contextmenu", noContextMenu, {
  4131. signal
  4132. });
  4133. div.tabIndex = -1;
  4134. }
  4135. this.div.prepend(this.#resizersDiv);
  4136. }
  4137. #resizerPointerdown(name, event) {
  4138. event.preventDefault();
  4139. const {
  4140. isMac
  4141. } = util_FeatureTest.platform;
  4142. if (event.button !== 0 || event.ctrlKey && isMac) {
  4143. return;
  4144. }
  4145. this.#altText?.toggle(false);
  4146. const boundResizerPointermove = this.#resizerPointermove.bind(this, name);
  4147. const savedDraggable = this._isDraggable;
  4148. this._isDraggable = false;
  4149. const signal = this._uiManager._signal;
  4150. const pointerMoveOptions = {
  4151. passive: true,
  4152. capture: true,
  4153. signal
  4154. };
  4155. this.parent.togglePointerEvents(false);
  4156. window.addEventListener("pointermove", boundResizerPointermove, pointerMoveOptions);
  4157. window.addEventListener("contextmenu", noContextMenu, {
  4158. signal
  4159. });
  4160. const savedX = this.x;
  4161. const savedY = this.y;
  4162. const savedWidth = this.width;
  4163. const savedHeight = this.height;
  4164. const savedParentCursor = this.parent.div.style.cursor;
  4165. const savedCursor = this.div.style.cursor;
  4166. this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor;
  4167. const pointerUpCallback = () => {
  4168. this.parent.togglePointerEvents(true);
  4169. this.#altText?.toggle(true);
  4170. this._isDraggable = savedDraggable;
  4171. window.removeEventListener("pointerup", pointerUpCallback);
  4172. window.removeEventListener("blur", pointerUpCallback);
  4173. window.removeEventListener("pointermove", boundResizerPointermove, pointerMoveOptions);
  4174. window.removeEventListener("contextmenu", noContextMenu);
  4175. this.parent.div.style.cursor = savedParentCursor;
  4176. this.div.style.cursor = savedCursor;
  4177. this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight);
  4178. };
  4179. window.addEventListener("pointerup", pointerUpCallback, {
  4180. signal
  4181. });
  4182. window.addEventListener("blur", pointerUpCallback, {
  4183. signal
  4184. });
  4185. }
  4186. #addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight) {
  4187. const newX = this.x;
  4188. const newY = this.y;
  4189. const newWidth = this.width;
  4190. const newHeight = this.height;
  4191. if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) {
  4192. return;
  4193. }
  4194. this.addCommands({
  4195. cmd: () => {
  4196. this.width = newWidth;
  4197. this.height = newHeight;
  4198. this.x = newX;
  4199. this.y = newY;
  4200. const [parentWidth, parentHeight] = this.parentDimensions;
  4201. this.setDims(parentWidth * newWidth, parentHeight * newHeight);
  4202. this.fixAndSetPosition();
  4203. },
  4204. undo: () => {
  4205. this.width = savedWidth;
  4206. this.height = savedHeight;
  4207. this.x = savedX;
  4208. this.y = savedY;
  4209. const [parentWidth, parentHeight] = this.parentDimensions;
  4210. this.setDims(parentWidth * savedWidth, parentHeight * savedHeight);
  4211. this.fixAndSetPosition();
  4212. },
  4213. mustExec: true
  4214. });
  4215. }
  4216. #resizerPointermove(name, event) {
  4217. const [parentWidth, parentHeight] = this.parentDimensions;
  4218. const savedX = this.x;
  4219. const savedY = this.y;
  4220. const savedWidth = this.width;
  4221. const savedHeight = this.height;
  4222. const minWidth = AnnotationEditor.MIN_SIZE / parentWidth;
  4223. const minHeight = AnnotationEditor.MIN_SIZE / parentHeight;
  4224. const round = x => Math.round(x * 10000) / 10000;
  4225. const rotationMatrix = this.#getRotationMatrix(this.rotation);
  4226. const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y];
  4227. const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation);
  4228. const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y];
  4229. let getPoint;
  4230. let getOpposite;
  4231. let isDiagonal = false;
  4232. let isHorizontal = false;
  4233. switch (name) {
  4234. case "topLeft":
  4235. isDiagonal = true;
  4236. getPoint = (w, h) => [0, 0];
  4237. getOpposite = (w, h) => [w, h];
  4238. break;
  4239. case "topMiddle":
  4240. getPoint = (w, h) => [w / 2, 0];
  4241. getOpposite = (w, h) => [w / 2, h];
  4242. break;
  4243. case "topRight":
  4244. isDiagonal = true;
  4245. getPoint = (w, h) => [w, 0];
  4246. getOpposite = (w, h) => [0, h];
  4247. break;
  4248. case "middleRight":
  4249. isHorizontal = true;
  4250. getPoint = (w, h) => [w, h / 2];
  4251. getOpposite = (w, h) => [0, h / 2];
  4252. break;
  4253. case "bottomRight":
  4254. isDiagonal = true;
  4255. getPoint = (w, h) => [w, h];
  4256. getOpposite = (w, h) => [0, 0];
  4257. break;
  4258. case "bottomMiddle":
  4259. getPoint = (w, h) => [w / 2, h];
  4260. getOpposite = (w, h) => [w / 2, 0];
  4261. break;
  4262. case "bottomLeft":
  4263. isDiagonal = true;
  4264. getPoint = (w, h) => [0, h];
  4265. getOpposite = (w, h) => [w, 0];
  4266. break;
  4267. case "middleLeft":
  4268. isHorizontal = true;
  4269. getPoint = (w, h) => [0, h / 2];
  4270. getOpposite = (w, h) => [w, h / 2];
  4271. break;
  4272. }
  4273. const point = getPoint(savedWidth, savedHeight);
  4274. const oppositePoint = getOpposite(savedWidth, savedHeight);
  4275. let transfOppositePoint = transf(...oppositePoint);
  4276. const oppositeX = round(savedX + transfOppositePoint[0]);
  4277. const oppositeY = round(savedY + transfOppositePoint[1]);
  4278. let ratioX = 1;
  4279. let ratioY = 1;
  4280. let [deltaX, deltaY] = this.screenToPageTranslation(event.movementX, event.movementY);
  4281. [deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight);
  4282. if (isDiagonal) {
  4283. const oldDiag = Math.hypot(savedWidth, savedHeight);
  4284. 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);
  4285. } else if (isHorizontal) {
  4286. ratioX = Math.max(minWidth, Math.min(1, Math.abs(oppositePoint[0] - point[0] - deltaX))) / savedWidth;
  4287. } else {
  4288. ratioY = Math.max(minHeight, Math.min(1, Math.abs(oppositePoint[1] - point[1] - deltaY))) / savedHeight;
  4289. }
  4290. const newWidth = round(savedWidth * ratioX);
  4291. const newHeight = round(savedHeight * ratioY);
  4292. transfOppositePoint = transf(...getOpposite(newWidth, newHeight));
  4293. const newX = oppositeX - transfOppositePoint[0];
  4294. const newY = oppositeY - transfOppositePoint[1];
  4295. this.width = newWidth;
  4296. this.height = newHeight;
  4297. this.x = newX;
  4298. this.y = newY;
  4299. this.setDims(parentWidth * newWidth, parentHeight * newHeight);
  4300. this.fixAndSetPosition();
  4301. }
  4302. altTextFinish() {
  4303. this.#altText?.finish();
  4304. }
  4305. async addEditToolbar() {
  4306. if (this.#editToolbar || this.#isInEditMode) {
  4307. return this.#editToolbar;
  4308. }
  4309. this.#editToolbar = new EditorToolbar(this);
  4310. this.div.append(this.#editToolbar.render());
  4311. if (this.#altText) {
  4312. this.#editToolbar.addAltTextButton(await this.#altText.render());
  4313. }
  4314. return this.#editToolbar;
  4315. }
  4316. removeEditToolbar() {
  4317. if (!this.#editToolbar) {
  4318. return;
  4319. }
  4320. this.#editToolbar.remove();
  4321. this.#editToolbar = null;
  4322. this.#altText?.destroy();
  4323. }
  4324. getClientDimensions() {
  4325. return this.div.getBoundingClientRect();
  4326. }
  4327. async addAltTextButton() {
  4328. if (this.#altText) {
  4329. return;
  4330. }
  4331. AltText.initialize(AnnotationEditor._l10nPromise);
  4332. this.#altText = new AltText(this);
  4333. if (this.#accessibilityData) {
  4334. this.#altText.data = this.#accessibilityData;
  4335. this.#accessibilityData = null;
  4336. }
  4337. await this.addEditToolbar();
  4338. }
  4339. get altTextData() {
  4340. return this.#altText?.data;
  4341. }
  4342. set altTextData(data) {
  4343. if (!this.#altText) {
  4344. return;
  4345. }
  4346. this.#altText.data = data;
  4347. }
  4348. hasAltText() {
  4349. return !this.#altText?.isEmpty();
  4350. }
  4351. render() {
  4352. this.div = document.createElement("div");
  4353. this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
  4354. this.div.className = this.name;
  4355. this.div.setAttribute("id", this.id);
  4356. this.div.tabIndex = this.#disabled ? -1 : 0;
  4357. if (!this._isVisible) {
  4358. this.div.classList.add("hidden");
  4359. }
  4360. this.setInForeground();
  4361. const signal = this._uiManager._signal;
  4362. this.div.addEventListener("focusin", this.#boundFocusin, {
  4363. signal
  4364. });
  4365. this.div.addEventListener("focusout", this.#boundFocusout, {
  4366. signal
  4367. });
  4368. const [parentWidth, parentHeight] = this.parentDimensions;
  4369. if (this.parentRotation % 180 !== 0) {
  4370. this.div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`;
  4371. this.div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`;
  4372. }
  4373. const [tx, ty] = this.getInitialTranslation();
  4374. this.translate(tx, ty);
  4375. bindEvents(this, this.div, ["pointerdown"]);
  4376. return this.div;
  4377. }
  4378. pointerdown(event) {
  4379. const {
  4380. isMac
  4381. } = util_FeatureTest.platform;
  4382. if (event.button !== 0 || event.ctrlKey && isMac) {
  4383. event.preventDefault();
  4384. return;
  4385. }
  4386. this.#hasBeenClicked = true;
  4387. if (this._isDraggable) {
  4388. this.#setUpDragSession(event);
  4389. return;
  4390. }
  4391. this.#selectOnPointerEvent(event);
  4392. }
  4393. #selectOnPointerEvent(event) {
  4394. const {
  4395. isMac
  4396. } = util_FeatureTest.platform;
  4397. if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) {
  4398. this.parent.toggleSelected(this);
  4399. } else {
  4400. this.parent.setSelected(this);
  4401. }
  4402. }
  4403. #setUpDragSession(event) {
  4404. const isSelected = this._uiManager.isSelected(this);
  4405. this._uiManager.setUpDragSession();
  4406. let pointerMoveOptions, pointerMoveCallback;
  4407. const signal = this._uiManager._signal;
  4408. if (isSelected) {
  4409. this.div.classList.add("moving");
  4410. pointerMoveOptions = {
  4411. passive: true,
  4412. capture: true,
  4413. signal
  4414. };
  4415. this.#prevDragX = event.clientX;
  4416. this.#prevDragY = event.clientY;
  4417. pointerMoveCallback = e => {
  4418. const {
  4419. clientX: x,
  4420. clientY: y
  4421. } = e;
  4422. const [tx, ty] = this.screenToPageTranslation(x - this.#prevDragX, y - this.#prevDragY);
  4423. this.#prevDragX = x;
  4424. this.#prevDragY = y;
  4425. this._uiManager.dragSelectedEditors(tx, ty);
  4426. };
  4427. window.addEventListener("pointermove", pointerMoveCallback, pointerMoveOptions);
  4428. }
  4429. const pointerUpCallback = () => {
  4430. window.removeEventListener("pointerup", pointerUpCallback);
  4431. window.removeEventListener("blur", pointerUpCallback);
  4432. if (isSelected) {
  4433. this.div.classList.remove("moving");
  4434. window.removeEventListener("pointermove", pointerMoveCallback, pointerMoveOptions);
  4435. }
  4436. this.#hasBeenClicked = false;
  4437. if (!this._uiManager.endDragSession()) {
  4438. this.#selectOnPointerEvent(event);
  4439. }
  4440. };
  4441. window.addEventListener("pointerup", pointerUpCallback, {
  4442. signal
  4443. });
  4444. window.addEventListener("blur", pointerUpCallback, {
  4445. signal
  4446. });
  4447. }
  4448. moveInDOM() {
  4449. if (this.#moveInDOMTimeout) {
  4450. clearTimeout(this.#moveInDOMTimeout);
  4451. }
  4452. this.#moveInDOMTimeout = setTimeout(() => {
  4453. this.#moveInDOMTimeout = null;
  4454. this.parent?.moveEditorInDOM(this);
  4455. }, 0);
  4456. }
  4457. _setParentAndPosition(parent, x, y) {
  4458. parent.changeParent(this);
  4459. this.x = x;
  4460. this.y = y;
  4461. this.fixAndSetPosition();
  4462. }
  4463. getRect(tx, ty, rotation = this.rotation) {
  4464. const scale = this.parentScale;
  4465. const [pageWidth, pageHeight] = this.pageDimensions;
  4466. const [pageX, pageY] = this.pageTranslation;
  4467. const shiftX = tx / scale;
  4468. const shiftY = ty / scale;
  4469. const x = this.x * pageWidth;
  4470. const y = this.y * pageHeight;
  4471. const width = this.width * pageWidth;
  4472. const height = this.height * pageHeight;
  4473. switch (rotation) {
  4474. case 0:
  4475. return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY];
  4476. case 90:
  4477. return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY];
  4478. case 180:
  4479. return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY];
  4480. case 270:
  4481. return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY];
  4482. default:
  4483. throw new Error("Invalid rotation");
  4484. }
  4485. }
  4486. getRectInCurrentCoords(rect, pageHeight) {
  4487. const [x1, y1, x2, y2] = rect;
  4488. const width = x2 - x1;
  4489. const height = y2 - y1;
  4490. switch (this.rotation) {
  4491. case 0:
  4492. return [x1, pageHeight - y2, width, height];
  4493. case 90:
  4494. return [x1, pageHeight - y1, height, width];
  4495. case 180:
  4496. return [x2, pageHeight - y1, width, height];
  4497. case 270:
  4498. return [x2, pageHeight - y2, height, width];
  4499. default:
  4500. throw new Error("Invalid rotation");
  4501. }
  4502. }
  4503. onceAdded() {}
  4504. isEmpty() {
  4505. return false;
  4506. }
  4507. enableEditMode() {
  4508. this.#isInEditMode = true;
  4509. }
  4510. disableEditMode() {
  4511. this.#isInEditMode = false;
  4512. }
  4513. isInEditMode() {
  4514. return this.#isInEditMode;
  4515. }
  4516. shouldGetKeyboardEvents() {
  4517. return this.#isResizerEnabledForKeyboard;
  4518. }
  4519. needsToBeRebuilt() {
  4520. return this.div && !this.isAttachedToDOM;
  4521. }
  4522. rebuild() {
  4523. const signal = this._uiManager._signal;
  4524. this.div?.addEventListener("focusin", this.#boundFocusin, {
  4525. signal
  4526. });
  4527. this.div?.addEventListener("focusout", this.#boundFocusout, {
  4528. signal
  4529. });
  4530. }
  4531. rotate(_angle) {}
  4532. serialize(isForCopying = false, context = null) {
  4533. unreachable("An editor must be serializable");
  4534. }
  4535. static deserialize(data, parent, uiManager) {
  4536. const editor = new this.prototype.constructor({
  4537. parent,
  4538. id: parent.getNextId(),
  4539. uiManager
  4540. });
  4541. editor.rotation = data.rotation;
  4542. editor.#accessibilityData = data.accessibilityData;
  4543. const [pageWidth, pageHeight] = editor.pageDimensions;
  4544. const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight);
  4545. editor.x = x / pageWidth;
  4546. editor.y = y / pageHeight;
  4547. editor.width = width / pageWidth;
  4548. editor.height = height / pageHeight;
  4549. return editor;
  4550. }
  4551. get hasBeenModified() {
  4552. return !!this.annotationElementId && (this.deleted || this.serialize() !== null);
  4553. }
  4554. remove() {
  4555. this.div.removeEventListener("focusin", this.#boundFocusin);
  4556. this.div.removeEventListener("focusout", this.#boundFocusout);
  4557. if (!this.isEmpty()) {
  4558. this.commit();
  4559. }
  4560. if (this.parent) {
  4561. this.parent.remove(this);
  4562. } else {
  4563. this._uiManager.removeEditor(this);
  4564. }
  4565. if (this.#moveInDOMTimeout) {
  4566. clearTimeout(this.#moveInDOMTimeout);
  4567. this.#moveInDOMTimeout = null;
  4568. }
  4569. this.#stopResizing();
  4570. this.removeEditToolbar();
  4571. if (this.#telemetryTimeouts) {
  4572. for (const timeout of this.#telemetryTimeouts.values()) {
  4573. clearTimeout(timeout);
  4574. }
  4575. this.#telemetryTimeouts = null;
  4576. }
  4577. this.parent = null;
  4578. }
  4579. get isResizable() {
  4580. return false;
  4581. }
  4582. makeResizable() {
  4583. if (this.isResizable) {
  4584. this.#createResizers();
  4585. this.#resizersDiv.classList.remove("hidden");
  4586. bindEvents(this, this.div, ["keydown"]);
  4587. }
  4588. }
  4589. get toolbarPosition() {
  4590. return null;
  4591. }
  4592. keydown(event) {
  4593. if (!this.isResizable || event.target !== this.div || event.key !== "Enter") {
  4594. return;
  4595. }
  4596. this._uiManager.setSelected(this);
  4597. this.#savedDimensions = {
  4598. savedX: this.x,
  4599. savedY: this.y,
  4600. savedWidth: this.width,
  4601. savedHeight: this.height
  4602. };
  4603. const children = this.#resizersDiv.children;
  4604. if (!this.#allResizerDivs) {
  4605. this.#allResizerDivs = Array.from(children);
  4606. const boundResizerKeydown = this.#resizerKeydown.bind(this);
  4607. const boundResizerBlur = this.#resizerBlur.bind(this);
  4608. const signal = this._uiManager._signal;
  4609. for (const div of this.#allResizerDivs) {
  4610. const name = div.getAttribute("data-resizer-name");
  4611. div.setAttribute("role", "spinbutton");
  4612. div.addEventListener("keydown", boundResizerKeydown, {
  4613. signal
  4614. });
  4615. div.addEventListener("blur", boundResizerBlur, {
  4616. signal
  4617. });
  4618. div.addEventListener("focus", this.#resizerFocus.bind(this, name), {
  4619. signal
  4620. });
  4621. AnnotationEditor._l10nPromise.get(`pdfjs-editor-resizer-label-${name}`).then(msg => div.setAttribute("aria-label", msg));
  4622. }
  4623. }
  4624. const first = this.#allResizerDivs[0];
  4625. let firstPosition = 0;
  4626. for (const div of children) {
  4627. if (div === first) {
  4628. break;
  4629. }
  4630. firstPosition++;
  4631. }
  4632. const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4);
  4633. if (nextFirstPosition !== firstPosition) {
  4634. if (nextFirstPosition < firstPosition) {
  4635. for (let i = 0; i < firstPosition - nextFirstPosition; i++) {
  4636. this.#resizersDiv.append(this.#resizersDiv.firstChild);
  4637. }
  4638. } else if (nextFirstPosition > firstPosition) {
  4639. for (let i = 0; i < nextFirstPosition - firstPosition; i++) {
  4640. this.#resizersDiv.firstChild.before(this.#resizersDiv.lastChild);
  4641. }
  4642. }
  4643. let i = 0;
  4644. for (const child of children) {
  4645. const div = this.#allResizerDivs[i++];
  4646. const name = div.getAttribute("data-resizer-name");
  4647. AnnotationEditor._l10nPromise.get(`pdfjs-editor-resizer-label-${name}`).then(msg => child.setAttribute("aria-label", msg));
  4648. }
  4649. }
  4650. this.#setResizerTabIndex(0);
  4651. this.#isResizerEnabledForKeyboard = true;
  4652. this.#resizersDiv.firstChild.focus({
  4653. focusVisible: true
  4654. });
  4655. event.preventDefault();
  4656. event.stopImmediatePropagation();
  4657. }
  4658. #resizerKeydown(event) {
  4659. AnnotationEditor._resizerKeyboardManager.exec(this, event);
  4660. }
  4661. #resizerBlur(event) {
  4662. if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) {
  4663. this.#stopResizing();
  4664. }
  4665. }
  4666. #resizerFocus(name) {
  4667. this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : "";
  4668. }
  4669. #setResizerTabIndex(value) {
  4670. if (!this.#allResizerDivs) {
  4671. return;
  4672. }
  4673. for (const div of this.#allResizerDivs) {
  4674. div.tabIndex = value;
  4675. }
  4676. }
  4677. _resizeWithKeyboard(x, y) {
  4678. if (!this.#isResizerEnabledForKeyboard) {
  4679. return;
  4680. }
  4681. this.#resizerPointermove(this.#focusedResizerName, {
  4682. movementX: x,
  4683. movementY: y
  4684. });
  4685. }
  4686. #stopResizing() {
  4687. this.#isResizerEnabledForKeyboard = false;
  4688. this.#setResizerTabIndex(-1);
  4689. if (this.#savedDimensions) {
  4690. const {
  4691. savedX,
  4692. savedY,
  4693. savedWidth,
  4694. savedHeight
  4695. } = this.#savedDimensions;
  4696. this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight);
  4697. this.#savedDimensions = null;
  4698. }
  4699. }
  4700. _stopResizingWithKeyboard() {
  4701. this.#stopResizing();
  4702. this.div.focus();
  4703. }
  4704. select() {
  4705. this.makeResizable();
  4706. this.div?.classList.add("selectedEditor");
  4707. if (!this.#editToolbar) {
  4708. this.addEditToolbar().then(() => {
  4709. if (this.div?.classList.contains("selectedEditor")) {
  4710. this.#editToolbar?.show();
  4711. }
  4712. });
  4713. return;
  4714. }
  4715. this.#editToolbar?.show();
  4716. }
  4717. unselect() {
  4718. this.#resizersDiv?.classList.add("hidden");
  4719. this.div?.classList.remove("selectedEditor");
  4720. if (this.div?.contains(document.activeElement)) {
  4721. this._uiManager.currentLayer.div.focus({
  4722. preventScroll: true
  4723. });
  4724. }
  4725. this.#editToolbar?.hide();
  4726. }
  4727. updateParams(type, value) {}
  4728. disableEditing() {}
  4729. enableEditing() {}
  4730. enterInEditMode() {}
  4731. getImageForAltText() {
  4732. return null;
  4733. }
  4734. get contentDiv() {
  4735. return this.div;
  4736. }
  4737. get isEditing() {
  4738. return this.#isEditing;
  4739. }
  4740. set isEditing(value) {
  4741. this.#isEditing = value;
  4742. if (!this.parent) {
  4743. return;
  4744. }
  4745. if (value) {
  4746. this.parent.setSelected(this);
  4747. this.parent.setActiveEditor(this);
  4748. } else {
  4749. this.parent.setActiveEditor(null);
  4750. }
  4751. }
  4752. setAspectRatio(width, height) {
  4753. this.#keepAspectRatio = true;
  4754. const aspectRatio = width / height;
  4755. const {
  4756. style
  4757. } = this.div;
  4758. style.aspectRatio = aspectRatio;
  4759. style.height = "auto";
  4760. }
  4761. static get MIN_SIZE() {
  4762. return 16;
  4763. }
  4764. static canCreateNewEmptyEditor() {
  4765. return true;
  4766. }
  4767. get telemetryInitialData() {
  4768. return {
  4769. action: "added"
  4770. };
  4771. }
  4772. get telemetryFinalData() {
  4773. return null;
  4774. }
  4775. _reportTelemetry(data, mustWait = false) {
  4776. if (mustWait) {
  4777. this.#telemetryTimeouts ||= new Map();
  4778. const {
  4779. action
  4780. } = data;
  4781. let timeout = this.#telemetryTimeouts.get(action);
  4782. if (timeout) {
  4783. clearTimeout(timeout);
  4784. }
  4785. timeout = setTimeout(() => {
  4786. this._reportTelemetry(data);
  4787. this.#telemetryTimeouts.delete(action);
  4788. if (this.#telemetryTimeouts.size === 0) {
  4789. this.#telemetryTimeouts = null;
  4790. }
  4791. }, AnnotationEditor._telemetryTimeout);
  4792. this.#telemetryTimeouts.set(action, timeout);
  4793. return;
  4794. }
  4795. data.type ||= this.editorType;
  4796. this._uiManager._eventBus.dispatch("reporttelemetry", {
  4797. source: this,
  4798. details: {
  4799. type: "editing",
  4800. data
  4801. }
  4802. });
  4803. }
  4804. show(visible = this._isVisible) {
  4805. this.div.classList.toggle("hidden", !visible);
  4806. this._isVisible = visible;
  4807. }
  4808. enable() {
  4809. if (this.div) {
  4810. this.div.tabIndex = 0;
  4811. }
  4812. this.#disabled = false;
  4813. }
  4814. disable() {
  4815. if (this.div) {
  4816. this.div.tabIndex = -1;
  4817. }
  4818. this.#disabled = true;
  4819. }
  4820. renderAnnotationElement(annotation) {
  4821. let content = annotation.container.querySelector(".annotationContent");
  4822. if (!content) {
  4823. content = document.createElement("div");
  4824. content.classList.add("annotationContent", this.editorType);
  4825. annotation.container.prepend(content);
  4826. } else if (content.nodeName === "CANVAS") {
  4827. const canvas = content;
  4828. content = document.createElement("div");
  4829. content.classList.add("annotationContent", this.editorType);
  4830. canvas.before(content);
  4831. }
  4832. return content;
  4833. }
  4834. resetAnnotationElement(annotation) {
  4835. const {
  4836. firstChild
  4837. } = annotation.container;
  4838. if (firstChild.nodeName === "DIV" && firstChild.classList.contains("annotationContent")) {
  4839. firstChild.remove();
  4840. }
  4841. }
  4842. }
  4843. class FakeEditor extends AnnotationEditor {
  4844. constructor(params) {
  4845. super(params);
  4846. this.annotationElementId = params.annotationElementId;
  4847. this.deleted = true;
  4848. }
  4849. serialize() {
  4850. return {
  4851. id: this.annotationElementId,
  4852. deleted: true,
  4853. pageIndex: this.pageIndex
  4854. };
  4855. }
  4856. }
  4857. ;// CONCATENATED MODULE: ./src/shared/murmurhash3.js
  4858. const SEED = 0xc3d2e1f0;
  4859. const MASK_HIGH = 0xffff0000;
  4860. const MASK_LOW = 0xffff;
  4861. class MurmurHash3_64 {
  4862. constructor(seed) {
  4863. this.h1 = seed ? seed & 0xffffffff : SEED;
  4864. this.h2 = seed ? seed & 0xffffffff : SEED;
  4865. }
  4866. update(input) {
  4867. let data, length;
  4868. if (typeof input === "string") {
  4869. data = new Uint8Array(input.length * 2);
  4870. length = 0;
  4871. for (let i = 0, ii = input.length; i < ii; i++) {
  4872. const code = input.charCodeAt(i);
  4873. if (code <= 0xff) {
  4874. data[length++] = code;
  4875. } else {
  4876. data[length++] = code >>> 8;
  4877. data[length++] = code & 0xff;
  4878. }
  4879. }
  4880. } else if (ArrayBuffer.isView(input)) {
  4881. data = input.slice();
  4882. length = data.byteLength;
  4883. } else {
  4884. throw new Error("Invalid data format, must be a string or TypedArray.");
  4885. }
  4886. const blockCounts = length >> 2;
  4887. const tailLength = length - blockCounts * 4;
  4888. const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts);
  4889. let k1 = 0,
  4890. k2 = 0;
  4891. let h1 = this.h1,
  4892. h2 = this.h2;
  4893. const C1 = 0xcc9e2d51,
  4894. C2 = 0x1b873593;
  4895. const C1_LOW = C1 & MASK_LOW,
  4896. C2_LOW = C2 & MASK_LOW;
  4897. for (let i = 0; i < blockCounts; i++) {
  4898. if (i & 1) {
  4899. k1 = dataUint32[i];
  4900. k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;
  4901. k1 = k1 << 15 | k1 >>> 17;
  4902. k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;
  4903. h1 ^= k1;
  4904. h1 = h1 << 13 | h1 >>> 19;
  4905. h1 = h1 * 5 + 0xe6546b64;
  4906. } else {
  4907. k2 = dataUint32[i];
  4908. k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW;
  4909. k2 = k2 << 15 | k2 >>> 17;
  4910. k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW;
  4911. h2 ^= k2;
  4912. h2 = h2 << 13 | h2 >>> 19;
  4913. h2 = h2 * 5 + 0xe6546b64;
  4914. }
  4915. }
  4916. k1 = 0;
  4917. switch (tailLength) {
  4918. case 3:
  4919. k1 ^= data[blockCounts * 4 + 2] << 16;
  4920. case 2:
  4921. k1 ^= data[blockCounts * 4 + 1] << 8;
  4922. case 1:
  4923. k1 ^= data[blockCounts * 4];
  4924. k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;
  4925. k1 = k1 << 15 | k1 >>> 17;
  4926. k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;
  4927. if (blockCounts & 1) {
  4928. h1 ^= k1;
  4929. } else {
  4930. h2 ^= k1;
  4931. }
  4932. }
  4933. this.h1 = h1;
  4934. this.h2 = h2;
  4935. }
  4936. hexdigest() {
  4937. let h1 = this.h1,
  4938. h2 = this.h2;
  4939. h1 ^= h2 >>> 1;
  4940. h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW;
  4941. h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16;
  4942. h1 ^= h2 >>> 1;
  4943. h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW;
  4944. h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16;
  4945. h1 ^= h2 >>> 1;
  4946. return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0");
  4947. }
  4948. }
  4949. ;// CONCATENATED MODULE: ./src/display/annotation_storage.js
  4950. const SerializableEmpty = Object.freeze({
  4951. map: null,
  4952. hash: "",
  4953. transfer: undefined
  4954. });
  4955. class AnnotationStorage {
  4956. #modified = false;
  4957. #modifiedIds = null;
  4958. #storage = new Map();
  4959. constructor() {
  4960. this.onSetModified = null;
  4961. this.onResetModified = null;
  4962. this.onAnnotationEditor = null;
  4963. }
  4964. getValue(key, defaultValue) {
  4965. const value = this.#storage.get(key);
  4966. if (value === undefined) {
  4967. return defaultValue;
  4968. }
  4969. return Object.assign(defaultValue, value);
  4970. }
  4971. getRawValue(key) {
  4972. return this.#storage.get(key);
  4973. }
  4974. remove(key) {
  4975. this.#storage.delete(key);
  4976. if (this.#storage.size === 0) {
  4977. this.resetModified();
  4978. }
  4979. if (typeof this.onAnnotationEditor === "function") {
  4980. for (const value of this.#storage.values()) {
  4981. if (value instanceof AnnotationEditor) {
  4982. return;
  4983. }
  4984. }
  4985. this.onAnnotationEditor(null);
  4986. }
  4987. }
  4988. setValue(key, value) {
  4989. const obj = this.#storage.get(key);
  4990. let modified = false;
  4991. if (obj !== undefined) {
  4992. for (const [entry, val] of Object.entries(value)) {
  4993. if (obj[entry] !== val) {
  4994. modified = true;
  4995. obj[entry] = val;
  4996. }
  4997. }
  4998. } else {
  4999. modified = true;
  5000. this.#storage.set(key, value);
  5001. }
  5002. if (modified) {
  5003. this.#setModified();
  5004. }
  5005. if (value instanceof AnnotationEditor && typeof this.onAnnotationEditor === "function") {
  5006. this.onAnnotationEditor(value.constructor._type);
  5007. }
  5008. }
  5009. has(key) {
  5010. return this.#storage.has(key);
  5011. }
  5012. getAll() {
  5013. return this.#storage.size > 0 ? objectFromMap(this.#storage) : null;
  5014. }
  5015. setAll(obj) {
  5016. for (const [key, val] of Object.entries(obj)) {
  5017. this.setValue(key, val);
  5018. }
  5019. }
  5020. get size() {
  5021. return this.#storage.size;
  5022. }
  5023. #setModified() {
  5024. if (!this.#modified) {
  5025. this.#modified = true;
  5026. if (typeof this.onSetModified === "function") {
  5027. this.onSetModified();
  5028. }
  5029. }
  5030. }
  5031. resetModified() {
  5032. if (this.#modified) {
  5033. this.#modified = false;
  5034. if (typeof this.onResetModified === "function") {
  5035. this.onResetModified();
  5036. }
  5037. }
  5038. }
  5039. get print() {
  5040. return new PrintAnnotationStorage(this);
  5041. }
  5042. get serializable() {
  5043. if (this.#storage.size === 0) {
  5044. return SerializableEmpty;
  5045. }
  5046. const map = new Map(),
  5047. hash = new MurmurHash3_64(),
  5048. transfer = [];
  5049. const context = Object.create(null);
  5050. let hasBitmap = false;
  5051. for (const [key, val] of this.#storage) {
  5052. const serialized = val instanceof AnnotationEditor ? val.serialize(false, context) : val;
  5053. if (serialized) {
  5054. map.set(key, serialized);
  5055. hash.update(`${key}:${JSON.stringify(serialized)}`);
  5056. hasBitmap ||= !!serialized.bitmap;
  5057. }
  5058. }
  5059. if (hasBitmap) {
  5060. for (const value of map.values()) {
  5061. if (value.bitmap) {
  5062. transfer.push(value.bitmap);
  5063. }
  5064. }
  5065. }
  5066. return map.size > 0 ? {
  5067. map,
  5068. hash: hash.hexdigest(),
  5069. transfer
  5070. } : SerializableEmpty;
  5071. }
  5072. get editorStats() {
  5073. let stats = null;
  5074. const typeToEditor = new Map();
  5075. for (const value of this.#storage.values()) {
  5076. if (!(value instanceof AnnotationEditor)) {
  5077. continue;
  5078. }
  5079. const editorStats = value.telemetryFinalData;
  5080. if (!editorStats) {
  5081. continue;
  5082. }
  5083. const {
  5084. type
  5085. } = editorStats;
  5086. if (!typeToEditor.has(type)) {
  5087. typeToEditor.set(type, Object.getPrototypeOf(value).constructor);
  5088. }
  5089. stats ||= Object.create(null);
  5090. const map = stats[type] ||= new Map();
  5091. for (const [key, val] of Object.entries(editorStats)) {
  5092. if (key === "type") {
  5093. continue;
  5094. }
  5095. let counters = map.get(key);
  5096. if (!counters) {
  5097. counters = new Map();
  5098. map.set(key, counters);
  5099. }
  5100. const count = counters.get(val) ?? 0;
  5101. counters.set(val, count + 1);
  5102. }
  5103. }
  5104. for (const [type, editor] of typeToEditor) {
  5105. stats[type] = editor.computeTelemetryFinalData(stats[type]);
  5106. }
  5107. return stats;
  5108. }
  5109. resetModifiedIds() {
  5110. this.#modifiedIds = null;
  5111. }
  5112. get modifiedIds() {
  5113. if (this.#modifiedIds) {
  5114. return this.#modifiedIds;
  5115. }
  5116. const ids = [];
  5117. for (const value of this.#storage.values()) {
  5118. if (!(value instanceof AnnotationEditor) || !value.annotationElementId || !value.serialize()) {
  5119. continue;
  5120. }
  5121. ids.push(value.annotationElementId);
  5122. }
  5123. return this.#modifiedIds = {
  5124. ids: new Set(ids),
  5125. hash: ids.join(",")
  5126. };
  5127. }
  5128. }
  5129. class PrintAnnotationStorage extends AnnotationStorage {
  5130. #serializable;
  5131. constructor(parent) {
  5132. super();
  5133. const {
  5134. map,
  5135. hash,
  5136. transfer
  5137. } = parent.serializable;
  5138. const clone = structuredClone(map, transfer ? {
  5139. transfer
  5140. } : null);
  5141. this.#serializable = {
  5142. map: clone,
  5143. hash,
  5144. transfer
  5145. };
  5146. }
  5147. get print() {
  5148. unreachable("Should not call PrintAnnotationStorage.print");
  5149. }
  5150. get serializable() {
  5151. return this.#serializable;
  5152. }
  5153. get modifiedIds() {
  5154. return shadow(this, "modifiedIds", {
  5155. ids: new Set(),
  5156. hash: ""
  5157. });
  5158. }
  5159. }
  5160. ;// CONCATENATED MODULE: ./src/display/font_loader.js
  5161. class FontLoader {
  5162. #systemFonts = new Set();
  5163. constructor({
  5164. ownerDocument = globalThis.document,
  5165. styleElement = null
  5166. }) {
  5167. this._document = ownerDocument;
  5168. this.nativeFontFaces = new Set();
  5169. this.styleElement = null;
  5170. this.loadingRequests = [];
  5171. this.loadTestFontId = 0;
  5172. }
  5173. addNativeFontFace(nativeFontFace) {
  5174. this.nativeFontFaces.add(nativeFontFace);
  5175. this._document.fonts.add(nativeFontFace);
  5176. }
  5177. removeNativeFontFace(nativeFontFace) {
  5178. this.nativeFontFaces.delete(nativeFontFace);
  5179. this._document.fonts.delete(nativeFontFace);
  5180. }
  5181. insertRule(rule) {
  5182. if (!this.styleElement) {
  5183. this.styleElement = this._document.createElement("style");
  5184. this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement);
  5185. }
  5186. const styleSheet = this.styleElement.sheet;
  5187. styleSheet.insertRule(rule, styleSheet.cssRules.length);
  5188. }
  5189. clear() {
  5190. for (const nativeFontFace of this.nativeFontFaces) {
  5191. this._document.fonts.delete(nativeFontFace);
  5192. }
  5193. this.nativeFontFaces.clear();
  5194. this.#systemFonts.clear();
  5195. if (this.styleElement) {
  5196. this.styleElement.remove();
  5197. this.styleElement = null;
  5198. }
  5199. }
  5200. async loadSystemFont({
  5201. systemFontInfo: info,
  5202. _inspectFont
  5203. }) {
  5204. if (!info || this.#systemFonts.has(info.loadedName)) {
  5205. return;
  5206. }
  5207. assert(!this.disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set.");
  5208. if (this.isFontLoadingAPISupported) {
  5209. const {
  5210. loadedName,
  5211. src,
  5212. style
  5213. } = info;
  5214. const fontFace = new FontFace(loadedName, src, style);
  5215. this.addNativeFontFace(fontFace);
  5216. try {
  5217. await fontFace.load();
  5218. this.#systemFonts.add(loadedName);
  5219. _inspectFont?.(info);
  5220. } catch {
  5221. warn(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`);
  5222. this.removeNativeFontFace(fontFace);
  5223. }
  5224. return;
  5225. }
  5226. unreachable("Not implemented: loadSystemFont without the Font Loading API.");
  5227. }
  5228. async bind(font) {
  5229. if (font.attached || font.missingFile && !font.systemFontInfo) {
  5230. return;
  5231. }
  5232. font.attached = true;
  5233. if (font.systemFontInfo) {
  5234. await this.loadSystemFont(font);
  5235. return;
  5236. }
  5237. if (this.isFontLoadingAPISupported) {
  5238. const nativeFontFace = font.createNativeFontFace();
  5239. if (nativeFontFace) {
  5240. this.addNativeFontFace(nativeFontFace);
  5241. try {
  5242. await nativeFontFace.loaded;
  5243. } catch (ex) {
  5244. warn(`Failed to load font '${nativeFontFace.family}': '${ex}'.`);
  5245. font.disableFontFace = true;
  5246. throw ex;
  5247. }
  5248. }
  5249. return;
  5250. }
  5251. const rule = font.createFontFaceRule();
  5252. if (rule) {
  5253. this.insertRule(rule);
  5254. if (this.isSyncFontLoadingSupported) {
  5255. return;
  5256. }
  5257. await new Promise(resolve => {
  5258. const request = this._queueLoadingCallback(resolve);
  5259. this._prepareFontLoadEvent(font, request);
  5260. });
  5261. }
  5262. }
  5263. get isFontLoadingAPISupported() {
  5264. const hasFonts = !!this._document?.fonts;
  5265. return shadow(this, "isFontLoadingAPISupported", hasFonts);
  5266. }
  5267. get isSyncFontLoadingSupported() {
  5268. let supported = false;
  5269. if (isNodeJS) {
  5270. supported = true;
  5271. } else if (typeof navigator !== "undefined" && typeof navigator?.userAgent === "string" && /Mozilla\/5.0.*?rv:\d+.*? Gecko/.test(navigator.userAgent)) {
  5272. supported = true;
  5273. }
  5274. return shadow(this, "isSyncFontLoadingSupported", supported);
  5275. }
  5276. _queueLoadingCallback(callback) {
  5277. function completeRequest() {
  5278. assert(!request.done, "completeRequest() cannot be called twice.");
  5279. request.done = true;
  5280. while (loadingRequests.length > 0 && loadingRequests[0].done) {
  5281. const otherRequest = loadingRequests.shift();
  5282. setTimeout(otherRequest.callback, 0);
  5283. }
  5284. }
  5285. const {
  5286. loadingRequests
  5287. } = this;
  5288. const request = {
  5289. done: false,
  5290. complete: completeRequest,
  5291. callback
  5292. };
  5293. loadingRequests.push(request);
  5294. return request;
  5295. }
  5296. get _loadTestFont() {
  5297. 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==");
  5298. return shadow(this, "_loadTestFont", testFont);
  5299. }
  5300. _prepareFontLoadEvent(font, request) {
  5301. function int32(data, offset) {
  5302. return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff;
  5303. }
  5304. function spliceString(s, offset, remove, insert) {
  5305. const chunk1 = s.substring(0, offset);
  5306. const chunk2 = s.substring(offset + remove);
  5307. return chunk1 + insert + chunk2;
  5308. }
  5309. let i, ii;
  5310. const canvas = this._document.createElement("canvas");
  5311. canvas.width = 1;
  5312. canvas.height = 1;
  5313. const ctx = canvas.getContext("2d");
  5314. let called = 0;
  5315. function isFontReady(name, callback) {
  5316. if (++called > 30) {
  5317. warn("Load test font never loaded.");
  5318. callback();
  5319. return;
  5320. }
  5321. ctx.font = "30px " + name;
  5322. ctx.fillText(".", 0, 20);
  5323. const imageData = ctx.getImageData(0, 0, 1, 1);
  5324. if (imageData.data[3] > 0) {
  5325. callback();
  5326. return;
  5327. }
  5328. setTimeout(isFontReady.bind(null, name, callback));
  5329. }
  5330. const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`;
  5331. let data = this._loadTestFont;
  5332. const COMMENT_OFFSET = 976;
  5333. data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId);
  5334. const CFF_CHECKSUM_OFFSET = 16;
  5335. const XXXX_VALUE = 0x58585858;
  5336. let checksum = int32(data, CFF_CHECKSUM_OFFSET);
  5337. for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {
  5338. checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0;
  5339. }
  5340. if (i < loadTestFontId.length) {
  5341. checksum = checksum - XXXX_VALUE + int32(loadTestFontId + "XXX", i) | 0;
  5342. }
  5343. data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum));
  5344. const url = `url(data:font/opentype;base64,${btoa(data)});`;
  5345. const rule = `@font-face {font-family:"${loadTestFontId}";src:${url}}`;
  5346. this.insertRule(rule);
  5347. const div = this._document.createElement("div");
  5348. div.style.visibility = "hidden";
  5349. div.style.width = div.style.height = "10px";
  5350. div.style.position = "absolute";
  5351. div.style.top = div.style.left = "0px";
  5352. for (const name of [font.loadedName, loadTestFontId]) {
  5353. const span = this._document.createElement("span");
  5354. span.textContent = "Hi";
  5355. span.style.fontFamily = name;
  5356. div.append(span);
  5357. }
  5358. this._document.body.append(div);
  5359. isFontReady(loadTestFontId, () => {
  5360. div.remove();
  5361. request.complete();
  5362. });
  5363. }
  5364. }
  5365. class FontFaceObject {
  5366. constructor(translatedData, {
  5367. disableFontFace = false,
  5368. inspectFont = null
  5369. }) {
  5370. this.compiledGlyphs = Object.create(null);
  5371. for (const i in translatedData) {
  5372. this[i] = translatedData[i];
  5373. }
  5374. this.disableFontFace = disableFontFace === true;
  5375. this._inspectFont = inspectFont;
  5376. }
  5377. createNativeFontFace() {
  5378. if (!this.data || this.disableFontFace) {
  5379. return null;
  5380. }
  5381. let nativeFontFace;
  5382. if (!this.cssFontInfo) {
  5383. nativeFontFace = new FontFace(this.loadedName, this.data, {});
  5384. } else {
  5385. const css = {
  5386. weight: this.cssFontInfo.fontWeight
  5387. };
  5388. if (this.cssFontInfo.italicAngle) {
  5389. css.style = `oblique ${this.cssFontInfo.italicAngle}deg`;
  5390. }
  5391. nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css);
  5392. }
  5393. this._inspectFont?.(this);
  5394. return nativeFontFace;
  5395. }
  5396. createFontFaceRule() {
  5397. if (!this.data || this.disableFontFace) {
  5398. return null;
  5399. }
  5400. const data = bytesToString(this.data);
  5401. const url = `url(data:${this.mimetype};base64,${btoa(data)});`;
  5402. let rule;
  5403. if (!this.cssFontInfo) {
  5404. rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
  5405. } else {
  5406. let css = `font-weight: ${this.cssFontInfo.fontWeight};`;
  5407. if (this.cssFontInfo.italicAngle) {
  5408. css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`;
  5409. }
  5410. rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`;
  5411. }
  5412. this._inspectFont?.(this, url);
  5413. return rule;
  5414. }
  5415. getPathGenerator(objs, character) {
  5416. if (this.compiledGlyphs[character] !== undefined) {
  5417. return this.compiledGlyphs[character];
  5418. }
  5419. let cmds;
  5420. try {
  5421. cmds = objs.get(this.loadedName + "_path_" + character);
  5422. } catch (ex) {
  5423. warn(`getPathGenerator - ignoring character: "${ex}".`);
  5424. }
  5425. if (!Array.isArray(cmds) || cmds.length === 0) {
  5426. return this.compiledGlyphs[character] = function (c, size) {};
  5427. }
  5428. const commands = [];
  5429. for (let i = 0, ii = cmds.length; i < ii;) {
  5430. switch (cmds[i++]) {
  5431. case FontRenderOps.BEZIER_CURVE_TO:
  5432. {
  5433. const [a, b, c, d, e, f] = cmds.slice(i, i + 6);
  5434. commands.push(ctx => ctx.bezierCurveTo(a, b, c, d, e, f));
  5435. i += 6;
  5436. }
  5437. break;
  5438. case FontRenderOps.MOVE_TO:
  5439. {
  5440. const [a, b] = cmds.slice(i, i + 2);
  5441. commands.push(ctx => ctx.moveTo(a, b));
  5442. i += 2;
  5443. }
  5444. break;
  5445. case FontRenderOps.LINE_TO:
  5446. {
  5447. const [a, b] = cmds.slice(i, i + 2);
  5448. commands.push(ctx => ctx.lineTo(a, b));
  5449. i += 2;
  5450. }
  5451. break;
  5452. case FontRenderOps.QUADRATIC_CURVE_TO:
  5453. {
  5454. const [a, b, c, d] = cmds.slice(i, i + 4);
  5455. commands.push(ctx => ctx.quadraticCurveTo(a, b, c, d));
  5456. i += 4;
  5457. }
  5458. break;
  5459. case FontRenderOps.RESTORE:
  5460. commands.push(ctx => ctx.restore());
  5461. break;
  5462. case FontRenderOps.SAVE:
  5463. commands.push(ctx => ctx.save());
  5464. break;
  5465. case FontRenderOps.SCALE:
  5466. assert(commands.length === 2, "Scale command is only valid at the third position.");
  5467. break;
  5468. case FontRenderOps.TRANSFORM:
  5469. {
  5470. const [a, b, c, d, e, f] = cmds.slice(i, i + 6);
  5471. commands.push(ctx => ctx.transform(a, b, c, d, e, f));
  5472. i += 6;
  5473. }
  5474. break;
  5475. case FontRenderOps.TRANSLATE:
  5476. {
  5477. const [a, b] = cmds.slice(i, i + 2);
  5478. commands.push(ctx => ctx.translate(a, b));
  5479. i += 2;
  5480. }
  5481. break;
  5482. }
  5483. }
  5484. return this.compiledGlyphs[character] = function glyphDrawer(ctx, size) {
  5485. commands[0](ctx);
  5486. commands[1](ctx);
  5487. ctx.scale(size, -size);
  5488. for (let i = 2, ii = commands.length; i < ii; i++) {
  5489. commands[i](ctx);
  5490. }
  5491. };
  5492. }
  5493. }
  5494. ;// CONCATENATED MODULE: ./src/display/node_utils.js
  5495. if (isNodeJS) {
  5496. var packageCapability = Promise.withResolvers();
  5497. var packageMap = null;
  5498. const loadPackages = async () => {
  5499. const fs = await import( /*webpackIgnore: true*/"fs"),
  5500. http = await import( /*webpackIgnore: true*/"http"),
  5501. https = await import( /*webpackIgnore: true*/"https"),
  5502. url = await import( /*webpackIgnore: true*/"url");
  5503. let canvas, path2d;
  5504. return new Map(Object.entries({
  5505. fs,
  5506. http,
  5507. https,
  5508. url,
  5509. canvas,
  5510. path2d
  5511. }));
  5512. };
  5513. loadPackages().then(map => {
  5514. packageMap = map;
  5515. packageCapability.resolve();
  5516. }, reason => {
  5517. warn(`loadPackages: ${reason}`);
  5518. packageMap = new Map();
  5519. packageCapability.resolve();
  5520. });
  5521. }
  5522. class NodePackages {
  5523. static get promise() {
  5524. return packageCapability.promise;
  5525. }
  5526. static get(name) {
  5527. return packageMap?.get(name);
  5528. }
  5529. }
  5530. const node_utils_fetchData = function (url) {
  5531. const fs = NodePackages.get("fs");
  5532. return fs.promises.readFile(url).then(data => new Uint8Array(data));
  5533. };
  5534. class NodeFilterFactory extends BaseFilterFactory {}
  5535. class NodeCanvasFactory extends BaseCanvasFactory {
  5536. _createCanvas(width, height) {
  5537. const canvas = NodePackages.get("canvas");
  5538. return canvas.createCanvas(width, height);
  5539. }
  5540. }
  5541. class NodeCMapReaderFactory extends BaseCMapReaderFactory {
  5542. _fetchData(url, compressionType) {
  5543. return node_utils_fetchData(url).then(data => ({
  5544. cMapData: data,
  5545. compressionType
  5546. }));
  5547. }
  5548. }
  5549. class NodeStandardFontDataFactory extends BaseStandardFontDataFactory {
  5550. _fetchData(url) {
  5551. return node_utils_fetchData(url);
  5552. }
  5553. }
  5554. ;// CONCATENATED MODULE: ./src/display/pattern_helper.js
  5555. const PathType = {
  5556. FILL: "Fill",
  5557. STROKE: "Stroke",
  5558. SHADING: "Shading"
  5559. };
  5560. function applyBoundingBox(ctx, bbox) {
  5561. if (!bbox) {
  5562. return;
  5563. }
  5564. const width = bbox[2] - bbox[0];
  5565. const height = bbox[3] - bbox[1];
  5566. const region = new Path2D();
  5567. region.rect(bbox[0], bbox[1], width, height);
  5568. ctx.clip(region);
  5569. }
  5570. class BaseShadingPattern {
  5571. constructor() {
  5572. if (this.constructor === BaseShadingPattern) {
  5573. unreachable("Cannot initialize BaseShadingPattern.");
  5574. }
  5575. }
  5576. getPattern() {
  5577. unreachable("Abstract method `getPattern` called.");
  5578. }
  5579. }
  5580. class RadialAxialShadingPattern extends BaseShadingPattern {
  5581. constructor(IR) {
  5582. super();
  5583. this._type = IR[1];
  5584. this._bbox = IR[2];
  5585. this._colorStops = IR[3];
  5586. this._p0 = IR[4];
  5587. this._p1 = IR[5];
  5588. this._r0 = IR[6];
  5589. this._r1 = IR[7];
  5590. this.matrix = null;
  5591. }
  5592. _createGradient(ctx) {
  5593. let grad;
  5594. if (this._type === "axial") {
  5595. grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]);
  5596. } else if (this._type === "radial") {
  5597. grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1);
  5598. }
  5599. for (const colorStop of this._colorStops) {
  5600. grad.addColorStop(colorStop[0], colorStop[1]);
  5601. }
  5602. return grad;
  5603. }
  5604. getPattern(ctx, owner, inverse, pathType) {
  5605. let pattern;
  5606. if (pathType === PathType.STROKE || pathType === PathType.FILL) {
  5607. const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0];
  5608. const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;
  5609. const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1;
  5610. const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height, true);
  5611. const tmpCtx = tmpCanvas.context;
  5612. tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
  5613. tmpCtx.beginPath();
  5614. tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
  5615. tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]);
  5616. inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]);
  5617. tmpCtx.transform(...owner.baseTransform);
  5618. if (this.matrix) {
  5619. tmpCtx.transform(...this.matrix);
  5620. }
  5621. applyBoundingBox(tmpCtx, this._bbox);
  5622. tmpCtx.fillStyle = this._createGradient(tmpCtx);
  5623. tmpCtx.fill();
  5624. pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat");
  5625. const domMatrix = new DOMMatrix(inverse);
  5626. pattern.setTransform(domMatrix);
  5627. } else {
  5628. applyBoundingBox(ctx, this._bbox);
  5629. pattern = this._createGradient(ctx);
  5630. }
  5631. return pattern;
  5632. }
  5633. }
  5634. function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
  5635. const coords = context.coords,
  5636. colors = context.colors;
  5637. const bytes = data.data,
  5638. rowSize = data.width * 4;
  5639. let tmp;
  5640. if (coords[p1 + 1] > coords[p2 + 1]) {
  5641. tmp = p1;
  5642. p1 = p2;
  5643. p2 = tmp;
  5644. tmp = c1;
  5645. c1 = c2;
  5646. c2 = tmp;
  5647. }
  5648. if (coords[p2 + 1] > coords[p3 + 1]) {
  5649. tmp = p2;
  5650. p2 = p3;
  5651. p3 = tmp;
  5652. tmp = c2;
  5653. c2 = c3;
  5654. c3 = tmp;
  5655. }
  5656. if (coords[p1 + 1] > coords[p2 + 1]) {
  5657. tmp = p1;
  5658. p1 = p2;
  5659. p2 = tmp;
  5660. tmp = c1;
  5661. c1 = c2;
  5662. c2 = tmp;
  5663. }
  5664. const x1 = (coords[p1] + context.offsetX) * context.scaleX;
  5665. const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
  5666. const x2 = (coords[p2] + context.offsetX) * context.scaleX;
  5667. const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
  5668. const x3 = (coords[p3] + context.offsetX) * context.scaleX;
  5669. const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
  5670. if (y1 >= y3) {
  5671. return;
  5672. }
  5673. const c1r = colors[c1],
  5674. c1g = colors[c1 + 1],
  5675. c1b = colors[c1 + 2];
  5676. const c2r = colors[c2],
  5677. c2g = colors[c2 + 1],
  5678. c2b = colors[c2 + 2];
  5679. const c3r = colors[c3],
  5680. c3g = colors[c3 + 1],
  5681. c3b = colors[c3 + 2];
  5682. const minY = Math.round(y1),
  5683. maxY = Math.round(y3);
  5684. let xa, car, cag, cab;
  5685. let xb, cbr, cbg, cbb;
  5686. for (let y = minY; y <= maxY; y++) {
  5687. if (y < y2) {
  5688. const k = y < y1 ? 0 : (y1 - y) / (y1 - y2);
  5689. xa = x1 - (x1 - x2) * k;
  5690. car = c1r - (c1r - c2r) * k;
  5691. cag = c1g - (c1g - c2g) * k;
  5692. cab = c1b - (c1b - c2b) * k;
  5693. } else {
  5694. let k;
  5695. if (y > y3) {
  5696. k = 1;
  5697. } else if (y2 === y3) {
  5698. k = 0;
  5699. } else {
  5700. k = (y2 - y) / (y2 - y3);
  5701. }
  5702. xa = x2 - (x2 - x3) * k;
  5703. car = c2r - (c2r - c3r) * k;
  5704. cag = c2g - (c2g - c3g) * k;
  5705. cab = c2b - (c2b - c3b) * k;
  5706. }
  5707. let k;
  5708. if (y < y1) {
  5709. k = 0;
  5710. } else if (y > y3) {
  5711. k = 1;
  5712. } else {
  5713. k = (y1 - y) / (y1 - y3);
  5714. }
  5715. xb = x1 - (x1 - x3) * k;
  5716. cbr = c1r - (c1r - c3r) * k;
  5717. cbg = c1g - (c1g - c3g) * k;
  5718. cbb = c1b - (c1b - c3b) * k;
  5719. const x1_ = Math.round(Math.min(xa, xb));
  5720. const x2_ = Math.round(Math.max(xa, xb));
  5721. let j = rowSize * y + x1_ * 4;
  5722. for (let x = x1_; x <= x2_; x++) {
  5723. k = (xa - x) / (xa - xb);
  5724. if (k < 0) {
  5725. k = 0;
  5726. } else if (k > 1) {
  5727. k = 1;
  5728. }
  5729. bytes[j++] = car - (car - cbr) * k | 0;
  5730. bytes[j++] = cag - (cag - cbg) * k | 0;
  5731. bytes[j++] = cab - (cab - cbb) * k | 0;
  5732. bytes[j++] = 255;
  5733. }
  5734. }
  5735. }
  5736. function drawFigure(data, figure, context) {
  5737. const ps = figure.coords;
  5738. const cs = figure.colors;
  5739. let i, ii;
  5740. switch (figure.type) {
  5741. case "lattice":
  5742. const verticesPerRow = figure.verticesPerRow;
  5743. const rows = Math.floor(ps.length / verticesPerRow) - 1;
  5744. const cols = verticesPerRow - 1;
  5745. for (i = 0; i < rows; i++) {
  5746. let q = i * verticesPerRow;
  5747. for (let j = 0; j < cols; j++, q++) {
  5748. drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]);
  5749. drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
  5750. }
  5751. }
  5752. break;
  5753. case "triangles":
  5754. for (i = 0, ii = ps.length; i < ii; i += 3) {
  5755. drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]);
  5756. }
  5757. break;
  5758. default:
  5759. throw new Error("illegal figure");
  5760. }
  5761. }
  5762. class MeshShadingPattern extends BaseShadingPattern {
  5763. constructor(IR) {
  5764. super();
  5765. this._coords = IR[2];
  5766. this._colors = IR[3];
  5767. this._figures = IR[4];
  5768. this._bounds = IR[5];
  5769. this._bbox = IR[7];
  5770. this._background = IR[8];
  5771. this.matrix = null;
  5772. }
  5773. _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) {
  5774. const EXPECTED_SCALE = 1.1;
  5775. const MAX_PATTERN_SIZE = 3000;
  5776. const BORDER_SIZE = 2;
  5777. const offsetX = Math.floor(this._bounds[0]);
  5778. const offsetY = Math.floor(this._bounds[1]);
  5779. const boundsWidth = Math.ceil(this._bounds[2]) - offsetX;
  5780. const boundsHeight = Math.ceil(this._bounds[3]) - offsetY;
  5781. const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
  5782. const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
  5783. const scaleX = boundsWidth / width;
  5784. const scaleY = boundsHeight / height;
  5785. const context = {
  5786. coords: this._coords,
  5787. colors: this._colors,
  5788. offsetX: -offsetX,
  5789. offsetY: -offsetY,
  5790. scaleX: 1 / scaleX,
  5791. scaleY: 1 / scaleY
  5792. };
  5793. const paddedWidth = width + BORDER_SIZE * 2;
  5794. const paddedHeight = height + BORDER_SIZE * 2;
  5795. const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight, false);
  5796. const tmpCtx = tmpCanvas.context;
  5797. const data = tmpCtx.createImageData(width, height);
  5798. if (backgroundColor) {
  5799. const bytes = data.data;
  5800. for (let i = 0, ii = bytes.length; i < ii; i += 4) {
  5801. bytes[i] = backgroundColor[0];
  5802. bytes[i + 1] = backgroundColor[1];
  5803. bytes[i + 2] = backgroundColor[2];
  5804. bytes[i + 3] = 255;
  5805. }
  5806. }
  5807. for (const figure of this._figures) {
  5808. drawFigure(data, figure, context);
  5809. }
  5810. tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
  5811. const canvas = tmpCanvas.canvas;
  5812. return {
  5813. canvas,
  5814. offsetX: offsetX - BORDER_SIZE * scaleX,
  5815. offsetY: offsetY - BORDER_SIZE * scaleY,
  5816. scaleX,
  5817. scaleY
  5818. };
  5819. }
  5820. getPattern(ctx, owner, inverse, pathType) {
  5821. applyBoundingBox(ctx, this._bbox);
  5822. let scale;
  5823. if (pathType === PathType.SHADING) {
  5824. scale = Util.singularValueDecompose2dScale(getCurrentTransform(ctx));
  5825. } else {
  5826. scale = Util.singularValueDecompose2dScale(owner.baseTransform);
  5827. if (this.matrix) {
  5828. const matrixScale = Util.singularValueDecompose2dScale(this.matrix);
  5829. scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]];
  5830. }
  5831. }
  5832. const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases);
  5833. if (pathType !== PathType.SHADING) {
  5834. ctx.setTransform(...owner.baseTransform);
  5835. if (this.matrix) {
  5836. ctx.transform(...this.matrix);
  5837. }
  5838. }
  5839. ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
  5840. ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY);
  5841. return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat");
  5842. }
  5843. }
  5844. class DummyShadingPattern extends BaseShadingPattern {
  5845. getPattern() {
  5846. return "hotpink";
  5847. }
  5848. }
  5849. function getShadingPattern(IR) {
  5850. switch (IR[0]) {
  5851. case "RadialAxial":
  5852. return new RadialAxialShadingPattern(IR);
  5853. case "Mesh":
  5854. return new MeshShadingPattern(IR);
  5855. case "Dummy":
  5856. return new DummyShadingPattern();
  5857. }
  5858. throw new Error(`Unknown IR type: ${IR[0]}`);
  5859. }
  5860. const PaintType = {
  5861. COLORED: 1,
  5862. UNCOLORED: 2
  5863. };
  5864. class TilingPattern {
  5865. static MAX_PATTERN_SIZE = 3000;
  5866. constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
  5867. this.operatorList = IR[2];
  5868. this.matrix = IR[3];
  5869. this.bbox = IR[4];
  5870. this.xstep = IR[5];
  5871. this.ystep = IR[6];
  5872. this.paintType = IR[7];
  5873. this.tilingType = IR[8];
  5874. this.color = color;
  5875. this.ctx = ctx;
  5876. this.canvasGraphicsFactory = canvasGraphicsFactory;
  5877. this.baseTransform = baseTransform;
  5878. }
  5879. createPatternCanvas(owner) {
  5880. const operatorList = this.operatorList;
  5881. const bbox = this.bbox;
  5882. const xstep = this.xstep;
  5883. const ystep = this.ystep;
  5884. const paintType = this.paintType;
  5885. const tilingType = this.tilingType;
  5886. const color = this.color;
  5887. const canvasGraphicsFactory = this.canvasGraphicsFactory;
  5888. info("TilingType: " + tilingType);
  5889. const x0 = bbox[0],
  5890. y0 = bbox[1],
  5891. x1 = bbox[2],
  5892. y1 = bbox[3];
  5893. const matrixScale = Util.singularValueDecompose2dScale(this.matrix);
  5894. const curMatrixScale = Util.singularValueDecompose2dScale(this.baseTransform);
  5895. const combinedScale = [matrixScale[0] * curMatrixScale[0], matrixScale[1] * curMatrixScale[1]];
  5896. const dimx = this.getSizeAndScale(xstep, this.ctx.canvas.width, combinedScale[0]);
  5897. const dimy = this.getSizeAndScale(ystep, this.ctx.canvas.height, combinedScale[1]);
  5898. const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size, true);
  5899. const tmpCtx = tmpCanvas.context;
  5900. const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
  5901. graphics.groupLevel = owner.groupLevel;
  5902. this.setFillAndStrokeStyleToContext(graphics, paintType, color);
  5903. let adjustedX0 = x0;
  5904. let adjustedY0 = y0;
  5905. let adjustedX1 = x1;
  5906. let adjustedY1 = y1;
  5907. if (x0 < 0) {
  5908. adjustedX0 = 0;
  5909. adjustedX1 += Math.abs(x0);
  5910. }
  5911. if (y0 < 0) {
  5912. adjustedY0 = 0;
  5913. adjustedY1 += Math.abs(y0);
  5914. }
  5915. tmpCtx.translate(-(dimx.scale * adjustedX0), -(dimy.scale * adjustedY0));
  5916. graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
  5917. tmpCtx.save();
  5918. this.clipBbox(graphics, adjustedX0, adjustedY0, adjustedX1, adjustedY1);
  5919. graphics.baseTransform = getCurrentTransform(graphics.ctx);
  5920. graphics.executeOperatorList(operatorList);
  5921. graphics.endDrawing();
  5922. return {
  5923. canvas: tmpCanvas.canvas,
  5924. scaleX: dimx.scale,
  5925. scaleY: dimy.scale,
  5926. offsetX: adjustedX0,
  5927. offsetY: adjustedY0
  5928. };
  5929. }
  5930. getSizeAndScale(step, realOutputSize, scale) {
  5931. step = Math.abs(step);
  5932. const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize);
  5933. let size = Math.ceil(step * scale);
  5934. if (size >= maxSize) {
  5935. size = maxSize;
  5936. } else {
  5937. scale = size / step;
  5938. }
  5939. return {
  5940. scale,
  5941. size
  5942. };
  5943. }
  5944. clipBbox(graphics, x0, y0, x1, y1) {
  5945. const bboxWidth = x1 - x0;
  5946. const bboxHeight = y1 - y0;
  5947. graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
  5948. graphics.current.updateRectMinMax(getCurrentTransform(graphics.ctx), [x0, y0, x1, y1]);
  5949. graphics.clip();
  5950. graphics.endPath();
  5951. }
  5952. setFillAndStrokeStyleToContext(graphics, paintType, color) {
  5953. const context = graphics.ctx,
  5954. current = graphics.current;
  5955. switch (paintType) {
  5956. case PaintType.COLORED:
  5957. const ctx = this.ctx;
  5958. context.fillStyle = ctx.fillStyle;
  5959. context.strokeStyle = ctx.strokeStyle;
  5960. current.fillColor = ctx.fillStyle;
  5961. current.strokeColor = ctx.strokeStyle;
  5962. break;
  5963. case PaintType.UNCOLORED:
  5964. const cssColor = Util.makeHexColor(color[0], color[1], color[2]);
  5965. context.fillStyle = cssColor;
  5966. context.strokeStyle = cssColor;
  5967. current.fillColor = cssColor;
  5968. current.strokeColor = cssColor;
  5969. break;
  5970. default:
  5971. throw new FormatError(`Unsupported paint type: ${paintType}`);
  5972. }
  5973. }
  5974. getPattern(ctx, owner, inverse, pathType) {
  5975. let matrix = inverse;
  5976. if (pathType !== PathType.SHADING) {
  5977. matrix = Util.transform(matrix, owner.baseTransform);
  5978. if (this.matrix) {
  5979. matrix = Util.transform(matrix, this.matrix);
  5980. }
  5981. }
  5982. const temporaryPatternCanvas = this.createPatternCanvas(owner);
  5983. let domMatrix = new DOMMatrix(matrix);
  5984. domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
  5985. domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY);
  5986. const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat");
  5987. pattern.setTransform(domMatrix);
  5988. return pattern;
  5989. }
  5990. }
  5991. ;// CONCATENATED MODULE: ./src/shared/image_utils.js
  5992. function convertToRGBA(params) {
  5993. switch (params.kind) {
  5994. case ImageKind.GRAYSCALE_1BPP:
  5995. return convertBlackAndWhiteToRGBA(params);
  5996. case ImageKind.RGB_24BPP:
  5997. return convertRGBToRGBA(params);
  5998. }
  5999. return null;
  6000. }
  6001. function convertBlackAndWhiteToRGBA({
  6002. src,
  6003. srcPos = 0,
  6004. dest,
  6005. width,
  6006. height,
  6007. nonBlackColor = 0xffffffff,
  6008. inverseDecode = false
  6009. }) {
  6010. const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;
  6011. const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor];
  6012. const widthInSource = width >> 3;
  6013. const widthRemainder = width & 7;
  6014. const srcLength = src.length;
  6015. dest = new Uint32Array(dest.buffer);
  6016. let destPos = 0;
  6017. for (let i = 0; i < height; i++) {
  6018. for (const max = srcPos + widthInSource; srcPos < max; srcPos++) {
  6019. const elem = srcPos < srcLength ? src[srcPos] : 255;
  6020. dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping;
  6021. dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping;
  6022. dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping;
  6023. dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping;
  6024. dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping;
  6025. dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping;
  6026. dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping;
  6027. dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping;
  6028. }
  6029. if (widthRemainder === 0) {
  6030. continue;
  6031. }
  6032. const elem = srcPos < srcLength ? src[srcPos++] : 255;
  6033. for (let j = 0; j < widthRemainder; j++) {
  6034. dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping;
  6035. }
  6036. }
  6037. return {
  6038. srcPos,
  6039. destPos
  6040. };
  6041. }
  6042. function convertRGBToRGBA({
  6043. src,
  6044. srcPos = 0,
  6045. dest,
  6046. destPos = 0,
  6047. width,
  6048. height
  6049. }) {
  6050. let i = 0;
  6051. const len32 = src.length >> 2;
  6052. const src32 = new Uint32Array(src.buffer, srcPos, len32);
  6053. if (FeatureTest.isLittleEndian) {
  6054. for (; i < len32 - 2; i += 3, destPos += 4) {
  6055. const s1 = src32[i];
  6056. const s2 = src32[i + 1];
  6057. const s3 = src32[i + 2];
  6058. dest[destPos] = s1 | 0xff000000;
  6059. dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000;
  6060. dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000;
  6061. dest[destPos + 3] = s3 >>> 8 | 0xff000000;
  6062. }
  6063. for (let j = i * 4, jj = src.length; j < jj; j += 3) {
  6064. dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000;
  6065. }
  6066. } else {
  6067. for (; i < len32 - 2; i += 3, destPos += 4) {
  6068. const s1 = src32[i];
  6069. const s2 = src32[i + 1];
  6070. const s3 = src32[i + 2];
  6071. dest[destPos] = s1 | 0xff;
  6072. dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff;
  6073. dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff;
  6074. dest[destPos + 3] = s3 << 8 | 0xff;
  6075. }
  6076. for (let j = i * 4, jj = src.length; j < jj; j += 3) {
  6077. dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff;
  6078. }
  6079. }
  6080. return {
  6081. srcPos,
  6082. destPos
  6083. };
  6084. }
  6085. function grayToRGBA(src, dest) {
  6086. if (FeatureTest.isLittleEndian) {
  6087. for (let i = 0, ii = src.length; i < ii; i++) {
  6088. dest[i] = src[i] * 0x10101 | 0xff000000;
  6089. }
  6090. } else {
  6091. for (let i = 0, ii = src.length; i < ii; i++) {
  6092. dest[i] = src[i] * 0x1010100 | 0x000000ff;
  6093. }
  6094. }
  6095. }
  6096. ;// CONCATENATED MODULE: ./src/display/canvas.js
  6097. const MIN_FONT_SIZE = 16;
  6098. const MAX_FONT_SIZE = 100;
  6099. const EXECUTION_TIME = 15;
  6100. const EXECUTION_STEPS = 10;
  6101. const MAX_SIZE_TO_COMPILE = 1000;
  6102. const FULL_CHUNK_HEIGHT = 16;
  6103. function mirrorContextOperations(ctx, destCtx) {
  6104. if (ctx._removeMirroring) {
  6105. throw new Error("Context is already forwarding operations.");
  6106. }
  6107. ctx.__originalSave = ctx.save;
  6108. ctx.__originalRestore = ctx.restore;
  6109. ctx.__originalRotate = ctx.rotate;
  6110. ctx.__originalScale = ctx.scale;
  6111. ctx.__originalTranslate = ctx.translate;
  6112. ctx.__originalTransform = ctx.transform;
  6113. ctx.__originalSetTransform = ctx.setTransform;
  6114. ctx.__originalResetTransform = ctx.resetTransform;
  6115. ctx.__originalClip = ctx.clip;
  6116. ctx.__originalMoveTo = ctx.moveTo;
  6117. ctx.__originalLineTo = ctx.lineTo;
  6118. ctx.__originalBezierCurveTo = ctx.bezierCurveTo;
  6119. ctx.__originalRect = ctx.rect;
  6120. ctx.__originalClosePath = ctx.closePath;
  6121. ctx.__originalBeginPath = ctx.beginPath;
  6122. ctx._removeMirroring = () => {
  6123. ctx.save = ctx.__originalSave;
  6124. ctx.restore = ctx.__originalRestore;
  6125. ctx.rotate = ctx.__originalRotate;
  6126. ctx.scale = ctx.__originalScale;
  6127. ctx.translate = ctx.__originalTranslate;
  6128. ctx.transform = ctx.__originalTransform;
  6129. ctx.setTransform = ctx.__originalSetTransform;
  6130. ctx.resetTransform = ctx.__originalResetTransform;
  6131. ctx.clip = ctx.__originalClip;
  6132. ctx.moveTo = ctx.__originalMoveTo;
  6133. ctx.lineTo = ctx.__originalLineTo;
  6134. ctx.bezierCurveTo = ctx.__originalBezierCurveTo;
  6135. ctx.rect = ctx.__originalRect;
  6136. ctx.closePath = ctx.__originalClosePath;
  6137. ctx.beginPath = ctx.__originalBeginPath;
  6138. delete ctx._removeMirroring;
  6139. };
  6140. ctx.save = function ctxSave() {
  6141. destCtx.save();
  6142. this.__originalSave();
  6143. };
  6144. ctx.restore = function ctxRestore() {
  6145. destCtx.restore();
  6146. this.__originalRestore();
  6147. };
  6148. ctx.translate = function ctxTranslate(x, y) {
  6149. destCtx.translate(x, y);
  6150. this.__originalTranslate(x, y);
  6151. };
  6152. ctx.scale = function ctxScale(x, y) {
  6153. destCtx.scale(x, y);
  6154. this.__originalScale(x, y);
  6155. };
  6156. ctx.transform = function ctxTransform(a, b, c, d, e, f) {
  6157. destCtx.transform(a, b, c, d, e, f);
  6158. this.__originalTransform(a, b, c, d, e, f);
  6159. };
  6160. ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
  6161. destCtx.setTransform(a, b, c, d, e, f);
  6162. this.__originalSetTransform(a, b, c, d, e, f);
  6163. };
  6164. ctx.resetTransform = function ctxResetTransform() {
  6165. destCtx.resetTransform();
  6166. this.__originalResetTransform();
  6167. };
  6168. ctx.rotate = function ctxRotate(angle) {
  6169. destCtx.rotate(angle);
  6170. this.__originalRotate(angle);
  6171. };
  6172. ctx.clip = function ctxRotate(rule) {
  6173. destCtx.clip(rule);
  6174. this.__originalClip(rule);
  6175. };
  6176. ctx.moveTo = function (x, y) {
  6177. destCtx.moveTo(x, y);
  6178. this.__originalMoveTo(x, y);
  6179. };
  6180. ctx.lineTo = function (x, y) {
  6181. destCtx.lineTo(x, y);
  6182. this.__originalLineTo(x, y);
  6183. };
  6184. ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
  6185. destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
  6186. this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
  6187. };
  6188. ctx.rect = function (x, y, width, height) {
  6189. destCtx.rect(x, y, width, height);
  6190. this.__originalRect(x, y, width, height);
  6191. };
  6192. ctx.closePath = function () {
  6193. destCtx.closePath();
  6194. this.__originalClosePath();
  6195. };
  6196. ctx.beginPath = function () {
  6197. destCtx.beginPath();
  6198. this.__originalBeginPath();
  6199. };
  6200. }
  6201. class CachedCanvases {
  6202. constructor(canvasFactory) {
  6203. this.canvasFactory = canvasFactory;
  6204. this.cache = Object.create(null);
  6205. }
  6206. getCanvas(id, width, height) {
  6207. let canvasEntry;
  6208. if (this.cache[id] !== undefined) {
  6209. canvasEntry = this.cache[id];
  6210. this.canvasFactory.reset(canvasEntry, width, height);
  6211. } else {
  6212. canvasEntry = this.canvasFactory.create(width, height);
  6213. this.cache[id] = canvasEntry;
  6214. }
  6215. return canvasEntry;
  6216. }
  6217. delete(id) {
  6218. delete this.cache[id];
  6219. }
  6220. clear() {
  6221. for (const id in this.cache) {
  6222. const canvasEntry = this.cache[id];
  6223. this.canvasFactory.destroy(canvasEntry);
  6224. delete this.cache[id];
  6225. }
  6226. }
  6227. }
  6228. function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) {
  6229. const [a, b, c, d, tx, ty] = getCurrentTransform(ctx);
  6230. if (b === 0 && c === 0) {
  6231. const tlX = destX * a + tx;
  6232. const rTlX = Math.round(tlX);
  6233. const tlY = destY * d + ty;
  6234. const rTlY = Math.round(tlY);
  6235. const brX = (destX + destW) * a + tx;
  6236. const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
  6237. const brY = (destY + destH) * d + ty;
  6238. const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
  6239. ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY);
  6240. ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight);
  6241. ctx.setTransform(a, b, c, d, tx, ty);
  6242. return [rWidth, rHeight];
  6243. }
  6244. if (a === 0 && d === 0) {
  6245. const tlX = destY * c + tx;
  6246. const rTlX = Math.round(tlX);
  6247. const tlY = destX * b + ty;
  6248. const rTlY = Math.round(tlY);
  6249. const brX = (destY + destH) * c + tx;
  6250. const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
  6251. const brY = (destX + destW) * b + ty;
  6252. const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
  6253. ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY);
  6254. ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth);
  6255. ctx.setTransform(a, b, c, d, tx, ty);
  6256. return [rHeight, rWidth];
  6257. }
  6258. ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
  6259. const scaleX = Math.hypot(a, b);
  6260. const scaleY = Math.hypot(c, d);
  6261. return [scaleX * destW, scaleY * destH];
  6262. }
  6263. function compileType3Glyph(imgData) {
  6264. const {
  6265. width,
  6266. height
  6267. } = imgData;
  6268. if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) {
  6269. return null;
  6270. }
  6271. const POINT_TO_PROCESS_LIMIT = 1000;
  6272. const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
  6273. const width1 = width + 1;
  6274. let points = new Uint8Array(width1 * (height + 1));
  6275. let i, j, j0;
  6276. const lineSize = width + 7 & ~7;
  6277. let data = new Uint8Array(lineSize * height),
  6278. pos = 0;
  6279. for (const elem of imgData.data) {
  6280. let mask = 128;
  6281. while (mask > 0) {
  6282. data[pos++] = elem & mask ? 0 : 255;
  6283. mask >>= 1;
  6284. }
  6285. }
  6286. let count = 0;
  6287. pos = 0;
  6288. if (data[pos] !== 0) {
  6289. points[0] = 1;
  6290. ++count;
  6291. }
  6292. for (j = 1; j < width; j++) {
  6293. if (data[pos] !== data[pos + 1]) {
  6294. points[j] = data[pos] ? 2 : 1;
  6295. ++count;
  6296. }
  6297. pos++;
  6298. }
  6299. if (data[pos] !== 0) {
  6300. points[j] = 2;
  6301. ++count;
  6302. }
  6303. for (i = 1; i < height; i++) {
  6304. pos = i * lineSize;
  6305. j0 = i * width1;
  6306. if (data[pos - lineSize] !== data[pos]) {
  6307. points[j0] = data[pos] ? 1 : 8;
  6308. ++count;
  6309. }
  6310. let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
  6311. for (j = 1; j < width; j++) {
  6312. sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + (data[pos - lineSize + 1] ? 8 : 0);
  6313. if (POINT_TYPES[sum]) {
  6314. points[j0 + j] = POINT_TYPES[sum];
  6315. ++count;
  6316. }
  6317. pos++;
  6318. }
  6319. if (data[pos - lineSize] !== data[pos]) {
  6320. points[j0 + j] = data[pos] ? 2 : 4;
  6321. ++count;
  6322. }
  6323. if (count > POINT_TO_PROCESS_LIMIT) {
  6324. return null;
  6325. }
  6326. }
  6327. pos = lineSize * (height - 1);
  6328. j0 = i * width1;
  6329. if (data[pos] !== 0) {
  6330. points[j0] = 8;
  6331. ++count;
  6332. }
  6333. for (j = 1; j < width; j++) {
  6334. if (data[pos] !== data[pos + 1]) {
  6335. points[j0 + j] = data[pos] ? 4 : 8;
  6336. ++count;
  6337. }
  6338. pos++;
  6339. }
  6340. if (data[pos] !== 0) {
  6341. points[j0 + j] = 4;
  6342. ++count;
  6343. }
  6344. if (count > POINT_TO_PROCESS_LIMIT) {
  6345. return null;
  6346. }
  6347. const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
  6348. const path = new Path2D();
  6349. for (i = 0; count && i <= height; i++) {
  6350. let p = i * width1;
  6351. const end = p + width;
  6352. while (p < end && !points[p]) {
  6353. p++;
  6354. }
  6355. if (p === end) {
  6356. continue;
  6357. }
  6358. path.moveTo(p % width1, i);
  6359. const p0 = p;
  6360. let type = points[p];
  6361. do {
  6362. const step = steps[type];
  6363. do {
  6364. p += step;
  6365. } while (!points[p]);
  6366. const pp = points[p];
  6367. if (pp !== 5 && pp !== 10) {
  6368. type = pp;
  6369. points[p] = 0;
  6370. } else {
  6371. type = pp & 0x33 * type >> 4;
  6372. points[p] &= type >> 2 | type << 2;
  6373. }
  6374. path.lineTo(p % width1, p / width1 | 0);
  6375. if (!points[p]) {
  6376. --count;
  6377. }
  6378. } while (p0 !== p);
  6379. --i;
  6380. }
  6381. data = null;
  6382. points = null;
  6383. const drawOutline = function (c) {
  6384. c.save();
  6385. c.scale(1 / width, -1 / height);
  6386. c.translate(0, -height);
  6387. c.fill(path);
  6388. c.beginPath();
  6389. c.restore();
  6390. };
  6391. return drawOutline;
  6392. }
  6393. class CanvasExtraState {
  6394. constructor(width, height) {
  6395. this.alphaIsShape = false;
  6396. this.fontSize = 0;
  6397. this.fontSizeScale = 1;
  6398. this.textMatrix = IDENTITY_MATRIX;
  6399. this.textMatrixScale = 1;
  6400. this.fontMatrix = FONT_IDENTITY_MATRIX;
  6401. this.leading = 0;
  6402. this.x = 0;
  6403. this.y = 0;
  6404. this.lineX = 0;
  6405. this.lineY = 0;
  6406. this.charSpacing = 0;
  6407. this.wordSpacing = 0;
  6408. this.textHScale = 1;
  6409. this.textRenderingMode = TextRenderingMode.FILL;
  6410. this.textRise = 0;
  6411. this.fillColor = "#000000";
  6412. this.strokeColor = "#000000";
  6413. this.patternFill = false;
  6414. this.fillAlpha = 1;
  6415. this.strokeAlpha = 1;
  6416. this.lineWidth = 1;
  6417. this.activeSMask = null;
  6418. this.transferMaps = "none";
  6419. this.startNewPathAndClipBox([0, 0, width, height]);
  6420. }
  6421. clone() {
  6422. const clone = Object.create(this);
  6423. clone.clipBox = this.clipBox.slice();
  6424. return clone;
  6425. }
  6426. setCurrentPoint(x, y) {
  6427. this.x = x;
  6428. this.y = y;
  6429. }
  6430. updatePathMinMax(transform, x, y) {
  6431. [x, y] = Util.applyTransform([x, y], transform);
  6432. this.minX = Math.min(this.minX, x);
  6433. this.minY = Math.min(this.minY, y);
  6434. this.maxX = Math.max(this.maxX, x);
  6435. this.maxY = Math.max(this.maxY, y);
  6436. }
  6437. updateRectMinMax(transform, rect) {
  6438. const p1 = Util.applyTransform(rect, transform);
  6439. const p2 = Util.applyTransform(rect.slice(2), transform);
  6440. const p3 = Util.applyTransform([rect[0], rect[3]], transform);
  6441. const p4 = Util.applyTransform([rect[2], rect[1]], transform);
  6442. this.minX = Math.min(this.minX, p1[0], p2[0], p3[0], p4[0]);
  6443. this.minY = Math.min(this.minY, p1[1], p2[1], p3[1], p4[1]);
  6444. this.maxX = Math.max(this.maxX, p1[0], p2[0], p3[0], p4[0]);
  6445. this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]);
  6446. }
  6447. updateScalingPathMinMax(transform, minMax) {
  6448. Util.scaleMinMax(transform, minMax);
  6449. this.minX = Math.min(this.minX, minMax[0]);
  6450. this.minY = Math.min(this.minY, minMax[1]);
  6451. this.maxX = Math.max(this.maxX, minMax[2]);
  6452. this.maxY = Math.max(this.maxY, minMax[3]);
  6453. }
  6454. updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
  6455. const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax);
  6456. if (minMax) {
  6457. return;
  6458. }
  6459. this.updateRectMinMax(transform, box);
  6460. }
  6461. getPathBoundingBox(pathType = PathType.FILL, transform = null) {
  6462. const box = [this.minX, this.minY, this.maxX, this.maxY];
  6463. if (pathType === PathType.STROKE) {
  6464. if (!transform) {
  6465. unreachable("Stroke bounding box must include transform.");
  6466. }
  6467. const scale = Util.singularValueDecompose2dScale(transform);
  6468. const xStrokePad = scale[0] * this.lineWidth / 2;
  6469. const yStrokePad = scale[1] * this.lineWidth / 2;
  6470. box[0] -= xStrokePad;
  6471. box[1] -= yStrokePad;
  6472. box[2] += xStrokePad;
  6473. box[3] += yStrokePad;
  6474. }
  6475. return box;
  6476. }
  6477. updateClipFromPath() {
  6478. const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox());
  6479. this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]);
  6480. }
  6481. isEmptyClip() {
  6482. return this.minX === Infinity;
  6483. }
  6484. startNewPathAndClipBox(box) {
  6485. this.clipBox = box;
  6486. this.minX = Infinity;
  6487. this.minY = Infinity;
  6488. this.maxX = 0;
  6489. this.maxY = 0;
  6490. }
  6491. getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
  6492. return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform));
  6493. }
  6494. }
  6495. function putBinaryImageData(ctx, imgData) {
  6496. if (typeof ImageData !== "undefined" && imgData instanceof ImageData) {
  6497. ctx.putImageData(imgData, 0, 0);
  6498. return;
  6499. }
  6500. const height = imgData.height,
  6501. width = imgData.width;
  6502. const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
  6503. const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
  6504. const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
  6505. const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
  6506. let srcPos = 0,
  6507. destPos;
  6508. const src = imgData.data;
  6509. const dest = chunkImgData.data;
  6510. let i, j, thisChunkHeight, elemsInThisChunk;
  6511. if (imgData.kind === util_ImageKind.GRAYSCALE_1BPP) {
  6512. const srcLength = src.byteLength;
  6513. const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);
  6514. const dest32DataLength = dest32.length;
  6515. const fullSrcDiff = width + 7 >> 3;
  6516. const white = 0xffffffff;
  6517. const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;
  6518. for (i = 0; i < totalChunks; i++) {
  6519. thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
  6520. destPos = 0;
  6521. for (j = 0; j < thisChunkHeight; j++) {
  6522. const srcDiff = srcLength - srcPos;
  6523. let k = 0;
  6524. const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7;
  6525. const kEndUnrolled = kEnd & ~7;
  6526. let mask = 0;
  6527. let srcByte = 0;
  6528. for (; k < kEndUnrolled; k += 8) {
  6529. srcByte = src[srcPos++];
  6530. dest32[destPos++] = srcByte & 128 ? white : black;
  6531. dest32[destPos++] = srcByte & 64 ? white : black;
  6532. dest32[destPos++] = srcByte & 32 ? white : black;
  6533. dest32[destPos++] = srcByte & 16 ? white : black;
  6534. dest32[destPos++] = srcByte & 8 ? white : black;
  6535. dest32[destPos++] = srcByte & 4 ? white : black;
  6536. dest32[destPos++] = srcByte & 2 ? white : black;
  6537. dest32[destPos++] = srcByte & 1 ? white : black;
  6538. }
  6539. for (; k < kEnd; k++) {
  6540. if (mask === 0) {
  6541. srcByte = src[srcPos++];
  6542. mask = 128;
  6543. }
  6544. dest32[destPos++] = srcByte & mask ? white : black;
  6545. mask >>= 1;
  6546. }
  6547. }
  6548. while (destPos < dest32DataLength) {
  6549. dest32[destPos++] = 0;
  6550. }
  6551. ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
  6552. }
  6553. } else if (imgData.kind === util_ImageKind.RGBA_32BPP) {
  6554. j = 0;
  6555. elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
  6556. for (i = 0; i < fullChunks; i++) {
  6557. dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
  6558. srcPos += elemsInThisChunk;
  6559. ctx.putImageData(chunkImgData, 0, j);
  6560. j += FULL_CHUNK_HEIGHT;
  6561. }
  6562. if (i < totalChunks) {
  6563. elemsInThisChunk = width * partialChunkHeight * 4;
  6564. dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
  6565. ctx.putImageData(chunkImgData, 0, j);
  6566. }
  6567. } else if (imgData.kind === util_ImageKind.RGB_24BPP) {
  6568. thisChunkHeight = FULL_CHUNK_HEIGHT;
  6569. elemsInThisChunk = width * thisChunkHeight;
  6570. for (i = 0; i < totalChunks; i++) {
  6571. if (i >= fullChunks) {
  6572. thisChunkHeight = partialChunkHeight;
  6573. elemsInThisChunk = width * thisChunkHeight;
  6574. }
  6575. destPos = 0;
  6576. for (j = elemsInThisChunk; j--;) {
  6577. dest[destPos++] = src[srcPos++];
  6578. dest[destPos++] = src[srcPos++];
  6579. dest[destPos++] = src[srcPos++];
  6580. dest[destPos++] = 255;
  6581. }
  6582. ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
  6583. }
  6584. } else {
  6585. throw new Error(`bad image kind: ${imgData.kind}`);
  6586. }
  6587. }
  6588. function putBinaryImageMask(ctx, imgData) {
  6589. if (imgData.bitmap) {
  6590. ctx.drawImage(imgData.bitmap, 0, 0);
  6591. return;
  6592. }
  6593. const height = imgData.height,
  6594. width = imgData.width;
  6595. const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
  6596. const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
  6597. const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
  6598. const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
  6599. let srcPos = 0;
  6600. const src = imgData.data;
  6601. const dest = chunkImgData.data;
  6602. for (let i = 0; i < totalChunks; i++) {
  6603. const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
  6604. ({
  6605. srcPos
  6606. } = convertBlackAndWhiteToRGBA({
  6607. src,
  6608. srcPos,
  6609. dest,
  6610. width,
  6611. height: thisChunkHeight,
  6612. nonBlackColor: 0
  6613. }));
  6614. ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
  6615. }
  6616. }
  6617. function copyCtxState(sourceCtx, destCtx) {
  6618. const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"];
  6619. for (const property of properties) {
  6620. if (sourceCtx[property] !== undefined) {
  6621. destCtx[property] = sourceCtx[property];
  6622. }
  6623. }
  6624. if (sourceCtx.setLineDash !== undefined) {
  6625. destCtx.setLineDash(sourceCtx.getLineDash());
  6626. destCtx.lineDashOffset = sourceCtx.lineDashOffset;
  6627. }
  6628. }
  6629. function resetCtxToDefault(ctx) {
  6630. ctx.strokeStyle = ctx.fillStyle = "#000000";
  6631. ctx.fillRule = "nonzero";
  6632. ctx.globalAlpha = 1;
  6633. ctx.lineWidth = 1;
  6634. ctx.lineCap = "butt";
  6635. ctx.lineJoin = "miter";
  6636. ctx.miterLimit = 10;
  6637. ctx.globalCompositeOperation = "source-over";
  6638. ctx.font = "10px sans-serif";
  6639. if (ctx.setLineDash !== undefined) {
  6640. ctx.setLineDash([]);
  6641. ctx.lineDashOffset = 0;
  6642. }
  6643. if (!isNodeJS) {
  6644. const {
  6645. filter
  6646. } = ctx;
  6647. if (filter !== "none" && filter !== "") {
  6648. ctx.filter = "none";
  6649. }
  6650. }
  6651. }
  6652. function getImageSmoothingEnabled(transform, interpolate) {
  6653. if (interpolate) {
  6654. return true;
  6655. }
  6656. const scale = Util.singularValueDecompose2dScale(transform);
  6657. scale[0] = Math.fround(scale[0]);
  6658. scale[1] = Math.fround(scale[1]);
  6659. const actualScale = Math.fround((globalThis.devicePixelRatio || 1) * PixelsPerInch.PDF_TO_CSS_UNITS);
  6660. return scale[0] <= actualScale && scale[1] <= actualScale;
  6661. }
  6662. const LINE_CAP_STYLES = ["butt", "round", "square"];
  6663. const LINE_JOIN_STYLES = ["miter", "round", "bevel"];
  6664. const NORMAL_CLIP = {};
  6665. const EO_CLIP = {};
  6666. class CanvasGraphics {
  6667. constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, {
  6668. optionalContentConfig,
  6669. markedContentStack = null
  6670. }, annotationCanvasMap, pageColors) {
  6671. this.ctx = canvasCtx;
  6672. this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
  6673. this.stateStack = [];
  6674. this.pendingClip = null;
  6675. this.pendingEOFill = false;
  6676. this.res = null;
  6677. this.xobjs = null;
  6678. this.commonObjs = commonObjs;
  6679. this.objs = objs;
  6680. this.canvasFactory = canvasFactory;
  6681. this.filterFactory = filterFactory;
  6682. this.groupStack = [];
  6683. this.processingType3 = null;
  6684. this.baseTransform = null;
  6685. this.baseTransformStack = [];
  6686. this.groupLevel = 0;
  6687. this.smaskStack = [];
  6688. this.smaskCounter = 0;
  6689. this.tempSMask = null;
  6690. this.suspendedCtx = null;
  6691. this.contentVisible = true;
  6692. this.markedContentStack = markedContentStack || [];
  6693. this.optionalContentConfig = optionalContentConfig;
  6694. this.cachedCanvases = new CachedCanvases(this.canvasFactory);
  6695. this.cachedPatterns = new Map();
  6696. this.annotationCanvasMap = annotationCanvasMap;
  6697. this.viewportScale = 1;
  6698. this.outputScaleX = 1;
  6699. this.outputScaleY = 1;
  6700. this.pageColors = pageColors;
  6701. this._cachedScaleForStroking = [-1, 0];
  6702. this._cachedGetSinglePixelWidth = null;
  6703. this._cachedBitmapsMap = new Map();
  6704. }
  6705. getObject(data, fallback = null) {
  6706. if (typeof data === "string") {
  6707. return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data);
  6708. }
  6709. return fallback;
  6710. }
  6711. beginDrawing({
  6712. transform,
  6713. viewport,
  6714. transparency = false,
  6715. background = null
  6716. }) {
  6717. const width = this.ctx.canvas.width;
  6718. const height = this.ctx.canvas.height;
  6719. const savedFillStyle = this.ctx.fillStyle;
  6720. this.ctx.fillStyle = background || "#ffffff";
  6721. this.ctx.fillRect(0, 0, width, height);
  6722. this.ctx.fillStyle = savedFillStyle;
  6723. if (transparency) {
  6724. const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height);
  6725. this.compositeCtx = this.ctx;
  6726. this.transparentCanvas = transparentCanvas.canvas;
  6727. this.ctx = transparentCanvas.context;
  6728. this.ctx.save();
  6729. this.ctx.transform(...getCurrentTransform(this.compositeCtx));
  6730. }
  6731. this.ctx.save();
  6732. resetCtxToDefault(this.ctx);
  6733. if (transform) {
  6734. this.ctx.transform(...transform);
  6735. this.outputScaleX = transform[0];
  6736. this.outputScaleY = transform[0];
  6737. }
  6738. this.ctx.transform(...viewport.transform);
  6739. this.viewportScale = viewport.scale;
  6740. this.baseTransform = getCurrentTransform(this.ctx);
  6741. }
  6742. executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper) {
  6743. const argsArray = operatorList.argsArray;
  6744. const fnArray = operatorList.fnArray;
  6745. let i = executionStartIdx || 0;
  6746. const argsArrayLen = argsArray.length;
  6747. if (argsArrayLen === i) {
  6748. return i;
  6749. }
  6750. const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function";
  6751. const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
  6752. let steps = 0;
  6753. const commonObjs = this.commonObjs;
  6754. const objs = this.objs;
  6755. let fnId;
  6756. while (true) {
  6757. if (stepper !== undefined && i === stepper.nextBreakPoint) {
  6758. stepper.breakIt(i, continueCallback);
  6759. return i;
  6760. }
  6761. fnId = fnArray[i];
  6762. if (fnId !== OPS.dependency) {
  6763. this[fnId].apply(this, argsArray[i]);
  6764. } else {
  6765. for (const depObjId of argsArray[i]) {
  6766. const objsPool = depObjId.startsWith("g_") ? commonObjs : objs;
  6767. if (!objsPool.has(depObjId)) {
  6768. objsPool.get(depObjId, continueCallback);
  6769. return i;
  6770. }
  6771. }
  6772. }
  6773. i++;
  6774. if (i === argsArrayLen) {
  6775. return i;
  6776. }
  6777. if (chunkOperations && ++steps > EXECUTION_STEPS) {
  6778. if (Date.now() > endTime) {
  6779. continueCallback();
  6780. return i;
  6781. }
  6782. steps = 0;
  6783. }
  6784. }
  6785. }
  6786. #restoreInitialState() {
  6787. while (this.stateStack.length || this.inSMaskMode) {
  6788. this.restore();
  6789. }
  6790. this.current.activeSMask = null;
  6791. this.ctx.restore();
  6792. if (this.transparentCanvas) {
  6793. this.ctx = this.compositeCtx;
  6794. this.ctx.save();
  6795. this.ctx.setTransform(1, 0, 0, 1, 0, 0);
  6796. this.ctx.drawImage(this.transparentCanvas, 0, 0);
  6797. this.ctx.restore();
  6798. this.transparentCanvas = null;
  6799. }
  6800. }
  6801. endDrawing() {
  6802. this.#restoreInitialState();
  6803. this.cachedCanvases.clear();
  6804. this.cachedPatterns.clear();
  6805. for (const cache of this._cachedBitmapsMap.values()) {
  6806. for (const canvas of cache.values()) {
  6807. if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) {
  6808. canvas.width = canvas.height = 0;
  6809. }
  6810. }
  6811. cache.clear();
  6812. }
  6813. this._cachedBitmapsMap.clear();
  6814. this.#drawFilter();
  6815. }
  6816. #drawFilter() {
  6817. if (this.pageColors) {
  6818. const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background);
  6819. if (hcmFilterId !== "none") {
  6820. const savedFilter = this.ctx.filter;
  6821. this.ctx.filter = hcmFilterId;
  6822. this.ctx.drawImage(this.ctx.canvas, 0, 0);
  6823. this.ctx.filter = savedFilter;
  6824. }
  6825. }
  6826. }
  6827. _scaleImage(img, inverseTransform) {
  6828. const width = img.width;
  6829. const height = img.height;
  6830. let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1);
  6831. let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1);
  6832. let paintWidth = width,
  6833. paintHeight = height;
  6834. let tmpCanvasId = "prescale1";
  6835. let tmpCanvas, tmpCtx;
  6836. while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {
  6837. let newWidth = paintWidth,
  6838. newHeight = paintHeight;
  6839. if (widthScale > 2 && paintWidth > 1) {
  6840. newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2);
  6841. widthScale /= paintWidth / newWidth;
  6842. }
  6843. if (heightScale > 2 && paintHeight > 1) {
  6844. newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2;
  6845. heightScale /= paintHeight / newHeight;
  6846. }
  6847. tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
  6848. tmpCtx = tmpCanvas.context;
  6849. tmpCtx.clearRect(0, 0, newWidth, newHeight);
  6850. tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);
  6851. img = tmpCanvas.canvas;
  6852. paintWidth = newWidth;
  6853. paintHeight = newHeight;
  6854. tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
  6855. }
  6856. return {
  6857. img,
  6858. paintWidth,
  6859. paintHeight
  6860. };
  6861. }
  6862. _createMaskCanvas(img) {
  6863. const ctx = this.ctx;
  6864. const {
  6865. width,
  6866. height
  6867. } = img;
  6868. const fillColor = this.current.fillColor;
  6869. const isPatternFill = this.current.patternFill;
  6870. const currentTransform = getCurrentTransform(ctx);
  6871. let cache, cacheKey, scaled, maskCanvas;
  6872. if ((img.bitmap || img.data) && img.count > 1) {
  6873. const mainKey = img.bitmap || img.data.buffer;
  6874. cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]);
  6875. cache = this._cachedBitmapsMap.get(mainKey);
  6876. if (!cache) {
  6877. cache = new Map();
  6878. this._cachedBitmapsMap.set(mainKey, cache);
  6879. }
  6880. const cachedImage = cache.get(cacheKey);
  6881. if (cachedImage && !isPatternFill) {
  6882. const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]);
  6883. const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]);
  6884. return {
  6885. canvas: cachedImage,
  6886. offsetX,
  6887. offsetY
  6888. };
  6889. }
  6890. scaled = cachedImage;
  6891. }
  6892. if (!scaled) {
  6893. maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
  6894. putBinaryImageMask(maskCanvas.context, img);
  6895. }
  6896. let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]);
  6897. maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
  6898. const [minX, minY, maxX, maxY] = Util.getAxialAlignedBoundingBox([0, 0, width, height], maskToCanvas);
  6899. const drawnWidth = Math.round(maxX - minX) || 1;
  6900. const drawnHeight = Math.round(maxY - minY) || 1;
  6901. const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight);
  6902. const fillCtx = fillCanvas.context;
  6903. const offsetX = minX;
  6904. const offsetY = minY;
  6905. fillCtx.translate(-offsetX, -offsetY);
  6906. fillCtx.transform(...maskToCanvas);
  6907. if (!scaled) {
  6908. scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx));
  6909. scaled = scaled.img;
  6910. if (cache && isPatternFill) {
  6911. cache.set(cacheKey, scaled);
  6912. }
  6913. }
  6914. fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate);
  6915. drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height);
  6916. fillCtx.globalCompositeOperation = "source-in";
  6917. const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]);
  6918. fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL) : fillColor;
  6919. fillCtx.fillRect(0, 0, width, height);
  6920. if (cache && !isPatternFill) {
  6921. this.cachedCanvases.delete("fillCanvas");
  6922. cache.set(cacheKey, fillCanvas.canvas);
  6923. }
  6924. return {
  6925. canvas: fillCanvas.canvas,
  6926. offsetX: Math.round(offsetX),
  6927. offsetY: Math.round(offsetY)
  6928. };
  6929. }
  6930. setLineWidth(width) {
  6931. if (width !== this.current.lineWidth) {
  6932. this._cachedScaleForStroking[0] = -1;
  6933. }
  6934. this.current.lineWidth = width;
  6935. this.ctx.lineWidth = width;
  6936. }
  6937. setLineCap(style) {
  6938. this.ctx.lineCap = LINE_CAP_STYLES[style];
  6939. }
  6940. setLineJoin(style) {
  6941. this.ctx.lineJoin = LINE_JOIN_STYLES[style];
  6942. }
  6943. setMiterLimit(limit) {
  6944. this.ctx.miterLimit = limit;
  6945. }
  6946. setDash(dashArray, dashPhase) {
  6947. const ctx = this.ctx;
  6948. if (ctx.setLineDash !== undefined) {
  6949. ctx.setLineDash(dashArray);
  6950. ctx.lineDashOffset = dashPhase;
  6951. }
  6952. }
  6953. setRenderingIntent(intent) {}
  6954. setFlatness(flatness) {}
  6955. setGState(states) {
  6956. for (const [key, value] of states) {
  6957. switch (key) {
  6958. case "LW":
  6959. this.setLineWidth(value);
  6960. break;
  6961. case "LC":
  6962. this.setLineCap(value);
  6963. break;
  6964. case "LJ":
  6965. this.setLineJoin(value);
  6966. break;
  6967. case "ML":
  6968. this.setMiterLimit(value);
  6969. break;
  6970. case "D":
  6971. this.setDash(value[0], value[1]);
  6972. break;
  6973. case "RI":
  6974. this.setRenderingIntent(value);
  6975. break;
  6976. case "FL":
  6977. this.setFlatness(value);
  6978. break;
  6979. case "Font":
  6980. this.setFont(value[0], value[1]);
  6981. break;
  6982. case "CA":
  6983. this.current.strokeAlpha = value;
  6984. break;
  6985. case "ca":
  6986. this.current.fillAlpha = value;
  6987. this.ctx.globalAlpha = value;
  6988. break;
  6989. case "BM":
  6990. this.ctx.globalCompositeOperation = value;
  6991. break;
  6992. case "SMask":
  6993. this.current.activeSMask = value ? this.tempSMask : null;
  6994. this.tempSMask = null;
  6995. this.checkSMaskState();
  6996. break;
  6997. case "TR":
  6998. this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value);
  6999. break;
  7000. }
  7001. }
  7002. }
  7003. get inSMaskMode() {
  7004. return !!this.suspendedCtx;
  7005. }
  7006. checkSMaskState() {
  7007. const inSMaskMode = this.inSMaskMode;
  7008. if (this.current.activeSMask && !inSMaskMode) {
  7009. this.beginSMaskMode();
  7010. } else if (!this.current.activeSMask && inSMaskMode) {
  7011. this.endSMaskMode();
  7012. }
  7013. }
  7014. beginSMaskMode() {
  7015. if (this.inSMaskMode) {
  7016. throw new Error("beginSMaskMode called while already in smask mode");
  7017. }
  7018. const drawnWidth = this.ctx.canvas.width;
  7019. const drawnHeight = this.ctx.canvas.height;
  7020. const cacheId = "smaskGroupAt" + this.groupLevel;
  7021. const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);
  7022. this.suspendedCtx = this.ctx;
  7023. this.ctx = scratchCanvas.context;
  7024. const ctx = this.ctx;
  7025. ctx.setTransform(...getCurrentTransform(this.suspendedCtx));
  7026. copyCtxState(this.suspendedCtx, ctx);
  7027. mirrorContextOperations(ctx, this.suspendedCtx);
  7028. this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
  7029. }
  7030. endSMaskMode() {
  7031. if (!this.inSMaskMode) {
  7032. throw new Error("endSMaskMode called while not in smask mode");
  7033. }
  7034. this.ctx._removeMirroring();
  7035. copyCtxState(this.ctx, this.suspendedCtx);
  7036. this.ctx = this.suspendedCtx;
  7037. this.suspendedCtx = null;
  7038. }
  7039. compose(dirtyBox) {
  7040. if (!this.current.activeSMask) {
  7041. return;
  7042. }
  7043. if (!dirtyBox) {
  7044. dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];
  7045. } else {
  7046. dirtyBox[0] = Math.floor(dirtyBox[0]);
  7047. dirtyBox[1] = Math.floor(dirtyBox[1]);
  7048. dirtyBox[2] = Math.ceil(dirtyBox[2]);
  7049. dirtyBox[3] = Math.ceil(dirtyBox[3]);
  7050. }
  7051. const smask = this.current.activeSMask;
  7052. const suspendedCtx = this.suspendedCtx;
  7053. this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);
  7054. this.ctx.save();
  7055. this.ctx.setTransform(1, 0, 0, 1, 0, 0);
  7056. this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
  7057. this.ctx.restore();
  7058. }
  7059. composeSMask(ctx, smask, layerCtx, layerBox) {
  7060. const layerOffsetX = layerBox[0];
  7061. const layerOffsetY = layerBox[1];
  7062. const layerWidth = layerBox[2] - layerOffsetX;
  7063. const layerHeight = layerBox[3] - layerOffsetY;
  7064. if (layerWidth === 0 || layerHeight === 0) {
  7065. return;
  7066. }
  7067. this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY);
  7068. ctx.save();
  7069. ctx.globalAlpha = 1;
  7070. ctx.globalCompositeOperation = "source-over";
  7071. ctx.setTransform(1, 0, 0, 1, 0, 0);
  7072. ctx.drawImage(layerCtx.canvas, 0, 0);
  7073. ctx.restore();
  7074. }
  7075. genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) {
  7076. let maskCanvas = maskCtx.canvas;
  7077. let maskX = layerOffsetX - maskOffsetX;
  7078. let maskY = layerOffsetY - maskOffsetY;
  7079. if (backdrop) {
  7080. if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) {
  7081. const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height);
  7082. const ctx = canvas.context;
  7083. ctx.drawImage(maskCanvas, -maskX, -maskY);
  7084. if (backdrop.some(c => c !== 0)) {
  7085. ctx.globalCompositeOperation = "destination-atop";
  7086. ctx.fillStyle = Util.makeHexColor(...backdrop);
  7087. ctx.fillRect(0, 0, width, height);
  7088. ctx.globalCompositeOperation = "source-over";
  7089. }
  7090. maskCanvas = canvas.canvas;
  7091. maskX = maskY = 0;
  7092. } else if (backdrop.some(c => c !== 0)) {
  7093. maskCtx.save();
  7094. maskCtx.globalAlpha = 1;
  7095. maskCtx.setTransform(1, 0, 0, 1, 0, 0);
  7096. const clip = new Path2D();
  7097. clip.rect(maskX, maskY, width, height);
  7098. maskCtx.clip(clip);
  7099. maskCtx.globalCompositeOperation = "destination-atop";
  7100. maskCtx.fillStyle = Util.makeHexColor(...backdrop);
  7101. maskCtx.fillRect(maskX, maskY, width, height);
  7102. maskCtx.restore();
  7103. }
  7104. }
  7105. layerCtx.save();
  7106. layerCtx.globalAlpha = 1;
  7107. layerCtx.setTransform(1, 0, 0, 1, 0, 0);
  7108. if (subtype === "Alpha" && transferMap) {
  7109. layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap);
  7110. } else if (subtype === "Luminosity") {
  7111. layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap);
  7112. }
  7113. const clip = new Path2D();
  7114. clip.rect(layerOffsetX, layerOffsetY, width, height);
  7115. layerCtx.clip(clip);
  7116. layerCtx.globalCompositeOperation = "destination-in";
  7117. layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height);
  7118. layerCtx.restore();
  7119. }
  7120. save() {
  7121. if (this.inSMaskMode) {
  7122. copyCtxState(this.ctx, this.suspendedCtx);
  7123. this.suspendedCtx.save();
  7124. } else {
  7125. this.ctx.save();
  7126. }
  7127. const old = this.current;
  7128. this.stateStack.push(old);
  7129. this.current = old.clone();
  7130. }
  7131. restore() {
  7132. if (this.stateStack.length === 0 && this.inSMaskMode) {
  7133. this.endSMaskMode();
  7134. }
  7135. if (this.stateStack.length !== 0) {
  7136. this.current = this.stateStack.pop();
  7137. if (this.inSMaskMode) {
  7138. this.suspendedCtx.restore();
  7139. copyCtxState(this.suspendedCtx, this.ctx);
  7140. } else {
  7141. this.ctx.restore();
  7142. }
  7143. this.checkSMaskState();
  7144. this.pendingClip = null;
  7145. this._cachedScaleForStroking[0] = -1;
  7146. this._cachedGetSinglePixelWidth = null;
  7147. }
  7148. }
  7149. transform(a, b, c, d, e, f) {
  7150. this.ctx.transform(a, b, c, d, e, f);
  7151. this._cachedScaleForStroking[0] = -1;
  7152. this._cachedGetSinglePixelWidth = null;
  7153. }
  7154. constructPath(ops, args, minMax) {
  7155. const ctx = this.ctx;
  7156. const current = this.current;
  7157. let x = current.x,
  7158. y = current.y;
  7159. let startX, startY;
  7160. const currentTransform = getCurrentTransform(ctx);
  7161. const isScalingMatrix = currentTransform[0] === 0 && currentTransform[3] === 0 || currentTransform[1] === 0 && currentTransform[2] === 0;
  7162. const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;
  7163. for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
  7164. switch (ops[i] | 0) {
  7165. case OPS.rectangle:
  7166. x = args[j++];
  7167. y = args[j++];
  7168. const width = args[j++];
  7169. const height = args[j++];
  7170. const xw = x + width;
  7171. const yh = y + height;
  7172. ctx.moveTo(x, y);
  7173. if (width === 0 || height === 0) {
  7174. ctx.lineTo(xw, yh);
  7175. } else {
  7176. ctx.lineTo(xw, y);
  7177. ctx.lineTo(xw, yh);
  7178. ctx.lineTo(x, yh);
  7179. }
  7180. if (!isScalingMatrix) {
  7181. current.updateRectMinMax(currentTransform, [x, y, xw, yh]);
  7182. }
  7183. ctx.closePath();
  7184. break;
  7185. case OPS.moveTo:
  7186. x = args[j++];
  7187. y = args[j++];
  7188. ctx.moveTo(x, y);
  7189. if (!isScalingMatrix) {
  7190. current.updatePathMinMax(currentTransform, x, y);
  7191. }
  7192. break;
  7193. case OPS.lineTo:
  7194. x = args[j++];
  7195. y = args[j++];
  7196. ctx.lineTo(x, y);
  7197. if (!isScalingMatrix) {
  7198. current.updatePathMinMax(currentTransform, x, y);
  7199. }
  7200. break;
  7201. case OPS.curveTo:
  7202. startX = x;
  7203. startY = y;
  7204. x = args[j + 4];
  7205. y = args[j + 5];
  7206. ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
  7207. current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y, minMaxForBezier);
  7208. j += 6;
  7209. break;
  7210. case OPS.curveTo2:
  7211. startX = x;
  7212. startY = y;
  7213. ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
  7214. current.updateCurvePathMinMax(currentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3], minMaxForBezier);
  7215. x = args[j + 2];
  7216. y = args[j + 3];
  7217. j += 4;
  7218. break;
  7219. case OPS.curveTo3:
  7220. startX = x;
  7221. startY = y;
  7222. x = args[j + 2];
  7223. y = args[j + 3];
  7224. ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
  7225. current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], x, y, x, y, minMaxForBezier);
  7226. j += 4;
  7227. break;
  7228. case OPS.closePath:
  7229. ctx.closePath();
  7230. break;
  7231. }
  7232. }
  7233. if (isScalingMatrix) {
  7234. current.updateScalingPathMinMax(currentTransform, minMaxForBezier);
  7235. }
  7236. current.setCurrentPoint(x, y);
  7237. }
  7238. closePath() {
  7239. this.ctx.closePath();
  7240. }
  7241. stroke(consumePath = true) {
  7242. const ctx = this.ctx;
  7243. const strokeColor = this.current.strokeColor;
  7244. ctx.globalAlpha = this.current.strokeAlpha;
  7245. if (this.contentVisible) {
  7246. if (typeof strokeColor === "object" && strokeColor?.getPattern) {
  7247. ctx.save();
  7248. ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE);
  7249. this.rescaleAndStroke(false);
  7250. ctx.restore();
  7251. } else {
  7252. this.rescaleAndStroke(true);
  7253. }
  7254. }
  7255. if (consumePath) {
  7256. this.consumePath(this.current.getClippedPathBoundingBox());
  7257. }
  7258. ctx.globalAlpha = this.current.fillAlpha;
  7259. }
  7260. closeStroke() {
  7261. this.closePath();
  7262. this.stroke();
  7263. }
  7264. fill(consumePath = true) {
  7265. const ctx = this.ctx;
  7266. const fillColor = this.current.fillColor;
  7267. const isPatternFill = this.current.patternFill;
  7268. let needRestore = false;
  7269. if (isPatternFill) {
  7270. ctx.save();
  7271. ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL);
  7272. needRestore = true;
  7273. }
  7274. const intersect = this.current.getClippedPathBoundingBox();
  7275. if (this.contentVisible && intersect !== null) {
  7276. if (this.pendingEOFill) {
  7277. ctx.fill("evenodd");
  7278. this.pendingEOFill = false;
  7279. } else {
  7280. ctx.fill();
  7281. }
  7282. }
  7283. if (needRestore) {
  7284. ctx.restore();
  7285. }
  7286. if (consumePath) {
  7287. this.consumePath(intersect);
  7288. }
  7289. }
  7290. eoFill() {
  7291. this.pendingEOFill = true;
  7292. this.fill();
  7293. }
  7294. fillStroke() {
  7295. this.fill(false);
  7296. this.stroke(false);
  7297. this.consumePath();
  7298. }
  7299. eoFillStroke() {
  7300. this.pendingEOFill = true;
  7301. this.fillStroke();
  7302. }
  7303. closeFillStroke() {
  7304. this.closePath();
  7305. this.fillStroke();
  7306. }
  7307. closeEOFillStroke() {
  7308. this.pendingEOFill = true;
  7309. this.closePath();
  7310. this.fillStroke();
  7311. }
  7312. endPath() {
  7313. this.consumePath();
  7314. }
  7315. clip() {
  7316. this.pendingClip = NORMAL_CLIP;
  7317. }
  7318. eoClip() {
  7319. this.pendingClip = EO_CLIP;
  7320. }
  7321. beginText() {
  7322. this.current.textMatrix = IDENTITY_MATRIX;
  7323. this.current.textMatrixScale = 1;
  7324. this.current.x = this.current.lineX = 0;
  7325. this.current.y = this.current.lineY = 0;
  7326. }
  7327. endText() {
  7328. const paths = this.pendingTextPaths;
  7329. const ctx = this.ctx;
  7330. if (paths === undefined) {
  7331. ctx.beginPath();
  7332. return;
  7333. }
  7334. ctx.save();
  7335. ctx.beginPath();
  7336. for (const path of paths) {
  7337. ctx.setTransform(...path.transform);
  7338. ctx.translate(path.x, path.y);
  7339. path.addToPath(ctx, path.fontSize);
  7340. }
  7341. ctx.restore();
  7342. ctx.clip();
  7343. ctx.beginPath();
  7344. delete this.pendingTextPaths;
  7345. }
  7346. setCharSpacing(spacing) {
  7347. this.current.charSpacing = spacing;
  7348. }
  7349. setWordSpacing(spacing) {
  7350. this.current.wordSpacing = spacing;
  7351. }
  7352. setHScale(scale) {
  7353. this.current.textHScale = scale / 100;
  7354. }
  7355. setLeading(leading) {
  7356. this.current.leading = -leading;
  7357. }
  7358. setFont(fontRefName, size) {
  7359. const fontObj = this.commonObjs.get(fontRefName);
  7360. const current = this.current;
  7361. if (!fontObj) {
  7362. throw new Error(`Can't find font for ${fontRefName}`);
  7363. }
  7364. current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX;
  7365. if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
  7366. warn("Invalid font matrix for font " + fontRefName);
  7367. }
  7368. if (size < 0) {
  7369. size = -size;
  7370. current.fontDirection = -1;
  7371. } else {
  7372. current.fontDirection = 1;
  7373. }
  7374. this.current.font = fontObj;
  7375. this.current.fontSize = size;
  7376. if (fontObj.isType3Font) {
  7377. return;
  7378. }
  7379. const name = fontObj.loadedName || "sans-serif";
  7380. const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`;
  7381. let bold = "normal";
  7382. if (fontObj.black) {
  7383. bold = "900";
  7384. } else if (fontObj.bold) {
  7385. bold = "bold";
  7386. }
  7387. const italic = fontObj.italic ? "italic" : "normal";
  7388. let browserFontSize = size;
  7389. if (size < MIN_FONT_SIZE) {
  7390. browserFontSize = MIN_FONT_SIZE;
  7391. } else if (size > MAX_FONT_SIZE) {
  7392. browserFontSize = MAX_FONT_SIZE;
  7393. }
  7394. this.current.fontSizeScale = size / browserFontSize;
  7395. this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;
  7396. }
  7397. setTextRenderingMode(mode) {
  7398. this.current.textRenderingMode = mode;
  7399. }
  7400. setTextRise(rise) {
  7401. this.current.textRise = rise;
  7402. }
  7403. moveText(x, y) {
  7404. this.current.x = this.current.lineX += x;
  7405. this.current.y = this.current.lineY += y;
  7406. }
  7407. setLeadingMoveText(x, y) {
  7408. this.setLeading(-y);
  7409. this.moveText(x, y);
  7410. }
  7411. setTextMatrix(a, b, c, d, e, f) {
  7412. this.current.textMatrix = [a, b, c, d, e, f];
  7413. this.current.textMatrixScale = Math.hypot(a, b);
  7414. this.current.x = this.current.lineX = 0;
  7415. this.current.y = this.current.lineY = 0;
  7416. }
  7417. nextLine() {
  7418. this.moveText(0, this.current.leading);
  7419. }
  7420. paintChar(character, x, y, patternTransform) {
  7421. const ctx = this.ctx;
  7422. const current = this.current;
  7423. const font = current.font;
  7424. const textRenderingMode = current.textRenderingMode;
  7425. const fontSize = current.fontSize / current.fontSizeScale;
  7426. const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
  7427. const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);
  7428. const patternFill = current.patternFill && !font.missingFile;
  7429. let addToPath;
  7430. if (font.disableFontFace || isAddToPathSet || patternFill) {
  7431. addToPath = font.getPathGenerator(this.commonObjs, character);
  7432. }
  7433. if (font.disableFontFace || patternFill) {
  7434. ctx.save();
  7435. ctx.translate(x, y);
  7436. ctx.beginPath();
  7437. addToPath(ctx, fontSize);
  7438. if (patternTransform) {
  7439. ctx.setTransform(...patternTransform);
  7440. }
  7441. if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  7442. ctx.fill();
  7443. }
  7444. if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  7445. ctx.stroke();
  7446. }
  7447. ctx.restore();
  7448. } else {
  7449. if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  7450. ctx.fillText(character, x, y);
  7451. }
  7452. if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  7453. ctx.strokeText(character, x, y);
  7454. }
  7455. }
  7456. if (isAddToPathSet) {
  7457. const paths = this.pendingTextPaths ||= [];
  7458. paths.push({
  7459. transform: getCurrentTransform(ctx),
  7460. x,
  7461. y,
  7462. fontSize,
  7463. addToPath
  7464. });
  7465. }
  7466. }
  7467. get isFontSubpixelAAEnabled() {
  7468. const {
  7469. context: ctx
  7470. } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10);
  7471. ctx.scale(1.5, 1);
  7472. ctx.fillText("I", 0, 10);
  7473. const data = ctx.getImageData(0, 0, 10, 10).data;
  7474. let enabled = false;
  7475. for (let i = 3; i < data.length; i += 4) {
  7476. if (data[i] > 0 && data[i] < 255) {
  7477. enabled = true;
  7478. break;
  7479. }
  7480. }
  7481. return shadow(this, "isFontSubpixelAAEnabled", enabled);
  7482. }
  7483. showText(glyphs) {
  7484. const current = this.current;
  7485. const font = current.font;
  7486. if (font.isType3Font) {
  7487. return this.showType3Text(glyphs);
  7488. }
  7489. const fontSize = current.fontSize;
  7490. if (fontSize === 0) {
  7491. return undefined;
  7492. }
  7493. const ctx = this.ctx;
  7494. const fontSizeScale = current.fontSizeScale;
  7495. const charSpacing = current.charSpacing;
  7496. const wordSpacing = current.wordSpacing;
  7497. const fontDirection = current.fontDirection;
  7498. const textHScale = current.textHScale * fontDirection;
  7499. const glyphsLength = glyphs.length;
  7500. const vertical = font.vertical;
  7501. const spacingDir = vertical ? 1 : -1;
  7502. const defaultVMetrics = font.defaultVMetrics;
  7503. const widthAdvanceScale = fontSize * current.fontMatrix[0];
  7504. const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
  7505. ctx.save();
  7506. ctx.transform(...current.textMatrix);
  7507. ctx.translate(current.x, current.y + current.textRise);
  7508. if (fontDirection > 0) {
  7509. ctx.scale(textHScale, -1);
  7510. } else {
  7511. ctx.scale(textHScale, 1);
  7512. }
  7513. let patternTransform;
  7514. if (current.patternFill) {
  7515. ctx.save();
  7516. const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL);
  7517. patternTransform = getCurrentTransform(ctx);
  7518. ctx.restore();
  7519. ctx.fillStyle = pattern;
  7520. }
  7521. let lineWidth = current.lineWidth;
  7522. const scale = current.textMatrixScale;
  7523. if (scale === 0 || lineWidth === 0) {
  7524. const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
  7525. if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  7526. lineWidth = this.getSinglePixelWidth();
  7527. }
  7528. } else {
  7529. lineWidth /= scale;
  7530. }
  7531. if (fontSizeScale !== 1.0) {
  7532. ctx.scale(fontSizeScale, fontSizeScale);
  7533. lineWidth /= fontSizeScale;
  7534. }
  7535. ctx.lineWidth = lineWidth;
  7536. if (font.isInvalidPDFjsFont) {
  7537. const chars = [];
  7538. let width = 0;
  7539. for (const glyph of glyphs) {
  7540. chars.push(glyph.unicode);
  7541. width += glyph.width;
  7542. }
  7543. ctx.fillText(chars.join(""), 0, 0);
  7544. current.x += width * widthAdvanceScale * textHScale;
  7545. ctx.restore();
  7546. this.compose();
  7547. return undefined;
  7548. }
  7549. let x = 0,
  7550. i;
  7551. for (i = 0; i < glyphsLength; ++i) {
  7552. const glyph = glyphs[i];
  7553. if (typeof glyph === "number") {
  7554. x += spacingDir * glyph * fontSize / 1000;
  7555. continue;
  7556. }
  7557. let restoreNeeded = false;
  7558. const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
  7559. const character = glyph.fontChar;
  7560. const accent = glyph.accent;
  7561. let scaledX, scaledY;
  7562. let width = glyph.width;
  7563. if (vertical) {
  7564. const vmetric = glyph.vmetric || defaultVMetrics;
  7565. const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale;
  7566. const vy = vmetric[2] * widthAdvanceScale;
  7567. width = vmetric ? -vmetric[0] : width;
  7568. scaledX = vx / fontSizeScale;
  7569. scaledY = (x + vy) / fontSizeScale;
  7570. } else {
  7571. scaledX = x / fontSizeScale;
  7572. scaledY = 0;
  7573. }
  7574. if (font.remeasure && width > 0) {
  7575. const measuredWidth = ctx.measureText(character).width * 1000 / fontSize * fontSizeScale;
  7576. if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
  7577. const characterScaleX = width / measuredWidth;
  7578. restoreNeeded = true;
  7579. ctx.save();
  7580. ctx.scale(characterScaleX, 1);
  7581. scaledX /= characterScaleX;
  7582. } else if (width !== measuredWidth) {
  7583. scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale;
  7584. }
  7585. }
  7586. if (this.contentVisible && (glyph.isInFont || font.missingFile)) {
  7587. if (simpleFillText && !accent) {
  7588. ctx.fillText(character, scaledX, scaledY);
  7589. } else {
  7590. this.paintChar(character, scaledX, scaledY, patternTransform);
  7591. if (accent) {
  7592. const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale;
  7593. const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale;
  7594. this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY, patternTransform);
  7595. }
  7596. }
  7597. }
  7598. const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;
  7599. x += charWidth;
  7600. if (restoreNeeded) {
  7601. ctx.restore();
  7602. }
  7603. }
  7604. if (vertical) {
  7605. current.y -= x;
  7606. } else {
  7607. current.x += x * textHScale;
  7608. }
  7609. ctx.restore();
  7610. this.compose();
  7611. return undefined;
  7612. }
  7613. showType3Text(glyphs) {
  7614. const ctx = this.ctx;
  7615. const current = this.current;
  7616. const font = current.font;
  7617. const fontSize = current.fontSize;
  7618. const fontDirection = current.fontDirection;
  7619. const spacingDir = font.vertical ? 1 : -1;
  7620. const charSpacing = current.charSpacing;
  7621. const wordSpacing = current.wordSpacing;
  7622. const textHScale = current.textHScale * fontDirection;
  7623. const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
  7624. const glyphsLength = glyphs.length;
  7625. const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE;
  7626. let i, glyph, width, spacingLength;
  7627. if (isTextInvisible || fontSize === 0) {
  7628. return;
  7629. }
  7630. this._cachedScaleForStroking[0] = -1;
  7631. this._cachedGetSinglePixelWidth = null;
  7632. ctx.save();
  7633. ctx.transform(...current.textMatrix);
  7634. ctx.translate(current.x, current.y);
  7635. ctx.scale(textHScale, fontDirection);
  7636. for (i = 0; i < glyphsLength; ++i) {
  7637. glyph = glyphs[i];
  7638. if (typeof glyph === "number") {
  7639. spacingLength = spacingDir * glyph * fontSize / 1000;
  7640. this.ctx.translate(spacingLength, 0);
  7641. current.x += spacingLength * textHScale;
  7642. continue;
  7643. }
  7644. const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
  7645. const operatorList = font.charProcOperatorList[glyph.operatorListId];
  7646. if (!operatorList) {
  7647. warn(`Type3 character "${glyph.operatorListId}" is not available.`);
  7648. continue;
  7649. }
  7650. if (this.contentVisible) {
  7651. this.processingType3 = glyph;
  7652. this.save();
  7653. ctx.scale(fontSize, fontSize);
  7654. ctx.transform(...fontMatrix);
  7655. this.executeOperatorList(operatorList);
  7656. this.restore();
  7657. }
  7658. const transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
  7659. width = transformed[0] * fontSize + spacing;
  7660. ctx.translate(width, 0);
  7661. current.x += width * textHScale;
  7662. }
  7663. ctx.restore();
  7664. this.processingType3 = null;
  7665. }
  7666. setCharWidth(xWidth, yWidth) {}
  7667. setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) {
  7668. this.ctx.rect(llx, lly, urx - llx, ury - lly);
  7669. this.ctx.clip();
  7670. this.endPath();
  7671. }
  7672. getColorN_Pattern(IR) {
  7673. let pattern;
  7674. if (IR[0] === "TilingPattern") {
  7675. const color = IR[1];
  7676. const baseTransform = this.baseTransform || getCurrentTransform(this.ctx);
  7677. const canvasGraphicsFactory = {
  7678. createCanvasGraphics: ctx => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {
  7679. optionalContentConfig: this.optionalContentConfig,
  7680. markedContentStack: this.markedContentStack
  7681. })
  7682. };
  7683. pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform);
  7684. } else {
  7685. pattern = this._getPattern(IR[1], IR[2]);
  7686. }
  7687. return pattern;
  7688. }
  7689. setStrokeColorN() {
  7690. this.current.strokeColor = this.getColorN_Pattern(arguments);
  7691. }
  7692. setFillColorN() {
  7693. this.current.fillColor = this.getColorN_Pattern(arguments);
  7694. this.current.patternFill = true;
  7695. }
  7696. setStrokeRGBColor(r, g, b) {
  7697. this.ctx.strokeStyle = this.current.strokeColor = Util.makeHexColor(r, g, b);
  7698. }
  7699. setStrokeTransparent() {
  7700. this.ctx.strokeStyle = this.current.strokeColor = "transparent";
  7701. }
  7702. setFillRGBColor(r, g, b) {
  7703. this.ctx.fillStyle = this.current.fillColor = Util.makeHexColor(r, g, b);
  7704. this.current.patternFill = false;
  7705. }
  7706. setFillTransparent() {
  7707. this.ctx.fillStyle = this.current.fillColor = "transparent";
  7708. this.current.patternFill = false;
  7709. }
  7710. _getPattern(objId, matrix = null) {
  7711. let pattern;
  7712. if (this.cachedPatterns.has(objId)) {
  7713. pattern = this.cachedPatterns.get(objId);
  7714. } else {
  7715. pattern = getShadingPattern(this.getObject(objId));
  7716. this.cachedPatterns.set(objId, pattern);
  7717. }
  7718. if (matrix) {
  7719. pattern.matrix = matrix;
  7720. }
  7721. return pattern;
  7722. }
  7723. shadingFill(objId) {
  7724. if (!this.contentVisible) {
  7725. return;
  7726. }
  7727. const ctx = this.ctx;
  7728. this.save();
  7729. const pattern = this._getPattern(objId);
  7730. ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING);
  7731. const inv = getCurrentTransformInverse(ctx);
  7732. if (inv) {
  7733. const {
  7734. width,
  7735. height
  7736. } = ctx.canvas;
  7737. const [x0, y0, x1, y1] = Util.getAxialAlignedBoundingBox([0, 0, width, height], inv);
  7738. this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
  7739. } else {
  7740. this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
  7741. }
  7742. this.compose(this.current.getClippedPathBoundingBox());
  7743. this.restore();
  7744. }
  7745. beginInlineImage() {
  7746. unreachable("Should not call beginInlineImage");
  7747. }
  7748. beginImageData() {
  7749. unreachable("Should not call beginImageData");
  7750. }
  7751. paintFormXObjectBegin(matrix, bbox) {
  7752. if (!this.contentVisible) {
  7753. return;
  7754. }
  7755. this.save();
  7756. this.baseTransformStack.push(this.baseTransform);
  7757. if (matrix) {
  7758. this.transform(...matrix);
  7759. }
  7760. this.baseTransform = getCurrentTransform(this.ctx);
  7761. if (bbox) {
  7762. const width = bbox[2] - bbox[0];
  7763. const height = bbox[3] - bbox[1];
  7764. this.ctx.rect(bbox[0], bbox[1], width, height);
  7765. this.current.updateRectMinMax(getCurrentTransform(this.ctx), bbox);
  7766. this.clip();
  7767. this.endPath();
  7768. }
  7769. }
  7770. paintFormXObjectEnd() {
  7771. if (!this.contentVisible) {
  7772. return;
  7773. }
  7774. this.restore();
  7775. this.baseTransform = this.baseTransformStack.pop();
  7776. }
  7777. beginGroup(group) {
  7778. if (!this.contentVisible) {
  7779. return;
  7780. }
  7781. this.save();
  7782. if (this.inSMaskMode) {
  7783. this.endSMaskMode();
  7784. this.current.activeSMask = null;
  7785. }
  7786. const currentCtx = this.ctx;
  7787. if (!group.isolated) {
  7788. info("TODO: Support non-isolated groups.");
  7789. }
  7790. if (group.knockout) {
  7791. warn("Knockout groups not supported.");
  7792. }
  7793. const currentTransform = getCurrentTransform(currentCtx);
  7794. if (group.matrix) {
  7795. currentCtx.transform(...group.matrix);
  7796. }
  7797. if (!group.bbox) {
  7798. throw new Error("Bounding box is required.");
  7799. }
  7800. let bounds = Util.getAxialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx));
  7801. const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];
  7802. bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
  7803. const offsetX = Math.floor(bounds[0]);
  7804. const offsetY = Math.floor(bounds[1]);
  7805. const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
  7806. const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
  7807. this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);
  7808. let cacheId = "groupAt" + this.groupLevel;
  7809. if (group.smask) {
  7810. cacheId += "_smask_" + this.smaskCounter++ % 2;
  7811. }
  7812. const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);
  7813. const groupCtx = scratchCanvas.context;
  7814. groupCtx.translate(-offsetX, -offsetY);
  7815. groupCtx.transform(...currentTransform);
  7816. if (group.smask) {
  7817. this.smaskStack.push({
  7818. canvas: scratchCanvas.canvas,
  7819. context: groupCtx,
  7820. offsetX,
  7821. offsetY,
  7822. subtype: group.smask.subtype,
  7823. backdrop: group.smask.backdrop,
  7824. transferMap: group.smask.transferMap || null,
  7825. startTransformInverse: null
  7826. });
  7827. } else {
  7828. currentCtx.setTransform(1, 0, 0, 1, 0, 0);
  7829. currentCtx.translate(offsetX, offsetY);
  7830. currentCtx.save();
  7831. }
  7832. copyCtxState(currentCtx, groupCtx);
  7833. this.ctx = groupCtx;
  7834. this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
  7835. this.groupStack.push(currentCtx);
  7836. this.groupLevel++;
  7837. }
  7838. endGroup(group) {
  7839. if (!this.contentVisible) {
  7840. return;
  7841. }
  7842. this.groupLevel--;
  7843. const groupCtx = this.ctx;
  7844. const ctx = this.groupStack.pop();
  7845. this.ctx = ctx;
  7846. this.ctx.imageSmoothingEnabled = false;
  7847. if (group.smask) {
  7848. this.tempSMask = this.smaskStack.pop();
  7849. this.restore();
  7850. } else {
  7851. this.ctx.restore();
  7852. const currentMtx = getCurrentTransform(this.ctx);
  7853. this.restore();
  7854. this.ctx.save();
  7855. this.ctx.setTransform(...currentMtx);
  7856. const dirtyBox = Util.getAxialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx);
  7857. this.ctx.drawImage(groupCtx.canvas, 0, 0);
  7858. this.ctx.restore();
  7859. this.compose(dirtyBox);
  7860. }
  7861. }
  7862. beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) {
  7863. this.#restoreInitialState();
  7864. resetCtxToDefault(this.ctx);
  7865. this.ctx.save();
  7866. this.save();
  7867. if (this.baseTransform) {
  7868. this.ctx.setTransform(...this.baseTransform);
  7869. }
  7870. if (rect) {
  7871. const width = rect[2] - rect[0];
  7872. const height = rect[3] - rect[1];
  7873. if (hasOwnCanvas && this.annotationCanvasMap) {
  7874. transform = transform.slice();
  7875. transform[4] -= rect[0];
  7876. transform[5] -= rect[1];
  7877. rect = rect.slice();
  7878. rect[0] = rect[1] = 0;
  7879. rect[2] = width;
  7880. rect[3] = height;
  7881. const [scaleX, scaleY] = Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx));
  7882. const {
  7883. viewportScale
  7884. } = this;
  7885. const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale);
  7886. const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale);
  7887. this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight);
  7888. const {
  7889. canvas,
  7890. context
  7891. } = this.annotationCanvas;
  7892. this.annotationCanvasMap.set(id, canvas);
  7893. this.annotationCanvas.savedCtx = this.ctx;
  7894. this.ctx = context;
  7895. this.ctx.save();
  7896. this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY);
  7897. resetCtxToDefault(this.ctx);
  7898. } else {
  7899. resetCtxToDefault(this.ctx);
  7900. this.ctx.rect(rect[0], rect[1], width, height);
  7901. this.ctx.clip();
  7902. this.endPath();
  7903. }
  7904. }
  7905. this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
  7906. this.transform(...transform);
  7907. this.transform(...matrix);
  7908. }
  7909. endAnnotation() {
  7910. if (this.annotationCanvas) {
  7911. this.ctx.restore();
  7912. this.#drawFilter();
  7913. this.ctx = this.annotationCanvas.savedCtx;
  7914. delete this.annotationCanvas.savedCtx;
  7915. delete this.annotationCanvas;
  7916. }
  7917. }
  7918. paintImageMaskXObject(img) {
  7919. if (!this.contentVisible) {
  7920. return;
  7921. }
  7922. const count = img.count;
  7923. img = this.getObject(img.data, img);
  7924. img.count = count;
  7925. const ctx = this.ctx;
  7926. const glyph = this.processingType3;
  7927. if (glyph) {
  7928. if (glyph.compiled === undefined) {
  7929. glyph.compiled = compileType3Glyph(img);
  7930. }
  7931. if (glyph.compiled) {
  7932. glyph.compiled(ctx);
  7933. return;
  7934. }
  7935. }
  7936. const mask = this._createMaskCanvas(img);
  7937. const maskCanvas = mask.canvas;
  7938. ctx.save();
  7939. ctx.setTransform(1, 0, 0, 1, 0, 0);
  7940. ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
  7941. ctx.restore();
  7942. this.compose();
  7943. }
  7944. paintImageMaskXObjectRepeat(img, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
  7945. if (!this.contentVisible) {
  7946. return;
  7947. }
  7948. img = this.getObject(img.data, img);
  7949. const ctx = this.ctx;
  7950. ctx.save();
  7951. const currentTransform = getCurrentTransform(ctx);
  7952. ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
  7953. const mask = this._createMaskCanvas(img);
  7954. ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]);
  7955. for (let i = 0, ii = positions.length; i < ii; i += 2) {
  7956. const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]);
  7957. const [x, y] = Util.applyTransform([0, 0], trans);
  7958. ctx.drawImage(mask.canvas, x, y);
  7959. }
  7960. ctx.restore();
  7961. this.compose();
  7962. }
  7963. paintImageMaskXObjectGroup(images) {
  7964. if (!this.contentVisible) {
  7965. return;
  7966. }
  7967. const ctx = this.ctx;
  7968. const fillColor = this.current.fillColor;
  7969. const isPatternFill = this.current.patternFill;
  7970. for (const image of images) {
  7971. const {
  7972. data,
  7973. width,
  7974. height,
  7975. transform
  7976. } = image;
  7977. const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
  7978. const maskCtx = maskCanvas.context;
  7979. maskCtx.save();
  7980. const img = this.getObject(data, image);
  7981. putBinaryImageMask(maskCtx, img);
  7982. maskCtx.globalCompositeOperation = "source-in";
  7983. maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL) : fillColor;
  7984. maskCtx.fillRect(0, 0, width, height);
  7985. maskCtx.restore();
  7986. ctx.save();
  7987. ctx.transform(...transform);
  7988. ctx.scale(1, -1);
  7989. drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
  7990. ctx.restore();
  7991. }
  7992. this.compose();
  7993. }
  7994. paintImageXObject(objId) {
  7995. if (!this.contentVisible) {
  7996. return;
  7997. }
  7998. const imgData = this.getObject(objId);
  7999. if (!imgData) {
  8000. warn("Dependent image isn't ready yet");
  8001. return;
  8002. }
  8003. this.paintInlineImageXObject(imgData);
  8004. }
  8005. paintImageXObjectRepeat(objId, scaleX, scaleY, positions) {
  8006. if (!this.contentVisible) {
  8007. return;
  8008. }
  8009. const imgData = this.getObject(objId);
  8010. if (!imgData) {
  8011. warn("Dependent image isn't ready yet");
  8012. return;
  8013. }
  8014. const width = imgData.width;
  8015. const height = imgData.height;
  8016. const map = [];
  8017. for (let i = 0, ii = positions.length; i < ii; i += 2) {
  8018. map.push({
  8019. transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]],
  8020. x: 0,
  8021. y: 0,
  8022. w: width,
  8023. h: height
  8024. });
  8025. }
  8026. this.paintInlineImageXObjectGroup(imgData, map);
  8027. }
  8028. applyTransferMapsToCanvas(ctx) {
  8029. if (this.current.transferMaps !== "none") {
  8030. ctx.filter = this.current.transferMaps;
  8031. ctx.drawImage(ctx.canvas, 0, 0);
  8032. ctx.filter = "none";
  8033. }
  8034. return ctx.canvas;
  8035. }
  8036. applyTransferMapsToBitmap(imgData) {
  8037. if (this.current.transferMaps === "none") {
  8038. return imgData.bitmap;
  8039. }
  8040. const {
  8041. bitmap,
  8042. width,
  8043. height
  8044. } = imgData;
  8045. const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
  8046. const tmpCtx = tmpCanvas.context;
  8047. tmpCtx.filter = this.current.transferMaps;
  8048. tmpCtx.drawImage(bitmap, 0, 0);
  8049. tmpCtx.filter = "none";
  8050. return tmpCanvas.canvas;
  8051. }
  8052. paintInlineImageXObject(imgData) {
  8053. if (!this.contentVisible) {
  8054. return;
  8055. }
  8056. const width = imgData.width;
  8057. const height = imgData.height;
  8058. const ctx = this.ctx;
  8059. this.save();
  8060. if (!isNodeJS) {
  8061. const {
  8062. filter
  8063. } = ctx;
  8064. if (filter !== "none" && filter !== "") {
  8065. ctx.filter = "none";
  8066. }
  8067. }
  8068. ctx.scale(1 / width, -1 / height);
  8069. let imgToPaint;
  8070. if (imgData.bitmap) {
  8071. imgToPaint = this.applyTransferMapsToBitmap(imgData);
  8072. } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) {
  8073. imgToPaint = imgData;
  8074. } else {
  8075. const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
  8076. const tmpCtx = tmpCanvas.context;
  8077. putBinaryImageData(tmpCtx, imgData);
  8078. imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
  8079. }
  8080. const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx));
  8081. ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate);
  8082. drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height);
  8083. this.compose();
  8084. this.restore();
  8085. }
  8086. paintInlineImageXObjectGroup(imgData, map) {
  8087. if (!this.contentVisible) {
  8088. return;
  8089. }
  8090. const ctx = this.ctx;
  8091. let imgToPaint;
  8092. if (imgData.bitmap) {
  8093. imgToPaint = imgData.bitmap;
  8094. } else {
  8095. const w = imgData.width;
  8096. const h = imgData.height;
  8097. const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h);
  8098. const tmpCtx = tmpCanvas.context;
  8099. putBinaryImageData(tmpCtx, imgData);
  8100. imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
  8101. }
  8102. for (const entry of map) {
  8103. ctx.save();
  8104. ctx.transform(...entry.transform);
  8105. ctx.scale(1, -1);
  8106. drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1);
  8107. ctx.restore();
  8108. }
  8109. this.compose();
  8110. }
  8111. paintSolidColorImageMask() {
  8112. if (!this.contentVisible) {
  8113. return;
  8114. }
  8115. this.ctx.fillRect(0, 0, 1, 1);
  8116. this.compose();
  8117. }
  8118. markPoint(tag) {}
  8119. markPointProps(tag, properties) {}
  8120. beginMarkedContent(tag) {
  8121. this.markedContentStack.push({
  8122. visible: true
  8123. });
  8124. }
  8125. beginMarkedContentProps(tag, properties) {
  8126. if (tag === "OC") {
  8127. this.markedContentStack.push({
  8128. visible: this.optionalContentConfig.isVisible(properties)
  8129. });
  8130. } else {
  8131. this.markedContentStack.push({
  8132. visible: true
  8133. });
  8134. }
  8135. this.contentVisible = this.isContentVisible();
  8136. }
  8137. endMarkedContent() {
  8138. this.markedContentStack.pop();
  8139. this.contentVisible = this.isContentVisible();
  8140. }
  8141. beginCompat() {}
  8142. endCompat() {}
  8143. consumePath(clipBox) {
  8144. const isEmpty = this.current.isEmptyClip();
  8145. if (this.pendingClip) {
  8146. this.current.updateClipFromPath();
  8147. }
  8148. if (!this.pendingClip) {
  8149. this.compose(clipBox);
  8150. }
  8151. const ctx = this.ctx;
  8152. if (this.pendingClip) {
  8153. if (!isEmpty) {
  8154. if (this.pendingClip === EO_CLIP) {
  8155. ctx.clip("evenodd");
  8156. } else {
  8157. ctx.clip();
  8158. }
  8159. }
  8160. this.pendingClip = null;
  8161. }
  8162. this.current.startNewPathAndClipBox(this.current.clipBox);
  8163. ctx.beginPath();
  8164. }
  8165. getSinglePixelWidth() {
  8166. if (!this._cachedGetSinglePixelWidth) {
  8167. const m = getCurrentTransform(this.ctx);
  8168. if (m[1] === 0 && m[2] === 0) {
  8169. this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3]));
  8170. } else {
  8171. const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]);
  8172. const normX = Math.hypot(m[0], m[2]);
  8173. const normY = Math.hypot(m[1], m[3]);
  8174. this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet;
  8175. }
  8176. }
  8177. return this._cachedGetSinglePixelWidth;
  8178. }
  8179. getScaleForStroking() {
  8180. if (this._cachedScaleForStroking[0] === -1) {
  8181. const {
  8182. lineWidth
  8183. } = this.current;
  8184. const {
  8185. a,
  8186. b,
  8187. c,
  8188. d
  8189. } = this.ctx.getTransform();
  8190. let scaleX, scaleY;
  8191. if (b === 0 && c === 0) {
  8192. const normX = Math.abs(a);
  8193. const normY = Math.abs(d);
  8194. if (normX === normY) {
  8195. if (lineWidth === 0) {
  8196. scaleX = scaleY = 1 / normX;
  8197. } else {
  8198. const scaledLineWidth = normX * lineWidth;
  8199. scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1;
  8200. }
  8201. } else if (lineWidth === 0) {
  8202. scaleX = 1 / normX;
  8203. scaleY = 1 / normY;
  8204. } else {
  8205. const scaledXLineWidth = normX * lineWidth;
  8206. const scaledYLineWidth = normY * lineWidth;
  8207. scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1;
  8208. scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1;
  8209. }
  8210. } else {
  8211. const absDet = Math.abs(a * d - b * c);
  8212. const normX = Math.hypot(a, b);
  8213. const normY = Math.hypot(c, d);
  8214. if (lineWidth === 0) {
  8215. scaleX = normY / absDet;
  8216. scaleY = normX / absDet;
  8217. } else {
  8218. const baseArea = lineWidth * absDet;
  8219. scaleX = normY > baseArea ? normY / baseArea : 1;
  8220. scaleY = normX > baseArea ? normX / baseArea : 1;
  8221. }
  8222. }
  8223. this._cachedScaleForStroking[0] = scaleX;
  8224. this._cachedScaleForStroking[1] = scaleY;
  8225. }
  8226. return this._cachedScaleForStroking;
  8227. }
  8228. rescaleAndStroke(saveRestore) {
  8229. const {
  8230. ctx
  8231. } = this;
  8232. const {
  8233. lineWidth
  8234. } = this.current;
  8235. const [scaleX, scaleY] = this.getScaleForStroking();
  8236. ctx.lineWidth = lineWidth || 1;
  8237. if (scaleX === 1 && scaleY === 1) {
  8238. ctx.stroke();
  8239. return;
  8240. }
  8241. const dashes = ctx.getLineDash();
  8242. if (saveRestore) {
  8243. ctx.save();
  8244. }
  8245. ctx.scale(scaleX, scaleY);
  8246. if (dashes.length > 0) {
  8247. const scale = Math.max(scaleX, scaleY);
  8248. ctx.setLineDash(dashes.map(x => x / scale));
  8249. ctx.lineDashOffset /= scale;
  8250. }
  8251. ctx.stroke();
  8252. if (saveRestore) {
  8253. ctx.restore();
  8254. }
  8255. }
  8256. isContentVisible() {
  8257. for (let i = this.markedContentStack.length - 1; i >= 0; i--) {
  8258. if (!this.markedContentStack[i].visible) {
  8259. return false;
  8260. }
  8261. }
  8262. return true;
  8263. }
  8264. }
  8265. for (const op in OPS) {
  8266. if (CanvasGraphics.prototype[op] !== undefined) {
  8267. CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
  8268. }
  8269. }
  8270. ;// CONCATENATED MODULE: ./src/display/worker_options.js
  8271. class GlobalWorkerOptions {
  8272. static #port = null;
  8273. static #src = "";
  8274. static get workerPort() {
  8275. return this.#port;
  8276. }
  8277. static set workerPort(val) {
  8278. if (!(typeof Worker !== "undefined" && val instanceof Worker) && val !== null) {
  8279. throw new Error("Invalid `workerPort` type.");
  8280. }
  8281. this.#port = val;
  8282. }
  8283. static get workerSrc() {
  8284. return this.#src;
  8285. }
  8286. static set workerSrc(val) {
  8287. if (typeof val !== "string") {
  8288. throw new Error("Invalid `workerSrc` type.");
  8289. }
  8290. this.#src = val;
  8291. }
  8292. }
  8293. ;// CONCATENATED MODULE: ./src/shared/message_handler.js
  8294. const CallbackKind = {
  8295. UNKNOWN: 0,
  8296. DATA: 1,
  8297. ERROR: 2
  8298. };
  8299. const StreamKind = {
  8300. UNKNOWN: 0,
  8301. CANCEL: 1,
  8302. CANCEL_COMPLETE: 2,
  8303. CLOSE: 3,
  8304. ENQUEUE: 4,
  8305. ERROR: 5,
  8306. PULL: 6,
  8307. PULL_COMPLETE: 7,
  8308. START_COMPLETE: 8
  8309. };
  8310. function wrapReason(reason) {
  8311. if (!(reason instanceof Error || typeof reason === "object" && reason !== null)) {
  8312. unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');
  8313. }
  8314. switch (reason.name) {
  8315. case "AbortException":
  8316. return new AbortException(reason.message);
  8317. case "MissingPDFException":
  8318. return new MissingPDFException(reason.message);
  8319. case "PasswordException":
  8320. return new PasswordException(reason.message, reason.code);
  8321. case "UnexpectedResponseException":
  8322. return new UnexpectedResponseException(reason.message, reason.status);
  8323. case "UnknownErrorException":
  8324. return new UnknownErrorException(reason.message, reason.details);
  8325. default:
  8326. return new UnknownErrorException(reason.message, reason.toString());
  8327. }
  8328. }
  8329. class MessageHandler {
  8330. constructor(sourceName, targetName, comObj) {
  8331. this.sourceName = sourceName;
  8332. this.targetName = targetName;
  8333. this.comObj = comObj;
  8334. this.callbackId = 1;
  8335. this.streamId = 1;
  8336. this.streamSinks = Object.create(null);
  8337. this.streamControllers = Object.create(null);
  8338. this.callbackCapabilities = Object.create(null);
  8339. this.actionHandler = Object.create(null);
  8340. this._onComObjOnMessage = event => {
  8341. const data = event.data;
  8342. if (data.targetName !== this.sourceName) {
  8343. return;
  8344. }
  8345. if (data.stream) {
  8346. this.#processStreamMessage(data);
  8347. return;
  8348. }
  8349. if (data.callback) {
  8350. const callbackId = data.callbackId;
  8351. const capability = this.callbackCapabilities[callbackId];
  8352. if (!capability) {
  8353. throw new Error(`Cannot resolve callback ${callbackId}`);
  8354. }
  8355. delete this.callbackCapabilities[callbackId];
  8356. if (data.callback === CallbackKind.DATA) {
  8357. capability.resolve(data.data);
  8358. } else if (data.callback === CallbackKind.ERROR) {
  8359. capability.reject(wrapReason(data.reason));
  8360. } else {
  8361. throw new Error("Unexpected callback case");
  8362. }
  8363. return;
  8364. }
  8365. const action = this.actionHandler[data.action];
  8366. if (!action) {
  8367. throw new Error(`Unknown action from worker: ${data.action}`);
  8368. }
  8369. if (data.callbackId) {
  8370. const cbSourceName = this.sourceName;
  8371. const cbTargetName = data.sourceName;
  8372. new Promise(function (resolve) {
  8373. resolve(action(data.data));
  8374. }).then(function (result) {
  8375. comObj.postMessage({
  8376. sourceName: cbSourceName,
  8377. targetName: cbTargetName,
  8378. callback: CallbackKind.DATA,
  8379. callbackId: data.callbackId,
  8380. data: result
  8381. });
  8382. }, function (reason) {
  8383. comObj.postMessage({
  8384. sourceName: cbSourceName,
  8385. targetName: cbTargetName,
  8386. callback: CallbackKind.ERROR,
  8387. callbackId: data.callbackId,
  8388. reason: wrapReason(reason)
  8389. });
  8390. });
  8391. return;
  8392. }
  8393. if (data.streamId) {
  8394. this.#createStreamSink(data);
  8395. return;
  8396. }
  8397. action(data.data);
  8398. };
  8399. comObj.addEventListener("message", this._onComObjOnMessage);
  8400. }
  8401. on(actionName, handler) {
  8402. const ah = this.actionHandler;
  8403. if (ah[actionName]) {
  8404. throw new Error(`There is already an actionName called "${actionName}"`);
  8405. }
  8406. ah[actionName] = handler;
  8407. }
  8408. send(actionName, data, transfers) {
  8409. this.comObj.postMessage({
  8410. sourceName: this.sourceName,
  8411. targetName: this.targetName,
  8412. action: actionName,
  8413. data
  8414. }, transfers);
  8415. }
  8416. sendWithPromise(actionName, data, transfers) {
  8417. const callbackId = this.callbackId++;
  8418. const capability = Promise.withResolvers();
  8419. this.callbackCapabilities[callbackId] = capability;
  8420. try {
  8421. this.comObj.postMessage({
  8422. sourceName: this.sourceName,
  8423. targetName: this.targetName,
  8424. action: actionName,
  8425. callbackId,
  8426. data
  8427. }, transfers);
  8428. } catch (ex) {
  8429. capability.reject(ex);
  8430. }
  8431. return capability.promise;
  8432. }
  8433. sendWithStream(actionName, data, queueingStrategy, transfers) {
  8434. const streamId = this.streamId++,
  8435. sourceName = this.sourceName,
  8436. targetName = this.targetName,
  8437. comObj = this.comObj;
  8438. return new ReadableStream({
  8439. start: controller => {
  8440. const startCapability = Promise.withResolvers();
  8441. this.streamControllers[streamId] = {
  8442. controller,
  8443. startCall: startCapability,
  8444. pullCall: null,
  8445. cancelCall: null,
  8446. isClosed: false
  8447. };
  8448. comObj.postMessage({
  8449. sourceName,
  8450. targetName,
  8451. action: actionName,
  8452. streamId,
  8453. data,
  8454. desiredSize: controller.desiredSize
  8455. }, transfers);
  8456. return startCapability.promise;
  8457. },
  8458. pull: controller => {
  8459. const pullCapability = Promise.withResolvers();
  8460. this.streamControllers[streamId].pullCall = pullCapability;
  8461. comObj.postMessage({
  8462. sourceName,
  8463. targetName,
  8464. stream: StreamKind.PULL,
  8465. streamId,
  8466. desiredSize: controller.desiredSize
  8467. });
  8468. return pullCapability.promise;
  8469. },
  8470. cancel: reason => {
  8471. assert(reason instanceof Error, "cancel must have a valid reason");
  8472. const cancelCapability = Promise.withResolvers();
  8473. this.streamControllers[streamId].cancelCall = cancelCapability;
  8474. this.streamControllers[streamId].isClosed = true;
  8475. comObj.postMessage({
  8476. sourceName,
  8477. targetName,
  8478. stream: StreamKind.CANCEL,
  8479. streamId,
  8480. reason: wrapReason(reason)
  8481. });
  8482. return cancelCapability.promise;
  8483. }
  8484. }, queueingStrategy);
  8485. }
  8486. #createStreamSink(data) {
  8487. const streamId = data.streamId,
  8488. sourceName = this.sourceName,
  8489. targetName = data.sourceName,
  8490. comObj = this.comObj;
  8491. const self = this,
  8492. action = this.actionHandler[data.action];
  8493. const streamSink = {
  8494. enqueue(chunk, size = 1, transfers) {
  8495. if (this.isCancelled) {
  8496. return;
  8497. }
  8498. const lastDesiredSize = this.desiredSize;
  8499. this.desiredSize -= size;
  8500. if (lastDesiredSize > 0 && this.desiredSize <= 0) {
  8501. this.sinkCapability = Promise.withResolvers();
  8502. this.ready = this.sinkCapability.promise;
  8503. }
  8504. comObj.postMessage({
  8505. sourceName,
  8506. targetName,
  8507. stream: StreamKind.ENQUEUE,
  8508. streamId,
  8509. chunk
  8510. }, transfers);
  8511. },
  8512. close() {
  8513. if (this.isCancelled) {
  8514. return;
  8515. }
  8516. this.isCancelled = true;
  8517. comObj.postMessage({
  8518. sourceName,
  8519. targetName,
  8520. stream: StreamKind.CLOSE,
  8521. streamId
  8522. });
  8523. delete self.streamSinks[streamId];
  8524. },
  8525. error(reason) {
  8526. assert(reason instanceof Error, "error must have a valid reason");
  8527. if (this.isCancelled) {
  8528. return;
  8529. }
  8530. this.isCancelled = true;
  8531. comObj.postMessage({
  8532. sourceName,
  8533. targetName,
  8534. stream: StreamKind.ERROR,
  8535. streamId,
  8536. reason: wrapReason(reason)
  8537. });
  8538. },
  8539. sinkCapability: Promise.withResolvers(),
  8540. onPull: null,
  8541. onCancel: null,
  8542. isCancelled: false,
  8543. desiredSize: data.desiredSize,
  8544. ready: null
  8545. };
  8546. streamSink.sinkCapability.resolve();
  8547. streamSink.ready = streamSink.sinkCapability.promise;
  8548. this.streamSinks[streamId] = streamSink;
  8549. new Promise(function (resolve) {
  8550. resolve(action(data.data, streamSink));
  8551. }).then(function () {
  8552. comObj.postMessage({
  8553. sourceName,
  8554. targetName,
  8555. stream: StreamKind.START_COMPLETE,
  8556. streamId,
  8557. success: true
  8558. });
  8559. }, function (reason) {
  8560. comObj.postMessage({
  8561. sourceName,
  8562. targetName,
  8563. stream: StreamKind.START_COMPLETE,
  8564. streamId,
  8565. reason: wrapReason(reason)
  8566. });
  8567. });
  8568. }
  8569. #processStreamMessage(data) {
  8570. const streamId = data.streamId,
  8571. sourceName = this.sourceName,
  8572. targetName = data.sourceName,
  8573. comObj = this.comObj;
  8574. const streamController = this.streamControllers[streamId],
  8575. streamSink = this.streamSinks[streamId];
  8576. switch (data.stream) {
  8577. case StreamKind.START_COMPLETE:
  8578. if (data.success) {
  8579. streamController.startCall.resolve();
  8580. } else {
  8581. streamController.startCall.reject(wrapReason(data.reason));
  8582. }
  8583. break;
  8584. case StreamKind.PULL_COMPLETE:
  8585. if (data.success) {
  8586. streamController.pullCall.resolve();
  8587. } else {
  8588. streamController.pullCall.reject(wrapReason(data.reason));
  8589. }
  8590. break;
  8591. case StreamKind.PULL:
  8592. if (!streamSink) {
  8593. comObj.postMessage({
  8594. sourceName,
  8595. targetName,
  8596. stream: StreamKind.PULL_COMPLETE,
  8597. streamId,
  8598. success: true
  8599. });
  8600. break;
  8601. }
  8602. if (streamSink.desiredSize <= 0 && data.desiredSize > 0) {
  8603. streamSink.sinkCapability.resolve();
  8604. }
  8605. streamSink.desiredSize = data.desiredSize;
  8606. new Promise(function (resolve) {
  8607. resolve(streamSink.onPull?.());
  8608. }).then(function () {
  8609. comObj.postMessage({
  8610. sourceName,
  8611. targetName,
  8612. stream: StreamKind.PULL_COMPLETE,
  8613. streamId,
  8614. success: true
  8615. });
  8616. }, function (reason) {
  8617. comObj.postMessage({
  8618. sourceName,
  8619. targetName,
  8620. stream: StreamKind.PULL_COMPLETE,
  8621. streamId,
  8622. reason: wrapReason(reason)
  8623. });
  8624. });
  8625. break;
  8626. case StreamKind.ENQUEUE:
  8627. assert(streamController, "enqueue should have stream controller");
  8628. if (streamController.isClosed) {
  8629. break;
  8630. }
  8631. streamController.controller.enqueue(data.chunk);
  8632. break;
  8633. case StreamKind.CLOSE:
  8634. assert(streamController, "close should have stream controller");
  8635. if (streamController.isClosed) {
  8636. break;
  8637. }
  8638. streamController.isClosed = true;
  8639. streamController.controller.close();
  8640. this.#deleteStreamController(streamController, streamId);
  8641. break;
  8642. case StreamKind.ERROR:
  8643. assert(streamController, "error should have stream controller");
  8644. streamController.controller.error(wrapReason(data.reason));
  8645. this.#deleteStreamController(streamController, streamId);
  8646. break;
  8647. case StreamKind.CANCEL_COMPLETE:
  8648. if (data.success) {
  8649. streamController.cancelCall.resolve();
  8650. } else {
  8651. streamController.cancelCall.reject(wrapReason(data.reason));
  8652. }
  8653. this.#deleteStreamController(streamController, streamId);
  8654. break;
  8655. case StreamKind.CANCEL:
  8656. if (!streamSink) {
  8657. break;
  8658. }
  8659. new Promise(function (resolve) {
  8660. resolve(streamSink.onCancel?.(wrapReason(data.reason)));
  8661. }).then(function () {
  8662. comObj.postMessage({
  8663. sourceName,
  8664. targetName,
  8665. stream: StreamKind.CANCEL_COMPLETE,
  8666. streamId,
  8667. success: true
  8668. });
  8669. }, function (reason) {
  8670. comObj.postMessage({
  8671. sourceName,
  8672. targetName,
  8673. stream: StreamKind.CANCEL_COMPLETE,
  8674. streamId,
  8675. reason: wrapReason(reason)
  8676. });
  8677. });
  8678. streamSink.sinkCapability.reject(wrapReason(data.reason));
  8679. streamSink.isCancelled = true;
  8680. delete this.streamSinks[streamId];
  8681. break;
  8682. default:
  8683. throw new Error("Unexpected stream case");
  8684. }
  8685. }
  8686. async #deleteStreamController(streamController, streamId) {
  8687. await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]);
  8688. delete this.streamControllers[streamId];
  8689. }
  8690. destroy() {
  8691. this.comObj.removeEventListener("message", this._onComObjOnMessage);
  8692. }
  8693. }
  8694. ;// CONCATENATED MODULE: ./src/display/metadata.js
  8695. class Metadata {
  8696. #metadataMap;
  8697. #data;
  8698. constructor({
  8699. parsedData,
  8700. rawData
  8701. }) {
  8702. this.#metadataMap = parsedData;
  8703. this.#data = rawData;
  8704. }
  8705. getRaw() {
  8706. return this.#data;
  8707. }
  8708. get(name) {
  8709. return this.#metadataMap.get(name) ?? null;
  8710. }
  8711. getAll() {
  8712. return objectFromMap(this.#metadataMap);
  8713. }
  8714. has(name) {
  8715. return this.#metadataMap.has(name);
  8716. }
  8717. }
  8718. ;// CONCATENATED MODULE: ./src/display/optional_content_config.js
  8719. const INTERNAL = Symbol("INTERNAL");
  8720. class OptionalContentGroup {
  8721. #isDisplay = false;
  8722. #isPrint = false;
  8723. #userSet = false;
  8724. #visible = true;
  8725. constructor(renderingIntent, {
  8726. name,
  8727. intent,
  8728. usage
  8729. }) {
  8730. this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY);
  8731. this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
  8732. this.name = name;
  8733. this.intent = intent;
  8734. this.usage = usage;
  8735. }
  8736. get visible() {
  8737. if (this.#userSet) {
  8738. return this.#visible;
  8739. }
  8740. if (!this.#visible) {
  8741. return false;
  8742. }
  8743. const {
  8744. print,
  8745. view
  8746. } = this.usage;
  8747. if (this.#isDisplay) {
  8748. return view?.viewState !== "OFF";
  8749. } else if (this.#isPrint) {
  8750. return print?.printState !== "OFF";
  8751. }
  8752. return true;
  8753. }
  8754. _setVisible(internal, visible, userSet = false) {
  8755. if (internal !== INTERNAL) {
  8756. unreachable("Internal method `_setVisible` called.");
  8757. }
  8758. this.#userSet = userSet;
  8759. this.#visible = visible;
  8760. }
  8761. }
  8762. class OptionalContentConfig {
  8763. #cachedGetHash = null;
  8764. #groups = new Map();
  8765. #initialHash = null;
  8766. #order = null;
  8767. constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) {
  8768. this.renderingIntent = renderingIntent;
  8769. this.name = null;
  8770. this.creator = null;
  8771. if (data === null) {
  8772. return;
  8773. }
  8774. this.name = data.name;
  8775. this.creator = data.creator;
  8776. this.#order = data.order;
  8777. for (const group of data.groups) {
  8778. this.#groups.set(group.id, new OptionalContentGroup(renderingIntent, group));
  8779. }
  8780. if (data.baseState === "OFF") {
  8781. for (const group of this.#groups.values()) {
  8782. group._setVisible(INTERNAL, false);
  8783. }
  8784. }
  8785. for (const on of data.on) {
  8786. this.#groups.get(on)._setVisible(INTERNAL, true);
  8787. }
  8788. for (const off of data.off) {
  8789. this.#groups.get(off)._setVisible(INTERNAL, false);
  8790. }
  8791. this.#initialHash = this.getHash();
  8792. }
  8793. #evaluateVisibilityExpression(array) {
  8794. const length = array.length;
  8795. if (length < 2) {
  8796. return true;
  8797. }
  8798. const operator = array[0];
  8799. for (let i = 1; i < length; i++) {
  8800. const element = array[i];
  8801. let state;
  8802. if (Array.isArray(element)) {
  8803. state = this.#evaluateVisibilityExpression(element);
  8804. } else if (this.#groups.has(element)) {
  8805. state = this.#groups.get(element).visible;
  8806. } else {
  8807. warn(`Optional content group not found: ${element}`);
  8808. return true;
  8809. }
  8810. switch (operator) {
  8811. case "And":
  8812. if (!state) {
  8813. return false;
  8814. }
  8815. break;
  8816. case "Or":
  8817. if (state) {
  8818. return true;
  8819. }
  8820. break;
  8821. case "Not":
  8822. return !state;
  8823. default:
  8824. return true;
  8825. }
  8826. }
  8827. return operator === "And";
  8828. }
  8829. isVisible(group) {
  8830. if (this.#groups.size === 0) {
  8831. return true;
  8832. }
  8833. if (!group) {
  8834. info("Optional content group not defined.");
  8835. return true;
  8836. }
  8837. if (group.type === "OCG") {
  8838. if (!this.#groups.has(group.id)) {
  8839. warn(`Optional content group not found: ${group.id}`);
  8840. return true;
  8841. }
  8842. return this.#groups.get(group.id).visible;
  8843. } else if (group.type === "OCMD") {
  8844. if (group.expression) {
  8845. return this.#evaluateVisibilityExpression(group.expression);
  8846. }
  8847. if (!group.policy || group.policy === "AnyOn") {
  8848. for (const id of group.ids) {
  8849. if (!this.#groups.has(id)) {
  8850. warn(`Optional content group not found: ${id}`);
  8851. return true;
  8852. }
  8853. if (this.#groups.get(id).visible) {
  8854. return true;
  8855. }
  8856. }
  8857. return false;
  8858. } else if (group.policy === "AllOn") {
  8859. for (const id of group.ids) {
  8860. if (!this.#groups.has(id)) {
  8861. warn(`Optional content group not found: ${id}`);
  8862. return true;
  8863. }
  8864. if (!this.#groups.get(id).visible) {
  8865. return false;
  8866. }
  8867. }
  8868. return true;
  8869. } else if (group.policy === "AnyOff") {
  8870. for (const id of group.ids) {
  8871. if (!this.#groups.has(id)) {
  8872. warn(`Optional content group not found: ${id}`);
  8873. return true;
  8874. }
  8875. if (!this.#groups.get(id).visible) {
  8876. return true;
  8877. }
  8878. }
  8879. return false;
  8880. } else if (group.policy === "AllOff") {
  8881. for (const id of group.ids) {
  8882. if (!this.#groups.has(id)) {
  8883. warn(`Optional content group not found: ${id}`);
  8884. return true;
  8885. }
  8886. if (this.#groups.get(id).visible) {
  8887. return false;
  8888. }
  8889. }
  8890. return true;
  8891. }
  8892. warn(`Unknown optional content policy ${group.policy}.`);
  8893. return true;
  8894. }
  8895. warn(`Unknown group type ${group.type}.`);
  8896. return true;
  8897. }
  8898. setVisibility(id, visible = true) {
  8899. const group = this.#groups.get(id);
  8900. if (!group) {
  8901. warn(`Optional content group not found: ${id}`);
  8902. return;
  8903. }
  8904. group._setVisible(INTERNAL, !!visible, true);
  8905. this.#cachedGetHash = null;
  8906. }
  8907. setOCGState({
  8908. state,
  8909. preserveRB
  8910. }) {
  8911. let operator;
  8912. for (const elem of state) {
  8913. switch (elem) {
  8914. case "ON":
  8915. case "OFF":
  8916. case "Toggle":
  8917. operator = elem;
  8918. continue;
  8919. }
  8920. const group = this.#groups.get(elem);
  8921. if (!group) {
  8922. continue;
  8923. }
  8924. switch (operator) {
  8925. case "ON":
  8926. group._setVisible(INTERNAL, true);
  8927. break;
  8928. case "OFF":
  8929. group._setVisible(INTERNAL, false);
  8930. break;
  8931. case "Toggle":
  8932. group._setVisible(INTERNAL, !group.visible);
  8933. break;
  8934. }
  8935. }
  8936. this.#cachedGetHash = null;
  8937. }
  8938. get hasInitialVisibility() {
  8939. return this.#initialHash === null || this.getHash() === this.#initialHash;
  8940. }
  8941. getOrder() {
  8942. if (!this.#groups.size) {
  8943. return null;
  8944. }
  8945. if (this.#order) {
  8946. return this.#order.slice();
  8947. }
  8948. return [...this.#groups.keys()];
  8949. }
  8950. getGroups() {
  8951. return this.#groups.size > 0 ? objectFromMap(this.#groups) : null;
  8952. }
  8953. getGroup(id) {
  8954. return this.#groups.get(id) || null;
  8955. }
  8956. getHash() {
  8957. if (this.#cachedGetHash !== null) {
  8958. return this.#cachedGetHash;
  8959. }
  8960. const hash = new MurmurHash3_64();
  8961. for (const [id, group] of this.#groups) {
  8962. hash.update(`${id}:${group.visible}`);
  8963. }
  8964. return this.#cachedGetHash = hash.hexdigest();
  8965. }
  8966. }
  8967. ;// CONCATENATED MODULE: ./src/display/transport_stream.js
  8968. class PDFDataTransportStream {
  8969. constructor(pdfDataRangeTransport, {
  8970. disableRange = false,
  8971. disableStream = false
  8972. }) {
  8973. assert(pdfDataRangeTransport, 'PDFDataTransportStream - missing required "pdfDataRangeTransport" argument.');
  8974. const {
  8975. length,
  8976. initialData,
  8977. progressiveDone,
  8978. contentDispositionFilename
  8979. } = pdfDataRangeTransport;
  8980. this._queuedChunks = [];
  8981. this._progressiveDone = progressiveDone;
  8982. this._contentDispositionFilename = contentDispositionFilename;
  8983. if (initialData?.length > 0) {
  8984. const buffer = initialData instanceof Uint8Array && initialData.byteLength === initialData.buffer.byteLength ? initialData.buffer : new Uint8Array(initialData).buffer;
  8985. this._queuedChunks.push(buffer);
  8986. }
  8987. this._pdfDataRangeTransport = pdfDataRangeTransport;
  8988. this._isStreamingSupported = !disableStream;
  8989. this._isRangeSupported = !disableRange;
  8990. this._contentLength = length;
  8991. this._fullRequestReader = null;
  8992. this._rangeReaders = [];
  8993. pdfDataRangeTransport.addRangeListener((begin, chunk) => {
  8994. this._onReceiveData({
  8995. begin,
  8996. chunk
  8997. });
  8998. });
  8999. pdfDataRangeTransport.addProgressListener((loaded, total) => {
  9000. this._onProgress({
  9001. loaded,
  9002. total
  9003. });
  9004. });
  9005. pdfDataRangeTransport.addProgressiveReadListener(chunk => {
  9006. this._onReceiveData({
  9007. chunk
  9008. });
  9009. });
  9010. pdfDataRangeTransport.addProgressiveDoneListener(() => {
  9011. this._onProgressiveDone();
  9012. });
  9013. pdfDataRangeTransport.transportReady();
  9014. }
  9015. _onReceiveData({
  9016. begin,
  9017. chunk
  9018. }) {
  9019. const buffer = chunk instanceof Uint8Array && chunk.byteLength === chunk.buffer.byteLength ? chunk.buffer : new Uint8Array(chunk).buffer;
  9020. if (begin === undefined) {
  9021. if (this._fullRequestReader) {
  9022. this._fullRequestReader._enqueue(buffer);
  9023. } else {
  9024. this._queuedChunks.push(buffer);
  9025. }
  9026. } else {
  9027. const found = this._rangeReaders.some(function (rangeReader) {
  9028. if (rangeReader._begin !== begin) {
  9029. return false;
  9030. }
  9031. rangeReader._enqueue(buffer);
  9032. return true;
  9033. });
  9034. assert(found, "_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found.");
  9035. }
  9036. }
  9037. get _progressiveDataLength() {
  9038. return this._fullRequestReader?._loaded ?? 0;
  9039. }
  9040. _onProgress(evt) {
  9041. if (evt.total === undefined) {
  9042. this._rangeReaders[0]?.onProgress?.({
  9043. loaded: evt.loaded
  9044. });
  9045. } else {
  9046. this._fullRequestReader?.onProgress?.({
  9047. loaded: evt.loaded,
  9048. total: evt.total
  9049. });
  9050. }
  9051. }
  9052. _onProgressiveDone() {
  9053. this._fullRequestReader?.progressiveDone();
  9054. this._progressiveDone = true;
  9055. }
  9056. _removeRangeReader(reader) {
  9057. const i = this._rangeReaders.indexOf(reader);
  9058. if (i >= 0) {
  9059. this._rangeReaders.splice(i, 1);
  9060. }
  9061. }
  9062. getFullReader() {
  9063. assert(!this._fullRequestReader, "PDFDataTransportStream.getFullReader can only be called once.");
  9064. const queuedChunks = this._queuedChunks;
  9065. this._queuedChunks = null;
  9066. return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone, this._contentDispositionFilename);
  9067. }
  9068. getRangeReader(begin, end) {
  9069. if (end <= this._progressiveDataLength) {
  9070. return null;
  9071. }
  9072. const reader = new PDFDataTransportStreamRangeReader(this, begin, end);
  9073. this._pdfDataRangeTransport.requestDataRange(begin, end);
  9074. this._rangeReaders.push(reader);
  9075. return reader;
  9076. }
  9077. cancelAllRequests(reason) {
  9078. this._fullRequestReader?.cancel(reason);
  9079. for (const reader of this._rangeReaders.slice(0)) {
  9080. reader.cancel(reason);
  9081. }
  9082. this._pdfDataRangeTransport.abort();
  9083. }
  9084. }
  9085. class PDFDataTransportStreamReader {
  9086. constructor(stream, queuedChunks, progressiveDone = false, contentDispositionFilename = null) {
  9087. this._stream = stream;
  9088. this._done = progressiveDone || false;
  9089. this._filename = isPdfFile(contentDispositionFilename) ? contentDispositionFilename : null;
  9090. this._queuedChunks = queuedChunks || [];
  9091. this._loaded = 0;
  9092. for (const chunk of this._queuedChunks) {
  9093. this._loaded += chunk.byteLength;
  9094. }
  9095. this._requests = [];
  9096. this._headersReady = Promise.resolve();
  9097. stream._fullRequestReader = this;
  9098. this.onProgress = null;
  9099. }
  9100. _enqueue(chunk) {
  9101. if (this._done) {
  9102. return;
  9103. }
  9104. if (this._requests.length > 0) {
  9105. const requestCapability = this._requests.shift();
  9106. requestCapability.resolve({
  9107. value: chunk,
  9108. done: false
  9109. });
  9110. } else {
  9111. this._queuedChunks.push(chunk);
  9112. }
  9113. this._loaded += chunk.byteLength;
  9114. }
  9115. get headersReady() {
  9116. return this._headersReady;
  9117. }
  9118. get filename() {
  9119. return this._filename;
  9120. }
  9121. get isRangeSupported() {
  9122. return this._stream._isRangeSupported;
  9123. }
  9124. get isStreamingSupported() {
  9125. return this._stream._isStreamingSupported;
  9126. }
  9127. get contentLength() {
  9128. return this._stream._contentLength;
  9129. }
  9130. async read() {
  9131. if (this._queuedChunks.length > 0) {
  9132. const chunk = this._queuedChunks.shift();
  9133. return {
  9134. value: chunk,
  9135. done: false
  9136. };
  9137. }
  9138. if (this._done) {
  9139. return {
  9140. value: undefined,
  9141. done: true
  9142. };
  9143. }
  9144. const requestCapability = Promise.withResolvers();
  9145. this._requests.push(requestCapability);
  9146. return requestCapability.promise;
  9147. }
  9148. cancel(reason) {
  9149. this._done = true;
  9150. for (const requestCapability of this._requests) {
  9151. requestCapability.resolve({
  9152. value: undefined,
  9153. done: true
  9154. });
  9155. }
  9156. this._requests.length = 0;
  9157. }
  9158. progressiveDone() {
  9159. if (this._done) {
  9160. return;
  9161. }
  9162. this._done = true;
  9163. }
  9164. }
  9165. class PDFDataTransportStreamRangeReader {
  9166. constructor(stream, begin, end) {
  9167. this._stream = stream;
  9168. this._begin = begin;
  9169. this._end = end;
  9170. this._queuedChunk = null;
  9171. this._requests = [];
  9172. this._done = false;
  9173. this.onProgress = null;
  9174. }
  9175. _enqueue(chunk) {
  9176. if (this._done) {
  9177. return;
  9178. }
  9179. if (this._requests.length === 0) {
  9180. this._queuedChunk = chunk;
  9181. } else {
  9182. const requestsCapability = this._requests.shift();
  9183. requestsCapability.resolve({
  9184. value: chunk,
  9185. done: false
  9186. });
  9187. for (const requestCapability of this._requests) {
  9188. requestCapability.resolve({
  9189. value: undefined,
  9190. done: true
  9191. });
  9192. }
  9193. this._requests.length = 0;
  9194. }
  9195. this._done = true;
  9196. this._stream._removeRangeReader(this);
  9197. }
  9198. get isStreamingSupported() {
  9199. return false;
  9200. }
  9201. async read() {
  9202. if (this._queuedChunk) {
  9203. const chunk = this._queuedChunk;
  9204. this._queuedChunk = null;
  9205. return {
  9206. value: chunk,
  9207. done: false
  9208. };
  9209. }
  9210. if (this._done) {
  9211. return {
  9212. value: undefined,
  9213. done: true
  9214. };
  9215. }
  9216. const requestCapability = Promise.withResolvers();
  9217. this._requests.push(requestCapability);
  9218. return requestCapability.promise;
  9219. }
  9220. cancel(reason) {
  9221. this._done = true;
  9222. for (const requestCapability of this._requests) {
  9223. requestCapability.resolve({
  9224. value: undefined,
  9225. done: true
  9226. });
  9227. }
  9228. this._requests.length = 0;
  9229. this._stream._removeRangeReader(this);
  9230. }
  9231. }
  9232. ;// CONCATENATED MODULE: ./src/display/content_disposition.js
  9233. function getFilenameFromContentDispositionHeader(contentDisposition) {
  9234. let needsEncodingFixup = true;
  9235. let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition);
  9236. if (tmp) {
  9237. tmp = tmp[1];
  9238. let filename = rfc2616unquote(tmp);
  9239. filename = unescape(filename);
  9240. filename = rfc5987decode(filename);
  9241. filename = rfc2047decode(filename);
  9242. return fixupEncoding(filename);
  9243. }
  9244. tmp = rfc2231getparam(contentDisposition);
  9245. if (tmp) {
  9246. const filename = rfc2047decode(tmp);
  9247. return fixupEncoding(filename);
  9248. }
  9249. tmp = toParamRegExp("filename", "i").exec(contentDisposition);
  9250. if (tmp) {
  9251. tmp = tmp[1];
  9252. let filename = rfc2616unquote(tmp);
  9253. filename = rfc2047decode(filename);
  9254. return fixupEncoding(filename);
  9255. }
  9256. function toParamRegExp(attributePattern, flags) {
  9257. return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags);
  9258. }
  9259. function textdecode(encoding, value) {
  9260. if (encoding) {
  9261. if (!/^[\x00-\xFF]+$/.test(value)) {
  9262. return value;
  9263. }
  9264. try {
  9265. const decoder = new TextDecoder(encoding, {
  9266. fatal: true
  9267. });
  9268. const buffer = stringToBytes(value);
  9269. value = decoder.decode(buffer);
  9270. needsEncodingFixup = false;
  9271. } catch {}
  9272. }
  9273. return value;
  9274. }
  9275. function fixupEncoding(value) {
  9276. if (needsEncodingFixup && /[\x80-\xff]/.test(value)) {
  9277. value = textdecode("utf-8", value);
  9278. if (needsEncodingFixup) {
  9279. value = textdecode("iso-8859-1", value);
  9280. }
  9281. }
  9282. return value;
  9283. }
  9284. function rfc2231getparam(contentDispositionStr) {
  9285. const matches = [];
  9286. let match;
  9287. const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig");
  9288. while ((match = iter.exec(contentDispositionStr)) !== null) {
  9289. let [, n, quot, part] = match;
  9290. n = parseInt(n, 10);
  9291. if (n in matches) {
  9292. if (n === 0) {
  9293. break;
  9294. }
  9295. continue;
  9296. }
  9297. matches[n] = [quot, part];
  9298. }
  9299. const parts = [];
  9300. for (let n = 0; n < matches.length; ++n) {
  9301. if (!(n in matches)) {
  9302. break;
  9303. }
  9304. let [quot, part] = matches[n];
  9305. part = rfc2616unquote(part);
  9306. if (quot) {
  9307. part = unescape(part);
  9308. if (n === 0) {
  9309. part = rfc5987decode(part);
  9310. }
  9311. }
  9312. parts.push(part);
  9313. }
  9314. return parts.join("");
  9315. }
  9316. function rfc2616unquote(value) {
  9317. if (value.startsWith('"')) {
  9318. const parts = value.slice(1).split('\\"');
  9319. for (let i = 0; i < parts.length; ++i) {
  9320. const quotindex = parts[i].indexOf('"');
  9321. if (quotindex !== -1) {
  9322. parts[i] = parts[i].slice(0, quotindex);
  9323. parts.length = i + 1;
  9324. }
  9325. parts[i] = parts[i].replaceAll(/\\(.)/g, "$1");
  9326. }
  9327. value = parts.join('"');
  9328. }
  9329. return value;
  9330. }
  9331. function rfc5987decode(extvalue) {
  9332. const encodingend = extvalue.indexOf("'");
  9333. if (encodingend === -1) {
  9334. return extvalue;
  9335. }
  9336. const encoding = extvalue.slice(0, encodingend);
  9337. const langvalue = extvalue.slice(encodingend + 1);
  9338. const value = langvalue.replace(/^[^']*'/, "");
  9339. return textdecode(encoding, value);
  9340. }
  9341. function rfc2047decode(value) {
  9342. if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) {
  9343. return value;
  9344. }
  9345. return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) {
  9346. if (encoding === "q" || encoding === "Q") {
  9347. text = text.replaceAll("_", " ");
  9348. text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) {
  9349. return String.fromCharCode(parseInt(hex, 16));
  9350. });
  9351. return textdecode(charset, text);
  9352. }
  9353. try {
  9354. text = atob(text);
  9355. } catch {}
  9356. return textdecode(charset, text);
  9357. });
  9358. }
  9359. return "";
  9360. }
  9361. ;// CONCATENATED MODULE: ./src/display/network_utils.js
  9362. function validateRangeRequestCapabilities({
  9363. getResponseHeader,
  9364. isHttp,
  9365. rangeChunkSize,
  9366. disableRange
  9367. }) {
  9368. const returnValues = {
  9369. allowRangeRequests: false,
  9370. suggestedLength: undefined
  9371. };
  9372. const length = parseInt(getResponseHeader("Content-Length"), 10);
  9373. if (!Number.isInteger(length)) {
  9374. return returnValues;
  9375. }
  9376. returnValues.suggestedLength = length;
  9377. if (length <= 2 * rangeChunkSize) {
  9378. return returnValues;
  9379. }
  9380. if (disableRange || !isHttp) {
  9381. return returnValues;
  9382. }
  9383. if (getResponseHeader("Accept-Ranges") !== "bytes") {
  9384. return returnValues;
  9385. }
  9386. const contentEncoding = getResponseHeader("Content-Encoding") || "identity";
  9387. if (contentEncoding !== "identity") {
  9388. return returnValues;
  9389. }
  9390. returnValues.allowRangeRequests = true;
  9391. return returnValues;
  9392. }
  9393. function extractFilenameFromHeader(getResponseHeader) {
  9394. const contentDisposition = getResponseHeader("Content-Disposition");
  9395. if (contentDisposition) {
  9396. let filename = getFilenameFromContentDispositionHeader(contentDisposition);
  9397. if (filename.includes("%")) {
  9398. try {
  9399. filename = decodeURIComponent(filename);
  9400. } catch {}
  9401. }
  9402. if (isPdfFile(filename)) {
  9403. return filename;
  9404. }
  9405. }
  9406. return null;
  9407. }
  9408. function createResponseStatusError(status, url) {
  9409. if (status === 404 || status === 0 && url.startsWith("file:")) {
  9410. return new MissingPDFException('Missing PDF "' + url + '".');
  9411. }
  9412. return new UnexpectedResponseException(`Unexpected server response (${status}) while retrieving PDF "${url}".`, status);
  9413. }
  9414. function validateResponseStatus(status) {
  9415. return status === 200 || status === 206;
  9416. }
  9417. ;// CONCATENATED MODULE: ./src/display/fetch_stream.js
  9418. function createFetchOptions(headers, withCredentials, abortController) {
  9419. return {
  9420. method: "GET",
  9421. headers,
  9422. signal: abortController.signal,
  9423. mode: "cors",
  9424. credentials: withCredentials ? "include" : "same-origin",
  9425. redirect: "follow"
  9426. };
  9427. }
  9428. function createHeaders(httpHeaders) {
  9429. const headers = new Headers();
  9430. for (const property in httpHeaders) {
  9431. const value = httpHeaders[property];
  9432. if (value === undefined) {
  9433. continue;
  9434. }
  9435. headers.append(property, value);
  9436. }
  9437. return headers;
  9438. }
  9439. function getArrayBuffer(val) {
  9440. if (val instanceof Uint8Array) {
  9441. return val.buffer;
  9442. }
  9443. if (val instanceof ArrayBuffer) {
  9444. return val;
  9445. }
  9446. warn(`getArrayBuffer - unexpected data format: ${val}`);
  9447. return new Uint8Array(val).buffer;
  9448. }
  9449. class PDFFetchStream {
  9450. constructor(source) {
  9451. this.source = source;
  9452. this.isHttp = /^https?:/i.test(source.url);
  9453. this.httpHeaders = this.isHttp && source.httpHeaders || {};
  9454. this._fullRequestReader = null;
  9455. this._rangeRequestReaders = [];
  9456. }
  9457. get _progressiveDataLength() {
  9458. return this._fullRequestReader?._loaded ?? 0;
  9459. }
  9460. getFullReader() {
  9461. assert(!this._fullRequestReader, "PDFFetchStream.getFullReader can only be called once.");
  9462. this._fullRequestReader = new PDFFetchStreamReader(this);
  9463. return this._fullRequestReader;
  9464. }
  9465. getRangeReader(begin, end) {
  9466. if (end <= this._progressiveDataLength) {
  9467. return null;
  9468. }
  9469. const reader = new PDFFetchStreamRangeReader(this, begin, end);
  9470. this._rangeRequestReaders.push(reader);
  9471. return reader;
  9472. }
  9473. cancelAllRequests(reason) {
  9474. this._fullRequestReader?.cancel(reason);
  9475. for (const reader of this._rangeRequestReaders.slice(0)) {
  9476. reader.cancel(reason);
  9477. }
  9478. }
  9479. }
  9480. class PDFFetchStreamReader {
  9481. constructor(stream) {
  9482. this._stream = stream;
  9483. this._reader = null;
  9484. this._loaded = 0;
  9485. this._filename = null;
  9486. const source = stream.source;
  9487. this._withCredentials = source.withCredentials || false;
  9488. this._contentLength = source.length;
  9489. this._headersCapability = Promise.withResolvers();
  9490. this._disableRange = source.disableRange || false;
  9491. this._rangeChunkSize = source.rangeChunkSize;
  9492. if (!this._rangeChunkSize && !this._disableRange) {
  9493. this._disableRange = true;
  9494. }
  9495. this._abortController = new AbortController();
  9496. this._isStreamingSupported = !source.disableStream;
  9497. this._isRangeSupported = !source.disableRange;
  9498. this._headers = createHeaders(this._stream.httpHeaders);
  9499. const url = source.url;
  9500. fetch(url, createFetchOptions(this._headers, this._withCredentials, this._abortController)).then(response => {
  9501. if (!validateResponseStatus(response.status)) {
  9502. throw createResponseStatusError(response.status, url);
  9503. }
  9504. this._reader = response.body.getReader();
  9505. this._headersCapability.resolve();
  9506. const getResponseHeader = name => response.headers.get(name);
  9507. const {
  9508. allowRangeRequests,
  9509. suggestedLength
  9510. } = validateRangeRequestCapabilities({
  9511. getResponseHeader,
  9512. isHttp: this._stream.isHttp,
  9513. rangeChunkSize: this._rangeChunkSize,
  9514. disableRange: this._disableRange
  9515. });
  9516. this._isRangeSupported = allowRangeRequests;
  9517. this._contentLength = suggestedLength || this._contentLength;
  9518. this._filename = extractFilenameFromHeader(getResponseHeader);
  9519. if (!this._isStreamingSupported && this._isRangeSupported) {
  9520. this.cancel(new AbortException("Streaming is disabled."));
  9521. }
  9522. }).catch(this._headersCapability.reject);
  9523. this.onProgress = null;
  9524. }
  9525. get headersReady() {
  9526. return this._headersCapability.promise;
  9527. }
  9528. get filename() {
  9529. return this._filename;
  9530. }
  9531. get contentLength() {
  9532. return this._contentLength;
  9533. }
  9534. get isRangeSupported() {
  9535. return this._isRangeSupported;
  9536. }
  9537. get isStreamingSupported() {
  9538. return this._isStreamingSupported;
  9539. }
  9540. async read() {
  9541. await this._headersCapability.promise;
  9542. const {
  9543. value,
  9544. done
  9545. } = await this._reader.read();
  9546. if (done) {
  9547. return {
  9548. value,
  9549. done
  9550. };
  9551. }
  9552. this._loaded += value.byteLength;
  9553. this.onProgress?.({
  9554. loaded: this._loaded,
  9555. total: this._contentLength
  9556. });
  9557. return {
  9558. value: getArrayBuffer(value),
  9559. done: false
  9560. };
  9561. }
  9562. cancel(reason) {
  9563. this._reader?.cancel(reason);
  9564. this._abortController.abort();
  9565. }
  9566. }
  9567. class PDFFetchStreamRangeReader {
  9568. constructor(stream, begin, end) {
  9569. this._stream = stream;
  9570. this._reader = null;
  9571. this._loaded = 0;
  9572. const source = stream.source;
  9573. this._withCredentials = source.withCredentials || false;
  9574. this._readCapability = Promise.withResolvers();
  9575. this._isStreamingSupported = !source.disableStream;
  9576. this._abortController = new AbortController();
  9577. this._headers = createHeaders(this._stream.httpHeaders);
  9578. this._headers.append("Range", `bytes=${begin}-${end - 1}`);
  9579. const url = source.url;
  9580. fetch(url, createFetchOptions(this._headers, this._withCredentials, this._abortController)).then(response => {
  9581. if (!validateResponseStatus(response.status)) {
  9582. throw createResponseStatusError(response.status, url);
  9583. }
  9584. this._readCapability.resolve();
  9585. this._reader = response.body.getReader();
  9586. }).catch(this._readCapability.reject);
  9587. this.onProgress = null;
  9588. }
  9589. get isStreamingSupported() {
  9590. return this._isStreamingSupported;
  9591. }
  9592. async read() {
  9593. await this._readCapability.promise;
  9594. const {
  9595. value,
  9596. done
  9597. } = await this._reader.read();
  9598. if (done) {
  9599. return {
  9600. value,
  9601. done
  9602. };
  9603. }
  9604. this._loaded += value.byteLength;
  9605. this.onProgress?.({
  9606. loaded: this._loaded
  9607. });
  9608. return {
  9609. value: getArrayBuffer(value),
  9610. done: false
  9611. };
  9612. }
  9613. cancel(reason) {
  9614. this._reader?.cancel(reason);
  9615. this._abortController.abort();
  9616. }
  9617. }
  9618. ;// CONCATENATED MODULE: ./src/display/network.js
  9619. const OK_RESPONSE = 200;
  9620. const PARTIAL_CONTENT_RESPONSE = 206;
  9621. function network_getArrayBuffer(xhr) {
  9622. const data = xhr.response;
  9623. if (typeof data !== "string") {
  9624. return data;
  9625. }
  9626. return stringToBytes(data).buffer;
  9627. }
  9628. class NetworkManager {
  9629. constructor(url, args = {}) {
  9630. this.url = url;
  9631. this.isHttp = /^https?:/i.test(url);
  9632. this.httpHeaders = this.isHttp && args.httpHeaders || Object.create(null);
  9633. this.withCredentials = args.withCredentials || false;
  9634. this.currXhrId = 0;
  9635. this.pendingRequests = Object.create(null);
  9636. }
  9637. requestRange(begin, end, listeners) {
  9638. const args = {
  9639. begin,
  9640. end
  9641. };
  9642. for (const prop in listeners) {
  9643. args[prop] = listeners[prop];
  9644. }
  9645. return this.request(args);
  9646. }
  9647. requestFull(listeners) {
  9648. return this.request(listeners);
  9649. }
  9650. request(args) {
  9651. const xhr = new XMLHttpRequest();
  9652. const xhrId = this.currXhrId++;
  9653. const pendingRequest = this.pendingRequests[xhrId] = {
  9654. xhr
  9655. };
  9656. xhr.open("GET", this.url);
  9657. xhr.withCredentials = this.withCredentials;
  9658. for (const property in this.httpHeaders) {
  9659. const value = this.httpHeaders[property];
  9660. if (value === undefined) {
  9661. continue;
  9662. }
  9663. xhr.setRequestHeader(property, value);
  9664. }
  9665. if (this.isHttp && "begin" in args && "end" in args) {
  9666. xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
  9667. pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE;
  9668. } else {
  9669. pendingRequest.expectedStatus = OK_RESPONSE;
  9670. }
  9671. xhr.responseType = "arraybuffer";
  9672. if (args.onError) {
  9673. xhr.onerror = function (evt) {
  9674. args.onError(xhr.status);
  9675. };
  9676. }
  9677. xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
  9678. xhr.onprogress = this.onProgress.bind(this, xhrId);
  9679. pendingRequest.onHeadersReceived = args.onHeadersReceived;
  9680. pendingRequest.onDone = args.onDone;
  9681. pendingRequest.onError = args.onError;
  9682. pendingRequest.onProgress = args.onProgress;
  9683. xhr.send(null);
  9684. return xhrId;
  9685. }
  9686. onProgress(xhrId, evt) {
  9687. const pendingRequest = this.pendingRequests[xhrId];
  9688. if (!pendingRequest) {
  9689. return;
  9690. }
  9691. pendingRequest.onProgress?.(evt);
  9692. }
  9693. onStateChange(xhrId, evt) {
  9694. const pendingRequest = this.pendingRequests[xhrId];
  9695. if (!pendingRequest) {
  9696. return;
  9697. }
  9698. const xhr = pendingRequest.xhr;
  9699. if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
  9700. pendingRequest.onHeadersReceived();
  9701. delete pendingRequest.onHeadersReceived;
  9702. }
  9703. if (xhr.readyState !== 4) {
  9704. return;
  9705. }
  9706. if (!(xhrId in this.pendingRequests)) {
  9707. return;
  9708. }
  9709. delete this.pendingRequests[xhrId];
  9710. if (xhr.status === 0 && this.isHttp) {
  9711. pendingRequest.onError?.(xhr.status);
  9712. return;
  9713. }
  9714. const xhrStatus = xhr.status || OK_RESPONSE;
  9715. const ok_response_on_range_request = xhrStatus === OK_RESPONSE && pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
  9716. if (!ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus) {
  9717. pendingRequest.onError?.(xhr.status);
  9718. return;
  9719. }
  9720. const chunk = network_getArrayBuffer(xhr);
  9721. if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
  9722. const rangeHeader = xhr.getResponseHeader("Content-Range");
  9723. const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
  9724. pendingRequest.onDone({
  9725. begin: parseInt(matches[1], 10),
  9726. chunk
  9727. });
  9728. } else if (chunk) {
  9729. pendingRequest.onDone({
  9730. begin: 0,
  9731. chunk
  9732. });
  9733. } else {
  9734. pendingRequest.onError?.(xhr.status);
  9735. }
  9736. }
  9737. getRequestXhr(xhrId) {
  9738. return this.pendingRequests[xhrId].xhr;
  9739. }
  9740. isPendingRequest(xhrId) {
  9741. return xhrId in this.pendingRequests;
  9742. }
  9743. abortRequest(xhrId) {
  9744. const xhr = this.pendingRequests[xhrId].xhr;
  9745. delete this.pendingRequests[xhrId];
  9746. xhr.abort();
  9747. }
  9748. }
  9749. class PDFNetworkStream {
  9750. constructor(source) {
  9751. this._source = source;
  9752. this._manager = new NetworkManager(source.url, {
  9753. httpHeaders: source.httpHeaders,
  9754. withCredentials: source.withCredentials
  9755. });
  9756. this._rangeChunkSize = source.rangeChunkSize;
  9757. this._fullRequestReader = null;
  9758. this._rangeRequestReaders = [];
  9759. }
  9760. _onRangeRequestReaderClosed(reader) {
  9761. const i = this._rangeRequestReaders.indexOf(reader);
  9762. if (i >= 0) {
  9763. this._rangeRequestReaders.splice(i, 1);
  9764. }
  9765. }
  9766. getFullReader() {
  9767. assert(!this._fullRequestReader, "PDFNetworkStream.getFullReader can only be called once.");
  9768. this._fullRequestReader = new PDFNetworkStreamFullRequestReader(this._manager, this._source);
  9769. return this._fullRequestReader;
  9770. }
  9771. getRangeReader(begin, end) {
  9772. const reader = new PDFNetworkStreamRangeRequestReader(this._manager, begin, end);
  9773. reader.onClosed = this._onRangeRequestReaderClosed.bind(this);
  9774. this._rangeRequestReaders.push(reader);
  9775. return reader;
  9776. }
  9777. cancelAllRequests(reason) {
  9778. this._fullRequestReader?.cancel(reason);
  9779. for (const reader of this._rangeRequestReaders.slice(0)) {
  9780. reader.cancel(reason);
  9781. }
  9782. }
  9783. }
  9784. class PDFNetworkStreamFullRequestReader {
  9785. constructor(manager, source) {
  9786. this._manager = manager;
  9787. const args = {
  9788. onHeadersReceived: this._onHeadersReceived.bind(this),
  9789. onDone: this._onDone.bind(this),
  9790. onError: this._onError.bind(this),
  9791. onProgress: this._onProgress.bind(this)
  9792. };
  9793. this._url = source.url;
  9794. this._fullRequestId = manager.requestFull(args);
  9795. this._headersReceivedCapability = Promise.withResolvers();
  9796. this._disableRange = source.disableRange || false;
  9797. this._contentLength = source.length;
  9798. this._rangeChunkSize = source.rangeChunkSize;
  9799. if (!this._rangeChunkSize && !this._disableRange) {
  9800. this._disableRange = true;
  9801. }
  9802. this._isStreamingSupported = false;
  9803. this._isRangeSupported = false;
  9804. this._cachedChunks = [];
  9805. this._requests = [];
  9806. this._done = false;
  9807. this._storedError = undefined;
  9808. this._filename = null;
  9809. this.onProgress = null;
  9810. }
  9811. _onHeadersReceived() {
  9812. const fullRequestXhrId = this._fullRequestId;
  9813. const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
  9814. const getResponseHeader = name => fullRequestXhr.getResponseHeader(name);
  9815. const {
  9816. allowRangeRequests,
  9817. suggestedLength
  9818. } = validateRangeRequestCapabilities({
  9819. getResponseHeader,
  9820. isHttp: this._manager.isHttp,
  9821. rangeChunkSize: this._rangeChunkSize,
  9822. disableRange: this._disableRange
  9823. });
  9824. if (allowRangeRequests) {
  9825. this._isRangeSupported = true;
  9826. }
  9827. this._contentLength = suggestedLength || this._contentLength;
  9828. this._filename = extractFilenameFromHeader(getResponseHeader);
  9829. if (this._isRangeSupported) {
  9830. this._manager.abortRequest(fullRequestXhrId);
  9831. }
  9832. this._headersReceivedCapability.resolve();
  9833. }
  9834. _onDone(data) {
  9835. if (data) {
  9836. if (this._requests.length > 0) {
  9837. const requestCapability = this._requests.shift();
  9838. requestCapability.resolve({
  9839. value: data.chunk,
  9840. done: false
  9841. });
  9842. } else {
  9843. this._cachedChunks.push(data.chunk);
  9844. }
  9845. }
  9846. this._done = true;
  9847. if (this._cachedChunks.length > 0) {
  9848. return;
  9849. }
  9850. for (const requestCapability of this._requests) {
  9851. requestCapability.resolve({
  9852. value: undefined,
  9853. done: true
  9854. });
  9855. }
  9856. this._requests.length = 0;
  9857. }
  9858. _onError(status) {
  9859. this._storedError = createResponseStatusError(status, this._url);
  9860. this._headersReceivedCapability.reject(this._storedError);
  9861. for (const requestCapability of this._requests) {
  9862. requestCapability.reject(this._storedError);
  9863. }
  9864. this._requests.length = 0;
  9865. this._cachedChunks.length = 0;
  9866. }
  9867. _onProgress(evt) {
  9868. this.onProgress?.({
  9869. loaded: evt.loaded,
  9870. total: evt.lengthComputable ? evt.total : this._contentLength
  9871. });
  9872. }
  9873. get filename() {
  9874. return this._filename;
  9875. }
  9876. get isRangeSupported() {
  9877. return this._isRangeSupported;
  9878. }
  9879. get isStreamingSupported() {
  9880. return this._isStreamingSupported;
  9881. }
  9882. get contentLength() {
  9883. return this._contentLength;
  9884. }
  9885. get headersReady() {
  9886. return this._headersReceivedCapability.promise;
  9887. }
  9888. async read() {
  9889. if (this._storedError) {
  9890. throw this._storedError;
  9891. }
  9892. if (this._cachedChunks.length > 0) {
  9893. const chunk = this._cachedChunks.shift();
  9894. return {
  9895. value: chunk,
  9896. done: false
  9897. };
  9898. }
  9899. if (this._done) {
  9900. return {
  9901. value: undefined,
  9902. done: true
  9903. };
  9904. }
  9905. const requestCapability = Promise.withResolvers();
  9906. this._requests.push(requestCapability);
  9907. return requestCapability.promise;
  9908. }
  9909. cancel(reason) {
  9910. this._done = true;
  9911. this._headersReceivedCapability.reject(reason);
  9912. for (const requestCapability of this._requests) {
  9913. requestCapability.resolve({
  9914. value: undefined,
  9915. done: true
  9916. });
  9917. }
  9918. this._requests.length = 0;
  9919. if (this._manager.isPendingRequest(this._fullRequestId)) {
  9920. this._manager.abortRequest(this._fullRequestId);
  9921. }
  9922. this._fullRequestReader = null;
  9923. }
  9924. }
  9925. class PDFNetworkStreamRangeRequestReader {
  9926. constructor(manager, begin, end) {
  9927. this._manager = manager;
  9928. const args = {
  9929. onDone: this._onDone.bind(this),
  9930. onError: this._onError.bind(this),
  9931. onProgress: this._onProgress.bind(this)
  9932. };
  9933. this._url = manager.url;
  9934. this._requestId = manager.requestRange(begin, end, args);
  9935. this._requests = [];
  9936. this._queuedChunk = null;
  9937. this._done = false;
  9938. this._storedError = undefined;
  9939. this.onProgress = null;
  9940. this.onClosed = null;
  9941. }
  9942. _close() {
  9943. this.onClosed?.(this);
  9944. }
  9945. _onDone(data) {
  9946. const chunk = data.chunk;
  9947. if (this._requests.length > 0) {
  9948. const requestCapability = this._requests.shift();
  9949. requestCapability.resolve({
  9950. value: chunk,
  9951. done: false
  9952. });
  9953. } else {
  9954. this._queuedChunk = chunk;
  9955. }
  9956. this._done = true;
  9957. for (const requestCapability of this._requests) {
  9958. requestCapability.resolve({
  9959. value: undefined,
  9960. done: true
  9961. });
  9962. }
  9963. this._requests.length = 0;
  9964. this._close();
  9965. }
  9966. _onError(status) {
  9967. this._storedError = createResponseStatusError(status, this._url);
  9968. for (const requestCapability of this._requests) {
  9969. requestCapability.reject(this._storedError);
  9970. }
  9971. this._requests.length = 0;
  9972. this._queuedChunk = null;
  9973. }
  9974. _onProgress(evt) {
  9975. if (!this.isStreamingSupported) {
  9976. this.onProgress?.({
  9977. loaded: evt.loaded
  9978. });
  9979. }
  9980. }
  9981. get isStreamingSupported() {
  9982. return false;
  9983. }
  9984. async read() {
  9985. if (this._storedError) {
  9986. throw this._storedError;
  9987. }
  9988. if (this._queuedChunk !== null) {
  9989. const chunk = this._queuedChunk;
  9990. this._queuedChunk = null;
  9991. return {
  9992. value: chunk,
  9993. done: false
  9994. };
  9995. }
  9996. if (this._done) {
  9997. return {
  9998. value: undefined,
  9999. done: true
  10000. };
  10001. }
  10002. const requestCapability = Promise.withResolvers();
  10003. this._requests.push(requestCapability);
  10004. return requestCapability.promise;
  10005. }
  10006. cancel(reason) {
  10007. this._done = true;
  10008. for (const requestCapability of this._requests) {
  10009. requestCapability.resolve({
  10010. value: undefined,
  10011. done: true
  10012. });
  10013. }
  10014. this._requests.length = 0;
  10015. if (this._manager.isPendingRequest(this._requestId)) {
  10016. this._manager.abortRequest(this._requestId);
  10017. }
  10018. this._close();
  10019. }
  10020. }
  10021. ;// CONCATENATED MODULE: ./src/display/node_stream.js
  10022. const fileUriRegex = /^file:\/\/\/[a-zA-Z]:\//;
  10023. function parseUrl(sourceUrl) {
  10024. const url = NodePackages.get("url");
  10025. const parsedUrl = url.parse(sourceUrl);
  10026. if (parsedUrl.protocol === "file:" || parsedUrl.host) {
  10027. return parsedUrl;
  10028. }
  10029. if (/^[a-z]:[/\\]/i.test(sourceUrl)) {
  10030. return url.parse(`file:///${sourceUrl}`);
  10031. }
  10032. if (!parsedUrl.host) {
  10033. parsedUrl.protocol = "file:";
  10034. }
  10035. return parsedUrl;
  10036. }
  10037. class PDFNodeStream {
  10038. constructor(source) {
  10039. this.source = source;
  10040. this.url = parseUrl(source.url);
  10041. this.isHttp = this.url.protocol === "http:" || this.url.protocol === "https:";
  10042. this.isFsUrl = this.url.protocol === "file:";
  10043. this.httpHeaders = this.isHttp && source.httpHeaders || {};
  10044. this._fullRequestReader = null;
  10045. this._rangeRequestReaders = [];
  10046. }
  10047. get _progressiveDataLength() {
  10048. return this._fullRequestReader?._loaded ?? 0;
  10049. }
  10050. getFullReader() {
  10051. assert(!this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once.");
  10052. this._fullRequestReader = this.isFsUrl ? new PDFNodeStreamFsFullReader(this) : new PDFNodeStreamFullReader(this);
  10053. return this._fullRequestReader;
  10054. }
  10055. getRangeReader(start, end) {
  10056. if (end <= this._progressiveDataLength) {
  10057. return null;
  10058. }
  10059. const rangeReader = this.isFsUrl ? new PDFNodeStreamFsRangeReader(this, start, end) : new PDFNodeStreamRangeReader(this, start, end);
  10060. this._rangeRequestReaders.push(rangeReader);
  10061. return rangeReader;
  10062. }
  10063. cancelAllRequests(reason) {
  10064. this._fullRequestReader?.cancel(reason);
  10065. for (const reader of this._rangeRequestReaders.slice(0)) {
  10066. reader.cancel(reason);
  10067. }
  10068. }
  10069. }
  10070. class BaseFullReader {
  10071. constructor(stream) {
  10072. this._url = stream.url;
  10073. this._done = false;
  10074. this._storedError = null;
  10075. this.onProgress = null;
  10076. const source = stream.source;
  10077. this._contentLength = source.length;
  10078. this._loaded = 0;
  10079. this._filename = null;
  10080. this._disableRange = source.disableRange || false;
  10081. this._rangeChunkSize = source.rangeChunkSize;
  10082. if (!this._rangeChunkSize && !this._disableRange) {
  10083. this._disableRange = true;
  10084. }
  10085. this._isStreamingSupported = !source.disableStream;
  10086. this._isRangeSupported = !source.disableRange;
  10087. this._readableStream = null;
  10088. this._readCapability = Promise.withResolvers();
  10089. this._headersCapability = Promise.withResolvers();
  10090. }
  10091. get headersReady() {
  10092. return this._headersCapability.promise;
  10093. }
  10094. get filename() {
  10095. return this._filename;
  10096. }
  10097. get contentLength() {
  10098. return this._contentLength;
  10099. }
  10100. get isRangeSupported() {
  10101. return this._isRangeSupported;
  10102. }
  10103. get isStreamingSupported() {
  10104. return this._isStreamingSupported;
  10105. }
  10106. async read() {
  10107. await this._readCapability.promise;
  10108. if (this._done) {
  10109. return {
  10110. value: undefined,
  10111. done: true
  10112. };
  10113. }
  10114. if (this._storedError) {
  10115. throw this._storedError;
  10116. }
  10117. const chunk = this._readableStream.read();
  10118. if (chunk === null) {
  10119. this._readCapability = Promise.withResolvers();
  10120. return this.read();
  10121. }
  10122. this._loaded += chunk.length;
  10123. this.onProgress?.({
  10124. loaded: this._loaded,
  10125. total: this._contentLength
  10126. });
  10127. const buffer = new Uint8Array(chunk).buffer;
  10128. return {
  10129. value: buffer,
  10130. done: false
  10131. };
  10132. }
  10133. cancel(reason) {
  10134. if (!this._readableStream) {
  10135. this._error(reason);
  10136. return;
  10137. }
  10138. this._readableStream.destroy(reason);
  10139. }
  10140. _error(reason) {
  10141. this._storedError = reason;
  10142. this._readCapability.resolve();
  10143. }
  10144. _setReadableStream(readableStream) {
  10145. this._readableStream = readableStream;
  10146. readableStream.on("readable", () => {
  10147. this._readCapability.resolve();
  10148. });
  10149. readableStream.on("end", () => {
  10150. readableStream.destroy();
  10151. this._done = true;
  10152. this._readCapability.resolve();
  10153. });
  10154. readableStream.on("error", reason => {
  10155. this._error(reason);
  10156. });
  10157. if (!this._isStreamingSupported && this._isRangeSupported) {
  10158. this._error(new AbortException("streaming is disabled"));
  10159. }
  10160. if (this._storedError) {
  10161. this._readableStream.destroy(this._storedError);
  10162. }
  10163. }
  10164. }
  10165. class BaseRangeReader {
  10166. constructor(stream) {
  10167. this._url = stream.url;
  10168. this._done = false;
  10169. this._storedError = null;
  10170. this.onProgress = null;
  10171. this._loaded = 0;
  10172. this._readableStream = null;
  10173. this._readCapability = Promise.withResolvers();
  10174. const source = stream.source;
  10175. this._isStreamingSupported = !source.disableStream;
  10176. }
  10177. get isStreamingSupported() {
  10178. return this._isStreamingSupported;
  10179. }
  10180. async read() {
  10181. await this._readCapability.promise;
  10182. if (this._done) {
  10183. return {
  10184. value: undefined,
  10185. done: true
  10186. };
  10187. }
  10188. if (this._storedError) {
  10189. throw this._storedError;
  10190. }
  10191. const chunk = this._readableStream.read();
  10192. if (chunk === null) {
  10193. this._readCapability = Promise.withResolvers();
  10194. return this.read();
  10195. }
  10196. this._loaded += chunk.length;
  10197. this.onProgress?.({
  10198. loaded: this._loaded
  10199. });
  10200. const buffer = new Uint8Array(chunk).buffer;
  10201. return {
  10202. value: buffer,
  10203. done: false
  10204. };
  10205. }
  10206. cancel(reason) {
  10207. if (!this._readableStream) {
  10208. this._error(reason);
  10209. return;
  10210. }
  10211. this._readableStream.destroy(reason);
  10212. }
  10213. _error(reason) {
  10214. this._storedError = reason;
  10215. this._readCapability.resolve();
  10216. }
  10217. _setReadableStream(readableStream) {
  10218. this._readableStream = readableStream;
  10219. readableStream.on("readable", () => {
  10220. this._readCapability.resolve();
  10221. });
  10222. readableStream.on("end", () => {
  10223. readableStream.destroy();
  10224. this._done = true;
  10225. this._readCapability.resolve();
  10226. });
  10227. readableStream.on("error", reason => {
  10228. this._error(reason);
  10229. });
  10230. if (this._storedError) {
  10231. this._readableStream.destroy(this._storedError);
  10232. }
  10233. }
  10234. }
  10235. function createRequestOptions(parsedUrl, headers) {
  10236. return {
  10237. protocol: parsedUrl.protocol,
  10238. auth: parsedUrl.auth,
  10239. host: parsedUrl.hostname,
  10240. port: parsedUrl.port,
  10241. path: parsedUrl.path,
  10242. method: "GET",
  10243. headers
  10244. };
  10245. }
  10246. class PDFNodeStreamFullReader extends BaseFullReader {
  10247. constructor(stream) {
  10248. super(stream);
  10249. const handleResponse = response => {
  10250. if (response.statusCode === 404) {
  10251. const error = new MissingPDFException(`Missing PDF "${this._url}".`);
  10252. this._storedError = error;
  10253. this._headersCapability.reject(error);
  10254. return;
  10255. }
  10256. this._headersCapability.resolve();
  10257. this._setReadableStream(response);
  10258. const getResponseHeader = name => this._readableStream.headers[name.toLowerCase()];
  10259. const {
  10260. allowRangeRequests,
  10261. suggestedLength
  10262. } = validateRangeRequestCapabilities({
  10263. getResponseHeader,
  10264. isHttp: stream.isHttp,
  10265. rangeChunkSize: this._rangeChunkSize,
  10266. disableRange: this._disableRange
  10267. });
  10268. this._isRangeSupported = allowRangeRequests;
  10269. this._contentLength = suggestedLength || this._contentLength;
  10270. this._filename = extractFilenameFromHeader(getResponseHeader);
  10271. };
  10272. this._request = null;
  10273. if (this._url.protocol === "http:") {
  10274. const http = NodePackages.get("http");
  10275. this._request = http.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);
  10276. } else {
  10277. const https = NodePackages.get("https");
  10278. this._request = https.request(createRequestOptions(this._url, stream.httpHeaders), handleResponse);
  10279. }
  10280. this._request.on("error", reason => {
  10281. this._storedError = reason;
  10282. this._headersCapability.reject(reason);
  10283. });
  10284. this._request.end();
  10285. }
  10286. }
  10287. class PDFNodeStreamRangeReader extends BaseRangeReader {
  10288. constructor(stream, start, end) {
  10289. super(stream);
  10290. this._httpHeaders = {};
  10291. for (const property in stream.httpHeaders) {
  10292. const value = stream.httpHeaders[property];
  10293. if (value === undefined) {
  10294. continue;
  10295. }
  10296. this._httpHeaders[property] = value;
  10297. }
  10298. this._httpHeaders.Range = `bytes=${start}-${end - 1}`;
  10299. const handleResponse = response => {
  10300. if (response.statusCode === 404) {
  10301. const error = new MissingPDFException(`Missing PDF "${this._url}".`);
  10302. this._storedError = error;
  10303. return;
  10304. }
  10305. this._setReadableStream(response);
  10306. };
  10307. this._request = null;
  10308. if (this._url.protocol === "http:") {
  10309. const http = NodePackages.get("http");
  10310. this._request = http.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);
  10311. } else {
  10312. const https = NodePackages.get("https");
  10313. this._request = https.request(createRequestOptions(this._url, this._httpHeaders), handleResponse);
  10314. }
  10315. this._request.on("error", reason => {
  10316. this._storedError = reason;
  10317. });
  10318. this._request.end();
  10319. }
  10320. }
  10321. class PDFNodeStreamFsFullReader extends BaseFullReader {
  10322. constructor(stream) {
  10323. super(stream);
  10324. let path = decodeURIComponent(this._url.path);
  10325. if (fileUriRegex.test(this._url.href)) {
  10326. path = path.replace(/^\//, "");
  10327. }
  10328. const fs = NodePackages.get("fs");
  10329. fs.promises.lstat(path).then(stat => {
  10330. this._contentLength = stat.size;
  10331. this._setReadableStream(fs.createReadStream(path));
  10332. this._headersCapability.resolve();
  10333. }, error => {
  10334. if (error.code === "ENOENT") {
  10335. error = new MissingPDFException(`Missing PDF "${path}".`);
  10336. }
  10337. this._storedError = error;
  10338. this._headersCapability.reject(error);
  10339. });
  10340. }
  10341. }
  10342. class PDFNodeStreamFsRangeReader extends BaseRangeReader {
  10343. constructor(stream, start, end) {
  10344. super(stream);
  10345. let path = decodeURIComponent(this._url.path);
  10346. if (fileUriRegex.test(this._url.href)) {
  10347. path = path.replace(/^\//, "");
  10348. }
  10349. const fs = NodePackages.get("fs");
  10350. this._setReadableStream(fs.createReadStream(path, {
  10351. start,
  10352. end: end - 1
  10353. }));
  10354. }
  10355. }
  10356. ;// CONCATENATED MODULE: ./src/display/text_layer.js
  10357. const MAX_TEXT_DIVS_TO_RENDER = 100000;
  10358. const DEFAULT_FONT_SIZE = 30;
  10359. const DEFAULT_FONT_ASCENT = 0.8;
  10360. class TextLayer {
  10361. #capability = Promise.withResolvers();
  10362. #container = null;
  10363. #disableProcessItems = false;
  10364. #fontInspectorEnabled = !!globalThis.FontInspector?.enabled;
  10365. #lang = null;
  10366. #layoutTextParams = null;
  10367. #pageHeight = 0;
  10368. #pageWidth = 0;
  10369. #reader = null;
  10370. #rootContainer = null;
  10371. #rotation = 0;
  10372. #scale = 0;
  10373. #styleCache = Object.create(null);
  10374. #textContentItemsStr = [];
  10375. #textContentSource = null;
  10376. #textDivs = [];
  10377. #textDivProperties = new WeakMap();
  10378. #transform = null;
  10379. static #ascentCache = new Map();
  10380. static #canvasContexts = new Map();
  10381. static #minFontSize = null;
  10382. static #pendingTextLayers = new Set();
  10383. constructor({
  10384. textContentSource,
  10385. container,
  10386. viewport
  10387. }) {
  10388. if (textContentSource instanceof ReadableStream) {
  10389. this.#textContentSource = textContentSource;
  10390. } else if (typeof textContentSource === "object") {
  10391. this.#textContentSource = new ReadableStream({
  10392. start(controller) {
  10393. controller.enqueue(textContentSource);
  10394. controller.close();
  10395. }
  10396. });
  10397. } else {
  10398. throw new Error('No "textContentSource" parameter specified.');
  10399. }
  10400. this.#container = this.#rootContainer = container;
  10401. this.#scale = viewport.scale * (globalThis.devicePixelRatio || 1);
  10402. this.#rotation = viewport.rotation;
  10403. this.#layoutTextParams = {
  10404. prevFontSize: null,
  10405. prevFontFamily: null,
  10406. div: null,
  10407. properties: null,
  10408. ctx: null
  10409. };
  10410. const {
  10411. pageWidth,
  10412. pageHeight,
  10413. pageX,
  10414. pageY
  10415. } = viewport.rawDims;
  10416. this.#transform = [1, 0, 0, -1, -pageX, pageY + pageHeight];
  10417. this.#pageWidth = pageWidth;
  10418. this.#pageHeight = pageHeight;
  10419. TextLayer.#ensureMinFontSizeComputed();
  10420. setLayerDimensions(container, viewport);
  10421. this.#capability.promise.catch(() => {}).then(() => {
  10422. TextLayer.#pendingTextLayers.delete(this);
  10423. this.#layoutTextParams = null;
  10424. this.#styleCache = null;
  10425. });
  10426. }
  10427. render() {
  10428. const pump = () => {
  10429. this.#reader.read().then(({
  10430. value,
  10431. done
  10432. }) => {
  10433. if (done) {
  10434. this.#capability.resolve();
  10435. return;
  10436. }
  10437. this.#lang ??= value.lang;
  10438. Object.assign(this.#styleCache, value.styles);
  10439. this.#processItems(value.items);
  10440. pump();
  10441. }, this.#capability.reject);
  10442. };
  10443. this.#reader = this.#textContentSource.getReader();
  10444. TextLayer.#pendingTextLayers.add(this);
  10445. pump();
  10446. return this.#capability.promise;
  10447. }
  10448. update({
  10449. viewport,
  10450. onBefore = null
  10451. }) {
  10452. const scale = viewport.scale * (globalThis.devicePixelRatio || 1);
  10453. const rotation = viewport.rotation;
  10454. if (rotation !== this.#rotation) {
  10455. onBefore?.();
  10456. this.#rotation = rotation;
  10457. setLayerDimensions(this.#rootContainer, {
  10458. rotation
  10459. });
  10460. }
  10461. if (scale !== this.#scale) {
  10462. onBefore?.();
  10463. this.#scale = scale;
  10464. const params = {
  10465. prevFontSize: null,
  10466. prevFontFamily: null,
  10467. div: null,
  10468. properties: null,
  10469. ctx: TextLayer.#getCtx(this.#lang)
  10470. };
  10471. for (const div of this.#textDivs) {
  10472. params.properties = this.#textDivProperties.get(div);
  10473. params.div = div;
  10474. this.#layout(params);
  10475. }
  10476. }
  10477. }
  10478. cancel() {
  10479. const abortEx = new AbortException("TextLayer task cancelled.");
  10480. this.#reader?.cancel(abortEx).catch(() => {});
  10481. this.#reader = null;
  10482. this.#capability.reject(abortEx);
  10483. }
  10484. get textDivs() {
  10485. return this.#textDivs;
  10486. }
  10487. get textContentItemsStr() {
  10488. return this.#textContentItemsStr;
  10489. }
  10490. #processItems(items) {
  10491. if (this.#disableProcessItems) {
  10492. return;
  10493. }
  10494. this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang);
  10495. const textDivs = this.#textDivs,
  10496. textContentItemsStr = this.#textContentItemsStr;
  10497. for (const item of items) {
  10498. if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) {
  10499. warn("Ignoring additional textDivs for performance reasons.");
  10500. this.#disableProcessItems = true;
  10501. return;
  10502. }
  10503. if (item.str === undefined) {
  10504. if (item.type === "beginMarkedContentProps" || item.type === "beginMarkedContent") {
  10505. const parent = this.#container;
  10506. this.#container = document.createElement("span");
  10507. this.#container.classList.add("markedContent");
  10508. if (item.id !== null) {
  10509. this.#container.setAttribute("id", `${item.id}`);
  10510. }
  10511. parent.append(this.#container);
  10512. } else if (item.type === "endMarkedContent") {
  10513. this.#container = this.#container.parentNode;
  10514. }
  10515. continue;
  10516. }
  10517. textContentItemsStr.push(item.str);
  10518. this.#appendText(item);
  10519. }
  10520. }
  10521. #appendText(geom) {
  10522. const textDiv = document.createElement("span");
  10523. const textDivProperties = {
  10524. angle: 0,
  10525. canvasWidth: 0,
  10526. hasText: geom.str !== "",
  10527. hasEOL: geom.hasEOL,
  10528. fontSize: 0
  10529. };
  10530. this.#textDivs.push(textDiv);
  10531. const tx = Util.transform(this.#transform, geom.transform);
  10532. let angle = Math.atan2(tx[1], tx[0]);
  10533. const style = this.#styleCache[geom.fontName];
  10534. if (style.vertical) {
  10535. angle += Math.PI / 2;
  10536. }
  10537. const fontFamily = this.#fontInspectorEnabled && style.fontSubstitution || style.fontFamily;
  10538. const fontHeight = Math.hypot(tx[2], tx[3]);
  10539. const fontAscent = fontHeight * TextLayer.#getAscent(fontFamily, this.#lang);
  10540. let left, top;
  10541. if (angle === 0) {
  10542. left = tx[4];
  10543. top = tx[5] - fontAscent;
  10544. } else {
  10545. left = tx[4] + fontAscent * Math.sin(angle);
  10546. top = tx[5] - fontAscent * Math.cos(angle);
  10547. }
  10548. const scaleFactorStr = "calc(var(--scale-factor)*";
  10549. const divStyle = textDiv.style;
  10550. if (this.#container === this.#rootContainer) {
  10551. divStyle.left = `${(100 * left / this.#pageWidth).toFixed(2)}%`;
  10552. divStyle.top = `${(100 * top / this.#pageHeight).toFixed(2)}%`;
  10553. } else {
  10554. divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
  10555. divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`;
  10556. }
  10557. divStyle.fontSize = `${scaleFactorStr}${(TextLayer.#minFontSize * fontHeight).toFixed(2)}px)`;
  10558. divStyle.fontFamily = fontFamily;
  10559. textDivProperties.fontSize = fontHeight;
  10560. textDiv.setAttribute("role", "presentation");
  10561. textDiv.textContent = geom.str;
  10562. textDiv.dir = geom.dir;
  10563. if (this.#fontInspectorEnabled) {
  10564. textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName;
  10565. }
  10566. if (angle !== 0) {
  10567. textDivProperties.angle = angle * (180 / Math.PI);
  10568. }
  10569. let shouldScaleText = false;
  10570. if (geom.str.length > 1) {
  10571. shouldScaleText = true;
  10572. } else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) {
  10573. const absScaleX = Math.abs(geom.transform[0]),
  10574. absScaleY = Math.abs(geom.transform[3]);
  10575. if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) {
  10576. shouldScaleText = true;
  10577. }
  10578. }
  10579. if (shouldScaleText) {
  10580. textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;
  10581. }
  10582. this.#textDivProperties.set(textDiv, textDivProperties);
  10583. this.#layoutTextParams.div = textDiv;
  10584. this.#layoutTextParams.properties = textDivProperties;
  10585. this.#layout(this.#layoutTextParams);
  10586. if (textDivProperties.hasText) {
  10587. this.#container.append(textDiv);
  10588. }
  10589. if (textDivProperties.hasEOL) {
  10590. const br = document.createElement("br");
  10591. br.setAttribute("role", "presentation");
  10592. this.#container.append(br);
  10593. }
  10594. }
  10595. #layout(params) {
  10596. const {
  10597. div,
  10598. properties,
  10599. ctx,
  10600. prevFontSize,
  10601. prevFontFamily
  10602. } = params;
  10603. const {
  10604. style
  10605. } = div;
  10606. let transform = "";
  10607. if (TextLayer.#minFontSize > 1) {
  10608. transform = `scale(${1 / TextLayer.#minFontSize})`;
  10609. }
  10610. if (properties.canvasWidth !== 0 && properties.hasText) {
  10611. const {
  10612. fontFamily
  10613. } = style;
  10614. const {
  10615. canvasWidth,
  10616. fontSize
  10617. } = properties;
  10618. if (prevFontSize !== fontSize || prevFontFamily !== fontFamily) {
  10619. ctx.font = `${fontSize * this.#scale}px ${fontFamily}`;
  10620. params.prevFontSize = fontSize;
  10621. params.prevFontFamily = fontFamily;
  10622. }
  10623. const {
  10624. width
  10625. } = ctx.measureText(div.textContent);
  10626. if (width > 0) {
  10627. transform = `scaleX(${canvasWidth * this.#scale / width}) ${transform}`;
  10628. }
  10629. }
  10630. if (properties.angle !== 0) {
  10631. transform = `rotate(${properties.angle}deg) ${transform}`;
  10632. }
  10633. if (transform.length > 0) {
  10634. style.transform = transform;
  10635. }
  10636. }
  10637. static cleanup() {
  10638. if (this.#pendingTextLayers.size > 0) {
  10639. return;
  10640. }
  10641. this.#ascentCache.clear();
  10642. for (const {
  10643. canvas
  10644. } of this.#canvasContexts.values()) {
  10645. canvas.remove();
  10646. }
  10647. this.#canvasContexts.clear();
  10648. }
  10649. static #getCtx(lang = null) {
  10650. let canvasContext = this.#canvasContexts.get(lang ||= "");
  10651. if (!canvasContext) {
  10652. const canvas = document.createElement("canvas");
  10653. canvas.className = "hiddenCanvasElement";
  10654. canvas.lang = lang;
  10655. document.body.append(canvas);
  10656. canvasContext = canvas.getContext("2d", {
  10657. alpha: false,
  10658. willReadFrequently: true
  10659. });
  10660. this.#canvasContexts.set(lang, canvasContext);
  10661. }
  10662. return canvasContext;
  10663. }
  10664. static #ensureMinFontSizeComputed() {
  10665. if (this.#minFontSize !== null) {
  10666. return;
  10667. }
  10668. const div = document.createElement("div");
  10669. div.style.opacity = 0;
  10670. div.style.lineHeight = 1;
  10671. div.style.fontSize = "1px";
  10672. div.style.position = "absolute";
  10673. div.textContent = "X";
  10674. document.body.append(div);
  10675. this.#minFontSize = div.getBoundingClientRect().height;
  10676. div.remove();
  10677. }
  10678. static #getAscent(fontFamily, lang) {
  10679. const cachedAscent = this.#ascentCache.get(fontFamily);
  10680. if (cachedAscent) {
  10681. return cachedAscent;
  10682. }
  10683. const ctx = this.#getCtx(lang);
  10684. const savedFont = ctx.font;
  10685. ctx.canvas.width = ctx.canvas.height = DEFAULT_FONT_SIZE;
  10686. ctx.font = `${DEFAULT_FONT_SIZE}px ${fontFamily}`;
  10687. const metrics = ctx.measureText("");
  10688. let ascent = metrics.fontBoundingBoxAscent;
  10689. let descent = Math.abs(metrics.fontBoundingBoxDescent);
  10690. if (ascent) {
  10691. const ratio = ascent / (ascent + descent);
  10692. this.#ascentCache.set(fontFamily, ratio);
  10693. ctx.canvas.width = ctx.canvas.height = 0;
  10694. ctx.font = savedFont;
  10695. return ratio;
  10696. }
  10697. ctx.strokeStyle = "red";
  10698. ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);
  10699. ctx.strokeText("g", 0, 0);
  10700. let pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;
  10701. descent = 0;
  10702. for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) {
  10703. if (pixels[i] > 0) {
  10704. descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE);
  10705. break;
  10706. }
  10707. }
  10708. ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);
  10709. ctx.strokeText("A", 0, DEFAULT_FONT_SIZE);
  10710. pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;
  10711. ascent = 0;
  10712. for (let i = 0, ii = pixels.length; i < ii; i += 4) {
  10713. if (pixels[i] > 0) {
  10714. ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE);
  10715. break;
  10716. }
  10717. }
  10718. ctx.canvas.width = ctx.canvas.height = 0;
  10719. ctx.font = savedFont;
  10720. const ratio = ascent ? ascent / (ascent + descent) : DEFAULT_FONT_ASCENT;
  10721. this.#ascentCache.set(fontFamily, ratio);
  10722. return ratio;
  10723. }
  10724. }
  10725. ;// CONCATENATED MODULE: ./src/display/xfa_text.js
  10726. class XfaText {
  10727. static textContent(xfa) {
  10728. const items = [];
  10729. const output = {
  10730. items,
  10731. styles: Object.create(null)
  10732. };
  10733. function walk(node) {
  10734. if (!node) {
  10735. return;
  10736. }
  10737. let str = null;
  10738. const name = node.name;
  10739. if (name === "#text") {
  10740. str = node.value;
  10741. } else if (!XfaText.shouldBuildText(name)) {
  10742. return;
  10743. } else if (node?.attributes?.textContent) {
  10744. str = node.attributes.textContent;
  10745. } else if (node.value) {
  10746. str = node.value;
  10747. }
  10748. if (str !== null) {
  10749. items.push({
  10750. str
  10751. });
  10752. }
  10753. if (!node.children) {
  10754. return;
  10755. }
  10756. for (const child of node.children) {
  10757. walk(child);
  10758. }
  10759. }
  10760. walk(xfa);
  10761. return output;
  10762. }
  10763. static shouldBuildText(name) {
  10764. return !(name === "textarea" || name === "input" || name === "option" || name === "select");
  10765. }
  10766. }
  10767. ;// CONCATENATED MODULE: ./src/display/api.js
  10768. const DEFAULT_RANGE_CHUNK_SIZE = 65536;
  10769. const RENDERING_CANCELLED_TIMEOUT = 100;
  10770. const DELAYED_CLEANUP_TIMEOUT = 5000;
  10771. const DefaultCanvasFactory = isNodeJS ? NodeCanvasFactory : DOMCanvasFactory;
  10772. const DefaultCMapReaderFactory = isNodeJS ? NodeCMapReaderFactory : DOMCMapReaderFactory;
  10773. const DefaultFilterFactory = isNodeJS ? NodeFilterFactory : DOMFilterFactory;
  10774. const DefaultStandardFontDataFactory = isNodeJS ? NodeStandardFontDataFactory : DOMStandardFontDataFactory;
  10775. function getDocument(src = {}) {
  10776. if (typeof src === "string" || src instanceof URL) {
  10777. src = {
  10778. url: src
  10779. };
  10780. } else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) {
  10781. src = {
  10782. data: src
  10783. };
  10784. }
  10785. const task = new PDFDocumentLoadingTask();
  10786. const {
  10787. docId
  10788. } = task;
  10789. const url = src.url ? getUrlProp(src.url) : null;
  10790. const data = src.data ? getDataProp(src.data) : null;
  10791. const httpHeaders = src.httpHeaders || null;
  10792. const withCredentials = src.withCredentials === true;
  10793. const password = src.password ?? null;
  10794. const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null;
  10795. const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : DEFAULT_RANGE_CHUNK_SIZE;
  10796. let worker = src.worker instanceof PDFWorker ? src.worker : null;
  10797. const verbosity = src.verbosity;
  10798. const docBaseUrl = typeof src.docBaseUrl === "string" && !isDataScheme(src.docBaseUrl) ? src.docBaseUrl : null;
  10799. const cMapUrl = typeof src.cMapUrl === "string" ? src.cMapUrl : null;
  10800. const cMapPacked = src.cMapPacked !== false;
  10801. const CMapReaderFactory = src.CMapReaderFactory || DefaultCMapReaderFactory;
  10802. const standardFontDataUrl = typeof src.standardFontDataUrl === "string" ? src.standardFontDataUrl : null;
  10803. const StandardFontDataFactory = src.StandardFontDataFactory || DefaultStandardFontDataFactory;
  10804. const ignoreErrors = src.stopAtErrors !== true;
  10805. const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1;
  10806. const isEvalSupported = src.isEvalSupported !== false;
  10807. const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !isNodeJS;
  10808. const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1;
  10809. const disableFontFace = typeof src.disableFontFace === "boolean" ? src.disableFontFace : isNodeJS;
  10810. const fontExtraProperties = src.fontExtraProperties === true;
  10811. const enableXfa = src.enableXfa === true;
  10812. const ownerDocument = src.ownerDocument || globalThis.document;
  10813. const disableRange = src.disableRange === true;
  10814. const disableStream = src.disableStream === true;
  10815. const disableAutoFetch = src.disableAutoFetch === true;
  10816. const pdfBug = src.pdfBug === true;
  10817. const enableHWA = src.enableHWA === true;
  10818. const length = rangeTransport ? rangeTransport.length : src.length ?? NaN;
  10819. const useSystemFonts = typeof src.useSystemFonts === "boolean" ? src.useSystemFonts : !isNodeJS && !disableFontFace;
  10820. const useWorkerFetch = typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && cMapUrl && standardFontDataUrl && isValidFetchUrl(cMapUrl, document.baseURI) && isValidFetchUrl(standardFontDataUrl, document.baseURI);
  10821. const canvasFactory = src.canvasFactory || new DefaultCanvasFactory({
  10822. ownerDocument,
  10823. enableHWA
  10824. });
  10825. const filterFactory = src.filterFactory || new DefaultFilterFactory({
  10826. docId,
  10827. ownerDocument
  10828. });
  10829. const styleElement = null;
  10830. setVerbosityLevel(verbosity);
  10831. const transportFactory = {
  10832. canvasFactory,
  10833. filterFactory
  10834. };
  10835. if (!useWorkerFetch) {
  10836. transportFactory.cMapReaderFactory = new CMapReaderFactory({
  10837. baseUrl: cMapUrl,
  10838. isCompressed: cMapPacked
  10839. });
  10840. transportFactory.standardFontDataFactory = new StandardFontDataFactory({
  10841. baseUrl: standardFontDataUrl
  10842. });
  10843. }
  10844. if (!worker) {
  10845. const workerParams = {
  10846. verbosity,
  10847. port: GlobalWorkerOptions.workerPort
  10848. };
  10849. worker = workerParams.port ? PDFWorker.fromPort(workerParams) : new PDFWorker(workerParams);
  10850. task._worker = worker;
  10851. }
  10852. const docParams = {
  10853. docId,
  10854. apiVersion: "4.5.136",
  10855. data,
  10856. password,
  10857. disableAutoFetch,
  10858. rangeChunkSize,
  10859. length,
  10860. docBaseUrl,
  10861. enableXfa,
  10862. evaluatorOptions: {
  10863. maxImageSize,
  10864. disableFontFace,
  10865. ignoreErrors,
  10866. isEvalSupported,
  10867. isOffscreenCanvasSupported,
  10868. canvasMaxAreaInBytes,
  10869. fontExtraProperties,
  10870. useSystemFonts,
  10871. cMapUrl: useWorkerFetch ? cMapUrl : null,
  10872. standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null
  10873. }
  10874. };
  10875. const transportParams = {
  10876. disableFontFace,
  10877. fontExtraProperties,
  10878. ownerDocument,
  10879. pdfBug,
  10880. styleElement,
  10881. loadingParams: {
  10882. disableAutoFetch,
  10883. enableXfa
  10884. }
  10885. };
  10886. worker.promise.then(function () {
  10887. if (task.destroyed) {
  10888. throw new Error("Loading aborted");
  10889. }
  10890. if (worker.destroyed) {
  10891. throw new Error("Worker was destroyed");
  10892. }
  10893. const workerIdPromise = worker.messageHandler.sendWithPromise("GetDocRequest", docParams, data ? [data.buffer] : null);
  10894. let networkStream;
  10895. if (rangeTransport) {
  10896. networkStream = new PDFDataTransportStream(rangeTransport, {
  10897. disableRange,
  10898. disableStream
  10899. });
  10900. } else if (!data) {
  10901. if (!url) {
  10902. throw new Error("getDocument - no `url` parameter provided.");
  10903. }
  10904. const createPDFNetworkStream = params => {
  10905. if (isNodeJS) {
  10906. const isFetchSupported = function () {
  10907. return typeof fetch !== "undefined" && typeof Response !== "undefined" && "body" in Response.prototype;
  10908. };
  10909. return isFetchSupported() && isValidFetchUrl(params.url) ? new PDFFetchStream(params) : new PDFNodeStream(params);
  10910. }
  10911. return isValidFetchUrl(params.url) ? new PDFFetchStream(params) : new PDFNetworkStream(params);
  10912. };
  10913. networkStream = createPDFNetworkStream({
  10914. url,
  10915. length,
  10916. httpHeaders,
  10917. withCredentials,
  10918. rangeChunkSize,
  10919. disableRange,
  10920. disableStream
  10921. });
  10922. }
  10923. return workerIdPromise.then(workerId => {
  10924. if (task.destroyed) {
  10925. throw new Error("Loading aborted");
  10926. }
  10927. if (worker.destroyed) {
  10928. throw new Error("Worker was destroyed");
  10929. }
  10930. const messageHandler = new MessageHandler(docId, workerId, worker.port);
  10931. const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory);
  10932. task._transport = transport;
  10933. messageHandler.send("Ready", null);
  10934. });
  10935. }).catch(task._capability.reject);
  10936. return task;
  10937. }
  10938. function getUrlProp(val) {
  10939. if (val instanceof URL) {
  10940. return val.href;
  10941. }
  10942. try {
  10943. return new URL(val, window.location).href;
  10944. } catch {
  10945. if (isNodeJS && typeof val === "string") {
  10946. return val;
  10947. }
  10948. }
  10949. throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property.");
  10950. }
  10951. function getDataProp(val) {
  10952. if (isNodeJS && typeof Buffer !== "undefined" && val instanceof Buffer) {
  10953. throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`.");
  10954. }
  10955. if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
  10956. return val;
  10957. }
  10958. if (typeof val === "string") {
  10959. return stringToBytes(val);
  10960. }
  10961. if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) {
  10962. return new Uint8Array(val);
  10963. }
  10964. throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property.");
  10965. }
  10966. function isRefProxy(ref) {
  10967. return typeof ref === "object" && Number.isInteger(ref?.num) && ref.num >= 0 && Number.isInteger(ref?.gen) && ref.gen >= 0;
  10968. }
  10969. class PDFDocumentLoadingTask {
  10970. static #docId = 0;
  10971. constructor() {
  10972. this._capability = Promise.withResolvers();
  10973. this._transport = null;
  10974. this._worker = null;
  10975. this.docId = `d${PDFDocumentLoadingTask.#docId++}`;
  10976. this.destroyed = false;
  10977. this.onPassword = null;
  10978. this.onProgress = null;
  10979. }
  10980. get promise() {
  10981. return this._capability.promise;
  10982. }
  10983. async destroy() {
  10984. this.destroyed = true;
  10985. try {
  10986. if (this._worker?.port) {
  10987. this._worker._pendingDestroy = true;
  10988. }
  10989. await this._transport?.destroy();
  10990. } catch (ex) {
  10991. if (this._worker?.port) {
  10992. delete this._worker._pendingDestroy;
  10993. }
  10994. throw ex;
  10995. }
  10996. this._transport = null;
  10997. if (this._worker) {
  10998. this._worker.destroy();
  10999. this._worker = null;
  11000. }
  11001. }
  11002. }
  11003. class PDFDataRangeTransport {
  11004. constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) {
  11005. this.length = length;
  11006. this.initialData = initialData;
  11007. this.progressiveDone = progressiveDone;
  11008. this.contentDispositionFilename = contentDispositionFilename;
  11009. this._rangeListeners = [];
  11010. this._progressListeners = [];
  11011. this._progressiveReadListeners = [];
  11012. this._progressiveDoneListeners = [];
  11013. this._readyCapability = Promise.withResolvers();
  11014. }
  11015. addRangeListener(listener) {
  11016. this._rangeListeners.push(listener);
  11017. }
  11018. addProgressListener(listener) {
  11019. this._progressListeners.push(listener);
  11020. }
  11021. addProgressiveReadListener(listener) {
  11022. this._progressiveReadListeners.push(listener);
  11023. }
  11024. addProgressiveDoneListener(listener) {
  11025. this._progressiveDoneListeners.push(listener);
  11026. }
  11027. onDataRange(begin, chunk) {
  11028. for (const listener of this._rangeListeners) {
  11029. listener(begin, chunk);
  11030. }
  11031. }
  11032. onDataProgress(loaded, total) {
  11033. this._readyCapability.promise.then(() => {
  11034. for (const listener of this._progressListeners) {
  11035. listener(loaded, total);
  11036. }
  11037. });
  11038. }
  11039. onDataProgressiveRead(chunk) {
  11040. this._readyCapability.promise.then(() => {
  11041. for (const listener of this._progressiveReadListeners) {
  11042. listener(chunk);
  11043. }
  11044. });
  11045. }
  11046. onDataProgressiveDone() {
  11047. this._readyCapability.promise.then(() => {
  11048. for (const listener of this._progressiveDoneListeners) {
  11049. listener();
  11050. }
  11051. });
  11052. }
  11053. transportReady() {
  11054. this._readyCapability.resolve();
  11055. }
  11056. requestDataRange(begin, end) {
  11057. unreachable("Abstract method PDFDataRangeTransport.requestDataRange");
  11058. }
  11059. abort() {}
  11060. }
  11061. class PDFDocumentProxy {
  11062. constructor(pdfInfo, transport) {
  11063. this._pdfInfo = pdfInfo;
  11064. this._transport = transport;
  11065. }
  11066. get annotationStorage() {
  11067. return this._transport.annotationStorage;
  11068. }
  11069. get filterFactory() {
  11070. return this._transport.filterFactory;
  11071. }
  11072. get numPages() {
  11073. return this._pdfInfo.numPages;
  11074. }
  11075. get fingerprints() {
  11076. return this._pdfInfo.fingerprints;
  11077. }
  11078. get isPureXfa() {
  11079. return shadow(this, "isPureXfa", !!this._transport._htmlForXfa);
  11080. }
  11081. get allXfaHtml() {
  11082. return this._transport._htmlForXfa;
  11083. }
  11084. getPage(pageNumber) {
  11085. return this._transport.getPage(pageNumber);
  11086. }
  11087. getPageIndex(ref) {
  11088. return this._transport.getPageIndex(ref);
  11089. }
  11090. getDestinations() {
  11091. return this._transport.getDestinations();
  11092. }
  11093. getDestination(id) {
  11094. return this._transport.getDestination(id);
  11095. }
  11096. getPageLabels() {
  11097. return this._transport.getPageLabels();
  11098. }
  11099. getPageLayout() {
  11100. return this._transport.getPageLayout();
  11101. }
  11102. getPageMode() {
  11103. return this._transport.getPageMode();
  11104. }
  11105. getViewerPreferences() {
  11106. return this._transport.getViewerPreferences();
  11107. }
  11108. getOpenAction() {
  11109. return this._transport.getOpenAction();
  11110. }
  11111. getAttachments() {
  11112. return this._transport.getAttachments();
  11113. }
  11114. getJSActions() {
  11115. return this._transport.getDocJSActions();
  11116. }
  11117. getOutline() {
  11118. return this._transport.getOutline();
  11119. }
  11120. getOptionalContentConfig({
  11121. intent = "display"
  11122. } = {}) {
  11123. const {
  11124. renderingIntent
  11125. } = this._transport.getRenderingIntent(intent);
  11126. return this._transport.getOptionalContentConfig(renderingIntent);
  11127. }
  11128. getPermissions() {
  11129. return this._transport.getPermissions();
  11130. }
  11131. getMetadata() {
  11132. return this._transport.getMetadata();
  11133. }
  11134. getMarkInfo() {
  11135. return this._transport.getMarkInfo();
  11136. }
  11137. getData() {
  11138. return this._transport.getData();
  11139. }
  11140. saveDocument() {
  11141. return this._transport.saveDocument();
  11142. }
  11143. getDownloadInfo() {
  11144. return this._transport.downloadInfoCapability.promise;
  11145. }
  11146. cleanup(keepLoadedFonts = false) {
  11147. return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
  11148. }
  11149. destroy() {
  11150. return this.loadingTask.destroy();
  11151. }
  11152. cachedPageNumber(ref) {
  11153. return this._transport.cachedPageNumber(ref);
  11154. }
  11155. get loadingParams() {
  11156. return this._transport.loadingParams;
  11157. }
  11158. get loadingTask() {
  11159. return this._transport.loadingTask;
  11160. }
  11161. getFieldObjects() {
  11162. return this._transport.getFieldObjects();
  11163. }
  11164. hasJSActions() {
  11165. return this._transport.hasJSActions();
  11166. }
  11167. getCalculationOrderIds() {
  11168. return this._transport.getCalculationOrderIds();
  11169. }
  11170. }
  11171. class PDFPageProxy {
  11172. #delayedCleanupTimeout = null;
  11173. #pendingCleanup = false;
  11174. constructor(pageIndex, pageInfo, transport, pdfBug = false) {
  11175. this._pageIndex = pageIndex;
  11176. this._pageInfo = pageInfo;
  11177. this._transport = transport;
  11178. this._stats = pdfBug ? new StatTimer() : null;
  11179. this._pdfBug = pdfBug;
  11180. this.commonObjs = transport.commonObjs;
  11181. this.objs = new PDFObjects();
  11182. this._maybeCleanupAfterRender = false;
  11183. this._intentStates = new Map();
  11184. this.destroyed = false;
  11185. }
  11186. get pageNumber() {
  11187. return this._pageIndex + 1;
  11188. }
  11189. get rotate() {
  11190. return this._pageInfo.rotate;
  11191. }
  11192. get ref() {
  11193. return this._pageInfo.ref;
  11194. }
  11195. get userUnit() {
  11196. return this._pageInfo.userUnit;
  11197. }
  11198. get view() {
  11199. return this._pageInfo.view;
  11200. }
  11201. getViewport({
  11202. scale,
  11203. rotation = this.rotate,
  11204. offsetX = 0,
  11205. offsetY = 0,
  11206. dontFlip = false
  11207. } = {}) {
  11208. return new PageViewport({
  11209. viewBox: this.view,
  11210. scale,
  11211. rotation,
  11212. offsetX,
  11213. offsetY,
  11214. dontFlip
  11215. });
  11216. }
  11217. getAnnotations({
  11218. intent = "display"
  11219. } = {}) {
  11220. const {
  11221. renderingIntent
  11222. } = this._transport.getRenderingIntent(intent);
  11223. return this._transport.getAnnotations(this._pageIndex, renderingIntent);
  11224. }
  11225. getJSActions() {
  11226. return this._transport.getPageJSActions(this._pageIndex);
  11227. }
  11228. get filterFactory() {
  11229. return this._transport.filterFactory;
  11230. }
  11231. get isPureXfa() {
  11232. return shadow(this, "isPureXfa", !!this._transport._htmlForXfa);
  11233. }
  11234. async getXfa() {
  11235. return this._transport._htmlForXfa?.children[this._pageIndex] || null;
  11236. }
  11237. render({
  11238. canvasContext,
  11239. viewport,
  11240. intent = "display",
  11241. annotationMode = AnnotationMode.ENABLE,
  11242. transform = null,
  11243. background = null,
  11244. optionalContentConfigPromise = null,
  11245. annotationCanvasMap = null,
  11246. pageColors = null,
  11247. printAnnotationStorage = null,
  11248. isEditing = false
  11249. }) {
  11250. this._stats?.time("Overall");
  11251. const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing);
  11252. const {
  11253. renderingIntent,
  11254. cacheKey
  11255. } = intentArgs;
  11256. this.#pendingCleanup = false;
  11257. this.#abortDelayedCleanup();
  11258. optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent);
  11259. let intentState = this._intentStates.get(cacheKey);
  11260. if (!intentState) {
  11261. intentState = Object.create(null);
  11262. this._intentStates.set(cacheKey, intentState);
  11263. }
  11264. if (intentState.streamReaderCancelTimeout) {
  11265. clearTimeout(intentState.streamReaderCancelTimeout);
  11266. intentState.streamReaderCancelTimeout = null;
  11267. }
  11268. const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
  11269. if (!intentState.displayReadyCapability) {
  11270. intentState.displayReadyCapability = Promise.withResolvers();
  11271. intentState.operatorList = {
  11272. fnArray: [],
  11273. argsArray: [],
  11274. lastChunk: false,
  11275. separateAnnots: null
  11276. };
  11277. this._stats?.time("Page Request");
  11278. this._pumpOperatorList(intentArgs);
  11279. }
  11280. const complete = error => {
  11281. intentState.renderTasks.delete(internalRenderTask);
  11282. if (this._maybeCleanupAfterRender || intentPrint) {
  11283. this.#pendingCleanup = true;
  11284. }
  11285. this.#tryCleanup(!intentPrint);
  11286. if (error) {
  11287. internalRenderTask.capability.reject(error);
  11288. this._abortOperatorList({
  11289. intentState,
  11290. reason: error instanceof Error ? error : new Error(error)
  11291. });
  11292. } else {
  11293. internalRenderTask.capability.resolve();
  11294. }
  11295. if (this._stats) {
  11296. this._stats.timeEnd("Rendering");
  11297. this._stats.timeEnd("Overall");
  11298. if (globalThis.Stats?.enabled) {
  11299. globalThis.Stats.add(this.pageNumber, this._stats);
  11300. }
  11301. }
  11302. };
  11303. const internalRenderTask = new InternalRenderTask({
  11304. callback: complete,
  11305. params: {
  11306. canvasContext,
  11307. viewport,
  11308. transform,
  11309. background
  11310. },
  11311. objs: this.objs,
  11312. commonObjs: this.commonObjs,
  11313. annotationCanvasMap,
  11314. operatorList: intentState.operatorList,
  11315. pageIndex: this._pageIndex,
  11316. canvasFactory: this._transport.canvasFactory,
  11317. filterFactory: this._transport.filterFactory,
  11318. useRequestAnimationFrame: !intentPrint,
  11319. pdfBug: this._pdfBug,
  11320. pageColors
  11321. });
  11322. (intentState.renderTasks ||= new Set()).add(internalRenderTask);
  11323. const renderTask = internalRenderTask.task;
  11324. Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => {
  11325. if (this.destroyed) {
  11326. complete();
  11327. return;
  11328. }
  11329. this._stats?.time("Rendering");
  11330. if (!(optionalContentConfig.renderingIntent & renderingIntent)) {
  11331. throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` " + "and `PDFDocumentProxy.getOptionalContentConfig` methods.");
  11332. }
  11333. internalRenderTask.initializeGraphics({
  11334. transparency,
  11335. optionalContentConfig
  11336. });
  11337. internalRenderTask.operatorListChanged();
  11338. }).catch(complete);
  11339. return renderTask;
  11340. }
  11341. getOperatorList({
  11342. intent = "display",
  11343. annotationMode = AnnotationMode.ENABLE,
  11344. printAnnotationStorage = null,
  11345. isEditing = false
  11346. } = {}) {
  11347. function operatorListChanged() {
  11348. if (intentState.operatorList.lastChunk) {
  11349. intentState.opListReadCapability.resolve(intentState.operatorList);
  11350. intentState.renderTasks.delete(opListTask);
  11351. }
  11352. }
  11353. const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing, true);
  11354. let intentState = this._intentStates.get(intentArgs.cacheKey);
  11355. if (!intentState) {
  11356. intentState = Object.create(null);
  11357. this._intentStates.set(intentArgs.cacheKey, intentState);
  11358. }
  11359. let opListTask;
  11360. if (!intentState.opListReadCapability) {
  11361. opListTask = Object.create(null);
  11362. opListTask.operatorListChanged = operatorListChanged;
  11363. intentState.opListReadCapability = Promise.withResolvers();
  11364. (intentState.renderTasks ||= new Set()).add(opListTask);
  11365. intentState.operatorList = {
  11366. fnArray: [],
  11367. argsArray: [],
  11368. lastChunk: false,
  11369. separateAnnots: null
  11370. };
  11371. this._stats?.time("Page Request");
  11372. this._pumpOperatorList(intentArgs);
  11373. }
  11374. return intentState.opListReadCapability.promise;
  11375. }
  11376. streamTextContent({
  11377. includeMarkedContent = false,
  11378. disableNormalization = false
  11379. } = {}) {
  11380. const TEXT_CONTENT_CHUNK_SIZE = 100;
  11381. return this._transport.messageHandler.sendWithStream("GetTextContent", {
  11382. pageIndex: this._pageIndex,
  11383. includeMarkedContent: includeMarkedContent === true,
  11384. disableNormalization: disableNormalization === true
  11385. }, {
  11386. highWaterMark: TEXT_CONTENT_CHUNK_SIZE,
  11387. size(textContent) {
  11388. return textContent.items.length;
  11389. }
  11390. });
  11391. }
  11392. getTextContent(params = {}) {
  11393. if (this._transport._htmlForXfa) {
  11394. return this.getXfa().then(xfa => XfaText.textContent(xfa));
  11395. }
  11396. const readableStream = this.streamTextContent(params);
  11397. return new Promise(function (resolve, reject) {
  11398. function pump() {
  11399. reader.read().then(function ({
  11400. value,
  11401. done
  11402. }) {
  11403. if (done) {
  11404. resolve(textContent);
  11405. return;
  11406. }
  11407. textContent.lang ??= value.lang;
  11408. Object.assign(textContent.styles, value.styles);
  11409. textContent.items.push(...value.items);
  11410. pump();
  11411. }, reject);
  11412. }
  11413. const reader = readableStream.getReader();
  11414. const textContent = {
  11415. items: [],
  11416. styles: Object.create(null),
  11417. lang: null
  11418. };
  11419. pump();
  11420. });
  11421. }
  11422. getStructTree() {
  11423. return this._transport.getStructTree(this._pageIndex);
  11424. }
  11425. _destroy() {
  11426. this.destroyed = true;
  11427. const waitOn = [];
  11428. for (const intentState of this._intentStates.values()) {
  11429. this._abortOperatorList({
  11430. intentState,
  11431. reason: new Error("Page was destroyed."),
  11432. force: true
  11433. });
  11434. if (intentState.opListReadCapability) {
  11435. continue;
  11436. }
  11437. for (const internalRenderTask of intentState.renderTasks) {
  11438. waitOn.push(internalRenderTask.completed);
  11439. internalRenderTask.cancel();
  11440. }
  11441. }
  11442. this.objs.clear();
  11443. this.#pendingCleanup = false;
  11444. this.#abortDelayedCleanup();
  11445. return Promise.all(waitOn);
  11446. }
  11447. cleanup(resetStats = false) {
  11448. this.#pendingCleanup = true;
  11449. const success = this.#tryCleanup(false);
  11450. if (resetStats && success) {
  11451. this._stats &&= new StatTimer();
  11452. }
  11453. return success;
  11454. }
  11455. #tryCleanup(delayed = false) {
  11456. this.#abortDelayedCleanup();
  11457. if (!this.#pendingCleanup || this.destroyed) {
  11458. return false;
  11459. }
  11460. if (delayed) {
  11461. this.#delayedCleanupTimeout = setTimeout(() => {
  11462. this.#delayedCleanupTimeout = null;
  11463. this.#tryCleanup(false);
  11464. }, DELAYED_CLEANUP_TIMEOUT);
  11465. return false;
  11466. }
  11467. for (const {
  11468. renderTasks,
  11469. operatorList
  11470. } of this._intentStates.values()) {
  11471. if (renderTasks.size > 0 || !operatorList.lastChunk) {
  11472. return false;
  11473. }
  11474. }
  11475. this._intentStates.clear();
  11476. this.objs.clear();
  11477. this.#pendingCleanup = false;
  11478. return true;
  11479. }
  11480. #abortDelayedCleanup() {
  11481. if (this.#delayedCleanupTimeout) {
  11482. clearTimeout(this.#delayedCleanupTimeout);
  11483. this.#delayedCleanupTimeout = null;
  11484. }
  11485. }
  11486. _startRenderPage(transparency, cacheKey) {
  11487. const intentState = this._intentStates.get(cacheKey);
  11488. if (!intentState) {
  11489. return;
  11490. }
  11491. this._stats?.timeEnd("Page Request");
  11492. intentState.displayReadyCapability?.resolve(transparency);
  11493. }
  11494. _renderPageChunk(operatorListChunk, intentState) {
  11495. for (let i = 0, ii = operatorListChunk.length; i < ii; i++) {
  11496. intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
  11497. intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
  11498. }
  11499. intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
  11500. intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots;
  11501. for (const internalRenderTask of intentState.renderTasks) {
  11502. internalRenderTask.operatorListChanged();
  11503. }
  11504. if (operatorListChunk.lastChunk) {
  11505. this.#tryCleanup(true);
  11506. }
  11507. }
  11508. _pumpOperatorList({
  11509. renderingIntent,
  11510. cacheKey,
  11511. annotationStorageSerializable,
  11512. modifiedIds
  11513. }) {
  11514. const {
  11515. map,
  11516. transfer
  11517. } = annotationStorageSerializable;
  11518. const readableStream = this._transport.messageHandler.sendWithStream("GetOperatorList", {
  11519. pageIndex: this._pageIndex,
  11520. intent: renderingIntent,
  11521. cacheKey,
  11522. annotationStorage: map,
  11523. modifiedIds
  11524. }, transfer);
  11525. const reader = readableStream.getReader();
  11526. const intentState = this._intentStates.get(cacheKey);
  11527. intentState.streamReader = reader;
  11528. const pump = () => {
  11529. reader.read().then(({
  11530. value,
  11531. done
  11532. }) => {
  11533. if (done) {
  11534. intentState.streamReader = null;
  11535. return;
  11536. }
  11537. if (this._transport.destroyed) {
  11538. return;
  11539. }
  11540. this._renderPageChunk(value, intentState);
  11541. pump();
  11542. }, reason => {
  11543. intentState.streamReader = null;
  11544. if (this._transport.destroyed) {
  11545. return;
  11546. }
  11547. if (intentState.operatorList) {
  11548. intentState.operatorList.lastChunk = true;
  11549. for (const internalRenderTask of intentState.renderTasks) {
  11550. internalRenderTask.operatorListChanged();
  11551. }
  11552. this.#tryCleanup(true);
  11553. }
  11554. if (intentState.displayReadyCapability) {
  11555. intentState.displayReadyCapability.reject(reason);
  11556. } else if (intentState.opListReadCapability) {
  11557. intentState.opListReadCapability.reject(reason);
  11558. } else {
  11559. throw reason;
  11560. }
  11561. });
  11562. };
  11563. pump();
  11564. }
  11565. _abortOperatorList({
  11566. intentState,
  11567. reason,
  11568. force = false
  11569. }) {
  11570. if (!intentState.streamReader) {
  11571. return;
  11572. }
  11573. if (intentState.streamReaderCancelTimeout) {
  11574. clearTimeout(intentState.streamReaderCancelTimeout);
  11575. intentState.streamReaderCancelTimeout = null;
  11576. }
  11577. if (!force) {
  11578. if (intentState.renderTasks.size > 0) {
  11579. return;
  11580. }
  11581. if (reason instanceof RenderingCancelledException) {
  11582. let delay = RENDERING_CANCELLED_TIMEOUT;
  11583. if (reason.extraDelay > 0 && reason.extraDelay < 1000) {
  11584. delay += reason.extraDelay;
  11585. }
  11586. intentState.streamReaderCancelTimeout = setTimeout(() => {
  11587. intentState.streamReaderCancelTimeout = null;
  11588. this._abortOperatorList({
  11589. intentState,
  11590. reason,
  11591. force: true
  11592. });
  11593. }, delay);
  11594. return;
  11595. }
  11596. }
  11597. intentState.streamReader.cancel(new AbortException(reason.message)).catch(() => {});
  11598. intentState.streamReader = null;
  11599. if (this._transport.destroyed) {
  11600. return;
  11601. }
  11602. for (const [curCacheKey, curIntentState] of this._intentStates) {
  11603. if (curIntentState === intentState) {
  11604. this._intentStates.delete(curCacheKey);
  11605. break;
  11606. }
  11607. }
  11608. this.cleanup();
  11609. }
  11610. get stats() {
  11611. return this._stats;
  11612. }
  11613. }
  11614. class LoopbackPort {
  11615. #listeners = new Set();
  11616. #deferred = Promise.resolve();
  11617. postMessage(obj, transfer) {
  11618. const event = {
  11619. data: structuredClone(obj, transfer ? {
  11620. transfer
  11621. } : null)
  11622. };
  11623. this.#deferred.then(() => {
  11624. for (const listener of this.#listeners) {
  11625. listener.call(this, event);
  11626. }
  11627. });
  11628. }
  11629. addEventListener(name, listener) {
  11630. this.#listeners.add(listener);
  11631. }
  11632. removeEventListener(name, listener) {
  11633. this.#listeners.delete(listener);
  11634. }
  11635. terminate() {
  11636. this.#listeners.clear();
  11637. }
  11638. }
  11639. const PDFWorkerUtil = {
  11640. isWorkerDisabled: false,
  11641. fakeWorkerId: 0
  11642. };
  11643. {
  11644. if (isNodeJS) {
  11645. PDFWorkerUtil.isWorkerDisabled = true;
  11646. GlobalWorkerOptions.workerSrc ||= "./pdf.worker.mjs";
  11647. }
  11648. PDFWorkerUtil.isSameOrigin = function (baseUrl, otherUrl) {
  11649. let base;
  11650. try {
  11651. base = new URL(baseUrl);
  11652. if (!base.origin || base.origin === "null") {
  11653. return false;
  11654. }
  11655. } catch {
  11656. return false;
  11657. }
  11658. const other = new URL(otherUrl, base);
  11659. return base.origin === other.origin;
  11660. };
  11661. PDFWorkerUtil.createCDNWrapper = function (url) {
  11662. const wrapper = `await import("${url}");`;
  11663. return URL.createObjectURL(new Blob([wrapper], {
  11664. type: "text/javascript"
  11665. }));
  11666. };
  11667. }
  11668. class PDFWorker {
  11669. static #workerPorts;
  11670. constructor({
  11671. name = null,
  11672. port = null,
  11673. verbosity = getVerbosityLevel()
  11674. } = {}) {
  11675. this.name = name;
  11676. this.destroyed = false;
  11677. this.verbosity = verbosity;
  11678. this._readyCapability = Promise.withResolvers();
  11679. this._port = null;
  11680. this._webWorker = null;
  11681. this._messageHandler = null;
  11682. if (port) {
  11683. if (PDFWorker.#workerPorts?.has(port)) {
  11684. throw new Error("Cannot use more than one PDFWorker per port.");
  11685. }
  11686. (PDFWorker.#workerPorts ||= new WeakMap()).set(port, this);
  11687. this._initializeFromPort(port);
  11688. return;
  11689. }
  11690. this._initialize();
  11691. }
  11692. get promise() {
  11693. if (isNodeJS) {
  11694. return Promise.all([NodePackages.promise, this._readyCapability.promise]);
  11695. }
  11696. return this._readyCapability.promise;
  11697. }
  11698. #resolve() {
  11699. this._readyCapability.resolve();
  11700. this._messageHandler.send("configure", {
  11701. verbosity: this.verbosity
  11702. });
  11703. }
  11704. get port() {
  11705. return this._port;
  11706. }
  11707. get messageHandler() {
  11708. return this._messageHandler;
  11709. }
  11710. _initializeFromPort(port) {
  11711. this._port = port;
  11712. this._messageHandler = new MessageHandler("main", "worker", port);
  11713. this._messageHandler.on("ready", function () {});
  11714. this.#resolve();
  11715. }
  11716. _initialize() {
  11717. if (PDFWorkerUtil.isWorkerDisabled || PDFWorker.#mainThreadWorkerMessageHandler) {
  11718. this._setupFakeWorker();
  11719. return;
  11720. }
  11721. let {
  11722. workerSrc
  11723. } = PDFWorker;
  11724. try {
  11725. if (!PDFWorkerUtil.isSameOrigin(window.location.href, workerSrc)) {
  11726. workerSrc = PDFWorkerUtil.createCDNWrapper(new URL(workerSrc, window.location).href);
  11727. }
  11728. const worker = new Worker(workerSrc, {
  11729. type: "module"
  11730. });
  11731. const messageHandler = new MessageHandler("main", "worker", worker);
  11732. const terminateEarly = () => {
  11733. ac.abort();
  11734. messageHandler.destroy();
  11735. worker.terminate();
  11736. if (this.destroyed) {
  11737. this._readyCapability.reject(new Error("Worker was destroyed"));
  11738. } else {
  11739. this._setupFakeWorker();
  11740. }
  11741. };
  11742. const ac = new AbortController();
  11743. worker.addEventListener("error", () => {
  11744. if (!this._webWorker) {
  11745. terminateEarly();
  11746. }
  11747. }, {
  11748. signal: ac.signal
  11749. });
  11750. messageHandler.on("test", data => {
  11751. ac.abort();
  11752. if (this.destroyed || !data) {
  11753. terminateEarly();
  11754. return;
  11755. }
  11756. this._messageHandler = messageHandler;
  11757. this._port = worker;
  11758. this._webWorker = worker;
  11759. this.#resolve();
  11760. });
  11761. messageHandler.on("ready", data => {
  11762. ac.abort();
  11763. if (this.destroyed) {
  11764. terminateEarly();
  11765. return;
  11766. }
  11767. try {
  11768. sendTest();
  11769. } catch {
  11770. this._setupFakeWorker();
  11771. }
  11772. });
  11773. const sendTest = () => {
  11774. const testObj = new Uint8Array();
  11775. messageHandler.send("test", testObj, [testObj.buffer]);
  11776. };
  11777. sendTest();
  11778. return;
  11779. } catch {
  11780. info("The worker has been disabled.");
  11781. }
  11782. this._setupFakeWorker();
  11783. }
  11784. _setupFakeWorker() {
  11785. if (!PDFWorkerUtil.isWorkerDisabled) {
  11786. warn("Setting up fake worker.");
  11787. PDFWorkerUtil.isWorkerDisabled = true;
  11788. }
  11789. PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => {
  11790. if (this.destroyed) {
  11791. this._readyCapability.reject(new Error("Worker was destroyed"));
  11792. return;
  11793. }
  11794. const port = new LoopbackPort();
  11795. this._port = port;
  11796. const id = `fake${PDFWorkerUtil.fakeWorkerId++}`;
  11797. const workerHandler = new MessageHandler(id + "_worker", id, port);
  11798. WorkerMessageHandler.setup(workerHandler, port);
  11799. this._messageHandler = new MessageHandler(id, id + "_worker", port);
  11800. this.#resolve();
  11801. }).catch(reason => {
  11802. this._readyCapability.reject(new Error(`Setting up fake worker failed: "${reason.message}".`));
  11803. });
  11804. }
  11805. destroy() {
  11806. this.destroyed = true;
  11807. if (this._webWorker) {
  11808. this._webWorker.terminate();
  11809. this._webWorker = null;
  11810. }
  11811. PDFWorker.#workerPorts?.delete(this._port);
  11812. this._port = null;
  11813. if (this._messageHandler) {
  11814. this._messageHandler.destroy();
  11815. this._messageHandler = null;
  11816. }
  11817. }
  11818. static fromPort(params) {
  11819. if (!params?.port) {
  11820. throw new Error("PDFWorker.fromPort - invalid method signature.");
  11821. }
  11822. const cachedPort = this.#workerPorts?.get(params.port);
  11823. if (cachedPort) {
  11824. if (cachedPort._pendingDestroy) {
  11825. throw new Error("PDFWorker.fromPort - the worker is being destroyed.\n" + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls.");
  11826. }
  11827. return cachedPort;
  11828. }
  11829. return new PDFWorker(params);
  11830. }
  11831. static get workerSrc() {
  11832. if (GlobalWorkerOptions.workerSrc) {
  11833. return GlobalWorkerOptions.workerSrc;
  11834. }
  11835. throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
  11836. }
  11837. static get #mainThreadWorkerMessageHandler() {
  11838. try {
  11839. return globalThis.pdfjsWorker?.WorkerMessageHandler || null;
  11840. } catch {
  11841. return null;
  11842. }
  11843. }
  11844. static get _setupFakeWorkerGlobal() {
  11845. const loader = async () => {
  11846. if (this.#mainThreadWorkerMessageHandler) {
  11847. return this.#mainThreadWorkerMessageHandler;
  11848. }
  11849. const worker = await import( /*webpackIgnore: true*/this.workerSrc);
  11850. return worker.WorkerMessageHandler;
  11851. };
  11852. return shadow(this, "_setupFakeWorkerGlobal", loader());
  11853. }
  11854. }
  11855. class WorkerTransport {
  11856. #methodPromises = new Map();
  11857. #pageCache = new Map();
  11858. #pagePromises = new Map();
  11859. #pageRefCache = new Map();
  11860. #passwordCapability = null;
  11861. constructor(messageHandler, loadingTask, networkStream, params, factory) {
  11862. this.messageHandler = messageHandler;
  11863. this.loadingTask = loadingTask;
  11864. this.commonObjs = new PDFObjects();
  11865. this.fontLoader = new FontLoader({
  11866. ownerDocument: params.ownerDocument,
  11867. styleElement: params.styleElement
  11868. });
  11869. this.loadingParams = params.loadingParams;
  11870. this._params = params;
  11871. this.canvasFactory = factory.canvasFactory;
  11872. this.filterFactory = factory.filterFactory;
  11873. this.cMapReaderFactory = factory.cMapReaderFactory;
  11874. this.standardFontDataFactory = factory.standardFontDataFactory;
  11875. this.destroyed = false;
  11876. this.destroyCapability = null;
  11877. this._networkStream = networkStream;
  11878. this._fullReader = null;
  11879. this._lastProgress = null;
  11880. this.downloadInfoCapability = Promise.withResolvers();
  11881. this.setupMessageHandler();
  11882. }
  11883. #cacheSimpleMethod(name, data = null) {
  11884. const cachedPromise = this.#methodPromises.get(name);
  11885. if (cachedPromise) {
  11886. return cachedPromise;
  11887. }
  11888. const promise = this.messageHandler.sendWithPromise(name, data);
  11889. this.#methodPromises.set(name, promise);
  11890. return promise;
  11891. }
  11892. get annotationStorage() {
  11893. return shadow(this, "annotationStorage", new AnnotationStorage());
  11894. }
  11895. getRenderingIntent(intent, annotationMode = AnnotationMode.ENABLE, printAnnotationStorage = null, isEditing = false, isOpList = false) {
  11896. let renderingIntent = RenderingIntentFlag.DISPLAY;
  11897. let annotationStorageSerializable = SerializableEmpty;
  11898. switch (intent) {
  11899. case "any":
  11900. renderingIntent = RenderingIntentFlag.ANY;
  11901. break;
  11902. case "display":
  11903. break;
  11904. case "print":
  11905. renderingIntent = RenderingIntentFlag.PRINT;
  11906. break;
  11907. default:
  11908. warn(`getRenderingIntent - invalid intent: ${intent}`);
  11909. }
  11910. const annotationStorage = renderingIntent & RenderingIntentFlag.PRINT && printAnnotationStorage instanceof PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage;
  11911. switch (annotationMode) {
  11912. case AnnotationMode.DISABLE:
  11913. renderingIntent += RenderingIntentFlag.ANNOTATIONS_DISABLE;
  11914. break;
  11915. case AnnotationMode.ENABLE:
  11916. break;
  11917. case AnnotationMode.ENABLE_FORMS:
  11918. renderingIntent += RenderingIntentFlag.ANNOTATIONS_FORMS;
  11919. break;
  11920. case AnnotationMode.ENABLE_STORAGE:
  11921. renderingIntent += RenderingIntentFlag.ANNOTATIONS_STORAGE;
  11922. annotationStorageSerializable = annotationStorage.serializable;
  11923. break;
  11924. default:
  11925. warn(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);
  11926. }
  11927. if (isEditing) {
  11928. renderingIntent += RenderingIntentFlag.IS_EDITING;
  11929. }
  11930. if (isOpList) {
  11931. renderingIntent += RenderingIntentFlag.OPLIST;
  11932. }
  11933. const {
  11934. ids: modifiedIds,
  11935. hash: modifiedIdsHash
  11936. } = annotationStorage.modifiedIds;
  11937. const cacheKeyBuf = [renderingIntent, annotationStorageSerializable.hash, modifiedIdsHash];
  11938. return {
  11939. renderingIntent,
  11940. cacheKey: cacheKeyBuf.join("_"),
  11941. annotationStorageSerializable,
  11942. modifiedIds
  11943. };
  11944. }
  11945. destroy() {
  11946. if (this.destroyCapability) {
  11947. return this.destroyCapability.promise;
  11948. }
  11949. this.destroyed = true;
  11950. this.destroyCapability = Promise.withResolvers();
  11951. this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback"));
  11952. const waitOn = [];
  11953. for (const page of this.#pageCache.values()) {
  11954. waitOn.push(page._destroy());
  11955. }
  11956. this.#pageCache.clear();
  11957. this.#pagePromises.clear();
  11958. this.#pageRefCache.clear();
  11959. if (this.hasOwnProperty("annotationStorage")) {
  11960. this.annotationStorage.resetModified();
  11961. }
  11962. const terminated = this.messageHandler.sendWithPromise("Terminate", null);
  11963. waitOn.push(terminated);
  11964. Promise.all(waitOn).then(() => {
  11965. this.commonObjs.clear();
  11966. this.fontLoader.clear();
  11967. this.#methodPromises.clear();
  11968. this.filterFactory.destroy();
  11969. TextLayer.cleanup();
  11970. this._networkStream?.cancelAllRequests(new AbortException("Worker was terminated."));
  11971. if (this.messageHandler) {
  11972. this.messageHandler.destroy();
  11973. this.messageHandler = null;
  11974. }
  11975. this.destroyCapability.resolve();
  11976. }, this.destroyCapability.reject);
  11977. return this.destroyCapability.promise;
  11978. }
  11979. setupMessageHandler() {
  11980. const {
  11981. messageHandler,
  11982. loadingTask
  11983. } = this;
  11984. messageHandler.on("GetReader", (data, sink) => {
  11985. assert(this._networkStream, "GetReader - no `IPDFStream` instance available.");
  11986. this._fullReader = this._networkStream.getFullReader();
  11987. this._fullReader.onProgress = evt => {
  11988. this._lastProgress = {
  11989. loaded: evt.loaded,
  11990. total: evt.total
  11991. };
  11992. };
  11993. sink.onPull = () => {
  11994. this._fullReader.read().then(function ({
  11995. value,
  11996. done
  11997. }) {
  11998. if (done) {
  11999. sink.close();
  12000. return;
  12001. }
  12002. assert(value instanceof ArrayBuffer, "GetReader - expected an ArrayBuffer.");
  12003. sink.enqueue(new Uint8Array(value), 1, [value]);
  12004. }).catch(reason => {
  12005. sink.error(reason);
  12006. });
  12007. };
  12008. sink.onCancel = reason => {
  12009. this._fullReader.cancel(reason);
  12010. sink.ready.catch(readyReason => {
  12011. if (this.destroyed) {
  12012. return;
  12013. }
  12014. throw readyReason;
  12015. });
  12016. };
  12017. });
  12018. messageHandler.on("ReaderHeadersReady", data => {
  12019. const headersCapability = Promise.withResolvers();
  12020. const fullReader = this._fullReader;
  12021. fullReader.headersReady.then(() => {
  12022. if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) {
  12023. if (this._lastProgress) {
  12024. loadingTask.onProgress?.(this._lastProgress);
  12025. }
  12026. fullReader.onProgress = evt => {
  12027. loadingTask.onProgress?.({
  12028. loaded: evt.loaded,
  12029. total: evt.total
  12030. });
  12031. };
  12032. }
  12033. headersCapability.resolve({
  12034. isStreamingSupported: fullReader.isStreamingSupported,
  12035. isRangeSupported: fullReader.isRangeSupported,
  12036. contentLength: fullReader.contentLength
  12037. });
  12038. }, headersCapability.reject);
  12039. return headersCapability.promise;
  12040. });
  12041. messageHandler.on("GetRangeReader", (data, sink) => {
  12042. assert(this._networkStream, "GetRangeReader - no `IPDFStream` instance available.");
  12043. const rangeReader = this._networkStream.getRangeReader(data.begin, data.end);
  12044. if (!rangeReader) {
  12045. sink.close();
  12046. return;
  12047. }
  12048. sink.onPull = () => {
  12049. rangeReader.read().then(function ({
  12050. value,
  12051. done
  12052. }) {
  12053. if (done) {
  12054. sink.close();
  12055. return;
  12056. }
  12057. assert(value instanceof ArrayBuffer, "GetRangeReader - expected an ArrayBuffer.");
  12058. sink.enqueue(new Uint8Array(value), 1, [value]);
  12059. }).catch(reason => {
  12060. sink.error(reason);
  12061. });
  12062. };
  12063. sink.onCancel = reason => {
  12064. rangeReader.cancel(reason);
  12065. sink.ready.catch(readyReason => {
  12066. if (this.destroyed) {
  12067. return;
  12068. }
  12069. throw readyReason;
  12070. });
  12071. };
  12072. });
  12073. messageHandler.on("GetDoc", ({
  12074. pdfInfo
  12075. }) => {
  12076. this._numPages = pdfInfo.numPages;
  12077. this._htmlForXfa = pdfInfo.htmlForXfa;
  12078. delete pdfInfo.htmlForXfa;
  12079. loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this));
  12080. });
  12081. messageHandler.on("DocException", function (ex) {
  12082. let reason;
  12083. switch (ex.name) {
  12084. case "PasswordException":
  12085. reason = new PasswordException(ex.message, ex.code);
  12086. break;
  12087. case "InvalidPDFException":
  12088. reason = new InvalidPDFException(ex.message);
  12089. break;
  12090. case "MissingPDFException":
  12091. reason = new MissingPDFException(ex.message);
  12092. break;
  12093. case "UnexpectedResponseException":
  12094. reason = new UnexpectedResponseException(ex.message, ex.status);
  12095. break;
  12096. case "UnknownErrorException":
  12097. reason = new UnknownErrorException(ex.message, ex.details);
  12098. break;
  12099. default:
  12100. unreachable("DocException - expected a valid Error.");
  12101. }
  12102. loadingTask._capability.reject(reason);
  12103. });
  12104. messageHandler.on("PasswordRequest", exception => {
  12105. this.#passwordCapability = Promise.withResolvers();
  12106. if (loadingTask.onPassword) {
  12107. const updatePassword = password => {
  12108. if (password instanceof Error) {
  12109. this.#passwordCapability.reject(password);
  12110. } else {
  12111. this.#passwordCapability.resolve({
  12112. password
  12113. });
  12114. }
  12115. };
  12116. try {
  12117. loadingTask.onPassword(updatePassword, exception.code);
  12118. } catch (ex) {
  12119. this.#passwordCapability.reject(ex);
  12120. }
  12121. } else {
  12122. this.#passwordCapability.reject(new PasswordException(exception.message, exception.code));
  12123. }
  12124. return this.#passwordCapability.promise;
  12125. });
  12126. messageHandler.on("DataLoaded", data => {
  12127. loadingTask.onProgress?.({
  12128. loaded: data.length,
  12129. total: data.length
  12130. });
  12131. this.downloadInfoCapability.resolve(data);
  12132. });
  12133. messageHandler.on("StartRenderPage", data => {
  12134. if (this.destroyed) {
  12135. return;
  12136. }
  12137. const page = this.#pageCache.get(data.pageIndex);
  12138. page._startRenderPage(data.transparency, data.cacheKey);
  12139. });
  12140. messageHandler.on("commonobj", ([id, type, exportedData]) => {
  12141. if (this.destroyed) {
  12142. return null;
  12143. }
  12144. if (this.commonObjs.has(id)) {
  12145. return null;
  12146. }
  12147. switch (type) {
  12148. case "Font":
  12149. const {
  12150. disableFontFace,
  12151. fontExtraProperties,
  12152. pdfBug
  12153. } = this._params;
  12154. if ("error" in exportedData) {
  12155. const exportedError = exportedData.error;
  12156. warn(`Error during font loading: ${exportedError}`);
  12157. this.commonObjs.resolve(id, exportedError);
  12158. break;
  12159. }
  12160. const inspectFont = pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null;
  12161. const font = new FontFaceObject(exportedData, {
  12162. disableFontFace,
  12163. inspectFont
  12164. });
  12165. this.fontLoader.bind(font).catch(() => messageHandler.sendWithPromise("FontFallback", {
  12166. id
  12167. })).finally(() => {
  12168. if (!fontExtraProperties && font.data) {
  12169. font.data = null;
  12170. }
  12171. this.commonObjs.resolve(id, font);
  12172. });
  12173. break;
  12174. case "CopyLocalImage":
  12175. const {
  12176. imageRef
  12177. } = exportedData;
  12178. assert(imageRef, "The imageRef must be defined.");
  12179. for (const pageProxy of this.#pageCache.values()) {
  12180. for (const [, data] of pageProxy.objs) {
  12181. if (data?.ref !== imageRef) {
  12182. continue;
  12183. }
  12184. if (!data.dataLen) {
  12185. return null;
  12186. }
  12187. this.commonObjs.resolve(id, structuredClone(data));
  12188. return data.dataLen;
  12189. }
  12190. }
  12191. break;
  12192. case "FontPath":
  12193. case "Image":
  12194. case "Pattern":
  12195. this.commonObjs.resolve(id, exportedData);
  12196. break;
  12197. default:
  12198. throw new Error(`Got unknown common object type ${type}`);
  12199. }
  12200. return null;
  12201. });
  12202. messageHandler.on("obj", ([id, pageIndex, type, imageData]) => {
  12203. if (this.destroyed) {
  12204. return;
  12205. }
  12206. const pageProxy = this.#pageCache.get(pageIndex);
  12207. if (pageProxy.objs.has(id)) {
  12208. return;
  12209. }
  12210. if (pageProxy._intentStates.size === 0) {
  12211. imageData?.bitmap?.close();
  12212. return;
  12213. }
  12214. switch (type) {
  12215. case "Image":
  12216. pageProxy.objs.resolve(id, imageData);
  12217. if (imageData?.dataLen > MAX_IMAGE_SIZE_TO_CACHE) {
  12218. pageProxy._maybeCleanupAfterRender = true;
  12219. }
  12220. break;
  12221. case "Pattern":
  12222. pageProxy.objs.resolve(id, imageData);
  12223. break;
  12224. default:
  12225. throw new Error(`Got unknown object type ${type}`);
  12226. }
  12227. });
  12228. messageHandler.on("DocProgress", data => {
  12229. if (this.destroyed) {
  12230. return;
  12231. }
  12232. loadingTask.onProgress?.({
  12233. loaded: data.loaded,
  12234. total: data.total
  12235. });
  12236. });
  12237. messageHandler.on("FetchBuiltInCMap", data => {
  12238. if (this.destroyed) {
  12239. return Promise.reject(new Error("Worker was destroyed."));
  12240. }
  12241. if (!this.cMapReaderFactory) {
  12242. return Promise.reject(new Error("CMapReaderFactory not initialized, see the `useWorkerFetch` parameter."));
  12243. }
  12244. return this.cMapReaderFactory.fetch(data);
  12245. });
  12246. messageHandler.on("FetchStandardFontData", data => {
  12247. if (this.destroyed) {
  12248. return Promise.reject(new Error("Worker was destroyed."));
  12249. }
  12250. if (!this.standardFontDataFactory) {
  12251. return Promise.reject(new Error("StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter."));
  12252. }
  12253. return this.standardFontDataFactory.fetch(data);
  12254. });
  12255. }
  12256. getData() {
  12257. return this.messageHandler.sendWithPromise("GetData", null);
  12258. }
  12259. saveDocument() {
  12260. if (this.annotationStorage.size <= 0) {
  12261. warn("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead.");
  12262. }
  12263. const {
  12264. map,
  12265. transfer
  12266. } = this.annotationStorage.serializable;
  12267. return this.messageHandler.sendWithPromise("SaveDocument", {
  12268. isPureXfa: !!this._htmlForXfa,
  12269. numPages: this._numPages,
  12270. annotationStorage: map,
  12271. filename: this._fullReader?.filename ?? null
  12272. }, transfer).finally(() => {
  12273. this.annotationStorage.resetModified();
  12274. });
  12275. }
  12276. getPage(pageNumber) {
  12277. if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this._numPages) {
  12278. return Promise.reject(new Error("Invalid page request."));
  12279. }
  12280. const pageIndex = pageNumber - 1,
  12281. cachedPromise = this.#pagePromises.get(pageIndex);
  12282. if (cachedPromise) {
  12283. return cachedPromise;
  12284. }
  12285. const promise = this.messageHandler.sendWithPromise("GetPage", {
  12286. pageIndex
  12287. }).then(pageInfo => {
  12288. if (this.destroyed) {
  12289. throw new Error("Transport destroyed");
  12290. }
  12291. if (pageInfo.refStr) {
  12292. this.#pageRefCache.set(pageInfo.refStr, pageNumber);
  12293. }
  12294. const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.pdfBug);
  12295. this.#pageCache.set(pageIndex, page);
  12296. return page;
  12297. });
  12298. this.#pagePromises.set(pageIndex, promise);
  12299. return promise;
  12300. }
  12301. getPageIndex(ref) {
  12302. if (!isRefProxy(ref)) {
  12303. return Promise.reject(new Error("Invalid pageIndex request."));
  12304. }
  12305. return this.messageHandler.sendWithPromise("GetPageIndex", {
  12306. num: ref.num,
  12307. gen: ref.gen
  12308. });
  12309. }
  12310. getAnnotations(pageIndex, intent) {
  12311. return this.messageHandler.sendWithPromise("GetAnnotations", {
  12312. pageIndex,
  12313. intent
  12314. });
  12315. }
  12316. getFieldObjects() {
  12317. return this.#cacheSimpleMethod("GetFieldObjects");
  12318. }
  12319. hasJSActions() {
  12320. return this.#cacheSimpleMethod("HasJSActions");
  12321. }
  12322. getCalculationOrderIds() {
  12323. return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null);
  12324. }
  12325. getDestinations() {
  12326. return this.messageHandler.sendWithPromise("GetDestinations", null);
  12327. }
  12328. getDestination(id) {
  12329. if (typeof id !== "string") {
  12330. return Promise.reject(new Error("Invalid destination request."));
  12331. }
  12332. return this.messageHandler.sendWithPromise("GetDestination", {
  12333. id
  12334. });
  12335. }
  12336. getPageLabels() {
  12337. return this.messageHandler.sendWithPromise("GetPageLabels", null);
  12338. }
  12339. getPageLayout() {
  12340. return this.messageHandler.sendWithPromise("GetPageLayout", null);
  12341. }
  12342. getPageMode() {
  12343. return this.messageHandler.sendWithPromise("GetPageMode", null);
  12344. }
  12345. getViewerPreferences() {
  12346. return this.messageHandler.sendWithPromise("GetViewerPreferences", null);
  12347. }
  12348. getOpenAction() {
  12349. return this.messageHandler.sendWithPromise("GetOpenAction", null);
  12350. }
  12351. getAttachments() {
  12352. return this.messageHandler.sendWithPromise("GetAttachments", null);
  12353. }
  12354. getDocJSActions() {
  12355. return this.#cacheSimpleMethod("GetDocJSActions");
  12356. }
  12357. getPageJSActions(pageIndex) {
  12358. return this.messageHandler.sendWithPromise("GetPageJSActions", {
  12359. pageIndex
  12360. });
  12361. }
  12362. getStructTree(pageIndex) {
  12363. return this.messageHandler.sendWithPromise("GetStructTree", {
  12364. pageIndex
  12365. });
  12366. }
  12367. getOutline() {
  12368. return this.messageHandler.sendWithPromise("GetOutline", null);
  12369. }
  12370. getOptionalContentConfig(renderingIntent) {
  12371. return this.#cacheSimpleMethod("GetOptionalContentConfig").then(data => new OptionalContentConfig(data, renderingIntent));
  12372. }
  12373. getPermissions() {
  12374. return this.messageHandler.sendWithPromise("GetPermissions", null);
  12375. }
  12376. getMetadata() {
  12377. const name = "GetMetadata",
  12378. cachedPromise = this.#methodPromises.get(name);
  12379. if (cachedPromise) {
  12380. return cachedPromise;
  12381. }
  12382. const promise = this.messageHandler.sendWithPromise(name, null).then(results => ({
  12383. info: results[0],
  12384. metadata: results[1] ? new Metadata(results[1]) : null,
  12385. contentDispositionFilename: this._fullReader?.filename ?? null,
  12386. contentLength: this._fullReader?.contentLength ?? null
  12387. }));
  12388. this.#methodPromises.set(name, promise);
  12389. return promise;
  12390. }
  12391. getMarkInfo() {
  12392. return this.messageHandler.sendWithPromise("GetMarkInfo", null);
  12393. }
  12394. async startCleanup(keepLoadedFonts = false) {
  12395. if (this.destroyed) {
  12396. return;
  12397. }
  12398. await this.messageHandler.sendWithPromise("Cleanup", null);
  12399. for (const page of this.#pageCache.values()) {
  12400. const cleanupSuccessful = page.cleanup();
  12401. if (!cleanupSuccessful) {
  12402. throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`);
  12403. }
  12404. }
  12405. this.commonObjs.clear();
  12406. if (!keepLoadedFonts) {
  12407. this.fontLoader.clear();
  12408. }
  12409. this.#methodPromises.clear();
  12410. this.filterFactory.destroy(true);
  12411. TextLayer.cleanup();
  12412. }
  12413. cachedPageNumber(ref) {
  12414. if (!isRefProxy(ref)) {
  12415. return null;
  12416. }
  12417. const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`;
  12418. return this.#pageRefCache.get(refStr) ?? null;
  12419. }
  12420. }
  12421. const INITIAL_DATA = Symbol("INITIAL_DATA");
  12422. class PDFObjects {
  12423. #objs = Object.create(null);
  12424. #ensureObj(objId) {
  12425. return this.#objs[objId] ||= {
  12426. ...Promise.withResolvers(),
  12427. data: INITIAL_DATA
  12428. };
  12429. }
  12430. get(objId, callback = null) {
  12431. if (callback) {
  12432. const obj = this.#ensureObj(objId);
  12433. obj.promise.then(() => callback(obj.data));
  12434. return null;
  12435. }
  12436. const obj = this.#objs[objId];
  12437. if (!obj || obj.data === INITIAL_DATA) {
  12438. throw new Error(`Requesting object that isn't resolved yet ${objId}.`);
  12439. }
  12440. return obj.data;
  12441. }
  12442. has(objId) {
  12443. const obj = this.#objs[objId];
  12444. return !!obj && obj.data !== INITIAL_DATA;
  12445. }
  12446. resolve(objId, data = null) {
  12447. const obj = this.#ensureObj(objId);
  12448. obj.data = data;
  12449. obj.resolve();
  12450. }
  12451. clear() {
  12452. for (const objId in this.#objs) {
  12453. const {
  12454. data
  12455. } = this.#objs[objId];
  12456. data?.bitmap?.close();
  12457. }
  12458. this.#objs = Object.create(null);
  12459. }
  12460. *[Symbol.iterator]() {
  12461. for (const objId in this.#objs) {
  12462. const {
  12463. data
  12464. } = this.#objs[objId];
  12465. if (data === INITIAL_DATA) {
  12466. continue;
  12467. }
  12468. yield [objId, data];
  12469. }
  12470. }
  12471. }
  12472. class RenderTask {
  12473. #internalRenderTask = null;
  12474. constructor(internalRenderTask) {
  12475. this.#internalRenderTask = internalRenderTask;
  12476. this.onContinue = null;
  12477. }
  12478. get promise() {
  12479. return this.#internalRenderTask.capability.promise;
  12480. }
  12481. cancel(extraDelay = 0) {
  12482. this.#internalRenderTask.cancel(null, extraDelay);
  12483. }
  12484. get separateAnnots() {
  12485. const {
  12486. separateAnnots
  12487. } = this.#internalRenderTask.operatorList;
  12488. if (!separateAnnots) {
  12489. return false;
  12490. }
  12491. const {
  12492. annotationCanvasMap
  12493. } = this.#internalRenderTask;
  12494. return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0;
  12495. }
  12496. }
  12497. class InternalRenderTask {
  12498. #rAF = null;
  12499. static #canvasInUse = new WeakSet();
  12500. constructor({
  12501. callback,
  12502. params,
  12503. objs,
  12504. commonObjs,
  12505. annotationCanvasMap,
  12506. operatorList,
  12507. pageIndex,
  12508. canvasFactory,
  12509. filterFactory,
  12510. useRequestAnimationFrame = false,
  12511. pdfBug = false,
  12512. pageColors = null
  12513. }) {
  12514. this.callback = callback;
  12515. this.params = params;
  12516. this.objs = objs;
  12517. this.commonObjs = commonObjs;
  12518. this.annotationCanvasMap = annotationCanvasMap;
  12519. this.operatorListIdx = null;
  12520. this.operatorList = operatorList;
  12521. this._pageIndex = pageIndex;
  12522. this.canvasFactory = canvasFactory;
  12523. this.filterFactory = filterFactory;
  12524. this._pdfBug = pdfBug;
  12525. this.pageColors = pageColors;
  12526. this.running = false;
  12527. this.graphicsReadyCallback = null;
  12528. this.graphicsReady = false;
  12529. this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined";
  12530. this.cancelled = false;
  12531. this.capability = Promise.withResolvers();
  12532. this.task = new RenderTask(this);
  12533. this._cancelBound = this.cancel.bind(this);
  12534. this._continueBound = this._continue.bind(this);
  12535. this._scheduleNextBound = this._scheduleNext.bind(this);
  12536. this._nextBound = this._next.bind(this);
  12537. this._canvas = params.canvasContext.canvas;
  12538. }
  12539. get completed() {
  12540. return this.capability.promise.catch(function () {});
  12541. }
  12542. initializeGraphics({
  12543. transparency = false,
  12544. optionalContentConfig
  12545. }) {
  12546. if (this.cancelled) {
  12547. return;
  12548. }
  12549. if (this._canvas) {
  12550. if (InternalRenderTask.#canvasInUse.has(this._canvas)) {
  12551. throw new Error("Cannot use the same canvas during multiple render() operations. " + "Use different canvas or ensure previous operations were " + "cancelled or completed.");
  12552. }
  12553. InternalRenderTask.#canvasInUse.add(this._canvas);
  12554. }
  12555. if (this._pdfBug && globalThis.StepperManager?.enabled) {
  12556. this.stepper = globalThis.StepperManager.create(this._pageIndex);
  12557. this.stepper.init(this.operatorList);
  12558. this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
  12559. }
  12560. const {
  12561. canvasContext,
  12562. viewport,
  12563. transform,
  12564. background
  12565. } = this.params;
  12566. this.gfx = new CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {
  12567. optionalContentConfig
  12568. }, this.annotationCanvasMap, this.pageColors);
  12569. this.gfx.beginDrawing({
  12570. transform,
  12571. viewport,
  12572. transparency,
  12573. background
  12574. });
  12575. this.operatorListIdx = 0;
  12576. this.graphicsReady = true;
  12577. this.graphicsReadyCallback?.();
  12578. }
  12579. cancel(error = null, extraDelay = 0) {
  12580. this.running = false;
  12581. this.cancelled = true;
  12582. this.gfx?.endDrawing();
  12583. if (this.#rAF) {
  12584. window.cancelAnimationFrame(this.#rAF);
  12585. this.#rAF = null;
  12586. }
  12587. InternalRenderTask.#canvasInUse.delete(this._canvas);
  12588. this.callback(error || new RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay));
  12589. }
  12590. operatorListChanged() {
  12591. if (!this.graphicsReady) {
  12592. this.graphicsReadyCallback ||= this._continueBound;
  12593. return;
  12594. }
  12595. this.stepper?.updateOperatorList(this.operatorList);
  12596. if (this.running) {
  12597. return;
  12598. }
  12599. this._continue();
  12600. }
  12601. _continue() {
  12602. this.running = true;
  12603. if (this.cancelled) {
  12604. return;
  12605. }
  12606. if (this.task.onContinue) {
  12607. this.task.onContinue(this._scheduleNextBound);
  12608. } else {
  12609. this._scheduleNext();
  12610. }
  12611. }
  12612. _scheduleNext() {
  12613. if (this._useRequestAnimationFrame) {
  12614. this.#rAF = window.requestAnimationFrame(() => {
  12615. this.#rAF = null;
  12616. this._nextBound().catch(this._cancelBound);
  12617. });
  12618. } else {
  12619. Promise.resolve().then(this._nextBound).catch(this._cancelBound);
  12620. }
  12621. }
  12622. async _next() {
  12623. if (this.cancelled) {
  12624. return;
  12625. }
  12626. this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper);
  12627. if (this.operatorListIdx === this.operatorList.argsArray.length) {
  12628. this.running = false;
  12629. if (this.operatorList.lastChunk) {
  12630. this.gfx.endDrawing();
  12631. InternalRenderTask.#canvasInUse.delete(this._canvas);
  12632. this.callback();
  12633. }
  12634. }
  12635. }
  12636. }
  12637. const version = "4.5.136";
  12638. const build = "3a21f03b0";
  12639. ;// CONCATENATED MODULE: ./src/shared/scripting_utils.js
  12640. function makeColorComp(n) {
  12641. return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0");
  12642. }
  12643. function scaleAndClamp(x) {
  12644. return Math.max(0, Math.min(255, 255 * x));
  12645. }
  12646. class ColorConverters {
  12647. static CMYK_G([c, y, m, k]) {
  12648. return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)];
  12649. }
  12650. static G_CMYK([g]) {
  12651. return ["CMYK", 0, 0, 0, 1 - g];
  12652. }
  12653. static G_RGB([g]) {
  12654. return ["RGB", g, g, g];
  12655. }
  12656. static G_rgb([g]) {
  12657. g = scaleAndClamp(g);
  12658. return [g, g, g];
  12659. }
  12660. static G_HTML([g]) {
  12661. const G = makeColorComp(g);
  12662. return `#${G}${G}${G}`;
  12663. }
  12664. static RGB_G([r, g, b]) {
  12665. return ["G", 0.3 * r + 0.59 * g + 0.11 * b];
  12666. }
  12667. static RGB_rgb(color) {
  12668. return color.map(scaleAndClamp);
  12669. }
  12670. static RGB_HTML(color) {
  12671. return `#${color.map(makeColorComp).join("")}`;
  12672. }
  12673. static T_HTML() {
  12674. return "#00000000";
  12675. }
  12676. static T_rgb() {
  12677. return [null];
  12678. }
  12679. static CMYK_RGB([c, y, m, k]) {
  12680. return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)];
  12681. }
  12682. static CMYK_rgb([c, y, m, k]) {
  12683. return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))];
  12684. }
  12685. static CMYK_HTML(components) {
  12686. const rgb = this.CMYK_RGB(components).slice(1);
  12687. return this.RGB_HTML(rgb);
  12688. }
  12689. static RGB_CMYK([r, g, b]) {
  12690. const c = 1 - r;
  12691. const m = 1 - g;
  12692. const y = 1 - b;
  12693. const k = Math.min(c, m, y);
  12694. return ["CMYK", c, m, y, k];
  12695. }
  12696. }
  12697. ;// CONCATENATED MODULE: ./src/display/xfa_layer.js
  12698. class XfaLayer {
  12699. static setupStorage(html, id, element, storage, intent) {
  12700. const storedData = storage.getValue(id, {
  12701. value: null
  12702. });
  12703. switch (element.name) {
  12704. case "textarea":
  12705. if (storedData.value !== null) {
  12706. html.textContent = storedData.value;
  12707. }
  12708. if (intent === "print") {
  12709. break;
  12710. }
  12711. html.addEventListener("input", event => {
  12712. storage.setValue(id, {
  12713. value: event.target.value
  12714. });
  12715. });
  12716. break;
  12717. case "input":
  12718. if (element.attributes.type === "radio" || element.attributes.type === "checkbox") {
  12719. if (storedData.value === element.attributes.xfaOn) {
  12720. html.setAttribute("checked", true);
  12721. } else if (storedData.value === element.attributes.xfaOff) {
  12722. html.removeAttribute("checked");
  12723. }
  12724. if (intent === "print") {
  12725. break;
  12726. }
  12727. html.addEventListener("change", event => {
  12728. storage.setValue(id, {
  12729. value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff")
  12730. });
  12731. });
  12732. } else {
  12733. if (storedData.value !== null) {
  12734. html.setAttribute("value", storedData.value);
  12735. }
  12736. if (intent === "print") {
  12737. break;
  12738. }
  12739. html.addEventListener("input", event => {
  12740. storage.setValue(id, {
  12741. value: event.target.value
  12742. });
  12743. });
  12744. }
  12745. break;
  12746. case "select":
  12747. if (storedData.value !== null) {
  12748. html.setAttribute("value", storedData.value);
  12749. for (const option of element.children) {
  12750. if (option.attributes.value === storedData.value) {
  12751. option.attributes.selected = true;
  12752. } else if (option.attributes.hasOwnProperty("selected")) {
  12753. delete option.attributes.selected;
  12754. }
  12755. }
  12756. }
  12757. html.addEventListener("input", event => {
  12758. const options = event.target.options;
  12759. const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value;
  12760. storage.setValue(id, {
  12761. value
  12762. });
  12763. });
  12764. break;
  12765. }
  12766. }
  12767. static setAttributes({
  12768. html,
  12769. element,
  12770. storage = null,
  12771. intent,
  12772. linkService
  12773. }) {
  12774. const {
  12775. attributes
  12776. } = element;
  12777. const isHTMLAnchorElement = html instanceof HTMLAnchorElement;
  12778. if (attributes.type === "radio") {
  12779. attributes.name = `${attributes.name}-${intent}`;
  12780. }
  12781. for (const [key, value] of Object.entries(attributes)) {
  12782. if (value === null || value === undefined) {
  12783. continue;
  12784. }
  12785. switch (key) {
  12786. case "class":
  12787. if (value.length) {
  12788. html.setAttribute(key, value.join(" "));
  12789. }
  12790. break;
  12791. case "dataId":
  12792. break;
  12793. case "id":
  12794. html.setAttribute("data-element-id", value);
  12795. break;
  12796. case "style":
  12797. Object.assign(html.style, value);
  12798. break;
  12799. case "textContent":
  12800. html.textContent = value;
  12801. break;
  12802. default:
  12803. if (!isHTMLAnchorElement || key !== "href" && key !== "newWindow") {
  12804. html.setAttribute(key, value);
  12805. }
  12806. }
  12807. }
  12808. if (isHTMLAnchorElement) {
  12809. linkService.addLinkAttributes(html, attributes.href, attributes.newWindow);
  12810. }
  12811. if (storage && attributes.dataId) {
  12812. this.setupStorage(html, attributes.dataId, element, storage);
  12813. }
  12814. }
  12815. static render(parameters) {
  12816. const storage = parameters.annotationStorage;
  12817. const linkService = parameters.linkService;
  12818. const root = parameters.xfaHtml;
  12819. const intent = parameters.intent || "display";
  12820. const rootHtml = document.createElement(root.name);
  12821. if (root.attributes) {
  12822. this.setAttributes({
  12823. html: rootHtml,
  12824. element: root,
  12825. intent,
  12826. linkService
  12827. });
  12828. }
  12829. const isNotForRichText = intent !== "richText";
  12830. const rootDiv = parameters.div;
  12831. rootDiv.append(rootHtml);
  12832. if (parameters.viewport) {
  12833. const transform = `matrix(${parameters.viewport.transform.join(",")})`;
  12834. rootDiv.style.transform = transform;
  12835. }
  12836. if (isNotForRichText) {
  12837. rootDiv.setAttribute("class", "xfaLayer xfaFont");
  12838. }
  12839. const textDivs = [];
  12840. if (root.children.length === 0) {
  12841. if (root.value) {
  12842. const node = document.createTextNode(root.value);
  12843. rootHtml.append(node);
  12844. if (isNotForRichText && XfaText.shouldBuildText(root.name)) {
  12845. textDivs.push(node);
  12846. }
  12847. }
  12848. return {
  12849. textDivs
  12850. };
  12851. }
  12852. const stack = [[root, -1, rootHtml]];
  12853. while (stack.length > 0) {
  12854. const [parent, i, html] = stack.at(-1);
  12855. if (i + 1 === parent.children.length) {
  12856. stack.pop();
  12857. continue;
  12858. }
  12859. const child = parent.children[++stack.at(-1)[1]];
  12860. if (child === null) {
  12861. continue;
  12862. }
  12863. const {
  12864. name
  12865. } = child;
  12866. if (name === "#text") {
  12867. const node = document.createTextNode(child.value);
  12868. textDivs.push(node);
  12869. html.append(node);
  12870. continue;
  12871. }
  12872. const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name);
  12873. html.append(childHtml);
  12874. if (child.attributes) {
  12875. this.setAttributes({
  12876. html: childHtml,
  12877. element: child,
  12878. storage,
  12879. intent,
  12880. linkService
  12881. });
  12882. }
  12883. if (child.children?.length > 0) {
  12884. stack.push([child, -1, childHtml]);
  12885. } else if (child.value) {
  12886. const node = document.createTextNode(child.value);
  12887. if (isNotForRichText && XfaText.shouldBuildText(name)) {
  12888. textDivs.push(node);
  12889. }
  12890. childHtml.append(node);
  12891. }
  12892. }
  12893. for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) {
  12894. el.setAttribute("readOnly", true);
  12895. }
  12896. return {
  12897. textDivs
  12898. };
  12899. }
  12900. static update(parameters) {
  12901. const transform = `matrix(${parameters.viewport.transform.join(",")})`;
  12902. parameters.div.style.transform = transform;
  12903. parameters.div.hidden = false;
  12904. }
  12905. }
  12906. ;// CONCATENATED MODULE: ./src/display/annotation_layer.js
  12907. const DEFAULT_TAB_INDEX = 1000;
  12908. const annotation_layer_DEFAULT_FONT_SIZE = 9;
  12909. const GetElementsByNameSet = new WeakSet();
  12910. function getRectDims(rect) {
  12911. return {
  12912. width: rect[2] - rect[0],
  12913. height: rect[3] - rect[1]
  12914. };
  12915. }
  12916. class AnnotationElementFactory {
  12917. static create(parameters) {
  12918. const subtype = parameters.data.annotationType;
  12919. switch (subtype) {
  12920. case AnnotationType.LINK:
  12921. return new LinkAnnotationElement(parameters);
  12922. case AnnotationType.TEXT:
  12923. return new TextAnnotationElement(parameters);
  12924. case AnnotationType.WIDGET:
  12925. const fieldType = parameters.data.fieldType;
  12926. switch (fieldType) {
  12927. case "Tx":
  12928. return new TextWidgetAnnotationElement(parameters);
  12929. case "Btn":
  12930. if (parameters.data.radioButton) {
  12931. return new RadioButtonWidgetAnnotationElement(parameters);
  12932. } else if (parameters.data.checkBox) {
  12933. return new CheckboxWidgetAnnotationElement(parameters);
  12934. }
  12935. return new PushButtonWidgetAnnotationElement(parameters);
  12936. case "Ch":
  12937. return new ChoiceWidgetAnnotationElement(parameters);
  12938. case "Sig":
  12939. return new SignatureWidgetAnnotationElement(parameters);
  12940. }
  12941. return new WidgetAnnotationElement(parameters);
  12942. case AnnotationType.POPUP:
  12943. return new PopupAnnotationElement(parameters);
  12944. case AnnotationType.FREETEXT:
  12945. return new FreeTextAnnotationElement(parameters);
  12946. case AnnotationType.LINE:
  12947. return new LineAnnotationElement(parameters);
  12948. case AnnotationType.SQUARE:
  12949. return new SquareAnnotationElement(parameters);
  12950. case AnnotationType.CIRCLE:
  12951. return new CircleAnnotationElement(parameters);
  12952. case AnnotationType.POLYLINE:
  12953. return new PolylineAnnotationElement(parameters);
  12954. case AnnotationType.CARET:
  12955. return new CaretAnnotationElement(parameters);
  12956. case AnnotationType.INK:
  12957. return new InkAnnotationElement(parameters);
  12958. case AnnotationType.POLYGON:
  12959. return new PolygonAnnotationElement(parameters);
  12960. case AnnotationType.HIGHLIGHT:
  12961. return new HighlightAnnotationElement(parameters);
  12962. case AnnotationType.UNDERLINE:
  12963. return new UnderlineAnnotationElement(parameters);
  12964. case AnnotationType.SQUIGGLY:
  12965. return new SquigglyAnnotationElement(parameters);
  12966. case AnnotationType.STRIKEOUT:
  12967. return new StrikeOutAnnotationElement(parameters);
  12968. case AnnotationType.STAMP:
  12969. return new StampAnnotationElement(parameters);
  12970. case AnnotationType.FILEATTACHMENT:
  12971. return new FileAttachmentAnnotationElement(parameters);
  12972. default:
  12973. return new AnnotationElement(parameters);
  12974. }
  12975. }
  12976. }
  12977. class AnnotationElement {
  12978. #updates = null;
  12979. #hasBorder = false;
  12980. #popupElement = null;
  12981. constructor(parameters, {
  12982. isRenderable = false,
  12983. ignoreBorder = false,
  12984. createQuadrilaterals = false
  12985. } = {}) {
  12986. this.isRenderable = isRenderable;
  12987. this.data = parameters.data;
  12988. this.layer = parameters.layer;
  12989. this.linkService = parameters.linkService;
  12990. this.downloadManager = parameters.downloadManager;
  12991. this.imageResourcesPath = parameters.imageResourcesPath;
  12992. this.renderForms = parameters.renderForms;
  12993. this.svgFactory = parameters.svgFactory;
  12994. this.annotationStorage = parameters.annotationStorage;
  12995. this.enableScripting = parameters.enableScripting;
  12996. this.hasJSActions = parameters.hasJSActions;
  12997. this._fieldObjects = parameters.fieldObjects;
  12998. this.parent = parameters.parent;
  12999. if (isRenderable) {
  13000. this.container = this._createContainer(ignoreBorder);
  13001. }
  13002. if (createQuadrilaterals) {
  13003. this._createQuadrilaterals();
  13004. }
  13005. }
  13006. static _hasPopupData({
  13007. titleObj,
  13008. contentsObj,
  13009. richText
  13010. }) {
  13011. return !!(titleObj?.str || contentsObj?.str || richText?.str);
  13012. }
  13013. get _isEditable() {
  13014. return this.data.isEditable;
  13015. }
  13016. get hasPopupData() {
  13017. return AnnotationElement._hasPopupData(this.data);
  13018. }
  13019. updateEdited(params) {
  13020. if (!this.container) {
  13021. return;
  13022. }
  13023. this.#updates ||= {
  13024. rect: this.data.rect.slice(0)
  13025. };
  13026. const {
  13027. rect
  13028. } = params;
  13029. if (rect) {
  13030. this.#setRectEdited(rect);
  13031. }
  13032. this.#popupElement?.popup.updateEdited(params);
  13033. }
  13034. resetEdited() {
  13035. if (!this.#updates) {
  13036. return;
  13037. }
  13038. this.#setRectEdited(this.#updates.rect);
  13039. this.#popupElement?.popup.resetEdited();
  13040. this.#updates = null;
  13041. }
  13042. #setRectEdited(rect) {
  13043. const {
  13044. container: {
  13045. style
  13046. },
  13047. data: {
  13048. rect: currentRect,
  13049. rotation
  13050. },
  13051. parent: {
  13052. viewport: {
  13053. rawDims: {
  13054. pageWidth,
  13055. pageHeight,
  13056. pageX,
  13057. pageY
  13058. }
  13059. }
  13060. }
  13061. } = this;
  13062. currentRect?.splice(0, 4, ...rect);
  13063. const {
  13064. width,
  13065. height
  13066. } = getRectDims(rect);
  13067. style.left = `${100 * (rect[0] - pageX) / pageWidth}%`;
  13068. style.top = `${100 * (pageHeight - rect[3] + pageY) / pageHeight}%`;
  13069. if (rotation === 0) {
  13070. style.width = `${100 * width / pageWidth}%`;
  13071. style.height = `${100 * height / pageHeight}%`;
  13072. } else {
  13073. this.setRotation(rotation);
  13074. }
  13075. }
  13076. _createContainer(ignoreBorder) {
  13077. const {
  13078. data,
  13079. parent: {
  13080. page,
  13081. viewport
  13082. }
  13083. } = this;
  13084. const container = document.createElement("section");
  13085. container.setAttribute("data-annotation-id", data.id);
  13086. if (!(this instanceof WidgetAnnotationElement)) {
  13087. container.tabIndex = DEFAULT_TAB_INDEX;
  13088. }
  13089. const {
  13090. style
  13091. } = container;
  13092. style.zIndex = this.parent.zIndex++;
  13093. if (data.popupRef) {
  13094. container.setAttribute("aria-haspopup", "dialog");
  13095. }
  13096. if (data.alternativeText) {
  13097. container.title = data.alternativeText;
  13098. }
  13099. if (data.noRotate) {
  13100. container.classList.add("norotate");
  13101. }
  13102. if (!data.rect || this instanceof PopupAnnotationElement) {
  13103. const {
  13104. rotation
  13105. } = data;
  13106. if (!data.hasOwnCanvas && rotation !== 0) {
  13107. this.setRotation(rotation, container);
  13108. }
  13109. return container;
  13110. }
  13111. const {
  13112. width,
  13113. height
  13114. } = getRectDims(data.rect);
  13115. if (!ignoreBorder && data.borderStyle.width > 0) {
  13116. style.borderWidth = `${data.borderStyle.width}px`;
  13117. const horizontalRadius = data.borderStyle.horizontalCornerRadius;
  13118. const verticalRadius = data.borderStyle.verticalCornerRadius;
  13119. if (horizontalRadius > 0 || verticalRadius > 0) {
  13120. const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`;
  13121. style.borderRadius = radius;
  13122. } else if (this instanceof RadioButtonWidgetAnnotationElement) {
  13123. const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`;
  13124. style.borderRadius = radius;
  13125. }
  13126. switch (data.borderStyle.style) {
  13127. case AnnotationBorderStyleType.SOLID:
  13128. style.borderStyle = "solid";
  13129. break;
  13130. case AnnotationBorderStyleType.DASHED:
  13131. style.borderStyle = "dashed";
  13132. break;
  13133. case AnnotationBorderStyleType.BEVELED:
  13134. warn("Unimplemented border style: beveled");
  13135. break;
  13136. case AnnotationBorderStyleType.INSET:
  13137. warn("Unimplemented border style: inset");
  13138. break;
  13139. case AnnotationBorderStyleType.UNDERLINE:
  13140. style.borderBottomStyle = "solid";
  13141. break;
  13142. default:
  13143. break;
  13144. }
  13145. const borderColor = data.borderColor || null;
  13146. if (borderColor) {
  13147. this.#hasBorder = true;
  13148. style.borderColor = Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0);
  13149. } else {
  13150. style.borderWidth = 0;
  13151. }
  13152. }
  13153. 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]]);
  13154. const {
  13155. pageWidth,
  13156. pageHeight,
  13157. pageX,
  13158. pageY
  13159. } = viewport.rawDims;
  13160. style.left = `${100 * (rect[0] - pageX) / pageWidth}%`;
  13161. style.top = `${100 * (rect[1] - pageY) / pageHeight}%`;
  13162. const {
  13163. rotation
  13164. } = data;
  13165. if (data.hasOwnCanvas || rotation === 0) {
  13166. style.width = `${100 * width / pageWidth}%`;
  13167. style.height = `${100 * height / pageHeight}%`;
  13168. } else {
  13169. this.setRotation(rotation, container);
  13170. }
  13171. return container;
  13172. }
  13173. setRotation(angle, container = this.container) {
  13174. if (!this.data.rect) {
  13175. return;
  13176. }
  13177. const {
  13178. pageWidth,
  13179. pageHeight
  13180. } = this.parent.viewport.rawDims;
  13181. const {
  13182. width,
  13183. height
  13184. } = getRectDims(this.data.rect);
  13185. let elementWidth, elementHeight;
  13186. if (angle % 180 === 0) {
  13187. elementWidth = 100 * width / pageWidth;
  13188. elementHeight = 100 * height / pageHeight;
  13189. } else {
  13190. elementWidth = 100 * height / pageWidth;
  13191. elementHeight = 100 * width / pageHeight;
  13192. }
  13193. container.style.width = `${elementWidth}%`;
  13194. container.style.height = `${elementHeight}%`;
  13195. container.setAttribute("data-main-rotation", (360 - angle) % 360);
  13196. }
  13197. get _commonActions() {
  13198. const setColor = (jsName, styleName, event) => {
  13199. const color = event.detail[jsName];
  13200. const colorType = color[0];
  13201. const colorArray = color.slice(1);
  13202. event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray);
  13203. this.annotationStorage.setValue(this.data.id, {
  13204. [styleName]: ColorConverters[`${colorType}_rgb`](colorArray)
  13205. });
  13206. };
  13207. return shadow(this, "_commonActions", {
  13208. display: event => {
  13209. const {
  13210. display
  13211. } = event.detail;
  13212. const hidden = display % 2 === 1;
  13213. this.container.style.visibility = hidden ? "hidden" : "visible";
  13214. this.annotationStorage.setValue(this.data.id, {
  13215. noView: hidden,
  13216. noPrint: display === 1 || display === 2
  13217. });
  13218. },
  13219. print: event => {
  13220. this.annotationStorage.setValue(this.data.id, {
  13221. noPrint: !event.detail.print
  13222. });
  13223. },
  13224. hidden: event => {
  13225. const {
  13226. hidden
  13227. } = event.detail;
  13228. this.container.style.visibility = hidden ? "hidden" : "visible";
  13229. this.annotationStorage.setValue(this.data.id, {
  13230. noPrint: hidden,
  13231. noView: hidden
  13232. });
  13233. },
  13234. focus: event => {
  13235. setTimeout(() => event.target.focus({
  13236. preventScroll: false
  13237. }), 0);
  13238. },
  13239. userName: event => {
  13240. event.target.title = event.detail.userName;
  13241. },
  13242. readonly: event => {
  13243. event.target.disabled = event.detail.readonly;
  13244. },
  13245. required: event => {
  13246. this._setRequired(event.target, event.detail.required);
  13247. },
  13248. bgColor: event => {
  13249. setColor("bgColor", "backgroundColor", event);
  13250. },
  13251. fillColor: event => {
  13252. setColor("fillColor", "backgroundColor", event);
  13253. },
  13254. fgColor: event => {
  13255. setColor("fgColor", "color", event);
  13256. },
  13257. textColor: event => {
  13258. setColor("textColor", "color", event);
  13259. },
  13260. borderColor: event => {
  13261. setColor("borderColor", "borderColor", event);
  13262. },
  13263. strokeColor: event => {
  13264. setColor("strokeColor", "borderColor", event);
  13265. },
  13266. rotation: event => {
  13267. const angle = event.detail.rotation;
  13268. this.setRotation(angle);
  13269. this.annotationStorage.setValue(this.data.id, {
  13270. rotation: angle
  13271. });
  13272. }
  13273. });
  13274. }
  13275. _dispatchEventFromSandbox(actions, jsEvent) {
  13276. const commonActions = this._commonActions;
  13277. for (const name of Object.keys(jsEvent.detail)) {
  13278. const action = actions[name] || commonActions[name];
  13279. action?.(jsEvent);
  13280. }
  13281. }
  13282. _setDefaultPropertiesFromJS(element) {
  13283. if (!this.enableScripting) {
  13284. return;
  13285. }
  13286. const storedData = this.annotationStorage.getRawValue(this.data.id);
  13287. if (!storedData) {
  13288. return;
  13289. }
  13290. const commonActions = this._commonActions;
  13291. for (const [actionName, detail] of Object.entries(storedData)) {
  13292. const action = commonActions[actionName];
  13293. if (action) {
  13294. const eventProxy = {
  13295. detail: {
  13296. [actionName]: detail
  13297. },
  13298. target: element
  13299. };
  13300. action(eventProxy);
  13301. delete storedData[actionName];
  13302. }
  13303. }
  13304. }
  13305. _createQuadrilaterals() {
  13306. if (!this.container) {
  13307. return;
  13308. }
  13309. const {
  13310. quadPoints
  13311. } = this.data;
  13312. if (!quadPoints) {
  13313. return;
  13314. }
  13315. const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect.map(x => Math.fround(x));
  13316. if (quadPoints.length === 8) {
  13317. const [trX, trY, blX, blY] = quadPoints.subarray(2, 6);
  13318. if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) {
  13319. return;
  13320. }
  13321. }
  13322. const {
  13323. style
  13324. } = this.container;
  13325. let svgBuffer;
  13326. if (this.#hasBorder) {
  13327. const {
  13328. borderColor,
  13329. borderWidth
  13330. } = style;
  13331. style.borderWidth = 0;
  13332. 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}">`];
  13333. this.container.classList.add("hasBorder");
  13334. }
  13335. const width = rectTrX - rectBlX;
  13336. const height = rectTrY - rectBlY;
  13337. const {
  13338. svgFactory
  13339. } = this;
  13340. const svg = svgFactory.createElement("svg");
  13341. svg.classList.add("quadrilateralsContainer");
  13342. svg.setAttribute("width", 0);
  13343. svg.setAttribute("height", 0);
  13344. const defs = svgFactory.createElement("defs");
  13345. svg.append(defs);
  13346. const clipPath = svgFactory.createElement("clipPath");
  13347. const id = `clippath_${this.data.id}`;
  13348. clipPath.setAttribute("id", id);
  13349. clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
  13350. defs.append(clipPath);
  13351. for (let i = 2, ii = quadPoints.length; i < ii; i += 8) {
  13352. const trX = quadPoints[i];
  13353. const trY = quadPoints[i + 1];
  13354. const blX = quadPoints[i + 2];
  13355. const blY = quadPoints[i + 3];
  13356. const rect = svgFactory.createElement("rect");
  13357. const x = (blX - rectBlX) / width;
  13358. const y = (rectTrY - trY) / height;
  13359. const rectWidth = (trX - blX) / width;
  13360. const rectHeight = (trY - blY) / height;
  13361. rect.setAttribute("x", x);
  13362. rect.setAttribute("y", y);
  13363. rect.setAttribute("width", rectWidth);
  13364. rect.setAttribute("height", rectHeight);
  13365. clipPath.append(rect);
  13366. svgBuffer?.push(`<rect vector-effect="non-scaling-stroke" x="${x}" y="${y}" width="${rectWidth}" height="${rectHeight}"/>`);
  13367. }
  13368. if (this.#hasBorder) {
  13369. svgBuffer.push(`</g></svg>')`);
  13370. style.backgroundImage = svgBuffer.join("");
  13371. }
  13372. this.container.append(svg);
  13373. this.container.style.clipPath = `url(#${id})`;
  13374. }
  13375. _createPopup() {
  13376. const {
  13377. container,
  13378. data
  13379. } = this;
  13380. container.setAttribute("aria-haspopup", "dialog");
  13381. const popup = this.#popupElement = new PopupAnnotationElement({
  13382. data: {
  13383. color: data.color,
  13384. titleObj: data.titleObj,
  13385. modificationDate: data.modificationDate,
  13386. contentsObj: data.contentsObj,
  13387. richText: data.richText,
  13388. parentRect: data.rect,
  13389. borderStyle: 0,
  13390. id: `popup_${data.id}`,
  13391. rotation: data.rotation
  13392. },
  13393. parent: this.parent,
  13394. elements: [this]
  13395. });
  13396. this.parent.div.append(popup.render());
  13397. }
  13398. render() {
  13399. unreachable("Abstract method `AnnotationElement.render` called");
  13400. }
  13401. _getElementsByName(name, skipId = null) {
  13402. const fields = [];
  13403. if (this._fieldObjects) {
  13404. const fieldObj = this._fieldObjects[name];
  13405. if (fieldObj) {
  13406. for (const {
  13407. page,
  13408. id,
  13409. exportValues
  13410. } of fieldObj) {
  13411. if (page === -1) {
  13412. continue;
  13413. }
  13414. if (id === skipId) {
  13415. continue;
  13416. }
  13417. const exportValue = typeof exportValues === "string" ? exportValues : null;
  13418. const domElement = document.querySelector(`[data-element-id="${id}"]`);
  13419. if (domElement && !GetElementsByNameSet.has(domElement)) {
  13420. warn(`_getElementsByName - element not allowed: ${id}`);
  13421. continue;
  13422. }
  13423. fields.push({
  13424. id,
  13425. exportValue,
  13426. domElement
  13427. });
  13428. }
  13429. }
  13430. return fields;
  13431. }
  13432. for (const domElement of document.getElementsByName(name)) {
  13433. const {
  13434. exportValue
  13435. } = domElement;
  13436. const id = domElement.getAttribute("data-element-id");
  13437. if (id === skipId) {
  13438. continue;
  13439. }
  13440. if (!GetElementsByNameSet.has(domElement)) {
  13441. continue;
  13442. }
  13443. fields.push({
  13444. id,
  13445. exportValue,
  13446. domElement
  13447. });
  13448. }
  13449. return fields;
  13450. }
  13451. show() {
  13452. if (this.container) {
  13453. this.container.hidden = false;
  13454. }
  13455. this.popup?.maybeShow();
  13456. }
  13457. hide() {
  13458. if (this.container) {
  13459. this.container.hidden = true;
  13460. }
  13461. this.popup?.forceHide();
  13462. }
  13463. getElementsToTriggerPopup() {
  13464. return this.container;
  13465. }
  13466. addHighlightArea() {
  13467. const triggers = this.getElementsToTriggerPopup();
  13468. if (Array.isArray(triggers)) {
  13469. for (const element of triggers) {
  13470. element.classList.add("highlightArea");
  13471. }
  13472. } else {
  13473. triggers.classList.add("highlightArea");
  13474. }
  13475. }
  13476. _editOnDoubleClick() {
  13477. if (!this._isEditable) {
  13478. return;
  13479. }
  13480. const {
  13481. annotationEditorType: mode,
  13482. data: {
  13483. id: editId
  13484. }
  13485. } = this;
  13486. this.container.addEventListener("dblclick", () => {
  13487. this.linkService.eventBus?.dispatch("switchannotationeditormode", {
  13488. source: this,
  13489. mode,
  13490. editId
  13491. });
  13492. });
  13493. }
  13494. }
  13495. class LinkAnnotationElement extends AnnotationElement {
  13496. constructor(parameters, options = null) {
  13497. super(parameters, {
  13498. isRenderable: true,
  13499. ignoreBorder: !!options?.ignoreBorder,
  13500. createQuadrilaterals: true
  13501. });
  13502. this.isTooltipOnly = parameters.data.isTooltipOnly;
  13503. }
  13504. render() {
  13505. const {
  13506. data,
  13507. linkService
  13508. } = this;
  13509. const link = document.createElement("a");
  13510. link.setAttribute("data-element-id", data.id);
  13511. let isBound = false;
  13512. if (data.url) {
  13513. linkService.addLinkAttributes(link, data.url, data.newWindow);
  13514. isBound = true;
  13515. } else if (data.action) {
  13516. this._bindNamedAction(link, data.action);
  13517. isBound = true;
  13518. } else if (data.attachment) {
  13519. this.#bindAttachment(link, data.attachment, data.attachmentDest);
  13520. isBound = true;
  13521. } else if (data.setOCGState) {
  13522. this.#bindSetOCGState(link, data.setOCGState);
  13523. isBound = true;
  13524. } else if (data.dest) {
  13525. this._bindLink(link, data.dest);
  13526. isBound = true;
  13527. } else {
  13528. if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) {
  13529. this._bindJSAction(link, data);
  13530. isBound = true;
  13531. }
  13532. if (data.resetForm) {
  13533. this._bindResetFormAction(link, data.resetForm);
  13534. isBound = true;
  13535. } else if (this.isTooltipOnly && !isBound) {
  13536. this._bindLink(link, "");
  13537. isBound = true;
  13538. }
  13539. }
  13540. this.container.classList.add("linkAnnotation");
  13541. if (isBound) {
  13542. this.container.append(link);
  13543. }
  13544. return this.container;
  13545. }
  13546. #setInternalLink() {
  13547. this.container.setAttribute("data-internal-link", "");
  13548. }
  13549. _bindLink(link, destination) {
  13550. link.href = this.linkService.getDestinationHash(destination);
  13551. link.onclick = () => {
  13552. if (destination) {
  13553. this.linkService.goToDestination(destination);
  13554. }
  13555. return false;
  13556. };
  13557. if (destination || destination === "") {
  13558. this.#setInternalLink();
  13559. }
  13560. }
  13561. _bindNamedAction(link, action) {
  13562. link.href = this.linkService.getAnchorUrl("");
  13563. link.onclick = () => {
  13564. this.linkService.executeNamedAction(action);
  13565. return false;
  13566. };
  13567. this.#setInternalLink();
  13568. }
  13569. #bindAttachment(link, attachment, dest = null) {
  13570. link.href = this.linkService.getAnchorUrl("");
  13571. if (attachment.description) {
  13572. link.title = attachment.description;
  13573. }
  13574. link.onclick = () => {
  13575. this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest);
  13576. return false;
  13577. };
  13578. this.#setInternalLink();
  13579. }
  13580. #bindSetOCGState(link, action) {
  13581. link.href = this.linkService.getAnchorUrl("");
  13582. link.onclick = () => {
  13583. this.linkService.executeSetOCGState(action);
  13584. return false;
  13585. };
  13586. this.#setInternalLink();
  13587. }
  13588. _bindJSAction(link, data) {
  13589. link.href = this.linkService.getAnchorUrl("");
  13590. const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]);
  13591. for (const name of Object.keys(data.actions)) {
  13592. const jsName = map.get(name);
  13593. if (!jsName) {
  13594. continue;
  13595. }
  13596. link[jsName] = () => {
  13597. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  13598. source: this,
  13599. detail: {
  13600. id: data.id,
  13601. name
  13602. }
  13603. });
  13604. return false;
  13605. };
  13606. }
  13607. if (!link.onclick) {
  13608. link.onclick = () => false;
  13609. }
  13610. this.#setInternalLink();
  13611. }
  13612. _bindResetFormAction(link, resetForm) {
  13613. const otherClickAction = link.onclick;
  13614. if (!otherClickAction) {
  13615. link.href = this.linkService.getAnchorUrl("");
  13616. }
  13617. this.#setInternalLink();
  13618. if (!this._fieldObjects) {
  13619. warn(`_bindResetFormAction - "resetForm" action not supported, ` + "ensure that the `fieldObjects` parameter is provided.");
  13620. if (!otherClickAction) {
  13621. link.onclick = () => false;
  13622. }
  13623. return;
  13624. }
  13625. link.onclick = () => {
  13626. otherClickAction?.();
  13627. const {
  13628. fields: resetFormFields,
  13629. refs: resetFormRefs,
  13630. include
  13631. } = resetForm;
  13632. const allFields = [];
  13633. if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) {
  13634. const fieldIds = new Set(resetFormRefs);
  13635. for (const fieldName of resetFormFields) {
  13636. const fields = this._fieldObjects[fieldName] || [];
  13637. for (const {
  13638. id
  13639. } of fields) {
  13640. fieldIds.add(id);
  13641. }
  13642. }
  13643. for (const fields of Object.values(this._fieldObjects)) {
  13644. for (const field of fields) {
  13645. if (fieldIds.has(field.id) === include) {
  13646. allFields.push(field);
  13647. }
  13648. }
  13649. }
  13650. } else {
  13651. for (const fields of Object.values(this._fieldObjects)) {
  13652. allFields.push(...fields);
  13653. }
  13654. }
  13655. const storage = this.annotationStorage;
  13656. const allIds = [];
  13657. for (const field of allFields) {
  13658. const {
  13659. id
  13660. } = field;
  13661. allIds.push(id);
  13662. switch (field.type) {
  13663. case "text":
  13664. {
  13665. const value = field.defaultValue || "";
  13666. storage.setValue(id, {
  13667. value
  13668. });
  13669. break;
  13670. }
  13671. case "checkbox":
  13672. case "radiobutton":
  13673. {
  13674. const value = field.defaultValue === field.exportValues;
  13675. storage.setValue(id, {
  13676. value
  13677. });
  13678. break;
  13679. }
  13680. case "combobox":
  13681. case "listbox":
  13682. {
  13683. const value = field.defaultValue || "";
  13684. storage.setValue(id, {
  13685. value
  13686. });
  13687. break;
  13688. }
  13689. default:
  13690. continue;
  13691. }
  13692. const domElement = document.querySelector(`[data-element-id="${id}"]`);
  13693. if (!domElement) {
  13694. continue;
  13695. } else if (!GetElementsByNameSet.has(domElement)) {
  13696. warn(`_bindResetFormAction - element not allowed: ${id}`);
  13697. continue;
  13698. }
  13699. domElement.dispatchEvent(new Event("resetform"));
  13700. }
  13701. if (this.enableScripting) {
  13702. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  13703. source: this,
  13704. detail: {
  13705. id: "app",
  13706. ids: allIds,
  13707. name: "ResetForm"
  13708. }
  13709. });
  13710. }
  13711. return false;
  13712. };
  13713. }
  13714. }
  13715. class TextAnnotationElement extends AnnotationElement {
  13716. constructor(parameters) {
  13717. super(parameters, {
  13718. isRenderable: true
  13719. });
  13720. }
  13721. render() {
  13722. this.container.classList.add("textAnnotation");
  13723. const image = document.createElement("img");
  13724. image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg";
  13725. image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type");
  13726. image.setAttribute("data-l10n-args", JSON.stringify({
  13727. type: this.data.name
  13728. }));
  13729. if (!this.data.popupRef && this.hasPopupData) {
  13730. this._createPopup();
  13731. }
  13732. this.container.append(image);
  13733. return this.container;
  13734. }
  13735. }
  13736. class WidgetAnnotationElement extends AnnotationElement {
  13737. render() {
  13738. return this.container;
  13739. }
  13740. showElementAndHideCanvas(element) {
  13741. if (this.data.hasOwnCanvas) {
  13742. if (element.previousSibling?.nodeName === "CANVAS") {
  13743. element.previousSibling.hidden = true;
  13744. }
  13745. element.hidden = false;
  13746. }
  13747. }
  13748. _getKeyModifier(event) {
  13749. return util_FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey;
  13750. }
  13751. _setEventListener(element, elementData, baseName, eventName, valueGetter) {
  13752. if (baseName.includes("mouse")) {
  13753. element.addEventListener(baseName, event => {
  13754. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  13755. source: this,
  13756. detail: {
  13757. id: this.data.id,
  13758. name: eventName,
  13759. value: valueGetter(event),
  13760. shift: event.shiftKey,
  13761. modifier: this._getKeyModifier(event)
  13762. }
  13763. });
  13764. });
  13765. } else {
  13766. element.addEventListener(baseName, event => {
  13767. if (baseName === "blur") {
  13768. if (!elementData.focused || !event.relatedTarget) {
  13769. return;
  13770. }
  13771. elementData.focused = false;
  13772. } else if (baseName === "focus") {
  13773. if (elementData.focused) {
  13774. return;
  13775. }
  13776. elementData.focused = true;
  13777. }
  13778. if (!valueGetter) {
  13779. return;
  13780. }
  13781. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  13782. source: this,
  13783. detail: {
  13784. id: this.data.id,
  13785. name: eventName,
  13786. value: valueGetter(event)
  13787. }
  13788. });
  13789. });
  13790. }
  13791. }
  13792. _setEventListeners(element, elementData, names, getter) {
  13793. for (const [baseName, eventName] of names) {
  13794. if (eventName === "Action" || this.data.actions?.[eventName]) {
  13795. if (eventName === "Focus" || eventName === "Blur") {
  13796. elementData ||= {
  13797. focused: false
  13798. };
  13799. }
  13800. this._setEventListener(element, elementData, baseName, eventName, getter);
  13801. if (eventName === "Focus" && !this.data.actions?.Blur) {
  13802. this._setEventListener(element, elementData, "blur", "Blur", null);
  13803. } else if (eventName === "Blur" && !this.data.actions?.Focus) {
  13804. this._setEventListener(element, elementData, "focus", "Focus", null);
  13805. }
  13806. }
  13807. }
  13808. }
  13809. _setBackgroundColor(element) {
  13810. const color = this.data.backgroundColor || null;
  13811. element.style.backgroundColor = color === null ? "transparent" : Util.makeHexColor(color[0], color[1], color[2]);
  13812. }
  13813. _setTextStyle(element) {
  13814. const TEXT_ALIGNMENT = ["left", "center", "right"];
  13815. const {
  13816. fontColor
  13817. } = this.data.defaultAppearanceData;
  13818. const fontSize = this.data.defaultAppearanceData.fontSize || annotation_layer_DEFAULT_FONT_SIZE;
  13819. const style = element.style;
  13820. let computedFontSize;
  13821. const BORDER_SIZE = 2;
  13822. const roundToOneDecimal = x => Math.round(10 * x) / 10;
  13823. if (this.data.multiLine) {
  13824. const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE);
  13825. const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1;
  13826. const lineHeight = height / numberOfLines;
  13827. computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / LINE_FACTOR));
  13828. } else {
  13829. const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE);
  13830. computedFontSize = Math.min(fontSize, roundToOneDecimal(height / LINE_FACTOR));
  13831. }
  13832. style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`;
  13833. style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);
  13834. if (this.data.textAlignment !== null) {
  13835. style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
  13836. }
  13837. }
  13838. _setRequired(element, isRequired) {
  13839. if (isRequired) {
  13840. element.setAttribute("required", true);
  13841. } else {
  13842. element.removeAttribute("required");
  13843. }
  13844. element.setAttribute("aria-required", isRequired);
  13845. }
  13846. }
  13847. class TextWidgetAnnotationElement extends WidgetAnnotationElement {
  13848. constructor(parameters) {
  13849. const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue;
  13850. super(parameters, {
  13851. isRenderable
  13852. });
  13853. }
  13854. setPropertyOnSiblings(base, key, value, keyInStorage) {
  13855. const storage = this.annotationStorage;
  13856. for (const element of this._getElementsByName(base.name, base.id)) {
  13857. if (element.domElement) {
  13858. element.domElement[key] = value;
  13859. }
  13860. storage.setValue(element.id, {
  13861. [keyInStorage]: value
  13862. });
  13863. }
  13864. }
  13865. render() {
  13866. const storage = this.annotationStorage;
  13867. const id = this.data.id;
  13868. this.container.classList.add("textWidgetAnnotation");
  13869. let element = null;
  13870. if (this.renderForms) {
  13871. const storedData = storage.getValue(id, {
  13872. value: this.data.fieldValue
  13873. });
  13874. let textContent = storedData.value || "";
  13875. const maxLen = storage.getValue(id, {
  13876. charLimit: this.data.maxLen
  13877. }).charLimit;
  13878. if (maxLen && textContent.length > maxLen) {
  13879. textContent = textContent.slice(0, maxLen);
  13880. }
  13881. let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join("\n") || null;
  13882. if (fieldFormattedValues && this.data.comb) {
  13883. fieldFormattedValues = fieldFormattedValues.replaceAll(/\s+/g, "");
  13884. }
  13885. const elementData = {
  13886. userValue: textContent,
  13887. formattedValue: fieldFormattedValues,
  13888. lastCommittedValue: null,
  13889. commitKey: 1,
  13890. focused: false
  13891. };
  13892. if (this.data.multiLine) {
  13893. element = document.createElement("textarea");
  13894. element.textContent = fieldFormattedValues ?? textContent;
  13895. if (this.data.doNotScroll) {
  13896. element.style.overflowY = "hidden";
  13897. }
  13898. } else {
  13899. element = document.createElement("input");
  13900. element.type = "text";
  13901. element.setAttribute("value", fieldFormattedValues ?? textContent);
  13902. if (this.data.doNotScroll) {
  13903. element.style.overflowX = "hidden";
  13904. }
  13905. }
  13906. if (this.data.hasOwnCanvas) {
  13907. element.hidden = true;
  13908. }
  13909. GetElementsByNameSet.add(element);
  13910. element.setAttribute("data-element-id", id);
  13911. element.disabled = this.data.readOnly;
  13912. element.name = this.data.fieldName;
  13913. element.tabIndex = DEFAULT_TAB_INDEX;
  13914. this._setRequired(element, this.data.required);
  13915. if (maxLen) {
  13916. element.maxLength = maxLen;
  13917. }
  13918. element.addEventListener("input", event => {
  13919. storage.setValue(id, {
  13920. value: event.target.value
  13921. });
  13922. this.setPropertyOnSiblings(element, "value", event.target.value, "value");
  13923. elementData.formattedValue = null;
  13924. });
  13925. element.addEventListener("resetform", event => {
  13926. const defaultValue = this.data.defaultFieldValue ?? "";
  13927. element.value = elementData.userValue = defaultValue;
  13928. elementData.formattedValue = null;
  13929. });
  13930. let blurListener = event => {
  13931. const {
  13932. formattedValue
  13933. } = elementData;
  13934. if (formattedValue !== null && formattedValue !== undefined) {
  13935. event.target.value = formattedValue;
  13936. }
  13937. event.target.scrollLeft = 0;
  13938. };
  13939. if (this.enableScripting && this.hasJSActions) {
  13940. element.addEventListener("focus", event => {
  13941. if (elementData.focused) {
  13942. return;
  13943. }
  13944. const {
  13945. target
  13946. } = event;
  13947. if (elementData.userValue) {
  13948. target.value = elementData.userValue;
  13949. }
  13950. elementData.lastCommittedValue = target.value;
  13951. elementData.commitKey = 1;
  13952. if (!this.data.actions?.Focus) {
  13953. elementData.focused = true;
  13954. }
  13955. });
  13956. element.addEventListener("updatefromsandbox", jsEvent => {
  13957. this.showElementAndHideCanvas(jsEvent.target);
  13958. const actions = {
  13959. value(event) {
  13960. elementData.userValue = event.detail.value ?? "";
  13961. storage.setValue(id, {
  13962. value: elementData.userValue.toString()
  13963. });
  13964. event.target.value = elementData.userValue;
  13965. },
  13966. formattedValue(event) {
  13967. const {
  13968. formattedValue
  13969. } = event.detail;
  13970. elementData.formattedValue = formattedValue;
  13971. if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) {
  13972. event.target.value = formattedValue;
  13973. }
  13974. storage.setValue(id, {
  13975. formattedValue
  13976. });
  13977. },
  13978. selRange(event) {
  13979. event.target.setSelectionRange(...event.detail.selRange);
  13980. },
  13981. charLimit: event => {
  13982. const {
  13983. charLimit
  13984. } = event.detail;
  13985. const {
  13986. target
  13987. } = event;
  13988. if (charLimit === 0) {
  13989. target.removeAttribute("maxLength");
  13990. return;
  13991. }
  13992. target.setAttribute("maxLength", charLimit);
  13993. let value = elementData.userValue;
  13994. if (!value || value.length <= charLimit) {
  13995. return;
  13996. }
  13997. value = value.slice(0, charLimit);
  13998. target.value = elementData.userValue = value;
  13999. storage.setValue(id, {
  14000. value
  14001. });
  14002. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  14003. source: this,
  14004. detail: {
  14005. id,
  14006. name: "Keystroke",
  14007. value,
  14008. willCommit: true,
  14009. commitKey: 1,
  14010. selStart: target.selectionStart,
  14011. selEnd: target.selectionEnd
  14012. }
  14013. });
  14014. }
  14015. };
  14016. this._dispatchEventFromSandbox(actions, jsEvent);
  14017. });
  14018. element.addEventListener("keydown", event => {
  14019. elementData.commitKey = 1;
  14020. let commitKey = -1;
  14021. if (event.key === "Escape") {
  14022. commitKey = 0;
  14023. } else if (event.key === "Enter" && !this.data.multiLine) {
  14024. commitKey = 2;
  14025. } else if (event.key === "Tab") {
  14026. elementData.commitKey = 3;
  14027. }
  14028. if (commitKey === -1) {
  14029. return;
  14030. }
  14031. const {
  14032. value
  14033. } = event.target;
  14034. if (elementData.lastCommittedValue === value) {
  14035. return;
  14036. }
  14037. elementData.lastCommittedValue = value;
  14038. elementData.userValue = value;
  14039. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  14040. source: this,
  14041. detail: {
  14042. id,
  14043. name: "Keystroke",
  14044. value,
  14045. willCommit: true,
  14046. commitKey,
  14047. selStart: event.target.selectionStart,
  14048. selEnd: event.target.selectionEnd
  14049. }
  14050. });
  14051. });
  14052. const _blurListener = blurListener;
  14053. blurListener = null;
  14054. element.addEventListener("blur", event => {
  14055. if (!elementData.focused || !event.relatedTarget) {
  14056. return;
  14057. }
  14058. if (!this.data.actions?.Blur) {
  14059. elementData.focused = false;
  14060. }
  14061. const {
  14062. value
  14063. } = event.target;
  14064. elementData.userValue = value;
  14065. if (elementData.lastCommittedValue !== value) {
  14066. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  14067. source: this,
  14068. detail: {
  14069. id,
  14070. name: "Keystroke",
  14071. value,
  14072. willCommit: true,
  14073. commitKey: elementData.commitKey,
  14074. selStart: event.target.selectionStart,
  14075. selEnd: event.target.selectionEnd
  14076. }
  14077. });
  14078. }
  14079. _blurListener(event);
  14080. });
  14081. if (this.data.actions?.Keystroke) {
  14082. element.addEventListener("beforeinput", event => {
  14083. elementData.lastCommittedValue = null;
  14084. const {
  14085. data,
  14086. target
  14087. } = event;
  14088. const {
  14089. value,
  14090. selectionStart,
  14091. selectionEnd
  14092. } = target;
  14093. let selStart = selectionStart,
  14094. selEnd = selectionEnd;
  14095. switch (event.inputType) {
  14096. case "deleteWordBackward":
  14097. {
  14098. const match = value.substring(0, selectionStart).match(/\w*[^\w]*$/);
  14099. if (match) {
  14100. selStart -= match[0].length;
  14101. }
  14102. break;
  14103. }
  14104. case "deleteWordForward":
  14105. {
  14106. const match = value.substring(selectionStart).match(/^[^\w]*\w*/);
  14107. if (match) {
  14108. selEnd += match[0].length;
  14109. }
  14110. break;
  14111. }
  14112. case "deleteContentBackward":
  14113. if (selectionStart === selectionEnd) {
  14114. selStart -= 1;
  14115. }
  14116. break;
  14117. case "deleteContentForward":
  14118. if (selectionStart === selectionEnd) {
  14119. selEnd += 1;
  14120. }
  14121. break;
  14122. }
  14123. event.preventDefault();
  14124. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  14125. source: this,
  14126. detail: {
  14127. id,
  14128. name: "Keystroke",
  14129. value,
  14130. change: data || "",
  14131. willCommit: false,
  14132. selStart,
  14133. selEnd
  14134. }
  14135. });
  14136. });
  14137. }
  14138. this._setEventListeners(element, elementData, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value);
  14139. }
  14140. if (blurListener) {
  14141. element.addEventListener("blur", blurListener);
  14142. }
  14143. if (this.data.comb) {
  14144. const fieldWidth = this.data.rect[2] - this.data.rect[0];
  14145. const combWidth = fieldWidth / maxLen;
  14146. element.classList.add("comb");
  14147. element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`;
  14148. }
  14149. } else {
  14150. element = document.createElement("div");
  14151. element.textContent = this.data.fieldValue;
  14152. element.style.verticalAlign = "middle";
  14153. element.style.display = "table-cell";
  14154. if (this.data.hasOwnCanvas) {
  14155. element.hidden = true;
  14156. }
  14157. }
  14158. this._setTextStyle(element);
  14159. this._setBackgroundColor(element);
  14160. this._setDefaultPropertiesFromJS(element);
  14161. this.container.append(element);
  14162. return this.container;
  14163. }
  14164. }
  14165. class SignatureWidgetAnnotationElement extends WidgetAnnotationElement {
  14166. constructor(parameters) {
  14167. super(parameters, {
  14168. isRenderable: !!parameters.data.hasOwnCanvas
  14169. });
  14170. }
  14171. }
  14172. class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
  14173. constructor(parameters) {
  14174. super(parameters, {
  14175. isRenderable: parameters.renderForms
  14176. });
  14177. }
  14178. render() {
  14179. const storage = this.annotationStorage;
  14180. const data = this.data;
  14181. const id = data.id;
  14182. let value = storage.getValue(id, {
  14183. value: data.exportValue === data.fieldValue
  14184. }).value;
  14185. if (typeof value === "string") {
  14186. value = value !== "Off";
  14187. storage.setValue(id, {
  14188. value
  14189. });
  14190. }
  14191. this.container.classList.add("buttonWidgetAnnotation", "checkBox");
  14192. const element = document.createElement("input");
  14193. GetElementsByNameSet.add(element);
  14194. element.setAttribute("data-element-id", id);
  14195. element.disabled = data.readOnly;
  14196. this._setRequired(element, this.data.required);
  14197. element.type = "checkbox";
  14198. element.name = data.fieldName;
  14199. if (value) {
  14200. element.setAttribute("checked", true);
  14201. }
  14202. element.setAttribute("exportValue", data.exportValue);
  14203. element.tabIndex = DEFAULT_TAB_INDEX;
  14204. element.addEventListener("change", event => {
  14205. const {
  14206. name,
  14207. checked
  14208. } = event.target;
  14209. for (const checkbox of this._getElementsByName(name, id)) {
  14210. const curChecked = checked && checkbox.exportValue === data.exportValue;
  14211. if (checkbox.domElement) {
  14212. checkbox.domElement.checked = curChecked;
  14213. }
  14214. storage.setValue(checkbox.id, {
  14215. value: curChecked
  14216. });
  14217. }
  14218. storage.setValue(id, {
  14219. value: checked
  14220. });
  14221. });
  14222. element.addEventListener("resetform", event => {
  14223. const defaultValue = data.defaultFieldValue || "Off";
  14224. event.target.checked = defaultValue === data.exportValue;
  14225. });
  14226. if (this.enableScripting && this.hasJSActions) {
  14227. element.addEventListener("updatefromsandbox", jsEvent => {
  14228. const actions = {
  14229. value(event) {
  14230. event.target.checked = event.detail.value !== "Off";
  14231. storage.setValue(id, {
  14232. value: event.target.checked
  14233. });
  14234. }
  14235. };
  14236. this._dispatchEventFromSandbox(actions, jsEvent);
  14237. });
  14238. 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);
  14239. }
  14240. this._setBackgroundColor(element);
  14241. this._setDefaultPropertiesFromJS(element);
  14242. this.container.append(element);
  14243. return this.container;
  14244. }
  14245. }
  14246. class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
  14247. constructor(parameters) {
  14248. super(parameters, {
  14249. isRenderable: parameters.renderForms
  14250. });
  14251. }
  14252. render() {
  14253. this.container.classList.add("buttonWidgetAnnotation", "radioButton");
  14254. const storage = this.annotationStorage;
  14255. const data = this.data;
  14256. const id = data.id;
  14257. let value = storage.getValue(id, {
  14258. value: data.fieldValue === data.buttonValue
  14259. }).value;
  14260. if (typeof value === "string") {
  14261. value = value !== data.buttonValue;
  14262. storage.setValue(id, {
  14263. value
  14264. });
  14265. }
  14266. if (value) {
  14267. for (const radio of this._getElementsByName(data.fieldName, id)) {
  14268. storage.setValue(radio.id, {
  14269. value: false
  14270. });
  14271. }
  14272. }
  14273. const element = document.createElement("input");
  14274. GetElementsByNameSet.add(element);
  14275. element.setAttribute("data-element-id", id);
  14276. element.disabled = data.readOnly;
  14277. this._setRequired(element, this.data.required);
  14278. element.type = "radio";
  14279. element.name = data.fieldName;
  14280. if (value) {
  14281. element.setAttribute("checked", true);
  14282. }
  14283. element.tabIndex = DEFAULT_TAB_INDEX;
  14284. element.addEventListener("change", event => {
  14285. const {
  14286. name,
  14287. checked
  14288. } = event.target;
  14289. for (const radio of this._getElementsByName(name, id)) {
  14290. storage.setValue(radio.id, {
  14291. value: false
  14292. });
  14293. }
  14294. storage.setValue(id, {
  14295. value: checked
  14296. });
  14297. });
  14298. element.addEventListener("resetform", event => {
  14299. const defaultValue = data.defaultFieldValue;
  14300. event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue;
  14301. });
  14302. if (this.enableScripting && this.hasJSActions) {
  14303. const pdfButtonValue = data.buttonValue;
  14304. element.addEventListener("updatefromsandbox", jsEvent => {
  14305. const actions = {
  14306. value: event => {
  14307. const checked = pdfButtonValue === event.detail.value;
  14308. for (const radio of this._getElementsByName(event.target.name)) {
  14309. const curChecked = checked && radio.id === id;
  14310. if (radio.domElement) {
  14311. radio.domElement.checked = curChecked;
  14312. }
  14313. storage.setValue(radio.id, {
  14314. value: curChecked
  14315. });
  14316. }
  14317. }
  14318. };
  14319. this._dispatchEventFromSandbox(actions, jsEvent);
  14320. });
  14321. 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);
  14322. }
  14323. this._setBackgroundColor(element);
  14324. this._setDefaultPropertiesFromJS(element);
  14325. this.container.append(element);
  14326. return this.container;
  14327. }
  14328. }
  14329. class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
  14330. constructor(parameters) {
  14331. super(parameters, {
  14332. ignoreBorder: parameters.data.hasAppearance
  14333. });
  14334. }
  14335. render() {
  14336. const container = super.render();
  14337. container.classList.add("buttonWidgetAnnotation", "pushButton");
  14338. const linkElement = container.lastChild;
  14339. if (this.enableScripting && this.hasJSActions && linkElement) {
  14340. this._setDefaultPropertiesFromJS(linkElement);
  14341. linkElement.addEventListener("updatefromsandbox", jsEvent => {
  14342. this._dispatchEventFromSandbox({}, jsEvent);
  14343. });
  14344. }
  14345. return container;
  14346. }
  14347. }
  14348. class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
  14349. constructor(parameters) {
  14350. super(parameters, {
  14351. isRenderable: parameters.renderForms
  14352. });
  14353. }
  14354. render() {
  14355. this.container.classList.add("choiceWidgetAnnotation");
  14356. const storage = this.annotationStorage;
  14357. const id = this.data.id;
  14358. const storedData = storage.getValue(id, {
  14359. value: this.data.fieldValue
  14360. });
  14361. const selectElement = document.createElement("select");
  14362. GetElementsByNameSet.add(selectElement);
  14363. selectElement.setAttribute("data-element-id", id);
  14364. selectElement.disabled = this.data.readOnly;
  14365. this._setRequired(selectElement, this.data.required);
  14366. selectElement.name = this.data.fieldName;
  14367. selectElement.tabIndex = DEFAULT_TAB_INDEX;
  14368. let addAnEmptyEntry = this.data.combo && this.data.options.length > 0;
  14369. if (!this.data.combo) {
  14370. selectElement.size = this.data.options.length;
  14371. if (this.data.multiSelect) {
  14372. selectElement.multiple = true;
  14373. }
  14374. }
  14375. selectElement.addEventListener("resetform", event => {
  14376. const defaultValue = this.data.defaultFieldValue;
  14377. for (const option of selectElement.options) {
  14378. option.selected = option.value === defaultValue;
  14379. }
  14380. });
  14381. for (const option of this.data.options) {
  14382. const optionElement = document.createElement("option");
  14383. optionElement.textContent = option.displayValue;
  14384. optionElement.value = option.exportValue;
  14385. if (storedData.value.includes(option.exportValue)) {
  14386. optionElement.setAttribute("selected", true);
  14387. addAnEmptyEntry = false;
  14388. }
  14389. selectElement.append(optionElement);
  14390. }
  14391. let removeEmptyEntry = null;
  14392. if (addAnEmptyEntry) {
  14393. const noneOptionElement = document.createElement("option");
  14394. noneOptionElement.value = " ";
  14395. noneOptionElement.setAttribute("hidden", true);
  14396. noneOptionElement.setAttribute("selected", true);
  14397. selectElement.prepend(noneOptionElement);
  14398. removeEmptyEntry = () => {
  14399. noneOptionElement.remove();
  14400. selectElement.removeEventListener("input", removeEmptyEntry);
  14401. removeEmptyEntry = null;
  14402. };
  14403. selectElement.addEventListener("input", removeEmptyEntry);
  14404. }
  14405. const getValue = isExport => {
  14406. const name = isExport ? "value" : "textContent";
  14407. const {
  14408. options,
  14409. multiple
  14410. } = selectElement;
  14411. if (!multiple) {
  14412. return options.selectedIndex === -1 ? null : options[options.selectedIndex][name];
  14413. }
  14414. return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]);
  14415. };
  14416. let selectedValues = getValue(false);
  14417. const getItems = event => {
  14418. const options = event.target.options;
  14419. return Array.prototype.map.call(options, option => ({
  14420. displayValue: option.textContent,
  14421. exportValue: option.value
  14422. }));
  14423. };
  14424. if (this.enableScripting && this.hasJSActions) {
  14425. selectElement.addEventListener("updatefromsandbox", jsEvent => {
  14426. const actions = {
  14427. value(event) {
  14428. removeEmptyEntry?.();
  14429. const value = event.detail.value;
  14430. const values = new Set(Array.isArray(value) ? value : [value]);
  14431. for (const option of selectElement.options) {
  14432. option.selected = values.has(option.value);
  14433. }
  14434. storage.setValue(id, {
  14435. value: getValue(true)
  14436. });
  14437. selectedValues = getValue(false);
  14438. },
  14439. multipleSelection(event) {
  14440. selectElement.multiple = true;
  14441. },
  14442. remove(event) {
  14443. const options = selectElement.options;
  14444. const index = event.detail.remove;
  14445. options[index].selected = false;
  14446. selectElement.remove(index);
  14447. if (options.length > 0) {
  14448. const i = Array.prototype.findIndex.call(options, option => option.selected);
  14449. if (i === -1) {
  14450. options[0].selected = true;
  14451. }
  14452. }
  14453. storage.setValue(id, {
  14454. value: getValue(true),
  14455. items: getItems(event)
  14456. });
  14457. selectedValues = getValue(false);
  14458. },
  14459. clear(event) {
  14460. while (selectElement.length !== 0) {
  14461. selectElement.remove(0);
  14462. }
  14463. storage.setValue(id, {
  14464. value: null,
  14465. items: []
  14466. });
  14467. selectedValues = getValue(false);
  14468. },
  14469. insert(event) {
  14470. const {
  14471. index,
  14472. displayValue,
  14473. exportValue
  14474. } = event.detail.insert;
  14475. const selectChild = selectElement.children[index];
  14476. const optionElement = document.createElement("option");
  14477. optionElement.textContent = displayValue;
  14478. optionElement.value = exportValue;
  14479. if (selectChild) {
  14480. selectChild.before(optionElement);
  14481. } else {
  14482. selectElement.append(optionElement);
  14483. }
  14484. storage.setValue(id, {
  14485. value: getValue(true),
  14486. items: getItems(event)
  14487. });
  14488. selectedValues = getValue(false);
  14489. },
  14490. items(event) {
  14491. const {
  14492. items
  14493. } = event.detail;
  14494. while (selectElement.length !== 0) {
  14495. selectElement.remove(0);
  14496. }
  14497. for (const item of items) {
  14498. const {
  14499. displayValue,
  14500. exportValue
  14501. } = item;
  14502. const optionElement = document.createElement("option");
  14503. optionElement.textContent = displayValue;
  14504. optionElement.value = exportValue;
  14505. selectElement.append(optionElement);
  14506. }
  14507. if (selectElement.options.length > 0) {
  14508. selectElement.options[0].selected = true;
  14509. }
  14510. storage.setValue(id, {
  14511. value: getValue(true),
  14512. items: getItems(event)
  14513. });
  14514. selectedValues = getValue(false);
  14515. },
  14516. indices(event) {
  14517. const indices = new Set(event.detail.indices);
  14518. for (const option of event.target.options) {
  14519. option.selected = indices.has(option.index);
  14520. }
  14521. storage.setValue(id, {
  14522. value: getValue(true)
  14523. });
  14524. selectedValues = getValue(false);
  14525. },
  14526. editable(event) {
  14527. event.target.disabled = !event.detail.editable;
  14528. }
  14529. };
  14530. this._dispatchEventFromSandbox(actions, jsEvent);
  14531. });
  14532. selectElement.addEventListener("input", event => {
  14533. const exportValue = getValue(true);
  14534. const change = getValue(false);
  14535. storage.setValue(id, {
  14536. value: exportValue
  14537. });
  14538. event.preventDefault();
  14539. this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
  14540. source: this,
  14541. detail: {
  14542. id,
  14543. name: "Keystroke",
  14544. value: selectedValues,
  14545. change,
  14546. changeEx: exportValue,
  14547. willCommit: false,
  14548. commitKey: 1,
  14549. keyDown: false
  14550. }
  14551. });
  14552. });
  14553. 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);
  14554. } else {
  14555. selectElement.addEventListener("input", function (event) {
  14556. storage.setValue(id, {
  14557. value: getValue(true)
  14558. });
  14559. });
  14560. }
  14561. if (this.data.combo) {
  14562. this._setTextStyle(selectElement);
  14563. } else {}
  14564. this._setBackgroundColor(selectElement);
  14565. this._setDefaultPropertiesFromJS(selectElement);
  14566. this.container.append(selectElement);
  14567. return this.container;
  14568. }
  14569. }
  14570. class PopupAnnotationElement extends AnnotationElement {
  14571. constructor(parameters) {
  14572. const {
  14573. data,
  14574. elements
  14575. } = parameters;
  14576. super(parameters, {
  14577. isRenderable: AnnotationElement._hasPopupData(data)
  14578. });
  14579. this.elements = elements;
  14580. this.popup = null;
  14581. }
  14582. render() {
  14583. this.container.classList.add("popupAnnotation");
  14584. const popup = this.popup = new PopupElement({
  14585. container: this.container,
  14586. color: this.data.color,
  14587. titleObj: this.data.titleObj,
  14588. modificationDate: this.data.modificationDate,
  14589. contentsObj: this.data.contentsObj,
  14590. richText: this.data.richText,
  14591. rect: this.data.rect,
  14592. parentRect: this.data.parentRect || null,
  14593. parent: this.parent,
  14594. elements: this.elements,
  14595. open: this.data.open
  14596. });
  14597. const elementIds = [];
  14598. for (const element of this.elements) {
  14599. element.popup = popup;
  14600. elementIds.push(element.data.id);
  14601. element.addHighlightArea();
  14602. }
  14603. this.container.setAttribute("aria-controls", elementIds.map(id => `${AnnotationPrefix}${id}`).join(","));
  14604. return this.container;
  14605. }
  14606. }
  14607. class PopupElement {
  14608. #boundKeyDown = this.#keyDown.bind(this);
  14609. #boundHide = this.#hide.bind(this);
  14610. #boundShow = this.#show.bind(this);
  14611. #boundToggle = this.#toggle.bind(this);
  14612. #color = null;
  14613. #container = null;
  14614. #contentsObj = null;
  14615. #dateObj = null;
  14616. #elements = null;
  14617. #parent = null;
  14618. #parentRect = null;
  14619. #pinned = false;
  14620. #popup = null;
  14621. #position = null;
  14622. #rect = null;
  14623. #richText = null;
  14624. #titleObj = null;
  14625. #updates = null;
  14626. #wasVisible = false;
  14627. constructor({
  14628. container,
  14629. color,
  14630. elements,
  14631. titleObj,
  14632. modificationDate,
  14633. contentsObj,
  14634. richText,
  14635. parent,
  14636. rect,
  14637. parentRect,
  14638. open
  14639. }) {
  14640. this.#container = container;
  14641. this.#titleObj = titleObj;
  14642. this.#contentsObj = contentsObj;
  14643. this.#richText = richText;
  14644. this.#parent = parent;
  14645. this.#color = color;
  14646. this.#rect = rect;
  14647. this.#parentRect = parentRect;
  14648. this.#elements = elements;
  14649. this.#dateObj = PDFDateString.toDateObject(modificationDate);
  14650. this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup());
  14651. for (const element of this.trigger) {
  14652. element.addEventListener("click", this.#boundToggle);
  14653. element.addEventListener("mouseenter", this.#boundShow);
  14654. element.addEventListener("mouseleave", this.#boundHide);
  14655. element.classList.add("popupTriggerArea");
  14656. }
  14657. for (const element of elements) {
  14658. element.container?.addEventListener("keydown", this.#boundKeyDown);
  14659. }
  14660. this.#container.hidden = true;
  14661. if (open) {
  14662. this.#toggle();
  14663. }
  14664. }
  14665. render() {
  14666. if (this.#popup) {
  14667. return;
  14668. }
  14669. const popup = this.#popup = document.createElement("div");
  14670. popup.className = "popup";
  14671. if (this.#color) {
  14672. const baseColor = popup.style.outlineColor = Util.makeHexColor(...this.#color);
  14673. if (CSS.supports("background-color", "color-mix(in srgb, red 30%, white)")) {
  14674. popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`;
  14675. } else {
  14676. const BACKGROUND_ENLIGHT = 0.7;
  14677. popup.style.backgroundColor = Util.makeHexColor(...this.#color.map(c => Math.floor(BACKGROUND_ENLIGHT * (255 - c) + c)));
  14678. }
  14679. }
  14680. const header = document.createElement("span");
  14681. header.className = "header";
  14682. const title = document.createElement("h1");
  14683. header.append(title);
  14684. ({
  14685. dir: title.dir,
  14686. str: title.textContent
  14687. } = this.#titleObj);
  14688. popup.append(header);
  14689. if (this.#dateObj) {
  14690. const modificationDate = document.createElement("span");
  14691. modificationDate.classList.add("popupDate");
  14692. modificationDate.setAttribute("data-l10n-id", "pdfjs-annotation-date-string");
  14693. modificationDate.setAttribute("data-l10n-args", JSON.stringify({
  14694. date: this.#dateObj.toLocaleDateString(),
  14695. time: this.#dateObj.toLocaleTimeString()
  14696. }));
  14697. header.append(modificationDate);
  14698. }
  14699. const html = this.#html;
  14700. if (html) {
  14701. XfaLayer.render({
  14702. xfaHtml: html,
  14703. intent: "richText",
  14704. div: popup
  14705. });
  14706. popup.lastChild.classList.add("richText", "popupContent");
  14707. } else {
  14708. const contents = this._formatContents(this.#contentsObj);
  14709. popup.append(contents);
  14710. }
  14711. this.#container.append(popup);
  14712. }
  14713. get #html() {
  14714. const richText = this.#richText;
  14715. const contentsObj = this.#contentsObj;
  14716. if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) {
  14717. return this.#richText.html || null;
  14718. }
  14719. return null;
  14720. }
  14721. get #fontSize() {
  14722. return this.#html?.attributes?.style?.fontSize || 0;
  14723. }
  14724. get #fontColor() {
  14725. return this.#html?.attributes?.style?.color || null;
  14726. }
  14727. #makePopupContent(text) {
  14728. const popupLines = [];
  14729. const popupContent = {
  14730. str: text,
  14731. html: {
  14732. name: "div",
  14733. attributes: {
  14734. dir: "auto"
  14735. },
  14736. children: [{
  14737. name: "p",
  14738. children: popupLines
  14739. }]
  14740. }
  14741. };
  14742. const lineAttributes = {
  14743. style: {
  14744. color: this.#fontColor,
  14745. fontSize: this.#fontSize ? `calc(${this.#fontSize}px * var(--scale-factor))` : ""
  14746. }
  14747. };
  14748. for (const line of text.split("\n")) {
  14749. popupLines.push({
  14750. name: "span",
  14751. value: line,
  14752. attributes: lineAttributes
  14753. });
  14754. }
  14755. return popupContent;
  14756. }
  14757. _formatContents({
  14758. str,
  14759. dir
  14760. }) {
  14761. const p = document.createElement("p");
  14762. p.classList.add("popupContent");
  14763. p.dir = dir;
  14764. const lines = str.split(/(?:\r\n?|\n)/);
  14765. for (let i = 0, ii = lines.length; i < ii; ++i) {
  14766. const line = lines[i];
  14767. p.append(document.createTextNode(line));
  14768. if (i < ii - 1) {
  14769. p.append(document.createElement("br"));
  14770. }
  14771. }
  14772. return p;
  14773. }
  14774. #keyDown(event) {
  14775. if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
  14776. return;
  14777. }
  14778. if (event.key === "Enter" || event.key === "Escape" && this.#pinned) {
  14779. this.#toggle();
  14780. }
  14781. }
  14782. updateEdited({
  14783. rect,
  14784. popupContent
  14785. }) {
  14786. this.#updates ||= {
  14787. contentsObj: this.#contentsObj,
  14788. richText: this.#richText
  14789. };
  14790. if (rect) {
  14791. this.#position = null;
  14792. }
  14793. if (popupContent) {
  14794. this.#richText = this.#makePopupContent(popupContent);
  14795. this.#contentsObj = null;
  14796. }
  14797. this.#popup?.remove();
  14798. this.#popup = null;
  14799. }
  14800. resetEdited() {
  14801. if (!this.#updates) {
  14802. return;
  14803. }
  14804. ({
  14805. contentsObj: this.#contentsObj,
  14806. richText: this.#richText
  14807. } = this.#updates);
  14808. this.#updates = null;
  14809. this.#popup?.remove();
  14810. this.#popup = null;
  14811. this.#position = null;
  14812. }
  14813. #setPosition() {
  14814. if (this.#position !== null) {
  14815. return;
  14816. }
  14817. const {
  14818. page: {
  14819. view
  14820. },
  14821. viewport: {
  14822. rawDims: {
  14823. pageWidth,
  14824. pageHeight,
  14825. pageX,
  14826. pageY
  14827. }
  14828. }
  14829. } = this.#parent;
  14830. let useParentRect = !!this.#parentRect;
  14831. let rect = useParentRect ? this.#parentRect : this.#rect;
  14832. for (const element of this.#elements) {
  14833. if (!rect || Util.intersect(element.data.rect, rect) !== null) {
  14834. rect = element.data.rect;
  14835. useParentRect = true;
  14836. break;
  14837. }
  14838. }
  14839. const normalizedRect = Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]);
  14840. const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5;
  14841. const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0;
  14842. const popupLeft = normalizedRect[0] + parentWidth;
  14843. const popupTop = normalizedRect[1];
  14844. this.#position = [100 * (popupLeft - pageX) / pageWidth, 100 * (popupTop - pageY) / pageHeight];
  14845. const {
  14846. style
  14847. } = this.#container;
  14848. style.left = `${this.#position[0]}%`;
  14849. style.top = `${this.#position[1]}%`;
  14850. }
  14851. #toggle() {
  14852. this.#pinned = !this.#pinned;
  14853. if (this.#pinned) {
  14854. this.#show();
  14855. this.#container.addEventListener("click", this.#boundToggle);
  14856. this.#container.addEventListener("keydown", this.#boundKeyDown);
  14857. } else {
  14858. this.#hide();
  14859. this.#container.removeEventListener("click", this.#boundToggle);
  14860. this.#container.removeEventListener("keydown", this.#boundKeyDown);
  14861. }
  14862. }
  14863. #show() {
  14864. if (!this.#popup) {
  14865. this.render();
  14866. }
  14867. if (!this.isVisible) {
  14868. this.#setPosition();
  14869. this.#container.hidden = false;
  14870. this.#container.style.zIndex = parseInt(this.#container.style.zIndex) + 1000;
  14871. } else if (this.#pinned) {
  14872. this.#container.classList.add("focused");
  14873. }
  14874. }
  14875. #hide() {
  14876. this.#container.classList.remove("focused");
  14877. if (this.#pinned || !this.isVisible) {
  14878. return;
  14879. }
  14880. this.#container.hidden = true;
  14881. this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000;
  14882. }
  14883. forceHide() {
  14884. this.#wasVisible = this.isVisible;
  14885. if (!this.#wasVisible) {
  14886. return;
  14887. }
  14888. this.#container.hidden = true;
  14889. }
  14890. maybeShow() {
  14891. if (!this.#wasVisible) {
  14892. return;
  14893. }
  14894. if (!this.#popup) {
  14895. this.#show();
  14896. }
  14897. this.#wasVisible = false;
  14898. this.#container.hidden = false;
  14899. }
  14900. get isVisible() {
  14901. return this.#container.hidden === false;
  14902. }
  14903. }
  14904. class FreeTextAnnotationElement extends AnnotationElement {
  14905. constructor(parameters) {
  14906. super(parameters, {
  14907. isRenderable: true,
  14908. ignoreBorder: true
  14909. });
  14910. this.textContent = parameters.data.textContent;
  14911. this.textPosition = parameters.data.textPosition;
  14912. this.annotationEditorType = AnnotationEditorType.FREETEXT;
  14913. }
  14914. render() {
  14915. this.container.classList.add("freeTextAnnotation");
  14916. if (this.textContent) {
  14917. const content = document.createElement("div");
  14918. content.classList.add("annotationTextContent");
  14919. content.setAttribute("role", "comment");
  14920. for (const line of this.textContent) {
  14921. const lineSpan = document.createElement("span");
  14922. lineSpan.textContent = line;
  14923. content.append(lineSpan);
  14924. }
  14925. this.container.append(content);
  14926. }
  14927. if (!this.data.popupRef && this.hasPopupData) {
  14928. this._createPopup();
  14929. }
  14930. this._editOnDoubleClick();
  14931. return this.container;
  14932. }
  14933. }
  14934. class LineAnnotationElement extends AnnotationElement {
  14935. #line = null;
  14936. constructor(parameters) {
  14937. super(parameters, {
  14938. isRenderable: true,
  14939. ignoreBorder: true
  14940. });
  14941. }
  14942. render() {
  14943. this.container.classList.add("lineAnnotation");
  14944. const data = this.data;
  14945. const {
  14946. width,
  14947. height
  14948. } = getRectDims(data.rect);
  14949. const svg = this.svgFactory.create(width, height, true);
  14950. const line = this.#line = this.svgFactory.createElement("svg:line");
  14951. line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]);
  14952. line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]);
  14953. line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]);
  14954. line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]);
  14955. line.setAttribute("stroke-width", data.borderStyle.width || 1);
  14956. line.setAttribute("stroke", "transparent");
  14957. line.setAttribute("fill", "transparent");
  14958. svg.append(line);
  14959. this.container.append(svg);
  14960. if (!data.popupRef && this.hasPopupData) {
  14961. this._createPopup();
  14962. }
  14963. return this.container;
  14964. }
  14965. getElementsToTriggerPopup() {
  14966. return this.#line;
  14967. }
  14968. addHighlightArea() {
  14969. this.container.classList.add("highlightArea");
  14970. }
  14971. }
  14972. class SquareAnnotationElement extends AnnotationElement {
  14973. #square = null;
  14974. constructor(parameters) {
  14975. super(parameters, {
  14976. isRenderable: true,
  14977. ignoreBorder: true
  14978. });
  14979. }
  14980. render() {
  14981. this.container.classList.add("squareAnnotation");
  14982. const data = this.data;
  14983. const {
  14984. width,
  14985. height
  14986. } = getRectDims(data.rect);
  14987. const svg = this.svgFactory.create(width, height, true);
  14988. const borderWidth = data.borderStyle.width;
  14989. const square = this.#square = this.svgFactory.createElement("svg:rect");
  14990. square.setAttribute("x", borderWidth / 2);
  14991. square.setAttribute("y", borderWidth / 2);
  14992. square.setAttribute("width", width - borderWidth);
  14993. square.setAttribute("height", height - borderWidth);
  14994. square.setAttribute("stroke-width", borderWidth || 1);
  14995. square.setAttribute("stroke", "transparent");
  14996. square.setAttribute("fill", "transparent");
  14997. svg.append(square);
  14998. this.container.append(svg);
  14999. if (!data.popupRef && this.hasPopupData) {
  15000. this._createPopup();
  15001. }
  15002. return this.container;
  15003. }
  15004. getElementsToTriggerPopup() {
  15005. return this.#square;
  15006. }
  15007. addHighlightArea() {
  15008. this.container.classList.add("highlightArea");
  15009. }
  15010. }
  15011. class CircleAnnotationElement extends AnnotationElement {
  15012. #circle = null;
  15013. constructor(parameters) {
  15014. super(parameters, {
  15015. isRenderable: true,
  15016. ignoreBorder: true
  15017. });
  15018. }
  15019. render() {
  15020. this.container.classList.add("circleAnnotation");
  15021. const data = this.data;
  15022. const {
  15023. width,
  15024. height
  15025. } = getRectDims(data.rect);
  15026. const svg = this.svgFactory.create(width, height, true);
  15027. const borderWidth = data.borderStyle.width;
  15028. const circle = this.#circle = this.svgFactory.createElement("svg:ellipse");
  15029. circle.setAttribute("cx", width / 2);
  15030. circle.setAttribute("cy", height / 2);
  15031. circle.setAttribute("rx", width / 2 - borderWidth / 2);
  15032. circle.setAttribute("ry", height / 2 - borderWidth / 2);
  15033. circle.setAttribute("stroke-width", borderWidth || 1);
  15034. circle.setAttribute("stroke", "transparent");
  15035. circle.setAttribute("fill", "transparent");
  15036. svg.append(circle);
  15037. this.container.append(svg);
  15038. if (!data.popupRef && this.hasPopupData) {
  15039. this._createPopup();
  15040. }
  15041. return this.container;
  15042. }
  15043. getElementsToTriggerPopup() {
  15044. return this.#circle;
  15045. }
  15046. addHighlightArea() {
  15047. this.container.classList.add("highlightArea");
  15048. }
  15049. }
  15050. class PolylineAnnotationElement extends AnnotationElement {
  15051. #polyline = null;
  15052. constructor(parameters) {
  15053. super(parameters, {
  15054. isRenderable: true,
  15055. ignoreBorder: true
  15056. });
  15057. this.containerClassName = "polylineAnnotation";
  15058. this.svgElementName = "svg:polyline";
  15059. }
  15060. render() {
  15061. this.container.classList.add(this.containerClassName);
  15062. const {
  15063. data: {
  15064. rect,
  15065. vertices,
  15066. borderStyle,
  15067. popupRef
  15068. }
  15069. } = this;
  15070. if (!vertices) {
  15071. return this.container;
  15072. }
  15073. const {
  15074. width,
  15075. height
  15076. } = getRectDims(rect);
  15077. const svg = this.svgFactory.create(width, height, true);
  15078. let points = [];
  15079. for (let i = 0, ii = vertices.length; i < ii; i += 2) {
  15080. const x = vertices[i] - rect[0];
  15081. const y = rect[3] - vertices[i + 1];
  15082. points.push(`${x},${y}`);
  15083. }
  15084. points = points.join(" ");
  15085. const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName);
  15086. polyline.setAttribute("points", points);
  15087. polyline.setAttribute("stroke-width", borderStyle.width || 1);
  15088. polyline.setAttribute("stroke", "transparent");
  15089. polyline.setAttribute("fill", "transparent");
  15090. svg.append(polyline);
  15091. this.container.append(svg);
  15092. if (!popupRef && this.hasPopupData) {
  15093. this._createPopup();
  15094. }
  15095. return this.container;
  15096. }
  15097. getElementsToTriggerPopup() {
  15098. return this.#polyline;
  15099. }
  15100. addHighlightArea() {
  15101. this.container.classList.add("highlightArea");
  15102. }
  15103. }
  15104. class PolygonAnnotationElement extends PolylineAnnotationElement {
  15105. constructor(parameters) {
  15106. super(parameters);
  15107. this.containerClassName = "polygonAnnotation";
  15108. this.svgElementName = "svg:polygon";
  15109. }
  15110. }
  15111. class CaretAnnotationElement extends AnnotationElement {
  15112. constructor(parameters) {
  15113. super(parameters, {
  15114. isRenderable: true,
  15115. ignoreBorder: true
  15116. });
  15117. }
  15118. render() {
  15119. this.container.classList.add("caretAnnotation");
  15120. if (!this.data.popupRef && this.hasPopupData) {
  15121. this._createPopup();
  15122. }
  15123. return this.container;
  15124. }
  15125. }
  15126. class InkAnnotationElement extends AnnotationElement {
  15127. #polylines = [];
  15128. constructor(parameters) {
  15129. super(parameters, {
  15130. isRenderable: true,
  15131. ignoreBorder: true
  15132. });
  15133. this.containerClassName = "inkAnnotation";
  15134. this.svgElementName = "svg:polyline";
  15135. this.annotationEditorType = AnnotationEditorType.INK;
  15136. }
  15137. render() {
  15138. this.container.classList.add(this.containerClassName);
  15139. const {
  15140. data: {
  15141. rect,
  15142. inkLists,
  15143. borderStyle,
  15144. popupRef
  15145. }
  15146. } = this;
  15147. const {
  15148. width,
  15149. height
  15150. } = getRectDims(rect);
  15151. const svg = this.svgFactory.create(width, height, true);
  15152. for (const inkList of inkLists) {
  15153. let points = [];
  15154. for (let i = 0, ii = inkList.length; i < ii; i += 2) {
  15155. const x = inkList[i] - rect[0];
  15156. const y = rect[3] - inkList[i + 1];
  15157. points.push(`${x},${y}`);
  15158. }
  15159. points = points.join(" ");
  15160. const polyline = this.svgFactory.createElement(this.svgElementName);
  15161. this.#polylines.push(polyline);
  15162. polyline.setAttribute("points", points);
  15163. polyline.setAttribute("stroke-width", borderStyle.width || 1);
  15164. polyline.setAttribute("stroke", "transparent");
  15165. polyline.setAttribute("fill", "transparent");
  15166. if (!popupRef && this.hasPopupData) {
  15167. this._createPopup();
  15168. }
  15169. svg.append(polyline);
  15170. }
  15171. this.container.append(svg);
  15172. return this.container;
  15173. }
  15174. getElementsToTriggerPopup() {
  15175. return this.#polylines;
  15176. }
  15177. addHighlightArea() {
  15178. this.container.classList.add("highlightArea");
  15179. }
  15180. }
  15181. class HighlightAnnotationElement extends AnnotationElement {
  15182. constructor(parameters) {
  15183. super(parameters, {
  15184. isRenderable: true,
  15185. ignoreBorder: true,
  15186. createQuadrilaterals: true
  15187. });
  15188. }
  15189. render() {
  15190. if (!this.data.popupRef && this.hasPopupData) {
  15191. this._createPopup();
  15192. }
  15193. this.container.classList.add("highlightAnnotation");
  15194. return this.container;
  15195. }
  15196. }
  15197. class UnderlineAnnotationElement extends AnnotationElement {
  15198. constructor(parameters) {
  15199. super(parameters, {
  15200. isRenderable: true,
  15201. ignoreBorder: true,
  15202. createQuadrilaterals: true
  15203. });
  15204. }
  15205. render() {
  15206. if (!this.data.popupRef && this.hasPopupData) {
  15207. this._createPopup();
  15208. }
  15209. this.container.classList.add("underlineAnnotation");
  15210. return this.container;
  15211. }
  15212. }
  15213. class SquigglyAnnotationElement extends AnnotationElement {
  15214. constructor(parameters) {
  15215. super(parameters, {
  15216. isRenderable: true,
  15217. ignoreBorder: true,
  15218. createQuadrilaterals: true
  15219. });
  15220. }
  15221. render() {
  15222. if (!this.data.popupRef && this.hasPopupData) {
  15223. this._createPopup();
  15224. }
  15225. this.container.classList.add("squigglyAnnotation");
  15226. return this.container;
  15227. }
  15228. }
  15229. class StrikeOutAnnotationElement extends AnnotationElement {
  15230. constructor(parameters) {
  15231. super(parameters, {
  15232. isRenderable: true,
  15233. ignoreBorder: true,
  15234. createQuadrilaterals: true
  15235. });
  15236. }
  15237. render() {
  15238. if (!this.data.popupRef && this.hasPopupData) {
  15239. this._createPopup();
  15240. }
  15241. this.container.classList.add("strikeoutAnnotation");
  15242. return this.container;
  15243. }
  15244. }
  15245. class StampAnnotationElement extends AnnotationElement {
  15246. constructor(parameters) {
  15247. super(parameters, {
  15248. isRenderable: true,
  15249. ignoreBorder: true
  15250. });
  15251. }
  15252. render() {
  15253. this.container.classList.add("stampAnnotation");
  15254. if (!this.data.popupRef && this.hasPopupData) {
  15255. this._createPopup();
  15256. }
  15257. return this.container;
  15258. }
  15259. }
  15260. class FileAttachmentAnnotationElement extends AnnotationElement {
  15261. #trigger = null;
  15262. constructor(parameters) {
  15263. super(parameters, {
  15264. isRenderable: true
  15265. });
  15266. const {
  15267. file
  15268. } = this.data;
  15269. this.filename = file.filename;
  15270. this.content = file.content;
  15271. this.linkService.eventBus?.dispatch("fileattachmentannotation", {
  15272. source: this,
  15273. ...file
  15274. });
  15275. }
  15276. render() {
  15277. this.container.classList.add("fileAttachmentAnnotation");
  15278. const {
  15279. container,
  15280. data
  15281. } = this;
  15282. let trigger;
  15283. if (data.hasAppearance || data.fillAlpha === 0) {
  15284. trigger = document.createElement("div");
  15285. } else {
  15286. trigger = document.createElement("img");
  15287. trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`;
  15288. if (data.fillAlpha && data.fillAlpha < 1) {
  15289. trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`;
  15290. }
  15291. }
  15292. trigger.addEventListener("dblclick", this.#download.bind(this));
  15293. this.#trigger = trigger;
  15294. const {
  15295. isMac
  15296. } = util_FeatureTest.platform;
  15297. container.addEventListener("keydown", evt => {
  15298. if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) {
  15299. this.#download();
  15300. }
  15301. });
  15302. if (!data.popupRef && this.hasPopupData) {
  15303. this._createPopup();
  15304. } else {
  15305. trigger.classList.add("popupTriggerArea");
  15306. }
  15307. container.append(trigger);
  15308. return container;
  15309. }
  15310. getElementsToTriggerPopup() {
  15311. return this.#trigger;
  15312. }
  15313. addHighlightArea() {
  15314. this.container.classList.add("highlightArea");
  15315. }
  15316. #download() {
  15317. this.downloadManager?.openOrDownloadData(this.content, this.filename);
  15318. }
  15319. }
  15320. class AnnotationLayer {
  15321. #accessibilityManager = null;
  15322. #annotationCanvasMap = null;
  15323. #editableAnnotations = new Map();
  15324. constructor({
  15325. div,
  15326. accessibilityManager,
  15327. annotationCanvasMap,
  15328. annotationEditorUIManager,
  15329. page,
  15330. viewport
  15331. }) {
  15332. this.div = div;
  15333. this.#accessibilityManager = accessibilityManager;
  15334. this.#annotationCanvasMap = annotationCanvasMap;
  15335. this.page = page;
  15336. this.viewport = viewport;
  15337. this.zIndex = 0;
  15338. this._annotationEditorUIManager = annotationEditorUIManager;
  15339. }
  15340. hasEditableAnnotations() {
  15341. return this.#editableAnnotations.size > 0;
  15342. }
  15343. #appendElement(element, id) {
  15344. const contentElement = element.firstChild || element;
  15345. contentElement.id = `${AnnotationPrefix}${id}`;
  15346. this.div.append(element);
  15347. this.#accessibilityManager?.moveElementInDOM(this.div, element, contentElement, false);
  15348. }
  15349. async render(params) {
  15350. const {
  15351. annotations
  15352. } = params;
  15353. const layer = this.div;
  15354. setLayerDimensions(layer, this.viewport);
  15355. const popupToElements = new Map();
  15356. const elementParams = {
  15357. data: null,
  15358. layer,
  15359. linkService: params.linkService,
  15360. downloadManager: params.downloadManager,
  15361. imageResourcesPath: params.imageResourcesPath || "",
  15362. renderForms: params.renderForms !== false,
  15363. svgFactory: new DOMSVGFactory(),
  15364. annotationStorage: params.annotationStorage || new AnnotationStorage(),
  15365. enableScripting: params.enableScripting === true,
  15366. hasJSActions: params.hasJSActions,
  15367. fieldObjects: params.fieldObjects,
  15368. parent: this,
  15369. elements: null
  15370. };
  15371. for (const data of annotations) {
  15372. if (data.noHTML) {
  15373. continue;
  15374. }
  15375. const isPopupAnnotation = data.annotationType === AnnotationType.POPUP;
  15376. if (!isPopupAnnotation) {
  15377. const {
  15378. width,
  15379. height
  15380. } = getRectDims(data.rect);
  15381. if (width <= 0 || height <= 0) {
  15382. continue;
  15383. }
  15384. } else {
  15385. const elements = popupToElements.get(data.id);
  15386. if (!elements) {
  15387. continue;
  15388. }
  15389. elementParams.elements = elements;
  15390. }
  15391. elementParams.data = data;
  15392. const element = AnnotationElementFactory.create(elementParams);
  15393. if (!element.isRenderable) {
  15394. continue;
  15395. }
  15396. if (!isPopupAnnotation && data.popupRef) {
  15397. const elements = popupToElements.get(data.popupRef);
  15398. if (!elements) {
  15399. popupToElements.set(data.popupRef, [element]);
  15400. } else {
  15401. elements.push(element);
  15402. }
  15403. }
  15404. const rendered = element.render();
  15405. if (data.hidden) {
  15406. rendered.style.visibility = "hidden";
  15407. }
  15408. this.#appendElement(rendered, data.id);
  15409. if (element._isEditable) {
  15410. this.#editableAnnotations.set(element.data.id, element);
  15411. this._annotationEditorUIManager?.renderAnnotationElement(element);
  15412. }
  15413. }
  15414. this.#setAnnotationCanvasMap();
  15415. }
  15416. update({
  15417. viewport
  15418. }) {
  15419. const layer = this.div;
  15420. this.viewport = viewport;
  15421. setLayerDimensions(layer, {
  15422. rotation: viewport.rotation
  15423. });
  15424. this.#setAnnotationCanvasMap();
  15425. layer.hidden = false;
  15426. }
  15427. #setAnnotationCanvasMap() {
  15428. if (!this.#annotationCanvasMap) {
  15429. return;
  15430. }
  15431. const layer = this.div;
  15432. for (const [id, canvas] of this.#annotationCanvasMap) {
  15433. const element = layer.querySelector(`[data-annotation-id="${id}"]`);
  15434. if (!element) {
  15435. continue;
  15436. }
  15437. canvas.className = "annotationContent";
  15438. const {
  15439. firstChild
  15440. } = element;
  15441. if (!firstChild) {
  15442. element.append(canvas);
  15443. } else if (firstChild.nodeName === "CANVAS") {
  15444. firstChild.replaceWith(canvas);
  15445. } else if (!firstChild.classList.contains("annotationContent")) {
  15446. firstChild.before(canvas);
  15447. } else {
  15448. firstChild.after(canvas);
  15449. }
  15450. }
  15451. this.#annotationCanvasMap.clear();
  15452. }
  15453. getEditableAnnotations() {
  15454. return Array.from(this.#editableAnnotations.values());
  15455. }
  15456. getEditableAnnotation(id) {
  15457. return this.#editableAnnotations.get(id);
  15458. }
  15459. }
  15460. ;// CONCATENATED MODULE: ./src/display/editor/freetext.js
  15461. const EOL_PATTERN = /\r\n?|\n/g;
  15462. class FreeTextEditor extends AnnotationEditor {
  15463. #boundEditorDivBlur = this.editorDivBlur.bind(this);
  15464. #boundEditorDivFocus = this.editorDivFocus.bind(this);
  15465. #boundEditorDivInput = this.editorDivInput.bind(this);
  15466. #boundEditorDivKeydown = this.editorDivKeydown.bind(this);
  15467. #boundEditorDivPaste = this.editorDivPaste.bind(this);
  15468. #color;
  15469. #content = "";
  15470. #editorDivId = `${this.id}-editor`;
  15471. #fontSize;
  15472. #initialData = null;
  15473. static _freeTextDefaultContent = "";
  15474. static _internalPadding = 0;
  15475. static _defaultColor = null;
  15476. static _defaultFontSize = 10;
  15477. static get _keyboardManager() {
  15478. const proto = FreeTextEditor.prototype;
  15479. const arrowChecker = self => self.isEmpty();
  15480. const small = AnnotationEditorUIManager.TRANSLATE_SMALL;
  15481. const big = AnnotationEditorUIManager.TRANSLATE_BIG;
  15482. return shadow(this, "_keyboardManager", new KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, {
  15483. bubbles: true
  15484. }], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, {
  15485. args: [-small, 0],
  15486. checker: arrowChecker
  15487. }], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, {
  15488. args: [-big, 0],
  15489. checker: arrowChecker
  15490. }], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, {
  15491. args: [small, 0],
  15492. checker: arrowChecker
  15493. }], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, {
  15494. args: [big, 0],
  15495. checker: arrowChecker
  15496. }], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, {
  15497. args: [0, -small],
  15498. checker: arrowChecker
  15499. }], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, {
  15500. args: [0, -big],
  15501. checker: arrowChecker
  15502. }], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, {
  15503. args: [0, small],
  15504. checker: arrowChecker
  15505. }], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, {
  15506. args: [0, big],
  15507. checker: arrowChecker
  15508. }]]));
  15509. }
  15510. static _type = "freetext";
  15511. static _editorType = AnnotationEditorType.FREETEXT;
  15512. constructor(params) {
  15513. super({
  15514. ...params,
  15515. name: "freeTextEditor"
  15516. });
  15517. this.#color = params.color || FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor;
  15518. this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;
  15519. }
  15520. static initialize(l10n, uiManager) {
  15521. AnnotationEditor.initialize(l10n, uiManager, {
  15522. strings: ["pdfjs-free-text-default-content"]
  15523. });
  15524. const style = getComputedStyle(document.documentElement);
  15525. this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding"));
  15526. }
  15527. static updateDefaultParams(type, value) {
  15528. switch (type) {
  15529. case AnnotationEditorParamsType.FREETEXT_SIZE:
  15530. FreeTextEditor._defaultFontSize = value;
  15531. break;
  15532. case AnnotationEditorParamsType.FREETEXT_COLOR:
  15533. FreeTextEditor._defaultColor = value;
  15534. break;
  15535. }
  15536. }
  15537. updateParams(type, value) {
  15538. switch (type) {
  15539. case AnnotationEditorParamsType.FREETEXT_SIZE:
  15540. this.#updateFontSize(value);
  15541. break;
  15542. case AnnotationEditorParamsType.FREETEXT_COLOR:
  15543. this.#updateColor(value);
  15544. break;
  15545. }
  15546. }
  15547. static get defaultPropertiesToUpdate() {
  15548. return [[AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || AnnotationEditor._defaultLineColor]];
  15549. }
  15550. get propertiesToUpdate() {
  15551. return [[AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [AnnotationEditorParamsType.FREETEXT_COLOR, this.#color]];
  15552. }
  15553. #updateFontSize(fontSize) {
  15554. const setFontsize = size => {
  15555. this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`;
  15556. this.translate(0, -(size - this.#fontSize) * this.parentScale);
  15557. this.#fontSize = size;
  15558. this.#setEditorDimensions();
  15559. };
  15560. const savedFontsize = this.#fontSize;
  15561. this.addCommands({
  15562. cmd: setFontsize.bind(this, fontSize),
  15563. undo: setFontsize.bind(this, savedFontsize),
  15564. post: this._uiManager.updateUI.bind(this._uiManager, this),
  15565. mustExec: true,
  15566. type: AnnotationEditorParamsType.FREETEXT_SIZE,
  15567. overwriteIfSameType: true,
  15568. keepUndo: true
  15569. });
  15570. }
  15571. #updateColor(color) {
  15572. const setColor = col => {
  15573. this.#color = this.editorDiv.style.color = col;
  15574. };
  15575. const savedColor = this.#color;
  15576. this.addCommands({
  15577. cmd: setColor.bind(this, color),
  15578. undo: setColor.bind(this, savedColor),
  15579. post: this._uiManager.updateUI.bind(this._uiManager, this),
  15580. mustExec: true,
  15581. type: AnnotationEditorParamsType.FREETEXT_COLOR,
  15582. overwriteIfSameType: true,
  15583. keepUndo: true
  15584. });
  15585. }
  15586. _translateEmpty(x, y) {
  15587. this._uiManager.translateSelectedEditors(x, y, true);
  15588. }
  15589. getInitialTranslation() {
  15590. const scale = this.parentScale;
  15591. return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale];
  15592. }
  15593. rebuild() {
  15594. if (!this.parent) {
  15595. return;
  15596. }
  15597. super.rebuild();
  15598. if (this.div === null) {
  15599. return;
  15600. }
  15601. if (!this.isAttachedToDOM) {
  15602. this.parent.add(this);
  15603. }
  15604. }
  15605. enableEditMode() {
  15606. if (this.isInEditMode()) {
  15607. return;
  15608. }
  15609. this.parent.setEditingState(false);
  15610. this.parent.updateToolbar(AnnotationEditorType.FREETEXT);
  15611. super.enableEditMode();
  15612. this.overlayDiv.classList.remove("enabled");
  15613. this.editorDiv.contentEditable = true;
  15614. this._isDraggable = false;
  15615. this.div.removeAttribute("aria-activedescendant");
  15616. const signal = this._uiManager._signal;
  15617. this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown, {
  15618. signal
  15619. });
  15620. this.editorDiv.addEventListener("focus", this.#boundEditorDivFocus, {
  15621. signal
  15622. });
  15623. this.editorDiv.addEventListener("blur", this.#boundEditorDivBlur, {
  15624. signal
  15625. });
  15626. this.editorDiv.addEventListener("input", this.#boundEditorDivInput, {
  15627. signal
  15628. });
  15629. this.editorDiv.addEventListener("paste", this.#boundEditorDivPaste, {
  15630. signal
  15631. });
  15632. }
  15633. disableEditMode() {
  15634. if (!this.isInEditMode()) {
  15635. return;
  15636. }
  15637. this.parent.setEditingState(true);
  15638. super.disableEditMode();
  15639. this.overlayDiv.classList.add("enabled");
  15640. this.editorDiv.contentEditable = false;
  15641. this.div.setAttribute("aria-activedescendant", this.#editorDivId);
  15642. this._isDraggable = true;
  15643. this.editorDiv.removeEventListener("keydown", this.#boundEditorDivKeydown);
  15644. this.editorDiv.removeEventListener("focus", this.#boundEditorDivFocus);
  15645. this.editorDiv.removeEventListener("blur", this.#boundEditorDivBlur);
  15646. this.editorDiv.removeEventListener("input", this.#boundEditorDivInput);
  15647. this.editorDiv.removeEventListener("paste", this.#boundEditorDivPaste);
  15648. this.div.focus({
  15649. preventScroll: true
  15650. });
  15651. this.isEditing = false;
  15652. this.parent.div.classList.add("freetextEditing");
  15653. }
  15654. focusin(event) {
  15655. if (!this._focusEventsAllowed) {
  15656. return;
  15657. }
  15658. super.focusin(event);
  15659. if (event.target !== this.editorDiv) {
  15660. this.editorDiv.focus();
  15661. }
  15662. }
  15663. onceAdded() {
  15664. if (this.width) {
  15665. return;
  15666. }
  15667. this.enableEditMode();
  15668. this.editorDiv.focus();
  15669. if (this._initialOptions?.isCentered) {
  15670. this.center();
  15671. }
  15672. this._initialOptions = null;
  15673. }
  15674. isEmpty() {
  15675. return !this.editorDiv || this.editorDiv.innerText.trim() === "";
  15676. }
  15677. remove() {
  15678. this.isEditing = false;
  15679. if (this.parent) {
  15680. this.parent.setEditingState(true);
  15681. this.parent.div.classList.add("freetextEditing");
  15682. }
  15683. super.remove();
  15684. }
  15685. #extractText() {
  15686. const buffer = [];
  15687. this.editorDiv.normalize();
  15688. for (const child of this.editorDiv.childNodes) {
  15689. buffer.push(FreeTextEditor.#getNodeContent(child));
  15690. }
  15691. return buffer.join("\n");
  15692. }
  15693. #setEditorDimensions() {
  15694. const [parentWidth, parentHeight] = this.parentDimensions;
  15695. let rect;
  15696. if (this.isAttachedToDOM) {
  15697. rect = this.div.getBoundingClientRect();
  15698. } else {
  15699. const {
  15700. currentLayer,
  15701. div
  15702. } = this;
  15703. const savedDisplay = div.style.display;
  15704. const savedVisibility = div.classList.contains("hidden");
  15705. div.classList.remove("hidden");
  15706. div.style.display = "hidden";
  15707. currentLayer.div.append(this.div);
  15708. rect = div.getBoundingClientRect();
  15709. div.remove();
  15710. div.style.display = savedDisplay;
  15711. div.classList.toggle("hidden", savedVisibility);
  15712. }
  15713. if (this.rotation % 180 === this.parentRotation % 180) {
  15714. this.width = rect.width / parentWidth;
  15715. this.height = rect.height / parentHeight;
  15716. } else {
  15717. this.width = rect.height / parentWidth;
  15718. this.height = rect.width / parentHeight;
  15719. }
  15720. this.fixAndSetPosition();
  15721. }
  15722. commit() {
  15723. if (!this.isInEditMode()) {
  15724. return;
  15725. }
  15726. super.commit();
  15727. this.disableEditMode();
  15728. const savedText = this.#content;
  15729. const newText = this.#content = this.#extractText().trimEnd();
  15730. if (savedText === newText) {
  15731. return;
  15732. }
  15733. const setText = text => {
  15734. this.#content = text;
  15735. if (!text) {
  15736. this.remove();
  15737. return;
  15738. }
  15739. this.#setContent();
  15740. this._uiManager.rebuild(this);
  15741. this.#setEditorDimensions();
  15742. };
  15743. this.addCommands({
  15744. cmd: () => {
  15745. setText(newText);
  15746. },
  15747. undo: () => {
  15748. setText(savedText);
  15749. },
  15750. mustExec: false
  15751. });
  15752. this.#setEditorDimensions();
  15753. }
  15754. shouldGetKeyboardEvents() {
  15755. return this.isInEditMode();
  15756. }
  15757. enterInEditMode() {
  15758. this.enableEditMode();
  15759. this.editorDiv.focus();
  15760. }
  15761. dblclick(event) {
  15762. this.enterInEditMode();
  15763. }
  15764. keydown(event) {
  15765. if (event.target === this.div && event.key === "Enter") {
  15766. this.enterInEditMode();
  15767. event.preventDefault();
  15768. }
  15769. }
  15770. editorDivKeydown(event) {
  15771. FreeTextEditor._keyboardManager.exec(this, event);
  15772. }
  15773. editorDivFocus(event) {
  15774. this.isEditing = true;
  15775. }
  15776. editorDivBlur(event) {
  15777. this.isEditing = false;
  15778. }
  15779. editorDivInput(event) {
  15780. this.parent.div.classList.toggle("freetextEditing", this.isEmpty());
  15781. }
  15782. disableEditing() {
  15783. this.editorDiv.setAttribute("role", "comment");
  15784. this.editorDiv.removeAttribute("aria-multiline");
  15785. }
  15786. enableEditing() {
  15787. this.editorDiv.setAttribute("role", "textbox");
  15788. this.editorDiv.setAttribute("aria-multiline", true);
  15789. }
  15790. render() {
  15791. if (this.div) {
  15792. return this.div;
  15793. }
  15794. let baseX, baseY;
  15795. if (this.width) {
  15796. baseX = this.x;
  15797. baseY = this.y;
  15798. }
  15799. super.render();
  15800. this.editorDiv = document.createElement("div");
  15801. this.editorDiv.className = "internal";
  15802. this.editorDiv.setAttribute("id", this.#editorDivId);
  15803. this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text");
  15804. this.enableEditing();
  15805. AnnotationEditor._l10nPromise.get("pdfjs-free-text-default-content").then(msg => this.editorDiv?.setAttribute("default-content", msg));
  15806. this.editorDiv.contentEditable = true;
  15807. const {
  15808. style
  15809. } = this.editorDiv;
  15810. style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
  15811. style.color = this.#color;
  15812. this.div.append(this.editorDiv);
  15813. this.overlayDiv = document.createElement("div");
  15814. this.overlayDiv.classList.add("overlay", "enabled");
  15815. this.div.append(this.overlayDiv);
  15816. bindEvents(this, this.div, ["dblclick", "keydown"]);
  15817. if (this.width) {
  15818. const [parentWidth, parentHeight] = this.parentDimensions;
  15819. if (this.annotationElementId) {
  15820. const {
  15821. position
  15822. } = this.#initialData;
  15823. let [tx, ty] = this.getInitialTranslation();
  15824. [tx, ty] = this.pageTranslationToScreen(tx, ty);
  15825. const [pageWidth, pageHeight] = this.pageDimensions;
  15826. const [pageX, pageY] = this.pageTranslation;
  15827. let posX, posY;
  15828. switch (this.rotation) {
  15829. case 0:
  15830. posX = baseX + (position[0] - pageX) / pageWidth;
  15831. posY = baseY + this.height - (position[1] - pageY) / pageHeight;
  15832. break;
  15833. case 90:
  15834. posX = baseX + (position[0] - pageX) / pageWidth;
  15835. posY = baseY - (position[1] - pageY) / pageHeight;
  15836. [tx, ty] = [ty, -tx];
  15837. break;
  15838. case 180:
  15839. posX = baseX - this.width + (position[0] - pageX) / pageWidth;
  15840. posY = baseY - (position[1] - pageY) / pageHeight;
  15841. [tx, ty] = [-tx, -ty];
  15842. break;
  15843. case 270:
  15844. posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth;
  15845. posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight;
  15846. [tx, ty] = [-ty, tx];
  15847. break;
  15848. }
  15849. this.setAt(posX * parentWidth, posY * parentHeight, tx, ty);
  15850. } else {
  15851. this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
  15852. }
  15853. this.#setContent();
  15854. this._isDraggable = true;
  15855. this.editorDiv.contentEditable = false;
  15856. } else {
  15857. this._isDraggable = false;
  15858. this.editorDiv.contentEditable = true;
  15859. }
  15860. return this.div;
  15861. }
  15862. static #getNodeContent(node) {
  15863. return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, "");
  15864. }
  15865. editorDivPaste(event) {
  15866. const clipboardData = event.clipboardData || window.clipboardData;
  15867. const {
  15868. types
  15869. } = clipboardData;
  15870. if (types.length === 1 && types[0] === "text/plain") {
  15871. return;
  15872. }
  15873. event.preventDefault();
  15874. const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n");
  15875. if (!paste) {
  15876. return;
  15877. }
  15878. const selection = window.getSelection();
  15879. if (!selection.rangeCount) {
  15880. return;
  15881. }
  15882. this.editorDiv.normalize();
  15883. selection.deleteFromDocument();
  15884. const range = selection.getRangeAt(0);
  15885. if (!paste.includes("\n")) {
  15886. range.insertNode(document.createTextNode(paste));
  15887. this.editorDiv.normalize();
  15888. selection.collapseToStart();
  15889. return;
  15890. }
  15891. const {
  15892. startContainer,
  15893. startOffset
  15894. } = range;
  15895. const bufferBefore = [];
  15896. const bufferAfter = [];
  15897. if (startContainer.nodeType === Node.TEXT_NODE) {
  15898. const parent = startContainer.parentElement;
  15899. bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, ""));
  15900. if (parent !== this.editorDiv) {
  15901. let buffer = bufferBefore;
  15902. for (const child of this.editorDiv.childNodes) {
  15903. if (child === parent) {
  15904. buffer = bufferAfter;
  15905. continue;
  15906. }
  15907. buffer.push(FreeTextEditor.#getNodeContent(child));
  15908. }
  15909. }
  15910. bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, ""));
  15911. } else if (startContainer === this.editorDiv) {
  15912. let buffer = bufferBefore;
  15913. let i = 0;
  15914. for (const child of this.editorDiv.childNodes) {
  15915. if (i++ === startOffset) {
  15916. buffer = bufferAfter;
  15917. }
  15918. buffer.push(FreeTextEditor.#getNodeContent(child));
  15919. }
  15920. }
  15921. this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`;
  15922. this.#setContent();
  15923. const newRange = new Range();
  15924. let beforeLength = bufferBefore.reduce((acc, line) => acc + line.length, 0);
  15925. for (const {
  15926. firstChild
  15927. } of this.editorDiv.childNodes) {
  15928. if (firstChild.nodeType === Node.TEXT_NODE) {
  15929. const length = firstChild.nodeValue.length;
  15930. if (beforeLength <= length) {
  15931. newRange.setStart(firstChild, beforeLength);
  15932. newRange.setEnd(firstChild, beforeLength);
  15933. break;
  15934. }
  15935. beforeLength -= length;
  15936. }
  15937. }
  15938. selection.removeAllRanges();
  15939. selection.addRange(newRange);
  15940. }
  15941. #setContent() {
  15942. this.editorDiv.replaceChildren();
  15943. if (!this.#content) {
  15944. return;
  15945. }
  15946. for (const line of this.#content.split("\n")) {
  15947. const div = document.createElement("div");
  15948. div.append(line ? document.createTextNode(line) : document.createElement("br"));
  15949. this.editorDiv.append(div);
  15950. }
  15951. }
  15952. #serializeContent() {
  15953. return this.#content.replaceAll("\xa0", " ");
  15954. }
  15955. static #deserializeContent(content) {
  15956. return content.replaceAll(" ", "\xa0");
  15957. }
  15958. get contentDiv() {
  15959. return this.editorDiv;
  15960. }
  15961. static deserialize(data, parent, uiManager) {
  15962. let initialData = null;
  15963. if (data instanceof FreeTextAnnotationElement) {
  15964. const {
  15965. data: {
  15966. defaultAppearanceData: {
  15967. fontSize,
  15968. fontColor
  15969. },
  15970. rect,
  15971. rotation,
  15972. id
  15973. },
  15974. textContent,
  15975. textPosition,
  15976. parent: {
  15977. page: {
  15978. pageNumber
  15979. }
  15980. }
  15981. } = data;
  15982. if (!textContent || textContent.length === 0) {
  15983. return null;
  15984. }
  15985. initialData = data = {
  15986. annotationType: AnnotationEditorType.FREETEXT,
  15987. color: Array.from(fontColor),
  15988. fontSize,
  15989. value: textContent.join("\n"),
  15990. position: textPosition,
  15991. pageIndex: pageNumber - 1,
  15992. rect: rect.slice(0),
  15993. rotation,
  15994. id,
  15995. deleted: false
  15996. };
  15997. }
  15998. const editor = super.deserialize(data, parent, uiManager);
  15999. editor.#fontSize = data.fontSize;
  16000. editor.#color = Util.makeHexColor(...data.color);
  16001. editor.#content = FreeTextEditor.#deserializeContent(data.value);
  16002. editor.annotationElementId = data.id || null;
  16003. editor.#initialData = initialData;
  16004. return editor;
  16005. }
  16006. serialize(isForCopying = false) {
  16007. if (this.isEmpty()) {
  16008. return null;
  16009. }
  16010. if (this.deleted) {
  16011. return {
  16012. pageIndex: this.pageIndex,
  16013. id: this.annotationElementId,
  16014. deleted: true
  16015. };
  16016. }
  16017. const padding = FreeTextEditor._internalPadding * this.parentScale;
  16018. const rect = this.getRect(padding, padding);
  16019. const color = AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.#color);
  16020. const serialized = {
  16021. annotationType: AnnotationEditorType.FREETEXT,
  16022. color,
  16023. fontSize: this.#fontSize,
  16024. value: this.#serializeContent(),
  16025. pageIndex: this.pageIndex,
  16026. rect,
  16027. rotation: this.rotation,
  16028. structTreeParentId: this._structTreeParentId
  16029. };
  16030. if (isForCopying) {
  16031. return serialized;
  16032. }
  16033. if (this.annotationElementId && !this.#hasElementChanged(serialized)) {
  16034. return null;
  16035. }
  16036. serialized.id = this.annotationElementId;
  16037. return serialized;
  16038. }
  16039. #hasElementChanged(serialized) {
  16040. const {
  16041. value,
  16042. fontSize,
  16043. color,
  16044. pageIndex
  16045. } = this.#initialData;
  16046. return this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex;
  16047. }
  16048. renderAnnotationElement(annotation) {
  16049. const content = super.renderAnnotationElement(annotation);
  16050. if (this.deleted) {
  16051. return content;
  16052. }
  16053. const {
  16054. style
  16055. } = content;
  16056. style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
  16057. style.color = this.#color;
  16058. content.replaceChildren();
  16059. for (const line of this.#content.split("\n")) {
  16060. const div = document.createElement("div");
  16061. div.append(line ? document.createTextNode(line) : document.createElement("br"));
  16062. content.append(div);
  16063. }
  16064. const padding = FreeTextEditor._internalPadding * this.parentScale;
  16065. annotation.updateEdited({
  16066. rect: this.getRect(padding, padding),
  16067. popupContent: this.#content
  16068. });
  16069. return content;
  16070. }
  16071. resetAnnotationElement(annotation) {
  16072. super.resetAnnotationElement(annotation);
  16073. annotation.resetEdited();
  16074. }
  16075. }
  16076. ;// CONCATENATED MODULE: ./src/display/editor/outliner.js
  16077. class Outliner {
  16078. #box;
  16079. #verticalEdges = [];
  16080. #intervals = [];
  16081. constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) {
  16082. let minX = Infinity;
  16083. let maxX = -Infinity;
  16084. let minY = Infinity;
  16085. let maxY = -Infinity;
  16086. const NUMBER_OF_DIGITS = 4;
  16087. const EPSILON = 10 ** -NUMBER_OF_DIGITS;
  16088. for (const {
  16089. x,
  16090. y,
  16091. width,
  16092. height
  16093. } of boxes) {
  16094. const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON;
  16095. const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON;
  16096. const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON;
  16097. const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON;
  16098. const left = [x1, y1, y2, true];
  16099. const right = [x2, y1, y2, false];
  16100. this.#verticalEdges.push(left, right);
  16101. minX = Math.min(minX, x1);
  16102. maxX = Math.max(maxX, x2);
  16103. minY = Math.min(minY, y1);
  16104. maxY = Math.max(maxY, y2);
  16105. }
  16106. const bboxWidth = maxX - minX + 2 * innerMargin;
  16107. const bboxHeight = maxY - minY + 2 * innerMargin;
  16108. const shiftedMinX = minX - innerMargin;
  16109. const shiftedMinY = minY - innerMargin;
  16110. const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2);
  16111. const lastPoint = [lastEdge[0], lastEdge[2]];
  16112. for (const edge of this.#verticalEdges) {
  16113. const [x, y1, y2] = edge;
  16114. edge[0] = (x - shiftedMinX) / bboxWidth;
  16115. edge[1] = (y1 - shiftedMinY) / bboxHeight;
  16116. edge[2] = (y2 - shiftedMinY) / bboxHeight;
  16117. }
  16118. this.#box = {
  16119. x: shiftedMinX,
  16120. y: shiftedMinY,
  16121. width: bboxWidth,
  16122. height: bboxHeight,
  16123. lastPoint
  16124. };
  16125. }
  16126. getOutlines() {
  16127. this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
  16128. const outlineVerticalEdges = [];
  16129. for (const edge of this.#verticalEdges) {
  16130. if (edge[3]) {
  16131. outlineVerticalEdges.push(...this.#breakEdge(edge));
  16132. this.#insert(edge);
  16133. } else {
  16134. this.#remove(edge);
  16135. outlineVerticalEdges.push(...this.#breakEdge(edge));
  16136. }
  16137. }
  16138. return this.#getOutlines(outlineVerticalEdges);
  16139. }
  16140. #getOutlines(outlineVerticalEdges) {
  16141. const edges = [];
  16142. const allEdges = new Set();
  16143. for (const edge of outlineVerticalEdges) {
  16144. const [x, y1, y2] = edge;
  16145. edges.push([x, y1, edge], [x, y2, edge]);
  16146. }
  16147. edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]);
  16148. for (let i = 0, ii = edges.length; i < ii; i += 2) {
  16149. const edge1 = edges[i][2];
  16150. const edge2 = edges[i + 1][2];
  16151. edge1.push(edge2);
  16152. edge2.push(edge1);
  16153. allEdges.add(edge1);
  16154. allEdges.add(edge2);
  16155. }
  16156. const outlines = [];
  16157. let outline;
  16158. while (allEdges.size > 0) {
  16159. const edge = allEdges.values().next().value;
  16160. let [x, y1, y2, edge1, edge2] = edge;
  16161. allEdges.delete(edge);
  16162. let lastPointX = x;
  16163. let lastPointY = y1;
  16164. outline = [x, y2];
  16165. outlines.push(outline);
  16166. while (true) {
  16167. let e;
  16168. if (allEdges.has(edge1)) {
  16169. e = edge1;
  16170. } else if (allEdges.has(edge2)) {
  16171. e = edge2;
  16172. } else {
  16173. break;
  16174. }
  16175. allEdges.delete(e);
  16176. [x, y1, y2, edge1, edge2] = e;
  16177. if (lastPointX !== x) {
  16178. outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2);
  16179. lastPointX = x;
  16180. }
  16181. lastPointY = lastPointY === y1 ? y2 : y1;
  16182. }
  16183. outline.push(lastPointX, lastPointY);
  16184. }
  16185. return new HighlightOutline(outlines, this.#box);
  16186. }
  16187. #binarySearch(y) {
  16188. const array = this.#intervals;
  16189. let start = 0;
  16190. let end = array.length - 1;
  16191. while (start <= end) {
  16192. const middle = start + end >> 1;
  16193. const y1 = array[middle][0];
  16194. if (y1 === y) {
  16195. return middle;
  16196. }
  16197. if (y1 < y) {
  16198. start = middle + 1;
  16199. } else {
  16200. end = middle - 1;
  16201. }
  16202. }
  16203. return end + 1;
  16204. }
  16205. #insert([, y1, y2]) {
  16206. const index = this.#binarySearch(y1);
  16207. this.#intervals.splice(index, 0, [y1, y2]);
  16208. }
  16209. #remove([, y1, y2]) {
  16210. const index = this.#binarySearch(y1);
  16211. for (let i = index; i < this.#intervals.length; i++) {
  16212. const [start, end] = this.#intervals[i];
  16213. if (start !== y1) {
  16214. break;
  16215. }
  16216. if (start === y1 && end === y2) {
  16217. this.#intervals.splice(i, 1);
  16218. return;
  16219. }
  16220. }
  16221. for (let i = index - 1; i >= 0; i--) {
  16222. const [start, end] = this.#intervals[i];
  16223. if (start !== y1) {
  16224. break;
  16225. }
  16226. if (start === y1 && end === y2) {
  16227. this.#intervals.splice(i, 1);
  16228. return;
  16229. }
  16230. }
  16231. }
  16232. #breakEdge(edge) {
  16233. const [x, y1, y2] = edge;
  16234. const results = [[x, y1, y2]];
  16235. const index = this.#binarySearch(y2);
  16236. for (let i = 0; i < index; i++) {
  16237. const [start, end] = this.#intervals[i];
  16238. for (let j = 0, jj = results.length; j < jj; j++) {
  16239. const [, y3, y4] = results[j];
  16240. if (end <= y3 || y4 <= start) {
  16241. continue;
  16242. }
  16243. if (y3 >= start) {
  16244. if (y4 > end) {
  16245. results[j][1] = end;
  16246. } else {
  16247. if (jj === 1) {
  16248. return [];
  16249. }
  16250. results.splice(j, 1);
  16251. j--;
  16252. jj--;
  16253. }
  16254. continue;
  16255. }
  16256. results[j][2] = start;
  16257. if (y4 > end) {
  16258. results.push([x, end, y4]);
  16259. }
  16260. }
  16261. }
  16262. return results;
  16263. }
  16264. }
  16265. class Outline {
  16266. toSVGPath() {
  16267. throw new Error("Abstract method `toSVGPath` must be implemented.");
  16268. }
  16269. get box() {
  16270. throw new Error("Abstract getter `box` must be implemented.");
  16271. }
  16272. serialize(_bbox, _rotation) {
  16273. throw new Error("Abstract method `serialize` must be implemented.");
  16274. }
  16275. get free() {
  16276. return this instanceof FreeHighlightOutline;
  16277. }
  16278. }
  16279. class HighlightOutline extends Outline {
  16280. #box;
  16281. #outlines;
  16282. constructor(outlines, box) {
  16283. super();
  16284. this.#outlines = outlines;
  16285. this.#box = box;
  16286. }
  16287. toSVGPath() {
  16288. const buffer = [];
  16289. for (const polygon of this.#outlines) {
  16290. let [prevX, prevY] = polygon;
  16291. buffer.push(`M${prevX} ${prevY}`);
  16292. for (let i = 2; i < polygon.length; i += 2) {
  16293. const x = polygon[i];
  16294. const y = polygon[i + 1];
  16295. if (x === prevX) {
  16296. buffer.push(`V${y}`);
  16297. prevY = y;
  16298. } else if (y === prevY) {
  16299. buffer.push(`H${x}`);
  16300. prevX = x;
  16301. }
  16302. }
  16303. buffer.push("Z");
  16304. }
  16305. return buffer.join(" ");
  16306. }
  16307. serialize([blX, blY, trX, trY], _rotation) {
  16308. const outlines = [];
  16309. const width = trX - blX;
  16310. const height = trY - blY;
  16311. for (const outline of this.#outlines) {
  16312. const points = new Array(outline.length);
  16313. for (let i = 0; i < outline.length; i += 2) {
  16314. points[i] = blX + outline[i] * width;
  16315. points[i + 1] = trY - outline[i + 1] * height;
  16316. }
  16317. outlines.push(points);
  16318. }
  16319. return outlines;
  16320. }
  16321. get box() {
  16322. return this.#box;
  16323. }
  16324. }
  16325. class FreeOutliner {
  16326. #box;
  16327. #bottom = [];
  16328. #innerMargin;
  16329. #isLTR;
  16330. #top = [];
  16331. #last = new Float64Array(18);
  16332. #lastX;
  16333. #lastY;
  16334. #min;
  16335. #min_dist;
  16336. #scaleFactor;
  16337. #thickness;
  16338. #points = [];
  16339. static #MIN_DIST = 8;
  16340. static #MIN_DIFF = 2;
  16341. static #MIN = FreeOutliner.#MIN_DIST + FreeOutliner.#MIN_DIFF;
  16342. constructor({
  16343. x,
  16344. y
  16345. }, box, scaleFactor, thickness, isLTR, innerMargin = 0) {
  16346. this.#box = box;
  16347. this.#thickness = thickness * scaleFactor;
  16348. this.#isLTR = isLTR;
  16349. this.#last.set([NaN, NaN, NaN, NaN, x, y], 6);
  16350. this.#innerMargin = innerMargin;
  16351. this.#min_dist = FreeOutliner.#MIN_DIST * scaleFactor;
  16352. this.#min = FreeOutliner.#MIN * scaleFactor;
  16353. this.#scaleFactor = scaleFactor;
  16354. this.#points.push(x, y);
  16355. }
  16356. get free() {
  16357. return true;
  16358. }
  16359. isEmpty() {
  16360. return isNaN(this.#last[8]);
  16361. }
  16362. #getLastCoords() {
  16363. const lastTop = this.#last.subarray(4, 6);
  16364. const lastBottom = this.#last.subarray(16, 18);
  16365. const [x, y, width, height] = this.#box;
  16366. 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];
  16367. }
  16368. add({
  16369. x,
  16370. y
  16371. }) {
  16372. this.#lastX = x;
  16373. this.#lastY = y;
  16374. const [layerX, layerY, layerWidth, layerHeight] = this.#box;
  16375. let [x1, y1, x2, y2] = this.#last.subarray(8, 12);
  16376. const diffX = x - x2;
  16377. const diffY = y - y2;
  16378. const d = Math.hypot(diffX, diffY);
  16379. if (d < this.#min) {
  16380. return false;
  16381. }
  16382. const diffD = d - this.#min_dist;
  16383. const K = diffD / d;
  16384. const shiftX = K * diffX;
  16385. const shiftY = K * diffY;
  16386. let x0 = x1;
  16387. let y0 = y1;
  16388. x1 = x2;
  16389. y1 = y2;
  16390. x2 += shiftX;
  16391. y2 += shiftY;
  16392. this.#points?.push(x, y);
  16393. const nX = -shiftY / diffD;
  16394. const nY = shiftX / diffD;
  16395. const thX = nX * this.#thickness;
  16396. const thY = nY * this.#thickness;
  16397. this.#last.set(this.#last.subarray(2, 8), 0);
  16398. this.#last.set([x2 + thX, y2 + thY], 4);
  16399. this.#last.set(this.#last.subarray(14, 18), 12);
  16400. this.#last.set([x2 - thX, y2 - thY], 16);
  16401. if (isNaN(this.#last[6])) {
  16402. if (this.#top.length === 0) {
  16403. this.#last.set([x1 + thX, y1 + thY], 2);
  16404. this.#top.push(NaN, NaN, NaN, NaN, (x1 + thX - layerX) / layerWidth, (y1 + thY - layerY) / layerHeight);
  16405. this.#last.set([x1 - thX, y1 - thY], 14);
  16406. this.#bottom.push(NaN, NaN, NaN, NaN, (x1 - thX - layerX) / layerWidth, (y1 - thY - layerY) / layerHeight);
  16407. }
  16408. this.#last.set([x0, y0, x1, y1, x2, y2], 6);
  16409. return !this.isEmpty();
  16410. }
  16411. this.#last.set([x0, y0, x1, y1, x2, y2], 6);
  16412. const angle = Math.abs(Math.atan2(y0 - y1, x0 - x1) - Math.atan2(shiftY, shiftX));
  16413. if (angle < Math.PI / 2) {
  16414. [x1, y1, x2, y2] = this.#last.subarray(2, 6);
  16415. this.#top.push(NaN, NaN, NaN, NaN, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight);
  16416. [x1, y1, x0, y0] = this.#last.subarray(14, 18);
  16417. this.#bottom.push(NaN, NaN, NaN, NaN, ((x0 + x1) / 2 - layerX) / layerWidth, ((y0 + y1) / 2 - layerY) / layerHeight);
  16418. return true;
  16419. }
  16420. [x0, y0, x1, y1, x2, y2] = this.#last.subarray(0, 6);
  16421. 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);
  16422. [x2, y2, x1, y1, x0, y0] = this.#last.subarray(12, 18);
  16423. 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);
  16424. return true;
  16425. }
  16426. toSVGPath() {
  16427. if (this.isEmpty()) {
  16428. return "";
  16429. }
  16430. const top = this.#top;
  16431. const bottom = this.#bottom;
  16432. const lastTop = this.#last.subarray(4, 6);
  16433. const lastBottom = this.#last.subarray(16, 18);
  16434. const [x, y, width, height] = this.#box;
  16435. const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords();
  16436. if (isNaN(this.#last[6]) && !this.isEmpty()) {
  16437. 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`;
  16438. }
  16439. const buffer = [];
  16440. buffer.push(`M${top[4]} ${top[5]}`);
  16441. for (let i = 6; i < top.length; i += 6) {
  16442. if (isNaN(top[i])) {
  16443. buffer.push(`L${top[i + 4]} ${top[i + 5]}`);
  16444. } else {
  16445. buffer.push(`C${top[i]} ${top[i + 1]} ${top[i + 2]} ${top[i + 3]} ${top[i + 4]} ${top[i + 5]}`);
  16446. }
  16447. }
  16448. 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}`);
  16449. for (let i = bottom.length - 6; i >= 6; i -= 6) {
  16450. if (isNaN(bottom[i])) {
  16451. buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`);
  16452. } else {
  16453. buffer.push(`C${bottom[i]} ${bottom[i + 1]} ${bottom[i + 2]} ${bottom[i + 3]} ${bottom[i + 4]} ${bottom[i + 5]}`);
  16454. }
  16455. }
  16456. buffer.push(`L${bottom[4]} ${bottom[5]} Z`);
  16457. return buffer.join(" ");
  16458. }
  16459. getOutlines() {
  16460. const top = this.#top;
  16461. const bottom = this.#bottom;
  16462. const last = this.#last;
  16463. const lastTop = last.subarray(4, 6);
  16464. const lastBottom = last.subarray(16, 18);
  16465. const [layerX, layerY, layerWidth, layerHeight] = this.#box;
  16466. const points = new Float64Array((this.#points?.length ?? 0) + 2);
  16467. for (let i = 0, ii = points.length - 2; i < ii; i += 2) {
  16468. points[i] = (this.#points[i] - layerX) / layerWidth;
  16469. points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight;
  16470. }
  16471. points[points.length - 2] = (this.#lastX - layerX) / layerWidth;
  16472. points[points.length - 1] = (this.#lastY - layerY) / layerHeight;
  16473. const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords();
  16474. if (isNaN(last[6]) && !this.isEmpty()) {
  16475. const outline = new Float64Array(36);
  16476. 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);
  16477. return new FreeHighlightOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR);
  16478. }
  16479. const outline = new Float64Array(this.#top.length + 24 + this.#bottom.length);
  16480. let N = top.length;
  16481. for (let i = 0; i < N; i += 2) {
  16482. if (isNaN(top[i])) {
  16483. outline[i] = outline[i + 1] = NaN;
  16484. continue;
  16485. }
  16486. outline[i] = top[i];
  16487. outline[i + 1] = top[i + 1];
  16488. }
  16489. 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], N);
  16490. N += 24;
  16491. for (let i = bottom.length - 6; i >= 6; i -= 6) {
  16492. for (let j = 0; j < 6; j += 2) {
  16493. if (isNaN(bottom[i + j])) {
  16494. outline[N] = outline[N + 1] = NaN;
  16495. N += 2;
  16496. continue;
  16497. }
  16498. outline[N] = bottom[i + j];
  16499. outline[N + 1] = bottom[i + j + 1];
  16500. N += 2;
  16501. }
  16502. }
  16503. outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], N);
  16504. return new FreeHighlightOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR);
  16505. }
  16506. }
  16507. class FreeHighlightOutline extends Outline {
  16508. #box;
  16509. #bbox = null;
  16510. #innerMargin;
  16511. #isLTR;
  16512. #points;
  16513. #scaleFactor;
  16514. #outline;
  16515. constructor(outline, points, box, scaleFactor, innerMargin, isLTR) {
  16516. super();
  16517. this.#outline = outline;
  16518. this.#points = points;
  16519. this.#box = box;
  16520. this.#scaleFactor = scaleFactor;
  16521. this.#innerMargin = innerMargin;
  16522. this.#isLTR = isLTR;
  16523. this.#computeMinMax(isLTR);
  16524. const {
  16525. x,
  16526. y,
  16527. width,
  16528. height
  16529. } = this.#bbox;
  16530. for (let i = 0, ii = outline.length; i < ii; i += 2) {
  16531. outline[i] = (outline[i] - x) / width;
  16532. outline[i + 1] = (outline[i + 1] - y) / height;
  16533. }
  16534. for (let i = 0, ii = points.length; i < ii; i += 2) {
  16535. points[i] = (points[i] - x) / width;
  16536. points[i + 1] = (points[i + 1] - y) / height;
  16537. }
  16538. }
  16539. toSVGPath() {
  16540. const buffer = [`M${this.#outline[4]} ${this.#outline[5]}`];
  16541. for (let i = 6, ii = this.#outline.length; i < ii; i += 6) {
  16542. if (isNaN(this.#outline[i])) {
  16543. buffer.push(`L${this.#outline[i + 4]} ${this.#outline[i + 5]}`);
  16544. continue;
  16545. }
  16546. 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]}`);
  16547. }
  16548. buffer.push("Z");
  16549. return buffer.join(" ");
  16550. }
  16551. serialize([blX, blY, trX, trY], rotation) {
  16552. const width = trX - blX;
  16553. const height = trY - blY;
  16554. let outline;
  16555. let points;
  16556. switch (rotation) {
  16557. case 0:
  16558. outline = this.#rescale(this.#outline, blX, trY, width, -height);
  16559. points = this.#rescale(this.#points, blX, trY, width, -height);
  16560. break;
  16561. case 90:
  16562. outline = this.#rescaleAndSwap(this.#outline, blX, blY, width, height);
  16563. points = this.#rescaleAndSwap(this.#points, blX, blY, width, height);
  16564. break;
  16565. case 180:
  16566. outline = this.#rescale(this.#outline, trX, blY, -width, height);
  16567. points = this.#rescale(this.#points, trX, blY, -width, height);
  16568. break;
  16569. case 270:
  16570. outline = this.#rescaleAndSwap(this.#outline, trX, trY, -width, -height);
  16571. points = this.#rescaleAndSwap(this.#points, trX, trY, -width, -height);
  16572. break;
  16573. }
  16574. return {
  16575. outline: Array.from(outline),
  16576. points: [Array.from(points)]
  16577. };
  16578. }
  16579. #rescale(src, tx, ty, sx, sy) {
  16580. const dest = new Float64Array(src.length);
  16581. for (let i = 0, ii = src.length; i < ii; i += 2) {
  16582. dest[i] = tx + src[i] * sx;
  16583. dest[i + 1] = ty + src[i + 1] * sy;
  16584. }
  16585. return dest;
  16586. }
  16587. #rescaleAndSwap(src, tx, ty, sx, sy) {
  16588. const dest = new Float64Array(src.length);
  16589. for (let i = 0, ii = src.length; i < ii; i += 2) {
  16590. dest[i] = tx + src[i + 1] * sx;
  16591. dest[i + 1] = ty + src[i] * sy;
  16592. }
  16593. return dest;
  16594. }
  16595. #computeMinMax(isLTR) {
  16596. const outline = this.#outline;
  16597. let lastX = outline[4];
  16598. let lastY = outline[5];
  16599. let minX = lastX;
  16600. let minY = lastY;
  16601. let maxX = lastX;
  16602. let maxY = lastY;
  16603. let lastPointX = lastX;
  16604. let lastPointY = lastY;
  16605. const ltrCallback = isLTR ? Math.max : Math.min;
  16606. for (let i = 6, ii = outline.length; i < ii; i += 6) {
  16607. if (isNaN(outline[i])) {
  16608. minX = Math.min(minX, outline[i + 4]);
  16609. minY = Math.min(minY, outline[i + 5]);
  16610. maxX = Math.max(maxX, outline[i + 4]);
  16611. maxY = Math.max(maxY, outline[i + 5]);
  16612. if (lastPointY < outline[i + 5]) {
  16613. lastPointX = outline[i + 4];
  16614. lastPointY = outline[i + 5];
  16615. } else if (lastPointY === outline[i + 5]) {
  16616. lastPointX = ltrCallback(lastPointX, outline[i + 4]);
  16617. }
  16618. } else {
  16619. const bbox = Util.bezierBoundingBox(lastX, lastY, ...outline.slice(i, i + 6));
  16620. minX = Math.min(minX, bbox[0]);
  16621. minY = Math.min(minY, bbox[1]);
  16622. maxX = Math.max(maxX, bbox[2]);
  16623. maxY = Math.max(maxY, bbox[3]);
  16624. if (lastPointY < bbox[3]) {
  16625. lastPointX = bbox[2];
  16626. lastPointY = bbox[3];
  16627. } else if (lastPointY === bbox[3]) {
  16628. lastPointX = ltrCallback(lastPointX, bbox[2]);
  16629. }
  16630. }
  16631. lastX = outline[i + 4];
  16632. lastY = outline[i + 5];
  16633. }
  16634. const x = minX - this.#innerMargin,
  16635. y = minY - this.#innerMargin,
  16636. width = maxX - minX + 2 * this.#innerMargin,
  16637. height = maxY - minY + 2 * this.#innerMargin;
  16638. this.#bbox = {
  16639. x,
  16640. y,
  16641. width,
  16642. height,
  16643. lastPoint: [lastPointX, lastPointY]
  16644. };
  16645. }
  16646. get box() {
  16647. return this.#bbox;
  16648. }
  16649. getNewOutline(thickness, innerMargin) {
  16650. const {
  16651. x,
  16652. y,
  16653. width,
  16654. height
  16655. } = this.#bbox;
  16656. const [layerX, layerY, layerWidth, layerHeight] = this.#box;
  16657. const sx = width * layerWidth;
  16658. const sy = height * layerHeight;
  16659. const tx = x * layerWidth + layerX;
  16660. const ty = y * layerHeight + layerY;
  16661. const outliner = new FreeOutliner({
  16662. x: this.#points[0] * sx + tx,
  16663. y: this.#points[1] * sy + ty
  16664. }, this.#box, this.#scaleFactor, thickness, this.#isLTR, innerMargin ?? this.#innerMargin);
  16665. for (let i = 2; i < this.#points.length; i += 2) {
  16666. outliner.add({
  16667. x: this.#points[i] * sx + tx,
  16668. y: this.#points[i + 1] * sy + ty
  16669. });
  16670. }
  16671. return outliner.getOutlines();
  16672. }
  16673. }
  16674. ;// CONCATENATED MODULE: ./src/display/editor/color_picker.js
  16675. class ColorPicker {
  16676. #boundKeyDown = this.#keyDown.bind(this);
  16677. #boundPointerDown = this.#pointerDown.bind(this);
  16678. #button = null;
  16679. #buttonSwatch = null;
  16680. #defaultColor;
  16681. #dropdown = null;
  16682. #dropdownWasFromKeyboard = false;
  16683. #isMainColorPicker = false;
  16684. #editor = null;
  16685. #eventBus;
  16686. #uiManager = null;
  16687. #type;
  16688. static get _keyboardManager() {
  16689. 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]]));
  16690. }
  16691. constructor({
  16692. editor = null,
  16693. uiManager = null
  16694. }) {
  16695. if (editor) {
  16696. this.#isMainColorPicker = false;
  16697. this.#type = AnnotationEditorParamsType.HIGHLIGHT_COLOR;
  16698. this.#editor = editor;
  16699. } else {
  16700. this.#isMainColorPicker = true;
  16701. this.#type = AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR;
  16702. }
  16703. this.#uiManager = editor?._uiManager || uiManager;
  16704. this.#eventBus = this.#uiManager._eventBus;
  16705. this.#defaultColor = editor?.color || this.#uiManager?.highlightColors.values().next().value || "#FFFF98";
  16706. }
  16707. renderButton() {
  16708. const button = this.#button = document.createElement("button");
  16709. button.className = "colorPicker";
  16710. button.tabIndex = "0";
  16711. button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button");
  16712. button.setAttribute("aria-haspopup", true);
  16713. const signal = this.#uiManager._signal;
  16714. button.addEventListener("click", this.#openDropdown.bind(this), {
  16715. signal
  16716. });
  16717. button.addEventListener("keydown", this.#boundKeyDown, {
  16718. signal
  16719. });
  16720. const swatch = this.#buttonSwatch = document.createElement("span");
  16721. swatch.className = "swatch";
  16722. swatch.setAttribute("aria-hidden", true);
  16723. swatch.style.backgroundColor = this.#defaultColor;
  16724. button.append(swatch);
  16725. return button;
  16726. }
  16727. renderMainDropdown() {
  16728. const dropdown = this.#dropdown = this.#getDropdownRoot();
  16729. dropdown.setAttribute("aria-orientation", "horizontal");
  16730. dropdown.setAttribute("aria-labelledby", "highlightColorPickerLabel");
  16731. return dropdown;
  16732. }
  16733. #getDropdownRoot() {
  16734. const div = document.createElement("div");
  16735. const signal = this.#uiManager._signal;
  16736. div.addEventListener("contextmenu", noContextMenu, {
  16737. signal
  16738. });
  16739. div.className = "dropdown";
  16740. div.role = "listbox";
  16741. div.setAttribute("aria-multiselectable", false);
  16742. div.setAttribute("aria-orientation", "vertical");
  16743. div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown");
  16744. for (const [name, color] of this.#uiManager.highlightColors) {
  16745. const button = document.createElement("button");
  16746. button.tabIndex = "0";
  16747. button.role = "option";
  16748. button.setAttribute("data-color", color);
  16749. button.title = name;
  16750. button.setAttribute("data-l10n-id", `pdfjs-editor-colorpicker-${name}`);
  16751. const swatch = document.createElement("span");
  16752. button.append(swatch);
  16753. swatch.className = "swatch";
  16754. swatch.style.backgroundColor = color;
  16755. button.setAttribute("aria-selected", color === this.#defaultColor);
  16756. button.addEventListener("click", this.#colorSelect.bind(this, color), {
  16757. signal
  16758. });
  16759. div.append(button);
  16760. }
  16761. div.addEventListener("keydown", this.#boundKeyDown, {
  16762. signal
  16763. });
  16764. return div;
  16765. }
  16766. #colorSelect(color, event) {
  16767. event.stopPropagation();
  16768. this.#eventBus.dispatch("switchannotationeditorparams", {
  16769. source: this,
  16770. type: this.#type,
  16771. value: color
  16772. });
  16773. }
  16774. _colorSelectFromKeyboard(event) {
  16775. if (event.target === this.#button) {
  16776. this.#openDropdown(event);
  16777. return;
  16778. }
  16779. const color = event.target.getAttribute("data-color");
  16780. if (!color) {
  16781. return;
  16782. }
  16783. this.#colorSelect(color, event);
  16784. }
  16785. _moveToNext(event) {
  16786. if (!this.#isDropdownVisible) {
  16787. this.#openDropdown(event);
  16788. return;
  16789. }
  16790. if (event.target === this.#button) {
  16791. this.#dropdown.firstChild?.focus();
  16792. return;
  16793. }
  16794. event.target.nextSibling?.focus();
  16795. }
  16796. _moveToPrevious(event) {
  16797. if (event.target === this.#dropdown?.firstChild || event.target === this.#button) {
  16798. if (this.#isDropdownVisible) {
  16799. this._hideDropdownFromKeyboard();
  16800. }
  16801. return;
  16802. }
  16803. if (!this.#isDropdownVisible) {
  16804. this.#openDropdown(event);
  16805. }
  16806. event.target.previousSibling?.focus();
  16807. }
  16808. _moveToBeginning(event) {
  16809. if (!this.#isDropdownVisible) {
  16810. this.#openDropdown(event);
  16811. return;
  16812. }
  16813. this.#dropdown.firstChild?.focus();
  16814. }
  16815. _moveToEnd(event) {
  16816. if (!this.#isDropdownVisible) {
  16817. this.#openDropdown(event);
  16818. return;
  16819. }
  16820. this.#dropdown.lastChild?.focus();
  16821. }
  16822. #keyDown(event) {
  16823. ColorPicker._keyboardManager.exec(this, event);
  16824. }
  16825. #openDropdown(event) {
  16826. if (this.#isDropdownVisible) {
  16827. this.hideDropdown();
  16828. return;
  16829. }
  16830. this.#dropdownWasFromKeyboard = event.detail === 0;
  16831. window.addEventListener("pointerdown", this.#boundPointerDown, {
  16832. signal: this.#uiManager._signal
  16833. });
  16834. if (this.#dropdown) {
  16835. this.#dropdown.classList.remove("hidden");
  16836. return;
  16837. }
  16838. const root = this.#dropdown = this.#getDropdownRoot();
  16839. this.#button.append(root);
  16840. }
  16841. #pointerDown(event) {
  16842. if (this.#dropdown?.contains(event.target)) {
  16843. return;
  16844. }
  16845. this.hideDropdown();
  16846. }
  16847. hideDropdown() {
  16848. this.#dropdown?.classList.add("hidden");
  16849. window.removeEventListener("pointerdown", this.#boundPointerDown);
  16850. }
  16851. get #isDropdownVisible() {
  16852. return this.#dropdown && !this.#dropdown.classList.contains("hidden");
  16853. }
  16854. _hideDropdownFromKeyboard() {
  16855. if (this.#isMainColorPicker) {
  16856. return;
  16857. }
  16858. if (!this.#isDropdownVisible) {
  16859. this.#editor?.unselect();
  16860. return;
  16861. }
  16862. this.hideDropdown();
  16863. this.#button.focus({
  16864. preventScroll: true,
  16865. focusVisible: this.#dropdownWasFromKeyboard
  16866. });
  16867. }
  16868. updateColor(color) {
  16869. if (this.#buttonSwatch) {
  16870. this.#buttonSwatch.style.backgroundColor = color;
  16871. }
  16872. if (!this.#dropdown) {
  16873. return;
  16874. }
  16875. const i = this.#uiManager.highlightColors.values();
  16876. for (const child of this.#dropdown.children) {
  16877. child.setAttribute("aria-selected", i.next().value === color);
  16878. }
  16879. }
  16880. destroy() {
  16881. this.#button?.remove();
  16882. this.#button = null;
  16883. this.#buttonSwatch = null;
  16884. this.#dropdown?.remove();
  16885. this.#dropdown = null;
  16886. }
  16887. }
  16888. ;// CONCATENATED MODULE: ./src/display/editor/highlight.js
  16889. class HighlightEditor extends AnnotationEditor {
  16890. #anchorNode = null;
  16891. #anchorOffset = 0;
  16892. #boxes;
  16893. #clipPathId = null;
  16894. #colorPicker = null;
  16895. #focusOutlines = null;
  16896. #focusNode = null;
  16897. #focusOffset = 0;
  16898. #highlightDiv = null;
  16899. #highlightOutlines = null;
  16900. #id = null;
  16901. #isFreeHighlight = false;
  16902. #boundKeydown = this.#keydown.bind(this);
  16903. #lastPoint = null;
  16904. #opacity;
  16905. #outlineId = null;
  16906. #text = "";
  16907. #thickness;
  16908. #methodOfCreation = "";
  16909. static _defaultColor = null;
  16910. static _defaultOpacity = 1;
  16911. static _defaultThickness = 12;
  16912. static _l10nPromise;
  16913. static _type = "highlight";
  16914. static _editorType = AnnotationEditorType.HIGHLIGHT;
  16915. static _freeHighlightId = -1;
  16916. static _freeHighlight = null;
  16917. static _freeHighlightClipId = "";
  16918. static get _keyboardManager() {
  16919. const proto = HighlightEditor.prototype;
  16920. return shadow(this, "_keyboardManager", new KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, {
  16921. args: [0]
  16922. }], [["ArrowRight", "mac+ArrowRight"], proto._moveCaret, {
  16923. args: [1]
  16924. }], [["ArrowUp", "mac+ArrowUp"], proto._moveCaret, {
  16925. args: [2]
  16926. }], [["ArrowDown", "mac+ArrowDown"], proto._moveCaret, {
  16927. args: [3]
  16928. }]]));
  16929. }
  16930. constructor(params) {
  16931. super({
  16932. ...params,
  16933. name: "highlightEditor"
  16934. });
  16935. this.color = params.color || HighlightEditor._defaultColor;
  16936. this.#thickness = params.thickness || HighlightEditor._defaultThickness;
  16937. this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
  16938. this.#boxes = params.boxes || null;
  16939. this.#methodOfCreation = params.methodOfCreation || "";
  16940. this.#text = params.text || "";
  16941. this._isDraggable = false;
  16942. if (params.highlightId > -1) {
  16943. this.#isFreeHighlight = true;
  16944. this.#createFreeOutlines(params);
  16945. this.#addToDrawLayer();
  16946. } else {
  16947. this.#anchorNode = params.anchorNode;
  16948. this.#anchorOffset = params.anchorOffset;
  16949. this.#focusNode = params.focusNode;
  16950. this.#focusOffset = params.focusOffset;
  16951. this.#createOutlines();
  16952. this.#addToDrawLayer();
  16953. this.rotate(this.rotation);
  16954. }
  16955. }
  16956. get telemetryInitialData() {
  16957. return {
  16958. action: "added",
  16959. type: this.#isFreeHighlight ? "free_highlight" : "highlight",
  16960. color: this._uiManager.highlightColorNames.get(this.color),
  16961. thickness: this.#thickness,
  16962. methodOfCreation: this.#methodOfCreation
  16963. };
  16964. }
  16965. get telemetryFinalData() {
  16966. return {
  16967. type: "highlight",
  16968. color: this._uiManager.highlightColorNames.get(this.color)
  16969. };
  16970. }
  16971. static computeTelemetryFinalData(data) {
  16972. return {
  16973. numberOfColors: data.get("color").size
  16974. };
  16975. }
  16976. #createOutlines() {
  16977. const outliner = new Outliner(this.#boxes, 0.001);
  16978. this.#highlightOutlines = outliner.getOutlines();
  16979. ({
  16980. x: this.x,
  16981. y: this.y,
  16982. width: this.width,
  16983. height: this.height
  16984. } = this.#highlightOutlines.box);
  16985. const outlinerForOutline = new Outliner(this.#boxes, 0.0025, 0.001, this._uiManager.direction === "ltr");
  16986. this.#focusOutlines = outlinerForOutline.getOutlines();
  16987. const {
  16988. lastPoint
  16989. } = this.#focusOutlines.box;
  16990. this.#lastPoint = [(lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height];
  16991. }
  16992. #createFreeOutlines({
  16993. highlightOutlines,
  16994. highlightId,
  16995. clipPathId
  16996. }) {
  16997. this.#highlightOutlines = highlightOutlines;
  16998. const extraThickness = 1.5;
  16999. this.#focusOutlines = highlightOutlines.getNewOutline(this.#thickness / 2 + extraThickness, 0.0025);
  17000. if (highlightId >= 0) {
  17001. this.#id = highlightId;
  17002. this.#clipPathId = clipPathId;
  17003. this.parent.drawLayer.finalizeLine(highlightId, highlightOutlines);
  17004. this.#outlineId = this.parent.drawLayer.highlightOutline(this.#focusOutlines);
  17005. } else if (this.parent) {
  17006. const angle = this.parent.viewport.rotation;
  17007. this.parent.drawLayer.updateLine(this.#id, highlightOutlines);
  17008. this.parent.drawLayer.updateBox(this.#id, HighlightEditor.#rotateBbox(this.#highlightOutlines.box, (angle - this.rotation + 360) % 360));
  17009. this.parent.drawLayer.updateLine(this.#outlineId, this.#focusOutlines);
  17010. this.parent.drawLayer.updateBox(this.#outlineId, HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle));
  17011. }
  17012. const {
  17013. x,
  17014. y,
  17015. width,
  17016. height
  17017. } = highlightOutlines.box;
  17018. switch (this.rotation) {
  17019. case 0:
  17020. this.x = x;
  17021. this.y = y;
  17022. this.width = width;
  17023. this.height = height;
  17024. break;
  17025. case 90:
  17026. {
  17027. const [pageWidth, pageHeight] = this.parentDimensions;
  17028. this.x = y;
  17029. this.y = 1 - x;
  17030. this.width = width * pageHeight / pageWidth;
  17031. this.height = height * pageWidth / pageHeight;
  17032. break;
  17033. }
  17034. case 180:
  17035. this.x = 1 - x;
  17036. this.y = 1 - y;
  17037. this.width = width;
  17038. this.height = height;
  17039. break;
  17040. case 270:
  17041. {
  17042. const [pageWidth, pageHeight] = this.parentDimensions;
  17043. this.x = 1 - y;
  17044. this.y = x;
  17045. this.width = width * pageHeight / pageWidth;
  17046. this.height = height * pageWidth / pageHeight;
  17047. break;
  17048. }
  17049. }
  17050. const {
  17051. lastPoint
  17052. } = this.#focusOutlines.box;
  17053. this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height];
  17054. }
  17055. static initialize(l10n, uiManager) {
  17056. AnnotationEditor.initialize(l10n, uiManager);
  17057. HighlightEditor._defaultColor ||= uiManager.highlightColors?.values().next().value || "#fff066";
  17058. }
  17059. static updateDefaultParams(type, value) {
  17060. switch (type) {
  17061. case AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
  17062. HighlightEditor._defaultColor = value;
  17063. break;
  17064. case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
  17065. HighlightEditor._defaultThickness = value;
  17066. break;
  17067. }
  17068. }
  17069. translateInPage(x, y) {}
  17070. get toolbarPosition() {
  17071. return this.#lastPoint;
  17072. }
  17073. updateParams(type, value) {
  17074. switch (type) {
  17075. case AnnotationEditorParamsType.HIGHLIGHT_COLOR:
  17076. this.#updateColor(value);
  17077. break;
  17078. case AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
  17079. this.#updateThickness(value);
  17080. break;
  17081. }
  17082. }
  17083. static get defaultPropertiesToUpdate() {
  17084. return [[AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR, HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, HighlightEditor._defaultThickness]];
  17085. }
  17086. get propertiesToUpdate() {
  17087. return [[AnnotationEditorParamsType.HIGHLIGHT_COLOR, this.color || HighlightEditor._defaultColor], [AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, this.#thickness || HighlightEditor._defaultThickness], [AnnotationEditorParamsType.HIGHLIGHT_FREE, this.#isFreeHighlight]];
  17088. }
  17089. #updateColor(color) {
  17090. const setColor = col => {
  17091. this.color = col;
  17092. this.parent?.drawLayer.changeColor(this.#id, col);
  17093. this.#colorPicker?.updateColor(col);
  17094. };
  17095. const savedColor = this.color;
  17096. this.addCommands({
  17097. cmd: setColor.bind(this, color),
  17098. undo: setColor.bind(this, savedColor),
  17099. post: this._uiManager.updateUI.bind(this._uiManager, this),
  17100. mustExec: true,
  17101. type: AnnotationEditorParamsType.HIGHLIGHT_COLOR,
  17102. overwriteIfSameType: true,
  17103. keepUndo: true
  17104. });
  17105. this._reportTelemetry({
  17106. action: "color_changed",
  17107. color: this._uiManager.highlightColorNames.get(color)
  17108. }, true);
  17109. }
  17110. #updateThickness(thickness) {
  17111. const savedThickness = this.#thickness;
  17112. const setThickness = th => {
  17113. this.#thickness = th;
  17114. this.#changeThickness(th);
  17115. };
  17116. this.addCommands({
  17117. cmd: setThickness.bind(this, thickness),
  17118. undo: setThickness.bind(this, savedThickness),
  17119. post: this._uiManager.updateUI.bind(this._uiManager, this),
  17120. mustExec: true,
  17121. type: AnnotationEditorParamsType.INK_THICKNESS,
  17122. overwriteIfSameType: true,
  17123. keepUndo: true
  17124. });
  17125. this._reportTelemetry({
  17126. action: "thickness_changed",
  17127. thickness
  17128. }, true);
  17129. }
  17130. async addEditToolbar() {
  17131. const toolbar = await super.addEditToolbar();
  17132. if (!toolbar) {
  17133. return null;
  17134. }
  17135. if (this._uiManager.highlightColors) {
  17136. this.#colorPicker = new ColorPicker({
  17137. editor: this
  17138. });
  17139. toolbar.addColorPicker(this.#colorPicker);
  17140. }
  17141. return toolbar;
  17142. }
  17143. disableEditing() {
  17144. super.disableEditing();
  17145. this.div.classList.toggle("disabled", true);
  17146. }
  17147. enableEditing() {
  17148. super.enableEditing();
  17149. this.div.classList.toggle("disabled", false);
  17150. }
  17151. fixAndSetPosition() {
  17152. return super.fixAndSetPosition(this.#getRotation());
  17153. }
  17154. getBaseTranslation() {
  17155. return [0, 0];
  17156. }
  17157. getRect(tx, ty) {
  17158. return super.getRect(tx, ty, this.#getRotation());
  17159. }
  17160. onceAdded() {
  17161. this.parent.addUndoableEditor(this);
  17162. this.div.focus();
  17163. }
  17164. remove() {
  17165. this.#cleanDrawLayer();
  17166. this._reportTelemetry({
  17167. action: "deleted"
  17168. });
  17169. super.remove();
  17170. }
  17171. rebuild() {
  17172. if (!this.parent) {
  17173. return;
  17174. }
  17175. super.rebuild();
  17176. if (this.div === null) {
  17177. return;
  17178. }
  17179. this.#addToDrawLayer();
  17180. if (!this.isAttachedToDOM) {
  17181. this.parent.add(this);
  17182. }
  17183. }
  17184. setParent(parent) {
  17185. let mustBeSelected = false;
  17186. if (this.parent && !parent) {
  17187. this.#cleanDrawLayer();
  17188. } else if (parent) {
  17189. this.#addToDrawLayer(parent);
  17190. mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor");
  17191. }
  17192. super.setParent(parent);
  17193. this.show(this._isVisible);
  17194. if (mustBeSelected) {
  17195. this.select();
  17196. }
  17197. }
  17198. #changeThickness(thickness) {
  17199. if (!this.#isFreeHighlight) {
  17200. return;
  17201. }
  17202. this.#createFreeOutlines({
  17203. highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2)
  17204. });
  17205. this.fixAndSetPosition();
  17206. const [parentWidth, parentHeight] = this.parentDimensions;
  17207. this.setDims(this.width * parentWidth, this.height * parentHeight);
  17208. }
  17209. #cleanDrawLayer() {
  17210. if (this.#id === null || !this.parent) {
  17211. return;
  17212. }
  17213. this.parent.drawLayer.remove(this.#id);
  17214. this.#id = null;
  17215. this.parent.drawLayer.remove(this.#outlineId);
  17216. this.#outlineId = null;
  17217. }
  17218. #addToDrawLayer(parent = this.parent) {
  17219. if (this.#id !== null) {
  17220. return;
  17221. }
  17222. ({
  17223. id: this.#id,
  17224. clipPathId: this.#clipPathId
  17225. } = parent.drawLayer.highlight(this.#highlightOutlines, this.color, this.#opacity));
  17226. this.#outlineId = parent.drawLayer.highlightOutline(this.#focusOutlines);
  17227. if (this.#highlightDiv) {
  17228. this.#highlightDiv.style.clipPath = this.#clipPathId;
  17229. }
  17230. }
  17231. static #rotateBbox({
  17232. x,
  17233. y,
  17234. width,
  17235. height
  17236. }, angle) {
  17237. switch (angle) {
  17238. case 90:
  17239. return {
  17240. x: 1 - y - height,
  17241. y: x,
  17242. width: height,
  17243. height: width
  17244. };
  17245. case 180:
  17246. return {
  17247. x: 1 - x - width,
  17248. y: 1 - y - height,
  17249. width,
  17250. height
  17251. };
  17252. case 270:
  17253. return {
  17254. x: y,
  17255. y: 1 - x - width,
  17256. width: height,
  17257. height: width
  17258. };
  17259. }
  17260. return {
  17261. x,
  17262. y,
  17263. width,
  17264. height
  17265. };
  17266. }
  17267. rotate(angle) {
  17268. const {
  17269. drawLayer
  17270. } = this.parent;
  17271. let box;
  17272. if (this.#isFreeHighlight) {
  17273. angle = (angle - this.rotation + 360) % 360;
  17274. box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle);
  17275. } else {
  17276. box = HighlightEditor.#rotateBbox(this, angle);
  17277. }
  17278. drawLayer.rotate(this.#id, angle);
  17279. drawLayer.rotate(this.#outlineId, angle);
  17280. drawLayer.updateBox(this.#id, box);
  17281. drawLayer.updateBox(this.#outlineId, HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle));
  17282. }
  17283. render() {
  17284. if (this.div) {
  17285. return this.div;
  17286. }
  17287. const div = super.render();
  17288. if (this.#text) {
  17289. div.setAttribute("aria-label", this.#text);
  17290. div.setAttribute("role", "mark");
  17291. }
  17292. if (this.#isFreeHighlight) {
  17293. div.classList.add("free");
  17294. } else {
  17295. this.div.addEventListener("keydown", this.#boundKeydown, {
  17296. signal: this._uiManager._signal
  17297. });
  17298. }
  17299. const highlightDiv = this.#highlightDiv = document.createElement("div");
  17300. div.append(highlightDiv);
  17301. highlightDiv.setAttribute("aria-hidden", "true");
  17302. highlightDiv.className = "internal";
  17303. highlightDiv.style.clipPath = this.#clipPathId;
  17304. const [parentWidth, parentHeight] = this.parentDimensions;
  17305. this.setDims(this.width * parentWidth, this.height * parentHeight);
  17306. bindEvents(this, this.#highlightDiv, ["pointerover", "pointerleave"]);
  17307. this.enableEditing();
  17308. return div;
  17309. }
  17310. pointerover() {
  17311. this.parent.drawLayer.addClass(this.#outlineId, "hovered");
  17312. }
  17313. pointerleave() {
  17314. this.parent.drawLayer.removeClass(this.#outlineId, "hovered");
  17315. }
  17316. #keydown(event) {
  17317. HighlightEditor._keyboardManager.exec(this, event);
  17318. }
  17319. _moveCaret(direction) {
  17320. this.parent.unselect(this);
  17321. switch (direction) {
  17322. case 0:
  17323. case 2:
  17324. this.#setCaret(true);
  17325. break;
  17326. case 1:
  17327. case 3:
  17328. this.#setCaret(false);
  17329. break;
  17330. }
  17331. }
  17332. #setCaret(start) {
  17333. if (!this.#anchorNode) {
  17334. return;
  17335. }
  17336. const selection = window.getSelection();
  17337. if (start) {
  17338. selection.setPosition(this.#anchorNode, this.#anchorOffset);
  17339. } else {
  17340. selection.setPosition(this.#focusNode, this.#focusOffset);
  17341. }
  17342. }
  17343. select() {
  17344. super.select();
  17345. if (!this.#outlineId) {
  17346. return;
  17347. }
  17348. this.parent?.drawLayer.removeClass(this.#outlineId, "hovered");
  17349. this.parent?.drawLayer.addClass(this.#outlineId, "selected");
  17350. }
  17351. unselect() {
  17352. super.unselect();
  17353. if (!this.#outlineId) {
  17354. return;
  17355. }
  17356. this.parent?.drawLayer.removeClass(this.#outlineId, "selected");
  17357. if (!this.#isFreeHighlight) {
  17358. this.#setCaret(false);
  17359. }
  17360. }
  17361. get _mustFixPosition() {
  17362. return !this.#isFreeHighlight;
  17363. }
  17364. show(visible = this._isVisible) {
  17365. super.show(visible);
  17366. if (this.parent) {
  17367. this.parent.drawLayer.show(this.#id, visible);
  17368. this.parent.drawLayer.show(this.#outlineId, visible);
  17369. }
  17370. }
  17371. #getRotation() {
  17372. return this.#isFreeHighlight ? this.rotation : 0;
  17373. }
  17374. #serializeBoxes() {
  17375. if (this.#isFreeHighlight) {
  17376. return null;
  17377. }
  17378. const [pageWidth, pageHeight] = this.pageDimensions;
  17379. const [pageX, pageY] = this.pageTranslation;
  17380. const boxes = this.#boxes;
  17381. const quadPoints = new Float32Array(boxes.length * 8);
  17382. let i = 0;
  17383. for (const {
  17384. x,
  17385. y,
  17386. width,
  17387. height
  17388. } of boxes) {
  17389. const sx = x * pageWidth + pageX;
  17390. const sy = (1 - y - height) * pageHeight + pageY;
  17391. quadPoints[i] = quadPoints[i + 4] = sx;
  17392. quadPoints[i + 1] = quadPoints[i + 3] = sy;
  17393. quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth;
  17394. quadPoints[i + 5] = quadPoints[i + 7] = sy + height * pageHeight;
  17395. i += 8;
  17396. }
  17397. return quadPoints;
  17398. }
  17399. #serializeOutlines(rect) {
  17400. return this.#highlightOutlines.serialize(rect, this.#getRotation());
  17401. }
  17402. static startHighlighting(parent, isLTR, {
  17403. target: textLayer,
  17404. x,
  17405. y
  17406. }) {
  17407. const {
  17408. x: layerX,
  17409. y: layerY,
  17410. width: parentWidth,
  17411. height: parentHeight
  17412. } = textLayer.getBoundingClientRect();
  17413. const pointerMove = e => {
  17414. this.#highlightMove(parent, e);
  17415. };
  17416. const signal = parent._signal;
  17417. const pointerDownOptions = {
  17418. capture: true,
  17419. passive: false,
  17420. signal
  17421. };
  17422. const pointerDown = e => {
  17423. e.preventDefault();
  17424. e.stopPropagation();
  17425. };
  17426. const pointerUpCallback = e => {
  17427. textLayer.removeEventListener("pointermove", pointerMove);
  17428. window.removeEventListener("blur", pointerUpCallback);
  17429. window.removeEventListener("pointerup", pointerUpCallback);
  17430. window.removeEventListener("pointerdown", pointerDown, pointerDownOptions);
  17431. window.removeEventListener("contextmenu", noContextMenu);
  17432. this.#endHighlight(parent, e);
  17433. };
  17434. window.addEventListener("blur", pointerUpCallback, {
  17435. signal
  17436. });
  17437. window.addEventListener("pointerup", pointerUpCallback, {
  17438. signal
  17439. });
  17440. window.addEventListener("pointerdown", pointerDown, pointerDownOptions);
  17441. window.addEventListener("contextmenu", noContextMenu, {
  17442. signal
  17443. });
  17444. textLayer.addEventListener("pointermove", pointerMove, {
  17445. signal
  17446. });
  17447. this._freeHighlight = new FreeOutliner({
  17448. x,
  17449. y
  17450. }, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001);
  17451. ({
  17452. id: this._freeHighlightId,
  17453. clipPathId: this._freeHighlightClipId
  17454. } = parent.drawLayer.highlight(this._freeHighlight, this._defaultColor, this._defaultOpacity, true));
  17455. }
  17456. static #highlightMove(parent, event) {
  17457. if (this._freeHighlight.add(event)) {
  17458. parent.drawLayer.updatePath(this._freeHighlightId, this._freeHighlight);
  17459. }
  17460. }
  17461. static #endHighlight(parent, event) {
  17462. if (!this._freeHighlight.isEmpty()) {
  17463. parent.createAndAddNewEditor(event, false, {
  17464. highlightId: this._freeHighlightId,
  17465. highlightOutlines: this._freeHighlight.getOutlines(),
  17466. clipPathId: this._freeHighlightClipId,
  17467. methodOfCreation: "main_toolbar"
  17468. });
  17469. } else {
  17470. parent.drawLayer.removeFreeHighlight(this._freeHighlightId);
  17471. }
  17472. this._freeHighlightId = -1;
  17473. this._freeHighlight = null;
  17474. this._freeHighlightClipId = "";
  17475. }
  17476. static deserialize(data, parent, uiManager) {
  17477. const editor = super.deserialize(data, parent, uiManager);
  17478. const {
  17479. rect: [blX, blY, trX, trY],
  17480. color,
  17481. quadPoints
  17482. } = data;
  17483. editor.color = Util.makeHexColor(...color);
  17484. editor.#opacity = data.opacity;
  17485. const [pageWidth, pageHeight] = editor.pageDimensions;
  17486. editor.width = (trX - blX) / pageWidth;
  17487. editor.height = (trY - blY) / pageHeight;
  17488. const boxes = editor.#boxes = [];
  17489. for (let i = 0; i < quadPoints.length; i += 8) {
  17490. boxes.push({
  17491. x: (quadPoints[4] - trX) / pageWidth,
  17492. y: (trY - (1 - quadPoints[i + 5])) / pageHeight,
  17493. width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth,
  17494. height: (quadPoints[i + 5] - quadPoints[i + 1]) / pageHeight
  17495. });
  17496. }
  17497. editor.#createOutlines();
  17498. return editor;
  17499. }
  17500. serialize(isForCopying = false) {
  17501. if (this.isEmpty() || isForCopying) {
  17502. return null;
  17503. }
  17504. const rect = this.getRect(0, 0);
  17505. const color = AnnotationEditor._colorManager.convert(this.color);
  17506. return {
  17507. annotationType: AnnotationEditorType.HIGHLIGHT,
  17508. color,
  17509. opacity: this.#opacity,
  17510. thickness: this.#thickness,
  17511. quadPoints: this.#serializeBoxes(),
  17512. outlines: this.#serializeOutlines(rect),
  17513. pageIndex: this.pageIndex,
  17514. rect,
  17515. rotation: this.#getRotation(),
  17516. structTreeParentId: this._structTreeParentId
  17517. };
  17518. }
  17519. static canCreateNewEmptyEditor() {
  17520. return false;
  17521. }
  17522. }
  17523. ;// CONCATENATED MODULE: ./src/display/editor/ink.js
  17524. class InkEditor extends AnnotationEditor {
  17525. #baseHeight = 0;
  17526. #baseWidth = 0;
  17527. #boundCanvasPointermove = this.canvasPointermove.bind(this);
  17528. #boundCanvasPointerleave = this.canvasPointerleave.bind(this);
  17529. #boundCanvasPointerup = this.canvasPointerup.bind(this);
  17530. #boundCanvasPointerdown = this.canvasPointerdown.bind(this);
  17531. #canvasContextMenuTimeoutId = null;
  17532. #currentPath2D = new Path2D();
  17533. #disableEditing = false;
  17534. #hasSomethingToDraw = false;
  17535. #isCanvasInitialized = false;
  17536. #observer = null;
  17537. #realWidth = 0;
  17538. #realHeight = 0;
  17539. #requestFrameCallback = null;
  17540. static _defaultColor = null;
  17541. static _defaultOpacity = 1;
  17542. static _defaultThickness = 1;
  17543. static _type = "ink";
  17544. static _editorType = AnnotationEditorType.INK;
  17545. constructor(params) {
  17546. super({
  17547. ...params,
  17548. name: "inkEditor"
  17549. });
  17550. this.color = params.color || null;
  17551. this.thickness = params.thickness || null;
  17552. this.opacity = params.opacity || null;
  17553. this.paths = [];
  17554. this.bezierPath2D = [];
  17555. this.allRawPaths = [];
  17556. this.currentPath = [];
  17557. this.scaleFactor = 1;
  17558. this.translationX = this.translationY = 0;
  17559. this.x = 0;
  17560. this.y = 0;
  17561. this._willKeepAspectRatio = true;
  17562. }
  17563. static initialize(l10n, uiManager) {
  17564. AnnotationEditor.initialize(l10n, uiManager);
  17565. }
  17566. static updateDefaultParams(type, value) {
  17567. switch (type) {
  17568. case AnnotationEditorParamsType.INK_THICKNESS:
  17569. InkEditor._defaultThickness = value;
  17570. break;
  17571. case AnnotationEditorParamsType.INK_COLOR:
  17572. InkEditor._defaultColor = value;
  17573. break;
  17574. case AnnotationEditorParamsType.INK_OPACITY:
  17575. InkEditor._defaultOpacity = value / 100;
  17576. break;
  17577. }
  17578. }
  17579. updateParams(type, value) {
  17580. switch (type) {
  17581. case AnnotationEditorParamsType.INK_THICKNESS:
  17582. this.#updateThickness(value);
  17583. break;
  17584. case AnnotationEditorParamsType.INK_COLOR:
  17585. this.#updateColor(value);
  17586. break;
  17587. case AnnotationEditorParamsType.INK_OPACITY:
  17588. this.#updateOpacity(value);
  17589. break;
  17590. }
  17591. }
  17592. static get defaultPropertiesToUpdate() {
  17593. return [[AnnotationEditorParamsType.INK_THICKNESS, InkEditor._defaultThickness], [AnnotationEditorParamsType.INK_COLOR, InkEditor._defaultColor || AnnotationEditor._defaultLineColor], [AnnotationEditorParamsType.INK_OPACITY, Math.round(InkEditor._defaultOpacity * 100)]];
  17594. }
  17595. get propertiesToUpdate() {
  17596. return [[AnnotationEditorParamsType.INK_THICKNESS, this.thickness || InkEditor._defaultThickness], [AnnotationEditorParamsType.INK_COLOR, this.color || InkEditor._defaultColor || AnnotationEditor._defaultLineColor], [AnnotationEditorParamsType.INK_OPACITY, Math.round(100 * (this.opacity ?? InkEditor._defaultOpacity))]];
  17597. }
  17598. #updateThickness(thickness) {
  17599. const setThickness = th => {
  17600. this.thickness = th;
  17601. this.#fitToContent();
  17602. };
  17603. const savedThickness = this.thickness;
  17604. this.addCommands({
  17605. cmd: setThickness.bind(this, thickness),
  17606. undo: setThickness.bind(this, savedThickness),
  17607. post: this._uiManager.updateUI.bind(this._uiManager, this),
  17608. mustExec: true,
  17609. type: AnnotationEditorParamsType.INK_THICKNESS,
  17610. overwriteIfSameType: true,
  17611. keepUndo: true
  17612. });
  17613. }
  17614. #updateColor(color) {
  17615. const setColor = col => {
  17616. this.color = col;
  17617. this.#redraw();
  17618. };
  17619. const savedColor = this.color;
  17620. this.addCommands({
  17621. cmd: setColor.bind(this, color),
  17622. undo: setColor.bind(this, savedColor),
  17623. post: this._uiManager.updateUI.bind(this._uiManager, this),
  17624. mustExec: true,
  17625. type: AnnotationEditorParamsType.INK_COLOR,
  17626. overwriteIfSameType: true,
  17627. keepUndo: true
  17628. });
  17629. }
  17630. #updateOpacity(opacity) {
  17631. const setOpacity = op => {
  17632. this.opacity = op;
  17633. this.#redraw();
  17634. };
  17635. opacity /= 100;
  17636. const savedOpacity = this.opacity;
  17637. this.addCommands({
  17638. cmd: setOpacity.bind(this, opacity),
  17639. undo: setOpacity.bind(this, savedOpacity),
  17640. post: this._uiManager.updateUI.bind(this._uiManager, this),
  17641. mustExec: true,
  17642. type: AnnotationEditorParamsType.INK_OPACITY,
  17643. overwriteIfSameType: true,
  17644. keepUndo: true
  17645. });
  17646. }
  17647. rebuild() {
  17648. if (!this.parent) {
  17649. return;
  17650. }
  17651. super.rebuild();
  17652. if (this.div === null) {
  17653. return;
  17654. }
  17655. if (!this.canvas) {
  17656. this.#createCanvas();
  17657. this.#createObserver();
  17658. }
  17659. if (!this.isAttachedToDOM) {
  17660. this.parent.add(this);
  17661. this.#setCanvasDims();
  17662. }
  17663. this.#fitToContent();
  17664. }
  17665. remove() {
  17666. if (this.canvas === null) {
  17667. return;
  17668. }
  17669. if (!this.isEmpty()) {
  17670. this.commit();
  17671. }
  17672. this.canvas.width = this.canvas.height = 0;
  17673. this.canvas.remove();
  17674. this.canvas = null;
  17675. if (this.#canvasContextMenuTimeoutId) {
  17676. clearTimeout(this.#canvasContextMenuTimeoutId);
  17677. this.#canvasContextMenuTimeoutId = null;
  17678. }
  17679. this.#observer?.disconnect();
  17680. this.#observer = null;
  17681. super.remove();
  17682. }
  17683. setParent(parent) {
  17684. if (!this.parent && parent) {
  17685. this._uiManager.removeShouldRescale(this);
  17686. } else if (this.parent && parent === null) {
  17687. this._uiManager.addShouldRescale(this);
  17688. }
  17689. super.setParent(parent);
  17690. }
  17691. onScaleChanging() {
  17692. const [parentWidth, parentHeight] = this.parentDimensions;
  17693. const width = this.width * parentWidth;
  17694. const height = this.height * parentHeight;
  17695. this.setDimensions(width, height);
  17696. }
  17697. enableEditMode() {
  17698. if (this.#disableEditing || this.canvas === null) {
  17699. return;
  17700. }
  17701. super.enableEditMode();
  17702. this._isDraggable = false;
  17703. this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown, {
  17704. signal: this._uiManager._signal
  17705. });
  17706. }
  17707. disableEditMode() {
  17708. if (!this.isInEditMode() || this.canvas === null) {
  17709. return;
  17710. }
  17711. super.disableEditMode();
  17712. this._isDraggable = !this.isEmpty();
  17713. this.div.classList.remove("editing");
  17714. this.canvas.removeEventListener("pointerdown", this.#boundCanvasPointerdown);
  17715. }
  17716. onceAdded() {
  17717. this._isDraggable = !this.isEmpty();
  17718. }
  17719. isEmpty() {
  17720. return this.paths.length === 0 || this.paths.length === 1 && this.paths[0].length === 0;
  17721. }
  17722. #getInitialBBox() {
  17723. const {
  17724. parentRotation,
  17725. parentDimensions: [width, height]
  17726. } = this;
  17727. switch (parentRotation) {
  17728. case 90:
  17729. return [0, height, height, width];
  17730. case 180:
  17731. return [width, height, width, height];
  17732. case 270:
  17733. return [width, 0, height, width];
  17734. default:
  17735. return [0, 0, width, height];
  17736. }
  17737. }
  17738. #setStroke() {
  17739. const {
  17740. ctx,
  17741. color,
  17742. opacity,
  17743. thickness,
  17744. parentScale,
  17745. scaleFactor
  17746. } = this;
  17747. ctx.lineWidth = thickness * parentScale / scaleFactor;
  17748. ctx.lineCap = "round";
  17749. ctx.lineJoin = "round";
  17750. ctx.miterLimit = 10;
  17751. ctx.strokeStyle = `${color}${opacityToHex(opacity)}`;
  17752. }
  17753. #startDrawing(x, y) {
  17754. const signal = this._uiManager._signal;
  17755. this.canvas.addEventListener("contextmenu", noContextMenu, {
  17756. signal
  17757. });
  17758. this.canvas.addEventListener("pointerleave", this.#boundCanvasPointerleave, {
  17759. signal
  17760. });
  17761. this.canvas.addEventListener("pointermove", this.#boundCanvasPointermove, {
  17762. signal
  17763. });
  17764. this.canvas.addEventListener("pointerup", this.#boundCanvasPointerup, {
  17765. signal
  17766. });
  17767. this.canvas.removeEventListener("pointerdown", this.#boundCanvasPointerdown);
  17768. this.isEditing = true;
  17769. if (!this.#isCanvasInitialized) {
  17770. this.#isCanvasInitialized = true;
  17771. this.#setCanvasDims();
  17772. this.thickness ||= InkEditor._defaultThickness;
  17773. this.color ||= InkEditor._defaultColor || AnnotationEditor._defaultLineColor;
  17774. this.opacity ??= InkEditor._defaultOpacity;
  17775. }
  17776. this.currentPath.push([x, y]);
  17777. this.#hasSomethingToDraw = false;
  17778. this.#setStroke();
  17779. this.#requestFrameCallback = () => {
  17780. this.#drawPoints();
  17781. if (this.#requestFrameCallback) {
  17782. window.requestAnimationFrame(this.#requestFrameCallback);
  17783. }
  17784. };
  17785. window.requestAnimationFrame(this.#requestFrameCallback);
  17786. }
  17787. #draw(x, y) {
  17788. const [lastX, lastY] = this.currentPath.at(-1);
  17789. if (this.currentPath.length > 1 && x === lastX && y === lastY) {
  17790. return;
  17791. }
  17792. const currentPath = this.currentPath;
  17793. let path2D = this.#currentPath2D;
  17794. currentPath.push([x, y]);
  17795. this.#hasSomethingToDraw = true;
  17796. if (currentPath.length <= 2) {
  17797. path2D.moveTo(...currentPath[0]);
  17798. path2D.lineTo(x, y);
  17799. return;
  17800. }
  17801. if (currentPath.length === 3) {
  17802. this.#currentPath2D = path2D = new Path2D();
  17803. path2D.moveTo(...currentPath[0]);
  17804. }
  17805. this.#makeBezierCurve(path2D, ...currentPath.at(-3), ...currentPath.at(-2), x, y);
  17806. }
  17807. #endPath() {
  17808. if (this.currentPath.length === 0) {
  17809. return;
  17810. }
  17811. const lastPoint = this.currentPath.at(-1);
  17812. this.#currentPath2D.lineTo(...lastPoint);
  17813. }
  17814. #stopDrawing(x, y) {
  17815. this.#requestFrameCallback = null;
  17816. x = Math.min(Math.max(x, 0), this.canvas.width);
  17817. y = Math.min(Math.max(y, 0), this.canvas.height);
  17818. this.#draw(x, y);
  17819. this.#endPath();
  17820. let bezier;
  17821. if (this.currentPath.length !== 1) {
  17822. bezier = this.#generateBezierPoints();
  17823. } else {
  17824. const xy = [x, y];
  17825. bezier = [[xy, xy.slice(), xy.slice(), xy]];
  17826. }
  17827. const path2D = this.#currentPath2D;
  17828. const currentPath = this.currentPath;
  17829. this.currentPath = [];
  17830. this.#currentPath2D = new Path2D();
  17831. const cmd = () => {
  17832. this.allRawPaths.push(currentPath);
  17833. this.paths.push(bezier);
  17834. this.bezierPath2D.push(path2D);
  17835. this._uiManager.rebuild(this);
  17836. };
  17837. const undo = () => {
  17838. this.allRawPaths.pop();
  17839. this.paths.pop();
  17840. this.bezierPath2D.pop();
  17841. if (this.paths.length === 0) {
  17842. this.remove();
  17843. } else {
  17844. if (!this.canvas) {
  17845. this.#createCanvas();
  17846. this.#createObserver();
  17847. }
  17848. this.#fitToContent();
  17849. }
  17850. };
  17851. this.addCommands({
  17852. cmd,
  17853. undo,
  17854. mustExec: true
  17855. });
  17856. }
  17857. #drawPoints() {
  17858. if (!this.#hasSomethingToDraw) {
  17859. return;
  17860. }
  17861. this.#hasSomethingToDraw = false;
  17862. const thickness = Math.ceil(this.thickness * this.parentScale);
  17863. const lastPoints = this.currentPath.slice(-3);
  17864. const x = lastPoints.map(xy => xy[0]);
  17865. const y = lastPoints.map(xy => xy[1]);
  17866. const xMin = Math.min(...x) - thickness;
  17867. const xMax = Math.max(...x) + thickness;
  17868. const yMin = Math.min(...y) - thickness;
  17869. const yMax = Math.max(...y) + thickness;
  17870. const {
  17871. ctx
  17872. } = this;
  17873. ctx.save();
  17874. ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  17875. for (const path of this.bezierPath2D) {
  17876. ctx.stroke(path);
  17877. }
  17878. ctx.stroke(this.#currentPath2D);
  17879. ctx.restore();
  17880. }
  17881. #makeBezierCurve(path2D, x0, y0, x1, y1, x2, y2) {
  17882. const prevX = (x0 + x1) / 2;
  17883. const prevY = (y0 + y1) / 2;
  17884. const x3 = (x1 + x2) / 2;
  17885. const y3 = (y1 + y2) / 2;
  17886. path2D.bezierCurveTo(prevX + 2 * (x1 - prevX) / 3, prevY + 2 * (y1 - prevY) / 3, x3 + 2 * (x1 - x3) / 3, y3 + 2 * (y1 - y3) / 3, x3, y3);
  17887. }
  17888. #generateBezierPoints() {
  17889. const path = this.currentPath;
  17890. if (path.length <= 2) {
  17891. return [[path[0], path[0], path.at(-1), path.at(-1)]];
  17892. }
  17893. const bezierPoints = [];
  17894. let i;
  17895. let [x0, y0] = path[0];
  17896. for (i = 1; i < path.length - 2; i++) {
  17897. const [x1, y1] = path[i];
  17898. const [x2, y2] = path[i + 1];
  17899. const x3 = (x1 + x2) / 2;
  17900. const y3 = (y1 + y2) / 2;
  17901. const control1 = [x0 + 2 * (x1 - x0) / 3, y0 + 2 * (y1 - y0) / 3];
  17902. const control2 = [x3 + 2 * (x1 - x3) / 3, y3 + 2 * (y1 - y3) / 3];
  17903. bezierPoints.push([[x0, y0], control1, control2, [x3, y3]]);
  17904. [x0, y0] = [x3, y3];
  17905. }
  17906. const [x1, y1] = path[i];
  17907. const [x2, y2] = path[i + 1];
  17908. const control1 = [x0 + 2 * (x1 - x0) / 3, y0 + 2 * (y1 - y0) / 3];
  17909. const control2 = [x2 + 2 * (x1 - x2) / 3, y2 + 2 * (y1 - y2) / 3];
  17910. bezierPoints.push([[x0, y0], control1, control2, [x2, y2]]);
  17911. return bezierPoints;
  17912. }
  17913. #redraw() {
  17914. if (this.isEmpty()) {
  17915. this.#updateTransform();
  17916. return;
  17917. }
  17918. this.#setStroke();
  17919. const {
  17920. canvas,
  17921. ctx
  17922. } = this;
  17923. ctx.setTransform(1, 0, 0, 1, 0, 0);
  17924. ctx.clearRect(0, 0, canvas.width, canvas.height);
  17925. this.#updateTransform();
  17926. for (const path of this.bezierPath2D) {
  17927. ctx.stroke(path);
  17928. }
  17929. }
  17930. commit() {
  17931. if (this.#disableEditing) {
  17932. return;
  17933. }
  17934. super.commit();
  17935. this.isEditing = false;
  17936. this.disableEditMode();
  17937. this.setInForeground();
  17938. this.#disableEditing = true;
  17939. this.div.classList.add("disabled");
  17940. this.#fitToContent(true);
  17941. this.select();
  17942. this.parent.addInkEditorIfNeeded(true);
  17943. this.moveInDOM();
  17944. this.div.focus({
  17945. preventScroll: true
  17946. });
  17947. }
  17948. focusin(event) {
  17949. if (!this._focusEventsAllowed) {
  17950. return;
  17951. }
  17952. super.focusin(event);
  17953. this.enableEditMode();
  17954. }
  17955. canvasPointerdown(event) {
  17956. if (event.button !== 0 || !this.isInEditMode() || this.#disableEditing) {
  17957. return;
  17958. }
  17959. this.setInForeground();
  17960. event.preventDefault();
  17961. if (!this.div.contains(document.activeElement)) {
  17962. this.div.focus({
  17963. preventScroll: true
  17964. });
  17965. }
  17966. this.#startDrawing(event.offsetX, event.offsetY);
  17967. }
  17968. canvasPointermove(event) {
  17969. event.preventDefault();
  17970. this.#draw(event.offsetX, event.offsetY);
  17971. }
  17972. canvasPointerup(event) {
  17973. event.preventDefault();
  17974. this.#endDrawing(event);
  17975. }
  17976. canvasPointerleave(event) {
  17977. this.#endDrawing(event);
  17978. }
  17979. #endDrawing(event) {
  17980. this.canvas.removeEventListener("pointerleave", this.#boundCanvasPointerleave);
  17981. this.canvas.removeEventListener("pointermove", this.#boundCanvasPointermove);
  17982. this.canvas.removeEventListener("pointerup", this.#boundCanvasPointerup);
  17983. this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown, {
  17984. signal: this._uiManager._signal
  17985. });
  17986. if (this.#canvasContextMenuTimeoutId) {
  17987. clearTimeout(this.#canvasContextMenuTimeoutId);
  17988. }
  17989. this.#canvasContextMenuTimeoutId = setTimeout(() => {
  17990. this.#canvasContextMenuTimeoutId = null;
  17991. this.canvas.removeEventListener("contextmenu", noContextMenu);
  17992. }, 10);
  17993. this.#stopDrawing(event.offsetX, event.offsetY);
  17994. this.addToAnnotationStorage();
  17995. this.setInBackground();
  17996. }
  17997. #createCanvas() {
  17998. this.canvas = document.createElement("canvas");
  17999. this.canvas.width = this.canvas.height = 0;
  18000. this.canvas.className = "inkEditorCanvas";
  18001. this.canvas.setAttribute("data-l10n-id", "pdfjs-ink-canvas");
  18002. this.div.append(this.canvas);
  18003. this.ctx = this.canvas.getContext("2d");
  18004. }
  18005. #createObserver() {
  18006. this.#observer = new ResizeObserver(entries => {
  18007. const rect = entries[0].contentRect;
  18008. if (rect.width && rect.height) {
  18009. this.setDimensions(rect.width, rect.height);
  18010. }
  18011. });
  18012. this.#observer.observe(this.div);
  18013. this._uiManager._signal.addEventListener("abort", () => {
  18014. this.#observer?.disconnect();
  18015. this.#observer = null;
  18016. }, {
  18017. once: true
  18018. });
  18019. }
  18020. get isResizable() {
  18021. return !this.isEmpty() && this.#disableEditing;
  18022. }
  18023. render() {
  18024. if (this.div) {
  18025. return this.div;
  18026. }
  18027. let baseX, baseY;
  18028. if (this.width) {
  18029. baseX = this.x;
  18030. baseY = this.y;
  18031. }
  18032. super.render();
  18033. this.div.setAttribute("data-l10n-id", "pdfjs-ink");
  18034. const [x, y, w, h] = this.#getInitialBBox();
  18035. this.setAt(x, y, 0, 0);
  18036. this.setDims(w, h);
  18037. this.#createCanvas();
  18038. if (this.width) {
  18039. const [parentWidth, parentHeight] = this.parentDimensions;
  18040. this.setAspectRatio(this.width * parentWidth, this.height * parentHeight);
  18041. this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
  18042. this.#isCanvasInitialized = true;
  18043. this.#setCanvasDims();
  18044. this.setDims(this.width * parentWidth, this.height * parentHeight);
  18045. this.#redraw();
  18046. this.div.classList.add("disabled");
  18047. } else {
  18048. this.div.classList.add("editing");
  18049. this.enableEditMode();
  18050. }
  18051. this.#createObserver();
  18052. return this.div;
  18053. }
  18054. #setCanvasDims() {
  18055. if (!this.#isCanvasInitialized) {
  18056. return;
  18057. }
  18058. const [parentWidth, parentHeight] = this.parentDimensions;
  18059. this.canvas.width = Math.ceil(this.width * parentWidth);
  18060. this.canvas.height = Math.ceil(this.height * parentHeight);
  18061. this.#updateTransform();
  18062. }
  18063. setDimensions(width, height) {
  18064. const roundedWidth = Math.round(width);
  18065. const roundedHeight = Math.round(height);
  18066. if (this.#realWidth === roundedWidth && this.#realHeight === roundedHeight) {
  18067. return;
  18068. }
  18069. this.#realWidth = roundedWidth;
  18070. this.#realHeight = roundedHeight;
  18071. this.canvas.style.visibility = "hidden";
  18072. const [parentWidth, parentHeight] = this.parentDimensions;
  18073. this.width = width / parentWidth;
  18074. this.height = height / parentHeight;
  18075. this.fixAndSetPosition();
  18076. if (this.#disableEditing) {
  18077. this.#setScaleFactor(width, height);
  18078. }
  18079. this.#setCanvasDims();
  18080. this.#redraw();
  18081. this.canvas.style.visibility = "visible";
  18082. this.fixDims();
  18083. }
  18084. #setScaleFactor(width, height) {
  18085. const padding = this.#getPadding();
  18086. const scaleFactorW = (width - padding) / this.#baseWidth;
  18087. const scaleFactorH = (height - padding) / this.#baseHeight;
  18088. this.scaleFactor = Math.min(scaleFactorW, scaleFactorH);
  18089. }
  18090. #updateTransform() {
  18091. const padding = this.#getPadding() / 2;
  18092. this.ctx.setTransform(this.scaleFactor, 0, 0, this.scaleFactor, this.translationX * this.scaleFactor + padding, this.translationY * this.scaleFactor + padding);
  18093. }
  18094. static #buildPath2D(bezier) {
  18095. const path2D = new Path2D();
  18096. for (let i = 0, ii = bezier.length; i < ii; i++) {
  18097. const [first, control1, control2, second] = bezier[i];
  18098. if (i === 0) {
  18099. path2D.moveTo(...first);
  18100. }
  18101. path2D.bezierCurveTo(control1[0], control1[1], control2[0], control2[1], second[0], second[1]);
  18102. }
  18103. return path2D;
  18104. }
  18105. static #toPDFCoordinates(points, rect, rotation) {
  18106. const [blX, blY, trX, trY] = rect;
  18107. switch (rotation) {
  18108. case 0:
  18109. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18110. points[i] += blX;
  18111. points[i + 1] = trY - points[i + 1];
  18112. }
  18113. break;
  18114. case 90:
  18115. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18116. const x = points[i];
  18117. points[i] = points[i + 1] + blX;
  18118. points[i + 1] = x + blY;
  18119. }
  18120. break;
  18121. case 180:
  18122. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18123. points[i] = trX - points[i];
  18124. points[i + 1] += blY;
  18125. }
  18126. break;
  18127. case 270:
  18128. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18129. const x = points[i];
  18130. points[i] = trX - points[i + 1];
  18131. points[i + 1] = trY - x;
  18132. }
  18133. break;
  18134. default:
  18135. throw new Error("Invalid rotation");
  18136. }
  18137. return points;
  18138. }
  18139. static #fromPDFCoordinates(points, rect, rotation) {
  18140. const [blX, blY, trX, trY] = rect;
  18141. switch (rotation) {
  18142. case 0:
  18143. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18144. points[i] -= blX;
  18145. points[i + 1] = trY - points[i + 1];
  18146. }
  18147. break;
  18148. case 90:
  18149. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18150. const x = points[i];
  18151. points[i] = points[i + 1] - blY;
  18152. points[i + 1] = x - blX;
  18153. }
  18154. break;
  18155. case 180:
  18156. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18157. points[i] = trX - points[i];
  18158. points[i + 1] -= blY;
  18159. }
  18160. break;
  18161. case 270:
  18162. for (let i = 0, ii = points.length; i < ii; i += 2) {
  18163. const x = points[i];
  18164. points[i] = trY - points[i + 1];
  18165. points[i + 1] = trX - x;
  18166. }
  18167. break;
  18168. default:
  18169. throw new Error("Invalid rotation");
  18170. }
  18171. return points;
  18172. }
  18173. #serializePaths(s, tx, ty, rect) {
  18174. const paths = [];
  18175. const padding = this.thickness / 2;
  18176. const shiftX = s * tx + padding;
  18177. const shiftY = s * ty + padding;
  18178. for (const bezier of this.paths) {
  18179. const buffer = [];
  18180. const points = [];
  18181. for (let j = 0, jj = bezier.length; j < jj; j++) {
  18182. const [first, control1, control2, second] = bezier[j];
  18183. if (first[0] === second[0] && first[1] === second[1] && jj === 1) {
  18184. const p0 = s * first[0] + shiftX;
  18185. const p1 = s * first[1] + shiftY;
  18186. buffer.push(p0, p1);
  18187. points.push(p0, p1);
  18188. break;
  18189. }
  18190. const p10 = s * first[0] + shiftX;
  18191. const p11 = s * first[1] + shiftY;
  18192. const p20 = s * control1[0] + shiftX;
  18193. const p21 = s * control1[1] + shiftY;
  18194. const p30 = s * control2[0] + shiftX;
  18195. const p31 = s * control2[1] + shiftY;
  18196. const p40 = s * second[0] + shiftX;
  18197. const p41 = s * second[1] + shiftY;
  18198. if (j === 0) {
  18199. buffer.push(p10, p11);
  18200. points.push(p10, p11);
  18201. }
  18202. buffer.push(p20, p21, p30, p31, p40, p41);
  18203. points.push(p20, p21);
  18204. if (j === jj - 1) {
  18205. points.push(p40, p41);
  18206. }
  18207. }
  18208. paths.push({
  18209. bezier: InkEditor.#toPDFCoordinates(buffer, rect, this.rotation),
  18210. points: InkEditor.#toPDFCoordinates(points, rect, this.rotation)
  18211. });
  18212. }
  18213. return paths;
  18214. }
  18215. #getBbox() {
  18216. let xMin = Infinity;
  18217. let xMax = -Infinity;
  18218. let yMin = Infinity;
  18219. let yMax = -Infinity;
  18220. for (const path of this.paths) {
  18221. for (const [first, control1, control2, second] of path) {
  18222. const bbox = Util.bezierBoundingBox(...first, ...control1, ...control2, ...second);
  18223. xMin = Math.min(xMin, bbox[0]);
  18224. yMin = Math.min(yMin, bbox[1]);
  18225. xMax = Math.max(xMax, bbox[2]);
  18226. yMax = Math.max(yMax, bbox[3]);
  18227. }
  18228. }
  18229. return [xMin, yMin, xMax, yMax];
  18230. }
  18231. #getPadding() {
  18232. return this.#disableEditing ? Math.ceil(this.thickness * this.parentScale) : 0;
  18233. }
  18234. #fitToContent(firstTime = false) {
  18235. if (this.isEmpty()) {
  18236. return;
  18237. }
  18238. if (!this.#disableEditing) {
  18239. this.#redraw();
  18240. return;
  18241. }
  18242. const bbox = this.#getBbox();
  18243. const padding = this.#getPadding();
  18244. this.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]);
  18245. this.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]);
  18246. const width = Math.ceil(padding + this.#baseWidth * this.scaleFactor);
  18247. const height = Math.ceil(padding + this.#baseHeight * this.scaleFactor);
  18248. const [parentWidth, parentHeight] = this.parentDimensions;
  18249. this.width = width / parentWidth;
  18250. this.height = height / parentHeight;
  18251. this.setAspectRatio(width, height);
  18252. const prevTranslationX = this.translationX;
  18253. const prevTranslationY = this.translationY;
  18254. this.translationX = -bbox[0];
  18255. this.translationY = -bbox[1];
  18256. this.#setCanvasDims();
  18257. this.#redraw();
  18258. this.#realWidth = width;
  18259. this.#realHeight = height;
  18260. this.setDims(width, height);
  18261. const unscaledPadding = firstTime ? padding / this.scaleFactor / 2 : 0;
  18262. this.translate(prevTranslationX - this.translationX - unscaledPadding, prevTranslationY - this.translationY - unscaledPadding);
  18263. }
  18264. static deserialize(data, parent, uiManager) {
  18265. if (data instanceof InkAnnotationElement) {
  18266. return null;
  18267. }
  18268. const editor = super.deserialize(data, parent, uiManager);
  18269. editor.thickness = data.thickness;
  18270. editor.color = Util.makeHexColor(...data.color);
  18271. editor.opacity = data.opacity;
  18272. const [pageWidth, pageHeight] = editor.pageDimensions;
  18273. const width = editor.width * pageWidth;
  18274. const height = editor.height * pageHeight;
  18275. const scaleFactor = editor.parentScale;
  18276. const padding = data.thickness / 2;
  18277. editor.#disableEditing = true;
  18278. editor.#realWidth = Math.round(width);
  18279. editor.#realHeight = Math.round(height);
  18280. const {
  18281. paths,
  18282. rect,
  18283. rotation
  18284. } = data;
  18285. for (let {
  18286. bezier
  18287. } of paths) {
  18288. bezier = InkEditor.#fromPDFCoordinates(bezier, rect, rotation);
  18289. const path = [];
  18290. editor.paths.push(path);
  18291. let p0 = scaleFactor * (bezier[0] - padding);
  18292. let p1 = scaleFactor * (bezier[1] - padding);
  18293. for (let i = 2, ii = bezier.length; i < ii; i += 6) {
  18294. const p10 = scaleFactor * (bezier[i] - padding);
  18295. const p11 = scaleFactor * (bezier[i + 1] - padding);
  18296. const p20 = scaleFactor * (bezier[i + 2] - padding);
  18297. const p21 = scaleFactor * (bezier[i + 3] - padding);
  18298. const p30 = scaleFactor * (bezier[i + 4] - padding);
  18299. const p31 = scaleFactor * (bezier[i + 5] - padding);
  18300. path.push([[p0, p1], [p10, p11], [p20, p21], [p30, p31]]);
  18301. p0 = p30;
  18302. p1 = p31;
  18303. }
  18304. const path2D = this.#buildPath2D(path);
  18305. editor.bezierPath2D.push(path2D);
  18306. }
  18307. const bbox = editor.#getBbox();
  18308. editor.#baseWidth = Math.max(AnnotationEditor.MIN_SIZE, bbox[2] - bbox[0]);
  18309. editor.#baseHeight = Math.max(AnnotationEditor.MIN_SIZE, bbox[3] - bbox[1]);
  18310. editor.#setScaleFactor(width, height);
  18311. return editor;
  18312. }
  18313. serialize() {
  18314. if (this.isEmpty()) {
  18315. return null;
  18316. }
  18317. const rect = this.getRect(0, 0);
  18318. const color = AnnotationEditor._colorManager.convert(this.ctx.strokeStyle);
  18319. return {
  18320. annotationType: AnnotationEditorType.INK,
  18321. color,
  18322. thickness: this.thickness,
  18323. opacity: this.opacity,
  18324. paths: this.#serializePaths(this.scaleFactor / this.parentScale, this.translationX, this.translationY, rect),
  18325. pageIndex: this.pageIndex,
  18326. rect,
  18327. rotation: this.rotation,
  18328. structTreeParentId: this._structTreeParentId
  18329. };
  18330. }
  18331. }
  18332. ;// CONCATENATED MODULE: ./src/display/editor/stamp.js
  18333. class StampEditor extends AnnotationEditor {
  18334. #bitmap = null;
  18335. #bitmapId = null;
  18336. #bitmapPromise = null;
  18337. #bitmapUrl = null;
  18338. #bitmapFile = null;
  18339. #bitmapFileName = "";
  18340. #canvas = null;
  18341. #hasMLBeenQueried = false;
  18342. #observer = null;
  18343. #resizeTimeoutId = null;
  18344. #isSvg = false;
  18345. #hasBeenAddedInUndoStack = false;
  18346. static _type = "stamp";
  18347. static _editorType = AnnotationEditorType.STAMP;
  18348. constructor(params) {
  18349. super({
  18350. ...params,
  18351. name: "stampEditor"
  18352. });
  18353. this.#bitmapUrl = params.bitmapUrl;
  18354. this.#bitmapFile = params.bitmapFile;
  18355. }
  18356. static initialize(l10n, uiManager) {
  18357. AnnotationEditor.initialize(l10n, uiManager);
  18358. }
  18359. static get supportedTypes() {
  18360. const types = ["apng", "avif", "bmp", "gif", "jpeg", "png", "svg+xml", "webp", "x-icon"];
  18361. return shadow(this, "supportedTypes", types.map(type => `image/${type}`));
  18362. }
  18363. static get supportedTypesStr() {
  18364. return shadow(this, "supportedTypesStr", this.supportedTypes.join(","));
  18365. }
  18366. static isHandlingMimeForPasting(mime) {
  18367. return this.supportedTypes.includes(mime);
  18368. }
  18369. static paste(item, parent) {
  18370. parent.pasteEditor(AnnotationEditorType.STAMP, {
  18371. bitmapFile: item.getAsFile()
  18372. });
  18373. }
  18374. #getBitmapFetched(data, fromId = false) {
  18375. if (!data) {
  18376. this.remove();
  18377. return;
  18378. }
  18379. this.#bitmap = data.bitmap;
  18380. if (!fromId) {
  18381. this.#bitmapId = data.id;
  18382. this.#isSvg = data.isSvg;
  18383. }
  18384. if (data.file) {
  18385. this.#bitmapFileName = data.file.name;
  18386. }
  18387. this.#createCanvas();
  18388. }
  18389. #getBitmapDone() {
  18390. this.#bitmapPromise = null;
  18391. this._uiManager.enableWaiting(false);
  18392. if (this.#canvas) {
  18393. this.div.focus();
  18394. }
  18395. }
  18396. #getBitmap() {
  18397. if (this.#bitmapId) {
  18398. this._uiManager.enableWaiting(true);
  18399. this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone());
  18400. return;
  18401. }
  18402. if (this.#bitmapUrl) {
  18403. const url = this.#bitmapUrl;
  18404. this.#bitmapUrl = null;
  18405. this._uiManager.enableWaiting(true);
  18406. this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());
  18407. return;
  18408. }
  18409. if (this.#bitmapFile) {
  18410. const file = this.#bitmapFile;
  18411. this.#bitmapFile = null;
  18412. this._uiManager.enableWaiting(true);
  18413. this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());
  18414. return;
  18415. }
  18416. const input = document.createElement("input");
  18417. input.type = "file";
  18418. input.accept = StampEditor.supportedTypesStr;
  18419. const signal = this._uiManager._signal;
  18420. this.#bitmapPromise = new Promise(resolve => {
  18421. input.addEventListener("change", async () => {
  18422. if (!input.files || input.files.length === 0) {
  18423. this.remove();
  18424. } else {
  18425. this._uiManager.enableWaiting(true);
  18426. const data = await this._uiManager.imageManager.getFromFile(input.files[0]);
  18427. this.#getBitmapFetched(data);
  18428. }
  18429. resolve();
  18430. }, {
  18431. signal
  18432. });
  18433. input.addEventListener("cancel", () => {
  18434. this.remove();
  18435. resolve();
  18436. }, {
  18437. signal
  18438. });
  18439. }).finally(() => this.#getBitmapDone());
  18440. input.click();
  18441. }
  18442. remove() {
  18443. if (this.#bitmapId) {
  18444. this.#bitmap = null;
  18445. this._uiManager.imageManager.deleteId(this.#bitmapId);
  18446. this.#canvas?.remove();
  18447. this.#canvas = null;
  18448. this.#observer?.disconnect();
  18449. this.#observer = null;
  18450. if (this.#resizeTimeoutId) {
  18451. clearTimeout(this.#resizeTimeoutId);
  18452. this.#resizeTimeoutId = null;
  18453. }
  18454. }
  18455. super.remove();
  18456. }
  18457. rebuild() {
  18458. if (!this.parent) {
  18459. if (this.#bitmapId) {
  18460. this.#getBitmap();
  18461. }
  18462. return;
  18463. }
  18464. super.rebuild();
  18465. if (this.div === null) {
  18466. return;
  18467. }
  18468. if (this.#bitmapId && this.#canvas === null) {
  18469. this.#getBitmap();
  18470. }
  18471. if (!this.isAttachedToDOM) {
  18472. this.parent.add(this);
  18473. }
  18474. }
  18475. onceAdded() {
  18476. this._isDraggable = true;
  18477. this.div.focus();
  18478. }
  18479. isEmpty() {
  18480. return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId);
  18481. }
  18482. get isResizable() {
  18483. return true;
  18484. }
  18485. render() {
  18486. if (this.div) {
  18487. return this.div;
  18488. }
  18489. let baseX, baseY;
  18490. if (this.width) {
  18491. baseX = this.x;
  18492. baseY = this.y;
  18493. }
  18494. super.render();
  18495. this.div.hidden = true;
  18496. this.addAltTextButton();
  18497. if (this.#bitmap) {
  18498. this.#createCanvas();
  18499. } else {
  18500. this.#getBitmap();
  18501. }
  18502. if (this.width) {
  18503. const [parentWidth, parentHeight] = this.parentDimensions;
  18504. this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
  18505. }
  18506. return this.div;
  18507. }
  18508. #createCanvas() {
  18509. const {
  18510. div
  18511. } = this;
  18512. let {
  18513. width,
  18514. height
  18515. } = this.#bitmap;
  18516. const [pageWidth, pageHeight] = this.pageDimensions;
  18517. const MAX_RATIO = 0.75;
  18518. if (this.width) {
  18519. width = this.width * pageWidth;
  18520. height = this.height * pageHeight;
  18521. } else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) {
  18522. const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height);
  18523. width *= factor;
  18524. height *= factor;
  18525. }
  18526. const [parentWidth, parentHeight] = this.parentDimensions;
  18527. this.setDims(width * parentWidth / pageWidth, height * parentHeight / pageHeight);
  18528. this._uiManager.enableWaiting(false);
  18529. const canvas = this.#canvas = document.createElement("canvas");
  18530. div.append(canvas);
  18531. div.hidden = false;
  18532. this.#drawBitmap(width, height);
  18533. this.#createObserver();
  18534. if (!this.#hasBeenAddedInUndoStack) {
  18535. this.parent.addUndoableEditor(this);
  18536. this.#hasBeenAddedInUndoStack = true;
  18537. }
  18538. this._reportTelemetry({
  18539. action: "inserted_image"
  18540. });
  18541. if (this.#bitmapFileName) {
  18542. canvas.setAttribute("aria-label", this.#bitmapFileName);
  18543. }
  18544. }
  18545. #setDimensions(width, height) {
  18546. const [parentWidth, parentHeight] = this.parentDimensions;
  18547. this.width = width / parentWidth;
  18548. this.height = height / parentHeight;
  18549. this.setDims(width, height);
  18550. if (this._initialOptions?.isCentered) {
  18551. this.center();
  18552. } else {
  18553. this.fixAndSetPosition();
  18554. }
  18555. this._initialOptions = null;
  18556. if (this.#resizeTimeoutId !== null) {
  18557. clearTimeout(this.#resizeTimeoutId);
  18558. }
  18559. const TIME_TO_WAIT = 200;
  18560. this.#resizeTimeoutId = setTimeout(() => {
  18561. this.#resizeTimeoutId = null;
  18562. this.#drawBitmap(width, height);
  18563. }, TIME_TO_WAIT);
  18564. }
  18565. #scaleBitmap(width, height) {
  18566. const {
  18567. width: bitmapWidth,
  18568. height: bitmapHeight
  18569. } = this.#bitmap;
  18570. let newWidth = bitmapWidth;
  18571. let newHeight = bitmapHeight;
  18572. let bitmap = this.#bitmap;
  18573. while (newWidth > 2 * width || newHeight > 2 * height) {
  18574. const prevWidth = newWidth;
  18575. const prevHeight = newHeight;
  18576. if (newWidth > 2 * width) {
  18577. newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2);
  18578. }
  18579. if (newHeight > 2 * height) {
  18580. newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2);
  18581. }
  18582. const offscreen = new OffscreenCanvas(newWidth, newHeight);
  18583. const ctx = offscreen.getContext("2d");
  18584. ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight);
  18585. bitmap = offscreen.transferToImageBitmap();
  18586. }
  18587. return bitmap;
  18588. }
  18589. async #mlGuessAltText(bitmap, width, height) {
  18590. if (this.#hasMLBeenQueried) {
  18591. return;
  18592. }
  18593. this.#hasMLBeenQueried = true;
  18594. const isMLEnabled = await this._uiManager.isMLEnabledFor("altText");
  18595. if (!isMLEnabled || this.hasAltText()) {
  18596. return;
  18597. }
  18598. const offscreen = new OffscreenCanvas(width, height);
  18599. const ctx = offscreen.getContext("2d", {
  18600. willReadFrequently: true
  18601. });
  18602. ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, width, height);
  18603. const response = await this._uiManager.mlGuess({
  18604. service: "moz-image-to-text",
  18605. request: {
  18606. data: ctx.getImageData(0, 0, width, height).data,
  18607. width,
  18608. height,
  18609. channels: 4
  18610. }
  18611. });
  18612. const altText = response?.output || "";
  18613. if (this.parent && altText && !this.hasAltText()) {
  18614. this.altTextData = {
  18615. altText,
  18616. decorative: false
  18617. };
  18618. }
  18619. }
  18620. #drawBitmap(width, height) {
  18621. width = Math.ceil(width);
  18622. height = Math.ceil(height);
  18623. const canvas = this.#canvas;
  18624. if (!canvas || canvas.width === width && canvas.height === height) {
  18625. return;
  18626. }
  18627. canvas.width = width;
  18628. canvas.height = height;
  18629. const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(width, height);
  18630. this.#mlGuessAltText(bitmap, width, height);
  18631. const ctx = canvas.getContext("2d");
  18632. ctx.filter = this._uiManager.hcmFilter;
  18633. ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, width, height);
  18634. }
  18635. getImageForAltText() {
  18636. return this.#canvas;
  18637. }
  18638. #serializeBitmap(toUrl) {
  18639. if (toUrl) {
  18640. if (this.#isSvg) {
  18641. const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId);
  18642. if (url) {
  18643. return url;
  18644. }
  18645. }
  18646. const canvas = document.createElement("canvas");
  18647. ({
  18648. width: canvas.width,
  18649. height: canvas.height
  18650. } = this.#bitmap);
  18651. const ctx = canvas.getContext("2d");
  18652. ctx.drawImage(this.#bitmap, 0, 0);
  18653. return canvas.toDataURL();
  18654. }
  18655. if (this.#isSvg) {
  18656. const [pageWidth, pageHeight] = this.pageDimensions;
  18657. const width = Math.round(this.width * pageWidth * PixelsPerInch.PDF_TO_CSS_UNITS);
  18658. const height = Math.round(this.height * pageHeight * PixelsPerInch.PDF_TO_CSS_UNITS);
  18659. const offscreen = new OffscreenCanvas(width, height);
  18660. const ctx = offscreen.getContext("2d");
  18661. ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height);
  18662. return offscreen.transferToImageBitmap();
  18663. }
  18664. return structuredClone(this.#bitmap);
  18665. }
  18666. #createObserver() {
  18667. if (!this._uiManager._signal) {
  18668. return;
  18669. }
  18670. this.#observer = new ResizeObserver(entries => {
  18671. const rect = entries[0].contentRect;
  18672. if (rect.width && rect.height) {
  18673. this.#setDimensions(rect.width, rect.height);
  18674. }
  18675. });
  18676. this.#observer.observe(this.div);
  18677. this._uiManager._signal.addEventListener("abort", () => {
  18678. this.#observer?.disconnect();
  18679. this.#observer = null;
  18680. }, {
  18681. once: true
  18682. });
  18683. }
  18684. static deserialize(data, parent, uiManager) {
  18685. if (data instanceof StampAnnotationElement) {
  18686. return null;
  18687. }
  18688. const editor = super.deserialize(data, parent, uiManager);
  18689. const {
  18690. rect,
  18691. bitmapUrl,
  18692. bitmapId,
  18693. isSvg,
  18694. accessibilityData
  18695. } = data;
  18696. if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) {
  18697. editor.#bitmapId = bitmapId;
  18698. } else {
  18699. editor.#bitmapUrl = bitmapUrl;
  18700. }
  18701. editor.#isSvg = isSvg;
  18702. const [parentWidth, parentHeight] = editor.pageDimensions;
  18703. editor.width = (rect[2] - rect[0]) / parentWidth;
  18704. editor.height = (rect[3] - rect[1]) / parentHeight;
  18705. if (accessibilityData) {
  18706. editor.altTextData = accessibilityData;
  18707. }
  18708. return editor;
  18709. }
  18710. serialize(isForCopying = false, context = null) {
  18711. if (this.isEmpty()) {
  18712. return null;
  18713. }
  18714. const serialized = {
  18715. annotationType: AnnotationEditorType.STAMP,
  18716. bitmapId: this.#bitmapId,
  18717. pageIndex: this.pageIndex,
  18718. rect: this.getRect(0, 0),
  18719. rotation: this.rotation,
  18720. isSvg: this.#isSvg,
  18721. structTreeParentId: this._structTreeParentId
  18722. };
  18723. if (isForCopying) {
  18724. serialized.bitmapUrl = this.#serializeBitmap(true);
  18725. serialized.accessibilityData = this.altTextData;
  18726. return serialized;
  18727. }
  18728. const {
  18729. decorative,
  18730. altText
  18731. } = this.altTextData;
  18732. if (!decorative && altText) {
  18733. serialized.accessibilityData = {
  18734. type: "Figure",
  18735. alt: altText
  18736. };
  18737. }
  18738. if (context === null) {
  18739. return serialized;
  18740. }
  18741. context.stamps ||= new Map();
  18742. const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null;
  18743. if (!context.stamps.has(this.#bitmapId)) {
  18744. context.stamps.set(this.#bitmapId, {
  18745. area,
  18746. serialized
  18747. });
  18748. serialized.bitmap = this.#serializeBitmap(false);
  18749. } else if (this.#isSvg) {
  18750. const prevData = context.stamps.get(this.#bitmapId);
  18751. if (area > prevData.area) {
  18752. prevData.area = area;
  18753. prevData.serialized.bitmap.close();
  18754. prevData.serialized.bitmap = this.#serializeBitmap(false);
  18755. }
  18756. }
  18757. return serialized;
  18758. }
  18759. }
  18760. ;// CONCATENATED MODULE: ./src/display/editor/annotation_editor_layer.js
  18761. class AnnotationEditorLayer {
  18762. #accessibilityManager;
  18763. #allowClick = false;
  18764. #annotationLayer = null;
  18765. #boundPointerup = null;
  18766. #boundPointerdown = null;
  18767. #boundTextLayerPointerDown = null;
  18768. #editorFocusTimeoutId = null;
  18769. #editors = new Map();
  18770. #hadPointerDown = false;
  18771. #isCleaningUp = false;
  18772. #isDisabling = false;
  18773. #textLayer = null;
  18774. #uiManager;
  18775. static _initialized = false;
  18776. static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor].map(type => [type._editorType, type]));
  18777. constructor({
  18778. uiManager,
  18779. pageIndex,
  18780. div,
  18781. accessibilityManager,
  18782. annotationLayer,
  18783. drawLayer,
  18784. textLayer,
  18785. viewport,
  18786. l10n
  18787. }) {
  18788. const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()];
  18789. if (!AnnotationEditorLayer._initialized) {
  18790. AnnotationEditorLayer._initialized = true;
  18791. for (const editorType of editorTypes) {
  18792. editorType.initialize(l10n, uiManager);
  18793. }
  18794. }
  18795. uiManager.registerEditorTypes(editorTypes);
  18796. this.#uiManager = uiManager;
  18797. this.pageIndex = pageIndex;
  18798. this.div = div;
  18799. this.#accessibilityManager = accessibilityManager;
  18800. this.#annotationLayer = annotationLayer;
  18801. this.viewport = viewport;
  18802. this.#textLayer = textLayer;
  18803. this.drawLayer = drawLayer;
  18804. this.#uiManager.addLayer(this);
  18805. }
  18806. get isEmpty() {
  18807. return this.#editors.size === 0;
  18808. }
  18809. get isInvisible() {
  18810. return this.isEmpty && this.#uiManager.getMode() === AnnotationEditorType.NONE;
  18811. }
  18812. updateToolbar(mode) {
  18813. this.#uiManager.updateToolbar(mode);
  18814. }
  18815. updateMode(mode = this.#uiManager.getMode()) {
  18816. this.#cleanup();
  18817. switch (mode) {
  18818. case AnnotationEditorType.NONE:
  18819. this.disableTextSelection();
  18820. this.togglePointerEvents(false);
  18821. this.toggleAnnotationLayerPointerEvents(true);
  18822. this.disableClick();
  18823. return;
  18824. case AnnotationEditorType.INK:
  18825. this.addInkEditorIfNeeded(false);
  18826. this.disableTextSelection();
  18827. this.togglePointerEvents(true);
  18828. this.disableClick();
  18829. break;
  18830. case AnnotationEditorType.HIGHLIGHT:
  18831. this.enableTextSelection();
  18832. this.togglePointerEvents(false);
  18833. this.disableClick();
  18834. break;
  18835. default:
  18836. this.disableTextSelection();
  18837. this.togglePointerEvents(true);
  18838. this.enableClick();
  18839. }
  18840. this.toggleAnnotationLayerPointerEvents(false);
  18841. const {
  18842. classList
  18843. } = this.div;
  18844. for (const editorType of AnnotationEditorLayer.#editorTypes.values()) {
  18845. classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType);
  18846. }
  18847. this.div.hidden = false;
  18848. }
  18849. hasTextLayer(textLayer) {
  18850. return textLayer === this.#textLayer?.div;
  18851. }
  18852. addInkEditorIfNeeded(isCommitting) {
  18853. if (this.#uiManager.getMode() !== AnnotationEditorType.INK) {
  18854. return;
  18855. }
  18856. if (!isCommitting) {
  18857. for (const editor of this.#editors.values()) {
  18858. if (editor.isEmpty()) {
  18859. editor.setInBackground();
  18860. return;
  18861. }
  18862. }
  18863. }
  18864. const editor = this.createAndAddNewEditor({
  18865. offsetX: 0,
  18866. offsetY: 0
  18867. }, false);
  18868. editor.setInBackground();
  18869. }
  18870. setEditingState(isEditing) {
  18871. this.#uiManager.setEditingState(isEditing);
  18872. }
  18873. addCommands(params) {
  18874. this.#uiManager.addCommands(params);
  18875. }
  18876. toggleDrawing(enabled = false) {
  18877. this.div.classList.toggle("drawing", !enabled);
  18878. }
  18879. togglePointerEvents(enabled = false) {
  18880. this.div.classList.toggle("disabled", !enabled);
  18881. }
  18882. toggleAnnotationLayerPointerEvents(enabled = false) {
  18883. this.#annotationLayer?.div.classList.toggle("disabled", !enabled);
  18884. }
  18885. enable() {
  18886. this.div.tabIndex = 0;
  18887. this.togglePointerEvents(true);
  18888. const annotationElementIds = new Set();
  18889. for (const editor of this.#editors.values()) {
  18890. editor.enableEditing();
  18891. editor.show(true);
  18892. if (editor.annotationElementId) {
  18893. this.#uiManager.removeChangedExistingAnnotation(editor);
  18894. annotationElementIds.add(editor.annotationElementId);
  18895. }
  18896. }
  18897. if (!this.#annotationLayer) {
  18898. return;
  18899. }
  18900. const editables = this.#annotationLayer.getEditableAnnotations();
  18901. for (const editable of editables) {
  18902. editable.hide();
  18903. if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) {
  18904. continue;
  18905. }
  18906. if (annotationElementIds.has(editable.data.id)) {
  18907. continue;
  18908. }
  18909. const editor = this.deserialize(editable);
  18910. if (!editor) {
  18911. continue;
  18912. }
  18913. this.addOrRebuild(editor);
  18914. editor.enableEditing();
  18915. }
  18916. }
  18917. disable() {
  18918. this.#isDisabling = true;
  18919. this.div.tabIndex = -1;
  18920. this.togglePointerEvents(false);
  18921. const changedAnnotations = new Map();
  18922. const resetAnnotations = new Map();
  18923. for (const editor of this.#editors.values()) {
  18924. editor.disableEditing();
  18925. if (!editor.annotationElementId) {
  18926. continue;
  18927. }
  18928. if (editor.serialize() !== null) {
  18929. changedAnnotations.set(editor.annotationElementId, editor);
  18930. continue;
  18931. } else {
  18932. resetAnnotations.set(editor.annotationElementId, editor);
  18933. }
  18934. this.getEditableAnnotation(editor.annotationElementId)?.show();
  18935. editor.remove();
  18936. }
  18937. if (this.#annotationLayer) {
  18938. const editables = this.#annotationLayer.getEditableAnnotations();
  18939. for (const editable of editables) {
  18940. const {
  18941. id
  18942. } = editable.data;
  18943. if (this.#uiManager.isDeletedAnnotationElement(id)) {
  18944. continue;
  18945. }
  18946. let editor = resetAnnotations.get(id);
  18947. if (editor) {
  18948. editor.resetAnnotationElement(editable);
  18949. editor.show(false);
  18950. editable.show();
  18951. continue;
  18952. }
  18953. editor = changedAnnotations.get(id);
  18954. if (editor) {
  18955. this.#uiManager.addChangedExistingAnnotation(editor);
  18956. editor.renderAnnotationElement(editable);
  18957. editor.show(false);
  18958. }
  18959. editable.show();
  18960. }
  18961. }
  18962. this.#cleanup();
  18963. if (this.isEmpty) {
  18964. this.div.hidden = true;
  18965. }
  18966. const {
  18967. classList
  18968. } = this.div;
  18969. for (const editorType of AnnotationEditorLayer.#editorTypes.values()) {
  18970. classList.remove(`${editorType._type}Editing`);
  18971. }
  18972. this.disableTextSelection();
  18973. this.toggleAnnotationLayerPointerEvents(true);
  18974. this.#isDisabling = false;
  18975. }
  18976. getEditableAnnotation(id) {
  18977. return this.#annotationLayer?.getEditableAnnotation(id) || null;
  18978. }
  18979. setActiveEditor(editor) {
  18980. const currentActive = this.#uiManager.getActive();
  18981. if (currentActive === editor) {
  18982. return;
  18983. }
  18984. this.#uiManager.setActiveEditor(editor);
  18985. }
  18986. enableTextSelection() {
  18987. this.div.tabIndex = -1;
  18988. if (this.#textLayer?.div && !this.#boundTextLayerPointerDown) {
  18989. this.#boundTextLayerPointerDown = this.#textLayerPointerDown.bind(this);
  18990. this.#textLayer.div.addEventListener("pointerdown", this.#boundTextLayerPointerDown, {
  18991. signal: this.#uiManager._signal
  18992. });
  18993. this.#textLayer.div.classList.add("highlighting");
  18994. }
  18995. }
  18996. disableTextSelection() {
  18997. this.div.tabIndex = 0;
  18998. if (this.#textLayer?.div && this.#boundTextLayerPointerDown) {
  18999. this.#textLayer.div.removeEventListener("pointerdown", this.#boundTextLayerPointerDown);
  19000. this.#boundTextLayerPointerDown = null;
  19001. this.#textLayer.div.classList.remove("highlighting");
  19002. }
  19003. }
  19004. #textLayerPointerDown(event) {
  19005. this.#uiManager.unselectAll();
  19006. const {
  19007. target
  19008. } = event;
  19009. if (target === this.#textLayer.div || target.classList.contains("endOfContent") && this.#textLayer.div.contains(target)) {
  19010. const {
  19011. isMac
  19012. } = util_FeatureTest.platform;
  19013. if (event.button !== 0 || event.ctrlKey && isMac) {
  19014. return;
  19015. }
  19016. this.#uiManager.showAllEditors("highlight", true, true);
  19017. this.#textLayer.div.classList.add("free");
  19018. this.toggleDrawing();
  19019. HighlightEditor.startHighlighting(this, this.#uiManager.direction === "ltr", event);
  19020. this.#textLayer.div.addEventListener("pointerup", () => {
  19021. this.#textLayer.div.classList.remove("free");
  19022. this.toggleDrawing(true);
  19023. }, {
  19024. once: true,
  19025. signal: this.#uiManager._signal
  19026. });
  19027. event.preventDefault();
  19028. }
  19029. }
  19030. enableClick() {
  19031. if (this.#boundPointerdown) {
  19032. return;
  19033. }
  19034. const signal = this.#uiManager._signal;
  19035. this.#boundPointerdown = this.pointerdown.bind(this);
  19036. this.#boundPointerup = this.pointerup.bind(this);
  19037. this.div.addEventListener("pointerdown", this.#boundPointerdown, {
  19038. signal
  19039. });
  19040. this.div.addEventListener("pointerup", this.#boundPointerup, {
  19041. signal
  19042. });
  19043. }
  19044. disableClick() {
  19045. if (!this.#boundPointerdown) {
  19046. return;
  19047. }
  19048. this.div.removeEventListener("pointerdown", this.#boundPointerdown);
  19049. this.div.removeEventListener("pointerup", this.#boundPointerup);
  19050. this.#boundPointerdown = null;
  19051. this.#boundPointerup = null;
  19052. }
  19053. attach(editor) {
  19054. this.#editors.set(editor.id, editor);
  19055. const {
  19056. annotationElementId
  19057. } = editor;
  19058. if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) {
  19059. this.#uiManager.removeDeletedAnnotationElement(editor);
  19060. }
  19061. }
  19062. detach(editor) {
  19063. this.#editors.delete(editor.id);
  19064. this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv);
  19065. if (!this.#isDisabling && editor.annotationElementId) {
  19066. this.#uiManager.addDeletedAnnotationElement(editor);
  19067. }
  19068. }
  19069. remove(editor) {
  19070. this.detach(editor);
  19071. this.#uiManager.removeEditor(editor);
  19072. editor.div.remove();
  19073. editor.isAttachedToDOM = false;
  19074. if (!this.#isCleaningUp) {
  19075. this.addInkEditorIfNeeded(false);
  19076. }
  19077. }
  19078. changeParent(editor) {
  19079. if (editor.parent === this) {
  19080. return;
  19081. }
  19082. if (editor.parent && editor.annotationElementId) {
  19083. this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId);
  19084. AnnotationEditor.deleteAnnotationElement(editor);
  19085. editor.annotationElementId = null;
  19086. }
  19087. this.attach(editor);
  19088. editor.parent?.detach(editor);
  19089. editor.setParent(this);
  19090. if (editor.div && editor.isAttachedToDOM) {
  19091. editor.div.remove();
  19092. this.div.append(editor.div);
  19093. }
  19094. }
  19095. add(editor) {
  19096. if (editor.parent === this && editor.isAttachedToDOM) {
  19097. return;
  19098. }
  19099. this.changeParent(editor);
  19100. this.#uiManager.addEditor(editor);
  19101. this.attach(editor);
  19102. if (!editor.isAttachedToDOM) {
  19103. const div = editor.render();
  19104. this.div.append(div);
  19105. editor.isAttachedToDOM = true;
  19106. }
  19107. editor.fixAndSetPosition();
  19108. editor.onceAdded();
  19109. this.#uiManager.addToAnnotationStorage(editor);
  19110. editor._reportTelemetry(editor.telemetryInitialData);
  19111. }
  19112. moveEditorInDOM(editor) {
  19113. if (!editor.isAttachedToDOM) {
  19114. return;
  19115. }
  19116. const {
  19117. activeElement
  19118. } = document;
  19119. if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) {
  19120. editor._focusEventsAllowed = false;
  19121. this.#editorFocusTimeoutId = setTimeout(() => {
  19122. this.#editorFocusTimeoutId = null;
  19123. if (!editor.div.contains(document.activeElement)) {
  19124. editor.div.addEventListener("focusin", () => {
  19125. editor._focusEventsAllowed = true;
  19126. }, {
  19127. once: true,
  19128. signal: this.#uiManager._signal
  19129. });
  19130. activeElement.focus();
  19131. } else {
  19132. editor._focusEventsAllowed = true;
  19133. }
  19134. }, 0);
  19135. }
  19136. editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true);
  19137. }
  19138. addOrRebuild(editor) {
  19139. if (editor.needsToBeRebuilt()) {
  19140. editor.parent ||= this;
  19141. editor.rebuild();
  19142. editor.show();
  19143. } else {
  19144. this.add(editor);
  19145. }
  19146. }
  19147. addUndoableEditor(editor) {
  19148. const cmd = () => editor._uiManager.rebuild(editor);
  19149. const undo = () => {
  19150. editor.remove();
  19151. };
  19152. this.addCommands({
  19153. cmd,
  19154. undo,
  19155. mustExec: false
  19156. });
  19157. }
  19158. getNextId() {
  19159. return this.#uiManager.getId();
  19160. }
  19161. get #currentEditorType() {
  19162. return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());
  19163. }
  19164. get _signal() {
  19165. return this.#uiManager._signal;
  19166. }
  19167. #createNewEditor(params) {
  19168. const editorType = this.#currentEditorType;
  19169. return editorType ? new editorType.prototype.constructor(params) : null;
  19170. }
  19171. canCreateNewEmptyEditor() {
  19172. return this.#currentEditorType?.canCreateNewEmptyEditor();
  19173. }
  19174. pasteEditor(mode, params) {
  19175. this.#uiManager.updateToolbar(mode);
  19176. this.#uiManager.updateMode(mode);
  19177. const {
  19178. offsetX,
  19179. offsetY
  19180. } = this.#getCenterPoint();
  19181. const id = this.getNextId();
  19182. const editor = this.#createNewEditor({
  19183. parent: this,
  19184. id,
  19185. x: offsetX,
  19186. y: offsetY,
  19187. uiManager: this.#uiManager,
  19188. isCentered: true,
  19189. ...params
  19190. });
  19191. if (editor) {
  19192. this.add(editor);
  19193. }
  19194. }
  19195. deserialize(data) {
  19196. return AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager) || null;
  19197. }
  19198. createAndAddNewEditor(event, isCentered, data = {}) {
  19199. const id = this.getNextId();
  19200. const editor = this.#createNewEditor({
  19201. parent: this,
  19202. id,
  19203. x: event.offsetX,
  19204. y: event.offsetY,
  19205. uiManager: this.#uiManager,
  19206. isCentered,
  19207. ...data
  19208. });
  19209. if (editor) {
  19210. this.add(editor);
  19211. }
  19212. return editor;
  19213. }
  19214. #getCenterPoint() {
  19215. const {
  19216. x,
  19217. y,
  19218. width,
  19219. height
  19220. } = this.div.getBoundingClientRect();
  19221. const tlX = Math.max(0, x);
  19222. const tlY = Math.max(0, y);
  19223. const brX = Math.min(window.innerWidth, x + width);
  19224. const brY = Math.min(window.innerHeight, y + height);
  19225. const centerX = (tlX + brX) / 2 - x;
  19226. const centerY = (tlY + brY) / 2 - y;
  19227. const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX];
  19228. return {
  19229. offsetX,
  19230. offsetY
  19231. };
  19232. }
  19233. addNewEditor() {
  19234. this.createAndAddNewEditor(this.#getCenterPoint(), true);
  19235. }
  19236. setSelected(editor) {
  19237. this.#uiManager.setSelected(editor);
  19238. }
  19239. toggleSelected(editor) {
  19240. this.#uiManager.toggleSelected(editor);
  19241. }
  19242. isSelected(editor) {
  19243. return this.#uiManager.isSelected(editor);
  19244. }
  19245. unselect(editor) {
  19246. this.#uiManager.unselect(editor);
  19247. }
  19248. pointerup(event) {
  19249. const {
  19250. isMac
  19251. } = util_FeatureTest.platform;
  19252. if (event.button !== 0 || event.ctrlKey && isMac) {
  19253. return;
  19254. }
  19255. if (event.target !== this.div) {
  19256. return;
  19257. }
  19258. if (!this.#hadPointerDown) {
  19259. return;
  19260. }
  19261. this.#hadPointerDown = false;
  19262. if (!this.#allowClick) {
  19263. this.#allowClick = true;
  19264. return;
  19265. }
  19266. if (this.#uiManager.getMode() === AnnotationEditorType.STAMP) {
  19267. this.#uiManager.unselectAll();
  19268. return;
  19269. }
  19270. this.createAndAddNewEditor(event, false);
  19271. }
  19272. pointerdown(event) {
  19273. if (this.#uiManager.getMode() === AnnotationEditorType.HIGHLIGHT) {
  19274. this.enableTextSelection();
  19275. }
  19276. if (this.#hadPointerDown) {
  19277. this.#hadPointerDown = false;
  19278. return;
  19279. }
  19280. const {
  19281. isMac
  19282. } = util_FeatureTest.platform;
  19283. if (event.button !== 0 || event.ctrlKey && isMac) {
  19284. return;
  19285. }
  19286. if (event.target !== this.div) {
  19287. return;
  19288. }
  19289. this.#hadPointerDown = true;
  19290. const editor = this.#uiManager.getActive();
  19291. this.#allowClick = !editor || editor.isEmpty();
  19292. }
  19293. findNewParent(editor, x, y) {
  19294. const layer = this.#uiManager.findParent(x, y);
  19295. if (layer === null || layer === this) {
  19296. return false;
  19297. }
  19298. layer.changeParent(editor);
  19299. return true;
  19300. }
  19301. destroy() {
  19302. if (this.#uiManager.getActive()?.parent === this) {
  19303. this.#uiManager.commitOrRemove();
  19304. this.#uiManager.setActiveEditor(null);
  19305. }
  19306. if (this.#editorFocusTimeoutId) {
  19307. clearTimeout(this.#editorFocusTimeoutId);
  19308. this.#editorFocusTimeoutId = null;
  19309. }
  19310. for (const editor of this.#editors.values()) {
  19311. this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv);
  19312. editor.setParent(null);
  19313. editor.isAttachedToDOM = false;
  19314. editor.div.remove();
  19315. }
  19316. this.div = null;
  19317. this.#editors.clear();
  19318. this.#uiManager.removeLayer(this);
  19319. }
  19320. #cleanup() {
  19321. this.#isCleaningUp = true;
  19322. for (const editor of this.#editors.values()) {
  19323. if (editor.isEmpty()) {
  19324. editor.remove();
  19325. }
  19326. }
  19327. this.#isCleaningUp = false;
  19328. }
  19329. render({
  19330. viewport
  19331. }) {
  19332. this.viewport = viewport;
  19333. setLayerDimensions(this.div, viewport);
  19334. for (const editor of this.#uiManager.getEditors(this.pageIndex)) {
  19335. this.add(editor);
  19336. editor.rebuild();
  19337. }
  19338. this.updateMode();
  19339. }
  19340. update({
  19341. viewport
  19342. }) {
  19343. this.#uiManager.commitOrRemove();
  19344. this.#cleanup();
  19345. const oldRotation = this.viewport.rotation;
  19346. const rotation = viewport.rotation;
  19347. this.viewport = viewport;
  19348. setLayerDimensions(this.div, {
  19349. rotation
  19350. });
  19351. if (oldRotation !== rotation) {
  19352. for (const editor of this.#editors.values()) {
  19353. editor.rotate(rotation);
  19354. }
  19355. }
  19356. this.addInkEditorIfNeeded(false);
  19357. }
  19358. get pageDimensions() {
  19359. const {
  19360. pageWidth,
  19361. pageHeight
  19362. } = this.viewport.rawDims;
  19363. return [pageWidth, pageHeight];
  19364. }
  19365. get scale() {
  19366. return this.#uiManager.viewParameters.realScale;
  19367. }
  19368. }
  19369. ;// CONCATENATED MODULE: ./src/display/draw_layer.js
  19370. class DrawLayer {
  19371. #parent = null;
  19372. #id = 0;
  19373. #mapping = new Map();
  19374. #toUpdate = new Map();
  19375. constructor({
  19376. pageIndex
  19377. }) {
  19378. this.pageIndex = pageIndex;
  19379. }
  19380. setParent(parent) {
  19381. if (!this.#parent) {
  19382. this.#parent = parent;
  19383. return;
  19384. }
  19385. if (this.#parent !== parent) {
  19386. if (this.#mapping.size > 0) {
  19387. for (const root of this.#mapping.values()) {
  19388. root.remove();
  19389. parent.append(root);
  19390. }
  19391. }
  19392. this.#parent = parent;
  19393. }
  19394. }
  19395. static get _svgFactory() {
  19396. return shadow(this, "_svgFactory", new DOMSVGFactory());
  19397. }
  19398. static #setBox(element, {
  19399. x = 0,
  19400. y = 0,
  19401. width = 1,
  19402. height = 1
  19403. } = {}) {
  19404. const {
  19405. style
  19406. } = element;
  19407. style.top = `${100 * y}%`;
  19408. style.left = `${100 * x}%`;
  19409. style.width = `${100 * width}%`;
  19410. style.height = `${100 * height}%`;
  19411. }
  19412. #createSVG(box) {
  19413. const svg = DrawLayer._svgFactory.create(1, 1, true);
  19414. this.#parent.append(svg);
  19415. svg.setAttribute("aria-hidden", true);
  19416. DrawLayer.#setBox(svg, box);
  19417. return svg;
  19418. }
  19419. #createClipPath(defs, pathId) {
  19420. const clipPath = DrawLayer._svgFactory.createElement("clipPath");
  19421. defs.append(clipPath);
  19422. const clipPathId = `clip_${pathId}`;
  19423. clipPath.setAttribute("id", clipPathId);
  19424. clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
  19425. const clipPathUse = DrawLayer._svgFactory.createElement("use");
  19426. clipPath.append(clipPathUse);
  19427. clipPathUse.setAttribute("href", `#${pathId}`);
  19428. clipPathUse.classList.add("clip");
  19429. return clipPathId;
  19430. }
  19431. highlight(outlines, color, opacity, isPathUpdatable = false) {
  19432. const id = this.#id++;
  19433. const root = this.#createSVG(outlines.box);
  19434. root.classList.add("highlight");
  19435. if (outlines.free) {
  19436. root.classList.add("free");
  19437. }
  19438. const defs = DrawLayer._svgFactory.createElement("defs");
  19439. root.append(defs);
  19440. const path = DrawLayer._svgFactory.createElement("path");
  19441. defs.append(path);
  19442. const pathId = `path_p${this.pageIndex}_${id}`;
  19443. path.setAttribute("id", pathId);
  19444. path.setAttribute("d", outlines.toSVGPath());
  19445. if (isPathUpdatable) {
  19446. this.#toUpdate.set(id, path);
  19447. }
  19448. const clipPathId = this.#createClipPath(defs, pathId);
  19449. const use = DrawLayer._svgFactory.createElement("use");
  19450. root.append(use);
  19451. root.setAttribute("fill", color);
  19452. root.setAttribute("fill-opacity", opacity);
  19453. use.setAttribute("href", `#${pathId}`);
  19454. this.#mapping.set(id, root);
  19455. return {
  19456. id,
  19457. clipPathId: `url(#${clipPathId})`
  19458. };
  19459. }
  19460. highlightOutline(outlines) {
  19461. const id = this.#id++;
  19462. const root = this.#createSVG(outlines.box);
  19463. root.classList.add("highlightOutline");
  19464. const defs = DrawLayer._svgFactory.createElement("defs");
  19465. root.append(defs);
  19466. const path = DrawLayer._svgFactory.createElement("path");
  19467. defs.append(path);
  19468. const pathId = `path_p${this.pageIndex}_${id}`;
  19469. path.setAttribute("id", pathId);
  19470. path.setAttribute("d", outlines.toSVGPath());
  19471. path.setAttribute("vector-effect", "non-scaling-stroke");
  19472. let maskId;
  19473. if (outlines.free) {
  19474. root.classList.add("free");
  19475. const mask = DrawLayer._svgFactory.createElement("mask");
  19476. defs.append(mask);
  19477. maskId = `mask_p${this.pageIndex}_${id}`;
  19478. mask.setAttribute("id", maskId);
  19479. mask.setAttribute("maskUnits", "objectBoundingBox");
  19480. const rect = DrawLayer._svgFactory.createElement("rect");
  19481. mask.append(rect);
  19482. rect.setAttribute("width", "1");
  19483. rect.setAttribute("height", "1");
  19484. rect.setAttribute("fill", "white");
  19485. const use = DrawLayer._svgFactory.createElement("use");
  19486. mask.append(use);
  19487. use.setAttribute("href", `#${pathId}`);
  19488. use.setAttribute("stroke", "none");
  19489. use.setAttribute("fill", "black");
  19490. use.setAttribute("fill-rule", "nonzero");
  19491. use.classList.add("mask");
  19492. }
  19493. const use1 = DrawLayer._svgFactory.createElement("use");
  19494. root.append(use1);
  19495. use1.setAttribute("href", `#${pathId}`);
  19496. if (maskId) {
  19497. use1.setAttribute("mask", `url(#${maskId})`);
  19498. }
  19499. const use2 = use1.cloneNode();
  19500. root.append(use2);
  19501. use1.classList.add("mainOutline");
  19502. use2.classList.add("secondaryOutline");
  19503. this.#mapping.set(id, root);
  19504. return id;
  19505. }
  19506. finalizeLine(id, line) {
  19507. const path = this.#toUpdate.get(id);
  19508. this.#toUpdate.delete(id);
  19509. this.updateBox(id, line.box);
  19510. path.setAttribute("d", line.toSVGPath());
  19511. }
  19512. updateLine(id, line) {
  19513. const root = this.#mapping.get(id);
  19514. const defs = root.firstChild;
  19515. const path = defs.firstChild;
  19516. path.setAttribute("d", line.toSVGPath());
  19517. }
  19518. removeFreeHighlight(id) {
  19519. this.remove(id);
  19520. this.#toUpdate.delete(id);
  19521. }
  19522. updatePath(id, line) {
  19523. this.#toUpdate.get(id).setAttribute("d", line.toSVGPath());
  19524. }
  19525. updateBox(id, box) {
  19526. DrawLayer.#setBox(this.#mapping.get(id), box);
  19527. }
  19528. show(id, visible) {
  19529. this.#mapping.get(id).classList.toggle("hidden", !visible);
  19530. }
  19531. rotate(id, angle) {
  19532. this.#mapping.get(id).setAttribute("data-main-rotation", angle);
  19533. }
  19534. changeColor(id, color) {
  19535. this.#mapping.get(id).setAttribute("fill", color);
  19536. }
  19537. changeOpacity(id, opacity) {
  19538. this.#mapping.get(id).setAttribute("fill-opacity", opacity);
  19539. }
  19540. addClass(id, className) {
  19541. this.#mapping.get(id).classList.add(className);
  19542. }
  19543. removeClass(id, className) {
  19544. this.#mapping.get(id).classList.remove(className);
  19545. }
  19546. remove(id) {
  19547. if (this.#parent === null) {
  19548. return;
  19549. }
  19550. this.#mapping.get(id).remove();
  19551. this.#mapping.delete(id);
  19552. }
  19553. destroy() {
  19554. this.#parent = null;
  19555. for (const root of this.#mapping.values()) {
  19556. root.remove();
  19557. }
  19558. this.#mapping.clear();
  19559. }
  19560. }
  19561. ;// CONCATENATED MODULE: ./src/pdf.js
  19562. const pdfjsVersion = "4.5.136";
  19563. const pdfjsBuild = "3a21f03b0";
  19564. var __webpack_exports__AbortException = __webpack_exports__.AbortException;
  19565. var __webpack_exports__AnnotationEditorLayer = __webpack_exports__.AnnotationEditorLayer;
  19566. var __webpack_exports__AnnotationEditorParamsType = __webpack_exports__.AnnotationEditorParamsType;
  19567. var __webpack_exports__AnnotationEditorType = __webpack_exports__.AnnotationEditorType;
  19568. var __webpack_exports__AnnotationEditorUIManager = __webpack_exports__.AnnotationEditorUIManager;
  19569. var __webpack_exports__AnnotationLayer = __webpack_exports__.AnnotationLayer;
  19570. var __webpack_exports__AnnotationMode = __webpack_exports__.AnnotationMode;
  19571. var __webpack_exports__CMapCompressionType = __webpack_exports__.CMapCompressionType;
  19572. var __webpack_exports__ColorPicker = __webpack_exports__.ColorPicker;
  19573. var __webpack_exports__DOMSVGFactory = __webpack_exports__.DOMSVGFactory;
  19574. var __webpack_exports__DrawLayer = __webpack_exports__.DrawLayer;
  19575. var __webpack_exports__FeatureTest = __webpack_exports__.FeatureTest;
  19576. var __webpack_exports__GlobalWorkerOptions = __webpack_exports__.GlobalWorkerOptions;
  19577. var __webpack_exports__ImageKind = __webpack_exports__.ImageKind;
  19578. var __webpack_exports__InvalidPDFException = __webpack_exports__.InvalidPDFException;
  19579. var __webpack_exports__MissingPDFException = __webpack_exports__.MissingPDFException;
  19580. var __webpack_exports__OPS = __webpack_exports__.OPS;
  19581. var __webpack_exports__PDFDataRangeTransport = __webpack_exports__.PDFDataRangeTransport;
  19582. var __webpack_exports__PDFDateString = __webpack_exports__.PDFDateString;
  19583. var __webpack_exports__PDFWorker = __webpack_exports__.PDFWorker;
  19584. var __webpack_exports__PasswordResponses = __webpack_exports__.PasswordResponses;
  19585. var __webpack_exports__PermissionFlag = __webpack_exports__.PermissionFlag;
  19586. var __webpack_exports__PixelsPerInch = __webpack_exports__.PixelsPerInch;
  19587. var __webpack_exports__RenderingCancelledException = __webpack_exports__.RenderingCancelledException;
  19588. var __webpack_exports__TextLayer = __webpack_exports__.TextLayer;
  19589. var __webpack_exports__UnexpectedResponseException = __webpack_exports__.UnexpectedResponseException;
  19590. var __webpack_exports__Util = __webpack_exports__.Util;
  19591. var __webpack_exports__VerbosityLevel = __webpack_exports__.VerbosityLevel;
  19592. var __webpack_exports__XfaLayer = __webpack_exports__.XfaLayer;
  19593. var __webpack_exports__build = __webpack_exports__.build;
  19594. var __webpack_exports__createValidAbsoluteUrl = __webpack_exports__.createValidAbsoluteUrl;
  19595. var __webpack_exports__fetchData = __webpack_exports__.fetchData;
  19596. var __webpack_exports__getDocument = __webpack_exports__.getDocument;
  19597. var __webpack_exports__getFilenameFromUrl = __webpack_exports__.getFilenameFromUrl;
  19598. var __webpack_exports__getPdfFilenameFromUrl = __webpack_exports__.getPdfFilenameFromUrl;
  19599. var __webpack_exports__getXfaPageViewport = __webpack_exports__.getXfaPageViewport;
  19600. var __webpack_exports__isDataScheme = __webpack_exports__.isDataScheme;
  19601. var __webpack_exports__isPdfFile = __webpack_exports__.isPdfFile;
  19602. var __webpack_exports__noContextMenu = __webpack_exports__.noContextMenu;
  19603. var __webpack_exports__normalizeUnicode = __webpack_exports__.normalizeUnicode;
  19604. var __webpack_exports__setLayerDimensions = __webpack_exports__.setLayerDimensions;
  19605. var __webpack_exports__shadow = __webpack_exports__.shadow;
  19606. var __webpack_exports__version = __webpack_exports__.version;
  19607. export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__CMapCompressionType as CMapCompressionType, __webpack_exports__ColorPicker as ColorPicker, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__TextLayer as TextLayer, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__version as version };
  19608. //# sourceMappingURL=pdf.mjs.map