diff --git a/app/src/asset/anno.ts b/app/src/asset/anno.ts index bd446ef75..47c8f7875 100644 --- a/app/src/asset/anno.ts +++ b/app/src/asset/anno.ts @@ -28,7 +28,7 @@ const rectAnno = (config: any, pdf: any, element: HTMLElement) => { // 右键 return; } - const canvasRect = pdf.pdfViewer._getCurrentVisiblePage().first.view.canvas.getBoundingClientRect(); + const canvasRect = pdf.pdfViewer._getVisiblePages().first.view.canvas.getBoundingClientRect(); const containerRet = config.mainContainer.getBoundingClientRect(); const mostLeft = canvasRect.left; const mostRight = canvasRect.right; diff --git a/app/src/asset/index.ts b/app/src/asset/index.ts index f693315a4..69c2f646f 100644 --- a/app/src/asset/index.ts +++ b/app/src/asset/index.ts @@ -322,7 +322,7 @@ export class Asset extends Model {
-
+

Enter the password to open this PDF file:

@@ -336,7 +336,7 @@ export class Asset extends Model {
-
+
${window.siyuan.languages.fileName}

-

@@ -427,6 +427,13 @@ export class Asset extends Model { ${window.siyuan.languages.remove}
+
+ + + + + +
`; // 初始化完成后需等待页签是否显示设置完成,才可以判断 pdf 是否能进行渲染 diff --git a/app/src/asset/pdf/annotation_layer_builder.js b/app/src/asset/pdf/annotation_layer_builder.js index 8d65c45d9..370756ad7 100644 --- a/app/src/asset/pdf/annotation_layer_builder.js +++ b/app/src/asset/pdf/annotation_layer_builder.js @@ -13,6 +13,15 @@ * limitations under the License. */ +/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */ +// eslint-disable-next-line max-len +/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */ +/** @typedef {import("./interfaces").IDownloadManager} IDownloadManager */ +/** @typedef {import("./interfaces").IL10n} IL10n */ +/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ +// eslint-disable-next-line max-len +/** @typedef {import("./textaccessibility.js").TextAccessibilityManager} TextAccessibilityManager */ + import { AnnotationLayer } from "./pdfjs"; import { NullL10n } from "./l10n_utils.js"; @@ -33,6 +42,7 @@ import { NullL10n } from "./l10n_utils.js"; * [fieldObjectsPromise] * @property {Object} [mouseState] * @property {Map} [annotationCanvasMap] + * @property {TextAccessibilityManager} accessibilityManager */ class AnnotationLayerBuilder { @@ -53,6 +63,7 @@ class AnnotationLayerBuilder { fieldObjectsPromise = null, mouseState = null, annotationCanvasMap = null, + accessibilityManager = null, }) { this.pageDiv = pageDiv; this.pdfPage = pdfPage; @@ -67,6 +78,7 @@ class AnnotationLayerBuilder { this._fieldObjectsPromise = fieldObjectsPromise; this._mouseState = mouseState; this._annotationCanvasMap = annotationCanvasMap; + this._accessibilityManager = accessibilityManager; this.div = null; this._cancelled = false; @@ -105,6 +117,7 @@ class AnnotationLayerBuilder { fieldObjects, mouseState: this._mouseState, annotationCanvasMap: this._annotationCanvasMap, + accessibilityManager: this._accessibilityManager, }; if (this.div) { @@ -116,7 +129,7 @@ class AnnotationLayerBuilder { // if there is at least one annotation. this.div = document.createElement("div"); this.div.className = "annotationLayer"; - this.pageDiv.appendChild(this.div); + this.pageDiv.append(this.div); parameters.div = this.div; AnnotationLayer.render(parameters); diff --git a/app/src/asset/pdf/app.js b/app/src/asset/pdf/app.js index a73104124..e6a6899a9 100644 --- a/app/src/asset/pdf/app.js +++ b/app/src/asset/pdf/app.js @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals PDFBug, Stats */ import { animationStarted, @@ -35,9 +34,8 @@ import { SpreadMode, TextLayerMode, } from './ui_utils.js' -import { AppOptions, OptionKind } from './app_options.js' -import { AutomationEventBus, EventBus } from './event_utils.js' import { + AnnotationEditorType, build, createPromiseCapability, getDocument, @@ -55,8 +53,11 @@ import { UNSUPPORTED_FEATURES, version, } from './pdfjs' +import { AppOptions, OptionKind } from './app_options.js' +import { AutomationEventBus, EventBus } from './event_utils.js' import { CursorTool, PDFCursorTools } from './pdf_cursor_tools.js' import { LinkTarget, PDFLinkService } from './pdf_link_service.js' +import { AnnotationEditorParams } from './annotation_editor_params.js' import { OverlayManager } from './overlay_manager.js' import { PasswordPrompt } from './password_prompt.js' import { PDFAttachmentViewer } from './pdf_attachment_viewer.js' @@ -194,6 +195,8 @@ class PDFViewerApplication { this.eventBus = null /** @type {IL10n} */ this.l10n = null + /** @type {AnnotationEditorParams} */ + this.annotationEditorParams = null this.isInitialViewSet = false this.downloadComplete = false this.isViewerEmbedded = window.parent !== window @@ -209,7 +212,10 @@ class PDFViewerApplication { this._saveInProgress = false this._docStats = null this._wheelUnusedTicks = 0 - this._idleCallbacks = new Set() + this._PDFBug = null + this._hasAnnotationEditors = false + this._title = document.title + this._printAnnotationStoragePromise = null } // Called once when the document is loaded. @@ -279,11 +285,15 @@ class PDFViewerApplication { if (!hash) { return } - const params = parseQueryString(hash), - waitOn = [] + const {mainContainer, viewerContainer} = this.appConfig, + params = parseQueryString(hash) if (params.get('disableworker') === 'true') { - waitOn.push(loadFakeWorker()) + try { + await loadFakeWorker() + } catch (ex) { + console.error(`_parseHashParameters: "${ex.message}".`) + } } if (params.has('disablerange')) { AppOptions.set('disableRange', params.get('disablerange') === 'true') @@ -317,8 +327,13 @@ class PDFViewerApplication { case 'visible': case 'shadow': case 'hover': - const viewer = this.appConfig.viewerContainer - viewer.classList.add(`textLayer-${params.get('textlayer')}`) + viewerContainer.classList.add(`textLayer-${params.get('textlayer')}`) + try { + await loadPDFBug(this) + this._PDFBug.loadCSS() + } catch (ex) { + console.error(`_parseHashParameters: "${ex.message}".`) + } break } } @@ -327,7 +342,12 @@ class PDFViewerApplication { AppOptions.set('fontExtraProperties', true) const enabled = params.get('pdfbug').split(',') - waitOn.push(initPDFBug(enabled, this)) + try { + await loadPDFBug(this) + this._PDFBug.init({OPS}, mainContainer, enabled) + } catch (ex) { + console.error(`_parseHashParameters: "${ex.message}".`) + } } // It is not possible to change locale for the (various) extension builds. if ( @@ -337,15 +357,6 @@ class PDFViewerApplication { ) { AppOptions.set('locale', params.get('locale')) } - - if (waitOn.length === 0) { - return - } - try { - await Promise.all(waitOn) - } catch (reason) { - console.error(`_parseHashParameters: "${reason.message}".`) - } } /** @@ -435,8 +446,18 @@ class PDFViewerApplication { }) this.pdfScriptingManager = pdfScriptingManager - const container = appConfig.mainContainer - const viewer = appConfig.viewerContainer + const container = appConfig.mainContainer, + viewer = appConfig.viewerContainer + const annotationEditorMode = AppOptions.get('annotationEditorMode') + const pageColors = + AppOptions.get('forcePageColors') || + window.matchMedia('(forced-colors: active)').matches + ? { + background: AppOptions.get('pageColorsBackground'), + foreground: AppOptions.get('pageColorsForeground'), + } + : null + this.pdfViewer = new PDFViewer({ container, viewer, @@ -447,15 +468,21 @@ class PDFViewerApplication { findController, scriptingManager: AppOptions.get('enableScripting') && pdfScriptingManager, - renderer: AppOptions.get('renderer'), + renderer: + typeof PDFJSDev === 'undefined' || + PDFJSDev.test('!PRODUCTION || GENERIC') + ? AppOptions.get('renderer') + : null, l10n: this.l10n, textLayerMode: AppOptions.get('textLayerMode'), annotationMode: AppOptions.get('annotationMode'), + annotationEditorMode, imageResourcesPath: AppOptions.get('imageResourcesPath'), enablePrintAutoRotate: AppOptions.get('enablePrintAutoRotate'), useOnlyCssZoom: AppOptions.get('useOnlyCssZoom'), maxCanvasPixels: AppOptions.get('maxCanvasPixels'), enablePermissions: AppOptions.get('enablePermissions'), + pageColors, }) pdfRenderingQueue.setViewer(this.pdfViewer) pdfLinkService.setViewer(this.pdfViewer) @@ -467,6 +494,7 @@ class PDFViewerApplication { renderingQueue: pdfRenderingQueue, linkService: pdfLinkService, l10n: this.l10n, + pageColors, }) pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer) @@ -484,11 +512,28 @@ class PDFViewerApplication { this.findBar = new PDFFindBar(appConfig.findBar, eventBus, this.l10n) } + if (annotationEditorMode !== AnnotationEditorType.DISABLE) { + this.annotationEditorParams = new AnnotationEditorParams( + appConfig.annotationEditorParams, + eventBus, + ) + } else { + for (const element of [ + document.getElementById('editorModeButtons'), + document.getElementById('editorModeSeparator'), + ]) { + element.hidden = true + } + } + this.pdfDocumentProperties = new PDFDocumentProperties( appConfig.documentProperties, this.overlayManager, eventBus, this.l10n, + /* fileNameLookup = */ () => { + return this._docFilename + }, ) this.pdfCursorTools = new PDFCursorTools({ @@ -501,8 +546,8 @@ class PDFViewerApplication { this.secondaryToolbar = new SecondaryToolbar( appConfig.secondaryToolbar, - container, eventBus, + this.externalServices, ) if (this.supportsFullscreen) { @@ -556,9 +601,7 @@ class PDFViewerApplication { run (config) { // NOTE - this.initialize(config).then(() => { - webViewerInitialized(this) - }) + this.initialize(config).then(webViewerInitialized(this)) } get initialized () { @@ -619,7 +662,7 @@ class PDFViewerApplication { } get loadingBar () { - const bar = new ProgressBar(this.appConfig.appContainer) + const bar = new ProgressBar('loadingBar') return shadow(this, 'loadingBar', bar) } @@ -679,12 +722,16 @@ class PDFViewerApplication { this.setTitle(title) } - setTitle (title) { + setTitle (title = this._title) { + this._title = title + if (this.isViewerEmbedded) { // Embedded PDF viewers should not be changing their parent page's title. return } - // NOTE document.title = title + const editorIndicator = + this._hasAnnotationEditors && !this.pdfRenderingQueue.printing + // NOTE document.title = `${editorIndicator ? '* ' : ''}${title}` } get _docFilename () { @@ -698,22 +745,7 @@ class PDFViewerApplication { */ _hideViewBookmark () { // URL does not reflect proper document location - hiding some buttons. - const {toolbar, secondaryToolbar} = this.appConfig - toolbar.viewBookmark.hidden = true - secondaryToolbar.viewBookmarkButton.hidden = true - } - - /** - * @private - */ - _cancelIdleCallbacks () { - if (!this._idleCallbacks.size) { - return - } - for (const callback of this._idleCallbacks) { - window.cancelIdleCallback(callback) - } - this._idleCallbacks.clear() + this.appConfig.secondaryToolbar.viewBookmarkButton.hidden = true } /** @@ -740,7 +772,7 @@ class PDFViewerApplication { ) { try { // Trigger saving, to prevent data loss in forms; see issue 12257. - await this.save({sourceEventType: 'save'}) + await this.save() } catch (reason) { // Ignoring errors, to ensure that document closing won't break. } @@ -771,10 +803,11 @@ class PDFViewerApplication { this._contentLength = null this._saveInProgress = false this._docStats = null + this._hasAnnotationEditors = false - this._cancelIdleCallbacks() promises.push(this.pdfScriptingManager.destroyPromise) + this.setTitle() this.pdfSidebar.reset() this.pdfOutlineViewer.reset() this.pdfAttachmentViewer.reset() @@ -784,10 +817,8 @@ class PDFViewerApplication { this.findBar?.reset() this.toolbar.reset() this.secondaryToolbar.reset() + this._PDFBug?.cleanup() - if (typeof PDFBug !== 'undefined') { - PDFBug.cleanup() - } await Promise.all(promises) } @@ -869,6 +900,7 @@ class PDFViewerApplication { return undefined // Ignore errors for previously opened PDF files. } + // NOTE let key = 'loadingError' if (reason instanceof InvalidPDFException) { key = 'invalidFileError' @@ -894,7 +926,7 @@ class PDFViewerApplication { throw new Error('PDF document not downloaded.') } - async download ({sourceEventType = 'download'} = {}) { + async download () { const url = this._downloadUrl, filename = this._docFilename try { @@ -903,7 +935,7 @@ class PDFViewerApplication { const data = await this.pdfDocument.getData() const blob = new Blob([data], {type: 'application/pdf'}) - await this.downloadManager.download(blob, url, filename, sourceEventType) + await this.downloadManager.download(blob, url, filename) } catch (reason) { // When the PDF document isn't ready, or the PDF file is still // downloading, simply download using the URL. @@ -911,7 +943,7 @@ class PDFViewerApplication { } } - async save ({sourceEventType = 'download'} = {}) { + async save () { if (this._saveInProgress) { return } @@ -926,23 +958,30 @@ class PDFViewerApplication { const data = await this.pdfDocument.saveDocument() const blob = new Blob([data], {type: 'application/pdf'}) - await this.downloadManager.download(blob, url, filename, sourceEventType) + await this.downloadManager.download(blob, url, filename) } catch (reason) { // When the PDF document isn't ready, or the PDF file is still // downloading, simply fallback to a "regular" download. console.error(`Error when saving the document: ${reason.message}`) - await this.download({sourceEventType}) + await this.download() } finally { await this.pdfScriptingManager.dispatchDidSave() this._saveInProgress = false } + + if (this._hasAnnotationEditors) { + this.externalServices.reportTelemetry({ + type: 'editing', + data: {type: 'save'}, + }) + } } - downloadOrSave (options) { + downloadOrSave () { if (this.pdfDocument?.annotationStorage.size > 0) { - this.save(options) + this.save() } else { - this.download(options) + this.download() } } @@ -978,6 +1017,7 @@ class PDFViewerApplication { * optionally a 'stack' property. */ _otherError (message, moreInfo = null) { + // NOTE const moreInfoText = [ `PDF.js v${version || '?'} (build: ${build || '?'})`, ] @@ -1049,31 +1089,33 @@ class PDFViewerApplication { // that we discard some of the loaded data. This can cause the loading // bar to move backwards. So prevent this by only updating the bar if it // increases. - if (percent > this.loadingBar.percent || isNaN(percent)) { - this.loadingBar.percent = percent - - // When disableAutoFetch is enabled, it's not uncommon for the entire file - // to never be fetched (depends on e.g. the file structure). In this case - // the loading bar will not be completely filled, nor will it be hidden. - // To prevent displaying a partially filled loading bar permanently, we - // hide it when no data has been loaded during a certain amount of time. - const disableAutoFetch = this.pdfDocument - ? this.pdfDocument.loadingParams.disableAutoFetch - : AppOptions.get('disableAutoFetch') - - if (disableAutoFetch && percent) { - if (this.disableAutoFetchLoadingBarTimeout) { - clearTimeout(this.disableAutoFetchLoadingBarTimeout) - this.disableAutoFetchLoadingBarTimeout = null - } - this.loadingBar.show() - - this.disableAutoFetchLoadingBarTimeout = setTimeout(() => { - this.loadingBar.hide() - this.disableAutoFetchLoadingBarTimeout = null - }, DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT) - } + if (percent <= this.loadingBar.percent) { + return } + this.loadingBar.percent = percent + + // When disableAutoFetch is enabled, it's not uncommon for the entire file + // to never be fetched (depends on e.g. the file structure). In this case + // the loading bar will not be completely filled, nor will it be hidden. + // To prevent displaying a partially filled loading bar permanently, we + // hide it when no data has been loaded during a certain amount of time. + const disableAutoFetch = + this.pdfDocument?.loadingParams.disableAutoFetch ?? + AppOptions.get('disableAutoFetch') + + if (!disableAutoFetch || isNaN(percent)) { + return + } + if (this.disableAutoFetchLoadingBarTimeout) { + clearTimeout(this.disableAutoFetchLoadingBarTimeout) + this.disableAutoFetchLoadingBarTimeout = null + } + this.loadingBar.show() + + this.disableAutoFetchLoadingBarTimeout = setTimeout(() => { + this.loadingBar.hide() + this.disableAutoFetchLoadingBarTimeout = null + }, DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT) } load (pdfDocument) { @@ -1113,7 +1155,7 @@ class PDFViewerApplication { baseDocumentUrl = location.href.split('#')[0] } this.pdfLinkService.setDocument(pdfDocument, baseDocumentUrl) - this.pdfDocumentProperties.setDocument(pdfDocument, this.url) + this.pdfDocumentProperties.setDocument(pdfDocument) const pdfViewer = this.pdfViewer pdfViewer.setDocument(pdfDocument) @@ -1291,19 +1333,6 @@ class PDFViewerApplication { } this.pdfLayerViewer.render({optionalContentConfig, pdfDocument}) }) - if ( - (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) || - 'requestIdleCallback' in window - ) { - const callback = window.requestIdleCallback( - () => { - this._collectTelemetry(pdfDocument) - this._idleCallbacks.delete(callback) - }, - {timeout: 1000}, - ) - this._idleCallbacks.add(callback) - } }) this._initializePageLabels(pdfDocument) @@ -1350,23 +1379,6 @@ class PDFViewerApplication { } } - /** - * A place to fetch data for telemetry after one page is rendered and the - * viewer is idle. - * @private - */ - async _collectTelemetry (pdfDocument) { - const markInfo = await this.pdfDocument.getMarkInfo() - if (pdfDocument !== this.pdfDocument) { - return // Document was closed while waiting for mark info. - } - const tagged = markInfo?.Marked || false - this.externalServices.reportTelemetry({ - type: 'tagged', - tagged, - }) - } - /** * @private */ @@ -1427,21 +1439,21 @@ class PDFViewerApplication { this._contentLength ??= contentLength // See `getDownloadInfo`-call above. // Provides some basic debug information + // NOTE // console.log( // `PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` + // `${(info.Producer || '-').trim()} / ${(info.Creator || '-').trim()}] ` + // `(PDF.js: ${version || '-'})`, // ) - let pdfTitle = info?.Title + let pdfTitle = info.Title const metadataTitle = metadata?.get('dc:title') if (metadataTitle) { // Ghostscript can produce invalid 'dc:title' Metadata entries: // - The title may be "Untitled" (fixes bug 1031612). // - The title may contain incorrectly encoded characters, which thus - // looks broken, hence we ignore the Metadata entry when it - // contains characters from the Specials Unicode block - // (fixes bug 1605526). + // looks broken, hence we ignore the Metadata entry when it contains + // characters from the Specials Unicode block (fixes bug 1605526). if ( metadataTitle !== 'Untitled' && !/[\uFFF0-\uFFFF]/g.test(metadataTitle) @@ -1451,10 +1463,10 @@ class PDFViewerApplication { } if (pdfTitle) { this.setTitle( - `${pdfTitle} - ${contentDispositionFilename || document.title}`, + `${pdfTitle} - ${this._contentDispositionFilename || this._title}`, ) - } else if (contentDispositionFilename) { - this.setTitle(contentDispositionFilename) + } else if (this._contentDispositionFilename) { + this.setTitle(this._contentDispositionFilename) } if ( @@ -1606,12 +1618,23 @@ class PDFViewerApplication { } } annotationStorage.onResetModified = () => { - // NOTE window.removeEventListener('beforeunload', beforeUnload) + // NOTE window.removeEventListener('beforeunload', beforeUnload) if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { delete this._annotationStorageModified } } + annotationStorage.onAnnotationEditor = typeStr => { + this._hasAnnotationEditors = !!typeStr + this.setTitle() + + if (typeStr) { + this.externalServices.reportTelemetry({ + type: 'editing', + data: {type: typeStr}, + }) + } + } } setInitialView ( @@ -1673,23 +1696,34 @@ class PDFViewerApplication { this.pdfViewer.cleanup() this.pdfThumbnailViewer.cleanup() - // We don't want to remove fonts used by active page SVGs. - this.pdfDocument.cleanup( - /* keepLoadedFonts = */ this.pdfViewer.renderer === RendererType.SVG, - ) + if ( + typeof PDFJSDev === 'undefined' || + PDFJSDev.test('!PRODUCTION || GENERIC') + ) { + // We don't want to remove fonts used by active page SVGs. + this.pdfDocument.cleanup( + /* keepLoadedFonts = */ this.pdfViewer.renderer === RendererType.SVG, + ) + } else { + this.pdfDocument.cleanup() + } } forceRendering () { this.pdfRenderingQueue.printing = !!this.printService this.pdfRenderingQueue.isThumbnailViewEnabled = - this.pdfSidebar.isThumbnailViewVisible + this.pdfSidebar.visibleView === SidebarView.THUMBS this.pdfRenderingQueue.renderHighestPriority() } beforePrint () { - // Given that the "beforeprint" browser event is synchronous, we - // unfortunately cannot await the scripting event dispatching here. - this.pdfScriptingManager.dispatchWillPrint() + this._printAnnotationStoragePromise = this.pdfScriptingManager.dispatchWillPrint(). + catch(() => { + /* Avoid breaking printing; ignoring errors. */ + }). + then(() => { + return this.pdfDocument?.annotationStorage.print + }) if (this.printService) { // There is no way to suppress beforePrint/afterPrint events, @@ -1699,6 +1733,7 @@ class PDFViewerApplication { } if (!this.supportsPrinting) { + // NOTE this._otherError(window.siyuan.languages.printingNotSupported) return } @@ -1706,6 +1741,7 @@ class PDFViewerApplication { // The beforePrint is a sync method and we need to know layout before // returning from this method. Ensure that we can get sizes of the pages. if (!this.pdfViewer.pageViewsReady) { + // NOTE window.alert(window.siyuan.languages.printingNotReady) return } @@ -1722,22 +1758,35 @@ class PDFViewerApplication { printContainer, printResolution, optionalContentConfigPromise, + this._printAnnotationStoragePromise, this.l10n, ) this.printService = printService this.forceRendering() + // Disable the editor-indicator during printing (fixes bug 1790552). + this.setTitle() printService.layout() this.externalServices.reportTelemetry({ type: 'print', }) + + if (this._hasAnnotationEditors) { + this.externalServices.reportTelemetry({ + type: 'editing', + data: {type: 'print'}, + }) + } } afterPrint () { - // Given that the "afterprint" browser event is synchronous, we - // unfortunately cannot await the scripting event dispatching here. - this.pdfScriptingManager.dispatchDidPrint() + if (this._printAnnotationStoragePromise) { + this._printAnnotationStoragePromise.then(() => { + this.pdfScriptingManager.dispatchDidPrint() + }) + this._printAnnotationStoragePromise = null + } if (this.printService) { this.printService.destroy() @@ -1746,6 +1795,8 @@ class PDFViewerApplication { this.pdfDocument?.annotationStorage.resetModified() } this.forceRendering() + // Re-enable the editor-indicator after printing (fixes bug 1790552). + this.setTitle() } rotatePages (delta) { @@ -1771,6 +1822,7 @@ class PDFViewerApplication { _boundEvents.beforePrint = this.beforePrint.bind(this) _boundEvents.afterPrint = this.afterPrint.bind(this) + eventBus._on('resize', webViewerResize) eventBus._on('hashchange', webViewerHashchange) eventBus._on('beforeprint', _boundEvents.beforePrint) eventBus._on('afterprint', _boundEvents.afterPrint) @@ -1784,9 +1836,16 @@ class PDFViewerApplication { eventBus._on('namedaction', webViewerNamedAction) eventBus._on('presentationmodechanged', webViewerPresentationModeChanged) eventBus._on('presentationmode', webViewerPresentationMode) + eventBus._on( + 'switchannotationeditormode', + webViewerSwitchAnnotationEditorMode, + ) + eventBus._on( + 'switchannotationeditorparams', + webViewerSwitchAnnotationEditorParams, + ) eventBus._on('print', webViewerPrint) eventBus._on('download', webViewerDownload) - eventBus._on('save', webViewerSave) eventBus._on('firstpage', webViewerFirstPage) eventBus._on('lastpage', webViewerLastPage) eventBus._on('nextpage', webViewerNextPage) @@ -1818,12 +1877,39 @@ class PDFViewerApplication { eventBus._on('fileinputchange', webViewerFileInputChange) eventBus._on('openfile', webViewerOpenFile) } + if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) { + eventBus._on( + 'annotationeditorstateschanged', + webViewerAnnotationEditorStatesChanged, + ) + } } bindWindowEvents () { - this.unbindWindowEvents() const {eventBus, _boundEvents} = this + function addWindowResolutionChange (evt = null) { + if (evt) { + webViewerResolutionChange(evt) + } + const mediaQueryList = window.matchMedia( + `(resolution: ${window.devicePixelRatio || 1}dppx)`, + ) + mediaQueryList.addEventListener('change', addWindowResolutionChange, { + once: true, + }) + + if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) { + return + } + _boundEvents.removeWindowResolutionChange ||= function () { + mediaQueryList.removeEventListener('change', addWindowResolutionChange) + _boundEvents.removeWindowResolutionChange = null + } + } + + addWindowResolutionChange() + _boundEvents.windowResize = () => { eventBus.dispatch('resize', {source: window}) } @@ -1864,8 +1950,12 @@ class PDFViewerApplication { } unbindEvents () { + if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) { + throw new Error('Not implemented: unbindEvents') + } const {eventBus, _boundEvents} = this + eventBus._off('resize', webViewerResize) eventBus._off('hashchange', webViewerHashchange) eventBus._off('beforeprint', _boundEvents.beforePrint) eventBus._off('afterprint', _boundEvents.afterPrint) @@ -1881,7 +1971,6 @@ class PDFViewerApplication { eventBus._off('presentationmode', webViewerPresentationMode) eventBus._off('print', webViewerPrint) eventBus._off('download', webViewerDownload) - eventBus._off('save', webViewerSave) eventBus._off('firstpage', webViewerFirstPage) eventBus._off('lastpage', webViewerLastPage) eventBus._off('nextpage', webViewerNextPage) @@ -1919,6 +2008,9 @@ class PDFViewerApplication { } unbindWindowEvents () { + if (typeof PDFJSDev !== 'undefined' && PDFJSDev.test('MOZCENTRAL')) { + throw new Error('Not implemented: unbindWindowEvents') + } const {_boundEvents} = this window.removeEventListener('visibilitychange', webViewerVisibilityChange) @@ -1937,6 +2029,7 @@ class PDFViewerApplication { _boundEvents.windowUpdateFromSandbox, ) + _boundEvents.removeWindowResolutionChange?.() _boundEvents.windowResize = null _boundEvents.windowHashChange = null _boundEvents.windowBeforePrint = null @@ -1966,9 +2059,8 @@ class PDFViewerApplication { * @private */ _unblockDocumentLoadEvent () { - if (document.blockUnblockOnload) { - document.blockUnblockOnload(false) - } + document.blockUnblockOnload?.(false) + // Ensure that this method is only ever run once. this._unblockDocumentLoadEvent = () => {} } @@ -1997,15 +2089,15 @@ class PDFViewerApplication { } } -let validateFileURL if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { const HOSTED_VIEWER_ORIGINS = [ 'null', 'http://mozilla.github.io', 'https://mozilla.github.io', ] - validateFileURL = function (file) { - if (file === undefined) { + // eslint-disable-next-line no-var + var validateFileURL = function (file) { + if (!file) { return } try { @@ -2022,6 +2114,7 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { throw new Error('file origin does not match viewer\'s') } } catch (ex) { + // NOTE console.log(window.siyuan.languages.loadingError, ex.message) throw ex } @@ -2029,155 +2122,140 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { } async function loadFakeWorker () { - if (!GlobalWorkerOptions.workerSrc) { - GlobalWorkerOptions.workerSrc = AppOptions.get('workerSrc') - } + GlobalWorkerOptions.workerSrc ||= AppOptions.get('workerSrc') + if (typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION')) { + // NOTE window.pdfjsWorker = await import(`${Constants.PROTYLE_CDN}/js/pdf/pdf.worker.js?v=2.14.102`) return } await loadScript(PDFWorker.workerSrc) } -async function initPDFBug (enabledTabs, pdfInstance) { - const {debuggerScriptPath, mainContainer} = pdfInstance.appConfig - await loadScript(debuggerScriptPath) - PDFBug.init({OPS}, mainContainer, enabledTabs) +async function loadPDFBug (self) { + const {debuggerScriptPath} = self.appConfig + const {PDFBug} = + typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION') + ? await import(debuggerScriptPath) // eslint-disable-line no-unsanitized/method + : await __non_webpack_import__(debuggerScriptPath) // eslint-disable-line no-undef + + self._PDFBug = PDFBug } -function reportPageStatsPDFBug ({pageNumber, source}) { - const pdfInstance = getPdfInstance(source.div) - if (!pdfInstance || typeof Stats === 'undefined' || !Stats.enabled) { +function reportPageStatsPDFBug ({pageNumber}) { + if (!globalThis.Stats?.enabled) { return } const pageView = PDFViewerApplication.pdfViewer.getPageView( /* index = */ pageNumber - 1, ) - const pageStats = pageView?.pdfPage?.stats - if (!pageStats) { - return - } - Stats.add(pageNumber, pageStats) + globalThis.Stats.add(pageNumber, pageView?.pdfPage?.stats) } function webViewerInitialized (pdf) { - const appConfig = pdf.appConfig - let file - if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - validateFileURL(appConfig.file) - } else if (PDFJSDev.test('MOZCENTRAL')) { - file = window.location.href - } else if (PDFJSDev.test('CHROME')) { - file = AppOptions.get('defaultUrl') - } + // NOTE + const {appConfig, eventBus} = pdf + const file = appConfig.file + // NOTE + // if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { + // const queryString = document.location.search.substring(1) + // const params = parseQueryString(queryString) + // file = params.get('file') ?? AppOptions.get('defaultUrl') + // validateFileURL(appConfig.file) + // } else if (PDFJSDev.test('MOZCENTRAL')) { + // file = window.location.href + // } else if (PDFJSDev.test('CHROME')) { + // file = AppOptions.get('defaultUrl') + // } - if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - const fileInput = document.createElement('input') - fileInput.id = appConfig.openFileInputName - fileInput.className = 'fileInput' - fileInput.setAttribute('type', 'file') - fileInput.oncontextmenu = noContextMenuHandler - document.body.appendChild(fileInput) + // NOTE + // if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { + // const fileInput = appConfig.openFileInput + // fileInput.value = null + // + // fileInput.addEventListener('change', function (evt) { + // const {files} = evt.target + // if (!files || files.length === 0) { + // return + // } + // eventBus.dispatch('fileinputchange', { + // source: this, + // fileInput: evt.target, + // }) + // }) + // + // // Enable dragging-and-dropping a new PDF file onto the viewerContainer. + // appConfig.mainContainer.addEventListener('dragover', function (evt) { + // evt.preventDefault() + // + // evt.dataTransfer.dropEffect = + // evt.dataTransfer.effectAllowed === 'copy' ? 'copy' : 'move' + // }) + // appConfig.mainContainer.addEventListener('drop', function (evt) { + // evt.preventDefault() + // + // const {files} = evt.dataTransfer + // if (!files || files.length === 0) { + // return + // } + // eventBus.dispatch('fileinputchange', { + // source: this, + // fileInput: evt.dataTransfer, + // }) + // }) + // } - if ( - !window.File || - !window.FileReader || - !window.FileList || - !window.Blob - ) { - appConfig.toolbar.openFile.hidden = true - appConfig.secondaryToolbar.openFileButton.hidden = true - } else { - fileInput.value = null - } - - fileInput.addEventListener('change', function (evt) { - const files = evt.target.files - if (!files || files.length === 0) { - return - } - pdf.eventBus.dispatch('fileinputchange', { - source: this, - fileInput: evt.target, - }) - }) - - // Enable dragging-and-dropping a new PDF file onto the viewerContainer. - appConfig.mainContainer.addEventListener('dragover', function (evt) { - evt.preventDefault() - - evt.dataTransfer.dropEffect = 'move' - }) - appConfig.mainContainer.addEventListener('drop', function (evt) { - evt.preventDefault() - - const files = evt.dataTransfer.files - if (!files || files.length === 0) { - return - } - pdf.eventBus.dispatch('fileinputchange', { - source: this, - fileInput: evt.dataTransfer, - }) - }) - } else { - appConfig.toolbar.openFile.hidden = true - appConfig.secondaryToolbar.openFileButton.hidden = true + if (!pdf.supportsDocumentFonts) { + AppOptions.set('disableFontFace', true) + // NOTE + console.warn('Web fonts are disabled: unable to use embedded PDF fonts.') } if (!pdf.supportsPrinting) { + // NOTE appConfig.toolbar.print?.classList.add('fn__hidden') appConfig.secondaryToolbar.printButton?.classList.add('fn__hidden') } if (!pdf.supportsFullscreen) { - appConfig.toolbar.presentationModeButton.classList.add('fn__hidden') - appConfig.secondaryToolbar.presentationModeButton?.classList.add( - 'fn__hidden') + // NOTE + appConfig.secondaryToolbar.presentationModeButton?.classList.add('fn__hidden') } if (pdf.supportsIntegratedFind) { + // NOTE appConfig.toolbar.viewFind.classList.add('fn__hidden') } appConfig.mainContainer.addEventListener( 'transitionend', function (evt) { - if (evt.target === /* mainContainer */ this) { - pdf.eventBus.dispatch('resize', {source: this}) + if (evt.target === /* mainContainer */ this && eventBus) { + eventBus.dispatch('resize', {source: this}) } }, true, ) try { - webViewerOpenFileViaURL(pdf) + if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { + if (file) { + pdf.open(file) + } else { + pdf._hideViewBookmark() + } + } else if (PDFJSDev.test('MOZCENTRAL || CHROME')) { + pdf.setTitleUsingUrl(file, /* downloadUrl = */ file) + pdf.initPassiveLoading() + } else { + throw new Error('Not implemented: webViewerInitialized') + } } catch (reason) { - pdf._documentError(window.siyuan.languages.loadingError, - reason) - } -} - -function webViewerOpenFileViaURL (pdf) { - const file = pdf.appConfig.file - if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - if (file) { - pdf.open(file) - } else { - pdf._hideViewBookmark() - } - } else if (PDFJSDev.test('MOZCENTRAL || CHROME')) { - pdf.setTitleUsingUrl(file, /* downloadUrl = */ file) - pdf.initPassiveLoading() - } else { - if (file) { - throw new Error('Not implemented: webViewerOpenFileViaURL') - } else { - pdf._hideViewBookmark() - } + pdf._documentError(window.siyuan.languages.loadingError, reason) } } +// NOTE function webViewerPageRendered ({pageNumber, error, source}) { const pdfInstance = getPdfInstance(source.div) if (!pdfInstance) { @@ -2190,7 +2268,7 @@ function webViewerPageRendered ({pageNumber, error, source}) { } // Use the rendered page to set the corresponding thumbnail image. - if (pdfInstance.pdfSidebar.isThumbnailViewVisible) { + if (pdfInstance.pdfSidebar.visibleView === SidebarView.THUMBS) { const pageView = pdfInstance.pdfViewer.getPageView( /* index = */ pageNumber - 1, ) @@ -2211,6 +2289,7 @@ function webViewerPageRendered ({pageNumber, error, source}) { pdfInstance._reportDocumentStatsTelemetry() } +// NOTE function webViewerPageMode ({mode, source}) { const pdfInstance = getPdfInstance(source.externalLinkTarget) if (!pdfInstance) { @@ -2242,6 +2321,7 @@ function webViewerPageMode ({mode, source}) { pdfInstance.pdfSidebar.switchView(view, /* forceOpen = */ true) } +// NOTE function webViewerNamedAction (evt) { const pdfInstance = getPdfInstance(evt.source.externalLinkTarget) if (!pdfInstance) { @@ -2265,11 +2345,12 @@ function webViewerNamedAction (evt) { break case 'SaveAs': - webViewerSave() + pdfInstance.downloadOrSave() break } } +// NOTE function webViewerPresentationModeChanged (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2278,29 +2359,29 @@ function webViewerPresentationModeChanged (evt) { pdfInstance.pdfViewer.presentationModeState = evt.state } -function webViewerSidebarViewChanged (evt) { - const pdfInstance = getPdfInstance(evt.source.outerContainer) +// NOTE +function webViewerSidebarViewChanged ({view, source}) { + const pdfInstance = getPdfInstance(source.outerContainer) if (!pdfInstance) { return } pdfInstance.pdfRenderingQueue.isThumbnailViewEnabled = - pdfInstance.pdfSidebar.isThumbnailViewVisible + view === SidebarView.THUMBS if (pdfInstance.isInitialViewSet) { // Only update the storage when the document has been loaded *and* rendered. - pdfInstance.store?.set('sidebarView', evt.view).catch(() => { + pdfInstance.store?.set('sidebarView', view).catch(() => { // Unable to write to storage. }) } } -function webViewerUpdateViewarea (evt) { - const pdfInstance = getPdfInstance(evt.source.container) +// NOTE +function webViewerUpdateViewarea ({location, source}) { + const pdfInstance = getPdfInstance(source.container) if (!pdfInstance) { return } - const location = evt.location - if (pdfInstance.isInitialViewSet) { // Only update the storage when the document has been loaded *and* rendered. pdfInstance.store?.setMultiple({ @@ -2316,7 +2397,6 @@ function webViewerUpdateViewarea (evt) { const href = pdfInstance.pdfLinkService.getAnchorUrl( location.pdfOpenParams, ) - pdfInstance.appConfig.toolbar.viewBookmark.href = href pdfInstance.appConfig.secondaryToolbar.viewBookmarkButton.href = href @@ -2328,12 +2408,16 @@ function webViewerUpdateViewarea (evt) { pdfInstance.toolbar.updateLoadingIndicatorState(loading) } +// NOTE function webViewerScrollModeChanged (evt) { const pdfInstance = getPdfInstance(evt.source.container) if (!pdfInstance) { return } - if (pdfInstance.isInitialViewSet) { + if ( + pdfInstance.isInitialViewSet && + !pdfInstance.pdfViewer.isInPresentationMode + ) { // Only update the storage when the document has been loaded *and* rendered. pdfInstance.store?.set('scrollMode', evt.mode).catch(() => { // Unable to write to storage. @@ -2341,12 +2425,13 @@ function webViewerScrollModeChanged (evt) { } } +// NOTE function webViewerSpreadModeChanged (evt) { const pdfInstance = getPdfInstance(evt.source.container) if (!pdfInstance) { return } - if (pdfInstance.isInitialViewSet) { + if (pdfInstance.isInitialViewSet && !pdfInstance.pdfViewer.isInPresentationMode) { // Only update the storage when the document has been loaded *and* rendered. pdfInstance.store?.set('spreadMode', evt.mode).catch(() => { // Unable to write to storage. @@ -2354,11 +2439,37 @@ function webViewerSpreadModeChanged (evt) { } } +// NOTE +function webViewerResize () { + // const {pdfDocument, pdfViewer, pdfRenderingQueue} = PDFViewerApplication + // + // if (pdfRenderingQueue.printing && window.matchMedia('print').matches) { + // // Work-around issue 15324 by ignoring "resize" events during printing. + // return + // } + // pdfViewer.updateContainerHeightCss() + // + // if (!pdfDocument) { + // return + // } + // const currentScaleValue = pdfViewer.currentScaleValue + // if ( + // currentScaleValue === 'auto' || + // currentScaleValue === 'page-fit' || + // currentScaleValue === 'page-width' + // ) { + // // Note: the scale is constant for 'page-actual'. + // pdfViewer.currentScaleValue = currentScaleValue + // } + // pdfViewer.update() +} + function webViewerHashchange (evt) { const hash = evt.hash if (!hash) { return } + // NOTE const pdfInstance = getPdfInstance(evt.source) if (!pdfInstance) { return @@ -2370,9 +2481,10 @@ function webViewerHashchange (evt) { } } -let webViewerFileInputChange, webViewerOpenFile if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - webViewerFileInputChange = function (evt) { + // eslint-disable-next-line no-var + var webViewerFileInputChange = function (evt) { + // NOTE if (evt.source.pdfViewer?.isInPresentationMode) { return // Opening a new PDF file isn't supported in Presentation Mode. } @@ -2385,12 +2497,15 @@ if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { // NOTE PDFViewerApplication.open(url) } - webViewerOpenFile = function (evt) { - const openFileInputName = evt.pdfInstance.appConfig.openFileInputName - document.getElementById(openFileInputName).click() + // eslint-disable-next-line no-var + var webViewerOpenFile = function (evt) { + // NOTE + const fileInput = evt.pdfInstance.appConfig.openFileInput + fileInput.click() } } +// NOTE function webViewerPresentationMode ({source}) { const pdfInstance = getPdfInstance(source.toolbar) if (!pdfInstance) { @@ -2399,32 +2514,45 @@ function webViewerPresentationMode ({source}) { pdfInstance.requestPresentationMode() } -function webViewerPrint ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerSwitchAnnotationEditorMode (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) + if (!pdfInstance) { + return + } + pdfInstance.pdfViewer.annotationEditorMode = evt.mode +} + +// NOTE +function webViewerSwitchAnnotationEditorParams (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) + if (!pdfInstance) { + return + } + pdfInstance.pdfViewer.annotationEditorParams = evt +} + +// NOTE +function webViewerPrint (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } pdfInstance.triggerPrinting() } -function webViewerDownload ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerDownload (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } - pdfInstance.downloadOrSave({sourceEventType: 'download'}) + pdfInstance.downloadOrSave() } -function webViewerSave ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) - if (!pdfInstance) { - return - } - pdfInstance.downloadOrSave({sourceEventType: 'save'}) -} - -function webViewerFirstPage ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerFirstPage (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } @@ -2433,8 +2561,9 @@ function webViewerFirstPage ({source}) { } } -function webViewerLastPage ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerLastPage (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } @@ -2443,46 +2572,52 @@ function webViewerLastPage ({source}) { } } -function webViewerNextPage ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerNextPage (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } pdfInstance.pdfViewer.nextPage() } -function webViewerPreviousPage ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerPreviousPage (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } pdfInstance.pdfViewer.previousPage() } -function webViewerZoomIn ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerZoomIn (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } pdfInstance.zoomIn() } -function webViewerZoomOut ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerZoomOut (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } pdfInstance.zoomOut() } -function webViewerZoomReset ({source}) { - const pdfInstance = getPdfInstance(source.toolbar) +// NOTE +function webViewerZoomReset (evt) { + const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { return } pdfInstance.zoomReset() } +// NOTE function webViewerPageNumberChanged (evt) { let pdfInstance if (evt.pdfInstance) { @@ -2499,6 +2634,7 @@ function webViewerPageNumberChanged (evt) { if (evt.value !== '') { pdfInstance.pdfLinkService.goToPage(evt.value) } + // NOTE if (evt.id) { hlPDFRect(pdfInstance.pdfViewer.container, evt.id) } @@ -2515,6 +2651,7 @@ function webViewerPageNumberChanged (evt) { } } +// NOTE function webViewerScaleChanged (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2523,6 +2660,7 @@ function webViewerScaleChanged (evt) { pdfInstance.pdfViewer.currentScaleValue = evt.value } +// NOTE function webViewerRotateCw (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2531,6 +2669,7 @@ function webViewerRotateCw (evt) { pdfInstance.rotatePages(90) } +// NOTE function webViewerRotateCcw (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2539,6 +2678,7 @@ function webViewerRotateCcw (evt) { pdfInstance.rotatePages(-90) } +// NOTE function webViewerOptionalContentConfig (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2547,6 +2687,7 @@ function webViewerOptionalContentConfig (evt) { pdfInstance.pdfViewer.optionalContentConfigPromise = evt.promise } +// NOTE function webViewerSwitchScrollMode (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2555,6 +2696,7 @@ function webViewerSwitchScrollMode (evt) { pdfInstance.pdfViewer.scrollMode = evt.mode } +// NOTE function webViewerSwitchSpreadMode (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2563,6 +2705,7 @@ function webViewerSwitchSpreadMode (evt) { pdfInstance.pdfViewer.spreadMode = evt.mode } +// NOTE function webViewerDocumentProperties (evt) { const pdfInstance = getPdfInstance(evt.source.toolbar) if (!pdfInstance) { @@ -2571,6 +2714,7 @@ function webViewerDocumentProperties (evt) { pdfInstance.pdfDocumentProperties.open() } +// NOTE function webViewerFindFromUrlHash (evt) { const pdfInstance = getPdfInstance(evt.source.bar) if (!pdfInstance) { @@ -2589,6 +2733,7 @@ function webViewerFindFromUrlHash (evt) { }) } +// NOTE function webViewerUpdateFindMatchesCount ({matchesCount, source}) { const pdfInstance = getPdfInstance(source._linkService.pdfViewer.container) if (!pdfInstance) { @@ -2601,6 +2746,7 @@ function webViewerUpdateFindMatchesCount ({matchesCount, source}) { } } +// NOTE function webViewerUpdateFindControlState ({ state, previous, @@ -2608,9 +2754,6 @@ function webViewerUpdateFindControlState ({ rawQuery, source, }) { - if (!source.state) { - return - } const pdfInstance = getPdfInstance(source.state.source.bar) if (!pdfInstance) { return @@ -2627,6 +2770,7 @@ function webViewerUpdateFindControlState ({ } } +// NOTE function webViewerScaleChanging (evt) { const pdfInstance = getPdfInstance(evt.source.container) if (!pdfInstance) { @@ -2637,6 +2781,7 @@ function webViewerScaleChanging (evt) { pdfInstance.pdfViewer.update() } +// NOTE function webViewerRotationChanging (evt) { const pdfInstance = getPdfInstance(evt.source.container) if (!pdfInstance) { @@ -2649,6 +2794,7 @@ function webViewerRotationChanging (evt) { pdfInstance.pdfViewer.currentPageNumber = evt.pageNumber } +// NOTE function webViewerPageChanging ({pageNumber, pageLabel, source}) { const pdfInstance = getPdfInstance(source.container) if (!pdfInstance) { @@ -2657,11 +2803,20 @@ function webViewerPageChanging ({pageNumber, pageLabel, source}) { pdfInstance.toolbar.setPageNumber(pageNumber, pageLabel) pdfInstance.secondaryToolbar.setPageNumber(pageNumber) - if (pdfInstance.pdfSidebar.isThumbnailViewVisible) { + if (pdfInstance.pdfSidebar.visibleView === SidebarView.THUMBS) { pdfInstance.pdfThumbnailViewer.scrollThumbnailIntoView(pageNumber) } } +// NOTE +function webViewerResolutionChange (evt) { + const pdfInstance = getPdfInstance(evt.source.container) + if (!pdfInstance) { + return + } + pdfInstance.pdfViewer.refresh() +} + function webViewerVisibilityChange (evt) { if (document.visibilityState === 'visible') { // Ignore mouse wheel zooming during tab switches (bug 1503412). @@ -2680,6 +2835,7 @@ function setZoomDisabledTimeout () { }, WHEEL_ZOOM_DISABLED_TIMEOUT) } +// NOTE function webViewerWheel (evt) { const pdfInstance = getPdfInstance(evt.target) if (!pdfInstance) { @@ -2771,6 +2927,7 @@ function webViewerTouchStart (evt) { } } +// NOTE function webViewerClick (evt) { const pdfInstance = getPdfInstance(evt.target) if (!pdfInstance) { @@ -2783,17 +2940,21 @@ function webViewerClick (evt) { if ( pdfInstance.pdfViewer.containsElement(evt.target) || (appConfig.toolbar.container.contains(evt.target) && - !appConfig.secondaryToolbar.toggleButton.contains(evt.target)) + evt.target !== appConfig.secondaryToolbar.toggleButton) ) { pdfInstance.secondaryToolbar.close() } } +// NOTE function webViewerKeyDown (evt) { const pdfInstance = getPdfInstance(evt.target) if (!pdfInstance || pdfInstance.overlayManager.active) { return } + if (pdfInstance.overlayManager.active) { + return + } const {eventBus, pdfViewer} = pdfInstance const isViewerInPresentationMode = pdfViewer.isInPresentationMode @@ -2890,7 +3051,7 @@ function webViewerKeyDown (evt) { case 79: // o if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - eventBus.dispatch('openfile', {source: window, pdfInstance: this}) + eventBus.dispatch('openfile', {source: window}) handled = true } break @@ -2904,6 +3065,10 @@ function webViewerKeyDown (evt) { case 80: // p pdfInstance.requestPresentationMode() handled = true + pdfInstance.externalServices.reportTelemetry({ + type: 'buttons', + data: {id: 'presentationModeKeyboard'}, + }) break case 71: // g // focuses input#pageNumber field @@ -3104,6 +3269,16 @@ function beforeUnload (evt) { return false } +// NOTE +function webViewerAnnotationEditorStatesChanged (data) { + const pdfInstance = getPdfInstance(data.target) + if (!pdfInstance || pdfInstance.overlayManager.active) { + return + } + pdfInstance + PDFViewerApplication.externalServices.updateEditorStates(data) +} + /* Abstract factory for the print service. */ const PDFPrintServiceFactory = { instance: { @@ -3117,5 +3292,6 @@ const PDFPrintServiceFactory = { export { PDFPrintServiceFactory, PDFViewerApplication, + // NOTE webViewerPageNumberChanged, } diff --git a/app/src/asset/pdf/app_options.js b/app/src/asset/pdf/app_options.js index 3c650718b..78b548a8b 100644 --- a/app/src/asset/pdf/app_options.js +++ b/app/src/asset/pdf/app_options.js @@ -17,28 +17,21 @@ import { Constants } from '../../constants' const compatibilityParams = Object.create(null) if (typeof PDFJSDev === 'undefined' || PDFJSDev.test('GENERIC')) { - const userAgent = - (typeof navigator !== 'undefined' && navigator.userAgent) || '' - const platform = - (typeof navigator !== 'undefined' && navigator.platform) || '' - const maxTouchPoints = - (typeof navigator !== 'undefined' && navigator.maxTouchPoints) || 1 + if ( + typeof PDFJSDev !== 'undefined' && + PDFJSDev.test('LIB') && + typeof navigator === 'undefined' + ) { + globalThis.navigator = Object.create(null) + } + const userAgent = navigator.userAgent || '' + const platform = navigator.platform || '' + const maxTouchPoints = navigator.maxTouchPoints || 1 const isAndroid = /Android/.test(userAgent) const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || - (platform === 'MacIntel' && maxTouchPoints > 1) - const isIOSChrome = /CriOS/.test(userAgent); - - // Disables URL.createObjectURL() usage in some environments. - // Support: Chrome on iOS - (function checkOnBlobSupport () { - // Sometimes Chrome on iOS loses data created with createObjectURL(), - // see issue 8081. - if (isIOSChrome) { - compatibilityParams.disableCreateObjectURL = true - } - })(); + (platform === 'MacIntel' && maxTouchPoints > 1); // Limit canvas size to 5 mega-pixels on mobile. // Support: Android, iOS @@ -62,9 +55,14 @@ const OptionKind = { * primitive types and cannot rely on any imported types. */ const defaultOptions = { + annotationEditorMode: { + /** @type {number} */ + value: 0, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, annotationMode: { /** @type {number} */ - value: 2, // https://github.com/siyuan-note/siyuan/issues/2975 DISABLE: 0, ENABLE: 1, ENABLE_FORMS: 2 (default), ENABLE_STORAGE: 3 + value: 2, kind: OptionKind.VIEWER + OptionKind.PREFERENCE, }, cursorToolOnLoad: { @@ -72,11 +70,6 @@ const defaultOptions = { value: 0, kind: OptionKind.VIEWER + OptionKind.PREFERENCE, }, - defaultUrl: { - /** @type {string} */ - value: 'compressed.tracemonkey-pldi-09.pdf', - kind: OptionKind.VIEWER, - }, defaultZoomValue: { /** @type {string} */ value: '', @@ -135,9 +128,23 @@ const defaultOptions = { maxCanvasPixels: { /** @type {number} */ value: 16777216, - compatibility: compatibilityParams.maxCanvasPixels, kind: OptionKind.VIEWER, }, + forcePageColors: { + /** @type {boolean} */ + value: false, + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, + pageColorsBackground: { + /** @type {string} */ + value: 'Canvas', + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, + pageColorsForeground: { + /** @type {string} */ + value: 'CanvasText', + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + }, pdfBugEnabled: { /** @type {boolean} */ value: typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION'), @@ -148,11 +155,6 @@ const defaultOptions = { value: 150, kind: OptionKind.VIEWER, }, - renderer: { - /** @type {string} */ - value: 'canvas', - kind: OptionKind.VIEWER, - }, sidebarViewOnLoad: { /** @type {number} */ value: -1, @@ -196,7 +198,10 @@ const defaultOptions = { }, cMapUrl: { /** @type {string} */ - value: 'cmaps/', + value: + typeof PDFJSDev === 'undefined' || !PDFJSDev.test('PRODUCTION') + ? '../external/bcmaps/' + : 'cmaps/', // NOTE kind: OptionKind.API, }, disableAutoFetch: { @@ -270,7 +275,8 @@ const defaultOptions = { }, workerSrc: { /** @type {string} */ - value: `${Constants.PROTYLE_CDN}/js/pdf/pdf.worker.js?v=2.14.102`, + // NOTE + value: `${Constants.PROTYLE_CDN}/js/pdf/pdf.worker.js?v=3.0.150`, kind: OptionKind.WORKER, }, } @@ -278,6 +284,11 @@ if ( typeof PDFJSDev === 'undefined' || PDFJSDev.test('!PRODUCTION || GENERIC') ) { + defaultOptions.defaultUrl = { + /** @type {string} */ + value: 'compressed.tracemonkey-pldi-09.pdf', + kind: OptionKind.VIEWER, + } defaultOptions.disablePreferences = { /** @type {boolean} */ value: typeof PDFJSDev !== 'undefined' && PDFJSDev.test('TESTING'), @@ -285,9 +296,14 @@ if ( } defaultOptions.locale = { /** @type {string} */ - value: typeof navigator !== 'undefined' ? navigator.language : 'en-US', + value: navigator.language || 'en-US', kind: OptionKind.VIEWER, } + defaultOptions.renderer = { + /** @type {string} */ + value: 'canvas', + kind: OptionKind.VIEWER + OptionKind.PREFERENCE, + } defaultOptions.sandboxBundleSrc = { /** @type {string} */ value: @@ -296,9 +312,12 @@ if ( : '../build/pdf.sandbox.js', kind: OptionKind.VIEWER, } - - defaultOptions.renderer.kind += OptionKind.PREFERENCE } else if (PDFJSDev.test('CHROME')) { + defaultOptions.defaultUrl = { + /** @type {string} */ + value: '', + kind: OptionKind.VIEWER, + } defaultOptions.disableTelemetry = { /** @type {boolean} */ value: false, @@ -325,7 +344,7 @@ class AppOptions { } const defaultOption = defaultOptions[name] if (defaultOption !== undefined) { - return defaultOption.compatibility ?? defaultOption.value + return compatibilityParams[name] ?? defaultOption.value } return undefined } @@ -357,7 +376,7 @@ class AppOptions { options[name] = userOption !== undefined ? userOption - : defaultOption.compatibility ?? defaultOption.value + : compatibilityParams[name] ?? defaultOption.value } return options } diff --git a/app/src/asset/pdf/base_tree_viewer.js b/app/src/asset/pdf/base_tree_viewer.js index b3eaee40d..cfd04220d 100644 --- a/app/src/asset/pdf/base_tree_viewer.js +++ b/app/src/asset/pdf/base_tree_viewer.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { removeNullCharacters } from "./ui_utils"; +import { removeNullCharacters } from "./ui_utils.js"; const TREEITEM_OFFSET_TOP = -100; // px const TREEITEM_SELECTED_CLASS = "selected"; @@ -74,6 +74,7 @@ class BaseTreeViewer { */ _addToggleButton(div, hidden = false) { const toggler = document.createElement("div"); + // NOTE toggler.innerHTML = `` toggler.className = "treeItemToggler"; if (hidden) { @@ -88,7 +89,7 @@ class BaseTreeViewer { this._toggleTreeItem(div, shouldShowAll); } }; - div.insertBefore(toggler, div.firstChild); + div.prepend(toggler); } /** @@ -123,7 +124,7 @@ class BaseTreeViewer { this._lastToggleIsShow = !fragment.querySelector(".treeItemsHidden"); } - this.container.appendChild(fragment); + this.container.append(fragment); this._dispatchEvent(count); } @@ -168,7 +169,7 @@ class BaseTreeViewer { this.container.scrollTo( treeItem.offsetLeft, - treeItem.offsetTop + TREEITEM_OFFSET_TOP + treeItem.offsetParent.offsetTop + treeItem.offsetTop + TREEITEM_OFFSET_TOP ); } } diff --git a/app/src/asset/pdf/base_viewer.js b/app/src/asset/pdf/base_viewer.js deleted file mode 100644 index 0f838a1e1..000000000 --- a/app/src/asset/pdf/base_viewer.js +++ /dev/null @@ -1,2087 +0,0 @@ -/* Copyright 2014 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - AnnotationMode, - createPromiseCapability, - PermissionFlag, - PixelsPerInch, - version, -} from "./pdfjs"; -import { - DEFAULT_SCALE, - DEFAULT_SCALE_DELTA, - DEFAULT_SCALE_VALUE, - getVisibleElements, - isPortraitOrientation, - isValidRotation, - isValidScrollMode, - isValidSpreadMode, - MAX_AUTO_SCALE, - MAX_SCALE, - MIN_SCALE, - PresentationModeState, - RendererType, - RenderingStates, - SCROLLBAR_PADDING, - scrollIntoView, - ScrollMode, - SpreadMode, - TextLayerMode, - UNKNOWN_SCALE, - VERTICAL_PADDING, - watchScroll, -} from "./ui_utils.js"; -import { AnnotationLayerBuilder } from "./annotation_layer_builder.js"; -import { NullL10n } from "./l10n_utils.js"; -import { PDFPageView } from "./pdf_page_view.js"; -import { PDFRenderingQueue } from "./pdf_rendering_queue.js"; -import { SimpleLinkService } from "./pdf_link_service.js"; -import { StructTreeLayerBuilder } from "./struct_tree_layer_builder.js"; -import { TextHighlighter } from "./text_highlighter.js"; -import { TextLayerBuilder } from "./text_layer_builder.js"; -import { XfaLayerBuilder } from "./xfa_layer_builder.js"; - -const DEFAULT_CACHE_SIZE = 10; -const ENABLE_PERMISSIONS_CLASS = "enablePermissions"; - -const PagesCountLimit = { - FORCE_SCROLL_MODE_PAGE: 15000, - FORCE_LAZY_PAGE_INIT: 7500, - PAUSE_EAGER_PAGE_INIT: 250, -}; - -/** - * @typedef {Object} PDFViewerOptions - * @property {HTMLDivElement} container - The container for the viewer element. - * @property {HTMLDivElement} [viewer] - The viewer element. - * @property {EventBus} eventBus - The application event bus. - * @property {IPDFLinkService} linkService - The navigation/linking service. - * @property {IDownloadManager} [downloadManager] - The download manager - * component. - * @property {PDFFindController} [findController] - The find controller - * component. - * @property {PDFScriptingManager} [scriptingManager] - The scripting manager - * component. - * @property {PDFRenderingQueue} [renderingQueue] - The rendering queue object. - * @property {boolean} [removePageBorders] - Removes the border shadow around - * the pages. The default value is `false`. - * @property {number} [textLayerMode] - Controls if the text layer used for - * selection and searching is created, and if the improved text selection - * behaviour is enabled. The constants from {TextLayerMode} should be used. - * The default value is `TextLayerMode.ENABLE`. - * @property {number} [annotationMode] - Controls if the annotation layer is - * created, and if interactive form elements or `AnnotationStorage`-data are - * being rendered. The constants from {@link AnnotationMode} should be used; - * see also {@link RenderParameters} and {@link GetOperatorListParameters}. - * The default value is `AnnotationMode.ENABLE_FORMS`. - * @property {string} [imageResourcesPath] - Path for image resources, mainly - * mainly for annotation icons. Include trailing slash. - * @property {boolean} [enablePrintAutoRotate] - Enables automatic rotation of - * landscape pages upon printing. The default is `false`. - * @property {string} renderer - 'canvas' or 'svg'. The default is 'canvas'. - * @property {boolean} [useOnlyCssZoom] - Enables CSS only zooming. The default - * value is `false`. - * @property {number} [maxCanvasPixels] - The maximum supported canvas size in - * total pixels, i.e. width * height. Use -1 for no limit. The default value - * is 4096 * 4096 (16 mega-pixels). - * @property {IL10n} l10n - Localization service. - * @property {boolean} [enablePermissions] - Enables PDF document permissions, - * when they exist. The default value is `false`. - */ - -class PDFPageViewBuffer { - // Here we rely on the fact that `Set`s preserve the insertion order. - #buf = new Set(); - - #size = 0; - - constructor(size) { - this.#size = size; - } - - push(view) { - const buf = this.#buf; - if (buf.has(view)) { - buf.delete(view); // Move the view to the "end" of the buffer. - } - buf.add(view); - - if (buf.size > this.#size) { - this.#destroyFirstView(); - } - } - - /** - * After calling resize, the size of the buffer will be `newSize`. - * The optional parameter `idsToKeep` is, if present, a Set of page-ids to - * push to the back of the buffer, delaying their destruction. The size of - * `idsToKeep` has no impact on the final size of the buffer; if `idsToKeep` - * is larger than `newSize`, some of those pages will be destroyed anyway. - */ - resize(newSize, idsToKeep = null) { - this.#size = newSize; - - const buf = this.#buf; - if (idsToKeep) { - const ii = buf.size; - let i = 1; - for (const view of buf) { - if (idsToKeep.has(view.id)) { - buf.delete(view); // Move the view to the "end" of the buffer. - buf.add(view); - } - if (++i > ii) { - break; - } - } - } - - while (buf.size > this.#size) { - this.#destroyFirstView(); - } - } - - has(view) { - return this.#buf.has(view); - } - - [Symbol.iterator]() { - return this.#buf.keys(); - } - - #destroyFirstView() { - const firstView = this.#buf.keys().next().value; - - firstView?.destroy(); - this.#buf.delete(firstView); - } -} - -/** - * Simple viewer control to display PDF content/pages. - * - * @implements {IPDFAnnotationLayerFactory} - * @implements {IPDFStructTreeLayerFactory} - * @implements {IPDFTextLayerFactory} - * @implements {IPDFXfaLayerFactory} - */ -class BaseViewer { - #buffer = null; - - #annotationMode = AnnotationMode.ENABLE_FORMS; - - #previousAnnotationMode = null; - - #enablePermissions = false; - - #previousContainerHeight = 0; - - #scrollModePageState = null; - - #onVisibilityChange = null; - - /** - * @param {PDFViewerOptions} options - */ - constructor(options) { - if (this.constructor === BaseViewer) { - throw new Error("Cannot initialize BaseViewer."); - } - this.container = options.container; - this.viewer = options.viewer || options.container.firstElementChild; - - if ( - typeof PDFJSDev === "undefined" || - PDFJSDev.test("!PRODUCTION || GENERIC") - ) { - if ( - !( - this.container?.tagName.toUpperCase() === "DIV" && - this.viewer?.tagName.toUpperCase() === "DIV" - ) - ) { - throw new Error("Invalid `container` and/or `viewer` option."); - } - - if ( - this.container.offsetParent && - getComputedStyle(this.container).position !== "absolute" - ) { - throw new Error("The `container` must be absolutely positioned."); - } - } - this.eventBus = options.eventBus; - this.linkService = options.linkService || new SimpleLinkService(); - this.downloadManager = options.downloadManager || null; - this.findController = options.findController || null; - this._scriptingManager = options.scriptingManager || null; - this.removePageBorders = options.removePageBorders || false; - this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; - this.#annotationMode = - options.annotationMode ?? AnnotationMode.ENABLE_FORMS; - this.imageResourcesPath = options.imageResourcesPath || ""; - this.enablePrintAutoRotate = options.enablePrintAutoRotate || false; - this.renderer = options.renderer || RendererType.CANVAS; - this.useOnlyCssZoom = options.useOnlyCssZoom || false; - this.maxCanvasPixels = options.maxCanvasPixels; - this.l10n = options.l10n || NullL10n; - this.#enablePermissions = options.enablePermissions || false; - - this.defaultRenderingQueue = !options.renderingQueue; - if (this.defaultRenderingQueue) { - // Custom rendering queue is not specified, using default one - this.renderingQueue = new PDFRenderingQueue(); - this.renderingQueue.setViewer(this); - } else { - this.renderingQueue = options.renderingQueue; - } - this._doc = document.documentElement; - - this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this)); - this.presentationModeState = PresentationModeState.UNKNOWN; - this._onBeforeDraw = this._onAfterDraw = null; - this._resetView(); - - if (this.removePageBorders) { - this.viewer.classList.add("removePageBorders"); - } - // Defer the dispatching of this event, to give other viewer components - // time to initialize *and* register 'baseviewerinit' event listeners. - Promise.resolve().then(() => { - this.eventBus.dispatch("baseviewerinit", { source: this }); - }); - } - - get pagesCount() { - return this._pages.length; - } - - getPageView(index) { - return this._pages[index]; - } - - /** - * @type {boolean} - True if all {PDFPageView} objects are initialized. - */ - get pageViewsReady() { - if (!this._pagesCapability.settled) { - return false; - } - // Prevent printing errors when 'disableAutoFetch' is set, by ensuring - // that *all* pages have in fact been completely loaded. - return this._pages.every(function (pageView) { - return pageView?.pdfPage; - }); - } - - /** - * @type {boolean} - */ - get renderForms() { - return this.#annotationMode === AnnotationMode.ENABLE_FORMS; - } - - /** - * @type {boolean} - */ - get enableScripting() { - return !!this._scriptingManager; - } - - /** - * @type {number} - */ - get currentPageNumber() { - return this._currentPageNumber; - } - - /** - * @param {number} val - The page number. - */ - set currentPageNumber(val) { - if (!Number.isInteger(val)) { - throw new Error("Invalid page number."); - } - if (!this.pdfDocument) { - return; - } - // The intent can be to just reset a scroll position and/or scale. - if (!this._setCurrentPageNumber(val, /* resetCurrentPageView = */ true)) { - console.error(`currentPageNumber: "${val}" is not a valid page.`); - } - } - - /** - * @returns {boolean} Whether the pageNumber is valid (within bounds). - * @private - */ - _setCurrentPageNumber(val, resetCurrentPageView = false) { - if (this._currentPageNumber === val) { - if (resetCurrentPageView) { - this._resetCurrentPageView(); - } - return true; - } - - if (!(0 < val && val <= this.pagesCount)) { - return false; - } - const previous = this._currentPageNumber; - this._currentPageNumber = val; - - this.eventBus.dispatch("pagechanging", { - source: this, - pageNumber: val, - pageLabel: this._pageLabels?.[val - 1] ?? null, - previous, - }); - - if (resetCurrentPageView) { - this._resetCurrentPageView(); - } - return true; - } - - /** - * @type {string|null} Returns the current page label, or `null` if no page - * labels exist. - */ - get currentPageLabel() { - return this._pageLabels?.[this._currentPageNumber - 1] ?? null; - } - - /** - * @param {string} val - The page label. - */ - set currentPageLabel(val) { - if (!this.pdfDocument) { - return; - } - let page = val | 0; // Fallback page number. - if (this._pageLabels) { - const i = this._pageLabels.indexOf(val); - if (i >= 0) { - page = i + 1; - } - } - // The intent can be to just reset a scroll position and/or scale. - if (!this._setCurrentPageNumber(page, /* resetCurrentPageView = */ true)) { - console.error(`currentPageLabel: "${val}" is not a valid page.`); - } - } - - /** - * @type {number} - */ - get currentScale() { - return this._currentScale !== UNKNOWN_SCALE - ? this._currentScale - : DEFAULT_SCALE; - } - - /** - * @param {number} val - Scale of the pages in percents. - */ - set currentScale(val) { - if (isNaN(val)) { - throw new Error("Invalid numeric scale."); - } - if (!this.pdfDocument) { - return; - } - this._setScale(val, false); - } - - /** - * @type {string} - */ - get currentScaleValue() { - return this._currentScaleValue; - } - - /** - * @param val - The scale of the pages (in percent or predefined value). - */ - set currentScaleValue(val) { - if (!this.pdfDocument) { - return; - } - this._setScale(val, false); - } - - /** - * @type {number} - */ - get pagesRotation() { - return this._pagesRotation; - } - - /** - * @param {number} rotation - The rotation of the pages (0, 90, 180, 270). - */ - set pagesRotation(rotation) { - if (!isValidRotation(rotation)) { - throw new Error("Invalid pages rotation angle."); - } - if (!this.pdfDocument) { - return; - } - // Normalize the rotation, by clamping it to the [0, 360) range. - rotation %= 360; - if (rotation < 0) { - rotation += 360; - } - if (this._pagesRotation === rotation) { - return; // The rotation didn't change. - } - this._pagesRotation = rotation; - - const pageNumber = this._currentPageNumber; - - const updateArgs = { rotation }; - for (const pageView of this._pages) { - pageView.update(updateArgs); - } - // Prevent errors in case the rotation changes *before* the scale has been - // set to a non-default value. - if (this._currentScaleValue) { - this._setScale(this._currentScaleValue, true); - } - - this.eventBus.dispatch("rotationchanging", { - source: this, - pagesRotation: rotation, - pageNumber, - }); - - if (this.defaultRenderingQueue) { - this.update(); - } - } - - get firstPagePromise() { - return this.pdfDocument ? this._firstPageCapability.promise : null; - } - - get onePageRendered() { - return this.pdfDocument ? this._onePageRenderedCapability.promise : null; - } - - get pagesPromise() { - return this.pdfDocument ? this._pagesCapability.promise : null; - } - - /** - * Currently only *some* permissions are supported. - */ - #initializePermissions(permissions) { - if (!permissions) { - return; - } - - if (!permissions.includes(PermissionFlag.COPY)) { - this.viewer.classList.add(ENABLE_PERMISSIONS_CLASS); - } - - if ( - !permissions.includes(PermissionFlag.MODIFY_ANNOTATIONS) && - !permissions.includes(PermissionFlag.FILL_INTERACTIVE_FORMS) - ) { - if (this.#annotationMode === AnnotationMode.ENABLE_FORMS) { - this.#previousAnnotationMode = this.#annotationMode; // Allow resetting. - this.#annotationMode = AnnotationMode.ENABLE; - } - } - } - - #onePageRenderedOrForceFetch() { - // Unless the viewer *and* its pages are visible, rendering won't start and - // `this._onePageRenderedCapability` thus won't be resolved. - // To ensure that automatic printing, on document load, still works even in - // those cases we force-allow fetching of all pages when: - // - The current window/tab is inactive, which will prevent rendering since - // `requestAnimationFrame` is being used; fixes bug 1746213. - // - The viewer is hidden in the DOM, e.g. in a `display: none`