UNPKG

49.8 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 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"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.PagesCountLimit = exports.PDFPageViewBuffer = exports.BaseViewer = void 0;
28
29var _pdf = require("../pdf");
30
31var _ui_utils = require("./ui_utils.js");
32
33var _annotation_editor_layer_builder = require("./annotation_editor_layer_builder.js");
34
35var _annotation_layer_builder = require("./annotation_layer_builder.js");
36
37var _app_options = require("./app_options.js");
38
39var _l10n_utils = require("./l10n_utils.js");
40
41var _pdf_page_view = require("./pdf_page_view.js");
42
43var _pdf_rendering_queue = require("./pdf_rendering_queue.js");
44
45var _pdf_link_service = require("./pdf_link_service.js");
46
47var _struct_tree_layer_builder = require("./struct_tree_layer_builder.js");
48
49var _text_highlighter = require("./text_highlighter.js");
50
51var _text_layer_builder = require("./text_layer_builder.js");
52
53var _xfa_layer_builder = require("./xfa_layer_builder.js");
54
55const DEFAULT_CACHE_SIZE = 10;
56const ENABLE_PERMISSIONS_CLASS = "enablePermissions";
57const PagesCountLimit = {
58 FORCE_SCROLL_MODE_PAGE: 15000,
59 FORCE_LAZY_PAGE_INIT: 7500,
60 PAUSE_EAGER_PAGE_INIT: 250
61};
62exports.PagesCountLimit = PagesCountLimit;
63const ANNOTATION_EDITOR_MODE = _app_options.compatibilityParams.annotationEditorMode ?? _pdf.AnnotationEditorType.DISABLE;
64
65function isValidAnnotationEditorMode(mode) {
66 return Object.values(_pdf.AnnotationEditorType).includes(mode) && mode !== _pdf.AnnotationEditorType.DISABLE;
67}
68
69class PDFPageViewBuffer {
70 #buf = new Set();
71 #size = 0;
72
73 constructor(size) {
74 this.#size = size;
75 }
76
77 push(view) {
78 const buf = this.#buf;
79
80 if (buf.has(view)) {
81 buf.delete(view);
82 }
83
84 buf.add(view);
85
86 if (buf.size > this.#size) {
87 this.#destroyFirstView();
88 }
89 }
90
91 resize(newSize, idsToKeep = null) {
92 this.#size = newSize;
93 const buf = this.#buf;
94
95 if (idsToKeep) {
96 const ii = buf.size;
97 let i = 1;
98
99 for (const view of buf) {
100 if (idsToKeep.has(view.id)) {
101 buf.delete(view);
102 buf.add(view);
103 }
104
105 if (++i > ii) {
106 break;
107 }
108 }
109 }
110
111 while (buf.size > this.#size) {
112 this.#destroyFirstView();
113 }
114 }
115
116 has(view) {
117 return this.#buf.has(view);
118 }
119
120 [Symbol.iterator]() {
121 return this.#buf.keys();
122 }
123
124 #destroyFirstView() {
125 const firstView = this.#buf.keys().next().value;
126 firstView?.destroy();
127 this.#buf.delete(firstView);
128 }
129
130}
131
132exports.PDFPageViewBuffer = PDFPageViewBuffer;
133
134class BaseViewer {
135 #buffer = null;
136 #annotationEditorMode = _pdf.AnnotationEditorType.DISABLE;
137 #annotationEditorUIManager = null;
138 #annotationMode = _pdf.AnnotationMode.ENABLE_FORMS;
139 #enablePermissions = false;
140 #previousContainerHeight = 0;
141 #scrollModePageState = null;
142 #onVisibilityChange = null;
143
144 constructor(options) {
145 if (this.constructor === BaseViewer) {
146 throw new Error("Cannot initialize BaseViewer.");
147 }
148
149 const viewerVersion = '2.15.349';
150
151 if (_pdf.version !== viewerVersion) {
152 throw new Error(`The API version "${_pdf.version}" does not match the Viewer version "${viewerVersion}".`);
153 }
154
155 this.container = options.container;
156 this.viewer = options.viewer || options.container.firstElementChild;
157
158 if (!(this.container?.tagName.toUpperCase() === "DIV" && this.viewer?.tagName.toUpperCase() === "DIV")) {
159 throw new Error("Invalid `container` and/or `viewer` option.");
160 }
161
162 if (this.container.offsetParent && getComputedStyle(this.container).position !== "absolute") {
163 throw new Error("The `container` must be absolutely positioned.");
164 }
165
166 this.eventBus = options.eventBus;
167 this.linkService = options.linkService || new _pdf_link_service.SimpleLinkService();
168 this.downloadManager = options.downloadManager || null;
169 this.findController = options.findController || null;
170 this._scriptingManager = options.scriptingManager || null;
171 this.removePageBorders = options.removePageBorders || false;
172 this.textLayerMode = options.textLayerMode ?? _ui_utils.TextLayerMode.ENABLE;
173 this.#annotationMode = options.annotationMode ?? _pdf.AnnotationMode.ENABLE_FORMS;
174 this.#annotationEditorMode = options.annotationEditorMode ?? ANNOTATION_EDITOR_MODE;
175 this.imageResourcesPath = options.imageResourcesPath || "";
176 this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
177 this.renderer = options.renderer || _ui_utils.RendererType.CANVAS;
178 this.useOnlyCssZoom = options.useOnlyCssZoom || false;
179 this.maxCanvasPixels = options.maxCanvasPixels;
180 this.l10n = options.l10n || _l10n_utils.NullL10n;
181 this.#enablePermissions = options.enablePermissions || false;
182 this.pageColors = options.pageColors || null;
183
184 if (this.pageColors && !(CSS.supports("color", this.pageColors.background) && CSS.supports("color", this.pageColors.foreground))) {
185 if (this.pageColors.background || this.pageColors.foreground) {
186 console.warn("BaseViewer: Ignoring `pageColors`-option, since the browser doesn't support the values used.");
187 }
188
189 this.pageColors = null;
190 }
191
192 this.defaultRenderingQueue = !options.renderingQueue;
193
194 if (this.defaultRenderingQueue) {
195 this.renderingQueue = new _pdf_rendering_queue.PDFRenderingQueue();
196 this.renderingQueue.setViewer(this);
197 } else {
198 this.renderingQueue = options.renderingQueue;
199 }
200
201 this.scroll = (0, _ui_utils.watchScroll)(this.container, this._scrollUpdate.bind(this));
202 this.presentationModeState = _ui_utils.PresentationModeState.UNKNOWN;
203 this._onBeforeDraw = this._onAfterDraw = null;
204
205 this._resetView();
206
207 if (this.removePageBorders) {
208 this.viewer.classList.add("removePageBorders");
209 }
210
211 this.updateContainerHeightCss();
212 }
213
214 get pagesCount() {
215 return this._pages.length;
216 }
217
218 getPageView(index) {
219 return this._pages[index];
220 }
221
222 get pageViewsReady() {
223 if (!this._pagesCapability.settled) {
224 return false;
225 }
226
227 return this._pages.every(function (pageView) {
228 return pageView?.pdfPage;
229 });
230 }
231
232 get renderForms() {
233 return this.#annotationMode === _pdf.AnnotationMode.ENABLE_FORMS;
234 }
235
236 get enableScripting() {
237 return !!this._scriptingManager;
238 }
239
240 get currentPageNumber() {
241 return this._currentPageNumber;
242 }
243
244 set currentPageNumber(val) {
245 if (!Number.isInteger(val)) {
246 throw new Error("Invalid page number.");
247 }
248
249 if (!this.pdfDocument) {
250 return;
251 }
252
253 if (!this._setCurrentPageNumber(val, true)) {
254 console.error(`currentPageNumber: "${val}" is not a valid page.`);
255 }
256 }
257
258 _setCurrentPageNumber(val, resetCurrentPageView = false) {
259 if (this._currentPageNumber === val) {
260 if (resetCurrentPageView) {
261 this.#resetCurrentPageView();
262 }
263
264 return true;
265 }
266
267 if (!(0 < val && val <= this.pagesCount)) {
268 return false;
269 }
270
271 const previous = this._currentPageNumber;
272 this._currentPageNumber = val;
273 this.eventBus.dispatch("pagechanging", {
274 source: this,
275 pageNumber: val,
276 pageLabel: this._pageLabels?.[val - 1] ?? null,
277 previous
278 });
279
280 if (resetCurrentPageView) {
281 this.#resetCurrentPageView();
282 }
283
284 return true;
285 }
286
287 get currentPageLabel() {
288 return this._pageLabels?.[this._currentPageNumber - 1] ?? null;
289 }
290
291 set currentPageLabel(val) {
292 if (!this.pdfDocument) {
293 return;
294 }
295
296 let page = val | 0;
297
298 if (this._pageLabels) {
299 const i = this._pageLabels.indexOf(val);
300
301 if (i >= 0) {
302 page = i + 1;
303 }
304 }
305
306 if (!this._setCurrentPageNumber(page, true)) {
307 console.error(`currentPageLabel: "${val}" is not a valid page.`);
308 }
309 }
310
311 get currentScale() {
312 return this._currentScale !== _ui_utils.UNKNOWN_SCALE ? this._currentScale : _ui_utils.DEFAULT_SCALE;
313 }
314
315 set currentScale(val) {
316 if (isNaN(val)) {
317 throw new Error("Invalid numeric scale.");
318 }
319
320 if (!this.pdfDocument) {
321 return;
322 }
323
324 this._setScale(val, false);
325 }
326
327 get currentScaleValue() {
328 return this._currentScaleValue;
329 }
330
331 set currentScaleValue(val) {
332 if (!this.pdfDocument) {
333 return;
334 }
335
336 this._setScale(val, false);
337 }
338
339 get pagesRotation() {
340 return this._pagesRotation;
341 }
342
343 set pagesRotation(rotation) {
344 if (!(0, _ui_utils.isValidRotation)(rotation)) {
345 throw new Error("Invalid pages rotation angle.");
346 }
347
348 if (!this.pdfDocument) {
349 return;
350 }
351
352 rotation %= 360;
353
354 if (rotation < 0) {
355 rotation += 360;
356 }
357
358 if (this._pagesRotation === rotation) {
359 return;
360 }
361
362 this._pagesRotation = rotation;
363 const pageNumber = this._currentPageNumber;
364 const updateArgs = {
365 rotation
366 };
367
368 for (const pageView of this._pages) {
369 pageView.update(updateArgs);
370 }
371
372 if (this._currentScaleValue) {
373 this._setScale(this._currentScaleValue, true);
374 }
375
376 this.eventBus.dispatch("rotationchanging", {
377 source: this,
378 pagesRotation: rotation,
379 pageNumber
380 });
381
382 if (this.defaultRenderingQueue) {
383 this.update();
384 }
385 }
386
387 get firstPagePromise() {
388 return this.pdfDocument ? this._firstPageCapability.promise : null;
389 }
390
391 get onePageRendered() {
392 return this.pdfDocument ? this._onePageRenderedCapability.promise : null;
393 }
394
395 get pagesPromise() {
396 return this.pdfDocument ? this._pagesCapability.promise : null;
397 }
398
399 #initializePermissions(permissions) {
400 const params = {
401 annotationEditorMode: this.#annotationEditorMode,
402 annotationMode: this.#annotationMode,
403 textLayerMode: this.textLayerMode
404 };
405
406 if (!permissions) {
407 return params;
408 }
409
410 if (!permissions.includes(_pdf.PermissionFlag.COPY)) {
411 this.viewer.classList.add(ENABLE_PERMISSIONS_CLASS);
412 }
413
414 if (!permissions.includes(_pdf.PermissionFlag.MODIFY_CONTENTS)) {
415 params.annotationEditorMode = _pdf.AnnotationEditorType.DISABLE;
416 }
417
418 if (!permissions.includes(_pdf.PermissionFlag.MODIFY_ANNOTATIONS) && !permissions.includes(_pdf.PermissionFlag.FILL_INTERACTIVE_FORMS) && this.#annotationMode === _pdf.AnnotationMode.ENABLE_FORMS) {
419 params.annotationMode = _pdf.AnnotationMode.ENABLE;
420 }
421
422 return params;
423 }
424
425 #onePageRenderedOrForceFetch() {
426 if (document.visibilityState === "hidden" || !this.container.offsetParent || this._getVisiblePages().views.length === 0) {
427 return Promise.resolve();
428 }
429
430 const visibilityChangePromise = new Promise(resolve => {
431 this.#onVisibilityChange = () => {
432 if (document.visibilityState !== "hidden") {
433 return;
434 }
435
436 resolve();
437 document.removeEventListener("visibilitychange", this.#onVisibilityChange);
438 this.#onVisibilityChange = null;
439 };
440
441 document.addEventListener("visibilitychange", this.#onVisibilityChange);
442 });
443 return Promise.race([this._onePageRenderedCapability.promise, visibilityChangePromise]);
444 }
445
446 setDocument(pdfDocument) {
447 if (this.pdfDocument) {
448 this.eventBus.dispatch("pagesdestroy", {
449 source: this
450 });
451
452 this._cancelRendering();
453
454 this._resetView();
455
456 if (this.findController) {
457 this.findController.setDocument(null);
458 }
459
460 if (this._scriptingManager) {
461 this._scriptingManager.setDocument(null);
462 }
463
464 if (this.#annotationEditorUIManager) {
465 this.#annotationEditorUIManager.destroy();
466 this.#annotationEditorUIManager = null;
467 }
468 }
469
470 this.pdfDocument = pdfDocument;
471
472 if (!pdfDocument) {
473 return;
474 }
475
476 const isPureXfa = pdfDocument.isPureXfa;
477 const pagesCount = pdfDocument.numPages;
478 const firstPagePromise = pdfDocument.getPage(1);
479 const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
480 const permissionsPromise = this.#enablePermissions ? pdfDocument.getPermissions() : Promise.resolve();
481
482 if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
483 console.warn("Forcing PAGE-scrolling for performance reasons, given the length of the document.");
484 const mode = this._scrollMode = _ui_utils.ScrollMode.PAGE;
485 this.eventBus.dispatch("scrollmodechanged", {
486 source: this,
487 mode
488 });
489 }
490
491 this._pagesCapability.promise.then(() => {
492 this.eventBus.dispatch("pagesloaded", {
493 source: this,
494 pagesCount
495 });
496 }, () => {});
497
498 this._onBeforeDraw = evt => {
499 const pageView = this._pages[evt.pageNumber - 1];
500
501 if (!pageView) {
502 return;
503 }
504
505 this.#buffer.push(pageView);
506 };
507
508 this.eventBus._on("pagerender", this._onBeforeDraw);
509
510 this._onAfterDraw = evt => {
511 if (evt.cssTransform || this._onePageRenderedCapability.settled) {
512 return;
513 }
514
515 this._onePageRenderedCapability.resolve({
516 timestamp: evt.timestamp
517 });
518
519 this.eventBus._off("pagerendered", this._onAfterDraw);
520
521 this._onAfterDraw = null;
522
523 if (this.#onVisibilityChange) {
524 document.removeEventListener("visibilitychange", this.#onVisibilityChange);
525 this.#onVisibilityChange = null;
526 }
527 };
528
529 this.eventBus._on("pagerendered", this._onAfterDraw);
530
531 Promise.all([firstPagePromise, permissionsPromise]).then(([firstPdfPage, permissions]) => {
532 if (pdfDocument !== this.pdfDocument) {
533 return;
534 }
535
536 this._firstPageCapability.resolve(firstPdfPage);
537
538 this._optionalContentConfigPromise = optionalContentConfigPromise;
539 const {
540 annotationEditorMode,
541 annotationMode,
542 textLayerMode
543 } = this.#initializePermissions(permissions);
544
545 if (annotationEditorMode !== _pdf.AnnotationEditorType.DISABLE) {
546 const mode = annotationEditorMode;
547
548 if (isPureXfa) {
549 console.warn("Warning: XFA-editing is not implemented.");
550 } else if (isValidAnnotationEditorMode(mode)) {
551 this.eventBus.dispatch("annotationeditormodechanged", {
552 source: this,
553 mode
554 });
555 this.#annotationEditorUIManager = new _pdf.AnnotationEditorUIManager(this.container, this.eventBus);
556
557 if (mode !== _pdf.AnnotationEditorType.NONE) {
558 this.#annotationEditorUIManager.updateMode(mode);
559 }
560 } else {
561 console.error(`Invalid AnnotationEditor mode: ${mode}`);
562 }
563 }
564
565 const viewerElement = this._scrollMode === _ui_utils.ScrollMode.PAGE ? null : this.viewer;
566 const scale = this.currentScale;
567 const viewport = firstPdfPage.getViewport({
568 scale: scale * _pdf.PixelsPerInch.PDF_TO_CSS_UNITS
569 });
570 const textLayerFactory = textLayerMode !== _ui_utils.TextLayerMode.DISABLE && !isPureXfa ? this : null;
571 const annotationLayerFactory = annotationMode !== _pdf.AnnotationMode.DISABLE ? this : null;
572 const xfaLayerFactory = isPureXfa ? this : null;
573 const annotationEditorLayerFactory = this.#annotationEditorUIManager ? this : null;
574
575 for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
576 const pageView = new _pdf_page_view.PDFPageView({
577 container: viewerElement,
578 eventBus: this.eventBus,
579 id: pageNum,
580 scale,
581 defaultViewport: viewport.clone(),
582 optionalContentConfigPromise,
583 renderingQueue: this.renderingQueue,
584 textLayerFactory,
585 textLayerMode,
586 annotationLayerFactory,
587 annotationMode,
588 xfaLayerFactory,
589 annotationEditorLayerFactory,
590 textHighlighterFactory: this,
591 structTreeLayerFactory: this,
592 imageResourcesPath: this.imageResourcesPath,
593 renderer: this.renderer,
594 useOnlyCssZoom: this.useOnlyCssZoom,
595 maxCanvasPixels: this.maxCanvasPixels,
596 pageColors: this.pageColors,
597 l10n: this.l10n
598 });
599
600 this._pages.push(pageView);
601 }
602
603 const firstPageView = this._pages[0];
604
605 if (firstPageView) {
606 firstPageView.setPdfPage(firstPdfPage);
607 this.linkService.cachePageRef(1, firstPdfPage.ref);
608 }
609
610 if (this._scrollMode === _ui_utils.ScrollMode.PAGE) {
611 this.#ensurePageViewVisible();
612 } else if (this._spreadMode !== _ui_utils.SpreadMode.NONE) {
613 this._updateSpreadMode();
614 }
615
616 this.#onePageRenderedOrForceFetch().then(async () => {
617 if (this.findController) {
618 this.findController.setDocument(pdfDocument);
619 }
620
621 if (this._scriptingManager) {
622 this._scriptingManager.setDocument(pdfDocument);
623 }
624
625 if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > PagesCountLimit.FORCE_LAZY_PAGE_INIT) {
626 this._pagesCapability.resolve();
627
628 return;
629 }
630
631 let getPagesLeft = pagesCount - 1;
632
633 if (getPagesLeft <= 0) {
634 this._pagesCapability.resolve();
635
636 return;
637 }
638
639 for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
640 const promise = pdfDocument.getPage(pageNum).then(pdfPage => {
641 const pageView = this._pages[pageNum - 1];
642
643 if (!pageView.pdfPage) {
644 pageView.setPdfPage(pdfPage);
645 }
646
647 this.linkService.cachePageRef(pageNum, pdfPage.ref);
648
649 if (--getPagesLeft === 0) {
650 this._pagesCapability.resolve();
651 }
652 }, reason => {
653 console.error(`Unable to get page ${pageNum} to initialize viewer`, reason);
654
655 if (--getPagesLeft === 0) {
656 this._pagesCapability.resolve();
657 }
658 });
659
660 if (pageNum % PagesCountLimit.PAUSE_EAGER_PAGE_INIT === 0) {
661 await promise;
662 }
663 }
664 });
665 this.eventBus.dispatch("pagesinit", {
666 source: this
667 });
668 pdfDocument.getMetadata().then(({
669 info
670 }) => {
671 if (pdfDocument !== this.pdfDocument) {
672 return;
673 }
674
675 if (info.Language) {
676 this.viewer.lang = info.Language;
677 }
678 });
679
680 if (this.defaultRenderingQueue) {
681 this.update();
682 }
683 }).catch(reason => {
684 console.error("Unable to initialize viewer", reason);
685
686 this._pagesCapability.reject(reason);
687 });
688 }
689
690 setPageLabels(labels) {
691 if (!this.pdfDocument) {
692 return;
693 }
694
695 if (!labels) {
696 this._pageLabels = null;
697 } else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
698 this._pageLabels = null;
699 console.error(`setPageLabels: Invalid page labels.`);
700 } else {
701 this._pageLabels = labels;
702 }
703
704 for (let i = 0, ii = this._pages.length; i < ii; i++) {
705 this._pages[i].setPageLabel(this._pageLabels?.[i] ?? null);
706 }
707 }
708
709 _resetView() {
710 this._pages = [];
711 this._currentPageNumber = 1;
712 this._currentScale = _ui_utils.UNKNOWN_SCALE;
713 this._currentScaleValue = null;
714 this._pageLabels = null;
715 this.#buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE);
716 this._location = null;
717 this._pagesRotation = 0;
718 this._optionalContentConfigPromise = null;
719 this._firstPageCapability = (0, _pdf.createPromiseCapability)();
720 this._onePageRenderedCapability = (0, _pdf.createPromiseCapability)();
721 this._pagesCapability = (0, _pdf.createPromiseCapability)();
722 this._scrollMode = _ui_utils.ScrollMode.VERTICAL;
723 this._previousScrollMode = _ui_utils.ScrollMode.UNKNOWN;
724 this._spreadMode = _ui_utils.SpreadMode.NONE;
725 this.#scrollModePageState = {
726 previousPageNumber: 1,
727 scrollDown: true,
728 pages: []
729 };
730
731 if (this._onBeforeDraw) {
732 this.eventBus._off("pagerender", this._onBeforeDraw);
733
734 this._onBeforeDraw = null;
735 }
736
737 if (this._onAfterDraw) {
738 this.eventBus._off("pagerendered", this._onAfterDraw);
739
740 this._onAfterDraw = null;
741 }
742
743 if (this.#onVisibilityChange) {
744 document.removeEventListener("visibilitychange", this.#onVisibilityChange);
745 this.#onVisibilityChange = null;
746 }
747
748 this.viewer.textContent = "";
749
750 this._updateScrollMode();
751
752 this.viewer.removeAttribute("lang");
753 this.viewer.classList.remove(ENABLE_PERMISSIONS_CLASS);
754 }
755
756 #ensurePageViewVisible() {
757 if (this._scrollMode !== _ui_utils.ScrollMode.PAGE) {
758 throw new Error("#ensurePageViewVisible: Invalid scrollMode value.");
759 }
760
761 const pageNumber = this._currentPageNumber,
762 state = this.#scrollModePageState,
763 viewer = this.viewer;
764 viewer.textContent = "";
765 state.pages.length = 0;
766
767 if (this._spreadMode === _ui_utils.SpreadMode.NONE && !this.isInPresentationMode) {
768 const pageView = this._pages[pageNumber - 1];
769 viewer.append(pageView.div);
770 state.pages.push(pageView);
771 } else {
772 const pageIndexSet = new Set(),
773 parity = this._spreadMode - 1;
774
775 if (parity === -1) {
776 pageIndexSet.add(pageNumber - 1);
777 } else if (pageNumber % 2 !== parity) {
778 pageIndexSet.add(pageNumber - 1);
779 pageIndexSet.add(pageNumber);
780 } else {
781 pageIndexSet.add(pageNumber - 2);
782 pageIndexSet.add(pageNumber - 1);
783 }
784
785 const spread = document.createElement("div");
786 spread.className = "spread";
787
788 if (this.isInPresentationMode) {
789 const dummyPage = document.createElement("div");
790 dummyPage.className = "dummyPage";
791 spread.append(dummyPage);
792 }
793
794 for (const i of pageIndexSet) {
795 const pageView = this._pages[i];
796
797 if (!pageView) {
798 continue;
799 }
800
801 spread.append(pageView.div);
802 state.pages.push(pageView);
803 }
804
805 viewer.append(spread);
806 }
807
808 state.scrollDown = pageNumber >= state.previousPageNumber;
809 state.previousPageNumber = pageNumber;
810 }
811
812 _scrollUpdate() {
813 if (this.pagesCount === 0) {
814 return;
815 }
816
817 this.update();
818 }
819
820 #scrollIntoView(pageView, pageSpot = null) {
821 const {
822 div,
823 id
824 } = pageView;
825
826 if (this._scrollMode === _ui_utils.ScrollMode.PAGE) {
827 this._setCurrentPageNumber(id);
828
829 this.#ensurePageViewVisible();
830 this.update();
831 }
832
833 if (!pageSpot && !this.isInPresentationMode) {
834 const left = div.offsetLeft + div.clientLeft,
835 right = left + div.clientWidth;
836 const {
837 scrollLeft,
838 clientWidth
839 } = this.container;
840
841 if (this._scrollMode === _ui_utils.ScrollMode.HORIZONTAL || left < scrollLeft || right > scrollLeft + clientWidth) {
842 pageSpot = {
843 left: 0,
844 top: 0
845 };
846 }
847 }
848
849 (0, _ui_utils.scrollIntoView)(div, pageSpot);
850 }
851
852 #isSameScale(newScale) {
853 return newScale === this._currentScale || Math.abs(newScale - this._currentScale) < 1e-15;
854 }
855
856 _setScaleUpdatePages(newScale, newValue, noScroll = false, preset = false) {
857 this._currentScaleValue = newValue.toString();
858
859 if (this.#isSameScale(newScale)) {
860 if (preset) {
861 this.eventBus.dispatch("scalechanging", {
862 source: this,
863 scale: newScale,
864 presetValue: newValue
865 });
866 }
867
868 return;
869 }
870
871 _ui_utils.docStyle.setProperty("--scale-factor", newScale * _pdf.PixelsPerInch.PDF_TO_CSS_UNITS);
872
873 const updateArgs = {
874 scale: newScale
875 };
876
877 for (const pageView of this._pages) {
878 pageView.update(updateArgs);
879 }
880
881 this._currentScale = newScale;
882
883 if (!noScroll) {
884 let page = this._currentPageNumber,
885 dest;
886
887 if (this._location && !(this.isInPresentationMode || this.isChangingPresentationMode)) {
888 page = this._location.pageNumber;
889 dest = [null, {
890 name: "XYZ"
891 }, this._location.left, this._location.top, null];
892 }
893
894 this.scrollPageIntoView({
895 pageNumber: page,
896 destArray: dest,
897 allowNegativeOffset: true
898 });
899 }
900
901 this.eventBus.dispatch("scalechanging", {
902 source: this,
903 scale: newScale,
904 presetValue: preset ? newValue : undefined
905 });
906
907 if (this.defaultRenderingQueue) {
908 this.update();
909 }
910
911 this.updateContainerHeightCss();
912 }
913
914 get _pageWidthScaleFactor() {
915 if (this._spreadMode !== _ui_utils.SpreadMode.NONE && this._scrollMode !== _ui_utils.ScrollMode.HORIZONTAL) {
916 return 2;
917 }
918
919 return 1;
920 }
921
922 _setScale(value, noScroll = false) {
923 let scale = parseFloat(value);
924
925 if (scale > 0) {
926 this._setScaleUpdatePages(scale, value, noScroll, false);
927 } else {
928 const currentPage = this._pages[this._currentPageNumber - 1];
929
930 if (!currentPage) {
931 return;
932 }
933
934 let hPadding = _ui_utils.SCROLLBAR_PADDING,
935 vPadding = _ui_utils.VERTICAL_PADDING;
936
937 if (this.isInPresentationMode) {
938 hPadding = vPadding = 4;
939 } else if (this.removePageBorders) {
940 hPadding = vPadding = 0;
941 } else if (this._scrollMode === _ui_utils.ScrollMode.HORIZONTAL) {
942 [hPadding, vPadding] = [vPadding, hPadding];
943 }
944
945 const pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale / this._pageWidthScaleFactor;
946 const pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale;
947
948 switch (value) {
949 case "page-actual":
950 scale = 1;
951 break;
952
953 case "page-width":
954 scale = pageWidthScale;
955 break;
956
957 case "page-height":
958 scale = pageHeightScale;
959 break;
960
961 case "page-fit":
962 scale = Math.min(pageWidthScale, pageHeightScale);
963 break;
964
965 case "auto":
966 const horizontalScale = (0, _ui_utils.isPortraitOrientation)(currentPage) ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
967 scale = Math.min(_ui_utils.MAX_AUTO_SCALE, horizontalScale);
968 break;
969
970 default:
971 console.error(`_setScale: "${value}" is an unknown zoom value.`);
972 return;
973 }
974
975 this._setScaleUpdatePages(scale, value, noScroll, true);
976 }
977 }
978
979 #resetCurrentPageView() {
980 const pageView = this._pages[this._currentPageNumber - 1];
981
982 if (this.isInPresentationMode) {
983 this._setScale(this._currentScaleValue, true);
984 }
985
986 this.#scrollIntoView(pageView);
987 }
988
989 pageLabelToPageNumber(label) {
990 if (!this._pageLabels) {
991 return null;
992 }
993
994 const i = this._pageLabels.indexOf(label);
995
996 if (i < 0) {
997 return null;
998 }
999
1000 return i + 1;
1001 }
1002
1003 scrollPageIntoView({
1004 pageNumber,
1005 destArray = null,
1006 allowNegativeOffset = false,
1007 ignoreDestinationZoom = false
1008 }) {
1009 if (!this.pdfDocument) {
1010 return;
1011 }
1012
1013 const pageView = Number.isInteger(pageNumber) && this._pages[pageNumber - 1];
1014
1015 if (!pageView) {
1016 console.error(`scrollPageIntoView: "${pageNumber}" is not a valid pageNumber parameter.`);
1017 return;
1018 }
1019
1020 if (this.isInPresentationMode || !destArray) {
1021 this._setCurrentPageNumber(pageNumber, true);
1022
1023 return;
1024 }
1025
1026 let x = 0,
1027 y = 0;
1028 let width = 0,
1029 height = 0,
1030 widthScale,
1031 heightScale;
1032 const changeOrientation = pageView.rotation % 180 !== 0;
1033 const pageWidth = (changeOrientation ? pageView.height : pageView.width) / pageView.scale / _pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
1034 const pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / _pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
1035 let scale = 0;
1036
1037 switch (destArray[1].name) {
1038 case "XYZ":
1039 x = destArray[2];
1040 y = destArray[3];
1041 scale = destArray[4];
1042 x = x !== null ? x : 0;
1043 y = y !== null ? y : pageHeight;
1044 break;
1045
1046 case "Fit":
1047 case "FitB":
1048 scale = "page-fit";
1049 break;
1050
1051 case "FitH":
1052 case "FitBH":
1053 y = destArray[2];
1054 scale = "page-width";
1055
1056 if (y === null && this._location) {
1057 x = this._location.left;
1058 y = this._location.top;
1059 } else if (typeof y !== "number" || y < 0) {
1060 y = pageHeight;
1061 }
1062
1063 break;
1064
1065 case "FitV":
1066 case "FitBV":
1067 x = destArray[2];
1068 width = pageWidth;
1069 height = pageHeight;
1070 scale = "page-height";
1071 break;
1072
1073 case "FitR":
1074 x = destArray[2];
1075 y = destArray[3];
1076 width = destArray[4] - x;
1077 height = destArray[5] - y;
1078 const hPadding = this.removePageBorders ? 0 : _ui_utils.SCROLLBAR_PADDING;
1079 const vPadding = this.removePageBorders ? 0 : _ui_utils.VERTICAL_PADDING;
1080 widthScale = (this.container.clientWidth - hPadding) / width / _pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
1081 heightScale = (this.container.clientHeight - vPadding) / height / _pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
1082 scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
1083 break;
1084
1085 default:
1086 console.error(`scrollPageIntoView: "${destArray[1].name}" is not a valid destination type.`);
1087 return;
1088 }
1089
1090 if (!ignoreDestinationZoom) {
1091 if (scale && scale !== this._currentScale) {
1092 this.currentScaleValue = scale;
1093 } else if (this._currentScale === _ui_utils.UNKNOWN_SCALE) {
1094 this.currentScaleValue = _ui_utils.DEFAULT_SCALE_VALUE;
1095 }
1096 }
1097
1098 if (scale === "page-fit" && !destArray[4]) {
1099 this.#scrollIntoView(pageView);
1100 return;
1101 }
1102
1103 const boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)];
1104 let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
1105 let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
1106
1107 if (!allowNegativeOffset) {
1108 left = Math.max(left, 0);
1109 top = Math.max(top, 0);
1110 }
1111
1112 this.#scrollIntoView(pageView, {
1113 left,
1114 top
1115 });
1116 }
1117
1118 _updateLocation(firstPage) {
1119 const currentScale = this._currentScale;
1120 const currentScaleValue = this._currentScaleValue;
1121 const normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue;
1122 const pageNumber = firstPage.id;
1123 const currentPageView = this._pages[pageNumber - 1];
1124 const container = this.container;
1125 const topLeft = currentPageView.getPagePoint(container.scrollLeft - firstPage.x, container.scrollTop - firstPage.y);
1126 const intLeft = Math.round(topLeft[0]);
1127 const intTop = Math.round(topLeft[1]);
1128 let pdfOpenParams = `#page=${pageNumber}`;
1129
1130 if (!this.isInPresentationMode) {
1131 pdfOpenParams += `&zoom=${normalizedScaleValue},${intLeft},${intTop}`;
1132 }
1133
1134 this._location = {
1135 pageNumber,
1136 scale: normalizedScaleValue,
1137 top: intTop,
1138 left: intLeft,
1139 rotation: this._pagesRotation,
1140 pdfOpenParams
1141 };
1142 }
1143
1144 update() {
1145 const visible = this._getVisiblePages();
1146
1147 const visiblePages = visible.views,
1148 numVisiblePages = visiblePages.length;
1149
1150 if (numVisiblePages === 0) {
1151 return;
1152 }
1153
1154 const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
1155 this.#buffer.resize(newCacheSize, visible.ids);
1156 this.renderingQueue.renderHighestPriority(visible);
1157 const isSimpleLayout = this._spreadMode === _ui_utils.SpreadMode.NONE && (this._scrollMode === _ui_utils.ScrollMode.PAGE || this._scrollMode === _ui_utils.ScrollMode.VERTICAL);
1158 const currentId = this._currentPageNumber;
1159 let stillFullyVisible = false;
1160
1161 for (const page of visiblePages) {
1162 if (page.percent < 100) {
1163 break;
1164 }
1165
1166 if (page.id === currentId && isSimpleLayout) {
1167 stillFullyVisible = true;
1168 break;
1169 }
1170 }
1171
1172 this._setCurrentPageNumber(stillFullyVisible ? currentId : visiblePages[0].id);
1173
1174 this._updateLocation(visible.first);
1175
1176 this.eventBus.dispatch("updateviewarea", {
1177 source: this,
1178 location: this._location
1179 });
1180 }
1181
1182 containsElement(element) {
1183 return this.container.contains(element);
1184 }
1185
1186 focus() {
1187 this.container.focus();
1188 }
1189
1190 get _isContainerRtl() {
1191 return getComputedStyle(this.container).direction === "rtl";
1192 }
1193
1194 get isInPresentationMode() {
1195 return this.presentationModeState === _ui_utils.PresentationModeState.FULLSCREEN;
1196 }
1197
1198 get isChangingPresentationMode() {
1199 return this.presentationModeState === _ui_utils.PresentationModeState.CHANGING;
1200 }
1201
1202 get isHorizontalScrollbarEnabled() {
1203 return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth;
1204 }
1205
1206 get isVerticalScrollbarEnabled() {
1207 return this.isInPresentationMode ? false : this.container.scrollHeight > this.container.clientHeight;
1208 }
1209
1210 _getVisiblePages() {
1211 const views = this._scrollMode === _ui_utils.ScrollMode.PAGE ? this.#scrollModePageState.pages : this._pages,
1212 horizontal = this._scrollMode === _ui_utils.ScrollMode.HORIZONTAL,
1213 rtl = horizontal && this._isContainerRtl;
1214 return (0, _ui_utils.getVisibleElements)({
1215 scrollEl: this.container,
1216 views,
1217 sortByVisibility: true,
1218 horizontal,
1219 rtl
1220 });
1221 }
1222
1223 isPageVisible(pageNumber) {
1224 if (!this.pdfDocument) {
1225 return false;
1226 }
1227
1228 if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) {
1229 console.error(`isPageVisible: "${pageNumber}" is not a valid page.`);
1230 return false;
1231 }
1232
1233 return this._getVisiblePages().ids.has(pageNumber);
1234 }
1235
1236 isPageCached(pageNumber) {
1237 if (!this.pdfDocument) {
1238 return false;
1239 }
1240
1241 if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) {
1242 console.error(`isPageCached: "${pageNumber}" is not a valid page.`);
1243 return false;
1244 }
1245
1246 const pageView = this._pages[pageNumber - 1];
1247 return this.#buffer.has(pageView);
1248 }
1249
1250 cleanup() {
1251 for (const pageView of this._pages) {
1252 if (pageView.renderingState !== _ui_utils.RenderingStates.FINISHED) {
1253 pageView.reset();
1254 }
1255 }
1256 }
1257
1258 _cancelRendering() {
1259 for (const pageView of this._pages) {
1260 pageView.cancelRendering();
1261 }
1262 }
1263
1264 async #ensurePdfPageLoaded(pageView) {
1265 if (pageView.pdfPage) {
1266 return pageView.pdfPage;
1267 }
1268
1269 try {
1270 const pdfPage = await this.pdfDocument.getPage(pageView.id);
1271
1272 if (!pageView.pdfPage) {
1273 pageView.setPdfPage(pdfPage);
1274 }
1275
1276 if (!this.linkService._cachedPageNumber?.(pdfPage.ref)) {
1277 this.linkService.cachePageRef(pageView.id, pdfPage.ref);
1278 }
1279
1280 return pdfPage;
1281 } catch (reason) {
1282 console.error("Unable to get page for page view", reason);
1283 return null;
1284 }
1285 }
1286
1287 #getScrollAhead(visible) {
1288 if (visible.first?.id === 1) {
1289 return true;
1290 } else if (visible.last?.id === this.pagesCount) {
1291 return false;
1292 }
1293
1294 switch (this._scrollMode) {
1295 case _ui_utils.ScrollMode.PAGE:
1296 return this.#scrollModePageState.scrollDown;
1297
1298 case _ui_utils.ScrollMode.HORIZONTAL:
1299 return this.scroll.right;
1300 }
1301
1302 return this.scroll.down;
1303 }
1304
1305 #toggleLoadingIconSpinner(visibleIds) {
1306 for (const id of visibleIds) {
1307 const pageView = this._pages[id - 1];
1308 pageView?.toggleLoadingIconSpinner(true);
1309 }
1310
1311 for (const pageView of this.#buffer) {
1312 if (visibleIds.has(pageView.id)) {
1313 continue;
1314 }
1315
1316 pageView.toggleLoadingIconSpinner(false);
1317 }
1318 }
1319
1320 forceRendering(currentlyVisiblePages) {
1321 const visiblePages = currentlyVisiblePages || this._getVisiblePages();
1322
1323 const scrollAhead = this.#getScrollAhead(visiblePages);
1324 const preRenderExtra = this._spreadMode !== _ui_utils.SpreadMode.NONE && this._scrollMode !== _ui_utils.ScrollMode.HORIZONTAL;
1325 const pageView = this.renderingQueue.getHighestPriority(visiblePages, this._pages, scrollAhead, preRenderExtra);
1326 this.#toggleLoadingIconSpinner(visiblePages.ids);
1327
1328 if (pageView) {
1329 this.#ensurePdfPageLoaded(pageView).then(() => {
1330 this.renderingQueue.renderView(pageView);
1331 });
1332 return true;
1333 }
1334
1335 return false;
1336 }
1337
1338 createTextLayerBuilder({
1339 textLayerDiv,
1340 pageIndex,
1341 viewport,
1342 enhanceTextSelection = false,
1343 eventBus,
1344 highlighter
1345 }) {
1346 return new _text_layer_builder.TextLayerBuilder({
1347 textLayerDiv,
1348 eventBus,
1349 pageIndex,
1350 viewport,
1351 enhanceTextSelection: this.isInPresentationMode ? false : enhanceTextSelection,
1352 highlighter
1353 });
1354 }
1355
1356 createTextHighlighter({
1357 pageIndex,
1358 eventBus
1359 }) {
1360 return new _text_highlighter.TextHighlighter({
1361 eventBus,
1362 pageIndex,
1363 findController: this.isInPresentationMode ? null : this.findController
1364 });
1365 }
1366
1367 createAnnotationLayerBuilder({
1368 pageDiv,
1369 pdfPage,
1370 annotationStorage = this.pdfDocument?.annotationStorage,
1371 imageResourcesPath = "",
1372 renderForms = true,
1373 l10n = _l10n_utils.NullL10n,
1374 enableScripting = this.enableScripting,
1375 hasJSActionsPromise = this.pdfDocument?.hasJSActions(),
1376 mouseState = this._scriptingManager?.mouseState,
1377 fieldObjectsPromise = this.pdfDocument?.getFieldObjects(),
1378 annotationCanvasMap = null
1379 }) {
1380 return new _annotation_layer_builder.AnnotationLayerBuilder({
1381 pageDiv,
1382 pdfPage,
1383 annotationStorage,
1384 imageResourcesPath,
1385 renderForms,
1386 linkService: this.linkService,
1387 downloadManager: this.downloadManager,
1388 l10n,
1389 enableScripting,
1390 hasJSActionsPromise,
1391 mouseState,
1392 fieldObjectsPromise,
1393 annotationCanvasMap
1394 });
1395 }
1396
1397 createAnnotationEditorLayerBuilder({
1398 uiManager = this.#annotationEditorUIManager,
1399 pageDiv,
1400 pdfPage,
1401 l10n,
1402 annotationStorage = this.pdfDocument?.annotationStorage
1403 }) {
1404 return new _annotation_editor_layer_builder.AnnotationEditorLayerBuilder({
1405 uiManager,
1406 pageDiv,
1407 pdfPage,
1408 annotationStorage,
1409 l10n
1410 });
1411 }
1412
1413 createXfaLayerBuilder({
1414 pageDiv,
1415 pdfPage,
1416 annotationStorage = this.pdfDocument?.annotationStorage
1417 }) {
1418 return new _xfa_layer_builder.XfaLayerBuilder({
1419 pageDiv,
1420 pdfPage,
1421 annotationStorage,
1422 linkService: this.linkService
1423 });
1424 }
1425
1426 createStructTreeLayerBuilder({
1427 pdfPage
1428 }) {
1429 return new _struct_tree_layer_builder.StructTreeLayerBuilder({
1430 pdfPage
1431 });
1432 }
1433
1434 get hasEqualPageSizes() {
1435 const firstPageView = this._pages[0];
1436
1437 for (let i = 1, ii = this._pages.length; i < ii; ++i) {
1438 const pageView = this._pages[i];
1439
1440 if (pageView.width !== firstPageView.width || pageView.height !== firstPageView.height) {
1441 return false;
1442 }
1443 }
1444
1445 return true;
1446 }
1447
1448 getPagesOverview() {
1449 return this._pages.map(pageView => {
1450 const viewport = pageView.pdfPage.getViewport({
1451 scale: 1
1452 });
1453
1454 if (!this.enablePrintAutoRotate || (0, _ui_utils.isPortraitOrientation)(viewport)) {
1455 return {
1456 width: viewport.width,
1457 height: viewport.height,
1458 rotation: viewport.rotation
1459 };
1460 }
1461
1462 return {
1463 width: viewport.height,
1464 height: viewport.width,
1465 rotation: (viewport.rotation - 90) % 360
1466 };
1467 });
1468 }
1469
1470 get optionalContentConfigPromise() {
1471 if (!this.pdfDocument) {
1472 return Promise.resolve(null);
1473 }
1474
1475 if (!this._optionalContentConfigPromise) {
1476 console.error("optionalContentConfigPromise: Not initialized yet.");
1477 return this.pdfDocument.getOptionalContentConfig();
1478 }
1479
1480 return this._optionalContentConfigPromise;
1481 }
1482
1483 set optionalContentConfigPromise(promise) {
1484 if (!(promise instanceof Promise)) {
1485 throw new Error(`Invalid optionalContentConfigPromise: ${promise}`);
1486 }
1487
1488 if (!this.pdfDocument) {
1489 return;
1490 }
1491
1492 if (!this._optionalContentConfigPromise) {
1493 return;
1494 }
1495
1496 this._optionalContentConfigPromise = promise;
1497 const updateArgs = {
1498 optionalContentConfigPromise: promise
1499 };
1500
1501 for (const pageView of this._pages) {
1502 pageView.update(updateArgs);
1503 }
1504
1505 this.update();
1506 this.eventBus.dispatch("optionalcontentconfigchanged", {
1507 source: this,
1508 promise
1509 });
1510 }
1511
1512 get scrollMode() {
1513 return this._scrollMode;
1514 }
1515
1516 set scrollMode(mode) {
1517 if (this._scrollMode === mode) {
1518 return;
1519 }
1520
1521 if (!(0, _ui_utils.isValidScrollMode)(mode)) {
1522 throw new Error(`Invalid scroll mode: ${mode}`);
1523 }
1524
1525 if (this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
1526 return;
1527 }
1528
1529 this._previousScrollMode = this._scrollMode;
1530 this._scrollMode = mode;
1531 this.eventBus.dispatch("scrollmodechanged", {
1532 source: this,
1533 mode
1534 });
1535
1536 this._updateScrollMode(this._currentPageNumber);
1537 }
1538
1539 _updateScrollMode(pageNumber = null) {
1540 const scrollMode = this._scrollMode,
1541 viewer = this.viewer;
1542 viewer.classList.toggle("scrollHorizontal", scrollMode === _ui_utils.ScrollMode.HORIZONTAL);
1543 viewer.classList.toggle("scrollWrapped", scrollMode === _ui_utils.ScrollMode.WRAPPED);
1544
1545 if (!this.pdfDocument || !pageNumber) {
1546 return;
1547 }
1548
1549 if (scrollMode === _ui_utils.ScrollMode.PAGE) {
1550 this.#ensurePageViewVisible();
1551 } else if (this._previousScrollMode === _ui_utils.ScrollMode.PAGE) {
1552 this._updateSpreadMode();
1553 }
1554
1555 if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
1556 this._setScale(this._currentScaleValue, true);
1557 }
1558
1559 this._setCurrentPageNumber(pageNumber, true);
1560
1561 this.update();
1562 }
1563
1564 get spreadMode() {
1565 return this._spreadMode;
1566 }
1567
1568 set spreadMode(mode) {
1569 if (this._spreadMode === mode) {
1570 return;
1571 }
1572
1573 if (!(0, _ui_utils.isValidSpreadMode)(mode)) {
1574 throw new Error(`Invalid spread mode: ${mode}`);
1575 }
1576
1577 this._spreadMode = mode;
1578 this.eventBus.dispatch("spreadmodechanged", {
1579 source: this,
1580 mode
1581 });
1582
1583 this._updateSpreadMode(this._currentPageNumber);
1584 }
1585
1586 _updateSpreadMode(pageNumber = null) {
1587 if (!this.pdfDocument) {
1588 return;
1589 }
1590
1591 const viewer = this.viewer,
1592 pages = this._pages;
1593
1594 if (this._scrollMode === _ui_utils.ScrollMode.PAGE) {
1595 this.#ensurePageViewVisible();
1596 } else {
1597 viewer.textContent = "";
1598
1599 if (this._spreadMode === _ui_utils.SpreadMode.NONE) {
1600 for (const pageView of this._pages) {
1601 viewer.append(pageView.div);
1602 }
1603 } else {
1604 const parity = this._spreadMode - 1;
1605 let spread = null;
1606
1607 for (let i = 0, ii = pages.length; i < ii; ++i) {
1608 if (spread === null) {
1609 spread = document.createElement("div");
1610 spread.className = "spread";
1611 viewer.append(spread);
1612 } else if (i % 2 === parity) {
1613 spread = spread.cloneNode(false);
1614 viewer.append(spread);
1615 }
1616
1617 spread.append(pages[i].div);
1618 }
1619 }
1620 }
1621
1622 if (!pageNumber) {
1623 return;
1624 }
1625
1626 if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
1627 this._setScale(this._currentScaleValue, true);
1628 }
1629
1630 this._setCurrentPageNumber(pageNumber, true);
1631
1632 this.update();
1633 }
1634
1635 _getPageAdvance(currentPageNumber, previous = false) {
1636 switch (this._scrollMode) {
1637 case _ui_utils.ScrollMode.WRAPPED:
1638 {
1639 const {
1640 views
1641 } = this._getVisiblePages(),
1642 pageLayout = new Map();
1643
1644 for (const {
1645 id,
1646 y,
1647 percent,
1648 widthPercent
1649 } of views) {
1650 if (percent === 0 || widthPercent < 100) {
1651 continue;
1652 }
1653
1654 let yArray = pageLayout.get(y);
1655
1656 if (!yArray) {
1657 pageLayout.set(y, yArray ||= []);
1658 }
1659
1660 yArray.push(id);
1661 }
1662
1663 for (const yArray of pageLayout.values()) {
1664 const currentIndex = yArray.indexOf(currentPageNumber);
1665
1666 if (currentIndex === -1) {
1667 continue;
1668 }
1669
1670 const numPages = yArray.length;
1671
1672 if (numPages === 1) {
1673 break;
1674 }
1675
1676 if (previous) {
1677 for (let i = currentIndex - 1, ii = 0; i >= ii; i--) {
1678 const currentId = yArray[i],
1679 expectedId = yArray[i + 1] - 1;
1680
1681 if (currentId < expectedId) {
1682 return currentPageNumber - expectedId;
1683 }
1684 }
1685 } else {
1686 for (let i = currentIndex + 1, ii = numPages; i < ii; i++) {
1687 const currentId = yArray[i],
1688 expectedId = yArray[i - 1] + 1;
1689
1690 if (currentId > expectedId) {
1691 return expectedId - currentPageNumber;
1692 }
1693 }
1694 }
1695
1696 if (previous) {
1697 const firstId = yArray[0];
1698
1699 if (firstId < currentPageNumber) {
1700 return currentPageNumber - firstId + 1;
1701 }
1702 } else {
1703 const lastId = yArray[numPages - 1];
1704
1705 if (lastId > currentPageNumber) {
1706 return lastId - currentPageNumber + 1;
1707 }
1708 }
1709
1710 break;
1711 }
1712
1713 break;
1714 }
1715
1716 case _ui_utils.ScrollMode.HORIZONTAL:
1717 {
1718 break;
1719 }
1720
1721 case _ui_utils.ScrollMode.PAGE:
1722 case _ui_utils.ScrollMode.VERTICAL:
1723 {
1724 if (this._spreadMode === _ui_utils.SpreadMode.NONE) {
1725 break;
1726 }
1727
1728 const parity = this._spreadMode - 1;
1729
1730 if (previous && currentPageNumber % 2 !== parity) {
1731 break;
1732 } else if (!previous && currentPageNumber % 2 === parity) {
1733 break;
1734 }
1735
1736 const {
1737 views
1738 } = this._getVisiblePages(),
1739 expectedId = previous ? currentPageNumber - 1 : currentPageNumber + 1;
1740
1741 for (const {
1742 id,
1743 percent,
1744 widthPercent
1745 } of views) {
1746 if (id !== expectedId) {
1747 continue;
1748 }
1749
1750 if (percent > 0 && widthPercent === 100) {
1751 return 2;
1752 }
1753
1754 break;
1755 }
1756
1757 break;
1758 }
1759 }
1760
1761 return 1;
1762 }
1763
1764 nextPage() {
1765 const currentPageNumber = this._currentPageNumber,
1766 pagesCount = this.pagesCount;
1767
1768 if (currentPageNumber >= pagesCount) {
1769 return false;
1770 }
1771
1772 const advance = this._getPageAdvance(currentPageNumber, false) || 1;
1773 this.currentPageNumber = Math.min(currentPageNumber + advance, pagesCount);
1774 return true;
1775 }
1776
1777 previousPage() {
1778 const currentPageNumber = this._currentPageNumber;
1779
1780 if (currentPageNumber <= 1) {
1781 return false;
1782 }
1783
1784 const advance = this._getPageAdvance(currentPageNumber, true) || 1;
1785 this.currentPageNumber = Math.max(currentPageNumber - advance, 1);
1786 return true;
1787 }
1788
1789 increaseScale(steps = 1) {
1790 let newScale = this._currentScale;
1791
1792 do {
1793 newScale = (newScale * _ui_utils.DEFAULT_SCALE_DELTA).toFixed(2);
1794 newScale = Math.ceil(newScale * 10) / 10;
1795 newScale = Math.min(_ui_utils.MAX_SCALE, newScale);
1796 } while (--steps > 0 && newScale < _ui_utils.MAX_SCALE);
1797
1798 this.currentScaleValue = newScale;
1799 }
1800
1801 decreaseScale(steps = 1) {
1802 let newScale = this._currentScale;
1803
1804 do {
1805 newScale = (newScale / _ui_utils.DEFAULT_SCALE_DELTA).toFixed(2);
1806 newScale = Math.floor(newScale * 10) / 10;
1807 newScale = Math.max(_ui_utils.MIN_SCALE, newScale);
1808 } while (--steps > 0 && newScale > _ui_utils.MIN_SCALE);
1809
1810 this.currentScaleValue = newScale;
1811 }
1812
1813 updateContainerHeightCss() {
1814 const height = this.container.clientHeight;
1815
1816 if (height !== this.#previousContainerHeight) {
1817 this.#previousContainerHeight = height;
1818
1819 _ui_utils.docStyle.setProperty("--viewer-container-height", `${height}px`);
1820 }
1821 }
1822
1823 get annotationEditorMode() {
1824 return this.#annotationEditorUIManager ? this.#annotationEditorMode : _pdf.AnnotationEditorType.DISABLE;
1825 }
1826
1827 set annotationEditorMode(mode) {
1828 if (!this.#annotationEditorUIManager) {
1829 throw new Error(`The AnnotationEditor is not enabled.`);
1830 }
1831
1832 if (this.#annotationEditorMode === mode) {
1833 return;
1834 }
1835
1836 if (!isValidAnnotationEditorMode(mode)) {
1837 throw new Error(`Invalid AnnotationEditor mode: ${mode}`);
1838 }
1839
1840 if (!this.pdfDocument) {
1841 return;
1842 }
1843
1844 this.#annotationEditorMode = mode;
1845 this.eventBus.dispatch("annotationeditormodechanged", {
1846 source: this,
1847 mode
1848 });
1849 this.#annotationEditorUIManager.updateMode(mode);
1850 }
1851
1852 set annotationEditorParams({
1853 type,
1854 value
1855 }) {
1856 if (!this.#annotationEditorUIManager) {
1857 throw new Error(`The AnnotationEditor is not enabled.`);
1858 }
1859
1860 this.#annotationEditorUIManager.updateParams(type, value);
1861 }
1862
1863}
1864
1865exports.BaseViewer = BaseViewer;
\No newline at end of file