Skip to content

Commit

Permalink
Refactor annotation flags code
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
timvandermeij committed Nov 21, 2015
1 parent df46b64 commit 011b620
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 44 deletions.
110 changes: 69 additions & 41 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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;
Expand All @@ -138,6 +140,62 @@ 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);
}
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.
*
Expand Down Expand Up @@ -237,32 +295,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) {
Expand Down Expand Up @@ -329,8 +361,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));
}
Expand Down Expand Up @@ -506,6 +538,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 = [];
Expand Down Expand Up @@ -539,17 +577,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;
})();
Expand Down
3 changes: 1 addition & 2 deletions src/core/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/shared/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 22 additions & 1 deletion test/unit/annotation_layer_spec.js
Original file line number Diff line number Diff line change
@@ -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', '');
Expand Down

0 comments on commit 011b620

Please sign in to comment.