Changeset 90280 in spip-zone


Ignore:
Timestamp:
Jun 15, 2015, 9:09:52 PM (4 years ago)
Author:
kent1@…
Message:

Gros saut de version, on passe de la version 0.8.1017 à 1.1.3 de la lib (à priori la dernière version stable d'après https://mozilla.github.io/pdf.js/getting_started/#download)

A priori fonctionnelle comme l'ancienne version... Mais ne sera pas compatible si des gens surchargent pdfjs.html :(

Version 0.5.0 du plugin

Location:
_plugins_/pdfjs
Files:
359 added
50 deleted
74 edited
1 moved

Legend:

Unmodified
Added
Removed
  • _plugins_/pdfjs/lib/pdfjs/build/pdf.js

    r80718 r90280  
    1515 * limitations under the License.
    1616 */
     17/*jshint globalstrict: false */
     18/* globals PDFJS */
    1719
    1820// Initializing PDFJS global object (if still undefined)
     
    2123}
    2224
    23 PDFJS.version = '0.8.1017';
    24 PDFJS.build = '35f5a1e';
     25PDFJS.version = '1.1.3';
     26PDFJS.build = '05991e9';
    2527
    2628(function pdfjsWrapper() {
     
    5153var globalScope = (typeof window === 'undefined') ? this : window;
    5254
    53 var isWorker = (typeof window == 'undefined');
     55var isWorker = (typeof window === 'undefined');
    5456
    5557var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
     
    6668  FILL_STROKE_MASK: 3,
    6769  ADD_TO_PATH_FLAG: 4
     70};
     71
     72var ImageKind = {
     73  GRAYSCALE_1BPP: 1,
     74  RGB_24BPP: 2,
     75  RGBA_32BPP: 3
     76};
     77
     78var AnnotationType = {
     79  WIDGET: 1,
     80  TEXT: 2,
     81  LINK: 3
     82};
     83
     84var StreamType = {
     85  UNKNOWN: 0,
     86  FLATE: 1,
     87  LZW: 2,
     88  DCT: 3,
     89  JPX: 4,
     90  JBIG: 5,
     91  A85: 6,
     92  AHX: 7,
     93  CCF: 8,
     94  RL: 9
     95};
     96
     97var FontType = {
     98  UNKNOWN: 0,
     99  TYPE1: 1,
     100  TYPE1C: 2,
     101  CIDFONTTYPE0: 3,
     102  CIDFONTTYPE0C: 4,
     103  TRUETYPE: 5,
     104  CIDFONTTYPE2: 6,
     105  TYPE3: 7,
     106  OPENTYPE: 8,
     107  TYPE0: 9,
     108  MMTYPE1: 10
    68109};
    69110
     
    173214  paintImageXObject: 85,
    174215  paintInlineImageXObject: 86,
    175   paintInlineImageXObjectGroup: 87
     216  paintInlineImageXObjectGroup: 87,
     217  paintImageXObjectRepeat: 88,
     218  paintImageMaskXObjectRepeat: 89,
     219  paintSolidColorImageMask: 90,
     220  constructPath: 91
    176221};
    177222
     
    219264
    220265function assert(cond, msg) {
    221   if (!cond)
     266  if (!cond) {
    222267    error(msg);
     268  }
    223269}
    224270
     
    251297// absolute URL, it will be returned as is.
    252298function combineUrl(baseUrl, url) {
    253   if (!url)
     299  if (!url) {
    254300    return baseUrl;
    255   if (/^[a-z][a-z0-9+\-.]*:/i.test(url))
     301  }
     302  if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) {
    256303    return url;
    257   if (url.charAt(0) == '/') {
     304  }
     305  var i;
     306  if (url.charAt(0) === '/') {
    258307    // absolute path
    259     var i = baseUrl.indexOf('://');
    260     i = baseUrl.indexOf('/', i + 3);
     308    i = baseUrl.indexOf('://');
     309    if (url.charAt(1) === '/') {
     310      ++i;
     311    } else {
     312      i = baseUrl.indexOf('/', i + 3);
     313    }
    261314    return baseUrl.substring(0, i) + url;
    262315  } else {
    263316    // relative path
    264     var pathLength = baseUrl.length, i;
     317    var pathLength = baseUrl.length;
    265318    i = baseUrl.lastIndexOf('#');
    266319    pathLength = i >= 0 ? i : pathLength;
     
    289342    case 'ftp':
    290343    case 'mailto':
     344    case 'tel':
    291345      return true;
    292346    default:
     
    295349}
    296350PDFJS.isValidUrl = isValidUrl;
    297 
    298 // In a well-formed PDF, |cond| holds.  If it doesn't, subsequent
    299 // behavior is undefined.
    300 function assertWellFormed(cond, msg) {
    301   if (!cond)
    302     error(msg);
    303 }
    304351
    305352function shadow(obj, prop, value) {
     
    310357  return value;
    311358}
     359PDFJS.shadow = shadow;
    312360
    313361var PasswordResponses = PDFJS.PasswordResponses = {
     
    328376  return PasswordException;
    329377})();
     378PDFJS.PasswordException = PasswordException;
    330379
    331380var UnknownErrorException = (function UnknownErrorExceptionClosure() {
     
    341390  return UnknownErrorException;
    342391})();
     392PDFJS.UnknownErrorException = UnknownErrorException;
    343393
    344394var InvalidPDFException = (function InvalidPDFExceptionClosure() {
     
    353403  return InvalidPDFException;
    354404})();
     405PDFJS.InvalidPDFException = InvalidPDFException;
    355406
    356407var MissingPDFException = (function MissingPDFExceptionClosure() {
     
    365416  return MissingPDFException;
    366417})();
     418PDFJS.MissingPDFException = MissingPDFException;
     419
     420var UnexpectedResponseException =
     421    (function UnexpectedResponseExceptionClosure() {
     422  function UnexpectedResponseException(msg, status) {
     423    this.name = 'UnexpectedResponseException';
     424    this.message = msg;
     425    this.status = status;
     426  }
     427
     428  UnexpectedResponseException.prototype = new Error();
     429  UnexpectedResponseException.constructor = UnexpectedResponseException;
     430
     431  return UnexpectedResponseException;
     432})();
     433PDFJS.UnexpectedResponseException = UnexpectedResponseException;
    367434
    368435var NotImplementedException = (function NotImplementedExceptionClosure() {
     
    406473
    407474function bytesToString(bytes) {
    408   var str = '';
     475  assert(bytes !== null && typeof bytes === 'object' &&
     476         bytes.length !== undefined, 'Invalid argument for bytesToString');
    409477  var length = bytes.length;
    410   for (var n = 0; n < length; ++n)
    411     str += String.fromCharCode(bytes[n]);
    412   return str;
     478  var MAX_ARGUMENT_COUNT = 8192;
     479  if (length < MAX_ARGUMENT_COUNT) {
     480    return String.fromCharCode.apply(null, bytes);
     481  }
     482  var strBuf = [];
     483  for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
     484    var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
     485    var chunk = bytes.subarray(i, chunkEnd);
     486    strBuf.push(String.fromCharCode.apply(null, chunk));
     487  }
     488  return strBuf.join('');
    413489}
    414490
    415491function stringToBytes(str) {
     492  assert(typeof str === 'string', 'Invalid argument for stringToBytes');
    416493  var length = str.length;
    417494  var bytes = new Uint8Array(length);
    418   for (var n = 0; n < length; ++n)
    419     bytes[n] = str.charCodeAt(n) & 0xFF;
     495  for (var i = 0; i < length; ++i) {
     496    bytes[i] = str.charCodeAt(i) & 0xFF;
     497  }
    420498  return bytes;
    421499}
    422500
     501function string32(value) {
     502  return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
     503                             (value >> 8) & 0xff, value & 0xff);
     504}
     505
     506function log2(x) {
     507  var n = 1, i = 0;
     508  while (x > n) {
     509    n <<= 1;
     510    i++;
     511  }
     512  return i;
     513}
     514
     515function readInt8(data, start) {
     516  return (data[start] << 24) >> 24;
     517}
     518
     519function readUint16(data, offset) {
     520  return (data[offset] << 8) | data[offset + 1];
     521}
     522
     523function readUint32(data, offset) {
     524  return ((data[offset] << 24) | (data[offset + 1] << 16) |
     525         (data[offset + 2] << 8) | data[offset + 3]) >>> 0;
     526}
     527
     528// Lazy test the endianness of the platform
     529// NOTE: This will be 'true' for simulated TypedArrays
     530function isLittleEndian() {
     531  var buffer8 = new Uint8Array(2);
     532  buffer8[0] = 1;
     533  var buffer16 = new Uint16Array(buffer8.buffer);
     534  return (buffer16[0] === 1);
     535}
     536
     537Object.defineProperty(PDFJS, 'isLittleEndian', {
     538  configurable: true,
     539  get: function PDFJS_isLittleEndian() {
     540    return shadow(PDFJS, 'isLittleEndian', isLittleEndian());
     541  }
     542});
     543
     544  // Lazy test if the userAgant support CanvasTypedArrays
     545function hasCanvasTypedArrays() {
     546  var canvas = document.createElement('canvas');
     547  canvas.width = canvas.height = 1;
     548  var ctx = canvas.getContext('2d');
     549  var imageData = ctx.createImageData(1, 1);
     550  return (typeof imageData.data.buffer !== 'undefined');
     551}
     552
     553Object.defineProperty(PDFJS, 'hasCanvasTypedArrays', {
     554  configurable: true,
     555  get: function PDFJS_hasCanvasTypedArrays() {
     556    return shadow(PDFJS, 'hasCanvasTypedArrays', hasCanvasTypedArrays());
     557  }
     558});
     559
     560var Uint32ArrayView = (function Uint32ArrayViewClosure() {
     561
     562  function Uint32ArrayView(buffer, length) {
     563    this.buffer = buffer;
     564    this.byteLength = buffer.length;
     565    this.length = length === undefined ? (this.byteLength >> 2) : length;
     566    ensureUint32ArrayViewProps(this.length);
     567  }
     568  Uint32ArrayView.prototype = Object.create(null);
     569
     570  var uint32ArrayViewSetters = 0;
     571  function createUint32ArrayProp(index) {
     572    return {
     573      get: function () {
     574        var buffer = this.buffer, offset = index << 2;
     575        return (buffer[offset] | (buffer[offset + 1] << 8) |
     576          (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0;
     577      },
     578      set: function (value) {
     579        var buffer = this.buffer, offset = index << 2;
     580        buffer[offset] = value & 255;
     581        buffer[offset + 1] = (value >> 8) & 255;
     582        buffer[offset + 2] = (value >> 16) & 255;
     583        buffer[offset + 3] = (value >>> 24) & 255;
     584      }
     585    };
     586  }
     587
     588  function ensureUint32ArrayViewProps(length) {
     589    while (uint32ArrayViewSetters < length) {
     590      Object.defineProperty(Uint32ArrayView.prototype,
     591        uint32ArrayViewSetters,
     592        createUint32ArrayProp(uint32ArrayViewSetters));
     593      uint32ArrayViewSetters++;
     594    }
     595  }
     596
     597  return Uint32ArrayView;
     598})();
     599
    423600var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
    424601
     
    426603  function Util() {}
    427604
    428   Util.makeCssRgb = function Util_makeCssRgb(rgb) {
    429     return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
    430   };
    431 
    432   Util.makeCssCmyk = function Util_makeCssCmyk(cmyk) {
    433     var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0);
    434     return Util.makeCssRgb(rgb);
     605  var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
     606
     607  // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids
     608  // creating many intermediate strings.
     609  Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
     610    rgbBuf[1] = r;
     611    rgbBuf[3] = g;
     612    rgbBuf[5] = b;
     613    return rgbBuf.join('');
    435614  };
    436615
     
    582761  };
    583762
    584   // TODO(mack): Rename appendToArray
    585   Util.concatenateToArray = function concatenateToArray(arr1, arr2) {
     763  Util.appendToArray = function Util_appendToArray(arr1, arr2) {
    586764    Array.prototype.push.apply(arr1, arr2);
    587765  };
    588766
    589   Util.prependToArray = function concatenateToArray(arr1, arr2) {
     767  Util.prependToArray = function Util_prependToArray(arr1, arr2) {
    590768    Array.prototype.unshift.apply(arr1, arr2);
    591769  };
     
    634812})();
    635813
     814/**
     815 * PDF page viewport created based on scale, rotation and offset.
     816 * @class
     817 * @alias PDFJS.PageViewport
     818 */
    636819var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
     820  /**
     821   * @constructor
     822   * @private
     823   * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates.
     824   * @param scale {number} scale of the viewport.
     825   * @param rotation {number} rotations of the viewport in degrees.
     826   * @param offsetX {number} offset X
     827   * @param offsetY {number} offset Y
     828   * @param dontFlip {boolean} if true, axis Y will not be flipped.
     829   */
    637830  function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
    638831    this.viewBox = viewBox;
     
    698891    this.fontScale = scale;
    699892  }
    700   PageViewport.prototype = {
     893  PageViewport.prototype = /** @lends PDFJS.PageViewport.prototype */ {
     894    /**
     895     * Clones viewport with additional properties.
     896     * @param args {Object} (optional) If specified, may contain the 'scale' or
     897     * 'rotation' properties to override the corresponding properties in
     898     * the cloned viewport.
     899     * @returns {PDFJS.PageViewport} Cloned viewport.
     900     */
    701901    clone: function PageViewPort_clone(args) {
    702902      args = args || {};
     
    706906                              this.offsetX, this.offsetY, args.dontFlip);
    707907    },
     908    /**
     909     * Converts PDF point to the viewport coordinates. For examples, useful for
     910     * converting PDF location into canvas pixel coordinates.
     911     * @param x {number} X coordinate.
     912     * @param y {number} Y coordinate.
     913     * @returns {Object} Object that contains 'x' and 'y' properties of the
     914     * point in the viewport coordinate space.
     915     * @see {@link convertToPdfPoint}
     916     * @see {@link convertToViewportRectangle}
     917     */
    708918    convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
    709919      return Util.applyTransform([x, y], this.transform);
    710920    },
     921    /**
     922     * Converts PDF rectangle to the viewport coordinates.
     923     * @param rect {Array} xMin, yMin, xMax and yMax coordinates.
     924     * @returns {Array} Contains corresponding coordinates of the rectangle
     925     * in the viewport coordinate space.
     926     * @see {@link convertToViewportPoint}
     927     */
    711928    convertToViewportRectangle:
    712929      function PageViewport_convertToViewportRectangle(rect) {
     
    715932      return [tl[0], tl[1], br[0], br[1]];
    716933    },
     934    /**
     935     * Converts viewport coordinates to the PDF location. For examples, useful
     936     * for converting canvas pixel location into PDF one.
     937     * @param x {number} X coordinate.
     938     * @param y {number} Y coordinate.
     939     * @returns {Object} Object that contains 'x' and 'y' properties of the
     940     * point in the PDF coordinate space.
     941     * @see {@link convertToViewportPoint}
     942     */
    717943    convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
    718944      return Util.applyInverseTransform([x, y], this.transform);
     
    735961
    736962function stringToPDFString(str) {
    737   var i, n = str.length, str2 = '';
     963  var i, n = str.length, strBuf = [];
    738964  if (str[0] === '\xFE' && str[1] === '\xFF') {
    739965    // UTF16BE BOM
    740     for (i = 2; i < n; i += 2)
    741       str2 += String.fromCharCode(
    742         (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1));
     966    for (i = 2; i < n; i += 2) {
     967      strBuf.push(String.fromCharCode(
     968        (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
     969    }
    743970  } else {
    744971    for (i = 0; i < n; ++i) {
    745972      var code = PDFStringTranslateTable[str.charCodeAt(i)];
    746       str2 += code ? String.fromCharCode(code) : str.charAt(i);
    747     }
    748   }
    749   return str2;
     973      strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
     974    }
     975  }
     976  return strBuf.join('');
    750977}
    751978
     
    762989
    763990function isBool(v) {
    764   return typeof v == 'boolean';
     991  return typeof v === 'boolean';
    765992}
    766993
    767994function isInt(v) {
    768   return typeof v == 'number' && ((v | 0) == v);
     995  return typeof v === 'number' && ((v | 0) === v);
    769996}
    770997
    771998function isNum(v) {
    772   return typeof v == 'number';
     999  return typeof v === 'number';
    7731000}
    7741001
    7751002function isString(v) {
    776   return typeof v == 'string';
    777 }
    778 
    779 function isNull(v) {
    780   return v === null;
     1003  return typeof v === 'string';
    7811004}
    7821005
     
    7861009
    7871010function isCmd(v, cmd) {
    788   return v instanceof Cmd && (!cmd || v.cmd == cmd);
     1011  return v instanceof Cmd && (cmd === undefined || v.cmd === cmd);
    7891012}
    7901013
     
    7971020  }
    7981021  var dictType = v.get('Type');
    799   return isName(dictType) && dictType.name == type;
     1022  return isName(dictType) && dictType.name === type;
    8001023}
    8011024
     
    8051028
    8061029function isStream(v) {
    807   return typeof v == 'object' && v !== null && v !== undefined &&
    808          ('getBytes' in v);
     1030  return typeof v === 'object' && v !== null && v.getBytes !== undefined;
    8091031}
    8101032
    8111033function isArrayBuffer(v) {
    812   return typeof v == 'object' && v !== null && v !== undefined &&
    813          ('byteLength' in v);
     1034  return typeof v === 'object' && v !== null && v.byteLength !== undefined;
    8141035}
    8151036
     
    8181039}
    8191040
    820 function isPDFFunction(v) {
    821   var fnDict;
    822   if (typeof v != 'object')
    823     return false;
    824   else if (isDict(v))
    825     fnDict = v;
    826   else if (isStream(v))
    827     fnDict = v.dict;
    828   else
    829     return false;
    830   return fnDict.has('FunctionType');
     1041/**
     1042 * Promise Capability object.
     1043 *
     1044 * @typedef {Object} PromiseCapability
     1045 * @property {Promise} promise - A promise object.
     1046 * @property {function} resolve - Fullfills the promise.
     1047 * @property {function} reject - Rejects the promise.
     1048 */
     1049
     1050/**
     1051 * Creates a promise capability object.
     1052 * @alias PDFJS.createPromiseCapability
     1053 *
     1054 * @return {PromiseCapability} A capability object contains:
     1055 * - a Promise, resolve and reject methods.
     1056 */
     1057function createPromiseCapability() {
     1058  var capability = {};
     1059  capability.promise = new Promise(function (resolve, reject) {
     1060    capability.resolve = resolve;
     1061    capability.reject = reject;
     1062  });
     1063  return capability;
    8311064}
    8321065
    833 /**
    834  * Legacy support for PDFJS Promise implementation.
    835  * TODO remove eventually
    836  */
    837 var LegacyPromise = PDFJS.LegacyPromise = (function LegacyPromiseClosure() {
    838   return function LegacyPromise() {
    839     var resolve, reject;
    840     var promise = new Promise(function (resolve_, reject_) {
    841       resolve = resolve_;
    842       reject = reject_;
    843     });
    844     promise.resolve = resolve;
    845     promise.reject = reject;
    846     return promise;
    847   };
    848 })();
     1066PDFJS.createPromiseCapability = createPromiseCapability;
    8491067
    8501068/**
    8511069 * Polyfill for Promises:
    852  * The following promise implementation tries to generally implment the
     1070 * The following promise implementation tries to generally implement the
    8531071 * Promise/A+ spec. Some notable differences from other promise libaries are:
    8541072 * - There currently isn't a seperate deferred and promise object.
     
    8851103    }
    8861104    if (typeof globalScope.Promise.resolve !== 'function') {
    887       globalScope.Promise.resolve = function (x) {
    888         return new globalScope.Promise(function (resolve) { resolve(x); });
     1105      globalScope.Promise.resolve = function (value) {
     1106        return new globalScope.Promise(function (resolve) { resolve(value); });
     1107      };
     1108    }
     1109    if (typeof globalScope.Promise.reject !== 'function') {
     1110      globalScope.Promise.reject = function (reason) {
     1111        return new globalScope.Promise(function (resolve, reject) {
     1112          reject(reason);
     1113        });
     1114      };
     1115    }
     1116    if (typeof globalScope.Promise.prototype.catch !== 'function') {
     1117      globalScope.Promise.prototype.catch = function (onReject) {
     1118        return globalScope.Promise.prototype.then(undefined, onReject);
    8891119      };
    8901120    }
     
    9071137
    9081138    scheduleHandlers: function scheduleHandlers(promise) {
    909       if (promise._status == STATUS_PENDING) {
     1139      if (promise._status === STATUS_PENDING) {
    9101140        return;
    9111141      }
     
    9331163        try {
    9341164          if (nextStatus === STATUS_RESOLVED) {
    935             if (typeof(handler.onResolve) == 'function') {
     1165            if (typeof handler.onResolve === 'function') {
    9361166              nextValue = handler.onResolve(nextValue);
    9371167            }
    938           } else if (typeof(handler.onReject) === 'function') {
     1168          } else if (typeof handler.onReject === 'function') {
    9391169              nextValue = handler.onReject(nextValue);
    9401170              nextStatus = STATUS_RESOLVED;
     
    10111241    this._status = STATUS_PENDING;
    10121242    this._handlers = [];
    1013     resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
     1243    try {
     1244      resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
     1245    } catch (e) {
     1246      this._reject(e);
     1247    }
    10141248  }
    10151249  /**
     
    10471281          results[i] = value;
    10481282          unresolved--;
    1049           if (unresolved === 0)
     1283          if (unresolved === 0) {
    10501284            resolveAll(results);
     1285          }
    10511286        };
    10521287      })(i);
     
    10621297  /**
    10631298   * Checks if the value is likely a promise (has a 'then' function).
    1064    * @return {boolean} true if x is thenable
     1299   * @return {boolean} true if value is thenable
    10651300   */
    10661301  Promise.isPromise = function Promise_isPromise(value) {
    10671302    return value && typeof value.then === 'function';
    10681303  };
     1304
    10691305  /**
    10701306   * Creates resolved promise
    1071    * @param x resolve value
     1307   * @param value resolve value
    10721308   * @returns {Promise}
    10731309   */
    1074   Promise.resolve = function Promise_resolve(x) {
    1075     return new Promise(function (resolve) { resolve(x); });
     1310  Promise.resolve = function Promise_resolve(value) {
     1311    return new Promise(function (resolve) { resolve(value); });
     1312  };
     1313
     1314  /**
     1315   * Creates rejected promise
     1316   * @param reason rejection value
     1317   * @returns {Promise}
     1318   */
     1319  Promise.reject = function Promise_reject(reason) {
     1320    return new Promise(function (resolve, reject) { reject(reason); });
    10761321  };
    10771322
     
    10881333      }
    10891334
    1090       if (status == STATUS_RESOLVED &&
     1335      if (status === STATUS_RESOLVED &&
    10911336          Promise.isPromise(value)) {
    10921337        value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
     
    11161361    then: function Promise_then(onResolve, onReject) {
    11171362      var nextPromise = new Promise(function (resolve, reject) {
    1118         this.resolve = reject;
     1363        this.resolve = resolve;
    11191364        this.reject = reject;
    11201365      });
     
    11271372      HandlerManager.scheduleHandlers(this);
    11281373      return nextPromise;
     1374    },
     1375
     1376    catch: function Promise_catch(onReject) {
     1377      return this.then(undefined, onReject);
    11291378    }
    11301379  };
     
    11351384var StatTimer = (function StatTimerClosure() {
    11361385  function rpad(str, pad, length) {
    1137     while (str.length < length)
     1386    while (str.length < length) {
    11381387      str += pad;
     1388    }
    11391389    return str;
    11401390  }
     
    11461396  StatTimer.prototype = {
    11471397    time: function StatTimer_time(name) {
    1148       if (!this.enabled)
     1398      if (!this.enabled) {
    11491399        return;
    1150       if (name in this.started)
     1400      }
     1401      if (name in this.started) {
    11511402        warn('Timer is already running for ' + name);
     1403      }
    11521404      this.started[name] = Date.now();
    11531405    },
    11541406    timeEnd: function StatTimer_timeEnd(name) {
    1155       if (!this.enabled)
     1407      if (!this.enabled) {
    11561408        return;
    1157       if (!(name in this.started))
     1409      }
     1410      if (!(name in this.started)) {
    11581411        warn('Timer has not been started for ' + name);
     1412      }
    11591413      this.times.push({
    11601414        'name': name,
     
    11661420    },
    11671421    toString: function StatTimer_toString() {
     1422      var i, ii;
    11681423      var times = this.times;
    11691424      var out = '';
    11701425      // Find the longest name for padding purposes.
    11711426      var longest = 0;
    1172       for (var i = 0, ii = times.length; i < ii; ++i) {
     1427      for (i = 0, ii = times.length; i < ii; ++i) {
    11731428        var name = times[i]['name'];
    1174         if (name.length > longest)
     1429        if (name.length > longest) {
    11751430          longest = name.length;
    1176       }
    1177       for (var i = 0, ii = times.length; i < ii; ++i) {
     1431        }
     1432      }
     1433      for (i = 0, ii = times.length; i < ii; ++i) {
    11781434        var span = times[i];
    11791435        var duration = span.end - span.start;
     
    11871443
    11881444PDFJS.createBlob = function createBlob(data, contentType) {
    1189   if (typeof Blob !== 'undefined')
     1445  if (typeof Blob !== 'undefined') {
    11901446    return new Blob([data], { type: contentType });
     1447  }
    11911448  // Blob builder is deprecated in FF14 and removed in FF18.
    11921449  var bb = new MozBlobBuilder();
     
    12261483  this.callbackIndex = 1;
    12271484  this.postMessageTransfers = true;
    1228   var callbacks = this.callbacks = {};
     1485  var callbacksCapabilities = this.callbacksCapabilities = {};
    12291486  var ah = this.actionHandler = {};
    12301487
     
    12431500    if (data.isReply) {
    12441501      var callbackId = data.callbackId;
    1245       if (data.callbackId in callbacks) {
    1246         var callback = callbacks[callbackId];
    1247         delete callbacks[callbackId];
    1248         callback(data.data);
     1502      if (data.callbackId in callbacksCapabilities) {
     1503        var callback = callbacksCapabilities[callbackId];
     1504        delete callbacksCapabilities[callbackId];
     1505        if ('error' in data) {
     1506          callback.reject(data.error);
     1507        } else {
     1508          callback.resolve(data.data);
     1509        }
    12491510      } else {
    12501511        error('Cannot resolve callback ' + callbackId);
     
    12531514      var action = ah[data.action];
    12541515      if (data.callbackId) {
    1255         var deferred = {};
    1256         var promise = new Promise(function (resolve, reject) {
    1257           deferred.resolve = resolve;
    1258           deferred.reject = reject;
    1259         });
    1260         deferred.promise = promise;
    1261         promise.then(function(resolvedData) {
     1516        Promise.resolve().then(function () {
     1517          return action[0].call(action[1], data.data);
     1518        }).then(function (result) {
    12621519          comObj.postMessage({
    12631520            isReply: true,
    12641521            callbackId: data.callbackId,
    1265             data: resolvedData
     1522            data: result
     1523          });
     1524        }, function (reason) {
     1525          comObj.postMessage({
     1526            isReply: true,
     1527            callbackId: data.callbackId,
     1528            error: reason
    12661529          });
    12671530        });
    1268         action[0].call(action[1], data.data, deferred);
    12691531      } else {
    12701532        action[0].call(action[1], data.data);
    12711533      }
    12721534    } else {
    1273       error('Unkown action from worker: ' + data.action);
     1535      error('Unknown action from worker: ' + data.action);
    12741536    }
    12751537  };
     
    12881550   * @param {String} actionName Action to call.
    12891551   * @param {JSON} data JSON data to send.
    1290    * @param {function} [callback] Optional callback that will handle a reply.
    12911552   * @param {Array} [transfers] Optional list of transfers/ArrayBuffers
    12921553   */
    1293   send: function messageHandlerSend(actionName, data, callback, transfers) {
     1554  send: function messageHandlerSend(actionName, data, transfers) {
    12941555    var message = {
    12951556      action: actionName,
    12961557      data: data
    12971558    };
    1298     if (callback) {
    1299       var callbackId = this.callbackIndex++;
    1300       this.callbacks[callbackId] = callback;
    1301       message.callbackId = callbackId;
    1302     }
     1559    this.postMessage(message, transfers);
     1560  },
     1561  /**
     1562   * Sends a message to the comObj to invoke the action with the supplied data.
     1563   * Expects that other side will callback with the response.
     1564   * @param {String} actionName Action to call.
     1565   * @param {JSON} data JSON data to send.
     1566   * @param {Array} [transfers] Optional list of transfers/ArrayBuffers.
     1567   * @returns {Promise} Promise to be resolved with response data.
     1568   */
     1569  sendWithPromise:
     1570    function messageHandlerSendWithPromise(actionName, data, transfers) {
     1571    var callbackId = this.callbackIndex++;
     1572    var message = {
     1573      action: actionName,
     1574      data: data,
     1575      callbackId: callbackId
     1576    };
     1577    var capability = createPromiseCapability();
     1578    this.callbacksCapabilities[callbackId] = capability;
     1579    try {
     1580      this.postMessage(message, transfers);
     1581    } catch (e) {
     1582      capability.reject(e);
     1583    }
     1584    return capability.promise;
     1585  },
     1586  /**
     1587   * Sends raw message to the comObj.
     1588   * @private
     1589   * @param message {Object} Raw message.
     1590   * @param transfers List of transfers/ArrayBuffers, or undefined.
     1591   */
     1592  postMessage: function (message, transfers) {
    13031593    if (transfers && this.postMessageTransfers) {
    13041594      this.comObj.postMessage(message, transfers);
     
    13141604    objs.resolve(id, img);
    13151605  });
     1606  img.onerror = (function loadJpegStream_onerrorClosure() {
     1607    objs.resolve(id, null);
     1608    warn('Error during JPEG image loading');
     1609  });
    13161610  img.src = imageUrl;
    13171611}
    1318 
    1319 
    1320 var ColorSpace = (function ColorSpaceClosure() {
    1321   // Constructor should define this.numComps, this.defaultColor, this.name
    1322   function ColorSpace() {
    1323     error('should not call ColorSpace constructor');
    1324   }
    1325 
    1326   ColorSpace.prototype = {
    1327     /**
    1328      * Converts the color value to the RGB color. The color components are
    1329      * located in the src array starting from the srcOffset. Returns the array
    1330      * of the rgb components, each value ranging from [0,255].
    1331      */
    1332     getRgb: function ColorSpace_getRgb(src, srcOffset) {
    1333       error('Should not call ColorSpace.getRgb');
    1334     },
    1335     /**
    1336      * Converts the color value to the RGB color, similar to the getRgb method.
    1337      * The result placed into the dest array starting from the destOffset.
    1338      */
    1339     getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) {
    1340       error('Should not call ColorSpace.getRgbItem');
    1341     },
    1342     /**
    1343      * Converts the specified number of the color values to the RGB colors.
    1344      * The colors are located in the src array starting from the srcOffset.
    1345      * The result is placed into the dest array starting from the destOffset.
    1346      * The src array items shall be in [0,2^bits) range, the dest array items
    1347      * will be in [0,255] range. alpha01 indicates how many alpha components
    1348      * there are in the dest array; it will be either 0 (RGB array) or 1 (RGBA
    1349      * array).
    1350      */
    1351     getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count,
    1352                                                    dest, destOffset, bits,
    1353                                                    alpha01) {
    1354       error('Should not call ColorSpace.getRgbBuffer');
    1355     },
    1356     /**
    1357      * Determines the number of bytes required to store the result of the
    1358      * conversion done by the getRgbBuffer method. As in getRgbBuffer,
    1359      * |alpha01| is either 0 (RGB output) or 1 (RGBA output).
    1360      */
    1361     getOutputLength: function ColorSpace_getOutputLength(inputLength,
    1362                                                          alpha01) {
    1363       error('Should not call ColorSpace.getOutputLength');
    1364     },
    1365     /**
    1366      * Returns true if source data will be equal the result/output data.
    1367      */
    1368     isPassthrough: function ColorSpace_isPassthrough(bits) {
    1369       return false;
    1370     },
    1371     /**
    1372      * Fills in the RGB colors in an RGBA buffer.
    1373      */
    1374     fillRgb: function ColorSpace_fillRgb(rgbaBuf, originalWidth,
    1375                                          originalHeight, width, height,
    1376                                          actualHeight, bpc, comps) {
    1377       var count = originalWidth * originalHeight;
    1378       var rgbBuf = null;
    1379       var numComponentColors = 1 << bpc;
    1380       var needsResizing = originalHeight != height || originalWidth != width;
    1381 
    1382       if (this.isPassthrough(bpc)) {
    1383         rgbBuf = comps;
    1384 
    1385       } else if (this.numComps === 1 && count > numComponentColors &&
    1386           this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
    1387         // Optimization: create a color map when there is just one component and
    1388         // we are converting more colors than the size of the color map. We
    1389         // don't build the map if the colorspace is gray or rgb since those
    1390         // methods are faster than building a map. This mainly offers big speed
    1391         // ups for indexed and alternate colorspaces.
    1392         //
    1393         // TODO it may be worth while to cache the color map. While running
    1394         // testing I never hit a cache so I will leave that out for now (perhaps
    1395         // we are reparsing colorspaces too much?).
    1396         var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) :
    1397                                    new Uint16Array(numComponentColors);
    1398         for (var i = 0; i < numComponentColors; i++) {
    1399           allColors[i] = i;
    1400         }
    1401         var colorMap = new Uint8Array(numComponentColors * 3);
    1402         this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc,
    1403                           /* alpha01 = */ 0);
    1404 
    1405         if (!needsResizing) {
    1406           // Fill in the RGB values directly into |rgbaBuf|.
    1407           var rgbaPos = 0;
    1408           for (var i = 0; i < count; ++i) {
    1409             var key = comps[i] * 3;
    1410             rgbaBuf[rgbaPos++] = colorMap[key];
    1411             rgbaBuf[rgbaPos++] = colorMap[key + 1];
    1412             rgbaBuf[rgbaPos++] = colorMap[key + 2];
    1413             rgbaPos++;
    1414           }
    1415         } else {
    1416           rgbBuf = new Uint8Array(count * 3);
    1417           var rgbPos = 0;
    1418           for (var i = 0; i < count; ++i) {
    1419             var key = comps[i] * 3;
    1420             rgbBuf[rgbPos++] = colorMap[key];
    1421             rgbBuf[rgbPos++] = colorMap[key + 1];
    1422             rgbBuf[rgbPos++] = colorMap[key + 2];
    1423           }
    1424         }
    1425       } else {
    1426         if (!needsResizing) {
    1427           // Fill in the RGB values directly into |rgbaBuf|.
    1428           this.getRgbBuffer(comps, 0, width * actualHeight, rgbaBuf, 0, bpc,
    1429                             /* alpha01 = */ 1);
    1430         } else {
    1431           rgbBuf = new Uint8Array(count * 3);
    1432           this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc,
    1433                             /* alpha01 = */ 0);
    1434         }
    1435       }
    1436 
    1437       if (rgbBuf) {
    1438         if (needsResizing) {
    1439           rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth,
    1440                                    originalHeight, width, height);
    1441         }
    1442         var rgbPos = 0;
    1443         var actualLength = width * actualHeight * 4;
    1444         for (var i = 0; i < actualLength; i += 4) {
    1445           rgbaBuf[i] = rgbBuf[rgbPos++];
    1446           rgbaBuf[i + 1] = rgbBuf[rgbPos++];
    1447           rgbaBuf[i + 2] = rgbBuf[rgbPos++];
    1448         }
    1449       }
    1450     },
    1451     /**
    1452      * True if the colorspace has components in the default range of [0, 1].
    1453      * This should be true for all colorspaces except for lab color spaces
    1454      * which are [0,100], [-128, 127], [-128, 127].
    1455      */
    1456     usesZeroToOneRange: true
    1457   };
    1458 
    1459   ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
    1460     var IR = ColorSpace.parseToIR(cs, xref, res);
    1461     if (IR instanceof AlternateCS)
    1462       return IR;
    1463 
    1464     return ColorSpace.fromIR(IR);
    1465   };
    1466 
    1467   ColorSpace.fromIR = function ColorSpace_fromIR(IR) {
    1468     var name = isArray(IR) ? IR[0] : IR;
    1469 
    1470     switch (name) {
    1471       case 'DeviceGrayCS':
    1472         return this.singletons.gray;
    1473       case 'DeviceRgbCS':
    1474         return this.singletons.rgb;
    1475       case 'DeviceCmykCS':
    1476         return this.singletons.cmyk;
    1477       case 'CalGrayCS':
    1478         var whitePoint = IR[1].WhitePoint;
    1479         var blackPoint = IR[1].BlackPoint;
    1480         var gamma = IR[1].Gamma;
    1481         return new CalGrayCS(whitePoint, blackPoint, gamma);
    1482       case 'PatternCS':
    1483         var basePatternCS = IR[1];
    1484         if (basePatternCS)
    1485           basePatternCS = ColorSpace.fromIR(basePatternCS);
    1486         return new PatternCS(basePatternCS);
    1487       case 'IndexedCS':
    1488         var baseIndexedCS = IR[1];
    1489         var hiVal = IR[2];
    1490         var lookup = IR[3];
    1491         return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
    1492       case 'AlternateCS':
    1493         var numComps = IR[1];
    1494         var alt = IR[2];
    1495         var tintFnIR = IR[3];
    1496 
    1497         return new AlternateCS(numComps, ColorSpace.fromIR(alt),
    1498                                 PDFFunction.fromIR(tintFnIR));
    1499       case 'LabCS':
    1500         var whitePoint = IR[1].WhitePoint;
    1501         var blackPoint = IR[1].BlackPoint;
    1502         var range = IR[1].Range;
    1503         return new LabCS(whitePoint, blackPoint, range);
    1504       default:
    1505         error('Unkown name ' + name);
    1506     }
    1507     return null;
    1508   };
    1509 
    1510   ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) {
    1511     if (isName(cs)) {
    1512       var colorSpaces = res.get('ColorSpace');
    1513       if (isDict(colorSpaces)) {
    1514         var refcs = colorSpaces.get(cs.name);
    1515         if (refcs)
    1516           cs = refcs;
    1517       }
    1518     }
    1519 
    1520     cs = xref.fetchIfRef(cs);
    1521     var mode;
    1522 
    1523     if (isName(cs)) {
    1524       mode = cs.name;
    1525       this.mode = mode;
    1526 
    1527       switch (mode) {
    1528         case 'DeviceGray':
    1529         case 'G':
    1530           return 'DeviceGrayCS';
    1531         case 'DeviceRGB':
    1532         case 'RGB':
    1533           return 'DeviceRgbCS';
    1534         case 'DeviceCMYK':
    1535         case 'CMYK':
    1536           return 'DeviceCmykCS';
    1537         case 'Pattern':
    1538           return ['PatternCS', null];
    1539         default:
    1540           error('unrecognized colorspace ' + mode);
    1541       }
    1542     } else if (isArray(cs)) {
    1543       mode = cs[0].name;
    1544       this.mode = mode;
    1545 
    1546       switch (mode) {
    1547         case 'DeviceGray':
    1548         case 'G':
    1549           return 'DeviceGrayCS';
    1550         case 'DeviceRGB':
    1551         case 'RGB':
    1552           return 'DeviceRgbCS';
    1553         case 'DeviceCMYK':
    1554         case 'CMYK':
    1555           return 'DeviceCmykCS';
    1556         case 'CalGray':
    1557           var params = cs[1].getAll();
    1558           return ['CalGrayCS', params];
    1559         case 'CalRGB':
    1560           return 'DeviceRgbCS';
    1561         case 'ICCBased':
    1562           var stream = xref.fetchIfRef(cs[1]);
    1563           var dict = stream.dict;
    1564           var numComps = dict.get('N');
    1565           if (numComps == 1)
    1566             return 'DeviceGrayCS';
    1567           if (numComps == 3)
    1568             return 'DeviceRgbCS';
    1569           if (numComps == 4)
    1570             return 'DeviceCmykCS';
    1571           break;
    1572         case 'Pattern':
    1573           var basePatternCS = cs[1];
    1574           if (basePatternCS)
    1575             basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
    1576           return ['PatternCS', basePatternCS];
    1577         case 'Indexed':
    1578         case 'I':
    1579           var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
    1580           var hiVal = cs[2] + 1;
    1581           var lookup = xref.fetchIfRef(cs[3]);
    1582           if (isStream(lookup)) {
    1583             lookup = lookup.getBytes();
    1584           }
    1585           return ['IndexedCS', baseIndexedCS, hiVal, lookup];
    1586         case 'Separation':
    1587         case 'DeviceN':
    1588           var name = cs[1];
    1589           var numComps = 1;
    1590           if (isName(name))
    1591             numComps = 1;
    1592           else if (isArray(name))
    1593             numComps = name.length;
    1594           var alt = ColorSpace.parseToIR(cs[2], xref, res);
    1595           var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
    1596           return ['AlternateCS', numComps, alt, tintFnIR];
    1597         case 'Lab':
    1598           var params = cs[1].getAll();
    1599           return ['LabCS', params];
    1600         default:
    1601           error('unimplemented color space object "' + mode + '"');
    1602       }
    1603     } else {
    1604       error('unrecognized color space object: "' + cs + '"');
    1605     }
    1606     return null;
    1607   };
    1608   /**
    1609    * Checks if a decode map matches the default decode map for a color space.
    1610    * This handles the general decode maps where there are two values per
    1611    * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color.
    1612    * This does not handle Lab, Indexed, or Pattern decode maps since they are
    1613    * slightly different.
    1614    * @param {Array} decode Decode map (usually from an image).
    1615    * @param {Number} n Number of components the color space has.
    1616    */
    1617   ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
    1618     if (!decode)
    1619       return true;
    1620 
    1621     if (n * 2 !== decode.length) {
    1622       warn('The decode map is not the correct length');
    1623       return true;
    1624     }
    1625     for (var i = 0, ii = decode.length; i < ii; i += 2) {
    1626       if (decode[i] !== 0 || decode[i + 1] != 1)
    1627         return false;
    1628     }
    1629     return true;
    1630   };
    1631 
    1632   ColorSpace.singletons = {
    1633     get gray() {
    1634       return shadow(this, 'gray', new DeviceGrayCS());
    1635     },
    1636     get rgb() {
    1637       return shadow(this, 'rgb', new DeviceRgbCS());
    1638     },
    1639     get cmyk() {
    1640       return shadow(this, 'cmyk', new DeviceCmykCS());
    1641     }
    1642   };
    1643 
    1644   return ColorSpace;
    1645 })();
    1646 
    1647 /**
    1648  * Alternate color space handles both Separation and DeviceN color spaces.  A
    1649  * Separation color space is actually just a DeviceN with one color component.
    1650  * Both color spaces use a tinting function to convert colors to a base color
    1651  * space.
    1652  */
    1653 var AlternateCS = (function AlternateCSClosure() {
    1654   function AlternateCS(numComps, base, tintFn) {
    1655     this.name = 'Alternate';
    1656     this.numComps = numComps;
    1657     this.defaultColor = new Float32Array(numComps);
    1658     for (var i = 0; i < numComps; ++i) {
    1659       this.defaultColor[i] = 1;
    1660     }
    1661     this.base = base;
    1662     this.tintFn = tintFn;
    1663   }
    1664 
    1665   AlternateCS.prototype = {
    1666     getRgb: function AlternateCS_getRgb(src, srcOffset) {
    1667       var rgb = new Uint8Array(3);
    1668       this.getRgbItem(src, srcOffset, rgb, 0);
    1669       return rgb;
    1670     },
    1671     getRgbItem: function AlternateCS_getRgbItem(src, srcOffset,
    1672                                                 dest, destOffset) {
    1673       var baseNumComps = this.base.numComps;
    1674       var input = 'subarray' in src ?
    1675         src.subarray(srcOffset, srcOffset + this.numComps) :
    1676         Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps);
    1677       var tinted = this.tintFn(input);
    1678       this.base.getRgbItem(tinted, 0, dest, destOffset);
    1679     },
    1680     getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count,
    1681                                                     dest, destOffset, bits,
    1682                                                     alpha01) {
    1683       var tintFn = this.tintFn;
    1684       var base = this.base;
    1685       var scale = 1 / ((1 << bits) - 1);
    1686       var baseNumComps = base.numComps;
    1687       var usesZeroToOneRange = base.usesZeroToOneRange;
    1688       var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) &&
    1689                           alpha01 === 0;
    1690       var pos = isPassthrough ? destOffset : 0;
    1691       var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count);
    1692       var numComps = this.numComps;
    1693 
    1694       var scaled = new Float32Array(numComps);
    1695       for (var i = 0; i < count; i++) {
    1696         for (var j = 0; j < numComps; j++) {
    1697           scaled[j] = src[srcOffset++] * scale;
    1698         }
    1699         var tinted = tintFn(scaled);
    1700         if (usesZeroToOneRange) {
    1701           for (var j = 0; j < baseNumComps; j++) {
    1702             baseBuf[pos++] = tinted[j] * 255;
    1703           }
    1704         } else {
    1705           base.getRgbItem(tinted, 0, baseBuf, pos);
    1706           pos += baseNumComps;
    1707         }
    1708       }
    1709       if (!isPassthrough) {
    1710         base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);
    1711       }
    1712     },
    1713     getOutputLength: function AlternateCS_getOutputLength(inputLength,
    1714                                                           alpha01) {
    1715       return this.base.getOutputLength(inputLength *
    1716                                        this.base.numComps / this.numComps,
    1717                                        alpha01);
    1718     },
    1719     isPassthrough: ColorSpace.prototype.isPassthrough,
    1720     fillRgb: ColorSpace.prototype.fillRgb,
    1721     isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
    1722       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    1723     },
    1724     usesZeroToOneRange: true
    1725   };
    1726 
    1727   return AlternateCS;
    1728 })();
    1729 
    1730 var PatternCS = (function PatternCSClosure() {
    1731   function PatternCS(baseCS) {
    1732     this.name = 'Pattern';
    1733     this.base = baseCS;
    1734   }
    1735   PatternCS.prototype = {};
    1736 
    1737   return PatternCS;
    1738 })();
    1739 
    1740 var IndexedCS = (function IndexedCSClosure() {
    1741   function IndexedCS(base, highVal, lookup) {
    1742     this.name = 'Indexed';
    1743     this.numComps = 1;
    1744     this.defaultColor = new Uint8Array([0]);
    1745     this.base = base;
    1746     this.highVal = highVal;
    1747 
    1748     var baseNumComps = base.numComps;
    1749     var length = baseNumComps * highVal;
    1750     var lookupArray;
    1751 
    1752     if (isStream(lookup)) {
    1753       lookupArray = new Uint8Array(length);
    1754       var bytes = lookup.getBytes(length);
    1755       lookupArray.set(bytes);
    1756     } else if (isString(lookup)) {
    1757       lookupArray = new Uint8Array(length);
    1758       for (var i = 0; i < length; ++i)
    1759         lookupArray[i] = lookup.charCodeAt(i);
    1760     } else if (lookup instanceof Uint8Array || lookup instanceof Array) {
    1761       lookupArray = lookup;
    1762     } else {
    1763       error('Unrecognized lookup table: ' + lookup);
    1764     }
    1765     this.lookup = lookupArray;
    1766   }
    1767 
    1768   IndexedCS.prototype = {
    1769     getRgb: function IndexedCS_getRgb(src, srcOffset) {
    1770       var numComps = this.base.numComps;
    1771       var start = src[srcOffset] * numComps;
    1772       return this.base.getRgb(this.lookup, start);
    1773     },
    1774     getRgbItem: function IndexedCS_getRgbItem(src, srcOffset,
    1775                                               dest, destOffset) {
    1776       var numComps = this.base.numComps;
    1777       var start = src[srcOffset] * numComps;
    1778       this.base.getRgbItem(this.lookup, start, dest, destOffset);
    1779     },
    1780     getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count,
    1781                                                   dest, destOffset, bits,
    1782                                                   alpha01) {
    1783       var base = this.base;
    1784       var numComps = base.numComps;
    1785       var outputDelta = base.getOutputLength(numComps, alpha01);
    1786       var lookup = this.lookup;
    1787 
    1788       for (var i = 0; i < count; ++i) {
    1789         var lookupPos = src[srcOffset++] * numComps;
    1790         base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01);
    1791         destOffset += outputDelta;
    1792       }
    1793     },
    1794     getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) {
    1795       return this.base.getOutputLength(inputLength * this.base.numComps,
    1796                                        alpha01);
    1797     },
    1798     isPassthrough: ColorSpace.prototype.isPassthrough,
    1799     fillRgb: ColorSpace.prototype.fillRgb,
    1800     isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
    1801       // indexed color maps shouldn't be changed
    1802       return true;
    1803     },
    1804     usesZeroToOneRange: true
    1805   };
    1806   return IndexedCS;
    1807 })();
    1808 
    1809 var DeviceGrayCS = (function DeviceGrayCSClosure() {
    1810   function DeviceGrayCS() {
    1811     this.name = 'DeviceGray';
    1812     this.numComps = 1;
    1813     this.defaultColor = new Float32Array([0]);
    1814   }
    1815 
    1816   DeviceGrayCS.prototype = {
    1817     getRgb: function DeviceGrayCS_getRgb(src, srcOffset) {
    1818       var rgb = new Uint8Array(3);
    1819       this.getRgbItem(src, srcOffset, rgb, 0);
    1820       return rgb;
    1821     },
    1822     getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset,
    1823                                                  dest, destOffset) {
    1824       var c = (src[srcOffset] * 255) | 0;
    1825       c = c < 0 ? 0 : c > 255 ? 255 : c;
    1826       dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
    1827     },
    1828     getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count,
    1829                                                      dest, destOffset, bits,
    1830                                                      alpha01) {
    1831       var scale = 255 / ((1 << bits) - 1);
    1832       var j = srcOffset, q = destOffset;
    1833       for (var i = 0; i < count; ++i) {
    1834         var c = (scale * src[j++]) | 0;
    1835         dest[q++] = c;
    1836         dest[q++] = c;
    1837         dest[q++] = c;
    1838         q += alpha01;
    1839       }
    1840     },
    1841     getOutputLength: function DeviceGrayCS_getOutputLength(inputLength,
    1842                                                            alpha01) {
    1843       return inputLength * (3 + alpha01);
    1844     },
    1845     isPassthrough: ColorSpace.prototype.isPassthrough,
    1846     fillRgb: ColorSpace.prototype.fillRgb,
    1847     isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
    1848       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    1849     },
    1850     usesZeroToOneRange: true
    1851   };
    1852   return DeviceGrayCS;
    1853 })();
    1854 
    1855 var DeviceRgbCS = (function DeviceRgbCSClosure() {
    1856   function DeviceRgbCS() {
    1857     this.name = 'DeviceRGB';
    1858     this.numComps = 3;
    1859     this.defaultColor = new Float32Array([0, 0, 0]);
    1860   }
    1861   DeviceRgbCS.prototype = {
    1862     getRgb: function DeviceRgbCS_getRgb(src, srcOffset) {
    1863       var rgb = new Uint8Array(3);
    1864       this.getRgbItem(src, srcOffset, rgb, 0);
    1865       return rgb;
    1866     },
    1867     getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset,
    1868                                                 dest, destOffset) {
    1869       var r = (src[srcOffset] * 255) | 0;
    1870       var g = (src[srcOffset + 1] * 255) | 0;
    1871       var b = (src[srcOffset + 2] * 255) | 0;
    1872       dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
    1873       dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
    1874       dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
    1875     },
    1876     getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count,
    1877                                                     dest, destOffset, bits,
    1878                                                     alpha01) {
    1879       if (bits === 8 && alpha01 === 0) {
    1880         dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset);
    1881         return;
    1882       }
    1883       var scale = 255 / ((1 << bits) - 1);
    1884       var j = srcOffset, q = destOffset;
    1885       for (var i = 0; i < count; ++i) {
    1886         dest[q++] = (scale * src[j++]) | 0;
    1887         dest[q++] = (scale * src[j++]) | 0;
    1888         dest[q++] = (scale * src[j++]) | 0;
    1889         q += alpha01;
    1890       }
    1891     },
    1892     getOutputLength: function DeviceRgbCS_getOutputLength(inputLength,
    1893                                                           alpha01) {
    1894       return (inputLength * (3 + alpha01) / 3) | 0;
    1895     },
    1896     isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
    1897       return bits == 8;
    1898     },
    1899     fillRgb: ColorSpace.prototype.fillRgb,
    1900     isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
    1901       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    1902     },
    1903     usesZeroToOneRange: true
    1904   };
    1905   return DeviceRgbCS;
    1906 })();
    1907 
    1908 var DeviceCmykCS = (function DeviceCmykCSClosure() {
    1909   // The coefficients below was found using numerical analysis: the method of
    1910   // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors,
    1911   // where color_value is the tabular value from the table of sampled RGB colors
    1912   // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding
    1913   // CMYK color conversion using the estimation below:
    1914   //   f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255
    1915   function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
    1916     var c = src[srcOffset + 0] * srcScale;
    1917     var m = src[srcOffset + 1] * srcScale;
    1918     var y = src[srcOffset + 2] * srcScale;
    1919     var k = src[srcOffset + 3] * srcScale;
    1920 
    1921     var r =
    1922       c * (-4.387332384609988 * c + 54.48615194189176 * m +
    1923            18.82290502165302 * y + 212.25662451639585 * k +
    1924            -285.2331026137004) +
    1925       m * (1.7149763477362134 * m - 5.6096736904047315 * y +
    1926            -17.873870861415444 * k - 5.497006427196366) +
    1927       y * (-2.5217340131683033 * y - 21.248923337353073 * k +
    1928            17.5119270841813) +
    1929       k * (-21.86122147463605 * k - 189.48180835922747) + 255;
    1930     var g =
    1931       c * (8.841041422036149 * c + 60.118027045597366 * m +
    1932            6.871425592049007 * y + 31.159100130055922 * k +
    1933            -79.2970844816548) +
    1934       m * (-15.310361306967817 * m + 17.575251261109482 * y +
    1935            131.35250912493976 * k - 190.9453302588951) +
    1936       y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
    1937       k * (-20.737325471181034 * k - 187.80453709719578) + 255;
    1938     var b =
    1939       c * (0.8842522430003296 * c + 8.078677503112928 * m +
    1940            30.89978309703729 * y - 0.23883238689178934 * k +
    1941            -14.183576799673286) +
    1942       m * (10.49593273432072 * m + 63.02378494754052 * y +
    1943            50.606957656360734 * k - 112.23884253719248) +
    1944       y * (0.03296041114873217 * y + 115.60384449646641 * k +
    1945            -193.58209356861505) +
    1946       k * (-22.33816807309886 * k - 180.12613974708367) + 255;
    1947 
    1948     dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
    1949     dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
    1950     dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
    1951   }
    1952 
    1953   function DeviceCmykCS() {
    1954     this.name = 'DeviceCMYK';
    1955     this.numComps = 4;
    1956     this.defaultColor = new Float32Array([0, 0, 0, 1]);
    1957   }
    1958   DeviceCmykCS.prototype = {
    1959     getRgb: function DeviceCmykCS_getRgb(src, srcOffset) {
    1960       var rgb = new Uint8Array(3);
    1961       convertToRgb(src, srcOffset, 1, rgb, 0);
    1962       return rgb;
    1963     },
    1964     getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset,
    1965                                                  dest, destOffset) {
    1966       convertToRgb(src, srcOffset, 1, dest, destOffset);
    1967     },
    1968     getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count,
    1969                                                      dest, destOffset, bits,
    1970                                                      alpha01) {
    1971       var scale = 1 / ((1 << bits) - 1);
    1972       for (var i = 0; i < count; i++) {
    1973         convertToRgb(src, srcOffset, scale, dest, destOffset);
    1974         srcOffset += 4;
    1975         destOffset += 3 + alpha01;
    1976       }
    1977     },
    1978     getOutputLength: function DeviceCmykCS_getOutputLength(inputLength,
    1979                                                            alpha01) {
    1980       return (inputLength / 4 * (3 + alpha01)) | 0;
    1981     },
    1982     isPassthrough: ColorSpace.prototype.isPassthrough,
    1983     fillRgb: ColorSpace.prototype.fillRgb,
    1984     isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
    1985       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    1986     },
    1987     usesZeroToOneRange: true
    1988   };
    1989 
    1990   return DeviceCmykCS;
    1991 })();
    1992 
    1993 //
    1994 // CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245
    1995 //
    1996 var CalGrayCS = (function CalGrayCSClosure() {
    1997   function CalGrayCS(whitePoint, blackPoint, gamma) {
    1998     this.name = 'CalGray';
    1999     this.numComps = 1;
    2000     this.defaultColor = new Float32Array([0]);
    2001 
    2002     if (!whitePoint) {
    2003       error('WhitePoint missing - required for color space CalGray');
    2004     }
    2005     blackPoint = blackPoint || [0, 0, 0];
    2006     gamma = gamma || 1;
    2007 
    2008     // Translate arguments to spec variables.
    2009     this.XW = whitePoint[0];
    2010     this.YW = whitePoint[1];
    2011     this.ZW = whitePoint[2];
    2012 
    2013     this.XB = blackPoint[0];
    2014     this.YB = blackPoint[1];
    2015     this.ZB = blackPoint[2];
    2016 
    2017     this.G = gamma;
    2018 
    2019     // Validate variables as per spec.
    2020     if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
    2021       error('Invalid WhitePoint components for ' + this.name +
    2022             ', no fallback available');
    2023     }
    2024 
    2025     if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
    2026       info('Invalid BlackPoint for ' + this.name + ', falling back to default');
    2027       this.XB = this.YB = this.ZB = 0;
    2028     }
    2029 
    2030     if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) {
    2031       warn(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB +
    2032            ', ZB: ' + this.ZB + ', only default values are supported.');
    2033     }
    2034 
    2035     if (this.G < 1) {
    2036       info('Invalid Gamma: ' + this.G + ' for ' + this.name +
    2037            ', falling back to default');
    2038       this.G = 1;
    2039     }
    2040   }
    2041 
    2042   function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
    2043     // A represents a gray component of a calibrated gray space.
    2044     // A <---> AG in the spec
    2045     var A = src[srcOffset] * scale;
    2046     var AG = Math.pow(A, cs.G);
    2047 
    2048     // Computes intermediate variables M, L, N as per spec.
    2049     // Except if other than default BlackPoint values are used.
    2050     var M = cs.XW * AG;
    2051     var L = cs.YW * AG;
    2052     var N = cs.ZW * AG;
    2053 
    2054     // Decode XYZ, as per spec.
    2055     var X = M;
    2056     var Y = L;
    2057     var Z = N;
    2058 
    2059     // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4.
    2060     // This yields values in range [0, 100].
    2061     var Lstar = Math.max(116 * Math.pow(Y, 1 / 3) - 16, 0);
    2062 
    2063     // Convert values to rgb range [0, 255].
    2064     dest[destOffset] = Lstar * 255 / 100;
    2065     dest[destOffset + 1] = Lstar * 255 / 100;
    2066     dest[destOffset + 2] = Lstar * 255 / 100;
    2067   }
    2068 
    2069   CalGrayCS.prototype = {
    2070     getRgb: function CalGrayCS_getRgb(src, srcOffset) {
    2071       var rgb = new Uint8Array(3);
    2072       this.getRgbItem(src, srcOffset, rgb, 0);
    2073       return rgb;
    2074     },
    2075     getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset,
    2076                                               dest, destOffset) {
    2077       convertToRgb(this, src, srcOffset, dest, destOffset, 1);
    2078     },
    2079     getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count,
    2080                                                   dest, destOffset, bits,
    2081                                                   alpha01) {
    2082       var scale = 1 / ((1 << bits) - 1);
    2083 
    2084       for (var i = 0; i < count; ++i) {
    2085         convertToRgb(this, src, srcOffset, dest, destOffset, scale);
    2086         srcOffset += 1;
    2087         destOffset += 3 + alpha01;
    2088       }
    2089     },
    2090     getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) {
    2091       return inputLength * (3 + alpha01);
    2092     },
    2093     isPassthrough: ColorSpace.prototype.isPassthrough,
    2094     fillRgb: ColorSpace.prototype.fillRgb,
    2095     isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) {
    2096       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
    2097     },
    2098     usesZeroToOneRange: true
    2099   };
    2100   return CalGrayCS;
    2101 })();
    2102 
    2103 //
    2104 // LabCS: Based on "PDF Reference, Sixth Ed", p.250
    2105 //
    2106 var LabCS = (function LabCSClosure() {
    2107   function LabCS(whitePoint, blackPoint, range) {
    2108     this.name = 'Lab';
    2109     this.numComps = 3;
    2110     this.defaultColor = new Float32Array([0, 0, 0]);
    2111 
    2112     if (!whitePoint)
    2113       error('WhitePoint missing - required for color space Lab');
    2114     blackPoint = blackPoint || [0, 0, 0];
    2115     range = range || [-100, 100, -100, 100];
    2116 
    2117     // Translate args to spec variables
    2118     this.XW = whitePoint[0];
    2119     this.YW = whitePoint[1];
    2120     this.ZW = whitePoint[2];
    2121     this.amin = range[0];
    2122     this.amax = range[1];
    2123     this.bmin = range[2];
    2124     this.bmax = range[3];
    2125 
    2126     // These are here just for completeness - the spec doesn't offer any
    2127     // formulas that use BlackPoint in Lab
    2128     this.XB = blackPoint[0];
    2129     this.YB = blackPoint[1];
    2130     this.ZB = blackPoint[2];
    2131 
    2132     // Validate vars as per spec
    2133     if (this.XW < 0 || this.ZW < 0 || this.YW !== 1)
    2134       error('Invalid WhitePoint components, no fallback available');
    2135 
    2136     if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
    2137       info('Invalid BlackPoint, falling back to default');
    2138       this.XB = this.YB = this.ZB = 0;
    2139     }
    2140 
    2141     if (this.amin > this.amax || this.bmin > this.bmax) {
    2142       info('Invalid Range, falling back to defaults');
    2143       this.amin = -100;
    2144       this.amax = 100;
    2145       this.bmin = -100;
    2146       this.bmax = 100;
    2147     }
    2148   }
    2149 
    2150   // Function g(x) from spec
    2151   function fn_g(x) {
    2152     if (x >= 6 / 29)
    2153       return x * x * x;
    2154     else
    2155       return (108 / 841) * (x - 4 / 29);
    2156   }
    2157 
    2158   function decode(value, high1, low2, high2) {
    2159     return low2 + (value) * (high2 - low2) / (high1);
    2160   }
    2161 
    2162   // If decoding is needed maxVal should be 2^bits per component - 1.
    2163   function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) {
    2164     // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax]
    2165     // not the usual [0, 1]. If a command like setFillColor is used the src
    2166     // values will already be within the correct range. However, if we are
    2167     // converting an image we have to map the values to the correct range given
    2168     // above.
    2169     // Ls,as,bs <---> L*,a*,b* in the spec
    2170     var Ls = src[srcOffset];
    2171     var as = src[srcOffset + 1];
    2172     var bs = src[srcOffset + 2];
    2173     if (maxVal !== false) {
    2174       Ls = decode(Ls, maxVal, 0, 100);
    2175       as = decode(as, maxVal, cs.amin, cs.amax);
    2176       bs = decode(bs, maxVal, cs.bmin, cs.bmax);
    2177     }
    2178 
    2179     // Adjust limits of 'as' and 'bs'
    2180     as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as;
    2181     bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs;
    2182 
    2183     // Computes intermediate variables X,Y,Z as per spec
    2184     var M = (Ls + 16) / 116;
    2185     var L = M + (as / 500);
    2186     var N = M - (bs / 200);
    2187 
    2188     var X = cs.XW * fn_g(L);
    2189     var Y = cs.YW * fn_g(M);
    2190     var Z = cs.ZW * fn_g(N);
    2191 
    2192     var r, g, b;
    2193     // Using different conversions for D50 and D65 white points,
    2194     // per http://www.color.org/srgb.pdf
    2195     if (cs.ZW < 1) {
    2196       // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249)
    2197       r = X * 3.1339 + Y * -1.6170 + Z * -0.4906;
    2198       g = X * -0.9785 + Y * 1.9160 + Z * 0.0333;
    2199       b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
    2200     } else {
    2201       // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888)
    2202       r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
    2203       g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
    2204       b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
    2205     }
    2206     // clamp color values to [0,1] range then convert to [0,255] range.
    2207     dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255;
    2208     dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255;
    2209     dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255;
    2210   }
    2211 
    2212   LabCS.prototype = {
    2213     getRgb: function LabCS_getRgb(src, srcOffset) {
    2214       var rgb = new Uint8Array(3);
    2215       convertToRgb(this, src, srcOffset, false, rgb, 0);
    2216       return rgb;
    2217     },
    2218     getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
    2219       convertToRgb(this, src, srcOffset, false, dest, destOffset);
    2220     },
    2221     getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count,
    2222                                               dest, destOffset, bits,
    2223                                               alpha01) {
    2224       var maxVal = (1 << bits) - 1;
    2225       for (var i = 0; i < count; i++) {
    2226         convertToRgb(this, src, srcOffset, maxVal, dest, destOffset);
    2227         srcOffset += 3;
    2228         destOffset += 3 + alpha01;
    2229       }
    2230     },
    2231     getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) {
    2232       return (inputLength * (3 + alpha01) / 3) | 0;
    2233     },
    2234     isPassthrough: ColorSpace.prototype.isPassthrough,
    2235     isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
    2236       // XXX: Decoding is handled with the lab conversion because of the strange
    2237       // ranges that are used.
    2238       return true;
    2239     },
    2240     usesZeroToOneRange: false
    2241   };
    2242   return LabCS;
    2243 })();
    2244 
    2245 
    2246 
    2247 var PDFFunction = (function PDFFunctionClosure() {
    2248   var CONSTRUCT_SAMPLED = 0;
    2249   var CONSTRUCT_INTERPOLATED = 2;
    2250   var CONSTRUCT_STICHED = 3;
    2251   var CONSTRUCT_POSTSCRIPT = 4;
    2252 
    2253   return {
    2254     getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps,
    2255                                                        str) {
    2256       var length = 1;
    2257       for (var i = 0, ii = size.length; i < ii; i++)
    2258         length *= size[i];
    2259       length *= outputSize;
    2260 
    2261       var array = [];
    2262       var codeSize = 0;
    2263       var codeBuf = 0;
    2264       // 32 is a valid bps so shifting won't work
    2265       var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1);
    2266 
    2267       var strBytes = str.getBytes((length * bps + 7) / 8);
    2268       var strIdx = 0;
    2269       for (var i = 0; i < length; i++) {
    2270         while (codeSize < bps) {
    2271           codeBuf <<= 8;
    2272           codeBuf |= strBytes[strIdx++];
    2273           codeSize += 8;
    2274         }
    2275         codeSize -= bps;
    2276         array.push((codeBuf >> codeSize) * sampleMul);
    2277         codeBuf &= (1 << codeSize) - 1;
    2278       }
    2279       return array;
    2280     },
    2281 
    2282     getIR: function PDFFunction_getIR(xref, fn) {
    2283       var dict = fn.dict;
    2284       if (!dict)
    2285         dict = fn;
    2286 
    2287       var types = [this.constructSampled,
    2288                    null,
    2289                    this.constructInterpolated,
    2290                    this.constructStiched,
    2291                    this.constructPostScript];
    2292 
    2293       var typeNum = dict.get('FunctionType');
    2294       var typeFn = types[typeNum];
    2295       if (!typeFn)
    2296         error('Unknown type of function');
    2297 
    2298       return typeFn.call(this, fn, dict, xref);
    2299     },
    2300 
    2301     fromIR: function PDFFunction_fromIR(IR) {
    2302       var type = IR[0];
    2303       switch (type) {
    2304         case CONSTRUCT_SAMPLED:
    2305           return this.constructSampledFromIR(IR);
    2306         case CONSTRUCT_INTERPOLATED:
    2307           return this.constructInterpolatedFromIR(IR);
    2308         case CONSTRUCT_STICHED:
    2309           return this.constructStichedFromIR(IR);
    2310         //case CONSTRUCT_POSTSCRIPT:
    2311         default:
    2312           return this.constructPostScriptFromIR(IR);
    2313       }
    2314     },
    2315 
    2316     parse: function PDFFunction_parse(xref, fn) {
    2317       var IR = this.getIR(xref, fn);
    2318       return this.fromIR(IR);
    2319     },
    2320 
    2321     constructSampled: function PDFFunction_constructSampled(str, dict) {
    2322       function toMultiArray(arr) {
    2323         var inputLength = arr.length;
    2324         var outputLength = arr.length / 2;
    2325         var out = [];
    2326         var index = 0;
    2327         for (var i = 0; i < inputLength; i += 2) {
    2328           out[index] = [arr[i], arr[i + 1]];
    2329           ++index;
    2330         }
    2331         return out;
    2332       }
    2333       var domain = dict.get('Domain');
    2334       var range = dict.get('Range');
    2335 
    2336       if (!domain || !range)
    2337         error('No domain or range');
    2338 
    2339       var inputSize = domain.length / 2;
    2340       var outputSize = range.length / 2;
    2341 
    2342       domain = toMultiArray(domain);
    2343       range = toMultiArray(range);
    2344 
    2345       var size = dict.get('Size');
    2346       var bps = dict.get('BitsPerSample');
    2347       var order = dict.get('Order') || 1;
    2348       if (order !== 1) {
    2349         // No description how cubic spline interpolation works in PDF32000:2008
    2350         // As in poppler, ignoring order, linear interpolation may work as good
    2351         info('No support for cubic spline interpolation: ' + order);
    2352       }
    2353 
    2354       var encode = dict.get('Encode');
    2355       if (!encode) {
    2356         encode = [];
    2357         for (var i = 0; i < inputSize; ++i) {
    2358           encode.push(0);
    2359           encode.push(size[i] - 1);
    2360         }
    2361       }
    2362       encode = toMultiArray(encode);
    2363 
    2364       var decode = dict.get('Decode');
    2365       if (!decode)
    2366         decode = range;
    2367       else
    2368         decode = toMultiArray(decode);
    2369 
    2370       var samples = this.getSampleArray(size, outputSize, bps, str);
    2371 
    2372       return [
    2373         CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size,
    2374         outputSize, Math.pow(2, bps) - 1, range
    2375       ];
    2376     },
    2377 
    2378     constructSampledFromIR: function PDFFunction_constructSampledFromIR(IR) {
    2379       // See chapter 3, page 109 of the PDF reference
    2380       function interpolate(x, xmin, xmax, ymin, ymax) {
    2381         return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin)));
    2382       }
    2383 
    2384       return function constructSampledFromIRResult(args) {
    2385         // See chapter 3, page 110 of the PDF reference.
    2386         var m = IR[1];
    2387         var domain = IR[2];
    2388         var encode = IR[3];
    2389         var decode = IR[4];
    2390         var samples = IR[5];
    2391         var size = IR[6];
    2392         var n = IR[7];
    2393         var mask = IR[8];
    2394         var range = IR[9];
    2395 
    2396         if (m != args.length)
    2397           error('Incorrect number of arguments: ' + m + ' != ' +
    2398                 args.length);
    2399 
    2400         var x = args;
    2401 
    2402         // Building the cube vertices: its part and sample index
    2403         // http://rjwagner49.com/Mathematics/Interpolation.pdf
    2404         var cubeVertices = 1 << m;
    2405         var cubeN = new Float64Array(cubeVertices);
    2406         var cubeVertex = new Uint32Array(cubeVertices);
    2407         for (var j = 0; j < cubeVertices; j++)
    2408           cubeN[j] = 1;
    2409 
    2410         var k = n, pos = 1;
    2411         // Map x_i to y_j for 0 <= i < m using the sampled function.
    2412         for (var i = 0; i < m; ++i) {
    2413           // x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
    2414           var domain_2i = domain[i][0];
    2415           var domain_2i_1 = domain[i][1];
    2416           var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1);
    2417 
    2418           // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1,
    2419           //                   Encode_2i, Encode_2i+1)
    2420           var e = interpolate(xi, domain_2i, domain_2i_1,
    2421                               encode[i][0], encode[i][1]);
    2422 
    2423           // e_i' = min(max(e_i, 0), Size_i - 1)
    2424           var size_i = size[i];
    2425           e = Math.min(Math.max(e, 0), size_i - 1);
    2426 
    2427           // Adjusting the cube: N and vertex sample index
    2428           var e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1;
    2429           var n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0);
    2430           var n1 = e - e0; // (e - e0) / (e1 - e0);
    2431           var offset0 = e0 * k;
    2432           var offset1 = offset0 + k; // e1 * k
    2433           for (var j = 0; j < cubeVertices; j++) {
    2434             if (j & pos) {
    2435               cubeN[j] *= n1;
    2436               cubeVertex[j] += offset1;
    2437             } else {
    2438               cubeN[j] *= n0;
    2439               cubeVertex[j] += offset0;
    2440             }
    2441           }
    2442 
    2443           k *= size_i;
    2444           pos <<= 1;
    2445         }
    2446 
    2447         var y = new Float64Array(n);
    2448         for (var j = 0; j < n; ++j) {
    2449           // Sum all cube vertices' samples portions
    2450           var rj = 0;
    2451           for (var i = 0; i < cubeVertices; i++)
    2452             rj += samples[cubeVertex[i] + j] * cubeN[i];
    2453 
    2454           // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1,
    2455           //                    Decode_2j, Decode_2j+1)
    2456           rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
    2457 
    2458           // y_j = min(max(r_j, range_2j), range_2j+1)
    2459           y[j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
    2460         }
    2461 
    2462         return y;
    2463       };
    2464     },
    2465 
    2466     constructInterpolated: function PDFFunction_constructInterpolated(str,
    2467                                                                       dict) {
    2468       var c0 = dict.get('C0') || [0];
    2469       var c1 = dict.get('C1') || [1];
    2470       var n = dict.get('N');
    2471 
    2472       if (!isArray(c0) || !isArray(c1))
    2473         error('Illegal dictionary for interpolated function');
    2474 
    2475       var length = c0.length;
    2476       var diff = [];
    2477       for (var i = 0; i < length; ++i)
    2478         diff.push(c1[i] - c0[i]);
    2479 
    2480       return [CONSTRUCT_INTERPOLATED, c0, diff, n];
    2481     },
    2482 
    2483     constructInterpolatedFromIR:
    2484       function PDFFunction_constructInterpolatedFromIR(IR) {
    2485       var c0 = IR[1];
    2486       var diff = IR[2];
    2487       var n = IR[3];
    2488 
    2489       var length = diff.length;
    2490 
    2491       return function constructInterpolatedFromIRResult(args) {
    2492         var x = n == 1 ? args[0] : Math.pow(args[0], n);
    2493 
    2494         var out = [];
    2495         for (var j = 0; j < length; ++j)
    2496           out.push(c0[j] + (x * diff[j]));
    2497 
    2498         return out;
    2499 
    2500       };
    2501     },
    2502 
    2503     constructStiched: function PDFFunction_constructStiched(fn, dict, xref) {
    2504       var domain = dict.get('Domain');
    2505 
    2506       if (!domain)
    2507         error('No domain');
    2508 
    2509       var inputSize = domain.length / 2;
    2510       if (inputSize != 1)
    2511         error('Bad domain for stiched function');
    2512 
    2513       var fnRefs = dict.get('Functions');
    2514       var fns = [];
    2515       for (var i = 0, ii = fnRefs.length; i < ii; ++i)
    2516         fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
    2517 
    2518       var bounds = dict.get('Bounds');
    2519       var encode = dict.get('Encode');
    2520 
    2521       return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
    2522     },
    2523 
    2524     constructStichedFromIR: function PDFFunction_constructStichedFromIR(IR) {
    2525       var domain = IR[1];
    2526       var bounds = IR[2];
    2527       var encode = IR[3];
    2528       var fnsIR = IR[4];
    2529       var fns = [];
    2530 
    2531       for (var i = 0, ii = fnsIR.length; i < ii; i++) {
    2532         fns.push(PDFFunction.fromIR(fnsIR[i]));
    2533       }
    2534 
    2535       return function constructStichedFromIRResult(args) {
    2536         var clip = function constructStichedFromIRClip(v, min, max) {
    2537           if (v > max)
    2538             v = max;
    2539           else if (v < min)
    2540             v = min;
    2541           return v;
    2542         };
    2543 
    2544         // clip to domain
    2545         var v = clip(args[0], domain[0], domain[1]);
    2546         // calulate which bound the value is in
    2547         for (var i = 0, ii = bounds.length; i < ii; ++i) {
    2548           if (v < bounds[i])
    2549             break;
    2550         }
    2551 
    2552         // encode value into domain of function
    2553         var dmin = domain[0];
    2554         if (i > 0)
    2555           dmin = bounds[i - 1];
    2556         var dmax = domain[1];
    2557         if (i < bounds.length)
    2558           dmax = bounds[i];
    2559 
    2560         var rmin = encode[2 * i];
    2561         var rmax = encode[2 * i + 1];
    2562 
    2563         var v2 = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
    2564 
    2565         // call the appropropriate function
    2566         return fns[i]([v2]);
    2567       };
    2568     },
    2569 
    2570     constructPostScript: function PDFFunction_constructPostScript(fn, dict,
    2571                                                                   xref) {
    2572       var domain = dict.get('Domain');
    2573       var range = dict.get('Range');
    2574 
    2575       if (!domain)
    2576         error('No domain.');
    2577 
    2578       if (!range)
    2579         error('No range.');
    2580 
    2581       var lexer = new PostScriptLexer(fn);
    2582       var parser = new PostScriptParser(lexer);
    2583       var code = parser.parse();
    2584 
    2585       return [CONSTRUCT_POSTSCRIPT, domain, range, code];
    2586     },
    2587 
    2588     constructPostScriptFromIR: function PDFFunction_constructPostScriptFromIR(
    2589                                           IR) {
    2590       var domain = IR[1];
    2591       var range = IR[2];
    2592       var code = IR[3];
    2593       var numOutputs = range.length / 2;
    2594       var evaluator = new PostScriptEvaluator(code);
    2595       // Cache the values for a big speed up, the cache size is limited though
    2596       // since the number of possible values can be huge from a PS function.
    2597       var cache = new FunctionCache();
    2598       return function constructPostScriptFromIRResult(args) {
    2599         var initialStack = [];
    2600         for (var i = 0, ii = (domain.length / 2); i < ii; ++i) {
    2601           initialStack.push(args[i]);
    2602         }
    2603 
    2604         var key = initialStack.join('_');
    2605         if (cache.has(key))
    2606           return cache.get(key);
    2607 
    2608         var stack = evaluator.execute(initialStack);
    2609         var transformed = [];
    2610         for (i = numOutputs - 1; i >= 0; --i) {
    2611           var out = stack.pop();
    2612           var rangeIndex = 2 * i;
    2613           if (out < range[rangeIndex])
    2614             out = range[rangeIndex];
    2615           else if (out > range[rangeIndex + 1])
    2616             out = range[rangeIndex + 1];
    2617           transformed[i] = out;
    2618         }
    2619         cache.set(key, transformed);
    2620         return transformed;
    2621       };
    2622     }
    2623   };
    2624 })();
    2625 
    2626 var FunctionCache = (function FunctionCacheClosure() {
    2627   // Of 10 PDF's with type4 functions the maxium number of distinct values seen
    2628   // was 256. This still may need some tweaking in the future though.
    2629   var MAX_CACHE_SIZE = 1024;
    2630   function FunctionCache() {
    2631     this.cache = {};
    2632     this.total = 0;
    2633   }
    2634   FunctionCache.prototype = {
    2635     has: function FunctionCache_has(key) {
    2636       return key in this.cache;
    2637     },
    2638     get: function FunctionCache_get(key) {
    2639       return this.cache[key];
    2640     },
    2641     set: function FunctionCache_set(key, value) {
    2642       if (this.total < MAX_CACHE_SIZE) {
    2643         this.cache[key] = value;
    2644         this.total++;
    2645       }
    2646     }
    2647   };
    2648   return FunctionCache;
    2649 })();
    2650 
    2651 var PostScriptStack = (function PostScriptStackClosure() {
    2652   var MAX_STACK_SIZE = 100;
    2653   function PostScriptStack(initialStack) {
    2654     this.stack = initialStack || [];
    2655   }
    2656 
    2657   PostScriptStack.prototype = {
    2658     push: function PostScriptStack_push(value) {
    2659       if (this.stack.length >= MAX_STACK_SIZE)
    2660         error('PostScript function stack overflow.');
    2661       this.stack.push(value);
    2662     },
    2663     pop: function PostScriptStack_pop() {
    2664       if (this.stack.length <= 0)
    2665         error('PostScript function stack underflow.');
    2666       return this.stack.pop();
    2667     },
    2668     copy: function PostScriptStack_copy(n) {
    2669       if (this.stack.length + n >= MAX_STACK_SIZE)
    2670         error('PostScript function stack overflow.');
    2671       var stack = this.stack;
    2672       for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++)
    2673         stack.push(stack[i]);
    2674     },
    2675     index: function PostScriptStack_index(n) {
    2676       this.push(this.stack[this.stack.length - n - 1]);
    2677     },
    2678     // rotate the last n stack elements p times
    2679     roll: function PostScriptStack_roll(n, p) {
    2680       var stack = this.stack;
    2681       var l = stack.length - n;
    2682       var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
    2683       for (i = l, j = r; i < j; i++, j--) {
    2684         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
    2685       }
    2686       for (i = l, j = c - 1; i < j; i++, j--) {
    2687         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
    2688       }
    2689       for (i = c, j = r; i < j; i++, j--) {
    2690         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
    2691       }
    2692     }
    2693   };
    2694   return PostScriptStack;
    2695 })();
    2696 var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
    2697   function PostScriptEvaluator(operators) {
    2698     this.operators = operators;
    2699   }
    2700   PostScriptEvaluator.prototype = {
    2701     execute: function PostScriptEvaluator_execute(initialStack) {
    2702       var stack = new PostScriptStack(initialStack);
    2703       var counter = 0;
    2704       var operators = this.operators;
    2705       var length = operators.length;
    2706       var operator, a, b;
    2707       while (counter < length) {
    2708         operator = operators[counter++];
    2709         if (typeof operator == 'number') {
    2710           // Operator is really an operand and should be pushed to the stack.
    2711           stack.push(operator);
    2712           continue;
    2713         }
    2714         switch (operator) {
    2715           // non standard ps operators
    2716           case 'jz': // jump if false
    2717             b = stack.pop();
    2718             a = stack.pop();
    2719             if (!a)
    2720               counter = b;
    2721             break;
    2722           case 'j': // jump
    2723             a = stack.pop();
    2724             counter = a;
    2725             break;
    2726 
    2727           // all ps operators in alphabetical order (excluding if/ifelse)
    2728           case 'abs':
    2729             a = stack.pop();
    2730             stack.push(Math.abs(a));
    2731             break;
    2732           case 'add':
    2733             b = stack.pop();
    2734             a = stack.pop();
    2735             stack.push(a + b);
    2736             break;
    2737           case 'and':
    2738             b = stack.pop();
    2739             a = stack.pop();
    2740             if (isBool(a) && isBool(b))
    2741               stack.push(a && b);
    2742             else
    2743               stack.push(a & b);
    2744             break;
    2745           case 'atan':
    2746             a = stack.pop();
    2747             stack.push(Math.atan(a));
    2748             break;
    2749           case 'bitshift':
    2750             b = stack.pop();
    2751             a = stack.pop();
    2752             if (a > 0)
    2753               stack.push(a << b);
    2754             else
    2755               stack.push(a >> b);
    2756             break;
    2757           case 'ceiling':
    2758             a = stack.pop();
    2759             stack.push(Math.ceil(a));
    2760             break;
    2761           case 'copy':
    2762             a = stack.pop();
    2763             stack.copy(a);
    2764             break;
    2765           case 'cos':
    2766             a = stack.pop();
    2767             stack.push(Math.cos(a));
    2768             break;
    2769           case 'cvi':
    2770             a = stack.pop() | 0;
    2771             stack.push(a);
    2772             break;
    2773           case 'cvr':
    2774             // noop
    2775             break;
    2776           case 'div':
    2777             b = stack.pop();
    2778             a = stack.pop();
    2779             stack.push(a / b);
    2780             break;
    2781           case 'dup':
    2782             stack.copy(1);
    2783             break;
    2784           case 'eq':
    2785             b = stack.pop();
    2786             a = stack.pop();
    2787             stack.push(a == b);
    2788             break;
    2789           case 'exch':
    2790             stack.roll(2, 1);
    2791             break;
    2792           case 'exp':
    2793             b = stack.pop();
    2794             a = stack.pop();
    2795             stack.push(Math.pow(a, b));
    2796             break;
    2797           case 'false':
    2798             stack.push(false);
    2799             break;
    2800           case 'floor':
    2801             a = stack.pop();
    2802             stack.push(Math.floor(a));
    2803             break;
    2804           case 'ge':
    2805             b = stack.pop();
    2806             a = stack.pop();
    2807             stack.push(a >= b);
    2808             break;
    2809           case 'gt':
    2810             b = stack.pop();
    2811             a = stack.pop();
    2812             stack.push(a > b);
    2813             break;
    2814           case 'idiv':
    2815             b = stack.pop();
    2816             a = stack.pop();
    2817             stack.push((a / b) | 0);
    2818             break;
    2819           case 'index':
    2820             a = stack.pop();
    2821             stack.index(a);
    2822             break;
    2823           case 'le':
    2824             b = stack.pop();
    2825             a = stack.pop();
    2826             stack.push(a <= b);
    2827             break;
    2828           case 'ln':
    2829             a = stack.pop();
    2830             stack.push(Math.log(a));
    2831             break;
    2832           case 'log':
    2833             a = stack.pop();
    2834             stack.push(Math.log(a) / Math.LN10);
    2835             break;
    2836           case 'lt':
    2837             b = stack.pop();
    2838             a = stack.pop();
    2839             stack.push(a < b);
    2840             break;
    2841           case 'mod':
    2842             b = stack.pop();
    2843             a = stack.pop();
    2844             stack.push(a % b);
    2845             break;
    2846           case 'mul':
    2847             b = stack.pop();
    2848             a = stack.pop();
    2849             stack.push(a * b);
    2850             break;
    2851           case 'ne':
    2852             b = stack.pop();
    2853             a = stack.pop();
    2854             stack.push(a != b);
    2855             break;
    2856           case 'neg':
    2857             a = stack.pop();
    2858             stack.push(-b);
    2859             break;
    2860           case 'not':
    2861             a = stack.pop();
    2862             if (isBool(a) && isBool(b))
    2863               stack.push(a && b);
    2864             else
    2865               stack.push(a & b);
    2866             break;
    2867           case 'or':
    2868             b = stack.pop();
    2869             a = stack.pop();
    2870             if (isBool(a) && isBool(b))
    2871               stack.push(a || b);
    2872             else
    2873               stack.push(a | b);
    2874             break;
    2875           case 'pop':
    2876             stack.pop();
    2877             break;
    2878           case 'roll':
    2879             b = stack.pop();
    2880             a = stack.pop();
    2881             stack.roll(a, b);
    2882             break;
    2883           case 'round':
    2884             a = stack.pop();
    2885             stack.push(Math.round(a));
    2886             break;
    2887           case 'sin':
    2888             a = stack.pop();
    2889             stack.push(Math.sin(a));
    2890             break;
    2891           case 'sqrt':
    2892             a = stack.pop();
    2893             stack.push(Math.sqrt(a));
    2894             break;
    2895           case 'sub':
    2896             b = stack.pop();
    2897             a = stack.pop();
    2898             stack.push(a - b);
    2899             break;
    2900           case 'true':
    2901             stack.push(true);
    2902             break;
    2903           case 'truncate':
    2904             a = stack.pop();
    2905             a = a < 0 ? Math.ceil(a) : Math.floor(a);
    2906             stack.push(a);
    2907             break;
    2908           case 'xor':
    2909             b = stack.pop();
    2910             a = stack.pop();
    2911             if (isBool(a) && isBool(b))
    2912               stack.push(a != b);
    2913             else
    2914               stack.push(a ^ b);
    2915             break;
    2916           default:
    2917             error('Unknown operator ' + operator);
    2918             break;
    2919         }
    2920       }
    2921       return stack.stack;
    2922     }
    2923   };
    2924   return PostScriptEvaluator;
    2925 })();
    2926 
    2927 
    2928 var Annotation = (function AnnotationClosure() {
    2929   // 12.5.5: Algorithm: Appearance streams
    2930   function getTransformMatrix(rect, bbox, matrix) {
    2931     var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);
    2932     var minX = bounds[0];
    2933     var minY = bounds[1];
    2934     var maxX = bounds[2];
    2935     var maxY = bounds[3];
    2936 
    2937     if (minX === maxX || minY === maxY) {
    2938       // From real-life file, bbox was [0, 0, 0, 0]. In this case,
    2939       // just apply the transform for rect
    2940       return [1, 0, 0, 1, rect[0], rect[1]];
    2941     }
    2942 
    2943     var xRatio = (rect[2] - rect[0]) / (maxX - minX);
    2944     var yRatio = (rect[3] - rect[1]) / (maxY - minY);
    2945     return [
    2946       xRatio,
    2947       0,
    2948       0,
    2949       yRatio,
    2950       rect[0] - minX * xRatio,
    2951       rect[1] - minY * yRatio
    2952     ];
    2953   }
    2954 
    2955   function getDefaultAppearance(dict) {
    2956     var appearanceState = dict.get('AP');
    2957     if (!isDict(appearanceState)) {
    2958       return;
    2959     }
    2960 
    2961     var appearance;
    2962     var appearances = appearanceState.get('N');
    2963     if (isDict(appearances)) {
    2964       var as = dict.get('AS');
    2965       if (as && appearances.has(as.name)) {
    2966         appearance = appearances.get(as.name);
    2967       }
    2968     } else {
    2969       appearance = appearances;
    2970     }
    2971     return appearance;
    2972   }
    2973 
    2974   function Annotation(params) {
    2975     if (params.data) {
    2976       this.data = params.data;
    2977       return;
    2978     }
    2979 
    2980     var dict = params.dict;
    2981     var data = this.data = {};
    2982 
    2983     data.subtype = dict.get('Subtype').name;
    2984     var rect = dict.get('Rect');
    2985     data.rect = Util.normalizeRect(rect);
    2986     data.annotationFlags = dict.get('F');
    2987 
    2988     var color = dict.get('C');
    2989     if (isArray(color) && color.length === 3) {
    2990       // TODO(mack): currently only supporting rgb; need support different
    2991       // colorspaces
    2992       data.color = color;
    2993     } else {
    2994       data.color = [0, 0, 0];
    2995     }
    2996 
    2997     // Some types of annotations have border style dict which has more
    2998     // info than the border array
    2999     if (dict.has('BS')) {
    3000       var borderStyle = dict.get('BS');
    3001       data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1;
    3002     } else {
    3003       var borderArray = dict.get('Border') || [0, 0, 1];
    3004       data.borderWidth = borderArray[2] || 0;
    3005 
    3006       // TODO: implement proper support for annotations with line dash patterns.
    3007       var dashArray = borderArray[3];
    3008       if (data.borderWidth > 0 && dashArray && isArray(dashArray)) {
    3009         var dashArrayLength = dashArray.length;
    3010         if (dashArrayLength > 0) {
    3011           // According to the PDF specification: the elements in a dashArray
    3012           // shall be numbers that are nonnegative and not all equal to zero.
    3013           var isInvalid = false;
    3014           var numPositive = 0;
    3015           for (var i = 0; i < dashArrayLength; i++) {
    3016             if (!(+dashArray[i] >= 0)) {
    3017               isInvalid = true;
    3018               break;
    3019             } else if (dashArray[i] > 0) {
    3020               numPositive++;
    3021             }
    3022           }
    3023           if (isInvalid || numPositive === 0) {
    3024             data.borderWidth = 0;
    3025           }
    3026         }
    3027       }
    3028     }
    3029 
    3030     this.appearance = getDefaultAppearance(dict);
    3031     data.hasAppearance = !!this.appearance;
    3032   }
    3033 
    3034   Annotation.prototype = {
    3035 
    3036     getData: function Annotation_getData() {
    3037       return this.data;
    3038     },
    3039 
    3040     hasHtml: function Annotation_hasHtml() {
    3041       return false;
    3042     },
    3043 
    3044     getHtmlElement: function Annotation_getHtmlElement(commonObjs) {
    3045       throw new NotImplementedException(
    3046         'getHtmlElement() should be implemented in subclass');
    3047     },
    3048 
    3049     // TODO(mack): Remove this, it's not really that helpful.
    3050     getEmptyContainer: function Annotation_getEmptyContainer(tagName, rect) {
    3051       assert(!isWorker,
    3052         'getEmptyContainer() should be called from main thread');
    3053 
    3054       rect = rect || this.data.rect;
    3055       var element = document.createElement(tagName);
    3056       element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
    3057       element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
    3058       return element;
    3059     },
    3060 
    3061     isViewable: function Annotation_isViewable() {
    3062       var data = this.data;
    3063       return !!(
    3064         data &&
    3065         (!data.annotationFlags ||
    3066          !(data.annotationFlags & 0x22)) && // Hidden or NoView
    3067         data.rect                            // rectangle is nessessary
    3068       );
    3069     },
    3070 
    3071     loadResources: function(keys) {
    3072       var promise = new LegacyPromise();
    3073       this.appearance.dict.getAsync('Resources').then(function(resources) {
    3074         if (!resources) {
    3075           promise.resolve();
    3076           return;
    3077         }
    3078         var objectLoader = new ObjectLoader(resources.map,
    3079                                             keys,
    3080                                             resources.xref);
    3081         objectLoader.load().then(function() {
    3082           promise.resolve(resources);
    3083         });
    3084       }.bind(this));
    3085 
    3086       return promise;
    3087     },
    3088 
    3089     getOperatorList: function Annotation_getToOperatorList(evaluator) {
    3090 
    3091       var promise = new LegacyPromise();
    3092 
    3093       if (!this.appearance) {
    3094         promise.resolve(new OperatorList());
    3095         return promise;
    3096       }
    3097 
    3098       var data = this.data;
    3099 
    3100       var appearanceDict = this.appearance.dict;
    3101       var resourcesPromise = this.loadResources([
    3102         'ExtGState',
    3103         'ColorSpace',
    3104         'Pattern',
    3105         'Shading',
    3106         'XObject',
    3107         'Font'
    3108         // ProcSet
    3109         // Properties
    3110       ]);
    3111       var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
    3112       var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
    3113       var transform = getTransformMatrix(data.rect, bbox, matrix);
    3114 
    3115       var border = data.border;
    3116 
    3117       resourcesPromise.then(function(resources) {
    3118         var opList = new OperatorList();
    3119         opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
    3120         evaluator.getOperatorList(this.appearance, resources, opList);
    3121         opList.addOp(OPS.endAnnotation, []);
    3122         promise.resolve(opList);
    3123       }.bind(this));
    3124 
    3125       return promise;
    3126     }
    3127   };
    3128 
    3129   Annotation.getConstructor =
    3130       function Annotation_getConstructor(subtype, fieldType) {
    3131 
    3132     if (!subtype) {
    3133       return;
    3134     }
    3135 
    3136     // TODO(mack): Implement FreeText annotations
    3137     if (subtype === 'Link') {
    3138       return LinkAnnotation;
    3139     } else if (subtype === 'Text') {
    3140       return TextAnnotation;
    3141     } else if (subtype === 'Widget') {
    3142       if (!fieldType) {
    3143         return;
    3144       }
    3145 
    3146       if (fieldType === 'Tx') {
    3147         return TextWidgetAnnotation;
    3148       } else {
    3149         return WidgetAnnotation;
    3150       }
    3151     } else {
    3152       return Annotation;
    3153     }
    3154   };
    3155 
    3156   // TODO(mack): Support loading annotation from data
    3157   Annotation.fromData = function Annotation_fromData(data) {
    3158     var subtype = data.subtype;
    3159     var fieldType = data.fieldType;
    3160     var Constructor = Annotation.getConstructor(subtype, fieldType);
    3161     if (Constructor) {
    3162       return new Constructor({ data: data });
    3163     }
    3164   };
    3165 
    3166   Annotation.fromRef = function Annotation_fromRef(xref, ref) {
    3167 
    3168     var dict = xref.fetchIfRef(ref);
    3169     if (!isDict(dict)) {
    3170       return;
    3171     }
    3172 
    3173     var subtype = dict.get('Subtype');
    3174     subtype = isName(subtype) ? subtype.name : '';
    3175     if (!subtype) {
    3176       return;
    3177     }
    3178 
    3179     var fieldType = Util.getInheritableProperty(dict, 'FT');
    3180     fieldType = isName(fieldType) ? fieldType.name : '';
    3181 
    3182     var Constructor = Annotation.getConstructor(subtype, fieldType);
    3183     if (!Constructor) {
    3184       return;
    3185     }
    3186 
    3187     var params = {
    3188       dict: dict,
    3189       ref: ref,
    3190     };
    3191 
    3192     var annotation = new Constructor(params);
    3193 
    3194     if (annotation.isViewable()) {
    3195       return annotation;
    3196     } else {
    3197       warn('unimplemented annotation type: ' + subtype);
    3198     }
    3199   };
    3200 
    3201   Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
    3202       annotations, opList, pdfManager, partialEvaluator) {
    3203 
    3204     function reject(e) {
    3205       annotationsReadyPromise.reject(e);
    3206     }
    3207 
    3208     var annotationsReadyPromise = new LegacyPromise();
    3209 
    3210     var annotationPromises = [];
    3211     for (var i = 0, n = annotations.length; i < n; ++i) {
    3212       annotationPromises.push(annotations[i].getOperatorList(partialEvaluator));
    3213     }
    3214     Promise.all(annotationPromises).then(function(datas) {
    3215       opList.addOp(OPS.beginAnnotations, []);
    3216       for (var i = 0, n = datas.length; i < n; ++i) {
    3217         var annotOpList = datas[i];
    3218         opList.addOpList(annotOpList);
    3219       }
    3220       opList.addOp(OPS.endAnnotations, []);
    3221       annotationsReadyPromise.resolve();
    3222     }, reject);
    3223 
    3224     return annotationsReadyPromise;
    3225   };
    3226 
    3227   return Annotation;
    3228 })();
    3229 PDFJS.Annotation = Annotation;
    3230 
    3231 
    3232 var WidgetAnnotation = (function WidgetAnnotationClosure() {
    3233 
    3234   function WidgetAnnotation(params) {
    3235     Annotation.call(this, params);
    3236 
    3237     if (params.data) {
    3238       return;
    3239     }
    3240 
    3241     var dict = params.dict;
    3242     var data = this.data;
    3243 
    3244     data.fieldValue = stringToPDFString(
    3245       Util.getInheritableProperty(dict, 'V') || '');
    3246     data.alternativeText = stringToPDFString(dict.get('TU') || '');
    3247     data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
    3248     var fieldType = Util.getInheritableProperty(dict, 'FT');
    3249     data.fieldType = isName(fieldType) ? fieldType.name : '';
    3250     data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
    3251     this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict();
    3252 
    3253     // Building the full field name by collecting the field and
    3254     // its ancestors 'T' data and joining them using '.'.
    3255     var fieldName = [];
    3256     var namedItem = dict;
    3257     var ref = params.ref;
    3258     while (namedItem) {
    3259       var parent = namedItem.get('Parent');
    3260       var parentRef = namedItem.getRaw('Parent');
    3261       var name = namedItem.get('T');
    3262       if (name) {
    3263         fieldName.unshift(stringToPDFString(name));
    3264       } else {
    3265         // The field name is absent, that means more than one field
    3266         // with the same name may exist. Replacing the empty name
    3267         // with the '`' plus index in the parent's 'Kids' array.
    3268         // This is not in the PDF spec but necessary to id the
    3269         // the input controls.
    3270         var kids = parent.get('Kids');
    3271         var j, jj;
    3272         for (j = 0, jj = kids.length; j < jj; j++) {
    3273           var kidRef = kids[j];
    3274           if (kidRef.num == ref.num && kidRef.gen == ref.gen)
    3275             break;
    3276         }
    3277         fieldName.unshift('`' + j);
    3278       }
    3279       namedItem = parent;
    3280       ref = parentRef;
    3281     }
    3282     data.fullName = fieldName.join('.');
    3283   }
    3284 
    3285   var parent = Annotation.prototype;
    3286   Util.inherit(WidgetAnnotation, Annotation, {
    3287     isViewable: function WidgetAnnotation_isViewable() {
    3288       if (this.data.fieldType === 'Sig') {
    3289         warn('unimplemented annotation type: Widget signature');
    3290         return false;
    3291       }
    3292 
    3293       return parent.isViewable.call(this);
    3294     }
    3295   });
    3296 
    3297   return WidgetAnnotation;
    3298 })();
    3299 
    3300 var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
    3301   function TextWidgetAnnotation(params) {
    3302     WidgetAnnotation.call(this, params);
    3303 
    3304     if (params.data) {
    3305       return;
    3306     }
    3307 
    3308     this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');
    3309   }
    3310 
    3311   // TODO(mack): This dupes some of the logic in CanvasGraphics.setFont()
    3312   function setTextStyles(element, item, fontObj) {
    3313 
    3314     var style = element.style;
    3315     style.fontSize = item.fontSize + 'px';
    3316     style.direction = item.fontDirection < 0 ? 'rtl': 'ltr';
    3317 
    3318     if (!fontObj) {
    3319       return;
    3320     }
    3321 
    3322     style.fontWeight = fontObj.black ?
    3323                             (fontObj.bold ? 'bolder' : 'bold') :
    3324                             (fontObj.bold ? 'bold' : 'normal');
    3325     style.fontStyle = fontObj.italic ? 'italic' : 'normal';
    3326 
    3327     var fontName = fontObj.loadedName;
    3328     var fontFamily = fontName ? '"' + fontName + '", ' : '';
    3329     // Use a reasonable default font if the font doesn't specify a fallback
    3330     var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif';
    3331     style.fontFamily = fontFamily + fallbackName;
    3332   }
    3333 
    3334 
    3335   var parent = WidgetAnnotation.prototype;
    3336   Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
    3337     hasHtml: function TextWidgetAnnotation_hasHtml() {
    3338       return !this.data.hasAppearance && !!this.data.fieldValue;
    3339     },
    3340 
    3341     getHtmlElement: function TextWidgetAnnotation_getHtmlElement(commonObjs) {
    3342       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
    3343 
    3344       var item = this.data;
    3345 
    3346       var element = this.getEmptyContainer('div');
    3347       element.style.display = 'table';
    3348 
    3349       var content = document.createElement('div');
    3350       content.textContent = item.fieldValue;
    3351       var textAlignment = item.textAlignment;
    3352       content.style.textAlign = ['left', 'center', 'right'][textAlignment];
    3353       content.style.verticalAlign = 'middle';
    3354       content.style.display = 'table-cell';
    3355 
    3356       var fontObj = item.fontRefName ?
    3357                     commonObjs.getData(item.fontRefName) : null;
    3358       var cssRules = setTextStyles(content, item, fontObj);
    3359 
    3360       element.appendChild(content);
    3361 
    3362       return element;
    3363     },
    3364 
    3365     getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
    3366       if (this.appearance) {
    3367         return Annotation.prototype.getOperatorList.call(this, evaluator);
    3368       }
    3369 
    3370       var promise = new LegacyPromise();
    3371       var opList = new OperatorList();
    3372       var data = this.data;
    3373 
    3374       // Even if there is an appearance stream, ignore it. This is the
    3375       // behaviour used by Adobe Reader.
    3376 
    3377       var defaultAppearance = data.defaultAppearance;
    3378       if (!defaultAppearance) {
    3379         promise.resolve(opList);
    3380         return promise;
    3381       }
    3382 
    3383       // Include any font resources found in the default appearance
    3384 
    3385       var stream = new Stream(stringToBytes(defaultAppearance));
    3386       evaluator.getOperatorList(stream, this.fieldResources, opList);
    3387       var appearanceFnArray = opList.fnArray;
    3388       var appearanceArgsArray = opList.argsArray;
    3389       var fnArray = [];
    3390       var argsArray = [];
    3391 
    3392       // TODO(mack): Add support for stroke color
    3393       data.rgb = [0, 0, 0];
    3394       // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
    3395       for (var i = 0, n = fnArray.length; i < n; ++i) {
    3396         var fnId = appearanceFnArray[i];
    3397         var args = appearanceArgsArray[i];
    3398 
    3399         if (fnId === OPS.setFont) {
    3400           data.fontRefName = args[0];
    3401           var size = args[1];
    3402           if (size < 0) {
    3403             data.fontDirection = -1;
    3404             data.fontSize = -size;
    3405           } else {
    3406             data.fontDirection = 1;
    3407             data.fontSize = size;
    3408           }
    3409         } else if (fnId === OPS.setFillRGBColor) {
    3410           data.rgb = args;
    3411         } else if (fnId === OPS.setFillGray) {
    3412           var rgbValue = args[0] * 255;
    3413           data.rgb = [rgbValue, rgbValue, rgbValue];
    3414         }
    3415       }
    3416       promise.resolve(opList);
    3417       return promise;
    3418     }
    3419   });
    3420 
    3421   return TextWidgetAnnotation;
    3422 })();
    3423 
    3424 var TextAnnotation = (function TextAnnotationClosure() {
    3425   function TextAnnotation(params) {
    3426     Annotation.call(this, params);
    3427 
    3428     if (params.data) {
    3429       return;
    3430     }
    3431 
    3432     var dict = params.dict;
    3433     var data = this.data;
    3434 
    3435     var content = dict.get('Contents');
    3436     var title = dict.get('T');
    3437     data.content = stringToPDFString(content || '');
    3438     data.title = stringToPDFString(title || '');
    3439     data.name = !dict.has('Name') ? 'Note' : dict.get('Name').name;
    3440   }
    3441 
    3442   var ANNOT_MIN_SIZE = 10;
    3443 
    3444   Util.inherit(TextAnnotation, Annotation, {
    3445 
    3446     getOperatorList: function TextAnnotation_getOperatorList(evaluator) {
    3447       var promise = new LegacyPromise();
    3448       promise.resolve(new OperatorList());
    3449       return promise;
    3450     },
    3451 
    3452     hasHtml: function TextAnnotation_hasHtml() {
    3453       return true;
    3454     },
    3455 
    3456     getHtmlElement: function TextAnnotation_getHtmlElement(commonObjs) {
    3457       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
    3458 
    3459       var item = this.data;
    3460       var rect = item.rect;
    3461 
    3462       // sanity check because of OOo-generated PDFs
    3463       if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) {
    3464         rect[3] = rect[1] + ANNOT_MIN_SIZE;
    3465       }
    3466       if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) {
    3467         rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
    3468       }
    3469 
    3470       var container = this.getEmptyContainer('section', rect);
    3471       container.className = 'annotText';
    3472 
    3473       var image = document.createElement('img');
    3474       image.style.height = container.style.height;
    3475       var iconName = item.name;
    3476       image.src = PDFJS.imageResourcesPath + 'annotation-' +
    3477         iconName.toLowerCase() + '.svg';
    3478       image.alt = '[{{type}} Annotation]';
    3479       image.dataset.l10nId = 'text_annotation_type';
    3480       image.dataset.l10nArgs = JSON.stringify({type: iconName});
    3481       var content = document.createElement('div');
    3482       content.setAttribute('hidden', true);
    3483       var title = document.createElement('h1');
    3484       var text = document.createElement('p');
    3485       content.style.left = Math.floor(rect[2] - rect[0]) + 'px';
    3486       content.style.top = '0px';
    3487       title.textContent = item.title;
    3488 
    3489       if (!item.content && !item.title) {
    3490         content.setAttribute('hidden', true);
    3491       } else {
    3492         var e = document.createElement('span');
    3493         var lines = item.content.split(/(?:\r\n?|\n)/);
    3494         for (var i = 0, ii = lines.length; i < ii; ++i) {
    3495           var line = lines[i];
    3496           e.appendChild(document.createTextNode(line));
    3497           if (i < (ii - 1))
    3498             e.appendChild(document.createElement('br'));
    3499         }
    3500         text.appendChild(e);
    3501 
    3502         var showAnnotation = function showAnnotation() {
    3503           container.style.zIndex += 1;
    3504           content.removeAttribute('hidden');
    3505         };
    3506 
    3507         var hideAnnotation = function hideAnnotation(e) {
    3508           if (e.toElement || e.relatedTarget) { // No context menu is used
    3509             container.style.zIndex -= 1;
    3510             content.setAttribute('hidden', true);
    3511           }
    3512         };
    3513 
    3514         content.addEventListener('mouseover', showAnnotation, false);
    3515         content.addEventListener('mouseout', hideAnnotation, false);
    3516         image.addEventListener('mouseover', showAnnotation, false);
    3517         image.addEventListener('mouseout', hideAnnotation, false);
    3518       }
    3519 
    3520       content.appendChild(title);
    3521       content.appendChild(text);
    3522       container.appendChild(image);
    3523       container.appendChild(content);
    3524 
    3525       return container;
    3526     }
    3527   });
    3528 
    3529   return TextAnnotation;
    3530 })();
    3531 
    3532 var LinkAnnotation = (function LinkAnnotationClosure() {
    3533   function LinkAnnotation(params) {
    3534     Annotation.call(this, params);
    3535 
    3536     if (params.data) {
    3537       return;
    3538     }
    3539 
    3540     var dict = params.dict;
    3541     var data = this.data;
    3542 
    3543     var action = dict.get('A');
    3544     if (action) {
    3545       var linkType = action.get('S').name;
    3546       if (linkType === 'URI') {
    3547         var url = action.get('URI');
    3548         if (isName(url)) {
    3549           // Some bad PDFs do not put parentheses around relative URLs.
    3550           url = '/' + url.name;
    3551         } else {
    3552           url = addDefaultProtocolToUrl(url);
    3553         }
    3554         // TODO: pdf spec mentions urls can be relative to a Base
    3555         // entry in the dictionary.
    3556         if (!isValidUrl(url, false)) {
    3557           url = '';
    3558         }
    3559         data.url = url;
    3560       } else if (linkType === 'GoTo') {
    3561         data.dest = action.get('D');
    3562       } else if (linkType === 'GoToR') {
    3563         var urlDict = action.get('F');
    3564         if (isDict(urlDict)) {
    3565           // We assume that the 'url' is a Filspec dictionary
    3566           // and fetch the url without checking any further
    3567           url = urlDict.get('F') || '';
    3568         }
    3569 
    3570         // TODO: pdf reference says that GoToR
    3571         // can also have 'NewWindow' attribute
    3572         if (!isValidUrl(url, false)) {
    3573           url = '';
    3574         }
    3575         data.url = url;
    3576         data.dest = action.get('D');
    3577       } else if (linkType === 'Named') {
    3578         data.action = action.get('N').name;
    3579       } else {
    3580         warn('unrecognized link type: ' + linkType);
    3581       }
    3582     } else if (dict.has('Dest')) {
    3583       // simple destination link
    3584       var dest = dict.get('Dest');
    3585       data.dest = isName(dest) ? dest.name : dest;
    3586     }
    3587   }
    3588 
    3589   // Lets URLs beginning with 'www.' default to using the 'http://' protocol.
    3590   function addDefaultProtocolToUrl(url) {
    3591     if (url.indexOf('www.') === 0) {
    3592       return ('http://' + url);
    3593     }
    3594     return url;
    3595   }
    3596 
    3597   Util.inherit(LinkAnnotation, Annotation, {
    3598     hasOperatorList: function LinkAnnotation_hasOperatorList() {
    3599       return false;
    3600     },
    3601 
    3602     hasHtml: function LinkAnnotation_hasHtml() {
    3603       return true;
    3604     },
    3605 
    3606     getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) {
    3607       var rect = this.data.rect;
    3608       var element = document.createElement('a');
    3609       var borderWidth = this.data.borderWidth;
    3610 
    3611       element.style.borderWidth = borderWidth + 'px';
    3612       var color = this.data.color;
    3613       var rgb = [];
    3614       for (var i = 0; i < 3; ++i) {
    3615         rgb[i] = Math.round(color[i] * 255);
    3616       }
    3617       element.style.borderColor = Util.makeCssRgb(rgb);
    3618       element.style.borderStyle = 'solid';
    3619 
    3620       var width = rect[2] - rect[0] - 2 * borderWidth;
    3621       var height = rect[3] - rect[1] - 2 * borderWidth;
    3622       element.style.width = width + 'px';
    3623       element.style.height = height + 'px';
    3624 
    3625       element.href = this.data.url || '';
    3626       return element;
    3627     }
    3628   });
    3629 
    3630   return LinkAnnotation;
    3631 })();
    36321612
    36331613
     
    36371617 * @var {number}
    36381618 */
    3639 PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize;
     1619PDFJS.maxImageSize = (PDFJS.maxImageSize === undefined ?
     1620                      -1 : PDFJS.maxImageSize);
     1621
     1622/**
     1623 * The url of where the predefined Adobe CMaps are located. Include trailing
     1624 * slash.
     1625 * @var {string}
     1626 */
     1627PDFJS.cMapUrl = (PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl);
     1628
     1629/**
     1630 * Specifies if CMaps are binary packed.
     1631 * @var {boolean}
     1632 */
     1633PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked;
    36401634
    36411635/**
     
    36451639 * @var {boolean}
    36461640 */
    3647 PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ?
    3648                         false : PDFJS.disableFontFace;
     1641PDFJS.disableFontFace = (PDFJS.disableFontFace === undefined ?
     1642                         false : PDFJS.disableFontFace);
    36491643
    36501644/**
     
    36531647 * @var {string}
    36541648 */
    3655 PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ?
    3656                            '' : PDFJS.imageResourcesPath;
     1649PDFJS.imageResourcesPath = (PDFJS.imageResourcesPath === undefined ?
     1650                            '' : PDFJS.imageResourcesPath);
    36571651
    36581652/**
     
    36621656 * @var {boolean}
    36631657 */
    3664 PDFJS.disableWorker = PDFJS.disableWorker === undefined ?
    3665                       false : PDFJS.disableWorker;
     1658PDFJS.disableWorker = (PDFJS.disableWorker === undefined ?
     1659                       false : PDFJS.disableWorker);
    36661660
    36671661/**
     
    36711665 * @var {string}
    36721666 */
    3673 PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc;
     1667PDFJS.workerSrc = (PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc);
    36741668
    36751669/**
     
    36791673 * @var {boolean}
    36801674 */
    3681 PDFJS.disableRange = PDFJS.disableRange === undefined ?
    3682                      false : PDFJS.disableRange;
     1675PDFJS.disableRange = (PDFJS.disableRange === undefined ?
     1676                      false : PDFJS.disableRange);
     1677
     1678/**
     1679 * Disable streaming of PDF file data. By default PDF.js attempts to load PDF
     1680 * in chunks. This default behavior can be disabled.
     1681 * @var {boolean}
     1682 */
     1683PDFJS.disableStream = (PDFJS.disableStream === undefined ?
     1684                       false : PDFJS.disableStream);
    36831685
    36841686/**
     
    36861688 * will automatically keep fetching more data even if it isn't needed to display
    36871689 * the current page. This default behavior can be disabled.
     1690 *
     1691 * NOTE: It is also necessary to disable streaming, see above,
     1692 *       in order for disabling of pre-fetching to work correctly.
    36881693 * @var {boolean}
    36891694 */
    3690 PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ?
    3691                          false : PDFJS.disableAutoFetch;
     1695PDFJS.disableAutoFetch = (PDFJS.disableAutoFetch === undefined ?
     1696                          false : PDFJS.disableAutoFetch);
    36921697
    36931698/**
     
    36951700 * @var {boolean}
    36961701 */
    3697 PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
     1702PDFJS.pdfBug = (PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug);
    36981703
    36991704/**
     
    37011706 * @var {boolean}
    37021707 */
    3703 PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ?
    3704                              true : PDFJS.postMessageTransfers;
     1708PDFJS.postMessageTransfers = (PDFJS.postMessageTransfers === undefined ?
     1709                              true : PDFJS.postMessageTransfers);
    37051710
    37061711/**
     
    37081713 * @var {boolean}
    37091714 */
    3710 PDFJS.disableCreateObjectURL = PDFJS.disableCreateObjectURL === undefined ?
    3711                                false : PDFJS.disableCreateObjectURL;
     1715PDFJS.disableCreateObjectURL = (PDFJS.disableCreateObjectURL === undefined ?
     1716                                false : PDFJS.disableCreateObjectURL);
     1717
     1718/**
     1719 * Disables WebGL usage.
     1720 * @var {boolean}
     1721 */
     1722PDFJS.disableWebGL = (PDFJS.disableWebGL === undefined ?
     1723                      true : PDFJS.disableWebGL);
     1724
     1725/**
     1726 * Disables fullscreen support, and by extension Presentation Mode,
     1727 * in browsers which support the fullscreen API.
     1728 * @var {boolean}
     1729 */
     1730PDFJS.disableFullscreen = (PDFJS.disableFullscreen === undefined ?
     1731                           false : PDFJS.disableFullscreen);
     1732
     1733/**
     1734 * Enables CSS only zooming.
     1735 * @var {boolean}
     1736 */
     1737PDFJS.useOnlyCssZoom = (PDFJS.useOnlyCssZoom === undefined ?
     1738                        false : PDFJS.useOnlyCssZoom);
    37121739
    37131740/**
     
    37191746 * @var {number}
    37201747 */
    3721 PDFJS.verbosity = PDFJS.verbosity === undefined ?
    3722                   PDFJS.VERBOSITY_LEVELS.warnings : PDFJS.verbosity;
     1748PDFJS.verbosity = (PDFJS.verbosity === undefined ?
     1749                   PDFJS.VERBOSITY_LEVELS.warnings : PDFJS.verbosity);
     1750
     1751/**
     1752 * The maximum supported canvas size in total pixels e.g. width * height.
     1753 * The default value is 4096 * 4096. Use -1 for no limit.
     1754 * @var {number}
     1755 */
     1756PDFJS.maxCanvasPixels = (PDFJS.maxCanvasPixels === undefined ?
     1757                         16777216 : PDFJS.maxCanvasPixels);
     1758
     1759/**
     1760 * Opens external links in a new window if enabled. The default behavior opens
     1761 * external links in the PDF.js window.
     1762 * @var {boolean}
     1763 */
     1764PDFJS.openExternalLinksInNewWindow = (
     1765  PDFJS.openExternalLinksInNewWindow === undefined ?
     1766    false : PDFJS.openExternalLinksInNewWindow);
    37231767
    37241768/**
     
    37271771 * @typedef {Object} DocumentInitParameters
    37281772 * @property {string}     url   - The URL of the PDF.
    3729  * @property {TypedArray} data  - A typed array with PDF data.
     1773 * @property {TypedArray|Array|string} data - Binary PDF data. Use typed arrays
     1774 *   (Uint8Array) to improve the memory usage. If PDF data is BASE64-encoded,
     1775 *   use atob() to convert it to a binary string first.
    37301776 * @property {Object}     httpHeaders - Basic authentication headers.
    37311777 * @property {boolean}    withCredentials - Indicates whether or not cross-site
     
    37361782 *   all of the pdf data. Used by the extension since some data is already
    37371783 *   loaded before the switch to range requests.
     1784 * @property {number}     length - The PDF file length. It's used for progress
     1785 *   reports and range requests operations.
     1786 * @property {PDFDataRangeTransport} range
     1787 */
     1788
     1789/**
     1790 * @typedef {Object} PDFDocumentStats
     1791 * @property {Array} streamTypes - Used stream types in the document (an item
     1792 *   is set to true if specific stream ID was used in the document).
     1793 * @property {Array} fontTypes - Used font type in the document (an item is set
     1794 *   to true if specific font ID was used in the document).
    37381795 */
    37391796
     
    37441801 * e.g. No cross domain requests without CORS.
    37451802 *
    3746  * @param {string|TypedArray|DocumentInitParameters} source Can be a url to
    3747  * where a PDF is located, a typed array (Uint8Array) already populated with
    3748  * data or parameter object.
     1803 * @param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src
     1804 * Can be a url to where a PDF is located, a typed array (Uint8Array)
     1805 * already populated with data or parameter object.
    37491806 *
    3750  * @param {Object} pdfDataRangeTransport is optional. It is used if you want
    3751  * to manually serve range requests for data in the PDF. See viewer.js for
    3752  * an example of pdfDataRangeTransport's interface.
     1807 * @param {PDFDataRangeTransport} pdfDataRangeTransport (deprecated) It is used
     1808 * if you want to manually serve range requests for data in the PDF.
    37531809 *
    3754  * @param {function} passwordCallback is optional. It is used to request a
     1810 * @param {function} passwordCallback (deprecated) It is used to request a
    37551811 * password if wrong or no password was provided. The callback receives two
    37561812 * parameters: function that needs to be called with new password and reason
    37571813 * (see {PasswordResponses}).
    37581814 *
    3759  * @return {Promise} A promise that is resolved with {@link PDFDocumentProxy}
    3760  *   object.
     1815 * @param {function} progressCallback (deprecated) It is used to be able to
     1816 * monitor the loading progress of the PDF file (necessary to implement e.g.
     1817 * a loading bar). The callback receives an {Object} with the properties:
     1818 * {number} loaded and {number} total.
     1819 *
     1820 * @return {PDFDocumentLoadingTask}
    37611821 */
    3762 PDFJS.getDocument = function getDocument(source,
     1822PDFJS.getDocument = function getDocument(src,
    37631823                                         pdfDataRangeTransport,
    37641824                                         passwordCallback,
    37651825                                         progressCallback) {
    3766   var workerInitializedPromise, workerReadyPromise, transport;
    3767 
    3768   if (typeof source === 'string') {
    3769     source = { url: source };
    3770   } else if (isArrayBuffer(source)) {
    3771     source = { data: source };
    3772   } else if (typeof source !== 'object') {
    3773     error('Invalid parameter in getDocument, need either Uint8Array, ' +
    3774           'string or a parameter object');
    3775   }
    3776 
    3777   if (!source.url && !source.data)
    3778     error('Invalid parameter array, need either .data or .url');
    3779 
    3780   // copy/use all keys as is except 'url' -- full path is required
     1826  var task = new PDFDocumentLoadingTask();
     1827
     1828  // Support of the obsolete arguments (for compatibility with API v1.0)
     1829  if (pdfDataRangeTransport) {
     1830    if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) {
     1831      // Not a PDFDataRangeTransport instance, trying to add missing properties.
     1832      pdfDataRangeTransport = Object.create(pdfDataRangeTransport);
     1833      pdfDataRangeTransport.length = src.length;
     1834      pdfDataRangeTransport.initialData = src.initialData;
     1835    }
     1836    src = Object.create(src);
     1837    src.range = pdfDataRangeTransport;
     1838  }
     1839  task.onPassword = passwordCallback || null;
     1840  task.onProgress = progressCallback || null;
     1841
     1842  var workerInitializedCapability, transport;
     1843  var source;
     1844  if (typeof src === 'string') {
     1845    source = { url: src };
     1846  } else if (isArrayBuffer(src)) {
     1847    source = { data: src };
     1848  } else if (src instanceof PDFDataRangeTransport) {
     1849    source = { range: src };
     1850  } else {
     1851    if (typeof src !== 'object') {
     1852      error('Invalid parameter in getDocument, need either Uint8Array, ' +
     1853        'string or a parameter object');
     1854    }
     1855    if (!src.url && !src.data && !src.range) {
     1856      error('Invalid parameter object: need either .data, .range or .url');
     1857    }
     1858
     1859    source = src;
     1860  }
     1861
    37811862  var params = {};
    37821863  for (var key in source) {
    37831864    if (key === 'url' && typeof window !== 'undefined') {
     1865      // The full path is required in the 'url' field.
    37841866      params[key] = combineUrl(window.location.href, source[key]);
    37851867      continue;
     1868    } else if (key === 'range') {
     1869      continue;
     1870    } else if (key === 'data' && !(source[key] instanceof Uint8Array)) {
     1871      // Converting string or array-like data to Uint8Array.
     1872      var pdfBytes = source[key];
     1873      if (typeof pdfBytes === 'string') {
     1874        params[key] = stringToBytes(pdfBytes);
     1875      } else if (typeof pdfBytes === 'object' && pdfBytes !== null &&
     1876                 !isNaN(pdfBytes.length)) {
     1877        params[key] = new Uint8Array(pdfBytes);
     1878      } else {
     1879        error('Invalid PDF binary data: either typed array, string or ' +
     1880              'array-like object is expected in the data property.');
     1881      }
     1882      continue;
    37861883    }
    37871884    params[key] = source[key];
    37881885  }
    37891886
    3790   workerInitializedPromise = new PDFJS.LegacyPromise();
    3791   workerReadyPromise = new PDFJS.LegacyPromise();
    3792   transport = new WorkerTransport(workerInitializedPromise,
    3793       workerReadyPromise, pdfDataRangeTransport, progressCallback);
    3794   workerInitializedPromise.then(function transportInitialized() {
    3795     transport.passwordCallback = passwordCallback;
    3796     transport.fetchDocument(params);
     1887  workerInitializedCapability = createPromiseCapability();
     1888  transport = new WorkerTransport(workerInitializedCapability, source.range);
     1889  workerInitializedCapability.promise.then(function transportInitialized() {
     1890    transport.fetchDocument(task, params);
    37971891  });
    3798   return workerReadyPromise;
     1892
     1893  return task;
    37991894};
     1895
     1896/**
     1897 * PDF document loading operation.
     1898 * @class
     1899 */
     1900var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
     1901  /** @constructs PDFDocumentLoadingTask */
     1902  function PDFDocumentLoadingTask() {
     1903    this._capability = createPromiseCapability();
     1904
     1905    /**
     1906     * Callback to request a password if wrong or no password was provided.
     1907     * The callback receives two parameters: function that needs to be called
     1908     * with new password and reason (see {PasswordResponses}).
     1909     */
     1910    this.onPassword = null;
     1911
     1912    /**
     1913     * Callback to be able to monitor the loading progress of the PDF file
     1914     * (necessary to implement e.g. a loading bar). The callback receives
     1915     * an {Object} with the properties: {number} loaded and {number} total.
     1916     */
     1917    this.onProgress = null;
     1918  }
     1919
     1920  PDFDocumentLoadingTask.prototype =
     1921      /** @lends PDFDocumentLoadingTask.prototype */ {
     1922    /**
     1923     * @return {Promise}
     1924     */
     1925    get promise() {
     1926      return this._capability.promise;
     1927    },
     1928
     1929    // TODO add cancel or abort method
     1930
     1931    /**
     1932     * Registers callbacks to indicate the document loading completion.
     1933     *
     1934     * @param {function} onFulfilled The callback for the loading completion.
     1935     * @param {function} onRejected The callback for the loading failure.
     1936     * @return {Promise} A promise that is resolved after the onFulfilled or
     1937     *                   onRejected callback.
     1938     */
     1939    then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) {
     1940      return this.promise.then.apply(this.promise, arguments);
     1941    }
     1942  };
     1943
     1944  return PDFDocumentLoadingTask;
     1945})();
     1946
     1947/**
     1948 * Abstract class to support range requests file loading.
     1949 * @class
     1950 */
     1951var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() {
     1952  /**
     1953   * @constructs PDFDataRangeTransport
     1954   * @param {number} length
     1955   * @param {Uint8Array} initialData
     1956   */
     1957  function PDFDataRangeTransport(length, initialData) {
     1958    this.length = length;
     1959    this.initialData = initialData;
     1960
     1961    this._rangeListeners = [];
     1962    this._progressListeners = [];
     1963    this._progressiveReadListeners = [];
     1964    this._readyCapability = createPromiseCapability();
     1965  }
     1966  PDFDataRangeTransport.prototype =
     1967      /** @lends PDFDataRangeTransport.prototype */ {
     1968    addRangeListener:
     1969        function PDFDataRangeTransport_addRangeListener(listener) {
     1970      this._rangeListeners.push(listener);
     1971    },
     1972
     1973    addProgressListener:
     1974        function PDFDataRangeTransport_addProgressListener(listener) {
     1975      this._progressListeners.push(listener);
     1976    },
     1977
     1978    addProgressiveReadListener:
     1979        function PDFDataRangeTransport_addProgressiveReadListener(listener) {
     1980      this._progressiveReadListeners.push(listener);
     1981    },
     1982
     1983    onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) {
     1984      var listeners = this._rangeListeners;
     1985      for (var i = 0, n = listeners.length; i < n; ++i) {
     1986        listeners[i](begin, chunk);
     1987      }
     1988    },
     1989
     1990    onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) {
     1991      this._readyCapability.promise.then(function () {
     1992        var listeners = this._progressListeners;
     1993        for (var i = 0, n = listeners.length; i < n; ++i) {
     1994          listeners[i](loaded);
     1995        }
     1996      }.bind(this));
     1997    },
     1998
     1999    onDataProgressiveRead:
     2000        function PDFDataRangeTransport_onDataProgress(chunk) {
     2001      this._readyCapability.promise.then(function () {
     2002        var listeners = this._progressiveReadListeners;
     2003        for (var i = 0, n = listeners.length; i < n; ++i) {
     2004          listeners[i](chunk);
     2005        }
     2006      }.bind(this));
     2007    },
     2008
     2009    transportReady: function PDFDataRangeTransport_transportReady() {
     2010      this._readyCapability.resolve();
     2011    },
     2012
     2013    requestDataRange:
     2014        function PDFDataRangeTransport_requestDataRange(begin, end) {
     2015      throw new Error('Abstract method PDFDataRangeTransport.requestDataRange');
     2016    }
     2017  };
     2018  return PDFDataRangeTransport;
     2019})();
     2020
     2021PDFJS.PDFDataRangeTransport = PDFDataRangeTransport;
    38002022
    38012023/**
     
    38242046    },
    38252047    /**
    3826      * @return {boolean} true if embedded document fonts are in use. Will be
    3827      * set during rendering of the pages.
    3828      */
    3829     get embeddedFontsUsed() {
    3830       return this.transport.embeddedFontsUsed;
    3831     },
    3832     /**
    38332048     * @param {number} pageNumber The page number to get. The first page is 1.
    38342049     * @return {Promise} A promise that is resolved with a {@link PDFPageProxy}
     
    38502065     * @return {Promise} A promise that is resolved with a lookup table for
    38512066     * mapping named destinations to reference numbers.
     2067     *
     2068     * This can be slow for large documents: use getDestination instead
    38522069     */
    38532070    getDestinations: function PDFDocumentProxy_getDestinations() {
    38542071      return this.transport.getDestinations();
     2072    },
     2073    /**
     2074     * @param {string} id The named destination to get.
     2075     * @return {Promise} A promise that is resolved with all information
     2076     * of the given named destination.
     2077     */
     2078    getDestination: function PDFDocumentProxy_getDestination(id) {
     2079      return this.transport.getDestination(id);
     2080    },
     2081    /**
     2082     * @return {Promise} A promise that is resolved with a lookup table for
     2083     * mapping named attachments to their content.
     2084     */
     2085    getAttachments: function PDFDocumentProxy_getAttachments() {
     2086      return this.transport.getAttachments();
    38552087    },
    38562088    /**
     
    38592091     */
    38602092    getJavaScript: function PDFDocumentProxy_getJavaScript() {
    3861       var promise = new PDFJS.LegacyPromise();
    3862       var js = this.pdfInfo.javaScript;
    3863       promise.resolve(js);
    3864       return promise;
     2093      return this.transport.getJavaScript();
    38652094    },
    38662095    /**
     
    38802109     */
    38812110    getOutline: function PDFDocumentProxy_getOutline() {
    3882       var promise = new PDFJS.LegacyPromise();
    3883       var outline = this.pdfInfo.outline;
    3884       promise.resolve(outline);
    3885       return promise;
     2111      return this.transport.getOutline();
    38862112    },
    38872113    /**
     
    38922118     */
    38932119    getMetadata: function PDFDocumentProxy_getMetadata() {
    3894       var promise = new PDFJS.LegacyPromise();
    3895       var info = this.pdfInfo.info;
    3896       var metadata = this.pdfInfo.metadata;
    3897       promise.resolve({
    3898         info: info,
    3899         metadata: metadata ? new PDFJS.Metadata(metadata) : null
    3900       });
    3901       return promise;
     2120      return this.transport.getMetadata();
    39022121    },
    39032122    /**
     
    39062125     */
    39072126    getData: function PDFDocumentProxy_getData() {
    3908       var promise = new PDFJS.LegacyPromise();
    3909       this.transport.getData(promise);
    3910       return promise;
     2127      return this.transport.getData();
    39112128    },
    39122129    /**
     
    39162133     */
    39172134    getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() {
    3918       return this.transport.downloadInfoPromise;
     2135      return this.transport.downloadInfoCapability.promise;
     2136    },
     2137    /**
     2138     * @return {Promise} A promise this is resolved with current stats about
     2139     * document structures (see {@link PDFDocumentStats}).
     2140     */
     2141    getStats: function PDFDocumentProxy_getStats() {
     2142      return this.transport.getStats();
    39192143    },
    39202144    /**
     
    39352159
    39362160/**
     2161 * Page text content.
     2162 *
     2163 * @typedef {Object} TextContent
     2164 * @property {array} items - array of {@link TextItem}
     2165 * @property {Object} styles - {@link TextStyles} objects, indexed by font
     2166 *                    name.
     2167 */
     2168
     2169/**
    39372170 * Page text content part.
    39382171 *
    3939  * @typedef {Object} BidiText
     2172 * @typedef {Object} TextItem
    39402173 * @property {string} str - text content.
    39412174 * @property {string} dir - text direction: 'ttb', 'ltr' or 'rtl'.
    3942  * @property {number} x - x position of the text on the page.
    3943  * @property {number} y - y position of the text on the page.
    3944  * @property {number} angle - text rotation.
    3945  * @property {number} size - font size.
     2175 * @property {array} transform - transformation matrix.
     2176 * @property {number} width - width in device space.
     2177 * @property {number} height - height in device space.
     2178 * @property {string} fontName - font name used by pdf.js for converted font.
     2179 */
     2180
     2181/**
     2182 * Text style.
     2183 *
     2184 * @typedef {Object} TextStyle
     2185 * @property {number} ascent - font ascent.
     2186 * @property {number} descent - font descent.
     2187 * @property {boolean} vertical - text is in vertical mode.
     2188 * @property {string} fontFamily - possible font family
     2189 */
     2190
     2191/**
     2192 * Page render parameters.
     2193 *
     2194 * @typedef {Object} RenderParameters
     2195 * @property {Object} canvasContext - A 2D context of a DOM Canvas object.
     2196 * @property {PDFJS.PageViewport} viewport - Rendering viewport obtained by
     2197 *                                calling of PDFPage.getViewport method.
     2198 * @property {string} intent - Rendering intent, can be 'display' or 'print'
     2199 *                    (default value is 'display').
     2200 * @property {Object} imageLayer - (optional) An object that has beginLayout,
     2201 *                    endLayout and appendImage functions.
     2202 * @property {function} continueCallback - (deprecated) A function that will be
     2203 *                      called each time the rendering is paused.  To continue
     2204 *                      rendering call the function that is the first argument
     2205 *                      to the callback.
     2206 */
     2207
     2208/**
     2209 * PDF page operator list.
     2210 *
     2211 * @typedef {Object} PDFOperatorList
     2212 * @property {Array} fnArray - Array containing the operator functions.
     2213 * @property {Array} argsArray - Array containing the arguments of the
     2214 *                               functions.
    39462215 */
    39472216
     
    39512220 */
    39522221var PDFPageProxy = (function PDFPageProxyClosure() {
    3953   function PDFPageProxy(pageInfo, transport) {
     2222  function PDFPageProxy(pageIndex, pageInfo, transport) {
     2223    this.pageIndex = pageIndex;
    39542224    this.pageInfo = pageInfo;
    39552225    this.transport = transport;
     
    39582228    this.commonObjs = transport.commonObjs;
    39592229    this.objs = new PDFObjects();
    3960     this.receivingOperatorList  = false;
    39612230    this.cleanupAfterRender = false;
    39622231    this.pendingDestroy = false;
    3963     this.renderTasks = [];
     2232    this.intentStates = {};
    39642233  }
    39652234  PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ {
     
    39682237     */
    39692238    get pageNumber() {
    3970       return this.pageInfo.pageIndex + 1;
     2239      return this.pageIndex + 1;
    39712240    },
    39722241    /**
     
    39942263     * @param {number} rotate Degrees to rotate the viewport. If omitted this
    39952264     * defaults to the page rotation.
    3996      * @return {PageViewport} Contains 'width' and 'height' properties along
    3997      * with transforms required for rendering.
     2265     * @return {PDFJS.PageViewport} Contains 'width' and 'height' properties
     2266     * along with transforms required for rendering.
    39982267     */
    39992268    getViewport: function PDFPageProxy_getViewport(scale, rotate) {
    4000       if (arguments.length < 2)
     2269      if (arguments.length < 2) {
    40012270        rotate = this.rotate;
     2271      }
    40022272      return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0);
    40032273    },
     
    40072277     */
    40082278    getAnnotations: function PDFPageProxy_getAnnotations() {
    4009       if (this.annotationsPromise)
     2279      if (this.annotationsPromise) {
    40102280        return this.annotationsPromise;
    4011 
    4012       var promise = new PDFJS.LegacyPromise();
     2281      }
     2282
     2283      var promise = this.transport.getAnnotations(this.pageIndex);
    40132284      this.annotationsPromise = promise;
    4014       this.transport.getAnnotations(this.pageInfo.pageIndex);
    40152285      return promise;
    40162286    },
    40172287    /**
    40182288     * Begins the process of rendering a page to the desired context.
    4019      * @param {Object} params A parameter object that supports:
    4020      * {
    4021      *   canvasContext(required): A 2D context of a DOM Canvas object.,
    4022      *   textLayer(optional): An object that has beginLayout, endLayout, and
    4023      *                        appendText functions.,
    4024      *   imageLayer(optional): An object that has beginLayout, endLayout and
    4025      *                         appendImage functions.,
    4026      *   continueCallback(optional): A function that will be called each time
    4027      *                               the rendering is paused.  To continue
    4028      *                               rendering call the function that is the
    4029      *                               first argument to the callback.
    4030      * }.
    4031      * @return {RenderTask} An extended promise that is resolved when the page
    4032      * finishes rendering (see RenderTask).
     2289     * @param {RenderParameters} params Page render parameters.
     2290     * @return {RenderTask} An object that contains the promise, which
     2291     *                      is resolved when the page finishes rendering.
    40332292     */
    40342293    render: function PDFPageProxy_render(params) {
     
    40402299      this.pendingDestroy = false;
    40412300
    4042       // If there is no displayReadyPromise yet, then the operatorList was never
    4043       // requested before. Make the request and create the promise.
    4044       if (!this.displayReadyPromise) {
    4045         this.receivingOperatorList = true;
    4046         this.displayReadyPromise = new LegacyPromise();
    4047         this.operatorList = {
     2301      var renderingIntent = (params.intent === 'print' ? 'print' : 'display');
     2302
     2303      if (!this.intentStates[renderingIntent]) {
     2304        this.intentStates[renderingIntent] = {};
     2305      }
     2306      var intentState = this.intentStates[renderingIntent];
     2307
     2308      // If there's no displayReadyCapability yet, then the operatorList
     2309      // was never requested before. Make the request and create the promise.
     2310      if (!intentState.displayReadyCapability) {
     2311        intentState.receivingOperatorList = true;
     2312        intentState.displayReadyCapability = createPromiseCapability();
     2313        intentState.operatorList = {
    40482314          fnArray: [],
    40492315          argsArray: [],
     
    40532319        this.stats.time('Page Request');
    40542320        this.transport.messageHandler.send('RenderPageRequest', {
    4055           pageIndex: this.pageNumber - 1
     2321          pageIndex: this.pageNumber - 1,
     2322          intent: renderingIntent
    40562323        });
    40572324      }
    40582325
    40592326      var internalRenderTask = new InternalRenderTask(complete, params,
    4060                                        this.objs, this.commonObjs,
    4061                                        this.operatorList, this.pageNumber);
    4062       this.renderTasks.push(internalRenderTask);
    4063       var renderTask = new RenderTask(internalRenderTask);
     2327                                                      this.objs,
     2328                                                      this.commonObjs,
     2329                                                      intentState.operatorList,
     2330                                                      this.pageNumber);
     2331      if (!intentState.renderTasks) {
     2332        intentState.renderTasks = [];
     2333      }
     2334      intentState.renderTasks.push(internalRenderTask);
     2335      var renderTask = internalRenderTask.task;
     2336
     2337      // Obsolete parameter support
     2338      if (params.continueCallback) {
     2339        renderTask.onContinue = params.continueCallback;
     2340      }
    40642341
    40652342      var self = this;
    4066       this.displayReadyPromise.then(
     2343      intentState.displayReadyCapability.promise.then(
    40672344        function pageDisplayReadyPromise(transparency) {
    40682345          if (self.pendingDestroy) {
     
    40802357
    40812358      function complete(error) {
    4082         var i = self.renderTasks.indexOf(internalRenderTask);
     2359        var i = intentState.renderTasks.indexOf(internalRenderTask);
    40832360        if (i >= 0) {
    4084           self.renderTasks.splice(i, 1);
     2361          intentState.renderTasks.splice(i, 1);
    40852362        }
    40862363
     
    40912368
    40922369        if (error) {
    4093           renderTask.promise.reject(error);
     2370          internalRenderTask.capability.reject(error);
    40942371        } else {
    4095           renderTask.promise.resolve();
     2372          internalRenderTask.capability.resolve();
    40962373        }
    40972374        stats.timeEnd('Rendering');
     
    41012378      return renderTask;
    41022379    },
     2380
    41032381    /**
    4104      * @return {Promise} That is resolved with the array of {@link BidiText}
    4105      * objects that represent the page text content.
     2382     * @return {Promise} A promise resolved with an {@link PDFOperatorList}
     2383     * object that represents page's operator list.
     2384     */
     2385    getOperatorList: function PDFPageProxy_getOperatorList() {
     2386      function operatorListChanged() {
     2387        if (intentState.operatorList.lastChunk) {
     2388          intentState.opListReadCapability.resolve(intentState.operatorList);
     2389        }
     2390      }
     2391
     2392      var renderingIntent = 'oplist';
     2393      if (!this.intentStates[renderingIntent]) {
     2394        this.intentStates[renderingIntent] = {};
     2395      }
     2396      var intentState = this.intentStates[renderingIntent];
     2397
     2398      if (!intentState.opListReadCapability) {
     2399        var opListTask = {};
     2400        opListTask.operatorListChanged = operatorListChanged;
     2401        intentState.receivingOperatorList = true;
     2402        intentState.opListReadCapability = createPromiseCapability();
     2403        intentState.renderTasks = [];
     2404        intentState.renderTasks.push(opListTask);
     2405        intentState.operatorList = {
     2406          fnArray: [],
     2407          argsArray: [],
     2408          lastChunk: false
     2409        };
     2410
     2411        this.transport.messageHandler.send('RenderPageRequest', {
     2412          pageIndex: this.pageIndex,
     2413          intent: renderingIntent
     2414        });
     2415      }
     2416      return intentState.opListReadCapability.promise;
     2417    },
     2418
     2419    /**
     2420     * @return {Promise} That is resolved a {@link TextContent}
     2421     * object that represent the page text content.
    41062422     */
    41072423    getTextContent: function PDFPageProxy_getTextContent() {
    4108       var promise = new PDFJS.LegacyPromise();
    4109       this.transport.messageHandler.send('GetTextContent', {
    4110           pageIndex: this.pageNumber - 1
    4111         },
    4112         function textContentCallback(textContent) {
    4113           promise.resolve(textContent);
    4114         }
    4115       );
    4116       return promise;
     2424      return this.transport.messageHandler.sendWithPromise('GetTextContent', {
     2425        pageIndex: this.pageNumber - 1
     2426      });
    41172427    },
    41182428    /**
     
    41302440    _tryDestroy: function PDFPageProxy__destroy() {
    41312441      if (!this.pendingDestroy ||
    4132           this.renderTasks.length !== 0 ||
    4133           this.receivingOperatorList) {
     2442          Object.keys(this.intentStates).some(function(intent) {
     2443            var intentState = this.intentStates[intent];
     2444            return (intentState.renderTasks.length !== 0 ||
     2445                    intentState.receivingOperatorList);
     2446          }, this)) {
    41342447        return;
    41352448      }
    41362449
    4137       delete this.operatorList;
    4138       delete this.displayReadyPromise;
     2450      Object.keys(this.intentStates).forEach(function(intent) {
     2451        delete this.intentStates[intent];
     2452      }, this);
    41392453      this.objs.clear();
     2454      this.annotationsPromise = null;
    41402455      this.pendingDestroy = false;
    41412456    },
     
    41442459     * @ignore
    41452460     */
    4146     _startRenderPage: function PDFPageProxy_startRenderPage(transparency) {
    4147       this.displayReadyPromise.resolve(transparency);
     2461    _startRenderPage: function PDFPageProxy_startRenderPage(transparency,
     2462                                                            intent) {
     2463      var intentState = this.intentStates[intent];
     2464      // TODO Refactor RenderPageRequest to separate rendering
     2465      // and operator list logic
     2466      if (intentState.displayReadyCapability) {
     2467        intentState.displayReadyCapability.resolve(transparency);
     2468      }
    41482469    },
    41492470    /**
     
    41512472     * @ignore
    41522473     */
    4153     _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) {
     2474    _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk,
     2475                                                            intent) {
     2476      var intentState = this.intentStates[intent];
     2477      var i, ii;
    41542478      // Add the new chunk to the current operator list.
    4155       for (var i = 0, ii = operatorListChunk.length; i < ii; i++) {
    4156         this.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
    4157         this.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
    4158       }
    4159       this.operatorList.lastChunk = operatorListChunk.lastChunk;
     2479      for (i = 0, ii = operatorListChunk.length; i < ii; i++) {
     2480        intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
     2481        intentState.operatorList.argsArray.push(
     2482          operatorListChunk.argsArray[i]);
     2483      }
     2484      intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
    41602485
    41612486      // Notify all the rendering tasks there are more operators to be consumed.
    4162       for (var i = 0; i < this.renderTasks.length; i++) {
    4163         this.renderTasks[i].operatorListChanged();
     2487      for (i = 0; i < intentState.renderTasks.length; i++) {
     2488        intentState.renderTasks[i].operatorListChanged();
    41642489      }
    41652490
    41662491      if (operatorListChunk.lastChunk) {
    4167         this.receivingOperatorList = false;
     2492        intentState.receivingOperatorList = false;
    41682493        this._tryDestroy();
    41692494      }
     
    41782503 */
    41792504var WorkerTransport = (function WorkerTransportClosure() {
    4180   function WorkerTransport(workerInitializedPromise, workerReadyPromise,
    4181       pdfDataRangeTransport, progressCallback) {
     2505  function WorkerTransport(workerInitializedCapability, pdfDataRangeTransport) {
    41822506    this.pdfDataRangeTransport = pdfDataRangeTransport;
    4183 
    4184     this.workerReadyPromise = workerReadyPromise;
    4185     this.progressCallback = progressCallback;
     2507    this.workerInitializedCapability = workerInitializedCapability;
    41862508    this.commonObjs = new PDFObjects();
     2509
     2510    this.loadingTask = null;
    41872511
    41882512    this.pageCache = [];
    41892513    this.pagePromises = [];
    4190     this.embeddedFontsUsed = false;
    4191     this.downloadInfoPromise = new PDFJS.LegacyPromise();
    4192     this.passwordCallback = null;
     2514    this.downloadInfoCapability = createPromiseCapability();
    41932515
    41942516    // If worker support isn't disabled explicit and the browser has worker
     
    42182540            }
    42192541            this.setupMessageHandler(messageHandler);
    4220             workerInitializedPromise.resolve();
     2542            workerInitializedCapability.resolve();
    42212543          } else {
    4222             globalScope.PDFJS.disableWorker = true;
    4223             this.loadFakeWorkerFiles().then(function() {
    4224               this.setupFakeWorker();
    4225               workerInitializedPromise.resolve();
    4226             }.bind(this));
     2544            this.setupFakeWorker();
    42272545          }
    42282546        }.bind(this));
     
    42322550        // typed array. Also, checking if we can use transfers.
    42332551        try {
    4234           messageHandler.send('test', testObj, null, [testObj.buffer]);
     2552          messageHandler.send('test', testObj, [testObj.buffer]);
    42352553        } catch (ex) {
    42362554          info('Cannot use postMessage transfers');
     
    42452563    // Either workers are disabled, not supported or have thrown an exception.
    42462564    // Thus, we fallback to a faked worker.
    4247     globalScope.PDFJS.disableWorker = true;
    4248     this.loadFakeWorkerFiles().then(function() {
    4249       this.setupFakeWorker();
    4250       workerInitializedPromise.resolve();
    4251     }.bind(this));
     2565    this.setupFakeWorker();
    42522566  }
    42532567  WorkerTransport.prototype = {
     
    42562570      this.pagePromises = [];
    42572571      var self = this;
    4258       this.messageHandler.send('Terminate', null, function () {
     2572      this.messageHandler.sendWithPromise('Terminate', null).then(function () {
    42592573        FontLoader.clear();
    42602574        if (self.worker) {
     
    42642578    },
    42652579
    4266     loadFakeWorkerFiles: function WorkerTransport_loadFakeWorkerFiles() {
    4267       if (!PDFJS.fakeWorkerFilesLoadedPromise) {
    4268         PDFJS.fakeWorkerFilesLoadedPromise = new LegacyPromise();
     2580    setupFakeWorker: function WorkerTransport_setupFakeWorker() {
     2581      globalScope.PDFJS.disableWorker = true;
     2582
     2583      if (!PDFJS.fakeWorkerFilesLoadedCapability) {
     2584        PDFJS.fakeWorkerFilesLoadedCapability = createPromiseCapability();
    42692585        // In the developer build load worker_loader which in turn loads all the
    42702586        // other files and resolves the promise. In production only the
    42712587        // pdf.worker.js file is needed.
    42722588        Util.loadScript(PDFJS.workerSrc, function() {
    4273           PDFJS.fakeWorkerFilesLoadedPromise.resolve();
     2589          PDFJS.fakeWorkerFilesLoadedCapability.resolve();
    42742590        });
    42752591      }
    4276       return PDFJS.fakeWorkerFilesLoadedPromise;
    4277     },
    4278 
    4279     setupFakeWorker: function WorkerTransport_setupFakeWorker() {
    4280       warn('Setting up fake worker.');
    4281       // If we don't use a worker, just post/sendMessage to the main thread.
    4282       var fakeWorker = {
    4283         postMessage: function WorkerTransport_postMessage(obj) {
    4284           fakeWorker.onmessage({data: obj});
    4285         },
    4286         terminate: function WorkerTransport_terminate() {}
    4287       };
    4288 
    4289       var messageHandler = new MessageHandler('main', fakeWorker);
    4290       this.setupMessageHandler(messageHandler);
    4291 
    4292       // If the main thread is our worker, setup the handling for the messages
    4293       // the main thread sends to it self.
    4294       PDFJS.WorkerMessageHandler.setup(messageHandler);
     2592      PDFJS.fakeWorkerFilesLoadedCapability.promise.then(function () {
     2593        warn('Setting up fake worker.');
     2594        // If we don't use a worker, just post/sendMessage to the main thread.
     2595        var fakeWorker = {
     2596          postMessage: function WorkerTransport_postMessage(obj) {
     2597            fakeWorker.onmessage({data: obj});
     2598          },
     2599          terminate: function WorkerTransport_terminate() {}
     2600        };
     2601
     2602        var messageHandler = new MessageHandler('main', fakeWorker);
     2603        this.setupMessageHandler(messageHandler);
     2604
     2605        // If the main thread is our worker, setup the handling for the messages
     2606        // the main thread sends to it self.
     2607        PDFJS.WorkerMessageHandler.setup(messageHandler);
     2608
     2609        this.workerInitializedCapability.resolve();
     2610      }.bind(this));
    42952611    },
    42962612
     
    43182634        });
    43192635
     2636        pdfDataRangeTransport.addProgressiveReadListener(function(chunk) {
     2637          messageHandler.send('OnDataRange', {
     2638            chunk: chunk
     2639          });
     2640        });
     2641
    43202642        messageHandler.on('RequestDataRange',
    43212643          function transportDataRange(data) {
     
    43262648      messageHandler.on('GetDoc', function transportDoc(data) {
    43272649        var pdfInfo = data.pdfInfo;
     2650        this.numPages = data.pdfInfo.numPages;
    43282651        var pdfDocument = new PDFDocumentProxy(pdfInfo, this);
    43292652        this.pdfDocument = pdfDocument;
    4330         this.workerReadyPromise.resolve(pdfDocument);
     2653        this.loadingTask._capability.resolve(pdfDocument);
    43312654      }, this);
    43322655
    4333       messageHandler.on('NeedPassword', function transportPassword(data) {
    4334         if (this.passwordCallback) {
    4335           return this.passwordCallback(updatePassword,
    4336                                        PasswordResponses.NEED_PASSWORD);
    4337         }
    4338         this.workerReadyPromise.reject(data.exception.message, data.exception);
     2656      messageHandler.on('NeedPassword',
     2657                        function transportNeedPassword(exception) {
     2658        var loadingTask = this.loadingTask;
     2659        if (loadingTask.onPassword) {
     2660          return loadingTask.onPassword(updatePassword,
     2661                                        PasswordResponses.NEED_PASSWORD);
     2662        }
     2663        loadingTask._capability.reject(
     2664          new PasswordException(exception.message, exception.code));
    43392665      }, this);
    43402666
    4341       messageHandler.on('IncorrectPassword', function transportBadPass(data) {
    4342         if (this.passwordCallback) {
    4343           return this.passwordCallback(updatePassword,
    4344                                        PasswordResponses.INCORRECT_PASSWORD);
    4345         }
    4346         this.workerReadyPromise.reject(data.exception.message, data.exception);
     2667      messageHandler.on('IncorrectPassword',
     2668                        function transportIncorrectPassword(exception) {
     2669        var loadingTask = this.loadingTask;
     2670        if (loadingTask.onPassword) {
     2671          return loadingTask.onPassword(updatePassword,
     2672                                        PasswordResponses.INCORRECT_PASSWORD);
     2673        }
     2674        loadingTask._capability.reject(
     2675          new PasswordException(exception.message, exception.code));
    43472676      }, this);
    43482677
    4349       messageHandler.on('InvalidPDF', function transportInvalidPDF(data) {
    4350         this.workerReadyPromise.reject(data.exception.name, data.exception);
     2678      messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) {
     2679        this.loadingTask._capability.reject(
     2680          new InvalidPDFException(exception.message));
    43512681      }, this);
    43522682
    4353       messageHandler.on('MissingPDF', function transportMissingPDF(data) {
    4354         this.workerReadyPromise.reject(data.exception.message, data.exception);
     2683      messageHandler.on('MissingPDF', function transportMissingPDF(exception) {
     2684        this.loadingTask._capability.reject(
     2685          new MissingPDFException(exception.message));
    43552686      }, this);
    43562687
    4357       messageHandler.on('UnknownError', function transportUnknownError(data) {
    4358         this.workerReadyPromise.reject(data.exception.message, data.exception);
     2688      messageHandler.on('UnexpectedResponse',
     2689                        function transportUnexpectedResponse(exception) {
     2690        this.loadingTask._capability.reject(
     2691          new UnexpectedResponseException(exception.message, exception.status));
    43592692      }, this);
    43602693
     2694      messageHandler.on('UnknownError',
     2695                        function transportUnknownError(exception) {
     2696        this.loadingTask._capability.reject(
     2697          new UnknownErrorException(exception.message, exception.details));
     2698      }, this);
     2699
    43612700      messageHandler.on('DataLoaded', function transportPage(data) {
    4362         this.downloadInfoPromise.resolve(data);
     2701        this.downloadInfoCapability.resolve(data);
    43632702      }, this);
    43642703
    4365       messageHandler.on('GetPage', function transportPage(data) {
    4366         var pageInfo = data.pageInfo;
    4367         var page = new PDFPageProxy(pageInfo, this);
    4368         this.pageCache[pageInfo.pageIndex] = page;
    4369         var promise = this.pagePromises[pageInfo.pageIndex];
    4370         promise.resolve(page);
    4371       }, this);
    4372 
    4373       messageHandler.on('GetAnnotations', function transportAnnotations(data) {
    4374         var annotations = data.annotations;
    4375         var promise = this.pageCache[data.pageIndex].annotationsPromise;
    4376         promise.resolve(annotations);
     2704      messageHandler.on('PDFManagerReady', function transportPage(data) {
     2705        if (this.pdfDataRangeTransport) {
     2706          this.pdfDataRangeTransport.transportReady();
     2707        }
    43772708      }, this);
    43782709
     
    43812712
    43822713        page.stats.timeEnd('Page Request');
    4383         page._startRenderPage(data.transparency);
     2714        page._startRenderPage(data.transparency, data.intent);
    43842715      }, this);
    43852716
     
    43872718        var page = this.pageCache[data.pageIndex];
    43882719
    4389         page._renderPageChunk(data.operatorList);
     2720        page._renderPageChunk(data.operatorList, data.intent);
    43902721      }, this);
    43912722
     
    43932724        var id = data[0];
    43942725        var type = data[1];
    4395         if (this.commonObjs.hasData(id))
     2726        if (this.commonObjs.hasData(id)) {
    43962727          return;
     2728        }
    43972729
    43982730        switch (type) {
     
    44072739              break;
    44082740            } else {
    4409               font = new FontFace(exportedData);
     2741              font = new FontFaceObject(exportedData);
    44102742            }
    44112743
     
    44302762        var type = data[2];
    44312763        var pageProxy = this.pageCache[pageIndex];
    4432         if (pageProxy.objs.hasData(id))
     2764        var imageData;
     2765        if (pageProxy.objs.hasData(id)) {
    44332766          return;
     2767        }
    44342768
    44352769        switch (type) {
    44362770          case 'JpegStream':
    4437             var imageData = data[3];
     2771            imageData = data[3];
    44382772            loadJpegStream(id, imageData, pageProxy.objs);
    44392773            break;
    44402774          case 'Image':
    4441             var imageData = data[3];
     2775            imageData = data[3];
    44422776            pageProxy.objs.resolve(id, imageData);
    44432777
    44442778            // heuristics that will allow not to store large data
    44452779            var MAX_IMAGE_SIZE_TO_STORE = 8000000;
    4446             if ('data' in imageData &&
     2780            if (imageData && 'data' in imageData &&
    44472781                imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
    44482782              pageProxy.cleanupAfterRender = true;
     
    44552789
    44562790      messageHandler.on('DocProgress', function transportDocProgress(data) {
    4457         if (this.progressCallback) {
    4458           this.progressCallback({
     2791        var loadingTask = this.loadingTask;
     2792        if (loadingTask.onProgress) {
     2793          loadingTask.onProgress({
    44592794            loaded: data.loaded,
    44602795            total: data.total
     
    44632798      }, this);
    44642799
    4465       messageHandler.on('DocError', function transportDocError(data) {
    4466         this.workerReadyPromise.reject(data);
    4467       }, this);
    4468 
    44692800      messageHandler.on('PageError', function transportError(data) {
    44702801        var page = this.pageCache[data.pageNum - 1];
    4471         if (page.displayReadyPromise)
    4472           page.displayReadyPromise.reject(data.error);
    4473         else
     2802        var intentState = page.intentStates[data.intent];
     2803        if (intentState.displayReadyCapability) {
     2804          intentState.displayReadyCapability.reject(data.error);
     2805        } else {
    44742806          error(data.error);
     2807        }
    44752808      }, this);
    44762809
    4477       messageHandler.on('JpegDecode', function(data, deferred) {
     2810      messageHandler.on('JpegDecode', function(data) {
    44782811        var imageUrl = data[0];
    44792812        var components = data[1];
    4480         if (components != 3 && components != 1)
    4481           error('Only 3 component or 1 component can be returned');
    4482 
    4483         var img = new Image();
    4484         img.onload = (function messageHandler_onloadClosure() {
    4485           var width = img.width;
    4486           var height = img.height;
    4487           var size = width * height;
    4488           var rgbaLength = size * 4;
    4489           var buf = new Uint8Array(size * components);
    4490           var tmpCanvas = createScratchCanvas(width, height);
    4491           var tmpCtx = tmpCanvas.getContext('2d');
    4492           tmpCtx.drawImage(img, 0, 0);
    4493           var data = tmpCtx.getImageData(0, 0, width, height).data;
    4494 
    4495           if (components == 3) {
    4496             for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
    4497               buf[j] = data[i];
    4498               buf[j + 1] = data[i + 1];
    4499               buf[j + 2] = data[i + 2];
     2813        if (components !== 3 && components !== 1) {
     2814          return Promise.reject(
     2815            new Error('Only 3 components or 1 component can be returned'));
     2816        }
     2817
     2818        return new Promise(function (resolve, reject) {
     2819          var img = new Image();
     2820          img.onload = function () {
     2821            var width = img.width;
     2822            var height = img.height;
     2823            var size = width * height;
     2824            var rgbaLength = size * 4;
     2825            var buf = new Uint8Array(size * components);
     2826            var tmpCanvas = createScratchCanvas(width, height);
     2827            var tmpCtx = tmpCanvas.getContext('2d');
     2828            tmpCtx.drawImage(img, 0, 0);
     2829            var data = tmpCtx.getImageData(0, 0, width, height).data;
     2830            var i, j;
     2831
     2832            if (components === 3) {
     2833              for (i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
     2834                buf[j] = data[i];
     2835                buf[j + 1] = data[i + 1];
     2836                buf[j + 2] = data[i + 2];
     2837              }
     2838            } else if (components === 1) {
     2839              for (i = 0, j = 0; i < rgbaLength; i += 4, j++) {
     2840                buf[j] = data[i];
     2841              }
    45002842            }
    4501           } else if (components == 1) {
    4502             for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) {
    4503               buf[j] = data[i];
    4504             }
    4505           }
    4506           deferred.resolve({ data: buf, width: width, height: height});
    4507         }).bind(this);
    4508         img.src = imageUrl;
     2843            resolve({ data: buf, width: width, height: height});
     2844          };
     2845          img.onerror = function () {
     2846            reject(new Error('JpegDecode failed to load image'));
     2847          };
     2848          img.src = imageUrl;
     2849        });
    45092850      });
    45102851    },
    45112852
    4512     fetchDocument: function WorkerTransport_fetchDocument(source) {
     2853    fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) {
     2854      this.loadingTask = loadingTask;
     2855
    45132856      source.disableAutoFetch = PDFJS.disableAutoFetch;
     2857      source.disableStream = PDFJS.disableStream;
    45142858      source.chunkedViewerLoading = !!this.pdfDataRangeTransport;
     2859      if (this.pdfDataRangeTransport) {
     2860        source.length = this.pdfDataRangeTransport.length;
     2861        source.initialData = this.pdfDataRangeTransport.initialData;
     2862      }
    45152863      this.messageHandler.send('GetDocRequest', {
    45162864        source: source,
    45172865        disableRange: PDFJS.disableRange,
    45182866        maxImageSize: PDFJS.maxImageSize,
     2867        cMapUrl: PDFJS.cMapUrl,
     2868        cMapPacked: PDFJS.cMapPacked,
    45192869        disableFontFace: PDFJS.disableFontFace,
    45202870        disableCreateObjectURL: PDFJS.disableCreateObjectURL,
     
    45232873    },
    45242874
    4525     getData: function WorkerTransport_getData(promise) {
    4526       this.messageHandler.send('GetData', null, function(data) {
    4527         promise.resolve(data);
     2875    getData: function WorkerTransport_getData() {
     2876      return this.messageHandler.sendWithPromise('GetData', null);
     2877    },
     2878
     2879    getPage: function WorkerTransport_getPage(pageNumber, capability) {
     2880      if (pageNumber <= 0 || pageNumber > this.numPages ||
     2881          (pageNumber|0) !== pageNumber) {
     2882        return Promise.reject(new Error('Invalid page request'));
     2883      }
     2884
     2885      var pageIndex = pageNumber - 1;
     2886      if (pageIndex in this.pagePromises) {
     2887        return this.pagePromises[pageIndex];
     2888      }
     2889      var promise = this.messageHandler.sendWithPromise('GetPage', {
     2890        pageIndex: pageIndex
     2891      }).then(function (pageInfo) {
     2892        var page = new PDFPageProxy(pageIndex, pageInfo, this);
     2893        this.pageCache[pageIndex] = page;
     2894        return page;
     2895      }.bind(this));
     2896      this.pagePromises[pageIndex] = promise;
     2897      return promise;
     2898    },
     2899
     2900    getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
     2901      return this.messageHandler.sendWithPromise('GetPageIndex', { ref: ref });
     2902    },
     2903
     2904    getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
     2905      return this.messageHandler.sendWithPromise('GetAnnotations',
     2906        { pageIndex: pageIndex });
     2907    },
     2908
     2909    getDestinations: function WorkerTransport_getDestinations() {
     2910      return this.messageHandler.sendWithPromise('GetDestinations', null);
     2911    },
     2912
     2913    getDestination: function WorkerTransport_getDestination(id) {
     2914      return this.messageHandler.sendWithPromise('GetDestination', { id: id } );
     2915    },
     2916
     2917    getAttachments: function WorkerTransport_getAttachments() {
     2918      return this.messageHandler.sendWithPromise('GetAttachments', null);
     2919    },
     2920
     2921    getJavaScript: function WorkerTransport_getJavaScript() {
     2922      return this.messageHandler.sendWithPromise('GetJavaScript', null);
     2923    },
     2924
     2925    getOutline: function WorkerTransport_getOutline() {
     2926      return this.messageHandler.sendWithPromise('GetOutline', null);
     2927    },
     2928
     2929    getMetadata: function WorkerTransport_getMetadata() {
     2930      return this.messageHandler.sendWithPromise('GetMetadata', null).
     2931        then(function transportMetadata(results) {
     2932        return {
     2933          info: results[0],
     2934          metadata: (results[1] ? new PDFJS.Metadata(results[1]) : null)
     2935        };
    45282936      });
    45292937    },
    45302938
    4531     getPage: function WorkerTransport_getPage(pageNumber, promise) {
    4532       var pageIndex = pageNumber - 1;
    4533       if (pageIndex in this.pagePromises)
    4534         return this.pagePromises[pageIndex];
    4535       var promise = new PDFJS.LegacyPromise();
    4536       this.pagePromises[pageIndex] = promise;
    4537       this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex });
    4538       return promise;
    4539     },
    4540 
    4541     getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
    4542       var promise = new PDFJS.LegacyPromise();
    4543       this.messageHandler.send('GetPageIndex', { ref: ref },
    4544         function (pageIndex) {
    4545           promise.resolve(pageIndex);
    4546         }
    4547       );
    4548       return promise;
    4549     },
    4550 
    4551     getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
    4552       this.messageHandler.send('GetAnnotationsRequest',
    4553         { pageIndex: pageIndex });
    4554     },
    4555 
    4556     getDestinations: function WorkerTransport_getDestinations() {
    4557       var promise = new PDFJS.LegacyPromise();
    4558       this.messageHandler.send('GetDestinations', null,
    4559         function transportDestinations(destinations) {
    4560           promise.resolve(destinations);
    4561         }
    4562       );
    4563       return promise;
     2939    getStats: function WorkerTransport_getStats() {
     2940      return this.messageHandler.sendWithPromise('GetStats', null);
    45642941    },
    45652942
    45662943    startCleanup: function WorkerTransport_startCleanup() {
    4567       this.messageHandler.send('Cleanup', null,
    4568         function endCleanup() {
    4569           for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
    4570             var page = this.pageCache[i];
    4571             if (page) {
    4572               page.destroy();
    4573             }
     2944      this.messageHandler.sendWithPromise('Cleanup', null).
     2945        then(function endCleanup() {
     2946        for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
     2947          var page = this.pageCache[i];
     2948          if (page) {
     2949            page.destroy();
    45742950          }
    4575           this.commonObjs.clear();
    4576           FontLoader.clear();
    4577         }.bind(this)
    4578       );
     2951        }
     2952        this.commonObjs.clear();
     2953        FontLoader.clear();
     2954      }.bind(this));
    45792955    }
    45802956  };
     
    46012977     */
    46022978    ensureObj: function PDFObjects_ensureObj(objId) {
    4603       if (this.objs[objId])
     2979      if (this.objs[objId]) {
    46042980        return this.objs[objId];
     2981      }
    46052982
    46062983      var obj = {
    4607         promise: new LegacyPromise(),
     2984        capability: createPromiseCapability(),
    46082985        data: null,
    46092986        resolved: false
     
    46273004      // not required to be resolved right now
    46283005      if (callback) {
    4629         this.ensureObj(objId).promise.then(callback);
     3006        this.ensureObj(objId).capability.promise.then(callback);
    46303007        return null;
    46313008      }
     
    46373014      // If there isn't an object yet or the object isn't resolved, then the
    46383015      // data isn't ready yet!
    4639       if (!obj || !obj.resolved)
     3016      if (!obj || !obj.resolved) {
    46403017        error('Requesting object that isn\'t resolved yet ' + objId);
     3018      }
    46413019
    46423020      return obj.data;
     
    46513029      obj.resolved = true;
    46523030      obj.data = data;
    4653       obj.promise.resolve(data);
     3031      obj.capability.resolve(data);
    46543032    },
    46553033
     
    46933071var RenderTask = (function RenderTaskClosure() {
    46943072  function RenderTask(internalRenderTask) {
    4695     this.internalRenderTask = internalRenderTask;
     3073    this._internalRenderTask = internalRenderTask;
     3074
     3075    /**
     3076     * Callback for incremental rendering -- a function that will be called
     3077     * each time the rendering is paused.  To continue rendering call the
     3078     * function that is the first argument to the callback.
     3079     * @type {function}
     3080     */
     3081    this.onContinue = null;
     3082  }
     3083
     3084  RenderTask.prototype = /** @lends RenderTask.prototype */ {
    46963085    /**
    46973086     * Promise for rendering task completion.
    4698      * @type {Promise}
     3087     * @return {Promise}
    46993088     */
    4700     this.promise = new PDFJS.LegacyPromise();
    4701   }
    4702 
    4703   RenderTask.prototype = /** @lends RenderTask.prototype */ {
     3089    get promise() {
     3090      return this._internalRenderTask.capability.promise;
     3091    },
     3092
    47043093    /**
    47053094     * Cancels the rendering task. If the task is currently rendering it will
     
    47083097     */
    47093098    cancel: function RenderTask_cancel() {
    4710       this.internalRenderTask.cancel();
    4711       this.promise.reject(new Error('Rendering is cancelled'));
     3099      this._internalRenderTask.cancel();
     3100    },
     3101
     3102    /**
     3103     * Registers callbacks to indicate the rendering task completion.
     3104     *
     3105     * @param {function} onFulfilled The callback for the rendering completion.
     3106     * @param {function} onRejected The callback for the rendering failure.
     3107     * @return {Promise} A promise that is resolved after the onFulfilled or
     3108     *                   onRejected callback.
     3109     */
     3110    then: function RenderTask_then(onFulfilled, onRejected) {
     3111      return this.promise.then.apply(this.promise, arguments);
    47123112    }
    47133113  };
     
    47353135    this.graphicsReady = false;
    47363136    this.cancelled = false;
     3137    this.capability = createPromiseCapability();
     3138    this.task = new RenderTask(this);
     3139    // caching this-bound methods
     3140    this._continueBound = this._continue.bind(this);
     3141    this._scheduleNextBound = this._scheduleNext.bind(this);
     3142    this._nextBound = this._next.bind(this);
    47373143  }
    47383144
     
    47543160      var params = this.params;
    47553161      this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
    4756                                     this.objs, params.textLayer,
    4757                                     params.imageLayer);
     3162                                    this.objs, params.imageLayer);
    47583163
    47593164      this.gfx.beginDrawing(params.viewport, transparency);
     
    47743179      if (!this.graphicsReady) {
    47753180        if (!this.graphicsReadyCallback) {
    4776           this.graphicsReadyCallback = this._continue.bind(this);
     3181          this.graphicsReadyCallback = this._continueBound;
    47773182        }
    47783183        return;
     
    47943199        return;
    47953200      }
    4796       if (this.params.continueCallback) {
    4797         this.params.continueCallback(this._next.bind(this));
     3201      if (this.task.onContinue) {
     3202        this.task.onContinue.call(this.task, this._scheduleNextBound);
    47983203      } else {
    4799         this._next();
    4800       }
     3204        this._scheduleNext();
     3205      }
     3206    },
     3207
     3208    _scheduleNext: function InternalRenderTask__scheduleNext() {
     3209      window.requestAnimationFrame(this._nextBound);
    48013210    },
    48023211
     
    48073216      this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList,
    48083217                                        this.operatorListIdx,
    4809                                         this._continue.bind(this),
     3218                                        this._continueBound,
    48103219                                        this.stepper);
    48113220      if (this.operatorListIdx === this.operatorList.argsArray.length) {
     
    48343243      for (var i = 0; i < bytes.length; i += 2) {
    48353244        var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
    4836         chars += code >= 32 && code < 127 && code != 60 && code != 62 &&
    4837           code != 38 && false ? String.fromCharCode(code) :
     3245        chars += code >= 32 && code < 127 && code !== 60 && code !== 62 &&
     3246          code !== 38 && false ? String.fromCharCode(code) :
    48383247          '&#x' + (0x10000 + code).toString(16).substring(1) + ';';
    48393248      }
     
    48653274      if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta>
    48663275        rdf = rdf.firstChild;
    4867         while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf')
     3276        while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
    48683277          rdf = rdf.nextSibling;
     3278        }
    48693279      }
    48703280
    48713281      var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
    4872       if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes())
     3282      if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) {
    48733283        return;
     3284      }
    48743285
    48753286      var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
    4876 
    48773287      for (i = 0, length = children.length; i < length; i++) {
    48783288        desc = children[i];
    4879         if (desc.nodeName.toLowerCase() !== 'rdf:description')
     3289        if (desc.nodeName.toLowerCase() !== 'rdf:description') {
    48803290          continue;
     3291        }
    48813292
    48823293        for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
     
    49083319// Minimal font size that would be used during canvas fillText operations.
    49093320var MIN_FONT_SIZE = 16;
     3321// Maximum font size that would be used during canvas fillText operations.
     3322var MAX_FONT_SIZE = 100;
     3323var MAX_GROUP_SIZE = 4096;
     3324
     3325// Heuristic value used when enforcing minimum line widths.
     3326var MIN_WIDTH_FACTOR = 0.65;
    49103327
    49113328var COMPILE_TYPE3_GLYPHS = true;
     3329var MAX_SIZE_TO_COMPILE = 1000;
     3330
     3331var FULL_CHUNK_HEIGHT = 16;
    49123332
    49133333function createScratchCanvas(width, height) {
     
    50433463                                                 trackTransform) {
    50443464      var canvasEntry;
    5045       if (id in cache) {
     3465      if (cache[id] !== undefined) {
    50463466        canvasEntry = cache[id];
    50473467        canvasEntry.canvas.width = width;
     
    50603480    },
    50613481    clear: function () {
    5062       cache = {};
     3482      for (var id in cache) {
     3483        var canvasEntry = cache[id];
     3484        // Zeroing the width and height causes Firefox to release graphics
     3485        // resources immediately, which can greatly reduce memory consumption.
     3486        canvasEntry.canvas.width = 0;
     3487        canvasEntry.canvas.height = 0;
     3488        delete cache[id];
     3489      }
    50633490    }
    50643491  };
     
    51793606    do {
    51803607      var step = steps[type];
    5181       do { p += step; } while (!points[p]);
     3608      do {
     3609        p += step;
     3610      } while (!points[p]);
    51823611
    51833612      pp = points[p];
     
    52303659    this.fontSizeScale = 1;
    52313660    this.textMatrix = IDENTITY_MATRIX;
     3661    this.textMatrixScale = 1;
    52323662    this.fontMatrix = FONT_IDENTITY_MATRIX;
    52333663    this.leading = 0;
     
    52443674    this.textRenderingMode = TextRenderingMode.FILL;
    52453675    this.textRise = 0;
    5246     // Color spaces
    5247     this.fillColorSpace = ColorSpace.singletons.gray;
    5248     this.fillColorSpaceObj = null;
    5249     this.strokeColorSpace = ColorSpace.singletons.gray;
    5250     this.strokeColorSpaceObj = null;
    5251     this.fillColorObj = null;
    5252     this.strokeColorObj = null;
    52533676    // Default fore and background colors
    52543677    this.fillColor = '#000000';
    52553678    this.strokeColor = '#000000';
     3679    this.patternFill = false;
    52563680    // Note: fill alpha applies to all non-stroking operations
    52573681    this.fillAlpha = 1;
     
    52793703  // before it stops and shedules a continue of execution.
    52803704  var EXECUTION_TIME = 15;
    5281 
    5282   function CanvasGraphics(canvasCtx, commonObjs, objs, textLayer, imageLayer) {
     3705  // Defines the number of steps before checking the execution time
     3706  var EXECUTION_STEPS = 10;
     3707
     3708  function CanvasGraphics(canvasCtx, commonObjs, objs, imageLayer) {
    52833709    this.ctx = canvasCtx;
    52843710    this.current = new CanvasExtraState();
     
    52903716    this.commonObjs = commonObjs;
    52913717    this.objs = objs;
    5292     this.textLayer = textLayer;
    52933718    this.imageLayer = imageLayer;
    52943719    this.groupStack = [];
     
    53053730      addContextCurrentTransform(canvasCtx);
    53063731    }
     3732    this.cachedGetSinglePixelWidth = null;
    53073733  }
    53083734
     
    53253751
    53263752    var height = imgData.height, width = imgData.width;
    5327     var fullChunkHeight = 16;
    5328     var fracChunks = height / fullChunkHeight;
    5329     var fullChunks = Math.floor(fracChunks);
    5330     var totalChunks = Math.ceil(fracChunks);
    5331     var partialChunkHeight = height - fullChunks * fullChunkHeight;
    5332 
    5333     var chunkImgData = ctx.createImageData(width, fullChunkHeight);
     3753    var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
     3754    var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
     3755    var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
     3756
     3757    var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
     3758    var srcPos = 0, destPos;
     3759    var src = imgData.data;
     3760    var dest = chunkImgData.data;
     3761    var i, j, thisChunkHeight, elemsInThisChunk;
     3762
     3763    // There are multiple forms in which the pixel data can be passed, and
     3764    // imgData.kind tells us which one this is.
     3765    if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
     3766      // Grayscale, 1 bit per pixel (i.e. black-and-white).
     3767      var srcLength = src.byteLength;
     3768      var dest32 = PDFJS.hasCanvasTypedArrays ? new Uint32Array(dest.buffer) :
     3769        new Uint32ArrayView(dest);
     3770      var dest32DataLength = dest32.length;
     3771      var fullSrcDiff = (width + 7) >> 3;
     3772      var white = 0xFFFFFFFF;
     3773      var black = (PDFJS.isLittleEndian || !PDFJS.hasCanvasTypedArrays) ?
     3774        0xFF000000 : 0x000000FF;
     3775      for (i = 0; i < totalChunks; i++) {
     3776        thisChunkHeight =
     3777          (i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight;
     3778        destPos = 0;
     3779        for (j = 0; j < thisChunkHeight; j++) {
     3780          var srcDiff = srcLength - srcPos;
     3781          var k = 0;
     3782          var kEnd = (srcDiff > fullSrcDiff) ? width : srcDiff * 8 - 7;
     3783          var kEndUnrolled = kEnd & ~7;
     3784          var mask = 0;
     3785          var srcByte = 0;
     3786          for (; k < kEndUnrolled; k += 8) {
     3787            srcByte = src[srcPos++];
     3788            dest32[destPos++] = (srcByte & 128) ? white : black;
     3789            dest32[destPos++] = (srcByte & 64) ? white : black;
     3790            dest32[destPos++] = (srcByte & 32) ? white : black;
     3791            dest32[destPos++] = (srcByte & 16) ? white : black;
     3792            dest32[destPos++] = (srcByte & 8) ? white : black;
     3793            dest32[destPos++] = (srcByte & 4) ? white : black;
     3794            dest32[destPos++] = (srcByte & 2) ? white : black;
     3795            dest32[destPos++] = (srcByte & 1) ? white : black;
     3796          }
     3797          for (; k < kEnd; k++) {
     3798             if (mask === 0) {
     3799               srcByte = src[srcPos++];
     3800               mask = 128;
     3801             }
     3802
     3803            dest32[destPos++] = (srcByte & mask) ? white : black;
     3804            mask >>= 1;
     3805          }
     3806        }
     3807        // We ran out of input. Make all remaining pixels transparent.
     3808        while (destPos < dest32DataLength) {
     3809          dest32[destPos++] = 0;
     3810        }
     3811
     3812        ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
     3813      }
     3814    } else if (imgData.kind === ImageKind.RGBA_32BPP) {
     3815      // RGBA, 32-bits per pixel.
     3816
     3817      j = 0;
     3818      elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
     3819      for (i = 0; i < fullChunks; i++) {
     3820        dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
     3821        srcPos += elemsInThisChunk;
     3822
     3823        ctx.putImageData(chunkImgData, 0, j);
     3824        j += FULL_CHUNK_HEIGHT;
     3825      }
     3826      if (i < totalChunks) {
     3827        elemsInThisChunk = width * partialChunkHeight * 4;
     3828        dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
     3829        ctx.putImageData(chunkImgData, 0, j);
     3830      }
     3831
     3832    } else if (imgData.kind === ImageKind.RGB_24BPP) {
     3833      // RGB, 24-bits per pixel.
     3834      thisChunkHeight = FULL_CHUNK_HEIGHT;
     3835      elemsInThisChunk = width * thisChunkHeight;
     3836      for (i = 0; i < totalChunks; i++) {
     3837        if (i >= fullChunks) {
     3838          thisChunkHeight = partialChunkHeight;
     3839          elemsInThisChunk = width * thisChunkHeight;
     3840        }
     3841
     3842        destPos = 0;
     3843        for (j = elemsInThisChunk; j--;) {
     3844          dest[destPos++] = src[srcPos++];
     3845          dest[destPos++] = src[srcPos++];
     3846          dest[destPos++] = src[srcPos++];
     3847          dest[destPos++] = 255;
     3848        }
     3849        ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
     3850      }
     3851    } else {
     3852      error('bad image kind: ' + imgData.kind);
     3853    }
     3854  }
     3855
     3856  function putBinaryImageMask(ctx, imgData) {
     3857    var height = imgData.height, width = imgData.width;
     3858    var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
     3859    var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
     3860    var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
     3861
     3862    var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
    53343863    var srcPos = 0;
    53353864    var src = imgData.data;
    5336     var dst = chunkImgData.data;
    5337 
    5338     // There are multiple forms in which the pixel data can be passed, and
    5339     // imgData.kind tells us which one this is.
    5340 
    5341     if (imgData.kind === 'grayscale_1bpp') {
    5342       // Grayscale, 1 bit per pixel (i.e. black-and-white).
    5343       var srcData = imgData.data;
    5344       var destData = chunkImgData.data;
    5345       var destDataLength = destData.length;
    5346       var origLength = imgData.origLength;
    5347       for (var i = 3; i < destDataLength; i += 4) {</