diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 25a66b956..12c80b4ef 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -122,6 +122,8 @@ class PDFPageView { #renderingState = RenderingStates.INITIAL; + #textLayerMode = TextLayerMode.ENABLE; + #useThumbnailCanvas = { initialOptionalContent: true, regularAnnotations: true, @@ -149,7 +151,7 @@ class PDFPageView { this._optionalContentConfigPromise = options.optionalContentConfigPromise || null; this.hasRestrictedScaling = false; - this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; + this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS; this.imageResourcesPath = options.imageResourcesPath || ""; @@ -798,7 +800,7 @@ class PDFPageView { if ( !this.textLayer && - this.textLayerMode !== TextLayerMode.DISABLE && + this.#textLayerMode !== TextLayerMode.DISABLE && !pdfPage.isPureXfa ) { this._accessibilityManager ||= new TextAccessibilityManager(); @@ -807,6 +809,8 @@ class PDFPageView { highlighter: this._textHighlighter, accessibilityManager: this._accessibilityManager, isOffscreenCanvasSupported: this.isOffscreenCanvasSupported, + enablePermissions: + this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS, }); div.append(this.textLayer.div); } diff --git a/web/pdf_viewer.css b/web/pdf_viewer.css index d8402fe96..710ad7740 100644 --- a/web/pdf_viewer.css +++ b/web/pdf_viewer.css @@ -185,11 +185,6 @@ display: none; } -.pdfViewer.enablePermissions .textLayer span { - user-select: none !important; - cursor: not-allowed; -} - .pdfPresentationMode .pdfViewer { padding-bottom: 0; } diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index d6df78efb..a55504479 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -64,7 +64,6 @@ import { PDFRenderingQueue } from "./pdf_rendering_queue.js"; import { SimpleLinkService } from "./pdf_link_service.js"; const DEFAULT_CACHE_SIZE = 10; -const ENABLE_PERMISSIONS_CLASS = "enablePermissions"; const PagesCountLimit = { FORCE_SCROLL_MODE_PAGE: 15000, @@ -206,7 +205,7 @@ class PDFViewer { #containerTopLeft = null; - #copyCallbackBound = this.#copyCallback.bind(this); + #copyCallbackBound = null; #enablePermissions = false; @@ -226,6 +225,8 @@ class PDFViewer { #scaleTimeoutId = null; + #textLayerMode = TextLayerMode.ENABLE; + /** * @param {PDFViewerOptions} options */ @@ -259,7 +260,7 @@ class PDFViewer { this.downloadManager = options.downloadManager || null; this.findController = options.findController || null; this._scriptingManager = options.scriptingManager || null; - this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; + this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; this.#annotationMode = options.annotationMode ?? AnnotationMode.ENABLE_FORMS; this.#annotationEditorMode = @@ -565,15 +566,6 @@ class PDFViewer { }; } - #createHiddenCopyElement() { - if (this.#hiddenCopyElement) { - return; - } - const element = (this.#hiddenCopyElement = document.createElement("div")); - element.id = "hiddenCopyElement"; - this.viewer.before(element); - } - /** * Currently only *some* permissions are supported. * @returns {Object} @@ -582,17 +574,17 @@ class PDFViewer { const params = { annotationEditorMode: this.#annotationEditorMode, annotationMode: this.#annotationMode, - textLayerMode: this.textLayerMode, + textLayerMode: this.#textLayerMode, }; if (!permissions) { - this.#createHiddenCopyElement(); return params; } - if (!permissions.includes(PermissionFlag.COPY)) { - this.viewer.classList.add(ENABLE_PERMISSIONS_CLASS); - } else { - this.#createHiddenCopyElement(); + if ( + !permissions.includes(PermissionFlag.COPY) && + this.#textLayerMode === TextLayerMode.ENABLE + ) { + params.textLayerMode = TextLayerMode.ENABLE_PERMISSIONS; } if (!permissions.includes(PermissionFlag.MODIFY_CONTENTS)) { @@ -683,7 +675,7 @@ class PDFViewer { return texts.join("\n"); } - #copyCallback(event) { + #copyCallback(textLayerMode, event) { const selection = document.getSelection(); const { focusNode, anchorNode } = selection; if ( @@ -699,6 +691,11 @@ class PDFViewer { // including this element so having it in the selection means that all // has been selected. + if (textLayerMode === TextLayerMode.ENABLE_PERMISSIONS) { + event.preventDefault(); + event.stopPropagation(); + return; + } // TODO: if all the pages are rendered we don't need to wait for // getAllText and we could just get text from the Selection object. @@ -831,6 +828,13 @@ class PDFViewer { const { annotationEditorMode, annotationMode, textLayerMode } = this.#initializePermissions(permissions); + if (textLayerMode !== TextLayerMode.DISABLE) { + const element = (this.#hiddenCopyElement = + document.createElement("div")); + element.id = "hiddenCopyElement"; + this.viewer.before(element); + } + if (annotationEditorMode !== AnnotationEditorType.DISABLE) { const mode = annotationEditorMode; @@ -906,6 +910,10 @@ class PDFViewer { this._scriptingManager?.setDocument(pdfDocument); // Enable scripting. if (this.#hiddenCopyElement) { + this.#copyCallbackBound = this.#copyCallback.bind( + this, + textLayerMode + ); document.addEventListener("copy", this.#copyCallbackBound); } @@ -1051,11 +1059,10 @@ class PDFViewer { this._updateScrollMode(); this.viewer.removeAttribute("lang"); - // Reset all PDF document permissions. - this.viewer.classList.remove(ENABLE_PERMISSIONS_CLASS); if (this.#hiddenCopyElement) { document.removeEventListener("copy", this.#copyCallbackBound); + this.#copyCallbackBound = null; this.#hiddenCopyElement.remove(); this.#hiddenCopyElement = null; diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index 81a77918c..e9db36e94 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -38,6 +38,8 @@ import { removeNullCharacters } from "./ui_utils.js"; * contain text that matches the PDF text they are overlaying. */ class TextLayerBuilder { + #enablePermissions = false; + #rotation = 0; #scale = 0; @@ -48,6 +50,7 @@ class TextLayerBuilder { highlighter = null, accessibilityManager = null, isOffscreenCanvasSupported = true, + enablePermissions = false, }) { this.textContentItemsStr = []; this.renderingDone = false; @@ -57,6 +60,7 @@ class TextLayerBuilder { this.highlighter = highlighter; this.accessibilityManager = accessibilityManager; this.isOffscreenCanvasSupported = isOffscreenCanvasSupported; + this.#enablePermissions = enablePermissions === true; this.div = document.createElement("div"); this.div.className = "textLayer"; @@ -215,11 +219,13 @@ class TextLayerBuilder { }); div.addEventListener("copy", event => { - const selection = document.getSelection(); - event.clipboardData.setData( - "text/plain", - removeNullCharacters(normalizeUnicode(selection.toString())) - ); + if (!this.#enablePermissions) { + const selection = document.getSelection(); + event.clipboardData.setData( + "text/plain", + removeNullCharacters(normalizeUnicode(selection.toString())) + ); + } event.preventDefault(); event.stopPropagation(); }); diff --git a/web/ui_utils.js b/web/ui_utils.js index 0c651087b..a5d397b50 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -49,6 +49,7 @@ const SidebarView = { const TextLayerMode = { DISABLE: 0, ENABLE: 1, + ENABLE_PERMISSIONS: 2, }; const ScrollMode = {