Implement a permissions API
This commit is contained in:
parent
4874e9ace0
commit
959ed3705b
@ -15,9 +15,9 @@
|
||||
|
||||
import {
|
||||
bytesToString, createPromiseCapability, createValidAbsoluteUrl, FormatError,
|
||||
info, InvalidPDFException, isBool, isString, MissingDataException, shadow,
|
||||
stringToPDFString, stringToUTF8String, toRomanNumerals, unreachable, warn,
|
||||
XRefParseException
|
||||
info, InvalidPDFException, isBool, isNum, isString, MissingDataException,
|
||||
PermissionFlag, shadow, stringToPDFString, stringToUTF8String,
|
||||
toRomanNumerals, unreachable, warn, XRefParseException
|
||||
} from '../shared/util';
|
||||
import {
|
||||
Dict, isCmd, isDict, isName, isRef, isRefsEqual, isStream, Ref, RefSet,
|
||||
@ -177,6 +177,48 @@ class Catalog {
|
||||
return (root.items.length > 0 ? root.items : null);
|
||||
}
|
||||
|
||||
get permissions() {
|
||||
let permissions = null;
|
||||
try {
|
||||
permissions = this._readPermissions();
|
||||
} catch (ex) {
|
||||
if (ex instanceof MissingDataException) {
|
||||
throw ex;
|
||||
}
|
||||
warn('Unable to read permissions.');
|
||||
}
|
||||
return shadow(this, 'permissions', permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_readPermissions() {
|
||||
const encrypt = this.xref.trailer.get('Encrypt');
|
||||
if (!isDict(encrypt)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let flags = encrypt.get('P');
|
||||
if (!isNum(flags)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// PDF integer objects are represented internally in signed 2's complement
|
||||
// form. Therefore, convert the signed decimal integer to a signed 2's
|
||||
// complement binary integer so we can use regular bitwise operations on it.
|
||||
flags += 2 ** 32;
|
||||
|
||||
const permissions = [];
|
||||
for (const key in PermissionFlag) {
|
||||
const value = PermissionFlag[key];
|
||||
if (flags & value) {
|
||||
permissions.push(value);
|
||||
}
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
get numPages() {
|
||||
const obj = this.toplevelPagesDict.get('Count');
|
||||
if (!Number.isInteger(obj)) {
|
||||
|
@ -703,6 +703,10 @@ var WorkerMessageHandler = {
|
||||
}
|
||||
);
|
||||
|
||||
handler.on('GetPermissions', function(data) {
|
||||
return pdfManager.ensureCatalog('permissions');
|
||||
});
|
||||
|
||||
handler.on('GetMetadata',
|
||||
function wphSetupGetMetadata(data) {
|
||||
return Promise.all([pdfManager.ensureDoc('documentInfo'),
|
||||
|
@ -691,6 +691,15 @@ class PDFDocumentProxy {
|
||||
return this._transport.getOutline();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Promise} A promise that is resolved with an {Array} that contains
|
||||
* the permission flags for the PDF document, or `null` when
|
||||
* no permissions are present in the PDF file.
|
||||
*/
|
||||
getPermissions() {
|
||||
return this._transport.getPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Promise} A promise that is resolved with an {Object} that has
|
||||
* `info` and `metadata` properties. `info` is an {Object} filled with
|
||||
@ -2141,6 +2150,10 @@ class WorkerTransport {
|
||||
return this.messageHandler.sendWithPromise('GetOutline', null);
|
||||
}
|
||||
|
||||
getPermissions() {
|
||||
return this.messageHandler.sendWithPromise('GetPermissions', null);
|
||||
}
|
||||
|
||||
getMetadata() {
|
||||
return this.messageHandler.sendWithPromise('GetMetadata', null).
|
||||
then((results) => {
|
||||
|
@ -25,6 +25,18 @@ const NativeImageDecoding = {
|
||||
DISPLAY: 'display',
|
||||
};
|
||||
|
||||
// Permission flags from Table 22, Section 7.6.3.2 of the PDF specification.
|
||||
const PermissionFlag = {
|
||||
PRINT: 0x04,
|
||||
MODIFY_CONTENTS: 0x08,
|
||||
COPY: 0x10,
|
||||
MODIFY_ANNOTATIONS: 0x20,
|
||||
FILL_INTERACTIVE_FORMS: 0x100,
|
||||
COPY_FOR_ACCESSIBILITY: 0x200,
|
||||
ASSEMBLE: 0x400,
|
||||
PRINT_HIGH_QUALITY: 0x800,
|
||||
};
|
||||
|
||||
var TextRenderingMode = {
|
||||
FILL: 0,
|
||||
STROKE: 1,
|
||||
@ -1014,6 +1026,7 @@ export {
|
||||
NativeImageDecoding,
|
||||
PasswordException,
|
||||
PasswordResponses,
|
||||
PermissionFlag,
|
||||
StreamType,
|
||||
TextRenderingMode,
|
||||
UnexpectedResponseException,
|
||||
|
3
test/pdfs/.gitignore
vendored
3
test/pdfs/.gitignore
vendored
@ -325,3 +325,6 @@
|
||||
!transparent.pdf
|
||||
!xobject-image.pdf
|
||||
!ccitt_EndOfBlock_false.pdf
|
||||
!issue9972-1.pdf
|
||||
!issue9972-2.pdf
|
||||
!issue9972-3.pdf
|
||||
|
BIN
test/pdfs/issue9972-1.pdf
Normal file
BIN
test/pdfs/issue9972-1.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue9972-2.pdf
Normal file
BIN
test/pdfs/issue9972-2.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue9972-3.pdf
Normal file
BIN
test/pdfs/issue9972-3.pdf
Normal file
Binary file not shown.
@ -18,7 +18,8 @@ import {
|
||||
} from './test_utils';
|
||||
import {
|
||||
createPromiseCapability, FontType, InvalidPDFException, MissingPDFException,
|
||||
OPS, PasswordException, PasswordResponses, StreamType, stringToBytes
|
||||
OPS, PasswordException, PasswordResponses, PermissionFlag, StreamType,
|
||||
stringToBytes
|
||||
} from '../../src/shared/util';
|
||||
import {
|
||||
DOMCanvasFactory, RenderingCancelledException, StatTimer
|
||||
@ -822,6 +823,63 @@ describe('api', function() {
|
||||
done.fail(reason);
|
||||
});
|
||||
});
|
||||
|
||||
it('gets non-existent permissions', function(done) {
|
||||
doc.getPermissions().then(function(permissions) {
|
||||
expect(permissions).toEqual(null);
|
||||
|
||||
done();
|
||||
}).catch(function(reason) {
|
||||
done.fail(reason);
|
||||
});
|
||||
});
|
||||
|
||||
it('gets permissions', function (done) {
|
||||
// Editing not allowed.
|
||||
const loadingTask0 =
|
||||
getDocument(buildGetDocumentParams('issue9972-1.pdf'));
|
||||
const promise0 = loadingTask0.promise.then(function(pdfDocument) {
|
||||
return pdfDocument.getPermissions();
|
||||
});
|
||||
|
||||
// Printing not allowed.
|
||||
const loadingTask1 =
|
||||
getDocument(buildGetDocumentParams('issue9972-2.pdf'));
|
||||
const promise1 = loadingTask1.promise.then(function(pdfDocument) {
|
||||
return pdfDocument.getPermissions();
|
||||
});
|
||||
|
||||
// Copying not allowed.
|
||||
const loadingTask2 =
|
||||
getDocument(buildGetDocumentParams('issue9972-3.pdf'));
|
||||
const promise2 = loadingTask2.promise.then(function(pdfDocument) {
|
||||
return pdfDocument.getPermissions();
|
||||
});
|
||||
|
||||
const totalPermissionCount = Object.keys(PermissionFlag).length;
|
||||
Promise.all([promise0, promise1, promise2]).then(function(permissions) {
|
||||
expect(permissions[0].length).toEqual(totalPermissionCount - 1);
|
||||
expect(permissions[0].includes(PermissionFlag.MODIFY_CONTENTS))
|
||||
.toBeFalsy();
|
||||
|
||||
expect(permissions[1].length).toEqual(totalPermissionCount - 2);
|
||||
expect(permissions[1].includes(PermissionFlag.PRINT)).toBeFalsy();
|
||||
expect(permissions[1].includes(PermissionFlag.PRINT_HIGH_QUALITY))
|
||||
.toBeFalsy();
|
||||
|
||||
expect(permissions[2].length).toEqual(totalPermissionCount - 1);
|
||||
expect(permissions[2].includes(PermissionFlag.COPY)).toBeFalsy();
|
||||
|
||||
Promise.all([
|
||||
loadingTask0.destroy(),
|
||||
loadingTask1.destroy(),
|
||||
loadingTask2.destroy(),
|
||||
]).then(done);
|
||||
}).catch(function(reason) {
|
||||
done.fail(reason);
|
||||
});
|
||||
});
|
||||
|
||||
it('gets metadata', function(done) {
|
||||
var promise = doc.getMetadata();
|
||||
promise.then(function({ info, metadata, contentDispositionFilename, }) {
|
||||
|
Loading…
Reference in New Issue
Block a user