From 0991c06395e2646fcdda7ddf3a63416f6bbbaaad Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 21 Nov 2015 23:25:17 +0100 Subject: [PATCH] Refactor annotation flags code This patch makes it possible to set and get all possible flags that the PDF specification defines. Even though we do not support all possible annotation types and not all possible annotation flags yet, this general framework makes it easy to access all flags for each annotation such that annotation type implementations can use this information. We add constants for all possible annotation flags such that we do not need to hardcode the flags in the code anymore. The `isViewable()` and `isPrintable()` methods are now easier to read. Additionally, unit tests have been added to ensure correct behavior. This is another part of #5218. --- src/core/annotation.js | 112 ++++++++++++++++++----------- src/core/core.js | 3 +- src/shared/util.js | 13 ++++ test/unit/annotation_layer_spec.js | 23 +++++- 4 files changed, 107 insertions(+), 44 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index 120afef3b87eb..8735dd4200611 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -15,7 +15,8 @@ /* globals PDFJS, Util, isDict, isName, stringToPDFString, warn, Dict, Stream, stringToBytes, Promise, isArray, ObjectLoader, OperatorList, isValidUrl, OPS, createPromiseCapability, AnnotationType, - stringToUTF8String, AnnotationBorderStyleType, ColorSpace */ + stringToUTF8String, AnnotationBorderStyleType, ColorSpace, + AnnotationFlag, isInt */ 'use strict'; @@ -121,7 +122,8 @@ var Annotation = (function AnnotationClosure() { var data = this.data = {}; data.subtype = dict.get('Subtype').name; - data.annotationFlags = dict.get('F'); + + this.setFlags(dict.get('F')); this.setRectangle(dict.get('Rect')); data.rect = this.rectangle; @@ -138,6 +140,64 @@ var Annotation = (function AnnotationClosure() { } Annotation.prototype = { + /** + * @return {boolean} + */ + get viewable() { + if (this.flags) { + return !this.hasFlag(AnnotationFlag.INVISIBLE) && + !this.hasFlag(AnnotationFlag.HIDDEN) && + !this.hasFlag(AnnotationFlag.NOVIEW); + } + return true; + }, + + /** + * @return {boolean} + */ + get printable() { + if (this.flags) { + return this.hasFlag(AnnotationFlag.PRINT) && + !this.hasFlag(AnnotationFlag.INVISIBLE) && + !this.hasFlag(AnnotationFlag.HIDDEN); + } + return false; + }, + + /** + * Set the flags. + * + * @public + * @memberof Annotation + * @param {number} flags - Unsigned 32-bit integer specifying annotation + * characteristics + * @see {@link shared/util.js} + */ + setFlags: function Annotation_setFlags(flags) { + if (isInt(flags)) { + this.flags = flags; + } else { + this.flags = 0; + } + }, + + /** + * Check if a provided flag is set. + * + * @public + * @memberof Annotation + * @param {number} flag - Hexadecimal representation for an annotation + * characteristic + * @return {boolean} + * @see {@link shared/util.js} + */ + hasFlag: function Annotation_hasFlag(flag) { + if (this.flags) { + return (this.flags & flag) > 0; + } + return false; + }, + /** * Set the rectangle. * @@ -237,32 +297,6 @@ var Annotation = (function AnnotationClosure() { } }, - isInvisible: function Annotation_isInvisible() { - var data = this.data; - return !!(data && - data.annotationFlags && // Default: not invisible - data.annotationFlags & 0x1); // Invisible - }, - - isViewable: function Annotation_isViewable() { - var data = this.data; - return !!(!this.isInvisible() && - data && - (!data.annotationFlags || - !(data.annotationFlags & 0x22)) && // Hidden or NoView - data.rect); // rectangle is necessary - }, - - isPrintable: function Annotation_isPrintable() { - var data = this.data; - return !!(!this.isInvisible() && - data && - data.annotationFlags && // Default: not printable - data.annotationFlags & 0x4 && // Print - !(data.annotationFlags & 0x2) && // Hidden - data.rect); // rectangle is necessary - }, - loadResources: function Annotation_loadResources(keys) { return new Promise(function (resolve, reject) { this.appearance.dict.getAsync('Resources').then(function (resources) { @@ -329,8 +363,8 @@ var Annotation = (function AnnotationClosure() { var annotationPromises = []; for (var i = 0, n = annotations.length; i < n; ++i) { - if (intent === 'display' && annotations[i].isViewable() || - intent === 'print' && annotations[i].isPrintable()) { + if (intent === 'display' && annotations[i].viewable || + intent === 'print' && annotations[i].printable) { annotationPromises.push( annotations[i].getOperatorList(partialEvaluator, task)); } @@ -506,6 +540,12 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty; + // Hide unsupported Widget signatures. + if (data.fieldType === 'Sig') { + warn('unimplemented annotation type: Widget signature'); + this.setFlags(AnnotationFlag.HIDDEN); + } + // Building the full field name by collecting the field and // its ancestors 'T' data and joining them using '.'. var fieldName = []; @@ -539,17 +579,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { data.fullName = fieldName.join('.'); } - var parent = Annotation.prototype; - Util.inherit(WidgetAnnotation, Annotation, { - isViewable: function WidgetAnnotation_isViewable() { - if (this.data.fieldType === 'Sig') { - warn('unimplemented annotation type: Widget signature'); - return false; - } - - return parent.isViewable.call(this); - } - }); + Util.inherit(WidgetAnnotation, Annotation, {}); return WidgetAnnotation; })(); diff --git a/src/core/core.js b/src/core/core.js index 9ff10cd56e80f..502081d0103c3 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -268,8 +268,7 @@ var Page = (function PageClosure() { for (var i = 0, n = annotationRefs.length; i < n; ++i) { var annotationRef = annotationRefs[i]; var annotation = annotationFactory.create(this.xref, annotationRef); - if (annotation && - (annotation.isViewable() || annotation.isPrintable())) { + if (annotation && (annotation.viewable || annotation.printable)) { annotations.push(annotation); } } diff --git a/src/shared/util.js b/src/shared/util.js index a99b05df909f3..6845a5fd44cc5 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -48,6 +48,19 @@ var AnnotationType = { LINK: 3 }; +var AnnotationFlag = { + INVISIBLE: 0x01, + HIDDEN: 0x02, + PRINT: 0x04, + NOZOOM: 0x08, + NOROTATE: 0x10, + NOVIEW: 0x20, + READONLY: 0x40, + LOCKED: 0x80, + TOGGLENOVIEW: 0x100, + LOCKEDCONTENTS: 0x200 +}; + var AnnotationBorderStyleType = { SOLID: 1, DASHED: 2, diff --git a/test/unit/annotation_layer_spec.js b/test/unit/annotation_layer_spec.js index 99d2f5adc51b0..d97861abaa86f 100644 --- a/test/unit/annotation_layer_spec.js +++ b/test/unit/annotation_layer_spec.js @@ -1,10 +1,31 @@ /* globals expect, it, describe, Dict, Name, Annotation, AnnotationBorderStyle, - AnnotationBorderStyleType */ + AnnotationBorderStyleType, AnnotationFlag */ 'use strict'; describe('Annotation layer', function() { describe('Annotation', function() { + it('should set and get flags', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setFlags(13); + + expect(annotation.hasFlag(AnnotationFlag.INVISIBLE)).toEqual(true); + expect(annotation.hasFlag(AnnotationFlag.NOZOOM)).toEqual(true); + expect(annotation.hasFlag(AnnotationFlag.PRINT)).toEqual(true); + expect(annotation.hasFlag(AnnotationFlag.READONLY)).toEqual(false); + }); + + it('should be viewable and not printable by default', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + + expect(annotation.viewable).toEqual(true); + expect(annotation.printable).toEqual(false); + }); + it('should set and get a valid rectangle', function() { var dict = new Dict(); dict.set('Subtype', '');