mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 03:47:32 -04:00 
			
		
		
		
	- Set up Elixir for asset management - CSS + JS will be handled via Elixir/Gulp in gulpfile.js - Removed outdates public/js files
		
			
				
	
	
		
			7421 lines
		
	
	
		
			232 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			7421 lines
		
	
	
		
			232 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 | |
| /* Copyright 2012 Mozilla Foundation
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| // Initializing PDFJS global object (if still undefined)
 | |
| if (typeof PDFJS === 'undefined') {
 | |
|   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 | |
| }
 | |
| 
 | |
| PDFJS.version = '0.8.765';
 | |
| PDFJS.build = '88ec2bd';
 | |
| 
 | |
| (function pdfjsWrapper() {
 | |
|   // Use strict in our context only - users might not want it
 | |
|   'use strict';
 | |
| 
 | |
| /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 | |
| /* Copyright 2012 Mozilla Foundation
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *     http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| /* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL */
 | |
| 
 | |
| 'use strict';
 | |
| 
 | |
| var globalScope = (typeof window === 'undefined') ? this : window;
 | |
| 
 | |
| var isWorker = (typeof window == 'undefined');
 | |
| 
 | |
| var ERRORS = 0, WARNINGS = 1, INFOS = 5;
 | |
| var verbosity = WARNINGS;
 | |
| 
 | |
| var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
 | |
| 
 | |
| var TextRenderingMode = {
 | |
|   FILL: 0,
 | |
|   STROKE: 1,
 | |
|   FILL_STROKE: 2,
 | |
|   INVISIBLE: 3,
 | |
|   FILL_ADD_TO_PATH: 4,
 | |
|   STROKE_ADD_TO_PATH: 5,
 | |
|   FILL_STROKE_ADD_TO_PATH: 6,
 | |
|   ADD_TO_PATH: 7,
 | |
|   FILL_STROKE_MASK: 3,
 | |
|   ADD_TO_PATH_FLAG: 4
 | |
| };
 | |
| 
 | |
| // The global PDFJS object exposes the API
 | |
| // In production, it will be declared outside a global wrapper
 | |
| // In development, it will be declared here
 | |
| if (!globalScope.PDFJS) {
 | |
|   globalScope.PDFJS = {};
 | |
| }
 | |
| 
 | |
| globalScope.PDFJS.pdfBug = false;
 | |
| 
 | |
| // All the possible operations for an operator list.
 | |
| var OPS = PDFJS.OPS = {
 | |
|   // Intentionally start from 1 so it is easy to spot bad operators that will be
 | |
|   // 0's.
 | |
|   dependency: 1,
 | |
|   setLineWidth: 2,
 | |
|   setLineCap: 3,
 | |
|   setLineJoin: 4,
 | |
|   setMiterLimit: 5,
 | |
|   setDash: 6,
 | |
|   setRenderingIntent: 7,
 | |
|   setFlatness: 8,
 | |
|   setGState: 9,
 | |
|   save: 10,
 | |
|   restore: 11,
 | |
|   transform: 12,
 | |
|   moveTo: 13,
 | |
|   lineTo: 14,
 | |
|   curveTo: 15,
 | |
|   curveTo2: 16,
 | |
|   curveTo3: 17,
 | |
|   closePath: 18,
 | |
|   rectangle: 19,
 | |
|   stroke: 20,
 | |
|   closeStroke: 21,
 | |
|   fill: 22,
 | |
|   eoFill: 23,
 | |
|   fillStroke: 24,
 | |
|   eoFillStroke: 25,
 | |
|   closeFillStroke: 26,
 | |
|   closeEOFillStroke: 27,
 | |
|   endPath: 28,
 | |
|   clip: 29,
 | |
|   eoClip: 30,
 | |
|   beginText: 31,
 | |
|   endText: 32,
 | |
|   setCharSpacing: 33,
 | |
|   setWordSpacing: 34,
 | |
|   setHScale: 35,
 | |
|   setLeading: 36,
 | |
|   setFont: 37,
 | |
|   setTextRenderingMode: 38,
 | |
|   setTextRise: 39,
 | |
|   moveText: 40,
 | |
|   setLeadingMoveText: 41,
 | |
|   setTextMatrix: 42,
 | |
|   nextLine: 43,
 | |
|   showText: 44,
 | |
|   showSpacedText: 45,
 | |
|   nextLineShowText: 46,
 | |
|   nextLineSetSpacingShowText: 47,
 | |
|   setCharWidth: 48,
 | |
|   setCharWidthAndBounds: 49,
 | |
|   setStrokeColorSpace: 50,
 | |
|   setFillColorSpace: 51,
 | |
|   setStrokeColor: 52,
 | |
|   setStrokeColorN: 53,
 | |
|   setFillColor: 54,
 | |
|   setFillColorN: 55,
 | |
|   setStrokeGray: 56,
 | |
|   setFillGray: 57,
 | |
|   setStrokeRGBColor: 58,
 | |
|   setFillRGBColor: 59,
 | |
|   setStrokeCMYKColor: 60,
 | |
|   setFillCMYKColor: 61,
 | |
|   shadingFill: 62,
 | |
|   beginInlineImage: 63,
 | |
|   beginImageData: 64,
 | |
|   endInlineImage: 65,
 | |
|   paintXObject: 66,
 | |
|   markPoint: 67,
 | |
|   markPointProps: 68,
 | |
|   beginMarkedContent: 69,
 | |
|   beginMarkedContentProps: 70,
 | |
|   endMarkedContent: 71,
 | |
|   beginCompat: 72,
 | |
|   endCompat: 73,
 | |
|   paintFormXObjectBegin: 74,
 | |
|   paintFormXObjectEnd: 75,
 | |
|   beginGroup: 76,
 | |
|   endGroup: 77,
 | |
|   beginAnnotations: 78,
 | |
|   endAnnotations: 79,
 | |
|   beginAnnotation: 80,
 | |
|   endAnnotation: 81,
 | |
|   paintJpegXObject: 82,
 | |
|   paintImageMaskXObject: 83,
 | |
|   paintImageMaskXObjectGroup: 84,
 | |
|   paintImageXObject: 85,
 | |
|   paintInlineImageXObject: 86,
 | |
|   paintInlineImageXObjectGroup: 87
 | |
| };
 | |
| 
 | |
| // Use only for debugging purposes. This should not be used in any code that is
 | |
| // in mozilla master.
 | |
| var log = (function() {
 | |
|   if ('console' in globalScope && 'log' in globalScope['console']) {
 | |
|     return globalScope['console']['log'].bind(globalScope['console']);
 | |
|   } else {
 | |
|     return function nop() {
 | |
|     };
 | |
|   }
 | |
| })();
 | |
| 
 | |
| // A notice for devs that will not trigger the fallback UI.  These are good
 | |
| // for things that are helpful to devs, such as warning that Workers were
 | |
| // disabled, which is important to devs but not end users.
 | |
| function info(msg) {
 | |
|   if (verbosity >= INFOS) {
 | |
|     log('Info: ' + msg);
 | |
|     PDFJS.LogManager.notify('info', msg);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Non-fatal warnings that should trigger the fallback UI.
 | |
| function warn(msg) {
 | |
|   if (verbosity >= WARNINGS) {
 | |
|     log('Warning: ' + msg);
 | |
|     PDFJS.LogManager.notify('warn', msg);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Fatal errors that should trigger the fallback UI and halt execution by
 | |
| // throwing an exception.
 | |
| function error(msg) {
 | |
|   // If multiple arguments were passed, pass them all to the log function.
 | |
|   if (arguments.length > 1) {
 | |
|     var logArguments = ['Error:'];
 | |
|     logArguments.push.apply(logArguments, arguments);
 | |
|     log.apply(null, logArguments);
 | |
|     // Join the arguments into a single string for the lines below.
 | |
|     msg = [].join.call(arguments, ' ');
 | |
|   } else {
 | |
|     log('Error: ' + msg);
 | |
|   }
 | |
|   log(backtrace());
 | |
|   PDFJS.LogManager.notify('error', msg);
 | |
|   throw new Error(msg);
 | |
| }
 | |
| 
 | |
| // Missing features that should trigger the fallback UI.
 | |
| function TODO(what) {
 | |
|   warn('TODO: ' + what);
 | |
| }
 | |
| 
 | |
| function backtrace() {
 | |
|   try {
 | |
|     throw new Error();
 | |
|   } catch (e) {
 | |
|     return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
 | |
|   }
 | |
| }
 | |
| 
 | |
| function assert(cond, msg) {
 | |
|   if (!cond)
 | |
|     error(msg);
 | |
| }
 | |
| 
 | |
| // Combines two URLs. The baseUrl shall be absolute URL. If the url is an
 | |
| // absolute URL, it will be returned as is.
 | |
| function combineUrl(baseUrl, url) {
 | |
|   if (!url)
 | |
|     return baseUrl;
 | |
|   if (url.indexOf(':') >= 0)
 | |
|     return url;
 | |
|   if (url.charAt(0) == '/') {
 | |
|     // absolute path
 | |
|     var i = baseUrl.indexOf('://');
 | |
|     i = baseUrl.indexOf('/', i + 3);
 | |
|     return baseUrl.substring(0, i) + url;
 | |
|   } else {
 | |
|     // relative path
 | |
|     var pathLength = baseUrl.length, i;
 | |
|     i = baseUrl.lastIndexOf('#');
 | |
|     pathLength = i >= 0 ? i : pathLength;
 | |
|     i = baseUrl.lastIndexOf('?', pathLength);
 | |
|     pathLength = i >= 0 ? i : pathLength;
 | |
|     var prefixLength = baseUrl.lastIndexOf('/', pathLength);
 | |
|     return baseUrl.substring(0, prefixLength + 1) + url;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Validates if URL is safe and allowed, e.g. to avoid XSS.
 | |
| function isValidUrl(url, allowRelative) {
 | |
|   if (!url) {
 | |
|     return false;
 | |
|   }
 | |
|   var colon = url.indexOf(':');
 | |
|   if (colon < 0) {
 | |
|     return allowRelative;
 | |
|   }
 | |
|   var protocol = url.substr(0, colon);
 | |
|   switch (protocol) {
 | |
|     case 'http':
 | |
|     case 'https':
 | |
|     case 'ftp':
 | |
|     case 'mailto':
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| PDFJS.isValidUrl = isValidUrl;
 | |
| 
 | |
| // In a well-formed PDF, |cond| holds.  If it doesn't, subsequent
 | |
| // behavior is undefined.
 | |
| function assertWellFormed(cond, msg) {
 | |
|   if (!cond)
 | |
|     error(msg);
 | |
| }
 | |
| 
 | |
| var LogManager = PDFJS.LogManager = (function LogManagerClosure() {
 | |
|   var loggers = [];
 | |
|   return {
 | |
|     addLogger: function logManager_addLogger(logger) {
 | |
|       loggers.push(logger);
 | |
|     },
 | |
|     notify: function(type, message) {
 | |
|       for (var i = 0, ii = loggers.length; i < ii; i++) {
 | |
|         var logger = loggers[i];
 | |
|         if (logger[type])
 | |
|           logger[type](message);
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| })();
 | |
| 
 | |
| function shadow(obj, prop, value) {
 | |
|   Object.defineProperty(obj, prop, { value: value,
 | |
|                                      enumerable: true,
 | |
|                                      configurable: true,
 | |
|                                      writable: false });
 | |
|   return value;
 | |
| }
 | |
| 
 | |
| var PasswordResponses = PDFJS.PasswordResponses = {
 | |
|   NEED_PASSWORD: 1,
 | |
|   INCORRECT_PASSWORD: 2
 | |
| };
 | |
| 
 | |
| var PasswordException = (function PasswordExceptionClosure() {
 | |
|   function PasswordException(msg, code) {
 | |
|     this.name = 'PasswordException';
 | |
|     this.message = msg;
 | |
|     this.code = code;
 | |
|   }
 | |
| 
 | |
|   PasswordException.prototype = new Error();
 | |
|   PasswordException.constructor = PasswordException;
 | |
| 
 | |
|   return PasswordException;
 | |
| })();
 | |
| 
 | |
| var UnknownErrorException = (function UnknownErrorExceptionClosure() {
 | |
|   function UnknownErrorException(msg, details) {
 | |
|     this.name = 'UnknownErrorException';
 | |
|     this.message = msg;
 | |
|     this.details = details;
 | |
|   }
 | |
| 
 | |
|   UnknownErrorException.prototype = new Error();
 | |
|   UnknownErrorException.constructor = UnknownErrorException;
 | |
| 
 | |
|   return UnknownErrorException;
 | |
| })();
 | |
| 
 | |
| var InvalidPDFException = (function InvalidPDFExceptionClosure() {
 | |
|   function InvalidPDFException(msg) {
 | |
|     this.name = 'InvalidPDFException';
 | |
|     this.message = msg;
 | |
|   }
 | |
| 
 | |
|   InvalidPDFException.prototype = new Error();
 | |
|   InvalidPDFException.constructor = InvalidPDFException;
 | |
| 
 | |
|   return InvalidPDFException;
 | |
| })();
 | |
| 
 | |
| var MissingPDFException = (function MissingPDFExceptionClosure() {
 | |
|   function MissingPDFException(msg) {
 | |
|     this.name = 'MissingPDFException';
 | |
|     this.message = msg;
 | |
|   }
 | |
| 
 | |
|   MissingPDFException.prototype = new Error();
 | |
|   MissingPDFException.constructor = MissingPDFException;
 | |
| 
 | |
|   return MissingPDFException;
 | |
| })();
 | |
| 
 | |
| var NotImplementedException = (function NotImplementedExceptionClosure() {
 | |
|   function NotImplementedException(msg) {
 | |
|     this.message = msg;
 | |
|   }
 | |
| 
 | |
|   NotImplementedException.prototype = new Error();
 | |
|   NotImplementedException.prototype.name = 'NotImplementedException';
 | |
|   NotImplementedException.constructor = NotImplementedException;
 | |
| 
 | |
|   return NotImplementedException;
 | |
| })();
 | |
| 
 | |
| var MissingDataException = (function MissingDataExceptionClosure() {
 | |
|   function MissingDataException(begin, end) {
 | |
|     this.begin = begin;
 | |
|     this.end = end;
 | |
|     this.message = 'Missing data [' + begin + ', ' + end + ')';
 | |
|   }
 | |
| 
 | |
|   MissingDataException.prototype = new Error();
 | |
|   MissingDataException.prototype.name = 'MissingDataException';
 | |
|   MissingDataException.constructor = MissingDataException;
 | |
| 
 | |
|   return MissingDataException;
 | |
| })();
 | |
| 
 | |
| var XRefParseException = (function XRefParseExceptionClosure() {
 | |
|   function XRefParseException(msg) {
 | |
|     this.message = msg;
 | |
|   }
 | |
| 
 | |
|   XRefParseException.prototype = new Error();
 | |
|   XRefParseException.prototype.name = 'XRefParseException';
 | |
|   XRefParseException.constructor = XRefParseException;
 | |
| 
 | |
|   return XRefParseException;
 | |
| })();
 | |
| 
 | |
| 
 | |
| function bytesToString(bytes) {
 | |
|   var str = '';
 | |
|   var length = bytes.length;
 | |
|   for (var n = 0; n < length; ++n)
 | |
|     str += String.fromCharCode(bytes[n]);
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| function stringToBytes(str) {
 | |
|   var length = str.length;
 | |
|   var bytes = new Uint8Array(length);
 | |
|   for (var n = 0; n < length; ++n)
 | |
|     bytes[n] = str.charCodeAt(n) & 0xFF;
 | |
|   return bytes;
 | |
| }
 | |
| 
 | |
| var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
 | |
| 
 | |
| var Util = PDFJS.Util = (function UtilClosure() {
 | |
|   function Util() {}
 | |
| 
 | |
|   Util.makeCssRgb = function Util_makeCssRgb(rgb) {
 | |
|     return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
 | |
|   };
 | |
| 
 | |
|   Util.makeCssCmyk = function Util_makeCssCmyk(cmyk) {
 | |
|     var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0);
 | |
|     return Util.makeCssRgb(rgb);
 | |
|   };
 | |
| 
 | |
|   // Concatenates two transformation matrices together and returns the result.
 | |
|   Util.transform = function Util_transform(m1, m2) {
 | |
|     return [
 | |
|       m1[0] * m2[0] + m1[2] * m2[1],
 | |
|       m1[1] * m2[0] + m1[3] * m2[1],
 | |
|       m1[0] * m2[2] + m1[2] * m2[3],
 | |
|       m1[1] * m2[2] + m1[3] * m2[3],
 | |
|       m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
 | |
|       m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
 | |
|     ];
 | |
|   };
 | |
| 
 | |
|   // For 2d affine transforms
 | |
|   Util.applyTransform = function Util_applyTransform(p, m) {
 | |
|     var xt = p[0] * m[0] + p[1] * m[2] + m[4];
 | |
|     var yt = p[0] * m[1] + p[1] * m[3] + m[5];
 | |
|     return [xt, yt];
 | |
|   };
 | |
| 
 | |
|   Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
 | |
|     var d = m[0] * m[3] - m[1] * m[2];
 | |
|     var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
 | |
|     var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
 | |
|     return [xt, yt];
 | |
|   };
 | |
| 
 | |
|   // Applies the transform to the rectangle and finds the minimum axially
 | |
|   // aligned bounding box.
 | |
|   Util.getAxialAlignedBoundingBox =
 | |
|     function Util_getAxialAlignedBoundingBox(r, m) {
 | |
| 
 | |
|     var p1 = Util.applyTransform(r, m);
 | |
|     var p2 = Util.applyTransform(r.slice(2, 4), m);
 | |
|     var p3 = Util.applyTransform([r[0], r[3]], m);
 | |
|     var p4 = Util.applyTransform([r[2], r[1]], m);
 | |
|     return [
 | |
|       Math.min(p1[0], p2[0], p3[0], p4[0]),
 | |
|       Math.min(p1[1], p2[1], p3[1], p4[1]),
 | |
|       Math.max(p1[0], p2[0], p3[0], p4[0]),
 | |
|       Math.max(p1[1], p2[1], p3[1], p4[1])
 | |
|     ];
 | |
|   };
 | |
| 
 | |
|   Util.inverseTransform = function Util_inverseTransform(m) {
 | |
|     var d = m[0] * m[3] - m[1] * m[2];
 | |
|     return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
 | |
|       (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
 | |
|   };
 | |
| 
 | |
|   // Apply a generic 3d matrix M on a 3-vector v:
 | |
|   //   | a b c |   | X |
 | |
|   //   | d e f | x | Y |
 | |
|   //   | g h i |   | Z |
 | |
|   // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
 | |
|   // with v as [X,Y,Z]
 | |
|   Util.apply3dTransform = function Util_apply3dTransform(m, v) {
 | |
|     return [
 | |
|       m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
 | |
|       m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
 | |
|       m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
 | |
|     ];
 | |
|   };
 | |
| 
 | |
|   // This calculation uses Singular Value Decomposition.
 | |
|   // The SVD can be represented with formula A = USV. We are interested in the
 | |
|   // matrix S here because it represents the scale values.
 | |
|   Util.singularValueDecompose2dScale =
 | |
|     function Util_singularValueDecompose2dScale(m) {
 | |
| 
 | |
|     var transpose = [m[0], m[2], m[1], m[3]];
 | |
| 
 | |
|     // Multiply matrix m with its transpose.
 | |
|     var a = m[0] * transpose[0] + m[1] * transpose[2];
 | |
|     var b = m[0] * transpose[1] + m[1] * transpose[3];
 | |
|     var c = m[2] * transpose[0] + m[3] * transpose[2];
 | |
|     var d = m[2] * transpose[1] + m[3] * transpose[3];
 | |
| 
 | |
|     // Solve the second degree polynomial to get roots.
 | |
|     var first = (a + d) / 2;
 | |
|     var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
 | |
|     var sx = first + second || 1;
 | |
|     var sy = first - second || 1;
 | |
| 
 | |
|     // Scale values are the square roots of the eigenvalues.
 | |
|     return [Math.sqrt(sx), Math.sqrt(sy)];
 | |
|   };
 | |
| 
 | |
|   // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
 | |
|   // For coordinate systems whose origin lies in the bottom-left, this
 | |
|   // means normalization to (BL,TR) ordering. For systems with origin in the
 | |
|   // top-left, this means (TL,BR) ordering.
 | |
|   Util.normalizeRect = function Util_normalizeRect(rect) {
 | |
|     var r = rect.slice(0); // clone rect
 | |
|     if (rect[0] > rect[2]) {
 | |
|       r[0] = rect[2];
 | |
|       r[2] = rect[0];
 | |
|     }
 | |
|     if (rect[1] > rect[3]) {
 | |
|       r[1] = rect[3];
 | |
|       r[3] = rect[1];
 | |
|     }
 | |
|     return r;
 | |
|   };
 | |
| 
 | |
|   // Returns a rectangle [x1, y1, x2, y2] corresponding to the
 | |
|   // intersection of rect1 and rect2. If no intersection, returns 'false'
 | |
|   // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
 | |
|   Util.intersect = function Util_intersect(rect1, rect2) {
 | |
|     function compare(a, b) {
 | |
|       return a - b;
 | |
|     }
 | |
| 
 | |
|     // Order points along the axes
 | |
|     var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
 | |
|         orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
 | |
|         result = [];
 | |
| 
 | |
|     rect1 = Util.normalizeRect(rect1);
 | |
|     rect2 = Util.normalizeRect(rect2);
 | |
| 
 | |
|     // X: first and second points belong to different rectangles?
 | |
|     if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
 | |
|         (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
 | |
|       // Intersection must be between second and third points
 | |
|       result[0] = orderedX[1];
 | |
|       result[2] = orderedX[2];
 | |
|     } else {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // Y: first and second points belong to different rectangles?
 | |
|     if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
 | |
|         (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
 | |
|       // Intersection must be between second and third points
 | |
|       result[1] = orderedY[1];
 | |
|       result[3] = orderedY[2];
 | |
|     } else {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
|   };
 | |
| 
 | |
|   Util.sign = function Util_sign(num) {
 | |
|     return num < 0 ? -1 : 1;
 | |
|   };
 | |
| 
 | |
|   // TODO(mack): Rename appendToArray
 | |
|   Util.concatenateToArray = function concatenateToArray(arr1, arr2) {
 | |
|     Array.prototype.push.apply(arr1, arr2);
 | |
|   };
 | |
| 
 | |
|   Util.prependToArray = function concatenateToArray(arr1, arr2) {
 | |
|     Array.prototype.unshift.apply(arr1, arr2);
 | |
|   };
 | |
| 
 | |
|   Util.extendObj = function extendObj(obj1, obj2) {
 | |
|     for (var key in obj2) {
 | |
|       obj1[key] = obj2[key];
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Util.getInheritableProperty = function Util_getInheritableProperty(dict,
 | |
|                                                                      name) {
 | |
|     while (dict && !dict.has(name)) {
 | |
|       dict = dict.get('Parent');
 | |
|     }
 | |
|     if (!dict) {
 | |
|       return null;
 | |
|     }
 | |
|     return dict.get(name);
 | |
|   };
 | |
| 
 | |
|   Util.inherit = function Util_inherit(sub, base, prototype) {
 | |
|     sub.prototype = Object.create(base.prototype);
 | |
|     sub.prototype.constructor = sub;
 | |
|     for (var prop in prototype) {
 | |
|       sub.prototype[prop] = prototype[prop];
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Util.loadScript = function Util_loadScript(src, callback) {
 | |
|     var script = document.createElement('script');
 | |
|     var loaded = false;
 | |
|     script.setAttribute('src', src);
 | |
|     if (callback) {
 | |
|       script.onload = function() {
 | |
|         if (!loaded) {
 | |
|           callback();
 | |
|         }
 | |
|         loaded = true;
 | |
|       };
 | |
|     }
 | |
|     document.getElementsByTagName('head')[0].appendChild(script);
 | |
|   };
 | |
| 
 | |
|   return Util;
 | |
| })();
 | |
| 
 | |
| var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
 | |
|   function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
 | |
|     this.viewBox = viewBox;
 | |
|     this.scale = scale;
 | |
|     this.rotation = rotation;
 | |
|     this.offsetX = offsetX;
 | |
|     this.offsetY = offsetY;
 | |
| 
 | |
|     // creating transform to convert pdf coordinate system to the normal
 | |
|     // canvas like coordinates taking in account scale and rotation
 | |
|     var centerX = (viewBox[2] + viewBox[0]) / 2;
 | |
|     var centerY = (viewBox[3] + viewBox[1]) / 2;
 | |
|     var rotateA, rotateB, rotateC, rotateD;
 | |
|     rotation = rotation % 360;
 | |
|     rotation = rotation < 0 ? rotation + 360 : rotation;
 | |
|     switch (rotation) {
 | |
|       case 180:
 | |
|         rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
 | |
|         break;
 | |
|       case 90:
 | |
|         rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
 | |
|         break;
 | |
|       case 270:
 | |
|         rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
 | |
|         break;
 | |
|       //case 0:
 | |
|       default:
 | |
|         rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     if (dontFlip) {
 | |
|       rotateC = -rotateC; rotateD = -rotateD;
 | |
|     }
 | |
| 
 | |
|     var offsetCanvasX, offsetCanvasY;
 | |
|     var width, height;
 | |
|     if (rotateA === 0) {
 | |
|       offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
 | |
|       offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
 | |
|       width = Math.abs(viewBox[3] - viewBox[1]) * scale;
 | |
|       height = Math.abs(viewBox[2] - viewBox[0]) * scale;
 | |
|     } else {
 | |
|       offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
 | |
|       offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
 | |
|       width = Math.abs(viewBox[2] - viewBox[0]) * scale;
 | |
|       height = Math.abs(viewBox[3] - viewBox[1]) * scale;
 | |
|     }
 | |
|     // creating transform for the following operations:
 | |
|     // translate(-centerX, -centerY), rotate and flip vertically,
 | |
|     // scale, and translate(offsetCanvasX, offsetCanvasY)
 | |
|     this.transform = [
 | |
|       rotateA * scale,
 | |
|       rotateB * scale,
 | |
|       rotateC * scale,
 | |
|       rotateD * scale,
 | |
|       offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
 | |
|       offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
 | |
|     ];
 | |
| 
 | |
|     this.width = width;
 | |
|     this.height = height;
 | |
|     this.fontScale = scale;
 | |
|   }
 | |
|   PageViewport.prototype = {
 | |
|     clone: function PageViewPort_clone(args) {
 | |
|       args = args || {};
 | |
|       var scale = 'scale' in args ? args.scale : this.scale;
 | |
|       var rotation = 'rotation' in args ? args.rotation : this.rotation;
 | |
|       return new PageViewport(this.viewBox.slice(), scale, rotation,
 | |
|                               this.offsetX, this.offsetY, args.dontFlip);
 | |
|     },
 | |
|     convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
 | |
|       return Util.applyTransform([x, y], this.transform);
 | |
|     },
 | |
|     convertToViewportRectangle:
 | |
|       function PageViewport_convertToViewportRectangle(rect) {
 | |
|       var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
 | |
|       var br = Util.applyTransform([rect[2], rect[3]], this.transform);
 | |
|       return [tl[0], tl[1], br[0], br[1]];
 | |
|     },
 | |
|     convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
 | |
|       return Util.applyInverseTransform([x, y], this.transform);
 | |
|     }
 | |
|   };
 | |
|   return PageViewport;
 | |
| })();
 | |
| 
 | |
| var PDFStringTranslateTable = [
 | |
|   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
|   0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0,
 | |
|   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
 | |
|   0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
 | |
|   0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
 | |
|   0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
 | |
| ];
 | |
| 
 | |
| function stringToPDFString(str) {
 | |
|   var i, n = str.length, str2 = '';
 | |
|   if (str[0] === '\xFE' && str[1] === '\xFF') {
 | |
|     // UTF16BE BOM
 | |
|     for (i = 2; i < n; i += 2)
 | |
|       str2 += String.fromCharCode(
 | |
|         (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1));
 | |
|   } else {
 | |
|     for (i = 0; i < n; ++i) {
 | |
|       var code = PDFStringTranslateTable[str.charCodeAt(i)];
 | |
|       str2 += code ? String.fromCharCode(code) : str.charAt(i);
 | |
|     }
 | |
|   }
 | |
|   return str2;
 | |
| }
 | |
| 
 | |
| function stringToUTF8String(str) {
 | |
|   return decodeURIComponent(escape(str));
 | |
| }
 | |
| 
 | |
| function isEmptyObj(obj) {
 | |
|   for (var key in obj) {
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| function isBool(v) {
 | |
|   return typeof v == 'boolean';
 | |
| }
 | |
| 
 | |
| function isInt(v) {
 | |
|   return typeof v == 'number' && ((v | 0) == v);
 | |
| }
 | |
| 
 | |
| function isNum(v) {
 | |
|   return typeof v == 'number';
 | |
| }
 | |
| 
 | |
| function isString(v) {
 | |
|   return typeof v == 'string';
 | |
| }
 | |
| 
 | |
| function isNull(v) {
 | |
|   return v === null;
 | |
| }
 | |
| 
 | |
| function isName(v) {
 | |
|   return v instanceof Name;
 | |
| }
 | |
| 
 | |
| function isCmd(v, cmd) {
 | |
|   return v instanceof Cmd && (!cmd || v.cmd == cmd);
 | |
| }
 | |
| 
 | |
| function isDict(v, type) {
 | |
|   if (!(v instanceof Dict)) {
 | |
|     return false;
 | |
|   }
 | |
|   if (!type) {
 | |
|     return true;
 | |
|   }
 | |
|   var dictType = v.get('Type');
 | |
|   return isName(dictType) && dictType.name == type;
 | |
| }
 | |
| 
 | |
| function isArray(v) {
 | |
|   return v instanceof Array;
 | |
| }
 | |
| 
 | |
| function isStream(v) {
 | |
|   return typeof v == 'object' && v !== null && v !== undefined &&
 | |
|          ('getBytes' in v);
 | |
| }
 | |
| 
 | |
| function isArrayBuffer(v) {
 | |
|   return typeof v == 'object' && v !== null && v !== undefined &&
 | |
|          ('byteLength' in v);
 | |
| }
 | |
| 
 | |
| function isRef(v) {
 | |
|   return v instanceof Ref;
 | |
| }
 | |
| 
 | |
| function isPDFFunction(v) {
 | |
|   var fnDict;
 | |
|   if (typeof v != 'object')
 | |
|     return false;
 | |
|   else if (isDict(v))
 | |
|     fnDict = v;
 | |
|   else if (isStream(v))
 | |
|     fnDict = v.dict;
 | |
|   else
 | |
|     return false;
 | |
|   return fnDict.has('FunctionType');
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * The following promise implementation tries to generally implment the
 | |
|  * Promise/A+ spec. Some notable differences from other promise libaries are:
 | |
|  * - There currently isn't a seperate deferred and promise object.
 | |
|  * - Unhandled rejections eventually show an error if they aren't handled.
 | |
|  *
 | |
|  * Based off of the work in:
 | |
|  * https://bugzilla.mozilla.org/show_bug.cgi?id=810490
 | |
|  */
 | |
| var Promise = PDFJS.Promise = (function PromiseClosure() {
 | |
|   var STATUS_PENDING = 0;
 | |
|   var STATUS_RESOLVED = 1;
 | |
|   var STATUS_REJECTED = 2;
 | |
| 
 | |
|   // In an attempt to avoid silent exceptions, unhandled rejections are
 | |
|   // tracked and if they aren't handled in a certain amount of time an
 | |
|   // error is logged.
 | |
|   var REJECTION_TIMEOUT = 500;
 | |
| 
 | |
|   var HandlerManager = {
 | |
|     handlers: [],
 | |
|     running: false,
 | |
|     unhandledRejections: [],
 | |
|     pendingRejectionCheck: false,
 | |
| 
 | |
|     scheduleHandlers: function scheduleHandlers(promise) {
 | |
|       if (promise._status == STATUS_PENDING) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       this.handlers = this.handlers.concat(promise._handlers);
 | |
|       promise._handlers = [];
 | |
| 
 | |
|       if (this.running) {
 | |
|         return;
 | |
|       }
 | |
|       this.running = true;
 | |
| 
 | |
|       setTimeout(this.runHandlers.bind(this), 0);
 | |
|     },
 | |
| 
 | |
|     runHandlers: function runHandlers() {
 | |
|       while (this.handlers.length > 0) {
 | |
|         var handler = this.handlers.shift();
 | |
| 
 | |
|         var nextStatus = handler.thisPromise._status;
 | |
|         var nextValue = handler.thisPromise._value;
 | |
| 
 | |
|         try {
 | |
|           if (nextStatus === STATUS_RESOLVED) {
 | |
|             if (typeof(handler.onResolve) == 'function') {
 | |
|               nextValue = handler.onResolve(nextValue);
 | |
|             }
 | |
|           } else if (typeof(handler.onReject) === 'function') {
 | |
|               nextValue = handler.onReject(nextValue);
 | |
|               nextStatus = STATUS_RESOLVED;
 | |
| 
 | |
|               if (handler.thisPromise._unhandledRejection) {
 | |
|                 this.removeUnhandeledRejection(handler.thisPromise);
 | |
|               }
 | |
|           }
 | |
|         } catch (ex) {
 | |
|           nextStatus = STATUS_REJECTED;
 | |
|           nextValue = ex;
 | |
|         }
 | |
| 
 | |
|         handler.nextPromise._updateStatus(nextStatus, nextValue);
 | |
|       }
 | |
| 
 | |
|       this.running = false;
 | |
|     },
 | |
| 
 | |
|     addUnhandledRejection: function addUnhandledRejection(promise) {
 | |
|       this.unhandledRejections.push({
 | |
|         promise: promise,
 | |
|         time: Date.now()
 | |
|       });
 | |
|       this.scheduleRejectionCheck();
 | |
|     },
 | |
| 
 | |
|     removeUnhandeledRejection: function removeUnhandeledRejection(promise) {
 | |
|       promise._unhandledRejection = false;
 | |
|       for (var i = 0; i < this.unhandledRejections.length; i++) {
 | |
|         if (this.unhandledRejections[i].promise === promise) {
 | |
|           this.unhandledRejections.splice(i);
 | |
|           i--;
 | |
|         }
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     scheduleRejectionCheck: function scheduleRejectionCheck() {
 | |
|       if (this.pendingRejectionCheck) {
 | |
|         return;
 | |
|       }
 | |
|       this.pendingRejectionCheck = true;
 | |
|       setTimeout(function rejectionCheck() {
 | |
|         this.pendingRejectionCheck = false;
 | |
|         var now = Date.now();
 | |
|         for (var i = 0; i < this.unhandledRejections.length; i++) {
 | |
|           if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) {
 | |
|             var unhandled = this.unhandledRejections[i].promise._value;
 | |
|             var msg = 'Unhandled rejection: ' + unhandled;
 | |
|             if (unhandled.stack) {
 | |
|               msg += '\n' + unhandled.stack;
 | |
|             }
 | |
|             warn(msg);
 | |
|             this.unhandledRejections.splice(i);
 | |
|             i--;
 | |
|           }
 | |
|         }
 | |
|         if (this.unhandledRejections.length) {
 | |
|           this.scheduleRejectionCheck();
 | |
|         }
 | |
|       }.bind(this), REJECTION_TIMEOUT);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   function Promise() {
 | |
|     this._status = STATUS_PENDING;
 | |
|     this._handlers = [];
 | |
|   }
 | |
|   /**
 | |
|    * Builds a promise that is resolved when all the passed in promises are
 | |
|    * resolved.
 | |
|    * @param {array} array of data and/or promises to wait for.
 | |
|    * @return {Promise} New dependant promise.
 | |
|    */
 | |
|   Promise.all = function Promise_all(promises) {
 | |
|     var deferred = new Promise();
 | |
|     var unresolved = promises.length;
 | |
|     var results = [];
 | |
|     if (unresolved === 0) {
 | |
|       deferred.resolve(results);
 | |
|       return deferred;
 | |
|     }
 | |
|     function reject(reason) {
 | |
|       if (deferred._status === STATUS_REJECTED) {
 | |
|         return;
 | |
|       }
 | |
|       results = [];
 | |
|       deferred.reject(reason);
 | |
|     }
 | |
|     for (var i = 0, ii = promises.length; i < ii; ++i) {
 | |
|       var promise = promises[i];
 | |
|       var resolve = (function(i) {
 | |
|         return function(value) {
 | |
|           if (deferred._status === STATUS_REJECTED) {
 | |
|             return;
 | |
|           }
 | |
|           results[i] = value;
 | |
|           unresolved--;
 | |
|           if (unresolved === 0)
 | |
|             deferred.resolve(results);
 | |
|         };
 | |
|       })(i);
 | |
|       if (Promise.isPromise(promise)) {
 | |
|         promise.then(resolve, reject);
 | |
|       } else {
 | |
|         resolve(promise);
 | |
|       }
 | |
|     }
 | |
|     return deferred;
 | |
|   };
 | |
| 
 | |
|   /**
 | |
|    * Checks if the value is likely a promise (has a 'then' function).
 | |
|    * @return {boolean} true if x is thenable
 | |
|    */
 | |
|   Promise.isPromise = function Promise_isPromise(value) {
 | |
|     return value && typeof value.then === 'function';
 | |
|   };
 | |
| 
 | |
|   Promise.prototype = {
 | |
|     _status: null,
 | |
|     _value: null,
 | |
|     _handlers: null,
 | |
|     _unhandledRejection: null,
 | |
| 
 | |
|     _updateStatus: function Promise__updateStatus(status, value) {
 | |
|       if (this._status === STATUS_RESOLVED ||
 | |
|           this._status === STATUS_REJECTED) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (status == STATUS_RESOLVED &&
 | |
|           Promise.isPromise(value)) {
 | |
|         value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
 | |
|                    this._updateStatus.bind(this, STATUS_REJECTED));
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       this._status = status;
 | |
|       this._value = value;
 | |
| 
 | |
|       if (status === STATUS_REJECTED && this._handlers.length === 0) {
 | |
|         this._unhandledRejection = true;
 | |
|         HandlerManager.addUnhandledRejection(this);
 | |
|       }
 | |
| 
 | |
|       HandlerManager.scheduleHandlers(this);
 | |
|     },
 | |
| 
 | |
|     get isResolved() {
 | |
|       return this._status === STATUS_RESOLVED;
 | |
|     },
 | |
| 
 | |
|     get isRejected() {
 | |
|       return this._status === STATUS_REJECTED;
 | |
|     },
 | |
| 
 | |
|     resolve: function Promise_resolve(value) {
 | |
|       this._updateStatus(STATUS_RESOLVED, value);
 | |
|     },
 | |
| 
 | |
|     reject: function Promise_reject(reason) {
 | |
|       this._updateStatus(STATUS_REJECTED, reason);
 | |
|     },
 | |
| 
 | |
|     then: function Promise_then(onResolve, onReject) {
 | |
|       var nextPromise = new Promise();
 | |
|       this._handlers.push({
 | |
|         thisPromise: this,
 | |
|         onResolve: onResolve,
 | |
|         onReject: onReject,
 | |
|         nextPromise: nextPromise
 | |
|       });
 | |
|       HandlerManager.scheduleHandlers(this);
 | |
|       return nextPromise;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   return Promise;
 | |
| })();
 | |
| 
 | |
| var StatTimer = (function StatTimerClosure() {
 | |
|   function rpad(str, pad, length) {
 | |
|     while (str.length < length)
 | |
|       str += pad;
 | |
|     return str;
 | |
|   }
 | |
|   function StatTimer() {
 | |
|     this.started = {};
 | |
|     this.times = [];
 | |
|     this.enabled = true;
 | |
|   }
 | |
|   StatTimer.prototype = {
 | |
|     time: function StatTimer_time(name) {
 | |
|       if (!this.enabled)
 | |
|         return;
 | |
|       if (name in this.started)
 | |
|         warn('Timer is already running for ' + name);
 | |
|       this.started[name] = Date.now();
 | |
|     },
 | |
|     timeEnd: function StatTimer_timeEnd(name) {
 | |
|       if (!this.enabled)
 | |
|         return;
 | |
|       if (!(name in this.started))
 | |
|         warn('Timer has not been started for ' + name);
 | |
|       this.times.push({
 | |
|         'name': name,
 | |
|         'start': this.started[name],
 | |
|         'end': Date.now()
 | |
|       });
 | |
|       // Remove timer from started so it can be called again.
 | |
|       delete this.started[name];
 | |
|     },
 | |
|     toString: function StatTimer_toString() {
 | |
|       var times = this.times;
 | |
|       var out = '';
 | |
|       // Find the longest name for padding purposes.
 | |
|       var longest = 0;
 | |
|       for (var i = 0, ii = times.length; i < ii; ++i) {
 | |
|         var name = times[i]['name'];
 | |
|         if (name.length > longest)
 | |
|           longest = name.length;
 | |
|       }
 | |
|       for (var i = 0, ii = times.length; i < ii; ++i) {
 | |
|         var span = times[i];
 | |
|         var duration = span.end - span.start;
 | |
|         out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
 | |
|       }
 | |
|       return out;
 | |
|     }
 | |
|   };
 | |
|   return StatTimer;
 | |
| })();
 | |
| 
 | |
| PDFJS.createBlob = function createBlob(data, contentType) {
 | |
|   if (typeof Blob !== 'undefined')
 | |
|     return new Blob([data], { type: contentType });
 | |
|   // Blob builder is deprecated in FF14 and removed in FF18.
 | |
|   var bb = new MozBlobBuilder();
 | |
|   bb.append(data);
 | |
|   return bb.getBlob(contentType);
 | |
| };
 | |
| 
 | |
| PDFJS.createObjectURL = (function createObjectURLClosure() {
 | |
|   if (typeof URL !== 'undefined' && URL.createObjectURL) {
 | |
|     return function createObjectURL(data, contentType) {
 | |
|       var blob = PDFJS.createBlob(data, contentType);
 | |
|       return URL.createObjectURL(blob);
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   // Blob/createObjectURL is not available, falling back to data schema.
 | |
|   var digits =
 | |
|     'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
 | |
| 
 | |
|   return function createObjectURL(data, contentType) {
 | |
|     var buffer = 'data:' + contentType + ';base64,';
 | |
|     for (var i = 0, ii = data.length; i < ii; i += 3) {
 | |
|       var b1 = data[i] & 0xFF;
 | |
|       var b2 = data[i + 1] & 0xFF;
 | |
|       var b3 = data[i + 2] & 0xFF;
 | |
|       var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
 | |
|       var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
 | |
|       var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
 | |
|       buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
 | |
|     }
 | |
|     return buffer;
 | |
|   };
 | |
| })();
 | |
| 
 | |
| function MessageHandler(name, comObj) {
 | |
|   this.name = name;
 | |
|   this.comObj = comObj;
 | |
|   this.callbackIndex = 1;
 | |
|   this.postMessageTransfers = true;
 | |
|   var callbacks = this.callbacks = {};
 | |
|   var ah = this.actionHandler = {};
 | |
| 
 | |
|   ah['console_log'] = [function ahConsoleLog(data) {
 | |
|     log.apply(null, data);
 | |
|   }];
 | |
|   // If there's no console available, console_error in the
 | |
|   // action handler will do nothing.
 | |
|   if ('console' in globalScope) {
 | |
|     ah['console_error'] = [function ahConsoleError(data) {
 | |
|       globalScope['console'].error.apply(null, data);
 | |
|     }];
 | |
|   } else {
 | |
|     ah['console_error'] = [function ahConsoleError(data) {
 | |
|       log.apply(null, data);
 | |
|     }];
 | |
|   }
 | |
|   ah['_warn'] = [function ah_Warn(data) {
 | |
|     warn(data);
 | |
|   }];
 | |
| 
 | |
|   comObj.onmessage = function messageHandlerComObjOnMessage(event) {
 | |
|     var data = event.data;
 | |
|     if (data.isReply) {
 | |
|       var callbackId = data.callbackId;
 | |
|       if (data.callbackId in callbacks) {
 | |
|         var callback = callbacks[callbackId];
 | |
|         delete callbacks[callbackId];
 | |
|         callback(data.data);
 | |
|       } else {
 | |
|         error('Cannot resolve callback ' + callbackId);
 | |
|       }
 | |
|     } else if (data.action in ah) {
 | |
|       var action = ah[data.action];
 | |
|       if (data.callbackId) {
 | |
|         var promise = new Promise();
 | |
|         promise.then(function(resolvedData) {
 | |
|           comObj.postMessage({
 | |
|             isReply: true,
 | |
|             callbackId: data.callbackId,
 | |
|             data: resolvedData
 | |
|           });
 | |
|         });
 | |
|         action[0].call(action[1], data.data, promise);
 | |
|       } else {
 | |
|         action[0].call(action[1], data.data);
 | |
|       }
 | |
|     } else {
 | |
|       error('Unkown action from worker: ' + data.action);
 | |
|     }
 | |
|   };
 | |
| }
 | |
| 
 | |
| MessageHandler.prototype = {
 | |
|   on: function messageHandlerOn(actionName, handler, scope) {
 | |
|     var ah = this.actionHandler;
 | |
|     if (ah[actionName]) {
 | |
|       error('There is already an actionName called "' + actionName + '"');
 | |
|     }
 | |
|     ah[actionName] = [handler, scope];
 | |
|   },
 | |
|   /**
 | |
|    * Sends a message to the comObj to invoke the action with the supplied data.
 | |
|    * @param {String} actionName Action to call.
 | |
|    * @param {JSON} data JSON data to send.
 | |
|    * @param {function} [callback] Optional callback that will handle a reply.
 | |
|    * @param {Array} [transfers] Optional list of transfers/ArrayBuffers
 | |
|    */
 | |
|   send: function messageHandlerSend(actionName, data, callback, transfers) {
 | |
|     var message = {
 | |
|       action: actionName,
 | |
|       data: data
 | |
|     };
 | |
|     if (callback) {
 | |
|       var callbackId = this.callbackIndex++;
 | |
|       this.callbacks[callbackId] = callback;
 | |
|       message.callbackId = callbackId;
 | |
|     }
 | |
|     if (transfers && this.postMessageTransfers) {
 | |
|       this.comObj.postMessage(message, transfers);
 | |
|     } else {
 | |
|       this.comObj.postMessage(message);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| function loadJpegStream(id, imageUrl, objs) {
 | |
|   var img = new Image();
 | |
|   img.onload = (function loadJpegStream_onloadClosure() {
 | |
|     objs.resolve(id, img);
 | |
|   });
 | |
|   img.src = imageUrl;
 | |
| }
 | |
| 
 | |
| 
 | |
| var ColorSpace = (function ColorSpaceClosure() {
 | |
|   // Constructor should define this.numComps, this.defaultColor, this.name
 | |
|   function ColorSpace() {
 | |
|     error('should not call ColorSpace constructor');
 | |
|   }
 | |
| 
 | |
|   ColorSpace.prototype = {
 | |
|     /**
 | |
|      * Converts the color value to the RGB color. The color components are
 | |
|      * located in the src array starting from the srcOffset. Returns the array
 | |
|      * of the rgb components, each value ranging from [0,255].
 | |
|      */
 | |
|     getRgb: function ColorSpace_getRgb(src, srcOffset) {
 | |
|       error('Should not call ColorSpace.getRgb');
 | |
|     },
 | |
|     /**
 | |
|      * Converts the color value to the RGB color, similar to the getRgb method.
 | |
|      * The result placed into the dest array starting from the destOffset.
 | |
|      */
 | |
|     getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) {
 | |
|       error('Should not call ColorSpace.getRgbItem');
 | |
|     },
 | |
|     /**
 | |
|      * Converts the specified number of the color values to the RGB colors.
 | |
|      * The colors are located in the src array starting from the srcOffset.
 | |
|      * The result is placed into the dest array starting from the destOffset.
 | |
|      * The src array items shall be in [0,2^bits) range, the dest array items
 | |
|      * will be in [0,255] range.
 | |
|      */
 | |
|     getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count,
 | |
|                                                    dest, destOffset, bits) {
 | |
|       error('Should not call ColorSpace.getRgbBuffer');
 | |
|     },
 | |
|     /**
 | |
|      * Determines amount of the bytes is required to store the reslut of the
 | |
|      * conversion that done by the getRgbBuffer method.
 | |
|      */
 | |
|     getOutputLength: function ColorSpace_getOutputLength(inputLength) {
 | |
|       error('Should not call ColorSpace.getOutputLength');
 | |
|     },
 | |
|     /**
 | |
|      * Returns true if source data will be equal the result/output data.
 | |
|      */
 | |
|     isPassthrough: function ColorSpace_isPassthrough(bits) {
 | |
|       return false;
 | |
|     },
 | |
|     /**
 | |
|      * Creates the output buffer and converts the specified number of the color
 | |
|      * values to the RGB colors, similar to the getRgbBuffer.
 | |
|      */
 | |
|     createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset,
 | |
|                                                          count, bits) {
 | |
|       if (this.isPassthrough(bits)) {
 | |
|         return src.subarray(srcOffset);
 | |
|       }
 | |
|       var dest = new Uint8Array(count * 3);
 | |
|       var numComponentColors = 1 << bits;
 | |
|       // Optimization: create a color map when there is just one component and
 | |
|       // we are converting more colors than the size of the color map. We
 | |
|       // don't build the map if the colorspace is gray or rgb since those
 | |
|       // methods are faster than building a map. This mainly offers big speed
 | |
|       // ups for indexed and alternate colorspaces.
 | |
|       if (this.numComps === 1 && count > numComponentColors &&
 | |
|           this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
 | |
|         // TODO it may be worth while to cache the color map. While running
 | |
|         // testing I never hit a cache so I will leave that out for now (perhaps
 | |
|         // we are reparsing colorspaces too much?).
 | |
|         var allColors = bits <= 8 ? new Uint8Array(numComponentColors) :
 | |
|                                     new Uint16Array(numComponentColors);
 | |
|         for (var i = 0; i < numComponentColors; i++) {
 | |
|           allColors[i] = i;
 | |
|         }
 | |
|         var colorMap = new Uint8Array(numComponentColors * 3);
 | |
|         this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bits);
 | |
| 
 | |
|         var destOffset = 0;
 | |
|         for (var i = 0; i < count; ++i) {
 | |
|           var key = src[srcOffset++] * 3;
 | |
|           dest[destOffset++] = colorMap[key];
 | |
|           dest[destOffset++] = colorMap[key + 1];
 | |
|           dest[destOffset++] = colorMap[key + 2];
 | |
|         }
 | |
|         return dest;
 | |
|       }
 | |
|       this.getRgbBuffer(src, srcOffset, count, dest, 0, bits);
 | |
|       return dest;
 | |
|     },
 | |
|     /**
 | |
|      * True if the colorspace has components in the default range of [0, 1].
 | |
|      * This should be true for all colorspaces except for lab color spaces
 | |
|      * which are [0,100], [-128, 127], [-128, 127].
 | |
|      */
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
| 
 | |
|   ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
 | |
|     var IR = ColorSpace.parseToIR(cs, xref, res);
 | |
|     if (IR instanceof AlternateCS)
 | |
|       return IR;
 | |
| 
 | |
|     return ColorSpace.fromIR(IR);
 | |
|   };
 | |
| 
 | |
|   ColorSpace.fromIR = function ColorSpace_fromIR(IR) {
 | |
|     var name = isArray(IR) ? IR[0] : IR;
 | |
| 
 | |
|     switch (name) {
 | |
|       case 'DeviceGrayCS':
 | |
|         return this.singletons.gray;
 | |
|       case 'DeviceRgbCS':
 | |
|         return this.singletons.rgb;
 | |
|       case 'DeviceCmykCS':
 | |
|         return this.singletons.cmyk;
 | |
|       case 'CalGrayCS':
 | |
|         var whitePoint = IR[1].WhitePoint;
 | |
|         var blackPoint = IR[1].BlackPoint;
 | |
|         var gamma = IR[1].Gamma;
 | |
|         return new CalGrayCS(whitePoint, blackPoint, gamma);
 | |
|       case 'PatternCS':
 | |
|         var basePatternCS = IR[1];
 | |
|         if (basePatternCS)
 | |
|           basePatternCS = ColorSpace.fromIR(basePatternCS);
 | |
|         return new PatternCS(basePatternCS);
 | |
|       case 'IndexedCS':
 | |
|         var baseIndexedCS = IR[1];
 | |
|         var hiVal = IR[2];
 | |
|         var lookup = IR[3];
 | |
|         return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup);
 | |
|       case 'AlternateCS':
 | |
|         var numComps = IR[1];
 | |
|         var alt = IR[2];
 | |
|         var tintFnIR = IR[3];
 | |
| 
 | |
|         return new AlternateCS(numComps, ColorSpace.fromIR(alt),
 | |
|                                 PDFFunction.fromIR(tintFnIR));
 | |
|       case 'LabCS':
 | |
|         var whitePoint = IR[1].WhitePoint;
 | |
|         var blackPoint = IR[1].BlackPoint;
 | |
|         var range = IR[1].Range;
 | |
|         return new LabCS(whitePoint, blackPoint, range);
 | |
|       default:
 | |
|         error('Unkown name ' + name);
 | |
|     }
 | |
|     return null;
 | |
|   };
 | |
| 
 | |
|   ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) {
 | |
|     if (isName(cs)) {
 | |
|       var colorSpaces = res.get('ColorSpace');
 | |
|       if (isDict(colorSpaces)) {
 | |
|         var refcs = colorSpaces.get(cs.name);
 | |
|         if (refcs)
 | |
|           cs = refcs;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     cs = xref.fetchIfRef(cs);
 | |
|     var mode;
 | |
| 
 | |
|     if (isName(cs)) {
 | |
|       mode = cs.name;
 | |
|       this.mode = mode;
 | |
| 
 | |
|       switch (mode) {
 | |
|         case 'DeviceGray':
 | |
|         case 'G':
 | |
|           return 'DeviceGrayCS';
 | |
|         case 'DeviceRGB':
 | |
|         case 'RGB':
 | |
|           return 'DeviceRgbCS';
 | |
|         case 'DeviceCMYK':
 | |
|         case 'CMYK':
 | |
|           return 'DeviceCmykCS';
 | |
|         case 'Pattern':
 | |
|           return ['PatternCS', null];
 | |
|         default:
 | |
|           error('unrecognized colorspace ' + mode);
 | |
|       }
 | |
|     } else if (isArray(cs)) {
 | |
|       mode = cs[0].name;
 | |
|       this.mode = mode;
 | |
| 
 | |
|       switch (mode) {
 | |
|         case 'DeviceGray':
 | |
|         case 'G':
 | |
|           return 'DeviceGrayCS';
 | |
|         case 'DeviceRGB':
 | |
|         case 'RGB':
 | |
|           return 'DeviceRgbCS';
 | |
|         case 'DeviceCMYK':
 | |
|         case 'CMYK':
 | |
|           return 'DeviceCmykCS';
 | |
|         case 'CalGray':
 | |
|           var params = cs[1].getAll();
 | |
|           return ['CalGrayCS', params];
 | |
|         case 'CalRGB':
 | |
|           return 'DeviceRgbCS';
 | |
|         case 'ICCBased':
 | |
|           var stream = xref.fetchIfRef(cs[1]);
 | |
|           var dict = stream.dict;
 | |
|           var numComps = dict.get('N');
 | |
|           if (numComps == 1)
 | |
|             return 'DeviceGrayCS';
 | |
|           if (numComps == 3)
 | |
|             return 'DeviceRgbCS';
 | |
|           if (numComps == 4)
 | |
|             return 'DeviceCmykCS';
 | |
|           break;
 | |
|         case 'Pattern':
 | |
|           var basePatternCS = cs[1];
 | |
|           if (basePatternCS)
 | |
|             basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
 | |
|           return ['PatternCS', basePatternCS];
 | |
|         case 'Indexed':
 | |
|         case 'I':
 | |
|           var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
 | |
|           var hiVal = cs[2] + 1;
 | |
|           var lookup = xref.fetchIfRef(cs[3]);
 | |
|           if (isStream(lookup)) {
 | |
|             lookup = lookup.getBytes();
 | |
|           }
 | |
|           return ['IndexedCS', baseIndexedCS, hiVal, lookup];
 | |
|         case 'Separation':
 | |
|         case 'DeviceN':
 | |
|           var name = cs[1];
 | |
|           var numComps = 1;
 | |
|           if (isName(name))
 | |
|             numComps = 1;
 | |
|           else if (isArray(name))
 | |
|             numComps = name.length;
 | |
|           var alt = ColorSpace.parseToIR(cs[2], xref, res);
 | |
|           var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
 | |
|           return ['AlternateCS', numComps, alt, tintFnIR];
 | |
|         case 'Lab':
 | |
|           var params = cs[1].getAll();
 | |
|           return ['LabCS', params];
 | |
|         default:
 | |
|           error('unimplemented color space object "' + mode + '"');
 | |
|       }
 | |
|     } else {
 | |
|       error('unrecognized color space object: "' + cs + '"');
 | |
|     }
 | |
|     return null;
 | |
|   };
 | |
|   /**
 | |
|    * Checks if a decode map matches the default decode map for a color space.
 | |
|    * This handles the general decode maps where there are two values per
 | |
|    * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color.
 | |
|    * This does not handle Lab, Indexed, or Pattern decode maps since they are
 | |
|    * slightly different.
 | |
|    * @param {Array} decode Decode map (usually from an image).
 | |
|    * @param {Number} n Number of components the color space has.
 | |
|    */
 | |
|   ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
 | |
|     if (!decode)
 | |
|       return true;
 | |
| 
 | |
|     if (n * 2 !== decode.length) {
 | |
|       warn('The decode map is not the correct length');
 | |
|       return true;
 | |
|     }
 | |
|     for (var i = 0, ii = decode.length; i < ii; i += 2) {
 | |
|       if (decode[i] !== 0 || decode[i + 1] != 1)
 | |
|         return false;
 | |
|     }
 | |
|     return true;
 | |
|   };
 | |
| 
 | |
|   ColorSpace.singletons = {
 | |
|     get gray() {
 | |
|       return shadow(this, 'gray', new DeviceGrayCS());
 | |
|     },
 | |
|     get rgb() {
 | |
|       return shadow(this, 'rgb', new DeviceRgbCS());
 | |
|     },
 | |
|     get cmyk() {
 | |
|       return shadow(this, 'cmyk', new DeviceCmykCS());
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   return ColorSpace;
 | |
| })();
 | |
| 
 | |
| /**
 | |
|  * Alternate color space handles both Separation and DeviceN color spaces.  A
 | |
|  * Separation color space is actually just a DeviceN with one color component.
 | |
|  * Both color spaces use a tinting function to convert colors to a base color
 | |
|  * space.
 | |
|  */
 | |
| var AlternateCS = (function AlternateCSClosure() {
 | |
|   function AlternateCS(numComps, base, tintFn) {
 | |
|     this.name = 'Alternate';
 | |
|     this.numComps = numComps;
 | |
|     this.defaultColor = new Float32Array(numComps);
 | |
|     for (var i = 0; i < numComps; ++i) {
 | |
|       this.defaultColor[i] = 1;
 | |
|     }
 | |
|     this.base = base;
 | |
|     this.tintFn = tintFn;
 | |
|   }
 | |
| 
 | |
|   AlternateCS.prototype = {
 | |
|     getRgb: function AlternateCS_getRgb(src, srcOffset) {
 | |
|       var rgb = new Uint8Array(3);
 | |
|       this.getRgbItem(src, srcOffset, rgb, 0);
 | |
|       return rgb;
 | |
|     },
 | |
|     getRgbItem: function AlternateCS_getRgbItem(src, srcOffset,
 | |
|                                                 dest, destOffset) {
 | |
|       var baseNumComps = this.base.numComps;
 | |
|       var input = 'subarray' in src ?
 | |
|         src.subarray(srcOffset, srcOffset + this.numComps) :
 | |
|         Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps);
 | |
|       var tinted = this.tintFn(input);
 | |
|       this.base.getRgbItem(tinted, 0, dest, destOffset);
 | |
|     },
 | |
|     getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                                     dest, destOffset, bits) {
 | |
|       var tintFn = this.tintFn;
 | |
|       var base = this.base;
 | |
|       var scale = 1 / ((1 << bits) - 1);
 | |
|       var baseNumComps = base.numComps;
 | |
|       var usesZeroToOneRange = base.usesZeroToOneRange;
 | |
|       var isPassthrough = base.isPassthrough(8) || !usesZeroToOneRange;
 | |
|       var pos = isPassthrough ? destOffset : 0;
 | |
|       var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count);
 | |
|       var numComps = this.numComps;
 | |
| 
 | |
|       var scaled = new Float32Array(numComps);
 | |
|       for (var i = 0; i < count; i++) {
 | |
|         for (var j = 0; j < numComps; j++) {
 | |
|           scaled[j] = src[srcOffset++] * scale;
 | |
|         }
 | |
|         var tinted = tintFn(scaled);
 | |
|         if (usesZeroToOneRange) {
 | |
|           for (var j = 0; j < baseNumComps; j++) {
 | |
|             baseBuf[pos++] = tinted[j] * 255;
 | |
|           }
 | |
|         } else {
 | |
|           base.getRgbItem(tinted, 0, baseBuf, pos);
 | |
|           pos += baseNumComps;
 | |
|         }
 | |
|       }
 | |
|       if (!isPassthrough) {
 | |
|         base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8);
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function AlternateCS_getOutputLength(inputLength) {
 | |
|       return this.base.getOutputLength(inputLength *
 | |
|                                        this.base.numComps / this.numComps);
 | |
|     },
 | |
|     isPassthrough: ColorSpace.prototype.isPassthrough,
 | |
|     createRgbBuffer: ColorSpace.prototype.createRgbBuffer,
 | |
|     isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
 | |
|       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
 | |
|     },
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
| 
 | |
|   return AlternateCS;
 | |
| })();
 | |
| 
 | |
| var PatternCS = (function PatternCSClosure() {
 | |
|   function PatternCS(baseCS) {
 | |
|     this.name = 'Pattern';
 | |
|     this.base = baseCS;
 | |
|   }
 | |
|   PatternCS.prototype = {};
 | |
| 
 | |
|   return PatternCS;
 | |
| })();
 | |
| 
 | |
| var IndexedCS = (function IndexedCSClosure() {
 | |
|   function IndexedCS(base, highVal, lookup) {
 | |
|     this.name = 'Indexed';
 | |
|     this.numComps = 1;
 | |
|     this.defaultColor = new Uint8Array([0]);
 | |
|     this.base = base;
 | |
|     this.highVal = highVal;
 | |
| 
 | |
|     var baseNumComps = base.numComps;
 | |
|     var length = baseNumComps * highVal;
 | |
|     var lookupArray;
 | |
| 
 | |
|     if (isStream(lookup)) {
 | |
|       lookupArray = new Uint8Array(length);
 | |
|       var bytes = lookup.getBytes(length);
 | |
|       lookupArray.set(bytes);
 | |
|     } else if (isString(lookup)) {
 | |
|       lookupArray = new Uint8Array(length);
 | |
|       for (var i = 0; i < length; ++i)
 | |
|         lookupArray[i] = lookup.charCodeAt(i);
 | |
|     } else if (lookup instanceof Uint8Array || lookup instanceof Array) {
 | |
|       lookupArray = lookup;
 | |
|     } else {
 | |
|       error('Unrecognized lookup table: ' + lookup);
 | |
|     }
 | |
|     this.lookup = lookupArray;
 | |
|   }
 | |
| 
 | |
|   IndexedCS.prototype = {
 | |
|     getRgb: function IndexedCS_getRgb(src, srcOffset) {
 | |
|       var numComps = this.base.numComps;
 | |
|       var start = src[srcOffset] * numComps;
 | |
|       return this.base.getRgb(this.lookup, start);
 | |
|     },
 | |
|     getRgbItem: function IndexedCS_getRgbItem(src, srcOffset,
 | |
|                                               dest, destOffset) {
 | |
|       var numComps = this.base.numComps;
 | |
|       var start = src[srcOffset] * numComps;
 | |
|       this.base.getRgbItem(this.lookup, start, dest, destOffset);
 | |
|     },
 | |
|     getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                                   dest, destOffset) {
 | |
|       var base = this.base;
 | |
|       var numComps = base.numComps;
 | |
|       var outputDelta = base.getOutputLength(numComps);
 | |
|       var lookup = this.lookup;
 | |
| 
 | |
|       for (var i = 0; i < count; ++i) {
 | |
|         var lookupPos = src[srcOffset++] * numComps;
 | |
|         base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8);
 | |
|         destOffset += outputDelta;
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function IndexedCS_getOutputLength(inputLength) {
 | |
|       return this.base.getOutputLength(inputLength * this.base.numComps);
 | |
|     },
 | |
|     isPassthrough: ColorSpace.prototype.isPassthrough,
 | |
|     createRgbBuffer: ColorSpace.prototype.createRgbBuffer,
 | |
|     isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
 | |
|       // indexed color maps shouldn't be changed
 | |
|       return true;
 | |
|     },
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
|   return IndexedCS;
 | |
| })();
 | |
| 
 | |
| var DeviceGrayCS = (function DeviceGrayCSClosure() {
 | |
|   function DeviceGrayCS() {
 | |
|     this.name = 'DeviceGray';
 | |
|     this.numComps = 1;
 | |
|     this.defaultColor = new Float32Array([0]);
 | |
|   }
 | |
| 
 | |
|   DeviceGrayCS.prototype = {
 | |
|     getRgb: function DeviceGrayCS_getRgb(src, srcOffset) {
 | |
|       var rgb = new Uint8Array(3);
 | |
|       this.getRgbItem(src, srcOffset, rgb, 0);
 | |
|       return rgb;
 | |
|     },
 | |
|     getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset,
 | |
|                                                  dest, destOffset) {
 | |
|       var c = (src[srcOffset] * 255) | 0;
 | |
|       c = c < 0 ? 0 : c > 255 ? 255 : c;
 | |
|       dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c;
 | |
|     },
 | |
|     getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                                      dest, destOffset, bits) {
 | |
|       var scale = 255 / ((1 << bits) - 1);
 | |
|       var j = srcOffset, q = destOffset;
 | |
|       for (var i = 0; i < count; ++i) {
 | |
|         var c = (scale * src[j++]) | 0;
 | |
|         dest[q++] = c;
 | |
|         dest[q++] = c;
 | |
|         dest[q++] = c;
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function DeviceGrayCS_getOutputLength(inputLength) {
 | |
|       return inputLength * 3;
 | |
|     },
 | |
|     isPassthrough: ColorSpace.prototype.isPassthrough,
 | |
|     createRgbBuffer: ColorSpace.prototype.createRgbBuffer,
 | |
|     isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
 | |
|       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
 | |
|     },
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
|   return DeviceGrayCS;
 | |
| })();
 | |
| 
 | |
| var DeviceRgbCS = (function DeviceRgbCSClosure() {
 | |
|   function DeviceRgbCS() {
 | |
|     this.name = 'DeviceRGB';
 | |
|     this.numComps = 3;
 | |
|     this.defaultColor = new Float32Array([0, 0, 0]);
 | |
|   }
 | |
|   DeviceRgbCS.prototype = {
 | |
|     getRgb: function DeviceRgbCS_getRgb(src, srcOffset) {
 | |
|       var rgb = new Uint8Array(3);
 | |
|       this.getRgbItem(src, srcOffset, rgb, 0);
 | |
|       return rgb;
 | |
|     },
 | |
|     getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset,
 | |
|                                                 dest, destOffset) {
 | |
|       var r = (src[srcOffset] * 255) | 0;
 | |
|       var g = (src[srcOffset + 1] * 255) | 0;
 | |
|       var b = (src[srcOffset + 2] * 255) | 0;
 | |
|       dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r;
 | |
|       dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g;
 | |
|       dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b;
 | |
|     },
 | |
|     getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                                     dest, destOffset, bits) {
 | |
|       var length = count * 3;
 | |
|       if (bits == 8) {
 | |
|         dest.set(src.subarray(srcOffset, srcOffset + length), destOffset);
 | |
|         return;
 | |
|       }
 | |
|       var scale = 255 / ((1 << bits) - 1);
 | |
|       var j = srcOffset, q = destOffset;
 | |
|       for (var i = 0; i < length; ++i) {
 | |
|         dest[q++] = (scale * src[j++]) | 0;
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function DeviceRgbCS_getOutputLength(inputLength) {
 | |
|       return inputLength;
 | |
|     },
 | |
|     isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
 | |
|       return bits == 8;
 | |
|     },
 | |
|     createRgbBuffer: ColorSpace.prototype.createRgbBuffer,
 | |
|     isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
 | |
|       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
 | |
|     },
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
|   return DeviceRgbCS;
 | |
| })();
 | |
| 
 | |
| var DeviceCmykCS = (function DeviceCmykCSClosure() {
 | |
|   // The coefficients below was found using numerical analysis: the method of
 | |
|   // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors,
 | |
|   // where color_value is the tabular value from the table of sampled RGB colors
 | |
|   // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding
 | |
|   // CMYK color conversion using the estimation below:
 | |
|   //   f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255
 | |
|   function convertToRgb(src, srcOffset, srcScale, dest, destOffset) {
 | |
|     var c = src[srcOffset + 0] * srcScale;
 | |
|     var m = src[srcOffset + 1] * srcScale;
 | |
|     var y = src[srcOffset + 2] * srcScale;
 | |
|     var k = src[srcOffset + 3] * srcScale;
 | |
| 
 | |
|     var r =
 | |
|       c * (-4.387332384609988 * c + 54.48615194189176 * m +
 | |
|            18.82290502165302 * y + 212.25662451639585 * k +
 | |
|            -285.2331026137004) +
 | |
|       m * (1.7149763477362134 * m - 5.6096736904047315 * y +
 | |
|            -17.873870861415444 * k - 5.497006427196366) +
 | |
|       y * (-2.5217340131683033 * y - 21.248923337353073 * k +
 | |
|            17.5119270841813) +
 | |
|       k * (-21.86122147463605 * k - 189.48180835922747) + 255;
 | |
|     var g =
 | |
|       c * (8.841041422036149 * c + 60.118027045597366 * m +
 | |
|            6.871425592049007 * y + 31.159100130055922 * k +
 | |
|            -79.2970844816548) +
 | |
|       m * (-15.310361306967817 * m + 17.575251261109482 * y +
 | |
|            131.35250912493976 * k - 190.9453302588951) +
 | |
|       y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
 | |
|       k * (-20.737325471181034 * k - 187.80453709719578) + 255;
 | |
|     var b =
 | |
|       c * (0.8842522430003296 * c + 8.078677503112928 * m +
 | |
|            30.89978309703729 * y - 0.23883238689178934 * k +
 | |
|            -14.183576799673286) +
 | |
|       m * (10.49593273432072 * m + 63.02378494754052 * y +
 | |
|            50.606957656360734 * k - 112.23884253719248) +
 | |
|       y * (0.03296041114873217 * y + 115.60384449646641 * k +
 | |
|            -193.58209356861505) +
 | |
|       k * (-22.33816807309886 * k - 180.12613974708367) + 255;
 | |
| 
 | |
|     dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r;
 | |
|     dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
 | |
|     dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
 | |
|   }
 | |
| 
 | |
|   function DeviceCmykCS() {
 | |
|     this.name = 'DeviceCMYK';
 | |
|     this.numComps = 4;
 | |
|     this.defaultColor = new Float32Array([0, 0, 0, 1]);
 | |
|   }
 | |
|   DeviceCmykCS.prototype = {
 | |
|     getRgb: function DeviceCmykCS_getRgb(src, srcOffset) {
 | |
|       var rgb = new Uint8Array(3);
 | |
|       convertToRgb(src, srcOffset, 1, rgb, 0);
 | |
|       return rgb;
 | |
|     },
 | |
|     getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset,
 | |
|                                                  dest, destOffset) {
 | |
|       convertToRgb(src, srcOffset, 1, dest, destOffset);
 | |
|     },
 | |
|     getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                                      dest, destOffset, bits) {
 | |
|       var scale = 1 / ((1 << bits) - 1);
 | |
|       for (var i = 0; i < count; i++) {
 | |
|         convertToRgb(src, srcOffset, scale, dest, destOffset);
 | |
|         srcOffset += 4;
 | |
|         destOffset += 3;
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function DeviceCmykCS_getOutputLength(inputLength) {
 | |
|       return (inputLength >> 2) * 3;
 | |
|     },
 | |
|     isPassthrough: ColorSpace.prototype.isPassthrough,
 | |
|     createRgbBuffer: ColorSpace.prototype.createRgbBuffer,
 | |
|     isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
 | |
|       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
 | |
|     },
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
| 
 | |
|   return DeviceCmykCS;
 | |
| })();
 | |
| 
 | |
| //
 | |
| // CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245
 | |
| //
 | |
| var CalGrayCS = (function CalGrayCSClosure() {
 | |
|   function CalGrayCS(whitePoint, blackPoint, gamma) {
 | |
|     this.name = 'CalGray';
 | |
|     this.numComps = 3;
 | |
|     this.defaultColor = new Float32Array([0, 0, 0]);
 | |
| 
 | |
|     if (!whitePoint) {
 | |
|       error('WhitePoint missing - required for color space CalGray');
 | |
|     }
 | |
|     blackPoint = blackPoint || [0, 0, 0];
 | |
|     gamma = gamma || 1;
 | |
| 
 | |
|     // Translate arguments to spec variables.
 | |
|     this.XW = whitePoint[0];
 | |
|     this.YW = whitePoint[1];
 | |
|     this.ZW = whitePoint[2];
 | |
| 
 | |
|     this.XB = blackPoint[0];
 | |
|     this.YB = blackPoint[1];
 | |
|     this.ZB = blackPoint[2];
 | |
| 
 | |
|     this.G = gamma;
 | |
| 
 | |
|     // Validate variables as per spec.
 | |
|     if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) {
 | |
|       error('Invalid WhitePoint components for ' + this.name +
 | |
|             ', no fallback available');
 | |
|     }
 | |
| 
 | |
|     if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
 | |
|       info('Invalid BlackPoint for ' + this.name + ', falling back to default');
 | |
|       this.XB = this.YB = this.ZB = 0;
 | |
|     }
 | |
| 
 | |
|     if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) {
 | |
|       TODO(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB +
 | |
|            ', ZB: ' + this.ZB + ', only default values are supported.');
 | |
|     }
 | |
| 
 | |
|     if (this.G < 1) {
 | |
|       info('Invalid Gamma: ' + this.G + ' for ' + this.name +
 | |
|            ', falling back to default');
 | |
|       this.G = 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   CalGrayCS.prototype = {
 | |
|     getRgb: function CalGrayCS_getRgb(src, srcOffset) {
 | |
|       var rgb = new Uint8Array(3);
 | |
|       this.getRgbItem(src, srcOffset, rgb, 0);
 | |
|       return rgb;
 | |
|     },
 | |
|     getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset,
 | |
|                                               dest, destOffset) {
 | |
|       // A represents a gray component of a calibrated gray space.
 | |
|       // A <---> AG in the spec
 | |
|       var A = src[srcOffset];
 | |
|       var AG = Math.pow(A, this.G);
 | |
| 
 | |
|       // Computes intermediate variables M, L, N as per spec.
 | |
|       // Except if other than default BlackPoint values are used.
 | |
|       var M = this.XW * AG;
 | |
|       var L = this.YW * AG;
 | |
|       var N = this.ZW * AG;
 | |
| 
 | |
|       // Decode XYZ, as per spec.
 | |
|       var X = M;
 | |
|       var Y = L;
 | |
|       var Z = N;
 | |
| 
 | |
|       // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4.
 | |
|       // This yields values in range [0, 100].
 | |
|       var Lstar = Math.max(116 * Math.pow(Y, 1 / 3) - 16, 0);
 | |
| 
 | |
|       // Convert values to rgb range [0, 255].
 | |
|       dest[destOffset] = Lstar * 255 / 100;
 | |
|       dest[destOffset + 1] = Lstar * 255 / 100;
 | |
|       dest[destOffset + 2] = Lstar * 255 / 100;
 | |
|     },
 | |
|     getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                                   dest, destOffset, bits) {
 | |
|       // TODO: This part is copied from DeviceGray. Make this utility function.
 | |
|       var scale = 255 / ((1 << bits) - 1);
 | |
|       var j = srcOffset, q = destOffset;
 | |
|       for (var i = 0; i < count; ++i) {
 | |
|         var c = (scale * src[j++]) | 0;
 | |
|         dest[q++] = c;
 | |
|         dest[q++] = c;
 | |
|         dest[q++] = c;
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function CalGrayCS_getOutputLength(inputLength) {
 | |
|       return inputLength * 3;
 | |
|     },
 | |
|     isPassthrough: ColorSpace.prototype.isPassthrough,
 | |
|     createRgbBuffer: ColorSpace.prototype.createRgbBuffer,
 | |
|     isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) {
 | |
|       return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
 | |
|     },
 | |
|     usesZeroToOneRange: true
 | |
|   };
 | |
|   return CalGrayCS;
 | |
| })();
 | |
| 
 | |
| //
 | |
| // LabCS: Based on "PDF Reference, Sixth Ed", p.250
 | |
| //
 | |
| var LabCS = (function LabCSClosure() {
 | |
|   function LabCS(whitePoint, blackPoint, range) {
 | |
|     this.name = 'Lab';
 | |
|     this.numComps = 3;
 | |
|     this.defaultColor = new Float32Array([0, 0, 0]);
 | |
| 
 | |
|     if (!whitePoint)
 | |
|       error('WhitePoint missing - required for color space Lab');
 | |
|     blackPoint = blackPoint || [0, 0, 0];
 | |
|     range = range || [-100, 100, -100, 100];
 | |
| 
 | |
|     // Translate args to spec variables
 | |
|     this.XW = whitePoint[0];
 | |
|     this.YW = whitePoint[1];
 | |
|     this.ZW = whitePoint[2];
 | |
|     this.amin = range[0];
 | |
|     this.amax = range[1];
 | |
|     this.bmin = range[2];
 | |
|     this.bmax = range[3];
 | |
| 
 | |
|     // These are here just for completeness - the spec doesn't offer any
 | |
|     // formulas that use BlackPoint in Lab
 | |
|     this.XB = blackPoint[0];
 | |
|     this.YB = blackPoint[1];
 | |
|     this.ZB = blackPoint[2];
 | |
| 
 | |
|     // Validate vars as per spec
 | |
|     if (this.XW < 0 || this.ZW < 0 || this.YW !== 1)
 | |
|       error('Invalid WhitePoint components, no fallback available');
 | |
| 
 | |
|     if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
 | |
|       info('Invalid BlackPoint, falling back to default');
 | |
|       this.XB = this.YB = this.ZB = 0;
 | |
|     }
 | |
| 
 | |
|     if (this.amin > this.amax || this.bmin > this.bmax) {
 | |
|       info('Invalid Range, falling back to defaults');
 | |
|       this.amin = -100;
 | |
|       this.amax = 100;
 | |
|       this.bmin = -100;
 | |
|       this.bmax = 100;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Function g(x) from spec
 | |
|   function fn_g(x) {
 | |
|     if (x >= 6 / 29)
 | |
|       return x * x * x;
 | |
|     else
 | |
|       return (108 / 841) * (x - 4 / 29);
 | |
|   }
 | |
| 
 | |
|   function decode(value, high1, low2, high2) {
 | |
|     return low2 + (value) * (high2 - low2) / (high1);
 | |
|   }
 | |
| 
 | |
|   // If decoding is needed maxVal should be 2^bits per component - 1.
 | |
|   function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) {
 | |
|     // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax]
 | |
|     // not the usual [0, 1]. If a command like setFillColor is used the src
 | |
|     // values will already be within the correct range. However, if we are
 | |
|     // converting an image we have to map the values to the correct range given
 | |
|     // above.
 | |
|     // Ls,as,bs <---> L*,a*,b* in the spec
 | |
|     var Ls = src[srcOffset];
 | |
|     var as = src[srcOffset + 1];
 | |
|     var bs = src[srcOffset + 2];
 | |
|     if (maxVal !== false) {
 | |
|       Ls = decode(Ls, maxVal, 0, 100);
 | |
|       as = decode(as, maxVal, cs.amin, cs.amax);
 | |
|       bs = decode(bs, maxVal, cs.bmin, cs.bmax);
 | |
|     }
 | |
| 
 | |
|     // Adjust limits of 'as' and 'bs'
 | |
|     as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as;
 | |
|     bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs;
 | |
| 
 | |
|     // Computes intermediate variables X,Y,Z as per spec
 | |
|     var M = (Ls + 16) / 116;
 | |
|     var L = M + (as / 500);
 | |
|     var N = M - (bs / 200);
 | |
| 
 | |
|     var X = cs.XW * fn_g(L);
 | |
|     var Y = cs.YW * fn_g(M);
 | |
|     var Z = cs.ZW * fn_g(N);
 | |
| 
 | |
|     var r, g, b;
 | |
|     // Using different conversions for D50 and D65 white points,
 | |
|     // per http://www.color.org/srgb.pdf
 | |
|     if (cs.ZW < 1) {
 | |
|       // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249)
 | |
|       r = X * 3.1339 + Y * -1.6170 + Z * -0.4906;
 | |
|       g = X * -0.9785 + Y * 1.9160 + Z * 0.0333;
 | |
|       b = X * 0.0720 + Y * -0.2290 + Z * 1.4057;
 | |
|     } else {
 | |
|       // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888)
 | |
|       r = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
 | |
|       g = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
 | |
|       b = X * 0.0557 + Y * -0.2040 + Z * 1.0570;
 | |
|     }
 | |
|     // clamp color values to [0,1] range then convert to [0,255] range.
 | |
|     dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255;
 | |
|     dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255;
 | |
|     dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255;
 | |
|   }
 | |
| 
 | |
|   LabCS.prototype = {
 | |
|     getRgb: function LabCS_getRgb(src, srcOffset) {
 | |
|       var rgb = new Uint8Array(3);
 | |
|       convertToRgb(this, src, srcOffset, false, rgb, 0);
 | |
|       return rgb;
 | |
|     },
 | |
|     getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) {
 | |
|       convertToRgb(this, src, srcOffset, false, dest, destOffset);
 | |
|     },
 | |
|     getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count,
 | |
|                                               dest, destOffset, bits) {
 | |
|       var maxVal = (1 << bits) - 1;
 | |
|       for (var i = 0; i < count; i++) {
 | |
|         convertToRgb(this, src, srcOffset, maxVal, dest, destOffset);
 | |
|         srcOffset += 3;
 | |
|         destOffset += 3;
 | |
|       }
 | |
|     },
 | |
|     getOutputLength: function LabCS_getOutputLength(inputLength) {
 | |
|       return inputLength;
 | |
|     },
 | |
|     isPassthrough: ColorSpace.prototype.isPassthrough,
 | |
|     isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
 | |
|       // XXX: Decoding is handled with the lab conversion because of the strange
 | |
|       // ranges that are used.
 | |
|       return true;
 | |
|     },
 | |
|     usesZeroToOneRange: false
 | |
|   };
 | |
|   return LabCS;
 | |
| })();
 | |
| 
 | |
| 
 | |
| 
 | |
| var PatternType = {
 | |
|   AXIAL: 2,
 | |
|   RADIAL: 3
 | |
| };
 | |
| 
 | |
| var Pattern = (function PatternClosure() {
 | |
|   // Constructor should define this.getPattern
 | |
|   function Pattern() {
 | |
|     error('should not call Pattern constructor');
 | |
|   }
 | |
| 
 | |
|   Pattern.prototype = {
 | |
|     // Input: current Canvas context
 | |
|     // Output: the appropriate fillStyle or strokeStyle
 | |
|     getPattern: function Pattern_getPattern(ctx) {
 | |
|       error('Should not call Pattern.getStyle: ' + ctx);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Pattern.shadingFromIR = function Pattern_shadingFromIR(raw) {
 | |
|     return Shadings[raw[0]].fromIR(raw);
 | |
|   };
 | |
| 
 | |
|   Pattern.parseShading = function Pattern_parseShading(shading, matrix, xref,
 | |
|                                                        res) {
 | |
| 
 | |
|     var dict = isStream(shading) ? shading.dict : shading;
 | |
|     var type = dict.get('ShadingType');
 | |
| 
 | |
|     switch (type) {
 | |
|       case PatternType.AXIAL:
 | |
|       case PatternType.RADIAL:
 | |
|         // Both radial and axial shadings are handled by RadialAxial shading.
 | |
|         return new Shadings.RadialAxial(dict, matrix, xref, res);
 | |
|       default:
 | |
|         TODO('Unsupported shading type: ' + type);
 | |
|         return new Shadings.Dummy();
 | |
|     }
 | |
|   };
 | |
|   return Pattern;
 | |
| })();
 | |
| 
 | |
| var Shadings = {};
 | |
| 
 | |
| // A small number to offset the first/last color stops so we can insert ones to
 | |
| // support extend.  Number.MIN_VALUE appears to be too small and breaks the
 | |
| // extend. 1e-7 works in FF but chrome seems to use an even smaller sized number
 | |
| // internally so we have to go bigger.
 | |
| Shadings.SMALL_NUMBER = 1e-2;
 | |
| 
 | |
| // Radial and axial shading have very similar implementations
 | |
| // If needed, the implementations can be broken into two classes
 | |
| Shadings.RadialAxial = (function RadialAxialClosure() {
 | |
|   function RadialAxial(dict, matrix, xref, res, ctx) {
 | |
|     this.matrix = matrix;
 | |
|     this.coordsArr = dict.get('Coords');
 | |
|     this.shadingType = dict.get('ShadingType');
 | |
|     this.type = 'Pattern';
 | |
|     this.ctx = ctx;
 | |
|     var cs = dict.get('ColorSpace', 'CS');
 | |
|     cs = ColorSpace.parse(cs, xref, res);
 | |
|     this.cs = cs;
 | |
| 
 | |
|     var t0 = 0.0, t1 = 1.0;
 | |
|     if (dict.has('Domain')) {
 | |
|       var domainArr = dict.get('Domain');
 | |
|       t0 = domainArr[0];
 | |
|       t1 = domainArr[1];
 | |
|     }
 | |
| 
 | |
|     var extendStart = false, extendEnd = false;
 | |
|     if (dict.has('Extend')) {
 | |
|       var extendArr = dict.get('Extend');
 | |
|       extendStart = extendArr[0];
 | |
|       extendEnd = extendArr[1];
 | |
|     }
 | |
| 
 | |
|     if (this.shadingType === PatternType.RADIAL &&
 | |
|        (!extendStart || !extendEnd)) {
 | |
|       // Radial gradient only currently works if either circle is fully within
 | |
|       // the other circle.
 | |
|       var x1 = this.coordsArr[0];
 | |
|       var y1 = this.coordsArr[1];
 | |
|       var r1 = this.coordsArr[2];
 | |
|       var x2 = this.coordsArr[3];
 | |
|       var y2 = this.coordsArr[4];
 | |
|       var r2 = this.coordsArr[5];
 | |
|       var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
 | |
|       if (r1 <= r2 + distance &&
 | |
|           r2 <= r1 + distance) {
 | |
|         warn('Unsupported radial gradient.');
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     this.extendStart = extendStart;
 | |
|     this.extendEnd = extendEnd;
 | |
| 
 | |
|     var fnObj = dict.get('Function');
 | |
|     var fn;
 | |
|     if (isArray(fnObj)) {
 | |
|       var fnArray = [];
 | |
|       for (var j = 0, jj = fnObj.length; j < jj; j++) {
 | |
|         var obj = xref.fetchIfRef(fnObj[j]);
 | |
|         if (!isPDFFunction(obj)) {
 | |
|           error('Invalid function');
 | |
|         }
 | |
|         fnArray.push(PDFFunction.parse(xref, obj));
 | |
|       }
 | |
|       fn = function radialAxialColorFunction(arg) {
 | |
|         var out = [];
 | |
|         for (var i = 0, ii = fnArray.length; i < ii; i++) {
 | |
|           out.push(fnArray[i](arg)[0]);
 | |
|         }
 | |
|         return out;
 | |
|       };
 | |
|     } else {
 | |
|       if (!isPDFFunction(fnObj)) {
 | |
|         error('Invalid function');
 | |
|       }
 | |
|       fn = PDFFunction.parse(xref, fnObj);
 | |
|     }
 | |
| 
 | |
|     // 10 samples seems good enough for now, but probably won't work
 | |
|     // if there are sharp color changes. Ideally, we would implement
 | |
|     // the spec faithfully and add lossless optimizations.
 | |
|     var diff = t1 - t0;
 | |
|     var step = diff / 10;
 | |
| 
 | |
|     var colorStops = this.colorStops = [];
 | |
| 
 | |
|     // Protect against bad domains so we don't end up in an infinte loop below.
 | |
|     if (t0 >= t1 || step <= 0) {
 | |
|       // Acrobat doesn't seem to handle these cases so we'll ignore for
 | |
|       // now.
 | |
|       info('Bad shading domain.');
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     for (var i = t0; i <= t1; i += step) {
 | |
|       var rgbColor = cs.getRgb(fn([i]), 0);
 | |
|       var cssColor = Util.makeCssRgb(rgbColor);
 | |
|       colorStops.push([(i - t0) / diff, cssColor]);
 | |
|     }
 | |
| 
 | |
|     var background = 'transparent';
 | |
|     if (dict.has('Background')) {
 | |
|       var rgbColor = cs.getRgb(dict.get('Background'), 0);
 | |
|       background = Util.makeCssRgb(rgbColor);
 | |
|     }
 | |
| 
 | |
|     if (!extendStart) {
 | |
|       // Insert a color stop at the front and offset the first real color stop
 | |
|       // so it doesn't conflict with the one we insert.
 | |
|       colorStops.unshift([0, background]);
 | |
|       colorStops[1][0] += Shadings.SMALL_NUMBER;
 | |
|     }
 | |
|     if (!extendEnd) {
 | |
|       // Same idea as above in extendStart but for the end.
 | |
|       colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
 | |
|       colorStops.push([1, background]);
 | |
|     }
 | |
| 
 | |
|     this.colorStops = colorStops;
 | |
|   }
 | |
| 
 | |
|   RadialAxial.fromIR = function RadialAxial_fromIR(raw) {
 | |
|     var type = raw[1];
 | |
|     var colorStops = raw[2];
 | |
|     var p0 = raw[3];
 | |
|     var p1 = raw[4];
 | |
|     var r0 = raw[5];
 | |
|     var r1 = raw[6];
 | |
|     return {
 | |
|       type: 'Pattern',
 | |
|       getPattern: function RadialAxial_getPattern(ctx) {
 | |
|         var grad;
 | |
|         if (type == PatternType.AXIAL)
 | |
|           grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
 | |
|         else if (type == PatternType.RADIAL)
 | |
|           grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
 | |
| 
 | |
|         for (var i = 0, ii = colorStops.length; i < ii; ++i) {
 | |
|           var c = colorStops[i];
 | |
|           grad.addColorStop(c[0], c[1]);
 | |
|         }
 | |
|         return grad;
 | |
|       }
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   RadialAxial.prototype = {
 | |
|     getIR: function RadialAxial_getIR() {
 | |
|       var coordsArr = this.coordsArr;
 | |
|       var type = this.shadingType;
 | |
|       if (type == PatternType.AXIAL) {
 | |
|         var p0 = [coordsArr[0], coordsArr[1]];
 | |
|         var p1 = [coordsArr[2], coordsArr[3]];
 | |
|         var r0 = null;
 | |
|         var r1 = null;
 | |
|       } else if (type == PatternType.RADIAL) {
 | |
|         var p0 = [coordsArr[0], coordsArr[1]];
 | |
|         var p1 = [coordsArr[3], coordsArr[4]];
 | |
|         var r0 = coordsArr[2];
 | |
|         var r1 = coordsArr[5];
 | |
|       } else {
 | |
|         error('getPattern type unknown: ' + type);
 | |
|       }
 | |
| 
 | |
|       var matrix = this.matrix;
 | |
|       if (matrix) {
 | |
|         p0 = Util.applyTransform(p0, matrix);
 | |
|         p1 = Util.applyTransform(p1, matrix);
 | |
|       }
 | |
| 
 | |
|       return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1];
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   return RadialAxial;
 | |
| })();
 | |
| 
 | |
| Shadings.Dummy = (function DummyClosure() {
 | |
|   function Dummy() {
 | |
|     this.type = 'Pattern';
 | |
|   }
 | |
| 
 | |
|   Dummy.fromIR = function Dummy_fromIR() {
 | |
|     return {
 | |
|       type: 'Pattern',
 | |
|       getPattern: function Dummy_fromIR_getPattern() {
 | |
|         return 'hotpink';
 | |
|       }
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   Dummy.prototype = {
 | |
|     getIR: function Dummy_getIR() {
 | |
|       return ['Dummy'];
 | |
|     }
 | |
|   };
 | |
|   return Dummy;
 | |
| })();
 | |
| 
 | |
| var TilingPattern = (function TilingPatternClosure() {
 | |
|   var PaintType = {
 | |
|     COLORED: 1,
 | |
|     UNCOLORED: 2
 | |
|   };
 | |
| 
 | |
|   var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
 | |
| 
 | |
|   function TilingPattern(IR, color, ctx, objs, commonObjs, baseTransform) {
 | |
|     this.name = IR[1][0].name;
 | |
|     this.operatorList = IR[2];
 | |
|     this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
 | |
|     this.bbox = IR[4];
 | |
|     this.xstep = IR[5];
 | |
|     this.ystep = IR[6];
 | |
|     this.paintType = IR[7];
 | |
|     this.tilingType = IR[8];
 | |
|     this.color = color;
 | |
|     this.objs = objs;
 | |
|     this.commonObjs = commonObjs;
 | |
|     this.baseTransform = baseTransform;
 | |
|     this.type = 'Pattern';
 | |
|     this.ctx = ctx;
 | |
|   }
 | |
| 
 | |
|   TilingPattern.getIR = function TilingPattern_getIR(operatorList, dict, args) {
 | |
|     var matrix = dict.get('Matrix');
 | |
|     var bbox = dict.get('BBox');
 | |
|     var xstep = dict.get('XStep');
 | |
|     var ystep = dict.get('YStep');
 | |
|     var paintType = dict.get('PaintType');
 | |
|     var tilingType = dict.get('TilingType');
 | |
| 
 | |
|     return [
 | |
|       'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep,
 | |
|       paintType, tilingType
 | |
|     ];
 | |
|   };
 | |
| 
 | |
|   TilingPattern.prototype = {
 | |
|     createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
 | |
|       var operatorList = this.operatorList;
 | |
|       var bbox = this.bbox;
 | |
|       var xstep = this.xstep;
 | |
|       var ystep = this.ystep;
 | |
|       var paintType = this.paintType;
 | |
|       var tilingType = this.tilingType;
 | |
|       var color = this.color;
 | |
|       var objs = this.objs;
 | |
|       var commonObjs = this.commonObjs;
 | |
|       var ctx = this.ctx;
 | |
| 
 | |
|       TODO('TilingType: ' + tilingType);
 | |
| 
 | |
|       var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
 | |
| 
 | |
|       var topLeft = [x0, y0];
 | |
|       // we want the canvas to be as large as the step size
 | |
|       var botRight = [x0 + xstep, y0 + ystep];
 | |
| 
 | |
|       var width = botRight[0] - topLeft[0];
 | |
|       var height = botRight[1] - topLeft[1];
 | |
| 
 | |
|       // Obtain scale from matrix and current transformation matrix.
 | |
|       var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
 | |
|       var curMatrixScale = Util.singularValueDecompose2dScale(
 | |
|                              this.baseTransform);
 | |
|       var combinedScale = [matrixScale[0] * curMatrixScale[0],
 | |
|                            matrixScale[1] * curMatrixScale[1]];
 | |
| 
 | |
|       // MAX_PATTERN_SIZE is used to avoid OOM situation.
 | |
|       // Use width and height values that are as close as possible to the end
 | |
|       // result when the pattern is used. Too low value makes the pattern look
 | |
|       // blurry. Too large value makes it look too crispy.
 | |
|       width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])),
 | |
|                        MAX_PATTERN_SIZE);
 | |
| 
 | |
|       height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
 | |
|                         MAX_PATTERN_SIZE);
 | |
| 
 | |
|       var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true);
 | |
|       var tmpCtx = tmpCanvas.context;
 | |
|       var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs);
 | |
|       graphics.groupLevel = owner.groupLevel;
 | |
| 
 | |
|       this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color);
 | |
| 
 | |
|       this.setScale(width, height, xstep, ystep);
 | |
|       this.transformToScale(graphics);
 | |
| 
 | |
|       // transform coordinates to pattern space
 | |
|       var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
 | |
|       graphics.transform.apply(graphics, tmpTranslate);
 | |
| 
 | |
|       this.clipBbox(graphics, bbox, x0, y0, x1, y1);
 | |
| 
 | |
|       graphics.executeOperatorList(operatorList);
 | |
|       return tmpCanvas.canvas;
 | |
|     },
 | |
| 
 | |
|     setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
 | |
|       this.scale = [width / xstep, height / ystep];
 | |
|     },
 | |
| 
 | |
|     transformToScale: function TilingPattern_transformToScale(graphics) {
 | |
|       var scale = this.scale;
 | |
|       var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
 | |
|       graphics.transform.apply(graphics, tmpScale);
 | |
|     },
 | |
| 
 | |
|     scaleToContext: function TilingPattern_scaleToContext() {
 | |
|       var scale = this.scale;
 | |
|       this.ctx.scale(1 / scale[0], 1 / scale[1]);
 | |
|     },
 | |
| 
 | |
|     clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
 | |
|       if (bbox && isArray(bbox) && 4 == bbox.length) {
 | |
|         var bboxWidth = x1 - x0;
 | |
|         var bboxHeight = y1 - y0;
 | |
|         graphics.rectangle(x0, y0, bboxWidth, bboxHeight);
 | |
|         graphics.clip();
 | |
|         graphics.endPath();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     setFillAndStrokeStyleToContext:
 | |
|       function setFillAndStrokeStyleToContext(context, paintType, color) {
 | |
|       switch (paintType) {
 | |
|         case PaintType.COLORED:
 | |
|           var ctx = this.ctx;
 | |
|           context.fillStyle = ctx.fillStyle;
 | |
|           context.strokeStyle = ctx.strokeStyle;
 | |
|           break;
 | |
|         case PaintType.UNCOLORED:
 | |
|           var rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);
 | |
|           var cssColor = Util.makeCssRgb(rgbColor);
 | |
|           context.fillStyle = cssColor;
 | |
|           context.strokeStyle = cssColor;
 | |
|           break;
 | |
|         default:
 | |
|           error('Unsupported paint type: ' + paintType);
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     getPattern: function TilingPattern_getPattern(ctx, owner) {
 | |
|       var temporaryPatternCanvas = this.createPatternCanvas(owner);
 | |
| 
 | |
|       var ctx = this.ctx;
 | |
|       ctx.setTransform.apply(ctx, this.baseTransform);
 | |
|       ctx.transform.apply(ctx, this.matrix);
 | |
|       this.scaleToContext();
 | |
| 
 | |
|       return ctx.createPattern(temporaryPatternCanvas, 'repeat');
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   return TilingPattern;
 | |
| })();
 | |
| 
 | |
| 
 | |
| 
 | |
| var PDFFunction = (function PDFFunctionClosure() {
 | |
|   var CONSTRUCT_SAMPLED = 0;
 | |
|   var CONSTRUCT_INTERPOLATED = 2;
 | |
|   var CONSTRUCT_STICHED = 3;
 | |
|   var CONSTRUCT_POSTSCRIPT = 4;
 | |
| 
 | |
|   return {
 | |
|     getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps,
 | |
|                                                        str) {
 | |
|       var length = 1;
 | |
|       for (var i = 0, ii = size.length; i < ii; i++)
 | |
|         length *= size[i];
 | |
|       length *= outputSize;
 | |
| 
 | |
|       var array = [];
 | |
|       var codeSize = 0;
 | |
|       var codeBuf = 0;
 | |
|       // 32 is a valid bps so shifting won't work
 | |
|       var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1);
 | |
| 
 | |
|       var strBytes = str.getBytes((length * bps + 7) / 8);
 | |
|       var strIdx = 0;
 | |
|       for (var i = 0; i < length; i++) {
 | |
|         while (codeSize < bps) {
 | |
|           codeBuf <<= 8;
 | |
|           codeBuf |= strBytes[strIdx++];
 | |
|           codeSize += 8;
 | |
|         }
 | |
|         codeSize -= bps;
 | |
|         array.push((codeBuf >> codeSize) * sampleMul);
 | |
|         codeBuf &= (1 << codeSize) - 1;
 | |
|       }
 | |
|       return array;
 | |
|     },
 | |
| 
 | |
|     getIR: function PDFFunction_getIR(xref, fn) {
 | |
|       var dict = fn.dict;
 | |
|       if (!dict)
 | |
|         dict = fn;
 | |
| 
 | |
|       var types = [this.constructSampled,
 | |
|                    null,
 | |
|                    this.constructInterpolated,
 | |
|                    this.constructStiched,
 | |
|                    this.constructPostScript];
 | |
| 
 | |
|       var typeNum = dict.get('FunctionType');
 | |
|       var typeFn = types[typeNum];
 | |
|       if (!typeFn)
 | |
|         error('Unknown type of function');
 | |
| 
 | |
|       return typeFn.call(this, fn, dict, xref);
 | |
|     },
 | |
| 
 | |
|     fromIR: function PDFFunction_fromIR(IR) {
 | |
|       var type = IR[0];
 | |
|       switch (type) {
 | |
|         case CONSTRUCT_SAMPLED:
 | |
|           return this.constructSampledFromIR(IR);
 | |
|         case CONSTRUCT_INTERPOLATED:
 | |
|           return this.constructInterpolatedFromIR(IR);
 | |
|         case CONSTRUCT_STICHED:
 | |
|           return this.constructStichedFromIR(IR);
 | |
|         //case CONSTRUCT_POSTSCRIPT:
 | |
|         default:
 | |
|           return this.constructPostScriptFromIR(IR);
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     parse: function PDFFunction_parse(xref, fn) {
 | |
|       var IR = this.getIR(xref, fn);
 | |
|       return this.fromIR(IR);
 | |
|     },
 | |
| 
 | |
|     constructSampled: function PDFFunction_constructSampled(str, dict) {
 | |
|       function toMultiArray(arr) {
 | |
|         var inputLength = arr.length;
 | |
|         var outputLength = arr.length / 2;
 | |
|         var out = [];
 | |
|         var index = 0;
 | |
|         for (var i = 0; i < inputLength; i += 2) {
 | |
|           out[index] = [arr[i], arr[i + 1]];
 | |
|           ++index;
 | |
|         }
 | |
|         return out;
 | |
|       }
 | |
|       var domain = dict.get('Domain');
 | |
|       var range = dict.get('Range');
 | |
| 
 | |
|       if (!domain || !range)
 | |
|         error('No domain or range');
 | |
| 
 | |
|       var inputSize = domain.length / 2;
 | |
|       var outputSize = range.length / 2;
 | |
| 
 | |
|       domain = toMultiArray(domain);
 | |
|       range = toMultiArray(range);
 | |
| 
 | |
|       var size = dict.get('Size');
 | |
|       var bps = dict.get('BitsPerSample');
 | |
|       var order = dict.get('Order') || 1;
 | |
|       if (order !== 1) {
 | |
|         // No description how cubic spline interpolation works in PDF32000:2008
 | |
|         // As in poppler, ignoring order, linear interpolation may work as good
 | |
|         TODO('No support for cubic spline interpolation: ' + order);
 | |
|       }
 | |
| 
 | |
|       var encode = dict.get('Encode');
 | |
|       if (!encode) {
 | |
|         encode = [];
 | |
|         for (var i = 0; i < inputSize; ++i) {
 | |
|           encode.push(0);
 | |
|           encode.push(size[i] - 1);
 | |
|         }
 | |
|       }
 | |
|       encode = toMultiArray(encode);
 | |
| 
 | |
|       var decode = dict.get('Decode');
 | |
|       if (!decode)
 | |
|         decode = range;
 | |
|       else
 | |
|         decode = toMultiArray(decode);
 | |
| 
 | |
|       var samples = this.getSampleArray(size, outputSize, bps, str);
 | |
| 
 | |
|       return [
 | |
|         CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size,
 | |
|         outputSize, Math.pow(2, bps) - 1, range
 | |
|       ];
 | |
|     },
 | |
| 
 | |
|     constructSampledFromIR: function PDFFunction_constructSampledFromIR(IR) {
 | |
|       // See chapter 3, page 109 of the PDF reference
 | |
|       function interpolate(x, xmin, xmax, ymin, ymax) {
 | |
|         return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin)));
 | |
|       }
 | |
| 
 | |
|       return function constructSampledFromIRResult(args) {
 | |
|         // See chapter 3, page 110 of the PDF reference.
 | |
|         var m = IR[1];
 | |
|         var domain = IR[2];
 | |
|         var encode = IR[3];
 | |
|         var decode = IR[4];
 | |
|         var samples = IR[5];
 | |
|         var size = IR[6];
 | |
|         var n = IR[7];
 | |
|         var mask = IR[8];
 | |
|         var range = IR[9];
 | |
| 
 | |
|         if (m != args.length)
 | |
|           error('Incorrect number of arguments: ' + m + ' != ' +
 | |
|                 args.length);
 | |
| 
 | |
|         var x = args;
 | |
| 
 | |
|         // Building the cube vertices: its part and sample index
 | |
|         // http://rjwagner49.com/Mathematics/Interpolation.pdf
 | |
|         var cubeVertices = 1 << m;
 | |
|         var cubeN = new Float64Array(cubeVertices);
 | |
|         var cubeVertex = new Uint32Array(cubeVertices);
 | |
|         for (var j = 0; j < cubeVertices; j++)
 | |
|           cubeN[j] = 1;
 | |
| 
 | |
|         var k = n, pos = 1;
 | |
|         // Map x_i to y_j for 0 <= i < m using the sampled function.
 | |
|         for (var i = 0; i < m; ++i) {
 | |
|           // x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
 | |
|           var domain_2i = domain[i][0];
 | |
|           var domain_2i_1 = domain[i][1];
 | |
|           var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1);
 | |
| 
 | |
|           // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1,
 | |
|           //                   Encode_2i, Encode_2i+1)
 | |
|           var e = interpolate(xi, domain_2i, domain_2i_1,
 | |
|                               encode[i][0], encode[i][1]);
 | |
| 
 | |
|           // e_i' = min(max(e_i, 0), Size_i - 1)
 | |
|           var size_i = size[i];
 | |
|           e = Math.min(Math.max(e, 0), size_i - 1);
 | |
| 
 | |
|           // Adjusting the cube: N and vertex sample index
 | |
|           var e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1;
 | |
|           var n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0);
 | |
|           var n1 = e - e0; // (e - e0) / (e1 - e0);
 | |
|           var offset0 = e0 * k;
 | |
|           var offset1 = offset0 + k; // e1 * k
 | |
|           for (var j = 0; j < cubeVertices; j++) {
 | |
|             if (j & pos) {
 | |
|               cubeN[j] *= n1;
 | |
|               cubeVertex[j] += offset1;
 | |
|             } else {
 | |
|               cubeN[j] *= n0;
 | |
|               cubeVertex[j] += offset0;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           k *= size_i;
 | |
|           pos <<= 1;
 | |
|         }
 | |
| 
 | |
|         var y = new Float64Array(n);
 | |
|         for (var j = 0; j < n; ++j) {
 | |
|           // Sum all cube vertices' samples portions
 | |
|           var rj = 0;
 | |
|           for (var i = 0; i < cubeVertices; i++)
 | |
|             rj += samples[cubeVertex[i] + j] * cubeN[i];
 | |
| 
 | |
|           // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1,
 | |
|           //                    Decode_2j, Decode_2j+1)
 | |
|           rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
 | |
| 
 | |
|           // y_j = min(max(r_j, range_2j), range_2j+1)
 | |
|           y[j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
 | |
|         }
 | |
| 
 | |
|         return y;
 | |
|       };
 | |
|     },
 | |
| 
 | |
|     constructInterpolated: function PDFFunction_constructInterpolated(str,
 | |
|                                                                       dict) {
 | |
|       var c0 = dict.get('C0') || [0];
 | |
|       var c1 = dict.get('C1') || [1];
 | |
|       var n = dict.get('N');
 | |
| 
 | |
|       if (!isArray(c0) || !isArray(c1))
 | |
|         error('Illegal dictionary for interpolated function');
 | |
| 
 | |
|       var length = c0.length;
 | |
|       var diff = [];
 | |
|       for (var i = 0; i < length; ++i)
 | |
|         diff.push(c1[i] - c0[i]);
 | |
| 
 | |
|       return [CONSTRUCT_INTERPOLATED, c0, diff, n];
 | |
|     },
 | |
| 
 | |
|     constructInterpolatedFromIR:
 | |
|       function PDFFunction_constructInterpolatedFromIR(IR) {
 | |
|       var c0 = IR[1];
 | |
|       var diff = IR[2];
 | |
|       var n = IR[3];
 | |
| 
 | |
|       var length = diff.length;
 | |
| 
 | |
|       return function constructInterpolatedFromIRResult(args) {
 | |
|         var x = n == 1 ? args[0] : Math.pow(args[0], n);
 | |
| 
 | |
|         var out = [];
 | |
|         for (var j = 0; j < length; ++j)
 | |
|           out.push(c0[j] + (x * diff[j]));
 | |
| 
 | |
|         return out;
 | |
| 
 | |
|       };
 | |
|     },
 | |
| 
 | |
|     constructStiched: function PDFFunction_constructStiched(fn, dict, xref) {
 | |
|       var domain = dict.get('Domain');
 | |
| 
 | |
|       if (!domain)
 | |
|         error('No domain');
 | |
| 
 | |
|       var inputSize = domain.length / 2;
 | |
|       if (inputSize != 1)
 | |
|         error('Bad domain for stiched function');
 | |
| 
 | |
|       var fnRefs = dict.get('Functions');
 | |
|       var fns = [];
 | |
|       for (var i = 0, ii = fnRefs.length; i < ii; ++i)
 | |
|         fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
 | |
| 
 | |
|       var bounds = dict.get('Bounds');
 | |
|       var encode = dict.get('Encode');
 | |
| 
 | |
|       return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
 | |
|     },
 | |
| 
 | |
|     constructStichedFromIR: function PDFFunction_constructStichedFromIR(IR) {
 | |
|       var domain = IR[1];
 | |
|       var bounds = IR[2];
 | |
|       var encode = IR[3];
 | |
|       var fnsIR = IR[4];
 | |
|       var fns = [];
 | |
| 
 | |
|       for (var i = 0, ii = fnsIR.length; i < ii; i++) {
 | |
|         fns.push(PDFFunction.fromIR(fnsIR[i]));
 | |
|       }
 | |
| 
 | |
|       return function constructStichedFromIRResult(args) {
 | |
|         var clip = function constructStichedFromIRClip(v, min, max) {
 | |
|           if (v > max)
 | |
|             v = max;
 | |
|           else if (v < min)
 | |
|             v = min;
 | |
|           return v;
 | |
|         };
 | |
| 
 | |
|         // clip to domain
 | |
|         var v = clip(args[0], domain[0], domain[1]);
 | |
|         // calulate which bound the value is in
 | |
|         for (var i = 0, ii = bounds.length; i < ii; ++i) {
 | |
|           if (v < bounds[i])
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         // encode value into domain of function
 | |
|         var dmin = domain[0];
 | |
|         if (i > 0)
 | |
|           dmin = bounds[i - 1];
 | |
|         var dmax = domain[1];
 | |
|         if (i < bounds.length)
 | |
|           dmax = bounds[i];
 | |
| 
 | |
|         var rmin = encode[2 * i];
 | |
|         var rmax = encode[2 * i + 1];
 | |
| 
 | |
|         var v2 = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
 | |
| 
 | |
|         // call the appropropriate function
 | |
|         return fns[i]([v2]);
 | |
|       };
 | |
|     },
 | |
| 
 | |
|     constructPostScript: function PDFFunction_constructPostScript(fn, dict,
 | |
|                                                                   xref) {
 | |
|       var domain = dict.get('Domain');
 | |
|       var range = dict.get('Range');
 | |
| 
 | |
|       if (!domain)
 | |
|         error('No domain.');
 | |
| 
 | |
|       if (!range)
 | |
|         error('No range.');
 | |
| 
 | |
|       var lexer = new PostScriptLexer(fn);
 | |
|       var parser = new PostScriptParser(lexer);
 | |
|       var code = parser.parse();
 | |
| 
 | |
|       return [CONSTRUCT_POSTSCRIPT, domain, range, code];
 | |
|     },
 | |
| 
 | |
|     constructPostScriptFromIR: function PDFFunction_constructPostScriptFromIR(
 | |
|                                           IR) {
 | |
|       var domain = IR[1];
 | |
|       var range = IR[2];
 | |
|       var code = IR[3];
 | |
|       var numOutputs = range.length / 2;
 | |
|       var evaluator = new PostScriptEvaluator(code);
 | |
|       // Cache the values for a big speed up, the cache size is limited though
 | |
|       // since the number of possible values can be huge from a PS function.
 | |
|       var cache = new FunctionCache();
 | |
|       return function constructPostScriptFromIRResult(args) {
 | |
|         var initialStack = [];
 | |
|         for (var i = 0, ii = (domain.length / 2); i < ii; ++i) {
 | |
|           initialStack.push(args[i]);
 | |
|         }
 | |
| 
 | |
|         var key = initialStack.join('_');
 | |
|         if (cache.has(key))
 | |
|           return cache.get(key);
 | |
| 
 | |
|         var stack = evaluator.execute(initialStack);
 | |
|         var transformed = [];
 | |
|         for (i = numOutputs - 1; i >= 0; --i) {
 | |
|           var out = stack.pop();
 | |
|           var rangeIndex = 2 * i;
 | |
|           if (out < range[rangeIndex])
 | |
|             out = range[rangeIndex];
 | |
|           else if (out > range[rangeIndex + 1])
 | |
|             out = range[rangeIndex + 1];
 | |
|           transformed[i] = out;
 | |
|         }
 | |
|         cache.set(key, transformed);
 | |
|         return transformed;
 | |
|       };
 | |
|     }
 | |
|   };
 | |
| })();
 | |
| 
 | |
| var FunctionCache = (function FunctionCacheClosure() {
 | |
|   // Of 10 PDF's with type4 functions the maxium number of distinct values seen
 | |
|   // was 256. This still may need some tweaking in the future though.
 | |
|   var MAX_CACHE_SIZE = 1024;
 | |
|   function FunctionCache() {
 | |
|     this.cache = {};
 | |
|     this.total = 0;
 | |
|   }
 | |
|   FunctionCache.prototype = {
 | |
|     has: function FunctionCache_has(key) {
 | |
|       return key in this.cache;
 | |
|     },
 | |
|     get: function FunctionCache_get(key) {
 | |
|       return this.cache[key];
 | |
|     },
 | |
|     set: function FunctionCache_set(key, value) {
 | |
|       if (this.total < MAX_CACHE_SIZE) {
 | |
|         this.cache[key] = value;
 | |
|         this.total++;
 | |
|       }
 | |
|     }
 | |
|   };
 | |
|   return FunctionCache;
 | |
| })();
 | |
| 
 | |
| var PostScriptStack = (function PostScriptStackClosure() {
 | |
|   var MAX_STACK_SIZE = 100;
 | |
|   function PostScriptStack(initialStack) {
 | |
|     this.stack = initialStack || [];
 | |
|   }
 | |
| 
 | |
|   PostScriptStack.prototype = {
 | |
|     push: function PostScriptStack_push(value) {
 | |
|       if (this.stack.length >= MAX_STACK_SIZE)
 | |
|         error('PostScript function stack overflow.');
 | |
|       this.stack.push(value);
 | |
|     },
 | |
|     pop: function PostScriptStack_pop() {
 | |
|       if (this.stack.length <= 0)
 | |
|         error('PostScript function stack underflow.');
 | |
|       return this.stack.pop();
 | |
|     },
 | |
|     copy: function PostScriptStack_copy(n) {
 | |
|       if (this.stack.length + n >= MAX_STACK_SIZE)
 | |
|         error('PostScript function stack overflow.');
 | |
|       var stack = this.stack;
 | |
|       for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++)
 | |
|         stack.push(stack[i]);
 | |
|     },
 | |
|     index: function PostScriptStack_index(n) {
 | |
|       this.push(this.stack[this.stack.length - n - 1]);
 | |
|     },
 | |
|     // rotate the last n stack elements p times
 | |
|     roll: function PostScriptStack_roll(n, p) {
 | |
|       var stack = this.stack;
 | |
|       var l = stack.length - n;
 | |
|       var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
 | |
|       for (i = l, j = r; i < j; i++, j--) {
 | |
|         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
 | |
|       }
 | |
|       for (i = l, j = c - 1; i < j; i++, j--) {
 | |
|         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
 | |
|       }
 | |
|       for (i = c, j = r; i < j; i++, j--) {
 | |
|         t = stack[i]; stack[i] = stack[j]; stack[j] = t;
 | |
|       }
 | |
|     }
 | |
|   };
 | |
|   return PostScriptStack;
 | |
| })();
 | |
| var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
 | |
|   function PostScriptEvaluator(operators, operands) {
 | |
|     this.operators = operators;
 | |
|     this.operands = operands;
 | |
|   }
 | |
|   PostScriptEvaluator.prototype = {
 | |
|     execute: function PostScriptEvaluator_execute(initialStack) {
 | |
|       var stack = new PostScriptStack(initialStack);
 | |
|       var counter = 0;
 | |
|       var operators = this.operators;
 | |
|       var length = operators.length;
 | |
|       var operator, a, b;
 | |
|       while (counter < length) {
 | |
|         operator = operators[counter++];
 | |
|         if (typeof operator == 'number') {
 | |
|           // Operator is really an operand and should be pushed to the stack.
 | |
|           stack.push(operator);
 | |
|           continue;
 | |
|         }
 | |
|         switch (operator) {
 | |
|           // non standard ps operators
 | |
|           case 'jz': // jump if false
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             if (!a)
 | |
|               counter = b;
 | |
|             break;
 | |
|           case 'j': // jump
 | |
|             a = stack.pop();
 | |
|             counter = a;
 | |
|             break;
 | |
| 
 | |
|           // all ps operators in alphabetical order (excluding if/ifelse)
 | |
|           case 'abs':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.abs(a));
 | |
|             break;
 | |
|           case 'add':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a + b);
 | |
|             break;
 | |
|           case 'and':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             if (isBool(a) && isBool(b))
 | |
|               stack.push(a && b);
 | |
|             else
 | |
|               stack.push(a & b);
 | |
|             break;
 | |
|           case 'atan':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.atan(a));
 | |
|             break;
 | |
|           case 'bitshift':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             if (a > 0)
 | |
|               stack.push(a << b);
 | |
|             else
 | |
|               stack.push(a >> b);
 | |
|             break;
 | |
|           case 'ceiling':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.ceil(a));
 | |
|             break;
 | |
|           case 'copy':
 | |
|             a = stack.pop();
 | |
|             stack.copy(a);
 | |
|             break;
 | |
|           case 'cos':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.cos(a));
 | |
|             break;
 | |
|           case 'cvi':
 | |
|             a = stack.pop() | 0;
 | |
|             stack.push(a);
 | |
|             break;
 | |
|           case 'cvr':
 | |
|             // noop
 | |
|             break;
 | |
|           case 'div':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a / b);
 | |
|             break;
 | |
|           case 'dup':
 | |
|             stack.copy(1);
 | |
|             break;
 | |
|           case 'eq':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a == b);
 | |
|             break;
 | |
|           case 'exch':
 | |
|             stack.roll(2, 1);
 | |
|             break;
 | |
|           case 'exp':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.pow(a, b));
 | |
|             break;
 | |
|           case 'false':
 | |
|             stack.push(false);
 | |
|             break;
 | |
|           case 'floor':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.floor(a));
 | |
|             break;
 | |
|           case 'ge':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a >= b);
 | |
|             break;
 | |
|           case 'gt':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a > b);
 | |
|             break;
 | |
|           case 'idiv':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push((a / b) | 0);
 | |
|             break;
 | |
|           case 'index':
 | |
|             a = stack.pop();
 | |
|             stack.index(a);
 | |
|             break;
 | |
|           case 'le':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a <= b);
 | |
|             break;
 | |
|           case 'ln':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.log(a));
 | |
|             break;
 | |
|           case 'log':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.log(a) / Math.LN10);
 | |
|             break;
 | |
|           case 'lt':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a < b);
 | |
|             break;
 | |
|           case 'mod':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a % b);
 | |
|             break;
 | |
|           case 'mul':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a * b);
 | |
|             break;
 | |
|           case 'ne':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a != b);
 | |
|             break;
 | |
|           case 'neg':
 | |
|             a = stack.pop();
 | |
|             stack.push(-b);
 | |
|             break;
 | |
|           case 'not':
 | |
|             a = stack.pop();
 | |
|             if (isBool(a) && isBool(b))
 | |
|               stack.push(a && b);
 | |
|             else
 | |
|               stack.push(a & b);
 | |
|             break;
 | |
|           case 'or':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             if (isBool(a) && isBool(b))
 | |
|               stack.push(a || b);
 | |
|             else
 | |
|               stack.push(a | b);
 | |
|             break;
 | |
|           case 'pop':
 | |
|             stack.pop();
 | |
|             break;
 | |
|           case 'roll':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.roll(a, b);
 | |
|             break;
 | |
|           case 'round':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.round(a));
 | |
|             break;
 | |
|           case 'sin':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.sin(a));
 | |
|             break;
 | |
|           case 'sqrt':
 | |
|             a = stack.pop();
 | |
|             stack.push(Math.sqrt(a));
 | |
|             break;
 | |
|           case 'sub':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             stack.push(a - b);
 | |
|             break;
 | |
|           case 'true':
 | |
|             stack.push(true);
 | |
|             break;
 | |
|           case 'truncate':
 | |
|             a = stack.pop();
 | |
|             a = a < 0 ? Math.ceil(a) : Math.floor(a);
 | |
|             stack.push(a);
 | |
|             break;
 | |
|           case 'xor':
 | |
|             b = stack.pop();
 | |
|             a = stack.pop();
 | |
|             if (isBool(a) && isBool(b))
 | |
|               stack.push(a != b);
 | |
|             else
 | |
|               stack.push(a ^ b);
 | |
|             break;
 | |
|           default:
 | |
|             error('Unknown operator ' + operator);
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
|       return stack.stack;
 | |
|     }
 | |
|   };
 | |
|   return PostScriptEvaluator;
 | |
| })();
 | |
| 
 | |
| var PostScriptParser = (function PostScriptParserClosure() {
 | |
|   function PostScriptParser(lexer) {
 | |
|     this.lexer = lexer;
 | |
|     this.operators = [];
 | |
|     this.token = null;
 | |
|     this.prev = null;
 | |
|   }
 | |
|   PostScriptParser.prototype = {
 | |
|     nextToken: function PostScriptParser_nextToken() {
 | |
|       this.prev = this.token;
 | |
|       this.token = this.lexer.getToken();
 | |
|     },
 | |
|     accept: function PostScriptParser_accept(type) {
 | |
|       if (this.token.type == type) {
 | |
|         this.nextToken();
 | |
|         return true;
 | |
|       }
 | |
|       return false;
 | |
|     },
 | |
|     expect: function PostScriptParser_expect(type) {
 | |
|       if (this.accept(type))
 | |
|         return true;
 | |
|       error('Unexpected symbol: found ' + this.token.type + ' expected ' +
 | |
|             type + '.');
 | |
|     },
 | |
|     parse: function PostScriptParser_parse() {
 | |
|       this.nextToken();
 | |
|       this.expect(PostScriptTokenTypes.LBRACE);
 | |
|       this.parseBlock();
 | |
|       this.expect(PostScriptTokenTypes.RBRACE);
 | |
|       return this.operators;
 | |
|     },
 | |
|     parseBlock: function PostScriptParser_parseBlock() {
 | |
|       while (true) {
 | |
|         if (this.accept(PostScriptTokenTypes.NUMBER)) {
 | |
|           this.operators.push(this.prev.value);
 | |
|         } else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
 | |
|           this.operators.push(this.prev.value);
 | |
|         } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
 | |
|           this.parseCondition();
 | |
|         } else {
 | |
|           return;
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     parseCondition: function PostScriptParser_parseCondition() {
 | |
|       // Add two place holders that will be updated later
 | |
|       var conditionLocation = this.operators.length;
 | |
|       this.operators.push(null, null);
 | |
| 
 | |
|       this.parseBlock();
 | |
|       this.expect(PostScriptTokenTypes.RBRACE);
 | |
|       if (this.accept(PostScriptTokenTypes.IF)) {
 | |
|         // The true block is right after the 'if' so it just falls through on
 | |
|         // true else it jumps and skips the true block.
 | |
|         this.operators[conditionLocation] = this.operators.length;
 | |
|         this.operators[conditionLocation + 1] = 'jz';
 | |
|       } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
 | |
|         var jumpLocation = this.operators.length;
 | |
|         this.operators.push(null, null);
 | |
|         var endOfTrue = this.operators.length;
 | |
|         this.parseBlock();
 | |
|         this.expect(PostScriptTokenTypes.RBRACE);
 | |
|         this.expect(PostScriptTokenTypes.IFELSE);
 | |
|         // The jump is added at the end of the true block to skip the false
 | |
|         // block.
 | |
|         this.operators[jumpLocation] = this.operators.length;
 | |
|         this.operators[jumpLocation + 1] = 'j';
 | |
| 
 | |
|         this.operators[conditionLocation] = endOfTrue;
 | |
|         this.operators[conditionLocation + 1] = 'jz';
 | |
|       } else {
 | |
|         error('PS Function: error parsing conditional.');
 | |
|       }
 | |
|     }
 | |
|   };
 | |
|   return PostScriptParser;
 | |
| })();
 | |
| 
 | |
| var PostScriptTokenTypes = {
 | |
|   LBRACE: 0,
 | |
|   RBRACE: 1,
 | |
|   NUMBER: 2,
 | |
|   OPERATOR: 3,
 | |
|   IF: 4,
 | |
|   IFELSE: 5
 | |
| };
 | |
| 
 | |
| var PostScriptToken = (function PostScriptTokenClosure() {
 | |
|   function PostScriptToken(type, value) {
 | |
|     this.type = type;
 | |
|     this.value = value;
 | |
|   }
 | |
| 
 | |
|   var opCache = {};
 | |
| 
 | |
|   PostScriptToken.getOperator = function PostScriptToken_getOperator(op) {
 | |
|     var opValue = opCache[op];
 | |
|     if (opValue)
 | |
|       return opValue;
 | |
| 
 | |
|     return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
 | |
|   };
 | |
| 
 | |
|   PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE,
 | |
|                                                 '{');
 | |
|   PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE,
 | |
|                                                 '}');
 | |
|   PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
 | |
|   PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE,
 | |
|                                                 'IFELSE');
 | |
|   return PostScriptToken;
 | |
| })();
 | |
| 
 | |
| var PostScriptLexer = (function PostScriptLexerClosure() {
 | |
|   function PostScriptLexer(stream) {
 | |
|     this.stream = stream;
 | |
|     this.nextChar();
 | |
|   }
 | |
|   PostScriptLexer.prototype = {
 | |
|     nextChar: function PostScriptLexer_nextChar() {
 | |
|       return (this.currentChar = this.stream.getByte());
 | |
|     },
 | |
|     getToken: function PostScriptLexer_getToken() {
 | |
|       var s = '';
 | |
|       var comment = false;
 | |
|       var ch = this.currentChar;
 | |
| 
 | |
|       // skip comments
 | |
|       while (true) {
 | |
|         if (ch < 0) {
 | |
|           return EOF;
 | |
|         }
 | |
| 
 | |
|         if (comment) {
 | |
|           if (ch === 0x0A || ch === 0x0D) {
 | |
|             comment = false;
 | |
|           }
 | |
|         } else if (ch == 0x25) { // '%'
 | |
|           comment = true;
 | |
|         } else if (!Lexer.isSpace(ch)) {
 | |
|           break;
 | |
|         }
 | |
|         ch = this.nextChar();
 | |
|       }
 | |
|       switch (ch | 0) {
 | |
|         case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
 | |
|         case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
 | |
|         case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
 | |
|           return new PostScriptToken(PostScriptTokenTypes.NUMBER,
 | |
|                                       this.getNumber());
 | |
|         case 0x7B: // '{'
 | |
|           this.nextChar();
 | |
|           return PostScriptToken.LBRACE;
 | |
|         case 0x7D: // '}'
 | |
|           this.nextChar();
 | |
|           return PostScriptToken.RBRACE;
 | |
|       }
 | |
|       // operator
 | |
|       var str = String.fromCharCode(ch);
 | |
|       while ((ch = this.nextChar()) >= 0 && // and 'A'-'Z', 'a'-'z'
 | |
|              ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) {
 | |
|         str += String.fromCharCode(ch);
 | |
|       }
 | |
|       switch (str.toLowerCase()) {
 | |
|         case 'if':
 | |
|           return PostScriptToken.IF;
 | |
|         case 'ifelse':
 | |
|           return PostScriptToken.IFELSE;
 | |
|         default:
 | |
|           return PostScriptToken.getOperator(str);
 | |
|       }
 | |
|     },
 | |
|     getNumber: function PostScriptLexer_getNumber() {
 | |
|       var ch = this.currentChar;
 | |
|       var str = String.fromCharCode(ch);
 | |
|       while ((ch = this.nextChar()) >= 0) {
 | |
|         if ((ch >= 0x30 && ch <= 0x39) || // '0'-'9'
 | |
|              ch === 0x2D || ch === 0x2E) { // '-', '.'
 | |
|           str += String.fromCharCode(ch);
 | |
|         } else {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       var value = parseFloat(str);
 | |
|       if (isNaN(value))
 | |
|         error('Invalid floating point number: ' + value);
 | |
|       return value;
 | |
|     }
 | |
|   };
 | |
|   return PostScriptLexer;
 | |
| })();
 | |
| 
 | |
| 
 | |
| 
 | |
| var Annotation = (function AnnotationClosure() {
 | |
|   // 12.5.5: Algorithm: Appearance streams
 | |
|   function getTransformMatrix(rect, bbox, matrix) {
 | |
|     var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);
 | |
|     var minX = bounds[0];
 | |
|     var minY = bounds[1];
 | |
|     var maxX = bounds[2];
 | |
|     var maxY = bounds[3];
 | |
| 
 | |
|     if (minX === maxX || minY === maxY) {
 | |
|       // From real-life file, bbox was [0, 0, 0, 0]. In this case,
 | |
|       // just apply the transform for rect
 | |
|       return [1, 0, 0, 1, rect[0], rect[1]];
 | |
|     }
 | |
| 
 | |
|     var xRatio = (rect[2] - rect[0]) / (maxX - minX);
 | |
|     var yRatio = (rect[3] - rect[1]) / (maxY - minY);
 | |
|     return [
 | |
|       xRatio,
 | |
|       0,
 | |
|       0,
 | |
|       yRatio,
 | |
|       rect[0] - minX * xRatio,
 | |
|       rect[1] - minY * yRatio
 | |
|     ];
 | |
|   }
 | |
| 
 | |
|   function getDefaultAppearance(dict) {
 | |
|     var appearanceState = dict.get('AP');
 | |
|     if (!isDict(appearanceState)) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var appearance;
 | |
|     var appearances = appearanceState.get('N');
 | |
|     if (isDict(appearances)) {
 | |
|       var as = dict.get('AS');
 | |
|       if (as && appearances.has(as.name)) {
 | |
|         appearance = appearances.get(as.name);
 | |
|       }
 | |
|     } else {
 | |
|       appearance = appearances;
 | |
|     }
 | |
|     return appearance;
 | |
|   }
 | |
| 
 | |
|   function Annotation(params) {
 | |
|     if (params.data) {
 | |
|       this.data = params.data;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var dict = params.dict;
 | |
|     var data = this.data = {};
 | |
| 
 | |
|     data.subtype = dict.get('Subtype').name;
 | |
|     var rect = dict.get('Rect');
 | |
|     data.rect = Util.normalizeRect(rect);
 | |
|     data.annotationFlags = dict.get('F');
 | |
| 
 | |
|     var color = dict.get('C');
 | |
|     if (isArray(color) && color.length === 3) {
 | |
|       // TODO(mack): currently only supporting rgb; need support different
 | |
|       // colorspaces
 | |
|       data.color = color;
 | |
|     } else {
 | |
|       data.color = [0, 0, 0];
 | |
|     }
 | |
| 
 | |
|     // Some types of annotations have border style dict which has more
 | |
|     // info than the border array
 | |
|     if (dict.has('BS')) {
 | |
|       var borderStyle = dict.get('BS');
 | |
|       data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1;
 | |
|     } else {
 | |
|       var borderArray = dict.get('Border') || [0, 0, 1];
 | |
|       data.borderWidth = borderArray[2] || 0;
 | |
|     }
 | |
| 
 | |
|     this.appearance = getDefaultAppearance(dict);
 | |
|     data.hasAppearance = !!this.appearance;
 | |
|   }
 | |
| 
 | |
|   Annotation.prototype = {
 | |
| 
 | |
|     getData: function Annotation_getData() {
 | |
|       return this.data;
 | |
|     },
 | |
| 
 | |
|     hasHtml: function Annotation_hasHtml() {
 | |
|       return false;
 | |
|     },
 | |
| 
 | |
|     getHtmlElement: function Annotation_getHtmlElement(commonObjs) {
 | |
|       throw new NotImplementedException(
 | |
|         'getHtmlElement() should be implemented in subclass');
 | |
|     },
 | |
| 
 | |
|     // TODO(mack): Remove this, it's not really that helpful.
 | |
|     getEmptyContainer: function Annotation_getEmptyContainer(tagName, rect) {
 | |
|       assert(!isWorker,
 | |
|         'getEmptyContainer() should be called from main thread');
 | |
| 
 | |
|       rect = rect || this.data.rect;
 | |
|       var element = document.createElement(tagName);
 | |
|       element.style.width = Math.ceil(rect[2] - rect[0]) + 'px';
 | |
|       element.style.height = Math.ceil(rect[3] - rect[1]) + 'px';
 | |
|       return element;
 | |
|     },
 | |
| 
 | |
|     isViewable: function Annotation_isViewable() {
 | |
|       var data = this.data;
 | |
|       return !!(
 | |
|         data &&
 | |
|         (!data.annotationFlags ||
 | |
|          !(data.annotationFlags & 0x22)) && // Hidden or NoView
 | |
|         data.rect                            // rectangle is nessessary
 | |
|       );
 | |
|     },
 | |
| 
 | |
|     loadResources: function(keys) {
 | |
|       var promise = new Promise();
 | |
|       this.appearance.dict.getAsync('Resources').then(function(resources) {
 | |
|         if (!resources) {
 | |
|           promise.resolve();
 | |
|           return;
 | |
|         }
 | |
|         var objectLoader = new ObjectLoader(resources.map,
 | |
|                                             keys,
 | |
|                                             resources.xref);
 | |
|         objectLoader.load().then(function() {
 | |
|           promise.resolve(resources);
 | |
|         });
 | |
|       }.bind(this));
 | |
| 
 | |
|       return promise;
 | |
|     },
 | |
| 
 | |
|     getOperatorList: function Annotation_getToOperatorList(evaluator) {
 | |
| 
 | |
|       var promise = new Promise();
 | |
| 
 | |
|       if (!this.appearance) {
 | |
|         promise.resolve(new OperatorList());
 | |
|         return promise;
 | |
|       }
 | |
| 
 | |
|       var data = this.data;
 | |
| 
 | |
|       var appearanceDict = this.appearance.dict;
 | |
|       var resourcesPromise = this.loadResources([
 | |
|         'ExtGState',
 | |
|         'ColorSpace',
 | |
|         'Pattern',
 | |
|         'Shading',
 | |
|         'XObject',
 | |
|         'Font'
 | |
|         // ProcSet
 | |
|         // Properties
 | |
|       ]);
 | |
|       var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
 | |
|       var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
 | |
|       var transform = getTransformMatrix(data.rect, bbox, matrix);
 | |
| 
 | |
|       var border = data.border;
 | |
| 
 | |
|       resourcesPromise.then(function(resources) {
 | |
|         var opList = new OperatorList();
 | |
|         opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
 | |
|         evaluator.getOperatorList(this.appearance, resources, opList);
 | |
|         opList.addOp(OPS.endAnnotation, []);
 | |
|         promise.resolve(opList);
 | |
|       }.bind(this));
 | |
| 
 | |
|       return promise;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Annotation.getConstructor =
 | |
|       function Annotation_getConstructor(subtype, fieldType) {
 | |
| 
 | |
|     if (!subtype) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // TODO(mack): Implement FreeText annotations
 | |
|     if (subtype === 'Link') {
 | |
|       return LinkAnnotation;
 | |
|     } else if (subtype === 'Text') {
 | |
|       return TextAnnotation;
 | |
|     } else if (subtype === 'Widget') {
 | |
|       if (!fieldType) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (fieldType === 'Tx') {
 | |
|         return TextWidgetAnnotation;
 | |
|       } else {
 | |
|         return WidgetAnnotation;
 | |
|       }
 | |
|     } else {
 | |
|       return Annotation;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // TODO(mack): Support loading annotation from data
 | |
|   Annotation.fromData = function Annotation_fromData(data) {
 | |
|     var subtype = data.subtype;
 | |
|     var fieldType = data.fieldType;
 | |
|     var Constructor = Annotation.getConstructor(subtype, fieldType);
 | |
|     if (Constructor) {
 | |
|       return new Constructor({ data: data });
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Annotation.fromRef = function Annotation_fromRef(xref, ref) {
 | |
| 
 | |
|     var dict = xref.fetchIfRef(ref);
 | |
|     if (!isDict(dict)) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var subtype = dict.get('Subtype');
 | |
|     subtype = isName(subtype) ? subtype.name : '';
 | |
|     if (!subtype) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var fieldType = Util.getInheritableProperty(dict, 'FT');
 | |
|     fieldType = isName(fieldType) ? fieldType.name : '';
 | |
| 
 | |
|     var Constructor = Annotation.getConstructor(subtype, fieldType);
 | |
|     if (!Constructor) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var params = {
 | |
|       dict: dict,
 | |
|       ref: ref,
 | |
|     };
 | |
| 
 | |
|     var annotation = new Constructor(params);
 | |
| 
 | |
|     if (annotation.isViewable()) {
 | |
|       return annotation;
 | |
|     } else {
 | |
|       TODO('unimplemented annotation type: ' + subtype);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
 | |
|       annotations, opList, pdfManager, partialEvaluator) {
 | |
| 
 | |
|     function reject(e) {
 | |
|       annotationsReadyPromise.reject(e);
 | |
|     }
 | |
| 
 | |
|     var annotationsReadyPromise = new Promise();
 | |
| 
 | |
|     var annotationPromises = [];
 | |
|     for (var i = 0, n = annotations.length; i < n; ++i) {
 | |
|       annotationPromises.push(annotations[i].getOperatorList(partialEvaluator));
 | |
|     }
 | |
|     Promise.all(annotationPromises).then(function(datas) {
 | |
|       opList.addOp(OPS.beginAnnotations, []);
 | |
|       for (var i = 0, n = datas.length; i < n; ++i) {
 | |
|         var annotOpList = datas[i];
 | |
|         opList.addOpList(annotOpList);
 | |
|       }
 | |
|       opList.addOp(OPS.endAnnotations, []);
 | |
|       annotationsReadyPromise.resolve();
 | |
|     }, reject);
 | |
| 
 | |
|     return annotationsReadyPromise;
 | |
|   };
 | |
| 
 | |
|   return Annotation;
 | |
| })();
 | |
| PDFJS.Annotation = Annotation;
 | |
| 
 | |
| 
 | |
| var WidgetAnnotation = (function WidgetAnnotationClosure() {
 | |
| 
 | |
|   function WidgetAnnotation(params) {
 | |
|     Annotation.call(this, params);
 | |
| 
 | |
|     if (params.data) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var dict = params.dict;
 | |
|     var data = this.data;
 | |
| 
 | |
|     data.fieldValue = stringToPDFString(
 | |
|       Util.getInheritableProperty(dict, 'V') || '');
 | |
|     data.alternativeText = stringToPDFString(dict.get('TU') || '');
 | |
|     data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
 | |
|     var fieldType = Util.getInheritableProperty(dict, 'FT');
 | |
|     data.fieldType = isName(fieldType) ? fieldType.name : '';
 | |
|     data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
 | |
|     this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict();
 | |
| 
 | |
|     // Building the full field name by collecting the field and
 | |
|     // its ancestors 'T' data and joining them using '.'.
 | |
|     var fieldName = [];
 | |
|     var namedItem = dict;
 | |
|     var ref = params.ref;
 | |
|     while (namedItem) {
 | |
|       var parent = namedItem.get('Parent');
 | |
|       var parentRef = namedItem.getRaw('Parent');
 | |
|       var name = namedItem.get('T');
 | |
|       if (name) {
 | |
|         fieldName.unshift(stringToPDFString(name));
 | |
|       } else {
 | |
|         // The field name is absent, that means more than one field
 | |
|         // with the same name may exist. Replacing the empty name
 | |
|         // with the '`' plus index in the parent's 'Kids' array.
 | |
|         // This is not in the PDF spec but necessary to id the
 | |
|         // the input controls.
 | |
|         var kids = parent.get('Kids');
 | |
|         var j, jj;
 | |
|         for (j = 0, jj = kids.length; j < jj; j++) {
 | |
|           var kidRef = kids[j];
 | |
|           if (kidRef.num == ref.num && kidRef.gen == ref.gen)
 | |
|             break;
 | |
|         }
 | |
|         fieldName.unshift('`' + j);
 | |
|       }
 | |
|       namedItem = parent;
 | |
|       ref = parentRef;
 | |
|     }
 | |
|     data.fullName = fieldName.join('.');
 | |
|   }
 | |
| 
 | |
|   var parent = Annotation.prototype;
 | |
|   Util.inherit(WidgetAnnotation, Annotation, {
 | |
|     isViewable: function WidgetAnnotation_isViewable() {
 | |
|       if (this.data.fieldType === 'Sig') {
 | |
|         TODO('unimplemented annotation type: Widget signature');
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       return parent.isViewable.call(this);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   return WidgetAnnotation;
 | |
| })();
 | |
| 
 | |
| var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
 | |
|   function TextWidgetAnnotation(params) {
 | |
|     WidgetAnnotation.call(this, params);
 | |
| 
 | |
|     if (params.data) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');
 | |
|   }
 | |
| 
 | |
|   // TODO(mack): This dupes some of the logic in CanvasGraphics.setFont()
 | |
|   function setTextStyles(element, item, fontObj) {
 | |
| 
 | |
|     var style = element.style;
 | |
|     style.fontSize = item.fontSize + 'px';
 | |
|     style.direction = item.fontDirection < 0 ? 'rtl': 'ltr';
 | |
| 
 | |
|     if (!fontObj) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     style.fontWeight = fontObj.black ?
 | |
|                             (fontObj.bold ? 'bolder' : 'bold') :
 | |
|                             (fontObj.bold ? 'bold' : 'normal');
 | |
|     style.fontStyle = fontObj.italic ? 'italic' : 'normal';
 | |
| 
 | |
|     var fontName = fontObj.loadedName;
 | |
|     var fontFamily = fontName ? '"' + fontName + '", ' : '';
 | |
|     // Use a reasonable default font if the font doesn't specify a fallback
 | |
|     var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif';
 | |
|     style.fontFamily = fontFamily + fallbackName;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   var parent = WidgetAnnotation.prototype;
 | |
|   Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
 | |
|     hasHtml: function TextWidgetAnnotation_hasHtml() {
 | |
|       return !this.data.hasAppearance && !!this.data.fieldValue;
 | |
|     },
 | |
| 
 | |
|     getHtmlElement: function TextWidgetAnnotation_getHtmlElement(commonObjs) {
 | |
|       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
 | |
| 
 | |
|       var item = this.data;
 | |
| 
 | |
|       var element = this.getEmptyContainer('div');
 | |
|       element.style.display = 'table';
 | |
| 
 | |
|       var content = document.createElement('div');
 | |
|       content.textContent = item.fieldValue;
 | |
|       var textAlignment = item.textAlignment;
 | |
|       content.style.textAlign = ['left', 'center', 'right'][textAlignment];
 | |
|       content.style.verticalAlign = 'middle';
 | |
|       content.style.display = 'table-cell';
 | |
| 
 | |
|       var fontObj = item.fontRefName ?
 | |
|                     commonObjs.getData(item.fontRefName) : null;
 | |
|       var cssRules = setTextStyles(content, item, fontObj);
 | |
| 
 | |
|       element.appendChild(content);
 | |
| 
 | |
|       return element;
 | |
|     },
 | |
| 
 | |
|     getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
 | |
|       if (this.appearance) {
 | |
|         return Annotation.prototype.getOperatorList.call(this, evaluator);
 | |
|       }
 | |
| 
 | |
|       var promise = new Promise();
 | |
|       var opList = new OperatorList();
 | |
|       var data = this.data;
 | |
| 
 | |
|       // Even if there is an appearance stream, ignore it. This is the
 | |
|       // behaviour used by Adobe Reader.
 | |
| 
 | |
|       var defaultAppearance = data.defaultAppearance;
 | |
|       if (!defaultAppearance) {
 | |
|         promise.resolve(opList);
 | |
|         return promise;
 | |
|       }
 | |
| 
 | |
|       // Include any font resources found in the default appearance
 | |
| 
 | |
|       var stream = new Stream(stringToBytes(defaultAppearance));
 | |
|       evaluator.getOperatorList(stream, this.fieldResources, opList);
 | |
|       var appearanceFnArray = opList.fnArray;
 | |
|       var appearanceArgsArray = opList.argsArray;
 | |
|       var fnArray = [];
 | |
|       var argsArray = [];
 | |
| 
 | |
|       // TODO(mack): Add support for stroke color
 | |
|       data.rgb = [0, 0, 0];
 | |
|       // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
 | |
|       for (var i = 0, n = fnArray.length; i < n; ++i) {
 | |
|         var fnId = appearanceFnArray[i];
 | |
|         var args = appearanceArgsArray[i];
 | |
| 
 | |
|         if (fnId === OPS.setFont) {
 | |
|           data.fontRefName = args[0];
 | |
|           var size = args[1];
 | |
|           if (size < 0) {
 | |
|             data.fontDirection = -1;
 | |
|             data.fontSize = -size;
 | |
|           } else {
 | |
|             data.fontDirection = 1;
 | |
|             data.fontSize = size;
 | |
|           }
 | |
|         } else if (fnId === OPS.setFillRGBColor) {
 | |
|           data.rgb = args;
 | |
|         } else if (fnId === OPS.setFillGray) {
 | |
|           var rgbValue = args[0] * 255;
 | |
|           data.rgb = [rgbValue, rgbValue, rgbValue];
 | |
|         }
 | |
|       }
 | |
|       promise.resolve(opList);
 | |
|       return promise;
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   return TextWidgetAnnotation;
 | |
| })();
 | |
| 
 | |
| var TextAnnotation = (function TextAnnotationClosure() {
 | |
|   function TextAnnotation(params) {
 | |
|     Annotation.call(this, params);
 | |
| 
 | |
|     if (params.data) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var dict = params.dict;
 | |
|     var data = this.data;
 | |
| 
 | |
|     var content = dict.get('Contents');
 | |
|     var title = dict.get('T');
 | |
|     data.content = stringToPDFString(content || '');
 | |
|     data.title = stringToPDFString(title || '');
 | |
|     data.name = !dict.has('Name') ? 'Note' : dict.get('Name').name;
 | |
|   }
 | |
| 
 | |
|   var ANNOT_MIN_SIZE = 10;
 | |
| 
 | |
|   Util.inherit(TextAnnotation, Annotation, {
 | |
| 
 | |
|     getOperatorList: function TextAnnotation_getOperatorList(evaluator) {
 | |
|       var promise = new Promise();
 | |
|       promise.resolve(new OperatorList());
 | |
|       return promise;
 | |
|     },
 | |
| 
 | |
|     hasHtml: function TextAnnotation_hasHtml() {
 | |
|       return true;
 | |
|     },
 | |
| 
 | |
|     getHtmlElement: function TextAnnotation_getHtmlElement(commonObjs) {
 | |
|       assert(!isWorker, 'getHtmlElement() shall be called from main thread');
 | |
| 
 | |
|       var item = this.data;
 | |
|       var rect = item.rect;
 | |
| 
 | |
|       // sanity check because of OOo-generated PDFs
 | |
|       if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) {
 | |
|         rect[3] = rect[1] + ANNOT_MIN_SIZE;
 | |
|       }
 | |
|       if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) {
 | |
|         rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
 | |
|       }
 | |
| 
 | |
|       var container = this.getEmptyContainer('section', rect);
 | |
|       container.className = 'annotText';
 | |
| 
 | |
|       var image = document.createElement('img');
 | |
|       image.style.height = container.style.height;
 | |
|       var iconName = item.name;
 | |
|       image.src = PDFJS.imageResourcesPath + 'annotation-' +
 | |
|         iconName.toLowerCase() + '.svg';
 | |
|       image.alt = '[{{type}} Annotation]';
 | |
|       image.dataset.l10nId = 'text_annotation_type';
 | |
|       image.dataset.l10nArgs = JSON.stringify({type: iconName});
 | |
|       var content = document.createElement('div');
 | |
|       content.setAttribute('hidden', true);
 | |
|       var title = document.createElement('h1');
 | |
|       var text = document.createElement('p');
 | |
|       content.style.left = Math.floor(rect[2] - rect[0]) + 'px';
 | |
|       content.style.top = '0px';
 | |
|       title.textContent = item.title;
 | |
| 
 | |
|       if (!item.content && !item.title) {
 | |
|         content.setAttribute('hidden', true);
 | |
|       } else {
 | |
|         var e = document.createElement('span');
 | |
|         var lines = item.content.split(/(?:\r\n?|\n)/);
 | |
|         for (var i = 0, ii = lines.length; i < ii; ++i) {
 | |
|           var line = lines[i];
 | |
|           e.appendChild(document.createTextNode(line));
 | |
|           if (i < (ii - 1))
 | |
|             e.appendChild(document.createElement('br'));
 | |
|         }
 | |
|         text.appendChild(e);
 | |
| 
 | |
|         var showAnnotation = function showAnnotation() {
 | |
|           container.style.zIndex += 1;
 | |
|           content.removeAttribute('hidden');
 | |
|         };
 | |
| 
 | |
|         var hideAnnotation = function hideAnnotation(e) {
 | |
|           if (e.toElement || e.relatedTarget) { // No context menu is used
 | |
|             container.style.zIndex -= 1;
 | |
|             content.setAttribute('hidden', true);
 | |
|           }
 | |
|         };
 | |
| 
 | |
|         content.addEventListener('mouseover', showAnnotation, false);
 | |
|         content.addEventListener('mouseout', hideAnnotation, false);
 | |
|         image.addEventListener('mouseover', showAnnotation, false);
 | |
|         image.addEventListener('mouseout', hideAnnotation, false);
 | |
|       }
 | |
| 
 | |
|       content.appendChild(title);
 | |
|       content.appendChild(text);
 | |
|       container.appendChild(image);
 | |
|       container.appendChild(content);
 | |
| 
 | |
|       return container;
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   return TextAnnotation;
 | |
| })();
 | |
| 
 | |
| var LinkAnnotation = (function LinkAnnotationClosure() {
 | |
|   function LinkAnnotation(params) {
 | |
|     Annotation.call(this, params);
 | |
| 
 | |
|     if (params.data) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var dict = params.dict;
 | |
|     var data = this.data;
 | |
| 
 | |
|     var action = dict.get('A');
 | |
|     if (action) {
 | |
|       var linkType = action.get('S').name;
 | |
|       if (linkType === 'URI') {
 | |
|         var url = addDefaultProtocolToUrl(action.get('URI'));
 | |
|         // TODO: pdf spec mentions urls can be relative to a Base
 | |
|         // entry in the dictionary.
 | |
|         if (!isValidUrl(url, false)) {
 | |
|           url = '';
 | |
|         }
 | |
|         data.url = url;
 | |
|       } else if (linkType === 'GoTo') {
 | |
|         data.dest = action.get('D');
 | |
|       } else if (linkType === 'GoToR') {
 | |
|         var urlDict = action.get('F');
 | |
|         if (isDict(urlDict)) {
 | |
|           // We assume that the 'url' is a Filspec dictionary
 | |
|           // and fetch the url without checking any further
 | |
|           url = urlDict.get('F') || '';
 | |
|         }
 | |
| 
 | |
|         // TODO: pdf reference says that GoToR
 | |
|         // can also have 'NewWindow' attribute
 | |
|         if (!isValidUrl(url, false)) {
 | |
|           url = '';
 | |
|         }
 | |
|         data.url = url;
 | |
|         data.dest = action.get('D');
 | |
|       } else if (linkType === 'Named') {
 | |
|         data.action = action.get('N').name;
 | |
|       } else {
 | |
|         TODO('unrecognized link type: ' + linkType);
 | |
|       }
 | |
|     } else if (dict.has('Dest')) {
 | |
|       // simple destination link
 | |
|       var dest = dict.get('Dest');
 | |
|       data.dest = isName(dest) ? dest.name : dest;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Lets URLs beginning with 'www.' default to using the 'http://' protocol.
 | |
|   function addDefaultProtocolToUrl(url) {
 | |
|     if (url.indexOf('www.') === 0) {
 | |
|       return ('http://' + url);
 | |
|     }
 | |
|     return url;
 | |
|   }
 | |
| 
 | |
|   Util.inherit(LinkAnnotation, Annotation, {
 | |
|     hasOperatorList: function LinkAnnotation_hasOperatorList() {
 | |
|       return false;
 | |
|     },
 | |
| 
 | |
|     hasHtml: function LinkAnnotation_hasHtml() {
 | |
|       return true;
 | |
|     },
 | |
| 
 | |
|     getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) {
 | |
|       var rect = this.data.rect;
 | |
|       var element = document.createElement('a');
 | |
|       var borderWidth = this.data.borderWidth;
 | |
| 
 | |
|       element.style.borderWidth = borderWidth + 'px';
 | |
|       var color = this.data.color;
 | |
|       var rgb = [];
 | |
|       for (var i = 0; i < 3; ++i) {
 | |
|         rgb[i] = Math.round(color[i] * 255);
 | |
|       }
 | |
|       element.style.borderColor = Util.makeCssRgb(rgb);
 | |
|       element.style.borderStyle = 'solid';
 | |
| 
 | |
|       var width = rect[2] - rect[0] - 2 * borderWidth;
 | |
|       var height = rect[3] - rect[1] - 2 * borderWidth;
 | |
|       element.style.width = width + 'px';
 | |
|       element.style.height = height + 'px';
 | |
| 
 | |
|       element.href = this.data.url || '';
 | |
|       return element;
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   return LinkAnnotation;
 | |
| })();
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * The maximum allowed image size in total pixels e.g. width * height. Images
 | |
|  * above this value will not be drawn. Use -1 for no limit.
 | |
|  * @var {Number}
 | |
|  */
 | |
| PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize;
 | |
| 
 | |
| /**
 | |
|  * By default fonts are converted to OpenType fonts and loaded via font face
 | |
|  * rules. If disabled, the font will be rendered using a built in font renderer
 | |
|  * that constructs the glyphs with primitive path commands.
 | |
|  * @var {Boolean}
 | |
|  */
 | |
| PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ?
 | |
|                         false : PDFJS.disableFontFace;
 | |
| 
 | |
| /**
 | |
|  * Path for image resources, mainly for annotation icons. Include trailing
 | |
|  * slash.
 | |
|  * @var {String}
 | |
|  */
 | |
| PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ?
 | |
|                            '' : PDFJS.imageResourcesPath;
 | |
| 
 | |
| /**
 | |
|  * Disable the web worker and run all code on the main thread. This will happen
 | |
|  * automatically if the browser doesn't support workers or sending typed arrays
 | |
|  * to workers.
 | |
|  * @var {Boolean}
 | |
|  */
 | |
| PDFJS.disableWorker = PDFJS.disableWorker === undefined ?
 | |
|                       false : PDFJS.disableWorker;
 | |
| 
 | |
| /**
 | |
|  * Path and filename of the worker file. Required when the worker is enabled in
 | |
|  * development mode. If unspecified in the production build, the worker will be
 | |
|  * loaded based on the location of the pdf.js file.
 | |
|  * @var {String}
 | |
|  */
 | |
| PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc;
 | |
| 
 | |
| /**
 | |
|  * Disable range request loading of PDF files. When enabled and if the server
 | |
|  * supports partial content requests then the PDF will be fetched in chunks.
 | |
|  * Enabled (false) by default.
 | |
|  * @var {Boolean}
 | |
|  */
 | |
| PDFJS.disableRange = PDFJS.disableRange === undefined ?
 | |
|                      false : PDFJS.disableRange;
 | |
| 
 | |
| /**
 | |
|  * Disable pre-fetching of PDF file data. When range requests are enabled PDF.js
 | |
|  * will automatically keep fetching more data even if it isn't needed to display
 | |
|  * the current page. This default behavior can be disabled.
 | |
|  * @var {Boolean}
 | |
|  */
 | |
| PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ?
 | |
|                          false : PDFJS.disableAutoFetch;
 | |
| 
 | |
| /**
 | |
|  * Enables special hooks for debugging PDF.js.
 | |
|  * @var {Boolean}
 | |
|  */
 | |
| PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
 | |
| 
 | |
| /**
 | |
|  * Enables transfer usage in postMessage for ArrayBuffers.
 | |
|  * @var {boolean}
 | |
|  */
 | |
| PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ?
 | |
|                              true : PDFJS.postMessageTransfers;
 | |
| /**
 | |
|  * This is the main entry point for loading a PDF and interacting with it.
 | |
|  * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
 | |
|  * is used, which means it must follow the same origin rules that any XHR does
 | |
|  * e.g. No cross domain requests without CORS.
 | |
|  *
 | |
|  * @param {string|TypedAray|object} source Can be an url to where a PDF is
 | |
|  * located, a typed array (Uint8Array) already populated with data or
 | |
|  * and parameter object with the following possible fields:
 | |
|  *  - url   - The URL of the PDF.
 | |
|  *  - data  - A typed array with PDF data.
 | |
|  *  - httpHeaders - Basic authentication headers.
 | |
|  *  - password - For decrypting password-protected PDFs.
 | |
|  *  - initialData - A typed array with the first portion or all of the pdf data.
 | |
|  *                  Used by the extension since some data is already loaded
 | |
|  *                  before the switch to range requests. 
 | |
|  *
 | |
|  * @param {object} pdfDataRangeTransport is optional. It is used if you want
 | |
|  * to manually serve range requests for data in the PDF. See viewer.js for
 | |
|  * an example of pdfDataRangeTransport's interface.
 | |
|  *
 | |
|  * @param {function} passwordCallback is optional. It is used to request a
 | |
|  * password if wrong or no password was provided. The callback receives two
 | |
|  * parameters: function that needs to be called with new password and reason
 | |
|  * (see {PasswordResponses}).
 | |
|  *
 | |
|  * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object.
 | |
|  */
 | |
| PDFJS.getDocument = function getDocument(source,
 | |
|                                          pdfDataRangeTransport,
 | |
|                                          passwordCallback,
 | |
|                                          progressCallback) {
 | |
|   var workerInitializedPromise, workerReadyPromise, transport;
 | |
| 
 | |
|   if (typeof source === 'string') {
 | |
|     source = { url: source };
 | |
|   } else if (isArrayBuffer(source)) {
 | |
|     source = { data: source };
 | |
|   } else if (typeof source !== 'object') {
 | |
|     error('Invalid parameter in getDocument, need either Uint8Array, ' +
 | |
|           'string or a parameter object');
 | |
|   }
 | |
| 
 | |
|   if (!source.url && !source.data)
 | |
|     error('Invalid parameter array, need either .data or .url');
 | |
| 
 | |
|   // copy/use all keys as is except 'url' -- full path is required
 | |
|   var params = {};
 | |
|   for (var key in source) {
 | |
|     if (key === 'url' && typeof window !== 'undefined') {
 | |
|       params[key] = combineUrl(window.location.href, source[key]);
 | |
|       continue;
 | |
|     }
 | |
|     params[key] = source[key];
 | |
|   }
 | |
| 
 | |
|   workerInitializedPromise = new PDFJS.Promise();
 | |
|   workerReadyPromise = new PDFJS.Promise();
 | |
|   transport = new WorkerTransport(workerInitializedPromise,
 | |
|       workerReadyPromise, pdfDataRangeTransport, progressCallback);
 | |
|   workerInitializedPromise.then(function transportInitialized() {
 | |
|     transport.passwordCallback = passwordCallback;
 | |
|     transport.fetchDocument(params);
 | |
|   });
 | |
|   return workerReadyPromise;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Proxy to a PDFDocument in the worker thread. Also, contains commonly used
 | |
|  * properties that can be read synchronously.
 | |
|  */
 | |
| var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
 | |
|   function PDFDocumentProxy(pdfInfo, transport) {
 | |
|     this.pdfInfo = pdfInfo;
 | |
|     this.transport = transport;
 | |
|   }
 | |
|   PDFDocumentProxy.prototype = {
 | |
|     /**
 | |
|      * @return {number} Total number of pages the PDF contains.
 | |
|      */
 | |
|     get numPages() {
 | |
|       return this.pdfInfo.numPages;
 | |
|     },
 | |
|     /**
 | |
|      * @return {string} A unique ID to identify a PDF. Not guaranteed to be
 | |
|      * unique.
 | |
|      */
 | |
|     get fingerprint() {
 | |
|       return this.pdfInfo.fingerprint;
 | |
|     },
 | |
|     /**
 | |
|      * @return {boolean} true if embedded document fonts are in use. Will be
 | |
|      * set during rendering of the pages.
 | |
|      */
 | |
|     get embeddedFontsUsed() {
 | |
|       return this.transport.embeddedFontsUsed;
 | |
|     },
 | |
|     /**
 | |
|      * @param {number} The page number to get. The first page is 1.
 | |
|      * @return {Promise} A promise that is resolved with a {PDFPageProxy}
 | |
|      * object.
 | |
|      */
 | |
|     getPage: function PDFDocumentProxy_getPage(number) {
 | |
|       return this.transport.getPage(number);
 | |
|     },
 | |
|     /**
 | |
|      * @param {object} Must have 'num' and 'gen' properties.
 | |
|      * @return {Promise} A promise that is resolved with the page index that is
 | |
|      * associated with the reference.
 | |
|      */
 | |
|     getPageIndex: function PDFDocumentProxy_getPageIndex(ref) {
 | |
|       return this.transport.getPageIndex(ref);
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved with a lookup table for
 | |
|      * mapping named destinations to reference numbers.
 | |
|      */
 | |
|     getDestinations: function PDFDocumentProxy_getDestinations() {
 | |
|       return this.transport.getDestinations();
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved with an array of all the
 | |
|      * JavaScript strings in the name tree.
 | |
|      */
 | |
|     getJavaScript: function PDFDocumentProxy_getDestinations() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       var js = this.pdfInfo.javaScript;
 | |
|       promise.resolve(js);
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved with an {array} that is a
 | |
|      * tree outline (if it has one) of the PDF. The tree is in the format of:
 | |
|      * [
 | |
|      *  {
 | |
|      *   title: string,
 | |
|      *   bold: boolean,
 | |
|      *   italic: boolean,
 | |
|      *   color: rgb array,
 | |
|      *   dest: dest obj,
 | |
|      *   items: array of more items like this
 | |
|      *  },
 | |
|      *  ...
 | |
|      * ].
 | |
|      */
 | |
|     getOutline: function PDFDocumentProxy_getOutline() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       var outline = this.pdfInfo.outline;
 | |
|       promise.resolve(outline);
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved with an {object} that has
 | |
|      * info and metadata properties.  Info is an {object} filled with anything
 | |
|      * available in the information dictionary and similarly metadata is a
 | |
|      * {Metadata} object with information from the metadata section of the PDF.
 | |
|      */
 | |
|     getMetadata: function PDFDocumentProxy_getMetadata() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       var info = this.pdfInfo.info;
 | |
|       var metadata = this.pdfInfo.metadata;
 | |
|       promise.resolve({
 | |
|         info: info,
 | |
|         metadata: metadata ? new PDFJS.Metadata(metadata) : null
 | |
|       });
 | |
|       return promise;
 | |
|     },
 | |
|     isEncrypted: function PDFDocumentProxy_isEncrypted() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       promise.resolve(this.pdfInfo.encrypted);
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved with a TypedArray that has
 | |
|      * the raw data from the PDF.
 | |
|      */
 | |
|     getData: function PDFDocumentProxy_getData() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       this.transport.getData(promise);
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved when the document's data
 | |
|      * is loaded
 | |
|      */
 | |
|     dataLoaded: function PDFDocumentProxy_dataLoaded() {
 | |
|       return this.transport.dataLoaded();
 | |
|     },
 | |
|     cleanup: function PDFDocumentProxy_cleanup() {
 | |
|       this.transport.startCleanup();
 | |
|     },
 | |
|     destroy: function PDFDocumentProxy_destroy() {
 | |
|       this.transport.destroy();
 | |
|     }
 | |
|   };
 | |
|   return PDFDocumentProxy;
 | |
| })();
 | |
| 
 | |
| var PDFPageProxy = (function PDFPageProxyClosure() {
 | |
|   function PDFPageProxy(pageInfo, transport) {
 | |
|     this.pageInfo = pageInfo;
 | |
|     this.transport = transport;
 | |
|     this.stats = new StatTimer();
 | |
|     this.stats.enabled = !!globalScope.PDFJS.enableStats;
 | |
|     this.commonObjs = transport.commonObjs;
 | |
|     this.objs = new PDFObjects();
 | |
|     this.receivingOperatorList  = false;
 | |
|     this.cleanupAfterRender = false;
 | |
|     this.pendingDestroy = false;
 | |
|     this.renderTasks = [];
 | |
|   }
 | |
|   PDFPageProxy.prototype = {
 | |
|     /**
 | |
|      * @return {number} Page number of the page. First page is 1.
 | |
|      */
 | |
|     get pageNumber() {
 | |
|       return this.pageInfo.pageIndex + 1;
 | |
|     },
 | |
|     /**
 | |
|      * @return {number} The number of degrees the page is rotated clockwise.
 | |
|      */
 | |
|     get rotate() {
 | |
|       return this.pageInfo.rotate;
 | |
|     },
 | |
|     /**
 | |
|      * @return {object} The reference that points to this page. It has 'num' and
 | |
|      * 'gen' properties.
 | |
|      */
 | |
|     get ref() {
 | |
|       return this.pageInfo.ref;
 | |
|     },
 | |
|     /**
 | |
|      * @return {array} An array of the visible portion of the PDF page in the
 | |
|      * user space units - [x1, y1, x2, y2].
 | |
|      */
 | |
|     get view() {
 | |
|       return this.pageInfo.view;
 | |
|     },
 | |
|     /**
 | |
|      * @param {number} scale The desired scale of the viewport.
 | |
|      * @param {number} rotate Degrees to rotate the viewport. If omitted this
 | |
|      * defaults to the page rotation.
 | |
|      * @return {PageViewport} Contains 'width' and 'height' properties along
 | |
|      * with transforms required for rendering.
 | |
|      */
 | |
|     getViewport: function PDFPageProxy_getViewport(scale, rotate) {
 | |
|       if (arguments.length < 2)
 | |
|         rotate = this.rotate;
 | |
|       return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0);
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} A promise that is resolved with an {array} of the
 | |
|      * annotation objects.
 | |
|      */
 | |
|     getAnnotations: function PDFPageProxy_getAnnotations() {
 | |
|       if (this.annotationsPromise)
 | |
|         return this.annotationsPromise;
 | |
| 
 | |
|       var promise = new PDFJS.Promise();
 | |
|       this.annotationsPromise = promise;
 | |
|       this.transport.getAnnotations(this.pageInfo.pageIndex);
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * Begins the process of rendering a page to the desired context.
 | |
|      * @param {object} params A parameter object that supports:
 | |
|      * {
 | |
|      *   canvasContext(required): A 2D context of a DOM Canvas object.,
 | |
|      *   textLayer(optional): An object that has beginLayout, endLayout, and
 | |
|      *                        appendText functions.,
 | |
|      *   imageLayer(optional): An object that has beginLayout, endLayout and
 | |
|      *                         appendImage functions.,
 | |
|      *   continueCallback(optional): A function that will be called each time
 | |
|      *                               the rendering is paused.  To continue
 | |
|      *                               rendering call the function that is the
 | |
|      *                               first argument to the callback.
 | |
|      * }.
 | |
|      * @return {RenderTask} An extended promise that is resolved when the page
 | |
|      * finishes rendering (see RenderTask).
 | |
|      */
 | |
|     render: function PDFPageProxy_render(params) {
 | |
|       var stats = this.stats;
 | |
|       stats.time('Overall');
 | |
| 
 | |
|       // If there was a pending destroy cancel it so no cleanup happens during
 | |
|       // this call to render.
 | |
|       this.pendingDestroy = false;
 | |
| 
 | |
|       // If there is no displayReadyPromise yet, then the operatorList was never
 | |
|       // requested before. Make the request and create the promise.
 | |
|       if (!this.displayReadyPromise) {
 | |
|         this.receivingOperatorList = true;
 | |
|         this.displayReadyPromise = new Promise();
 | |
|         this.operatorList = {
 | |
|           fnArray: [],
 | |
|           argsArray: [],
 | |
|           lastChunk: false
 | |
|         };
 | |
| 
 | |
|         this.stats.time('Page Request');
 | |
|         this.transport.messageHandler.send('RenderPageRequest', {
 | |
|           pageIndex: this.pageNumber - 1
 | |
|         });
 | |
|       }
 | |
| 
 | |
|       var internalRenderTask = new InternalRenderTask(complete, params,
 | |
|                                        this.objs, this.commonObjs,
 | |
|                                        this.operatorList, this.pageNumber);
 | |
|       this.renderTasks.push(internalRenderTask);
 | |
|       var renderTask = new RenderTask(internalRenderTask);
 | |
| 
 | |
|       var self = this;
 | |
|       this.displayReadyPromise.then(
 | |
|         function pageDisplayReadyPromise(transparency) {
 | |
|           if (self.pendingDestroy) {
 | |
|             complete();
 | |
|             return;
 | |
|           }
 | |
|           stats.time('Rendering');
 | |
|           internalRenderTask.initalizeGraphics(transparency);
 | |
|           internalRenderTask.operatorListChanged();
 | |
|         },
 | |
|         function pageDisplayReadPromiseError(reason) {
 | |
|           complete(reason);
 | |
|         }
 | |
|       );
 | |
| 
 | |
|       function complete(error) {
 | |
|         var i = self.renderTasks.indexOf(internalRenderTask);
 | |
|         if (i >= 0) {
 | |
|           self.renderTasks.splice(i, 1);
 | |
|         }
 | |
| 
 | |
|         if (self.cleanupAfterRender) {
 | |
|           self.pendingDestroy = true;
 | |
|         }
 | |
|         self._tryDestroy();
 | |
| 
 | |
|         if (error) {
 | |
|           renderTask.reject(error);
 | |
|         } else {
 | |
|           renderTask.resolve();
 | |
|         }
 | |
|         stats.timeEnd('Rendering');
 | |
|         stats.timeEnd('Overall');
 | |
|       }
 | |
| 
 | |
|       return renderTask;
 | |
|     },
 | |
|     /**
 | |
|      * @return {Promise} That is resolved with the a {string} that is the text
 | |
|      * content from the page.
 | |
|      */
 | |
|     getTextContent: function PDFPageProxy_getTextContent() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       this.transport.messageHandler.send('GetTextContent', {
 | |
|           pageIndex: this.pageNumber - 1
 | |
|         },
 | |
|         function textContentCallback(textContent) {
 | |
|           promise.resolve(textContent);
 | |
|         }
 | |
|       );
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * Stub for future feature.
 | |
|      */
 | |
|     getOperationList: function PDFPageProxy_getOperationList() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       var operationList = { // not implemented
 | |
|         dependencyFontsID: null,
 | |
|         operatorList: null
 | |
|       };
 | |
|       promise.resolve(operationList);
 | |
|       return promise;
 | |
|     },
 | |
|     /**
 | |
|      * Destroys resources allocated by the page.
 | |
|      */
 | |
|     destroy: function PDFPageProxy_destroy() {
 | |
|       this.pendingDestroy = true;
 | |
|       this._tryDestroy();
 | |
|     },
 | |
|     /**
 | |
|      * For internal use only. Attempts to clean up if rendering is in a state
 | |
|      * where that's possible.
 | |
|      */
 | |
|     _tryDestroy: function PDFPageProxy__destroy() {
 | |
|       if (!this.pendingDestroy ||
 | |
|           this.renderTasks.length !== 0 ||
 | |
|           this.receivingOperatorList) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       delete this.operatorList;
 | |
|       delete this.displayReadyPromise;
 | |
|       this.objs.clear();
 | |
|       this.pendingDestroy = false;
 | |
|     },
 | |
|     /**
 | |
|      * For internal use only.
 | |
|      */
 | |
|     _startRenderPage: function PDFPageProxy_startRenderPage(transparency) {
 | |
|       this.displayReadyPromise.resolve(transparency);
 | |
|     },
 | |
|     /**
 | |
|      * For internal use only.
 | |
|      */
 | |
|     _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) {
 | |
|       // Add the new chunk to the current operator list.
 | |
|       for (var i = 0, ii = operatorListChunk.length; i < ii; i++) {
 | |
|         this.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
 | |
|         this.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
 | |
|       }
 | |
|       this.operatorList.lastChunk = operatorListChunk.lastChunk;
 | |
| 
 | |
|       // Notify all the rendering tasks there are more operators to be consumed.
 | |
|       for (var i = 0; i < this.renderTasks.length; i++) {
 | |
|         this.renderTasks[i].operatorListChanged();
 | |
|       }
 | |
| 
 | |
|       if (operatorListChunk.lastChunk) {
 | |
|         this.receivingOperatorList = false;
 | |
|         this._tryDestroy();
 | |
|       }
 | |
|     }
 | |
|   };
 | |
|   return PDFPageProxy;
 | |
| })();
 | |
| /**
 | |
|  * For internal use only.
 | |
|  */
 | |
| var WorkerTransport = (function WorkerTransportClosure() {
 | |
|   function WorkerTransport(workerInitializedPromise, workerReadyPromise,
 | |
|       pdfDataRangeTransport, progressCallback) {
 | |
|     this.pdfDataRangeTransport = pdfDataRangeTransport;
 | |
| 
 | |
|     this.workerReadyPromise = workerReadyPromise;
 | |
|     this.progressCallback = progressCallback;
 | |
|     this.commonObjs = new PDFObjects();
 | |
| 
 | |
|     this.pageCache = [];
 | |
|     this.pagePromises = [];
 | |
|     this.embeddedFontsUsed = false;
 | |
| 
 | |
|     this.passwordCallback = null;
 | |
| 
 | |
|     // If worker support isn't disabled explicit and the browser has worker
 | |
|     // support, create a new web worker and test if it/the browser fullfills
 | |
|     // all requirements to run parts of pdf.js in a web worker.
 | |
|     // Right now, the requirement is, that an Uint8Array is still an Uint8Array
 | |
|     // as it arrives on the worker. Chrome added this with version 15.
 | |
|     if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
 | |
|       var workerSrc = PDFJS.workerSrc;
 | |
|       if (!workerSrc) {
 | |
|         error('No PDFJS.workerSrc specified');
 | |
|       }
 | |
| 
 | |
|       try {
 | |
|         // Some versions of FF can't create a worker on localhost, see:
 | |
|         // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
 | |
|         var worker = new Worker(workerSrc);
 | |
|         var messageHandler = new MessageHandler('main', worker);
 | |
|         this.messageHandler = messageHandler;
 | |
| 
 | |
|         messageHandler.on('test', function transportTest(data) {
 | |
|           var supportTypedArray = data && data.supportTypedArray;
 | |
|           if (supportTypedArray) {
 | |
|             this.worker = worker;
 | |
|             if (!data.supportTransfers) {
 | |
|               PDFJS.postMessageTransfers = false;
 | |
|             }
 | |
|             this.setupMessageHandler(messageHandler);
 | |
|             workerInitializedPromise.resolve();
 | |
|           } else {
 | |
|             globalScope.PDFJS.disableWorker = true;
 | |
|             this.loadFakeWorkerFiles().then(function() {
 | |
|               this.setupFakeWorker();
 | |
|               workerInitializedPromise.resolve();
 | |
|             }.bind(this));
 | |
|           }
 | |
|         }.bind(this));
 | |
| 
 | |
|         var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]);
 | |
|         // Some versions of Opera throw a DATA_CLONE_ERR on serializing the
 | |
|         // typed array. Also, checking if we can use transfers.
 | |
|         try {
 | |
|           messageHandler.send('test', testObj, null, [testObj.buffer]);
 | |
|         } catch (ex) {
 | |
|           info('Cannot use postMessage transfers');
 | |
|           testObj[0] = 0;
 | |
|           messageHandler.send('test', testObj);
 | |
|         }
 | |
|         return;
 | |
|       } catch (e) {
 | |
|         info('The worker has been disabled.');
 | |
|       }
 | |
|     }
 | |
|     // Either workers are disabled, not supported or have thrown an exception.
 | |
|     // Thus, we fallback to a faked worker.
 | |
|     globalScope.PDFJS.disableWorker = true;
 | |
|     this.loadFakeWorkerFiles().then(function() {
 | |
|       this.setupFakeWorker();
 | |
|       workerInitializedPromise.resolve();
 | |
|     }.bind(this));
 | |
|   }
 | |
|   WorkerTransport.prototype = {
 | |
|     destroy: function WorkerTransport_destroy() {
 | |
|       this.pageCache = [];
 | |
|       this.pagePromises = [];
 | |
|       var self = this;
 | |
|       this.messageHandler.send('Terminate', null, function () {
 | |
|         if (self.worker) {
 | |
|           self.worker.terminate();
 | |
|         }
 | |
|       });
 | |
|     },
 | |
| 
 | |
|     loadFakeWorkerFiles: function WorkerTransport_loadFakeWorkerFiles() {
 | |
|       if (!PDFJS.fakeWorkerFilesLoadedPromise) {
 | |
|         PDFJS.fakeWorkerFilesLoadedPromise = new Promise();
 | |
|         // In the developer build load worker_loader which in turn loads all the
 | |
|         // other files and resolves the promise. In production only the
 | |
|         // pdf.worker.js file is needed.
 | |
|         Util.loadScript(PDFJS.workerSrc, function() {
 | |
|           PDFJS.fakeWorkerFilesLoadedPromise.resolve();
 | |
|         });
 | |
|       }
 | |
|       return PDFJS.fakeWorkerFilesLoadedPromise;
 | |
|     },
 | |
| 
 | |
|     setupFakeWorker: function WorkerTransport_setupFakeWorker() {
 | |
|       warn('Setting up fake worker.');
 | |
|       // If we don't use a worker, just post/sendMessage to the main thread.
 | |
|       var fakeWorker = {
 | |
|         postMessage: function WorkerTransport_postMessage(obj) {
 | |
|           fakeWorker.onmessage({data: obj});
 | |
|         },
 | |
|         terminate: function WorkerTransport_terminate() {}
 | |
|       };
 | |
| 
 | |
|       var messageHandler = new MessageHandler('main', fakeWorker);
 | |
|       this.setupMessageHandler(messageHandler);
 | |
| 
 | |
|       // If the main thread is our worker, setup the handling for the messages
 | |
|       // the main thread sends to it self.
 | |
|       PDFJS.WorkerMessageHandler.setup(messageHandler);
 | |
|     },
 | |
| 
 | |
|     setupMessageHandler:
 | |
|       function WorkerTransport_setupMessageHandler(messageHandler) {
 | |
|       this.messageHandler = messageHandler;
 | |
| 
 | |
|       function updatePassword(password) {
 | |
|         messageHandler.send('UpdatePassword', password);
 | |
|       }
 | |
| 
 | |
|       var pdfDataRangeTransport = this.pdfDataRangeTransport;
 | |
|       if (pdfDataRangeTransport) {
 | |
|         pdfDataRangeTransport.addRangeListener(function(begin, chunk) {
 | |
|           messageHandler.send('OnDataRange', {
 | |
|             begin: begin,
 | |
|             chunk: chunk
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         pdfDataRangeTransport.addProgressListener(function(loaded) {
 | |
|           messageHandler.send('OnDataProgress', {
 | |
|             loaded: loaded
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         messageHandler.on('RequestDataRange',
 | |
|           function transportDataRange(data) {
 | |
|             pdfDataRangeTransport.requestDataRange(data.begin, data.end);
 | |
|           }, this);
 | |
|       }
 | |
| 
 | |
|       messageHandler.on('GetDoc', function transportDoc(data) {
 | |
|         var pdfInfo = data.pdfInfo;
 | |
|         var pdfDocument = new PDFDocumentProxy(pdfInfo, this);
 | |
|         this.pdfDocument = pdfDocument;
 | |
|         this.workerReadyPromise.resolve(pdfDocument);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('NeedPassword', function transportPassword(data) {
 | |
|         if (this.passwordCallback) {
 | |
|           return this.passwordCallback(updatePassword,
 | |
|                                        PasswordResponses.NEED_PASSWORD);
 | |
|         }
 | |
|         this.workerReadyPromise.reject(data.exception.message, data.exception);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('IncorrectPassword', function transportBadPass(data) {
 | |
|         if (this.passwordCallback) {
 | |
|           return this.passwordCallback(updatePassword,
 | |
|                                        PasswordResponses.INCORRECT_PASSWORD);
 | |
|         }
 | |
|         this.workerReadyPromise.reject(data.exception.message, data.exception);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('InvalidPDF', function transportInvalidPDF(data) {
 | |
|         this.workerReadyPromise.reject(data.exception.name, data.exception);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('MissingPDF', function transportMissingPDF(data) {
 | |
|         this.workerReadyPromise.reject(data.exception.message, data.exception);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('UnknownError', function transportUnknownError(data) {
 | |
|         this.workerReadyPromise.reject(data.exception.message, data.exception);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('GetPage', function transportPage(data) {
 | |
|         var pageInfo = data.pageInfo;
 | |
|         var page = new PDFPageProxy(pageInfo, this);
 | |
|         this.pageCache[pageInfo.pageIndex] = page;
 | |
|         var promise = this.pagePromises[pageInfo.pageIndex];
 | |
|         promise.resolve(page);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('GetAnnotations', function transportAnnotations(data) {
 | |
|         var annotations = data.annotations;
 | |
|         var promise = this.pageCache[data.pageIndex].annotationsPromise;
 | |
|         promise.resolve(annotations);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('StartRenderPage', function transportRender(data) {
 | |
|         var page = this.pageCache[data.pageIndex];
 | |
| 
 | |
|         page.stats.timeEnd('Page Request');
 | |
|         page._startRenderPage(data.transparency);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('RenderPageChunk', function transportRender(data) {
 | |
|         var page = this.pageCache[data.pageIndex];
 | |
| 
 | |
|         page._renderPageChunk(data.operatorList);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('commonobj', function transportObj(data) {
 | |
|         var id = data[0];
 | |
|         var type = data[1];
 | |
|         if (this.commonObjs.hasData(id))
 | |
|           return;
 | |
| 
 | |
|         switch (type) {
 | |
|           case 'Font':
 | |
|             var exportedData = data[2];
 | |
| 
 | |
|             var font;
 | |
|             if ('error' in exportedData) {
 | |
|               var error = exportedData.error;
 | |
|               warn('Error during font loading: ' + error);
 | |
|               this.commonObjs.resolve(id, error);
 | |
|               break;
 | |
|             } else {
 | |
|               font = new FontFace(exportedData);
 | |
|             }
 | |
| 
 | |
|             FontLoader.bind(
 | |
|               [font],
 | |
|               function fontReady(fontObjs) {
 | |
|                 this.commonObjs.resolve(id, font);
 | |
|               }.bind(this)
 | |
|             );
 | |
|             break;
 | |
|           case 'FontPath':
 | |
|             this.commonObjs.resolve(id, data[2]);
 | |
|             break;
 | |
|           default:
 | |
|             error('Got unknown common object type ' + type);
 | |
|         }
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('obj', function transportObj(data) {
 | |
|         var id = data[0];
 | |
|         var pageIndex = data[1];
 | |
|         var type = data[2];
 | |
|         var pageProxy = this.pageCache[pageIndex];
 | |
|         if (pageProxy.objs.hasData(id))
 | |
|           return;
 | |
| 
 | |
|         switch (type) {
 | |
|           case 'JpegStream':
 | |
|             var imageData = data[3];
 | |
|             loadJpegStream(id, imageData, pageProxy.objs);
 | |
|             break;
 | |
|           case 'Image':
 | |
|             var imageData = data[3];
 | |
|             pageProxy.objs.resolve(id, imageData);
 | |
| 
 | |
|             // heuristics that will allow not to store large data
 | |
|             var MAX_IMAGE_SIZE_TO_STORE = 8000000;
 | |
|             if ('data' in imageData &&
 | |
|                 imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
 | |
|               pageProxy.cleanupAfterRender = true;
 | |
|             }
 | |
|             break;
 | |
|           default:
 | |
|             error('Got unknown object type ' + type);
 | |
|         }
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('DocProgress', function transportDocProgress(data) {
 | |
|         if (this.progressCallback) {
 | |
|           this.progressCallback({
 | |
|             loaded: data.loaded,
 | |
|             total: data.total
 | |
|           });
 | |
|         }
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('DocError', function transportDocError(data) {
 | |
|         this.workerReadyPromise.reject(data);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('PageError', function transportError(data) {
 | |
|         var page = this.pageCache[data.pageNum - 1];
 | |
|         if (page.displayReadyPromise)
 | |
|           page.displayReadyPromise.reject(data.error);
 | |
|         else
 | |
|           error(data.error);
 | |
|       }, this);
 | |
| 
 | |
|       messageHandler.on('JpegDecode', function(data, promise) {
 | |
|         var imageUrl = data[0];
 | |
|         var components = data[1];
 | |
|         if (components != 3 && components != 1)
 | |
|           error('Only 3 component or 1 component can be returned');
 | |
| 
 | |
|         var img = new Image();
 | |
|         img.onload = (function messageHandler_onloadClosure() {
 | |
|           var width = img.width;
 | |
|           var height = img.height;
 | |
|           var size = width * height;
 | |
|           var rgbaLength = size * 4;
 | |
|           var buf = new Uint8Array(size * components);
 | |
|           var tmpCanvas = createScratchCanvas(width, height);
 | |
|           var tmpCtx = tmpCanvas.getContext('2d');
 | |
|           tmpCtx.drawImage(img, 0, 0);
 | |
|           var data = tmpCtx.getImageData(0, 0, width, height).data;
 | |
| 
 | |
|           if (components == 3) {
 | |
|             for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
 | |
|               buf[j] = data[i];
 | |
|               buf[j + 1] = data[i + 1];
 | |
|               buf[j + 2] = data[i + 2];
 | |
|             }
 | |
|           } else if (components == 1) {
 | |
|             for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) {
 | |
|               buf[j] = data[i];
 | |
|             }
 | |
|           }
 | |
|           promise.resolve({ data: buf, width: width, height: height});
 | |
|         }).bind(this);
 | |
|         img.src = imageUrl;
 | |
|       });
 | |
|     },
 | |
| 
 | |
|     fetchDocument: function WorkerTransport_fetchDocument(source) {
 | |
|       source.disableAutoFetch = PDFJS.disableAutoFetch;
 | |
|       source.chunkedViewerLoading = !!this.pdfDataRangeTransport;
 | |
|       this.messageHandler.send('GetDocRequest', {
 | |
|         source: source,
 | |
|         disableRange: PDFJS.disableRange,
 | |
|         maxImageSize: PDFJS.maxImageSize,
 | |
|         disableFontFace: PDFJS.disableFontFace
 | |
|       });
 | |
|     },
 | |
| 
 | |
|     getData: function WorkerTransport_getData(promise) {
 | |
|       this.messageHandler.send('GetData', null, function(data) {
 | |
|         promise.resolve(data);
 | |
|       });
 | |
|     },
 | |
| 
 | |
|     dataLoaded: function WorkerTransport_dataLoaded() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       this.messageHandler.send('DataLoaded', null, function(args) {
 | |
|         promise.resolve(args);
 | |
|       });
 | |
|       return promise;
 | |
|     },
 | |
| 
 | |
|     getPage: function WorkerTransport_getPage(pageNumber, promise) {
 | |
|       var pageIndex = pageNumber - 1;
 | |
|       if (pageIndex in this.pagePromises)
 | |
|         return this.pagePromises[pageIndex];
 | |
|       var promise = new PDFJS.Promise('Page ' + pageNumber);
 | |
|       this.pagePromises[pageIndex] = promise;
 | |
|       this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex });
 | |
|       return promise;
 | |
|     },
 | |
| 
 | |
|     getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       this.messageHandler.send('GetPageIndex', { ref: ref },
 | |
|         function (pageIndex) {
 | |
|           promise.resolve(pageIndex);
 | |
|         }
 | |
|       );
 | |
|       return promise;
 | |
|     },
 | |
| 
 | |
|     getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
 | |
|       this.messageHandler.send('GetAnnotationsRequest',
 | |
|         { pageIndex: pageIndex });
 | |
|     },
 | |
| 
 | |
|     getDestinations: function WorkerTransport_getDestinations() {
 | |
|       var promise = new PDFJS.Promise();
 | |
|       this.messageHandler.send('GetDestinations', null,
 | |
|         function transportDestinations(destinations) {
 | |
|           promise.resolve(destinations);
 | |
|         }
 | |
|       );
 | |
|       return promise;
 | |
|     },
 | |
| 
 | |
|     startCleanup: function WorkerTransport_startCleanup() {
 | |
|       this.messageHandler.send('Cleanup', null,
 | |
|         function endCleanup() {
 | |
|           for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
 | |
|             var page = this.pageCache[i];
 | |
|             if (page) {
 | |
|               page.destroy();
 | |
|             }
 | |
|           }
 | |
|           this.commonObjs.clear();
 | |
|           FontLoader.clear();
 | |
|         }.bind(this)
 | |
|       );
 | |
|     }
 | |
|   };
 | |
|   return WorkerTransport;
 | |
| 
 | |
| })();
 | |
| 
 | |
| /**
 | |
|  * A PDF document and page is built of many objects. E.g. there are objects
 | |
|  * for fonts, images, rendering code and such. These objects might get processed
 | |
|  * inside of a worker. The `PDFObjects` implements some basic functions to
 | |
|  * manage these objects.
 | |
|  */
 | |
| var PDFObjects = (function PDFObjectsClosure() {
 | |
|   function PDFObjects() {
 | |
|     this.objs = {};
 | |
|   }
 | |
| 
 | |
|   PDFObjects.prototype = {
 | |
|     /**
 | |
|      * Internal function.
 | |
|      * Ensures there is an object defined for `objId`.
 | |
|      */
 | |
|     ensureObj: function PDFObjects_ensureObj(objId) {
 | |
|       if (this.objs[objId])
 | |
|         return this.objs[objId];
 | |
| 
 | |
|       var obj = {
 | |
|         promise: new Promise(objId),
 | |
|         data: null,
 | |
|         resolved: false
 | |
|       };
 | |
|       this.objs[objId] = obj;
 | |
| 
 | |
|       return obj;
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * If called *without* callback, this returns the data of `objId` but the
 | |
|      * object needs to be resolved. If it isn't, this function throws.
 | |
|      *
 | |
|      * If called *with* a callback, the callback is called with the data of the
 | |
|      * object once the object is resolved. That means, if you call this
 | |
|      * function and the object is already resolved, the callback gets called
 | |
|      * right away.
 | |
|      */
 | |
|     get: function PDFObjects_get(objId, callback) {
 | |
|       // If there is a callback, then the get can be async and the object is
 | |
|       // not required to be resolved right now
 | |
|       if (callback) {
 | |
|         this.ensureObj(objId).promise.then(callback);
 | |
|         return null;
 | |
|       }
 | |
| 
 | |
|       // If there isn't a callback, the user expects to get the resolved data
 | |
|       // directly.
 | |
|       var obj = this.objs[objId];
 | |
| 
 | |
|       // If there isn't an object yet or the object isn't resolved, then the
 | |
|       // data isn't ready yet!
 | |
|       if (!obj || !obj.resolved)
 | |
|         error('Requesting object that isn\'t resolved yet ' + objId);
 | |
| 
 | |
|       return obj.data;
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * Resolves the object `objId` with optional `data`.
 | |
|      */
 | |
|     resolve: function PDFObjects_resolve(objId, data) {
 | |
|       var obj = this.ensureObj(objId);
 | |
| 
 | |
|       obj.resolved = true;
 | |
|       obj.data = data;
 | |
|       obj.promise.resolve(data);
 | |
|     },
 | |
| 
 | |
|     isResolved: function PDFObjects_isResolved(objId) {
 | |
|       var objs = this.objs;
 | |
| 
 | |
|       if (!objs[objId]) {
 | |
|         return false;
 | |
|       } else {
 | |
|         return objs[objId].resolved;
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     hasData: function PDFObjects_hasData(objId) {
 | |
|       return this.isResolved(objId);
 | |
|     },
 | |
| 
 | |
|     /**
 | |
|      * Returns the data of `objId` if object exists, null otherwise.
 | |
|      */
 | |
|     getData: function PDFObjects_getData(objId) {
 | |
|       var objs = this.objs;
 | |
|       if (!objs[objId] || !objs[objId].resolved) {
 | |
|         return null;
 | |
|       } else {
 | |
|         return objs[objId].data;
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     clear: function PDFObjects_clear() {
 | |
|       this.objs = {};
 | |
|     }
 | |
|   };
 | |
|   return PDFObjects;
 | |
| })();
 | |
| /*
 | |
|  * RenderTask is basically a promise but adds a cancel function to terminate it.
 | |
|  */
 | |
| var RenderTask = (function RenderTaskClosure() {
 | |
|   function RenderTask(internalRenderTask) {
 | |
|     this.internalRenderTask = internalRenderTask;
 | |
|     Promise.call(this);
 | |
|   }
 | |
| 
 | |
|   RenderTask.prototype = Object.create(Promise.prototype);
 | |
| 
 | |
|   /**
 | |
|    * Cancel the rendering task. If the task is curently rendering it will not be
 | |
|    * cancelled until graphics pauses with a timeout. The promise that this
 | |
|    * object extends will resolved when cancelled.
 | |
|    */
 | |
|   RenderTask.prototype.cancel = function RenderTask_cancel() {
 | |
|     this.internalRenderTask.cancel();
 | |
|   };
 | |
| 
 | |
|   return RenderTask;
 | |
| })();
 | |
| 
 | |
| var InternalRenderTask = (function InternalRenderTaskClosure() {
 | |
| 
 | |
|   function InternalRenderTask(callback, params, objs, commonObjs, operatorList,
 | |
|                               pageNumber) {
 | |
|     this.callback = callback;
 | |
|     this.params = params;
 | |
|     this.objs = objs;
 | |
|     this.commonObjs = commonObjs;
 | |
|     this.operatorListIdx = null;
 | |
|     this.operatorList = operatorList;
 | |
|     this.pageNumber = pageNumber;
 | |
|     this.running = false;
 | |
|     this.graphicsReadyCallback = null;
 | |
|     this.graphicsReady = false;
 | |
|     this.cancelled = false;
 | |
|   }
 | |
| 
 | |
|   InternalRenderTask.prototype = {
 | |
| 
 | |
|     initalizeGraphics:
 | |
|         function InternalRenderTask_initalizeGraphics(transparency) {
 | |
| 
 | |
|       if (this.cancelled) {
 | |
|         return;
 | |
|       }
 | |
|       if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
 | |
|           globalScope.StepperManager.enabled) {
 | |
|         this.stepper = globalScope.StepperManager.create(this.pageNumber - 1);
 | |
|         this.stepper.init(this.operatorList);
 | |
|         this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
 | |
|       }
 | |
| 
 | |
|       var params = this.params;
 | |
|       this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
 | |
|                                     this.objs, params.textLayer,
 | |
|                                     params.imageLayer);
 | |
| 
 | |
|       this.gfx.beginDrawing(params.viewport, transparency);
 | |
|       this.operatorListIdx = 0;
 | |
|       this.graphicsReady = true;
 | |
|       if (this.graphicsReadyCallback) {
 | |
|         this.graphicsReadyCallback();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     cancel: function InternalRenderTask_cancel() {
 | |
|       this.running = false;
 | |
|       this.cancelled = true;
 | |
|       this.callback('cancelled');
 | |
|     },
 | |
| 
 | |
|     operatorListChanged: function InternalRenderTask_operatorListChanged() {
 | |
|       if (!this.graphicsReady) {
 | |
|         if (!this.graphicsReadyCallback) {
 | |
|           this.graphicsReadyCallback = this._continue.bind(this);
 | |
|         }
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (this.stepper) {
 | |
|         this.stepper.updateOperatorList(this.operatorList);
 | |
|       }
 | |
| 
 | |
|       if (this.running) {
 | |
|         return;
 | |
|       }
 | |
|       this._continue();
 | |
|     },
 | |
| 
 | |
|     _continue: function InternalRenderTask__continue() {
 | |
|       this.running = true;
 | |
|       if (this.cancelled) {
 | |
|         return;
 | |
|       }
 | |
|       if (this.params.continueCallback) {
 | |
|         this.params.continueCallback(this._next.bind(this));
 | |
|       } else {
 | |
|         this._next();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     _next: function InternalRenderTask__next() {
 | |
|       if (this.cancelled) {
 | |
|         return;
 | |
|       }
 | |
|       this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList,
 | |
|                                         this.operatorListIdx,
 | |
|                                         this._continue.bind(this),
 | |
|                                         this.stepper);
 | |
|       if (this.operatorListIdx === this.operatorList.argsArray.length) {
 | |
|         this.running = false;
 | |
|         if (this.operatorList.lastChunk) {
 | |
|           this.gfx.endDrawing();
 | |
|           this.callback();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   };
 | |
| 
 | |
|   return InternalRenderTask;
 | |
| })();
 | |
| 
 | |
| 
 | |
| var Metadata = PDFJS.Metadata = (function MetadataClosure() {
 | |
|   function fixMetadata(meta) {
 | |
|     return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) {
 | |
|       var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g,
 | |
|                                 function(code, d1, d2, d3) {
 | |
|         return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);
 | |
|       });
 | |
|       var chars = '';
 | |
|       for (var i = 0; i < bytes.length; i += 2) {
 | |
|         var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
 | |
|         chars += code >= 32 && code < 127 && code != 60 && code != 62 &&
 | |
|           code != 38 && false ? String.fromCharCode(code) :
 | |
|           '&#x' + (0x10000 + code).toString(16).substring(1) + ';';
 | |
|       }
 | |
|       return '>' + chars;
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   function Metadata(meta) {
 | |
|     if (typeof meta === 'string') {
 | |
|       // Ghostscript produces invalid metadata
 | |
|       meta = fixMetadata(meta);
 | |
| 
 | |
|       var parser = new DOMParser();
 | |
|       meta = parser.parseFromString(meta, 'application/xml');
 | |
|     } else if (!(meta instanceof Document)) {
 | |
|       error('Metadata: Invalid metadata object');
 | |
|     }
 | |
| 
 | |
|     this.metaDocument = meta;
 | |
|     this.metadata = {};
 | |
|     this.parse();
 | |
|   }
 | |
| 
 | |
|   Metadata.prototype = {
 | |
|     parse: function Metadata_parse() {
 | |
|       var doc = this.metaDocument;
 | |
|       var rdf = doc.documentElement;
 | |
| 
 | |
|       if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta>
 | |
|         rdf = rdf.firstChild;
 | |
|         while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf')
 | |
|           rdf = rdf.nextSibling;
 | |
|       }
 | |
| 
 | |
|       var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
 | |
|       if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes())
 | |
|         return;
 | |
| 
 | |
|       var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
 | |
| 
 | |
|       for (i = 0, length = children.length; i < length; i++) {
 | |
|         desc = children[i];
 | |
|         if (desc.nodeName.toLowerCase() !== 'rdf:description')
 | |
|           continue;
 | |
| 
 | |
|         for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
 | |
|           if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') {
 | |
|             entry = desc.childNodes[ii];
 | |
|             name = entry.nodeName.toLowerCase();
 | |
|             this.metadata[name] = entry.textContent.trim();
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     get: function Metadata_get(name) {
 | |
|       return this.metadata[name] || null;
 | |
|     },
 | |
| 
 | |
|     has: function Metadata_has(name) {
 | |
|       return typeof this.metadata[name] !== 'undefined';
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   return Metadata;
 | |
| })();
 | |
| 
 | |
| 
 | |
| // <canvas> contexts store most of the state we need natively.
 | |
| // However, PDF needs a bit more state, which we store here.
 | |
| 
 | |
| // Minimal font size that would be used during canvas fillText operations.
 | |
| var MIN_FONT_SIZE = 16;
 | |
| 
 | |
| var COMPILE_TYPE3_GLYPHS = true;
 | |
| 
 | |
| function createScratchCanvas(width, height) {
 | |
|   var canvas = document.createElement('canvas');
 | |
|   canvas.width = width;
 | |
|   canvas.height = height;
 | |
|   return canvas;
 | |
| }
 | |
| 
 | |
| function addContextCurrentTransform(ctx) {
 | |
|   // If the context doesn't expose a `mozCurrentTransform`, add a JS based on.
 | |
|   if (!ctx.mozCurrentTransform) {
 | |
|     // Store the original context
 | |
|     ctx._scaleX = ctx._scaleX || 1.0;
 | |
|     ctx._scaleY = ctx._scaleY || 1.0;
 | |
|     ctx._originalSave = ctx.save;
 | |
|     ctx._originalRestore = ctx.restore;
 | |
|     ctx._originalRotate = ctx.rotate;
 | |
|     ctx._originalScale = ctx.scale;
 | |
|     ctx._originalTranslate = ctx.translate;
 | |
|     ctx._originalTransform = ctx.transform;
 | |
|     ctx._originalSetTransform = ctx.setTransform;
 | |
| 
 | |
|     ctx._transformMatrix = [ctx._scaleX, 0, 0, ctx._scaleY, 0, 0];
 | |
|     ctx._transformStack = [];
 | |
| 
 | |
|     Object.defineProperty(ctx, 'mozCurrentTransform', {
 | |
|       get: function getCurrentTransform() {
 | |
|         return this._transformMatrix;
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
 | |
|       get: function getCurrentTransformInverse() {
 | |
|         // Calculation done using WolframAlpha:
 | |
|         // http://www.wolframalpha.com/input/?
 | |
|         //   i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
 | |
| 
 | |
|         var m = this._transformMatrix;
 | |
|         var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
 | |
| 
 | |
|         var ad_bc = a * d - b * c;
 | |
|         var bc_ad = b * c - a * d;
 | |
| 
 | |
|         return [
 | |
|           d / ad_bc,
 | |
|           b / bc_ad,
 | |
|           c / bc_ad,
 | |
|           a / ad_bc,
 | |
|           (d * e - c * f) / bc_ad,
 | |
|           (b * e - a * f) / ad_bc
 | |
|         ];
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     ctx.save = function ctxSave() {
 | |
|       var old = this._transformMatrix;
 | |
|       this._transformStack.push(old);
 | |
|       this._transformMatrix = old.slice(0, 6);
 | |
| 
 | |
|       this._originalSave();
 | |
|     };
 | |
| 
 | |
|     ctx.restore = function ctxRestore() {
 | |
|       var prev = this._transformStack.pop();
 | |
|       if (prev) {
 | |
|         this._transformMatrix = prev;
 | |
|         this._originalRestore();
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     ctx.translate = function ctxTranslate(x, y) {
 | |
|       var m = this._transformMatrix;
 | |
|       m[4] = m[0] * x + m[2] * y + m[4];
 | |
|       m[5] = m[1] * x + m[3] * y + m[5];
 | |
| 
 | |
|       this._originalTranslate(x, y);
 | |
|     };
 | |
| 
 | |
|     ctx.scale = function ctxScale(x, y) {
 | |
|       var m = this._transformMatrix;
 | |
|       m[0] = m[0] * x;
 | |
|       m[1] = m[1] * x;
 | |
|       m[2] = m[2] * y;
 | |
|       m[3] = m[3] * y;
 | |
| 
 | |
|       this._originalScale(x, y);
 | |
|     };
 | |
| 
 | |
|     ctx.transform = function ctxTransform(a, b, c, d, e, f) {
 | |
|       var m = this._transformMatrix;
 | |
|       this._transformMatrix = [
 | |
|         m[0] * a + m[2] * b,
 | |
|         m[1] * a + m[3] * b,
 | |
|         m[0] * c + m[2] * d,
 | |
|         m[1] * c + m[3] * d,
 | |
|         m[0] * e + m[2] * f + m[4],
 | |
|         m[1] * e + m[3] * f + m[5]
 | |
|       ];
 | |
| 
 | |
|       ctx._originalTransform(a, b, c, d, e, f);
 | |
|     };
 | |
| 
 | |
|     ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
 | |
|       this._transformMatrix = [a, b, c, d, e, f];
 | |
| 
 | |
|       ctx._originalSetTransform(a, b, c, d, e, f);
 | |
|     };
 | |
| 
 | |
|     ctx.rotate = function ctxRotate(angle) {
 | |
|       var cosValue = Math.cos(angle);
 | |
|       var sinValue = Math.sin(angle);
 | |
| 
 | |
|       var m = this._transformMatrix;
 | |
|       this._transformMatrix = [
 | |
|         m[0] * cosValue + m[2] * sinValue,
 | |
|         m[1] * cosValue + m[3] * sinValue,
 | |
|         m[0] * (-sinValue) + m[2] * cosValue,
 | |
|         m[1] * (-sinValue) + m[3] * cosValue,
 | |
|         m[4],
 | |
|         m[5]
 | |
|       ];
 | |
| 
 | |
|       this._originalRotate(angle);
 | |
|     };
 | |
|   }
 | |
| }
 | |
| 
 | |
| var CachedCanvases = (function CachedCanvasesClosure() {
 | |
|   var cache = {};
 | |
|   return {
 | |
|     getCanvas: function CachedCanvases_getCanvas(id, width, height,
 | |
|                                                  trackTransform) {
 | |
|       var canvasEntry;
 | |
|       if (id in cache) {
 | |
|         canvasEntry = cache[id];
 | |
|         canvasEntry.canvas.width = width;
 | |
|         canvasEntry.canvas.height = height;
 | |
|         // reset canvas transform for emulated mozCurrentTransform, if needed
 | |
|         canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
 | |
|       } else {
 | |
|         var canvas = createScratchCanvas(width, height);
 | |
|         var ctx = canvas.getContext('2d');
 | |
|         if (trackTransform) {
 | |
|           addContextCurrentTransform(ctx);
 | |
|         }
 | |
|         cache[id] = canvasEntry = {canvas: canvas, context: ctx};
 | |
|       }
 | |
|       return canvasEntry;
 | |
|     },
 | |
|     clear: function () {
 | |
|       cache = {};
 | |
|     }
 | |
|   };
 | |
| })();
 | |
| 
 | |
| function compileType3Glyph(imgData) {
 | |
|   var POINT_TO_PROCESS_LIMIT = 1000;
 | |
| 
 | |
|   var width = imgData.width, height = imgData.height;
 | |
|   var i, j, j0, width1 = width + 1;
 | |
|   var points = new Uint8Array(width1 * (height + 1));
 | |
|   var POINT_TYPES =
 | |
|       new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
 | |
|   // finding iteresting points: every point is located between mask pixels,
 | |
|   // so there will be points of the (width + 1)x(height + 1) grid. Every point
 | |
|   // will have flags assigned based on neighboring mask pixels:
 | |
|   //   4 | 8
 | |
|   //   --P--
 | |
|   //   2 | 1
 | |
|   // We are interested only in points with the flags:
 | |
|   //   - outside corners: 1, 2, 4, 8;
 | |
|   //   - inside corners: 7, 11, 13, 14;
 | |
|   //   - and, intersections: 5, 10.
 | |
|   var pos = 3, data = imgData.data, lineSize = width * 4, count = 0;
 | |
|   if (data[3] !== 0) {
 | |
|     points[0] = 1;
 | |
|     ++count;
 | |
|   }
 | |
|   for (j = 1; j < width; j++) {
 | |
|     if (data[pos] !== data[pos + 4]) {
 | |
|       points[j] = data[pos] ? 2 : 1;
 | |
|       ++count;
 | |
|     }
 | |
|     pos += 4;
 | |
|   }
 | |
|   if (data[pos] !== 0) {
 | |
|     points[j] = 2;
 | |
|     ++count;
 | |
|   }
 | |
|   pos += 4;
 | |
|   for (i = 1; i < height; i++) {
 | |
|     j0 = i * width1;
 | |
|     if (data[pos - lineSize] !== data[pos]) {
 | |
|       points[j0] = data[pos] ? 1 : 8;
 | |
|       ++count;
 | |
|     }
 | |
|     // 'sum' is the position of the current pixel configuration in the 'TYPES'
 | |
|     // array (in order 8-1-2-4, so we can use '>>2' to shift the column).
 | |
|     var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
 | |
|     for (j = 1; j < width; j++) {
 | |
|       sum = (sum >> 2) + (data[pos + 4] ? 4 : 0) +
 | |
|             (data[pos - lineSize + 4] ? 8 : 0);
 | |
|       if (POINT_TYPES[sum]) {
 | |
|         points[j0 + j] = POINT_TYPES[sum];
 | |
|         ++count;
 | |
|       }
 | |
|       pos += 4;
 | |
|     }
 | |
|     if (data[pos - lineSize] !== data[pos]) {
 | |
|       points[j0 + j] = data[pos] ? 2 : 4;
 | |
|       ++count;
 | |
|     }
 | |
|     pos += 4;
 | |
| 
 | |
|     if (count > POINT_TO_PROCESS_LIMIT) {
 | |
|       return null;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   pos -= lineSize;
 | |
|   j0 = i * width1;
 | |
|   if (data[pos] !== 0) {
 | |
|     points[j0] = 8;
 | |
|     ++count;
 | |
|   }
 | |
|   for (j = 1; j < width; j++) {
 | |
|     if (data[pos] !== data[pos + 4]) {
 | |
|       points[j0 + j] = data[pos] ? 4 : 8;
 | |
|       ++count;
 | |
|     }
 | |
|     pos += 4;
 | |
|   }
 | |
|   if (data[pos] !== 0) {
 | |
|     points[j0 + j] = 4;
 | |
|     ++count;
 | |
|   }
 | |
|   if (count > POINT_TO_PROCESS_LIMIT) {
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   // building outlines
 | |
|   var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
 | |
|   var outlines = [];
 | |
|   for (i = 0; count && i <= height; i++) {
 | |
|     var p = i * width1;
 | |
|     var end = p + width;
 | |
|     while (p < end && !points[p]) {
 | |
|       p++;
 | |
|     }
 | |
|     if (p === end) {
 | |
|       continue;
 | |
|     }
 | |
|     var coords = [p % width1, i];
 | |
| 
 | |
|     var type = points[p], p0 = p, pp;
 | |
|     do {
 | |
|       var step = steps[type];
 | |
|       do { p += step; } while (!points[p]);
 | |
| 
 | |
|       pp = points[p];
 | |
|       if (pp !== 5 && pp !== 10) {
 | |
|         // set new direction
 | |
|         type = pp;
 | |
|         // delete mark
 | |
|         points[p] = 0;
 | |
|       } else { // type is 5 or 10, ie, a crossing
 | |
|         // set new direction
 | |
|         type = pp & ((0x33 * type) >> 4);
 | |
|         // set new type for "future hit"
 | |
|         points[p] &= (type >> 2 | type << 2);
 | |
|       }
 | |
| 
 | |
|       coords.push(p % width1);
 | |
|       coords.push((p / width1) | 0);
 | |
|       --count;
 | |
|     } while (p0 !== p);
 | |
|     outlines.push(coords);
 | |
|     --i;
 | |
|   }
 | |
| 
 | |
|   var drawOutline = function(c) {
 | |
|     c.save();
 | |
|     // the path shall be painted in [0..1]x[0..1] space
 | |
|     c.scale(1 / width, -1 / height);
 | |
|     c.translate(0, -height);
 | |
|     c.beginPath();
 | |
|     for (var i = 0, ii = outlines.length; i < ii; i++) {
 | |
|       var o = outlines[i];
 | |
|       c.moveTo(o[0], o[1]);
 | |
|       for (var j = 2, jj = o.length; j < jj; j += 2) {
 | |
|         c.lineTo(o[j], o[j+1]);
 | |
|       }
 | |
|     }
 | |
|     c.fill();
 | |
|     c.beginPath();
 | |
|     c.restore();
 | |
|   };
 | |
| 
 | |
|   return drawOutline;
 | |
| }
 | |
| 
 | |
| var CanvasExtraState = (function CanvasExtraStateClosure() {
 | |
|   function CanvasExtraState(old) {
 | |
|     // Are soft masks and alpha values shapes or opacities?
 | |
|     this.alphaIsShape = false;
 | |
|     this.fontSize = 0;
 | |
|     this.fontSizeScale = 1;
 | |
|     this.textMatrix = IDENTITY_MATRIX;
 | |
|     this.fontMatrix = FONT_IDENTITY_MATRIX;
 | |
|     this.leading = 0;
 | |
|     // Current point (in user coordinates)
 | |
|     this.x = 0;
 | |
|     this.y = 0;
 | |
|     // Start of text line (in text coordinates)
 | |
|     this.lineX = 0;
 | |
|     this.lineY = 0;
 | |
|     // Character and word spacing
 | |
|     this.charSpacing = 0;
 | |
|     this.wordSpacing = 0;
 | |
|     this.textHScale = 1;
 | |
|     this.textRenderingMode = TextRenderingMode.FILL;
 | |
|     this.textRise = 0;
 | |
|     // Color spaces
 | |
|     this.fillColorSpace = ColorSpace.singletons.gray;
 | |
|     this.fillColorSpaceObj = null;
 | |
|     this.strokeColorSpace = ColorSpace.singletons.gray;
 | |
|     this.strokeColorSpaceObj = null;
 | |
|     this.fillColorObj = null;
 | |
|     this.strokeColorObj = null;
 | |
|     // Default fore and background colors
 | |
|     this.fillColor = '#000000';
 | |
|     this.strokeColor = '#000000';
 | |
|     // Note: fill alpha applies to all non-stroking operations
 | |
|     this.fillAlpha = 1;
 | |
|     this.strokeAlpha = 1;
 | |
|     this.lineWidth = 1;
 | |
|     this.paintFormXObjectDepth = 0;
 | |
| 
 | |
|     this.old = old;
 | |
|   }
 | |
| 
 | |
|   CanvasExtraState.prototype = {
 | |
|     clone: function CanvasExtraState_clone() {
 | |
|       return Object.create(this);
 | |
|     },
 | |
|     setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) {
 | |
|       this.x = x;
 | |
|       this.y = y;
 | |
|     }
 | |
|   };
 | |
|   return CanvasExtraState;
 | |
| })();
 | |
| 
 | |
| var CanvasGraphics = (function CanvasGraphicsClosure() {
 | |
|   // Defines the time the executeOperatorList is going to be executing
 | |
|   // before it stops and shedules a continue of execution.
 | |
|   var EXECUTION_TIME = 15;
 | |
| 
 | |
|   function CanvasGraphics(canvasCtx, commonObjs, objs, textLayer, imageLayer) {
 | |
|     this.ctx = canvasCtx;
 | |
|     this.current = new CanvasExtraState();
 | |
|     this.stateStack = [];
 | |
|     this.pendingClip = null;
 | |
|     this.pendingEOFill = false;
 | |
|     this.res = null;
 | |
|     this.xobjs = null;
 | |
|     this.commonObjs = commonObjs;
 | |
|     this.objs = objs;
 | |
|     this.textLayer = textLayer;
 | |
|     this.imageLayer = imageLayer;
 | |
|     this.groupStack = [];
 | |
|     this.processingType3 = null;
 | |
|     // Patterns are painted relative to the initial page/form transform, see pdf
 | |
|     // spec 8.7.2 NOTE 1.
 | |
|     this.baseTransform = null;
 | |
|     this.baseTransformStack = [];
 | |
|     this.groupLevel = 0;
 | |
|     if (canvasCtx) {
 | |
|       addContextCurrentTransform(canvasCtx);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function putBinaryImageData(ctx, imgData) {
 | |
|     if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
 | |
|       ctx.putImageData(imgData, 0, 0);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     var tmpImgData = ctx.createImageData(imgData.width, imgData.height);
 | |
| 
 | |
|     var data = imgData.data;
 | |
|     var tmpImgDataPixels = tmpImgData.data;
 | |
|     if ('set' in tmpImgDataPixels)
 | |
|       tmpImgDataPixels.set(data);
 | |
|     else {
 | |
|       // Copy over the imageData pixel by pixel.
 | |
|       for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
 | |
|         tmpImgDataPixels[i] = data[i];
 | |
|     }
 | |
| 
 | |
|     ctx.putImageData(tmpImgData, 0, 0);
 | |
|   }
 | |
| 
 | |
|   function copyCtxState(sourceCtx, destCtx) {
 | |
|     var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha',
 | |
|                       'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
 | |
|                       'globalCompositeOperation', 'font'];
 | |
|     for (var i = 0, ii = properties.length; i < ii; i++) {
 | |
|       var property = properties[i];
 | |
|       if (property in sourceCtx) {
 | |
|         destCtx[property] = sourceCtx[property];
 | |
|       }
 | |
|     }
 | |
|     if ('setLineDash' in sourceCtx) {
 | |
|       destCtx.setLineDash(sourceCtx.getLineDash());
 | |
|       destCtx.lineDashOffset =  sourceCtx.lineDashOffset;
 | |
|     } else if ('mozDash' in sourceCtx) {
 | |
|       destCtx.mozDash = sourceCtx.mozDash;
 | |
|       destCtx.mozDashOffset = sourceCtx.mozDashOffset;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   var LINE_CAP_STYLES = ['butt', 'round', 'square'];
 | |
|   var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
 | |
|   var NORMAL_CLIP = {};
 | |
|   var EO_CLIP = {};
 | |
| 
 | |
|   CanvasGraphics.prototype = {
 | |
| 
 | |
|     beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) {
 | |
|       // For pdfs that use blend modes we have to clear the canvas else certain
 | |
|       // blend modes can look wrong since we'd be blending with a white
 | |
|       // backdrop. The problem with a transparent backdrop though is we then
 | |
|       // don't get sub pixel anti aliasing on text, so we fill with white if
 | |
|       // we can.
 | |
|       var width = this.ctx.canvas.width;
 | |
|       var height = this.ctx.canvas.height;
 | |
|       if (transparency) {
 | |
|         this.ctx.clearRect(0, 0, width, height);
 | |
|       } else {
 | |
|         this.ctx.mozOpaque = true;
 | |
|         this.ctx.save();
 | |
|         this.ctx.fillStyle = 'rgb(255, 255, 255)';
 | |
|         this.ctx.fillRect(0, 0, width, height);
 | |
|         this.ctx.restore();
 | |
|       }
 | |
| 
 | |
|       var transform = viewport.transform;
 | |
|       this.baseTransform = transform.slice();
 | |
|       this.ctx.save();
 | |
|       this.ctx.transform.apply(this.ctx, transform);
 | |
| 
 | |
|       if (this.textLayer) {
 | |
|         this.textLayer.beginLayout();
 | |
|       }
 | |
|       if (this.imageLayer) {
 | |
|         this.imageLayer.beginLayout();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     executeOperatorList: function CanvasGraphics_executeOperatorList(
 | |
|                                     operatorList,
 | |
|                                     executionStartIdx, continueCallback,
 | |
|                                     stepper) {
 | |
|       var argsArray = operatorList.argsArray;
 | |
|       var fnArray = operatorList.fnArray;
 | |
|       var i = executionStartIdx || 0;
 | |
|       var argsArrayLen = argsArray.length;
 | |
| 
 | |
|       // Sometimes the OperatorList to execute is empty.
 | |
|       if (argsArrayLen == i) {
 | |
|         return i;
 | |
|       }
 | |
| 
 | |
|       var executionEndIdx;
 | |
|       var endTime = Date.now() + EXECUTION_TIME;
 | |
| 
 | |
|       var commonObjs = this.commonObjs;
 | |
|       var objs = this.objs;
 | |
|       var fnId;
 | |
| 
 | |
|       while (true) {
 | |
|         if (stepper && i === stepper.nextBreakPoint) {
 | |
|           stepper.breakIt(i, continueCallback);
 | |
|           return i;
 | |
|         }
 | |
| 
 | |
|         fnId = fnArray[i];
 | |
| 
 | |
|         if (fnId !== OPS.dependency) {
 | |
|           this[fnId].apply(this, argsArray[i]);
 | |
|         } else {
 | |
|           var deps = argsArray[i];
 | |
|           for (var n = 0, nn = deps.length; n < nn; n++) {
 | |
|             var depObjId = deps[n];
 | |
|             var common = depObjId.substring(0, 2) == 'g_';
 | |
| 
 | |
|             // If the promise isn't resolved yet, add the continueCallback
 | |
|             // to the promise and bail out.
 | |
|             if (!common && !objs.isResolved(depObjId)) {
 | |
|               objs.get(depObjId, continueCallback);
 | |
|               return i;
 | |
|             }
 | |
|             if (common && !commonObjs.isResolved(depObjId)) {
 | |
|               commonObjs.get(depObjId, continueCallback);
 | |
|               return i;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         i++;
 | |
| 
 | |
|         // If the entire operatorList was executed, stop as were done.
 | |
|         if (i == argsArrayLen) {
 | |
|           return i;
 | |
|         }
 | |
| 
 | |
|         // If the execution took longer then a certain amount of time, schedule
 | |
|         // to continue exeution after a short delay.
 | |
|         // However, this is only possible if a 'continueCallback' is passed in.
 | |
|         if (continueCallback && Date.now() > endTime) {
 | |
|           setTimeout(continueCallback, 0);
 | |
|           return i;
 | |
|         }
 | |
| 
 | |
|         // If the operatorList isn't executed completely yet OR the execution
 | |
|         // time was short enough, do another execution round.
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     endDrawing: function CanvasGraphics_endDrawing() {
 | |
|       this.ctx.restore();
 | |
|       CachedCanvases.clear();
 | |
| 
 | |
|       if (this.textLayer) {
 | |
|         this.textLayer.endLayout();
 | |
|       }
 | |
|       if (this.imageLayer) {
 | |
|         this.imageLayer.endLayout();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     // Graphics state
 | |
|     setLineWidth: function CanvasGraphics_setLineWidth(width) {
 | |
|       this.current.lineWidth = width;
 | |
|       this.ctx.lineWidth = width;
 | |
|     },
 | |
|     setLineCap: function CanvasGraphics_setLineCap(style) {
 | |
|       this.ctx.lineCap = LINE_CAP_STYLES[style];
 | |
|     },
 | |
|     setLineJoin: function CanvasGraphics_setLineJoin(style) {
 | |
|       this.ctx.lineJoin = LINE_JOIN_STYLES[style];
 | |
|     },
 | |
|     setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
 | |
|       this.ctx.miterLimit = limit;
 | |
|     },
 | |
|     setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
 | |
|       var ctx = this.ctx;
 | |
|       if ('setLineDash' in ctx) {
 | |
|         ctx.setLineDash(dashArray);
 | |
|         ctx.lineDashOffset = dashPhase;
 | |
|       } else {
 | |
|         ctx.mozDash = dashArray;
 | |
|         ctx.mozDashOffset = dashPhase;
 | |
|       }
 | |
|     },
 | |
|     setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
 | |
|       // Maybe if we one day fully support color spaces this will be important
 | |
|       // for now we can ignore.
 | |
|       // TODO set rendering intent?
 | |
|     },
 | |
|     setFlatness: function CanvasGraphics_setFlatness(flatness) {
 | |
|       // There's no way to control this with canvas, but we can safely ignore.
 | |
|       // TODO set flatness?
 | |
|     },
 | |
|     setGState: function CanvasGraphics_setGState(states) {
 | |
|       for (var i = 0, ii = states.length; i < ii; i++) {
 | |
|         var state = states[i];
 | |
|         var key = state[0];
 | |
|         var value = state[1];
 | |
| 
 | |
|         switch (key) {
 | |
|           case 'LW':
 | |
|             this.setLineWidth(value);
 | |
|             break;
 | |
|           case 'LC':
 | |
|             this.setLineCap(value);
 | |
|             break;
 | |
|           case 'LJ':
 | |
|             this.setLineJoin(value);
 | |
|             break;
 | |
|           case 'ML':
 | |
|             this.setMiterLimit(value);
 | |
|             break;
 | |
|           case 'D':
 | |
|             this.setDash(value[0], value[1]);
 | |
|             break;
 | |
|           case 'RI':
 | |
|             this.setRenderingIntent(value);
 | |
|             break;
 | |
|           case 'FL':
 | |
|             this.setFlatness(value);
 | |
|             break;
 | |
|           case 'Font':
 | |
|             this.setFont(value[0], value[1]);
 | |
|             break;
 | |
|           case 'CA':
 | |
|             this.current.strokeAlpha = state[1];
 | |
|             break;
 | |
|           case 'ca':
 | |
|             this.current.fillAlpha = state[1];
 | |
|             this.ctx.globalAlpha = state[1];
 | |
|             break;
 | |
|           case 'BM':
 | |
|             if (value && value.name && (value.name !== 'Normal')) {
 | |
|               var mode = value.name.replace(/([A-Z])/g,
 | |
|                 function(c) {
 | |
|                   return '-' + c.toLowerCase();
 | |
|                 }
 | |
|               ).substring(1);
 | |
|               this.ctx.globalCompositeOperation = mode;
 | |
|               if (this.ctx.globalCompositeOperation !== mode) {
 | |
|                 warn('globalCompositeOperation "' + mode +
 | |
|                      '" is not supported');
 | |
|               }
 | |
|             } else {
 | |
|               this.ctx.globalCompositeOperation = 'source-over';
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     save: function CanvasGraphics_save() {
 | |
|       this.ctx.save();
 | |
|       var old = this.current;
 | |
|       this.stateStack.push(old);
 | |
|       this.current = old.clone();
 | |
|     },
 | |
|     restore: function CanvasGraphics_restore() {
 | |
|       var prev = this.stateStack.pop();
 | |
|       if (prev) {
 | |
|         this.current = prev;
 | |
|         this.ctx.restore();
 | |
|       }
 | |
|     },
 | |
|     transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
 | |
|       this.ctx.transform(a, b, c, d, e, f);
 | |
|     },
 | |
| 
 | |
|     // Path
 | |
|     moveTo: function CanvasGraphics_moveTo(x, y) {
 | |
|       this.ctx.moveTo(x, y);
 | |
|       this.current.setCurrentPoint(x, y);
 | |
|     },
 | |
|     lineTo: function CanvasGraphics_lineTo(x, y) {
 | |
|       this.ctx.lineTo(x, y);
 | |
|       this.current.setCurrentPoint(x, y);
 | |
|     },
 | |
|     curveTo: function CanvasGraphics_curveTo(x1, y1, x2, y2, x3, y3) {
 | |
|       this.ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
 | |
|       this.current.setCurrentPoint(x3, y3);
 | |
|     },
 | |
|     curveTo2: function CanvasGraphics_curveTo2(x2, y2, x3, y3) {
 | |
|       var current = this.current;
 | |
|       this.ctx.bezierCurveTo(current.x, current.y, x2, y2, x3, y3);
 | |
|       current.setCurrentPoint(x3, y3);
 | |
|     },
 | |
|     curveTo3: function CanvasGraphics_curveTo3(x1, y1, x3, y3) {
 | |
|       this.curveTo(x1, y1, x3, y3, x3, y3);
 | |
|       this.current.setCurrentPoint(x3, y3);
 | |
|     },
 | |
|     closePath: function CanvasGraphics_closePath() {
 | |
|       this.ctx.closePath();
 | |
|     },
 | |
|     rectangle: function CanvasGraphics_rectangle(x, y, width, height) {
 | |
|       this.ctx.rect(x, y, width, height);
 | |
|     },
 | |
|     stroke: function CanvasGraphics_stroke(consumePath) {
 | |
|       consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
 | |
|       var ctx = this.ctx;
 | |
|       var strokeColor = this.current.strokeColor;
 | |
|       if (this.current.lineWidth === 0)
 | |
|         ctx.lineWidth = this.getSinglePixelWidth();
 | |
|       // For stroke we want to temporarily change the global alpha to the
 | |
|       // stroking alpha.
 | |
|       ctx.globalAlpha = this.current.strokeAlpha;
 | |
|       if (strokeColor && strokeColor.hasOwnProperty('type') &&
 | |
|           strokeColor.type === 'Pattern') {
 | |
|         // for patterns, we transform to pattern space, calculate
 | |
|         // the pattern, call stroke, and restore to user space
 | |
|         ctx.save();
 | |
|         ctx.strokeStyle = strokeColor.getPattern(ctx, this);
 | |
|         ctx.stroke();
 | |
|         ctx.restore();
 | |
|       } else {
 | |
|         ctx.stroke();
 | |
|       }
 | |
|       if (consumePath)
 | |
|         this.consumePath();
 | |
|       // Restore the global alpha to the fill alpha
 | |
|       ctx.globalAlpha = this.current.fillAlpha;
 | |
|     },
 | |
|     closeStroke: function CanvasGraphics_closeStroke() {
 | |
|       this.closePath();
 | |
|       this.stroke();
 | |
|     },
 | |
|     fill: function CanvasGraphics_fill(consumePath) {
 | |
|       consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
 | |
|       var ctx = this.ctx;
 | |
|       var fillColor = this.current.fillColor;
 | |
|       var needRestore = false;
 | |
| 
 | |
|       if (fillColor && fillColor.hasOwnProperty('type') &&
 | |
|           fillColor.type === 'Pattern') {
 | |
|         ctx.save();
 | |
|         ctx.fillStyle = fillColor.getPattern(ctx, this);
 | |
|         needRestore = true;
 | |
|       }
 | |
| 
 | |
|       if (this.pendingEOFill) {
 | |
|         if ('mozFillRule' in this.ctx) {
 | |
|           this.ctx.mozFillRule = 'evenodd';
 | |
|           this.ctx.fill();
 | |
|           this.ctx.mozFillRule = 'nonzero';
 | |
|         } else {
 | |
|           try {
 | |
|             this.ctx.fill('evenodd');
 | |
|           } catch (ex) {
 | |
|             // shouldn't really happen, but browsers might think differently
 | |
|             this.ctx.fill();
 | |
|           }
 | |
|         }
 | |
|         this.pendingEOFill = false;
 | |
|       } else {
 | |
|         this.ctx.fill();
 | |
|       }
 | |
| 
 | |
|       if (needRestore) {
 | |
|         ctx.restore();
 | |
|       }
 | |
|       if (consumePath) {
 | |
|         this.consumePath();
 | |
|       }
 | |
|     },
 | |
|     eoFill: function CanvasGraphics_eoFill() {
 | |
|       this.pendingEOFill = true;
 | |
|       this.fill();
 | |
|     },
 | |
|     fillStroke: function CanvasGraphics_fillStroke() {
 | |
|       this.fill(false);
 | |
|       this.stroke(false);
 | |
| 
 | |
|       this.consumePath();
 | |
|     },
 | |
|     eoFillStroke: function CanvasGraphics_eoFillStroke() {
 | |
|       this.pendingEOFill = true;
 | |
|       this.fillStroke();
 | |
|     },
 | |
|     closeFillStroke: function CanvasGraphics_closeFillStroke() {
 | |
|       this.closePath();
 | |
|       this.fillStroke();
 | |
|     },
 | |
|     closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() {
 | |
|       this.pendingEOFill = true;
 | |
|       this.closePath();
 | |
|       this.fillStroke();
 | |
|     },
 | |
|     endPath: function CanvasGraphics_endPath() {
 | |
|       this.consumePath();
 | |
|     },
 | |
| 
 | |
|     // Clipping
 | |
|     clip: function CanvasGraphics_clip() {
 | |
|       this.pendingClip = NORMAL_CLIP;
 | |
|     },
 | |
|     eoClip: function CanvasGraphics_eoClip() {
 | |
|       this.pendingClip = EO_CLIP;
 | |
|     },
 | |
| 
 | |
|     // Text
 | |
|     beginText: function CanvasGraphics_beginText() {
 | |
|       this.current.textMatrix = IDENTITY_MATRIX;
 | |
|       this.current.x = this.current.lineX = 0;
 | |
|       this.current.y = this.current.lineY = 0;
 | |
|     },
 | |
|     endText: function CanvasGraphics_endText() {
 | |
|       if (!('pendingTextPaths' in this)) {
 | |
|         this.ctx.beginPath();
 | |
|         return;
 | |
|       }
 | |
|       var paths = this.pendingTextPaths;
 | |
|       var ctx = this.ctx;
 | |
| 
 | |
|       ctx.save();
 | |
|       ctx.beginPath();
 | |
|       for (var i = 0; i < paths.length; i++) {
 | |
|         var path = paths[i];
 | |
|         ctx.setTransform.apply(ctx, path.transform);
 | |
|         ctx.translate(path.x, path.y);
 | |
|         path.addToPath(ctx, path.fontSize);
 | |
|       }
 | |
|       ctx.restore();
 | |
|       ctx.clip();
 | |
|       ctx.beginPath();
 | |
|       delete this.pendingTextPaths;
 | |
|     },
 | |
|     setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
 | |
|       this.current.charSpacing = spacing;
 | |
|     },
 | |
|     setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
 | |
|       this.current.wordSpacing = spacing;
 | |
|     },
 | |
|     setHScale: function CanvasGraphics_setHScale(scale) {
 | |
|       this.current.textHScale = scale / 100;
 | |
|     },
 | |
|     setLeading: function CanvasGraphics_setLeading(leading) {
 | |
|       this.current.leading = -leading;
 | |
|     },
 | |
|     setFont: function CanvasGraphics_setFont(fontRefName, size) {
 | |
|       var fontObj = this.commonObjs.get(fontRefName);
 | |
|       var current = this.current;
 | |
| 
 | |
|       if (!fontObj)
 | |
|         error('Can\'t find font for ' + fontRefName);
 | |
| 
 | |
|       current.fontMatrix = fontObj.fontMatrix ? fontObj.fontMatrix :
 | |
|                                                 FONT_IDENTITY_MATRIX;
 | |
| 
 | |
|       // A valid matrix needs all main diagonal elements to be non-zero
 | |
|       // This also ensures we bypass FF bugzilla bug #719844.
 | |
|       if (current.fontMatrix[0] === 0 ||
 | |
|           current.fontMatrix[3] === 0) {
 | |
|         warn('Invalid font matrix for font ' + fontRefName);
 | |
|       }
 | |
| 
 | |
|       // The spec for Tf (setFont) says that 'size' specifies the font 'scale',
 | |
|       // and in some docs this can be negative (inverted x-y axes).
 | |
|       if (size < 0) {
 | |
|         size = -size;
 | |
|         current.fontDirection = -1;
 | |
|       } else {
 | |
|         current.fontDirection = 1;
 | |
|       }
 | |
| 
 | |
|       this.current.font = fontObj;
 | |
|       this.current.fontSize = size;
 | |
| 
 | |
|       if (fontObj.coded)
 | |
|         return; // we don't need ctx.font for Type3 fonts
 | |
| 
 | |
|       var name = fontObj.loadedName || 'sans-serif';
 | |
|       var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
 | |
|                                  (fontObj.bold ? 'bold' : 'normal');
 | |
| 
 | |
|       var italic = fontObj.italic ? 'italic' : 'normal';
 | |
|       var typeface = '"' + name + '", ' + fontObj.fallbackName;
 | |
| 
 | |
|       // Some font backends cannot handle fonts below certain size.
 | |
|       // Keeping the font at minimal size and using the fontSizeScale to change
 | |
|       // the current transformation matrix before the fillText/strokeText.
 | |
|       // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
 | |
|       var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
 | |
|       this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
 | |
|                                    size / MIN_FONT_SIZE;
 | |
| 
 | |
|       var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
 | |
|       this.ctx.font = rule;
 | |
|     },
 | |
|     setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
 | |
|       this.current.textRenderingMode = mode;
 | |
|     },
 | |
|     setTextRise: function CanvasGraphics_setTextRise(rise) {
 | |
|       this.current.textRise = rise;
 | |
|     },
 | |
|     moveText: function CanvasGraphics_moveText(x, y) {
 | |
|       this.current.x = this.current.lineX += x;
 | |
|       this.current.y = this.current.lineY += y;
 | |
|     },
 | |
|     setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
 | |
|       this.setLeading(-y);
 | |
|       this.moveText(x, y);
 | |
|     },
 | |
|     setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) {
 | |
|       this.current.textMatrix = [a, b, c, d, e, f];
 | |
| 
 | |
|       this.current.x = this.current.lineX = 0;
 | |
|       this.current.y = this.current.lineY = 0;
 | |
|     },
 | |
|     nextLine: function CanvasGraphics_nextLine() {
 | |
|       this.moveText(0, this.current.leading);
 | |
|     },
 | |
|     applyTextTransforms: function CanvasGraphics_applyTextTransforms() {
 | |
|       var ctx = this.ctx;
 | |
|       var current = this.current;
 | |
|       ctx.transform.apply(ctx, current.textMatrix);
 | |
|       ctx.translate(current.x, current.y + current.textRise);
 | |
|       if (current.fontDirection > 0) {
 | |
|         ctx.scale(current.textHScale, -1);
 | |
|       } else {
 | |
|         ctx.scale(-current.textHScale, 1);
 | |
|       }
 | |
|     },
 | |
|     createTextGeometry: function CanvasGraphics_createTextGeometry() {
 | |
|       var geometry = {};
 | |
|       var ctx = this.ctx;
 | |
|       var font = this.current.font;
 | |
|       var ctxMatrix = ctx.mozCurrentTransform;
 | |
|       var a = ctxMatrix[0], b = ctxMatrix[1], c = ctxMatrix[2];
 | |
|       var d = ctxMatrix[3], e = ctxMatrix[4], f = ctxMatrix[5];
 | |
|       var sx = (a >= 0) ?
 | |
|           Math.sqrt((a * a) + (b * b)) : -Math.sqrt((a * a) + (b * b));
 | |
|       var sy = (d >= 0) ?
 | |
|           Math.sqrt((c * c) + (d * d)) : -Math.sqrt((c * c) + (d * d));
 | |
|       var angle = Math.atan2(b, a);
 | |
|       var x = e;
 | |
|       var y = f;
 | |
|       geometry.x = x;
 | |
|       geometry.y = y;
 | |
|       geometry.hScale = sx;
 | |
|       geometry.vScale = sy;
 | |
|       geometry.angle = angle;
 | |
|       geometry.spaceWidth = font.spaceWidth;
 | |
|       geometry.fontName = font.loadedName;
 | |
|       geometry.fontFamily = font.fallbackName;
 | |
|       geometry.fontSize = this.current.fontSize;
 | |
|       return geometry;
 | |
|     },
 | |
| 
 | |
|     paintChar: function (character, x, y) {
 | |
|       var ctx = this.ctx;
 | |
|       var current = this.current;
 | |
|       var font = current.font;
 | |
|       var fontSize = current.fontSize / current.fontSizeScale;
 | |
|       var textRenderingMode = current.textRenderingMode;
 | |
|       var fillStrokeMode = textRenderingMode &
 | |
|         TextRenderingMode.FILL_STROKE_MASK;
 | |
|       var isAddToPathSet = !!(textRenderingMode &
 | |
|         TextRenderingMode.ADD_TO_PATH_FLAG);
 | |
| 
 | |
|       var addToPath;
 | |
|       if (font.disableFontFace || isAddToPathSet) {
 | |
|         addToPath = font.getPathGenerator(this.commonObjs, character);
 | |
|       }
 | |
| 
 | |
|       if (font.disableFontFace) {
 | |
|         ctx.save();
 | |
|         ctx.translate(x, y);
 | |
|         ctx.beginPath();
 | |
|         addToPath(ctx, fontSize);
 | |
|         if (fillStrokeMode === TextRenderingMode.FILL ||
 | |
|             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
 | |
|           ctx.fill();
 | |
|         }
 | |
|         if (fillStrokeMode === TextRenderingMode.STROKE ||
 | |
|             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
 | |
|           ctx.stroke();
 | |
|         }
 | |
|         ctx.restore();
 | |
|       } else {
 | |
|         if (fillStrokeMode === TextRenderingMode.FILL ||
 | |
|             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
 | |
|           ctx.fillText(character, x, y);
 | |
|         }
 | |
|         if (fillStrokeMode === TextRenderingMode.STROKE ||
 | |
|             fillStrokeMode === TextRenderingMode.FILL_STROKE) {
 | |
|           ctx.strokeText(character, x, y);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (isAddToPathSet) {
 | |
|         var paths = this.pendingTextPaths || (this.pendingTextPaths = []);
 | |
|         paths.push({
 | |
|           transform: ctx.mozCurrentTransform,
 | |
|           x: x,
 | |
|           y: y,
 | |
|           fontSize: fontSize,
 | |
|           addToPath: addToPath
 | |
|         });
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     showText: function CanvasGraphics_showText(glyphs, skipTextSelection) {
 | |
|       var ctx = this.ctx;
 | |
|       var current = this.current;
 | |
|       var font = current.font;
 | |
|       var fontSize = current.fontSize;
 | |
|       var fontSizeScale = current.fontSizeScale;
 | |
|       var charSpacing = current.charSpacing;
 | |
|       var wordSpacing = current.wordSpacing;
 | |
|       var textHScale = current.textHScale * current.fontDirection;
 | |
|       var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
 | |
|       var glyphsLength = glyphs.length;
 | |
|       var textLayer = this.textLayer;
 | |
|       var geom;
 | |
|       var textSelection = textLayer && !skipTextSelection ? true : false;
 | |
|       var canvasWidth = 0.0;
 | |
|       var vertical = font.vertical;
 | |
|       var defaultVMetrics = font.defaultVMetrics;
 | |
| 
 | |
|       // Type3 fonts - each glyph is a "mini-PDF"
 | |
|       if (font.coded) {
 | |
|         ctx.save();
 | |
|         ctx.transform.apply(ctx, current.textMatrix);
 | |
|         ctx.translate(current.x, current.y);
 | |
| 
 | |
|         ctx.scale(textHScale, 1);
 | |
| 
 | |
|         if (textSelection) {
 | |
|           this.save();
 | |
|           ctx.scale(1, -1);
 | |
|           geom = this.createTextGeometry();
 | |
|           this.restore();
 | |
|         }
 | |
|         for (var i = 0; i < glyphsLength; ++i) {
 | |
| 
 | |
|           var glyph = glyphs[i];
 | |
|           if (glyph === null) {
 | |
|             // word break
 | |
|             this.ctx.translate(wordSpacing, 0);
 | |
|             current.x += wordSpacing * textHScale;
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           this.processingType3 = glyph;
 | |
|           this.save();
 | |
|           ctx.scale(fontSize, fontSize);
 | |
|           ctx.transform.apply(ctx, fontMatrix);
 | |
|           this.executeOperatorList(glyph.operatorList);
 | |
|           this.restore();
 | |
| 
 | |
|           var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
 | |
|           var width = (transformed[0] * fontSize + charSpacing) *
 | |
|                       current.fontDirection;
 | |
| 
 | |
|           ctx.translate(width, 0);
 | |
|           current.x += width * textHScale;
 | |
| 
 | |
|           canvasWidth += width;
 | |
|         }
 | |
|         ctx.restore();
 | |
|         this.processingType3 = null;
 | |
|       } else {
 | |
|         ctx.save();
 | |
|         this.applyTextTransforms();
 | |
| 
 | |
|         var lineWidth = current.lineWidth;
 | |
|         var a1 = current.textMatrix[0], b1 = current.textMatrix[1];
 | |
|         var scale = Math.sqrt(a1 * a1 + b1 * b1);
 | |
|         if (scale === 0 || lineWidth === 0)
 | |
|           lineWidth = this.getSinglePixelWidth();
 | |
|         else
 | |
|           lineWidth /= scale;
 | |
| 
 | |
|         if (textSelection)
 | |
|           geom = this.createTextGeometry();
 | |
| 
 | |
|         if (fontSizeScale != 1.0) {
 | |
|           ctx.scale(fontSizeScale, fontSizeScale);
 | |
|           lineWidth /= fontSizeScale;
 | |
|         }
 | |
| 
 | |
|         ctx.lineWidth = lineWidth;
 | |
| 
 | |
|         var x = 0;
 | |
|         for (var i = 0; i < glyphsLength; ++i) {
 | |
|           var glyph = glyphs[i];
 | |
|           if (glyph === null) {
 | |
|             // word break
 | |
|             x += current.fontDirection * wordSpacing;
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           var restoreNeeded = false;
 | |
|           var character = glyph.fontChar;
 | |
|           var vmetric = glyph.vmetric || defaultVMetrics;
 | |
|           if (vertical) {
 | |
|             var vx = glyph.vmetric ? vmetric[1] : glyph.width * 0.5;
 | |
|             vx = -vx * fontSize * current.fontMatrix[0];
 | |
|             var vy = vmetric[2] * fontSize * current.fontMatrix[0];
 | |
|           }
 | |
|           var width = vmetric ? -vmetric[0] : glyph.width;
 | |
|           var charWidth = width * fontSize * current.fontMatrix[0] +
 | |
|                           charSpacing * current.fontDirection;
 | |
|           var accent = glyph.accent;
 | |
| 
 | |
|           var scaledX, scaledY, scaledAccentX, scaledAccentY;
 | |
|           if (!glyph.disabled) {
 | |
|             if (vertical) {
 | |
|               scaledX = vx / fontSizeScale;
 | |
|               scaledY = (x + vy) / fontSizeScale;
 | |
|             } else {
 | |
|               scaledX = x / fontSizeScale;
 | |
|               scaledY = 0;
 | |
|             }
 | |
| 
 | |
|             if (font.remeasure && width > 0) {
 | |
|               // some standard fonts may not have the exact width, trying to
 | |
|               // rescale per character
 | |
|               var measuredWidth = ctx.measureText(character).width * 1000 /
 | |
|                 current.fontSize * current.fontSizeScale;
 | |
|               var characterScaleX = width / measuredWidth;
 | |
|               restoreNeeded = true;
 | |
|               ctx.save();
 | |
|               ctx.scale(characterScaleX, 1);
 | |
|               scaledX /= characterScaleX;
 | |
|               if (accent) {
 | |
|                 scaledAccentX /= characterScaleX;
 | |
|               }
 | |
|             }
 | |
| 
 | |
|             this.paintChar(character, scaledX, scaledY);
 | |
|             if (accent) {
 | |
|               scaledAccentX = scaledX + accent.offset.x / fontSizeScale;
 | |
|               scaledAccentY = scaledY - accent.offset.y / fontSizeScale;
 | |
|               this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY);
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           x += charWidth;
 | |
| 
 | |
|           canvasWidth += charWidth;
 | |
| 
 | |
|           if (restoreNeeded) {
 | |
|             ctx.restore();
 | |
|           }
 | |
|         }
 | |
|         if (vertical) {
 | |
|           current.y -= x * textHScale;
 | |
|         } else {
 | |
|           current.x += x * textHScale;
 | |
|         }
 | |
|         ctx.restore();
 | |
|       }
 | |
| 
 | |
|       if (textSelection) {
 | |
|         geom.canvasWidth = canvasWidth;
 | |
|         if (vertical) {
 | |
|           var VERTICAL_TEXT_ROTATION = Math.PI / 2;
 | |
|           geom.angle += VERTICAL_TEXT_ROTATION;
 | |
|         }
 | |
|         this.textLayer.appendText(geom);
 | |
|       }
 | |
| 
 | |
|       return canvasWidth;
 | |
|     },
 | |
|     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
 | |
|       var ctx = this.ctx;
 | |
|       var current = this.current;
 | |
|       var font = current.font;
 | |
|       var fontSize = current.fontSize;
 | |
|       // TJ array's number is independent from fontMatrix
 | |
|       var textHScale = current.textHScale * 0.001 * current.fontDirection;
 | |
|       var arrLength = arr.length;
 | |
|       var textLayer = this.textLayer;
 | |
|       var geom;
 | |
|       var canvasWidth = 0.0;
 | |
|       var textSelection = textLayer ? true : false;
 | |
|       var vertical = font.vertical;
 | |
|       var spacingAccumulator = 0;
 | |
| 
 | |
|       if (textSelection) {
 | |
|         ctx.save();
 | |
|         this.applyTextTransforms();
 | |
|         geom = this.createTextGeometry();
 | |
|         ctx.restore();
 | |
|       }
 | |
| 
 | |
|       for (var i = 0; i < arrLength; ++i) {
 | |
|         var e = arr[i];
 | |
|         if (isNum(e)) {
 | |
|           var spacingLength = -e * fontSize * textHScale;
 | |
|           if (vertical) {
 | |
|             current.y += spacingLength;
 | |
|           } else {
 | |
|             current.x += spacingLength;
 | |
|           }
 | |
| 
 | |
|           if (textSelection)
 | |
|             spacingAccumulator += spacingLength;
 | |
|         } else {
 | |
|           var shownCanvasWidth = this.showText(e, true);
 | |
| 
 | |
|           if (textSelection) {
 | |
|             canvasWidth += spacingAccumulator + shownCanvasWidth;
 | |
|             spacingAccumulator = 0;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (textSelection) {
 | |
|         geom.canvasWidth = canvasWidth;
 | |
|         if (vertical) {
 | |
|           var VERTICAL_TEXT_ROTATION = Math.PI / 2;
 | |
|           geom.angle += VERTICAL_TEXT_ROTATION;
 | |
|         }
 | |
|         this.textLayer.appendText(geom);
 | |
|       }
 | |
|     },
 | |
|     nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
 | |
|       this.nextLine();
 | |
|       this.showText(text);
 | |
|     },
 | |
|     nextLineSetSpacingShowText:
 | |
|       function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing,
 | |
|                                                          charSpacing,
 | |
|                                                          text) {
 | |
|       this.setWordSpacing(wordSpacing);
 | |
|       this.setCharSpacing(charSpacing);
 | |
|       this.nextLineShowText(text);
 | |
|     },
 | |
| 
 | |
|     // Type3 fonts
 | |
|     setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) {
 | |
|       // We can safely ignore this since the width should be the same
 | |
|       // as the width in the Widths array.
 | |
|     },
 | |
|     setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth,
 | |
|                                                                         yWidth,
 | |
|                                                                         llx,
 | |
|                                                                         lly,
 | |
|                                                                         urx,
 | |
|                                                                         ury) {
 | |
|       // TODO According to the spec we're also suppose to ignore any operators
 | |
|       // that set color or include images while processing this type3 font.
 | |
|       this.rectangle(llx, lly, urx - llx, ury - lly);
 | |
|       this.clip();
 | |
|       this.endPath();
 | |
|     },
 | |
| 
 | |
|     // Color
 | |
|     setStrokeColorSpace: function CanvasGraphics_setStrokeColorSpace(raw) {
 | |
|       this.current.strokeColorSpace = ColorSpace.fromIR(raw);
 | |
|     },
 | |
|     setFillColorSpace: function CanvasGraphics_setFillColorSpace(raw) {
 | |
|       this.current.fillColorSpace = ColorSpace.fromIR(raw);
 | |
|     },
 | |
|     setStrokeColor: function CanvasGraphics_setStrokeColor(/*...*/) {
 | |
|       var cs = this.current.strokeColorSpace;
 | |
|       var rgbColor = cs.getRgb(arguments, 0);
 | |
|       var color = Util.makeCssRgb(rgbColor);
 | |
|       this.ctx.strokeStyle = color;
 | |
|       this.current.strokeColor = color;
 | |
|     },
 | |
|     getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR, cs) {
 | |
|       if (IR[0] == 'TilingPattern') {
 | |
|         var args = IR[1];
 | |
|         var base = cs.base;
 | |
|         var color;
 | |
|         if (base) {
 | |
|           var baseComps = base.numComps;
 | |
| 
 | |
|           color = base.getRgb(args, 0);
 | |
|         }
 | |
|         var pattern = new TilingPattern(IR, color, this.ctx, this.objs,
 | |
|                                         this.commonObjs, this.baseTransform);
 | |
|       } else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') {
 | |
|         var pattern = Pattern.shadingFromIR(IR);
 | |
|       } else {
 | |
|         error('Unkown IR type ' + IR[0]);
 | |
|       }
 | |
|       return pattern;
 | |
|     },
 | |
|     setStrokeColorN: function CanvasGraphics_setStrokeColorN(/*...*/) {
 | |
|       var cs = this.current.strokeColorSpace;
 | |
| 
 | |
|       if (cs.name == 'Pattern') {
 | |
|         this.current.strokeColor = this.getColorN_Pattern(arguments, cs);
 | |
|       } else {
 | |
|         this.setStrokeColor.apply(this, arguments);
 | |
|       }
 | |
|     },
 | |
|     setFillColor: function CanvasGraphics_setFillColor(/*...*/) {
 | |
|       var cs = this.current.fillColorSpace;
 | |
|       var rgbColor = cs.getRgb(arguments, 0);
 | |
|       var color = Util.makeCssRgb(rgbColor);
 | |
|       this.ctx.fillStyle = color;
 | |
|       this.current.fillColor = color;
 | |
|     },
 | |
|     setFillColorN: function CanvasGraphics_setFillColorN(/*...*/) {
 | |
|       var cs = this.current.fillColorSpace;
 | |
| 
 | |
|       if (cs.name == 'Pattern') {
 | |
|         this.current.fillColor = this.getColorN_Pattern(arguments, cs);
 | |
|       } else {
 | |
|         this.setFillColor.apply(this, arguments);
 | |
|       }
 | |
|     },
 | |
|     setStrokeGray: function CanvasGraphics_setStrokeGray(gray) {
 | |
|       this.current.strokeColorSpace = ColorSpace.singletons.gray;
 | |
| 
 | |
|       var rgbColor = this.current.strokeColorSpace.getRgb(arguments, 0);
 | |
|       var color = Util.makeCssRgb(rgbColor);
 | |
|       this.ctx.strokeStyle = color;
 | |
|       this.current.strokeColor = color;
 | |
|     },
 | |
|     setFillGray: function CanvasGraphics_setFillGray(gray) {
 | |
|       this.current.fillColorSpace = ColorSpace.singletons.gray;
 | |
| 
 | |
|       var rgbColor = this.current.fillColorSpace.getRgb(arguments, 0);
 | |
|       var color = Util.makeCssRgb(rgbColor);
 | |
|       this.ctx.fillStyle = color;
 | |
|       this.current.fillColor = color;
 | |
|     },
 | |
|     setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) {
 | |
|       this.current.strokeColorSpace = ColorSpace.singletons.rgb;
 | |
| 
 | |
|       var rgbColor = this.current.strokeColorSpace.getRgb(arguments, 0);
 | |
|       var color = Util.makeCssRgb(rgbColor);
 | |
|       this.ctx.strokeStyle = color;
 | |
|       this.current.strokeColor = color;
 | |
|     },
 | |
|     setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) {
 | |
|       this.current.fillColorSpace = ColorSpace.singletons.rgb;
 | |
| 
 | |
|       var rgbColor = this.current.fillColorSpace.getRgb(arguments, 0);
 | |
|       var color = Util.makeCssRgb(rgbColor);
 | |
|       this.ctx.fillStyle = color;
 | |
|       this.current.fillColor = color;
 | |
|     },
 | |
|     setStrokeCMYKColor: function CanvasGraphics_setStrokeCMYKColor(c, m, y, k) {
 | |
|       this.current.strokeColorSpace = ColorSpace.singletons.cmyk;
 | |
| 
 | |
|       var color = Util.makeCssCmyk(arguments);
 | |
|       this.ctx.strokeStyle = color;
 | |
|       this.current.strokeColor = color;
 | |
|     },
 | |
|     setFillCMYKColor: function CanvasGraphics_setFillCMYKColor(c, m, y, k) {
 | |
|       this.current.fillColorSpace = ColorSpace.singletons.cmyk;
 | |
| 
 | |
|       var color = Util.makeCssCmyk(arguments);
 | |
|       this.ctx.fillStyle = color;
 | |
|       this.current.fillColor = color;
 | |
|     },
 | |
| 
 | |
|     shadingFill: function CanvasGraphics_shadingFill(patternIR) {
 | |
|       var ctx = this.ctx;
 | |
| 
 | |
|       this.save();
 | |
|       var pattern = Pattern.shadingFromIR(patternIR);
 | |
|       ctx.fillStyle = pattern.getPattern(ctx, this);
 | |
| 
 | |
|       var inv = ctx.mozCurrentTransformInverse;
 | |
|       if (inv) {
 | |
|         var canvas = ctx.canvas;
 | |
|         var width = canvas.width;
 | |
|         var height = canvas.height;
 | |
| 
 | |
|         var bl = Util.applyTransform([0, 0], inv);
 | |
|         var br = Util.applyTransform([0, height], inv);
 | |
|         var ul = Util.applyTransform([width, 0], inv);
 | |
|         var ur = Util.applyTransform([width, height], inv);
 | |
| 
 | |
|         var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
 | |
|         var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
 | |
|         var x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
 | |
|         var y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
 | |
| 
 | |
|         this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
 | |
|       } else {
 | |
|         // HACK to draw the gradient onto an infinite rectangle.
 | |
|         // PDF gradients are drawn across the entire image while
 | |
|         // Canvas only allows gradients to be drawn in a rectangle
 | |
|         // The following bug should allow us to remove this.
 | |
|         // https://bugzilla.mozilla.org/show_bug.cgi?id=664884
 | |
| 
 | |
|         this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
 | |
|       }
 | |
| 
 | |
|       this.restore();
 | |
|     },
 | |
| 
 | |
|     // Images
 | |
|     beginInlineImage: function CanvasGraphics_beginInlineImage() {
 | |
|       error('Should not call beginInlineImage');
 | |
|     },
 | |
|     beginImageData: function CanvasGraphics_beginImageData() {
 | |
|       error('Should not call beginImageData');
 | |
|     },
 | |
| 
 | |
|     paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix,
 | |
|                                                                         bbox) {
 | |
|       this.save();
 | |
|       this.current.paintFormXObjectDepth++;
 | |
|       this.baseTransformStack.push(this.baseTransform);
 | |
| 
 | |
|       if (matrix && isArray(matrix) && 6 == matrix.length)
 | |
|         this.transform.apply(this, matrix);
 | |
| 
 | |
|       this.baseTransform = this.ctx.mozCurrentTransform;
 | |
| 
 | |
|       if (bbox && isArray(bbox) && 4 == bbox.length) {
 | |
|         var width = bbox[2] - bbox[0];
 | |
|         var height = bbox[3] - bbox[1];
 | |
|         this.rectangle(bbox[0], bbox[1], width, height);
 | |
|         this.clip();
 | |
|         this.endPath();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
 | |
|       var depth = this.current.paintFormXObjectDepth;
 | |
|       do {
 | |
|         this.restore();
 | |
|         // some pdf don't close all restores inside object
 | |
|         // closing those for them
 | |
|       } while (this.current.paintFormXObjectDepth >= depth);
 | |
|       this.baseTransform = this.baseTransformStack.pop();
 | |
|     },
 | |
| 
 | |
|     beginGroup: function CanvasGraphics_beginGroup(group) {
 | |
|       this.save();
 | |
|       var currentCtx = this.ctx;
 | |
|       // TODO non-isolated groups - according to Rik at adobe non-isolated
 | |
|       // group results aren't usually that different and they even have tools
 | |
|       // that ignore this setting. Notes from Rik on implmenting:
 | |
|       // - When you encounter an transparency group, create a new canvas with
 | |
|       // the dimensions of the bbox
 | |
|       // - copy the content from the previous canvas to the new canvas
 | |
|       // - draw as usual
 | |
|       // - remove the backdrop alpha:
 | |
|       // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha
 | |
|       // value of your transparency group and 'alphaBackdrop' the alpha of the
 | |
|       // backdrop
 | |
|       // - remove background color:
 | |
|       // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)
 | |
|       if (!group.isolated) {
 | |
|         info('TODO: Support non-isolated groups.');
 | |
|       }
 | |
| 
 | |
|       // TODO knockout - supposedly possible with the clever use of compositing
 | |
|       // modes.
 | |
|       if (group.knockout) {
 | |
|         TODO('Support knockout groups.');
 | |
|       }
 | |
| 
 | |
|       var currentTransform = currentCtx.mozCurrentTransform;
 | |
|       if (group.matrix) {
 | |
|         currentCtx.transform.apply(currentCtx, group.matrix);
 | |
|       }
 | |
|       assert(group.bbox, 'Bounding box is required.');
 | |
| 
 | |
|       // Based on the current transform figure out how big the bounding box
 | |
|       // will actually be.
 | |
|       var bounds = Util.getAxialAlignedBoundingBox(
 | |
|                     group.bbox,
 | |
|                     currentCtx.mozCurrentTransform);
 | |
|       // Clip the bounding box to the current canvas.
 | |
|       var canvasBounds = [0,
 | |
|                           0,
 | |
|                           currentCtx.canvas.width,
 | |
|                           currentCtx.canvas.height];
 | |
|       bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
 | |
|       // Use ceil in case we're between sizes so we don't create canvas that is
 | |
|       // too small and make the canvas at least 1x1 pixels.
 | |
|       var drawnWidth = Math.max(Math.ceil(bounds[2] - bounds[0]), 1);
 | |
|       var drawnHeight = Math.max(Math.ceil(bounds[3] - bounds[1]), 1);
 | |
| 
 | |
|       var scratchCanvas = CachedCanvases.getCanvas(
 | |
|         'groupAt' + this.groupLevel, drawnWidth, drawnHeight, true);
 | |
|       var groupCtx = scratchCanvas.context;
 | |
|       // Since we created a new canvas that is just the size of the bounding box
 | |
|       // we have to translate the group ctx.
 | |
|       var offsetX = bounds[0];
 | |
|       var offsetY = bounds[1];
 | |
|       groupCtx.translate(-offsetX, -offsetY);
 | |
|       groupCtx.transform.apply(groupCtx, currentTransform);
 | |
| 
 | |
|       // Setup the current ctx so when the group is popped we draw it the right
 | |
|       // location.
 | |
|       currentCtx.setTransform(1, 0, 0, 1, 0, 0);
 | |
|       currentCtx.translate(offsetX, offsetY);
 | |
|       // The transparency group inherits all off the current graphics state
 | |
|       // except the blend mode, soft mask, and alpha constants.
 | |
|       copyCtxState(currentCtx, groupCtx);
 | |
|       this.ctx = groupCtx;
 | |
|       this.setGState([
 | |
|         ['SMask', 'None'],
 | |
|         ['BM', 'Normal'],
 | |
|         ['ca', 1],
 | |
|         ['CA', 1]
 | |
|       ]);
 | |
|       this.groupStack.push(currentCtx);
 | |
|       this.groupLevel++;
 | |
|     },
 | |
| 
 | |
|     endGroup: function CanvasGraphics_endGroup(group) {
 | |
|       this.groupLevel--;
 | |
|       var groupCtx = this.ctx;
 | |
|       this.ctx = this.groupStack.pop();
 | |
|       // Turn off image smoothing to avoid sub pixel interpolation which can
 | |
|       // look kind of blurry for some pdfs.
 | |
|       if ('imageSmoothingEnabled' in this.ctx) {
 | |
|         this.ctx.imageSmoothingEnabled = false;
 | |
|       } else {
 | |
|         this.ctx.mozImageSmoothingEnabled = false;
 | |
|       }
 | |
|       this.ctx.drawImage(groupCtx.canvas, 0, 0);
 | |
|       this.restore();
 | |
|     },
 | |
| 
 | |
|     beginAnnotations: function CanvasGraphics_beginAnnotations() {
 | |
|       this.save();
 | |
|       this.current = new CanvasExtraState();
 | |
|     },
 | |
| 
 | |
|     endAnnotations: function CanvasGraphics_endAnnotations() {
 | |
|       this.restore();
 | |
|     },
 | |
| 
 | |
|     beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform,
 | |
|                                                              matrix) {
 | |
|       this.save();
 | |
| 
 | |
|       if (rect && isArray(rect) && 4 == rect.length) {
 | |
|         var width = rect[2] - rect[0];
 | |
|         var height = rect[3] - rect[1];
 | |
|         this.rectangle(rect[0], rect[1], width, height);
 | |
|         this.clip();
 | |
|         this.endPath();
 | |
|       }
 | |
| 
 | |
|       this.transform.apply(this, transform);
 | |
|       this.transform.apply(this, matrix);
 | |
|     },
 | |
| 
 | |
|     endAnnotation: function CanvasGraphics_endAnnotation() {
 | |
|       this.restore();
 | |
|     },
 | |
| 
 | |
|     paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
 | |
|       var domImage = this.objs.get(objId);
 | |
|       if (!domImage) {
 | |
|         error('Dependent image isn\'t ready yet');
 | |
|       }
 | |
| 
 | |
|       this.save();
 | |
| 
 | |
|       var ctx = this.ctx;
 | |
|       // scale the image to the unit square
 | |
|       ctx.scale(1 / w, -1 / h);
 | |
| 
 | |
|       ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
 | |
|                     0, -h, w, h);
 | |
|       if (this.imageLayer) {
 | |
|         var currentTransform = ctx.mozCurrentTransformInverse;
 | |
|         var position = this.getCanvasPosition(0, 0);
 | |
|         this.imageLayer.appendImage({
 | |
|           objId: objId,
 | |
|           left: position[0],
 | |
|           top: position[1],
 | |
|           width: w / currentTransform[0],
 | |
|           height: h / currentTransform[3]
 | |
|         });
 | |
|       }
 | |
|       this.restore();
 | |
|     },
 | |
| 
 | |
|     paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
 | |
|       var ctx = this.ctx;
 | |
|       var width = img.width, height = img.height;
 | |
| 
 | |
|       var glyph = this.processingType3;
 | |
| 
 | |
|       if (COMPILE_TYPE3_GLYPHS && glyph && !('compiled' in glyph)) {
 | |
|         var MAX_SIZE_TO_COMPILE = 1000;
 | |
|         if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) {
 | |
|           glyph.compiled =
 | |
|             compileType3Glyph({data: img.data, width: width, height: height});
 | |
|         } else {
 | |
|           glyph.compiled = null;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (glyph && glyph.compiled) {
 | |
|         glyph.compiled(ctx);
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
 | |
|       var maskCtx = maskCanvas.context;
 | |
|       maskCtx.save();
 | |
| 
 | |
|       putBinaryImageData(maskCtx, img);
 | |
| 
 | |
|       maskCtx.globalCompositeOperation = 'source-in';
 | |
| 
 | |
|       var fillColor = this.current.fillColor;
 | |
|       maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
 | |
|                           fillColor.type === 'Pattern') ?
 | |
|                           fillColor.getPattern(maskCtx, this) : fillColor;
 | |
|       maskCtx.fillRect(0, 0, width, height);
 | |
| 
 | |
|       maskCtx.restore();
 | |
| 
 | |
|       this.paintInlineImageXObject(maskCanvas.canvas);
 | |
|     },
 | |
| 
 | |
|     paintImageMaskXObjectGroup:
 | |
|       function CanvasGraphics_paintImageMaskXObjectGroup(images) {
 | |
|       var ctx = this.ctx;
 | |
| 
 | |
|       for (var i = 0, ii = images.length; i < ii; i++) {
 | |
|         var image = images[i];
 | |
|         var width = image.width, height = image.height;
 | |
| 
 | |
|         var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height);
 | |
|         var maskCtx = maskCanvas.context;
 | |
|         maskCtx.save();
 | |
| 
 | |
|         putBinaryImageData(maskCtx, image);
 | |
| 
 | |
|         maskCtx.globalCompositeOperation = 'source-in';
 | |
| 
 | |
|         var fillColor = this.current.fillColor;
 | |
|         maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') &&
 | |
|                             fillColor.type === 'Pattern') ?
 | |
|                             fillColor.getPattern(maskCtx, this) : fillColor;
 | |
|         maskCtx.fillRect(0, 0, width, height);
 | |
| 
 | |
|         maskCtx.restore();
 | |
| 
 | |
|         ctx.save();
 | |
|         ctx.transform.apply(ctx, image.transform);
 | |
|         ctx.scale(1, -1);
 | |
|         ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
 | |
|                       0, -1, 1, 1);
 | |
|         ctx.restore();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
 | |
|       var imgData = this.objs.get(objId);
 | |
|       if (!imgData)
 | |
|         error('Dependent image isn\'t ready yet');
 | |
| 
 | |
|       this.paintInlineImageXObject(imgData);
 | |
|     },
 | |
| 
 | |
|     paintInlineImageXObject:
 | |
|       function CanvasGraphics_paintInlineImageXObject(imgData) {
 | |
|       var width = imgData.width;
 | |
|       var height = imgData.height;
 | |
|       var ctx = this.ctx;
 | |
| 
 | |
|       this.save();
 | |
|       // scale the image to the unit square
 | |
|       ctx.scale(1 / width, -1 / height);
 | |
| 
 | |
|       var currentTransform = ctx.mozCurrentTransformInverse;
 | |
|       var a = currentTransform[0], b = currentTransform[1];
 | |
|       var widthScale = Math.max(Math.sqrt(a * a + b * b), 1);
 | |
|       var c = currentTransform[2], d = currentTransform[3];
 | |
|       var heightScale = Math.max(Math.sqrt(c * c + d * d), 1);
 | |
| 
 | |
|       var imgToPaint;
 | |
|       // instanceof HTMLElement does not work in jsdom node.js module
 | |
|       if (imgData instanceof HTMLElement || !imgData.data) {
 | |
|         imgToPaint = imgData;
 | |
|       } else {
 | |
|         var tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height);
 | |
|         var tmpCtx = tmpCanvas.context;
 | |
|         putBinaryImageData(tmpCtx, imgData);
 | |
|         imgToPaint = tmpCanvas.canvas;
 | |
|       }
 | |
| 
 | |
|       var paintWidth = width, paintHeight = height;
 | |
|       var tmpCanvasId = 'prescale1';
 | |
|       // Vertial or horizontal scaling shall not be more than 2 to not loose the
 | |
|       // pixels during drawImage operation, painting on the temporary canvas(es)
 | |
|       // that are twice smaller in size
 | |
|       while ((widthScale > 2 && paintWidth > 1) ||
 | |
|              (heightScale > 2 && paintHeight > 1)) {
 | |
|         var newWidth = paintWidth, newHeight = paintHeight;
 | |
|         if (widthScale > 2 && paintWidth > 1) {
 | |
|           newWidth = Math.ceil(paintWidth / 2);
 | |
|           widthScale /= paintWidth / newWidth;
 | |
|         }
 | |
|         if (heightScale > 2 && paintHeight > 1) {
 | |
|           newHeight = Math.ceil(paintHeight / 2);
 | |
|           heightScale /= paintHeight / newHeight;
 | |
|         }
 | |
|         var tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId,
 | |
|                                                  newWidth, newHeight);
 | |
|         tmpCtx = tmpCanvas.context;
 | |
|         tmpCtx.clearRect(0, 0, newWidth, newHeight);
 | |
|         tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
 | |
|                                      0, 0, newWidth, newHeight);
 | |
|         imgToPaint = tmpCanvas.canvas;
 | |
|         paintWidth = newWidth;
 | |
|         paintHeight = newHeight;
 | |
|         tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1';
 | |
|       }
 | |
|       ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
 | |
|                                 0, -height, width, height);
 | |
| 
 | |
|       if (this.imageLayer) {
 | |
|         var position = this.getCanvasPosition(0, -height);
 | |
|         this.imageLayer.appendImage({
 | |
|           imgData: imgData,
 | |
|           left: position[0],
 | |
|           top: position[1],
 | |
|           width: width / currentTransform[0],
 | |
|           height: height / currentTransform[3]
 | |
|         });
 | |
|       }
 | |
|       this.restore();
 | |
|     },
 | |
| 
 | |
|     paintInlineImageXObjectGroup:
 | |
|       function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
 | |
|       var ctx = this.ctx;
 | |
|       var w = imgData.width;
 | |
|       var h = imgData.height;
 | |
| 
 | |
|       var tmpCanvas = CachedCanvases.getCanvas('inlineImage', w, h);
 | |
|       var tmpCtx = tmpCanvas.context;
 | |
|       putBinaryImageData(tmpCtx, imgData);
 | |
| 
 | |
|       for (var i = 0, ii = map.length; i < ii; i++) {
 | |
|         var entry = map[i];
 | |
|         ctx.save();
 | |
|         ctx.transform.apply(ctx, entry.transform);
 | |
|         ctx.scale(1, -1);
 | |
|         ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h,
 | |
|                       0, -1, 1, 1);
 | |
|         if (this.imageLayer) {
 | |
|           var position = this.getCanvasPosition(entry.x, entry.y);
 | |
|           this.imageLayer.appendImage({
 | |
|             imgData: imgData,
 | |
|             left: position[0],
 | |
|             top: position[1],
 | |
|             width: w,
 | |
|             height: h
 | |
|           });
 | |
|         }
 | |
|         ctx.restore();
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     // Marked content
 | |
| 
 | |
|     markPoint: function CanvasGraphics_markPoint(tag) {
 | |
|       // TODO Marked content.
 | |
|     },
 | |
|     markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
 | |
|       // TODO Marked content.
 | |
|     },
 | |
|     beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
 | |
|       // TODO Marked content.
 | |
|     },
 | |
|     beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
 | |
|                                         tag, properties) {
 | |
|       // TODO Marked content.
 | |
|     },
 | |
|     endMarkedContent: function CanvasGraphics_endMarkedContent() {
 | |
|       // TODO Marked content.
 | |
|     },
 | |
| 
 | |
|     // Compatibility
 | |
| 
 | |
|     beginCompat: function CanvasGraphics_beginCompat() {
 | |
|       // TODO ignore undefined operators (should we do that anyway?)
 | |
|     },
 | |
|     endCompat: function CanvasGraphics_endCompat() {
 | |
|       // TODO stop ignoring undefined operators
 | |
|     },
 | |
| 
 | |
|     // Helper functions
 | |
| 
 | |
|     consumePath: function CanvasGraphics_consumePath() {
 | |
|       if (this.pendingClip) {
 | |
|         if (this.pendingClip == EO_CLIP) {
 | |
|           if ('mozFillRule' in this.ctx) {
 | |
|             this.ctx.mozFillRule = 'evenodd';
 | |
|             this.ctx.clip();
 | |
|             this.ctx.mozFillRule = 'nonzero';
 | |
|           } else {
 | |
|             try {
 | |
|               this.ctx.clip('evenodd');
 | |
|             } catch (ex) {
 | |
|               // shouldn't really happen, but browsers might think differently
 | |
|               this.ctx.clip();
 | |
|             }
 | |
|           }
 | |
|         } else {
 | |
|           this.ctx.clip();
 | |
|         }
 | |
|         this.pendingClip = null;
 | |
|       }
 | |
|       this.ctx.beginPath();
 | |
|     },
 | |
|     getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
 | |
|       var inverse = this.ctx.mozCurrentTransformInverse;
 | |
|       // max of the current horizontal and vertical scale
 | |
|       return Math.sqrt(Math.max(
 | |
|         (inverse[0] * inverse[0] + inverse[1] * inverse[1]),
 | |
|         (inverse[2] * inverse[2] + inverse[3] * inverse[3])));
 | |
|     },
 | |
|     getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) {
 | |
|         var transform = this.ctx.mozCurrentTransform;
 | |
|         return [
 | |
|           transform[0] * x + transform[2] * y + transform[4],
 | |
|           transform[1] * x + transform[3] * y + transform[5]
 | |
|         ];
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   for (var op in OPS) {
 | |
|     CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
 | |
|   }
 | |
| 
 | |
|   return CanvasGraphics;
 | |
| })();
 | |
| 
 | |
| 
 | |
| 
 | |
| PDFJS.disableFontFace = false;
 | |
| 
 | |
| var FontLoader = {
 | |
|   insertRule: function fontLoaderInsertRule(rule) {
 | |
|     var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
 | |
|     if (!styleElement) {
 | |
|         styleElement = document.createElement('style');
 | |
|         styleElement.id = 'PDFJS_FONT_STYLE_TAG';
 | |
|         document.documentElement.getElementsByTagName('head')[0].appendChild(
 | |
|           styleElement);
 | |
|     }
 | |
| 
 | |
|     var styleSheet = styleElement.sheet;
 | |
|     styleSheet.insertRule(rule, styleSheet.cssRules.length);
 | |
|   },
 | |
|   clear: function fontLoaderClear() {
 | |
|     var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
 | |
|     if (styleElement) {
 | |
|       styleElement.parentNode.removeChild(styleElement);
 | |
|     }
 | |
|   },
 | |
|   get loadTestFont() {
 | |
|     // This is a CFF font with 1 glyph for '.' that fills its entire width and
 | |
|     // height.
 | |
|     return shadow(this, 'loadTestFont', atob(
 | |
|       'T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQ' +
 | |
|       'AABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwA' +
 | |
|       'AAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbm' +
 | |
|       'FtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAA' +
 | |
|       'AADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6A' +
 | |
|       'ABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAA' +
 | |
|       'MQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAA' +
 | |
|       'AAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAA' +
 | |
|       'AAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQ' +
 | |
|       'AAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMA' +
 | |
|       'AQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAA' +
 | |
|       'EAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAA' +
 | |
|       'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAA' +
 | |
|       'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
 | |
|       'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
 | |
|       'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
 | |
|       'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA' +
 | |
|       'AAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgc' +
 | |
|       'A/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF' +
 | |
|       'hYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQA' +
 | |
|       'AAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAg' +
 | |
|       'ABAAAAAAAAAAAD6AAAAAAAAA=='
 | |
|     ));
 | |
|   },
 | |
| 
 | |
|   loadTestFontId: 0,
 | |
| 
 | |
|   loadingContext: {
 | |
|     requests: [],
 | |
|     nextRequestId: 0
 | |
|   },
 | |
| 
 | |
|   isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() {
 | |
|     if (isWorker)
 | |
|       return false;
 | |
| 
 | |
|     // User agent string sniffing is bad, but there is no reliable way to tell
 | |
|     // if font is fully loaded and ready to be used with canvas.
 | |
|     var userAgent = window.navigator.userAgent;
 | |
|     var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent);
 | |
|     if (m && m[1] >= 14)
 | |
|       return true;
 | |
|     // TODO other browsers
 | |
|     return false;
 | |
|   })(),
 | |
| 
 | |
|   bind: function fontLoaderBind(fonts, callback) {
 | |
|     assert(!isWorker, 'bind() shall be called from main thread');
 | |
| 
 | |
|     var rules = [], fontsToLoad = [];
 | |
|     for (var i = 0, ii = fonts.length; i < ii; i++) {
 | |
|       var font = fonts[i];
 | |
| 
 | |
|       // Add the font to the DOM only once or skip if the font
 | |
|       // is already loaded.
 | |
|       if (font.attached || font.loading === false) {
 | |
|         continue;
 | |
|       }
 | |
|       font.attached = true;
 | |
| 
 | |
|       var rule = font.bindDOM();
 | |
|       if (rule) {
 | |
|         rules.push(rule);
 | |
|         fontsToLoad.push(font);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     var request = FontLoader.queueLoadingCallback(callback);
 | |
|     if (rules.length > 0 && !this.isSyncFontLoadingSupported) {
 | |
|       FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request);
 | |
|     } else {
 | |
|       request.complete();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) {
 | |
|     function LoadLoader_completeRequest() {
 | |
|       assert(!request.end, 'completeRequest() cannot be called twice');
 | |
|       request.end = Date.now();
 | |
| 
 | |
|       // sending all completed requests in order how they were queued
 | |
|       while (context.requests.length > 0 && context.requests[0].end) {
 | |
|         var otherRequest = context.requests.shift();
 | |
|         setTimeout(otherRequest.callback, 0);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     var context = FontLoader.loadingContext;
 | |
|     var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++);
 | |
|     var request = {
 | |
|       id: requestId,
 | |
|       complete: LoadLoader_completeRequest,
 | |
|       callback: callback,
 | |
|       started: Date.now()
 | |
|     };
 | |
|     context.requests.push(request);
 | |
|     return request;
 | |
|   },
 | |
| 
 | |
|   prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules,
 | |
|                                                                 fonts,
 | |
|                                                                 request) {
 | |
|       /** Hack begin */
 | |
|       // There's currently no event when a font has finished downloading so the
 | |
|       // following code is a dirty hack to 'guess' when a font is
 | |
|       // ready. It's assumed fonts are loaded in order, so add a known test
 | |
|       // font after the desired fonts and then test for the loading of that
 | |
|       // test font.
 | |
| 
 | |
|       function int32(data, offset) {
 | |
|         return (data.charCodeAt(offset) << 24) |
 | |
|                (data.charCodeAt(offset + 1) << 16) |
 | |
|                (data.charCodeAt(offset + 2) << 8) |
 | |
|                (data.charCodeAt(offset + 3) & 0xff);
 | |
|       }
 | |
| 
 | |
|       function string32(value) {
 | |
|         return String.fromCharCode((value >> 24) & 0xff) +
 | |
|                String.fromCharCode((value >> 16) & 0xff) +
 | |
|                String.fromCharCode((value >> 8) & 0xff) +
 | |
|                String.fromCharCode(value & 0xff);
 | |
|       }
 | |
| 
 | |
|       function spliceString(s, offset, remove, insert) {
 | |
|         var chunk1 = data.substr(0, offset);
 | |
|         var chunk2 = data.substr(offset + remove);
 | |
|         return chunk1 + insert + chunk2;
 | |
|       }
 | |
| 
 | |
|       var i, ii;
 | |
| 
 | |
|       var canvas = document.createElement('canvas');
 | |
|       canvas.width = 1;
 | |
|       canvas.height = 1;
 | |
|       var ctx = canvas.getContext('2d');
 | |
| 
 | |
|       var called = 0;
 | |
|       function isFontReady(name, callback) {
 | |
|         called++;
 | |
|         // With setTimeout clamping this gives the font ~100ms to load.
 | |
|         if(called > 30) {
 | |
|           warn('Load test font never loaded.');
 | |
|           callback();
 | |
|           return;
 | |
|         }
 | |
|         ctx.font = '30px ' + name;
 | |
|         ctx.fillText('.', 0, 20);
 | |
|         var imageData = ctx.getImageData(0, 0, 1, 1);
 | |
|         if (imageData.data[3] > 0) {
 | |
|           callback();
 | |
|           return;
 | |
|         }
 | |
|         setTimeout(isFontReady.bind(null, name, callback));
 | |
|       }
 | |
| 
 | |
|       var loadTestFontId = 'lt' + Date.now() + this.loadTestFontId++;
 | |
|       // Chromium seems to cache fonts based on a hash of the actual font data,
 | |
|       // so the font must be modified for each load test else it will appear to
 | |
|       // be loaded already.
 | |
|       // TODO: This could maybe be made faster by avoiding the btoa of the full
 | |
|       // font by splitting it in chunks before hand and padding the font id.
 | |
|       var data = this.loadTestFont;
 | |
|       var COMMENT_OFFSET = 976; // has to be on 4 byte boundary (for checksum)
 | |
|       data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length,
 | |
|                           loadTestFontId);
 | |
|       // CFF checksum is important for IE, adjusting it
 | |
|       var CFF_CHECKSUM_OFFSET = 16;
 | |
|       var XXXX_VALUE = 0x58585858; // the "comment" filled with 'X'
 | |
|       var checksum = int32(data, CFF_CHECKSUM_OFFSET);
 | |
|       for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {
 | |
|         checksum = (checksum - XXXX_VALUE + int32(loadTestFontId, i)) | 0;
 | |
|       }
 | |
|       if (i < loadTestFontId.length) { // align to 4 bytes boundary
 | |
|         checksum = (checksum - XXXX_VALUE +
 | |
|                     int32(loadTestFontId + 'XXX', i)) | 0;
 | |
|       }
 | |
|       data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum));
 | |
| 
 | |
|       var url = 'url(data:font/opentype;base64,' + btoa(data) + ');';
 | |
|       var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' +
 | |
|                  url + '}';
 | |
|       FontLoader.insertRule(rule);
 | |
| 
 | |
|       var names = [];
 | |
|       for (i = 0, ii = fonts.length; i < ii; i++) {
 | |
|         names.push(fonts[i].loadedName);
 | |
|       }
 | |
|       names.push(loadTestFontId);
 | |
| 
 | |
|       var div = document.createElement('div');
 | |
|       div.setAttribute('style',
 | |
|                        'visibility: hidden;' +
 | |
|                        'width: 10px; height: 10px;' +
 | |
|                        'position: absolute; top: 0px; left: 0px;');
 | |
|       for (i = 0, ii = names.length; i < ii; ++i) {
 | |
|         var span = document.createElement('span');
 | |
|         span.textContent = 'Hi';
 | |
|         span.style.fontFamily = names[i];
 | |
|         div.appendChild(span);
 | |
|       }
 | |
|       document.body.appendChild(div);
 | |
| 
 | |
|       isFontReady(loadTestFontId, function() {
 | |
|         document.body.removeChild(div);
 | |
|         request.complete();
 | |
|       });
 | |
|       /** Hack end */
 | |
|   }
 | |
| };
 | |
| 
 | |
| var FontFace = (function FontFaceClosure() {
 | |
|   function FontFace(name, file, properties) {
 | |
|     this.compiledGlyphs = {};
 | |
|     if (arguments.length === 1) {
 | |
|       // importing translated data
 | |
|       var data = arguments[0];
 | |
|       for (var i in data) {
 | |
|         this[i] = data[i];
 | |
|       }
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   FontFace.prototype = {
 | |
|     bindDOM: function FontFace_bindDOM() {
 | |
|       if (!this.data)
 | |
|         return null;
 | |
| 
 | |
|       if (PDFJS.disableFontFace) {
 | |
|         this.disableFontFace = true;
 | |
|         return null;
 | |
|       }
 | |
| 
 | |
|       var data = bytesToString(this.data);
 | |
|       var fontName = this.loadedName;
 | |
| 
 | |
|       // Add the font-face rule to the document
 | |
|       var url = ('url(data:' + this.mimetype + ';base64,' +
 | |
|                  window.btoa(data) + ');');
 | |
|       var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
 | |
| 
 | |
|       FontLoader.insertRule(rule);
 | |
| 
 | |
|       if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
 | |
|           globalScope['FontInspector'].enabled)
 | |
|         globalScope['FontInspector'].fontAdded(this, url);
 | |
| 
 | |
|       return rule;
 | |
|     },
 | |
|     getPathGenerator: function (objs, character) {
 | |
|       if (!(character in this.compiledGlyphs)) {
 | |
|         var js = objs.get(this.loadedName + '_path_' + character);
 | |
|         /*jshint -W054 */
 | |
|         this.compiledGlyphs[character] = new Function('c', 'size', js);
 | |
|       }
 | |
|       return this.compiledGlyphs[character];
 | |
|     }
 | |
|   };
 | |
|   return FontFace;
 | |
| })();
 | |
| 
 | |
| 
 | |
| }).call((typeof window === 'undefined') ? this : window);
 | |
| 
 | |
| if (!PDFJS.workerSrc && typeof document !== 'undefined') {
 | |
|   // workerSrc is not set -- using last script url to define default location
 | |
|   PDFJS.workerSrc = (function () {
 | |
|     'use strict';
 | |
|     var scriptTagContainer = document.body ||
 | |
|                              document.getElementsByTagName('head')[0];
 | |
|     var pdfjsSrc = scriptTagContainer.lastChild.src;
 | |
|     return pdfjsSrc && pdfjsSrc.replace(/\.js$/i, '.worker.js');
 | |
|   })();
 | |
| }
 | |
| 
 | |
| 
 |