GitHub PullRequestReviewCommentEvent at 2014-08-27 13:31:10
Repository: pdf.js
Actor: Snuffleupagus
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/mozilla/pdf.js/pulls/comments/16774507","id":16774507,"diff_hunk":"@@ -14,521 +14,846 @@\n  * See the License for the specific language governing permissions and\n  * limitations under the License.\n  */\n-/* globals PDFJS, Util, isDict, isName, stringToPDFString, warn, Dict, Stream,\n-           stringToBytes, assert, Promise, isArray, ObjectLoader, OperatorList,\n-           isValidUrl, OPS, createPromiseCapability, AnnotationType */\n+/* globals AnnotationType, AnnotationFlag, Util, Promise, OperatorList,\n+           AnnotationBorderStyleType, isDict, warn, isArray, DeviceGrayCS,\n+           DeviceRgbCS, DeviceCmykCS, isName, createPromiseCapability, OPS */\n \n 'use strict';\n \n-var DEFAULT_ICON_SIZE = 22; // px\n-var SUPPORTED_TYPES = ['Link', 'Text', 'Widget'];\n+var DEFAULT_RECTANGLE = [0, 0, 0, 0];\n+var DEFAULT_DASH_ARRAY = [3];\n \n+/**\n+ * Contains basic data for each annotation type. Each annotation\n+ * type extends this class to obtain basic functionality.\n+ *\n+ * @class\n+ */\n var Annotation = (function AnnotationClosure() {\n-  // 12.5.5: Algorithm: Appearance streams\n-  function getTransformMatrix(rect, bbox, matrix) {\n-    var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);\n-    var minX = bounds[0];\n-    var minY = bounds[1];\n-    var maxX = bounds[2];\n-    var maxY = bounds[3];\n-\n-    if (minX === maxX || minY === maxY) {\n-      // From real-life file, bbox was [0, 0, 0, 0]. In this case,\n-      // just apply the transform for rect\n-      return [1, 0, 0, 1, rect[0], rect[1]];\n-    }\n-\n-    var xRatio = (rect[2] - rect[0]) / (maxX - minX);\n-    var yRatio = (rect[3] - rect[1]) / (maxY - minY);\n-    return [\n-      xRatio,\n-      0,\n-      0,\n-      yRatio,\n-      rect[0] - minX * xRatio,\n-      rect[1] - minY * yRatio\n-    ];\n+  /*\n+   * @constructor\n+   * @private\n+   */\n+  function Annotation() {\n+    this.id = null;\n+    this.type = null;\n+    this.contents = null;\n+    this.name = null;\n+    this.date = null;\n+    this.rectangle = null;\n+    this.borderStyle = new AnnotationBorderStyle();\n+    this.color = null;\n+    this.flags = 0;\n   }\n \n-  function getDefaultAppearance(dict) {\n-    var appearanceState = dict.get('AP');\n-    if (!isDict(appearanceState)) {\n-      return;\n-    }\n-\n-    var appearance;\n-    var appearances = appearanceState.get('N');\n-    if (isDict(appearances)) {\n-      var as = dict.get('AS');\n-      if (as && appearances.has(as.name)) {\n-        appearance = appearances.get(as.name);\n+  Annotation.prototype = {\n+    /**\n+     * Set the ID.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param id {integer} The ID\n+     */\n+    setId: function Annotation_setId(id) {\n+      if (id === (id | 0)) {\n+        this.id = id;\n       }\n-    } else {\n-      appearance = appearances;\n-    }\n-    return appearance;\n-  }\n+    },\n \n-  function Annotation(params) {\n-    var dict = params.dict;\n-    var data = this.data = {};\n-\n-    data.subtype = dict.get('Subtype').name;\n-    var rect = dict.get('Rect') || [0, 0, 0, 0];\n-    data.rect = Util.normalizeRect(rect);\n-    data.annotationFlags = dict.get('F');\n-\n-    var color = dict.get('C');\n-    if (isArray(color) && color.length === 3) {\n-      // TODO(mack): currently only supporting rgb; need support different\n-      // colorspaces\n-      data.color = color;\n-    } else {\n-      data.color = [0, 0, 0];\n-    }\n+    /**\n+     * Get the ID.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default null\n+     * @return {integer|null} The ID, either a valid integer or null if\n+     *                        no (valid) ID has been set.\n+     */\n+    getId: function Annotation_getId() {\n+      return this.id;\n+    },\n \n-    // Some types of annotations have border style dict which has more\n-    // info than the border array\n-    if (dict.has('BS')) {\n-      var borderStyle = dict.get('BS');\n-      data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1;\n-    } else {\n-      var borderArray = dict.get('Border') || [0, 0, 1];\n-      data.borderWidth = borderArray[2] || 0;\n-\n-      // TODO: implement proper support for annotations with line dash patterns.\n-      var dashArray = borderArray[3];\n-      if (data.borderWidth > 0 && dashArray) {\n-        if (!isArray(dashArray)) {\n-          // Ignore the border if dashArray is not actually an array,\n-          // this is consistent with the behaviour in Adobe Reader. \n-          data.borderWidth = 0;\n-        } else {\n-          var dashArrayLength = dashArray.length;\n-          if (dashArrayLength > 0) {\n-            // According to the PDF specification: the elements in a dashArray\n-            // shall be numbers that are nonnegative and not all equal to zero.\n-            var isInvalid = false;\n-            var numPositive = 0;\n-            for (var i = 0; i < dashArrayLength; i++) {\n-              var validNumber = (+dashArray[i] >= 0);\n-              if (!validNumber) {\n-                isInvalid = true;\n-                break;\n-              } else if (dashArray[i] > 0) {\n-                numPositive++;\n-              }\n-            }\n-            if (isInvalid || numPositive === 0) {\n-              data.borderWidth = 0;\n-            }\n-          }\n+    /**\n+     * Set the type.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param type {integer} The type\n+     * @see {@link shared/util.js}\n+     */\n+    setType: function Annotation_setType(type) {\n+      if (type === (type | 0)) {\n+        if (type >= 1 && type <= Object.keys(AnnotationType).length) {\n+          this.type = type;\n         }\n       }\n-    }\n+    },\n \n-    this.appearance = getDefaultAppearance(dict);\n-    data.hasAppearance = !!this.appearance;\n-    data.id = params.ref.num;\n-  }\n+    /**\n+     * Get the type.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default null\n+     * @return {integer|null} The type, either a valid integer or null if\n+     *                        no (valid) type has been set.\n+     * @see {@link shared/util.js}\n+     */\n+    getType: function Annotation_getType() {\n+      return this.type;\n+    },\n \n-  Annotation.prototype = {\n+    /**\n+     * Set the contents.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param contents {string} The contents\n+     */\n+    setContents: function Annotation_setContents(contents) {\n+      this.contents = contents || null;\n+    },\n \n-    getData: function Annotation_getData() {\n-      return this.data;\n+    /**\n+     * Get the contents.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default null\n+     * @return {string|null} The contents, either a valid string or null if\n+     *                       no (valid) contents have been set.\n+     */\n+    getContents: function Annotation_getContents() {\n+      return this.contents;\n     },\n \n-    isInvisible: function Annotation_isInvisible() {\n-      var data = this.data;\n-      if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) {\n-        return false;\n-      } else {\n-        return !!(data &&\n-                  data.annotationFlags &&            // Default: not invisible\n-                  data.annotationFlags & 0x1);       // Invisible\n-      }\n+    /**\n+     * Set the name.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param name {string} The name\n+     */\n+    setName: function Annotation_setName(name) {\n+      this.name = name || null;\n     },\n \n-    isViewable: function Annotation_isViewable() {\n-      var data = this.data;\n-      return !!(!this.isInvisible() &&\n-                data &&\n-                (!data.annotationFlags ||\n-                 !(data.annotationFlags & 0x22)) &&  // Hidden or NoView\n-                data.rect);                          // rectangle is necessary\n+    /**\n+     * Get the name.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default null\n+     * @return {string|null} The name, either a valid string or null if\n+     *                       no (valid) name has been set.\n+     */\n+    getName: function Annotation_getName() {\n+      return this.name;\n     },\n \n-    isPrintable: function Annotation_isPrintable() {\n-      var data = this.data;\n-      return !!(!this.isInvisible() &&\n-                data &&\n-                data.annotationFlags &&              // Default: not printable\n-                data.annotationFlags & 0x4 &&        // Print\n-                !(data.annotationFlags & 0x2) &&     // Hidden\n-                data.rect);                          // rectangle is necessary\n-    },\n-\n-    loadResources: function Annotation_loadResources(keys) {\n-      return new Promise(function (resolve, reject) {\n-        this.appearance.dict.getAsync('Resources').then(function (resources) {\n-          if (!resources) {\n-            resolve();\n-            return;\n-          }\n-          var objectLoader = new ObjectLoader(resources.map,\n-                                              keys,\n-                                              resources.xref);\n-          objectLoader.load().then(function() {\n-            resolve(resources);\n-          }, reject);\n-        }, reject);\n-      }.bind(this));\n+    /**\n+     * Set the (modification) date. Note that this is a PDF date string\n+     * that needs conversion before being used.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param date {string} The (modification) date\n+     */\n+    setDate: function Annotation_setDate(date) {\n+      this.date = date || null;\n     },\n \n-    getOperatorList: function Annotation_getOperatorList(evaluator) {\n+    /**\n+     * Get the (modification) date.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default null\n+     * @return {string|null} The (modification) date, either a valid PDF date\n+     *                       string or null if no (valid) date has been set.\n+     */\n+    getDate: function Annotation_getDate() {\n+      return this.date;\n+    },\n \n-      if (!this.appearance) {\n-        return Promise.resolve(new OperatorList());\n+    /**\n+     * Set the display rectangle.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param rectangle {Array} The display rectangle\n+     */\n+    setRectangle: function Annotation_setRectangle(rectangle) {\n+      if (isArray(rectangle) && rectangle.length === 4) {\n+        this.rectangle = Util.normalizeRect(rectangle);\n       }\n+    },\n \n-      var data = this.data;\n-\n-      var appearanceDict = this.appearance.dict;\n-      var resourcesPromise = this.loadResources([\n-        'ExtGState',\n-        'ColorSpace',\n-        'Pattern',\n-        'Shading',\n-        'XObject',\n-        'Font'\n-        // ProcSet\n-        // Properties\n-      ]);\n-      var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];\n-      var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];\n-      var transform = getTransformMatrix(data.rect, bbox, matrix);\n-      var self = this;\n-\n-      return resourcesPromise.then(function(resources) {\n-          var opList = new OperatorList();\n-          opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);\n-          return evaluator.getOperatorList(self.appearance, resources, opList).\n-            then(function () {\n-              opList.addOp(OPS.endAnnotation, []);\n-              self.appearance.reset();\n-              return opList;\n-            });\n-        });\n-    }\n-  };\n+    /**\n+     * Get the display rectangle.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default [0, 0, 0, 0]\n+     * @return {Array} The display rectangle\n+     */\n+    getRectangle: function Annotation_getRectangle() {\n+      return this.rectangle || DEFAULT_RECTANGLE;\n+    },\n \n-  Annotation.getConstructor =\n-      function Annotation_getConstructor(subtype, fieldType) {\n+    /**\n+     * Set the border style (as AnnotationBorderStyle object).\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param borderStyle {Dict} The border style dictionary\n+     */\n+    setBorderStyle: function Annotation_setBorderStyle(borderStyle) {\n+      if (isDict(borderStyle)) {\n+        if (borderStyle.has('BS')) {\n+          var dict = borderStyle.get('BS');\n+\n+          if (!dict.has('Type') || (isName(dict.get('Type')) &&\n+                                    dict.get('Type').name === 'Border')) {\n+            this.borderStyle.setWidth(dict.get('W'));\n+            this.borderStyle.setStyle(dict.get('S').name);\n+            this.borderStyle.setDashArray(dict.get('D'));\n+          }\n+        } else if (borderStyle.has('Border')) {\n+          var array = borderStyle.get('Border');\n+          if (isArray(array) && array.length >= 3) {\n+            this.borderStyle.setHorizontalCornerRadius(array[0]);\n+            this.borderStyle.setVerticalCornerRadius(array[1]);\n+            this.borderStyle.setWidth(array[2]);\n+            this.borderStyle.setStyle('S');\n+            \n+            if (array.length === 4) { // Dash array available\n+              this.borderStyle.setDashArray(array[3]);\n+            }\n+          }\n+        }\n+      }\n+    },\n \n-    if (!subtype) {\n-      return;\n-    }\n+    /**\n+     * Get the border style (as AnnotationBorderStyle object).\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default defaults of AnnotationBorderStyle\n+     * @return {AnnotationBorderStyle} The border style object\n+     */\n+    getBorderStyle: function Annotation_getBorderStyle() {\n+      return this.borderStyle;\n+    },\n \n-    // TODO(mack): Implement FreeText annotations\n-    if (subtype === 'Link') {\n-      return LinkAnnotation;\n-    } else if (subtype === 'Text') {\n-      return TextAnnotation;\n-    } else if (subtype === 'Widget') {\n-      if (!fieldType) {\n-        return;\n-      }\n+    /**\n+     * Set the color and take care of color space conversion.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param color {Array} The color of a particular color space\n+     */\n+    setColor: function Annotation_setColorSpace(color) {\n+      if (isArray(color)) {\n+        var rgbColor = new Uint8Array(3);\n+\n+        switch (color.length) {\n+          case 0: // Transparent border, so we do not need to draw it at all\n+            this.borderStyle.setWidth(0);\n+            break;\n \n-      if (fieldType === 'Tx') {\n-        return TextWidgetAnnotation;\n-      } else {\n-        return WidgetAnnotation;\n-      }\n-    } else {\n-      return Annotation;\n-    }\n-  };\n+          case 1: // Convert gray to RGB\n+            var grayCS = new DeviceGrayCS();\n+            grayCS.getRgbItem(color, 0, rgbColor, 0);\n+            this.color = rgbColor;\n+            break;\n \n-  Annotation.fromRef = function Annotation_fromRef(xref, ref) {\n+          case 3: // Convert RGB percentages to RGB\n+            var rgbCS = new DeviceRgbCS();\n+            rgbCS.getRgbItem(color, 0, rgbColor, 0);\n+            this.color = rgbColor;\n+            break;\n \n-    var dict = xref.fetchIfRef(ref);\n-    if (!isDict(dict)) {\n-      return;\n-    }\n+          case 4: // Convert CMYK to RGB\n+            var cmykCS = new DeviceCmykCS();\n+            cmykCS.getRgbItem(color, 0, rgbColor, 0);\n+            this.color = rgbColor;\n+            break;\n \n-    var subtype = dict.get('Subtype');\n-    subtype = isName(subtype) ? subtype.name : '';\n-    if (!subtype) {\n-      return;\n-    }\n+          default:\n+            break;\n+        }\n+      }\n+    },\n \n-    var fieldType = Util.getInheritableProperty(dict, 'FT');\n-    fieldType = isName(fieldType) ? fieldType.name : '';\n+    /**\n+     * Get the color in RGB color space.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default null\n+     * @return {Array} The color in RGB color space\n+     */\n+    getColor: function Annotation_getColor() {\n+      return this.color;\n+    },\n \n-    var Constructor = Annotation.getConstructor(subtype, fieldType);\n-    if (!Constructor) {\n-      return;\n-    }\n+    /**\n+     * Set the flags.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @param flags {integer} The flags\n+     * @see {@link shared/util.js}\n+     */\n+    setFlags: function Annotation_setFlag(flags) {\n+      if (flags === (flags | 0)) {\n+        this.flags = flags;\n+      }\n+    },\n \n-    var params = {\n-      dict: dict,\n-      ref: ref,\n-    };\n+    /**\n+     * Check if a provided flag is set.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default false\n+     * @return {boolean} Whether or not the provided flag is set\n+     */\n+    hasFlag: function Annotation_hasFlag(bitMask) {\n+      if (this.flags) {\n+        return (this.flags & bitMask) > 0;\n+      }\n+      return false;\n+    },\n \n-    var annotation = new Constructor(params);\n+    /**\n+     * Check if the annotation is viewable.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default true\n+     * @return {boolean} Whether or not the annotation is viewable\n+     */\n+    isViewable: function Annotation_isViewable() {\n+      if (this.flags) {\n+        return !this.hasFlag(AnnotationFlag.INVISIBLE) &&\n+               !this.hasFlag(AnnotationFlag.HIDDEN) &&\n+               !this.hasFlag(AnnotationFlag.NOVIEW);\n+      }\n+      return true;\n+    },\n \n-    if (annotation.isViewable() || annotation.isPrintable()) {\n-      return annotation;\n-    } else {\n-      if (SUPPORTED_TYPES.indexOf(subtype) === -1) {\n-        warn('unimplemented annotation type: ' + subtype);\n+    /**\n+     * Check if the annotation is printable.\n+     *\n+     * @public\n+     * @memberof Annotation\n+     * @default false\n+     * @return {boolean} Whether or not the annotation is printable\n+     */\n+    isPrintable: function Annotation_isPrintable() {\n+      if (this.flags) {\n+        return this.hasFlag(AnnotationFlag.PRINT);\n       }\n+      return false;\n     }\n   };\n \n-  Annotation.appendToOperatorList = function Annotation_appendToOperatorList(\n-      annotations, opList, pdfManager, partialEvaluator, intent) {\n-\n-    function reject(e) {\n-      annotationsReadyCapability.reject(e);\n-    }\n+  return Annotation;\n+})();\n \n-    var annotationsReadyCapability = createPromiseCapability();\n+/**\n+ * Contains all data regarding an annotation's border style.\n+ *\n+ * @class\n+ */\n+var AnnotationBorderStyle = (function AnnotationBorderStyleClosure() {\n+  /**\n+   * @constructor\n+   * @private\n+   */\n+  function AnnotationBorderStyle() {\n+    this.width = 1;\n+    this.style = AnnotationBorderStyleType.SOLID;\n+    this.dashArray = null;\n+    this.horizontalCornerRadius = 0;\n+    this.verticalCornerRadius = 0;\n+  }\n \n-    var annotationPromises = [];\n-    for (var i = 0, n = annotations.length; i < n; ++i) {\n-      if (intent === 'display' && annotations[i].isViewable() ||\n-          intent === 'print' && annotations[i].isPrintable()) {\n-        annotationPromises.push(\n-          annotations[i].getOperatorList(partialEvaluator));\n+  AnnotationBorderStyle.prototype = {\n+    /**\n+     * Set the width.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @param width {integer} The width\n+     */\n+    setWidth: function Annotation_setWidth(width) {\n+      if (width === (width | 0)) {\n+        this.width = width;\n       }\n-    }\n-    Promise.all(annotationPromises).then(function(datas) {\n-      opList.addOp(OPS.beginAnnotations, []);\n-      for (var i = 0, n = datas.length; i < n; ++i) {\n-        var annotOpList = datas[i];\n-        opList.addOpList(annotOpList);\n-      }\n-      opList.addOp(OPS.endAnnotations, []);\n-      annotationsReadyCapability.resolve();\n-    }, reject);\n+    },\n \n-    return annotationsReadyCapability.promise;\n-  };\n+    /**\n+     * Get the width.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @default 1\n+     * @return {integer} The width\n+     */\n+    getWidth: function Annotation_getWidth() {\n+      return this.width;\n+    },\n \n-  return Annotation;\n-})();\n+    /**\n+     * Set the style.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @param style {string} The style\n+     * @see {@link shared/util.js}\n+     */\n+    setStyle: function Annotation_setStyle(style) {\n+      switch (style) {\n+        case 'S':\n+          this.style = AnnotationBorderStyleType.SOLID;\n+          break;\n+\n+        case 'D':\n+          this.style = AnnotationBorderStyleType.DASHED;\n+          break;\n+\n+        case 'B':\n+          this.style = AnnotationBorderStyleType.BEVELED;\n+          break;\n+\n+        case 'I':\n+          this.style = AnnotationBorderStyleType.INSET;\n+          break;\n+\n+        case 'U':\n+          this.style = AnnotationBorderStyleType.UNDERLINE;\n+          break;\n+\n+        default:\n+          break;\n+      }\n+    },\n+\n+    /**\n+     * Get the style.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @default AnnotationBorderStyleType.SOLID\n+     * @return {integer} The style\n+     * @see {@link shared/util.js}\n+     */\n+    getStyle: function Annotation_getStyle() {\n+      return this.style;\n+    },\n \n-var WidgetAnnotation = (function WidgetAnnotationClosure() {\n-\n-  function WidgetAnnotation(params) {\n-    Annotation.call(this, params);\n-\n-    var dict = params.dict;\n-    var data = this.data;\n-\n-    data.fieldValue = stringToPDFString(\n-      Util.getInheritableProperty(dict, 'V') || '');\n-    data.alternativeText = stringToPDFString(dict.get('TU') || '');\n-    data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';\n-    var fieldType = Util.getInheritableProperty(dict, 'FT');\n-    data.fieldType = isName(fieldType) ? fieldType.name : '';\n-    data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;\n-    this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;\n-\n-    // Building the full field name by collecting the field and\n-    // its ancestors 'T' data and joining them using '.'.\n-    var fieldName = [];\n-    var namedItem = dict;\n-    var ref = params.ref;\n-    while (namedItem) {\n-      var parent = namedItem.get('Parent');\n-      var parentRef = namedItem.getRaw('Parent');\n-      var name = namedItem.get('T');\n-      if (name) {\n-        fieldName.unshift(stringToPDFString(name));\n-      } else {\n-        // The field name is absent, that means more than one field\n-        // with the same name may exist. Replacing the empty name\n-        // with the '`' plus index in the parent's 'Kids' array.\n-        // This is not in the PDF spec but necessary to id the\n-        // the input controls.\n-        var kids = parent.get('Kids');\n-        var j, jj;\n-        for (j = 0, jj = kids.length; j < jj; j++) {\n-          var kidRef = kids[j];\n-          if (kidRef.num === ref.num && kidRef.gen === ref.gen) {\n+    /**\n+     * Set the dash array.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @param dashArray {Array} The dash array with at least one element\n+     */\n+    setDashArray: function Annotation_setDashArray(dashArray) {\n+      if (isArray(dashArray) && dashArray.length > 0) {\n+        // According to the PDF specification: the elements in a dashArray\n+        // shall be numbers that are nonnegative and not all equal to zero.\n+        var isInvalid = false;\n+        var numPositive = 0;\n+        for (var i = 0, len = dashArray.length; i < len; i++) {\n+          var validNumber = (+dashArray[i] >= 0);\n+          if (!validNumber) {\n+            isInvalid = true;\n             break;\n+          } else if (dashArray[i] > 0) {\n+            numPositive++;\n           }\n         }\n-        fieldName.unshift('`' + j);\n+        if (!isInvalid && numPositive > 0) {\n+          this.dashArray = dashArray;\n+        } else {\n+          this.width = 0; // Adobe behavior when the array is invalid.\n+        }\n+      } else if(dashArray) {\n+        this.width = 0; // Adobe behavior when the array is invalid.\n       }\n-      namedItem = parent;\n-      ref = parentRef;\n-    }\n-    data.fullName = fieldName.join('.');\n-  }\n+    },\n+\n+    /**\n+     * Get the dash array.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @default [3]\n+     * @return {Array} The dash array\n+     */\n+    getDashArray: function Annotation_getDashArray() {\n+      return this.dashArray || DEFAULT_DASH_ARRAY;\n+    },\n+\n+    /**\n+     * Set the horizontal corner radius (from a Border dictionary).\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @param radius {integer} The horizontal corner radius\n+     */\n+    setHorizontalCornerRadius:\n+        function Annotation_setHorizontalCornerRadius(radius) {\n+      if (radius === (radius | 0)) {\n+        this.horizontalCornerRadius = radius;\n+      }\n+    },\n+\n+    /**\n+     * Get the horizontal corner radius.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @default 0\n+     * @return {integer} The horizontal corner radius\n+     */\n+    getHorizontalCornerRadius:\n+        function Annotation_getHorizontalCornerRadius() {\n+      return this.horizontalCornerRadius;\n+    },\n \n-  var parent = Annotation.prototype;\n-  Util.inherit(WidgetAnnotation, Annotation, {\n-    isViewable: function WidgetAnnotation_isViewable() {\n-      if (this.data.fieldType === 'Sig') {\n-        warn('unimplemented annotation type: Widget signature');\n-        return false;\n+    /**\n+     * Set the vertical corner radius (from a Border dictionary).\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @param radius {integer} The vertical corner radius\n+     */\n+    setVerticalCornerRadius:\n+        function Annotation_setVerticalCornerRadius(radius) {\n+      if (radius === (radius | 0)) {\n+        this.verticalCornerRadius = radius;\n       }\n+    },\n \n-      return parent.isViewable.call(this);\n+    /**\n+     * Get the vertical corner radius.\n+     *\n+     * @public\n+     * @memberof AnnotationBorderStyle\n+     * @default 0\n+     * @return {integer} The vertical corner radius\n+     */\n+    getVerticalCornerRadius:\n+        function Annotation_getVerticalCornerRadius() {\n+      return this.verticalCornerRadius;\n     }\n-  });\n+  };\n \n-  return WidgetAnnotation;\n+  return AnnotationBorderStyle;\n })();\n \n-var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {\n-  function TextWidgetAnnotation(params) {\n-    WidgetAnnotation.call(this, params);\n-\n-    this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');\n-    this.data.annotationType = AnnotationType.WIDGET;\n-    this.data.hasHtml = !this.data.hasAppearance && !!this.data.fieldValue;\n+/**\n+ * Provides an interface to annotations (used by\n+ * src/core/core.js).\n+ *\n+ * @class\n+ */\n+var AnnotationLayer = (function AnnotationLayerClosure() {\n+  /**\n+   * @constructor\n+   * @private\n+   */\n+  function AnnotationLayer() {\n+    this.annotationHandler = new AnnotationHandler();\n   }\n \n-  Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {\n-    getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {\n-      if (this.appearance) {\n-        return Annotation.prototype.getOperatorList.call(this, evaluator);\n+  AnnotationLayer.prototype = {\n+    /**\n+     * Creates a single annotation of a requested type.\n+     *\n+     * @public\n+     * @memberof AnnotationLayer\n+     * @param params {Object} Parameters (such as ID) for creating\n+     *                        the annotation.\n+     * @return {Annotation|null} Either an annotation object of a supported\n+     *                           type or null if the requested type is not\n+     *                           supported or the annotation is not viewable \n+     *                           or printable.\n+     */\n+    getAnnotation: function AnnotationLayer_getAnnotation(dict, ref) {\n+      // Make sure we are dealing with a valid annotation dictionary.\n+      if (!isDict(dict) || (isDict(dict) && isName(dict.get('Type')) &&\n+                            dict.get('Type').name !== 'Annot')) {\n+        warn('Invalid annotation dictionary');\n+        return;\n       }\n \n-      var opList = new OperatorList();\n-      var data = this.data;\n-\n-      // Even if there is an appearance stream, ignore it. This is the\n-      // behaviour used by Adobe Reader.\n-      if (!data.defaultAppearance) {\n-        return Promise.resolve(opList);\n+      var subType = dict.get('Subtype');\n+      if (!isName(subType)) {\n+        return;\n+      }\n+      subType = subType.name;\n+\n+      // Create and configure the annotation object.\n+      var annotation = null;\n+      var params = {\n+        dict: dict,\n+        ref: ref\n+      };\n+\n+      switch (subType) {\n+        case 'Link':\n+          annotation = this.annotationHandler.createLinkAnnotation(params);\n+          break;\n+\n+        default:\n+          warn('Unimplemented annotation type: ' + subType);\n+          return null;\n       }\n \n-      var stream = new Stream(stringToBytes(data.defaultAppearance));\n-      return evaluator.getOperatorList(stream, this.fieldResources, opList).\n-        then(function () {\n-          return opList;\n-        });\n-    }\n-  });\n+      if (annotation.isViewable() || annotation.isPrintable()) {\n+        return annotation;\n+      }\n+      return null;\n+    },\n \n-  return TextWidgetAnnotation;\n-})();\n+    /**\n+     * Appends annotations to the operator list.\n+     *\n+     * @public\n+     * @memberof AnnotationLayer\n+     * @param annotations {Array} All available annotations\n+     * @param opList {OperatorList} The current operator list\n+     * @param partialEvaluator {PartialEvaluator} The partial evaluator\n+     * @param intent {string} The rendering intent (display or print)\n+     * @return {Promise} Promise indicating that the annotations are ready.\n+     */\n+    appendToOperatorList:\n+        function AnnotationLayer_appendToOperatorList(annotations, opList,\n+                                                      partialEvaluator,\n+                                                      intent) {\n+      function reject(e) {\n+        annotationsReadyCapability.reject(e);\n+      }\n \n-var InteractiveAnnotation = (function InteractiveAnnotationClosure() {\n-  function InteractiveAnnotation(params) {\n-    Annotation.call(this, params);\n+      var annotationsReadyCapability = createPromiseCapability();","path":"src/core/annotation.js","position":981,"original_position":981,"commit_id":"82096a340bd4c89b3bdd745f2d9d9d7a942e0d12","original_commit_id":"82096a340bd4c89b3bdd745f2d9d9d7a942e0d12","user":{"login":"Snuffleupagus","id":2692120,"avatar_url":"https://avatars.githubusercontent.com/u/2692120?v=2","gravatar_id":"e05382aa67ec592c3eb32e9cd9edd65e","url":"https://api.github.com/users/Snuffleupagus","html_url":"https://github.com/Snuffleupagus","followers_url":"https://api.github.com/users/Snuffleupagus/followers","following_url":"https://api.github.com/users/Snuffleupagus/following{/other_user}","gists_url":"https://api.github.com/users/Snuffleupagus/gists{/gist_id}","starred_url":"https://api.github.com/users/Snuffleupagus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Snuffleupagus/subscriptions","organizations_url":"https://api.github.com/users/Snuffleupagus/orgs","repos_url":"https://api.github.com/users/Snuffleupagus/repos","events_url":"https://api.github.com/users/Snuffleupagus/events{/privacy}","received_events_url":"https://api.github.com/users/Snuffleupagus/received_events","type":"User","site_admin":false},"body":"Is it necessary to use `createPromiseCapability`, couldn't you just `return Promise.all(...).then(...);` instead?","created_at":"2014-08-27T13:31:10Z","updated_at":"2014-08-27T13:31:10Z","html_url":"https://github.com/mozilla/pdf.js/pull/5218#discussion_r16774507","pull_request_url":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218","_links":{"self":{"href":"https://api.github.com/repos/mozilla/pdf.js/pulls/comments/16774507"},"html":{"href":"https://github.com/mozilla/pdf.js/pull/5218#discussion_r16774507"},"pull_request":{"href":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218"}}},"pull_request":{"url":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218","id":20042973,"html_url":"https://github.com/mozilla/pdf.js/pull/5218","diff_url":"https://github.com/mozilla/pdf.js/pull/5218.diff","patch_url":"https://github.com/mozilla/pdf.js/pull/5218.patch","issue_url":"https://api.github.com/repos/mozilla/pdf.js/issues/5218","number":5218,"state":"open","locked":false,"title":"Complete rewrite of the annotation layer","user":{"login":"timvandermeij","id":1993262,"avatar_url":"https://avatars.githubusercontent.com/u/1993262?v=2","gravatar_id":"b19e1f4e6d482316177aab766f78a867","url":"https://api.github.com/users/timvandermeij","html_url":"https://github.com/timvandermeij","followers_url":"https://api.github.com/users/timvandermeij/followers","following_url":"https://api.github.com/users/timvandermeij/following{/other_user}","gists_url":"https://api.github.com/users/timvandermeij/gists{/gist_id}","starred_url":"https://api.github.com/users/timvandermeij/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/timvandermeij/subscriptions","organizations_url":"https://api.github.com/users/timvandermeij/orgs","repos_url":"https://api.github.com/users/timvandermeij/repos","events_url":"https://api.github.com/users/timvandermeij/events{/privacy}","received_events_url":"https://api.github.com/users/timvandermeij/received_events","type":"User","site_admin":false},"body":"*My apologies in advance for the wall of text!*\r\n\r\nThis PR aims to provide a drop-in replacement for the current annotation code. In my opinion the current code is not structured enough, incomplete, not documented enough and it lacks basic testing. My goals with this PR are to provide a solid framework to create and present annotations in which we can easily implement new annotation types. It provides the necessary abstraction to do this.\r\n\r\nThe basis of this PR is the base class `Annotation`. It contains all information a basic annotation should have. Specific annotation types, such as Link or Text, will extend this base class with their own specific methods (see `src/core/annotation_types/link_annotation.js` for example). This is not only a nice way to work with annotations, but it's also what the PDF specification recommends.\r\n\r\nThe existing `AnnotationUtils` class and `web/page_view.js` have been rewritten to work with this new framework, but they have also been simplified a lot because most logic is now where it belongs: either in the annotation object itself or in `annotation_handler.js`. I wanted to avoid having styles for example being applied in many places, which is what happens in the current code. Doing so makes sure that for example `page_view.js` only deals with printing the annotations and not also with styling them anymore.\r\n\r\nAside from rewriting the current annotation layer for the sake of readability, structure and maintainability, I also felt that the current implementation was incomplete at some points and almost none of it was documented. What I did was read the entire part of the PDF specification on annotations and made an exhaustive overview of all features the annotation layer should have. This has been done so we know exactly what we do and do not support currently. This can be found in the implementation overview document (see below).\r\n\r\nAfter having made the abovementioned overview, I have also implemented some new features that were missing in the previous implementation layer. Examples are:\r\n\r\n- Implemented full support for radii in a Border dictionary.\r\n- Implemented full support for colorspaces used by annotations.\r\n- Implemented full support for the 'underline' border style.\r\n- Implemented partial support for the 'dashed' border style (because of CSS limitations we can also show the default dashed border style, but it's better than showing nothing).\r\n- Improved handling of `GoToR` link types.\r\n\r\nFinally, after rewriting everything and adding new features, I decided to fix the lack of tests. I have added unit tests for practically all features of the annotation layer that are currently implemented.\r\n\r\nThis is still a work in progress. The to do list can be found in the implementation overview document (see below).\r\n\r\nI have prepared a ZIP file with eight test documents for the Link annotation and the implementation overview document as referenced above. This file can be downloaded from http://www39.zippyshare.com/v/28118637/file.html.\r\n\r\n- [ ] Rendering of LinkAnnotation3.pdf seems better to me than in Reader (because we actually use the border width as per spec and Reader seems to ignore this), but is OK to do this differently?","created_at":"2014-08-20T12:59:29Z","updated_at":"2014-08-27T13:31:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7562f20e9b2fb3d80371306ef0dd6e0c1cae7853","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218/commits","review_comments_url":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218/comments","review_comment_url":"https://api.github.com/repos/mozilla/pdf.js/pulls/comments/{number}","comments_url":"https://api.github.com/repos/mozilla/pdf.js/issues/5218/comments","statuses_url":"https://api.github.com/repos/mozilla/pdf.js/statuses/82096a340bd4c89b3bdd745f2d9d9d7a942e0d12","head":{"label":"timvandermeij:annotation-layer","ref":"annotation-layer","sha":"82096a340bd4c89b3bdd745f2d9d9d7a942e0d12","user":{"login":"timvandermeij","id":1993262,"avatar_url":"https://avatars.githubusercontent.com/u/1993262?v=2","gravatar_id":"b19e1f4e6d482316177aab766f78a867","url":"https://api.github.com/users/timvandermeij","html_url":"https://github.com/timvandermeij","followers_url":"https://api.github.com/users/timvandermeij/followers","following_url":"https://api.github.com/users/timvandermeij/following{/other_user}","gists_url":"https://api.github.com/users/timvandermeij/gists{/gist_id}","starred_url":"https://api.github.com/users/timvandermeij/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/timvandermeij/subscriptions","organizations_url":"https://api.github.com/users/timvandermeij/orgs","repos_url":"https://api.github.com/users/timvandermeij/repos","events_url":"https://api.github.com/users/timvandermeij/events{/privacy}","received_events_url":"https://api.github.com/users/timvandermeij/received_events","type":"User","site_admin":false},"repo":{"id":6161909,"name":"pdf.js","full_name":"timvandermeij/pdf.js","owner":{"login":"timvandermeij","id":1993262,"avatar_url":"https://avatars.githubusercontent.com/u/1993262?v=2","gravatar_id":"b19e1f4e6d482316177aab766f78a867","url":"https://api.github.com/users/timvandermeij","html_url":"https://github.com/timvandermeij","followers_url":"https://api.github.com/users/timvandermeij/followers","following_url":"https://api.github.com/users/timvandermeij/following{/other_user}","gists_url":"https://api.github.com/users/timvandermeij/gists{/gist_id}","starred_url":"https://api.github.com/users/timvandermeij/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/timvandermeij/subscriptions","organizations_url":"https://api.github.com/users/timvandermeij/orgs","repos_url":"https://api.github.com/users/timvandermeij/repos","events_url":"https://api.github.com/users/timvandermeij/events{/privacy}","received_events_url":"https://api.github.com/users/timvandermeij/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/timvandermeij/pdf.js","description":"PDF Reader in JavaScript","fork":true,"url":"https://api.github.com/repos/timvandermeij/pdf.js","forks_url":"https://api.github.com/repos/timvandermeij/pdf.js/forks","keys_url":"https://api.github.com/repos/timvandermeij/pdf.js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/timvandermeij/pdf.js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/timvandermeij/pdf.js/teams","hooks_url":"https://api.github.com/repos/timvandermeij/pdf.js/hooks","issue_events_url":"https://api.github.com/repos/timvandermeij/pdf.js/issues/events{/number}","events_url":"https://api.github.com/repos/timvandermeij/pdf.js/events","assignees_url":"https://api.github.com/repos/timvandermeij/pdf.js/assignees{/user}","branches_url":"https://api.github.com/repos/timvandermeij/pdf.js/branches{/branch}","tags_url":"https://api.github.com/repos/timvandermeij/pdf.js/tags","blobs_url":"https://api.github.com/repos/timvandermeij/pdf.js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/timvandermeij/pdf.js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/timvandermeij/pdf.js/git/refs{/sha}","trees_url":"https://api.github.com/repos/timvandermeij/pdf.js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/timvandermeij/pdf.js/statuses/{sha}","languages_url":"https://api.github.com/repos/timvandermeij/pdf.js/languages","stargazers_url":"https://api.github.com/repos/timvandermeij/pdf.js/stargazers","contributors_url":"https://api.github.com/repos/timvandermeij/pdf.js/contributors","subscribers_url":"https://api.github.com/repos/timvandermeij/pdf.js/subscribers","subscription_url":"https://api.github.com/repos/timvandermeij/pdf.js/subscription","commits_url":"https://api.github.com/repos/timvandermeij/pdf.js/commits{/sha}","git_commits_url":"https://api.github.com/repos/timvandermeij/pdf.js/git/commits{/sha}","comments_url":"https://api.github.com/repos/timvandermeij/pdf.js/comments{/number}","issue_comment_url":"https://api.github.com/repos/timvandermeij/pdf.js/issues/comments/{number}","contents_url":"https://api.github.com/repos/timvandermeij/pdf.js/contents/{+path}","compare_url":"https://api.github.com/repos/timvandermeij/pdf.js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/timvandermeij/pdf.js/merges","archive_url":"https://api.github.com/repos/timvandermeij/pdf.js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/timvandermeij/pdf.js/downloads","issues_url":"https://api.github.com/repos/timvandermeij/pdf.js/issues{/number}","pulls_url":"https://api.github.com/repos/timvandermeij/pdf.js/pulls{/number}","milestones_url":"https://api.github.com/repos/timvandermeij/pdf.js/milestones{/number}","notifications_url":"https://api.github.com/repos/timvandermeij/pdf.js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/timvandermeij/pdf.js/labels{/name}","releases_url":"https://api.github.com/repos/timvandermeij/pdf.js/releases{/id}","created_at":"2012-10-10T18:16:22Z","updated_at":"2014-06-25T20:59:29Z","pushed_at":"2014-08-25T22:05:21Z","git_url":"git://github.com/timvandermeij/pdf.js.git","ssh_url":"git@github.com:timvandermeij/pdf.js.git","clone_url":"https://github.com/timvandermeij/pdf.js.git","svn_url":"https://github.com/timvandermeij/pdf.js","homepage":"","size":44520,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"mozilla:master","ref":"master","sha":"4834f1c28924b3322f00b8383ddb8478193be930","user":{"login":"mozilla","id":131524,"avatar_url":"https://avatars.githubusercontent.com/u/131524?v=2","gravatar_id":"9ba739c8f3288e256b13553e4d257b5e","url":"https://api.github.com/users/mozilla","html_url":"https://github.com/mozilla","followers_url":"https://api.github.com/users/mozilla/followers","following_url":"https://api.github.com/users/mozilla/following{/other_user}","gists_url":"https://api.github.com/users/mozilla/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla/subscriptions","organizations_url":"https://api.github.com/users/mozilla/orgs","repos_url":"https://api.github.com/users/mozilla/repos","events_url":"https://api.github.com/users/mozilla/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla/received_events","type":"Organization","site_admin":false},"repo":{"id":1663468,"name":"pdf.js","full_name":"mozilla/pdf.js","owner":{"login":"mozilla","id":131524,"avatar_url":"https://avatars.githubusercontent.com/u/131524?v=2","gravatar_id":"9ba739c8f3288e256b13553e4d257b5e","url":"https://api.github.com/users/mozilla","html_url":"https://github.com/mozilla","followers_url":"https://api.github.com/users/mozilla/followers","following_url":"https://api.github.com/users/mozilla/following{/other_user}","gists_url":"https://api.github.com/users/mozilla/gists{/gist_id}","starred_url":"https://api.github.com/users/mozilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mozilla/subscriptions","organizations_url":"https://api.github.com/users/mozilla/orgs","repos_url":"https://api.github.com/users/mozilla/repos","events_url":"https://api.github.com/users/mozilla/events{/privacy}","received_events_url":"https://api.github.com/users/mozilla/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mozilla/pdf.js","description":"PDF Reader in JavaScript","fork":false,"url":"https://api.github.com/repos/mozilla/pdf.js","forks_url":"https://api.github.com/repos/mozilla/pdf.js/forks","keys_url":"https://api.github.com/repos/mozilla/pdf.js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mozilla/pdf.js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mozilla/pdf.js/teams","hooks_url":"https://api.github.com/repos/mozilla/pdf.js/hooks","issue_events_url":"https://api.github.com/repos/mozilla/pdf.js/issues/events{/number}","events_url":"https://api.github.com/repos/mozilla/pdf.js/events","assignees_url":"https://api.github.com/repos/mozilla/pdf.js/assignees{/user}","branches_url":"https://api.github.com/repos/mozilla/pdf.js/branches{/branch}","tags_url":"https://api.github.com/repos/mozilla/pdf.js/tags","blobs_url":"https://api.github.com/repos/mozilla/pdf.js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mozilla/pdf.js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mozilla/pdf.js/git/refs{/sha}","trees_url":"https://api.github.com/repos/mozilla/pdf.js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mozilla/pdf.js/statuses/{sha}","languages_url":"https://api.github.com/repos/mozilla/pdf.js/languages","stargazers_url":"https://api.github.com/repos/mozilla/pdf.js/stargazers","contributors_url":"https://api.github.com/repos/mozilla/pdf.js/contributors","subscribers_url":"https://api.github.com/repos/mozilla/pdf.js/subscribers","subscription_url":"https://api.github.com/repos/mozilla/pdf.js/subscription","commits_url":"https://api.github.com/repos/mozilla/pdf.js/commits{/sha}","git_commits_url":"https://api.github.com/repos/mozilla/pdf.js/git/commits{/sha}","comments_url":"https://api.github.com/repos/mozilla/pdf.js/comments{/number}","issue_comment_url":"https://api.github.com/repos/mozilla/pdf.js/issues/comments/{number}","contents_url":"https://api.github.com/repos/mozilla/pdf.js/contents/{+path}","compare_url":"https://api.github.com/repos/mozilla/pdf.js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mozilla/pdf.js/merges","archive_url":"https://api.github.com/repos/mozilla/pdf.js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mozilla/pdf.js/downloads","issues_url":"https://api.github.com/repos/mozilla/pdf.js/issues{/number}","pulls_url":"https://api.github.com/repos/mozilla/pdf.js/pulls{/number}","milestones_url":"https://api.github.com/repos/mozilla/pdf.js/milestones{/number}","notifications_url":"https://api.github.com/repos/mozilla/pdf.js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mozilla/pdf.js/labels{/name}","releases_url":"https://api.github.com/repos/mozilla/pdf.js/releases{/id}","created_at":"2011-04-26T06:32:03Z","updated_at":"2014-08-27T12:06:05Z","pushed_at":"2014-08-26T10:39:50Z","git_url":"git://github.com/mozilla/pdf.js.git","ssh_url":"git@github.com:mozilla/pdf.js.git","clone_url":"https://github.com/mozilla/pdf.js.git","svn_url":"https://github.com/mozilla/pdf.js","homepage":"","size":117096,"stargazers_count":9480,"watchers_count":9480,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":2038,"mirror_url":null,"open_issues_count":438,"forks":2038,"open_issues":438,"watchers":9480,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218"},"html":{"href":"https://github.com/mozilla/pdf.js/pull/5218"},"issue":{"href":"https://api.github.com/repos/mozilla/pdf.js/issues/5218"},"comments":{"href":"https://api.github.com/repos/mozilla/pdf.js/issues/5218/comments"},"review_comments":{"href":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218/comments"},"review_comment":{"href":"https://api.github.com/repos/mozilla/pdf.js/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/mozilla/pdf.js/pulls/5218/commits"},"statuses":{"href":"https://api.github.com/repos/mozilla/pdf.js/statuses/82096a340bd4c89b3bdd745f2d9d9d7a942e0d12"}}}}