Skip to content

Commit

Permalink
Refactor annotation code to use a factory
Browse files Browse the repository at this point in the history
Currently, `src/core/core.js` uses the `fromRef` method on an `Annotation` object to obtain the right annotation type object (such as `LinkAnnotation` or `TextAnnotation`). That method in turn uses a method `getConstructor` to find out which annotation type object must be returned.

Aside from the fact that there is currently a lot of code to achieve this, these methods should not be part of the base `Annotation` class at all. Creation of annotation object should be done by a factory (as also recommended by @yurydelendik at mozilla#5218 (comment)) that handles finding out the correct annotation type object and returning it. This patch implements this separation of concerns.

Doing this allows us to also simplify the code quite a bit and to make it more readable. Additionally, we are now able to get rid of the hardcoded array of supported annotation types. The factory takes care of checking the annotation types and falls back to returning the base annotation type (and issuing a warning, which the current code also does not do well) when an annotation type is unsupported.

I have manually tested this commit with 20 test PDFs with different annotation types, such as /Link, /Text, /Widget, /FileAttachment and /FreeText. All render identically before and after the patch, and unsupported annotation types are now properly indicated with a warning in the console.
  • Loading branch information
timvandermeij committed Jul 26, 2015
1 parent d08895d commit 357227f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 74 deletions.
126 changes: 54 additions & 72 deletions src/core/annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,57 @@
'use strict';

var DEFAULT_ICON_SIZE = 22; // px
var SUPPORTED_TYPES = ['Link', 'Text', 'Widget'];

/**
* @constructor
*/
function AnnotationFactory() {}
AnnotationFactory.prototype = {
/**
* @param {XRef} xref
* @param {Object} ref
* @returns {Annotation}
*/
create: function AnnotationFactory_create(xref, ref) {
var dict = xref.fetchIfRef(ref);
if (!isDict(dict)) {
return;
}

// Determine the annotation's subtype
var subtype = dict.get('Subtype');
subtype = isName(subtype) ? subtype.name : '';

// Determine the annotation's field type
var fieldType = Util.getInheritableProperty(dict, 'FT');
fieldType = isName(fieldType) ? fieldType.name : '';

// Return the correct annotation object based on the subtype and field type
var parameters = {
dict: dict,
ref: ref
};

switch (subtype) {
case 'Link':
return new LinkAnnotation(parameters);

case 'Text':
return new TextAnnotation(parameters);

case 'Widget':
if (fieldType === 'Tx') {
return new TextWidgetAnnotation(parameters);
}
return new WidgetAnnotation(parameters);

default:
warn('Unimplemented annotation type "' + subtype + '", ' +
'falling back to base annotation');
return new Annotation(parameters);
}
}
};

var Annotation = (function AnnotationClosure() {
// 12.5.5: Algorithm: Appearance streams
Expand Down Expand Up @@ -193,13 +243,9 @@ var Annotation = (function AnnotationClosure() {

isInvisible: function Annotation_isInvisible() {
var data = this.data;
if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) {
return false;
} else {
return !!(data &&
data.annotationFlags && // Default: not invisible
data.annotationFlags & 0x1); // Invisible
}
return !!(data &&
data.annotationFlags && // Default: not invisible
data.annotationFlags & 0x1); // Invisible
},

isViewable: function Annotation_isViewable() {
Expand Down Expand Up @@ -275,70 +321,6 @@ var Annotation = (function AnnotationClosure() {
}
};

Annotation.getConstructor =
function Annotation_getConstructor(subtype, fieldType) {

if (!subtype) {
return;
}

// TODO(mack): Implement FreeText annotations
if (subtype === 'Link') {
return LinkAnnotation;
} else if (subtype === 'Text') {
return TextAnnotation;
} else if (subtype === 'Widget') {
if (!fieldType) {
return;
}

if (fieldType === 'Tx') {
return TextWidgetAnnotation;
} else {
return WidgetAnnotation;
}
} else {
return Annotation;
}
};

Annotation.fromRef = function Annotation_fromRef(xref, ref) {

var dict = xref.fetchIfRef(ref);
if (!isDict(dict)) {
return;
}

var subtype = dict.get('Subtype');
subtype = isName(subtype) ? subtype.name : '';
if (!subtype) {
return;
}

var fieldType = Util.getInheritableProperty(dict, 'FT');
fieldType = isName(fieldType) ? fieldType.name : '';

var Constructor = Annotation.getConstructor(subtype, fieldType);
if (!Constructor) {
return;
}

var params = {
dict: dict,
ref: ref,
};

var annotation = new Constructor(params);

if (annotation.isViewable() || annotation.isPrintable()) {
return annotation;
} else {
if (SUPPORTED_TYPES.indexOf(subtype) === -1) {
warn('unimplemented annotation type: ' + subtype);
}
}
};

Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
annotations, opList, pdfManager, partialEvaluator, intent) {

Expand Down
5 changes: 3 additions & 2 deletions src/core/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,11 @@ var Page = (function PageClosure() {
get annotations() {
var annotations = [];
var annotationRefs = this.getInheritedPageProp('Annots') || [];
var annotationFactory = new AnnotationFactory();
for (var i = 0, n = annotationRefs.length; i < n; ++i) {
var annotationRef = annotationRefs[i];
var annotation = Annotation.fromRef(this.xref, annotationRef);
if (annotation) {
var annotation = annotationFactory.create(this.xref, annotationRef);
if (annotation && (annotation.isViewable() || annotation.isPrintable())) {
annotations.push(annotation);
}
}
Expand Down

0 comments on commit 357227f

Please sign in to comment.