browser/extensions/pdfjs/content/build/pdf.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
     3 /* Copyright 2012 Mozilla Foundation
     4  *
     5  * Licensed under the Apache License, Version 2.0 (the "License");
     6  * you may not use this file except in compliance with the License.
     7  * You may obtain a copy of the License at
     8  *
     9  *     http://www.apache.org/licenses/LICENSE-2.0
    10  *
    11  * Unless required by applicable law or agreed to in writing, software
    12  * distributed under the License is distributed on an "AS IS" BASIS,
    13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  * See the License for the specific language governing permissions and
    15  * limitations under the License.
    16  */
    17 /*jshint globalstrict: false */
    19 // Initializing PDFJS global object (if still undefined)
    20 if (typeof PDFJS === 'undefined') {
    21   (typeof window !== 'undefined' ? window : this).PDFJS = {};
    22 }
    24 PDFJS.version = '1.0.68';
    25 PDFJS.build = 'ead4cbf';
    27 (function pdfjsWrapper() {
    28   // Use strict in our context only - users might not want it
    29   'use strict';
    31 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
    32 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
    33 /* Copyright 2012 Mozilla Foundation
    34  *
    35  * Licensed under the Apache License, Version 2.0 (the "License");
    36  * you may not use this file except in compliance with the License.
    37  * You may obtain a copy of the License at
    38  *
    39  *     http://www.apache.org/licenses/LICENSE-2.0
    40  *
    41  * Unless required by applicable law or agreed to in writing, software
    42  * distributed under the License is distributed on an "AS IS" BASIS,
    43  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    44  * See the License for the specific language governing permissions and
    45  * limitations under the License.
    46  */
    47 /* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL,
    48            Promise */
    50 'use strict';
    52 var globalScope = (typeof window === 'undefined') ? this : window;
    54 var isWorker = (typeof window == 'undefined');
    56 var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
    58 var TextRenderingMode = {
    59   FILL: 0,
    60   STROKE: 1,
    61   FILL_STROKE: 2,
    62   INVISIBLE: 3,
    63   FILL_ADD_TO_PATH: 4,
    64   STROKE_ADD_TO_PATH: 5,
    65   FILL_STROKE_ADD_TO_PATH: 6,
    66   ADD_TO_PATH: 7,
    67   FILL_STROKE_MASK: 3,
    68   ADD_TO_PATH_FLAG: 4
    69 };
    71 var ImageKind = {
    72   GRAYSCALE_1BPP: 1,
    73   RGB_24BPP: 2,
    74   RGBA_32BPP: 3
    75 };
    77 // The global PDFJS object exposes the API
    78 // In production, it will be declared outside a global wrapper
    79 // In development, it will be declared here
    80 if (!globalScope.PDFJS) {
    81   globalScope.PDFJS = {};
    82 }
    84 globalScope.PDFJS.pdfBug = false;
    86 PDFJS.VERBOSITY_LEVELS = {
    87   errors: 0,
    88   warnings: 1,
    89   infos: 5
    90 };
    92 // All the possible operations for an operator list.
    93 var OPS = PDFJS.OPS = {
    94   // Intentionally start from 1 so it is easy to spot bad operators that will be
    95   // 0's.
    96   dependency: 1,
    97   setLineWidth: 2,
    98   setLineCap: 3,
    99   setLineJoin: 4,
   100   setMiterLimit: 5,
   101   setDash: 6,
   102   setRenderingIntent: 7,
   103   setFlatness: 8,
   104   setGState: 9,
   105   save: 10,
   106   restore: 11,
   107   transform: 12,
   108   moveTo: 13,
   109   lineTo: 14,
   110   curveTo: 15,
   111   curveTo2: 16,
   112   curveTo3: 17,
   113   closePath: 18,
   114   rectangle: 19,
   115   stroke: 20,
   116   closeStroke: 21,
   117   fill: 22,
   118   eoFill: 23,
   119   fillStroke: 24,
   120   eoFillStroke: 25,
   121   closeFillStroke: 26,
   122   closeEOFillStroke: 27,
   123   endPath: 28,
   124   clip: 29,
   125   eoClip: 30,
   126   beginText: 31,
   127   endText: 32,
   128   setCharSpacing: 33,
   129   setWordSpacing: 34,
   130   setHScale: 35,
   131   setLeading: 36,
   132   setFont: 37,
   133   setTextRenderingMode: 38,
   134   setTextRise: 39,
   135   moveText: 40,
   136   setLeadingMoveText: 41,
   137   setTextMatrix: 42,
   138   nextLine: 43,
   139   showText: 44,
   140   showSpacedText: 45,
   141   nextLineShowText: 46,
   142   nextLineSetSpacingShowText: 47,
   143   setCharWidth: 48,
   144   setCharWidthAndBounds: 49,
   145   setStrokeColorSpace: 50,
   146   setFillColorSpace: 51,
   147   setStrokeColor: 52,
   148   setStrokeColorN: 53,
   149   setFillColor: 54,
   150   setFillColorN: 55,
   151   setStrokeGray: 56,
   152   setFillGray: 57,
   153   setStrokeRGBColor: 58,
   154   setFillRGBColor: 59,
   155   setStrokeCMYKColor: 60,
   156   setFillCMYKColor: 61,
   157   shadingFill: 62,
   158   beginInlineImage: 63,
   159   beginImageData: 64,
   160   endInlineImage: 65,
   161   paintXObject: 66,
   162   markPoint: 67,
   163   markPointProps: 68,
   164   beginMarkedContent: 69,
   165   beginMarkedContentProps: 70,
   166   endMarkedContent: 71,
   167   beginCompat: 72,
   168   endCompat: 73,
   169   paintFormXObjectBegin: 74,
   170   paintFormXObjectEnd: 75,
   171   beginGroup: 76,
   172   endGroup: 77,
   173   beginAnnotations: 78,
   174   endAnnotations: 79,
   175   beginAnnotation: 80,
   176   endAnnotation: 81,
   177   paintJpegXObject: 82,
   178   paintImageMaskXObject: 83,
   179   paintImageMaskXObjectGroup: 84,
   180   paintImageXObject: 85,
   181   paintInlineImageXObject: 86,
   182   paintInlineImageXObjectGroup: 87,
   183   paintImageXObjectRepeat: 88,
   184   paintImageMaskXObjectRepeat: 89,
   185   paintSolidColorImageMask: 90
   186 };
   188 // A notice for devs. These are good for things that are helpful to devs, such
   189 // as warning that Workers were disabled, which is important to devs but not
   190 // end users.
   191 function info(msg) {
   192   if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) {
   193     console.log('Info: ' + msg);
   194   }
   195 }
   197 // Non-fatal warnings.
   198 function warn(msg) {
   199   if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) {
   200     console.log('Warning: ' + msg);
   201   }
   202 }
   204 // Fatal errors that should trigger the fallback UI and halt execution by
   205 // throwing an exception.
   206 function error(msg) {
   207   // If multiple arguments were passed, pass them all to the log function.
   208   if (arguments.length > 1) {
   209     var logArguments = ['Error:'];
   210     logArguments.push.apply(logArguments, arguments);
   211     console.log.apply(console, logArguments);
   212     // Join the arguments into a single string for the lines below.
   213     msg = [].join.call(arguments, ' ');
   214   } else {
   215     console.log('Error: ' + msg);
   216   }
   217   console.log(backtrace());
   218   UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown);
   219   throw new Error(msg);
   220 }
   222 function backtrace() {
   223   try {
   224     throw new Error();
   225   } catch (e) {
   226     return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
   227   }
   228 }
   230 function assert(cond, msg) {
   231   if (!cond) {
   232     error(msg);
   233   }
   234 }
   236 var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = {
   237   unknown: 'unknown',
   238   forms: 'forms',
   239   javaScript: 'javaScript',
   240   smask: 'smask',
   241   shadingPattern: 'shadingPattern',
   242   font: 'font'
   243 };
   245 var UnsupportedManager = PDFJS.UnsupportedManager =
   246   (function UnsupportedManagerClosure() {
   247   var listeners = [];
   248   return {
   249     listen: function (cb) {
   250       listeners.push(cb);
   251     },
   252     notify: function (featureId) {
   253       warn('Unsupported feature "' + featureId + '"');
   254       for (var i = 0, ii = listeners.length; i < ii; i++) {
   255         listeners[i](featureId);
   256       }
   257     }
   258   };
   259 })();
   261 // Combines two URLs. The baseUrl shall be absolute URL. If the url is an
   262 // absolute URL, it will be returned as is.
   263 function combineUrl(baseUrl, url) {
   264   if (!url) {
   265     return baseUrl;
   266   }
   267   if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) {
   268     return url;
   269   }
   270   var i;
   271   if (url.charAt(0) == '/') {
   272     // absolute path
   273     i = baseUrl.indexOf('://');
   274     if (url.charAt(1) === '/') {
   275       ++i;
   276     } else {
   277       i = baseUrl.indexOf('/', i + 3);
   278     }
   279     return baseUrl.substring(0, i) + url;
   280   } else {
   281     // relative path
   282     var pathLength = baseUrl.length;
   283     i = baseUrl.lastIndexOf('#');
   284     pathLength = i >= 0 ? i : pathLength;
   285     i = baseUrl.lastIndexOf('?', pathLength);
   286     pathLength = i >= 0 ? i : pathLength;
   287     var prefixLength = baseUrl.lastIndexOf('/', pathLength);
   288     return baseUrl.substring(0, prefixLength + 1) + url;
   289   }
   290 }
   292 // Validates if URL is safe and allowed, e.g. to avoid XSS.
   293 function isValidUrl(url, allowRelative) {
   294   if (!url) {
   295     return false;
   296   }
   297   // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1)
   298   // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
   299   var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url);
   300   if (!protocol) {
   301     return allowRelative;
   302   }
   303   protocol = protocol[0].toLowerCase();
   304   switch (protocol) {
   305     case 'http':
   306     case 'https':
   307     case 'ftp':
   308     case 'mailto':
   309       return true;
   310     default:
   311       return false;
   312   }
   313 }
   314 PDFJS.isValidUrl = isValidUrl;
   316 function shadow(obj, prop, value) {
   317   Object.defineProperty(obj, prop, { value: value,
   318                                      enumerable: true,
   319                                      configurable: true,
   320                                      writable: false });
   321   return value;
   322 }
   324 var PasswordResponses = PDFJS.PasswordResponses = {
   325   NEED_PASSWORD: 1,
   326   INCORRECT_PASSWORD: 2
   327 };
   329 var PasswordException = (function PasswordExceptionClosure() {
   330   function PasswordException(msg, code) {
   331     this.name = 'PasswordException';
   332     this.message = msg;
   333     this.code = code;
   334   }
   336   PasswordException.prototype = new Error();
   337   PasswordException.constructor = PasswordException;
   339   return PasswordException;
   340 })();
   342 var UnknownErrorException = (function UnknownErrorExceptionClosure() {
   343   function UnknownErrorException(msg, details) {
   344     this.name = 'UnknownErrorException';
   345     this.message = msg;
   346     this.details = details;
   347   }
   349   UnknownErrorException.prototype = new Error();
   350   UnknownErrorException.constructor = UnknownErrorException;
   352   return UnknownErrorException;
   353 })();
   355 var InvalidPDFException = (function InvalidPDFExceptionClosure() {
   356   function InvalidPDFException(msg) {
   357     this.name = 'InvalidPDFException';
   358     this.message = msg;
   359   }
   361   InvalidPDFException.prototype = new Error();
   362   InvalidPDFException.constructor = InvalidPDFException;
   364   return InvalidPDFException;
   365 })();
   367 var MissingPDFException = (function MissingPDFExceptionClosure() {
   368   function MissingPDFException(msg) {
   369     this.name = 'MissingPDFException';
   370     this.message = msg;
   371   }
   373   MissingPDFException.prototype = new Error();
   374   MissingPDFException.constructor = MissingPDFException;
   376   return MissingPDFException;
   377 })();
   379 var NotImplementedException = (function NotImplementedExceptionClosure() {
   380   function NotImplementedException(msg) {
   381     this.message = msg;
   382   }
   384   NotImplementedException.prototype = new Error();
   385   NotImplementedException.prototype.name = 'NotImplementedException';
   386   NotImplementedException.constructor = NotImplementedException;
   388   return NotImplementedException;
   389 })();
   391 var MissingDataException = (function MissingDataExceptionClosure() {
   392   function MissingDataException(begin, end) {
   393     this.begin = begin;
   394     this.end = end;
   395     this.message = 'Missing data [' + begin + ', ' + end + ')';
   396   }
   398   MissingDataException.prototype = new Error();
   399   MissingDataException.prototype.name = 'MissingDataException';
   400   MissingDataException.constructor = MissingDataException;
   402   return MissingDataException;
   403 })();
   405 var XRefParseException = (function XRefParseExceptionClosure() {
   406   function XRefParseException(msg) {
   407     this.message = msg;
   408   }
   410   XRefParseException.prototype = new Error();
   411   XRefParseException.prototype.name = 'XRefParseException';
   412   XRefParseException.constructor = XRefParseException;
   414   return XRefParseException;
   415 })();
   418 function bytesToString(bytes) {
   419   var length = bytes.length;
   420   var MAX_ARGUMENT_COUNT = 8192;
   421   if (length < MAX_ARGUMENT_COUNT) {
   422     return String.fromCharCode.apply(null, bytes);
   423   }
   424   var strBuf = [];
   425   for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
   426     var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
   427     var chunk = bytes.subarray(i, chunkEnd);
   428     strBuf.push(String.fromCharCode.apply(null, chunk));
   429   }
   430   return strBuf.join('');
   431 }
   433 function stringToArray(str) {
   434   var length = str.length;
   435   var array = [];
   436   for (var i = 0; i < length; ++i) {
   437     array[i] = str.charCodeAt(i);
   438   }
   439   return array;
   440 }
   442 function stringToBytes(str) {
   443   var length = str.length;
   444   var bytes = new Uint8Array(length);
   445   for (var i = 0; i < length; ++i) {
   446     bytes[i] = str.charCodeAt(i) & 0xFF;
   447   }
   448   return bytes;
   449 }
   451 function string32(value) {
   452   return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
   453                              (value >> 8) & 0xff, value & 0xff);
   454 }
   456 function log2(x) {
   457   var n = 1, i = 0;
   458   while (x > n) {
   459     n <<= 1;
   460     i++;
   461   }
   462   return i;
   463 }
   465 function readInt8(data, start) {
   466   return (data[start] << 24) >> 24;
   467 }
   469 function readUint16(data, offset) {
   470   return (data[offset] << 8) | data[offset + 1];
   471 }
   473 function readUint32(data, offset) {
   474   return ((data[offset] << 24) | (data[offset + 1] << 16) |
   475          (data[offset + 2] << 8) | data[offset + 3]) >>> 0;
   476 }
   478 // Lazy test the endianness of the platform
   479 // NOTE: This will be 'true' for simulated TypedArrays
   480 function isLittleEndian() {
   481   var buffer8 = new Uint8Array(2);
   482   buffer8[0] = 1;
   483   var buffer16 = new Uint16Array(buffer8.buffer);
   484   return (buffer16[0] === 1);
   485 }
   487 Object.defineProperty(PDFJS, 'isLittleEndian', {
   488   configurable: true,
   489   get: function PDFJS_isLittleEndian() {
   490     return shadow(PDFJS, 'isLittleEndian', isLittleEndian());
   491   }
   492 });
   494   PDFJS.hasCanvasTypedArrays = true;
   496 var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
   498 var Util = PDFJS.Util = (function UtilClosure() {
   499   function Util() {}
   501   Util.makeCssRgb = function Util_makeCssRgb(rgb) {
   502     return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
   503   };
   505   Util.makeCssCmyk = function Util_makeCssCmyk(cmyk) {
   506     var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0);
   507     return Util.makeCssRgb(rgb);
   508   };
   510   // Concatenates two transformation matrices together and returns the result.
   511   Util.transform = function Util_transform(m1, m2) {
   512     return [
   513       m1[0] * m2[0] + m1[2] * m2[1],
   514       m1[1] * m2[0] + m1[3] * m2[1],
   515       m1[0] * m2[2] + m1[2] * m2[3],
   516       m1[1] * m2[2] + m1[3] * m2[3],
   517       m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
   518       m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
   519     ];
   520   };
   522   // For 2d affine transforms
   523   Util.applyTransform = function Util_applyTransform(p, m) {
   524     var xt = p[0] * m[0] + p[1] * m[2] + m[4];
   525     var yt = p[0] * m[1] + p[1] * m[3] + m[5];
   526     return [xt, yt];
   527   };
   529   Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
   530     var d = m[0] * m[3] - m[1] * m[2];
   531     var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
   532     var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
   533     return [xt, yt];
   534   };
   536   // Applies the transform to the rectangle and finds the minimum axially
   537   // aligned bounding box.
   538   Util.getAxialAlignedBoundingBox =
   539     function Util_getAxialAlignedBoundingBox(r, m) {
   541     var p1 = Util.applyTransform(r, m);
   542     var p2 = Util.applyTransform(r.slice(2, 4), m);
   543     var p3 = Util.applyTransform([r[0], r[3]], m);
   544     var p4 = Util.applyTransform([r[2], r[1]], m);
   545     return [
   546       Math.min(p1[0], p2[0], p3[0], p4[0]),
   547       Math.min(p1[1], p2[1], p3[1], p4[1]),
   548       Math.max(p1[0], p2[0], p3[0], p4[0]),
   549       Math.max(p1[1], p2[1], p3[1], p4[1])
   550     ];
   551   };
   553   Util.inverseTransform = function Util_inverseTransform(m) {
   554     var d = m[0] * m[3] - m[1] * m[2];
   555     return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
   556       (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
   557   };
   559   // Apply a generic 3d matrix M on a 3-vector v:
   560   //   | a b c |   | X |
   561   //   | d e f | x | Y |
   562   //   | g h i |   | Z |
   563   // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
   564   // with v as [X,Y,Z]
   565   Util.apply3dTransform = function Util_apply3dTransform(m, v) {
   566     return [
   567       m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
   568       m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
   569       m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
   570     ];
   571   };
   573   // This calculation uses Singular Value Decomposition.
   574   // The SVD can be represented with formula A = USV. We are interested in the
   575   // matrix S here because it represents the scale values.
   576   Util.singularValueDecompose2dScale =
   577     function Util_singularValueDecompose2dScale(m) {
   579     var transpose = [m[0], m[2], m[1], m[3]];
   581     // Multiply matrix m with its transpose.
   582     var a = m[0] * transpose[0] + m[1] * transpose[2];
   583     var b = m[0] * transpose[1] + m[1] * transpose[3];
   584     var c = m[2] * transpose[0] + m[3] * transpose[2];
   585     var d = m[2] * transpose[1] + m[3] * transpose[3];
   587     // Solve the second degree polynomial to get roots.
   588     var first = (a + d) / 2;
   589     var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
   590     var sx = first + second || 1;
   591     var sy = first - second || 1;
   593     // Scale values are the square roots of the eigenvalues.
   594     return [Math.sqrt(sx), Math.sqrt(sy)];
   595   };
   597   // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
   598   // For coordinate systems whose origin lies in the bottom-left, this
   599   // means normalization to (BL,TR) ordering. For systems with origin in the
   600   // top-left, this means (TL,BR) ordering.
   601   Util.normalizeRect = function Util_normalizeRect(rect) {
   602     var r = rect.slice(0); // clone rect
   603     if (rect[0] > rect[2]) {
   604       r[0] = rect[2];
   605       r[2] = rect[0];
   606     }
   607     if (rect[1] > rect[3]) {
   608       r[1] = rect[3];
   609       r[3] = rect[1];
   610     }
   611     return r;
   612   };
   614   // Returns a rectangle [x1, y1, x2, y2] corresponding to the
   615   // intersection of rect1 and rect2. If no intersection, returns 'false'
   616   // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
   617   Util.intersect = function Util_intersect(rect1, rect2) {
   618     function compare(a, b) {
   619       return a - b;
   620     }
   622     // Order points along the axes
   623     var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
   624         orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
   625         result = [];
   627     rect1 = Util.normalizeRect(rect1);
   628     rect2 = Util.normalizeRect(rect2);
   630     // X: first and second points belong to different rectangles?
   631     if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
   632         (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
   633       // Intersection must be between second and third points
   634       result[0] = orderedX[1];
   635       result[2] = orderedX[2];
   636     } else {
   637       return false;
   638     }
   640     // Y: first and second points belong to different rectangles?
   641     if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
   642         (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
   643       // Intersection must be between second and third points
   644       result[1] = orderedY[1];
   645       result[3] = orderedY[2];
   646     } else {
   647       return false;
   648     }
   650     return result;
   651   };
   653   Util.sign = function Util_sign(num) {
   654     return num < 0 ? -1 : 1;
   655   };
   657   // TODO(mack): Rename appendToArray
   658   Util.concatenateToArray = function concatenateToArray(arr1, arr2) {
   659     Array.prototype.push.apply(arr1, arr2);
   660   };
   662   Util.prependToArray = function concatenateToArray(arr1, arr2) {
   663     Array.prototype.unshift.apply(arr1, arr2);
   664   };
   666   Util.extendObj = function extendObj(obj1, obj2) {
   667     for (var key in obj2) {
   668       obj1[key] = obj2[key];
   669     }
   670   };
   672   Util.getInheritableProperty = function Util_getInheritableProperty(dict,
   673                                                                      name) {
   674     while (dict && !dict.has(name)) {
   675       dict = dict.get('Parent');
   676     }
   677     if (!dict) {
   678       return null;
   679     }
   680     return dict.get(name);
   681   };
   683   Util.inherit = function Util_inherit(sub, base, prototype) {
   684     sub.prototype = Object.create(base.prototype);
   685     sub.prototype.constructor = sub;
   686     for (var prop in prototype) {
   687       sub.prototype[prop] = prototype[prop];
   688     }
   689   };
   691   Util.loadScript = function Util_loadScript(src, callback) {
   692     var script = document.createElement('script');
   693     var loaded = false;
   694     script.setAttribute('src', src);
   695     if (callback) {
   696       script.onload = function() {
   697         if (!loaded) {
   698           callback();
   699         }
   700         loaded = true;
   701       };
   702     }
   703     document.getElementsByTagName('head')[0].appendChild(script);
   704   };
   706   return Util;
   707 })();
   709 var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
   710   function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
   711     this.viewBox = viewBox;
   712     this.scale = scale;
   713     this.rotation = rotation;
   714     this.offsetX = offsetX;
   715     this.offsetY = offsetY;
   717     // creating transform to convert pdf coordinate system to the normal
   718     // canvas like coordinates taking in account scale and rotation
   719     var centerX = (viewBox[2] + viewBox[0]) / 2;
   720     var centerY = (viewBox[3] + viewBox[1]) / 2;
   721     var rotateA, rotateB, rotateC, rotateD;
   722     rotation = rotation % 360;
   723     rotation = rotation < 0 ? rotation + 360 : rotation;
   724     switch (rotation) {
   725       case 180:
   726         rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
   727         break;
   728       case 90:
   729         rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
   730         break;
   731       case 270:
   732         rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
   733         break;
   734       //case 0:
   735       default:
   736         rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
   737         break;
   738     }
   740     if (dontFlip) {
   741       rotateC = -rotateC; rotateD = -rotateD;
   742     }
   744     var offsetCanvasX, offsetCanvasY;
   745     var width, height;
   746     if (rotateA === 0) {
   747       offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
   748       offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
   749       width = Math.abs(viewBox[3] - viewBox[1]) * scale;
   750       height = Math.abs(viewBox[2] - viewBox[0]) * scale;
   751     } else {
   752       offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
   753       offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
   754       width = Math.abs(viewBox[2] - viewBox[0]) * scale;
   755       height = Math.abs(viewBox[3] - viewBox[1]) * scale;
   756     }
   757     // creating transform for the following operations:
   758     // translate(-centerX, -centerY), rotate and flip vertically,
   759     // scale, and translate(offsetCanvasX, offsetCanvasY)
   760     this.transform = [
   761       rotateA * scale,
   762       rotateB * scale,
   763       rotateC * scale,
   764       rotateD * scale,
   765       offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
   766       offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
   767     ];
   769     this.width = width;
   770     this.height = height;
   771     this.fontScale = scale;
   772   }
   773   PageViewport.prototype = {
   774     clone: function PageViewPort_clone(args) {
   775       args = args || {};
   776       var scale = 'scale' in args ? args.scale : this.scale;
   777       var rotation = 'rotation' in args ? args.rotation : this.rotation;
   778       return new PageViewport(this.viewBox.slice(), scale, rotation,
   779                               this.offsetX, this.offsetY, args.dontFlip);
   780     },
   781     convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
   782       return Util.applyTransform([x, y], this.transform);
   783     },
   784     convertToViewportRectangle:
   785       function PageViewport_convertToViewportRectangle(rect) {
   786       var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
   787       var br = Util.applyTransform([rect[2], rect[3]], this.transform);
   788       return [tl[0], tl[1], br[0], br[1]];
   789     },
   790     convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
   791       return Util.applyInverseTransform([x, y], this.transform);
   792     }
   793   };
   794   return PageViewport;
   795 })();
   797 var PDFStringTranslateTable = [
   798   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   799   0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0,
   800   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   801   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   802   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   803   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
   804   0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
   805   0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
   806   0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
   807 ];
   809 function stringToPDFString(str) {
   810   var i, n = str.length, strBuf = [];
   811   if (str[0] === '\xFE' && str[1] === '\xFF') {
   812     // UTF16BE BOM
   813     for (i = 2; i < n; i += 2) {
   814       strBuf.push(String.fromCharCode(
   815         (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
   816     }
   817   } else {
   818     for (i = 0; i < n; ++i) {
   819       var code = PDFStringTranslateTable[str.charCodeAt(i)];
   820       strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
   821     }
   822   }
   823   return strBuf.join('');
   824 }
   826 function stringToUTF8String(str) {
   827   return decodeURIComponent(escape(str));
   828 }
   830 function isEmptyObj(obj) {
   831   for (var key in obj) {
   832     return false;
   833   }
   834   return true;
   835 }
   837 function isBool(v) {
   838   return typeof v == 'boolean';
   839 }
   841 function isInt(v) {
   842   return typeof v == 'number' && ((v | 0) == v);
   843 }
   845 function isNum(v) {
   846   return typeof v == 'number';
   847 }
   849 function isString(v) {
   850   return typeof v == 'string';
   851 }
   853 function isNull(v) {
   854   return v === null;
   855 }
   857 function isName(v) {
   858   return v instanceof Name;
   859 }
   861 function isCmd(v, cmd) {
   862   return v instanceof Cmd && (!cmd || v.cmd == cmd);
   863 }
   865 function isDict(v, type) {
   866   if (!(v instanceof Dict)) {
   867     return false;
   868   }
   869   if (!type) {
   870     return true;
   871   }
   872   var dictType = v.get('Type');
   873   return isName(dictType) && dictType.name == type;
   874 }
   876 function isArray(v) {
   877   return v instanceof Array;
   878 }
   880 function isStream(v) {
   881   return typeof v == 'object' && v !== null && v !== undefined &&
   882          ('getBytes' in v);
   883 }
   885 function isArrayBuffer(v) {
   886   return typeof v == 'object' && v !== null && v !== undefined &&
   887          ('byteLength' in v);
   888 }
   890 function isRef(v) {
   891   return v instanceof Ref;
   892 }
   894 function isPDFFunction(v) {
   895   var fnDict;
   896   if (typeof v != 'object') {
   897     return false;
   898   } else if (isDict(v)) {
   899     fnDict = v;
   900   } else if (isStream(v)) {
   901     fnDict = v.dict;
   902   } else {
   903     return false;
   904   }
   905   return fnDict.has('FunctionType');
   906 }
   908 /**
   909  * Legacy support for PDFJS Promise implementation.
   910  * TODO remove eventually
   911  * @ignore
   912  */
   913 var LegacyPromise = PDFJS.LegacyPromise = (function LegacyPromiseClosure() {
   914   return function LegacyPromise() {
   915     var resolve, reject;
   916     var promise = new Promise(function (resolve_, reject_) {
   917       resolve = resolve_;
   918       reject = reject_;
   919     });
   920     promise.resolve = resolve;
   921     promise.reject = reject;
   922     return promise;
   923   };
   924 })();
   926 /**
   927  * Polyfill for Promises:
   928  * The following promise implementation tries to generally implment the
   929  * Promise/A+ spec. Some notable differences from other promise libaries are:
   930  * - There currently isn't a seperate deferred and promise object.
   931  * - Unhandled rejections eventually show an error if they aren't handled.
   932  *
   933  * Based off of the work in:
   934  * https://bugzilla.mozilla.org/show_bug.cgi?id=810490
   935  */
   936 (function PromiseClosure() {
   937   if (globalScope.Promise) {
   938     // Promises existing in the DOM/Worker, checking presence of all/resolve
   939     if (typeof globalScope.Promise.all !== 'function') {
   940       globalScope.Promise.all = function (iterable) {
   941         var count = 0, results = [], resolve, reject;
   942         var promise = new globalScope.Promise(function (resolve_, reject_) {
   943           resolve = resolve_;
   944           reject = reject_;
   945         });
   946         iterable.forEach(function (p, i) {
   947           count++;
   948           p.then(function (result) {
   949             results[i] = result;
   950             count--;
   951             if (count === 0) {
   952               resolve(results);
   953             }
   954           }, reject);
   955         });
   956         if (count === 0) {
   957           resolve(results);
   958         }
   959         return promise;
   960       };
   961     }
   962     if (typeof globalScope.Promise.resolve !== 'function') {
   963       globalScope.Promise.resolve = function (x) {
   964         return new globalScope.Promise(function (resolve) { resolve(x); });
   965       };
   966     }
   967     return;
   968   }
   969   throw new Error('DOM Promise is not present');
   970 })();
   972 var StatTimer = (function StatTimerClosure() {
   973   function rpad(str, pad, length) {
   974     while (str.length < length) {
   975       str += pad;
   976     }
   977     return str;
   978   }
   979   function StatTimer() {
   980     this.started = {};
   981     this.times = [];
   982     this.enabled = true;
   983   }
   984   StatTimer.prototype = {
   985     time: function StatTimer_time(name) {
   986       if (!this.enabled) {
   987         return;
   988       }
   989       if (name in this.started) {
   990         warn('Timer is already running for ' + name);
   991       }
   992       this.started[name] = Date.now();
   993     },
   994     timeEnd: function StatTimer_timeEnd(name) {
   995       if (!this.enabled) {
   996         return;
   997       }
   998       if (!(name in this.started)) {
   999         warn('Timer has not been started for ' + name);
  1001       this.times.push({
  1002         'name': name,
  1003         'start': this.started[name],
  1004         'end': Date.now()
  1005       });
  1006       // Remove timer from started so it can be called again.
  1007       delete this.started[name];
  1008     },
  1009     toString: function StatTimer_toString() {
  1010       var i, ii;
  1011       var times = this.times;
  1012       var out = '';
  1013       // Find the longest name for padding purposes.
  1014       var longest = 0;
  1015       for (i = 0, ii = times.length; i < ii; ++i) {
  1016         var name = times[i]['name'];
  1017         if (name.length > longest) {
  1018           longest = name.length;
  1021       for (i = 0, ii = times.length; i < ii; ++i) {
  1022         var span = times[i];
  1023         var duration = span.end - span.start;
  1024         out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
  1026       return out;
  1028   };
  1029   return StatTimer;
  1030 })();
  1032 PDFJS.createBlob = function createBlob(data, contentType) {
  1033   if (typeof Blob !== 'undefined') {
  1034     return new Blob([data], { type: contentType });
  1036   // Blob builder is deprecated in FF14 and removed in FF18.
  1037   var bb = new MozBlobBuilder();
  1038   bb.append(data);
  1039   return bb.getBlob(contentType);
  1040 };
  1042 PDFJS.createObjectURL = (function createObjectURLClosure() {
  1043   // Blob/createObjectURL is not available, falling back to data schema.
  1044   var digits =
  1045     'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  1047   return function createObjectURL(data, contentType) {
  1048     if (!PDFJS.disableCreateObjectURL &&
  1049         typeof URL !== 'undefined' && URL.createObjectURL) {
  1050       var blob = PDFJS.createBlob(data, contentType);
  1051       return URL.createObjectURL(blob);
  1054     var buffer = 'data:' + contentType + ';base64,';
  1055     for (var i = 0, ii = data.length; i < ii; i += 3) {
  1056       var b1 = data[i] & 0xFF;
  1057       var b2 = data[i + 1] & 0xFF;
  1058       var b3 = data[i + 2] & 0xFF;
  1059       var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
  1060       var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
  1061       var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
  1062       buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
  1064     return buffer;
  1065   };
  1066 })();
  1068 function MessageHandler(name, comObj) {
  1069   this.name = name;
  1070   this.comObj = comObj;
  1071   this.callbackIndex = 1;
  1072   this.postMessageTransfers = true;
  1073   var callbacks = this.callbacks = {};
  1074   var ah = this.actionHandler = {};
  1076   ah['console_log'] = [function ahConsoleLog(data) {
  1077     console.log.apply(console, data);
  1078   }];
  1079   ah['console_error'] = [function ahConsoleError(data) {
  1080     console.error.apply(console, data);
  1081   }];
  1082   ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) {
  1083     UnsupportedManager.notify(data);
  1084   }];
  1086   comObj.onmessage = function messageHandlerComObjOnMessage(event) {
  1087     var data = event.data;
  1088     if (data.isReply) {
  1089       var callbackId = data.callbackId;
  1090       if (data.callbackId in callbacks) {
  1091         var callback = callbacks[callbackId];
  1092         delete callbacks[callbackId];
  1093         callback(data.data);
  1094       } else {
  1095         error('Cannot resolve callback ' + callbackId);
  1097     } else if (data.action in ah) {
  1098       var action = ah[data.action];
  1099       if (data.callbackId) {
  1100         var deferred = {};
  1101         var promise = new Promise(function (resolve, reject) {
  1102           deferred.resolve = resolve;
  1103           deferred.reject = reject;
  1104         });
  1105         deferred.promise = promise;
  1106         promise.then(function(resolvedData) {
  1107           comObj.postMessage({
  1108             isReply: true,
  1109             callbackId: data.callbackId,
  1110             data: resolvedData
  1111           });
  1112         });
  1113         action[0].call(action[1], data.data, deferred);
  1114       } else {
  1115         action[0].call(action[1], data.data);
  1117     } else {
  1118       error('Unkown action from worker: ' + data.action);
  1120   };
  1123 MessageHandler.prototype = {
  1124   on: function messageHandlerOn(actionName, handler, scope) {
  1125     var ah = this.actionHandler;
  1126     if (ah[actionName]) {
  1127       error('There is already an actionName called "' + actionName + '"');
  1129     ah[actionName] = [handler, scope];
  1130   },
  1131   /**
  1132    * Sends a message to the comObj to invoke the action with the supplied data.
  1133    * @param {String} actionName Action to call.
  1134    * @param {JSON} data JSON data to send.
  1135    * @param {function} [callback] Optional callback that will handle a reply.
  1136    * @param {Array} [transfers] Optional list of transfers/ArrayBuffers
  1137    */
  1138   send: function messageHandlerSend(actionName, data, callback, transfers) {
  1139     var message = {
  1140       action: actionName,
  1141       data: data
  1142     };
  1143     if (callback) {
  1144       var callbackId = this.callbackIndex++;
  1145       this.callbacks[callbackId] = callback;
  1146       message.callbackId = callbackId;
  1148     if (transfers && this.postMessageTransfers) {
  1149       this.comObj.postMessage(message, transfers);
  1150     } else {
  1151       this.comObj.postMessage(message);
  1154 };
  1156 function loadJpegStream(id, imageUrl, objs) {
  1157   var img = new Image();
  1158   img.onload = (function loadJpegStream_onloadClosure() {
  1159     objs.resolve(id, img);
  1160   });
  1161   img.src = imageUrl;
  1165 var ColorSpace = (function ColorSpaceClosure() {
  1166   // Constructor should define this.numComps, this.defaultColor, this.name
  1167   function ColorSpace() {
  1168     error('should not call ColorSpace constructor');
  1171   ColorSpace.prototype = {
  1172     /**
  1173      * Converts the color value to the RGB color. The color components are
  1174      * located in the src array starting from the srcOffset. Returns the array
  1175      * of the rgb components, each value ranging from [0,255].
  1176      */
  1177     getRgb: function ColorSpace_getRgb(src, srcOffset) {
  1178       var rgb = new Uint8Array(3);
  1179       this.getRgbItem(src, srcOffset, rgb, 0);
  1180       return rgb;
  1181     },
  1182     /**
  1183      * Converts the color value to the RGB color, similar to the getRgb method.
  1184      * The result placed into the dest array starting from the destOffset.
  1185      */
  1186     getRgbItem: function ColorSpace_getRgbItem(src, srcOffset,
  1187                                                dest, destOffset) {
  1188       error('Should not call ColorSpace.getRgbItem');
  1189     },
  1190     /**
  1191      * Converts the specified number of the color values to the RGB colors.
  1192      * The colors are located in the src array starting from the srcOffset.
  1193      * The result is placed into the dest array starting from the destOffset.
  1194      * The src array items shall be in [0,2^bits) range, the dest array items
  1195      * will be in [0,255] range. alpha01 indicates how many alpha components
  1196      * there are in the dest array; it will be either 0 (RGB array) or 1 (RGBA
  1197      * array).
  1198      */
  1199     getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count,
  1200                                                    dest, destOffset, bits,
  1201                                                    alpha01) {
  1202       error('Should not call ColorSpace.getRgbBuffer');
  1203     },
  1204     /**
  1205      * Determines the number of bytes required to store the result of the
  1206      * conversion done by the getRgbBuffer method. As in getRgbBuffer,
  1207      * |alpha01| is either 0 (RGB output) or 1 (RGBA output).
  1208      */
  1209     getOutputLength: function ColorSpace_getOutputLength(inputLength,
  1210                                                          alpha01) {
  1211       error('Should not call ColorSpace.getOutputLength');
  1212     },
  1213     /**
  1214      * Returns true if source data will be equal the result/output data.
  1215      */
  1216     isPassthrough: function ColorSpace_isPassthrough(bits) {
  1217       return false;
  1218     },
  1219     /**
  1220      * Fills in the RGB colors in the destination buffer.  alpha01 indicates
  1221      * how many alpha components there are in the dest array; it will be either
  1222      * 0 (RGB array) or 1 (RGBA array).
  1223      */
  1224     fillRgb: function ColorSpace_fillRgb(dest, originalWidth,
  1225                                          originalHeight, width, height,
  1226                                          actualHeight, bpc, comps, alpha01) {
  1227       var count = originalWidth * originalHeight;
  1228       var rgbBuf = null;
  1229       var numComponentColors = 1 << bpc;
  1230       var needsResizing = originalHeight != height || originalWidth != width;
  1231       var i, ii;
  1233       if (this.isPassthrough(bpc)) {
  1234         rgbBuf = comps;
  1235       } else if (this.numComps === 1 && count > numComponentColors &&
  1236           this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
  1237         // Optimization: create a color map when there is just one component and
  1238         // we are converting more colors than the size of the color map. We
  1239         // don't build the map if the colorspace is gray or rgb since those
  1240         // methods are faster than building a map. This mainly offers big speed
  1241         // ups for indexed and alternate colorspaces.
  1242         //
  1243         // TODO it may be worth while to cache the color map. While running
  1244         // testing I never hit a cache so I will leave that out for now (perhaps
  1245         // we are reparsing colorspaces too much?).
  1246         var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) :
  1247                                    new Uint16Array(numComponentColors);
  1248         var key;
  1249         for (i = 0; i < numComponentColors; i++) {
  1250           allColors[i] = i;
  1252         var colorMap = new Uint8Array(numComponentColors * 3);
  1253         this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc,
  1254                           /* alpha01 = */ 0);
  1256         var destPos, rgbPos;
  1257         if (!needsResizing) {
  1258           // Fill in the RGB values directly into |dest|.
  1259           destPos = 0;
  1260           for (i = 0; i < count; ++i) {
  1261             key = comps[i] * 3;
  1262             dest[destPos++] = colorMap[key];
  1263             dest[destPos++] = colorMap[key + 1];
  1264             dest[destPos++] = colorMap[key + 2];
  1265             destPos += alpha01;
  1267         } else {
  1268           rgbBuf = new Uint8Array(count * 3);
  1269           rgbPos = 0;
  1270           for (i = 0; i < count; ++i) {
  1271             key = comps[i] * 3;
  1272             rgbBuf[rgbPos++] = colorMap[key];
  1273             rgbBuf[rgbPos++] = colorMap[key + 1];
  1274             rgbBuf[rgbPos++] = colorMap[key + 2];
  1277       } else {
  1278         if (!needsResizing) {
  1279           // Fill in the RGB values directly into |dest|.
  1280           this.getRgbBuffer(comps, 0, width * actualHeight, dest, 0, bpc,
  1281                             alpha01);
  1282         } else {
  1283           rgbBuf = new Uint8Array(count * 3);
  1284           this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc,
  1285                             /* alpha01 = */ 0);
  1289       if (rgbBuf) {
  1290         if (needsResizing) {
  1291           rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth,
  1292                                    originalHeight, width, height);
  1294         rgbPos = 0;
  1295         destPos = 0;
  1296         for (i = 0, ii = width * actualHeight; i < ii; i++) {
  1297           dest[destPos++] = rgbBuf[rgbPos++];
  1298           dest[destPos++] = rgbBuf[rgbPos++];
  1299           dest[destPos++] = rgbBuf[rgbPos++];
  1300           destPos += alpha01;
  1303     },
  1304     /**
  1305      * True if the colorspace has components in the default range of [0, 1].
  1306      * This should be true for all colorspaces except for lab color spaces
  1307      * which are [0,100], [-128, 127], [-128, 127].
  1308      */
  1309     usesZeroToOneRange: true
  1310   };
  1312   ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
  1313     var IR = ColorSpace.parseToIR(cs, xref, res);
  1314     if (IR instanceof AlternateCS) {
  1315       return IR;
  1317     return ColorSpace.fromIR(IR);
  1318   };
  1320   ColorSpace.fromIR = function ColorSpace_fromIR(IR) {
  1321     var name = isArray(IR) ? IR[0] : IR;
  1322     var whitePoint, blackPoint;
  1324     switch (name) {
  1325       case 'DeviceGrayCS':
  1326         return this.singletons.gray;
  1327       case 'DeviceRgbCS':
  1328         return this.singletons.rgb;
  1329       case 'DeviceCmykCS':
  1330         return this.singletons.cmyk;
  1331       case 'CalGrayCS':
  1332         whitePoint = IR[1].WhitePoint;
  1333         blackPoint = IR[1].BlackPoint;
  1334         var gamma = IR[1].Gamma;
  1335         return new CalGrayCS(whitePoint, blackPoint, gamma);
  1336       case 'PatternCS':
  1337         var basePatternCS = IR[1];
  1338         if (basePatternCS) {
  1339           basePatternCS = ColorSpace.fromIR(basePatternCS);
  1341         return new PatternCS(basePatternCS);
  1342       case 'IndexedCS':
  1343         var baseIndexedCS = IR[1];
  1344         var hiVal = IR[2];
  1345         var lookup = IR[3];
  1346         return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
  1347       case 'AlternateCS':
  1348         var numComps = IR[1];
  1349         var alt = IR[2];
  1350         var tintFnIR = IR[3];
  1352         return new AlternateCS(numComps, ColorSpace.fromIR(alt),
  1353                                 PDFFunction.fromIR(tintFnIR));
  1354       case 'LabCS':
  1355         whitePoint = IR[1].WhitePoint;
  1356         blackPoint = IR[1].BlackPoint;
  1357         var range = IR[1].Range;
  1358         return new LabCS(whitePoint, blackPoint, range);
  1359       default:
  1360         error('Unkown name ' + name);
  1362     return null;
  1363   };
  1365   ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) {
  1366     if (isName(cs)) {
  1367       var colorSpaces = res.get('ColorSpace');
  1368       if (isDict(colorSpaces)) {
  1369         var refcs = colorSpaces.get(cs.name);
  1370         if (refcs) {
  1371           cs = refcs;
  1376     cs = xref.fetchIfRef(cs);
  1377     var mode;
  1379     if (isName(cs)) {
  1380       mode = cs.name;
  1381       this.mode = mode;
  1383       switch (mode) {
  1384         case 'DeviceGray':
  1385         case 'G':
  1386           return 'DeviceGrayCS';
  1387         case 'DeviceRGB':
  1388         case 'RGB':
  1389           return 'DeviceRgbCS';
  1390         case 'DeviceCMYK':
  1391         case 'CMYK':
  1392           return 'DeviceCmykCS';
  1393         case 'Pattern':
  1394           return ['PatternCS', null];
  1395         default:
  1396           error('unrecognized colorspace ' + mode);
  1398     } else if (isArray(cs)) {
  1399       mode = cs[0].name;
  1400       this.mode = mode;
  1401       var numComps, params;
  1403       switch (mode) {
  1404         case 'DeviceGray':
  1405         case 'G':
  1406           return 'DeviceGrayCS';
  1407         case 'DeviceRGB':
  1408         case 'RGB':
  1409           return 'DeviceRgbCS';
  1410         case 'DeviceCMYK':
  1411         case 'CMYK':
  1412           return 'DeviceCmykCS';
  1413         case 'CalGray':
  1414           params = cs[1].getAll();
  1415           return ['CalGrayCS', params];
  1416         case 'CalRGB':
  1417           return 'DeviceRgbCS';
  1418         case 'ICCBased':
  1419           var stream = xref.fetchIfRef(cs[1]);
  1420           var dict = stream.dict;
  1421           numComps = dict.get('N');
  1422           if (numComps == 1) {
  1423             return 'DeviceGrayCS';
  1424           } else if (numComps == 3) {
  1425             return 'DeviceRgbCS';
  1426           } else if (numComps == 4) {
  1427             return 'DeviceCmykCS';
  1429           break;
  1430         case 'Pattern':
  1431           var basePatternCS = cs[1];
  1432           if (basePatternCS) {
  1433             basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
  1435           return ['PatternCS', basePatternCS];
  1436         case 'Indexed':
  1437         case 'I':
  1438           var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
  1439           var hiVal = cs[2] + 1;
  1440           var lookup = xref.fetchIfRef(cs[3]);
  1441           if (isStream(lookup)) {
  1442             lookup = lookup.getBytes();
  1444           return ['IndexedCS', baseIndexedCS, hiVal, lookup];
  1445         case 'Separation':
  1446         case 'DeviceN':
  1447           var name = cs[1];
  1448           numComps = 1;
  1449           if (isName(name)) {
  1450             numComps = 1;
  1451           } else if (isArray(name)) {
  1452             numComps = name.length;
  1454           var alt = ColorSpace.parseToIR(cs[2], xref, res);
  1455           var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
  1456           return ['AlternateCS', numComps, alt, tintFnIR];
  1457         case 'Lab':
  1458           params = cs[1].getAll();
  1459           return ['LabCS', params];
  1460         default:
  1461           error('unimplemented color space object "' + mode + '"');
  1463     } else {
  1464       error('unrecognized color space object: "' + cs + '"');
  1466     return null;
  1467   };
  1468   /**
  1469    * Checks if a decode map matches the default decode map for a color space.
  1470    * This handles the general decode maps where there are two values per
  1471    * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color.
  1472    * This does not handle Lab, Indexed, or Pattern decode maps since they are
  1473    * slightly different.
  1474    * @param {Array} decode Decode map (usually from an image).
  1475    * @param {Number} n Number of components the color space has.
  1476    */
  1477   ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
  1478     if (!decode) {
  1479       return true;
  1482     if (n * 2 !== decode.length) {
  1483       warn('The decode map is not the correct length');
  1484       return true;
  1486     for (var i = 0, ii = decode.length; i < ii; i += 2) {
  1487       if (decode[i] !== 0 || decode[i + 1] != 1) {
  1488         return false;
  1491     return true;
  1492   };
  1494   ColorSpace.singletons = {
  1495     get gray() {
  1496       return shadow(this, 'gray', new DeviceGrayCS());
  1497     },
  1498     get rgb() {
  1499       return shadow(this, 'rgb', new DeviceRgbCS());
  1500     },
  1501     get cmyk() {
  1502       return shadow(this, 'cmyk', new DeviceCmykCS());
  1504   };
  1506   return ColorSpace;
  1507 })();
  1509 /**
  1510  * Alternate color space handles both Separation and DeviceN color spaces.  A
  1511  * Separation color space is actually just a DeviceN with one color component.
  1512  * Both color spaces use a tinting function to convert colors to a base color
  1513  * space.
  1514  */
  1515 var AlternateCS = (function AlternateCSClosure() {
  1516   function AlternateCS(numComps, base, tintFn) {
  1517     this.name = 'Alternate';
  1518     this.numComps = numComps;
  1519     this.defaultColor = new Float32Array(numComps);
  1520     for (var i = 0; i < numComps; ++i) {
  1521       this.defaultColor[i] = 1;
  1523     this.base = base;
  1524     this.tintFn = tintFn;
  1527   AlternateCS.prototype = {
  1528     getRgb: ColorSpace.prototype.getRgb,
  1529     getRgbItem: function AlternateCS_getRgbItem(src, srcOffset,
  1530                                                 dest, destOffset) {
  1531       var baseNumComps = this.base.numComps;
  1532       var input = 'subarray' in src ?
  1533         src.subarray(srcOffset, srcOffset + this.numComps) :
  1534         Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps);
  1535       var tinted = this.tintFn(input);
  1536       this.base.getRgbItem(tinted, 0, dest, destOffset);
  1537     },
  1538     getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count,
  1539                                                     dest, destOffset, bits,
  1540                                                     alpha01) {
  1541       var tintFn = this.tintFn;
  1542       var base = this.base;
  1543       var scale = 1 / ((1 << bits) - 1);
  1544       var baseNumComps = base.numComps;
  1545       var usesZeroToOneRange = base.usesZeroToOneRange;
  1546       var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) &&
  1547                           alpha01 === 0;
  1548       var pos = isPassthrough ? destOffset : 0;
  1549       var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count);
  1550       var numComps = this.numComps;
  1552       var scaled = new Float32Array(numComps);
  1553       var i, j;
  1554       for (i = 0; i < count; i++) {
  1555         for (j = 0; j < numComps; j++) {
  1556           scaled[j] = src[srcOffset++] * scale;
  1558         var tinted = tintFn(scaled);
  1559         if (usesZeroToOneRange) {
  1560           for (j = 0; j < baseNumComps; j++) {
  1561             baseBuf[pos++] = tinted[j] * 255;
  1563         } else {
  1564           base.getRgbItem(tinted, 0, baseBuf, pos);
  1565           pos += baseNumComps;
  1568       if (!isPassthrough) {
  1569         base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01);
  1571     },
  1572     getOutputLength: function AlternateCS_getOutputLength(inputLength,
  1573                                                           alpha01) {
  1574       return this.base.getOutputLength(inputLength *
  1575                                        this.base.numComps / this.numComps,
  1576                                        alpha01);
  1577     },
  1578     isPassthrough: ColorSpace.prototype.isPassthrough,
  1579     fillRgb: ColorSpace.prototype.fillRgb,
  1580     isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
  1581       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
  1582     },
  1583     usesZeroToOneRange: true
  1584   };
  1586   return AlternateCS;
  1587 })();
  1589 var PatternCS = (function PatternCSClosure() {
  1590   function PatternCS(baseCS) {
  1591     this.name = 'Pattern';
  1592     this.base = baseCS;
  1594   PatternCS.prototype = {};
  1596   return PatternCS;
  1597 })();
  1599 var IndexedCS = (function IndexedCSClosure() {
  1600   function IndexedCS(base, highVal, lookup) {
  1601     this.name = 'Indexed';
  1602     this.numComps = 1;
  1603     this.defaultColor = new Uint8Array([0]);
  1604     this.base = base;
  1605     this.highVal = highVal;
  1607     var baseNumComps = base.numComps;
  1608     var length = baseNumComps * highVal;
  1609     var lookupArray;
  1611     if (isStream(lookup)) {
  1612       lookupArray = new Uint8Array(length);
  1613       var bytes = lookup.getBytes(length);
  1614       lookupArray.set(bytes);
  1615     } else if (isString(lookup)) {
  1616       lookupArray = new Uint8Array(length);
  1617       for (var i = 0; i < length; ++i) {
  1618         lookupArray[i] = lookup.charCodeAt(i);
  1620     } else if (lookup instanceof Uint8Array || lookup instanceof Array) {
  1621       lookupArray = lookup;
  1622     } else {
  1623       error('Unrecognized lookup table: ' + lookup);
  1625     this.lookup = lookupArray;
  1628   IndexedCS.prototype = {
  1629     getRgb: ColorSpace.prototype.getRgb,
  1630     getRgbItem: function IndexedCS_getRgbItem(src, srcOffset,
  1631                                               dest, destOffset) {
  1632       var numComps = this.base.numComps;
  1633       var start = src[srcOffset] * numComps;
  1634       this.base.getRgbItem(this.lookup, start, dest, destOffset);
  1635     },
  1636     getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count,
  1637                                                   dest, destOffset, bits,
  1638                                                   alpha01) {
  1639       var base = this.base;
  1640       var numComps = base.numComps;
  1641       var outputDelta = base.getOutputLength(numComps, alpha01);
  1642       var lookup = this.lookup;
  1644       for (var i = 0; i < count; ++i) {
  1645         var lookupPos = src[srcOffset++] * numComps;
  1646         base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01);
  1647         destOffset += outputDelta;
  1649     },
  1650     getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) {
  1651       return this.base.getOutputLength(inputLength * this.base.numComps,
  1652                                        alpha01);
  1653     },
  1654     isPassthrough: ColorSpace.prototype.isPassthrough,
  1655     fillRgb: ColorSpace.prototype.fillRgb,
  1656     isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
  1657       // indexed color maps shouldn't be changed
  1658       return true;
  1659     },
  1660     usesZeroToOneRange: true
  1661   };
  1662   return IndexedCS;
  1663 })();
  1665 var DeviceGrayCS = (function DeviceGrayCSClosure() {
  1666   function DeviceGrayCS() {
  1667     this.name = 'DeviceGray';
  1668     this.numComps = 1;
  1669     this.defaultColor = new Float32Array([0]);
  1672   DeviceGrayCS.prototype = {
  1673     getRgb: ColorSpace.prototype.getRgb,
  1674     getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset,
  1675                                                  dest, destOffset) {
  1676       var c = (src[srcOffset] * 255) | 0;
  1677       c = c < 0 ? 0 : c > 255 ? 255 : c;
  1678       dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
  1679     },
  1680     getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count,
  1681                                                      dest, destOffset, bits,
  1682                                                      alpha01) {
  1683       var scale = 255 / ((1 << bits) - 1);
  1684       var j = srcOffset, q = destOffset;
  1685       for (var i = 0; i < count; ++i) {
  1686         var c = (scale * src[j++]) | 0;
  1687         dest[q++] = c;
  1688         dest[q++] = c;
  1689         dest[q++] = c;
  1690         q += alpha01;
  1692     },
  1693     getOutputLength: function DeviceGrayCS_getOutputLength(inputLength,
  1694                                                            alpha01) {
  1695       return inputLength * (3 + alpha01);
  1696     },
  1697     isPassthrough: ColorSpace.prototype.isPassthrough,
  1698     fillRgb: ColorSpace.prototype.fillRgb,
  1699     isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
  1700       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
  1701     },
  1702     usesZeroToOneRange: true
  1703   };
  1704   return DeviceGrayCS;
  1705 })();
  1707 var DeviceRgbCS = (function DeviceRgbCSClosure() {
  1708   function DeviceRgbCS() {
  1709     this.name = 'DeviceRGB';
  1710     this.numComps = 3;
  1711     this.defaultColor = new Float32Array([0, 0, 0]);
  1713   DeviceRgbCS.prototype = {
  1714     getRgb: ColorSpace.prototype.getRgb,
  1715     getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset,
  1716                                                 dest, destOffset) {
  1717       var r = (src[srcOffset] * 255) | 0;
  1718       var g = (src[srcOffset + 1] * 255) | 0;
  1719       var b = (src[srcOffset + 2] * 255) | 0;
  1720       dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
  1721       dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
  1722       dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
  1723     },
  1724     getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count,
  1725                                                     dest, destOffset, bits,
  1726                                                     alpha01) {
  1727       if (bits === 8 && alpha01 === 0) {
  1728         dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset);
  1729         return;
  1731       var scale = 255 / ((1 << bits) - 1);
  1732       var j = srcOffset, q = destOffset;
  1733       for (var i = 0; i < count; ++i) {
  1734         dest[q++] = (scale * src[j++]) | 0;
  1735         dest[q++] = (scale * src[j++]) | 0;
  1736         dest[q++] = (scale * src[j++]) | 0;
  1737         q += alpha01;
  1739     },
  1740     getOutputLength: function DeviceRgbCS_getOutputLength(inputLength,
  1741                                                           alpha01) {
  1742       return (inputLength * (3 + alpha01) / 3) | 0;
  1743     },
  1744     isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
  1745       return bits == 8;
  1746     },
  1747     fillRgb: ColorSpace.prototype.fillRgb,
  1748     isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
  1749       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
  1750     },
  1751     usesZeroToOneRange: true
  1752   };
  1753   return DeviceRgbCS;
  1754 })();
  1756 var DeviceCmykCS = (function DeviceCmykCSClosure() {
  1757   // The coefficients below was found using numerical analysis: the method of
  1758   // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors,
  1759   // where color_value is the tabular value from the table of sampled RGB colors
  1760   // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding
  1761   // CMYK color conversion using the estimation below:
  1762   //   f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255
  1763   function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
  1764     var c = src[srcOffset + 0] * srcScale;
  1765     var m = src[srcOffset + 1] * srcScale;
  1766     var y = src[srcOffset + 2] * srcScale;
  1767     var k = src[srcOffset + 3] * srcScale;
  1769     var r =
  1770       (c * (-4.387332384609988 * c + 54.48615194189176 * m +
  1771             18.82290502165302 * y + 212.25662451639585 * k +
  1772             -285.2331026137004) +
  1773        m * (1.7149763477362134 * m - 5.6096736904047315 * y +
  1774             -17.873870861415444 * k - 5.497006427196366) +
  1775        y * (-2.5217340131683033 * y - 21.248923337353073 * k +
  1776             17.5119270841813) +
  1777        k * (-21.86122147463605 * k - 189.48180835922747) + 255) | 0;
  1778     var g =
  1779       (c * (8.841041422036149 * c + 60.118027045597366 * m +
  1780             6.871425592049007 * y + 31.159100130055922 * k +
  1781             -79.2970844816548) +
  1782        m * (-15.310361306967817 * m + 17.575251261109482 * y +
  1783             131.35250912493976 * k - 190.9453302588951) +
  1784        y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
  1785        k * (-20.737325471181034 * k - 187.80453709719578) + 255) | 0;
  1786     var b =
  1787       (c * (0.8842522430003296 * c + 8.078677503112928 * m +
  1788             30.89978309703729 * y - 0.23883238689178934 * k +
  1789             -14.183576799673286) +
  1790        m * (10.49593273432072 * m + 63.02378494754052 * y +
  1791             50.606957656360734 * k - 112.23884253719248) +
  1792        y * (0.03296041114873217 * y + 115.60384449646641 * k +
  1793             -193.58209356861505) +
  1794        k * (-22.33816807309886 * k - 180.12613974708367) + 255) | 0;
  1796     dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
  1797     dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
  1798     dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
  1801   function DeviceCmykCS() {
  1802     this.name = 'DeviceCMYK';
  1803     this.numComps = 4;
  1804     this.defaultColor = new Float32Array([0, 0, 0, 1]);
  1806   DeviceCmykCS.prototype = {
  1807     getRgb: ColorSpace.prototype.getRgb,
  1808     getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset,
  1809                                                  dest, destOffset) {
  1810       convertToRgb(src, srcOffset, 1, dest, destOffset);
  1811     },
  1812     getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count,
  1813                                                      dest, destOffset, bits,
  1814                                                      alpha01) {
  1815       var scale = 1 / ((1 << bits) - 1);
  1816       for (var i = 0; i < count; i++) {
  1817         convertToRgb(src, srcOffset, scale, dest, destOffset);
  1818         srcOffset += 4;
  1819         destOffset += 3 + alpha01;
  1821     },
  1822     getOutputLength: function DeviceCmykCS_getOutputLength(inputLength,
  1823                                                            alpha01) {
  1824       return (inputLength / 4 * (3 + alpha01)) | 0;
  1825     },
  1826     isPassthrough: ColorSpace.prototype.isPassthrough,
  1827     fillRgb: ColorSpace.prototype.fillRgb,
  1828     isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
  1829       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
  1830     },
  1831     usesZeroToOneRange: true
  1832   };
  1834   return DeviceCmykCS;
  1835 })();
  1837 //
  1838 // CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245
  1839 //
  1840 var CalGrayCS = (function CalGrayCSClosure() {
  1841   function CalGrayCS(whitePoint, blackPoint, gamma) {
  1842     this.name = 'CalGray';
  1843     this.numComps = 1;
  1844     this.defaultColor = new Float32Array([0]);
  1846     if (!whitePoint) {
  1847       error('WhitePoint missing - required for color space CalGray');
  1849     blackPoint = blackPoint || [0, 0, 0];
  1850     gamma = gamma || 1;
  1852     // Translate arguments to spec variables.
  1853     this.XW = whitePoint[0];
  1854     this.YW = whitePoint[1];
  1855     this.ZW = whitePoint[2];
  1857     this.XB = blackPoint[0];
  1858     this.YB = blackPoint[1];
  1859     this.ZB = blackPoint[2];
  1861     this.G = gamma;
  1863     // Validate variables as per spec.
  1864     if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
  1865       error('Invalid WhitePoint components for ' + this.name +
  1866             ', no fallback available');
  1869     if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
  1870       info('Invalid BlackPoint for ' + this.name + ', falling back to default');
  1871       this.XB = this.YB = this.ZB = 0;
  1874     if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) {
  1875       warn(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB +
  1876            ', ZB: ' + this.ZB + ', only default values are supported.');
  1879     if (this.G < 1) {
  1880       info('Invalid Gamma: ' + this.G + ' for ' + this.name +
  1881            ', falling back to default');
  1882       this.G = 1;
  1886   function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) {
  1887     // A represents a gray component of a calibrated gray space.
  1888     // A <---> AG in the spec
  1889     var A = src[srcOffset] * scale;
  1890     var AG = Math.pow(A, cs.G);
  1892     // Computes intermediate variables M, L, N as per spec.
  1893     // Except if other than default BlackPoint values are used.
  1894     var M = cs.XW * AG;
  1895     var L = cs.YW * AG;
  1896     var N = cs.ZW * AG;
  1898     // Decode XYZ, as per spec.
  1899     var X = M;
  1900     var Y = L;
  1901     var Z = N;
  1903     // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4.
  1904     // This yields values in range [0, 100].
  1905     var Lstar = Math.max(116 * Math.pow(Y, 1 / 3) - 16, 0);
  1907     // Convert values to rgb range [0, 255].
  1908     dest[destOffset] = Lstar * 255 / 100;
  1909     dest[destOffset + 1] = Lstar * 255 / 100;
  1910     dest[destOffset + 2] = Lstar * 255 / 100;
  1913   CalGrayCS.prototype = {
  1914     getRgb: ColorSpace.prototype.getRgb,
  1915     getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset,
  1916                                               dest, destOffset) {
  1917       convertToRgb(this, src, srcOffset, dest, destOffset, 1);
  1918     },
  1919     getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count,
  1920                                                   dest, destOffset, bits,
  1921                                                   alpha01) {
  1922       var scale = 1 / ((1 << bits) - 1);
  1924       for (var i = 0; i < count; ++i) {
  1925         convertToRgb(this, src, srcOffset, dest, destOffset, scale);
  1926         srcOffset += 1;
  1927         destOffset += 3 + alpha01;
  1929     },
  1930     getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) {
  1931       return inputLength * (3 + alpha01);
  1932     },
  1933     isPassthrough: ColorSpace.prototype.isPassthrough,
  1934     fillRgb: ColorSpace.prototype.fillRgb,
  1935     isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) {
  1936       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
  1937     },
  1938     usesZeroToOneRange: true
  1939   };
  1940   return CalGrayCS;
  1941 })();
  1943 //
  1944 // LabCS: Based on "PDF Reference, Sixth Ed", p.250
  1945 //
  1946 var LabCS = (function LabCSClosure() {
  1947   function LabCS(whitePoint, blackPoint, range) {
  1948     this.name = 'Lab';
  1949     this.numComps = 3;
  1950     this.defaultColor = new Float32Array([0, 0, 0]);
  1952     if (!whitePoint) {
  1953       error('WhitePoint missing - required for color space Lab');
  1955     blackPoint = blackPoint || [0, 0, 0];
  1956     range = range || [-100, 100, -100, 100];
  1958     // Translate args to spec variables
  1959     this.XW = whitePoint[0];
  1960     this.YW = whitePoint[1];
  1961     this.ZW = whitePoint[2];
  1962     this.amin = range[0];
  1963     this.amax = range[1];
  1964     this.bmin = range[2];
  1965     this.bmax = range[3];
  1967     // These are here just for completeness - the spec doesn't offer any
  1968     // formulas that use BlackPoint in Lab
  1969     this.XB = blackPoint[0];
  1970     this.YB = blackPoint[1];
  1971     this.ZB = blackPoint[2];
  1973     // Validate vars as per spec
  1974     if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
  1975       error('Invalid WhitePoint components, no fallback available');
  1978     if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
  1979       info('Invalid BlackPoint, falling back to default');
  1980       this.XB = this.YB = this.ZB = 0;
  1983     if (this.amin > this.amax || this.bmin > this.bmax) {
  1984       info('Invalid Range, falling back to defaults');
  1985       this.amin = -100;
  1986       this.amax = 100;
  1987       this.bmin = -100;
  1988       this.bmax = 100;
  1992   // Function g(x) from spec
  1993   function fn_g(x) {
  1994     if (x >= 6 / 29) {
  1995       return x * x * x;
  1996     } else {
  1997       return (108 / 841) * (x - 4 / 29);
  2001   function decode(value, high1, low2, high2) {
  2002     return low2 + (value) * (high2 - low2) / (high1);
  2005   // If decoding is needed maxVal should be 2^bits per component - 1.
  2006   function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) {
  2007     // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax]
  2008     // not the usual [0, 1]. If a command like setFillColor is used the src
  2009     // values will already be within the correct range. However, if we are
  2010     // converting an image we have to map the values to the correct range given
  2011     // above.
  2012     // Ls,as,bs <---> L*,a*,b* in the spec
  2013     var Ls = src[srcOffset];
  2014     var as = src[srcOffset + 1];
  2015     var bs = src[srcOffset + 2];
  2016     if (maxVal !== false) {
  2017       Ls = decode(Ls, maxVal, 0, 100);
  2018       as = decode(as, maxVal, cs.amin, cs.amax);
  2019       bs = decode(bs, maxVal, cs.bmin, cs.bmax);
  2022     // Adjust limits of 'as' and 'bs'
  2023     as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as;
  2024     bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs;
  2026     // Computes intermediate variables X,Y,Z as per spec
  2027     var M = (Ls + 16) / 116;
  2028     var L = M + (as / 500);
  2029     var N = M - (bs / 200);
  2031     var X = cs.XW * fn_g(L);
  2032     var Y = cs.YW * fn_g(M);
  2033     var Z = cs.ZW * fn_g(N);
  2035     var r, g, b;
  2036     // Using different conversions for D50 and D65 white points,
  2037     // per http://www.color.org/srgb.pdf
  2038     if (cs.ZW < 1) {
  2039       // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249)
  2040       r = X * 3.1339 + Y * -1.6170 + Z * -0.4906;
  2041       g = X * -0.9785 + Y * 1.9160 + Z * 0.0333;
  2042       b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
  2043     } else {
  2044       // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888)
  2045       r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
  2046       g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
  2047       b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
  2049     // clamp color values to [0,1] range then convert to [0,255] range.
  2050     dest[destOffset] = r <= 0 ? 0 : r >= 1 ? 255 : Math.sqrt(r) * 255 | 0;
  2051     dest[destOffset + 1] = g <= 0 ? 0 : g >= 1 ? 255 : Math.sqrt(g) * 255 | 0;
  2052     dest[destOffset + 2] = b <= 0 ? 0 : b >= 1 ? 255 : Math.sqrt(b) * 255 | 0;
  2055   LabCS.prototype = {
  2056     getRgb: ColorSpace.prototype.getRgb,
  2057     getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
  2058       convertToRgb(this, src, srcOffset, false, dest, destOffset);
  2059     },
  2060     getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count,
  2061                                               dest, destOffset, bits,
  2062                                               alpha01) {
  2063       var maxVal = (1 << bits) - 1;
  2064       for (var i = 0; i < count; i++) {
  2065         convertToRgb(this, src, srcOffset, maxVal, dest, destOffset);
  2066         srcOffset += 3;
  2067         destOffset += 3 + alpha01;
  2069     },
  2070     getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) {
  2071       return (inputLength * (3 + alpha01) / 3) | 0;
  2072     },
  2073     isPassthrough: ColorSpace.prototype.isPassthrough,
  2074     isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
  2075       // XXX: Decoding is handled with the lab conversion because of the strange
  2076       // ranges that are used.
  2077       return true;
  2078     },
  2079     usesZeroToOneRange: false
  2080   };
  2081   return LabCS;
  2082 })();
  2086 var PDFFunction = (function PDFFunctionClosure() {
  2087   var CONSTRUCT_SAMPLED = 0;
  2088   var CONSTRUCT_INTERPOLATED = 2;
  2089   var CONSTRUCT_STICHED = 3;
  2090   var CONSTRUCT_POSTSCRIPT = 4;
  2092   return {
  2093     getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps,
  2094                                                        str) {
  2095       var i, ii;
  2096       var length = 1;
  2097       for (i = 0, ii = size.length; i < ii; i++) {
  2098         length *= size[i];
  2100       length *= outputSize;
  2102       var array = [];
  2103       var codeSize = 0;
  2104       var codeBuf = 0;
  2105       // 32 is a valid bps so shifting won't work
  2106       var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1);
  2108       var strBytes = str.getBytes((length * bps + 7) / 8);
  2109       var strIdx = 0;
  2110       for (i = 0; i < length; i++) {
  2111         while (codeSize < bps) {
  2112           codeBuf <<= 8;
  2113           codeBuf |= strBytes[strIdx++];
  2114           codeSize += 8;
  2116         codeSize -= bps;
  2117         array.push((codeBuf >> codeSize) * sampleMul);
  2118         codeBuf &= (1 << codeSize) - 1;
  2120       return array;
  2121     },
  2123     getIR: function PDFFunction_getIR(xref, fn) {
  2124       var dict = fn.dict;
  2125       if (!dict) {
  2126         dict = fn;
  2129       var types = [this.constructSampled,
  2130                    null,
  2131                    this.constructInterpolated,
  2132                    this.constructStiched,
  2133                    this.constructPostScript];
  2135       var typeNum = dict.get('FunctionType');
  2136       var typeFn = types[typeNum];
  2137       if (!typeFn) {
  2138         error('Unknown type of function');
  2141       return typeFn.call(this, fn, dict, xref);
  2142     },
  2144     fromIR: function PDFFunction_fromIR(IR) {
  2145       var type = IR[0];
  2146       switch (type) {
  2147         case CONSTRUCT_SAMPLED:
  2148           return this.constructSampledFromIR(IR);
  2149         case CONSTRUCT_INTERPOLATED:
  2150           return this.constructInterpolatedFromIR(IR);
  2151         case CONSTRUCT_STICHED:
  2152           return this.constructStichedFromIR(IR);
  2153         //case CONSTRUCT_POSTSCRIPT:
  2154         default:
  2155           return this.constructPostScriptFromIR(IR);
  2157     },
  2159     parse: function PDFFunction_parse(xref, fn) {
  2160       var IR = this.getIR(xref, fn);
  2161       return this.fromIR(IR);
  2162     },
  2164     constructSampled: function PDFFunction_constructSampled(str, dict) {
  2165       function toMultiArray(arr) {
  2166         var inputLength = arr.length;
  2167         var out = [];
  2168         var index = 0;
  2169         for (var i = 0; i < inputLength; i += 2) {
  2170           out[index] = [arr[i], arr[i + 1]];
  2171           ++index;
  2173         return out;
  2175       var domain = dict.get('Domain');
  2176       var range = dict.get('Range');
  2178       if (!domain || !range) {
  2179         error('No domain or range');
  2182       var inputSize = domain.length / 2;
  2183       var outputSize = range.length / 2;
  2185       domain = toMultiArray(domain);
  2186       range = toMultiArray(range);
  2188       var size = dict.get('Size');
  2189       var bps = dict.get('BitsPerSample');
  2190       var order = dict.get('Order') || 1;
  2191       if (order !== 1) {
  2192         // No description how cubic spline interpolation works in PDF32000:2008
  2193         // As in poppler, ignoring order, linear interpolation may work as good
  2194         info('No support for cubic spline interpolation: ' + order);
  2197       var encode = dict.get('Encode');
  2198       if (!encode) {
  2199         encode = [];
  2200         for (var i = 0; i < inputSize; ++i) {
  2201           encode.push(0);
  2202           encode.push(size[i] - 1);
  2205       encode = toMultiArray(encode);
  2207       var decode = dict.get('Decode');
  2208       if (!decode) {
  2209         decode = range;
  2210       } else {
  2211         decode = toMultiArray(decode);
  2214       var samples = this.getSampleArray(size, outputSize, bps, str);
  2216       return [
  2217         CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size,
  2218         outputSize, Math.pow(2, bps) - 1, range
  2219       ];
  2220     },
  2222     constructSampledFromIR: function PDFFunction_constructSampledFromIR(IR) {
  2223       // See chapter 3, page 109 of the PDF reference
  2224       function interpolate(x, xmin, xmax, ymin, ymax) {
  2225         return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin)));
  2228       return function constructSampledFromIRResult(args) {
  2229         // See chapter 3, page 110 of the PDF reference.
  2230         var m = IR[1];
  2231         var domain = IR[2];
  2232         var encode = IR[3];
  2233         var decode = IR[4];
  2234         var samples = IR[5];
  2235         var size = IR[6];
  2236         var n = IR[7];
  2237         //var mask = IR[8];
  2238         var range = IR[9];
  2240         if (m != args.length) {
  2241           error('Incorrect number of arguments: ' + m + ' != ' +
  2242                 args.length);
  2245         var x = args;
  2247         // Building the cube vertices: its part and sample index
  2248         // http://rjwagner49.com/Mathematics/Interpolation.pdf
  2249         var cubeVertices = 1 << m;
  2250         var cubeN = new Float64Array(cubeVertices);
  2251         var cubeVertex = new Uint32Array(cubeVertices);
  2252         var i, j;
  2253         for (j = 0; j < cubeVertices; j++) {
  2254           cubeN[j] = 1;
  2257         var k = n, pos = 1;
  2258         // Map x_i to y_j for 0 <= i < m using the sampled function.
  2259         for (i = 0; i < m; ++i) {
  2260           // x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
  2261           var domain_2i = domain[i][0];
  2262           var domain_2i_1 = domain[i][1];
  2263           var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1);
  2265           // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1,
  2266           //                   Encode_2i, Encode_2i+1)
  2267           var e = interpolate(xi, domain_2i, domain_2i_1,
  2268                               encode[i][0], encode[i][1]);
  2270           // e_i' = min(max(e_i, 0), Size_i - 1)
  2271           var size_i = size[i];
  2272           e = Math.min(Math.max(e, 0), size_i - 1);
  2274           // Adjusting the cube: N and vertex sample index
  2275           var e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1;
  2276           var n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0);
  2277           var n1 = e - e0; // (e - e0) / (e1 - e0);
  2278           var offset0 = e0 * k;
  2279           var offset1 = offset0 + k; // e1 * k
  2280           for (j = 0; j < cubeVertices; j++) {
  2281             if (j & pos) {
  2282               cubeN[j] *= n1;
  2283               cubeVertex[j] += offset1;
  2284             } else {
  2285               cubeN[j] *= n0;
  2286               cubeVertex[j] += offset0;
  2290           k *= size_i;
  2291           pos <<= 1;
  2294         var y = new Float64Array(n);
  2295         for (j = 0; j < n; ++j) {
  2296           // Sum all cube vertices' samples portions
  2297           var rj = 0;
  2298           for (i = 0; i < cubeVertices; i++) {
  2299             rj += samples[cubeVertex[i] + j] * cubeN[i];
  2302           // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1,
  2303           //                    Decode_2j, Decode_2j+1)
  2304           rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
  2306           // y_j = min(max(r_j, range_2j), range_2j+1)
  2307           y[j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
  2310         return y;
  2311       };
  2312     },
  2314     constructInterpolated: function PDFFunction_constructInterpolated(str,
  2315                                                                       dict) {
  2316       var c0 = dict.get('C0') || [0];
  2317       var c1 = dict.get('C1') || [1];
  2318       var n = dict.get('N');
  2320       if (!isArray(c0) || !isArray(c1)) {
  2321         error('Illegal dictionary for interpolated function');
  2324       var length = c0.length;
  2325       var diff = [];
  2326       for (var i = 0; i < length; ++i) {
  2327         diff.push(c1[i] - c0[i]);
  2330       return [CONSTRUCT_INTERPOLATED, c0, diff, n];
  2331     },
  2333     constructInterpolatedFromIR:
  2334       function PDFFunction_constructInterpolatedFromIR(IR) {
  2335       var c0 = IR[1];
  2336       var diff = IR[2];
  2337       var n = IR[3];
  2339       var length = diff.length;
  2341       return function constructInterpolatedFromIRResult(args) {
  2342         var x = n == 1 ? args[0] : Math.pow(args[0], n);
  2344         var out = [];
  2345         for (var j = 0; j < length; ++j) {
  2346           out.push(c0[j] + (x * diff[j]));
  2349         return out;
  2351       };
  2352     },
  2354     constructStiched: function PDFFunction_constructStiched(fn, dict, xref) {
  2355       var domain = dict.get('Domain');
  2357       if (!domain) {
  2358         error('No domain');
  2361       var inputSize = domain.length / 2;
  2362       if (inputSize != 1) {
  2363         error('Bad domain for stiched function');
  2366       var fnRefs = dict.get('Functions');
  2367       var fns = [];
  2368       for (var i = 0, ii = fnRefs.length; i < ii; ++i) {
  2369         fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
  2372       var bounds = dict.get('Bounds');
  2373       var encode = dict.get('Encode');
  2375       return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
  2376     },
  2378     constructStichedFromIR: function PDFFunction_constructStichedFromIR(IR) {
  2379       var domain = IR[1];
  2380       var bounds = IR[2];
  2381       var encode = IR[3];
  2382       var fnsIR = IR[4];
  2383       var fns = [];
  2385       for (var i = 0, ii = fnsIR.length; i < ii; i++) {
  2386         fns.push(PDFFunction.fromIR(fnsIR[i]));
  2389       return function constructStichedFromIRResult(args) {
  2390         var clip = function constructStichedFromIRClip(v, min, max) {
  2391           if (v > max) {
  2392             v = max;
  2393           } else if (v < min) {
  2394             v = min;
  2396           return v;
  2397         };
  2399         // clip to domain
  2400         var v = clip(args[0], domain[0], domain[1]);
  2401         // calulate which bound the value is in
  2402         for (var i = 0, ii = bounds.length; i < ii; ++i) {
  2403           if (v < bounds[i]) {
  2404             break;
  2408         // encode value into domain of function
  2409         var dmin = domain[0];
  2410         if (i > 0) {
  2411           dmin = bounds[i - 1];
  2413         var dmax = domain[1];
  2414         if (i < bounds.length) {
  2415           dmax = bounds[i];
  2418         var rmin = encode[2 * i];
  2419         var rmax = encode[2 * i + 1];
  2421         var v2 = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
  2423         // call the appropriate function
  2424         return fns[i]([v2]);
  2425       };
  2426     },
  2428     constructPostScript: function PDFFunction_constructPostScript(fn, dict,
  2429                                                                   xref) {
  2430       var domain = dict.get('Domain');
  2431       var range = dict.get('Range');
  2433       if (!domain) {
  2434         error('No domain.');
  2437       if (!range) {
  2438         error('No range.');
  2441       var lexer = new PostScriptLexer(fn);
  2442       var parser = new PostScriptParser(lexer);
  2443       var code = parser.parse();
  2445       return [CONSTRUCT_POSTSCRIPT, domain, range, code];
  2446     },
  2448     constructPostScriptFromIR: function PDFFunction_constructPostScriptFromIR(
  2449                                           IR) {
  2450       var domain = IR[1];
  2451       var range = IR[2];
  2452       var code = IR[3];
  2453       var numOutputs = range.length / 2;
  2454       var evaluator = new PostScriptEvaluator(code);
  2455       // Cache the values for a big speed up, the cache size is limited though
  2456       // since the number of possible values can be huge from a PS function.
  2457       var cache = new FunctionCache();
  2458       return function constructPostScriptFromIRResult(args) {
  2459         var initialStack = [];
  2460         for (var i = 0, ii = (domain.length / 2); i < ii; ++i) {
  2461           initialStack.push(args[i]);
  2464         var key = initialStack.join('_');
  2465         if (cache.has(key)) {
  2466           return cache.get(key);
  2469         var stack = evaluator.execute(initialStack);
  2470         var transformed = [];
  2471         for (i = numOutputs - 1; i >= 0; --i) {
  2472           var out = stack.pop();
  2473           var rangeIndex = 2 * i;
  2474           if (out < range[rangeIndex]) {
  2475             out = range[rangeIndex];
  2476           } else if (out > range[rangeIndex + 1]) {
  2477             out = range[rangeIndex + 1];
  2479           transformed[i] = out;
  2481         cache.set(key, transformed);
  2482         return transformed;
  2483       };
  2485   };
  2486 })();
  2488 var FunctionCache = (function FunctionCacheClosure() {
  2489   // Of 10 PDF's with type4 functions the maxium number of distinct values seen
  2490   // was 256. This still may need some tweaking in the future though.
  2491   var MAX_CACHE_SIZE = 1024;
  2492   function FunctionCache() {
  2493     this.cache = {};
  2494     this.total = 0;
  2496   FunctionCache.prototype = {
  2497     has: function FunctionCache_has(key) {
  2498       return key in this.cache;
  2499     },
  2500     get: function FunctionCache_get(key) {
  2501       return this.cache[key];
  2502     },
  2503     set: function FunctionCache_set(key, value) {
  2504       if (this.total < MAX_CACHE_SIZE) {
  2505         this.cache[key] = value;
  2506         this.total++;
  2509   };
  2510   return FunctionCache;
  2511 })();
  2513 var PostScriptStack = (function PostScriptStackClosure() {
  2514   var MAX_STACK_SIZE = 100;
  2515   function PostScriptStack(initialStack) {
  2516     this.stack = initialStack || [];
  2519   PostScriptStack.prototype = {
  2520     push: function PostScriptStack_push(value) {
  2521       if (this.stack.length >= MAX_STACK_SIZE) {
  2522         error('PostScript function stack overflow.');
  2524       this.stack.push(value);
  2525     },
  2526     pop: function PostScriptStack_pop() {
  2527       if (this.stack.length <= 0) {
  2528         error('PostScript function stack underflow.');
  2530       return this.stack.pop();
  2531     },
  2532     copy: function PostScriptStack_copy(n) {
  2533       if (this.stack.length + n >= MAX_STACK_SIZE) {
  2534         error('PostScript function stack overflow.');
  2536       var stack = this.stack;
  2537       for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
  2538         stack.push(stack[i]);
  2540     },
  2541     index: function PostScriptStack_index(n) {
  2542       this.push(this.stack[this.stack.length - n - 1]);
  2543     },
  2544     // rotate the last n stack elements p times
  2545     roll: function PostScriptStack_roll(n, p) {
  2546       var stack = this.stack;
  2547       var l = stack.length - n;
  2548       var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
  2549       for (i = l, j = r; i < j; i++, j--) {
  2550         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
  2552       for (i = l, j = c - 1; i < j; i++, j--) {
  2553         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
  2555       for (i = c, j = r; i < j; i++, j--) {
  2556         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
  2559   };
  2560   return PostScriptStack;
  2561 })();
  2562 var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
  2563   function PostScriptEvaluator(operators) {
  2564     this.operators = operators;
  2566   PostScriptEvaluator.prototype = {
  2567     execute: function PostScriptEvaluator_execute(initialStack) {
  2568       var stack = new PostScriptStack(initialStack);
  2569       var counter = 0;
  2570       var operators = this.operators;
  2571       var length = operators.length;
  2572       var operator, a, b;
  2573       while (counter < length) {
  2574         operator = operators[counter++];
  2575         if (typeof operator == 'number') {
  2576           // Operator is really an operand and should be pushed to the stack.
  2577           stack.push(operator);
  2578           continue;
  2580         switch (operator) {
  2581           // non standard ps operators
  2582           case 'jz': // jump if false
  2583             b = stack.pop();
  2584             a = stack.pop();
  2585             if (!a) {
  2586               counter = b;
  2588             break;
  2589           case 'j': // jump
  2590             a = stack.pop();
  2591             counter = a;
  2592             break;
  2594           // all ps operators in alphabetical order (excluding if/ifelse)
  2595           case 'abs':
  2596             a = stack.pop();
  2597             stack.push(Math.abs(a));
  2598             break;
  2599           case 'add':
  2600             b = stack.pop();
  2601             a = stack.pop();
  2602             stack.push(a + b);
  2603             break;
  2604           case 'and':
  2605             b = stack.pop();
  2606             a = stack.pop();
  2607             if (isBool(a) && isBool(b)) {
  2608               stack.push(a && b);
  2609             } else {
  2610               stack.push(a & b);
  2612             break;
  2613           case 'atan':
  2614             a = stack.pop();
  2615             stack.push(Math.atan(a));
  2616             break;
  2617           case 'bitshift':
  2618             b = stack.pop();
  2619             a = stack.pop();
  2620             if (a > 0) {
  2621               stack.push(a << b);
  2622             } else {
  2623               stack.push(a >> b);
  2625             break;
  2626           case 'ceiling':
  2627             a = stack.pop();
  2628             stack.push(Math.ceil(a));
  2629             break;
  2630           case 'copy':
  2631             a = stack.pop();
  2632             stack.copy(a);
  2633             break;
  2634           case 'cos':
  2635             a = stack.pop();
  2636             stack.push(Math.cos(a));
  2637             break;
  2638           case 'cvi':
  2639             a = stack.pop() | 0;
  2640             stack.push(a);
  2641             break;
  2642           case 'cvr':
  2643             // noop
  2644             break;
  2645           case 'div':
  2646             b = stack.pop();
  2647             a = stack.pop();
  2648             stack.push(a / b);
  2649             break;
  2650           case 'dup':
  2651             stack.copy(1);
  2652             break;
  2653           case 'eq':
  2654             b = stack.pop();
  2655             a = stack.pop();
  2656             stack.push(a == b);
  2657             break;
  2658           case 'exch':
  2659             stack.roll(2, 1);
  2660             break;
  2661           case 'exp':
  2662             b = stack.pop();
  2663             a = stack.pop();
  2664             stack.push(Math.pow(a, b));
  2665             break;
  2666           case 'false':
  2667             stack.push(false);
  2668             break;
  2669           case 'floor':
  2670             a = stack.pop();
  2671             stack.push(Math.floor(a));
  2672             break;
  2673           case 'ge':
  2674             b = stack.pop();
  2675             a = stack.pop();
  2676             stack.push(a >= b);
  2677             break;
  2678           case 'gt':
  2679             b = stack.pop();
  2680             a = stack.pop();
  2681             stack.push(a > b);
  2682             break;
  2683           case 'idiv':
  2684             b = stack.pop();
  2685             a = stack.pop();
  2686             stack.push((a / b) | 0);
  2687             break;
  2688           case 'index':
  2689             a = stack.pop();
  2690             stack.index(a);
  2691             break;
  2692           case 'le':
  2693             b = stack.pop();
  2694             a = stack.pop();
  2695             stack.push(a <= b);
  2696             break;
  2697           case 'ln':
  2698             a = stack.pop();
  2699             stack.push(Math.log(a));
  2700             break;
  2701           case 'log':
  2702             a = stack.pop();
  2703             stack.push(Math.log(a) / Math.LN10);
  2704             break;
  2705           case 'lt':
  2706             b = stack.pop();
  2707             a = stack.pop();
  2708             stack.push(a < b);
  2709             break;
  2710           case 'mod':
  2711             b = stack.pop();
  2712             a = stack.pop();
  2713             stack.push(a % b);
  2714             break;
  2715           case 'mul':
  2716             b = stack.pop();
  2717             a = stack.pop();
  2718             stack.push(a * b);
  2719             break;
  2720           case 'ne':
  2721             b = stack.pop();
  2722             a = stack.pop();
  2723             stack.push(a != b);
  2724             break;
  2725           case 'neg':
  2726             a = stack.pop();
  2727             stack.push(-b);
  2728             break;
  2729           case 'not':
  2730             a = stack.pop();
  2731             if (isBool(a) && isBool(b)) {
  2732               stack.push(a && b);
  2733             } else {
  2734               stack.push(a & b);
  2736             break;
  2737           case 'or':
  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);
  2745             break;
  2746           case 'pop':
  2747             stack.pop();
  2748             break;
  2749           case 'roll':
  2750             b = stack.pop();
  2751             a = stack.pop();
  2752             stack.roll(a, b);
  2753             break;
  2754           case 'round':
  2755             a = stack.pop();
  2756             stack.push(Math.round(a));
  2757             break;
  2758           case 'sin':
  2759             a = stack.pop();
  2760             stack.push(Math.sin(a));
  2761             break;
  2762           case 'sqrt':
  2763             a = stack.pop();
  2764             stack.push(Math.sqrt(a));
  2765             break;
  2766           case 'sub':
  2767             b = stack.pop();
  2768             a = stack.pop();
  2769             stack.push(a - b);
  2770             break;
  2771           case 'true':
  2772             stack.push(true);
  2773             break;
  2774           case 'truncate':
  2775             a = stack.pop();
  2776             a = a < 0 ? Math.ceil(a) : Math.floor(a);
  2777             stack.push(a);
  2778             break;
  2779           case 'xor':
  2780             b = stack.pop();
  2781             a = stack.pop();
  2782             if (isBool(a) && isBool(b)) {
  2783               stack.push(a != b);
  2784             } else {
  2785               stack.push(a ^ b);
  2787             break;
  2788           default:
  2789             error('Unknown operator ' + operator);
  2790             break;
  2793       return stack.stack;
  2795   };
  2796   return PostScriptEvaluator;
  2797 })();
  2800 var HIGHLIGHT_OFFSET = 4; // px
  2801 var SUPPORTED_TYPES = ['Link', 'Text', 'Widget'];
  2803 var Annotation = (function AnnotationClosure() {
  2804   // 12.5.5: Algorithm: Appearance streams
  2805   function getTransformMatrix(rect, bbox, matrix) {
  2806     var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);
  2807     var minX = bounds[0];
  2808     var minY = bounds[1];
  2809     var maxX = bounds[2];
  2810     var maxY = bounds[3];
  2812     if (minX === maxX || minY === maxY) {
  2813       // From real-life file, bbox was [0, 0, 0, 0]. In this case,
  2814       // just apply the transform for rect
  2815       return [1, 0, 0, 1, rect[0], rect[1]];
  2818     var xRatio = (rect[2] - rect[0]) / (maxX - minX);
  2819     var yRatio = (rect[3] - rect[1]) / (maxY - minY);
  2820     return [
  2821       xRatio,
  2822       0,
  2823       0,
  2824       yRatio,
  2825       rect[0] - minX * xRatio,
  2826       rect[1] - minY * yRatio
  2827     ];
  2830   function getDefaultAppearance(dict) {
  2831     var appearanceState = dict.get('AP');
  2832     if (!isDict(appearanceState)) {
  2833       return;
  2836     var appearance;
  2837     var appearances = appearanceState.get('N');
  2838     if (isDict(appearances)) {
  2839       var as = dict.get('AS');
  2840       if (as && appearances.has(as.name)) {
  2841         appearance = appearances.get(as.name);
  2843     } else {
  2844       appearance = appearances;
  2846     return appearance;
  2849   function Annotation(params) {
  2850     if (params.data) {
  2851       this.data = params.data;
  2852       return;
  2855     var dict = params.dict;
  2856     var data = this.data = {};
  2858     data.subtype = dict.get('Subtype').name;
  2859     var rect = dict.get('Rect') || [0, 0, 0, 0];
  2860     data.rect = Util.normalizeRect(rect);
  2861     data.annotationFlags = dict.get('F');
  2863     var color = dict.get('C');
  2864     if (isArray(color) && color.length === 3) {
  2865       // TODO(mack): currently only supporting rgb; need support different
  2866       // colorspaces
  2867       data.color = color;
  2868     } else {
  2869       data.color = [0, 0, 0];
  2872     // Some types of annotations have border style dict which has more
  2873     // info than the border array
  2874     if (dict.has('BS')) {
  2875       var borderStyle = dict.get('BS');
  2876       data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1;
  2877     } else {
  2878       var borderArray = dict.get('Border') || [0, 0, 1];
  2879       data.borderWidth = borderArray[2] || 0;
  2881       // TODO: implement proper support for annotations with line dash patterns.
  2882       var dashArray = borderArray[3];
  2883       if (data.borderWidth > 0 && dashArray && isArray(dashArray)) {
  2884         var dashArrayLength = dashArray.length;
  2885         if (dashArrayLength > 0) {
  2886           // According to the PDF specification: the elements in a dashArray
  2887           // shall be numbers that are nonnegative and not all equal to zero.
  2888           var isInvalid = false;
  2889           var numPositive = 0;
  2890           for (var i = 0; i < dashArrayLength; i++) {
  2891             var validNumber = (+dashArray[i] >= 0);
  2892             if (!validNumber) {
  2893               isInvalid = true;
  2894               break;
  2895             } else if (dashArray[i] > 0) {
  2896               numPositive++;
  2899           if (isInvalid || numPositive === 0) {
  2900             data.borderWidth = 0;
  2906     this.appearance = getDefaultAppearance(dict);
  2907     data.hasAppearance = !!this.appearance;
  2908     data.id = params.ref.num;
  2911   Annotation.prototype = {
  2913     getData: function Annotation_getData() {
  2914       return this.data;
  2915     },
  2917     hasHtml: function Annotation_hasHtml() {
  2918       return false;
  2919     },
  2921     getHtmlElement: function Annotation_getHtmlElement(commonObjs) {
  2922       throw new NotImplementedException(
  2923         'getHtmlElement() should be implemented in subclass');
  2924     },
  2926     // TODO(mack): Remove this, it's not really that helpful.
  2927     getEmptyContainer: function Annotation_getEmptyContainer(tagName, rect,
  2928                                                              borderWidth) {
  2929       assert(!isWorker,
  2930         'getEmptyContainer() should be called from main thread');
  2932       var bWidth = borderWidth || 0;
  2934       rect = rect || this.data.rect;
  2935       var element = document.createElement(tagName);
  2936       element.style.borderWidth = bWidth + 'px';
  2937       var width = rect[2] - rect[0] - 2 * bWidth;
  2938       var height = rect[3] - rect[1] - 2 * bWidth;
  2939       element.style.width = width + 'px';
  2940       element.style.height = height + 'px';
  2941       return element;
  2942     },
  2944     isInvisible: function Annotation_isInvisible() {
  2945       var data = this.data;
  2946       if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) {
  2947         return false;
  2948       } else {
  2949         return !!(data &&
  2950                   data.annotationFlags &&            // Default: not invisible
  2951                   data.annotationFlags & 0x1);       // Invisible
  2953     },
  2955     isViewable: function Annotation_isViewable() {
  2956       var data = this.data;
  2957       return !!(!this.isInvisible() &&
  2958                 data &&
  2959                 (!data.annotationFlags ||
  2960                  !(data.annotationFlags & 0x22)) &&  // Hidden or NoView
  2961                 data.rect);                          // rectangle is nessessary
  2962     },
  2964     isPrintable: function Annotation_isPrintable() {
  2965       var data = this.data;
  2966       return !!(!this.isInvisible() &&
  2967                 data &&
  2968                 data.annotationFlags &&              // Default: not printable
  2969                 data.annotationFlags & 0x4 &&        // Print
  2970                 data.rect);                          // rectangle is nessessary
  2971     },
  2973     loadResources: function(keys) {
  2974       var promise = new LegacyPromise();
  2975       this.appearance.dict.getAsync('Resources').then(function(resources) {
  2976         if (!resources) {
  2977           promise.resolve();
  2978           return;
  2980         var objectLoader = new ObjectLoader(resources.map,
  2981                                             keys,
  2982                                             resources.xref);
  2983         objectLoader.load().then(function() {
  2984           promise.resolve(resources);
  2985         });
  2986       }.bind(this));
  2988       return promise;
  2989     },
  2991     getOperatorList: function Annotation_getOperatorList(evaluator) {
  2993       var promise = new LegacyPromise();
  2995       if (!this.appearance) {
  2996         promise.resolve(new OperatorList());
  2997         return promise;
  3000       var data = this.data;
  3002       var appearanceDict = this.appearance.dict;
  3003       var resourcesPromise = this.loadResources([
  3004         'ExtGState',
  3005         'ColorSpace',
  3006         'Pattern',
  3007         'Shading',
  3008         'XObject',
  3009         'Font'
  3010         // ProcSet
  3011         // Properties
  3012       ]);
  3013       var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
  3014       var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
  3015       var transform = getTransformMatrix(data.rect, bbox, matrix);
  3017       resourcesPromise.then(function(resources) {
  3018         var opList = new OperatorList();
  3019         opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
  3020         evaluator.getOperatorList(this.appearance, resources, opList);
  3021         opList.addOp(OPS.endAnnotation, []);
  3022         promise.resolve(opList);
  3024         this.appearance.reset();
  3025       }.bind(this));
  3027       return promise;
  3029   };
  3031   Annotation.getConstructor =
  3032       function Annotation_getConstructor(subtype, fieldType) {
  3034     if (!subtype) {
  3035       return;
  3038     // TODO(mack): Implement FreeText annotations
  3039     if (subtype === 'Link') {
  3040       return LinkAnnotation;
  3041     } else if (subtype === 'Text') {
  3042       return TextAnnotation;
  3043     } else if (subtype === 'Widget') {
  3044       if (!fieldType) {
  3045         return;
  3048       if (fieldType === 'Tx') {
  3049         return TextWidgetAnnotation;
  3050       } else {
  3051         return WidgetAnnotation;
  3053     } else {
  3054       return Annotation;
  3056   };
  3058   // TODO(mack): Support loading annotation from data
  3059   Annotation.fromData = function Annotation_fromData(data) {
  3060     var subtype = data.subtype;
  3061     var fieldType = data.fieldType;
  3062     var Constructor = Annotation.getConstructor(subtype, fieldType);
  3063     if (Constructor) {
  3064       return new Constructor({ data: data });
  3066   };
  3068   Annotation.fromRef = function Annotation_fromRef(xref, ref) {
  3070     var dict = xref.fetchIfRef(ref);
  3071     if (!isDict(dict)) {
  3072       return;
  3075     var subtype = dict.get('Subtype');
  3076     subtype = isName(subtype) ? subtype.name : '';
  3077     if (!subtype) {
  3078       return;
  3081     var fieldType = Util.getInheritableProperty(dict, 'FT');
  3082     fieldType = isName(fieldType) ? fieldType.name : '';
  3084     var Constructor = Annotation.getConstructor(subtype, fieldType);
  3085     if (!Constructor) {
  3086       return;
  3089     var params = {
  3090       dict: dict,
  3091       ref: ref,
  3092     };
  3094     var annotation = new Constructor(params);
  3096     if (annotation.isViewable() || annotation.isPrintable()) {
  3097       return annotation;
  3098     } else {
  3099       warn('unimplemented annotation type: ' + subtype);
  3101   };
  3103   Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
  3104       annotations, opList, pdfManager, partialEvaluator, intent) {
  3106     function reject(e) {
  3107       annotationsReadyPromise.reject(e);
  3110     var annotationsReadyPromise = new LegacyPromise();
  3112     var annotationPromises = [];
  3113     for (var i = 0, n = annotations.length; i < n; ++i) {
  3114       if (intent === 'display' && annotations[i].isViewable() ||
  3115           intent === 'print' && annotations[i].isPrintable()) {
  3116         annotationPromises.push(
  3117           annotations[i].getOperatorList(partialEvaluator));
  3120     Promise.all(annotationPromises).then(function(datas) {
  3121       opList.addOp(OPS.beginAnnotations, []);
  3122       for (var i = 0, n = datas.length; i < n; ++i) {
  3123         var annotOpList = datas[i];
  3124         opList.addOpList(annotOpList);
  3126       opList.addOp(OPS.endAnnotations, []);
  3127       annotationsReadyPromise.resolve();
  3128     }, reject);
  3130     return annotationsReadyPromise;
  3131   };
  3133   return Annotation;
  3134 })();
  3135 PDFJS.Annotation = Annotation;
  3138 var WidgetAnnotation = (function WidgetAnnotationClosure() {
  3140   function WidgetAnnotation(params) {
  3141     Annotation.call(this, params);
  3143     if (params.data) {
  3144       return;
  3147     var dict = params.dict;
  3148     var data = this.data;
  3150     data.fieldValue = stringToPDFString(
  3151       Util.getInheritableProperty(dict, 'V') || '');
  3152     data.alternativeText = stringToPDFString(dict.get('TU') || '');
  3153     data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
  3154     var fieldType = Util.getInheritableProperty(dict, 'FT');
  3155     data.fieldType = isName(fieldType) ? fieldType.name : '';
  3156     data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
  3157     this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
  3159     // Building the full field name by collecting the field and
  3160     // its ancestors 'T' data and joining them using '.'.
  3161     var fieldName = [];
  3162     var namedItem = dict;
  3163     var ref = params.ref;
  3164     while (namedItem) {
  3165       var parent = namedItem.get('Parent');
  3166       var parentRef = namedItem.getRaw('Parent');
  3167       var name = namedItem.get('T');
  3168       if (name) {
  3169         fieldName.unshift(stringToPDFString(name));
  3170       } else {
  3171         // The field name is absent, that means more than one field
  3172         // with the same name may exist. Replacing the empty name
  3173         // with the '`' plus index in the parent's 'Kids' array.
  3174         // This is not in the PDF spec but necessary to id the
  3175         // the input controls.
  3176         var kids = parent.get('Kids');
  3177         var j, jj;
  3178         for (j = 0, jj = kids.length; j < jj; j++) {
  3179           var kidRef = kids[j];
  3180           if (kidRef.num == ref.num && kidRef.gen == ref.gen) {
  3181             break;
  3184         fieldName.unshift('`' + j);
  3186       namedItem = parent;
  3187       ref = parentRef;
  3189     data.fullName = fieldName.join('.');
  3192   var parent = Annotation.prototype;
  3193   Util.inherit(WidgetAnnotation, Annotation, {
  3194     isViewable: function WidgetAnnotation_isViewable() {
  3195       if (this.data.fieldType === 'Sig') {
  3196         warn('unimplemented annotation type: Widget signature');
  3197         return false;
  3200       return parent.isViewable.call(this);
  3202   });
  3204   return WidgetAnnotation;
  3205 })();
  3207 var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
  3208   function TextWidgetAnnotation(params) {
  3209     WidgetAnnotation.call(this, params);
  3211     if (params.data) {
  3212       return;
  3215     this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');
  3218   // TODO(mack): This dupes some of the logic in CanvasGraphics.setFont()
  3219   function setTextStyles(element, item, fontObj) {
  3221     var style = element.style;
  3222     style.fontSize = item.fontSize + 'px';
  3223     style.direction = item.fontDirection < 0 ? 'rtl': 'ltr';
  3225     if (!fontObj) {
  3226       return;
  3229     style.fontWeight = fontObj.black ?
  3230                             (fontObj.bold ? 'bolder' : 'bold') :
  3231                             (fontObj.bold ? 'bold' : 'normal');
  3232     style.fontStyle = fontObj.italic ? 'italic' : 'normal';
  3234     var fontName = fontObj.loadedName;
  3235     var fontFamily = fontName ? '"' + fontName + '", ' : '';
  3236     // Use a reasonable default font if the font doesn't specify a fallback
  3237     var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif';
  3238     style.fontFamily = fontFamily + fallbackName;
  3242   Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
  3243     hasHtml: function TextWidgetAnnotation_hasHtml() {
  3244       return !this.data.hasAppearance && !!this.data.fieldValue;
  3245     },
  3247     getHtmlElement: function TextWidgetAnnotation_getHtmlElement(commonObjs) {
  3248       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
  3250       var item = this.data;
  3252       var element = this.getEmptyContainer('div');
  3253       element.style.display = 'table';
  3255       var content = document.createElement('div');
  3256       content.textContent = item.fieldValue;
  3257       var textAlignment = item.textAlignment;
  3258       content.style.textAlign = ['left', 'center', 'right'][textAlignment];
  3259       content.style.verticalAlign = 'middle';
  3260       content.style.display = 'table-cell';
  3262       var fontObj = item.fontRefName ?
  3263                     commonObjs.getData(item.fontRefName) : null;
  3264       setTextStyles(content, item, fontObj);
  3266       element.appendChild(content);
  3268       return element;
  3269     },
  3271     getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
  3272       if (this.appearance) {
  3273         return Annotation.prototype.getOperatorList.call(this, evaluator);
  3276       var promise = new LegacyPromise();
  3277       var opList = new OperatorList();
  3278       var data = this.data;
  3280       // Even if there is an appearance stream, ignore it. This is the
  3281       // behaviour used by Adobe Reader.
  3283       var defaultAppearance = data.defaultAppearance;
  3284       if (!defaultAppearance) {
  3285         promise.resolve(opList);
  3286         return promise;
  3289       // Include any font resources found in the default appearance
  3291       var stream = new Stream(stringToBytes(defaultAppearance));
  3292       evaluator.getOperatorList(stream, this.fieldResources, opList);
  3293       var appearanceFnArray = opList.fnArray;
  3294       var appearanceArgsArray = opList.argsArray;
  3295       var fnArray = [];
  3297       // TODO(mack): Add support for stroke color
  3298       data.rgb = [0, 0, 0];
  3299       // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
  3300       for (var i = 0, n = fnArray.length; i < n; ++i) {
  3301         var fnId = appearanceFnArray[i];
  3302         var args = appearanceArgsArray[i];
  3304         if (fnId === OPS.setFont) {
  3305           data.fontRefName = args[0];
  3306           var size = args[1];
  3307           if (size < 0) {
  3308             data.fontDirection = -1;
  3309             data.fontSize = -size;
  3310           } else {
  3311             data.fontDirection = 1;
  3312             data.fontSize = size;
  3314         } else if (fnId === OPS.setFillRGBColor) {
  3315           data.rgb = args;
  3316         } else if (fnId === OPS.setFillGray) {
  3317           var rgbValue = args[0] * 255;
  3318           data.rgb = [rgbValue, rgbValue, rgbValue];
  3321       promise.resolve(opList);
  3322       return promise;
  3324   });
  3326   return TextWidgetAnnotation;
  3327 })();
  3329 var InteractiveAnnotation = (function InteractiveAnnotationClosure() {
  3330   function InteractiveAnnotation(params) {
  3331     Annotation.call(this, params);
  3334   Util.inherit(InteractiveAnnotation, Annotation, {
  3335     hasHtml: function InteractiveAnnotation_hasHtml() {
  3336       return true;
  3337     },
  3339     highlight: function InteractiveAnnotation_highlight() {
  3340       if (this.highlightElement &&
  3341          this.highlightElement.hasAttribute('hidden')) {
  3342         this.highlightElement.removeAttribute('hidden');
  3344     },
  3346     unhighlight: function InteractiveAnnotation_unhighlight() {
  3347       if (this.highlightElement &&
  3348          !this.highlightElement.hasAttribute('hidden')) {
  3349         this.highlightElement.setAttribute('hidden', true);
  3351     },
  3353     initContainer: function InteractiveAnnotation_initContainer() {
  3355       var item = this.data;
  3356       var rect = item.rect;
  3358       var container = this.getEmptyContainer('section', rect, item.borderWidth);
  3359       container.style.backgroundColor = item.color;
  3361       var color = item.color;
  3362       var rgb = [];
  3363       for (var i = 0; i < 3; ++i) {
  3364         rgb[i] = Math.round(color[i] * 255);
  3366       item.colorCssRgb = Util.makeCssRgb(rgb);
  3368       var highlight = document.createElement('div');
  3369       highlight.className = 'annotationHighlight';
  3370       highlight.style.left = highlight.style.top = -HIGHLIGHT_OFFSET + 'px';
  3371       highlight.style.right = highlight.style.bottom = -HIGHLIGHT_OFFSET + 'px';
  3372       highlight.setAttribute('hidden', true);
  3374       this.highlightElement = highlight;
  3375       container.appendChild(this.highlightElement);
  3377       return container;
  3379   });
  3381   return InteractiveAnnotation;
  3382 })();
  3384 var TextAnnotation = (function TextAnnotationClosure() {
  3385   function TextAnnotation(params) {
  3386     InteractiveAnnotation.call(this, params);
  3388     if (params.data) {
  3389       return;
  3392     var dict = params.dict;
  3393     var data = this.data;
  3395     var content = dict.get('Contents');
  3396     var title = dict.get('T');
  3397     data.content = stringToPDFString(content || '');
  3398     data.title = stringToPDFString(title || '');
  3400     if (data.hasAppearance) {
  3401       data.name = 'NoIcon';
  3402     } else {
  3403       data.name = dict.has('Name') ? dict.get('Name').name : 'Note';
  3406     if (dict.has('C')) {
  3407       data.hasBgColor = true;
  3411   var ANNOT_MIN_SIZE = 10;
  3413   Util.inherit(TextAnnotation, InteractiveAnnotation, {
  3415     getHtmlElement: function TextAnnotation_getHtmlElement(commonObjs) {
  3416       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
  3418       var item = this.data;
  3419       var rect = item.rect;
  3421       // sanity check because of OOo-generated PDFs
  3422       if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) {
  3423         rect[3] = rect[1] + ANNOT_MIN_SIZE;
  3425       if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) {
  3426         rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
  3429       var container = this.initContainer();
  3430       container.className = 'annotText';
  3432       var image  = document.createElement('img');
  3433       image.style.height = container.style.height;
  3434       image.style.width = container.style.width;
  3435       var iconName = item.name;
  3436       image.src = PDFJS.imageResourcesPath + 'annotation-' +
  3437         iconName.toLowerCase() + '.svg';
  3438       image.alt = '[{{type}} Annotation]';
  3439       image.dataset.l10nId = 'text_annotation_type';
  3440       image.dataset.l10nArgs = JSON.stringify({type: iconName});
  3442       var contentWrapper = document.createElement('div');
  3443       contentWrapper.className = 'annotTextContentWrapper';
  3444       contentWrapper.style.left = Math.floor(rect[2] - rect[0] + 5) + 'px';
  3445       contentWrapper.style.top = '-10px';
  3447       var content = document.createElement('div');
  3448       content.className = 'annotTextContent';
  3449       content.setAttribute('hidden', true);
  3451       var i, ii;
  3452       if (item.hasBgColor) {
  3453         var color = item.color;
  3454         var rgb = [];
  3455         for (i = 0; i < 3; ++i) {
  3456           // Enlighten the color (70%)
  3457           var c = Math.round(color[i] * 255);
  3458           rgb[i] = Math.round((255 - c) * 0.7) + c;
  3460         content.style.backgroundColor = Util.makeCssRgb(rgb);
  3463       var title = document.createElement('h1');
  3464       var text = document.createElement('p');
  3465       title.textContent = item.title;
  3467       if (!item.content && !item.title) {
  3468         content.setAttribute('hidden', true);
  3469       } else {
  3470         var e = document.createElement('span');
  3471         var lines = item.content.split(/(?:\r\n?|\n)/);
  3472         for (i = 0, ii = lines.length; i < ii; ++i) {
  3473           var line = lines[i];
  3474           e.appendChild(document.createTextNode(line));
  3475           if (i < (ii - 1)) {
  3476             e.appendChild(document.createElement('br'));
  3479         text.appendChild(e);
  3481         var pinned = false;
  3483         var showAnnotation = function showAnnotation(pin) {
  3484           if (pin) {
  3485             pinned = true;
  3487           if (content.hasAttribute('hidden')) {
  3488             container.style.zIndex += 1;
  3489             content.removeAttribute('hidden');
  3491         };
  3493         var hideAnnotation = function hideAnnotation(unpin) {
  3494           if (unpin) {
  3495             pinned = false;
  3497           if (!content.hasAttribute('hidden') && !pinned) {
  3498             container.style.zIndex -= 1;
  3499             content.setAttribute('hidden', true);
  3501         };
  3503         var toggleAnnotation = function toggleAnnotation() {
  3504           if (pinned) {
  3505             hideAnnotation(true);
  3506           } else {
  3507             showAnnotation(true);
  3509         };
  3511         image.addEventListener('click', function image_clickHandler() {
  3512           toggleAnnotation();
  3513         }, false);
  3514         image.addEventListener('mouseover', function image_mouseOverHandler() {
  3515           showAnnotation();
  3516         }, false);
  3517         image.addEventListener('mouseout', function image_mouseOutHandler() {
  3518           hideAnnotation();
  3519         }, false);
  3521         content.addEventListener('click', function content_clickHandler() {
  3522           hideAnnotation(true);
  3523         }, false);
  3526       content.appendChild(title);
  3527       content.appendChild(text);
  3528       contentWrapper.appendChild(content);
  3529       container.appendChild(image);
  3530       container.appendChild(contentWrapper);
  3532       return container;
  3534   });
  3536   return TextAnnotation;
  3537 })();
  3539 var LinkAnnotation = (function LinkAnnotationClosure() {
  3540   function LinkAnnotation(params) {
  3541     InteractiveAnnotation.call(this, params);
  3543     if (params.data) {
  3544       return;
  3547     var dict = params.dict;
  3548     var data = this.data;
  3550     var action = dict.get('A');
  3551     if (action) {
  3552       var linkType = action.get('S').name;
  3553       if (linkType === 'URI') {
  3554         var url = action.get('URI');
  3555         if (isName(url)) {
  3556           // Some bad PDFs do not put parentheses around relative URLs.
  3557           url = '/' + url.name;
  3558         } else if (url) {
  3559           url = addDefaultProtocolToUrl(url);
  3561         // TODO: pdf spec mentions urls can be relative to a Base
  3562         // entry in the dictionary.
  3563         if (!isValidUrl(url, false)) {
  3564           url = '';
  3566         data.url = url;
  3567       } else if (linkType === 'GoTo') {
  3568         data.dest = action.get('D');
  3569       } else if (linkType === 'GoToR') {
  3570         var urlDict = action.get('F');
  3571         if (isDict(urlDict)) {
  3572           // We assume that the 'url' is a Filspec dictionary
  3573           // and fetch the url without checking any further
  3574           url = urlDict.get('F') || '';
  3577         // TODO: pdf reference says that GoToR
  3578         // can also have 'NewWindow' attribute
  3579         if (!isValidUrl(url, false)) {
  3580           url = '';
  3582         data.url = url;
  3583         data.dest = action.get('D');
  3584       } else if (linkType === 'Named') {
  3585         data.action = action.get('N').name;
  3586       } else {
  3587         warn('unrecognized link type: ' + linkType);
  3589     } else if (dict.has('Dest')) {
  3590       // simple destination link
  3591       var dest = dict.get('Dest');
  3592       data.dest = isName(dest) ? dest.name : dest;
  3596   // Lets URLs beginning with 'www.' default to using the 'http://' protocol.
  3597   function addDefaultProtocolToUrl(url) {
  3598     if (url && url.indexOf('www.') === 0) {
  3599       return ('http://' + url);
  3601     return url;
  3604   Util.inherit(LinkAnnotation, InteractiveAnnotation, {
  3605     hasOperatorList: function LinkAnnotation_hasOperatorList() {
  3606       return false;
  3607     },
  3609     getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) {
  3611       var container = this.initContainer();
  3612       container.className = 'annotLink';
  3614       var item = this.data;
  3616       container.style.borderColor = item.colorCssRgb;
  3617       container.style.borderStyle = 'solid';
  3619       var link = document.createElement('a');
  3620       link.href = link.title = this.data.url || '';
  3622       container.appendChild(link);
  3624       return container;
  3626   });
  3628   return LinkAnnotation;
  3629 })();
  3632 /**
  3633  * The maximum allowed image size in total pixels e.g. width * height. Images
  3634  * above this value will not be drawn. Use -1 for no limit.
  3635  * @var {number}
  3636  */
  3637 PDFJS.maxImageSize = (PDFJS.maxImageSize === undefined ?
  3638                       -1 : PDFJS.maxImageSize);
  3640 /**
  3641  * The url of where the predefined Adobe CMaps are located. Include trailing
  3642  * slash.
  3643  * @var {string}
  3644  */
  3645 PDFJS.cMapUrl = (PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl);
  3647 /**
  3648  * Specifies if CMaps are binary packed.
  3649  * @var {boolean}
  3650  */
  3651 PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked;
  3653 /*
  3654  * By default fonts are converted to OpenType fonts and loaded via font face
  3655  * rules. If disabled, the font will be rendered using a built in font renderer
  3656  * that constructs the glyphs with primitive path commands.
  3657  * @var {boolean}
  3658  */
  3659 PDFJS.disableFontFace = (PDFJS.disableFontFace === undefined ?
  3660                          false : PDFJS.disableFontFace);
  3662 /**
  3663  * Path for image resources, mainly for annotation icons. Include trailing
  3664  * slash.
  3665  * @var {string}
  3666  */
  3667 PDFJS.imageResourcesPath = (PDFJS.imageResourcesPath === undefined ?
  3668                             '' : PDFJS.imageResourcesPath);
  3670 /**
  3671  * Disable the web worker and run all code on the main thread. This will happen
  3672  * automatically if the browser doesn't support workers or sending typed arrays
  3673  * to workers.
  3674  * @var {boolean}
  3675  */
  3676 PDFJS.disableWorker = (PDFJS.disableWorker === undefined ?
  3677                        false : PDFJS.disableWorker);
  3679 /**
  3680  * Path and filename of the worker file. Required when the worker is enabled in
  3681  * development mode. If unspecified in the production build, the worker will be
  3682  * loaded based on the location of the pdf.js file.
  3683  * @var {string}
  3684  */
  3685 PDFJS.workerSrc = (PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc);
  3687 /**
  3688  * Disable range request loading of PDF files. When enabled and if the server
  3689  * supports partial content requests then the PDF will be fetched in chunks.
  3690  * Enabled (false) by default.
  3691  * @var {boolean}
  3692  */
  3693 PDFJS.disableRange = (PDFJS.disableRange === undefined ?
  3694                       false : PDFJS.disableRange);
  3696 /**
  3697  * Disable pre-fetching of PDF file data. When range requests are enabled PDF.js
  3698  * will automatically keep fetching more data even if it isn't needed to display
  3699  * the current page. This default behavior can be disabled.
  3700  * @var {boolean}
  3701  */
  3702 PDFJS.disableAutoFetch = (PDFJS.disableAutoFetch === undefined ?
  3703                           false : PDFJS.disableAutoFetch);
  3705 /**
  3706  * Enables special hooks for debugging PDF.js.
  3707  * @var {boolean}
  3708  */
  3709 PDFJS.pdfBug = (PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug);
  3711 /**
  3712  * Enables transfer usage in postMessage for ArrayBuffers.
  3713  * @var {boolean}
  3714  */
  3715 PDFJS.postMessageTransfers = (PDFJS.postMessageTransfers === undefined ?
  3716                               true : PDFJS.postMessageTransfers);
  3718 /**
  3719  * Disables URL.createObjectURL usage.
  3720  * @var {boolean}
  3721  */
  3722 PDFJS.disableCreateObjectURL = (PDFJS.disableCreateObjectURL === undefined ?
  3723                                 false : PDFJS.disableCreateObjectURL);
  3725 /**
  3726  * Disables WebGL usage.
  3727  * @var {boolean}
  3728  */
  3729 PDFJS.disableWebGL = (PDFJS.disableWebGL === undefined ?
  3730                       true : PDFJS.disableWebGL);
  3732 /**
  3733  * Controls the logging level.
  3734  * The constants from PDFJS.VERBOSITY_LEVELS should be used:
  3735  * - errors
  3736  * - warnings [default]
  3737  * - infos
  3738  * @var {number}
  3739  */
  3740 PDFJS.verbosity = (PDFJS.verbosity === undefined ?
  3741                    PDFJS.VERBOSITY_LEVELS.warnings : PDFJS.verbosity);
  3743 /**
  3744  * Document initialization / loading parameters object.
  3746  * @typedef {Object} DocumentInitParameters
  3747  * @property {string}     url   - The URL of the PDF.
  3748  * @property {TypedArray} data  - A typed array with PDF data.
  3749  * @property {Object}     httpHeaders - Basic authentication headers.
  3750  * @property {boolean}    withCredentials - Indicates whether or not cross-site
  3751  *   Access-Control requests should be made using credentials such as cookies
  3752  *   or authorization headers. The default is false.
  3753  * @property {string}     password - For decrypting password-protected PDFs.
  3754  * @property {TypedArray} initialData - A typed array with the first portion or
  3755  *   all of the pdf data. Used by the extension since some data is already
  3756  *   loaded before the switch to range requests.
  3757  */
  3759 /**
  3760  * This is the main entry point for loading a PDF and interacting with it.
  3761  * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
  3762  * is used, which means it must follow the same origin rules that any XHR does
  3763  * e.g. No cross domain requests without CORS.
  3765  * @param {string|TypedArray|DocumentInitParameters} source Can be a url to
  3766  * where a PDF is located, a typed array (Uint8Array) already populated with
  3767  * data or parameter object.
  3769  * @param {Object} pdfDataRangeTransport is optional. It is used if you want
  3770  * to manually serve range requests for data in the PDF. See viewer.js for
  3771  * an example of pdfDataRangeTransport's interface.
  3773  * @param {function} passwordCallback is optional. It is used to request a
  3774  * password if wrong or no password was provided. The callback receives two
  3775  * parameters: function that needs to be called with new password and reason
  3776  * (see {PasswordResponses}).
  3778  * @return {Promise} A promise that is resolved with {@link PDFDocumentProxy}
  3779  *   object.
  3780  */
  3781 PDFJS.getDocument = function getDocument(source,
  3782                                          pdfDataRangeTransport,
  3783                                          passwordCallback,
  3784                                          progressCallback) {
  3785   var workerInitializedPromise, workerReadyPromise, transport;
  3787   if (typeof source === 'string') {
  3788     source = { url: source };
  3789   } else if (isArrayBuffer(source)) {
  3790     source = { data: source };
  3791   } else if (typeof source !== 'object') {
  3792     error('Invalid parameter in getDocument, need either Uint8Array, ' +
  3793           'string or a parameter object');
  3796   if (!source.url && !source.data) {
  3797     error('Invalid parameter array, need either .data or .url');
  3800   // copy/use all keys as is except 'url' -- full path is required
  3801   var params = {};
  3802   for (var key in source) {
  3803     if (key === 'url' && typeof window !== 'undefined') {
  3804       params[key] = combineUrl(window.location.href, source[key]);
  3805       continue;
  3807     params[key] = source[key];
  3810   workerInitializedPromise = new PDFJS.LegacyPromise();
  3811   workerReadyPromise = new PDFJS.LegacyPromise();
  3812   transport = new WorkerTransport(workerInitializedPromise, workerReadyPromise,
  3813                                   pdfDataRangeTransport, progressCallback);
  3814   workerInitializedPromise.then(function transportInitialized() {
  3815     transport.passwordCallback = passwordCallback;
  3816     transport.fetchDocument(params);
  3817   });
  3818   return workerReadyPromise;
  3819 };
  3821 /**
  3822  * Proxy to a PDFDocument in the worker thread. Also, contains commonly used
  3823  * properties that can be read synchronously.
  3824  * @class
  3825  */
  3826 var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
  3827   function PDFDocumentProxy(pdfInfo, transport) {
  3828     this.pdfInfo = pdfInfo;
  3829     this.transport = transport;
  3831   PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ {
  3832     /**
  3833      * @return {number} Total number of pages the PDF contains.
  3834      */
  3835     get numPages() {
  3836       return this.pdfInfo.numPages;
  3837     },
  3838     /**
  3839      * @return {string} A unique ID to identify a PDF. Not guaranteed to be
  3840      * unique.
  3841      */
  3842     get fingerprint() {
  3843       return this.pdfInfo.fingerprint;
  3844     },
  3845     /**
  3846      * @param {number} pageNumber The page number to get. The first page is 1.
  3847      * @return {Promise} A promise that is resolved with a {@link PDFPageProxy}
  3848      * object.
  3849      */
  3850     getPage: function PDFDocumentProxy_getPage(pageNumber) {
  3851       return this.transport.getPage(pageNumber);
  3852     },
  3853     /**
  3854      * @param {{num: number, gen: number}} ref The page reference. Must have
  3855      *   the 'num' and 'gen' properties.
  3856      * @return {Promise} A promise that is resolved with the page index that is
  3857      * associated with the reference.
  3858      */
  3859     getPageIndex: function PDFDocumentProxy_getPageIndex(ref) {
  3860       return this.transport.getPageIndex(ref);
  3861     },
  3862     /**
  3863      * @return {Promise} A promise that is resolved with a lookup table for
  3864      * mapping named destinations to reference numbers.
  3865      */
  3866     getDestinations: function PDFDocumentProxy_getDestinations() {
  3867       return this.transport.getDestinations();
  3868     },
  3869     /**
  3870      * @return {Promise} A promise that is resolved with a lookup table for
  3871      * mapping named attachments to their content.
  3872      */
  3873     getAttachments: function PDFDocumentProxy_getAttachments() {
  3874       return this.transport.getAttachments();
  3875     },
  3876     /**
  3877      * @return {Promise} A promise that is resolved with an array of all the
  3878      * JavaScript strings in the name tree.
  3879      */
  3880     getJavaScript: function PDFDocumentProxy_getJavaScript() {
  3881       var promise = new PDFJS.LegacyPromise();
  3882       var js = this.pdfInfo.javaScript;
  3883       promise.resolve(js);
  3884       return promise;
  3885     },
  3886     /**
  3887      * @return {Promise} A promise that is resolved with an {Array} that is a
  3888      * tree outline (if it has one) of the PDF. The tree is in the format of:
  3889      * [
  3890      *  {
  3891      *   title: string,
  3892      *   bold: boolean,
  3893      *   italic: boolean,
  3894      *   color: rgb array,
  3895      *   dest: dest obj,
  3896      *   items: array of more items like this
  3897      *  },
  3898      *  ...
  3899      * ].
  3900      */
  3901     getOutline: function PDFDocumentProxy_getOutline() {
  3902       var promise = new PDFJS.LegacyPromise();
  3903       var outline = this.pdfInfo.outline;
  3904       promise.resolve(outline);
  3905       return promise;
  3906     },
  3907     /**
  3908      * @return {Promise} A promise that is resolved with an {Object} that has
  3909      * info and metadata properties.  Info is an {Object} filled with anything
  3910      * available in the information dictionary and similarly metadata is a
  3911      * {Metadata} object with information from the metadata section of the PDF.
  3912      */
  3913     getMetadata: function PDFDocumentProxy_getMetadata() {
  3914       var promise = new PDFJS.LegacyPromise();
  3915       var info = this.pdfInfo.info;
  3916       var metadata = this.pdfInfo.metadata;
  3917       promise.resolve({
  3918         info: info,
  3919         metadata: (metadata ? new PDFJS.Metadata(metadata) : null)
  3920       });
  3921       return promise;
  3922     },
  3923     /**
  3924      * @return {Promise} A promise that is resolved with a TypedArray that has
  3925      * the raw data from the PDF.
  3926      */
  3927     getData: function PDFDocumentProxy_getData() {
  3928       var promise = new PDFJS.LegacyPromise();
  3929       this.transport.getData(promise);
  3930       return promise;
  3931     },
  3932     /**
  3933      * @return {Promise} A promise that is resolved when the document's data
  3934      * is loaded. It is resolved with an {Object} that contains the length
  3935      * property that indicates size of the PDF data in bytes.
  3936      */
  3937     getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() {
  3938       return this.transport.downloadInfoPromise;
  3939     },
  3940     /**
  3941      * Cleans up resources allocated by the document, e.g. created @font-face.
  3942      */
  3943     cleanup: function PDFDocumentProxy_cleanup() {
  3944       this.transport.startCleanup();
  3945     },
  3946     /**
  3947      * Destroys current document instance and terminates worker.
  3948      */
  3949     destroy: function PDFDocumentProxy_destroy() {
  3950       this.transport.destroy();
  3952   };
  3953   return PDFDocumentProxy;
  3954 })();
  3956 /**
  3957  * Page text content.
  3959  * @typedef {Object} TextContent
  3960  * @property {array} items - array of {@link TextItem}
  3961  * @property {Object} styles - {@link TextStyles} objects, indexed by font
  3962  *                    name.
  3963  */
  3965 /**
  3966  * Page text content part.
  3968  * @typedef {Object} TextItem
  3969  * @property {string} str - text content.
  3970  * @property {string} dir - text direction: 'ttb', 'ltr' or 'rtl'.
  3971  * @property {array} transform - transformation matrix.
  3972  * @property {number} width - width in device space.
  3973  * @property {number} height - height in device space.
  3974  * @property {string} fontName - font name used by pdf.js for converted font.
  3975  */
  3977 /**
  3978  * Text style.
  3980  * @typedef {Object} TextStyle
  3981  * @property {number} ascent - font ascent.
  3982  * @property {number} descent - font descent.
  3983  * @property {boolean} vertical - text is in vertical mode.
  3984  * @property {string} fontFamily - possible font family
  3985  */
  3987 /**
  3988  * Page render parameters.
  3990  * @typedef {Object} RenderParameters
  3991  * @property {Object} canvasContext - A 2D context of a DOM Canvas object.
  3992  * @property {PageViewport} viewport - Rendering viewport obtained by
  3993  *                          calling of PDFPage.getViewport method.
  3994  * @property {string} intent - Rendering intent, can be 'display' or 'print'
  3995  *                    (default value is 'display').
  3996  * @property {Object} imageLayer - (optional) An object that has beginLayout,
  3997  *                    endLayout and appendImage functions.
  3998  * @property {function} continueCallback - (optional) A function that will be
  3999  *                      called each time the rendering is paused.  To continue
  4000  *                      rendering call the function that is the first argument
  4001  *                      to the callback.
  4002  */
  4004 /**
  4005  * Proxy to a PDFPage in the worker thread.
  4006  * @class
  4007  */
  4008 var PDFPageProxy = (function PDFPageProxyClosure() {
  4009   function PDFPageProxy(pageInfo, transport) {
  4010     this.pageInfo = pageInfo;
  4011     this.transport = transport;
  4012     this.stats = new StatTimer();
  4013     this.stats.enabled = !!globalScope.PDFJS.enableStats;
  4014     this.commonObjs = transport.commonObjs;
  4015     this.objs = new PDFObjects();
  4016     this.cleanupAfterRender = false;
  4017     this.pendingDestroy = false;
  4018     this.intentStates = {};
  4020   PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ {
  4021     /**
  4022      * @return {number} Page number of the page. First page is 1.
  4023      */
  4024     get pageNumber() {
  4025       return this.pageInfo.pageIndex + 1;
  4026     },
  4027     /**
  4028      * @return {number} The number of degrees the page is rotated clockwise.
  4029      */
  4030     get rotate() {
  4031       return this.pageInfo.rotate;
  4032     },
  4033     /**
  4034      * @return {Object} The reference that points to this page. It has 'num' and
  4035      * 'gen' properties.
  4036      */
  4037     get ref() {
  4038       return this.pageInfo.ref;
  4039     },
  4040     /**
  4041      * @return {Array} An array of the visible portion of the PDF page in the
  4042      * user space units - [x1, y1, x2, y2].
  4043      */
  4044     get view() {
  4045       return this.pageInfo.view;
  4046     },
  4047     /**
  4048      * @param {number} scale The desired scale of the viewport.
  4049      * @param {number} rotate Degrees to rotate the viewport. If omitted this
  4050      * defaults to the page rotation.
  4051      * @return {PageViewport} Contains 'width' and 'height' properties along
  4052      * with transforms required for rendering.
  4053      */
  4054     getViewport: function PDFPageProxy_getViewport(scale, rotate) {
  4055       if (arguments.length < 2) {
  4056         rotate = this.rotate;
  4058       return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0);
  4059     },
  4060     /**
  4061      * @return {Promise} A promise that is resolved with an {Array} of the
  4062      * annotation objects.
  4063      */
  4064     getAnnotations: function PDFPageProxy_getAnnotations() {
  4065       if (this.annotationsPromise) {
  4066         return this.annotationsPromise;
  4069       var promise = new PDFJS.LegacyPromise();
  4070       this.annotationsPromise = promise;
  4071       this.transport.getAnnotations(this.pageInfo.pageIndex);
  4072       return promise;
  4073     },
  4074     /**
  4075      * Begins the process of rendering a page to the desired context.
  4076      * @param {RenderParameters} params Page render parameters.
  4077      * @return {RenderTask} An object that contains the promise, which
  4078      *                      is resolved when the page finishes rendering.
  4079      */
  4080     render: function PDFPageProxy_render(params) {
  4081       var stats = this.stats;
  4082       stats.time('Overall');
  4084       // If there was a pending destroy cancel it so no cleanup happens during
  4085       // this call to render.
  4086       this.pendingDestroy = false;
  4088       var renderingIntent = ('intent' in params ?
  4089         (params.intent == 'print' ? 'print' : 'display') : 'display');
  4091       if (!this.intentStates[renderingIntent]) {
  4092         this.intentStates[renderingIntent] = {};
  4094       var intentState = this.intentStates[renderingIntent];
  4096       // If there is no displayReadyPromise yet, then the operatorList was never
  4097       // requested before. Make the request and create the promise.
  4098       if (!intentState.displayReadyPromise) {
  4099         intentState.receivingOperatorList = true;
  4100         intentState.displayReadyPromise = new LegacyPromise();
  4101         intentState.operatorList = {
  4102           fnArray: [],
  4103           argsArray: [],
  4104           lastChunk: false
  4105         };
  4107         this.stats.time('Page Request');
  4108         this.transport.messageHandler.send('RenderPageRequest', {
  4109           pageIndex: this.pageNumber - 1,
  4110           intent: renderingIntent
  4111         });
  4114       var internalRenderTask = new InternalRenderTask(complete, params,
  4115                                                       this.objs,
  4116                                                       this.commonObjs,
  4117                                                       intentState.operatorList,
  4118                                                       this.pageNumber);
  4119       if (!intentState.renderTasks) {
  4120         intentState.renderTasks = [];
  4122       intentState.renderTasks.push(internalRenderTask);
  4123       var renderTask = new RenderTask(internalRenderTask);
  4125       var self = this;
  4126       intentState.displayReadyPromise.then(
  4127         function pageDisplayReadyPromise(transparency) {
  4128           if (self.pendingDestroy) {
  4129             complete();
  4130             return;
  4132           stats.time('Rendering');
  4133           internalRenderTask.initalizeGraphics(transparency);
  4134           internalRenderTask.operatorListChanged();
  4135         },
  4136         function pageDisplayReadPromiseError(reason) {
  4137           complete(reason);
  4139       );
  4141       function complete(error) {
  4142         var i = intentState.renderTasks.indexOf(internalRenderTask);
  4143         if (i >= 0) {
  4144           intentState.renderTasks.splice(i, 1);
  4147         if (self.cleanupAfterRender) {
  4148           self.pendingDestroy = true;
  4150         self._tryDestroy();
  4152         if (error) {
  4153           renderTask.promise.reject(error);
  4154         } else {
  4155           renderTask.promise.resolve();
  4157         stats.timeEnd('Rendering');
  4158         stats.timeEnd('Overall');
  4161       return renderTask;
  4162     },
  4163     /**
  4164      * @return {Promise} That is resolved a {@link TextContent}
  4165      * object that represent the page text content.
  4166      */
  4167     getTextContent: function PDFPageProxy_getTextContent() {
  4168       var promise = new PDFJS.LegacyPromise();
  4169       this.transport.messageHandler.send('GetTextContent', {
  4170           pageIndex: this.pageNumber - 1
  4171         },
  4172         function textContentCallback(textContent) {
  4173           promise.resolve(textContent);
  4175       );
  4176       return promise;
  4177     },
  4178     /**
  4179      * Destroys resources allocated by the page.
  4180      */
  4181     destroy: function PDFPageProxy_destroy() {
  4182       this.pendingDestroy = true;
  4183       this._tryDestroy();
  4184     },
  4185     /**
  4186      * For internal use only. Attempts to clean up if rendering is in a state
  4187      * where that's possible.
  4188      * @ignore
  4189      */
  4190     _tryDestroy: function PDFPageProxy__destroy() {
  4191       if (!this.pendingDestroy ||
  4192           Object.keys(this.intentStates).some(function(intent) {
  4193             var intentState = this.intentStates[intent];
  4194             return (intentState.renderTasks.length !== 0 ||
  4195                     intentState.receivingOperatorList);
  4196           }, this)) {
  4197         return;
  4200       Object.keys(this.intentStates).forEach(function(intent) {
  4201         delete this.intentStates[intent];
  4202       }, this);
  4203       this.objs.clear();
  4204       this.pendingDestroy = false;
  4205     },
  4206     /**
  4207      * For internal use only.
  4208      * @ignore
  4209      */
  4210     _startRenderPage: function PDFPageProxy_startRenderPage(transparency,
  4211                                                             intent) {
  4212       var intentState = this.intentStates[intent];
  4213       intentState.displayReadyPromise.resolve(transparency);
  4214     },
  4215     /**
  4216      * For internal use only.
  4217      * @ignore
  4218      */
  4219     _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk,
  4220                                                             intent) {
  4221       var intentState = this.intentStates[intent];
  4222       var i, ii;
  4223       // Add the new chunk to the current operator list.
  4224       for (i = 0, ii = operatorListChunk.length; i < ii; i++) {
  4225         intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
  4226         intentState.operatorList.argsArray.push(
  4227           operatorListChunk.argsArray[i]);
  4229       intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
  4231       // Notify all the rendering tasks there are more operators to be consumed.
  4232       for (i = 0; i < intentState.renderTasks.length; i++) {
  4233         intentState.renderTasks[i].operatorListChanged();
  4236       if (operatorListChunk.lastChunk) {
  4237         intentState.receivingOperatorList = false;
  4238         this._tryDestroy();
  4241   };
  4242   return PDFPageProxy;
  4243 })();
  4245 /**
  4246  * For internal use only.
  4247  * @ignore
  4248  */
  4249 var WorkerTransport = (function WorkerTransportClosure() {
  4250   function WorkerTransport(workerInitializedPromise, workerReadyPromise,
  4251                            pdfDataRangeTransport, progressCallback) {
  4252     this.pdfDataRangeTransport = pdfDataRangeTransport;
  4254     this.workerReadyPromise = workerReadyPromise;
  4255     this.progressCallback = progressCallback;
  4256     this.commonObjs = new PDFObjects();
  4258     this.pageCache = [];
  4259     this.pagePromises = [];
  4260     this.downloadInfoPromise = new PDFJS.LegacyPromise();
  4261     this.passwordCallback = null;
  4263     // If worker support isn't disabled explicit and the browser has worker
  4264     // support, create a new web worker and test if it/the browser fullfills
  4265     // all requirements to run parts of pdf.js in a web worker.
  4266     // Right now, the requirement is, that an Uint8Array is still an Uint8Array
  4267     // as it arrives on the worker. Chrome added this with version 15.
  4268     if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
  4269       var workerSrc = PDFJS.workerSrc;
  4270       if (!workerSrc) {
  4271         error('No PDFJS.workerSrc specified');
  4274       try {
  4275         // Some versions of FF can't create a worker on localhost, see:
  4276         // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
  4277         var worker = new Worker(workerSrc);
  4278         var messageHandler = new MessageHandler('main', worker);
  4279         this.messageHandler = messageHandler;
  4281         messageHandler.on('test', function transportTest(data) {
  4282           var supportTypedArray = data && data.supportTypedArray;
  4283           if (supportTypedArray) {
  4284             this.worker = worker;
  4285             if (!data.supportTransfers) {
  4286               PDFJS.postMessageTransfers = false;
  4288             this.setupMessageHandler(messageHandler);
  4289             workerInitializedPromise.resolve();
  4290           } else {
  4291             globalScope.PDFJS.disableWorker = true;
  4292             this.loadFakeWorkerFiles().then(function() {
  4293               this.setupFakeWorker();
  4294               workerInitializedPromise.resolve();
  4295             }.bind(this));
  4297         }.bind(this));
  4299         var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]);
  4300         // Some versions of Opera throw a DATA_CLONE_ERR on serializing the
  4301         // typed array. Also, checking if we can use transfers.
  4302         try {
  4303           messageHandler.send('test', testObj, null, [testObj.buffer]);
  4304         } catch (ex) {
  4305           info('Cannot use postMessage transfers');
  4306           testObj[0] = 0;
  4307           messageHandler.send('test', testObj);
  4309         return;
  4310       } catch (e) {
  4311         info('The worker has been disabled.');
  4314     // Either workers are disabled, not supported or have thrown an exception.
  4315     // Thus, we fallback to a faked worker.
  4316     globalScope.PDFJS.disableWorker = true;
  4317     this.loadFakeWorkerFiles().then(function() {
  4318       this.setupFakeWorker();
  4319       workerInitializedPromise.resolve();
  4320     }.bind(this));
  4322   WorkerTransport.prototype = {
  4323     destroy: function WorkerTransport_destroy() {
  4324       this.pageCache = [];
  4325       this.pagePromises = [];
  4326       var self = this;
  4327       this.messageHandler.send('Terminate', null, function () {
  4328         FontLoader.clear();
  4329         if (self.worker) {
  4330           self.worker.terminate();
  4332       });
  4333     },
  4335     loadFakeWorkerFiles: function WorkerTransport_loadFakeWorkerFiles() {
  4336       if (!PDFJS.fakeWorkerFilesLoadedPromise) {
  4337         PDFJS.fakeWorkerFilesLoadedPromise = new LegacyPromise();
  4338         // In the developer build load worker_loader which in turn loads all the
  4339         // other files and resolves the promise. In production only the
  4340         // pdf.worker.js file is needed.
  4341         Util.loadScript(PDFJS.workerSrc, function() {
  4342           PDFJS.fakeWorkerFilesLoadedPromise.resolve();
  4343         });
  4345       return PDFJS.fakeWorkerFilesLoadedPromise;
  4346     },
  4348     setupFakeWorker: function WorkerTransport_setupFakeWorker() {
  4349       warn('Setting up fake worker.');
  4350       // If we don't use a worker, just post/sendMessage to the main thread.
  4351       var fakeWorker = {
  4352         postMessage: function WorkerTransport_postMessage(obj) {
  4353           fakeWorker.onmessage({data: obj});
  4354         },
  4355         terminate: function WorkerTransport_terminate() {}
  4356       };
  4358       var messageHandler = new MessageHandler('main', fakeWorker);
  4359       this.setupMessageHandler(messageHandler);
  4361       // If the main thread is our worker, setup the handling for the messages
  4362       // the main thread sends to it self.
  4363       PDFJS.WorkerMessageHandler.setup(messageHandler);
  4364     },
  4366     setupMessageHandler:
  4367       function WorkerTransport_setupMessageHandler(messageHandler) {
  4368       this.messageHandler = messageHandler;
  4370       function updatePassword(password) {
  4371         messageHandler.send('UpdatePassword', password);
  4374       var pdfDataRangeTransport = this.pdfDataRangeTransport;
  4375       if (pdfDataRangeTransport) {
  4376         pdfDataRangeTransport.addRangeListener(function(begin, chunk) {
  4377           messageHandler.send('OnDataRange', {
  4378             begin: begin,
  4379             chunk: chunk
  4380           });
  4381         });
  4383         pdfDataRangeTransport.addProgressListener(function(loaded) {
  4384           messageHandler.send('OnDataProgress', {
  4385             loaded: loaded
  4386           });
  4387         });
  4389         messageHandler.on('RequestDataRange',
  4390           function transportDataRange(data) {
  4391             pdfDataRangeTransport.requestDataRange(data.begin, data.end);
  4392           }, this);
  4395       messageHandler.on('GetDoc', function transportDoc(data) {
  4396         var pdfInfo = data.pdfInfo;
  4397         this.numPages = data.pdfInfo.numPages;
  4398         var pdfDocument = new PDFDocumentProxy(pdfInfo, this);
  4399         this.pdfDocument = pdfDocument;
  4400         this.workerReadyPromise.resolve(pdfDocument);
  4401       }, this);
  4403       messageHandler.on('NeedPassword', function transportPassword(data) {
  4404         if (this.passwordCallback) {
  4405           return this.passwordCallback(updatePassword,
  4406                                        PasswordResponses.NEED_PASSWORD);
  4408         this.workerReadyPromise.reject(data.exception.message, data.exception);
  4409       }, this);
  4411       messageHandler.on('IncorrectPassword', function transportBadPass(data) {
  4412         if (this.passwordCallback) {
  4413           return this.passwordCallback(updatePassword,
  4414                                        PasswordResponses.INCORRECT_PASSWORD);
  4416         this.workerReadyPromise.reject(data.exception.message, data.exception);
  4417       }, this);
  4419       messageHandler.on('InvalidPDF', function transportInvalidPDF(data) {
  4420         this.workerReadyPromise.reject(data.exception.name, data.exception);
  4421       }, this);
  4423       messageHandler.on('MissingPDF', function transportMissingPDF(data) {
  4424         this.workerReadyPromise.reject(data.exception.message, data.exception);
  4425       }, this);
  4427       messageHandler.on('UnknownError', function transportUnknownError(data) {
  4428         this.workerReadyPromise.reject(data.exception.message, data.exception);
  4429       }, this);
  4431       messageHandler.on('DataLoaded', function transportPage(data) {
  4432         this.downloadInfoPromise.resolve(data);
  4433       }, this);
  4435       messageHandler.on('GetPage', function transportPage(data) {
  4436         var pageInfo = data.pageInfo;
  4437         var page = new PDFPageProxy(pageInfo, this);
  4438         this.pageCache[pageInfo.pageIndex] = page;
  4439         var promise = this.pagePromises[pageInfo.pageIndex];
  4440         promise.resolve(page);
  4441       }, this);
  4443       messageHandler.on('GetAnnotations', function transportAnnotations(data) {
  4444         var annotations = data.annotations;
  4445         var promise = this.pageCache[data.pageIndex].annotationsPromise;
  4446         promise.resolve(annotations);
  4447       }, this);
  4449       messageHandler.on('StartRenderPage', function transportRender(data) {
  4450         var page = this.pageCache[data.pageIndex];
  4452         page.stats.timeEnd('Page Request');
  4453         page._startRenderPage(data.transparency, data.intent);
  4454       }, this);
  4456       messageHandler.on('RenderPageChunk', function transportRender(data) {
  4457         var page = this.pageCache[data.pageIndex];
  4459         page._renderPageChunk(data.operatorList, data.intent);
  4460       }, this);
  4462       messageHandler.on('commonobj', function transportObj(data) {
  4463         var id = data[0];
  4464         var type = data[1];
  4465         if (this.commonObjs.hasData(id)) {
  4466           return;
  4469         switch (type) {
  4470           case 'Font':
  4471             var exportedData = data[2];
  4473             var font;
  4474             if ('error' in exportedData) {
  4475               var error = exportedData.error;
  4476               warn('Error during font loading: ' + error);
  4477               this.commonObjs.resolve(id, error);
  4478               break;
  4479             } else {
  4480               font = new FontFace(exportedData);
  4483             FontLoader.bind(
  4484               [font],
  4485               function fontReady(fontObjs) {
  4486                 this.commonObjs.resolve(id, font);
  4487               }.bind(this)
  4488             );
  4489             break;
  4490           case 'FontPath':
  4491             this.commonObjs.resolve(id, data[2]);
  4492             break;
  4493           default:
  4494             error('Got unknown common object type ' + type);
  4496       }, this);
  4498       messageHandler.on('obj', function transportObj(data) {
  4499         var id = data[0];
  4500         var pageIndex = data[1];
  4501         var type = data[2];
  4502         var pageProxy = this.pageCache[pageIndex];
  4503         var imageData;
  4504         if (pageProxy.objs.hasData(id)) {
  4505           return;
  4508         switch (type) {
  4509           case 'JpegStream':
  4510             imageData = data[3];
  4511             loadJpegStream(id, imageData, pageProxy.objs);
  4512             break;
  4513           case 'Image':
  4514             imageData = data[3];
  4515             pageProxy.objs.resolve(id, imageData);
  4517             // heuristics that will allow not to store large data
  4518             var MAX_IMAGE_SIZE_TO_STORE = 8000000;
  4519             if (imageData && 'data' in imageData &&
  4520                 imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
  4521               pageProxy.cleanupAfterRender = true;
  4523             break;
  4524           default:
  4525             error('Got unknown object type ' + type);
  4527       }, this);
  4529       messageHandler.on('DocProgress', function transportDocProgress(data) {
  4530         if (this.progressCallback) {
  4531           this.progressCallback({
  4532             loaded: data.loaded,
  4533             total: data.total
  4534           });
  4536       }, this);
  4538       messageHandler.on('DocError', function transportDocError(data) {
  4539         this.workerReadyPromise.reject(data);
  4540       }, this);
  4542       messageHandler.on('PageError', function transportError(data, intent) {
  4543         var page = this.pageCache[data.pageNum - 1];
  4544         var intentState = page.intentStates[intent];
  4545         if (intentState.displayReadyPromise) {
  4546           intentState.displayReadyPromise.reject(data.error);
  4547         } else {
  4548           error(data.error);
  4550       }, this);
  4552       messageHandler.on('JpegDecode', function(data, deferred) {
  4553         var imageUrl = data[0];
  4554         var components = data[1];
  4555         if (components != 3 && components != 1) {
  4556           error('Only 3 component or 1 component can be returned');
  4559         var img = new Image();
  4560         img.onload = (function messageHandler_onloadClosure() {
  4561           var width = img.width;
  4562           var height = img.height;
  4563           var size = width * height;
  4564           var rgbaLength = size * 4;
  4565           var buf = new Uint8Array(size * components);
  4566           var tmpCanvas = createScratchCanvas(width, height);
  4567           var tmpCtx = tmpCanvas.getContext('2d');
  4568           tmpCtx.drawImage(img, 0, 0);
  4569           var data = tmpCtx.getImageData(0, 0, width, height).data;
  4570           var i, j;
  4572           if (components == 3) {
  4573             for (i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
  4574               buf[j] = data[i];
  4575               buf[j + 1] = data[i + 1];
  4576               buf[j + 2] = data[i + 2];
  4578           } else if (components == 1) {
  4579             for (i = 0, j = 0; i < rgbaLength; i += 4, j++) {
  4580               buf[j] = data[i];
  4583           deferred.resolve({ data: buf, width: width, height: height});
  4584         }).bind(this);
  4585         img.src = imageUrl;
  4586       });
  4587     },
  4589     fetchDocument: function WorkerTransport_fetchDocument(source) {
  4590       source.disableAutoFetch = PDFJS.disableAutoFetch;
  4591       source.chunkedViewerLoading = !!this.pdfDataRangeTransport;
  4592       this.messageHandler.send('GetDocRequest', {
  4593         source: source,
  4594         disableRange: PDFJS.disableRange,
  4595         maxImageSize: PDFJS.maxImageSize,
  4596         cMapUrl: PDFJS.cMapUrl,
  4597         cMapPacked: PDFJS.cMapPacked,
  4598         disableFontFace: PDFJS.disableFontFace,
  4599         disableCreateObjectURL: PDFJS.disableCreateObjectURL,
  4600         verbosity: PDFJS.verbosity
  4601       });
  4602     },
  4604     getData: function WorkerTransport_getData(promise) {
  4605       this.messageHandler.send('GetData', null, function(data) {
  4606         promise.resolve(data);
  4607       });
  4608     },
  4610     getPage: function WorkerTransport_getPage(pageNumber, promise) {
  4611       if (pageNumber <= 0 || pageNumber > this.numPages ||
  4612           (pageNumber|0) !== pageNumber) {
  4613         var pagePromise = new PDFJS.LegacyPromise();
  4614         pagePromise.reject(new Error('Invalid page request'));
  4615         return pagePromise;
  4618       var pageIndex = pageNumber - 1;
  4619       if (pageIndex in this.pagePromises) {
  4620         return this.pagePromises[pageIndex];
  4622       promise = new PDFJS.LegacyPromise();
  4623       this.pagePromises[pageIndex] = promise;
  4624       this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex });
  4625       return promise;
  4626     },
  4628     getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
  4629       var promise = new PDFJS.LegacyPromise();
  4630       this.messageHandler.send('GetPageIndex', { ref: ref },
  4631         function (pageIndex) {
  4632           promise.resolve(pageIndex);
  4634       );
  4635       return promise;
  4636     },
  4638     getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
  4639       this.messageHandler.send('GetAnnotationsRequest',
  4640         { pageIndex: pageIndex });
  4641     },
  4643     getDestinations: function WorkerTransport_getDestinations() {
  4644       var promise = new PDFJS.LegacyPromise();
  4645       this.messageHandler.send('GetDestinations', null,
  4646         function transportDestinations(destinations) {
  4647           promise.resolve(destinations);
  4649       );
  4650       return promise;
  4651     },
  4653     getAttachments: function WorkerTransport_getAttachments() {
  4654       var promise = new PDFJS.LegacyPromise();
  4655       this.messageHandler.send('GetAttachments', null,
  4656         function transportAttachments(attachments) {
  4657           promise.resolve(attachments);
  4659       );
  4660       return promise;
  4661     },
  4663     startCleanup: function WorkerTransport_startCleanup() {
  4664       this.messageHandler.send('Cleanup', null,
  4665         function endCleanup() {
  4666           for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
  4667             var page = this.pageCache[i];
  4668             if (page) {
  4669               page.destroy();
  4672           this.commonObjs.clear();
  4673           FontLoader.clear();
  4674         }.bind(this)
  4675       );
  4677   };
  4678   return WorkerTransport;
  4680 })();
  4682 /**
  4683  * A PDF document and page is built of many objects. E.g. there are objects
  4684  * for fonts, images, rendering code and such. These objects might get processed
  4685  * inside of a worker. The `PDFObjects` implements some basic functions to
  4686  * manage these objects.
  4687  * @ignore
  4688  */
  4689 var PDFObjects = (function PDFObjectsClosure() {
  4690   function PDFObjects() {
  4691     this.objs = {};
  4694   PDFObjects.prototype = {
  4695     /**
  4696      * Internal function.
  4697      * Ensures there is an object defined for `objId`.
  4698      */
  4699     ensureObj: function PDFObjects_ensureObj(objId) {
  4700       if (this.objs[objId]) {
  4701         return this.objs[objId];
  4704       var obj = {
  4705         promise: new LegacyPromise(),
  4706         data: null,
  4707         resolved: false
  4708       };
  4709       this.objs[objId] = obj;
  4711       return obj;
  4712     },
  4714     /**
  4715      * If called *without* callback, this returns the data of `objId` but the
  4716      * object needs to be resolved. If it isn't, this function throws.
  4718      * If called *with* a callback, the callback is called with the data of the
  4719      * object once the object is resolved. That means, if you call this
  4720      * function and the object is already resolved, the callback gets called
  4721      * right away.
  4722      */
  4723     get: function PDFObjects_get(objId, callback) {
  4724       // If there is a callback, then the get can be async and the object is
  4725       // not required to be resolved right now
  4726       if (callback) {
  4727         this.ensureObj(objId).promise.then(callback);
  4728         return null;
  4731       // If there isn't a callback, the user expects to get the resolved data
  4732       // directly.
  4733       var obj = this.objs[objId];
  4735       // If there isn't an object yet or the object isn't resolved, then the
  4736       // data isn't ready yet!
  4737       if (!obj || !obj.resolved) {
  4738         error('Requesting object that isn\'t resolved yet ' + objId);
  4741       return obj.data;
  4742     },
  4744     /**
  4745      * Resolves the object `objId` with optional `data`.
  4746      */
  4747     resolve: function PDFObjects_resolve(objId, data) {
  4748       var obj = this.ensureObj(objId);
  4750       obj.resolved = true;
  4751       obj.data = data;
  4752       obj.promise.resolve(data);
  4753     },
  4755     isResolved: function PDFObjects_isResolved(objId) {
  4756       var objs = this.objs;
  4758       if (!objs[objId]) {
  4759         return false;
  4760       } else {
  4761         return objs[objId].resolved;
  4763     },
  4765     hasData: function PDFObjects_hasData(objId) {
  4766       return this.isResolved(objId);
  4767     },
  4769     /**
  4770      * Returns the data of `objId` if object exists, null otherwise.
  4771      */
  4772     getData: function PDFObjects_getData(objId) {
  4773       var objs = this.objs;
  4774       if (!objs[objId] || !objs[objId].resolved) {
  4775         return null;
  4776       } else {
  4777         return objs[objId].data;
  4779     },
  4781     clear: function PDFObjects_clear() {
  4782       this.objs = {};
  4784   };
  4785   return PDFObjects;
  4786 })();
  4788 /**
  4789  * Allows controlling of the rendering tasks.
  4790  * @class
  4791  */
  4792 var RenderTask = (function RenderTaskClosure() {
  4793   function RenderTask(internalRenderTask) {
  4794     this.internalRenderTask = internalRenderTask;
  4795     /**
  4796      * Promise for rendering task completion.
  4797      * @type {Promise}
  4798      */
  4799     this.promise = new PDFJS.LegacyPromise();
  4802   RenderTask.prototype = /** @lends RenderTask.prototype */ {
  4803     /**
  4804      * Cancels the rendering task. If the task is currently rendering it will
  4805      * not be cancelled until graphics pauses with a timeout. The promise that
  4806      * this object extends will resolved when cancelled.
  4807      */
  4808     cancel: function RenderTask_cancel() {
  4809       this.internalRenderTask.cancel();
  4810       this.promise.reject(new Error('Rendering is cancelled'));
  4811     },
  4813     /**
  4814      * Registers callback to indicate the rendering task completion.
  4816      * @param {function} onFulfilled The callback for the rendering completion.
  4817      * @param {function} onRejected The callback for the rendering failure.
  4818      * @return {Promise} A promise that is resolved after the onFulfilled or
  4819      *                   onRejected callback.
  4820      */
  4821     then: function RenderTask_then(onFulfilled, onRejected) {
  4822       return this.promise.then(onFulfilled, onRejected);
  4824   };
  4826   return RenderTask;
  4827 })();
  4829 /**
  4830  * For internal use only.
  4831  * @ignore
  4832  */
  4833 var InternalRenderTask = (function InternalRenderTaskClosure() {
  4835   function InternalRenderTask(callback, params, objs, commonObjs, operatorList,
  4836                               pageNumber) {
  4837     this.callback = callback;
  4838     this.params = params;
  4839     this.objs = objs;
  4840     this.commonObjs = commonObjs;
  4841     this.operatorListIdx = null;
  4842     this.operatorList = operatorList;
  4843     this.pageNumber = pageNumber;
  4844     this.running = false;
  4845     this.graphicsReadyCallback = null;
  4846     this.graphicsReady = false;
  4847     this.cancelled = false;
  4850   InternalRenderTask.prototype = {
  4852     initalizeGraphics:
  4853         function InternalRenderTask_initalizeGraphics(transparency) {
  4855       if (this.cancelled) {
  4856         return;
  4858       if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
  4859           globalScope.StepperManager.enabled) {
  4860         this.stepper = globalScope.StepperManager.create(this.pageNumber - 1);
  4861         this.stepper.init(this.operatorList);
  4862         this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
  4865       var params = this.params;
  4866       this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
  4867                                     this.objs, params.imageLayer);
  4869       this.gfx.beginDrawing(params.viewport, transparency);
  4870       this.operatorListIdx = 0;
  4871       this.graphicsReady = true;
  4872       if (this.graphicsReadyCallback) {
  4873         this.graphicsReadyCallback();
  4875     },
  4877     cancel: function InternalRenderTask_cancel() {
  4878       this.running = false;
  4879       this.cancelled = true;
  4880       this.callback('cancelled');
  4881     },
  4883     operatorListChanged: function InternalRenderTask_operatorListChanged() {
  4884       if (!this.graphicsReady) {
  4885         if (!this.graphicsReadyCallback) {
  4886           this.graphicsReadyCallback = this._continue.bind(this);
  4888         return;
  4891       if (this.stepper) {
  4892         this.stepper.updateOperatorList(this.operatorList);
  4895       if (this.running) {
  4896         return;
  4898       this._continue();
  4899     },
  4901     _continue: function InternalRenderTask__continue() {
  4902       this.running = true;
  4903       if (this.cancelled) {
  4904         return;
  4906       if (this.params.continueCallback) {
  4907         this.params.continueCallback(this._next.bind(this));
  4908       } else {
  4909         this._next();
  4911     },
  4913     _next: function InternalRenderTask__next() {
  4914       if (this.cancelled) {
  4915         return;
  4917       this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList,
  4918                                         this.operatorListIdx,
  4919                                         this._continue.bind(this),
  4920                                         this.stepper);
  4921       if (this.operatorListIdx === this.operatorList.argsArray.length) {
  4922         this.running = false;
  4923         if (this.operatorList.lastChunk) {
  4924           this.gfx.endDrawing();
  4925           this.callback();
  4930   };
  4932   return InternalRenderTask;
  4933 })();
  4936 var Metadata = PDFJS.Metadata = (function MetadataClosure() {
  4937   function fixMetadata(meta) {
  4938     return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) {
  4939       var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g,
  4940                                 function(code, d1, d2, d3) {
  4941         return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);
  4942       });
  4943       var chars = '';
  4944       for (var i = 0; i < bytes.length; i += 2) {
  4945         var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
  4946         chars += code >= 32 && code < 127 && code != 60 && code != 62 &&
  4947           code != 38 && false ? String.fromCharCode(code) :
  4948           '&#x' + (0x10000 + code).toString(16).substring(1) + ';';
  4950       return '>' + chars;
  4951     });
  4954   function Metadata(meta) {
  4955     if (typeof meta === 'string') {
  4956       // Ghostscript produces invalid metadata
  4957       meta = fixMetadata(meta);
  4959       var parser = new DOMParser();
  4960       meta = parser.parseFromString(meta, 'application/xml');
  4961     } else if (!(meta instanceof Document)) {
  4962       error('Metadata: Invalid metadata object');
  4965     this.metaDocument = meta;
  4966     this.metadata = {};
  4967     this.parse();
  4970   Metadata.prototype = {
  4971     parse: function Metadata_parse() {
  4972       var doc = this.metaDocument;
  4973       var rdf = doc.documentElement;
  4975       if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta>
  4976         rdf = rdf.firstChild;
  4977         while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
  4978           rdf = rdf.nextSibling;
  4982       var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
  4983       if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) {
  4984         return;
  4987       var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
  4988       for (i = 0, length = children.length; i < length; i++) {
  4989         desc = children[i];
  4990         if (desc.nodeName.toLowerCase() !== 'rdf:description') {
  4991           continue;
  4994         for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
  4995           if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') {
  4996             entry = desc.childNodes[ii];
  4997             name = entry.nodeName.toLowerCase();
  4998             this.metadata[name] = entry.textContent.trim();
  5002     },
  5004     get: function Metadata_get(name) {
  5005       return this.metadata[name] || null;
  5006     },
  5008     has: function Metadata_has(name) {
  5009       return typeof this.metadata[name] !== 'undefined';
  5011   };
  5013   return Metadata;
  5014 })();
  5017 // <canvas> contexts store most of the state we need natively.
  5018 // However, PDF needs a bit more state, which we store here.
  5020 // Minimal font size that would be used during canvas fillText operations.
  5021 var MIN_FONT_SIZE = 16;
  5022 var MAX_GROUP_SIZE = 4096;
  5024 var COMPILE_TYPE3_GLYPHS = true;
  5026 function createScratchCanvas(width, height) {
  5027   var canvas = document.createElement('canvas');
  5028   canvas.width = width;
  5029   canvas.height = height;
  5030   return canvas;
  5033 function addContextCurrentTransform(ctx) {
  5034   // If the context doesn't expose a `mozCurrentTransform`, add a JS based on.
  5035   if (!ctx.mozCurrentTransform) {
  5036     // Store the original context
  5037     ctx._scaleX = ctx._scaleX || 1.0;
  5038     ctx._scaleY = ctx._scaleY || 1.0;
  5039     ctx._originalSave = ctx.save;
  5040     ctx._originalRestore = ctx.restore;
  5041     ctx._originalRotate = ctx.rotate;
  5042     ctx._originalScale = ctx.scale;
  5043     ctx._originalTranslate = ctx.translate;
  5044     ctx._originalTransform = ctx.transform;
  5045     ctx._originalSetTransform = ctx.setTransform;
  5047     ctx._transformMatrix = [ctx._scaleX, 0, 0, ctx._scaleY, 0, 0];
  5048     ctx._transformStack = [];
  5050     Object.defineProperty(ctx, 'mozCurrentTransform', {
  5051       get: function getCurrentTransform() {
  5052         return this._transformMatrix;
  5054     });
  5056     Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
  5057       get: function getCurrentTransformInverse() {
  5058         // Calculation done using WolframAlpha:
  5059         // http://www.wolframalpha.com/input/?
  5060         //   i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
  5062         var m = this._transformMatrix;
  5063         var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
  5065         var ad_bc = a * d - b * c;
  5066         var bc_ad = b * c - a * d;
  5068         return [
  5069           d / ad_bc,
  5070           b / bc_ad,
  5071           c / bc_ad,
  5072           a / ad_bc,
  5073           (d * e - c * f) / bc_ad,
  5074           (b * e - a * f) / ad_bc
  5075         ];
  5077     });
  5079     ctx.save = function ctxSave() {
  5080       var old = this._transformMatrix;
  5081       this._transformStack.push(old);
  5082       this._transformMatrix = old.slice(0, 6);
  5084       this._originalSave();
  5085     };
  5087     ctx.restore = function ctxRestore() {
  5088       var prev = this._transformStack.pop();
  5089       if (prev) {
  5090         this._transformMatrix = prev;
  5091         this._originalRestore();
  5093     };
  5095     ctx.translate = function ctxTranslate(x, y) {
  5096       var m = this._transformMatrix;
  5097       m[4] = m[0] * x + m[2] * y + m[4];
  5098       m[5] = m[1] * x + m[3] * y + m[5];
  5100       this._originalTranslate(x, y);
  5101     };
  5103     ctx.scale = function ctxScale(x, y) {
  5104       var m = this._transformMatrix;
  5105       m[0] = m[0] * x;
  5106       m[1] = m[1] * x;
  5107       m[2] = m[2] * y;
  5108       m[3] = m[3] * y;
  5110       this._originalScale(x, y);
  5111     };
  5113     ctx.transform = function ctxTransform(a, b, c, d, e, f) {
  5114       var m = this._transformMatrix;
  5115       this._transformMatrix = [
  5116         m[0] * a + m[2] * b,
  5117         m[1] * a + m[3] * b,
  5118         m[0] * c + m[2] * d,
  5119         m[1] * c + m[3] * d,
  5120         m[0] * e + m[2] * f + m[4],
  5121         m[1] * e + m[3] * f + m[5]
  5122       ];
  5124       ctx._originalTransform(a, b, c, d, e, f);
  5125     };
  5127     ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
  5128       this._transformMatrix = [a, b, c, d, e, f];
  5130       ctx._originalSetTransform(a, b, c, d, e, f);
  5131     };
  5133     ctx.rotate = function ctxRotate(angle) {
  5134       var cosValue = Math.cos(angle);
  5135       var sinValue = Math.sin(angle);
  5137       var m = this._transformMatrix;
  5138       this._transformMatrix = [
  5139         m[0] * cosValue + m[2] * sinValue,
  5140         m[1] * cosValue + m[3] * sinValue,
  5141         m[0] * (-sinValue) + m[2] * cosValue,
  5142         m[1] * (-sinValue) + m[3] * cosValue,
  5143         m[4],
  5144         m[5]
  5145       ];
  5147       this._originalRotate(angle);
  5148     };
  5152 var CachedCanvases = (function CachedCanvasesClosure() {
  5153   var cache = {};
  5154   return {
  5155     getCanvas: function CachedCanvases_getCanvas(id, width, height,
  5156                                                  trackTransform) {
  5157       var canvasEntry;
  5158       if (id in cache) {
  5159         canvasEntry = cache[id];
  5160         canvasEntry.canvas.width = width;
  5161         canvasEntry.canvas.height = height;
  5162         // reset canvas transform for emulated mozCurrentTransform, if needed
  5163         canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
  5164       } else {
  5165         var canvas = createScratchCanvas(width, height);
  5166         var ctx = canvas.getContext('2d');
  5167         if (trackTransform) {
  5168           addContextCurrentTransform(ctx);
  5170         cache[id] = canvasEntry = {canvas: canvas, context: ctx};
  5172       return canvasEntry;
  5173     },
  5174     clear: function () {
  5175       cache = {};
  5177   };
  5178 })();
  5180 function compileType3Glyph(imgData) {
  5181   var POINT_TO_PROCESS_LIMIT = 1000;
  5183   var width = imgData.width, height = imgData.height;
  5184   var i, j, j0, width1 = width + 1;
  5185   var points = new Uint8Array(width1 * (height + 1));
  5186   var POINT_TYPES =
  5187       new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
  5189   // decodes bit-packed mask data
  5190   var lineSize = (width + 7) & ~7, data0 = imgData.data;
  5191   var data = new Uint8Array(lineSize * height), pos = 0, ii;
  5192   for (i = 0, ii = data0.length; i < ii; i++) {
  5193     var mask = 128, elem = data0[i];
  5194     while (mask > 0) {
  5195       data[pos++] = (elem & mask) ? 0 : 255;
  5196       mask >>= 1;
  5200   // finding iteresting points: every point is located between mask pixels,
  5201   // so there will be points of the (width + 1)x(height + 1) grid. Every point
  5202   // will have flags assigned based on neighboring mask pixels:
  5203   //   4 | 8
  5204   //   --P--
  5205   //   2 | 1
  5206   // We are interested only in points with the flags:
  5207   //   - outside corners: 1, 2, 4, 8;
  5208   //   - inside corners: 7, 11, 13, 14;
  5209   //   - and, intersections: 5, 10.
  5210   var count = 0;
  5211   pos = 0;
  5212   if (data[pos] !== 0) {
  5213     points[0] = 1;
  5214     ++count;
  5216   for (j = 1; j < width; j++) {
  5217     if (data[pos] !== data[pos + 1]) {
  5218       points[j] = data[pos] ? 2 : 1;
  5219       ++count;
  5221     pos++;
  5223   if (data[pos] !== 0) {
  5224     points[j] = 2;
  5225     ++count;
  5227   for (i = 1; i < height; i++) {
  5228     pos = i * lineSize;
  5229     j0 = i * width1;
  5230     if (data[pos - lineSize] !== data[pos]) {
  5231       points[j0] = data[pos] ? 1 : 8;
  5232       ++count;
  5234     // 'sum' is the position of the current pixel configuration in the 'TYPES'
  5235     // array (in order 8-1-2-4, so we can use '>>2' to shift the column).
  5236     var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
  5237     for (j = 1; j < width; j++) {
  5238       sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) +
  5239             (data[pos - lineSize + 1] ? 8 : 0);
  5240       if (POINT_TYPES[sum]) {
  5241         points[j0 + j] = POINT_TYPES[sum];
  5242         ++count;
  5244       pos++;
  5246     if (data[pos - lineSize] !== data[pos]) {
  5247       points[j0 + j] = data[pos] ? 2 : 4;
  5248       ++count;
  5251     if (count > POINT_TO_PROCESS_LIMIT) {
  5252       return null;
  5256   pos = lineSize * (height - 1);
  5257   j0 = i * width1;
  5258   if (data[pos] !== 0) {
  5259     points[j0] = 8;
  5260     ++count;
  5262   for (j = 1; j < width; j++) {
  5263     if (data[pos] !== data[pos + 1]) {
  5264       points[j0 + j] = data[pos] ? 4 : 8;
  5265       ++count;
  5267     pos++;
  5269   if (data[pos] !== 0) {
  5270     points[j0 + j] = 4;
  5271     ++count;
  5273   if (count > POINT_TO_PROCESS_LIMIT) {
  5274     return null;
  5277   // building outlines
  5278   var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
  5279   var outlines = [];
  5280   for (i = 0; count && i <= height; i++) {
  5281     var p = i * width1;
  5282     var end = p + width;
  5283     while (p < end && !points[p]) {
  5284       p++;
  5286     if (p === end) {
  5287       continue;
  5289     var coords = [p % width1, i];
  5291     var type = points[p], p0 = p, pp;
  5292     do {
  5293       var step = steps[type];
  5294       do {
  5295         p += step;
  5296       } while (!points[p]);
  5298       pp = points[p];
  5299       if (pp !== 5 && pp !== 10) {
  5300         // set new direction
  5301         type = pp;
  5302         // delete mark
  5303         points[p] = 0;
  5304       } else { // type is 5 or 10, ie, a crossing
  5305         // set new direction
  5306         type = pp & ((0x33 * type) >> 4);
  5307         // set new type for "future hit"
  5308         points[p] &= (type >> 2 | type << 2);
  5311       coords.push(p % width1);
  5312       coords.push((p / width1) | 0);
  5313       --count;
  5314     } while (p0 !== p);
  5315     outlines.push(coords);
  5316     --i;
  5319   var drawOutline = function(c) {
  5320     c.save();
  5321     // the path shall be painted in [0..1]x[0..1] space
  5322     c.scale(1 / width, -1 / height);
  5323     c.translate(0, -height);
  5324     c.beginPath();
  5325     for (var i = 0, ii = outlines.length; i < ii; i++) {
  5326       var o = outlines[i];
  5327       c.moveTo(o[0], o[1]);
  5328       for (var j = 2, jj = o.length; j < jj; j += 2) {
  5329         c.lineTo(o[j], o[j+1]);
  5332     c.fill();
  5333     c.beginPath();
  5334     c.restore();
  5335   };
  5337   return drawOutline;
  5340 var CanvasExtraState = (function CanvasExtraStateClosure() {
  5341   function CanvasExtraState(old) {
  5342     // Are soft masks and alpha values shapes or opacities?
  5343     this.alphaIsShape = false;
  5344     this.fontSize = 0;
  5345     this.fontSizeScale = 1;
  5346     this.textMatrix = IDENTITY_MATRIX;
  5347     this.fontMatrix = FONT_IDENTITY_MATRIX;
  5348     this.leading = 0;
  5349     // Current point (in user coordinates)
  5350     this.x = 0;
  5351     this.y = 0;
  5352     // Start of text line (in text coordinates)
  5353     this.lineX = 0;
  5354     this.lineY = 0;
  5355     // Character and word spacing
  5356     this.charSpacing = 0;
  5357     this.wordSpacing = 0;
  5358     this.textHScale = 1;
  5359     this.textRenderingMode = TextRenderingMode.FILL;
  5360     this.textRise = 0;
  5361     // Color spaces
  5362     this.fillColorSpace = ColorSpace.singletons.gray;
  5363     this.fillColorSpaceObj = null;
  5364     this.strokeColorSpace = ColorSpace.singletons.gray;
  5365     this.strokeColorSpaceObj = null;
  5366     this.fillColorObj = null;
  5367     this.strokeColorObj = null;
  5368     // Default fore and background colors
  5369     this.fillColor = '#000000';
  5370     this.strokeColor = '#000000';
  5371     // Note: fill alpha applies to all non-stroking operations
  5372     this.fillAlpha = 1;
  5373     this.strokeAlpha = 1;
  5374     this.lineWidth = 1;
  5375     this.activeSMask = null; // nonclonable field (see the save method below)
  5377     this.old = old;
  5380   CanvasExtraState.prototype = {
  5381     clone: function CanvasExtraState_clone() {
  5382       return Object.create(this);
  5383     },
  5384     setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) {
  5385       this.x = x;
  5386       this.y = y;
  5388   };
  5389   return CanvasExtraState;
  5390 })();
  5392 var CanvasGraphics = (function CanvasGraphicsClosure() {
  5393   // Defines the time the executeOperatorList is going to be executing
  5394   // before it stops and shedules a continue of execution.
  5395   var EXECUTION_TIME = 15;
  5397   function CanvasGraphics(canvasCtx, commonObjs, objs, imageLayer) {
  5398     this.ctx = canvasCtx;
  5399     this.current = new CanvasExtraState();
  5400     this.stateStack = [];
  5401     this.pendingClip = null;
  5402     this.pendingEOFill = false;
  5403     this.res = null;
  5404     this.xobjs = null;
  5405     this.commonObjs = commonObjs;
  5406     this.objs = objs;
  5407     this.imageLayer = imageLayer;
  5408     this.groupStack = [];
  5409     this.processingType3 = null;
  5410     // Patterns are painted relative to the initial page/form transform, see pdf
  5411     // spec 8.7.2 NOTE 1.
  5412     this.baseTransform = null;
  5413     this.baseTransformStack = [];
  5414     this.groupLevel = 0;
  5415     this.smaskStack = [];
  5416     this.smaskCounter = 0;
  5417     this.tempSMask = null;
  5418     if (canvasCtx) {
  5419       addContextCurrentTransform(canvasCtx);
  5423   function putBinaryImageData(ctx, imgData) {
  5424     if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
  5425       ctx.putImageData(imgData, 0, 0);
  5426       return;
  5429     // Put the image data to the canvas in chunks, rather than putting the
  5430     // whole image at once.  This saves JS memory, because the ImageData object
  5431     // is smaller. It also possibly saves C++ memory within the implementation
  5432     // of putImageData(). (E.g. in Firefox we make two short-lived copies of
  5433     // the data passed to putImageData()). |n| shouldn't be too small, however,
  5434     // because too many putImageData() calls will slow things down.
  5435     //
  5436     // Note: as written, if the last chunk is partial, the putImageData() call
  5437     // will (conceptually) put pixels past the bounds of the canvas.  But
  5438     // that's ok; any such pixels are ignored.
  5440     var height = imgData.height, width = imgData.width;
  5441     var fullChunkHeight = 16;
  5442     var fracChunks = height / fullChunkHeight;
  5443     var fullChunks = Math.floor(fracChunks);
  5444     var totalChunks = Math.ceil(fracChunks);
  5445     var partialChunkHeight = height - fullChunks * fullChunkHeight;
  5447     var chunkImgData = ctx.createImageData(width, fullChunkHeight);
  5448     var srcPos = 0, destPos;
  5449     var src = imgData.data;
  5450     var dest = chunkImgData.data;
  5451     var i, j, thisChunkHeight, elemsInThisChunk;
  5453     // There are multiple forms in which the pixel data can be passed, and
  5454     // imgData.kind tells us which one this is.
  5455     if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
  5456       // Grayscale, 1 bit per pixel (i.e. black-and-white).
  5457       var srcLength = src.byteLength;
  5458       var dest32 = PDFJS.hasCanvasTypedArrays ? new Uint32Array(dest.buffer) :
  5459         new Uint32ArrayView(dest);
  5460       var dest32DataLength = dest32.length;
  5461       var fullSrcDiff = (width + 7) >> 3;
  5462       var white = 0xFFFFFFFF;
  5463       var black = (PDFJS.isLittleEndian || !PDFJS.hasCanvasTypedArrays) ?
  5464         0xFF000000 : 0x000000FF;
  5465       for (i = 0; i < totalChunks; i++) {
  5466         thisChunkHeight =
  5467           (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
  5468         destPos = 0;
  5469         for (j = 0; j < thisChunkHeight; j++) {
  5470           var srcDiff = srcLength - srcPos;
  5471           var k = 0;
  5472           var kEnd = (srcDiff > fullSrcDiff) ? width : srcDiff * 8 - 7;
  5473           var kEndUnrolled = kEnd & ~7;
  5474           var mask = 0;
  5475           var srcByte = 0;
  5476           for (; k < kEndUnrolled; k += 8) {
  5477             srcByte = src[srcPos++];
  5478             dest32[destPos++] = (srcByte & 128) ? white : black;
  5479             dest32[destPos++] = (srcByte & 64) ? white : black;
  5480             dest32[destPos++] = (srcByte & 32) ? white : black;
  5481             dest32[destPos++] = (srcByte & 16) ? white : black;
  5482             dest32[destPos++] = (srcByte & 8) ? white : black;
  5483             dest32[destPos++] = (srcByte & 4) ? white : black;
  5484             dest32[destPos++] = (srcByte & 2) ? white : black;
  5485             dest32[destPos++] = (srcByte & 1) ? white : black;
  5487           for (; k < kEnd; k++) {
  5488              if (mask === 0) {
  5489                srcByte = src[srcPos++];
  5490                mask = 128;
  5493             dest32[destPos++] = (srcByte & mask) ? white : black;
  5494             mask >>= 1;
  5497         // We ran out of input. Make all remaining pixels transparent.
  5498         while (destPos < dest32DataLength) {
  5499           dest32[destPos++] = 0;
  5502         ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
  5504     } else if (imgData.kind === ImageKind.RGBA_32BPP) {
  5505       // RGBA, 32-bits per pixel.
  5507       for (i = 0; i < totalChunks; i++) {
  5508         thisChunkHeight =
  5509           (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
  5510         elemsInThisChunk = imgData.width * thisChunkHeight * 4;
  5512         dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
  5513         srcPos += elemsInThisChunk;
  5515         ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
  5517     } else if (imgData.kind === ImageKind.RGB_24BPP) {
  5518       // RGB, 24-bits per pixel.
  5519       for (i = 0; i < totalChunks; i++) {
  5520         thisChunkHeight =
  5521           (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
  5522         elemsInThisChunk = imgData.width * thisChunkHeight * 3;
  5523         destPos = 0;
  5524         for (j = 0; j < elemsInThisChunk; j += 3) {
  5525           dest[destPos++] = src[srcPos++];
  5526           dest[destPos++] = src[srcPos++];
  5527           dest[destPos++] = src[srcPos++];
  5528           dest[destPos++] = 255;
  5530         ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
  5532     } else {
  5533       error('bad image kind: ' + imgData.kind);
  5537   function putBinaryImageMask(ctx, imgData) {
  5538     var height = imgData.height, width = imgData.width;
  5539     var fullChunkHeight = 16;
  5540     var fracChunks = height / fullChunkHeight;
  5541     var fullChunks = Math.floor(fracChunks);
  5542     var totalChunks = Math.ceil(fracChunks);
  5543     var partialChunkHeight = height - fullChunks * fullChunkHeight;
  5545     var chunkImgData = ctx.createImageData(width, fullChunkHeight);
  5546     var srcPos = 0;
  5547     var src = imgData.data;
  5548     var dest = chunkImgData.data;
  5550     for (var i = 0; i < totalChunks; i++) {
  5551       var thisChunkHeight =
  5552         (i < fullChunks) ? fullChunkHeight : partialChunkHeight;
  5554       // Expand the mask so it can be used by the canvas.  Any required
  5555       // inversion has already been handled.
  5556       var destPos = 3; // alpha component offset
  5557       for (var j = 0; j < thisChunkHeight; j++) {
  5558         var mask = 0;
  5559         for (var k = 0; k < width; k++) {
  5560           if (!mask) {
  5561             var elem = src[srcPos++];
  5562             mask = 128;
  5564           dest[destPos] = (elem & mask) ? 0 : 255;
  5565           destPos += 4;
  5566           mask >>= 1;
  5569       ctx.putImageData(chunkImgData, 0, i * fullChunkHeight);
  5573   function copyCtxState(sourceCtx, destCtx) {
  5574     var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha',
  5575                       'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
  5576                       'globalCompositeOperation', 'font'];
  5577     for (var i = 0, ii = properties.length; i < ii; i++) {
  5578       var property = properties[i];
  5579       if (property in sourceCtx) {
  5580         destCtx[property] = sourceCtx[property];
  5583     if ('setLineDash' in sourceCtx) {
  5584       destCtx.setLineDash(sourceCtx.getLineDash());
  5585       destCtx.lineDashOffset =  sourceCtx.lineDashOffset;
  5586     } else if ('mozDash' in sourceCtx) {
  5587       destCtx.mozDash = sourceCtx.mozDash;
  5588       destCtx.mozDashOffset = sourceCtx.mozDashOffset;
  5592   function genericComposeSMask(maskCtx, layerCtx, width, height,
  5593                                subtype, backdrop) {
  5594     var addBackdropFn;
  5595     if (backdrop) {
  5596       addBackdropFn = function (r0, g0, b0, bytes) {
  5597         var length = bytes.length;
  5598         for (var i = 3; i < length; i += 4) {
  5599           var alpha = bytes[i] / 255;
  5600           if (alpha === 0) {
  5601             bytes[i - 3] = r0;
  5602             bytes[i - 2] = g0;
  5603             bytes[i - 1] = b0;
  5604           } else if (alpha < 1) {
  5605             var alpha_ = 1 - alpha;
  5606             bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) | 0;
  5607             bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) | 0;
  5608             bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) | 0;
  5611       }.bind(null, backdrop[0], backdrop[1], backdrop[2]);
  5612     } else {
  5613       addBackdropFn = function () {};
  5616     var composeFn;
  5617     if (subtype === 'Luminosity') {
  5618       composeFn = function (maskDataBytes, layerDataBytes) {
  5619         var length = maskDataBytes.length;
  5620         for (var i = 3; i < length; i += 4) {
  5621           var y = ((maskDataBytes[i - 3] * 77) +     // * 0.3 / 255 * 0x10000
  5622                    (maskDataBytes[i - 2] * 152) +    // * 0.59 ....
  5623                    (maskDataBytes[i - 1] * 28)) | 0; // * 0.11 ....
  5624           layerDataBytes[i] = (layerDataBytes[i] * y) >> 16;
  5626       };
  5627     } else {
  5628       composeFn = function (maskDataBytes, layerDataBytes) {
  5629         var length = maskDataBytes.length;
  5630         for (var i = 3; i < length; i += 4) {
  5631           var alpha = maskDataBytes[i];
  5632           layerDataBytes[i] = (layerDataBytes[i] * alpha / 255) | 0;
  5634       };
  5637     // processing image in chunks to save memory
  5638     var PIXELS_TO_PROCESS = 65536;
  5639     var chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));
  5640     for (var row = 0; row < height; row += chunkSize) {
  5641       var chunkHeight = Math.min(chunkSize, height - row);
  5642       var maskData = maskCtx.getImageData(0, row, width, chunkHeight);
  5643       var layerData = layerCtx.getImageData(0, row, width, chunkHeight);
  5645       addBackdropFn(maskData.data);
  5646       composeFn(maskData.data, layerData.data);
  5648       maskCtx.putImageData(layerData, 0, row);
  5652   function composeSMask(ctx, smask, layerCtx) {
  5653     var mask = smask.canvas;
  5654     var maskCtx = smask.context;
  5656     ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY,
  5657                      smask.offsetX, smask.offsetY);
  5659     var backdrop;
  5660     if (smask.backdrop) {
  5661       var cs = smask.colorSpace || ColorSpace.singletons.rgb;
  5662       backdrop = cs.getRgb(smask.backdrop, 0);
  5664     if (WebGLUtils.isEnabled) {
  5665       var composed = WebGLUtils.composeSMask(layerCtx.canvas, mask,
  5666         {subtype: smask.subtype, backdrop: backdrop});
  5667       ctx.setTransform(1, 0, 0, 1, 0, 0);
  5668       ctx.drawImage(composed, smask.offsetX, smask.offsetY);
  5669       return;
  5671     genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height,
  5672                         smask.subtype, backdrop);
  5673     ctx.drawImage(mask, 0, 0);
  5676   var LINE_CAP_STYLES = ['butt', 'round', 'square'];
  5677   var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
  5678   var NORMAL_CLIP = {};
  5679   var EO_CLIP = {};
  5681   CanvasGraphics.prototype = {
  5683     beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) {
  5684       // For pdfs that use blend modes we have to clear the canvas else certain
  5685       // blend modes can look wrong since we'd be blending with a white
  5686       // backdrop. The problem with a transparent backdrop though is we then
  5687       // don't get sub pixel anti aliasing on text, so we fill with white if
  5688       // we can.
  5689       var width = this.ctx.canvas.width;
  5690       var height = this.ctx.canvas.height;
  5691       if (transparency) {
  5692         this.ctx.clearRect(0, 0, width, height);
  5693       } else {
  5694         this.ctx.mozOpaque = true;
  5695         this.ctx.save();
  5696         this.ctx.fillStyle = 'rgb(255, 255, 255)';
  5697         this.ctx.fillRect(0, 0, width, height);
  5698         this.ctx.restore();
  5701       var transform = viewport.transform;
  5703       this.ctx.save();
  5704       this.ctx.transform.apply(this.ctx, transform);
  5706       this.baseTransform = this.ctx.mozCurrentTransform.slice();
  5708       if (this.imageLayer) {
  5709         this.imageLayer.beginLayout();
  5711     },
  5713     executeOperatorList: function CanvasGraphics_executeOperatorList(
  5714                                     operatorList,
  5715                                     executionStartIdx, continueCallback,
  5716                                     stepper) {
  5717       var argsArray = operatorList.argsArray;
  5718       var fnArray = operatorList.fnArray;
  5719       var i = executionStartIdx || 0;
  5720       var argsArrayLen = argsArray.length;
  5722       // Sometimes the OperatorList to execute is empty.
  5723       if (argsArrayLen == i) {
  5724         return i;
  5727       var endTime = Date.now() + EXECUTION_TIME;
  5729       var commonObjs = this.commonObjs;
  5730       var objs = this.objs;
  5731       var fnId;
  5732       var deferred = Promise.resolve();
  5734       while (true) {
  5735         if (stepper && i === stepper.nextBreakPoint) {
  5736           stepper.breakIt(i, continueCallback);
  5737           return i;
  5740         fnId = fnArray[i];
  5742         if (fnId !== OPS.dependency) {
  5743           this[fnId].apply(this, argsArray[i]);
  5744         } else {
  5745           var deps = argsArray[i];
  5746           for (var n = 0, nn = deps.length; n < nn; n++) {
  5747             var depObjId = deps[n];
  5748             var common = depObjId.substring(0, 2) == 'g_';
  5750             // If the promise isn't resolved yet, add the continueCallback
  5751             // to the promise and bail out.
  5752             if (!common && !objs.isResolved(depObjId)) {
  5753               objs.get(depObjId, continueCallback);
  5754               return i;
  5756             if (common && !commonObjs.isResolved(depObjId)) {
  5757               commonObjs.get(depObjId, continueCallback);
  5758               return i;
  5763         i++;
  5765         // If the entire operatorList was executed, stop as were done.
  5766         if (i == argsArrayLen) {
  5767           return i;
  5770         // If the execution took longer then a certain amount of time, schedule
  5771         // to continue exeution after a short delay.
  5772         // However, this is only possible if a 'continueCallback' is passed in.
  5773         if (continueCallback && Date.now() > endTime) {
  5774           deferred.then(continueCallback);
  5775           return i;
  5778         // If the operatorList isn't executed completely yet OR the execution
  5779         // time was short enough, do another execution round.
  5781     },
  5783     endDrawing: function CanvasGraphics_endDrawing() {
  5784       this.ctx.restore();
  5785       CachedCanvases.clear();
  5786       WebGLUtils.clear();
  5788       if (this.imageLayer) {
  5789         this.imageLayer.endLayout();
  5791     },
  5793     // Graphics state
  5794     setLineWidth: function CanvasGraphics_setLineWidth(width) {
  5795       this.current.lineWidth = width;
  5796       this.ctx.lineWidth = width;
  5797     },
  5798     setLineCap: function CanvasGraphics_setLineCap(style) {
  5799       this.ctx.lineCap = LINE_CAP_STYLES[style];
  5800     },
  5801     setLineJoin: function CanvasGraphics_setLineJoin(style) {
  5802       this.ctx.lineJoin = LINE_JOIN_STYLES[style];
  5803     },
  5804     setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
  5805       this.ctx.miterLimit = limit;
  5806     },
  5807     setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
  5808       var ctx = this.ctx;
  5809       if ('setLineDash' in ctx) {
  5810         ctx.setLineDash(dashArray);
  5811         ctx.lineDashOffset = dashPhase;
  5812       } else {
  5813         ctx.mozDash = dashArray;
  5814         ctx.mozDashOffset = dashPhase;
  5816     },
  5817     setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
  5818       // Maybe if we one day fully support color spaces this will be important
  5819       // for now we can ignore.
  5820       // TODO set rendering intent?
  5821     },
  5822     setFlatness: function CanvasGraphics_setFlatness(flatness) {
  5823       // There's no way to control this with canvas, but we can safely ignore.
  5824       // TODO set flatness?
  5825     },
  5826     setGState: function CanvasGraphics_setGState(states) {
  5827       for (var i = 0, ii = states.length; i < ii; i++) {
  5828         var state = states[i];
  5829         var key = state[0];
  5830         var value = state[1];
  5832         switch (key) {
  5833           case 'LW':
  5834             this.setLineWidth(value);
  5835             break;
  5836           case 'LC':
  5837             this.setLineCap(value);
  5838             break;
  5839           case 'LJ':
  5840             this.setLineJoin(value);
  5841             break;
  5842           case 'ML':
  5843             this.setMiterLimit(value);
  5844             break;
  5845           case 'D':
  5846             this.setDash(value[0], value[1]);
  5847             break;
  5848           case 'RI':
  5849             this.setRenderingIntent(value);
  5850             break;
  5851           case 'FL':
  5852             this.setFlatness(value);
  5853             break;
  5854           case 'Font':
  5855             this.setFont(value[0], value[1]);
  5856             break;
  5857           case 'CA':
  5858             this.current.strokeAlpha = state[1];
  5859             break;
  5860           case 'ca':
  5861             this.current.fillAlpha = state[1];
  5862             this.ctx.globalAlpha = state[1];
  5863             break;
  5864           case 'BM':
  5865             if (value && value.name && (value.name !== 'Normal')) {
  5866               var mode = value.name.replace(/([A-Z])/g,
  5867                 function(c) {
  5868                   return '-' + c.toLowerCase();
  5870               ).substring(1);
  5871               this.ctx.globalCompositeOperation = mode;
  5872               if (this.ctx.globalCompositeOperation !== mode) {
  5873                 warn('globalCompositeOperation "' + mode +
  5874                      '" is not supported');
  5876             } else {
  5877               this.ctx.globalCompositeOperation = 'source-over';
  5879             break;
  5880           case 'SMask':
  5881             if (this.current.activeSMask) {
  5882               this.endSMaskGroup();
  5884             this.current.activeSMask = value ? this.tempSMask : null;
  5885             if (this.current.activeSMask) {
  5886               this.beginSMaskGroup();
  5888             this.tempSMask = null;
  5889             break;
  5892     },
  5893     beginSMaskGroup: function CanvasGraphics_beginSMaskGroup() {
  5895       var activeSMask = this.current.activeSMask;
  5896       var drawnWidth = activeSMask.canvas.width;
  5897       var drawnHeight = activeSMask.canvas.height;
  5898       var cacheId = 'smaskGroupAt' + this.groupLevel;
  5899       var scratchCanvas = CachedCanvases.getCanvas(
  5900         cacheId, drawnWidth, drawnHeight, true);
  5902       var currentCtx = this.ctx;
  5903       var currentTransform = currentCtx.mozCurrentTransform;
  5904       this.ctx.save();
  5906       var groupCtx = scratchCanvas.context;
  5907       groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY);
  5908       groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY);
  5909       groupCtx.transform.apply(groupCtx, currentTransform);
  5911       copyCtxState(currentCtx, groupCtx);
  5912       this.ctx = groupCtx;
  5913       this.setGState([
  5914         ['BM', 'Normal'],
  5915         ['ca', 1],
  5916         ['CA', 1]
  5917       ]);
  5918       this.groupStack.push(currentCtx);
  5919       this.groupLevel++;
  5920     },
  5921     endSMaskGroup: function CanvasGraphics_endSMaskGroup() {
  5922       var groupCtx = this.ctx;
  5923       this.groupLevel--;
  5924       this.ctx = this.groupStack.pop();
  5926       composeSMask(this.ctx, this.current.activeSMask, groupCtx);
  5927       this.ctx.restore();
  5928     },
  5929     save: function CanvasGraphics_save() {
  5930       this.ctx.save();
  5931       var old = this.current;
  5932       this.stateStack.push(old);
  5933       this.current = old.clone();
  5934       if (this.current.activeSMask) {
  5935         this.current.activeSMask = null;
  5937     },
  5938     restore: function CanvasGraphics_restore() {
  5939       var prev = this.stateStack.pop();
  5940       if (prev) {
  5941         if (this.current.activeSMask) {
  5942           this.endSMaskGroup();
  5945         this.current = prev;
  5946         this.ctx.restore();
  5948     },
  5949     transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
  5950       this.ctx.transform(a, b, c, d, e, f);
  5951     },
  5953     // Path
  5954     moveTo: function CanvasGraphics_moveTo(x, y) {
  5955       this.ctx.moveTo(x, y);
  5956       this.current.setCurrentPoint(x, y);
  5957     },
  5958     lineTo: function CanvasGraphics_lineTo(x, y) {
  5959       this.ctx.lineTo(x, y);
  5960       this.current.setCurrentPoint(x, y);
  5961     },
  5962     curveTo: function CanvasGraphics_curveTo(x1, y1, x2, y2, x3, y3) {
  5963       this.ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
  5964       this.current.setCurrentPoint(x3, y3);
  5965     },
  5966     curveTo2: function CanvasGraphics_curveTo2(x2, y2, x3, y3) {
  5967       var current = this.current;
  5968       this.ctx.bezierCurveTo(current.x, current.y, x2, y2, x3, y3);
  5969       current.setCurrentPoint(x3, y3);
  5970     },
  5971     curveTo3: function CanvasGraphics_curveTo3(x1, y1, x3, y3) {
  5972       this.curveTo(x1, y1, x3, y3, x3, y3);
  5973       this.current.setCurrentPoint(x3, y3);
  5974     },
  5975     closePath: function CanvasGraphics_closePath() {
  5976       this.ctx.closePath();
  5977     },
  5978     rectangle: function CanvasGraphics_rectangle(x, y, width, height) {
  5979       if (width === 0) {
  5980         width = this.getSinglePixelWidth();
  5982       if (height === 0) {
  5983         height = this.getSinglePixelWidth();
  5986       this.ctx.rect(x, y, width, height);
  5987     },
  5988     stroke: function CanvasGraphics_stroke(consumePath) {
  5989       consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
  5990       var ctx = this.ctx;
  5991       var strokeColor = this.current.strokeColor;
  5992       if (this.current.lineWidth === 0) {
  5993         ctx.lineWidth = this.getSinglePixelWidth();
  5995       // For stroke we want to temporarily change the global alpha to the
  5996       // stroking alpha.
  5997       ctx.globalAlpha = this.current.strokeAlpha;
  5998       if (strokeColor && strokeColor.hasOwnProperty('type') &&
  5999           strokeColor.type === 'Pattern') {
  6000         // for patterns, we transform to pattern space, calculate
  6001         // the pattern, call stroke, and restore to user space
  6002         ctx.save();
  6003         ctx.strokeStyle = strokeColor.getPattern(ctx, this);
  6004         ctx.stroke();
  6005         ctx.restore();
  6006       } else {
  6007         ctx.stroke();
  6009       if (consumePath) {
  6010         this.consumePath();
  6012       // Restore the global alpha to the fill alpha
  6013       ctx.globalAlpha = this.current.fillAlpha;
  6014     },
  6015     closeStroke: function CanvasGraphics_closeStroke() {
  6016       this.closePath();
  6017       this.stroke();
  6018     },
  6019     fill: function CanvasGraphics_fill(consumePath) {
  6020       consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
  6021       var ctx = this.ctx;
  6022       var fillColor = this.current.fillColor;
  6023       var needRestore = false;
  6025       if (fillColor && fillColor.hasOwnProperty('type') &&
  6026           fillColor.type === 'Pattern') {
  6027         ctx.save();
  6028         ctx.fillStyle = fillColor.getPattern(ctx, this);
  6029         needRestore = true;
  6032       if (this.pendingEOFill) {
  6033         if ('mozFillRule' in this.ctx) {
  6034           this.ctx.mozFillRule = 'evenodd';
  6035           this.ctx.fill();
  6036           this.ctx.mozFillRule = 'nonzero';
  6037         } else {
  6038           try {
  6039             this.ctx.fill('evenodd');
  6040           } catch (ex) {
  6041             // shouldn't really happen, but browsers might think differently
  6042             this.ctx.fill();
  6045         this.pendingEOFill = false;
  6046       } else {
  6047         this.ctx.fill();
  6050       if (needRestore) {
  6051         ctx.restore();
  6053       if (consumePath) {
  6054         this.consumePath();
  6056     },
  6057     eoFill: function CanvasGraphics_eoFill() {
  6058       this.pendingEOFill = true;
  6059       this.fill();
  6060     },
  6061     fillStroke: function CanvasGraphics_fillStroke() {
  6062       this.fill(false);
  6063       this.stroke(false);
  6065       this.consumePath();
  6066     },
  6067     eoFillStroke: function CanvasGraphics_eoFillStroke() {
  6068       this.pendingEOFill = true;
  6069       this.fillStroke();
  6070     },
  6071     closeFillStroke: function CanvasGraphics_closeFillStroke() {
  6072       this.closePath();
  6073       this.fillStroke();
  6074     },
  6075     closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() {
  6076       this.pendingEOFill = true;
  6077       this.closePath();
  6078       this.fillStroke();
  6079     },
  6080     endPath: function CanvasGraphics_endPath() {
  6081       this.consumePath();
  6082     },
  6084     // Clipping
  6085     clip: function CanvasGraphics_clip() {
  6086       this.pendingClip = NORMAL_CLIP;
  6087     },
  6088     eoClip: function CanvasGraphics_eoClip() {
  6089       this.pendingClip = EO_CLIP;
  6090     },
  6092     // Text
  6093     beginText: function CanvasGraphics_beginText() {
  6094       this.current.textMatrix = IDENTITY_MATRIX;
  6095       this.current.x = this.current.lineX = 0;
  6096       this.current.y = this.current.lineY = 0;
  6097     },
  6098     endText: function CanvasGraphics_endText() {
  6099       if (!('pendingTextPaths' in this)) {
  6100         this.ctx.beginPath();
  6101         return;
  6103       var paths = this.pendingTextPaths;
  6104       var ctx = this.ctx;
  6106       ctx.save();
  6107       ctx.beginPath();
  6108       for (var i = 0; i < paths.length; i++) {
  6109         var path = paths[i];
  6110         ctx.setTransform.apply(ctx, path.transform);
  6111         ctx.translate(path.x, path.y);
  6112         path.addToPath(ctx, path.fontSize);
  6114       ctx.restore();
  6115       ctx.clip();
  6116       ctx.beginPath();
  6117       delete this.pendingTextPaths;
  6118     },
  6119     setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
  6120       this.current.charSpacing = spacing;
  6121     },
  6122     setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
  6123       this.current.wordSpacing = spacing;
  6124     },
  6125     setHScale: function CanvasGraphics_setHScale(scale) {
  6126       this.current.textHScale = scale / 100;
  6127     },
  6128     setLeading: function CanvasGraphics_setLeading(leading) {
  6129       this.current.leading = -leading;
  6130     },
  6131     setFont: function CanvasGraphics_setFont(fontRefName, size) {
  6132       var fontObj = this.commonObjs.get(fontRefName);
  6133       var current = this.current;
  6135       if (!fontObj) {
  6136         error('Can\'t find font for ' + fontRefName);
  6139       current.fontMatrix = (fontObj.fontMatrix ?
  6140                             fontObj.fontMatrix : FONT_IDENTITY_MATRIX);
  6142       // A valid matrix needs all main diagonal elements to be non-zero
  6143       // This also ensures we bypass FF bugzilla bug #719844.
  6144       if (current.fontMatrix[0] === 0 ||
  6145           current.fontMatrix[3] === 0) {
  6146         warn('Invalid font matrix for font ' + fontRefName);
  6149       // The spec for Tf (setFont) says that 'size' specifies the font 'scale',
  6150       // and in some docs this can be negative (inverted x-y axes).
  6151       if (size < 0) {
  6152         size = -size;
  6153         current.fontDirection = -1;
  6154       } else {
  6155         current.fontDirection = 1;
  6158       this.current.font = fontObj;
  6159       this.current.fontSize = size;
  6161       if (fontObj.coded) {
  6162         return; // we don't need ctx.font for Type3 fonts
  6165       var name = fontObj.loadedName || 'sans-serif';
  6166       var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
  6167                                  (fontObj.bold ? 'bold' : 'normal');
  6169       var italic = fontObj.italic ? 'italic' : 'normal';
  6170       var typeface = '"' + name + '", ' + fontObj.fallbackName;
  6172       // Some font backends cannot handle fonts below certain size.
  6173       // Keeping the font at minimal size and using the fontSizeScale to change
  6174       // the current transformation matrix before the fillText/strokeText.
  6175       // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
  6176       var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
  6177       this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
  6178                                    size / MIN_FONT_SIZE;
  6180       var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
  6181       this.ctx.font = rule;
  6182     },
  6183     setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
  6184       this.current.textRenderingMode = mode;
  6185     },
  6186     setTextRise: function CanvasGraphics_setTextRise(rise) {
  6187       this.current.textRise = rise;
  6188     },
  6189     moveText: function CanvasGraphics_moveText(x, y) {
  6190       this.current.x = this.current.lineX += x;
  6191       this.current.y = this.current.lineY += y;
  6192     },
  6193     setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
  6194       this.setLeading(-y);
  6195       this.moveText(x, y);
  6196     },
  6197     setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) {
  6198       this.current.textMatrix = [a, b, c, d, e, f];
  6200       this.current.x = this.current.lineX = 0;
  6201       this.current.y = this.current.lineY = 0;
  6202     },
  6203     nextLine: function CanvasGraphics_nextLine() {
  6204       this.moveText(0, this.current.leading);
  6205     },
  6206     applyTextTransforms: function CanvasGraphics_applyTextTransforms() {
  6207       var ctx = this.ctx;
  6208       var current = this.current;
  6209       ctx.transform.apply(ctx, current.textMatrix);
  6210       ctx.translate(current.x, current.y + current.textRise);
  6211       if (current.fontDirection > 0) {
  6212         ctx.scale(current.textHScale, -1);
  6213       } else {
  6214         ctx.scale(-current.textHScale, 1);
  6216     },
  6218     paintChar: function (character, x, y) {
  6219       var ctx = this.ctx;
  6220       var current = this.current;
  6221       var font = current.font;
  6222       var fontSize = current.fontSize / current.fontSizeScale;
  6223       var textRenderingMode = current.textRenderingMode;
  6224       var fillStrokeMode = textRenderingMode &
  6225         TextRenderingMode.FILL_STROKE_MASK;
  6226       var isAddToPathSet = !!(textRenderingMode &
  6227         TextRenderingMode.ADD_TO_PATH_FLAG);
  6229       var addToPath;
  6230       if (font.disableFontFace || isAddToPathSet) {
  6231         addToPath = font.getPathGenerator(this.commonObjs, character);
  6234       if (font.disableFontFace) {
  6235         ctx.save();
  6236         ctx.translate(x, y);
  6237         ctx.beginPath();
  6238         addToPath(ctx, fontSize);
  6239         if (fillStrokeMode === TextRenderingMode.FILL ||
  6240             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  6241           ctx.fill();
  6243         if (fillStrokeMode === TextRenderingMode.STROKE ||
  6244             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  6245           ctx.stroke();
  6247         ctx.restore();
  6248       } else {
  6249         if (fillStrokeMode === TextRenderingMode.FILL ||
  6250             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  6251           ctx.fillText(character, x, y);
  6253         if (fillStrokeMode === TextRenderingMode.STROKE ||
  6254             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
  6255           ctx.strokeText(character, x, y);
  6259       if (isAddToPathSet) {
  6260         var paths = this.pendingTextPaths || (this.pendingTextPaths = []);
  6261         paths.push({
  6262           transform: ctx.mozCurrentTransform,
  6263           x: x,
  6264           y: y,
  6265           fontSize: fontSize,
  6266           addToPath: addToPath
  6267         });
  6269     },
  6271     get isFontSubpixelAAEnabled() {
  6272       // Checks if anti-aliasing is enabled when scaled text is painted.
  6273       // On Windows GDI scaled fonts looks bad.
  6274       var ctx = document.createElement('canvas').getContext('2d');
  6275       ctx.scale(1.5, 1);
  6276       ctx.fillText('I', 0, 10);
  6277       var data = ctx.getImageData(0, 0, 10, 10).data;
  6278       var enabled = false;
  6279       for (var i = 3; i < data.length; i += 4) {
  6280         if (data[i] > 0 && data[i] < 255) {
  6281           enabled = true;
  6282           break;
  6285       return shadow(this, 'isFontSubpixelAAEnabled', enabled);
  6286     },
  6288     showText: function CanvasGraphics_showText(glyphs) {
  6289       var ctx = this.ctx;
  6290       var current = this.current;
  6291       var font = current.font;
  6292       var fontSize = current.fontSize;
  6293       var fontSizeScale = current.fontSizeScale;
  6294       var charSpacing = current.charSpacing;
  6295       var wordSpacing = current.wordSpacing;
  6296       var textHScale = current.textHScale * current.fontDirection;
  6297       var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
  6298       var glyphsLength = glyphs.length;
  6299       var vertical = font.vertical;
  6300       var defaultVMetrics = font.defaultVMetrics;
  6301       var i, glyph, width;
  6303       if (fontSize === 0) {
  6304         return;
  6307       // Type3 fonts - each glyph is a "mini-PDF"
  6308       if (font.coded) {
  6309         ctx.save();
  6310         ctx.transform.apply(ctx, current.textMatrix);
  6311         ctx.translate(current.x, current.y);
  6313         ctx.scale(textHScale, 1);
  6315         for (i = 0; i < glyphsLength; ++i) {
  6316           glyph = glyphs[i];
  6317           if (glyph === null) {
  6318             // word break
  6319             this.ctx.translate(wordSpacing, 0);
  6320             current.x += wordSpacing * textHScale;
  6321             continue;
  6324           this.processingType3 = glyph;
  6325           this.save();
  6326           ctx.scale(fontSize, fontSize);
  6327           ctx.transform.apply(ctx, fontMatrix);
  6328           this.executeOperatorList(glyph.operatorList);
  6329           this.restore();
  6331           var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
  6332           width = ((transformed[0] * fontSize + charSpacing) *
  6333                    current.fontDirection);
  6335           ctx.translate(width, 0);
  6336           current.x += width * textHScale;
  6338         ctx.restore();
  6339         this.processingType3 = null;
  6340       } else {
  6341         ctx.save();
  6342         this.applyTextTransforms();
  6344         var lineWidth = current.lineWidth;
  6345         var a1 = current.textMatrix[0], b1 = current.textMatrix[1];
  6346         var scale = Math.sqrt(a1 * a1 + b1 * b1);
  6347         if (scale === 0 || lineWidth === 0) {
  6348           lineWidth = this.getSinglePixelWidth();
  6349         } else {
  6350           lineWidth /= scale;
  6353         if (fontSizeScale != 1.0) {
  6354           ctx.scale(fontSizeScale, fontSizeScale);
  6355           lineWidth /= fontSizeScale;
  6358         ctx.lineWidth = lineWidth;
  6360         var x = 0;
  6361         for (i = 0; i < glyphsLength; ++i) {
  6362           glyph = glyphs[i];
  6363           if (glyph === null) {
  6364             // word break
  6365             x += current.fontDirection * wordSpacing;
  6366             continue;
  6369           var restoreNeeded = false;
  6370           var character = glyph.fontChar;
  6371           var vmetric = glyph.vmetric || defaultVMetrics;
  6372           if (vertical) {
  6373             var vx = glyph.vmetric ? vmetric[1] : glyph.width * 0.5;
  6374             vx = -vx * fontSize * current.fontMatrix[0];
  6375             var vy = vmetric[2] * fontSize * current.fontMatrix[0];
  6377           width = vmetric ? -vmetric[0] : glyph.width;
  6378           var charWidth = width * fontSize * current.fontMatrix[0] +
  6379                           charSpacing * current.fontDirection;
  6380           var accent = glyph.accent;
  6382           var scaledX, scaledY, scaledAccentX, scaledAccentY;
  6384           if (vertical) {
  6385             scaledX = vx / fontSizeScale;
  6386             scaledY = (x + vy) / fontSizeScale;
  6387           } else {
  6388             scaledX = x / fontSizeScale;
  6389             scaledY = 0;
  6392           if (font.remeasure && width > 0 && this.isFontSubpixelAAEnabled) {
  6393             // some standard fonts may not have the exact width, trying to
  6394             // rescale per character
  6395             var measuredWidth = ctx.measureText(character).width * 1000 /
  6396               current.fontSize * current.fontSizeScale;
  6397             var characterScaleX = width / measuredWidth;
  6398             restoreNeeded = true;
  6399             ctx.save();
  6400             ctx.scale(characterScaleX, 1);
  6401             scaledX /= characterScaleX;
  6402             if (accent) {
  6403               scaledAccentX /= characterScaleX;
  6407           this.paintChar(character, scaledX, scaledY);
  6408           if (accent) {
  6409             scaledAccentX = scaledX + accent.offset.x / fontSizeScale;
  6410             scaledAccentY = scaledY - accent.offset.y / fontSizeScale;
  6411             this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY);
  6414           x += charWidth;
  6416           if (restoreNeeded) {
  6417             ctx.restore();
  6420         if (vertical) {
  6421           current.y -= x * textHScale;
  6422         } else {
  6423           current.x += x * textHScale;
  6425         ctx.restore();
  6427     },
  6428     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
  6429       var current = this.current;
  6430       var font = current.font;
  6431       var fontSize = current.fontSize;
  6432       // TJ array's number is independent from fontMatrix
  6433       var textHScale = current.textHScale * 0.001 * current.fontDirection;
  6434       var arrLength = arr.length;
  6435       var vertical = font.vertical;
  6437       for (var i = 0; i < arrLength; ++i) {
  6438         var e = arr[i];
  6439         if (isNum(e)) {
  6440           var spacingLength = -e * fontSize * textHScale;
  6441           if (vertical) {
  6442             current.y += spacingLength;
  6443           } else {
  6444             current.x += spacingLength;
  6447         } else {
  6448           this.showText(e);
  6451     },
  6452     nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
  6453       this.nextLine();
  6454       this.showText(text);
  6455     },
  6456     nextLineSetSpacingShowText:
  6457       function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing,
  6458                                                          charSpacing,
  6459                                                          text) {
  6460       this.setWordSpacing(wordSpacing);
  6461       this.setCharSpacing(charSpacing);
  6462       this.nextLineShowText(text);
  6463     },
  6465     // Type3 fonts
  6466     setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) {
  6467       // We can safely ignore this since the width should be the same
  6468       // as the width in the Widths array.
  6469     },
  6470     setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth,
  6471                                                                         yWidth,
  6472                                                                         llx,
  6473                                                                         lly,
  6474                                                                         urx,
  6475                                                                         ury) {
  6476       // TODO According to the spec we're also suppose to ignore any operators
  6477       // that set color or include images while processing this type3 font.
  6478       this.rectangle(llx, lly, urx - llx, ury - lly);
  6479       this.clip();
  6480       this.endPath();
  6481     },
  6483     // Color
  6484     setStrokeColorSpace: function CanvasGraphics_setStrokeColorSpace(raw) {
  6485       this.current.strokeColorSpace = ColorSpace.fromIR(raw);
  6486     },
  6487     setFillColorSpace: function CanvasGraphics_setFillColorSpace(raw) {
  6488       this.current.fillColorSpace = ColorSpace.fromIR(raw);
  6489     },
  6490     setStrokeColor: function CanvasGraphics_setStrokeColor(/*...*/) {
  6491       var cs = this.current.strokeColorSpace;
  6492       var rgbColor = cs.getRgb(arguments, 0);
  6493       var color = Util.makeCssRgb(rgbColor);
  6494       this.ctx.strokeStyle = color;
  6495       this.current.strokeColor = color;
  6496     },
  6497     getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR, cs) {
  6498       var pattern;
  6499       if (IR[0] == 'TilingPattern') {
  6500         var args = IR[1];
  6501         var base = cs.base;
  6502         var color;
  6503         if (base) {
  6504           color = base.getRgb(args, 0);
  6506         pattern = new TilingPattern(IR, color, this.ctx, this.objs,
  6507                                     this.commonObjs, this.baseTransform);
  6508       } else {
  6509         pattern = getShadingPatternFromIR(IR);
  6511       return pattern;
  6512     },
  6513     setStrokeColorN: function CanvasGraphics_setStrokeColorN(/*...*/) {
  6514       var cs = this.current.strokeColorSpace;
  6516       if (cs.name == 'Pattern') {
  6517         this.current.strokeColor = this.getColorN_Pattern(arguments, cs);
  6518       } else {
  6519         this.setStrokeColor.apply(this, arguments);
  6521     },
  6522     setFillColor: function CanvasGraphics_setFillColor(/*...*/) {
  6523       var cs = this.current.fillColorSpace;
  6524       var rgbColor = cs.getRgb(arguments, 0);
  6525       var color = Util.makeCssRgb(rgbColor);
  6526       this.ctx.fillStyle = color;
  6527       this.current.fillColor = color;
  6528     },
  6529     setFillColorN: function CanvasGraphics_setFillColorN(/*...*/) {
  6530       var cs = this.current.fillColorSpace;
  6532       if (cs.name == 'Pattern') {
  6533         this.current.fillColor = this.getColorN_Pattern(arguments, cs);
  6534       } else {
  6535         this.setFillColor.apply(this, arguments);
  6537     },
  6538     setStrokeGray: function CanvasGraphics_setStrokeGray(gray) {
  6539       this.current.strokeColorSpace = ColorSpace.singletons.gray;
  6541       var rgbColor = this.current.strokeColorSpace.getRgb(arguments, 0);
  6542       var color = Util.makeCssRgb(rgbColor);
  6543       this.ctx.strokeStyle = color;
  6544       this.current.strokeColor = color;
  6545     },
  6546     setFillGray: function CanvasGraphics_setFillGray(gray) {
  6547       this.current.fillColorSpace = ColorSpace.singletons.gray;
  6549       var rgbColor = this.current.fillColorSpace.getRgb(arguments, 0);
  6550       var color = Util.makeCssRgb(rgbColor);
  6551       this.ctx.fillStyle = color;
  6552       this.current.fillColor = color;
  6553     },
  6554     setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) {
  6555       this.current.strokeColorSpace = ColorSpace.singletons.rgb;
  6557       var rgbColor = this.current.strokeColorSpace.getRgb(arguments, 0);
  6558       var color = Util.makeCssRgb(rgbColor);
  6559       this.ctx.strokeStyle = color;
  6560       this.current.strokeColor = color;
  6561     },
  6562     setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) {
  6563       this.current.fillColorSpace = ColorSpace.singletons.rgb;
  6565       var rgbColor = this.current.fillColorSpace.getRgb(arguments, 0);
  6566       var color = Util.makeCssRgb(rgbColor);
  6567       this.ctx.fillStyle = color;
  6568       this.current.fillColor = color;
  6569     },
  6570     setStrokeCMYKColor: function CanvasGraphics_setStrokeCMYKColor(c, m, y, k) {
  6571       this.current.strokeColorSpace = ColorSpace.singletons.cmyk;
  6573       var color = Util.makeCssCmyk(arguments);
  6574       this.ctx.strokeStyle = color;
  6575       this.current.strokeColor = color;
  6576     },
  6577     setFillCMYKColor: function CanvasGraphics_setFillCMYKColor(c, m, y, k) {
  6578       this.current.fillColorSpace = ColorSpace.singletons.cmyk;
  6580       var color = Util.makeCssCmyk(arguments);
  6581       this.ctx.fillStyle = color;
  6582       this.current.fillColor = color;
  6583     },
  6585     shadingFill: function CanvasGraphics_shadingFill(patternIR) {
  6586       var ctx = this.ctx;
  6588       this.save();
  6589       var pattern = getShadingPatternFromIR(patternIR);
  6590       ctx.fillStyle = pattern.getPattern(ctx, this, true);
  6592       var inv = ctx.mozCurrentTransformInverse;
  6593       if (inv) {
  6594         var canvas = ctx.canvas;
  6595         var width = canvas.width;
  6596         var height = canvas.height;
  6598         var bl = Util.applyTransform([0, 0], inv);
  6599         var br = Util.applyTransform([0, height], inv);
  6600         var ul = Util.applyTransform([width, 0], inv);
  6601         var ur = Util.applyTransform([width, height], inv);
  6603         var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
  6604         var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
  6605         var x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
  6606         var y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
  6608         this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
  6609       } else {
  6610         // HACK to draw the gradient onto an infinite rectangle.
  6611         // PDF gradients are drawn across the entire image while
  6612         // Canvas only allows gradients to be drawn in a rectangle
  6613         // The following bug should allow us to remove this.
  6614         // https://bugzilla.mozilla.org/show_bug.cgi?id=664884
  6616         this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
  6619       this.restore();
  6620     },
  6622     // Images
  6623     beginInlineImage: function CanvasGraphics_beginInlineImage() {
  6624       error('Should not call beginInlineImage');
  6625     },
  6626     beginImageData: function CanvasGraphics_beginImageData() {
  6627       error('Should not call beginImageData');
  6628     },
  6630     paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix,
  6631                                                                         bbox) {
  6632       this.save();
  6633       this.baseTransformStack.push(this.baseTransform);
  6635       if (matrix && isArray(matrix) && 6 == matrix.length) {
  6636         this.transform.apply(this, matrix);
  6639       this.baseTransform = this.ctx.mozCurrentTransform;
  6641       if (bbox && isArray(bbox) && 4 == bbox.length) {
  6642         var width = bbox[2] - bbox[0];
  6643         var height = bbox[3] - bbox[1];
  6644         this.rectangle(bbox[0], bbox[1], width, height);
  6645         this.clip();
  6646         this.endPath();
  6648     },
  6650     paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
  6651       this.restore();
  6652       this.baseTransform = this.baseTransformStack.pop();
  6653     },
  6655     beginGroup: function CanvasGraphics_beginGroup(group) {
  6656       this.save();
  6657       var currentCtx = this.ctx;
  6658       // TODO non-isolated groups - according to Rik at adobe non-isolated
  6659       // group results aren't usually that different and they even have tools
  6660       // that ignore this setting. Notes from Rik on implmenting:
  6661       // - When you encounter an transparency group, create a new canvas with
  6662       // the dimensions of the bbox
  6663       // - copy the content from the previous canvas to the new canvas
  6664       // - draw as usual
  6665       // - remove the backdrop alpha:
  6666       // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha
  6667       // value of your transparency group and 'alphaBackdrop' the alpha of the
  6668       // backdrop
  6669       // - remove background color:
  6670       // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)
  6671       if (!group.isolated) {
  6672         info('TODO: Support non-isolated groups.');
  6675       // TODO knockout - supposedly possible with the clever use of compositing
  6676       // modes.
  6677       if (group.knockout) {
  6678         warn('Knockout groups not supported.');
  6681       var currentTransform = currentCtx.mozCurrentTransform;
  6682       if (group.matrix) {
  6683         currentCtx.transform.apply(currentCtx, group.matrix);
  6685       assert(group.bbox, 'Bounding box is required.');
  6687       // Based on the current transform figure out how big the bounding box
  6688       // will actually be.
  6689       var bounds = Util.getAxialAlignedBoundingBox(
  6690                     group.bbox,
  6691                     currentCtx.mozCurrentTransform);
  6692       // Clip the bounding box to the current canvas.
  6693       var canvasBounds = [0,
  6694                           0,
  6695                           currentCtx.canvas.width,
  6696                           currentCtx.canvas.height];
  6697       bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
  6698       // Use ceil in case we're between sizes so we don't create canvas that is
  6699       // too small and make the canvas at least 1x1 pixels.
  6700       var offsetX = Math.floor(bounds[0]);
  6701       var offsetY = Math.floor(bounds[1]);
  6702       var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
  6703       var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
  6704       var scaleX = 1, scaleY = 1;
  6705       if (drawnWidth > MAX_GROUP_SIZE) {
  6706         scaleX = drawnWidth / MAX_GROUP_SIZE;
  6707         drawnWidth = MAX_GROUP_SIZE;
  6709       if (drawnHeight > MAX_GROUP_SIZE) {
  6710         scaleY = drawnHeight / MAX_GROUP_SIZE;
  6711         drawnHeight = MAX_GROUP_SIZE;
  6714       var cacheId = 'groupAt' + this.groupLevel;
  6715       if (group.smask) {
  6716         // Using two cache entries is case if masks are used one after another.
  6717         cacheId +=  '_smask_' + ((this.smaskCounter++) % 2);
  6719       var scratchCanvas = CachedCanvases.getCanvas(
  6720         cacheId, drawnWidth, drawnHeight, true);
  6721       var groupCtx = scratchCanvas.context;
  6723       // Since we created a new canvas that is just the size of the bounding box
  6724       // we have to translate the group ctx.
  6725       groupCtx.scale(1 / scaleX, 1 / scaleY);
  6726       groupCtx.translate(-offsetX, -offsetY);
  6727       groupCtx.transform.apply(groupCtx, currentTransform);
  6729       if (group.smask) {
  6730         // Saving state and cached mask to be used in setGState.
  6731         this.smaskStack.push({
  6732           canvas: scratchCanvas.canvas,
  6733           context: groupCtx,
  6734           offsetX: offsetX,
  6735           offsetY: offsetY,
  6736           scaleX: scaleX,
  6737           scaleY: scaleY,
  6738           subtype: group.smask.subtype,
  6739           backdrop: group.smask.backdrop,
  6740           colorSpace: group.colorSpace && ColorSpace.fromIR(group.colorSpace)
  6741         });
  6742       } else {
  6743         // Setup the current ctx so when the group is popped we draw it at the
  6744         // right location.
  6745         currentCtx.setTransform(1, 0, 0, 1, 0, 0);
  6746         currentCtx.translate(offsetX, offsetY);
  6747         currentCtx.scale(scaleX, scaleY);
  6749       // The transparency group inherits all off the current graphics state
  6750       // except the blend mode, soft mask, and alpha constants.
  6751       copyCtxState(currentCtx, groupCtx);
  6752       this.ctx = groupCtx;
  6753       this.setGState([
  6754         ['BM', 'Normal'],
  6755         ['ca', 1],
  6756         ['CA', 1]
  6757       ]);
  6758       this.groupStack.push(currentCtx);
  6759       this.groupLevel++;
  6760     },
  6762     endGroup: function CanvasGraphics_endGroup(group) {
  6763       this.groupLevel--;
  6764       var groupCtx = this.ctx;
  6765       this.ctx = this.groupStack.pop();
  6766       // Turn off image smoothing to avoid sub pixel interpolation which can
  6767       // look kind of blurry for some pdfs.
  6768       if ('imageSmoothingEnabled' in this.ctx) {
  6769         this.ctx.imageSmoothingEnabled = false;
  6770       } else {
  6771         this.ctx.mozImageSmoothingEnabled = false;
  6773       if (group.smask) {
  6774         this.tempSMask = this.smaskStack.pop();
  6775       } else {
  6776         this.ctx.drawImage(groupCtx.canvas, 0, 0);
  6778       this.restore();
  6779     },
  6781     beginAnnotations: function CanvasGraphics_beginAnnotations() {
  6782       this.save();
  6783       this.current = new CanvasExtraState();
  6784     },
  6786     endAnnotations: function CanvasGraphics_endAnnotations() {
  6787       this.restore();
  6788     },
  6790     beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform,
  6791                                                              matrix) {
  6792       this.save();
  6794       if (rect && isArray(rect) && 4 == rect.length) {
  6795         var width = rect[2] - rect[0];
  6796         var height = rect[3] - rect[1];
  6797         this.rectangle(rect[0], rect[1], width, height);
  6798         this.clip();
  6799         this.endPath();
  6802       this.transform.apply(this, transform);
  6803       this.transform.apply(this, matrix);
  6804     },
  6806     endAnnotation: function CanvasGraphics_endAnnotation() {
  6807       this.restore();
  6808     },
  6810     paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
  6811       var domImage = this.objs.get(objId);
  6812       if (!domImage) {
  6813         warn('Dependent image isn\'t ready yet');
  6814         return;
  6817       this.save();
  6819       var ctx = this.ctx;
  6820       // scale the image to the unit square
  6821       ctx.scale(1 / w, -1 / h);
  6823       ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
  6824                     0, -h, w, h);
  6825       if (this.imageLayer) {
  6826         var currentTransform = ctx.mozCurrentTransformInverse;
  6827         var position = this.getCanvasPosition(0, 0);
  6828         this.imageLayer.appendImage({
  6829           objId: objId,
  6830           left: position[0],
  6831           top: position[1],
  6832           width: w / currentTransform[0],
  6833           height: h / currentTransform[3]
  6834         });
  6836       this.restore();
  6837     },
  6839     paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
  6840       var ctx = this.ctx;
  6841       var width = img.width, height = img.height;
  6843       var glyph = this.processingType3;
  6845       if (COMPILE_TYPE3_GLYPHS && glyph && !('compiled' in glyph)) {
  6846         var MAX_SIZE_TO_COMPILE = 1000;
  6847         if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) {
  6848           glyph.compiled =
  6849             compileType3Glyph({data: img.data, width: width, height: height});
  6850         } else {
  6851           glyph.compiled = null;
  6855       if (glyph && glyph.compiled) {
  6856         glyph.compiled(ctx);
  6857         return;
  6860       var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
  6861       var maskCtx = maskCanvas.context;
  6862       maskCtx.save();
  6864       putBinaryImageMask(maskCtx, img);
  6866       maskCtx.globalCompositeOperation = 'source-in';
  6868       var fillColor = this.current.fillColor;
  6869       maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
  6870                           fillColor.type === 'Pattern') ?
  6871                           fillColor.getPattern(maskCtx, this) : fillColor;
  6872       maskCtx.fillRect(0, 0, width, height);
  6874       maskCtx.restore();
  6876       this.paintInlineImageXObject(maskCanvas.canvas);
  6877     },
  6879     paintImageMaskXObjectRepeat:
  6880       function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX,
  6881                                                           scaleY, positions) {
  6882       var width = imgData.width;
  6883       var height = imgData.height;
  6884       var ctx = this.ctx;
  6886       var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
  6887       var maskCtx = maskCanvas.context;
  6888       maskCtx.save();
  6890       putBinaryImageMask(maskCtx, imgData);
  6892       maskCtx.globalCompositeOperation = 'source-in';
  6894       var fillColor = this.current.fillColor;
  6895       maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
  6896         fillColor.type === 'Pattern') ?
  6897         fillColor.getPattern(maskCtx, this) : fillColor;
  6898       maskCtx.fillRect(0, 0, width, height);
  6900       maskCtx.restore();
  6902       for (var i = 0, ii = positions.length; i < ii; i += 2) {
  6903         ctx.save();
  6904         ctx.transform(scaleX, 0, 0, scaleY, positions[i], positions[i + 1]);
  6905         ctx.scale(1, -1);
  6906         ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
  6907           0, -1, 1, 1);
  6908         ctx.restore();
  6910     },
  6912     paintImageMaskXObjectGroup:
  6913       function CanvasGraphics_paintImageMaskXObjectGroup(images) {
  6914       var ctx = this.ctx;
  6916       for (var i = 0, ii = images.length; i < ii; i++) {
  6917         var image = images[i];
  6918         var width = image.width, height = image.height;
  6920         var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
  6921         var maskCtx = maskCanvas.context;
  6922         maskCtx.save();
  6924         putBinaryImageMask(maskCtx, image);
  6926         maskCtx.globalCompositeOperation = 'source-in';
  6928         var fillColor = this.current.fillColor;
  6929         maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
  6930                             fillColor.type === 'Pattern') ?
  6931                             fillColor.getPattern(maskCtx, this) : fillColor;
  6932         maskCtx.fillRect(0, 0, width, height);
  6934         maskCtx.restore();
  6936         ctx.save();
  6937         ctx.transform.apply(ctx, image.transform);
  6938         ctx.scale(1, -1);
  6939         ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
  6940                       0, -1, 1, 1);
  6941         ctx.restore();
  6943     },
  6945     paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
  6946       var imgData = this.objs.get(objId);
  6947       if (!imgData) {
  6948         warn('Dependent image isn\'t ready yet');
  6949         return;
  6952       this.paintInlineImageXObject(imgData);
  6953     },
  6955     paintImageXObjectRepeat:
  6956       function CanvasGraphics_paintImageXObjectRepeat(objId, scaleX, scaleY,
  6957                                                           positions) {
  6958       var imgData = this.objs.get(objId);
  6959       if (!imgData) {
  6960         warn('Dependent image isn\'t ready yet');
  6961         return;
  6964       var width = imgData.width;
  6965       var height = imgData.height;
  6966       var map = [];
  6967       for (var i = 0, ii = positions.length; i < ii; i += 2) {
  6968         map.push({transform: [scaleX, 0, 0, scaleY, positions[i],
  6969                  positions[i + 1]], x: 0, y: 0, w: width, h: height});
  6971       this.paintInlineImageXObjectGroup(imgData, map);
  6972     },
  6974     paintInlineImageXObject:
  6975       function CanvasGraphics_paintInlineImageXObject(imgData) {
  6976       var width = imgData.width;
  6977       var height = imgData.height;
  6978       var ctx = this.ctx;
  6980       this.save();
  6981       // scale the image to the unit square
  6982       ctx.scale(1 / width, -1 / height);
  6984       var currentTransform = ctx.mozCurrentTransformInverse;
  6985       var a = currentTransform[0], b = currentTransform[1];
  6986       var widthScale = Math.max(Math.sqrt(a * a + b * b), 1);
  6987       var c = currentTransform[2], d = currentTransform[3];
  6988       var heightScale = Math.max(Math.sqrt(c * c + d * d), 1);
  6990       var imgToPaint, tmpCanvas;
  6991       // instanceof HTMLElement does not work in jsdom node.js module
  6992       if (imgData instanceof HTMLElement || !imgData.data) {
  6993         imgToPaint = imgData;
  6994       } else {
  6995         tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height);
  6996         var tmpCtx = tmpCanvas.context;
  6997         putBinaryImageData(tmpCtx, imgData);
  6998         imgToPaint = tmpCanvas.canvas;
  7001       var paintWidth = width, paintHeight = height;
  7002       var tmpCanvasId = 'prescale1';
  7003       // Vertial or horizontal scaling shall not be more than 2 to not loose the
  7004       // pixels during drawImage operation, painting on the temporary canvas(es)
  7005       // that are twice smaller in size
  7006       while ((widthScale > 2 && paintWidth > 1) ||
  7007              (heightScale > 2 && paintHeight > 1)) {
  7008         var newWidth = paintWidth, newHeight = paintHeight;
  7009         if (widthScale > 2 && paintWidth > 1) {
  7010           newWidth = Math.ceil(paintWidth / 2);
  7011           widthScale /= paintWidth / newWidth;
  7013         if (heightScale > 2 && paintHeight > 1) {
  7014           newHeight = Math.ceil(paintHeight / 2);
  7015           heightScale /= paintHeight / newHeight;
  7017         tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
  7018         tmpCtx = tmpCanvas.context;
  7019         tmpCtx.clearRect(0, 0, newWidth, newHeight);
  7020         tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
  7021                                      0, 0, newWidth, newHeight);
  7022         imgToPaint = tmpCanvas.canvas;
  7023         paintWidth = newWidth;
  7024         paintHeight = newHeight;
  7025         tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1';
  7027       ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
  7028                                 0, -height, width, height);
  7030       if (this.imageLayer) {
  7031         var position = this.getCanvasPosition(0, -height);
  7032         this.imageLayer.appendImage({
  7033           imgData: imgData,
  7034           left: position[0],
  7035           top: position[1],
  7036           width: width / currentTransform[0],
  7037           height: height / currentTransform[3]
  7038         });
  7040       this.restore();
  7041     },
  7043     paintInlineImageXObjectGroup:
  7044       function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
  7045       var ctx = this.ctx;
  7046       var w = imgData.width;
  7047       var h = imgData.height;
  7049       var tmpCanvas = CachedCanvases.getCanvas('inlineImage', w, h);
  7050       var tmpCtx = tmpCanvas.context;
  7051       putBinaryImageData(tmpCtx, imgData);
  7053       for (var i = 0, ii = map.length; i < ii; i++) {
  7054         var entry = map[i];
  7055         ctx.save();
  7056         ctx.transform.apply(ctx, entry.transform);
  7057         ctx.scale(1, -1);
  7058         ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h,
  7059                       0, -1, 1, 1);
  7060         if (this.imageLayer) {
  7061           var position = this.getCanvasPosition(entry.x, entry.y);
  7062           this.imageLayer.appendImage({
  7063             imgData: imgData,
  7064             left: position[0],
  7065             top: position[1],
  7066             width: w,
  7067             height: h
  7068           });
  7070         ctx.restore();
  7072     },
  7074     paintSolidColorImageMask:
  7075       function CanvasGraphics_paintSolidColorImageMask() {
  7076         this.ctx.fillRect(0, 0, 1, 1);
  7077     },
  7079     // Marked content
  7081     markPoint: function CanvasGraphics_markPoint(tag) {
  7082       // TODO Marked content.
  7083     },
  7084     markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
  7085       // TODO Marked content.
  7086     },
  7087     beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
  7088       // TODO Marked content.
  7089     },
  7090     beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
  7091                                         tag, properties) {
  7092       // TODO Marked content.
  7093     },
  7094     endMarkedContent: function CanvasGraphics_endMarkedContent() {
  7095       // TODO Marked content.
  7096     },
  7098     // Compatibility
  7100     beginCompat: function CanvasGraphics_beginCompat() {
  7101       // TODO ignore undefined operators (should we do that anyway?)
  7102     },
  7103     endCompat: function CanvasGraphics_endCompat() {
  7104       // TODO stop ignoring undefined operators
  7105     },
  7107     // Helper functions
  7109     consumePath: function CanvasGraphics_consumePath() {
  7110       if (this.pendingClip) {
  7111         if (this.pendingClip == EO_CLIP) {
  7112           if ('mozFillRule' in this.ctx) {
  7113             this.ctx.mozFillRule = 'evenodd';
  7114             this.ctx.clip();
  7115             this.ctx.mozFillRule = 'nonzero';
  7116           } else {
  7117             try {
  7118               this.ctx.clip('evenodd');
  7119             } catch (ex) {
  7120               // shouldn't really happen, but browsers might think differently
  7121               this.ctx.clip();
  7124         } else {
  7125           this.ctx.clip();
  7127         this.pendingClip = null;
  7129       this.ctx.beginPath();
  7130     },
  7131     getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
  7132       var inverse = this.ctx.mozCurrentTransformInverse;
  7133       // max of the current horizontal and vertical scale
  7134       return Math.sqrt(Math.max(
  7135         (inverse[0] * inverse[0] + inverse[1] * inverse[1]),
  7136         (inverse[2] * inverse[2] + inverse[3] * inverse[3])));
  7137     },
  7138     getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) {
  7139         var transform = this.ctx.mozCurrentTransform;
  7140         return [
  7141           transform[0] * x + transform[2] * y + transform[4],
  7142           transform[1] * x + transform[3] * y + transform[5]
  7143         ];
  7145   };
  7147   for (var op in OPS) {
  7148     CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
  7151   return CanvasGraphics;
  7152 })();
  7156 var WebGLUtils = (function WebGLUtilsClosure() {
  7157   function loadShader(gl, code, shaderType) {
  7158     var shader = gl.createShader(shaderType);
  7159     gl.shaderSource(shader, code);
  7160     gl.compileShader(shader);
  7161     var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  7162     if (!compiled) {
  7163       var errorMsg = gl.getShaderInfoLog(shader);
  7164       throw new Error('Error during shader compilation: ' + errorMsg);
  7166     return shader;
  7168   function createVertexShader(gl, code) {
  7169     return loadShader(gl, code, gl.VERTEX_SHADER);
  7171   function createFragmentShader(gl, code) {
  7172     return loadShader(gl, code, gl.FRAGMENT_SHADER);
  7174   function createProgram(gl, shaders) {
  7175     var program = gl.createProgram();
  7176     for (var i = 0, ii = shaders.length; i < ii; ++i) {
  7177       gl.attachShader(program, shaders[i]);
  7179     gl.linkProgram(program);
  7180     var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  7181     if (!linked) {
  7182       var errorMsg = gl.getProgramInfoLog(program);
  7183       throw new Error('Error during program linking: ' + errorMsg);
  7185     return program;
  7187   function createTexture(gl, image, textureId) {
  7188     gl.activeTexture(textureId);
  7189     var texture = gl.createTexture();
  7190     gl.bindTexture(gl.TEXTURE_2D, texture);
  7192     // Set the parameters so we can render any size image.
  7193     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  7194     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  7195     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  7196     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  7198     // Upload the image into the texture.
  7199     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
  7200     return texture;
  7203   var currentGL, currentCanvas;
  7204   function generageGL() {
  7205     if (currentGL) {
  7206       return;
  7208     currentCanvas = document.createElement('canvas');
  7209     currentGL = currentCanvas.getContext('webgl',
  7210       { premultipliedalpha: false });
  7213   var smaskVertexShaderCode = '\
  7214   attribute vec2 a_position;                                    \
  7215   attribute vec2 a_texCoord;                                    \
  7217   uniform vec2 u_resolution;                                    \
  7219   varying vec2 v_texCoord;                                      \
  7221   void main() {                                                 \
  7222     vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0;   \
  7223     gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);          \
  7225     v_texCoord = a_texCoord;                                    \
  7226   }                                                             ';
  7228   var smaskFragmentShaderCode = '\
  7229   precision mediump float;                                      \
  7231   uniform vec4 u_backdrop;                                      \
  7232   uniform int u_subtype;                                        \
  7233   uniform sampler2D u_image;                                    \
  7234   uniform sampler2D u_mask;                                     \
  7236   varying vec2 v_texCoord;                                      \
  7238   void main() {                                                 \
  7239     vec4 imageColor = texture2D(u_image, v_texCoord);           \
  7240     vec4 maskColor = texture2D(u_mask, v_texCoord);             \
  7241     if (u_backdrop.a > 0.0) {                                   \
  7242       maskColor.rgb = maskColor.rgb * maskColor.a +             \
  7243                       u_backdrop.rgb * (1.0 - maskColor.a);     \
  7244     }                                                           \
  7245     float lum;                                                  \
  7246     if (u_subtype == 0) {                                       \
  7247       lum = maskColor.a;                                        \
  7248     } else {                                                    \
  7249       lum = maskColor.r * 0.3 + maskColor.g * 0.59 +            \
  7250             maskColor.b * 0.11;                                 \
  7251     }                                                           \
  7252     imageColor.a *= lum;                                        \
  7253     imageColor.rgb *= imageColor.a;                             \
  7254     gl_FragColor = imageColor;                                  \
  7255   }                                                             ';
  7257   var smaskCache = null;
  7259   function initSmaskGL() {
  7260     var canvas, gl;
  7262     generageGL();
  7263     canvas = currentCanvas;
  7264     currentCanvas = null;
  7265     gl = currentGL;
  7266     currentGL = null;
  7268     // setup a GLSL program
  7269     var vertexShader = createVertexShader(gl, smaskVertexShaderCode);
  7270     var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
  7271     var program = createProgram(gl, [vertexShader, fragmentShader]);
  7272     gl.useProgram(program);
  7274     var cache = {};
  7275     cache.gl = gl;
  7276     cache.canvas = canvas;
  7277     cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
  7278     cache.positionLocation = gl.getAttribLocation(program, 'a_position');
  7279     cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop');
  7280     cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype');
  7282     var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
  7283     var texLayerLocation = gl.getUniformLocation(program, 'u_image');
  7284     var texMaskLocation = gl.getUniformLocation(program, 'u_mask');
  7286     // provide texture coordinates for the rectangle.
  7287     var texCoordBuffer = gl.createBuffer();
  7288     gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
  7289     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  7290       0.0,  0.0,
  7291       1.0,  0.0,
  7292       0.0,  1.0,
  7293       0.0,  1.0,
  7294       1.0,  0.0,
  7295       1.0,  1.0]), gl.STATIC_DRAW);
  7296     gl.enableVertexAttribArray(texCoordLocation);
  7297     gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
  7299     gl.uniform1i(texLayerLocation, 0);
  7300     gl.uniform1i(texMaskLocation, 1);
  7302     smaskCache = cache;
  7305   function composeSMask(layer, mask, properties) {
  7306     var width = layer.width, height = layer.height;
  7308     if (!smaskCache) {
  7309       initSmaskGL();
  7311     var cache = smaskCache,canvas = cache.canvas, gl = cache.gl;
  7312     canvas.width = width;
  7313     canvas.height = height;
  7314     gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  7315     gl.uniform2f(cache.resolutionLocation, width, height);
  7317     if (properties.backdrop) {
  7318       gl.uniform4f(cache.resolutionLocation, properties.backdrop[0],
  7319                    properties.backdrop[1], properties.backdrop[2], 1);
  7320     } else {
  7321       gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0);
  7323     gl.uniform1i(cache.subtypeLocation,
  7324                  properties.subtype === 'Luminosity' ? 1 : 0);
  7326     // Create a textures
  7327     var texture = createTexture(gl, layer, gl.TEXTURE0);
  7328     var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
  7331     // Create a buffer and put a single clipspace rectangle in
  7332     // it (2 triangles)
  7333     var buffer = gl.createBuffer();
  7334     gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  7335     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  7336       0, 0,
  7337       width, 0,
  7338       0, height,
  7339       0, height,
  7340       width, 0,
  7341       width, height]), gl.STATIC_DRAW);
  7342     gl.enableVertexAttribArray(cache.positionLocation);
  7343     gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
  7345     // draw
  7346     gl.clearColor(0, 0, 0, 0);
  7347     gl.enable(gl.BLEND);
  7348     gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  7349     gl.clear(gl.COLOR_BUFFER_BIT);
  7351     gl.drawArrays(gl.TRIANGLES, 0, 6);
  7353     gl.flush();
  7355     gl.deleteTexture(texture);
  7356     gl.deleteTexture(maskTexture);
  7357     gl.deleteBuffer(buffer);
  7359     return canvas;
  7362   var figuresVertexShaderCode = '\
  7363   attribute vec2 a_position;                                    \
  7364   attribute vec3 a_color;                                       \
  7366   uniform vec2 u_resolution;                                    \
  7367   uniform vec2 u_scale;                                         \
  7368   uniform vec2 u_offset;                                        \
  7370   varying vec4 v_color;                                         \
  7372   void main() {                                                 \
  7373     vec2 position = (a_position + u_offset) * u_scale;          \
  7374     vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0;     \
  7375     gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);          \
  7377     v_color = vec4(a_color / 255.0, 1.0);                       \
  7378   }                                                             ';
  7380   var figuresFragmentShaderCode = '\
  7381   precision mediump float;                                      \
  7383   varying vec4 v_color;                                         \
  7385   void main() {                                                 \
  7386     gl_FragColor = v_color;                                     \
  7387   }                                                             ';
  7389   var figuresCache = null;
  7391   function initFiguresGL() {
  7392     var canvas, gl;
  7394     generageGL();
  7395     canvas = currentCanvas;
  7396     currentCanvas = null;
  7397     gl = currentGL;
  7398     currentGL = null;
  7400     // setup a GLSL program
  7401     var vertexShader = createVertexShader(gl, figuresVertexShaderCode);
  7402     var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
  7403     var program = createProgram(gl, [vertexShader, fragmentShader]);
  7404     gl.useProgram(program);
  7406     var cache = {};
  7407     cache.gl = gl;
  7408     cache.canvas = canvas;
  7409     cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
  7410     cache.scaleLocation = gl.getUniformLocation(program, 'u_scale');
  7411     cache.offsetLocation = gl.getUniformLocation(program, 'u_offset');
  7412     cache.positionLocation = gl.getAttribLocation(program, 'a_position');
  7413     cache.colorLocation = gl.getAttribLocation(program, 'a_color');
  7415     figuresCache = cache;
  7418   function drawFigures(width, height, backgroundColor, figures, context) {
  7419     if (!figuresCache) {
  7420       initFiguresGL();
  7422     var cache = figuresCache, canvas = cache.canvas, gl = cache.gl;
  7424     canvas.width = width;
  7425     canvas.height = height;
  7426     gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  7427     gl.uniform2f(cache.resolutionLocation, width, height);
  7429     // count triangle points
  7430     var count = 0;
  7431     var i, ii, rows;
  7432     for (i = 0, ii = figures.length; i < ii; i++) {
  7433       switch (figures[i].type) {
  7434         case 'lattice':
  7435           rows = (figures[i].coords.length / figures[i].verticesPerRow) | 0;
  7436           count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6;
  7437           break;
  7438         case 'triangles':
  7439           count += figures[i].coords.length;
  7440           break;
  7443     // transfer data
  7444     var coords = new Float32Array(count * 2);
  7445     var colors = new Uint8Array(count * 3);
  7446     var coordsMap = context.coords, colorsMap = context.colors;
  7447     var pIndex = 0, cIndex = 0;
  7448     for (i = 0, ii = figures.length; i < ii; i++) {
  7449       var figure = figures[i], ps = figure.coords, cs = figure.colors;
  7450       switch (figure.type) {
  7451         case 'lattice':
  7452           var cols = figure.verticesPerRow;
  7453           rows = (ps.length / cols) | 0;
  7454           for (var row = 1; row < rows; row++) {
  7455             var offset = row * cols + 1;
  7456             for (var col = 1; col < cols; col++, offset++) {
  7457               coords[pIndex] = coordsMap[ps[offset - cols - 1]];
  7458               coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1];
  7459               coords[pIndex + 2] = coordsMap[ps[offset - cols]];
  7460               coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1];
  7461               coords[pIndex + 4] = coordsMap[ps[offset - 1]];
  7462               coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1];
  7463               colors[cIndex] = colorsMap[cs[offset - cols - 1]];
  7464               colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1];
  7465               colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2];
  7466               colors[cIndex + 3] = colorsMap[cs[offset - cols]];
  7467               colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1];
  7468               colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2];
  7469               colors[cIndex + 6] = colorsMap[cs[offset - 1]];
  7470               colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1];
  7471               colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2];
  7473               coords[pIndex + 6] = coords[pIndex + 2];
  7474               coords[pIndex + 7] = coords[pIndex + 3];
  7475               coords[pIndex + 8] = coords[pIndex + 4];
  7476               coords[pIndex + 9] = coords[pIndex + 5];
  7477               coords[pIndex + 10] = coordsMap[ps[offset]];
  7478               coords[pIndex + 11] = coordsMap[ps[offset] + 1];
  7479               colors[cIndex + 9] = colors[cIndex + 3];
  7480               colors[cIndex + 10] = colors[cIndex + 4];
  7481               colors[cIndex + 11] = colors[cIndex + 5];
  7482               colors[cIndex + 12] = colors[cIndex + 6];
  7483               colors[cIndex + 13] = colors[cIndex + 7];
  7484               colors[cIndex + 14] = colors[cIndex + 8];
  7485               colors[cIndex + 15] = colorsMap[cs[offset]];
  7486               colors[cIndex + 16] = colorsMap[cs[offset] + 1];
  7487               colors[cIndex + 17] = colorsMap[cs[offset] + 2];
  7488               pIndex += 12;
  7489               cIndex += 18;
  7492           break;
  7493         case 'triangles':
  7494           for (var j = 0, jj = ps.length; j < jj; j++) {
  7495             coords[pIndex] = coordsMap[ps[j]];
  7496             coords[pIndex + 1] = coordsMap[ps[j] + 1];
  7497             colors[cIndex] = colorsMap[cs[i]];
  7498             colors[cIndex + 1] = colorsMap[cs[j] + 1];
  7499             colors[cIndex + 2] = colorsMap[cs[j] + 2];
  7500             pIndex += 2;
  7501             cIndex += 3;
  7503           break;
  7507     // draw
  7508     if (backgroundColor) {
  7509       gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255,
  7510                     backgroundColor[2] / 255, 1.0);
  7511     } else {
  7512       gl.clearColor(0, 0, 0, 0);
  7514     gl.clear(gl.COLOR_BUFFER_BIT);
  7516     var coordsBuffer = gl.createBuffer();
  7517     gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer);
  7518     gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
  7519     gl.enableVertexAttribArray(cache.positionLocation);
  7520     gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
  7522     var colorsBuffer = gl.createBuffer();
  7523     gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
  7524     gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
  7525     gl.enableVertexAttribArray(cache.colorLocation);
  7526     gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false,
  7527                            0, 0);
  7529     gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY);
  7530     gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY);
  7532     gl.drawArrays(gl.TRIANGLES, 0, count);
  7534     gl.flush();
  7536     gl.deleteBuffer(coordsBuffer);
  7537     gl.deleteBuffer(colorsBuffer);
  7539     return canvas;
  7542   function cleanup() {
  7543     smaskCache = null;
  7544     figuresCache = null;
  7547   return {
  7548     get isEnabled() {
  7549       if (PDFJS.disableWebGL) {
  7550         return false;
  7552       var enabled = false;
  7553       try {
  7554         generageGL();
  7555         enabled = !!currentGL;
  7556       } catch (e) { }
  7557       return shadow(this, 'isEnabled', enabled);
  7558     },
  7559     composeSMask: composeSMask,
  7560     drawFigures: drawFigures,
  7561     clear: cleanup
  7562   };
  7563 })();
  7566 var ShadingIRs = {};
  7568 ShadingIRs.RadialAxial = {
  7569   fromIR: function RadialAxial_fromIR(raw) {
  7570     var type = raw[1];
  7571     var colorStops = raw[2];
  7572     var p0 = raw[3];
  7573     var p1 = raw[4];
  7574     var r0 = raw[5];
  7575     var r1 = raw[6];
  7576     return {
  7577       type: 'Pattern',
  7578       getPattern: function RadialAxial_getPattern(ctx) {
  7579         var grad;
  7580         if (type === 'axial') {
  7581           grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
  7582         } else if (type === 'radial') {
  7583           grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
  7586         for (var i = 0, ii = colorStops.length; i < ii; ++i) {
  7587           var c = colorStops[i];
  7588           grad.addColorStop(c[0], c[1]);
  7590         return grad;
  7592     };
  7594 };
  7596 var createMeshCanvas = (function createMeshCanvasClosure() {
  7597   function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
  7598     // Very basic Gouraud-shaded triangle rasterization algorithm.
  7599     var coords = context.coords, colors = context.colors;
  7600     var bytes = data.data, rowSize = data.width * 4;
  7601     var tmp;
  7602     if (coords[p1 + 1] > coords[p2 + 1]) {
  7603       tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp;
  7605     if (coords[p2 + 1] > coords[p3 + 1]) {
  7606       tmp = p2; p2 = p3; p3 = tmp; tmp = c2; c2 = c3; c3 = tmp;
  7608     if (coords[p1 + 1] > coords[p2 + 1]) {
  7609       tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp;
  7611     var x1 = (coords[p1] + context.offsetX) * context.scaleX;
  7612     var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
  7613     var x2 = (coords[p2] + context.offsetX) * context.scaleX;
  7614     var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
  7615     var x3 = (coords[p3] + context.offsetX) * context.scaleX;
  7616     var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
  7617     if (y1 >= y3) {
  7618       return;
  7620     var c1r = colors[c1], c1g = colors[c1 + 1], c1b = colors[c1 + 2];
  7621     var c2r = colors[c2], c2g = colors[c2 + 1], c2b = colors[c2 + 2];
  7622     var c3r = colors[c3], c3g = colors[c3 + 1], c3b = colors[c3 + 2];
  7624     var minY = Math.round(y1), maxY = Math.round(y3);
  7625     var xa, car, cag, cab;
  7626     var xb, cbr, cbg, cbb;
  7627     var k;
  7628     for (var y = minY; y <= maxY; y++) {
  7629       if (y < y2) {
  7630         k = y < y1 ? 0 : y1 === y2 ? 1 : (y1 - y) / (y1 - y2);
  7631         xa = x1 - (x1 - x2) * k;
  7632         car = c1r - (c1r - c2r) * k;
  7633         cag = c1g - (c1g - c2g) * k;
  7634         cab = c1b - (c1b - c2b) * k;
  7635       } else {
  7636         k = y > y3 ? 1 : y2 === y3 ? 0 : (y2 - y) / (y2 - y3);
  7637         xa = x2 - (x2 - x3) * k;
  7638         car = c2r - (c2r - c3r) * k;
  7639         cag = c2g - (c2g - c3g) * k;
  7640         cab = c2b - (c2b - c3b) * k;
  7642       k = y < y1 ? 0 : y > y3 ? 1 : (y1 - y) / (y1 - y3);
  7643       xb = x1 - (x1 - x3) * k;
  7644       cbr = c1r - (c1r - c3r) * k;
  7645       cbg = c1g - (c1g - c3g) * k;
  7646       cbb = c1b - (c1b - c3b) * k;
  7647       var x1_ = Math.round(Math.min(xa, xb));
  7648       var x2_ = Math.round(Math.max(xa, xb));
  7649       var j = rowSize * y + x1_ * 4;
  7650       for (var x = x1_; x <= x2_; x++) {
  7651         k = (xa - x) / (xa - xb);
  7652         k = k < 0 ? 0 : k > 1 ? 1 : k;
  7653         bytes[j++] = (car - (car - cbr) * k) | 0;
  7654         bytes[j++] = (cag - (cag - cbg) * k) | 0;
  7655         bytes[j++] = (cab - (cab - cbb) * k) | 0;
  7656         bytes[j++] = 255;
  7661   function drawFigure(data, figure, context) {
  7662     var ps = figure.coords;
  7663     var cs = figure.colors;
  7664     var i, ii;
  7665     switch (figure.type) {
  7666       case 'lattice':
  7667         var verticesPerRow = figure.verticesPerRow;
  7668         var rows = Math.floor(ps.length / verticesPerRow) - 1;
  7669         var cols = verticesPerRow - 1;
  7670         for (i = 0; i < rows; i++) {
  7671           var q = i * verticesPerRow;
  7672           for (var j = 0; j < cols; j++, q++) {
  7673             drawTriangle(data, context,
  7674               ps[q], ps[q + 1], ps[q + verticesPerRow],
  7675               cs[q], cs[q + 1], cs[q + verticesPerRow]);
  7676             drawTriangle(data, context,
  7677               ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow],
  7678               cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
  7681         break;
  7682       case 'triangles':
  7683         for (i = 0, ii = ps.length; i < ii; i += 3) {
  7684           drawTriangle(data, context,
  7685             ps[i], ps[i + 1], ps[i + 2],
  7686             cs[i], cs[i + 1], cs[i + 2]);
  7688         break;
  7689       default:
  7690         error('illigal figure');
  7691         break;
  7695   function createMeshCanvas(bounds, combinesScale, coords, colors, figures,
  7696                             backgroundColor) {
  7697     // we will increase scale on some weird factor to let antialiasing take
  7698     // care of "rough" edges
  7699     var EXPECTED_SCALE = 1.1;
  7700     // MAX_PATTERN_SIZE is used to avoid OOM situation.
  7701     var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
  7703     var offsetX = Math.floor(bounds[0]);
  7704     var offsetY = Math.floor(bounds[1]);
  7705     var boundsWidth = Math.ceil(bounds[2]) - offsetX;
  7706     var boundsHeight = Math.ceil(bounds[3]) - offsetY;
  7708     var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] *
  7709       EXPECTED_SCALE)), MAX_PATTERN_SIZE);
  7710     var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] *
  7711       EXPECTED_SCALE)), MAX_PATTERN_SIZE);
  7712     var scaleX = boundsWidth / width;
  7713     var scaleY = boundsHeight / height;
  7715     var context = {
  7716       coords: coords,
  7717       colors: colors,
  7718       offsetX: -offsetX,
  7719       offsetY: -offsetY,
  7720       scaleX: 1 / scaleX,
  7721       scaleY: 1 / scaleY
  7722     };
  7724     var canvas, tmpCanvas, i, ii;
  7725     if (WebGLUtils.isEnabled) {
  7726       canvas = WebGLUtils.drawFigures(width, height, backgroundColor,
  7727                                       figures, context);
  7729       // https://bugzilla.mozilla.org/show_bug.cgi?id=972126
  7730       tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false);
  7731       tmpCanvas.context.drawImage(canvas, 0, 0);
  7732       canvas = tmpCanvas.canvas;
  7733     } else {
  7734       tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false);
  7735       var tmpCtx = tmpCanvas.context;
  7737       var data = tmpCtx.createImageData(width, height);
  7738       if (backgroundColor) {
  7739         var bytes = data.data;
  7740         for (i = 0, ii = bytes.length; i < ii; i += 4) {
  7741           bytes[i] = backgroundColor[0];
  7742           bytes[i + 1] = backgroundColor[1];
  7743           bytes[i + 2] = backgroundColor[2];
  7744           bytes[i + 3] = 255;
  7747       for (i = 0; i < figures.length; i++) {
  7748         drawFigure(data, figures[i], context);
  7750       tmpCtx.putImageData(data, 0, 0);
  7751       canvas = tmpCanvas.canvas;
  7754     return {canvas: canvas, offsetX: offsetX, offsetY: offsetY,
  7755             scaleX: scaleX, scaleY: scaleY};
  7757   return createMeshCanvas;
  7758 })();
  7760 ShadingIRs.Mesh = {
  7761   fromIR: function Mesh_fromIR(raw) {
  7762     //var type = raw[1];
  7763     var coords = raw[2];
  7764     var colors = raw[3];
  7765     var figures = raw[4];
  7766     var bounds = raw[5];
  7767     var matrix = raw[6];
  7768     //var bbox = raw[7];
  7769     var background = raw[8];
  7770     return {
  7771       type: 'Pattern',
  7772       getPattern: function Mesh_getPattern(ctx, owner, shadingFill) {
  7773         var combinedScale;
  7774         // Obtain scale from matrix and current transformation matrix.
  7775         if (shadingFill) {
  7776           combinedScale = Util.singularValueDecompose2dScale(
  7777             ctx.mozCurrentTransform);
  7778         } else {
  7779           var matrixScale = Util.singularValueDecompose2dScale(matrix);
  7780           var curMatrixScale = Util.singularValueDecompose2dScale(
  7781             owner.baseTransform);
  7782           combinedScale = [matrixScale[0] * curMatrixScale[0],
  7783             matrixScale[1] * curMatrixScale[1]];
  7787         // Rasterizing on the main thread since sending/queue large canvases
  7788         // might cause OOM.
  7789         var temporaryPatternCanvas = createMeshCanvas(bounds, combinedScale,
  7790           coords, colors, figures, shadingFill ? null : background);
  7792         if (!shadingFill) {
  7793           ctx.setTransform.apply(ctx, owner.baseTransform);
  7794           if (matrix) {
  7795             ctx.transform.apply(ctx, matrix);
  7799         ctx.translate(temporaryPatternCanvas.offsetX,
  7800                       temporaryPatternCanvas.offsetY);
  7801         ctx.scale(temporaryPatternCanvas.scaleX,
  7802                   temporaryPatternCanvas.scaleY);
  7804         return ctx.createPattern(temporaryPatternCanvas.canvas, 'no-repeat');
  7806     };
  7808 };
  7810 ShadingIRs.Dummy = {
  7811   fromIR: function Dummy_fromIR() {
  7812     return {
  7813       type: 'Pattern',
  7814       getPattern: function Dummy_fromIR_getPattern() {
  7815         return 'hotpink';
  7817     };
  7819 };
  7821 function getShadingPatternFromIR(raw) {
  7822   var shadingIR = ShadingIRs[raw[0]];
  7823   if (!shadingIR) {
  7824     error('Unknown IR type: ' + raw[0]);
  7826   return shadingIR.fromIR(raw);
  7829 var TilingPattern = (function TilingPatternClosure() {
  7830   var PaintType = {
  7831     COLORED: 1,
  7832     UNCOLORED: 2
  7833   };
  7835   var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
  7837   function TilingPattern(IR, color, ctx, objs, commonObjs, baseTransform) {
  7838     this.name = IR[1][0].name;
  7839     this.operatorList = IR[2];
  7840     this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
  7841     this.bbox = IR[4];
  7842     this.xstep = IR[5];
  7843     this.ystep = IR[6];
  7844     this.paintType = IR[7];
  7845     this.tilingType = IR[8];
  7846     this.color = color;
  7847     this.objs = objs;
  7848     this.commonObjs = commonObjs;
  7849     this.baseTransform = baseTransform;
  7850     this.type = 'Pattern';
  7851     this.ctx = ctx;
  7854   TilingPattern.prototype = {
  7855     createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
  7856       var operatorList = this.operatorList;
  7857       var bbox = this.bbox;
  7858       var xstep = this.xstep;
  7859       var ystep = this.ystep;
  7860       var paintType = this.paintType;
  7861       var tilingType = this.tilingType;
  7862       var color = this.color;
  7863       var objs = this.objs;
  7864       var commonObjs = this.commonObjs;
  7866       info('TilingType: ' + tilingType);
  7868       var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
  7870       var topLeft = [x0, y0];
  7871       // we want the canvas to be as large as the step size
  7872       var botRight = [x0 + xstep, y0 + ystep];
  7874       var width = botRight[0] - topLeft[0];
  7875       var height = botRight[1] - topLeft[1];
  7877       // Obtain scale from matrix and current transformation matrix.
  7878       var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
  7879       var curMatrixScale = Util.singularValueDecompose2dScale(
  7880         this.baseTransform);
  7881       var combinedScale = [matrixScale[0] * curMatrixScale[0],
  7882         matrixScale[1] * curMatrixScale[1]];
  7884       // MAX_PATTERN_SIZE is used to avoid OOM situation.
  7885       // Use width and height values that are as close as possible to the end
  7886       // result when the pattern is used. Too low value makes the pattern look
  7887       // blurry. Too large value makes it look too crispy.
  7888       width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])),
  7889         MAX_PATTERN_SIZE);
  7891       height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
  7892         MAX_PATTERN_SIZE);
  7894       var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true);
  7895       var tmpCtx = tmpCanvas.context;
  7896       var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs);
  7897       graphics.groupLevel = owner.groupLevel;
  7899       this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color);
  7901       this.setScale(width, height, xstep, ystep);
  7902       this.transformToScale(graphics);
  7904       // transform coordinates to pattern space
  7905       var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
  7906       graphics.transform.apply(graphics, tmpTranslate);
  7908       this.clipBbox(graphics, bbox, x0, y0, x1, y1);
  7910       graphics.executeOperatorList(operatorList);
  7911       return tmpCanvas.canvas;
  7912     },
  7914     setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
  7915       this.scale = [width / xstep, height / ystep];
  7916     },
  7918     transformToScale: function TilingPattern_transformToScale(graphics) {
  7919       var scale = this.scale;
  7920       var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
  7921       graphics.transform.apply(graphics, tmpScale);
  7922     },
  7924     scaleToContext: function TilingPattern_scaleToContext() {
  7925       var scale = this.scale;
  7926       this.ctx.scale(1 / scale[0], 1 / scale[1]);
  7927     },
  7929     clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
  7930       if (bbox && isArray(bbox) && 4 == bbox.length) {
  7931         var bboxWidth = x1 - x0;
  7932         var bboxHeight = y1 - y0;
  7933         graphics.rectangle(x0, y0, bboxWidth, bboxHeight);
  7934         graphics.clip();
  7935         graphics.endPath();
  7937     },
  7939     setFillAndStrokeStyleToContext:
  7940       function setFillAndStrokeStyleToContext(context, paintType, color) {
  7941         switch (paintType) {
  7942           case PaintType.COLORED:
  7943             var ctx = this.ctx;
  7944             context.fillStyle = ctx.fillStyle;
  7945             context.strokeStyle = ctx.strokeStyle;
  7946             break;
  7947           case PaintType.UNCOLORED:
  7948             var rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);
  7949             var cssColor = Util.makeCssRgb(rgbColor);
  7950             context.fillStyle = cssColor;
  7951             context.strokeStyle = cssColor;
  7952             break;
  7953           default:
  7954             error('Unsupported paint type: ' + paintType);
  7956       },
  7958     getPattern: function TilingPattern_getPattern(ctx, owner) {
  7959       var temporaryPatternCanvas = this.createPatternCanvas(owner);
  7961       ctx = this.ctx;
  7962       ctx.setTransform.apply(ctx, this.baseTransform);
  7963       ctx.transform.apply(ctx, this.matrix);
  7964       this.scaleToContext();
  7966       return ctx.createPattern(temporaryPatternCanvas, 'repeat');
  7968   };
  7970   return TilingPattern;
  7971 })();
  7974 PDFJS.disableFontFace = false;
  7976 var FontLoader = {
  7977   insertRule: function fontLoaderInsertRule(rule) {
  7978     var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
  7979     if (!styleElement) {
  7980       styleElement = document.createElement('style');
  7981       styleElement.id = 'PDFJS_FONT_STYLE_TAG';
  7982       document.documentElement.getElementsByTagName('head')[0].appendChild(
  7983         styleElement);
  7986     var styleSheet = styleElement.sheet;
  7987     styleSheet.insertRule(rule, styleSheet.cssRules.length);
  7988   },
  7990   clear: function fontLoaderClear() {
  7991     var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
  7992     if (styleElement) {
  7993       styleElement.parentNode.removeChild(styleElement);
  7995   },
  7996   bind: function fontLoaderBind(fonts, callback) {
  7997     assert(!isWorker, 'bind() shall be called from main thread');
  7999     for (var i = 0, ii = fonts.length; i < ii; i++) {
  8000       var font = fonts[i];
  8001       if (font.attached) {
  8002         continue;
  8005       font.attached = true;
  8006       font.bindDOM()
  8009     setTimeout(callback);
  8011 };
  8013 var FontFace = (function FontFaceClosure() {
  8014   function FontFace(name, file, properties) {
  8015     this.compiledGlyphs = {};
  8016     if (arguments.length === 1) {
  8017       // importing translated data
  8018       var data = arguments[0];
  8019       for (var i in data) {
  8020         this[i] = data[i];
  8022       return;
  8025   FontFace.prototype = {
  8026     bindDOM: function FontFace_bindDOM() {
  8027       if (!this.data) {
  8028         return null;
  8031       if (PDFJS.disableFontFace) {
  8032         this.disableFontFace = true;
  8033         return null;
  8036       var data = bytesToString(new Uint8Array(this.data));
  8037       var fontName = this.loadedName;
  8039       // Add the font-face rule to the document
  8040       var url = ('url(data:' + this.mimetype + ';base64,' +
  8041                  window.btoa(data) + ');');
  8042       var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
  8043       FontLoader.insertRule(rule);
  8045       if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
  8046           globalScope['FontInspector'].enabled) {
  8047         globalScope['FontInspector'].fontAdded(this, url);
  8050       return rule;
  8051     },
  8053     getPathGenerator: function (objs, character) {
  8054       if (!(character in this.compiledGlyphs)) {
  8055         var js = objs.get(this.loadedName + '_path_' + character);
  8056         /*jshint -W054 */
  8057         this.compiledGlyphs[character] = new Function('c', 'size', js);
  8059       return this.compiledGlyphs[character];
  8061   };
  8062   return FontFace;
  8063 })();
  8066 }).call((typeof window === 'undefined') ? this : window);

mercurial