Add a new pdfjs.enablePermissions preference, off by default, to allow the PDF documents to disable copying in the viewer (bug 792816)

*Please note:* Most of the necessary API work was done in PR 10033, and the only remaining thing to do here was to implement it in the viewer.

The new preference should thus allow e.g. enterprise users to disable copying in the viewer, for PDF documents whose permissions specify that.

In order to simplify things the "copy"-permission was implemented using CSS, as suggested in https://bugzilla.mozilla.org/show_bug.cgi?id=792816#c55, which should hopefully suffice.[1]
The advantage of this approach, as opposed to e.g. disabling the `textLayer` completely, is first of all that it ensures that searching still works correctly even in copy-protected documents. Secondly this also greatly simplifies the overall implementation, since it doesn't require a lot of code for something that's disabled by default.

---
[1] As the discussion in the bug shows, this kind of copy-protection is not very strong and is also generally easy to remove/circumvent in various ways. Hence a simple solution, targeting "regular"-users rather than "power"-users is hopefully deemed acceptable here.
This commit is contained in:
Jonas Jenwald 2020-04-08 16:53:31 +02:00
parent 4fe92605b7
commit 8521f70157
4 changed files with 48 additions and 0 deletions

View File

@ -151,6 +151,10 @@
"type": "boolean",
"default": false
},
"enablePermissions": {
"type": "boolean",
"default": false
},
"historyUpdateUrl": {
"type": "boolean",
"default": false

View File

@ -48,6 +48,7 @@ import {
MissingPDFException,
OPS,
PDFWorker,
PermissionFlag,
shadow,
UnexpectedResponseException,
UNSUPPORTED_FEATURES,
@ -77,6 +78,7 @@ const DEFAULT_SCALE_DELTA = 1.1;
const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000; // ms
const FORCE_PAGES_LOADED_TIMEOUT = 10000; // ms
const WHEEL_ZOOM_DISABLED_TIMEOUT = 1000; // ms
const ENABLE_PERMISSIONS_CLASS = "enablePermissions";
const ViewOnLoad = {
UNKNOWN: -1,
@ -679,6 +681,7 @@ const PDFViewerApplication = {
this.pdfLinkService.setDocument(null);
this.pdfDocumentProperties.setDocument(null);
}
webViewerResetPermissions();
this.store = null;
this.isInitialViewSet = false;
this.downloadComplete = false;
@ -1149,6 +1152,10 @@ const PDFViewerApplication = {
pdfViewer.focus();
}
// Currently only the "copy"-permission is supported, hence we delay
// the `getPermissions` API call until *after* rendering has started.
this._initializePermissions(pdfDocument);
// For documents with different page sizes, once all pages are
// resolved, ensure that the correct location becomes visible on load.
// (To reduce the risk, in very large and/or slow loading documents,
@ -1467,6 +1474,24 @@ const PDFViewerApplication = {
}
},
/**
* @private
*/
async _initializePermissions(pdfDocument) {
const permissions = await pdfDocument.getPermissions();
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the permissions resolved.
}
if (!permissions || !AppOptions.get("enablePermissions")) {
return;
}
// Currently only the "copy"-permission is supported.
if (!permissions.includes(PermissionFlag.COPY)) {
this.appConfig.viewerContainer.classList.add(ENABLE_PERMISSIONS_CLASS);
}
},
setInitialView(
storedHash,
{ rotation, sidebarView, scrollMode, spreadMode } = {}
@ -1979,6 +2004,15 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
};
}
function webViewerResetPermissions() {
const { appConfig } = PDFViewerApplication;
if (!appConfig) {
return;
}
// Currently only the "copy"-permission is supported.
appConfig.viewerContainer.classList.remove(ENABLE_PERMISSIONS_CLASS);
}
function webViewerPageRendered(evt) {
const pageNumber = evt.pageNumber;
const pageIndex = pageNumber - 1;

View File

@ -53,6 +53,11 @@ const defaultOptions = {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
enablePermissions: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
/**
* The `disablePreferences` is, conditionally, defined below.
*/

View File

@ -55,6 +55,11 @@ select {
display: none !important;
}
.pdfViewer.enablePermissions .textLayer > span {
user-select: none !important;
cursor: not-allowed;
}
#viewerContainer.pdfPresentationMode:-ms-fullscreen {
top: 0px !important;
overflow: hidden !important;