From 24d741d045ba5b5296da042c65718a363a400c33 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 26 Aug 2017 23:49:11 +0200 Subject: [PATCH 1/5] Convert `src/core/annotation.js` to ES6 syntax --- src/core/annotation.js | 1242 +++++++++++++++++++--------------------- 1 file changed, 581 insertions(+), 661 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index b1320287c..f036cd571 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -24,12 +24,7 @@ import { ColorSpace } from './colorspace'; import { OperatorList } from './evaluator'; import { Stream } from './stream'; -/** - * @class - * @alias AnnotationFactory - */ -function AnnotationFactory() {} -AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ { +class AnnotationFactory { /** * @param {XRef} xref * @param {Object} ref @@ -37,19 +32,19 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ { * @param {Object} idFactory * @returns {Annotation} */ - create: function AnnotationFactory_create(xref, ref, pdfManager, idFactory) { - var dict = xref.fetchIfRef(ref); + create(xref, ref, pdfManager, idFactory) { + let dict = xref.fetchIfRef(ref); if (!isDict(dict)) { return; } - var id = isRef(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); + let id = isRef(ref) ? ref.toString() : 'annot_' + idFactory.createObjId(); // Determine the annotation's subtype. - var subtype = dict.get('Subtype'); + let subtype = dict.get('Subtype'); subtype = isName(subtype) ? subtype.name : null; // Return the right annotation object based on the subtype and field type. - var parameters = { + let parameters = { xref, dict, ref: isRef(ref) ? ref : null, @@ -66,7 +61,7 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ { return new TextAnnotation(parameters); case 'Widget': - var fieldType = Util.getInheritableProperty(dict, 'FT'); + let fieldType = Util.getInheritableProperty(dict, 'FT'); fieldType = isName(fieldType) ? fieldType.name : null; switch (fieldType) { @@ -111,38 +106,38 @@ AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ { } return new Annotation(parameters); } - }, -}; + } +} -var Annotation = (function AnnotationClosure() { +function getTransformMatrix(rect, bbox, matrix) { // 12.5.5: Algorithm: Appearance streams - function getTransformMatrix(rect, bbox, matrix) { - var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); - var minX = bounds[0]; - var minY = bounds[1]; - var maxX = bounds[2]; - var maxY = bounds[3]; + let bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); + let minX = bounds[0]; + let minY = bounds[1]; + let maxX = bounds[2]; + let maxY = bounds[3]; - if (minX === maxX || minY === maxY) { - // From real-life file, bbox was [0, 0, 0, 0]. In this case, - // just apply the transform for rect - return [1, 0, 0, 1, rect[0], rect[1]]; - } - - var xRatio = (rect[2] - rect[0]) / (maxX - minX); - var yRatio = (rect[3] - rect[1]) / (maxY - minY); - return [ - xRatio, - 0, - 0, - yRatio, - rect[0] - minX * xRatio, - rect[1] - minY * yRatio - ]; + if (minX === maxX || minY === maxY) { + // From real-life file, bbox was [0, 0, 0, 0]. In this case, + // just apply the transform for rect + return [1, 0, 0, 1, rect[0], rect[1]]; } - function Annotation(params) { - var dict = params.dict; + let xRatio = (rect[2] - rect[0]) / (maxX - minX); + let yRatio = (rect[3] - rect[1]) / (maxY - minY); + return [ + xRatio, + 0, + 0, + yRatio, + rect[0] - minX * xRatio, + rect[1] - minY * yRatio + ]; +} + +class Annotation { + constructor(params) { + let dict = params.dict; this.setFlags(dict.get('F')); this.setRectangle(dict.getArray('Rect')); @@ -151,306 +146,296 @@ var Annotation = (function AnnotationClosure() { this.setAppearance(dict); // Expose public properties using a data object. - this.data = {}; - this.data.id = params.id; - this.data.subtype = params.subtype; - this.data.annotationFlags = this.flags; - this.data.rect = this.rectangle; - this.data.color = this.color; - this.data.borderStyle = this.borderStyle; - this.data.hasAppearance = !!this.appearance; + this.data = { + annotationFlags: this.flags, + borderStyle: this.borderStyle, + color: this.color, + hasAppearance: !!this.appearance, + id: params.id, + rect: this.rectangle, + subtype: params.subtype, + }; } - Annotation.prototype = { - /** - * @private - */ - _hasFlag: function Annotation_hasFlag(flags, flag) { - return !!(flags & flag); - }, + /** + * @private + */ + _hasFlag(flags, flag) { + return !!(flags & flag); + } - /** - * @private - */ - _isViewable: function Annotation_isViewable(flags) { - return !this._hasFlag(flags, AnnotationFlag.INVISIBLE) && - !this._hasFlag(flags, AnnotationFlag.HIDDEN) && - !this._hasFlag(flags, AnnotationFlag.NOVIEW); - }, + /** + * @private + */ + _isViewable(flags) { + return !this._hasFlag(flags, AnnotationFlag.INVISIBLE) && + !this._hasFlag(flags, AnnotationFlag.HIDDEN) && + !this._hasFlag(flags, AnnotationFlag.NOVIEW); + } - /** - * @private - */ - _isPrintable: function AnnotationFlag_isPrintable(flags) { - return this._hasFlag(flags, AnnotationFlag.PRINT) && - !this._hasFlag(flags, AnnotationFlag.INVISIBLE) && - !this._hasFlag(flags, AnnotationFlag.HIDDEN); - }, + /** + * @private + */ + _isPrintable(flags) { + return this._hasFlag(flags, AnnotationFlag.PRINT) && + !this._hasFlag(flags, AnnotationFlag.INVISIBLE) && + !this._hasFlag(flags, AnnotationFlag.HIDDEN); + } - /** - * @return {boolean} - */ - get viewable() { - if (this.flags === 0) { - return true; - } - return this._isViewable(this.flags); - }, + /** + * @return {boolean} + */ + get viewable() { + if (this.flags === 0) { + return true; + } + return this._isViewable(this.flags); + } - /** - * @return {boolean} - */ - get printable() { - if (this.flags === 0) { - return false; - } - return this._isPrintable(this.flags); - }, + /** + * @return {boolean} + */ + get printable() { + if (this.flags === 0) { + return false; + } + return this._isPrintable(this.flags); + } - /** - * 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) { - this.flags = (isInt(flags) && flags > 0) ? flags : 0; - }, + /** + * Set the flags. + * + * @public + * @memberof Annotation + * @param {number} flags - Unsigned 32-bit integer specifying annotation + * characteristics + * @see {@link shared/util.js} + */ + setFlags(flags) { + this.flags = (isInt(flags) && flags > 0) ? 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) { - return this._hasFlag(this.flags, flag); - }, + /** + * 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(flag) { + return this._hasFlag(this.flags, flag); + } - /** - * Set the rectangle. - * - * @public - * @memberof Annotation - * @param {Array} rectangle - The rectangle array with exactly four entries - */ - setRectangle: function Annotation_setRectangle(rectangle) { - if (isArray(rectangle) && rectangle.length === 4) { - this.rectangle = Util.normalizeRect(rectangle); - } else { - this.rectangle = [0, 0, 0, 0]; - } - }, + /** + * Set the rectangle. + * + * @public + * @memberof Annotation + * @param {Array} rectangle - The rectangle array with exactly four entries + */ + setRectangle(rectangle) { + if (isArray(rectangle) && rectangle.length === 4) { + this.rectangle = Util.normalizeRect(rectangle); + } else { + this.rectangle = [0, 0, 0, 0]; + } + } - /** - * Set the color and take care of color space conversion. - * - * @public - * @memberof Annotation - * @param {Array} color - The color array containing either 0 - * (transparent), 1 (grayscale), 3 (RGB) or - * 4 (CMYK) elements - */ - setColor: function Annotation_setColor(color) { - var rgbColor = new Uint8Array(3); // Black in RGB color space (default) - if (!isArray(color)) { + /** + * Set the color and take care of color space conversion. + * + * @public + * @memberof Annotation + * @param {Array} color - The color array containing either 0 + * (transparent), 1 (grayscale), 3 (RGB) or + * 4 (CMYK) elements + */ + setColor(color) { + let rgbColor = new Uint8Array(3); // Black in RGB color space (default) + if (!isArray(color)) { + this.color = rgbColor; + return; + } + + switch (color.length) { + case 0: // Transparent, which we indicate with a null value + this.color = null; + break; + + case 1: // Convert grayscale to RGB + ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); this.color = rgbColor; - return; + break; + + case 3: // Convert RGB percentages to RGB + ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + + case 4: // Convert CMYK to RGB + ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + + default: + this.color = rgbColor; + break; + } + } + + /** + * Set the border style (as AnnotationBorderStyle object). + * + * @public + * @memberof Annotation + * @param {Dict} borderStyle - The border style dictionary + */ + setBorderStyle(borderStyle) { + this.borderStyle = new AnnotationBorderStyle(); + if (!isDict(borderStyle)) { + return; + } + if (borderStyle.has('BS')) { + let dict = borderStyle.get('BS'); + let dictType = dict.get('Type'); + + if (!dictType || isName(dictType, 'Border')) { + this.borderStyle.setWidth(dict.get('W')); + this.borderStyle.setStyle(dict.get('S')); + this.borderStyle.setDashArray(dict.getArray('D')); } + } else if (borderStyle.has('Border')) { + let array = borderStyle.getArray('Border'); + if (isArray(array) && array.length >= 3) { + this.borderStyle.setHorizontalCornerRadius(array[0]); + this.borderStyle.setVerticalCornerRadius(array[1]); + this.borderStyle.setWidth(array[2]); - switch (color.length) { - case 0: // Transparent, which we indicate with a null value - this.color = null; - break; - - case 1: // Convert grayscale to RGB - ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - - case 3: // Convert RGB percentages to RGB - ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - - case 4: // Convert CMYK to RGB - ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); - this.color = rgbColor; - break; - - default: - this.color = rgbColor; - break; - } - }, - - /** - * Set the border style (as AnnotationBorderStyle object). - * - * @public - * @memberof Annotation - * @param {Dict} borderStyle - The border style dictionary - */ - setBorderStyle: function Annotation_setBorderStyle(borderStyle) { - this.borderStyle = new AnnotationBorderStyle(); - if (!isDict(borderStyle)) { - return; - } - if (borderStyle.has('BS')) { - var dict = borderStyle.get('BS'); - var dictType = dict.get('Type'); - - if (!dictType || isName(dictType, 'Border')) { - this.borderStyle.setWidth(dict.get('W')); - this.borderStyle.setStyle(dict.get('S')); - this.borderStyle.setDashArray(dict.getArray('D')); + if (array.length === 4) { // Dash array available + this.borderStyle.setDashArray(array[3]); } - } else if (borderStyle.has('Border')) { - var array = borderStyle.getArray('Border'); - if (isArray(array) && array.length >= 3) { - this.borderStyle.setHorizontalCornerRadius(array[0]); - this.borderStyle.setVerticalCornerRadius(array[1]); - this.borderStyle.setWidth(array[2]); - - if (array.length === 4) { // Dash array available - this.borderStyle.setDashArray(array[3]); - } - } - } else { - // There are no border entries in the dictionary. According to the - // specification, we should draw a solid border of width 1 in that - // case, but Adobe Reader did not implement that part of the - // specification and instead draws no border at all, so we do the same. - // See also https://github.com/mozilla/pdf.js/issues/6179. - this.borderStyle.setWidth(0); } - }, + } else { + // There are no border entries in the dictionary. According to the + // specification, we should draw a solid border of width 1 in that + // case, but Adobe Reader did not implement that part of the + // specification and instead draws no border at all, so we do the same. + // See also https://github.com/mozilla/pdf.js/issues/6179. + this.borderStyle.setWidth(0); + } + } - /** - * Set the (normal) appearance. - * - * @public - * @memberof Annotation - * @param {Dict} dict - The annotation's data dictionary - */ - setAppearance: function Annotation_setAppearance(dict) { - this.appearance = null; + /** + * Set the (normal) appearance. + * + * @public + * @memberof Annotation + * @param {Dict} dict - The annotation's data dictionary + */ + setAppearance(dict) { + this.appearance = null; - var appearanceStates = dict.get('AP'); - if (!isDict(appearanceStates)) { + let appearanceStates = dict.get('AP'); + if (!isDict(appearanceStates)) { + return; + } + + // In case the normal appearance is a stream, then it is used directly. + let normalAppearanceState = appearanceStates.get('N'); + if (isStream(normalAppearanceState)) { + this.appearance = normalAppearanceState; + return; + } + if (!isDict(normalAppearanceState)) { + return; + } + + // In case the normal appearance is a dictionary, the `AS` entry provides + // the key of the stream in this dictionary. + let as = dict.get('AS'); + if (!isName(as) || !normalAppearanceState.has(as.name)) { + return; + } + this.appearance = normalAppearanceState.get(as.name); + } + + /** + * Prepare the annotation for working with a popup in the display layer. + * + * @private + * @memberof Annotation + * @param {Dict} dict - The annotation's data dictionary + */ + _preparePopup(dict) { + if (!dict.has('C')) { + // Fall back to the default background color. + this.data.color = null; + } + + this.data.hasPopup = dict.has('Popup'); + this.data.title = stringToPDFString(dict.get('T') || ''); + this.data.contents = stringToPDFString(dict.get('Contents') || ''); + } + + loadResources(keys) { + return this.appearance.dict.getAsync('Resources').then((resources) => { + if (!resources) { return; } + let objectLoader = new ObjectLoader(resources, keys, resources.xref); - // In case the normal appearance is a stream, then it is used directly. - var normalAppearanceState = appearanceStates.get('N'); - if (isStream(normalAppearanceState)) { - this.appearance = normalAppearanceState; - return; - } - if (!isDict(normalAppearanceState)) { - return; - } - - // In case the normal appearance is a dictionary, the `AS` entry provides - // the key of the stream in this dictionary. - var as = dict.get('AS'); - if (!isName(as) || !normalAppearanceState.has(as.name)) { - return; - } - this.appearance = normalAppearanceState.get(as.name); - }, - - /** - * Prepare the annotation for working with a popup in the display layer. - * - * @private - * @memberof Annotation - * @param {Dict} dict - The annotation's data dictionary - */ - _preparePopup: function Annotation_preparePopup(dict) { - if (!dict.has('C')) { - // Fall back to the default background color. - this.data.color = null; - } - - this.data.hasPopup = dict.has('Popup'); - this.data.title = stringToPDFString(dict.get('T') || ''); - this.data.contents = stringToPDFString(dict.get('Contents') || ''); - }, - - loadResources: function Annotation_loadResources(keys) { - return this.appearance.dict.getAsync('Resources').then((resources) => { - if (!resources) { - return; - } - let objectLoader = new ObjectLoader(resources, keys, resources.xref); - - return objectLoader.load().then(function() { - return resources; - }); + return objectLoader.load().then(function() { + return resources; }); - }, + }); + } - getOperatorList: function Annotation_getOperatorList(evaluator, task, - renderForms) { - if (!this.appearance) { - return Promise.resolve(new OperatorList()); - } + getOperatorList(evaluator, task, renderForms) { + if (!this.appearance) { + return Promise.resolve(new OperatorList()); + } - var data = this.data; - var appearanceDict = this.appearance.dict; - var resourcesPromise = this.loadResources([ - 'ExtGState', - 'ColorSpace', - 'Pattern', - 'Shading', - 'XObject', - 'Font' - // ProcSet - // Properties - ]); - var bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; - var matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; - var transform = getTransformMatrix(data.rect, bbox, matrix); + let data = this.data; + let appearanceDict = this.appearance.dict; + let resourcesPromise = this.loadResources([ + 'ExtGState', + 'ColorSpace', + 'Pattern', + 'Shading', + 'XObject', + 'Font', + // ProcSet + // Properties + ]); + let bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; + let matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; + let transform = getTransformMatrix(data.rect, bbox, matrix); - return resourcesPromise.then((resources) => { - var opList = new OperatorList(); - opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); - return evaluator.getOperatorList({ - stream: this.appearance, - task, - resources, - operatorList: opList, - }).then(() => { - opList.addOp(OPS.endAnnotation, []); - this.appearance.reset(); - return opList; - }); + return resourcesPromise.then((resources) => { + let opList = new OperatorList(); + opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); + return evaluator.getOperatorList({ + stream: this.appearance, + task, + resources, + operatorList: opList, + }).then(() => { + opList.addOp(OPS.endAnnotation, []); + this.appearance.reset(); + return opList; }); - }, - }; - - return Annotation; -})(); + }); + } +} /** * Contains all data regarding an annotation's border style. - * - * @class */ -var AnnotationBorderStyle = (function AnnotationBorderStyleClosure() { - /** - * @constructor - * @private - */ - function AnnotationBorderStyle() { +class AnnotationBorderStyle { + constructor() { this.width = 1; this.style = AnnotationBorderStyleType.SOLID; this.dashArray = [3]; @@ -458,132 +443,126 @@ var AnnotationBorderStyle = (function AnnotationBorderStyleClosure() { this.verticalCornerRadius = 0; } - AnnotationBorderStyle.prototype = { - /** - * Set the width. - * - * @public - * @memberof AnnotationBorderStyle - * @param {integer} width - The width - */ - setWidth: function AnnotationBorderStyle_setWidth(width) { - if (width === (width | 0)) { - this.width = width; - } - }, + /** + * Set the width. + * + * @public + * @memberof AnnotationBorderStyle + * @param {integer} width - The width + */ + setWidth(width) { + if (width === (width | 0)) { + this.width = width; + } + } - /** - * Set the style. - * - * @public - * @memberof AnnotationBorderStyle - * @param {Object} style - The style object - * @see {@link shared/util.js} - */ - setStyle: function AnnotationBorderStyle_setStyle(style) { - if (!style) { - return; - } - switch (style.name) { - case 'S': - this.style = AnnotationBorderStyleType.SOLID; + /** + * Set the style. + * + * @public + * @memberof AnnotationBorderStyle + * @param {Object} style - The style object + * @see {@link shared/util.js} + */ + setStyle(style) { + if (!style) { + return; + } + switch (style.name) { + case 'S': + this.style = AnnotationBorderStyleType.SOLID; + break; + + case 'D': + this.style = AnnotationBorderStyleType.DASHED; + break; + + case 'B': + this.style = AnnotationBorderStyleType.BEVELED; + break; + + case 'I': + this.style = AnnotationBorderStyleType.INSET; + break; + + case 'U': + this.style = AnnotationBorderStyleType.UNDERLINE; + break; + + default: + break; + } + } + + /** + * Set the dash array. + * + * @public + * @memberof AnnotationBorderStyle + * @param {Array} dashArray - The dash array with at least one element + */ + setDashArray(dashArray) { + // We validate the dash array, but we do not use it because CSS does not + // allow us to change spacing of dashes. For more information, visit + // http://www.w3.org/TR/css3-background/#the-border-style. + if (isArray(dashArray) && dashArray.length > 0) { + // According to the PDF specification: the elements in `dashArray` + // shall be numbers that are nonnegative and not all equal to zero. + let isValid = true; + let allZeros = true; + for (let i = 0, len = dashArray.length; i < len; i++) { + let element = dashArray[i]; + let validNumber = (+element >= 0); + if (!validNumber) { + isValid = false; break; - - case 'D': - this.style = AnnotationBorderStyleType.DASHED; - break; - - case 'B': - this.style = AnnotationBorderStyleType.BEVELED; - break; - - case 'I': - this.style = AnnotationBorderStyleType.INSET; - break; - - case 'U': - this.style = AnnotationBorderStyleType.UNDERLINE; - break; - - default: - break; - } - }, - - /** - * Set the dash array. - * - * @public - * @memberof AnnotationBorderStyle - * @param {Array} dashArray - The dash array with at least one element - */ - setDashArray: function AnnotationBorderStyle_setDashArray(dashArray) { - // We validate the dash array, but we do not use it because CSS does not - // allow us to change spacing of dashes. For more information, visit - // http://www.w3.org/TR/css3-background/#the-border-style. - if (isArray(dashArray) && dashArray.length > 0) { - // According to the PDF specification: the elements in a dashArray - // shall be numbers that are nonnegative and not all equal to zero. - var isValid = true; - var allZeros = true; - for (var i = 0, len = dashArray.length; i < len; i++) { - var element = dashArray[i]; - var validNumber = (+element >= 0); - if (!validNumber) { - isValid = false; - break; - } else if (element > 0) { - allZeros = false; - } + } else if (element > 0) { + allZeros = false; } - if (isValid && !allZeros) { - this.dashArray = dashArray; - } else { - this.width = 0; // Adobe behavior when the array is invalid. - } - } else if (dashArray) { + } + if (isValid && !allZeros) { + this.dashArray = dashArray; + } else { this.width = 0; // Adobe behavior when the array is invalid. } - }, + } else if (dashArray) { + this.width = 0; // Adobe behavior when the array is invalid. + } + } - /** - * Set the horizontal corner radius (from a Border dictionary). - * - * @public - * @memberof AnnotationBorderStyle - * @param {integer} radius - The horizontal corner radius - */ - setHorizontalCornerRadius: - function AnnotationBorderStyle_setHorizontalCornerRadius(radius) { - if (radius === (radius | 0)) { - this.horizontalCornerRadius = radius; - } - }, + /** + * Set the horizontal corner radius (from a Border dictionary). + * + * @public + * @memberof AnnotationBorderStyle + * @param {integer} radius - The horizontal corner radius + */ + setHorizontalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.horizontalCornerRadius = radius; + } + } - /** - * Set the vertical corner radius (from a Border dictionary). - * - * @public - * @memberof AnnotationBorderStyle - * @param {integer} radius - The vertical corner radius - */ - setVerticalCornerRadius: - function AnnotationBorderStyle_setVerticalCornerRadius(radius) { - if (radius === (radius | 0)) { - this.verticalCornerRadius = radius; - } - }, - }; + /** + * Set the vertical corner radius (from a Border dictionary). + * + * @public + * @memberof AnnotationBorderStyle + * @param {integer} radius - The vertical corner radius + */ + setVerticalCornerRadius(radius) { + if (radius === (radius | 0)) { + this.verticalCornerRadius = radius; + } + } +} - return AnnotationBorderStyle; -})(); +class WidgetAnnotation extends Annotation { + constructor(params) { + super(params); -var WidgetAnnotation = (function WidgetAnnotationClosure() { - function WidgetAnnotation(params) { - Annotation.call(this, params); - - var dict = params.dict; - var data = this.data; + let dict = params.dict; + let data = this.data; data.annotationType = AnnotationType.WIDGET; data.fieldName = this._constructFieldName(dict); @@ -591,7 +570,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { /* getArray = */ true); data.alternativeText = stringToPDFString(dict.get('TU') || ''); data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || ''; - var fieldType = Util.getInheritableProperty(dict, 'FT'); + let fieldType = Util.getInheritableProperty(dict, 'FT'); data.fieldType = isName(fieldType) ? fieldType.name : null; this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty; @@ -608,87 +587,83 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { } } - Util.inherit(WidgetAnnotation, Annotation, { - /** - * Construct the (fully qualified) field name from the (partial) field - * names of the field and its ancestors. - * - * @private - * @memberof WidgetAnnotation - * @param {Dict} dict - Complete widget annotation dictionary - * @return {string} - */ - _constructFieldName: function WidgetAnnotation_constructFieldName(dict) { - // Both the `Parent` and `T` fields are optional. While at least one of - // them should be provided, bad PDF generators may fail to do so. - if (!dict.has('T') && !dict.has('Parent')) { - warn('Unknown field name, falling back to empty field name.'); - return ''; + /** + * Construct the (fully qualified) field name from the (partial) field + * names of the field and its ancestors. + * + * @private + * @memberof WidgetAnnotation + * @param {Dict} dict - Complete widget annotation dictionary + * @return {string} + */ + _constructFieldName(dict) { + // Both the `Parent` and `T` fields are optional. While at least one of + // them should be provided, bad PDF generators may fail to do so. + if (!dict.has('T') && !dict.has('Parent')) { + warn('Unknown field name, falling back to empty field name.'); + return ''; + } + + // If no parent exists, the partial and fully qualified names are equal. + if (!dict.has('Parent')) { + return stringToPDFString(dict.get('T')); + } + + // Form the fully qualified field name by appending the partial name to + // the parent's fully qualified name, separated by a period. + let fieldName = []; + if (dict.has('T')) { + fieldName.unshift(stringToPDFString(dict.get('T'))); + } + + let loopDict = dict; + while (loopDict.has('Parent')) { + loopDict = loopDict.get('Parent'); + if (!isDict(loopDict)) { + // Even though it is not allowed according to the PDF specification, + // bad PDF generators may provide a `Parent` entry that is not a + // dictionary, but `null` for example (issue 8143). + break; } - // If no parent exists, the partial and fully qualified names are equal. - if (!dict.has('Parent')) { - return stringToPDFString(dict.get('T')); + if (loopDict.has('T')) { + fieldName.unshift(stringToPDFString(loopDict.get('T'))); } + } + return fieldName.join('.'); + } - // Form the fully qualified field name by appending the partial name to - // the parent's fully qualified name, separated by a period. - var fieldName = []; - if (dict.has('T')) { - fieldName.unshift(stringToPDFString(dict.get('T'))); - } + /** + * Check if a provided field flag is set. + * + * @public + * @memberof WidgetAnnotation + * @param {number} flag - Hexadecimal representation for an annotation + * field characteristic + * @return {boolean} + * @see {@link shared/util.js} + */ + hasFieldFlag(flag) { + return !!(this.data.fieldFlags & flag); + } +} - var loopDict = dict; - while (loopDict.has('Parent')) { - loopDict = loopDict.get('Parent'); - if (!isDict(loopDict)) { - // Even though it is not allowed according to the PDF specification, - // bad PDF generators may provide a `Parent` entry that is not a - // dictionary, but `null` for example (issue 8143). - break; - } - - if (loopDict.has('T')) { - fieldName.unshift(stringToPDFString(loopDict.get('T'))); - } - } - return fieldName.join('.'); - }, - - /** - * Check if a provided field flag is set. - * - * @public - * @memberof WidgetAnnotation - * @param {number} flag - Hexadecimal representation for an annotation - * field characteristic - * @return {boolean} - * @see {@link shared/util.js} - */ - hasFieldFlag: function WidgetAnnotation_hasFieldFlag(flag) { - return !!(this.data.fieldFlags & flag); - }, - }); - - return WidgetAnnotation; -})(); - -var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { - function TextWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); +class TextWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); // The field value is always a string. this.data.fieldValue = stringToPDFString(this.data.fieldValue || ''); // Determine the alignment of text in the field. - var alignment = Util.getInheritableProperty(params.dict, 'Q'); + let alignment = Util.getInheritableProperty(params.dict, 'Q'); if (!isInt(alignment) || alignment < 0 || alignment > 2) { alignment = null; } this.data.textAlignment = alignment; // Determine the maximum length of text in the field. - var maximumLength = Util.getInheritableProperty(params.dict, 'MaxLen'); + let maximumLength = Util.getInheritableProperty(params.dict, 'MaxLen'); if (!isInt(maximumLength) || maximumLength < 0) { maximumLength = null; } @@ -703,47 +678,41 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { this.data.maxLen !== null; } - Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { - getOperatorList: - function TextWidgetAnnotation_getOperatorList(evaluator, task, - renderForms) { - var operatorList = new OperatorList(); + getOperatorList(evaluator, task, renderForms) { + let operatorList = new OperatorList(); - // Do not render form elements on the canvas when interactive forms are - // enabled. The display layer is responsible for rendering them instead. - if (renderForms) { - return Promise.resolve(operatorList); - } + // Do not render form elements on the canvas when interactive forms are + // enabled. The display layer is responsible for rendering them instead. + if (renderForms) { + return Promise.resolve(operatorList); + } - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, - renderForms); - } + if (this.appearance) { + return Annotation.prototype.getOperatorList.call(this, evaluator, task, + renderForms); + } - // Even if there is an appearance stream, ignore it. This is the - // behaviour used by Adobe Reader. - if (!this.data.defaultAppearance) { - return Promise.resolve(operatorList); - } + // Even if there is an appearance stream, ignore it. This is the + // behaviour used by Adobe Reader. + if (!this.data.defaultAppearance) { + return Promise.resolve(operatorList); + } - var stream = new Stream(stringToBytes(this.data.defaultAppearance)); - return evaluator.getOperatorList({ - stream, - task, - resources: this.fieldResources, - operatorList, - }).then(function () { - return operatorList; - }); - }, - }); + let stream = new Stream(stringToBytes(this.data.defaultAppearance)); + return evaluator.getOperatorList({ + stream, + task, + resources: this.fieldResources, + operatorList, + }).then(function () { + return operatorList; + }); + } +} - return TextWidgetAnnotation; -})(); - -var ButtonWidgetAnnotation = (function ButtonWidgetAnnotationClosure() { - function ButtonWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); +class ButtonWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); this.data.checkBox = !this.hasFieldFlag(AnnotationFieldFlag.RADIO) && !this.hasFieldFlag(AnnotationFieldFlag.PUSHBUTTON); @@ -761,25 +730,25 @@ var ButtonWidgetAnnotation = (function ButtonWidgetAnnotationClosure() { // The parent field's `V` entry holds a `Name` object with the appearance // state of whichever child field is currently in the "on" state. - var fieldParent = params.dict.get('Parent'); + let fieldParent = params.dict.get('Parent'); if (isDict(fieldParent) && fieldParent.has('V')) { - var fieldParentValue = fieldParent.get('V'); + let fieldParentValue = fieldParent.get('V'); if (isName(fieldParentValue)) { this.data.fieldValue = fieldParentValue.name; } } // The button's value corresponds to its appearance state. - var appearanceStates = params.dict.get('AP'); + let appearanceStates = params.dict.get('AP'); if (!isDict(appearanceStates)) { return; } - var normalAppearanceState = appearanceStates.get('N'); + let normalAppearanceState = appearanceStates.get('N'); if (!isDict(normalAppearanceState)) { return; } - var keys = normalAppearanceState.getKeys(); - for (var i = 0, ii = keys.length; i < ii; i++) { + let keys = normalAppearanceState.getKeys(); + for (let i = 0, ii = keys.length; i < ii; i++) { if (keys[i] !== 'Off') { this.data.buttonValue = keys[i]; break; @@ -788,32 +757,26 @@ var ButtonWidgetAnnotation = (function ButtonWidgetAnnotationClosure() { } } - Util.inherit(ButtonWidgetAnnotation, WidgetAnnotation, { - getOperatorList: - function ButtonWidgetAnnotation_getOperatorList(evaluator, task, - renderForms) { - var operatorList = new OperatorList(); + getOperatorList(evaluator, task, renderForms) { + let operatorList = new OperatorList(); - // Do not render form elements on the canvas when interactive forms are - // enabled. The display layer is responsible for rendering them instead. - if (renderForms) { - return Promise.resolve(operatorList); - } - - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, - renderForms); - } + // Do not render form elements on the canvas when interactive forms are + // enabled. The display layer is responsible for rendering them instead. + if (renderForms) { return Promise.resolve(operatorList); - }, - }); + } - return ButtonWidgetAnnotation; -})(); + if (this.appearance) { + return Annotation.prototype.getOperatorList.call(this, evaluator, task, + renderForms); + } + return Promise.resolve(operatorList); + } +} -var ChoiceWidgetAnnotation = (function ChoiceWidgetAnnotationClosure() { - function ChoiceWidgetAnnotation(params) { - WidgetAnnotation.call(this, params); +class ChoiceWidgetAnnotation extends WidgetAnnotation { + constructor(params) { + super(params); // Determine the options. The options array may consist of strings or // arrays. If the array consists of arrays, then the first element of @@ -826,12 +789,12 @@ var ChoiceWidgetAnnotation = (function ChoiceWidgetAnnotationClosure() { // inherit the options from a parent annotation (issue 8094). this.data.options = []; - var options = Util.getInheritableProperty(params.dict, 'Opt'); + let options = Util.getInheritableProperty(params.dict, 'Opt'); if (isArray(options)) { - var xref = params.xref; - for (var i = 0, ii = options.length; i < ii; i++) { - var option = xref.fetchIfRef(options[i]); - var isOptionArray = isArray(option); + let xref = params.xref; + for (let i = 0, ii = options.length; i < ii; i++) { + let option = xref.fetchIfRef(options[i]); + let isOptionArray = isArray(option); this.data.options[i] = { exportValue: isOptionArray ? xref.fetchIfRef(option[0]) : option, @@ -852,31 +815,25 @@ var ChoiceWidgetAnnotation = (function ChoiceWidgetAnnotationClosure() { this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT); } - Util.inherit(ChoiceWidgetAnnotation, WidgetAnnotation, { - getOperatorList: - function ChoiceWidgetAnnotation_getOperatorList(evaluator, task, - renderForms) { - var operatorList = new OperatorList(); + getOperatorList(evaluator, task, renderForms) { + let operatorList = new OperatorList(); - // Do not render form elements on the canvas when interactive forms are - // enabled. The display layer is responsible for rendering them instead. - if (renderForms) { - return Promise.resolve(operatorList); - } + // Do not render form elements on the canvas when interactive forms are + // enabled. The display layer is responsible for rendering them instead. + if (renderForms) { + return Promise.resolve(operatorList); + } - return Annotation.prototype.getOperatorList.call(this, evaluator, task, - renderForms); - }, - }); + return Annotation.prototype.getOperatorList.call(this, evaluator, task, + renderForms); + } +} - return ChoiceWidgetAnnotation; -})(); +class TextAnnotation extends Annotation { + constructor(parameters) { + const DEFAULT_ICON_SIZE = 22; // px -var TextAnnotation = (function TextAnnotationClosure() { - var DEFAULT_ICON_SIZE = 22; // px - - function TextAnnotation(parameters) { - Annotation.call(this, parameters); + super(parameters); this.data.annotationType = AnnotationType.TEXT; @@ -890,45 +847,36 @@ var TextAnnotation = (function TextAnnotationClosure() { } this._preparePopup(parameters.dict); } +} - Util.inherit(TextAnnotation, Annotation, {}); +class LinkAnnotation extends Annotation { + constructor(params) { + super(params); - return TextAnnotation; -})(); - -var LinkAnnotation = (function LinkAnnotationClosure() { - function LinkAnnotation(params) { - Annotation.call(this, params); - - var data = this.data; - data.annotationType = AnnotationType.LINK; + this.data.annotationType = AnnotationType.LINK; Catalog.parseDestDictionary({ destDict: params.dict, - resultObj: data, + resultObj: this.data, docBaseUrl: params.pdfManager.docBaseUrl, }); } +} - Util.inherit(LinkAnnotation, Annotation, {}); - - return LinkAnnotation; -})(); - -var PopupAnnotation = (function PopupAnnotationClosure() { - function PopupAnnotation(parameters) { - Annotation.call(this, parameters); +class PopupAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = AnnotationType.POPUP; - var dict = parameters.dict; - var parentItem = dict.get('Parent'); + let dict = parameters.dict; + let parentItem = dict.get('Parent'); if (!parentItem) { warn('Popup annotation has a missing or invalid parent annotation.'); return; } - var parentSubtype = parentItem.get('Subtype'); + let parentSubtype = parentItem.get('Subtype'); this.data.parentType = isName(parentSubtype) ? parentSubtype.name : null; this.data.parentId = dict.getRaw('Parent').toString(); this.data.title = stringToPDFString(parentItem.get('T') || ''); @@ -946,101 +894,73 @@ var PopupAnnotation = (function PopupAnnotationClosure() { // that is most likely a bug. Fallback to inherit the flags from the parent // annotation (this is consistent with the behaviour in Adobe Reader). if (!this.viewable) { - var parentFlags = parentItem.get('F'); + let parentFlags = parentItem.get('F'); if (this._isViewable(parentFlags)) { this.setFlags(parentFlags); } } } +} - Util.inherit(PopupAnnotation, Annotation, {}); - - return PopupAnnotation; -})(); - -var LineAnnotation = (function LineAnnotationClosure() { - function LineAnnotation(parameters) { - Annotation.call(this, parameters); +class LineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = AnnotationType.LINE; - var dict = parameters.dict; + let dict = parameters.dict; this.data.lineCoordinates = Util.normalizeRect(dict.getArray('L')); this._preparePopup(dict); } +} - Util.inherit(LineAnnotation, Annotation, {}); - - return LineAnnotation; -})(); - -var HighlightAnnotation = (function HighlightAnnotationClosure() { - function HighlightAnnotation(parameters) { - Annotation.call(this, parameters); +class HighlightAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = AnnotationType.HIGHLIGHT; this._preparePopup(parameters.dict); } +} - Util.inherit(HighlightAnnotation, Annotation, {}); - - return HighlightAnnotation; -})(); - -var UnderlineAnnotation = (function UnderlineAnnotationClosure() { - function UnderlineAnnotation(parameters) { - Annotation.call(this, parameters); +class UnderlineAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = AnnotationType.UNDERLINE; this._preparePopup(parameters.dict); } +} - Util.inherit(UnderlineAnnotation, Annotation, {}); - - return UnderlineAnnotation; -})(); - -var SquigglyAnnotation = (function SquigglyAnnotationClosure() { - function SquigglyAnnotation(parameters) { - Annotation.call(this, parameters); +class SquigglyAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = AnnotationType.SQUIGGLY; this._preparePopup(parameters.dict); } +} - Util.inherit(SquigglyAnnotation, Annotation, {}); - - return SquigglyAnnotation; -})(); - -var StrikeOutAnnotation = (function StrikeOutAnnotationClosure() { - function StrikeOutAnnotation(parameters) { - Annotation.call(this, parameters); +class StrikeOutAnnotation extends Annotation { + constructor(parameters) { + super(parameters); this.data.annotationType = AnnotationType.STRIKEOUT; this._preparePopup(parameters.dict); } +} - Util.inherit(StrikeOutAnnotation, Annotation, {}); +class FileAttachmentAnnotation extends Annotation { + constructor(parameters) { + super(parameters); - return StrikeOutAnnotation; -})(); - -var FileAttachmentAnnotation = (function FileAttachmentAnnotationClosure() { - function FileAttachmentAnnotation(parameters) { - Annotation.call(this, parameters); - - var file = new FileSpec(parameters.dict.get('FS'), parameters.xref); + let file = new FileSpec(parameters.dict.get('FS'), parameters.xref); this.data.annotationType = AnnotationType.FILEATTACHMENT; this.data.file = file.serializable; this._preparePopup(parameters.dict); } - - Util.inherit(FileAttachmentAnnotation, Annotation, {}); - - return FileAttachmentAnnotation; -})(); +} export { Annotation, From af10f8b5866a7ec6a2ae1d988271b6ff8f70f5a4 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sun, 27 Aug 2017 00:24:27 +0200 Subject: [PATCH 2/5] Convert `src/display/annotation_layer.js` to ES6 syntax --- src/display/annotation_layer.js | 1756 ++++++++++++++----------------- 1 file changed, 794 insertions(+), 962 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 7166d0d4e..0b95e10d8 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -33,19 +33,13 @@ import { * @property {boolean} renderInteractiveForms */ -/** - * @class - * @alias AnnotationElementFactory - */ -function AnnotationElementFactory() {} -AnnotationElementFactory.prototype = - /** @lends AnnotationElementFactory.prototype */ { +class AnnotationElementFactory { /** * @param {AnnotationElementParameters} parameters * @returns {AnnotationElement} */ - create: function AnnotationElementFactory_create(parameters) { - var subtype = parameters.data.annotationType; + create(parameters) { + let subtype = parameters.data.annotationType; switch (subtype) { case AnnotationType.LINK: @@ -55,7 +49,7 @@ AnnotationElementFactory.prototype = return new TextAnnotationElement(parameters); case AnnotationType.WIDGET: - var fieldType = parameters.data.fieldType; + let fieldType = parameters.data.fieldType; switch (fieldType) { case 'Tx': @@ -97,16 +91,12 @@ AnnotationElementFactory.prototype = default: return new AnnotationElement(parameters); } - }, -}; + } +} -/** - * @class - * @alias AnnotationElement - */ -var AnnotationElement = (function AnnotationElementClosure() { - function AnnotationElement(parameters, isRenderable, ignoreBorder) { - this.isRenderable = isRenderable || false; +class AnnotationElement { + constructor(parameters, isRenderable = true, ignoreBorder = false) { + this.isRenderable = isRenderable; this.data = parameters.data; this.layer = parameters.layer; this.page = parameters.page; @@ -121,610 +111,517 @@ var AnnotationElement = (function AnnotationElementClosure() { } } - AnnotationElement.prototype = /** @lends AnnotationElement.prototype */ { - /** - * Create an empty container for the annotation's HTML element. - * - * @private - * @param {boolean} ignoreBorder - * @memberof AnnotationElement - * @returns {HTMLSectionElement} - */ - _createContainer: - function AnnotationElement_createContainer(ignoreBorder) { - var data = this.data, page = this.page, viewport = this.viewport; - var container = document.createElement('section'); - var width = data.rect[2] - data.rect[0]; - var height = data.rect[3] - data.rect[1]; + /** + * Create an empty container for the annotation's HTML element. + * + * @private + * @param {boolean} ignoreBorder + * @memberof AnnotationElement + * @returns {HTMLSectionElement} + */ + _createContainer(ignoreBorder = false) { + let data = this.data, page = this.page, viewport = this.viewport; + let container = document.createElement('section'); + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; - container.setAttribute('data-annotation-id', data.id); + container.setAttribute('data-annotation-id', data.id); - // Do *not* modify `data.rect`, since that will corrupt the annotation - // position on subsequent calls to `_createContainer` (see issue 6804). - var rect = Util.normalizeRect([ - data.rect[0], - page.view[3] - data.rect[1] + page.view[1], - data.rect[2], - page.view[3] - data.rect[3] + page.view[1] - ]); + // Do *not* modify `data.rect`, since that will corrupt the annotation + // position on subsequent calls to `_createContainer` (see issue 6804). + let rect = Util.normalizeRect([ + data.rect[0], + page.view[3] - data.rect[1] + page.view[1], + data.rect[2], + page.view[3] - data.rect[3] + page.view[1] + ]); - CustomStyle.setProp('transform', container, - 'matrix(' + viewport.transform.join(',') + ')'); - CustomStyle.setProp('transformOrigin', container, - -rect[0] + 'px ' + -rect[1] + 'px'); + CustomStyle.setProp('transform', container, + 'matrix(' + viewport.transform.join(',') + ')'); + CustomStyle.setProp('transformOrigin', container, + -rect[0] + 'px ' + -rect[1] + 'px'); - if (!ignoreBorder && data.borderStyle.width > 0) { - container.style.borderWidth = data.borderStyle.width + 'px'; - if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) { - // Underline styles only have a bottom border, so we do not need - // to adjust for all borders. This yields a similar result as - // Adobe Acrobat/Reader. - width = width - 2 * data.borderStyle.width; - height = height - 2 * data.borderStyle.width; - } - - var horizontalRadius = data.borderStyle.horizontalCornerRadius; - var verticalRadius = data.borderStyle.verticalCornerRadius; - if (horizontalRadius > 0 || verticalRadius > 0) { - var radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; - CustomStyle.setProp('borderRadius', container, radius); - } - - switch (data.borderStyle.style) { - case AnnotationBorderStyleType.SOLID: - container.style.borderStyle = 'solid'; - break; - - case AnnotationBorderStyleType.DASHED: - container.style.borderStyle = 'dashed'; - break; - - case AnnotationBorderStyleType.BEVELED: - warn('Unimplemented border style: beveled'); - break; - - case AnnotationBorderStyleType.INSET: - warn('Unimplemented border style: inset'); - break; - - case AnnotationBorderStyleType.UNDERLINE: - container.style.borderBottomStyle = 'solid'; - break; - - default: - break; - } - - if (data.color) { - container.style.borderColor = - Util.makeCssRgb(data.color[0] | 0, - data.color[1] | 0, - data.color[2] | 0); - } else { - // Transparent (invisible) border, so do not draw it at all. - container.style.borderWidth = 0; - } + if (!ignoreBorder && data.borderStyle.width > 0) { + container.style.borderWidth = data.borderStyle.width + 'px'; + if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) { + // Underline styles only have a bottom border, so we do not need + // to adjust for all borders. This yields a similar result as + // Adobe Acrobat/Reader. + width = width - 2 * data.borderStyle.width; + height = height - 2 * data.borderStyle.width; } - container.style.left = rect[0] + 'px'; - container.style.top = rect[1] + 'px'; - - container.style.width = width + 'px'; - container.style.height = height + 'px'; - - return container; - }, - - /** - * Create a popup for the annotation's HTML element. This is used for - * annotations that do not have a Popup entry in the dictionary, but - * are of a type that works with popups (such as Highlight annotations). - * - * @private - * @param {HTMLSectionElement} container - * @param {HTMLDivElement|HTMLImageElement|null} trigger - * @param {Object} data - * @memberof AnnotationElement - */ - _createPopup: - function AnnotationElement_createPopup(container, trigger, data) { - // If no trigger element is specified, create it. - if (!trigger) { - trigger = document.createElement('div'); - trigger.style.height = container.style.height; - trigger.style.width = container.style.width; - container.appendChild(trigger); + let horizontalRadius = data.borderStyle.horizontalCornerRadius; + let verticalRadius = data.borderStyle.verticalCornerRadius; + if (horizontalRadius > 0 || verticalRadius > 0) { + let radius = horizontalRadius + 'px / ' + verticalRadius + 'px'; + CustomStyle.setProp('borderRadius', container, radius); } - var popupElement = new PopupElement({ - container, - trigger, - color: data.color, - title: data.title, - contents: data.contents, - hideWrapper: true, - }); - var popup = popupElement.render(); + switch (data.borderStyle.style) { + case AnnotationBorderStyleType.SOLID: + container.style.borderStyle = 'solid'; + break; - // Position the popup next to the annotation's container. - popup.style.left = container.style.width; + case AnnotationBorderStyleType.DASHED: + container.style.borderStyle = 'dashed'; + break; - container.appendChild(popup); - }, + case AnnotationBorderStyleType.BEVELED: + warn('Unimplemented border style: beveled'); + break; - /** - * Render the annotation's HTML element in the empty container. - * - * @public - * @memberof AnnotationElement - */ - render: function AnnotationElement_render() { - throw new Error('Abstract method AnnotationElement.render called'); - }, - }; + case AnnotationBorderStyleType.INSET: + warn('Unimplemented border style: inset'); + break; - return AnnotationElement; -})(); + case AnnotationBorderStyleType.UNDERLINE: + container.style.borderBottomStyle = 'solid'; + break; -/** - * @class - * @alias LinkAnnotationElement - */ -var LinkAnnotationElement = (function LinkAnnotationElementClosure() { - function LinkAnnotationElement(parameters) { - AnnotationElement.call(this, parameters, true); - } - - Util.inherit(LinkAnnotationElement, AnnotationElement, { - /** - * Render the link annotation's HTML element in the empty container. - * - * @public - * @memberof LinkAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function LinkAnnotationElement_render() { - this.container.className = 'linkAnnotation'; - - var link = document.createElement('a'); - addLinkAttributes(link, { - url: this.data.url, - target: (this.data.newWindow ? LinkTarget.BLANK : undefined), - }); - - if (!this.data.url) { - if (this.data.action) { - this._bindNamedAction(link, this.data.action); - } else { - this._bindLink(link, this.data.dest); - } + default: + break; } - this.container.appendChild(link); - return this.container; - }, - - /** - * Bind internal links to the link element. - * - * @private - * @param {Object} link - * @param {Object} destination - * @memberof LinkAnnotationElement - */ - _bindLink(link, destination) { - link.href = this.linkService.getDestinationHash(destination); - link.onclick = () => { - if (destination) { - this.linkService.navigateTo(destination); - } - return false; - }; - if (destination) { - link.className = 'internalLink'; - } - }, - - /** - * Bind named actions to the link element. - * - * @private - * @param {Object} link - * @param {Object} action - * @memberof LinkAnnotationElement - */ - _bindNamedAction(link, action) { - link.href = this.linkService.getAnchorUrl(''); - link.onclick = () => { - this.linkService.executeNamedAction(action); - return false; - }; - link.className = 'internalLink'; - }, - }); - - return LinkAnnotationElement; -})(); - -/** - * @class - * @alias TextAnnotationElement - */ -var TextAnnotationElement = (function TextAnnotationElementClosure() { - function TextAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || - parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable); - } - - Util.inherit(TextAnnotationElement, AnnotationElement, { - /** - * Render the text annotation's HTML element in the empty container. - * - * @public - * @memberof TextAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function TextAnnotationElement_render() { - this.container.className = 'textAnnotation'; - - var image = document.createElement('img'); - image.style.height = this.container.style.height; - image.style.width = this.container.style.width; - image.src = this.imageResourcesPath + 'annotation-' + - this.data.name.toLowerCase() + '.svg'; - image.alt = '[{{type}} Annotation]'; - image.dataset.l10nId = 'text_annotation_type'; - image.dataset.l10nArgs = JSON.stringify({ type: this.data.name, }); - - if (!this.data.hasPopup) { - this._createPopup(this.container, image, this.data); - } - - this.container.appendChild(image); - return this.container; - }, - }); - - return TextAnnotationElement; -})(); - -/** - * @class - * @alias WidgetAnnotationElement - */ -var WidgetAnnotationElement = (function WidgetAnnotationElementClosure() { - function WidgetAnnotationElement(parameters, isRenderable) { - AnnotationElement.call(this, parameters, isRenderable); - } - - Util.inherit(WidgetAnnotationElement, AnnotationElement, { - /** - * Render the widget annotation's HTML element in the empty container. - * - * @public - * @memberof WidgetAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function WidgetAnnotationElement_render() { - // Show only the container for unsupported field types. - return this.container; - }, - }); - - return WidgetAnnotationElement; -})(); - -/** - * @class - * @alias TextWidgetAnnotationElement - */ -var TextWidgetAnnotationElement = ( - function TextWidgetAnnotationElementClosure() { - var TEXT_ALIGNMENT = ['left', 'center', 'right']; - - function TextWidgetAnnotationElement(parameters) { - var isRenderable = parameters.renderInteractiveForms || - (!parameters.data.hasAppearance && !!parameters.data.fieldValue); - WidgetAnnotationElement.call(this, parameters, isRenderable); - } - - Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, { - /** - * Render the text widget annotation's HTML element in the empty container. - * - * @public - * @memberof TextWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function TextWidgetAnnotationElement_render() { - this.container.className = 'textWidgetAnnotation'; - - var element = null; - if (this.renderInteractiveForms) { - // NOTE: We cannot set the values using `element.value` below, since it - // prevents the AnnotationLayer rasterizer in `test/driver.js` - // from parsing the elements correctly for the reference tests. - if (this.data.multiLine) { - element = document.createElement('textarea'); - element.textContent = this.data.fieldValue; - } else { - element = document.createElement('input'); - element.type = 'text'; - element.setAttribute('value', this.data.fieldValue); - } - - element.disabled = this.data.readOnly; - - if (this.data.maxLen !== null) { - element.maxLength = this.data.maxLen; - } - - if (this.data.comb) { - var fieldWidth = this.data.rect[2] - this.data.rect[0]; - var combWidth = fieldWidth / this.data.maxLen; - - element.classList.add('comb'); - element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)'; - } + if (data.color) { + container.style.borderColor = Util.makeCssRgb(data.color[0] | 0, + data.color[1] | 0, + data.color[2] | 0); } else { - element = document.createElement('div'); + // Transparent (invisible) border, so do not draw it at all. + container.style.borderWidth = 0; + } + } + + container.style.left = rect[0] + 'px'; + container.style.top = rect[1] + 'px'; + + container.style.width = width + 'px'; + container.style.height = height + 'px'; + + return container; + } + + /** + * Create a popup for the annotation's HTML element. This is used for + * annotations that do not have a Popup entry in the dictionary, but + * are of a type that works with popups (such as Highlight annotations). + * + * @private + * @param {HTMLSectionElement} container + * @param {HTMLDivElement|HTMLImageElement|null} trigger + * @param {Object} data + * @memberof AnnotationElement + */ + _createPopup(container, trigger, data) { + // If no trigger element is specified, create it. + if (!trigger) { + trigger = document.createElement('div'); + trigger.style.height = container.style.height; + trigger.style.width = container.style.width; + container.appendChild(trigger); + } + + let popupElement = new PopupElement({ + container, + trigger, + color: data.color, + title: data.title, + contents: data.contents, + hideWrapper: true, + }); + let popup = popupElement.render(); + + // Position the popup next to the annotation's container. + popup.style.left = container.style.width; + + container.appendChild(popup); + } + + /** + * Render the annotation's HTML element in the empty container. + * + * @public + * @memberof AnnotationElement + */ + render() { + throw new Error('Abstract method `AnnotationElement.render` called'); + } +} + +class LinkAnnotationElement extends AnnotationElement { + /** + * Render the link annotation's HTML element in the empty container. + * + * @public + * @memberof LinkAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'linkAnnotation'; + + let link = document.createElement('a'); + addLinkAttributes(link, { + url: this.data.url, + target: (this.data.newWindow ? LinkTarget.BLANK : undefined), + }); + + if (!this.data.url) { + if (this.data.action) { + this._bindNamedAction(link, this.data.action); + } else { + this._bindLink(link, this.data.dest); + } + } + + this.container.appendChild(link); + return this.container; + } + + /** + * Bind internal links to the link element. + * + * @private + * @param {Object} link + * @param {Object} destination + * @memberof LinkAnnotationElement + */ + _bindLink(link, destination) { + link.href = this.linkService.getDestinationHash(destination); + link.onclick = () => { + if (destination) { + this.linkService.navigateTo(destination); + } + return false; + }; + if (destination) { + link.className = 'internalLink'; + } + } + + /** + * Bind named actions to the link element. + * + * @private + * @param {Object} link + * @param {Object} action + * @memberof LinkAnnotationElement + */ + _bindNamedAction(link, action) { + link.href = this.linkService.getAnchorUrl(''); + link.onclick = () => { + this.linkService.executeNamedAction(action); + return false; + }; + link.className = 'internalLink'; + } +} + +class TextAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || + parameters.data.title || parameters.data.contents); + super(parameters, isRenderable); + } + + /** + * Render the text annotation's HTML element in the empty container. + * + * @public + * @memberof TextAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'textAnnotation'; + + let image = document.createElement('img'); + image.style.height = this.container.style.height; + image.style.width = this.container.style.width; + image.src = this.imageResourcesPath + 'annotation-' + + this.data.name.toLowerCase() + '.svg'; + image.alt = '[{{type}} Annotation]'; + image.dataset.l10nId = 'text_annotation_type'; + image.dataset.l10nArgs = JSON.stringify({ type: this.data.name, }); + + if (!this.data.hasPopup) { + this._createPopup(this.container, image, this.data); + } + + this.container.appendChild(image); + return this.container; + } +} + +class WidgetAnnotationElement extends AnnotationElement { + /** + * Render the widget annotation's HTML element in the empty container. + * + * @public + * @memberof WidgetAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + // Show only the container for unsupported field types. + return this.container; + } +} + +class TextWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + let isRenderable = parameters.renderInteractiveForms || + (!parameters.data.hasAppearance && !!parameters.data.fieldValue); + super(parameters, isRenderable); + } + + /** + * Render the text widget annotation's HTML element in the empty container. + * + * @public + * @memberof TextWidgetAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + const TEXT_ALIGNMENT = ['left', 'center', 'right']; + + this.container.className = 'textWidgetAnnotation'; + + let element = null; + if (this.renderInteractiveForms) { + // NOTE: We cannot set the values using `element.value` below, since it + // prevents the AnnotationLayer rasterizer in `test/driver.js` + // from parsing the elements correctly for the reference tests. + if (this.data.multiLine) { + element = document.createElement('textarea'); element.textContent = this.data.fieldValue; - element.style.verticalAlign = 'middle'; - element.style.display = 'table-cell'; - - var font = null; - if (this.data.fontRefName) { - font = this.page.commonObjs.getData(this.data.fontRefName); - } - this._setTextStyle(element, font); + } else { + element = document.createElement('input'); + element.type = 'text'; + element.setAttribute('value', this.data.fieldValue); } - if (this.data.textAlignment !== null) { - element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; - } - - this.container.appendChild(element); - return this.container; - }, - - /** - * Apply text styles to the text in the element. - * - * @private - * @param {HTMLDivElement} element - * @param {Object} font - * @memberof TextWidgetAnnotationElement - */ - _setTextStyle: - function TextWidgetAnnotationElement_setTextStyle(element, font) { - // TODO: This duplicates some of the logic in CanvasGraphics.setFont(). - var style = element.style; - style.fontSize = this.data.fontSize + 'px'; - style.direction = (this.data.fontDirection < 0 ? 'rtl' : 'ltr'); - - if (!font) { - return; - } - - style.fontWeight = (font.black ? - (font.bold ? '900' : 'bold') : - (font.bold ? 'bold' : 'normal')); - style.fontStyle = (font.italic ? 'italic' : 'normal'); - - // Use a reasonable default font if the font doesn't specify a fallback. - var fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : ''; - var fallbackName = font.fallbackName || 'Helvetica, sans-serif'; - style.fontFamily = fontFamily + fallbackName; - }, - }); - - return TextWidgetAnnotationElement; -})(); - -/** - * @class - * @alias CheckboxWidgetAnnotationElement - */ -var CheckboxWidgetAnnotationElement = - (function CheckboxWidgetAnnotationElementClosure() { - function CheckboxWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, - parameters.renderInteractiveForms); - } - - Util.inherit(CheckboxWidgetAnnotationElement, WidgetAnnotationElement, { - /** - * Render the checkbox widget annotation's HTML element - * in the empty container. - * - * @public - * @memberof CheckboxWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function CheckboxWidgetAnnotationElement_render() { - this.container.className = 'buttonWidgetAnnotation checkBox'; - - var element = document.createElement('input'); element.disabled = this.data.readOnly; - element.type = 'checkbox'; - if (this.data.fieldValue && this.data.fieldValue !== 'Off') { - element.setAttribute('checked', true); + + if (this.data.maxLen !== null) { + element.maxLength = this.data.maxLen; } - this.container.appendChild(element); - return this.container; - }, - }); + if (this.data.comb) { + let fieldWidth = this.data.rect[2] - this.data.rect[0]; + let combWidth = fieldWidth / this.data.maxLen; - return CheckboxWidgetAnnotationElement; -})(); + element.classList.add('comb'); + element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)'; + } + } else { + element = document.createElement('div'); + element.textContent = this.data.fieldValue; + element.style.verticalAlign = 'middle'; + element.style.display = 'table-cell'; -/** - * @class - * @alias RadioButtonWidgetAnnotationElement - */ -var RadioButtonWidgetAnnotationElement = - (function RadioButtonWidgetAnnotationElementClosure() { - function RadioButtonWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, - parameters.renderInteractiveForms); + let font = null; + if (this.data.fontRefName) { + font = this.page.commonObjs.getData(this.data.fontRefName); + } + this._setTextStyle(element, font); + } + + if (this.data.textAlignment !== null) { + element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment]; + } + + this.container.appendChild(element); + return this.container; } - Util.inherit(RadioButtonWidgetAnnotationElement, WidgetAnnotationElement, { - /** - * Render the radio button widget annotation's HTML element - * in the empty container. - * - * @public - * @memberof RadioButtonWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function RadioButtonWidgetAnnotationElement_render() { - this.container.className = 'buttonWidgetAnnotation radioButton'; + /** + * Apply text styles to the text in the element. + * + * @private + * @param {HTMLDivElement} element + * @param {Object} font + * @memberof TextWidgetAnnotationElement + */ + _setTextStyle(element, font) { + // TODO: This duplicates some of the logic in CanvasGraphics.setFont(). + let style = element.style; + style.fontSize = this.data.fontSize + 'px'; + style.direction = (this.data.fontDirection < 0 ? 'rtl' : 'ltr'); - var element = document.createElement('input'); - element.disabled = this.data.readOnly; - element.type = 'radio'; - element.name = this.data.fieldName; - if (this.data.fieldValue === this.data.buttonValue) { - element.setAttribute('checked', true); - } + if (!font) { + return; + } - this.container.appendChild(element); - return this.container; - }, - }); + style.fontWeight = (font.black ? + (font.bold ? '900' : 'bold') : + (font.bold ? 'bold' : 'normal')); + style.fontStyle = (font.italic ? 'italic' : 'normal'); - return RadioButtonWidgetAnnotationElement; -})(); + // Use a reasonable default font if the font doesn't specify a fallback. + let fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : ''; + let fallbackName = font.fallbackName || 'Helvetica, sans-serif'; + style.fontFamily = fontFamily + fallbackName; + } +} - /** - * @class - * @alias ChoiceWidgetAnnotationElement - */ -var ChoiceWidgetAnnotationElement = ( - function ChoiceWidgetAnnotationElementClosure() { - function ChoiceWidgetAnnotationElement(parameters) { - WidgetAnnotationElement.call(this, parameters, - parameters.renderInteractiveForms); +class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); } - Util.inherit(ChoiceWidgetAnnotationElement, WidgetAnnotationElement, { - /** - * Render the choice widget annotation's HTML element in the empty - * container. - * - * @public - * @memberof ChoiceWidgetAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function ChoiceWidgetAnnotationElement_render() { - this.container.className = 'choiceWidgetAnnotation'; + /** + * Render the checkbox widget annotation's HTML element + * in the empty container. + * + * @public + * @memberof CheckboxWidgetAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'buttonWidgetAnnotation checkBox'; - var selectElement = document.createElement('select'); - selectElement.disabled = this.data.readOnly; + let element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'checkbox'; + if (this.data.fieldValue && this.data.fieldValue !== 'Off') { + element.setAttribute('checked', true); + } - if (!this.data.combo) { - // List boxes have a size and (optionally) multiple selection. - selectElement.size = this.data.options.length; + this.container.appendChild(element); + return this.container; + } +} - if (this.data.multiSelect) { - selectElement.multiple = true; - } - } - - // Insert the options into the choice field. - for (var i = 0, ii = this.data.options.length; i < ii; i++) { - var option = this.data.options[i]; - - var optionElement = document.createElement('option'); - optionElement.textContent = option.displayValue; - optionElement.value = option.exportValue; - - if (this.data.fieldValue.indexOf(option.displayValue) >= 0) { - optionElement.setAttribute('selected', true); - } - - selectElement.appendChild(optionElement); - } - - this.container.appendChild(selectElement); - return this.container; - }, - }); - - return ChoiceWidgetAnnotationElement; -})(); - -/** - * @class - * @alias PopupAnnotationElement - */ -var PopupAnnotationElement = (function PopupAnnotationElementClosure() { - // Do not render popup annotations for parent elements with these types as - // they create the popups themselves (because of custom trigger divs). - var IGNORE_TYPES = ['Line']; - - function PopupAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable); +class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); } - Util.inherit(PopupAnnotationElement, AnnotationElement, { - /** - * Render the popup annotation's HTML element in the empty container. - * - * @public - * @memberof PopupAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function PopupAnnotationElement_render() { - this.container.className = 'popupAnnotation'; + /** + * Render the radio button widget annotation's HTML element + * in the empty container. + * + * @public + * @memberof RadioButtonWidgetAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'buttonWidgetAnnotation radioButton'; - if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) { - return this.container; + let element = document.createElement('input'); + element.disabled = this.data.readOnly; + element.type = 'radio'; + element.name = this.data.fieldName; + if (this.data.fieldValue === this.data.buttonValue) { + element.setAttribute('checked', true); + } + + this.container.appendChild(element); + return this.container; + } +} + +class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { + constructor(parameters) { + super(parameters, parameters.renderInteractiveForms); + } + + /** + * Render the choice widget annotation's HTML element in the empty + * container. + * + * @public + * @memberof ChoiceWidgetAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'choiceWidgetAnnotation'; + + let selectElement = document.createElement('select'); + selectElement.disabled = this.data.readOnly; + + if (!this.data.combo) { + // List boxes have a size and (optionally) multiple selection. + selectElement.size = this.data.options.length; + + if (this.data.multiSelect) { + selectElement.multiple = true; + } + } + + // Insert the options into the choice field. + for (let i = 0, ii = this.data.options.length; i < ii; i++) { + let option = this.data.options[i]; + + let optionElement = document.createElement('option'); + optionElement.textContent = option.displayValue; + optionElement.value = option.exportValue; + + if (this.data.fieldValue.indexOf(option.displayValue) >= 0) { + optionElement.setAttribute('selected', true); } - var selector = '[data-annotation-id="' + this.data.parentId + '"]'; - var parentElement = this.layer.querySelector(selector); - if (!parentElement) { - return this.container; - } + selectElement.appendChild(optionElement); + } - var popup = new PopupElement({ - container: this.container, - trigger: parentElement, - color: this.data.color, - title: this.data.title, - contents: this.data.contents, - }); + this.container.appendChild(selectElement); + return this.container; + } +} - // Position the popup next to the parent annotation's container. - // PDF viewers ignore a popup annotation's rectangle. - var parentLeft = parseFloat(parentElement.style.left); - var parentWidth = parseFloat(parentElement.style.width); - CustomStyle.setProp('transformOrigin', this.container, - -(parentLeft + parentWidth) + 'px -' + - parentElement.style.top); - this.container.style.left = (parentLeft + parentWidth) + 'px'; +class PopupAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.title || parameters.data.contents); + super(parameters, isRenderable); + } - this.container.appendChild(popup.render()); + /** + * Render the popup annotation's HTML element in the empty container. + * + * @public + * @memberof PopupAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + // Do not render popup annotations for parent elements with these types as + // they create the popups themselves (because of custom trigger divs). + const IGNORE_TYPES = ['Line']; + + this.container.className = 'popupAnnotation'; + + if (IGNORE_TYPES.indexOf(this.data.parentType) >= 0) { return this.container; - }, - }); + } - return PopupAnnotationElement; -})(); + let selector = '[data-annotation-id="' + this.data.parentId + '"]'; + let parentElement = this.layer.querySelector(selector); + if (!parentElement) { + return this.container; + } -/** - * @class - * @alias PopupElement - */ -var PopupElement = (function PopupElementClosure() { - var BACKGROUND_ENLIGHT = 0.7; + let popup = new PopupElement({ + container: this.container, + trigger: parentElement, + color: this.data.color, + title: this.data.title, + contents: this.data.contents, + }); - function PopupElement(parameters) { + // Position the popup next to the parent annotation's container. + // PDF viewers ignore a popup annotation's rectangle. + let parentLeft = parseFloat(parentElement.style.left); + let parentWidth = parseFloat(parentElement.style.width); + CustomStyle.setProp('transformOrigin', this.container, + -(parentLeft + parentWidth) + 'px -' + + parentElement.style.top); + this.container.style.left = (parentLeft + parentWidth) + 'px'; + + this.container.appendChild(popup.render()); + return this.container; + } +} + +class PopupElement { + constructor(parameters) { this.container = parameters.container; this.trigger = parameters.trigger; this.color = parameters.color; @@ -735,334 +632,279 @@ var PopupElement = (function PopupElementClosure() { this.pinned = false; } - PopupElement.prototype = /** @lends PopupElement.prototype */ { - /** - * Render the popup's HTML element. - * - * @public - * @memberof PopupElement - * @returns {HTMLSectionElement} - */ - render: function PopupElement_render() { - var wrapper = document.createElement('div'); - wrapper.className = 'popupWrapper'; + /** + * Render the popup's HTML element. + * + * @public + * @memberof PopupElement + * @returns {HTMLSectionElement} + */ + render() { + const BACKGROUND_ENLIGHT = 0.7; - // For Popup annotations we hide the entire section because it contains - // only the popup. However, for Text annotations without a separate Popup - // annotation, we cannot hide the entire container as the image would - // disappear too. In that special case, hiding the wrapper suffices. - this.hideElement = (this.hideWrapper ? wrapper : this.container); + let wrapper = document.createElement('div'); + wrapper.className = 'popupWrapper'; + + // For Popup annotations we hide the entire section because it contains + // only the popup. However, for Text annotations without a separate Popup + // annotation, we cannot hide the entire container as the image would + // disappear too. In that special case, hiding the wrapper suffices. + this.hideElement = (this.hideWrapper ? wrapper : this.container); + this.hideElement.setAttribute('hidden', true); + + let popup = document.createElement('div'); + popup.className = 'popup'; + + let color = this.color; + if (color) { + // Enlighten the color. + let r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; + let g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; + let b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; + popup.style.backgroundColor = Util.makeCssRgb(r | 0, g | 0, b | 0); + } + + let contents = this._formatContents(this.contents); + let title = document.createElement('h1'); + title.textContent = this.title; + + // Attach the event listeners to the trigger element. + this.trigger.addEventListener('click', this._toggle.bind(this)); + this.trigger.addEventListener('mouseover', this._show.bind(this, false)); + this.trigger.addEventListener('mouseout', this._hide.bind(this, false)); + popup.addEventListener('click', this._hide.bind(this, true)); + + popup.appendChild(title); + popup.appendChild(contents); + wrapper.appendChild(popup); + return wrapper; + } + + /** + * Format the contents of the popup by adding newlines where necessary. + * + * @private + * @param {string} contents + * @memberof PopupElement + * @returns {HTMLParagraphElement} + */ + _formatContents(contents) { + let p = document.createElement('p'); + let lines = contents.split(/(?:\r\n?|\n)/); + for (let i = 0, ii = lines.length; i < ii; ++i) { + let line = lines[i]; + p.appendChild(document.createTextNode(line)); + if (i < (ii - 1)) { + p.appendChild(document.createElement('br')); + } + } + return p; + } + + /** + * Toggle the visibility of the popup. + * + * @private + * @memberof PopupElement + */ + _toggle() { + if (this.pinned) { + this._hide(true); + } else { + this._show(true); + } + } + + /** + * Show the popup. + * + * @private + * @param {boolean} pin + * @memberof PopupElement + */ + _show(pin = false) { + if (pin) { + this.pinned = true; + } + if (this.hideElement.hasAttribute('hidden')) { + this.hideElement.removeAttribute('hidden'); + this.container.style.zIndex += 1; + } + } + + /** + * Hide the popup. + * + * @private + * @param {boolean} unpin + * @memberof PopupElement + */ + _hide(unpin = true) { + if (unpin) { + this.pinned = false; + } + if (!this.hideElement.hasAttribute('hidden') && !this.pinned) { this.hideElement.setAttribute('hidden', true); + this.container.style.zIndex -= 1; + } + } +} - var popup = document.createElement('div'); - popup.className = 'popup'; - - var color = this.color; - if (color) { - // Enlighten the color. - var r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; - var g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; - var b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; - popup.style.backgroundColor = Util.makeCssRgb(r | 0, g | 0, b | 0); - } - - var contents = this._formatContents(this.contents); - var title = document.createElement('h1'); - title.textContent = this.title; - - // Attach the event listeners to the trigger element. - this.trigger.addEventListener('click', this._toggle.bind(this)); - this.trigger.addEventListener('mouseover', this._show.bind(this, false)); - this.trigger.addEventListener('mouseout', this._hide.bind(this, false)); - popup.addEventListener('click', this._hide.bind(this, true)); - - popup.appendChild(title); - popup.appendChild(contents); - wrapper.appendChild(popup); - return wrapper; - }, - - /** - * Format the contents of the popup by adding newlines where necessary. - * - * @private - * @param {string} contents - * @memberof PopupElement - * @returns {HTMLParagraphElement} - */ - _formatContents: function PopupElement_formatContents(contents) { - var p = document.createElement('p'); - var lines = contents.split(/(?:\r\n?|\n)/); - for (var i = 0, ii = lines.length; i < ii; ++i) { - var line = lines[i]; - p.appendChild(document.createTextNode(line)); - if (i < (ii - 1)) { - p.appendChild(document.createElement('br')); - } - } - return p; - }, - - /** - * Toggle the visibility of the popup. - * - * @private - * @memberof PopupElement - */ - _toggle: function PopupElement_toggle() { - if (this.pinned) { - this._hide(true); - } else { - this._show(true); - } - }, - - /** - * Show the popup. - * - * @private - * @param {boolean} pin - * @memberof PopupElement - */ - _show: function PopupElement_show(pin) { - if (pin) { - this.pinned = true; - } - if (this.hideElement.hasAttribute('hidden')) { - this.hideElement.removeAttribute('hidden'); - this.container.style.zIndex += 1; - } - }, - - /** - * Hide the popup. - * - * @private - * @param {boolean} unpin - * @memberof PopupElement - */ - _hide: function PopupElement_hide(unpin) { - if (unpin) { - this.pinned = false; - } - if (!this.hideElement.hasAttribute('hidden') && !this.pinned) { - this.hideElement.setAttribute('hidden', true); - this.container.style.zIndex -= 1; - } - }, - }; - - return PopupElement; -})(); - -/** - * @class - * @alias LineAnnotationElement - */ -var LineAnnotationElement = (function LineAnnotationElementClosure() { - var SVG_NS = 'http://www.w3.org/2000/svg'; - - function LineAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || +class LineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, - /* ignoreBorder = */ true); + super(parameters, isRenderable, /* ignoreBorder = */ true); } - Util.inherit(LineAnnotationElement, AnnotationElement, { - /** - * Render the line annotation's HTML element in the empty container. - * - * @public - * @memberof LineAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function LineAnnotationElement_render() { - this.container.className = 'lineAnnotation'; + /** + * Render the line annotation's HTML element in the empty container. + * + * @public + * @memberof LineAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + const SVG_NS = 'http://www.w3.org/2000/svg'; - // Create an invisible line with the same starting and ending coordinates - // that acts as the trigger for the popup. Only the line itself should - // trigger the popup, not the entire container. - var data = this.data; - var width = data.rect[2] - data.rect[0]; - var height = data.rect[3] - data.rect[1]; + this.container.className = 'lineAnnotation'; - var svg = document.createElementNS(SVG_NS, 'svg:svg'); - svg.setAttributeNS(null, 'version', '1.1'); - svg.setAttributeNS(null, 'width', width + 'px'); - svg.setAttributeNS(null, 'height', height + 'px'); - svg.setAttributeNS(null, 'preserveAspectRatio', 'none'); - svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); + // Create an invisible line with the same starting and ending coordinates + // that acts as the trigger for the popup. Only the line itself should + // trigger the popup, not the entire container. + let data = this.data; + let width = data.rect[2] - data.rect[0]; + let height = data.rect[3] - data.rect[1]; - // PDF coordinates are calculated from a bottom left origin, so transform - // the line coordinates to a top left origin for the SVG element. - var line = document.createElementNS(SVG_NS, 'svg:line'); - line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]); - line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]); - line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]); - line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]); - line.setAttributeNS(null, 'stroke-width', data.borderStyle.width); - line.setAttributeNS(null, 'stroke', 'transparent'); + let svg = document.createElementNS(SVG_NS, 'svg:svg'); + svg.setAttributeNS(null, 'version', '1.1'); + svg.setAttributeNS(null, 'width', width + 'px'); + svg.setAttributeNS(null, 'height', height + 'px'); + svg.setAttributeNS(null, 'preserveAspectRatio', 'none'); + svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); - svg.appendChild(line); - this.container.append(svg); + // PDF coordinates are calculated from a bottom left origin, so transform + // the line coordinates to a top left origin for the SVG element. + let line = document.createElementNS(SVG_NS, 'svg:line'); + line.setAttributeNS(null, 'x1', data.rect[2] - data.lineCoordinates[0]); + line.setAttributeNS(null, 'y1', data.rect[3] - data.lineCoordinates[1]); + line.setAttributeNS(null, 'x2', data.rect[2] - data.lineCoordinates[2]); + line.setAttributeNS(null, 'y2', data.rect[3] - data.lineCoordinates[3]); + line.setAttributeNS(null, 'stroke-width', data.borderStyle.width); + line.setAttributeNS(null, 'stroke', 'transparent'); - // Create the popup ourselves so that we can bind it to the line instead - // of to the entire container (which is the default). - this._createPopup(this.container, line, this.data); + svg.appendChild(line); + this.container.append(svg); - return this.container; - }, - }); + // Create the popup ourselves so that we can bind it to the line instead + // of to the entire container (which is the default). + this._createPopup(this.container, line, this.data); - return LineAnnotationElement; -})(); + return this.container; + } +} -/** - * @class - * @alias HighlightAnnotationElement - */ -var HighlightAnnotationElement = ( - function HighlightAnnotationElementClosure() { - function HighlightAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || +class HighlightAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, - /* ignoreBorder = */ true); + super(parameters, isRenderable, /* ignoreBorder = */ true); } - Util.inherit(HighlightAnnotationElement, AnnotationElement, { - /** - * Render the highlight annotation's HTML element in the empty container. - * - * @public - * @memberof HighlightAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function HighlightAnnotationElement_render() { - this.container.className = 'highlightAnnotation'; + /** + * Render the highlight annotation's HTML element in the empty container. + * + * @public + * @memberof HighlightAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'highlightAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - }, - }); + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} - return HighlightAnnotationElement; -})(); - -/** - * @class - * @alias UnderlineAnnotationElement - */ -var UnderlineAnnotationElement = ( - function UnderlineAnnotationElementClosure() { - function UnderlineAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || +class UnderlineAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, - /* ignoreBorder = */ true); + super(parameters, isRenderable, /* ignoreBorder = */ true); } - Util.inherit(UnderlineAnnotationElement, AnnotationElement, { - /** - * Render the underline annotation's HTML element in the empty container. - * - * @public - * @memberof UnderlineAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function UnderlineAnnotationElement_render() { - this.container.className = 'underlineAnnotation'; + /** + * Render the underline annotation's HTML element in the empty container. + * + * @public + * @memberof UnderlineAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'underlineAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - }, - }); + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} - return UnderlineAnnotationElement; -})(); - -/** - * @class - * @alias SquigglyAnnotationElement - */ -var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() { - function SquigglyAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || +class SquigglyAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, - /* ignoreBorder = */ true); + super(parameters, isRenderable, /* ignoreBorder = */ true); } - Util.inherit(SquigglyAnnotationElement, AnnotationElement, { - /** - * Render the squiggly annotation's HTML element in the empty container. - * - * @public - * @memberof SquigglyAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function SquigglyAnnotationElement_render() { - this.container.className = 'squigglyAnnotation'; + /** + * Render the squiggly annotation's HTML element in the empty container. + * + * @public + * @memberof SquigglyAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'squigglyAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - }, - }); + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} - return SquigglyAnnotationElement; -})(); - -/** - * @class - * @alias StrikeOutAnnotationElement - */ -var StrikeOutAnnotationElement = ( - function StrikeOutAnnotationElementClosure() { - function StrikeOutAnnotationElement(parameters) { - var isRenderable = !!(parameters.data.hasPopup || +class StrikeOutAnnotationElement extends AnnotationElement { + constructor(parameters) { + let isRenderable = !!(parameters.data.hasPopup || parameters.data.title || parameters.data.contents); - AnnotationElement.call(this, parameters, isRenderable, - /* ignoreBorder = */ true); + super(parameters, isRenderable, /* ignoreBorder = */ true); } - Util.inherit(StrikeOutAnnotationElement, AnnotationElement, { - /** - * Render the strikeout annotation's HTML element in the empty container. - * - * @public - * @memberof StrikeOutAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function StrikeOutAnnotationElement_render() { - this.container.className = 'strikeoutAnnotation'; + /** + * Render the strikeout annotation's HTML element in the empty container. + * + * @public + * @memberof StrikeOutAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'strikeoutAnnotation'; - if (!this.data.hasPopup) { - this._createPopup(this.container, null, this.data); - } - return this.container; - }, - }); + if (!this.data.hasPopup) { + this._createPopup(this.container, null, this.data); + } + return this.container; + } +} - return StrikeOutAnnotationElement; -})(); +class FileAttachmentAnnotationElement extends AnnotationElement { + constructor(parameters) { + super(parameters, true); -/** - * @class - * @alias FileAttachmentAnnotationElement - */ -var FileAttachmentAnnotationElement = ( - function FileAttachmentAnnotationElementClosure() { - function FileAttachmentAnnotationElement(parameters) { - AnnotationElement.call(this, parameters, true); - - var file = this.data.file; + let file = this.data.file; this.filename = getFilenameFromUrl(file.filename); this.content = file.content; @@ -1073,48 +915,44 @@ var FileAttachmentAnnotationElement = ( }); } - Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, { - /** - * Render the file attachment annotation's HTML element in the empty - * container. - * - * @public - * @memberof FileAttachmentAnnotationElement - * @returns {HTMLSectionElement} - */ - render: function FileAttachmentAnnotationElement_render() { - this.container.className = 'fileAttachmentAnnotation'; + /** + * Render the file attachment annotation's HTML element in the empty + * container. + * + * @public + * @memberof FileAttachmentAnnotationElement + * @returns {HTMLSectionElement} + */ + render() { + this.container.className = 'fileAttachmentAnnotation'; - var trigger = document.createElement('div'); - trigger.style.height = this.container.style.height; - trigger.style.width = this.container.style.width; - trigger.addEventListener('dblclick', this._download.bind(this)); + let trigger = document.createElement('div'); + trigger.style.height = this.container.style.height; + trigger.style.width = this.container.style.width; + trigger.addEventListener('dblclick', this._download.bind(this)); - if (!this.data.hasPopup && (this.data.title || this.data.contents)) { - this._createPopup(this.container, trigger, this.data); - } + if (!this.data.hasPopup && (this.data.title || this.data.contents)) { + this._createPopup(this.container, trigger, this.data); + } - this.container.appendChild(trigger); - return this.container; - }, + this.container.appendChild(trigger); + return this.container; + } - /** - * Download the file attachment associated with this annotation. - * - * @private - * @memberof FileAttachmentAnnotationElement - */ - _download: function FileAttachmentAnnotationElement_download() { - if (!this.downloadManager) { - warn('Download cannot be started due to unavailable download manager'); - return; - } - this.downloadManager.downloadData(this.content, this.filename, ''); - }, - }); - - return FileAttachmentAnnotationElement; -})(); + /** + * Download the file attachment associated with this annotation. + * + * @private + * @memberof FileAttachmentAnnotationElement + */ + _download() { + if (!this.downloadManager) { + warn('Download cannot be started due to unavailable download manager'); + return; + } + this.downloadManager.downloadData(this.content, this.filename, ''); + } +} /** * @typedef {Object} AnnotationLayerParameters @@ -1127,65 +965,59 @@ var FileAttachmentAnnotationElement = ( * @property {boolean} renderInteractiveForms */ -/** - * @class - * @alias AnnotationLayer - */ -var AnnotationLayer = (function AnnotationLayerClosure() { - return { - /** - * Render a new annotation layer with all annotation elements. - * - * @public - * @param {AnnotationLayerParameters} parameters - * @memberof AnnotationLayer - */ - render: function AnnotationLayer_render(parameters) { - var annotationElementFactory = new AnnotationElementFactory(); +class AnnotationLayer { + /** + * Render a new annotation layer with all annotation elements. + * + * @public + * @param {AnnotationLayerParameters} parameters + * @memberof AnnotationLayer + */ + static render(parameters) { + let annotationElementFactory = new AnnotationElementFactory(); - for (var i = 0, ii = parameters.annotations.length; i < ii; i++) { - var data = parameters.annotations[i]; - if (!data) { - continue; - } - var element = annotationElementFactory.create({ - data, - layer: parameters.div, - page: parameters.page, - viewport: parameters.viewport, - linkService: parameters.linkService, - downloadManager: parameters.downloadManager, - imageResourcesPath: parameters.imageResourcesPath || - getDefaultSetting('imageResourcesPath'), - renderInteractiveForms: parameters.renderInteractiveForms || false, - }); - if (element.isRenderable) { - parameters.div.appendChild(element.render()); - } + for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { + let data = parameters.annotations[i]; + if (!data) { + continue; } - }, + let element = annotationElementFactory.create({ + data, + layer: parameters.div, + page: parameters.page, + viewport: parameters.viewport, + linkService: parameters.linkService, + downloadManager: parameters.downloadManager, + imageResourcesPath: parameters.imageResourcesPath || + getDefaultSetting('imageResourcesPath'), + renderInteractiveForms: parameters.renderInteractiveForms || false, + }); + if (element.isRenderable) { + parameters.div.appendChild(element.render()); + } + } + } - /** - * Update the annotation elements on existing annotation layer. - * - * @public - * @param {AnnotationLayerParameters} parameters - * @memberof AnnotationLayer - */ - update: function AnnotationLayer_update(parameters) { - for (var i = 0, ii = parameters.annotations.length; i < ii; i++) { - var data = parameters.annotations[i]; - var element = parameters.div.querySelector( - '[data-annotation-id="' + data.id + '"]'); - if (element) { - CustomStyle.setProp('transform', element, - 'matrix(' + parameters.viewport.transform.join(',') + ')'); - } + /** + * Update the annotation elements on existing annotation layer. + * + * @public + * @param {AnnotationLayerParameters} parameters + * @memberof AnnotationLayer + */ + static update(parameters) { + for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { + let data = parameters.annotations[i]; + let element = parameters.div.querySelector( + '[data-annotation-id="' + data.id + '"]'); + if (element) { + CustomStyle.setProp('transform', element, + 'matrix(' + parameters.viewport.transform.join(',') + ')'); } - parameters.div.removeAttribute('hidden'); - }, - }; -})(); + } + parameters.div.removeAttribute('hidden'); + } +} export { AnnotationLayer, From 4f0285739403ebf30ef7be4a05c8a639d7d4edab Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sun, 27 Aug 2017 00:30:00 +0200 Subject: [PATCH 3/5] Let the two annotation factories use static methods This corresponds to how other factories are implemented. --- src/core/annotation.js | 2 +- src/core/document.js | 3 +- src/display/annotation_layer.js | 6 +-- test/unit/annotation_spec.js | 90 ++++++++++++++++----------------- 4 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index f036cd571..f2099a984 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -32,7 +32,7 @@ class AnnotationFactory { * @param {Object} idFactory * @returns {Annotation} */ - create(xref, ref, pdfManager, idFactory) { + static create(xref, ref, pdfManager, idFactory) { let dict = xref.fetchIfRef(ref); if (!isDict(dict)) { return; diff --git a/src/core/document.js b/src/core/document.js index 0a477aa5a..6af6033e9 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -316,10 +316,9 @@ 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 = annotationFactory.create(this.xref, annotationRef, + var annotation = AnnotationFactory.create(this.xref, annotationRef, this.pdfManager, this.idFactory); if (annotation) { diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 0b95e10d8..13c15184e 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -38,7 +38,7 @@ class AnnotationElementFactory { * @param {AnnotationElementParameters} parameters * @returns {AnnotationElement} */ - create(parameters) { + static create(parameters) { let subtype = parameters.data.annotationType; switch (subtype) { @@ -974,14 +974,12 @@ class AnnotationLayer { * @memberof AnnotationLayer */ static render(parameters) { - let annotationElementFactory = new AnnotationElementFactory(); - for (let i = 0, ii = parameters.annotations.length; i < ii; i++) { let data = parameters.annotations[i]; if (!data) { continue; } - let element = annotationElementFactory.create({ + let element = AnnotationElementFactory.create({ data, layer: parameters.div, page: parameters.page, diff --git a/test/unit/annotation_spec.js b/test/unit/annotation_spec.js index 15975f9f0..6f54e1586 100644 --- a/test/unit/annotation_spec.js +++ b/test/unit/annotation_spec.js @@ -44,10 +44,9 @@ describe('annotation', function() { } IdFactoryMock.prototype = {}; - var annotationFactory, pdfManagerMock, idFactoryMock; + var pdfManagerMock, idFactoryMock; beforeAll(function (done) { - annotationFactory = new AnnotationFactory(); pdfManagerMock = new PDFManagerMock({ docBaseUrl: null, }); @@ -56,7 +55,6 @@ describe('annotation', function() { }); afterAll(function () { - annotationFactory = null; pdfManagerMock = null; idFactoryMock = null; }); @@ -72,7 +70,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -92,9 +90,9 @@ describe('annotation', function() { startObjId: 0, }); - var annotation1 = annotationFactory.create(xref, annotationDict, + var annotation1 = AnnotationFactory.create(xref, annotationDict, pdfManagerMock, idFactory); - var annotation2 = annotationFactory.create(xref, annotationDict, + var annotation2 = AnnotationFactory.create(xref, annotationDict, pdfManagerMock, idFactory); var data1 = annotation1.data, data2 = annotation2.data; expect(data1.annotationType).toEqual(AnnotationType.LINK); @@ -113,7 +111,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toBeUndefined(); @@ -298,7 +296,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -326,7 +324,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -359,7 +357,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -389,7 +387,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -418,7 +416,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -450,7 +448,7 @@ describe('annotation', function() { docBaseUrl: 'http://www.example.com/test/pdfs/qwerty.pdf', }); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManager, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -479,7 +477,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -508,7 +506,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -548,7 +546,7 @@ describe('annotation', function() { docBaseUrl: 'http://www.example.com/test/pdfs/qwerty.pdf', }); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManager, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -583,7 +581,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; @@ -635,7 +633,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -656,7 +654,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -679,7 +677,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - var annotation = annotationFactory.create(xref, annotationRef, + var annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -709,7 +707,7 @@ describe('annotation', function() { { ref: annotationRef, data: annotationDict, } ]); - let annotation = annotationFactory.create(xref, annotationRef, + let annotation = AnnotationFactory.create(xref, annotationRef, pdfManagerMock, idFactoryMock); let data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINK); @@ -742,7 +740,7 @@ describe('annotation', function() { { ref: widgetRef, data: widgetDict, } ]); - var annotation = annotationFactory.create(xref, widgetRef, + var annotation = AnnotationFactory.create(xref, widgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -759,7 +757,7 @@ describe('annotation', function() { { ref: widgetRef, data: widgetDict, } ]); - var annotation = annotationFactory.create(xref, widgetRef, + var annotation = AnnotationFactory.create(xref, widgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -783,7 +781,7 @@ describe('annotation', function() { { ref: widgetRef, data: widgetDict, } ]); - var annotation = annotationFactory.create(xref, widgetRef, + var annotation = AnnotationFactory.create(xref, widgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -805,7 +803,7 @@ describe('annotation', function() { { ref: widgetRef, data: widgetDict, } ]); - var annotation = annotationFactory.create(xref, widgetRef, + var annotation = AnnotationFactory.create(xref, widgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -837,7 +835,7 @@ describe('annotation', function() { { ref: textWidgetRef, data: textWidgetDict, } ]); - var annotation = annotationFactory.create(xref, textWidgetRef, + var annotation = AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -860,7 +858,7 @@ describe('annotation', function() { { ref: textWidgetRef, data: textWidgetDict, } ]); - var annotation = annotationFactory.create(xref, textWidgetRef, + var annotation = AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -884,7 +882,7 @@ describe('annotation', function() { { ref: textWidgetRef, data: textWidgetDict, } ]); - var annotation = annotationFactory.create(xref, textWidgetRef, + var annotation = AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -903,7 +901,7 @@ describe('annotation', function() { { ref: textWidgetRef, data: textWidgetDict, } ]); - var annotation = annotationFactory.create(xref, textWidgetRef, + var annotation = AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -920,7 +918,7 @@ describe('annotation', function() { { ref: textWidgetRef, data: textWidgetDict, } ]); - var annotation = annotationFactory.create(xref, textWidgetRef, + var annotation = AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -948,7 +946,7 @@ describe('annotation', function() { { ref: textWidgetRef, data: textWidgetDict, } ]); - var annotation = annotationFactory.create(xref, textWidgetRef, + var annotation = AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; @@ -989,7 +987,7 @@ describe('annotation', function() { { ref: buttonWidgetRef, data: buttonWidgetDict, } ]); - var annotation = annotationFactory.create(xref, buttonWidgetRef, + var annotation = AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1018,7 +1016,7 @@ describe('annotation', function() { { ref: buttonWidgetRef, data: buttonWidgetDict, } ]); - var annotation = annotationFactory.create(xref, buttonWidgetRef, + var annotation = AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1044,7 +1042,7 @@ describe('annotation', function() { { ref: buttonWidgetRef, data: buttonWidgetDict, } ]); - var annotation = annotationFactory.create(xref, buttonWidgetRef, + var annotation = AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1078,7 +1076,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1113,7 +1111,7 @@ describe('annotation', function() { { ref: optionOneRef, data: optionOneArr, }, ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1145,7 +1143,7 @@ describe('annotation', function() { { ref: optionBarRef, data: optionBarStr, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1173,7 +1171,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, }, ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1191,7 +1189,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1209,7 +1207,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1223,7 +1221,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1241,7 +1239,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1261,7 +1259,7 @@ describe('annotation', function() { { ref: choiceWidgetRef, data: choiceWidgetDict, } ]); - var annotation = annotationFactory.create(xref, choiceWidgetRef, + var annotation = AnnotationFactory.create(xref, choiceWidgetRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.WIDGET); @@ -1284,7 +1282,7 @@ describe('annotation', function() { { ref: lineRef, data: lineDict, } ]); - var annotation = annotationFactory.create(xref, lineRef, pdfManagerMock, + var annotation = AnnotationFactory.create(xref, lineRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.LINE); @@ -1337,7 +1335,7 @@ describe('annotation', function() { fileSpecDict.assignXref(xref); fileAttachmentDict.assignXref(xref); - var annotation = annotationFactory.create(xref, fileAttachmentRef, + var annotation = AnnotationFactory.create(xref, fileAttachmentRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.FILEATTACHMENT); @@ -1366,7 +1364,7 @@ describe('annotation', function() { { ref: popupRef, data: popupDict, } ]); - var annotation = annotationFactory.create(xref, popupRef, + var annotation = AnnotationFactory.create(xref, popupRef, pdfManagerMock, idFactoryMock); var data = annotation.data; expect(data.annotationType).toEqual(AnnotationType.POPUP); From 2512eccbf09f33811325dcbad7efa4f4f68ccc93 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sun, 27 Aug 2017 00:48:02 +0200 Subject: [PATCH 4/5] Implement `getOperatorList` method in the `WidgetAnnotation` class to avoid duplication in subclasses --- src/core/annotation.js | 53 +++++++++++------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index f2099a984..4fca1e83b 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -646,6 +646,15 @@ class WidgetAnnotation extends Annotation { hasFieldFlag(flag) { return !!(this.data.fieldFlags & flag); } + + getOperatorList(evaluator, task, renderForms) { + // Do not render form elements on the canvas when interactive forms are + // enabled. The display layer is responsible for rendering them instead. + if (renderForms) { + return Promise.resolve(new OperatorList()); + } + return super.getOperatorList(evaluator, task, renderForms); + } } class TextWidgetAnnotation extends WidgetAnnotation { @@ -679,19 +688,12 @@ class TextWidgetAnnotation extends WidgetAnnotation { } getOperatorList(evaluator, task, renderForms) { + if (renderForms || this.appearance) { + return super.getOperatorList(evaluator, task, renderForms); + } + let operatorList = new OperatorList(); - // Do not render form elements on the canvas when interactive forms are - // enabled. The display layer is responsible for rendering them instead. - if (renderForms) { - return Promise.resolve(operatorList); - } - - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, - renderForms); - } - // Even if there is an appearance stream, ignore it. This is the // behaviour used by Adobe Reader. if (!this.data.defaultAppearance) { @@ -756,22 +758,6 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { } } } - - getOperatorList(evaluator, task, renderForms) { - let operatorList = new OperatorList(); - - // Do not render form elements on the canvas when interactive forms are - // enabled. The display layer is responsible for rendering them instead. - if (renderForms) { - return Promise.resolve(operatorList); - } - - if (this.appearance) { - return Annotation.prototype.getOperatorList.call(this, evaluator, task, - renderForms); - } - return Promise.resolve(operatorList); - } } class ChoiceWidgetAnnotation extends WidgetAnnotation { @@ -814,19 +800,6 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation { this.data.combo = this.hasFieldFlag(AnnotationFieldFlag.COMBO); this.data.multiSelect = this.hasFieldFlag(AnnotationFieldFlag.MULTISELECT); } - - getOperatorList(evaluator, task, renderForms) { - let operatorList = new OperatorList(); - - // Do not render form elements on the canvas when interactive forms are - // enabled. The display layer is responsible for rendering them instead. - if (renderForms) { - return Promise.resolve(operatorList); - } - - return Annotation.prototype.getOperatorList.call(this, evaluator, task, - renderForms); - } } class TextAnnotation extends Annotation { From 7787987a4bb41e88feadda4dd636599804503f08 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sun, 27 Aug 2017 01:07:51 +0200 Subject: [PATCH 5/5] Update `webpack` and `webpack-stream` to the latest version Aside from being up-to-date, this may improve build time/size. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d6df16135..c5472d91d 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "typogr": "^0.6.6", "uglify-js": "^2.6.1", "vinyl-fs": "^2.4.4", - "webpack": "^2.2.1", - "webpack-stream": "^3.2.0", + "webpack": "^3.5.5", + "webpack-stream": "^4.0.0", "wintersmith": "^2.0.0", "yargs": "^3.14.0" },