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.
This commit is contained in:
Tim van der Meij 2015-11-21 23:25:17 +01:00
parent df46b64045
commit 0991c06395
4 changed files with 107 additions and 44 deletions

View File

@ -15,7 +15,8 @@
/* globals PDFJS, Util, isDict, isName, stringToPDFString, warn, Dict, Stream, /* globals PDFJS, Util, isDict, isName, stringToPDFString, warn, Dict, Stream,
stringToBytes, Promise, isArray, ObjectLoader, OperatorList, stringToBytes, Promise, isArray, ObjectLoader, OperatorList,
isValidUrl, OPS, createPromiseCapability, AnnotationType, isValidUrl, OPS, createPromiseCapability, AnnotationType,
stringToUTF8String, AnnotationBorderStyleType, ColorSpace */ stringToUTF8String, AnnotationBorderStyleType, ColorSpace,
AnnotationFlag, isInt */
'use strict'; 'use strict';
@ -121,7 +122,8 @@ var Annotation = (function AnnotationClosure() {
var data = this.data = {}; var data = this.data = {};
data.subtype = dict.get('Subtype').name; data.subtype = dict.get('Subtype').name;
data.annotationFlags = dict.get('F');
this.setFlags(dict.get('F'));
this.setRectangle(dict.get('Rect')); this.setRectangle(dict.get('Rect'));
data.rect = this.rectangle; data.rect = this.rectangle;
@ -138,6 +140,64 @@ var Annotation = (function AnnotationClosure() {
} }
Annotation.prototype = { 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. * 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) { loadResources: function Annotation_loadResources(keys) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
this.appearance.dict.getAsync('Resources').then(function (resources) { this.appearance.dict.getAsync('Resources').then(function (resources) {
@ -329,8 +363,8 @@ var Annotation = (function AnnotationClosure() {
var annotationPromises = []; var annotationPromises = [];
for (var i = 0, n = annotations.length; i < n; ++i) { for (var i = 0, n = annotations.length; i < n; ++i) {
if (intent === 'display' && annotations[i].isViewable() || if (intent === 'display' && annotations[i].viewable ||
intent === 'print' && annotations[i].isPrintable()) { intent === 'print' && annotations[i].printable) {
annotationPromises.push( annotationPromises.push(
annotations[i].getOperatorList(partialEvaluator, task)); annotations[i].getOperatorList(partialEvaluator, task));
} }
@ -506,6 +540,12 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty; 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 // Building the full field name by collecting the field and
// its ancestors 'T' data and joining them using '.'. // its ancestors 'T' data and joining them using '.'.
var fieldName = []; var fieldName = [];
@ -539,17 +579,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
data.fullName = fieldName.join('.'); data.fullName = fieldName.join('.');
} }
var parent = Annotation.prototype; Util.inherit(WidgetAnnotation, Annotation, {});
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);
}
});
return WidgetAnnotation; return WidgetAnnotation;
})(); })();

View File

@ -268,8 +268,7 @@ var Page = (function PageClosure() {
for (var i = 0, n = annotationRefs.length; i < n; ++i) { for (var i = 0, n = annotationRefs.length; i < n; ++i) {
var annotationRef = annotationRefs[i]; var annotationRef = annotationRefs[i];
var annotation = annotationFactory.create(this.xref, annotationRef); var annotation = annotationFactory.create(this.xref, annotationRef);
if (annotation && if (annotation && (annotation.viewable || annotation.printable)) {
(annotation.isViewable() || annotation.isPrintable())) {
annotations.push(annotation); annotations.push(annotation);
} }
} }

View File

@ -48,6 +48,19 @@ var AnnotationType = {
LINK: 3 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 = { var AnnotationBorderStyleType = {
SOLID: 1, SOLID: 1,
DASHED: 2, DASHED: 2,

View File

@ -1,10 +1,31 @@
/* globals expect, it, describe, Dict, Name, Annotation, AnnotationBorderStyle, /* globals expect, it, describe, Dict, Name, Annotation, AnnotationBorderStyle,
AnnotationBorderStyleType */ AnnotationBorderStyleType, AnnotationFlag */
'use strict'; 'use strict';
describe('Annotation layer', function() { describe('Annotation layer', function() {
describe('Annotation', 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() { it('should set and get a valid rectangle', function() {
var dict = new Dict(); var dict = new Dict();
dict.set('Subtype', ''); dict.set('Subtype', '');