aboutsummaryrefslogtreecommitdiff
path: root/static/dist/js/pdf.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/dist/js/pdf.js')
-rw-r--r--static/dist/js/pdf.js11515
1 files changed, 11515 insertions, 0 deletions
diff --git a/static/dist/js/pdf.js b/static/dist/js/pdf.js
new file mode 100644
index 00000000..2d3c3c83
--- /dev/null
+++ b/static/dist/js/pdf.js
@@ -0,0 +1,11515 @@
1/* Copyright 2012 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15/* jshint globalstrict: false */
16/* umdutils ignore */
17
18(function (root, factory) {
19 'use strict';
20 if (typeof define === 'function' && define.amd) {
21define('pdfjs-dist/build/pdf', ['exports'], factory);
22 } else if (typeof exports !== 'undefined') {
23 factory(exports);
24 } else {
25factory((root.pdfjsDistBuildPdf = {}));
26 }
27}(this, function (exports) {
28 // Use strict in our context only - users might not want it
29 'use strict';
30
31var pdfjsVersion = '1.6.210';
32var pdfjsBuild = '4ce2356';
33
34 var pdfjsFilePath =
35 typeof document !== 'undefined' && document.currentScript ?
36 document.currentScript.src : null;
37
38 var pdfjsLibs = {};
39
40 (function pdfjsWrapper() {
41
42
43
44(function (root, factory) {
45 {
46 factory((root.pdfjsSharedUtil = {}));
47 }
48}(this, function (exports) {
49
50var globalScope = (typeof window !== 'undefined') ? window :
51 (typeof global !== 'undefined') ? global :
52 (typeof self !== 'undefined') ? self : this;
53
54var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
55
56var TextRenderingMode = {
57 FILL: 0,
58 STROKE: 1,
59 FILL_STROKE: 2,
60 INVISIBLE: 3,
61 FILL_ADD_TO_PATH: 4,
62 STROKE_ADD_TO_PATH: 5,
63 FILL_STROKE_ADD_TO_PATH: 6,
64 ADD_TO_PATH: 7,
65 FILL_STROKE_MASK: 3,
66 ADD_TO_PATH_FLAG: 4
67};
68
69var ImageKind = {
70 GRAYSCALE_1BPP: 1,
71 RGB_24BPP: 2,
72 RGBA_32BPP: 3
73};
74
75var AnnotationType = {
76 TEXT: 1,
77 LINK: 2,
78 FREETEXT: 3,
79 LINE: 4,
80 SQUARE: 5,
81 CIRCLE: 6,
82 POLYGON: 7,
83 POLYLINE: 8,
84 HIGHLIGHT: 9,
85 UNDERLINE: 10,
86 SQUIGGLY: 11,
87 STRIKEOUT: 12,
88 STAMP: 13,
89 CARET: 14,
90 INK: 15,
91 POPUP: 16,
92 FILEATTACHMENT: 17,
93 SOUND: 18,
94 MOVIE: 19,
95 WIDGET: 20,
96 SCREEN: 21,
97 PRINTERMARK: 22,
98 TRAPNET: 23,
99 WATERMARK: 24,
100 THREED: 25,
101 REDACT: 26
102};
103
104var AnnotationFlag = {
105 INVISIBLE: 0x01,
106 HIDDEN: 0x02,
107 PRINT: 0x04,
108 NOZOOM: 0x08,
109 NOROTATE: 0x10,
110 NOVIEW: 0x20,
111 READONLY: 0x40,
112 LOCKED: 0x80,
113 TOGGLENOVIEW: 0x100,
114 LOCKEDCONTENTS: 0x200
115};
116
117var AnnotationFieldFlag = {
118 READONLY: 0x0000001,
119 REQUIRED: 0x0000002,
120 NOEXPORT: 0x0000004,
121 MULTILINE: 0x0001000,
122 PASSWORD: 0x0002000,
123 NOTOGGLETOOFF: 0x0004000,
124 RADIO: 0x0008000,
125 PUSHBUTTON: 0x0010000,
126 COMBO: 0x0020000,
127 EDIT: 0x0040000,
128 SORT: 0x0080000,
129 FILESELECT: 0x0100000,
130 MULTISELECT: 0x0200000,
131 DONOTSPELLCHECK: 0x0400000,
132 DONOTSCROLL: 0x0800000,
133 COMB: 0x1000000,
134 RICHTEXT: 0x2000000,
135 RADIOSINUNISON: 0x2000000,
136 COMMITONSELCHANGE: 0x4000000,
137};
138
139var AnnotationBorderStyleType = {
140 SOLID: 1,
141 DASHED: 2,
142 BEVELED: 3,
143 INSET: 4,
144 UNDERLINE: 5
145};
146
147var StreamType = {
148 UNKNOWN: 0,
149 FLATE: 1,
150 LZW: 2,
151 DCT: 3,
152 JPX: 4,
153 JBIG: 5,
154 A85: 6,
155 AHX: 7,
156 CCF: 8,
157 RL: 9
158};
159
160var FontType = {
161 UNKNOWN: 0,
162 TYPE1: 1,
163 TYPE1C: 2,
164 CIDFONTTYPE0: 3,
165 CIDFONTTYPE0C: 4,
166 TRUETYPE: 5,
167 CIDFONTTYPE2: 6,
168 TYPE3: 7,
169 OPENTYPE: 8,
170 TYPE0: 9,
171 MMTYPE1: 10
172};
173
174var VERBOSITY_LEVELS = {
175 errors: 0,
176 warnings: 1,
177 infos: 5
178};
179
180// All the possible operations for an operator list.
181var OPS = {
182 // Intentionally start from 1 so it is easy to spot bad operators that will be
183 // 0's.
184 dependency: 1,
185 setLineWidth: 2,
186 setLineCap: 3,
187 setLineJoin: 4,
188 setMiterLimit: 5,
189 setDash: 6,
190 setRenderingIntent: 7,
191 setFlatness: 8,
192 setGState: 9,
193 save: 10,
194 restore: 11,
195 transform: 12,
196 moveTo: 13,
197 lineTo: 14,
198 curveTo: 15,
199 curveTo2: 16,
200 curveTo3: 17,
201 closePath: 18,
202 rectangle: 19,
203 stroke: 20,
204 closeStroke: 21,
205 fill: 22,
206 eoFill: 23,
207 fillStroke: 24,
208 eoFillStroke: 25,
209 closeFillStroke: 26,
210 closeEOFillStroke: 27,
211 endPath: 28,
212 clip: 29,
213 eoClip: 30,
214 beginText: 31,
215 endText: 32,
216 setCharSpacing: 33,
217 setWordSpacing: 34,
218 setHScale: 35,
219 setLeading: 36,
220 setFont: 37,
221 setTextRenderingMode: 38,
222 setTextRise: 39,
223 moveText: 40,
224 setLeadingMoveText: 41,
225 setTextMatrix: 42,
226 nextLine: 43,
227 showText: 44,
228 showSpacedText: 45,
229 nextLineShowText: 46,
230 nextLineSetSpacingShowText: 47,
231 setCharWidth: 48,
232 setCharWidthAndBounds: 49,
233 setStrokeColorSpace: 50,
234 setFillColorSpace: 51,
235 setStrokeColor: 52,
236 setStrokeColorN: 53,
237 setFillColor: 54,
238 setFillColorN: 55,
239 setStrokeGray: 56,
240 setFillGray: 57,
241 setStrokeRGBColor: 58,
242 setFillRGBColor: 59,
243 setStrokeCMYKColor: 60,
244 setFillCMYKColor: 61,
245 shadingFill: 62,
246 beginInlineImage: 63,
247 beginImageData: 64,
248 endInlineImage: 65,
249 paintXObject: 66,
250 markPoint: 67,
251 markPointProps: 68,
252 beginMarkedContent: 69,
253 beginMarkedContentProps: 70,
254 endMarkedContent: 71,
255 beginCompat: 72,
256 endCompat: 73,
257 paintFormXObjectBegin: 74,
258 paintFormXObjectEnd: 75,
259 beginGroup: 76,
260 endGroup: 77,
261 beginAnnotations: 78,
262 endAnnotations: 79,
263 beginAnnotation: 80,
264 endAnnotation: 81,
265 paintJpegXObject: 82,
266 paintImageMaskXObject: 83,
267 paintImageMaskXObjectGroup: 84,
268 paintImageXObject: 85,
269 paintInlineImageXObject: 86,
270 paintInlineImageXObjectGroup: 87,
271 paintImageXObjectRepeat: 88,
272 paintImageMaskXObjectRepeat: 89,
273 paintSolidColorImageMask: 90,
274 constructPath: 91
275};
276
277var verbosity = VERBOSITY_LEVELS.warnings;
278
279function setVerbosityLevel(level) {
280 verbosity = level;
281}
282
283function getVerbosityLevel() {
284 return verbosity;
285}
286
287// A notice for devs. These are good for things that are helpful to devs, such
288// as warning that Workers were disabled, which is important to devs but not
289// end users.
290function info(msg) {
291 if (verbosity >= VERBOSITY_LEVELS.infos) {
292 console.log('Info: ' + msg);
293 }
294}
295
296// Non-fatal warnings.
297function warn(msg) {
298 if (verbosity >= VERBOSITY_LEVELS.warnings) {
299 console.log('Warning: ' + msg);
300 }
301}
302
303// Deprecated API function -- display regardless of the PDFJS.verbosity setting.
304function deprecated(details) {
305 console.log('Deprecated API usage: ' + details);
306}
307
308// Fatal errors that should trigger the fallback UI and halt execution by
309// throwing an exception.
310function error(msg) {
311 if (verbosity >= VERBOSITY_LEVELS.errors) {
312 console.log('Error: ' + msg);
313 console.log(backtrace());
314 }
315 throw new Error(msg);
316}
317
318function backtrace() {
319 try {
320 throw new Error();
321 } catch (e) {
322 return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
323 }
324}
325
326function assert(cond, msg) {
327 if (!cond) {
328 error(msg);
329 }
330}
331
332var UNSUPPORTED_FEATURES = {
333 unknown: 'unknown',
334 forms: 'forms',
335 javaScript: 'javaScript',
336 smask: 'smask',
337 shadingPattern: 'shadingPattern',
338 font: 'font'
339};
340
341// Checks if URLs have the same origin. For non-HTTP based URLs, returns false.
342function isSameOrigin(baseUrl, otherUrl) {
343 try {
344 var base = new URL(baseUrl);
345 if (!base.origin || base.origin === 'null') {
346 return false; // non-HTTP url
347 }
348 } catch (e) {
349 return false;
350 }
351
352 var other = new URL(otherUrl, base);
353 return base.origin === other.origin;
354}
355
356// Validates if URL is safe and allowed, e.g. to avoid XSS.
357function isValidUrl(url, allowRelative) {
358 if (!url || typeof url !== 'string') {
359 return false;
360 }
361 // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1)
362 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
363 var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url);
364 if (!protocol) {
365 return allowRelative;
366 }
367 protocol = protocol[0].toLowerCase();
368 switch (protocol) {
369 case 'http':
370 case 'https':
371 case 'ftp':
372 case 'mailto':
373 case 'tel':
374 return true;
375 default:
376 return false;
377 }
378}
379
380function shadow(obj, prop, value) {
381 Object.defineProperty(obj, prop, { value: value,
382 enumerable: true,
383 configurable: true,
384 writable: false });
385 return value;
386}
387
388function getLookupTableFactory(initializer) {
389 var lookup;
390 return function () {
391 if (initializer) {
392 lookup = Object.create(null);
393 initializer(lookup);
394 initializer = null;
395 }
396 return lookup;
397 };
398}
399
400var PasswordResponses = {
401 NEED_PASSWORD: 1,
402 INCORRECT_PASSWORD: 2
403};
404
405var PasswordException = (function PasswordExceptionClosure() {
406 function PasswordException(msg, code) {
407 this.name = 'PasswordException';
408 this.message = msg;
409 this.code = code;
410 }
411
412 PasswordException.prototype = new Error();
413 PasswordException.constructor = PasswordException;
414
415 return PasswordException;
416})();
417
418var UnknownErrorException = (function UnknownErrorExceptionClosure() {
419 function UnknownErrorException(msg, details) {
420 this.name = 'UnknownErrorException';
421 this.message = msg;
422 this.details = details;
423 }
424
425 UnknownErrorException.prototype = new Error();
426 UnknownErrorException.constructor = UnknownErrorException;
427
428 return UnknownErrorException;
429})();
430
431var InvalidPDFException = (function InvalidPDFExceptionClosure() {
432 function InvalidPDFException(msg) {
433 this.name = 'InvalidPDFException';
434 this.message = msg;
435 }
436
437 InvalidPDFException.prototype = new Error();
438 InvalidPDFException.constructor = InvalidPDFException;
439
440 return InvalidPDFException;
441})();
442
443var MissingPDFException = (function MissingPDFExceptionClosure() {
444 function MissingPDFException(msg) {
445 this.name = 'MissingPDFException';
446 this.message = msg;
447 }
448
449 MissingPDFException.prototype = new Error();
450 MissingPDFException.constructor = MissingPDFException;
451
452 return MissingPDFException;
453})();
454
455var UnexpectedResponseException =
456 (function UnexpectedResponseExceptionClosure() {
457 function UnexpectedResponseException(msg, status) {
458 this.name = 'UnexpectedResponseException';
459 this.message = msg;
460 this.status = status;
461 }
462
463 UnexpectedResponseException.prototype = new Error();
464 UnexpectedResponseException.constructor = UnexpectedResponseException;
465
466 return UnexpectedResponseException;
467})();
468
469var NotImplementedException = (function NotImplementedExceptionClosure() {
470 function NotImplementedException(msg) {
471 this.message = msg;
472 }
473
474 NotImplementedException.prototype = new Error();
475 NotImplementedException.prototype.name = 'NotImplementedException';
476 NotImplementedException.constructor = NotImplementedException;
477
478 return NotImplementedException;
479})();
480
481var MissingDataException = (function MissingDataExceptionClosure() {
482 function MissingDataException(begin, end) {
483 this.begin = begin;
484 this.end = end;
485 this.message = 'Missing data [' + begin + ', ' + end + ')';
486 }
487
488 MissingDataException.prototype = new Error();
489 MissingDataException.prototype.name = 'MissingDataException';
490 MissingDataException.constructor = MissingDataException;
491
492 return MissingDataException;
493})();
494
495var XRefParseException = (function XRefParseExceptionClosure() {
496 function XRefParseException(msg) {
497 this.message = msg;
498 }
499
500 XRefParseException.prototype = new Error();
501 XRefParseException.prototype.name = 'XRefParseException';
502 XRefParseException.constructor = XRefParseException;
503
504 return XRefParseException;
505})();
506
507var NullCharactersRegExp = /\x00/g;
508
509function removeNullCharacters(str) {
510 if (typeof str !== 'string') {
511 warn('The argument for removeNullCharacters must be a string.');
512 return str;
513 }
514 return str.replace(NullCharactersRegExp, '');
515}
516
517function bytesToString(bytes) {
518 assert(bytes !== null && typeof bytes === 'object' &&
519 bytes.length !== undefined, 'Invalid argument for bytesToString');
520 var length = bytes.length;
521 var MAX_ARGUMENT_COUNT = 8192;
522 if (length < MAX_ARGUMENT_COUNT) {
523 return String.fromCharCode.apply(null, bytes);
524 }
525 var strBuf = [];
526 for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
527 var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
528 var chunk = bytes.subarray(i, chunkEnd);
529 strBuf.push(String.fromCharCode.apply(null, chunk));
530 }
531 return strBuf.join('');
532}
533
534function stringToBytes(str) {
535 assert(typeof str === 'string', 'Invalid argument for stringToBytes');
536 var length = str.length;
537 var bytes = new Uint8Array(length);
538 for (var i = 0; i < length; ++i) {
539 bytes[i] = str.charCodeAt(i) & 0xFF;
540 }
541 return bytes;
542}
543
544/**
545 * Gets length of the array (Array, Uint8Array, or string) in bytes.
546 * @param {Array|Uint8Array|string} arr
547 * @returns {number}
548 */
549function arrayByteLength(arr) {
550 if (arr.length !== undefined) {
551 return arr.length;
552 }
553 assert(arr.byteLength !== undefined);
554 return arr.byteLength;
555}
556
557/**
558 * Combines array items (arrays) into single Uint8Array object.
559 * @param {Array} arr - the array of the arrays (Array, Uint8Array, or string).
560 * @returns {Uint8Array}
561 */
562function arraysToBytes(arr) {
563 // Shortcut: if first and only item is Uint8Array, return it.
564 if (arr.length === 1 && (arr[0] instanceof Uint8Array)) {
565 return arr[0];
566 }
567 var resultLength = 0;
568 var i, ii = arr.length;
569 var item, itemLength ;
570 for (i = 0; i < ii; i++) {
571 item = arr[i];
572 itemLength = arrayByteLength(item);
573 resultLength += itemLength;
574 }
575 var pos = 0;
576 var data = new Uint8Array(resultLength);
577 for (i = 0; i < ii; i++) {
578 item = arr[i];
579 if (!(item instanceof Uint8Array)) {
580 if (typeof item === 'string') {
581 item = stringToBytes(item);
582 } else {
583 item = new Uint8Array(item);
584 }
585 }
586 itemLength = item.byteLength;
587 data.set(item, pos);
588 pos += itemLength;
589 }
590 return data;
591}
592
593function string32(value) {
594 return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
595 (value >> 8) & 0xff, value & 0xff);
596}
597
598function log2(x) {
599 var n = 1, i = 0;
600 while (x > n) {
601 n <<= 1;
602 i++;
603 }
604 return i;
605}
606
607function readInt8(data, start) {
608 return (data[start] << 24) >> 24;
609}
610
611function readUint16(data, offset) {
612 return (data[offset] << 8) | data[offset + 1];
613}
614
615function readUint32(data, offset) {
616 return ((data[offset] << 24) | (data[offset + 1] << 16) |
617 (data[offset + 2] << 8) | data[offset + 3]) >>> 0;
618}
619
620// Lazy test the endianness of the platform
621// NOTE: This will be 'true' for simulated TypedArrays
622function isLittleEndian() {
623 var buffer8 = new Uint8Array(2);
624 buffer8[0] = 1;
625 var buffer16 = new Uint16Array(buffer8.buffer);
626 return (buffer16[0] === 1);
627}
628
629// Checks if it's possible to eval JS expressions.
630function isEvalSupported() {
631 try {
632 /* jshint evil: true */
633 new Function('');
634 return true;
635 } catch (e) {
636 return false;
637 }
638}
639
640var Uint32ArrayView = (function Uint32ArrayViewClosure() {
641
642 function Uint32ArrayView(buffer, length) {
643 this.buffer = buffer;
644 this.byteLength = buffer.length;
645 this.length = length === undefined ? (this.byteLength >> 2) : length;
646 ensureUint32ArrayViewProps(this.length);
647 }
648 Uint32ArrayView.prototype = Object.create(null);
649
650 var uint32ArrayViewSetters = 0;
651 function createUint32ArrayProp(index) {
652 return {
653 get: function () {
654 var buffer = this.buffer, offset = index << 2;
655 return (buffer[offset] | (buffer[offset + 1] << 8) |
656 (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0;
657 },
658 set: function (value) {
659 var buffer = this.buffer, offset = index << 2;
660 buffer[offset] = value & 255;
661 buffer[offset + 1] = (value >> 8) & 255;
662 buffer[offset + 2] = (value >> 16) & 255;
663 buffer[offset + 3] = (value >>> 24) & 255;
664 }
665 };
666 }
667
668 function ensureUint32ArrayViewProps(length) {
669 while (uint32ArrayViewSetters < length) {
670 Object.defineProperty(Uint32ArrayView.prototype,
671 uint32ArrayViewSetters,
672 createUint32ArrayProp(uint32ArrayViewSetters));
673 uint32ArrayViewSetters++;
674 }
675 }
676
677 return Uint32ArrayView;
678})();
679
680exports.Uint32ArrayView = Uint32ArrayView;
681
682var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
683
684var Util = (function UtilClosure() {
685 function Util() {}
686
687 var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
688
689 // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids
690 // creating many intermediate strings.
691 Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
692 rgbBuf[1] = r;
693 rgbBuf[3] = g;
694 rgbBuf[5] = b;
695 return rgbBuf.join('');
696 };
697
698 // Concatenates two transformation matrices together and returns the result.
699 Util.transform = function Util_transform(m1, m2) {
700 return [
701 m1[0] * m2[0] + m1[2] * m2[1],
702 m1[1] * m2[0] + m1[3] * m2[1],
703 m1[0] * m2[2] + m1[2] * m2[3],
704 m1[1] * m2[2] + m1[3] * m2[3],
705 m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
706 m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
707 ];
708 };
709
710 // For 2d affine transforms
711 Util.applyTransform = function Util_applyTransform(p, m) {
712 var xt = p[0] * m[0] + p[1] * m[2] + m[4];
713 var yt = p[0] * m[1] + p[1] * m[3] + m[5];
714 return [xt, yt];
715 };
716
717 Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
718 var d = m[0] * m[3] - m[1] * m[2];
719 var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
720 var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
721 return [xt, yt];
722 };
723
724 // Applies the transform to the rectangle and finds the minimum axially
725 // aligned bounding box.
726 Util.getAxialAlignedBoundingBox =
727 function Util_getAxialAlignedBoundingBox(r, m) {
728
729 var p1 = Util.applyTransform(r, m);
730 var p2 = Util.applyTransform(r.slice(2, 4), m);
731 var p3 = Util.applyTransform([r[0], r[3]], m);
732 var p4 = Util.applyTransform([r[2], r[1]], m);
733 return [
734 Math.min(p1[0], p2[0], p3[0], p4[0]),
735 Math.min(p1[1], p2[1], p3[1], p4[1]),
736 Math.max(p1[0], p2[0], p3[0], p4[0]),
737 Math.max(p1[1], p2[1], p3[1], p4[1])
738 ];
739 };
740
741 Util.inverseTransform = function Util_inverseTransform(m) {
742 var d = m[0] * m[3] - m[1] * m[2];
743 return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
744 (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
745 };
746
747 // Apply a generic 3d matrix M on a 3-vector v:
748 // | a b c | | X |
749 // | d e f | x | Y |
750 // | g h i | | Z |
751 // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
752 // with v as [X,Y,Z]
753 Util.apply3dTransform = function Util_apply3dTransform(m, v) {
754 return [
755 m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
756 m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
757 m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
758 ];
759 };
760
761 // This calculation uses Singular Value Decomposition.
762 // The SVD can be represented with formula A = USV. We are interested in the
763 // matrix S here because it represents the scale values.
764 Util.singularValueDecompose2dScale =
765 function Util_singularValueDecompose2dScale(m) {
766
767 var transpose = [m[0], m[2], m[1], m[3]];
768
769 // Multiply matrix m with its transpose.
770 var a = m[0] * transpose[0] + m[1] * transpose[2];
771 var b = m[0] * transpose[1] + m[1] * transpose[3];
772 var c = m[2] * transpose[0] + m[3] * transpose[2];
773 var d = m[2] * transpose[1] + m[3] * transpose[3];
774
775 // Solve the second degree polynomial to get roots.
776 var first = (a + d) / 2;
777 var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
778 var sx = first + second || 1;
779 var sy = first - second || 1;
780
781 // Scale values are the square roots of the eigenvalues.
782 return [Math.sqrt(sx), Math.sqrt(sy)];
783 };
784
785 // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
786 // For coordinate systems whose origin lies in the bottom-left, this
787 // means normalization to (BL,TR) ordering. For systems with origin in the
788 // top-left, this means (TL,BR) ordering.
789 Util.normalizeRect = function Util_normalizeRect(rect) {
790 var r = rect.slice(0); // clone rect
791 if (rect[0] > rect[2]) {
792 r[0] = rect[2];
793 r[2] = rect[0];
794 }
795 if (rect[1] > rect[3]) {
796 r[1] = rect[3];
797 r[3] = rect[1];
798 }
799 return r;
800 };
801
802 // Returns a rectangle [x1, y1, x2, y2] corresponding to the
803 // intersection of rect1 and rect2. If no intersection, returns 'false'
804 // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
805 Util.intersect = function Util_intersect(rect1, rect2) {
806 function compare(a, b) {
807 return a - b;
808 }
809
810 // Order points along the axes
811 var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
812 orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
813 result = [];
814
815 rect1 = Util.normalizeRect(rect1);
816 rect2 = Util.normalizeRect(rect2);
817
818 // X: first and second points belong to different rectangles?
819 if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
820 (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
821 // Intersection must be between second and third points
822 result[0] = orderedX[1];
823 result[2] = orderedX[2];
824 } else {
825 return false;
826 }
827
828 // Y: first and second points belong to different rectangles?
829 if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
830 (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
831 // Intersection must be between second and third points
832 result[1] = orderedY[1];
833 result[3] = orderedY[2];
834 } else {
835 return false;
836 }
837
838 return result;
839 };
840
841 Util.sign = function Util_sign(num) {
842 return num < 0 ? -1 : 1;
843 };
844
845 var ROMAN_NUMBER_MAP = [
846 '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM',
847 '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC',
848 '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'
849 ];
850 /**
851 * Converts positive integers to (upper case) Roman numerals.
852 * @param {integer} number - The number that should be converted.
853 * @param {boolean} lowerCase - Indicates if the result should be converted
854 * to lower case letters. The default is false.
855 * @return {string} The resulting Roman number.
856 */
857 Util.toRoman = function Util_toRoman(number, lowerCase) {
858 assert(isInt(number) && number > 0,
859 'The number should be a positive integer.');
860 var pos, romanBuf = [];
861 // Thousands
862 while (number >= 1000) {
863 number -= 1000;
864 romanBuf.push('M');
865 }
866 // Hundreds
867 pos = (number / 100) | 0;
868 number %= 100;
869 romanBuf.push(ROMAN_NUMBER_MAP[pos]);
870 // Tens
871 pos = (number / 10) | 0;
872 number %= 10;
873 romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]);
874 // Ones
875 romanBuf.push(ROMAN_NUMBER_MAP[20 + number]);
876
877 var romanStr = romanBuf.join('');
878 return (lowerCase ? romanStr.toLowerCase() : romanStr);
879 };
880
881 Util.appendToArray = function Util_appendToArray(arr1, arr2) {
882 Array.prototype.push.apply(arr1, arr2);
883 };
884
885 Util.prependToArray = function Util_prependToArray(arr1, arr2) {
886 Array.prototype.unshift.apply(arr1, arr2);
887 };
888
889 Util.extendObj = function extendObj(obj1, obj2) {
890 for (var key in obj2) {
891 obj1[key] = obj2[key];
892 }
893 };
894
895 Util.getInheritableProperty = function Util_getInheritableProperty(dict,
896 name) {
897 while (dict && !dict.has(name)) {
898 dict = dict.get('Parent');
899 }
900 if (!dict) {
901 return null;
902 }
903 return dict.get(name);
904 };
905
906 Util.inherit = function Util_inherit(sub, base, prototype) {
907 sub.prototype = Object.create(base.prototype);
908 sub.prototype.constructor = sub;
909 for (var prop in prototype) {
910 sub.prototype[prop] = prototype[prop];
911 }
912 };
913
914 Util.loadScript = function Util_loadScript(src, callback) {
915 var script = document.createElement('script');
916 var loaded = false;
917 script.setAttribute('src', src);
918 if (callback) {
919 script.onload = function() {
920 if (!loaded) {
921 callback();
922 }
923 loaded = true;
924 };
925 }
926 document.getElementsByTagName('head')[0].appendChild(script);
927 };
928
929 return Util;
930})();
931
932/**
933 * PDF page viewport created based on scale, rotation and offset.
934 * @class
935 * @alias PageViewport
936 */
937var PageViewport = (function PageViewportClosure() {
938 /**
939 * @constructor
940 * @private
941 * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates.
942 * @param scale {number} scale of the viewport.
943 * @param rotation {number} rotations of the viewport in degrees.
944 * @param offsetX {number} offset X
945 * @param offsetY {number} offset Y
946 * @param dontFlip {boolean} if true, axis Y will not be flipped.
947 */
948 function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
949 this.viewBox = viewBox;
950 this.scale = scale;
951 this.rotation = rotation;
952 this.offsetX = offsetX;
953 this.offsetY = offsetY;
954
955 // creating transform to convert pdf coordinate system to the normal
956 // canvas like coordinates taking in account scale and rotation
957 var centerX = (viewBox[2] + viewBox[0]) / 2;
958 var centerY = (viewBox[3] + viewBox[1]) / 2;
959 var rotateA, rotateB, rotateC, rotateD;
960 rotation = rotation % 360;
961 rotation = rotation < 0 ? rotation + 360 : rotation;
962 switch (rotation) {
963 case 180:
964 rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
965 break;
966 case 90:
967 rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
968 break;
969 case 270:
970 rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
971 break;
972 //case 0:
973 default:
974 rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
975 break;
976 }
977
978 if (dontFlip) {
979 rotateC = -rotateC; rotateD = -rotateD;
980 }
981
982 var offsetCanvasX, offsetCanvasY;
983 var width, height;
984 if (rotateA === 0) {
985 offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
986 offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
987 width = Math.abs(viewBox[3] - viewBox[1]) * scale;
988 height = Math.abs(viewBox[2] - viewBox[0]) * scale;
989 } else {
990 offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
991 offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
992 width = Math.abs(viewBox[2] - viewBox[0]) * scale;
993 height = Math.abs(viewBox[3] - viewBox[1]) * scale;
994 }
995 // creating transform for the following operations:
996 // translate(-centerX, -centerY), rotate and flip vertically,
997 // scale, and translate(offsetCanvasX, offsetCanvasY)
998 this.transform = [
999 rotateA * scale,
1000 rotateB * scale,
1001 rotateC * scale,
1002 rotateD * scale,
1003 offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
1004 offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
1005 ];
1006
1007 this.width = width;
1008 this.height = height;
1009 this.fontScale = scale;
1010 }
1011 PageViewport.prototype = /** @lends PageViewport.prototype */ {
1012 /**
1013 * Clones viewport with additional properties.
1014 * @param args {Object} (optional) If specified, may contain the 'scale' or
1015 * 'rotation' properties to override the corresponding properties in
1016 * the cloned viewport.
1017 * @returns {PageViewport} Cloned viewport.
1018 */
1019 clone: function PageViewPort_clone(args) {
1020 args = args || {};
1021 var scale = 'scale' in args ? args.scale : this.scale;
1022 var rotation = 'rotation' in args ? args.rotation : this.rotation;
1023 return new PageViewport(this.viewBox.slice(), scale, rotation,
1024 this.offsetX, this.offsetY, args.dontFlip);
1025 },
1026 /**
1027 * Converts PDF point to the viewport coordinates. For examples, useful for
1028 * converting PDF location into canvas pixel coordinates.
1029 * @param x {number} X coordinate.
1030 * @param y {number} Y coordinate.
1031 * @returns {Object} Object that contains 'x' and 'y' properties of the
1032 * point in the viewport coordinate space.
1033 * @see {@link convertToPdfPoint}
1034 * @see {@link convertToViewportRectangle}
1035 */
1036 convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
1037 return Util.applyTransform([x, y], this.transform);
1038 },
1039 /**
1040 * Converts PDF rectangle to the viewport coordinates.
1041 * @param rect {Array} xMin, yMin, xMax and yMax coordinates.
1042 * @returns {Array} Contains corresponding coordinates of the rectangle
1043 * in the viewport coordinate space.
1044 * @see {@link convertToViewportPoint}
1045 */
1046 convertToViewportRectangle:
1047 function PageViewport_convertToViewportRectangle(rect) {
1048 var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
1049 var br = Util.applyTransform([rect[2], rect[3]], this.transform);
1050 return [tl[0], tl[1], br[0], br[1]];
1051 },
1052 /**
1053 * Converts viewport coordinates to the PDF location. For examples, useful
1054 * for converting canvas pixel location into PDF one.
1055 * @param x {number} X coordinate.
1056 * @param y {number} Y coordinate.
1057 * @returns {Object} Object that contains 'x' and 'y' properties of the
1058 * point in the PDF coordinate space.
1059 * @see {@link convertToViewportPoint}
1060 */
1061 convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
1062 return Util.applyInverseTransform([x, y], this.transform);
1063 }
1064 };
1065 return PageViewport;
1066})();
1067
1068var PDFStringTranslateTable = [
1069 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1070 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0,
1071 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,
1072 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,
1073 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,
1074 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
1075 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
1076 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
1077 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
1078];
1079
1080function stringToPDFString(str) {
1081 var i, n = str.length, strBuf = [];
1082 if (str[0] === '\xFE' && str[1] === '\xFF') {
1083 // UTF16BE BOM
1084 for (i = 2; i < n; i += 2) {
1085 strBuf.push(String.fromCharCode(
1086 (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
1087 }
1088 } else {
1089 for (i = 0; i < n; ++i) {
1090 var code = PDFStringTranslateTable[str.charCodeAt(i)];
1091 strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
1092 }
1093 }
1094 return strBuf.join('');
1095}
1096
1097function stringToUTF8String(str) {
1098 return decodeURIComponent(escape(str));
1099}
1100
1101function utf8StringToString(str) {
1102 return unescape(encodeURIComponent(str));
1103}
1104
1105function isEmptyObj(obj) {
1106 for (var key in obj) {
1107 return false;
1108 }
1109 return true;
1110}
1111
1112function isBool(v) {
1113 return typeof v === 'boolean';
1114}
1115
1116function isInt(v) {
1117 return typeof v === 'number' && ((v | 0) === v);
1118}
1119
1120function isNum(v) {
1121 return typeof v === 'number';
1122}
1123
1124function isString(v) {
1125 return typeof v === 'string';
1126}
1127
1128function isArray(v) {
1129 return v instanceof Array;
1130}
1131
1132function isArrayBuffer(v) {
1133 return typeof v === 'object' && v !== null && v.byteLength !== undefined;
1134}
1135
1136// Checks if ch is one of the following characters: SPACE, TAB, CR or LF.
1137function isSpace(ch) {
1138 return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A);
1139}
1140
1141/**
1142 * Promise Capability object.
1143 *
1144 * @typedef {Object} PromiseCapability
1145 * @property {Promise} promise - A promise object.
1146 * @property {function} resolve - Fulfills the promise.
1147 * @property {function} reject - Rejects the promise.
1148 */
1149
1150/**
1151 * Creates a promise capability object.
1152 * @alias createPromiseCapability
1153 *
1154 * @return {PromiseCapability} A capability object contains:
1155 * - a Promise, resolve and reject methods.
1156 */
1157function createPromiseCapability() {
1158 var capability = {};
1159 capability.promise = new Promise(function (resolve, reject) {
1160 capability.resolve = resolve;
1161 capability.reject = reject;
1162 });
1163 return capability;
1164}
1165
1166/**
1167 * Polyfill for Promises:
1168 * The following promise implementation tries to generally implement the
1169 * Promise/A+ spec. Some notable differences from other promise libraries are:
1170 * - There currently isn't a separate deferred and promise object.
1171 * - Unhandled rejections eventually show an error if they aren't handled.
1172 *
1173 * Based off of the work in:
1174 * https://bugzilla.mozilla.org/show_bug.cgi?id=810490
1175 */
1176(function PromiseClosure() {
1177 if (globalScope.Promise) {
1178 // Promises existing in the DOM/Worker, checking presence of all/resolve
1179 if (typeof globalScope.Promise.all !== 'function') {
1180 globalScope.Promise.all = function (iterable) {
1181 var count = 0, results = [], resolve, reject;
1182 var promise = new globalScope.Promise(function (resolve_, reject_) {
1183 resolve = resolve_;
1184 reject = reject_;
1185 });
1186 iterable.forEach(function (p, i) {
1187 count++;
1188 p.then(function (result) {
1189 results[i] = result;
1190 count--;
1191 if (count === 0) {
1192 resolve(results);
1193 }
1194 }, reject);
1195 });
1196 if (count === 0) {
1197 resolve(results);
1198 }
1199 return promise;
1200 };
1201 }
1202 if (typeof globalScope.Promise.resolve !== 'function') {
1203 globalScope.Promise.resolve = function (value) {
1204 return new globalScope.Promise(function (resolve) { resolve(value); });
1205 };
1206 }
1207 if (typeof globalScope.Promise.reject !== 'function') {
1208 globalScope.Promise.reject = function (reason) {
1209 return new globalScope.Promise(function (resolve, reject) {
1210 reject(reason);
1211 });
1212 };
1213 }
1214 if (typeof globalScope.Promise.prototype.catch !== 'function') {
1215 globalScope.Promise.prototype.catch = function (onReject) {
1216 return globalScope.Promise.prototype.then(undefined, onReject);
1217 };
1218 }
1219 return;
1220 }
1221 var STATUS_PENDING = 0;
1222 var STATUS_RESOLVED = 1;
1223 var STATUS_REJECTED = 2;
1224
1225 // In an attempt to avoid silent exceptions, unhandled rejections are
1226 // tracked and if they aren't handled in a certain amount of time an
1227 // error is logged.
1228 var REJECTION_TIMEOUT = 500;
1229
1230 var HandlerManager = {
1231 handlers: [],
1232 running: false,
1233 unhandledRejections: [],
1234 pendingRejectionCheck: false,
1235
1236 scheduleHandlers: function scheduleHandlers(promise) {
1237 if (promise._status === STATUS_PENDING) {
1238 return;
1239 }
1240
1241 this.handlers = this.handlers.concat(promise._handlers);
1242 promise._handlers = [];
1243
1244 if (this.running) {
1245 return;
1246 }
1247 this.running = true;
1248
1249 setTimeout(this.runHandlers.bind(this), 0);
1250 },
1251
1252 runHandlers: function runHandlers() {
1253 var RUN_TIMEOUT = 1; // ms
1254 var timeoutAt = Date.now() + RUN_TIMEOUT;
1255 while (this.handlers.length > 0) {
1256 var handler = this.handlers.shift();
1257
1258 var nextStatus = handler.thisPromise._status;
1259 var nextValue = handler.thisPromise._value;
1260
1261 try {
1262 if (nextStatus === STATUS_RESOLVED) {
1263 if (typeof handler.onResolve === 'function') {
1264 nextValue = handler.onResolve(nextValue);
1265 }
1266 } else if (typeof handler.onReject === 'function') {
1267 nextValue = handler.onReject(nextValue);
1268 nextStatus = STATUS_RESOLVED;
1269
1270 if (handler.thisPromise._unhandledRejection) {
1271 this.removeUnhandeledRejection(handler.thisPromise);
1272 }
1273 }
1274 } catch (ex) {
1275 nextStatus = STATUS_REJECTED;
1276 nextValue = ex;
1277 }
1278
1279 handler.nextPromise._updateStatus(nextStatus, nextValue);
1280 if (Date.now() >= timeoutAt) {
1281 break;
1282 }
1283 }
1284
1285 if (this.handlers.length > 0) {
1286 setTimeout(this.runHandlers.bind(this), 0);
1287 return;
1288 }
1289
1290 this.running = false;
1291 },
1292
1293 addUnhandledRejection: function addUnhandledRejection(promise) {
1294 this.unhandledRejections.push({
1295 promise: promise,
1296 time: Date.now()
1297 });
1298 this.scheduleRejectionCheck();
1299 },
1300
1301 removeUnhandeledRejection: function removeUnhandeledRejection(promise) {
1302 promise._unhandledRejection = false;
1303 for (var i = 0; i < this.unhandledRejections.length; i++) {
1304 if (this.unhandledRejections[i].promise === promise) {
1305 this.unhandledRejections.splice(i);
1306 i--;
1307 }
1308 }
1309 },
1310
1311 scheduleRejectionCheck: function scheduleRejectionCheck() {
1312 if (this.pendingRejectionCheck) {
1313 return;
1314 }
1315 this.pendingRejectionCheck = true;
1316 setTimeout(function rejectionCheck() {
1317 this.pendingRejectionCheck = false;
1318 var now = Date.now();
1319 for (var i = 0; i < this.unhandledRejections.length; i++) {
1320 if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) {
1321 var unhandled = this.unhandledRejections[i].promise._value;
1322 var msg = 'Unhandled rejection: ' + unhandled;
1323 if (unhandled.stack) {
1324 msg += '\n' + unhandled.stack;
1325 }
1326 warn(msg);
1327 this.unhandledRejections.splice(i);
1328 i--;
1329 }
1330 }
1331 if (this.unhandledRejections.length) {
1332 this.scheduleRejectionCheck();
1333 }
1334 }.bind(this), REJECTION_TIMEOUT);
1335 }
1336 };
1337
1338 function Promise(resolver) {
1339 this._status = STATUS_PENDING;
1340 this._handlers = [];
1341 try {
1342 resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
1343 } catch (e) {
1344 this._reject(e);
1345 }
1346 }
1347 /**
1348 * Builds a promise that is resolved when all the passed in promises are
1349 * resolved.
1350 * @param {array} promises array of data and/or promises to wait for.
1351 * @return {Promise} New dependent promise.
1352 */
1353 Promise.all = function Promise_all(promises) {
1354 var resolveAll, rejectAll;
1355 var deferred = new Promise(function (resolve, reject) {
1356 resolveAll = resolve;
1357 rejectAll = reject;
1358 });
1359 var unresolved = promises.length;
1360 var results = [];
1361 if (unresolved === 0) {
1362 resolveAll(results);
1363 return deferred;
1364 }
1365 function reject(reason) {
1366 if (deferred._status === STATUS_REJECTED) {
1367 return;
1368 }
1369 results = [];
1370 rejectAll(reason);
1371 }
1372 for (var i = 0, ii = promises.length; i < ii; ++i) {
1373 var promise = promises[i];
1374 var resolve = (function(i) {
1375 return function(value) {
1376 if (deferred._status === STATUS_REJECTED) {
1377 return;
1378 }
1379 results[i] = value;
1380 unresolved--;
1381 if (unresolved === 0) {
1382 resolveAll(results);
1383 }
1384 };
1385 })(i);
1386 if (Promise.isPromise(promise)) {
1387 promise.then(resolve, reject);
1388 } else {
1389 resolve(promise);
1390 }
1391 }
1392 return deferred;
1393 };
1394
1395 /**
1396 * Checks if the value is likely a promise (has a 'then' function).
1397 * @return {boolean} true if value is thenable
1398 */
1399 Promise.isPromise = function Promise_isPromise(value) {
1400 return value && typeof value.then === 'function';
1401 };
1402
1403 /**
1404 * Creates resolved promise
1405 * @param value resolve value
1406 * @returns {Promise}
1407 */
1408 Promise.resolve = function Promise_resolve(value) {
1409 return new Promise(function (resolve) { resolve(value); });
1410 };
1411
1412 /**
1413 * Creates rejected promise
1414 * @param reason rejection value
1415 * @returns {Promise}
1416 */
1417 Promise.reject = function Promise_reject(reason) {
1418 return new Promise(function (resolve, reject) { reject(reason); });
1419 };
1420
1421 Promise.prototype = {
1422 _status: null,
1423 _value: null,
1424 _handlers: null,
1425 _unhandledRejection: null,
1426
1427 _updateStatus: function Promise__updateStatus(status, value) {
1428 if (this._status === STATUS_RESOLVED ||
1429 this._status === STATUS_REJECTED) {
1430 return;
1431 }
1432
1433 if (status === STATUS_RESOLVED &&
1434 Promise.isPromise(value)) {
1435 value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
1436 this._updateStatus.bind(this, STATUS_REJECTED));
1437 return;
1438 }
1439
1440 this._status = status;
1441 this._value = value;
1442
1443 if (status === STATUS_REJECTED && this._handlers.length === 0) {
1444 this._unhandledRejection = true;
1445 HandlerManager.addUnhandledRejection(this);
1446 }
1447
1448 HandlerManager.scheduleHandlers(this);
1449 },
1450
1451 _resolve: function Promise_resolve(value) {
1452 this._updateStatus(STATUS_RESOLVED, value);
1453 },
1454
1455 _reject: function Promise_reject(reason) {
1456 this._updateStatus(STATUS_REJECTED, reason);
1457 },
1458
1459 then: function Promise_then(onResolve, onReject) {
1460 var nextPromise = new Promise(function (resolve, reject) {
1461 this.resolve = resolve;
1462 this.reject = reject;
1463 });
1464 this._handlers.push({
1465 thisPromise: this,
1466 onResolve: onResolve,
1467 onReject: onReject,
1468 nextPromise: nextPromise
1469 });
1470 HandlerManager.scheduleHandlers(this);
1471 return nextPromise;
1472 },
1473
1474 catch: function Promise_catch(onReject) {
1475 return this.then(undefined, onReject);
1476 }
1477 };
1478
1479 globalScope.Promise = Promise;
1480})();
1481
1482(function WeakMapClosure() {
1483 if (globalScope.WeakMap) {
1484 return;
1485 }
1486
1487 var id = 0;
1488 function WeakMap() {
1489 this.id = '$weakmap' + (id++);
1490 }
1491 WeakMap.prototype = {
1492 has: function(obj) {
1493 return !!Object.getOwnPropertyDescriptor(obj, this.id);
1494 },
1495 get: function(obj, defaultValue) {
1496 return this.has(obj) ? obj[this.id] : defaultValue;
1497 },
1498 set: function(obj, value) {
1499 Object.defineProperty(obj, this.id, {
1500 value: value,
1501 enumerable: false,
1502 configurable: true
1503 });
1504 },
1505 delete: function(obj) {
1506 delete obj[this.id];
1507 }
1508 };
1509
1510 globalScope.WeakMap = WeakMap;
1511})();
1512
1513var StatTimer = (function StatTimerClosure() {
1514 function rpad(str, pad, length) {
1515 while (str.length < length) {
1516 str += pad;
1517 }
1518 return str;
1519 }
1520 function StatTimer() {
1521 this.started = Object.create(null);
1522 this.times = [];
1523 this.enabled = true;
1524 }
1525 StatTimer.prototype = {
1526 time: function StatTimer_time(name) {
1527 if (!this.enabled) {
1528 return;
1529 }
1530 if (name in this.started) {
1531 warn('Timer is already running for ' + name);
1532 }
1533 this.started[name] = Date.now();
1534 },
1535 timeEnd: function StatTimer_timeEnd(name) {
1536 if (!this.enabled) {
1537 return;
1538 }
1539 if (!(name in this.started)) {
1540 warn('Timer has not been started for ' + name);
1541 }
1542 this.times.push({
1543 'name': name,
1544 'start': this.started[name],
1545 'end': Date.now()
1546 });
1547 // Remove timer from started so it can be called again.
1548 delete this.started[name];
1549 },
1550 toString: function StatTimer_toString() {
1551 var i, ii;
1552 var times = this.times;
1553 var out = '';
1554 // Find the longest name for padding purposes.
1555 var longest = 0;
1556 for (i = 0, ii = times.length; i < ii; ++i) {
1557 var name = times[i]['name'];
1558 if (name.length > longest) {
1559 longest = name.length;
1560 }
1561 }
1562 for (i = 0, ii = times.length; i < ii; ++i) {
1563 var span = times[i];
1564 var duration = span.end - span.start;
1565 out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
1566 }
1567 return out;
1568 }
1569 };
1570 return StatTimer;
1571})();
1572
1573var createBlob = function createBlob(data, contentType) {
1574 if (typeof Blob !== 'undefined') {
1575 return new Blob([data], { type: contentType });
1576 }
1577 warn('The "Blob" constructor is not supported.');
1578};
1579
1580var createObjectURL = (function createObjectURLClosure() {
1581 // Blob/createObjectURL is not available, falling back to data schema.
1582 var digits =
1583 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
1584
1585 return function createObjectURL(data, contentType, forceDataSchema) {
1586 if (!forceDataSchema &&
1587 typeof URL !== 'undefined' && URL.createObjectURL) {
1588 var blob = createBlob(data, contentType);
1589 return URL.createObjectURL(blob);
1590 }
1591
1592 var buffer = 'data:' + contentType + ';base64,';
1593 for (var i = 0, ii = data.length; i < ii; i += 3) {
1594 var b1 = data[i] & 0xFF;
1595 var b2 = data[i + 1] & 0xFF;
1596 var b3 = data[i + 2] & 0xFF;
1597 var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
1598 var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
1599 var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
1600 buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
1601 }
1602 return buffer;
1603 };
1604})();
1605
1606function MessageHandler(sourceName, targetName, comObj) {
1607 this.sourceName = sourceName;
1608 this.targetName = targetName;
1609 this.comObj = comObj;
1610 this.callbackIndex = 1;
1611 this.postMessageTransfers = true;
1612 var callbacksCapabilities = this.callbacksCapabilities = Object.create(null);
1613 var ah = this.actionHandler = Object.create(null);
1614
1615 this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) {
1616 var data = event.data;
1617 if (data.targetName !== this.sourceName) {
1618 return;
1619 }
1620 if (data.isReply) {
1621 var callbackId = data.callbackId;
1622 if (data.callbackId in callbacksCapabilities) {
1623 var callback = callbacksCapabilities[callbackId];
1624 delete callbacksCapabilities[callbackId];
1625 if ('error' in data) {
1626 callback.reject(data.error);
1627 } else {
1628 callback.resolve(data.data);
1629 }
1630 } else {
1631 error('Cannot resolve callback ' + callbackId);
1632 }
1633 } else if (data.action in ah) {
1634 var action = ah[data.action];
1635 if (data.callbackId) {
1636 var sourceName = this.sourceName;
1637 var targetName = data.sourceName;
1638 Promise.resolve().then(function () {
1639 return action[0].call(action[1], data.data);
1640 }).then(function (result) {
1641 comObj.postMessage({
1642 sourceName: sourceName,
1643 targetName: targetName,
1644 isReply: true,
1645 callbackId: data.callbackId,
1646 data: result
1647 });
1648 }, function (reason) {
1649 if (reason instanceof Error) {
1650 // Serialize error to avoid "DataCloneError"
1651 reason = reason + '';
1652 }
1653 comObj.postMessage({
1654 sourceName: sourceName,
1655 targetName: targetName,
1656 isReply: true,
1657 callbackId: data.callbackId,
1658 error: reason
1659 });
1660 });
1661 } else {
1662 action[0].call(action[1], data.data);
1663 }
1664 } else {
1665 error('Unknown action from worker: ' + data.action);
1666 }
1667 }.bind(this);
1668 comObj.addEventListener('message', this._onComObjOnMessage);
1669}
1670
1671MessageHandler.prototype = {
1672 on: function messageHandlerOn(actionName, handler, scope) {
1673 var ah = this.actionHandler;
1674 if (ah[actionName]) {
1675 error('There is already an actionName called "' + actionName + '"');
1676 }
1677 ah[actionName] = [handler, scope];
1678 },
1679 /**
1680 * Sends a message to the comObj to invoke the action with the supplied data.
1681 * @param {String} actionName Action to call.
1682 * @param {JSON} data JSON data to send.
1683 * @param {Array} [transfers] Optional list of transfers/ArrayBuffers
1684 */
1685 send: function messageHandlerSend(actionName, data, transfers) {
1686 var message = {
1687 sourceName: this.sourceName,
1688 targetName: this.targetName,
1689 action: actionName,
1690 data: data
1691 };
1692 this.postMessage(message, transfers);
1693 },
1694 /**
1695 * Sends a message to the comObj to invoke the action with the supplied data.
1696 * Expects that other side will callback with the response.
1697 * @param {String} actionName Action to call.
1698 * @param {JSON} data JSON data to send.
1699 * @param {Array} [transfers] Optional list of transfers/ArrayBuffers.
1700 * @returns {Promise} Promise to be resolved with response data.
1701 */
1702 sendWithPromise:
1703 function messageHandlerSendWithPromise(actionName, data, transfers) {
1704 var callbackId = this.callbackIndex++;
1705 var message = {
1706 sourceName: this.sourceName,
1707 targetName: this.targetName,
1708 action: actionName,
1709 data: data,
1710 callbackId: callbackId
1711 };
1712 var capability = createPromiseCapability();
1713 this.callbacksCapabilities[callbackId] = capability;
1714 try {
1715 this.postMessage(message, transfers);
1716 } catch (e) {
1717 capability.reject(e);
1718 }
1719 return capability.promise;
1720 },
1721 /**
1722 * Sends raw message to the comObj.
1723 * @private
1724 * @param message {Object} Raw message.
1725 * @param transfers List of transfers/ArrayBuffers, or undefined.
1726 */
1727 postMessage: function (message, transfers) {
1728 if (transfers && this.postMessageTransfers) {
1729 this.comObj.postMessage(message, transfers);
1730 } else {
1731 this.comObj.postMessage(message);
1732 }
1733 },
1734
1735 destroy: function () {
1736 this.comObj.removeEventListener('message', this._onComObjOnMessage);
1737 }
1738};
1739
1740function loadJpegStream(id, imageUrl, objs) {
1741 var img = new Image();
1742 img.onload = (function loadJpegStream_onloadClosure() {
1743 objs.resolve(id, img);
1744 });
1745 img.onerror = (function loadJpegStream_onerrorClosure() {
1746 objs.resolve(id, null);
1747 warn('Error during JPEG image loading');
1748 });
1749 img.src = imageUrl;
1750}
1751
1752 // Polyfill from https://github.com/Polymer/URL
1753/* Any copyright is dedicated to the Public Domain.
1754 * http://creativecommons.org/publicdomain/zero/1.0/ */
1755(function checkURLConstructor(scope) {
1756 // feature detect for URL constructor
1757 var hasWorkingUrl = false;
1758 try {
1759 if (typeof URL === 'function' &&
1760 typeof URL.prototype === 'object' &&
1761 ('origin' in URL.prototype)) {
1762 var u = new URL('b', 'http://a');
1763 u.pathname = 'c%20d';
1764 hasWorkingUrl = u.href === 'http://a/c%20d';
1765 }
1766 } catch(e) { }
1767
1768 if (hasWorkingUrl) {
1769 return;
1770 }
1771
1772 var relative = Object.create(null);
1773 relative['ftp'] = 21;
1774 relative['file'] = 0;
1775 relative['gopher'] = 70;
1776 relative['http'] = 80;
1777 relative['https'] = 443;
1778 relative['ws'] = 80;
1779 relative['wss'] = 443;
1780
1781 var relativePathDotMapping = Object.create(null);
1782 relativePathDotMapping['%2e'] = '.';
1783 relativePathDotMapping['.%2e'] = '..';
1784 relativePathDotMapping['%2e.'] = '..';
1785 relativePathDotMapping['%2e%2e'] = '..';
1786
1787 function isRelativeScheme(scheme) {
1788 return relative[scheme] !== undefined;
1789 }
1790
1791 function invalid() {
1792 clear.call(this);
1793 this._isInvalid = true;
1794 }
1795
1796 function IDNAToASCII(h) {
1797 if ('' === h) {
1798 invalid.call(this);
1799 }
1800 // XXX
1801 return h.toLowerCase();
1802 }
1803
1804 function percentEscape(c) {
1805 var unicode = c.charCodeAt(0);
1806 if (unicode > 0x20 &&
1807 unicode < 0x7F &&
1808 // " # < > ? `
1809 [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) === -1
1810 ) {
1811 return c;
1812 }
1813 return encodeURIComponent(c);
1814 }
1815
1816 function percentEscapeQuery(c) {
1817 // XXX This actually needs to encode c using encoding and then
1818 // convert the bytes one-by-one.
1819
1820 var unicode = c.charCodeAt(0);
1821 if (unicode > 0x20 &&
1822 unicode < 0x7F &&
1823 // " # < > ` (do not escape '?')
1824 [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) === -1
1825 ) {
1826 return c;
1827 }
1828 return encodeURIComponent(c);
1829 }
1830
1831 var EOF, ALPHA = /[a-zA-Z]/,
1832 ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
1833
1834 function parse(input, stateOverride, base) {
1835 function err(message) {
1836 errors.push(message);
1837 }
1838
1839 var state = stateOverride || 'scheme start',
1840 cursor = 0,
1841 buffer = '',
1842 seenAt = false,
1843 seenBracket = false,
1844 errors = [];
1845
1846 loop: while ((input[cursor - 1] !== EOF || cursor === 0) &&
1847 !this._isInvalid) {
1848 var c = input[cursor];
1849 switch (state) {
1850 case 'scheme start':
1851 if (c && ALPHA.test(c)) {
1852 buffer += c.toLowerCase(); // ASCII-safe
1853 state = 'scheme';
1854 } else if (!stateOverride) {
1855 buffer = '';
1856 state = 'no scheme';
1857 continue;
1858 } else {
1859 err('Invalid scheme.');
1860 break loop;
1861 }
1862 break;
1863
1864 case 'scheme':
1865 if (c && ALPHANUMERIC.test(c)) {
1866 buffer += c.toLowerCase(); // ASCII-safe
1867 } else if (':' === c) {
1868 this._scheme = buffer;
1869 buffer = '';
1870 if (stateOverride) {
1871 break loop;
1872 }
1873 if (isRelativeScheme(this._scheme)) {
1874 this._isRelative = true;
1875 }
1876 if ('file' === this._scheme) {
1877 state = 'relative';
1878 } else if (this._isRelative && base &&
1879 base._scheme === this._scheme) {
1880 state = 'relative or authority';
1881 } else if (this._isRelative) {
1882 state = 'authority first slash';
1883 } else {
1884 state = 'scheme data';
1885 }
1886 } else if (!stateOverride) {
1887 buffer = '';
1888 cursor = 0;
1889 state = 'no scheme';
1890 continue;
1891 } else if (EOF === c) {
1892 break loop;
1893 } else {
1894 err('Code point not allowed in scheme: ' + c);
1895 break loop;
1896 }
1897 break;
1898
1899 case 'scheme data':
1900 if ('?' === c) {
1901 this._query = '?';
1902 state = 'query';
1903 } else if ('#' === c) {
1904 this._fragment = '#';
1905 state = 'fragment';
1906 } else {
1907 // XXX error handling
1908 if (EOF !== c && '\t' !== c && '\n' !== c && '\r' !== c) {
1909 this._schemeData += percentEscape(c);
1910 }
1911 }
1912 break;
1913
1914 case 'no scheme':
1915 if (!base || !(isRelativeScheme(base._scheme))) {
1916 err('Missing scheme.');
1917 invalid.call(this);
1918 } else {
1919 state = 'relative';
1920 continue;
1921 }
1922 break;
1923
1924 case 'relative or authority':
1925 if ('/' === c && '/' === input[cursor+1]) {
1926 state = 'authority ignore slashes';
1927 } else {
1928 err('Expected /, got: ' + c);
1929 state = 'relative';
1930 continue;
1931 }
1932 break;
1933
1934 case 'relative':
1935 this._isRelative = true;
1936 if ('file' !== this._scheme) {
1937 this._scheme = base._scheme;
1938 }
1939 if (EOF === c) {
1940 this._host = base._host;
1941 this._port = base._port;
1942 this._path = base._path.slice();
1943 this._query = base._query;
1944 this._username = base._username;
1945 this._password = base._password;
1946 break loop;
1947 } else if ('/' === c || '\\' === c) {
1948 if ('\\' === c) {
1949 err('\\ is an invalid code point.');
1950 }
1951 state = 'relative slash';
1952 } else if ('?' === c) {
1953 this._host = base._host;
1954 this._port = base._port;
1955 this._path = base._path.slice();
1956 this._query = '?';
1957 this._username = base._username;
1958 this._password = base._password;
1959 state = 'query';
1960 } else if ('#' === c) {
1961 this._host = base._host;
1962 this._port = base._port;
1963 this._path = base._path.slice();
1964 this._query = base._query;
1965 this._fragment = '#';
1966 this._username = base._username;
1967 this._password = base._password;
1968 state = 'fragment';
1969 } else {
1970 var nextC = input[cursor+1];
1971 var nextNextC = input[cursor+2];
1972 if ('file' !== this._scheme || !ALPHA.test(c) ||
1973 (nextC !== ':' && nextC !== '|') ||
1974 (EOF !== nextNextC && '/' !== nextNextC && '\\' !== nextNextC &&
1975 '?' !== nextNextC && '#' !== nextNextC)) {
1976 this._host = base._host;
1977 this._port = base._port;
1978 this._username = base._username;
1979 this._password = base._password;
1980 this._path = base._path.slice();
1981 this._path.pop();
1982 }
1983 state = 'relative path';
1984 continue;
1985 }
1986 break;
1987
1988 case 'relative slash':
1989 if ('/' === c || '\\' === c) {
1990 if ('\\' === c) {
1991 err('\\ is an invalid code point.');
1992 }
1993 if ('file' === this._scheme) {
1994 state = 'file host';
1995 } else {
1996 state = 'authority ignore slashes';
1997 }
1998 } else {
1999 if ('file' !== this._scheme) {
2000 this._host = base._host;
2001 this._port = base._port;
2002 this._username = base._username;
2003 this._password = base._password;
2004 }
2005 state = 'relative path';
2006 continue;
2007 }
2008 break;
2009
2010 case 'authority first slash':
2011 if ('/' === c) {
2012 state = 'authority second slash';
2013 } else {
2014 err('Expected \'/\', got: ' + c);
2015 state = 'authority ignore slashes';
2016 continue;
2017 }
2018 break;
2019
2020 case 'authority second slash':
2021 state = 'authority ignore slashes';
2022 if ('/' !== c) {
2023 err('Expected \'/\', got: ' + c);
2024 continue;
2025 }
2026 break;
2027
2028 case 'authority ignore slashes':
2029 if ('/' !== c && '\\' !== c) {
2030 state = 'authority';
2031 continue;
2032 } else {
2033 err('Expected authority, got: ' + c);
2034 }
2035 break;
2036
2037 case 'authority':
2038 if ('@' === c) {
2039 if (seenAt) {
2040 err('@ already seen.');
2041 buffer += '%40';
2042 }
2043 seenAt = true;
2044 for (var i = 0; i < buffer.length; i++) {
2045 var cp = buffer[i];
2046 if ('\t' === cp || '\n' === cp || '\r' === cp) {
2047 err('Invalid whitespace in authority.');
2048 continue;
2049 }
2050 // XXX check URL code points
2051 if (':' === cp && null === this._password) {
2052 this._password = '';
2053 continue;
2054 }
2055 var tempC = percentEscape(cp);
2056 if (null !== this._password) {
2057 this._password += tempC;
2058 } else {
2059 this._username += tempC;
2060 }
2061 }
2062 buffer = '';
2063 } else if (EOF === c || '/' === c || '\\' === c ||
2064 '?' === c || '#' === c) {
2065 cursor -= buffer.length;
2066 buffer = '';
2067 state = 'host';
2068 continue;
2069 } else {
2070 buffer += c;
2071 }
2072 break;
2073
2074 case 'file host':
2075 if (EOF === c || '/' === c || '\\' === c || '?' === c || '#' === c) {
2076 if (buffer.length === 2 && ALPHA.test(buffer[0]) &&
2077 (buffer[1] === ':' || buffer[1] === '|')) {
2078 state = 'relative path';
2079 } else if (buffer.length === 0) {
2080 state = 'relative path start';
2081 } else {
2082 this._host = IDNAToASCII.call(this, buffer);
2083 buffer = '';
2084 state = 'relative path start';
2085 }
2086 continue;
2087 } else if ('\t' === c || '\n' === c || '\r' === c) {
2088 err('Invalid whitespace in file host.');
2089 } else {
2090 buffer += c;
2091 }
2092 break;
2093
2094 case 'host':
2095 case 'hostname':
2096 if (':' === c && !seenBracket) {
2097 // XXX host parsing
2098 this._host = IDNAToASCII.call(this, buffer);
2099 buffer = '';
2100 state = 'port';
2101 if ('hostname' === stateOverride) {
2102 break loop;
2103 }
2104 } else if (EOF === c || '/' === c ||
2105 '\\' === c || '?' === c || '#' === c) {
2106 this._host = IDNAToASCII.call(this, buffer);
2107 buffer = '';
2108 state = 'relative path start';
2109 if (stateOverride) {
2110 break loop;
2111 }
2112 continue;
2113 } else if ('\t' !== c && '\n' !== c && '\r' !== c) {
2114 if ('[' === c) {
2115 seenBracket = true;
2116 } else if (']' === c) {
2117 seenBracket = false;
2118 }
2119 buffer += c;
2120 } else {
2121 err('Invalid code point in host/hostname: ' + c);
2122 }
2123 break;
2124
2125 case 'port':
2126 if (/[0-9]/.test(c)) {
2127 buffer += c;
2128 } else if (EOF === c || '/' === c || '\\' === c ||
2129 '?' === c || '#' === c || stateOverride) {
2130 if ('' !== buffer) {
2131 var temp = parseInt(buffer, 10);
2132 if (temp !== relative[this._scheme]) {
2133 this._port = temp + '';
2134 }
2135 buffer = '';
2136 }
2137 if (stateOverride) {
2138 break loop;
2139 }
2140 state = 'relative path start';
2141 continue;
2142 } else if ('\t' === c || '\n' === c || '\r' === c) {
2143 err('Invalid code point in port: ' + c);
2144 } else {
2145 invalid.call(this);
2146 }
2147 break;
2148
2149 case 'relative path start':
2150 if ('\\' === c) {
2151 err('\'\\\' not allowed in path.');
2152 }
2153 state = 'relative path';
2154 if ('/' !== c && '\\' !== c) {
2155 continue;
2156 }
2157 break;
2158
2159 case 'relative path':
2160 if (EOF === c || '/' === c || '\\' === c ||
2161 (!stateOverride && ('?' === c || '#' === c))) {
2162 if ('\\' === c) {
2163 err('\\ not allowed in relative path.');
2164 }
2165 var tmp;
2166 if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
2167 buffer = tmp;
2168 }
2169 if ('..' === buffer) {
2170 this._path.pop();
2171 if ('/' !== c && '\\' !== c) {
2172 this._path.push('');
2173 }
2174 } else if ('.' === buffer && '/' !== c && '\\' !== c) {
2175 this._path.push('');
2176 } else if ('.' !== buffer) {
2177 if ('file' === this._scheme && this._path.length === 0 &&
2178 buffer.length === 2 && ALPHA.test(buffer[0]) &&
2179 buffer[1] === '|') {
2180 buffer = buffer[0] + ':';
2181 }
2182 this._path.push(buffer);
2183 }
2184 buffer = '';
2185 if ('?' === c) {
2186 this._query = '?';
2187 state = 'query';
2188 } else if ('#' === c) {
2189 this._fragment = '#';
2190 state = 'fragment';
2191 }
2192 } else if ('\t' !== c && '\n' !== c && '\r' !== c) {
2193 buffer += percentEscape(c);
2194 }
2195 break;
2196
2197 case 'query':
2198 if (!stateOverride && '#' === c) {
2199 this._fragment = '#';
2200 state = 'fragment';
2201 } else if (EOF !== c && '\t' !== c && '\n' !== c && '\r' !== c) {
2202 this._query += percentEscapeQuery(c);
2203 }
2204 break;
2205
2206 case 'fragment':
2207 if (EOF !== c && '\t' !== c && '\n' !== c && '\r' !== c) {
2208 this._fragment += c;
2209 }
2210 break;
2211 }
2212
2213 cursor++;
2214 }
2215 }
2216
2217 function clear() {
2218 this._scheme = '';
2219 this._schemeData = '';
2220 this._username = '';
2221 this._password = null;
2222 this._host = '';
2223 this._port = '';
2224 this._path = [];
2225 this._query = '';
2226 this._fragment = '';
2227 this._isInvalid = false;
2228 this._isRelative = false;
2229 }
2230
2231 // Does not process domain names or IP addresses.
2232 // Does not handle encoding for the query parameter.
2233 function JURL(url, base /* , encoding */) {
2234 if (base !== undefined && !(base instanceof JURL)) {
2235 base = new JURL(String(base));
2236 }
2237
2238 this._url = url;
2239 clear.call(this);
2240
2241 var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
2242 // encoding = encoding || 'utf-8'
2243
2244 parse.call(this, input, null, base);
2245 }
2246
2247 JURL.prototype = {
2248 toString: function() {
2249 return this.href;
2250 },
2251 get href() {
2252 if (this._isInvalid) {
2253 return this._url;
2254 }
2255 var authority = '';
2256 if ('' !== this._username || null !== this._password) {
2257 authority = this._username +
2258 (null !== this._password ? ':' + this._password : '') + '@';
2259 }
2260
2261 return this.protocol +
2262 (this._isRelative ? '//' + authority + this.host : '') +
2263 this.pathname + this._query + this._fragment;
2264 },
2265 set href(href) {
2266 clear.call(this);
2267 parse.call(this, href);
2268 },
2269
2270 get protocol() {
2271 return this._scheme + ':';
2272 },
2273 set protocol(protocol) {
2274 if (this._isInvalid) {
2275 return;
2276 }
2277 parse.call(this, protocol + ':', 'scheme start');
2278 },
2279
2280 get host() {
2281 return this._isInvalid ? '' : this._port ?
2282 this._host + ':' + this._port : this._host;
2283 },
2284 set host(host) {
2285 if (this._isInvalid || !this._isRelative) {
2286 return;
2287 }
2288 parse.call(this, host, 'host');
2289 },
2290
2291 get hostname() {
2292 return this._host;
2293 },
2294 set hostname(hostname) {
2295 if (this._isInvalid || !this._isRelative) {
2296 return;
2297 }
2298 parse.call(this, hostname, 'hostname');
2299 },
2300
2301 get port() {
2302 return this._port;
2303 },
2304 set port(port) {
2305 if (this._isInvalid || !this._isRelative) {
2306 return;
2307 }
2308 parse.call(this, port, 'port');
2309 },
2310
2311 get pathname() {
2312 return this._isInvalid ? '' : this._isRelative ?
2313 '/' + this._path.join('/') : this._schemeData;
2314 },
2315 set pathname(pathname) {
2316 if (this._isInvalid || !this._isRelative) {
2317 return;
2318 }
2319 this._path = [];
2320 parse.call(this, pathname, 'relative path start');
2321 },
2322
2323 get search() {
2324 return this._isInvalid || !this._query || '?' === this._query ?
2325 '' : this._query;
2326 },
2327 set search(search) {
2328 if (this._isInvalid || !this._isRelative) {
2329 return;
2330 }
2331 this._query = '?';
2332 if ('?' === search[0]) {
2333 search = search.slice(1);
2334 }
2335 parse.call(this, search, 'query');
2336 },
2337
2338 get hash() {
2339 return this._isInvalid || !this._fragment || '#' === this._fragment ?
2340 '' : this._fragment;
2341 },
2342 set hash(hash) {
2343 if (this._isInvalid) {
2344 return;
2345 }
2346 this._fragment = '#';
2347 if ('#' === hash[0]) {
2348 hash = hash.slice(1);
2349 }
2350 parse.call(this, hash, 'fragment');
2351 },
2352
2353 get origin() {
2354 var host;
2355 if (this._isInvalid || !this._scheme) {
2356 return '';
2357 }
2358 // javascript: Gecko returns String(""), WebKit/Blink String("null")
2359 // Gecko throws error for "data://"
2360 // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
2361 // Gecko returns String("") for file: mailto:
2362 // WebKit/Blink returns String("SCHEME://") for file: mailto:
2363 switch (this._scheme) {
2364 case 'data':
2365 case 'file':
2366 case 'javascript':
2367 case 'mailto':
2368 return 'null';
2369 }
2370 host = this.host;
2371 if (!host) {
2372 return '';
2373 }
2374 return this._scheme + '://' + host;
2375 }
2376 };
2377
2378 // Copy over the static methods
2379 var OriginalURL = scope.URL;
2380 if (OriginalURL) {
2381 JURL.createObjectURL = function(blob) {
2382 // IE extension allows a second optional options argument.
2383 // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
2384 return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
2385 };
2386 JURL.revokeObjectURL = function(url) {
2387 OriginalURL.revokeObjectURL(url);
2388 };
2389 }
2390
2391 scope.URL = JURL;
2392})(globalScope);
2393
2394exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX;
2395exports.IDENTITY_MATRIX = IDENTITY_MATRIX;
2396exports.OPS = OPS;
2397exports.VERBOSITY_LEVELS = VERBOSITY_LEVELS;
2398exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES;
2399exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
2400exports.AnnotationFieldFlag = AnnotationFieldFlag;
2401exports.AnnotationFlag = AnnotationFlag;
2402exports.AnnotationType = AnnotationType;
2403exports.FontType = FontType;
2404exports.ImageKind = ImageKind;
2405exports.InvalidPDFException = InvalidPDFException;
2406exports.MessageHandler = MessageHandler;
2407exports.MissingDataException = MissingDataException;
2408exports.MissingPDFException = MissingPDFException;
2409exports.NotImplementedException = NotImplementedException;
2410exports.PageViewport = PageViewport;
2411exports.PasswordException = PasswordException;
2412exports.PasswordResponses = PasswordResponses;
2413exports.StatTimer = StatTimer;
2414exports.StreamType = StreamType;
2415exports.TextRenderingMode = TextRenderingMode;
2416exports.UnexpectedResponseException = UnexpectedResponseException;
2417exports.UnknownErrorException = UnknownErrorException;
2418exports.Util = Util;
2419exports.XRefParseException = XRefParseException;
2420exports.arrayByteLength = arrayByteLength;
2421exports.arraysToBytes = arraysToBytes;
2422exports.assert = assert;
2423exports.bytesToString = bytesToString;
2424exports.createBlob = createBlob;
2425exports.createPromiseCapability = createPromiseCapability;
2426exports.createObjectURL = createObjectURL;
2427exports.deprecated = deprecated;
2428exports.error = error;
2429exports.getLookupTableFactory = getLookupTableFactory;
2430exports.getVerbosityLevel = getVerbosityLevel;
2431exports.globalScope = globalScope;
2432exports.info = info;
2433exports.isArray = isArray;
2434exports.isArrayBuffer = isArrayBuffer;
2435exports.isBool = isBool;
2436exports.isEmptyObj = isEmptyObj;
2437exports.isInt = isInt;
2438exports.isNum = isNum;
2439exports.isString = isString;
2440exports.isSpace = isSpace;
2441exports.isSameOrigin = isSameOrigin;
2442exports.isValidUrl = isValidUrl;
2443exports.isLittleEndian = isLittleEndian;
2444exports.isEvalSupported = isEvalSupported;
2445exports.loadJpegStream = loadJpegStream;
2446exports.log2 = log2;
2447exports.readInt8 = readInt8;
2448exports.readUint16 = readUint16;
2449exports.readUint32 = readUint32;
2450exports.removeNullCharacters = removeNullCharacters;
2451exports.setVerbosityLevel = setVerbosityLevel;
2452exports.shadow = shadow;
2453exports.string32 = string32;
2454exports.stringToBytes = stringToBytes;
2455exports.stringToPDFString = stringToPDFString;
2456exports.stringToUTF8String = stringToUTF8String;
2457exports.utf8StringToString = utf8StringToString;
2458exports.warn = warn;
2459}));
2460
2461
2462(function (root, factory) {
2463 {
2464 factory((root.pdfjsDisplayDOMUtils = {}), root.pdfjsSharedUtil);
2465 }
2466}(this, function (exports, sharedUtil) {
2467
2468var removeNullCharacters = sharedUtil.removeNullCharacters;
2469var warn = sharedUtil.warn;
2470
2471/**
2472 * Optimised CSS custom property getter/setter.
2473 * @class
2474 */
2475var CustomStyle = (function CustomStyleClosure() {
2476
2477 // As noted on: http://www.zachstronaut.com/posts/2009/02/17/
2478 // animate-css-transforms-firefox-webkit.html
2479 // in some versions of IE9 it is critical that ms appear in this list
2480 // before Moz
2481 var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
2482 var _cache = Object.create(null);
2483
2484 function CustomStyle() {}
2485
2486 CustomStyle.getProp = function get(propName, element) {
2487 // check cache only when no element is given
2488 if (arguments.length === 1 && typeof _cache[propName] === 'string') {
2489 return _cache[propName];
2490 }
2491
2492 element = element || document.documentElement;
2493 var style = element.style, prefixed, uPropName;
2494
2495 // test standard property first
2496 if (typeof style[propName] === 'string') {
2497 return (_cache[propName] = propName);
2498 }
2499
2500 // capitalize
2501 uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
2502
2503 // test vendor specific properties
2504 for (var i = 0, l = prefixes.length; i < l; i++) {
2505 prefixed = prefixes[i] + uPropName;
2506 if (typeof style[prefixed] === 'string') {
2507 return (_cache[propName] = prefixed);
2508 }
2509 }
2510
2511 //if all fails then set to undefined
2512 return (_cache[propName] = 'undefined');
2513 };
2514
2515 CustomStyle.setProp = function set(propName, element, str) {
2516 var prop = this.getProp(propName);
2517 if (prop !== 'undefined') {
2518 element.style[prop] = str;
2519 }
2520 };
2521
2522 return CustomStyle;
2523})();
2524
2525function hasCanvasTypedArrays() {
2526 var canvas = document.createElement('canvas');
2527 canvas.width = canvas.height = 1;
2528 var ctx = canvas.getContext('2d');
2529 var imageData = ctx.createImageData(1, 1);
2530 return (typeof imageData.data.buffer !== 'undefined');
2531}
2532
2533var LinkTarget = {
2534 NONE: 0, // Default value.
2535 SELF: 1,
2536 BLANK: 2,
2537 PARENT: 3,
2538 TOP: 4,
2539};
2540
2541var LinkTargetStringMap = [
2542 '',
2543 '_self',
2544 '_blank',
2545 '_parent',
2546 '_top'
2547];
2548
2549/**
2550 * @typedef ExternalLinkParameters
2551 * @typedef {Object} ExternalLinkParameters
2552 * @property {string} url - An absolute URL.
2553 * @property {LinkTarget} target - The link target.
2554 * @property {string} rel - The link relationship.
2555 */
2556
2557/**
2558 * Adds various attributes (href, title, target, rel) to hyperlinks.
2559 * @param {HTMLLinkElement} link - The link element.
2560 * @param {ExternalLinkParameters} params
2561 */
2562function addLinkAttributes(link, params) {
2563 var url = params && params.url;
2564 link.href = link.title = (url ? removeNullCharacters(url) : '');
2565
2566 if (url) {
2567 var target = params.target;
2568 if (typeof target === 'undefined') {
2569 target = getDefaultSetting('externalLinkTarget');
2570 }
2571 link.target = LinkTargetStringMap[target];
2572
2573 var rel = params.rel;
2574 if (typeof rel === 'undefined') {
2575 rel = getDefaultSetting('externalLinkRel');
2576 }
2577 link.rel = rel;
2578 }
2579}
2580
2581// Gets the file name from a given URL.
2582function getFilenameFromUrl(url) {
2583 var anchor = url.indexOf('#');
2584 var query = url.indexOf('?');
2585 var end = Math.min(
2586 anchor > 0 ? anchor : url.length,
2587 query > 0 ? query : url.length);
2588 return url.substring(url.lastIndexOf('/', end) + 1, end);
2589}
2590
2591function getDefaultSetting(id) {
2592 // The list of the settings and their default is maintained for backward
2593 // compatibility and shall not be extended or modified. See also global.js.
2594 var globalSettings = sharedUtil.globalScope.PDFJS;
2595 switch (id) {
2596 case 'pdfBug':
2597 return globalSettings ? globalSettings.pdfBug : false;
2598 case 'disableAutoFetch':
2599 return globalSettings ? globalSettings.disableAutoFetch : false;
2600 case 'disableStream':
2601 return globalSettings ? globalSettings.disableStream : false;
2602 case 'disableRange':
2603 return globalSettings ? globalSettings.disableRange : false;
2604 case 'disableFontFace':
2605 return globalSettings ? globalSettings.disableFontFace : false;
2606 case 'disableCreateObjectURL':
2607 return globalSettings ? globalSettings.disableCreateObjectURL : false;
2608 case 'disableWebGL':
2609 return globalSettings ? globalSettings.disableWebGL : true;
2610 case 'cMapUrl':
2611 return globalSettings ? globalSettings.cMapUrl : null;
2612 case 'cMapPacked':
2613 return globalSettings ? globalSettings.cMapPacked : false;
2614 case 'postMessageTransfers':
2615 return globalSettings ? globalSettings.postMessageTransfers : true;
2616 case 'workerSrc':
2617 return globalSettings ? globalSettings.workerSrc : null;
2618 case 'disableWorker':
2619 return globalSettings ? globalSettings.disableWorker : false;
2620 case 'maxImageSize':
2621 return globalSettings ? globalSettings.maxImageSize : -1;
2622 case 'imageResourcesPath':
2623 return globalSettings ? globalSettings.imageResourcesPath : '';
2624 case 'isEvalSupported':
2625 return globalSettings ? globalSettings.isEvalSupported : true;
2626 case 'externalLinkTarget':
2627 if (!globalSettings) {
2628 return LinkTarget.NONE;
2629 }
2630 switch (globalSettings.externalLinkTarget) {
2631 case LinkTarget.NONE:
2632 case LinkTarget.SELF:
2633 case LinkTarget.BLANK:
2634 case LinkTarget.PARENT:
2635 case LinkTarget.TOP:
2636 return globalSettings.externalLinkTarget;
2637 }
2638 warn('PDFJS.externalLinkTarget is invalid: ' +
2639 globalSettings.externalLinkTarget);
2640 // Reset the external link target, to suppress further warnings.
2641 globalSettings.externalLinkTarget = LinkTarget.NONE;
2642 return LinkTarget.NONE;
2643 case 'externalLinkRel':
2644 return globalSettings ? globalSettings.externalLinkRel : 'noreferrer';
2645 case 'enableStats':
2646 return !!(globalSettings && globalSettings.enableStats);
2647 default:
2648 throw new Error('Unknown default setting: ' + id);
2649 }
2650}
2651
2652function isExternalLinkTargetSet() {
2653 var externalLinkTarget = getDefaultSetting('externalLinkTarget');
2654 switch (externalLinkTarget) {
2655 case LinkTarget.NONE:
2656 return false;
2657 case LinkTarget.SELF:
2658 case LinkTarget.BLANK:
2659 case LinkTarget.PARENT:
2660 case LinkTarget.TOP:
2661 return true;
2662 }
2663}
2664
2665exports.CustomStyle = CustomStyle;
2666exports.addLinkAttributes = addLinkAttributes;
2667exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
2668exports.getFilenameFromUrl = getFilenameFromUrl;
2669exports.LinkTarget = LinkTarget;
2670exports.hasCanvasTypedArrays = hasCanvasTypedArrays;
2671exports.getDefaultSetting = getDefaultSetting;
2672}));
2673
2674
2675(function (root, factory) {
2676 {
2677 factory((root.pdfjsDisplayFontLoader = {}), root.pdfjsSharedUtil);
2678 }
2679}(this, function (exports, sharedUtil) {
2680
2681var assert = sharedUtil.assert;
2682var bytesToString = sharedUtil.bytesToString;
2683var string32 = sharedUtil.string32;
2684var shadow = sharedUtil.shadow;
2685var warn = sharedUtil.warn;
2686
2687function FontLoader(docId) {
2688 this.docId = docId;
2689 this.styleElement = null;
2690 this.nativeFontFaces = [];
2691 this.loadTestFontId = 0;
2692 this.loadingContext = {
2693 requests: [],
2694 nextRequestId: 0
2695 };
2696}
2697FontLoader.prototype = {
2698 insertRule: function fontLoaderInsertRule(rule) {
2699 var styleElement = this.styleElement;
2700 if (!styleElement) {
2701 styleElement = this.styleElement = document.createElement('style');
2702 styleElement.id = 'PDFJS_FONT_STYLE_TAG_' + this.docId;
2703 document.documentElement.getElementsByTagName('head')[0].appendChild(
2704 styleElement);
2705 }
2706
2707 var styleSheet = styleElement.sheet;
2708 styleSheet.insertRule(rule, styleSheet.cssRules.length);
2709 },
2710
2711 clear: function fontLoaderClear() {
2712 var styleElement = this.styleElement;
2713 if (styleElement) {
2714 styleElement.parentNode.removeChild(styleElement);
2715 styleElement = this.styleElement = null;
2716 }
2717 this.nativeFontFaces.forEach(function(nativeFontFace) {
2718 document.fonts.delete(nativeFontFace);
2719 });
2720 this.nativeFontFaces.length = 0;
2721 },
2722 get loadTestFont() {
2723 // This is a CFF font with 1 glyph for '.' that fills its entire width and
2724 // height.
2725 return shadow(this, 'loadTestFont', atob(
2726 'T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQ' +
2727 'AABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwA' +
2728 'AAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbm' +
2729 'FtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAA' +
2730 'AADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6A' +
2731 'ABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAA' +
2732 'MQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAA' +
2733 'AAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAA' +
2734 'AAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQ' +
2735 'AAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMA' +
2736 'AQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAA' +
2737 'EAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAA' +
2738 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAA' +
2739 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
2740 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
2741 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
2742 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA' +
2743 'AAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgc' +
2744 'A/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF' +
2745 'hYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQA' +
2746 'AAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAg' +
2747 'ABAAAAAAAAAAAD6AAAAAAAAA=='
2748 ));
2749 },
2750
2751 addNativeFontFace: function fontLoader_addNativeFontFace(nativeFontFace) {
2752 this.nativeFontFaces.push(nativeFontFace);
2753 document.fonts.add(nativeFontFace);
2754 },
2755
2756 bind: function fontLoaderBind(fonts, callback) {
2757 var rules = [];
2758 var fontsToLoad = [];
2759 var fontLoadPromises = [];
2760 var getNativeFontPromise = function(nativeFontFace) {
2761 // Return a promise that is always fulfilled, even when the font fails to
2762 // load.
2763 return nativeFontFace.loaded.catch(function(e) {
2764 warn('Failed to load font "' + nativeFontFace.family + '": ' + e);
2765 });
2766 };
2767 for (var i = 0, ii = fonts.length; i < ii; i++) {
2768 var font = fonts[i];
2769
2770 // Add the font to the DOM only once or skip if the font
2771 // is already loaded.
2772 if (font.attached || font.loading === false) {
2773 continue;
2774 }
2775 font.attached = true;
2776
2777 if (FontLoader.isFontLoadingAPISupported) {
2778 var nativeFontFace = font.createNativeFontFace();
2779 if (nativeFontFace) {
2780 this.addNativeFontFace(nativeFontFace);
2781 fontLoadPromises.push(getNativeFontPromise(nativeFontFace));
2782 }
2783 } else {
2784 var rule = font.createFontFaceRule();
2785 if (rule) {
2786 this.insertRule(rule);
2787 rules.push(rule);
2788 fontsToLoad.push(font);
2789 }
2790 }
2791 }
2792
2793 var request = this.queueLoadingCallback(callback);
2794 if (FontLoader.isFontLoadingAPISupported) {
2795 Promise.all(fontLoadPromises).then(function() {
2796 request.complete();
2797 });
2798 } else if (rules.length > 0 && !FontLoader.isSyncFontLoadingSupported) {
2799 this.prepareFontLoadEvent(rules, fontsToLoad, request);
2800 } else {
2801 request.complete();
2802 }
2803 },
2804
2805 queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) {
2806 function LoadLoader_completeRequest() {
2807 assert(!request.end, 'completeRequest() cannot be called twice');
2808 request.end = Date.now();
2809
2810 // sending all completed requests in order how they were queued
2811 while (context.requests.length > 0 && context.requests[0].end) {
2812 var otherRequest = context.requests.shift();
2813 setTimeout(otherRequest.callback, 0);
2814 }
2815 }
2816
2817 var context = this.loadingContext;
2818 var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++);
2819 var request = {
2820 id: requestId,
2821 complete: LoadLoader_completeRequest,
2822 callback: callback,
2823 started: Date.now()
2824 };
2825 context.requests.push(request);
2826 return request;
2827 },
2828
2829 prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules,
2830 fonts,
2831 request) {
2832 /** Hack begin */
2833 // There's currently no event when a font has finished downloading so the
2834 // following code is a dirty hack to 'guess' when a font is
2835 // ready. It's assumed fonts are loaded in order, so add a known test
2836 // font after the desired fonts and then test for the loading of that
2837 // test font.
2838
2839 function int32(data, offset) {
2840 return (data.charCodeAt(offset) << 24) |
2841 (data.charCodeAt(offset + 1) << 16) |
2842 (data.charCodeAt(offset + 2) << 8) |
2843 (data.charCodeAt(offset + 3) & 0xff);
2844 }
2845
2846 function spliceString(s, offset, remove, insert) {
2847 var chunk1 = s.substr(0, offset);
2848 var chunk2 = s.substr(offset + remove);
2849 return chunk1 + insert + chunk2;
2850 }
2851
2852 var i, ii;
2853
2854 var canvas = document.createElement('canvas');
2855 canvas.width = 1;
2856 canvas.height = 1;
2857 var ctx = canvas.getContext('2d');
2858
2859 var called = 0;
2860 function isFontReady(name, callback) {
2861 called++;
2862 // With setTimeout clamping this gives the font ~100ms to load.
2863 if(called > 30) {
2864 warn('Load test font never loaded.');
2865 callback();
2866 return;
2867 }
2868 ctx.font = '30px ' + name;
2869 ctx.fillText('.', 0, 20);
2870 var imageData = ctx.getImageData(0, 0, 1, 1);
2871 if (imageData.data[3] > 0) {
2872 callback();
2873 return;
2874 }
2875 setTimeout(isFontReady.bind(null, name, callback));
2876 }
2877
2878 var loadTestFontId = 'lt' + Date.now() + this.loadTestFontId++;
2879 // Chromium seems to cache fonts based on a hash of the actual font data,
2880 // so the font must be modified for each load test else it will appear to
2881 // be loaded already.
2882 // TODO: This could maybe be made faster by avoiding the btoa of the full
2883 // font by splitting it in chunks before hand and padding the font id.
2884 var data = this.loadTestFont;
2885 var COMMENT_OFFSET = 976; // has to be on 4 byte boundary (for checksum)
2886 data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length,
2887 loadTestFontId);
2888 // CFF checksum is important for IE, adjusting it
2889 var CFF_CHECKSUM_OFFSET = 16;
2890 var XXXX_VALUE = 0x58585858; // the "comment" filled with 'X'
2891 var checksum = int32(data, CFF_CHECKSUM_OFFSET);
2892 for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {
2893 checksum = (checksum - XXXX_VALUE + int32(loadTestFontId, i)) | 0;
2894 }
2895 if (i < loadTestFontId.length) { // align to 4 bytes boundary
2896 checksum = (checksum - XXXX_VALUE +
2897 int32(loadTestFontId + 'XXX', i)) | 0;
2898 }
2899 data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum));
2900
2901 var url = 'url(data:font/opentype;base64,' + btoa(data) + ');';
2902 var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' +
2903 url + '}';
2904 this.insertRule(rule);
2905
2906 var names = [];
2907 for (i = 0, ii = fonts.length; i < ii; i++) {
2908 names.push(fonts[i].loadedName);
2909 }
2910 names.push(loadTestFontId);
2911
2912 var div = document.createElement('div');
2913 div.setAttribute('style',
2914 'visibility: hidden;' +
2915 'width: 10px; height: 10px;' +
2916 'position: absolute; top: 0px; left: 0px;');
2917 for (i = 0, ii = names.length; i < ii; ++i) {
2918 var span = document.createElement('span');
2919 span.textContent = 'Hi';
2920 span.style.fontFamily = names[i];
2921 div.appendChild(span);
2922 }
2923 document.body.appendChild(div);
2924
2925 isFontReady(loadTestFontId, function() {
2926 document.body.removeChild(div);
2927 request.complete();
2928 });
2929 /** Hack end */
2930 }
2931};
2932FontLoader.isFontLoadingAPISupported = typeof document !== 'undefined' &&
2933 !!document.fonts;
2934Object.defineProperty(FontLoader, 'isSyncFontLoadingSupported', {
2935 get: function () {
2936 if (typeof navigator === 'undefined') {
2937 // node.js - we can pretend sync font loading is supported.
2938 return shadow(FontLoader, 'isSyncFontLoadingSupported', true);
2939 }
2940
2941 var supported = false;
2942
2943 // User agent string sniffing is bad, but there is no reliable way to tell
2944 // if font is fully loaded and ready to be used with canvas.
2945 var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(navigator.userAgent);
2946 if (m && m[1] >= 14) {
2947 supported = true;
2948 }
2949 // TODO other browsers
2950 return shadow(FontLoader, 'isSyncFontLoadingSupported', supported);
2951 },
2952 enumerable: true,
2953 configurable: true
2954});
2955
2956var IsEvalSupportedCached = {
2957 get value() {
2958 return shadow(this, 'value', sharedUtil.isEvalSupported());
2959 }
2960};
2961
2962var FontFaceObject = (function FontFaceObjectClosure() {
2963 function FontFaceObject(translatedData, options) {
2964 this.compiledGlyphs = Object.create(null);
2965 // importing translated data
2966 for (var i in translatedData) {
2967 this[i] = translatedData[i];
2968 }
2969 this.options = options;
2970 }
2971 FontFaceObject.prototype = {
2972 createNativeFontFace: function FontFaceObject_createNativeFontFace() {
2973 if (!this.data) {
2974 return null;
2975 }
2976
2977 if (this.options.disableFontFace) {
2978 this.disableFontFace = true;
2979 return null;
2980 }
2981
2982 var nativeFontFace = new FontFace(this.loadedName, this.data, {});
2983
2984 if (this.options.fontRegistry) {
2985 this.options.fontRegistry.registerFont(this);
2986 }
2987 return nativeFontFace;
2988 },
2989
2990 createFontFaceRule: function FontFaceObject_createFontFaceRule() {
2991 if (!this.data) {
2992 return null;
2993 }
2994
2995 if (this.options.disableFontFace) {
2996 this.disableFontFace = true;
2997 return null;
2998 }
2999
3000 var data = bytesToString(new Uint8Array(this.data));
3001 var fontName = this.loadedName;
3002
3003 // Add the font-face rule to the document
3004 var url = ('url(data:' + this.mimetype + ';base64,' + btoa(data) + ');');
3005 var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
3006
3007 if (this.options.fontRegistry) {
3008 this.options.fontRegistry.registerFont(this, url);
3009 }
3010
3011 return rule;
3012 },
3013
3014 getPathGenerator:
3015 function FontFaceObject_getPathGenerator(objs, character) {
3016 if (!(character in this.compiledGlyphs)) {
3017 var cmds = objs.get(this.loadedName + '_path_' + character);
3018 var current, i, len;
3019
3020 // If we can, compile cmds into JS for MAXIMUM SPEED
3021 if (this.options.isEvalSupported && IsEvalSupportedCached.value) {
3022 var args, js = '';
3023 for (i = 0, len = cmds.length; i < len; i++) {
3024 current = cmds[i];
3025
3026 if (current.args !== undefined) {
3027 args = current.args.join(',');
3028 } else {
3029 args = '';
3030 }
3031
3032 js += 'c.' + current.cmd + '(' + args + ');\n';
3033 }
3034 /* jshint -W054 */
3035 this.compiledGlyphs[character] = new Function('c', 'size', js);
3036 } else {
3037 // But fall back on using Function.prototype.apply() if we're
3038 // blocked from using eval() for whatever reason (like CSP policies)
3039 this.compiledGlyphs[character] = function(c, size) {
3040 for (i = 0, len = cmds.length; i < len; i++) {
3041 current = cmds[i];
3042
3043 if (current.cmd === 'scale') {
3044 current.args = [size, -size];
3045 }
3046
3047 c[current.cmd].apply(c, current.args);
3048 }
3049 };
3050 }
3051 }
3052 return this.compiledGlyphs[character];
3053 }
3054 };
3055 return FontFaceObject;
3056})();
3057
3058exports.FontFaceObject = FontFaceObject;
3059exports.FontLoader = FontLoader;
3060}));
3061
3062
3063(function (root, factory) {
3064 {
3065 factory((root.pdfjsDisplayMetadata = {}), root.pdfjsSharedUtil);
3066 }
3067}(this, function (exports, sharedUtil) {
3068
3069var error = sharedUtil.error;
3070
3071 function fixMetadata(meta) {
3072 return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) {
3073 var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g,
3074 function(code, d1, d2, d3) {
3075 return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);
3076 });
3077 var chars = '';
3078 for (var i = 0; i < bytes.length; i += 2) {
3079 var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
3080 chars += code >= 32 && code < 127 && code !== 60 && code !== 62 &&
3081 code !== 38 && false ? String.fromCharCode(code) :
3082 '&#x' + (0x10000 + code).toString(16).substring(1) + ';';
3083 }
3084 return '>' + chars;
3085 });
3086 }
3087
3088 function Metadata(meta) {
3089 if (typeof meta === 'string') {
3090 // Ghostscript produces invalid metadata
3091 meta = fixMetadata(meta);
3092
3093 var parser = new DOMParser();
3094 meta = parser.parseFromString(meta, 'application/xml');
3095 } else if (!(meta instanceof Document)) {
3096 error('Metadata: Invalid metadata object');
3097 }
3098
3099 this.metaDocument = meta;
3100 this.metadata = Object.create(null);
3101 this.parse();
3102 }
3103
3104 Metadata.prototype = {
3105 parse: function Metadata_parse() {
3106 var doc = this.metaDocument;
3107 var rdf = doc.documentElement;
3108
3109 if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta>
3110 rdf = rdf.firstChild;
3111 while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
3112 rdf = rdf.nextSibling;
3113 }
3114 }
3115
3116 var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
3117 if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) {
3118 return;
3119 }
3120
3121 var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
3122 for (i = 0, length = children.length; i < length; i++) {
3123 desc = children[i];
3124 if (desc.nodeName.toLowerCase() !== 'rdf:description') {
3125 continue;
3126 }
3127
3128 for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
3129 if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') {
3130 entry = desc.childNodes[ii];
3131 name = entry.nodeName.toLowerCase();
3132 this.metadata[name] = entry.textContent.trim();
3133 }
3134 }
3135 }
3136 },
3137
3138 get: function Metadata_get(name) {
3139 return this.metadata[name] || null;
3140 },
3141
3142 has: function Metadata_has(name) {
3143 return typeof this.metadata[name] !== 'undefined';
3144 }
3145 };
3146
3147exports.Metadata = Metadata;
3148}));
3149
3150
3151(function (root, factory) {
3152 {
3153 factory((root.pdfjsDisplaySVG = {}), root.pdfjsSharedUtil);
3154 }
3155}(this, function (exports, sharedUtil) {
3156var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
3157var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX;
3158var ImageKind = sharedUtil.ImageKind;
3159var OPS = sharedUtil.OPS;
3160var Util = sharedUtil.Util;
3161var isNum = sharedUtil.isNum;
3162var isArray = sharedUtil.isArray;
3163var warn = sharedUtil.warn;
3164var createObjectURL = sharedUtil.createObjectURL;
3165
3166var SVG_DEFAULTS = {
3167 fontStyle: 'normal',
3168 fontWeight: 'normal',
3169 fillColor: '#000000'
3170};
3171
3172var convertImgDataToPng = (function convertImgDataToPngClosure() {
3173 var PNG_HEADER =
3174 new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
3175
3176 var CHUNK_WRAPPER_SIZE = 12;
3177
3178 var crcTable = new Int32Array(256);
3179 for (var i = 0; i < 256; i++) {
3180 var c = i;
3181 for (var h = 0; h < 8; h++) {
3182 if (c & 1) {
3183 c = 0xedB88320 ^ ((c >> 1) & 0x7fffffff);
3184 } else {
3185 c = (c >> 1) & 0x7fffffff;
3186 }
3187 }
3188 crcTable[i] = c;
3189 }
3190
3191 function crc32(data, start, end) {
3192 var crc = -1;
3193 for (var i = start; i < end; i++) {
3194 var a = (crc ^ data[i]) & 0xff;
3195 var b = crcTable[a];
3196 crc = (crc >>> 8) ^ b;
3197 }
3198 return crc ^ -1;
3199 }
3200
3201 function writePngChunk(type, body, data, offset) {
3202 var p = offset;
3203 var len = body.length;
3204
3205 data[p] = len >> 24 & 0xff;
3206 data[p + 1] = len >> 16 & 0xff;
3207 data[p + 2] = len >> 8 & 0xff;
3208 data[p + 3] = len & 0xff;
3209 p += 4;
3210
3211 data[p] = type.charCodeAt(0) & 0xff;
3212 data[p + 1] = type.charCodeAt(1) & 0xff;
3213 data[p + 2] = type.charCodeAt(2) & 0xff;
3214 data[p + 3] = type.charCodeAt(3) & 0xff;
3215 p += 4;
3216
3217 data.set(body, p);
3218 p += body.length;
3219
3220 var crc = crc32(data, offset + 4, p);
3221
3222 data[p] = crc >> 24 & 0xff;
3223 data[p + 1] = crc >> 16 & 0xff;
3224 data[p + 2] = crc >> 8 & 0xff;
3225 data[p + 3] = crc & 0xff;
3226 }
3227
3228 function adler32(data, start, end) {
3229 var a = 1;
3230 var b = 0;
3231 for (var i = start; i < end; ++i) {
3232 a = (a + (data[i] & 0xff)) % 65521;
3233 b = (b + a) % 65521;
3234 }
3235 return (b << 16) | a;
3236 }
3237
3238 function encode(imgData, kind, forceDataSchema) {
3239 var width = imgData.width;
3240 var height = imgData.height;
3241 var bitDepth, colorType, lineSize;
3242 var bytes = imgData.data;
3243
3244 switch (kind) {
3245 case ImageKind.GRAYSCALE_1BPP:
3246 colorType = 0;
3247 bitDepth = 1;
3248 lineSize = (width + 7) >> 3;
3249 break;
3250 case ImageKind.RGB_24BPP:
3251 colorType = 2;
3252 bitDepth = 8;
3253 lineSize = width * 3;
3254 break;
3255 case ImageKind.RGBA_32BPP:
3256 colorType = 6;
3257 bitDepth = 8;
3258 lineSize = width * 4;
3259 break;
3260 default:
3261 throw new Error('invalid format');
3262 }
3263
3264 // prefix every row with predictor 0
3265 var literals = new Uint8Array((1 + lineSize) * height);
3266 var offsetLiterals = 0, offsetBytes = 0;
3267 var y, i;
3268 for (y = 0; y < height; ++y) {
3269 literals[offsetLiterals++] = 0; // no prediction
3270 literals.set(bytes.subarray(offsetBytes, offsetBytes + lineSize),
3271 offsetLiterals);
3272 offsetBytes += lineSize;
3273 offsetLiterals += lineSize;
3274 }
3275
3276 if (kind === ImageKind.GRAYSCALE_1BPP) {
3277 // inverting for B/W
3278 offsetLiterals = 0;
3279 for (y = 0; y < height; y++) {
3280 offsetLiterals++; // skipping predictor
3281 for (i = 0; i < lineSize; i++) {
3282 literals[offsetLiterals++] ^= 0xFF;
3283 }
3284 }
3285 }
3286
3287 var ihdr = new Uint8Array([
3288 width >> 24 & 0xff,
3289 width >> 16 & 0xff,
3290 width >> 8 & 0xff,
3291 width & 0xff,
3292 height >> 24 & 0xff,
3293 height >> 16 & 0xff,
3294 height >> 8 & 0xff,
3295 height & 0xff,
3296 bitDepth, // bit depth
3297 colorType, // color type
3298 0x00, // compression method
3299 0x00, // filter method
3300 0x00 // interlace method
3301 ]);
3302
3303 var len = literals.length;
3304 var maxBlockLength = 0xFFFF;
3305
3306 var deflateBlocks = Math.ceil(len / maxBlockLength);
3307 var idat = new Uint8Array(2 + len + deflateBlocks * 5 + 4);
3308 var pi = 0;
3309 idat[pi++] = 0x78; // compression method and flags
3310 idat[pi++] = 0x9c; // flags
3311
3312 var pos = 0;
3313 while (len > maxBlockLength) {
3314 // writing non-final DEFLATE blocks type 0 and length of 65535
3315 idat[pi++] = 0x00;
3316 idat[pi++] = 0xff;
3317 idat[pi++] = 0xff;
3318 idat[pi++] = 0x00;
3319 idat[pi++] = 0x00;
3320 idat.set(literals.subarray(pos, pos + maxBlockLength), pi);
3321 pi += maxBlockLength;
3322 pos += maxBlockLength;
3323 len -= maxBlockLength;
3324 }
3325
3326 // writing non-final DEFLATE blocks type 0
3327 idat[pi++] = 0x01;
3328 idat[pi++] = len & 0xff;
3329 idat[pi++] = len >> 8 & 0xff;
3330 idat[pi++] = (~len & 0xffff) & 0xff;
3331 idat[pi++] = (~len & 0xffff) >> 8 & 0xff;
3332 idat.set(literals.subarray(pos), pi);
3333 pi += literals.length - pos;
3334
3335 var adler = adler32(literals, 0, literals.length); // checksum
3336 idat[pi++] = adler >> 24 & 0xff;
3337 idat[pi++] = adler >> 16 & 0xff;
3338 idat[pi++] = adler >> 8 & 0xff;
3339 idat[pi++] = adler & 0xff;
3340
3341 // PNG will consists: header, IHDR+data, IDAT+data, and IEND.
3342 var pngLength = PNG_HEADER.length + (CHUNK_WRAPPER_SIZE * 3) +
3343 ihdr.length + idat.length;
3344 var data = new Uint8Array(pngLength);
3345 var offset = 0;
3346 data.set(PNG_HEADER, offset);
3347 offset += PNG_HEADER.length;
3348 writePngChunk('IHDR', ihdr, data, offset);
3349 offset += CHUNK_WRAPPER_SIZE + ihdr.length;
3350 writePngChunk('IDATA', idat, data, offset);
3351 offset += CHUNK_WRAPPER_SIZE + idat.length;
3352 writePngChunk('IEND', new Uint8Array(0), data, offset);
3353
3354 return createObjectURL(data, 'image/png', forceDataSchema);
3355 }
3356
3357 return function convertImgDataToPng(imgData, forceDataSchema) {
3358 var kind = (imgData.kind === undefined ?
3359 ImageKind.GRAYSCALE_1BPP : imgData.kind);
3360 return encode(imgData, kind, forceDataSchema);
3361 };
3362})();
3363
3364var SVGExtraState = (function SVGExtraStateClosure() {
3365 function SVGExtraState() {
3366 this.fontSizeScale = 1;
3367 this.fontWeight = SVG_DEFAULTS.fontWeight;
3368 this.fontSize = 0;
3369
3370 this.textMatrix = IDENTITY_MATRIX;
3371 this.fontMatrix = FONT_IDENTITY_MATRIX;
3372 this.leading = 0;
3373
3374 // Current point (in user coordinates)
3375 this.x = 0;
3376 this.y = 0;
3377
3378 // Start of text line (in text coordinates)
3379 this.lineX = 0;
3380 this.lineY = 0;
3381
3382 // Character and word spacing
3383 this.charSpacing = 0;
3384 this.wordSpacing = 0;
3385 this.textHScale = 1;
3386 this.textRise = 0;
3387
3388 // Default foreground and background colors
3389 this.fillColor = SVG_DEFAULTS.fillColor;
3390 this.strokeColor = '#000000';
3391
3392 this.fillAlpha = 1;
3393 this.strokeAlpha = 1;
3394 this.lineWidth = 1;
3395 this.lineJoin = '';
3396 this.lineCap = '';
3397 this.miterLimit = 0;
3398
3399 this.dashArray = [];
3400 this.dashPhase = 0;
3401
3402 this.dependencies = [];
3403
3404 // Clipping
3405 this.clipId = '';
3406 this.pendingClip = false;
3407
3408 this.maskId = '';
3409 }
3410
3411 SVGExtraState.prototype = {
3412 clone: function SVGExtraState_clone() {
3413 return Object.create(this);
3414 },
3415 setCurrentPoint: function SVGExtraState_setCurrentPoint(x, y) {
3416 this.x = x;
3417 this.y = y;
3418 }
3419 };
3420 return SVGExtraState;
3421})();
3422
3423var SVGGraphics = (function SVGGraphicsClosure() {
3424 function createScratchSVG(width, height) {
3425 var NS = 'http://www.w3.org/2000/svg';
3426 var svg = document.createElementNS(NS, 'svg:svg');
3427 svg.setAttributeNS(null, 'version', '1.1');
3428 svg.setAttributeNS(null, 'width', width + 'px');
3429 svg.setAttributeNS(null, 'height', height + 'px');
3430 svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height);
3431 return svg;
3432 }
3433
3434 function opListToTree(opList) {
3435 var opTree = [];
3436 var tmp = [];
3437 var opListLen = opList.length;
3438
3439 for (var x = 0; x < opListLen; x++) {
3440 if (opList[x].fn === 'save') {
3441 opTree.push({'fnId': 92, 'fn': 'group', 'items': []});
3442 tmp.push(opTree);
3443 opTree = opTree[opTree.length - 1].items;
3444 continue;
3445 }
3446
3447 if(opList[x].fn === 'restore') {
3448 opTree = tmp.pop();
3449 } else {
3450 opTree.push(opList[x]);
3451 }
3452 }
3453 return opTree;
3454 }
3455
3456 /**
3457 * Formats float number.
3458 * @param value {number} number to format.
3459 * @returns {string}
3460 */
3461 function pf(value) {
3462 if (value === (value | 0)) { // integer number
3463 return value.toString();
3464 }
3465 var s = value.toFixed(10);
3466 var i = s.length - 1;
3467 if (s[i] !== '0') {
3468 return s;
3469 }
3470 // removing trailing zeros
3471 do {
3472 i--;
3473 } while (s[i] === '0');
3474 return s.substr(0, s[i] === '.' ? i : i + 1);
3475 }
3476
3477 /**
3478 * Formats transform matrix. The standard rotation, scale and translate
3479 * matrices are replaced by their shorter forms, and for identity matrix
3480 * returns empty string to save the memory.
3481 * @param m {Array} matrix to format.
3482 * @returns {string}
3483 */
3484 function pm(m) {
3485 if (m[4] === 0 && m[5] === 0) {
3486 if (m[1] === 0 && m[2] === 0) {
3487 if (m[0] === 1 && m[3] === 1) {
3488 return '';
3489 }
3490 return 'scale(' + pf(m[0]) + ' ' + pf(m[3]) + ')';
3491 }
3492 if (m[0] === m[3] && m[1] === -m[2]) {
3493 var a = Math.acos(m[0]) * 180 / Math.PI;
3494 return 'rotate(' + pf(a) + ')';
3495 }
3496 } else {
3497 if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1) {
3498 return 'translate(' + pf(m[4]) + ' ' + pf(m[5]) + ')';
3499 }
3500 }
3501 return 'matrix(' + pf(m[0]) + ' ' + pf(m[1]) + ' ' + pf(m[2]) + ' ' +
3502 pf(m[3]) + ' ' + pf(m[4]) + ' ' + pf(m[5]) + ')';
3503 }
3504
3505 function SVGGraphics(commonObjs, objs, forceDataSchema) {
3506 this.current = new SVGExtraState();
3507 this.transformMatrix = IDENTITY_MATRIX; // Graphics state matrix
3508 this.transformStack = [];
3509 this.extraStack = [];
3510 this.commonObjs = commonObjs;
3511 this.objs = objs;
3512 this.pendingEOFill = false;
3513
3514 this.embedFonts = false;
3515 this.embeddedFonts = Object.create(null);
3516 this.cssStyle = null;
3517 this.forceDataSchema = !!forceDataSchema;
3518 }
3519
3520 var NS = 'http://www.w3.org/2000/svg';
3521 var XML_NS = 'http://www.w3.org/XML/1998/namespace';
3522 var XLINK_NS = 'http://www.w3.org/1999/xlink';
3523 var LINE_CAP_STYLES = ['butt', 'round', 'square'];
3524 var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
3525 var clipCount = 0;
3526 var maskCount = 0;
3527
3528 SVGGraphics.prototype = {
3529 save: function SVGGraphics_save() {
3530 this.transformStack.push(this.transformMatrix);
3531 var old = this.current;
3532 this.extraStack.push(old);
3533 this.current = old.clone();
3534 },
3535
3536 restore: function SVGGraphics_restore() {
3537 this.transformMatrix = this.transformStack.pop();
3538 this.current = this.extraStack.pop();
3539
3540 this.tgrp = document.createElementNS(NS, 'svg:g');
3541 this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
3542 this.pgrp.appendChild(this.tgrp);
3543 },
3544
3545 group: function SVGGraphics_group(items) {
3546 this.save();
3547 this.executeOpTree(items);
3548 this.restore();
3549 },
3550
3551 loadDependencies: function SVGGraphics_loadDependencies(operatorList) {
3552 var fnArray = operatorList.fnArray;
3553 var fnArrayLen = fnArray.length;
3554 var argsArray = operatorList.argsArray;
3555
3556 var self = this;
3557 for (var i = 0; i < fnArrayLen; i++) {
3558 if (OPS.dependency === fnArray[i]) {
3559 var deps = argsArray[i];
3560 for (var n = 0, nn = deps.length; n < nn; n++) {
3561 var obj = deps[n];
3562 var common = obj.substring(0, 2) === 'g_';
3563 var promise;
3564 if (common) {
3565 promise = new Promise(function(resolve) {
3566 self.commonObjs.get(obj, resolve);
3567 });
3568 } else {
3569 promise = new Promise(function(resolve) {
3570 self.objs.get(obj, resolve);
3571 });
3572 }
3573 this.current.dependencies.push(promise);
3574 }
3575 }
3576 }
3577 return Promise.all(this.current.dependencies);
3578 },
3579
3580 transform: function SVGGraphics_transform(a, b, c, d, e, f) {
3581 var transformMatrix = [a, b, c, d, e, f];
3582 this.transformMatrix = Util.transform(this.transformMatrix,
3583 transformMatrix);
3584
3585 this.tgrp = document.createElementNS(NS, 'svg:g');
3586 this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
3587 },
3588
3589 getSVG: function SVGGraphics_getSVG(operatorList, viewport) {
3590 this.svg = createScratchSVG(viewport.width, viewport.height);
3591 this.viewport = viewport;
3592
3593 return this.loadDependencies(operatorList).then(function () {
3594 this.transformMatrix = IDENTITY_MATRIX;
3595 this.pgrp = document.createElementNS(NS, 'svg:g'); // Parent group
3596 this.pgrp.setAttributeNS(null, 'transform', pm(viewport.transform));
3597 this.tgrp = document.createElementNS(NS, 'svg:g'); // Transform group
3598 this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
3599 this.defs = document.createElementNS(NS, 'svg:defs');
3600 this.pgrp.appendChild(this.defs);
3601 this.pgrp.appendChild(this.tgrp);
3602 this.svg.appendChild(this.pgrp);
3603 var opTree = this.convertOpList(operatorList);
3604 this.executeOpTree(opTree);
3605 return this.svg;
3606 }.bind(this));
3607 },
3608
3609 convertOpList: function SVGGraphics_convertOpList(operatorList) {
3610 var argsArray = operatorList.argsArray;
3611 var fnArray = operatorList.fnArray;
3612 var fnArrayLen = fnArray.length;
3613 var REVOPS = [];
3614 var opList = [];
3615
3616 for (var op in OPS) {
3617 REVOPS[OPS[op]] = op;
3618 }
3619
3620 for (var x = 0; x < fnArrayLen; x++) {
3621 var fnId = fnArray[x];
3622 opList.push({'fnId' : fnId, 'fn': REVOPS[fnId], 'args': argsArray[x]});
3623 }
3624 return opListToTree(opList);
3625 },
3626
3627 executeOpTree: function SVGGraphics_executeOpTree(opTree) {
3628 var opTreeLen = opTree.length;
3629 for(var x = 0; x < opTreeLen; x++) {
3630 var fn = opTree[x].fn;
3631 var fnId = opTree[x].fnId;
3632 var args = opTree[x].args;
3633
3634 switch (fnId | 0) {
3635 case OPS.beginText:
3636 this.beginText();
3637 break;
3638 case OPS.setLeading:
3639 this.setLeading(args);
3640 break;
3641 case OPS.setLeadingMoveText:
3642 this.setLeadingMoveText(args[0], args[1]);
3643 break;
3644 case OPS.setFont:
3645 this.setFont(args);
3646 break;
3647 case OPS.showText:
3648 this.showText(args[0]);
3649 break;
3650 case OPS.showSpacedText:
3651 this.showText(args[0]);
3652 break;
3653 case OPS.endText:
3654 this.endText();
3655 break;
3656 case OPS.moveText:
3657 this.moveText(args[0], args[1]);
3658 break;
3659 case OPS.setCharSpacing:
3660 this.setCharSpacing(args[0]);
3661 break;
3662 case OPS.setWordSpacing:
3663 this.setWordSpacing(args[0]);
3664 break;
3665 case OPS.setHScale:
3666 this.setHScale(args[0]);
3667 break;
3668 case OPS.setTextMatrix:
3669 this.setTextMatrix(args[0], args[1], args[2],
3670 args[3], args[4], args[5]);
3671 break;
3672 case OPS.setLineWidth:
3673 this.setLineWidth(args[0]);
3674 break;
3675 case OPS.setLineJoin:
3676 this.setLineJoin(args[0]);
3677 break;
3678 case OPS.setLineCap:
3679 this.setLineCap(args[0]);
3680 break;
3681 case OPS.setMiterLimit:
3682 this.setMiterLimit(args[0]);
3683 break;
3684 case OPS.setFillRGBColor:
3685 this.setFillRGBColor(args[0], args[1], args[2]);
3686 break;
3687 case OPS.setStrokeRGBColor:
3688 this.setStrokeRGBColor(args[0], args[1], args[2]);
3689 break;
3690 case OPS.setDash:
3691 this.setDash(args[0], args[1]);
3692 break;
3693 case OPS.setGState:
3694 this.setGState(args[0]);
3695 break;
3696 case OPS.fill:
3697 this.fill();
3698 break;
3699 case OPS.eoFill:
3700 this.eoFill();
3701 break;
3702 case OPS.stroke:
3703 this.stroke();
3704 break;
3705 case OPS.fillStroke:
3706 this.fillStroke();
3707 break;
3708 case OPS.eoFillStroke:
3709 this.eoFillStroke();
3710 break;
3711 case OPS.clip:
3712 this.clip('nonzero');
3713 break;
3714 case OPS.eoClip:
3715 this.clip('evenodd');
3716 break;
3717 case OPS.paintSolidColorImageMask:
3718 this.paintSolidColorImageMask();
3719 break;
3720 case OPS.paintJpegXObject:
3721 this.paintJpegXObject(args[0], args[1], args[2]);
3722 break;
3723 case OPS.paintImageXObject:
3724 this.paintImageXObject(args[0]);
3725 break;
3726 case OPS.paintInlineImageXObject:
3727 this.paintInlineImageXObject(args[0]);
3728 break;
3729 case OPS.paintImageMaskXObject:
3730 this.paintImageMaskXObject(args[0]);
3731 break;
3732 case OPS.paintFormXObjectBegin:
3733 this.paintFormXObjectBegin(args[0], args[1]);
3734 break;
3735 case OPS.paintFormXObjectEnd:
3736 this.paintFormXObjectEnd();
3737 break;
3738 case OPS.closePath:
3739 this.closePath();
3740 break;
3741 case OPS.closeStroke:
3742 this.closeStroke();
3743 break;
3744 case OPS.closeFillStroke:
3745 this.closeFillStroke();
3746 break;
3747 case OPS.nextLine:
3748 this.nextLine();
3749 break;
3750 case OPS.transform:
3751 this.transform(args[0], args[1], args[2], args[3],
3752 args[4], args[5]);
3753 break;
3754 case OPS.constructPath:
3755 this.constructPath(args[0], args[1]);
3756 break;
3757 case OPS.endPath:
3758 this.endPath();
3759 break;
3760 case 92:
3761 this.group(opTree[x].items);
3762 break;
3763 default:
3764 warn('Unimplemented method '+ fn);
3765 break;
3766 }
3767 }
3768 },
3769
3770 setWordSpacing: function SVGGraphics_setWordSpacing(wordSpacing) {
3771 this.current.wordSpacing = wordSpacing;
3772 },
3773
3774 setCharSpacing: function SVGGraphics_setCharSpacing(charSpacing) {
3775 this.current.charSpacing = charSpacing;
3776 },
3777
3778 nextLine: function SVGGraphics_nextLine() {
3779 this.moveText(0, this.current.leading);
3780 },
3781
3782 setTextMatrix: function SVGGraphics_setTextMatrix(a, b, c, d, e, f) {
3783 var current = this.current;
3784 this.current.textMatrix = this.current.lineMatrix = [a, b, c, d, e, f];
3785
3786 this.current.x = this.current.lineX = 0;
3787 this.current.y = this.current.lineY = 0;
3788
3789 current.xcoords = [];
3790 current.tspan = document.createElementNS(NS, 'svg:tspan');
3791 current.tspan.setAttributeNS(null, 'font-family', current.fontFamily);
3792 current.tspan.setAttributeNS(null, 'font-size',
3793 pf(current.fontSize) + 'px');
3794 current.tspan.setAttributeNS(null, 'y', pf(-current.y));
3795
3796 current.txtElement = document.createElementNS(NS, 'svg:text');
3797 current.txtElement.appendChild(current.tspan);
3798 },
3799
3800 beginText: function SVGGraphics_beginText() {
3801 this.current.x = this.current.lineX = 0;
3802 this.current.y = this.current.lineY = 0;
3803 this.current.textMatrix = IDENTITY_MATRIX;
3804 this.current.lineMatrix = IDENTITY_MATRIX;
3805 this.current.tspan = document.createElementNS(NS, 'svg:tspan');
3806 this.current.txtElement = document.createElementNS(NS, 'svg:text');
3807 this.current.txtgrp = document.createElementNS(NS, 'svg:g');
3808 this.current.xcoords = [];
3809 },
3810
3811 moveText: function SVGGraphics_moveText(x, y) {
3812 var current = this.current;
3813 this.current.x = this.current.lineX += x;
3814 this.current.y = this.current.lineY += y;
3815
3816 current.xcoords = [];
3817 current.tspan = document.createElementNS(NS, 'svg:tspan');
3818 current.tspan.setAttributeNS(null, 'font-family', current.fontFamily);
3819 current.tspan.setAttributeNS(null, 'font-size',
3820 pf(current.fontSize) + 'px');
3821 current.tspan.setAttributeNS(null, 'y', pf(-current.y));
3822 },
3823
3824 showText: function SVGGraphics_showText(glyphs) {
3825 var current = this.current;
3826 var font = current.font;
3827 var fontSize = current.fontSize;
3828
3829 if (fontSize === 0) {
3830 return;
3831 }
3832
3833 var charSpacing = current.charSpacing;
3834 var wordSpacing = current.wordSpacing;
3835 var fontDirection = current.fontDirection;
3836 var textHScale = current.textHScale * fontDirection;
3837 var glyphsLength = glyphs.length;
3838 var vertical = font.vertical;
3839 var widthAdvanceScale = fontSize * current.fontMatrix[0];
3840
3841 var x = 0, i;
3842 for (i = 0; i < glyphsLength; ++i) {
3843 var glyph = glyphs[i];
3844 if (glyph === null) {
3845 // word break
3846 x += fontDirection * wordSpacing;
3847 continue;
3848 } else if (isNum(glyph)) {
3849 x += -glyph * fontSize * 0.001;
3850 continue;
3851 }
3852 current.xcoords.push(current.x + x * textHScale);
3853
3854 var width = glyph.width;
3855 var character = glyph.fontChar;
3856 var charWidth = width * widthAdvanceScale + charSpacing * fontDirection;
3857 x += charWidth;
3858
3859 current.tspan.textContent += character;
3860 }
3861 if (vertical) {
3862 current.y -= x * textHScale;
3863 } else {
3864 current.x += x * textHScale;
3865 }
3866
3867 current.tspan.setAttributeNS(null, 'x',
3868 current.xcoords.map(pf).join(' '));
3869 current.tspan.setAttributeNS(null, 'y', pf(-current.y));
3870 current.tspan.setAttributeNS(null, 'font-family', current.fontFamily);
3871 current.tspan.setAttributeNS(null, 'font-size',
3872 pf(current.fontSize) + 'px');
3873 if (current.fontStyle !== SVG_DEFAULTS.fontStyle) {
3874 current.tspan.setAttributeNS(null, 'font-style', current.fontStyle);
3875 }
3876 if (current.fontWeight !== SVG_DEFAULTS.fontWeight) {
3877 current.tspan.setAttributeNS(null, 'font-weight', current.fontWeight);
3878 }
3879 if (current.fillColor !== SVG_DEFAULTS.fillColor) {
3880 current.tspan.setAttributeNS(null, 'fill', current.fillColor);
3881 }
3882
3883 current.txtElement.setAttributeNS(null, 'transform',
3884 pm(current.textMatrix) +
3885 ' scale(1, -1)' );
3886 current.txtElement.setAttributeNS(XML_NS, 'xml:space', 'preserve');
3887 current.txtElement.appendChild(current.tspan);
3888 current.txtgrp.appendChild(current.txtElement);
3889
3890 this.tgrp.appendChild(current.txtElement);
3891
3892 },
3893
3894 setLeadingMoveText: function SVGGraphics_setLeadingMoveText(x, y) {
3895 this.setLeading(-y);
3896 this.moveText(x, y);
3897 },
3898
3899 addFontStyle: function SVGGraphics_addFontStyle(fontObj) {
3900 if (!this.cssStyle) {
3901 this.cssStyle = document.createElementNS(NS, 'svg:style');
3902 this.cssStyle.setAttributeNS(null, 'type', 'text/css');
3903 this.defs.appendChild(this.cssStyle);
3904 }
3905
3906 var url = createObjectURL(fontObj.data, fontObj.mimetype,
3907 this.forceDataSchema);
3908 this.cssStyle.textContent +=
3909 '@font-face { font-family: "' + fontObj.loadedName + '";' +
3910 ' src: url(' + url + '); }\n';
3911 },
3912
3913 setFont: function SVGGraphics_setFont(details) {
3914 var current = this.current;
3915 var fontObj = this.commonObjs.get(details[0]);
3916 var size = details[1];
3917 this.current.font = fontObj;
3918
3919 if (this.embedFonts && fontObj.data &&
3920 !this.embeddedFonts[fontObj.loadedName]) {
3921 this.addFontStyle(fontObj);
3922 this.embeddedFonts[fontObj.loadedName] = fontObj;
3923 }
3924
3925 current.fontMatrix = (fontObj.fontMatrix ?
3926 fontObj.fontMatrix : FONT_IDENTITY_MATRIX);
3927
3928 var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
3929 (fontObj.bold ? 'bold' : 'normal');
3930 var italic = fontObj.italic ? 'italic' : 'normal';
3931
3932 if (size < 0) {
3933 size = -size;
3934 current.fontDirection = -1;
3935 } else {
3936 current.fontDirection = 1;
3937 }
3938 current.fontSize = size;
3939 current.fontFamily = fontObj.loadedName;
3940 current.fontWeight = bold;
3941 current.fontStyle = italic;
3942
3943 current.tspan = document.createElementNS(NS, 'svg:tspan');
3944 current.tspan.setAttributeNS(null, 'y', pf(-current.y));
3945 current.xcoords = [];
3946 },
3947
3948 endText: function SVGGraphics_endText() {
3949 if (this.current.pendingClip) {
3950 this.cgrp.appendChild(this.tgrp);
3951 this.pgrp.appendChild(this.cgrp);
3952 } else {
3953 this.pgrp.appendChild(this.tgrp);
3954 }
3955 this.tgrp = document.createElementNS(NS, 'svg:g');
3956 this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
3957 },
3958
3959 // Path properties
3960 setLineWidth: function SVGGraphics_setLineWidth(width) {
3961 this.current.lineWidth = width;
3962 },
3963 setLineCap: function SVGGraphics_setLineCap(style) {
3964 this.current.lineCap = LINE_CAP_STYLES[style];
3965 },
3966 setLineJoin: function SVGGraphics_setLineJoin(style) {
3967 this.current.lineJoin = LINE_JOIN_STYLES[style];
3968 },
3969 setMiterLimit: function SVGGraphics_setMiterLimit(limit) {
3970 this.current.miterLimit = limit;
3971 },
3972 setStrokeRGBColor: function SVGGraphics_setStrokeRGBColor(r, g, b) {
3973 var color = Util.makeCssRgb(r, g, b);
3974 this.current.strokeColor = color;
3975 },
3976 setFillRGBColor: function SVGGraphics_setFillRGBColor(r, g, b) {
3977 var color = Util.makeCssRgb(r, g, b);
3978 this.current.fillColor = color;
3979 this.current.tspan = document.createElementNS(NS, 'svg:tspan');
3980 this.current.xcoords = [];
3981 },
3982 setDash: function SVGGraphics_setDash(dashArray, dashPhase) {
3983 this.current.dashArray = dashArray;
3984 this.current.dashPhase = dashPhase;
3985 },
3986
3987 constructPath: function SVGGraphics_constructPath(ops, args) {
3988 var current = this.current;
3989 var x = current.x, y = current.y;
3990 current.path = document.createElementNS(NS, 'svg:path');
3991 var d = [];
3992 var opLength = ops.length;
3993
3994 for (var i = 0, j = 0; i < opLength; i++) {
3995 switch (ops[i] | 0) {
3996 case OPS.rectangle:
3997 x = args[j++];
3998 y = args[j++];
3999 var width = args[j++];
4000 var height = args[j++];
4001 var xw = x + width;
4002 var yh = y + height;
4003 d.push('M', pf(x), pf(y), 'L', pf(xw) , pf(y), 'L', pf(xw), pf(yh),
4004 'L', pf(x), pf(yh), 'Z');
4005 break;
4006 case OPS.moveTo:
4007 x = args[j++];
4008 y = args[j++];
4009 d.push('M', pf(x), pf(y));
4010 break;
4011 case OPS.lineTo:
4012 x = args[j++];
4013 y = args[j++];
4014 d.push('L', pf(x) , pf(y));
4015 break;
4016 case OPS.curveTo:
4017 x = args[j + 4];
4018 y = args[j + 5];
4019 d.push('C', pf(args[j]), pf(args[j + 1]), pf(args[j + 2]),
4020 pf(args[j + 3]), pf(x), pf(y));
4021 j += 6;
4022 break;
4023 case OPS.curveTo2:
4024 x = args[j + 2];
4025 y = args[j + 3];
4026 d.push('C', pf(x), pf(y), pf(args[j]), pf(args[j + 1]),
4027 pf(args[j + 2]), pf(args[j + 3]));
4028 j += 4;
4029 break;
4030 case OPS.curveTo3:
4031 x = args[j + 2];
4032 y = args[j + 3];
4033 d.push('C', pf(args[j]), pf(args[j + 1]), pf(x), pf(y),
4034 pf(x), pf(y));
4035 j += 4;
4036 break;
4037 case OPS.closePath:
4038 d.push('Z');
4039 break;
4040 }
4041 }
4042 current.path.setAttributeNS(null, 'd', d.join(' '));
4043 current.path.setAttributeNS(null, 'stroke-miterlimit',
4044 pf(current.miterLimit));
4045 current.path.setAttributeNS(null, 'stroke-linecap', current.lineCap);
4046 current.path.setAttributeNS(null, 'stroke-linejoin', current.lineJoin);
4047 current.path.setAttributeNS(null, 'stroke-width',
4048 pf(current.lineWidth) + 'px');
4049 current.path.setAttributeNS(null, 'stroke-dasharray',
4050 current.dashArray.map(pf).join(' '));
4051 current.path.setAttributeNS(null, 'stroke-dashoffset',
4052 pf(current.dashPhase) + 'px');
4053 current.path.setAttributeNS(null, 'fill', 'none');
4054
4055 this.tgrp.appendChild(current.path);
4056 if (current.pendingClip) {
4057 this.cgrp.appendChild(this.tgrp);
4058 this.pgrp.appendChild(this.cgrp);
4059 } else {
4060 this.pgrp.appendChild(this.tgrp);
4061 }
4062 // Saving a reference in current.element so that it can be addressed
4063 // in 'fill' and 'stroke'
4064 current.element = current.path;
4065 current.setCurrentPoint(x, y);
4066 },
4067
4068 endPath: function SVGGraphics_endPath() {
4069 var current = this.current;
4070 if (current.pendingClip) {
4071 this.cgrp.appendChild(this.tgrp);
4072 this.pgrp.appendChild(this.cgrp);
4073 } else {
4074 this.pgrp.appendChild(this.tgrp);
4075 }
4076 this.tgrp = document.createElementNS(NS, 'svg:g');
4077 this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
4078 },
4079
4080 clip: function SVGGraphics_clip(type) {
4081 var current = this.current;
4082 // Add current path to clipping path
4083 current.clipId = 'clippath' + clipCount;
4084 clipCount++;
4085 this.clippath = document.createElementNS(NS, 'svg:clipPath');
4086 this.clippath.setAttributeNS(null, 'id', current.clipId);
4087 var clipElement = current.element.cloneNode();
4088 if (type === 'evenodd') {
4089 clipElement.setAttributeNS(null, 'clip-rule', 'evenodd');
4090 } else {
4091 clipElement.setAttributeNS(null, 'clip-rule', 'nonzero');
4092 }
4093 this.clippath.setAttributeNS(null, 'transform', pm(this.transformMatrix));
4094 this.clippath.appendChild(clipElement);
4095 this.defs.appendChild(this.clippath);
4096
4097 // Create a new group with that attribute
4098 current.pendingClip = true;
4099 this.cgrp = document.createElementNS(NS, 'svg:g');
4100 this.cgrp.setAttributeNS(null, 'clip-path',
4101 'url(#' + current.clipId + ')');
4102 this.pgrp.appendChild(this.cgrp);
4103 },
4104
4105 closePath: function SVGGraphics_closePath() {
4106 var current = this.current;
4107 var d = current.path.getAttributeNS(null, 'd');
4108 d += 'Z';
4109 current.path.setAttributeNS(null, 'd', d);
4110 },
4111
4112 setLeading: function SVGGraphics_setLeading(leading) {
4113 this.current.leading = -leading;
4114 },
4115
4116 setTextRise: function SVGGraphics_setTextRise(textRise) {
4117 this.current.textRise = textRise;
4118 },
4119
4120 setHScale: function SVGGraphics_setHScale(scale) {
4121 this.current.textHScale = scale / 100;
4122 },
4123
4124 setGState: function SVGGraphics_setGState(states) {
4125 for (var i = 0, ii = states.length; i < ii; i++) {
4126 var state = states[i];
4127 var key = state[0];
4128 var value = state[1];
4129
4130 switch (key) {
4131 case 'LW':
4132 this.setLineWidth(value);
4133 break;
4134 case 'LC':
4135 this.setLineCap(value);
4136 break;
4137 case 'LJ':
4138 this.setLineJoin(value);
4139 break;
4140 case 'ML':
4141 this.setMiterLimit(value);
4142 break;
4143 case 'D':
4144 this.setDash(value[0], value[1]);
4145 break;
4146 case 'RI':
4147 break;
4148 case 'FL':
4149 break;
4150 case 'Font':
4151 this.setFont(value);
4152 break;
4153 case 'CA':
4154 break;
4155 case 'ca':
4156 break;
4157 case 'BM':
4158 break;
4159 case 'SMask':
4160 break;
4161 }
4162 }
4163 },
4164
4165 fill: function SVGGraphics_fill() {
4166 var current = this.current;
4167 current.element.setAttributeNS(null, 'fill', current.fillColor);
4168 },
4169
4170 stroke: function SVGGraphics_stroke() {
4171 var current = this.current;
4172 current.element.setAttributeNS(null, 'stroke', current.strokeColor);
4173 current.element.setAttributeNS(null, 'fill', 'none');
4174 },
4175
4176 eoFill: function SVGGraphics_eoFill() {
4177 var current = this.current;
4178 current.element.setAttributeNS(null, 'fill', current.fillColor);
4179 current.element.setAttributeNS(null, 'fill-rule', 'evenodd');
4180 },
4181
4182 fillStroke: function SVGGraphics_fillStroke() {
4183 // Order is important since stroke wants fill to be none.
4184 // First stroke, then if fill needed, it will be overwritten.
4185 this.stroke();
4186 this.fill();
4187 },
4188
4189 eoFillStroke: function SVGGraphics_eoFillStroke() {
4190 this.current.element.setAttributeNS(null, 'fill-rule', 'evenodd');
4191 this.fillStroke();
4192 },
4193
4194 closeStroke: function SVGGraphics_closeStroke() {
4195 this.closePath();
4196 this.stroke();
4197 },
4198
4199 closeFillStroke: function SVGGraphics_closeFillStroke() {
4200 this.closePath();
4201 this.fillStroke();
4202 },
4203
4204 paintSolidColorImageMask:
4205 function SVGGraphics_paintSolidColorImageMask() {
4206 var current = this.current;
4207 var rect = document.createElementNS(NS, 'svg:rect');
4208 rect.setAttributeNS(null, 'x', '0');
4209 rect.setAttributeNS(null, 'y', '0');
4210 rect.setAttributeNS(null, 'width', '1px');
4211 rect.setAttributeNS(null, 'height', '1px');
4212 rect.setAttributeNS(null, 'fill', current.fillColor);
4213 this.tgrp.appendChild(rect);
4214 },
4215
4216 paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) {
4217 var current = this.current;
4218 var imgObj = this.objs.get(objId);
4219 var imgEl = document.createElementNS(NS, 'svg:image');
4220 imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src);
4221 imgEl.setAttributeNS(null, 'width', imgObj.width + 'px');
4222 imgEl.setAttributeNS(null, 'height', imgObj.height + 'px');
4223 imgEl.setAttributeNS(null, 'x', '0');
4224 imgEl.setAttributeNS(null, 'y', pf(-h));
4225 imgEl.setAttributeNS(null, 'transform',
4226 'scale(' + pf(1 / w) + ' ' + pf(-1 / h) + ')');
4227
4228 this.tgrp.appendChild(imgEl);
4229 if (current.pendingClip) {
4230 this.cgrp.appendChild(this.tgrp);
4231 this.pgrp.appendChild(this.cgrp);
4232 } else {
4233 this.pgrp.appendChild(this.tgrp);
4234 }
4235 },
4236
4237 paintImageXObject: function SVGGraphics_paintImageXObject(objId) {
4238 var imgData = this.objs.get(objId);
4239 if (!imgData) {
4240 warn('Dependent image isn\'t ready yet');
4241 return;
4242 }
4243 this.paintInlineImageXObject(imgData);
4244 },
4245
4246 paintInlineImageXObject:
4247 function SVGGraphics_paintInlineImageXObject(imgData, mask) {
4248 var current = this.current;
4249 var width = imgData.width;
4250 var height = imgData.height;
4251
4252 var imgSrc = convertImgDataToPng(imgData, this.forceDataSchema);
4253 var cliprect = document.createElementNS(NS, 'svg:rect');
4254 cliprect.setAttributeNS(null, 'x', '0');
4255 cliprect.setAttributeNS(null, 'y', '0');
4256 cliprect.setAttributeNS(null, 'width', pf(width));
4257 cliprect.setAttributeNS(null, 'height', pf(height));
4258 current.element = cliprect;
4259 this.clip('nonzero');
4260 var imgEl = document.createElementNS(NS, 'svg:image');
4261 imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgSrc);
4262 imgEl.setAttributeNS(null, 'x', '0');
4263 imgEl.setAttributeNS(null, 'y', pf(-height));
4264 imgEl.setAttributeNS(null, 'width', pf(width) + 'px');
4265 imgEl.setAttributeNS(null, 'height', pf(height) + 'px');
4266 imgEl.setAttributeNS(null, 'transform',
4267 'scale(' + pf(1 / width) + ' ' +
4268 pf(-1 / height) + ')');
4269 if (mask) {
4270 mask.appendChild(imgEl);
4271 } else {
4272 this.tgrp.appendChild(imgEl);
4273 }
4274 if (current.pendingClip) {
4275 this.cgrp.appendChild(this.tgrp);
4276 this.pgrp.appendChild(this.cgrp);
4277 } else {
4278 this.pgrp.appendChild(this.tgrp);
4279 }
4280 },
4281
4282 paintImageMaskXObject:
4283 function SVGGraphics_paintImageMaskXObject(imgData) {
4284 var current = this.current;
4285 var width = imgData.width;
4286 var height = imgData.height;
4287 var fillColor = current.fillColor;
4288
4289 current.maskId = 'mask' + maskCount++;
4290 var mask = document.createElementNS(NS, 'svg:mask');
4291 mask.setAttributeNS(null, 'id', current.maskId);
4292
4293 var rect = document.createElementNS(NS, 'svg:rect');
4294 rect.setAttributeNS(null, 'x', '0');
4295 rect.setAttributeNS(null, 'y', '0');
4296 rect.setAttributeNS(null, 'width', pf(width));
4297 rect.setAttributeNS(null, 'height', pf(height));
4298 rect.setAttributeNS(null, 'fill', fillColor);
4299 rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')');
4300 this.defs.appendChild(mask);
4301 this.tgrp.appendChild(rect);
4302
4303 this.paintInlineImageXObject(imgData, mask);
4304 },
4305
4306 paintFormXObjectBegin:
4307 function SVGGraphics_paintFormXObjectBegin(matrix, bbox) {
4308 this.save();
4309
4310 if (isArray(matrix) && matrix.length === 6) {
4311 this.transform(matrix[0], matrix[1], matrix[2],
4312 matrix[3], matrix[4], matrix[5]);
4313 }
4314
4315 if (isArray(bbox) && bbox.length === 4) {
4316 var width = bbox[2] - bbox[0];
4317 var height = bbox[3] - bbox[1];
4318
4319 var cliprect = document.createElementNS(NS, 'svg:rect');
4320 cliprect.setAttributeNS(null, 'x', bbox[0]);
4321 cliprect.setAttributeNS(null, 'y', bbox[1]);
4322 cliprect.setAttributeNS(null, 'width', pf(width));
4323 cliprect.setAttributeNS(null, 'height', pf(height));
4324 this.current.element = cliprect;
4325 this.clip('nonzero');
4326 this.endPath();
4327 }
4328 },
4329
4330 paintFormXObjectEnd:
4331 function SVGGraphics_paintFormXObjectEnd() {
4332 this.restore();
4333 }
4334 };
4335 return SVGGraphics;
4336})();
4337
4338exports.SVGGraphics = SVGGraphics;
4339}));
4340
4341
4342(function (root, factory) {
4343 {
4344 factory((root.pdfjsDisplayAnnotationLayer = {}), root.pdfjsSharedUtil,
4345 root.pdfjsDisplayDOMUtils);
4346 }
4347}(this, function (exports, sharedUtil, displayDOMUtils) {
4348
4349var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
4350var AnnotationType = sharedUtil.AnnotationType;
4351var Util = sharedUtil.Util;
4352var addLinkAttributes = displayDOMUtils.addLinkAttributes;
4353var LinkTarget = displayDOMUtils.LinkTarget;
4354var getFilenameFromUrl = displayDOMUtils.getFilenameFromUrl;
4355var warn = sharedUtil.warn;
4356var CustomStyle = displayDOMUtils.CustomStyle;
4357var getDefaultSetting = displayDOMUtils.getDefaultSetting;
4358
4359/**
4360 * @typedef {Object} AnnotationElementParameters
4361 * @property {Object} data
4362 * @property {HTMLDivElement} layer
4363 * @property {PDFPage} page
4364 * @property {PageViewport} viewport
4365 * @property {IPDFLinkService} linkService
4366 * @property {DownloadManager} downloadManager
4367 * @property {string} imageResourcesPath
4368 * @property {boolean} renderInteractiveForms
4369 */
4370
4371/**
4372 * @class
4373 * @alias AnnotationElementFactory
4374 */
4375function AnnotationElementFactory() {}
4376AnnotationElementFactory.prototype =
4377 /** @lends AnnotationElementFactory.prototype */ {
4378 /**
4379 * @param {AnnotationElementParameters} parameters
4380 * @returns {AnnotationElement}
4381 */
4382 create: function AnnotationElementFactory_create(parameters) {
4383 var subtype = parameters.data.annotationType;
4384
4385 switch (subtype) {
4386 case AnnotationType.LINK:
4387 return new LinkAnnotationElement(parameters);
4388
4389 case AnnotationType.TEXT:
4390 return new TextAnnotationElement(parameters);
4391
4392 case AnnotationType.WIDGET:
4393 var fieldType = parameters.data.fieldType;
4394
4395 switch (fieldType) {
4396 case 'Tx':
4397 return new TextWidgetAnnotationElement(parameters);
4398 }
4399 return new WidgetAnnotationElement(parameters);
4400
4401 case AnnotationType.POPUP:
4402 return new PopupAnnotationElement(parameters);
4403
4404 case AnnotationType.HIGHLIGHT:
4405 return new HighlightAnnotationElement(parameters);
4406
4407 case AnnotationType.UNDERLINE:
4408 return new UnderlineAnnotationElement(parameters);
4409
4410 case AnnotationType.SQUIGGLY:
4411 return new SquigglyAnnotationElement(parameters);
4412
4413 case AnnotationType.STRIKEOUT:
4414 return new StrikeOutAnnotationElement(parameters);
4415
4416 case AnnotationType.FILEATTACHMENT:
4417 return new FileAttachmentAnnotationElement(parameters);
4418
4419 default:
4420 return new AnnotationElement(parameters);
4421 }
4422 }
4423};
4424
4425/**
4426 * @class
4427 * @alias AnnotationElement
4428 */
4429var AnnotationElement = (function AnnotationElementClosure() {
4430 function AnnotationElement(parameters, isRenderable) {
4431 this.isRenderable = isRenderable || false;
4432 this.data = parameters.data;
4433 this.layer = parameters.layer;
4434 this.page = parameters.page;
4435 this.viewport = parameters.viewport;
4436 this.linkService = parameters.linkService;
4437 this.downloadManager = parameters.downloadManager;
4438 this.imageResourcesPath = parameters.imageResourcesPath;
4439 this.renderInteractiveForms = parameters.renderInteractiveForms;
4440
4441 if (isRenderable) {
4442 this.container = this._createContainer();
4443 }
4444 }
4445
4446 AnnotationElement.prototype = /** @lends AnnotationElement.prototype */ {
4447 /**
4448 * Create an empty container for the annotation's HTML element.
4449 *
4450 * @private
4451 * @memberof AnnotationElement
4452 * @returns {HTMLSectionElement}
4453 */
4454 _createContainer: function AnnotationElement_createContainer() {
4455 var data = this.data, page = this.page, viewport = this.viewport;
4456 var container = document.createElement('section');
4457 var width = data.rect[2] - data.rect[0];
4458 var height = data.rect[3] - data.rect[1];
4459
4460 container.setAttribute('data-annotation-id', data.id);
4461
4462 // Do *not* modify `data.rect`, since that will corrupt the annotation
4463 // position on subsequent calls to `_createContainer` (see issue 6804).
4464 var rect = Util.normalizeRect([
4465 data.rect[0],
4466 page.view[3] - data.rect[1] + page.view[1],
4467 data.rect[2],
4468 page.view[3] - data.rect[3] + page.view[1]
4469 ]);
4470
4471 CustomStyle.setProp('transform', container,
4472 'matrix(' + viewport.transform.join(',') + ')');
4473 CustomStyle.setProp('transformOrigin', container,
4474 -rect[0] + 'px ' + -rect[1] + 'px');
4475
4476 if (data.borderStyle.width > 0) {
4477 container.style.borderWidth = data.borderStyle.width + 'px';
4478 if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) {
4479 // Underline styles only have a bottom border, so we do not need
4480 // to adjust for all borders. This yields a similar result as
4481 // Adobe Acrobat/Reader.
4482 width = width - 2 * data.borderStyle.width;
4483 height = height - 2 * data.borderStyle.width;
4484 }
4485
4486 var horizontalRadius = data.borderStyle.horizontalCornerRadius;
4487 var verticalRadius = data.borderStyle.verticalCornerRadius;
4488 if (horizontalRadius > 0 || verticalRadius > 0) {
4489 var radius = horizontalRadius + 'px / ' + verticalRadius + 'px';
4490 CustomStyle.setProp('borderRadius', container, radius);
4491 }
4492
4493 switch (data.borderStyle.style) {
4494 case AnnotationBorderStyleType.SOLID:
4495 container.style.borderStyle = 'solid';
4496 break;
4497
4498 case AnnotationBorderStyleType.DASHED:
4499 container.style.borderStyle = 'dashed';
4500 break;
4501
4502 case AnnotationBorderStyleType.BEVELED:
4503 warn('Unimplemented border style: beveled');
4504 break;
4505
4506 case AnnotationBorderStyleType.INSET:
4507 warn('Unimplemented border style: inset');
4508 break;
4509
4510 case AnnotationBorderStyleType.UNDERLINE:
4511 container.style.borderBottomStyle = 'solid';
4512 break;
4513
4514 default:
4515 break;
4516 }
4517
4518 if (data.color) {
4519 container.style.borderColor =
4520 Util.makeCssRgb(data.color[0] | 0,
4521 data.color[1] | 0,
4522 data.color[2] | 0);
4523 } else {
4524 // Transparent (invisible) border, so do not draw it at all.
4525 container.style.borderWidth = 0;
4526 }
4527 }
4528
4529 container.style.left = rect[0] + 'px';
4530 container.style.top = rect[1] + 'px';
4531
4532 container.style.width = width + 'px';
4533 container.style.height = height + 'px';
4534
4535 return container;
4536 },
4537
4538 /**
4539 * Create a popup for the annotation's HTML element. This is used for
4540 * annotations that do not have a Popup entry in the dictionary, but
4541 * are of a type that works with popups (such as Highlight annotations).
4542 *
4543 * @private
4544 * @param {HTMLSectionElement} container
4545 * @param {HTMLDivElement|HTMLImageElement|null} trigger
4546 * @param {Object} data
4547 * @memberof AnnotationElement
4548 */
4549 _createPopup:
4550 function AnnotationElement_createPopup(container, trigger, data) {
4551 // If no trigger element is specified, create it.
4552 if (!trigger) {
4553 trigger = document.createElement('div');
4554 trigger.style.height = container.style.height;
4555 trigger.style.width = container.style.width;
4556 container.appendChild(trigger);
4557 }
4558
4559 var popupElement = new PopupElement({
4560 container: container,
4561 trigger: trigger,
4562 color: data.color,
4563 title: data.title,
4564 contents: data.contents,
4565 hideWrapper: true
4566 });
4567 var popup = popupElement.render();
4568
4569 // Position the popup next to the annotation's container.
4570 popup.style.left = container.style.width;
4571
4572 container.appendChild(popup);
4573 },
4574
4575 /**
4576 * Render the annotation's HTML element in the empty container.
4577 *
4578 * @public
4579 * @memberof AnnotationElement
4580 */
4581 render: function AnnotationElement_render() {
4582 throw new Error('Abstract method AnnotationElement.render called');
4583 }
4584 };
4585
4586 return AnnotationElement;
4587})();
4588
4589/**
4590 * @class
4591 * @alias LinkAnnotationElement
4592 */
4593var LinkAnnotationElement = (function LinkAnnotationElementClosure() {
4594 function LinkAnnotationElement(parameters) {
4595 AnnotationElement.call(this, parameters, true);
4596 }
4597
4598 Util.inherit(LinkAnnotationElement, AnnotationElement, {
4599 /**
4600 * Render the link annotation's HTML element in the empty container.
4601 *
4602 * @public
4603 * @memberof LinkAnnotationElement
4604 * @returns {HTMLSectionElement}
4605 */
4606 render: function LinkAnnotationElement_render() {
4607 this.container.className = 'linkAnnotation';
4608
4609 var link = document.createElement('a');
4610 addLinkAttributes(link, {
4611 url: this.data.url,
4612 target: (this.data.newWindow ? LinkTarget.BLANK : undefined),
4613 });
4614
4615 if (!this.data.url) {
4616 if (this.data.action) {
4617 this._bindNamedAction(link, this.data.action);
4618 } else {
4619 this._bindLink(link, (this.data.dest || null));
4620 }
4621 }
4622
4623 this.container.appendChild(link);
4624 return this.container;
4625 },
4626
4627 /**
4628 * Bind internal links to the link element.
4629 *
4630 * @private
4631 * @param {Object} link
4632 * @param {Object} destination
4633 * @memberof LinkAnnotationElement
4634 */
4635 _bindLink: function LinkAnnotationElement_bindLink(link, destination) {
4636 var self = this;
4637
4638 link.href = this.linkService.getDestinationHash(destination);
4639 link.onclick = function() {
4640 if (destination) {
4641 self.linkService.navigateTo(destination);
4642 }
4643 return false;
4644 };
4645 if (destination) {
4646 link.className = 'internalLink';
4647 }
4648 },
4649
4650 /**
4651 * Bind named actions to the link element.
4652 *
4653 * @private
4654 * @param {Object} link
4655 * @param {Object} action
4656 * @memberof LinkAnnotationElement
4657 */
4658 _bindNamedAction:
4659 function LinkAnnotationElement_bindNamedAction(link, action) {
4660 var self = this;
4661
4662 link.href = this.linkService.getAnchorUrl('');
4663 link.onclick = function() {
4664 self.linkService.executeNamedAction(action);
4665 return false;
4666 };
4667 link.className = 'internalLink';
4668 }
4669 });
4670
4671 return LinkAnnotationElement;
4672})();
4673
4674/**
4675 * @class
4676 * @alias TextAnnotationElement
4677 */
4678var TextAnnotationElement = (function TextAnnotationElementClosure() {
4679 function TextAnnotationElement(parameters) {
4680 var isRenderable = !!(parameters.data.hasPopup ||
4681 parameters.data.title || parameters.data.contents);
4682 AnnotationElement.call(this, parameters, isRenderable);
4683 }
4684
4685 Util.inherit(TextAnnotationElement, AnnotationElement, {
4686 /**
4687 * Render the text annotation's HTML element in the empty container.
4688 *
4689 * @public
4690 * @memberof TextAnnotationElement
4691 * @returns {HTMLSectionElement}
4692 */
4693 render: function TextAnnotationElement_render() {
4694 this.container.className = 'textAnnotation';
4695
4696 var image = document.createElement('img');
4697 image.style.height = this.container.style.height;
4698 image.style.width = this.container.style.width;
4699 image.src = this.imageResourcesPath + 'annotation-' +
4700 this.data.name.toLowerCase() + '.svg';
4701 image.alt = '[{{type}} Annotation]';
4702 image.dataset.l10nId = 'text_annotation_type';
4703 image.dataset.l10nArgs = JSON.stringify({type: this.data.name});
4704
4705 if (!this.data.hasPopup) {
4706 this._createPopup(this.container, image, this.data);
4707 }
4708
4709 this.container.appendChild(image);
4710 return this.container;
4711 }
4712 });
4713
4714 return TextAnnotationElement;
4715})();
4716
4717/**
4718 * @class
4719 * @alias WidgetAnnotationElement
4720 */
4721var WidgetAnnotationElement = (function WidgetAnnotationElementClosure() {
4722 function WidgetAnnotationElement(parameters) {
4723 var isRenderable = parameters.renderInteractiveForms ||
4724 (!parameters.data.hasAppearance && !!parameters.data.fieldValue);
4725 AnnotationElement.call(this, parameters, isRenderable);
4726 }
4727
4728 Util.inherit(WidgetAnnotationElement, AnnotationElement, {
4729 /**
4730 * Render the widget annotation's HTML element in the empty container.
4731 *
4732 * @public
4733 * @memberof WidgetAnnotationElement
4734 * @returns {HTMLSectionElement}
4735 */
4736 render: function WidgetAnnotationElement_render() {
4737 // Show only the container for unsupported field types.
4738 return this.container;
4739 }
4740 });
4741
4742 return WidgetAnnotationElement;
4743})();
4744
4745/**
4746 * @class
4747 * @alias TextWidgetAnnotationElement
4748 */
4749var TextWidgetAnnotationElement = (
4750 function TextWidgetAnnotationElementClosure() {
4751 var TEXT_ALIGNMENT = ['left', 'center', 'right'];
4752
4753 function TextWidgetAnnotationElement(parameters) {
4754 WidgetAnnotationElement.call(this, parameters);
4755 }
4756
4757 Util.inherit(TextWidgetAnnotationElement, WidgetAnnotationElement, {
4758 /**
4759 * Render the text widget annotation's HTML element in the empty container.
4760 *
4761 * @public
4762 * @memberof TextWidgetAnnotationElement
4763 * @returns {HTMLSectionElement}
4764 */
4765 render: function TextWidgetAnnotationElement_render() {
4766 this.container.className = 'textWidgetAnnotation';
4767
4768 var element = null;
4769 if (this.renderInteractiveForms) {
4770 // NOTE: We cannot set the values using `element.value` below, since it
4771 // prevents the AnnotationLayer rasterizer in `test/driver.js`
4772 // from parsing the elements correctly for the reference tests.
4773 if (this.data.multiLine) {
4774 element = document.createElement('textarea');
4775 element.textContent = this.data.fieldValue;
4776 } else {
4777 element = document.createElement('input');
4778 element.type = 'text';
4779 element.setAttribute('value', this.data.fieldValue);
4780 }
4781
4782 element.disabled = this.data.readOnly;
4783
4784 if (this.data.maxLen !== null) {
4785 element.maxLength = this.data.maxLen;
4786 }
4787
4788 if (this.data.comb) {
4789 var fieldWidth = this.data.rect[2] - this.data.rect[0];
4790 var combWidth = fieldWidth / this.data.maxLen;
4791
4792 element.classList.add('comb');
4793 element.style.letterSpacing = 'calc(' + combWidth + 'px - 1ch)';
4794 }
4795 } else {
4796 element = document.createElement('div');
4797 element.textContent = this.data.fieldValue;
4798 element.style.verticalAlign = 'middle';
4799 element.style.display = 'table-cell';
4800
4801 var font = null;
4802 if (this.data.fontRefName) {
4803 font = this.page.commonObjs.getData(this.data.fontRefName);
4804 }
4805 this._setTextStyle(element, font);
4806 }
4807
4808 if (this.data.textAlignment !== null) {
4809 element.style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
4810 }
4811
4812 this.container.appendChild(element);
4813 return this.container;
4814 },
4815
4816 /**
4817 * Apply text styles to the text in the element.
4818 *
4819 * @private
4820 * @param {HTMLDivElement} element
4821 * @param {Object} font
4822 * @memberof TextWidgetAnnotationElement
4823 */
4824 _setTextStyle:
4825 function TextWidgetAnnotationElement_setTextStyle(element, font) {
4826 // TODO: This duplicates some of the logic in CanvasGraphics.setFont().
4827 var style = element.style;
4828 style.fontSize = this.data.fontSize + 'px';
4829 style.direction = (this.data.fontDirection < 0 ? 'rtl': 'ltr');
4830
4831 if (!font) {
4832 return;
4833 }
4834
4835 style.fontWeight = (font.black ?
4836 (font.bold ? '900' : 'bold') :
4837 (font.bold ? 'bold' : 'normal'));
4838 style.fontStyle = (font.italic ? 'italic' : 'normal');
4839
4840 // Use a reasonable default font if the font doesn't specify a fallback.
4841 var fontFamily = font.loadedName ? '"' + font.loadedName + '", ' : '';
4842 var fallbackName = font.fallbackName || 'Helvetica, sans-serif';
4843 style.fontFamily = fontFamily + fallbackName;
4844 }
4845 });
4846
4847 return TextWidgetAnnotationElement;
4848})();
4849
4850/**
4851 * @class
4852 * @alias PopupAnnotationElement
4853 */
4854var PopupAnnotationElement = (function PopupAnnotationElementClosure() {
4855 function PopupAnnotationElement(parameters) {
4856 var isRenderable = !!(parameters.data.title || parameters.data.contents);
4857 AnnotationElement.call(this, parameters, isRenderable);
4858 }
4859
4860 Util.inherit(PopupAnnotationElement, AnnotationElement, {
4861 /**
4862 * Render the popup annotation's HTML element in the empty container.
4863 *
4864 * @public
4865 * @memberof PopupAnnotationElement
4866 * @returns {HTMLSectionElement}
4867 */
4868 render: function PopupAnnotationElement_render() {
4869 this.container.className = 'popupAnnotation';
4870
4871 var selector = '[data-annotation-id="' + this.data.parentId + '"]';
4872 var parentElement = this.layer.querySelector(selector);
4873 if (!parentElement) {
4874 return this.container;
4875 }
4876
4877 var popup = new PopupElement({
4878 container: this.container,
4879 trigger: parentElement,
4880 color: this.data.color,
4881 title: this.data.title,
4882 contents: this.data.contents
4883 });
4884
4885 // Position the popup next to the parent annotation's container.
4886 // PDF viewers ignore a popup annotation's rectangle.
4887 var parentLeft = parseFloat(parentElement.style.left);
4888 var parentWidth = parseFloat(parentElement.style.width);
4889 CustomStyle.setProp('transformOrigin', this.container,
4890 -(parentLeft + parentWidth) + 'px -' +
4891 parentElement.style.top);
4892 this.container.style.left = (parentLeft + parentWidth) + 'px';
4893
4894 this.container.appendChild(popup.render());
4895 return this.container;
4896 }
4897 });
4898
4899 return PopupAnnotationElement;
4900})();
4901
4902/**
4903 * @class
4904 * @alias PopupElement
4905 */
4906var PopupElement = (function PopupElementClosure() {
4907 var BACKGROUND_ENLIGHT = 0.7;
4908
4909 function PopupElement(parameters) {
4910 this.container = parameters.container;
4911 this.trigger = parameters.trigger;
4912 this.color = parameters.color;
4913 this.title = parameters.title;
4914 this.contents = parameters.contents;
4915 this.hideWrapper = parameters.hideWrapper || false;
4916
4917 this.pinned = false;
4918 }
4919
4920 PopupElement.prototype = /** @lends PopupElement.prototype */ {
4921 /**
4922 * Render the popup's HTML element.
4923 *
4924 * @public
4925 * @memberof PopupElement
4926 * @returns {HTMLSectionElement}
4927 */
4928 render: function PopupElement_render() {
4929 var wrapper = document.createElement('div');
4930 wrapper.className = 'popupWrapper';
4931
4932 // For Popup annotations we hide the entire section because it contains
4933 // only the popup. However, for Text annotations without a separate Popup
4934 // annotation, we cannot hide the entire container as the image would
4935 // disappear too. In that special case, hiding the wrapper suffices.
4936 this.hideElement = (this.hideWrapper ? wrapper : this.container);
4937 this.hideElement.setAttribute('hidden', true);
4938
4939 var popup = document.createElement('div');
4940 popup.className = 'popup';
4941
4942 var color = this.color;
4943 if (color) {
4944 // Enlighten the color.
4945 var r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0];
4946 var g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1];
4947 var b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2];
4948 popup.style.backgroundColor = Util.makeCssRgb(r | 0, g | 0, b | 0);
4949 }
4950
4951 var contents = this._formatContents(this.contents);
4952 var title = document.createElement('h1');
4953 title.textContent = this.title;
4954
4955 // Attach the event listeners to the trigger element.
4956 this.trigger.addEventListener('click', this._toggle.bind(this));
4957 this.trigger.addEventListener('mouseover', this._show.bind(this, false));
4958 this.trigger.addEventListener('mouseout', this._hide.bind(this, false));
4959 popup.addEventListener('click', this._hide.bind(this, true));
4960
4961 popup.appendChild(title);
4962 popup.appendChild(contents);
4963 wrapper.appendChild(popup);
4964 return wrapper;
4965 },
4966
4967 /**
4968 * Format the contents of the popup by adding newlines where necessary.
4969 *
4970 * @private
4971 * @param {string} contents
4972 * @memberof PopupElement
4973 * @returns {HTMLParagraphElement}
4974 */
4975 _formatContents: function PopupElement_formatContents(contents) {
4976 var p = document.createElement('p');
4977 var lines = contents.split(/(?:\r\n?|\n)/);
4978 for (var i = 0, ii = lines.length; i < ii; ++i) {
4979 var line = lines[i];
4980 p.appendChild(document.createTextNode(line));
4981 if (i < (ii - 1)) {
4982 p.appendChild(document.createElement('br'));
4983 }
4984 }
4985 return p;
4986 },
4987
4988 /**
4989 * Toggle the visibility of the popup.
4990 *
4991 * @private
4992 * @memberof PopupElement
4993 */
4994 _toggle: function PopupElement_toggle() {
4995 if (this.pinned) {
4996 this._hide(true);
4997 } else {
4998 this._show(true);
4999 }
5000 },
5001
5002 /**
5003 * Show the popup.
5004 *
5005 * @private
5006 * @param {boolean} pin
5007 * @memberof PopupElement
5008 */
5009 _show: function PopupElement_show(pin) {
5010 if (pin) {
5011 this.pinned = true;
5012 }
5013 if (this.hideElement.hasAttribute('hidden')) {
5014 this.hideElement.removeAttribute('hidden');
5015 this.container.style.zIndex += 1;
5016 }
5017 },
5018
5019 /**
5020 * Hide the popup.
5021 *
5022 * @private
5023 * @param {boolean} unpin
5024 * @memberof PopupElement
5025 */
5026 _hide: function PopupElement_hide(unpin) {
5027 if (unpin) {
5028 this.pinned = false;
5029 }
5030 if (!this.hideElement.hasAttribute('hidden') && !this.pinned) {
5031 this.hideElement.setAttribute('hidden', true);
5032 this.container.style.zIndex -= 1;
5033 }
5034 }
5035 };
5036
5037 return PopupElement;
5038})();
5039
5040/**
5041 * @class
5042 * @alias HighlightAnnotationElement
5043 */
5044var HighlightAnnotationElement = (
5045 function HighlightAnnotationElementClosure() {
5046 function HighlightAnnotationElement(parameters) {
5047 var isRenderable = !!(parameters.data.hasPopup ||
5048 parameters.data.title || parameters.data.contents);
5049 AnnotationElement.call(this, parameters, isRenderable);
5050 }
5051
5052 Util.inherit(HighlightAnnotationElement, AnnotationElement, {
5053 /**
5054 * Render the highlight annotation's HTML element in the empty container.
5055 *
5056 * @public
5057 * @memberof HighlightAnnotationElement
5058 * @returns {HTMLSectionElement}
5059 */
5060 render: function HighlightAnnotationElement_render() {
5061 this.container.className = 'highlightAnnotation';
5062
5063 if (!this.data.hasPopup) {
5064 this._createPopup(this.container, null, this.data);
5065 }
5066
5067 return this.container;
5068 }
5069 });
5070
5071 return HighlightAnnotationElement;
5072})();
5073
5074/**
5075 * @class
5076 * @alias UnderlineAnnotationElement
5077 */
5078var UnderlineAnnotationElement = (
5079 function UnderlineAnnotationElementClosure() {
5080 function UnderlineAnnotationElement(parameters) {
5081 var isRenderable = !!(parameters.data.hasPopup ||
5082 parameters.data.title || parameters.data.contents);
5083 AnnotationElement.call(this, parameters, isRenderable);
5084 }
5085
5086 Util.inherit(UnderlineAnnotationElement, AnnotationElement, {
5087 /**
5088 * Render the underline annotation's HTML element in the empty container.
5089 *
5090 * @public
5091 * @memberof UnderlineAnnotationElement
5092 * @returns {HTMLSectionElement}
5093 */
5094 render: function UnderlineAnnotationElement_render() {
5095 this.container.className = 'underlineAnnotation';
5096
5097 if (!this.data.hasPopup) {
5098 this._createPopup(this.container, null, this.data);
5099 }
5100
5101 return this.container;
5102 }
5103 });
5104
5105 return UnderlineAnnotationElement;
5106})();
5107
5108/**
5109 * @class
5110 * @alias SquigglyAnnotationElement
5111 */
5112var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
5113 function SquigglyAnnotationElement(parameters) {
5114 var isRenderable = !!(parameters.data.hasPopup ||
5115 parameters.data.title || parameters.data.contents);
5116 AnnotationElement.call(this, parameters, isRenderable);
5117 }
5118
5119 Util.inherit(SquigglyAnnotationElement, AnnotationElement, {
5120 /**
5121 * Render the squiggly annotation's HTML element in the empty container.
5122 *
5123 * @public
5124 * @memberof SquigglyAnnotationElement
5125 * @returns {HTMLSectionElement}
5126 */
5127 render: function SquigglyAnnotationElement_render() {
5128 this.container.className = 'squigglyAnnotation';
5129
5130 if (!this.data.hasPopup) {
5131 this._createPopup(this.container, null, this.data);
5132 }
5133
5134 return this.container;
5135 }
5136 });
5137
5138 return SquigglyAnnotationElement;
5139})();
5140
5141/**
5142 * @class
5143 * @alias StrikeOutAnnotationElement
5144 */
5145var StrikeOutAnnotationElement = (
5146 function StrikeOutAnnotationElementClosure() {
5147 function StrikeOutAnnotationElement(parameters) {
5148 var isRenderable = !!(parameters.data.hasPopup ||
5149 parameters.data.title || parameters.data.contents);
5150 AnnotationElement.call(this, parameters, isRenderable);
5151 }
5152
5153 Util.inherit(StrikeOutAnnotationElement, AnnotationElement, {
5154 /**
5155 * Render the strikeout annotation's HTML element in the empty container.
5156 *
5157 * @public
5158 * @memberof StrikeOutAnnotationElement
5159 * @returns {HTMLSectionElement}
5160 */
5161 render: function StrikeOutAnnotationElement_render() {
5162 this.container.className = 'strikeoutAnnotation';
5163
5164 if (!this.data.hasPopup) {
5165 this._createPopup(this.container, null, this.data);
5166 }
5167
5168 return this.container;
5169 }
5170 });
5171
5172 return StrikeOutAnnotationElement;
5173})();
5174
5175/**
5176 * @class
5177 * @alias FileAttachmentAnnotationElement
5178 */
5179var FileAttachmentAnnotationElement = (
5180 function FileAttachmentAnnotationElementClosure() {
5181 function FileAttachmentAnnotationElement(parameters) {
5182 AnnotationElement.call(this, parameters, true);
5183
5184 this.filename = getFilenameFromUrl(parameters.data.file.filename);
5185 this.content = parameters.data.file.content;
5186 }
5187
5188 Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, {
5189 /**
5190 * Render the file attachment annotation's HTML element in the empty
5191 * container.
5192 *
5193 * @public
5194 * @memberof FileAttachmentAnnotationElement
5195 * @returns {HTMLSectionElement}
5196 */
5197 render: function FileAttachmentAnnotationElement_render() {
5198 this.container.className = 'fileAttachmentAnnotation';
5199
5200 var trigger = document.createElement('div');
5201 trigger.style.height = this.container.style.height;
5202 trigger.style.width = this.container.style.width;
5203 trigger.addEventListener('dblclick', this._download.bind(this));
5204
5205 if (!this.data.hasPopup && (this.data.title || this.data.contents)) {
5206 this._createPopup(this.container, trigger, this.data);
5207 }
5208
5209 this.container.appendChild(trigger);
5210 return this.container;
5211 },
5212
5213 /**
5214 * Download the file attachment associated with this annotation.
5215 *
5216 * @private
5217 * @memberof FileAttachmentAnnotationElement
5218 */
5219 _download: function FileAttachmentAnnotationElement_download() {
5220 if (!this.downloadManager) {
5221 warn('Download cannot be started due to unavailable download manager');
5222 return;
5223 }
5224 this.downloadManager.downloadData(this.content, this.filename, '');
5225 }
5226 });
5227
5228 return FileAttachmentAnnotationElement;
5229})();
5230
5231/**
5232 * @typedef {Object} AnnotationLayerParameters
5233 * @property {PageViewport} viewport
5234 * @property {HTMLDivElement} div
5235 * @property {Array} annotations
5236 * @property {PDFPage} page
5237 * @property {IPDFLinkService} linkService
5238 * @property {string} imageResourcesPath
5239 * @property {boolean} renderInteractiveForms
5240 */
5241
5242/**
5243 * @class
5244 * @alias AnnotationLayer
5245 */
5246var AnnotationLayer = (function AnnotationLayerClosure() {
5247 return {
5248 /**
5249 * Render a new annotation layer with all annotation elements.
5250 *
5251 * @public
5252 * @param {AnnotationLayerParameters} parameters
5253 * @memberof AnnotationLayer
5254 */
5255 render: function AnnotationLayer_render(parameters) {
5256 var annotationElementFactory = new AnnotationElementFactory();
5257
5258 for (var i = 0, ii = parameters.annotations.length; i < ii; i++) {
5259 var data = parameters.annotations[i];
5260 if (!data) {
5261 continue;
5262 }
5263
5264 var properties = {
5265 data: data,
5266 layer: parameters.div,
5267 page: parameters.page,
5268 viewport: parameters.viewport,
5269 linkService: parameters.linkService,
5270 downloadManager: parameters.downloadManager,
5271 imageResourcesPath: parameters.imageResourcesPath ||
5272 getDefaultSetting('imageResourcesPath'),
5273 renderInteractiveForms: parameters.renderInteractiveForms || false,
5274 };
5275 var element = annotationElementFactory.create(properties);
5276 if (element.isRenderable) {
5277 parameters.div.appendChild(element.render());
5278 }
5279 }
5280 },
5281
5282 /**
5283 * Update the annotation elements on existing annotation layer.
5284 *
5285 * @public
5286 * @param {AnnotationLayerParameters} parameters
5287 * @memberof AnnotationLayer
5288 */
5289 update: function AnnotationLayer_update(parameters) {
5290 for (var i = 0, ii = parameters.annotations.length; i < ii; i++) {
5291 var data = parameters.annotations[i];
5292 var element = parameters.div.querySelector(
5293 '[data-annotation-id="' + data.id + '"]');
5294 if (element) {
5295 CustomStyle.setProp('transform', element,
5296 'matrix(' + parameters.viewport.transform.join(',') + ')');
5297 }
5298 }
5299 parameters.div.removeAttribute('hidden');
5300 }
5301 };
5302})();
5303
5304exports.AnnotationLayer = AnnotationLayer;
5305}));
5306
5307
5308(function (root, factory) {
5309 {
5310 factory((root.pdfjsDisplayTextLayer = {}), root.pdfjsSharedUtil,
5311 root.pdfjsDisplayDOMUtils);
5312 }
5313}(this, function (exports, sharedUtil, displayDOMUtils) {
5314
5315var Util = sharedUtil.Util;
5316var createPromiseCapability = sharedUtil.createPromiseCapability;
5317var CustomStyle = displayDOMUtils.CustomStyle;
5318var getDefaultSetting = displayDOMUtils.getDefaultSetting;
5319
5320/**
5321 * Text layer render parameters.
5322 *
5323 * @typedef {Object} TextLayerRenderParameters
5324 * @property {TextContent} textContent - Text content to render (the object is
5325 * returned by the page's getTextContent() method).
5326 * @property {HTMLElement} container - HTML element that will contain text runs.
5327 * @property {PageViewport} viewport - The target viewport to properly
5328 * layout the text runs.
5329 * @property {Array} textDivs - (optional) HTML elements that are correspond
5330 * the text items of the textContent input. This is output and shall be
5331 * initially be set to empty array.
5332 * @property {number} timeout - (optional) Delay in milliseconds before
5333 * rendering of the text runs occurs.
5334 * @property {boolean} enhanceTextSelection - (optional) Whether to turn on the
5335 * text selection enhancement.
5336 */
5337var renderTextLayer = (function renderTextLayerClosure() {
5338 var MAX_TEXT_DIVS_TO_RENDER = 100000;
5339
5340 var NonWhitespaceRegexp = /\S/;
5341
5342 function isAllWhitespace(str) {
5343 return !NonWhitespaceRegexp.test(str);
5344 }
5345
5346 // Text layers may contain many thousand div's, and using `styleBuf` avoids
5347 // creating many intermediate strings when building their 'style' properties.
5348 var styleBuf = ['left: ', 0, 'px; top: ', 0, 'px; font-size: ', 0,
5349 'px; font-family: ', '', ';'];
5350
5351 function appendText(task, geom, styles) {
5352 // Initialize all used properties to keep the caches monomorphic.
5353 var textDiv = document.createElement('div');
5354 var textDivProperties = {
5355 style: null,
5356 angle: 0,
5357 canvasWidth: 0,
5358 isWhitespace: false,
5359 originalTransform: null,
5360 paddingBottom: 0,
5361 paddingLeft: 0,
5362 paddingRight: 0,
5363 paddingTop: 0,
5364 scale: 1,
5365 };
5366
5367 task._textDivs.push(textDiv);
5368 if (isAllWhitespace(geom.str)) {
5369 textDivProperties.isWhitespace = true;
5370 task._textDivProperties.set(textDiv, textDivProperties);
5371 return;
5372 }
5373
5374 var tx = Util.transform(task._viewport.transform, geom.transform);
5375 var angle = Math.atan2(tx[1], tx[0]);
5376 var style = styles[geom.fontName];
5377 if (style.vertical) {
5378 angle += Math.PI / 2;
5379 }
5380 var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
5381 var fontAscent = fontHeight;
5382 if (style.ascent) {
5383 fontAscent = style.ascent * fontAscent;
5384 } else if (style.descent) {
5385 fontAscent = (1 + style.descent) * fontAscent;
5386 }
5387
5388 var left;
5389 var top;
5390 if (angle === 0) {
5391 left = tx[4];
5392 top = tx[5] - fontAscent;
5393 } else {
5394 left = tx[4] + (fontAscent * Math.sin(angle));
5395 top = tx[5] - (fontAscent * Math.cos(angle));
5396 }
5397 styleBuf[1] = left;
5398 styleBuf[3] = top;
5399 styleBuf[5] = fontHeight;
5400 styleBuf[7] = style.fontFamily;
5401 textDivProperties.style = styleBuf.join('');
5402 textDiv.setAttribute('style', textDivProperties.style);
5403
5404 textDiv.textContent = geom.str;
5405 // |fontName| is only used by the Font Inspector. This test will succeed
5406 // when e.g. the Font Inspector is off but the Stepper is on, but it's
5407 // not worth the effort to do a more accurate test. We only use `dataset`
5408 // here to make the font name available for the debugger.
5409 if (getDefaultSetting('pdfBug')) {
5410 textDiv.dataset.fontName = geom.fontName;
5411 }
5412 if (angle !== 0) {
5413 textDivProperties.angle = angle * (180 / Math.PI);
5414 }
5415 // We don't bother scaling single-char text divs, because it has very
5416 // little effect on text highlighting. This makes scrolling on docs with
5417 // lots of such divs a lot faster.
5418 if (geom.str.length > 1) {
5419 if (style.vertical) {
5420 textDivProperties.canvasWidth = geom.height * task._viewport.scale;
5421 } else {
5422 textDivProperties.canvasWidth = geom.width * task._viewport.scale;
5423 }
5424 }
5425 task._textDivProperties.set(textDiv, textDivProperties);
5426
5427 if (task._enhanceTextSelection) {
5428 var angleCos = 1, angleSin = 0;
5429 if (angle !== 0) {
5430 angleCos = Math.cos(angle);
5431 angleSin = Math.sin(angle);
5432 }
5433 var divWidth = (style.vertical ? geom.height : geom.width) *
5434 task._viewport.scale;
5435 var divHeight = fontHeight;
5436
5437 var m, b;
5438 if (angle !== 0) {
5439 m = [angleCos, angleSin, -angleSin, angleCos, left, top];
5440 b = Util.getAxialAlignedBoundingBox([0, 0, divWidth, divHeight], m);
5441 } else {
5442 b = [left, top, left + divWidth, top + divHeight];
5443 }
5444
5445 task._bounds.push({
5446 left: b[0],
5447 top: b[1],
5448 right: b[2],
5449 bottom: b[3],
5450 div: textDiv,
5451 size: [divWidth, divHeight],
5452 m: m
5453 });
5454 }
5455 }
5456
5457 function render(task) {
5458 if (task._canceled) {
5459 return;
5460 }
5461 var textLayerFrag = task._container;
5462 var textDivs = task._textDivs;
5463 var capability = task._capability;
5464 var textDivsLength = textDivs.length;
5465
5466 // No point in rendering many divs as it would make the browser
5467 // unusable even after the divs are rendered.
5468 if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
5469 task._renderingDone = true;
5470 capability.resolve();
5471 return;
5472 }
5473
5474 var canvas = document.createElement('canvas');
5475 canvas.mozOpaque = true;
5476 var ctx = canvas.getContext('2d', {alpha: false});
5477
5478 var lastFontSize;
5479 var lastFontFamily;
5480 for (var i = 0; i < textDivsLength; i++) {
5481 var textDiv = textDivs[i];
5482 var textDivProperties = task._textDivProperties.get(textDiv);
5483 if (textDivProperties.isWhitespace) {
5484 continue;
5485 }
5486
5487 var fontSize = textDiv.style.fontSize;
5488 var fontFamily = textDiv.style.fontFamily;
5489
5490 // Only build font string and set to context if different from last.
5491 if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) {
5492 ctx.font = fontSize + ' ' + fontFamily;
5493 lastFontSize = fontSize;
5494 lastFontFamily = fontFamily;
5495 }
5496
5497 var width = ctx.measureText(textDiv.textContent).width;
5498 textLayerFrag.appendChild(textDiv);
5499
5500 var transform = '';
5501 if (textDivProperties.canvasWidth !== 0 && width > 0) {
5502 textDivProperties.scale = textDivProperties.canvasWidth / width;
5503 transform = 'scaleX(' + textDivProperties.scale + ')';
5504 }
5505 if (textDivProperties.angle !== 0) {
5506 transform = 'rotate(' + textDivProperties.angle + 'deg) ' + transform;
5507 }
5508 if (transform !== '') {
5509 textDivProperties.originalTransform = transform;
5510 CustomStyle.setProp('transform', textDiv, transform);
5511 }
5512 task._textDivProperties.set(textDiv, textDivProperties);
5513 }
5514 task._renderingDone = true;
5515 capability.resolve();
5516 }
5517
5518 function expand(task) {
5519 var bounds = task._bounds;
5520 var viewport = task._viewport;
5521
5522 var expanded = expandBounds(viewport.width, viewport.height, bounds);
5523 for (var i = 0; i < expanded.length; i++) {
5524 var div = bounds[i].div;
5525 var divProperties = task._textDivProperties.get(div);
5526 if (divProperties.angle === 0) {
5527 divProperties.paddingLeft = bounds[i].left - expanded[i].left;
5528 divProperties.paddingTop = bounds[i].top - expanded[i].top;
5529 divProperties.paddingRight = expanded[i].right - bounds[i].right;
5530 divProperties.paddingBottom = expanded[i].bottom - bounds[i].bottom;
5531 task._textDivProperties.set(div, divProperties);
5532 continue;
5533 }
5534 // Box is rotated -- trying to find padding so rotated div will not
5535 // exceed its expanded bounds.
5536 var e = expanded[i], b = bounds[i];
5537 var m = b.m, c = m[0], s = m[1];
5538 // Finding intersections with expanded box.
5539 var points = [[0, 0], [0, b.size[1]], [b.size[0], 0], b.size];
5540 var ts = new Float64Array(64);
5541 points.forEach(function (p, i) {
5542 var t = Util.applyTransform(p, m);
5543 ts[i + 0] = c && (e.left - t[0]) / c;
5544 ts[i + 4] = s && (e.top - t[1]) / s;
5545 ts[i + 8] = c && (e.right - t[0]) / c;
5546 ts[i + 12] = s && (e.bottom - t[1]) / s;
5547
5548 ts[i + 16] = s && (e.left - t[0]) / -s;
5549 ts[i + 20] = c && (e.top - t[1]) / c;
5550 ts[i + 24] = s && (e.right - t[0]) / -s;
5551 ts[i + 28] = c && (e.bottom - t[1]) / c;
5552
5553 ts[i + 32] = c && (e.left - t[0]) / -c;
5554 ts[i + 36] = s && (e.top - t[1]) / -s;
5555 ts[i + 40] = c && (e.right - t[0]) / -c;
5556 ts[i + 44] = s && (e.bottom - t[1]) / -s;
5557
5558 ts[i + 48] = s && (e.left - t[0]) / s;
5559 ts[i + 52] = c && (e.top - t[1]) / -c;
5560 ts[i + 56] = s && (e.right - t[0]) / s;
5561 ts[i + 60] = c && (e.bottom - t[1]) / -c;
5562 });
5563 var findPositiveMin = function (ts, offset, count) {
5564 var result = 0;
5565 for (var i = 0; i < count; i++) {
5566 var t = ts[offset++];
5567 if (t > 0) {
5568 result = result ? Math.min(t, result) : t;
5569 }
5570 }
5571 return result;
5572 };
5573 // Not based on math, but to simplify calculations, using cos and sin
5574 // absolute values to not exceed the box (it can but insignificantly).
5575 var boxScale = 1 + Math.min(Math.abs(c), Math.abs(s));
5576 divProperties.paddingLeft = findPositiveMin(ts, 32, 16) / boxScale;
5577 divProperties.paddingTop = findPositiveMin(ts, 48, 16) / boxScale;
5578 divProperties.paddingRight = findPositiveMin(ts, 0, 16) / boxScale;
5579 divProperties.paddingBottom = findPositiveMin(ts, 16, 16) / boxScale;
5580 task._textDivProperties.set(div, divProperties);
5581 }
5582 }
5583
5584 function expandBounds(width, height, boxes) {
5585 var bounds = boxes.map(function (box, i) {
5586 return {
5587 x1: box.left,
5588 y1: box.top,
5589 x2: box.right,
5590 y2: box.bottom,
5591 index: i,
5592 x1New: undefined,
5593 x2New: undefined
5594 };
5595 });
5596 expandBoundsLTR(width, bounds);
5597 var expanded = new Array(boxes.length);
5598 bounds.forEach(function (b) {
5599 var i = b.index;
5600 expanded[i] = {
5601 left: b.x1New,
5602 top: 0,
5603 right: b.x2New,
5604 bottom: 0
5605 };
5606 });
5607
5608 // Rotating on 90 degrees and extending extended boxes. Reusing the bounds
5609 // array and objects.
5610 boxes.map(function (box, i) {
5611 var e = expanded[i], b = bounds[i];
5612 b.x1 = box.top;
5613 b.y1 = width - e.right;
5614 b.x2 = box.bottom;
5615 b.y2 = width - e.left;
5616 b.index = i;
5617 b.x1New = undefined;
5618 b.x2New = undefined;
5619 });
5620 expandBoundsLTR(height, bounds);
5621
5622 bounds.forEach(function (b) {
5623 var i = b.index;
5624 expanded[i].top = b.x1New;
5625 expanded[i].bottom = b.x2New;
5626 });
5627 return expanded;
5628 }
5629
5630 function expandBoundsLTR(width, bounds) {
5631 // Sorting by x1 coordinate and walk by the bounds in the same order.
5632 bounds.sort(function (a, b) { return a.x1 - b.x1 || a.index - b.index; });
5633
5634 // First we see on the horizon is a fake boundary.
5635 var fakeBoundary = {
5636 x1: -Infinity,
5637 y1: -Infinity,
5638 x2: 0,
5639 y2: Infinity,
5640 index: -1,
5641 x1New: 0,
5642 x2New: 0
5643 };
5644 var horizon = [{
5645 start: -Infinity,
5646 end: Infinity,
5647 boundary: fakeBoundary
5648 }];
5649
5650 bounds.forEach(function (boundary) {
5651 // Searching for the affected part of horizon.
5652 // TODO red-black tree or simple binary search
5653 var i = 0;
5654 while (i < horizon.length && horizon[i].end <= boundary.y1) {
5655 i++;
5656 }
5657 var j = horizon.length - 1;
5658 while(j >= 0 && horizon[j].start >= boundary.y2) {
5659 j--;
5660 }
5661
5662 var horizonPart, affectedBoundary;
5663 var q, k, maxXNew = -Infinity;
5664 for (q = i; q <= j; q++) {
5665 horizonPart = horizon[q];
5666 affectedBoundary = horizonPart.boundary;
5667 var xNew;
5668 if (affectedBoundary.x2 > boundary.x1) {
5669 // In the middle of the previous element, new x shall be at the
5670 // boundary start. Extending if further if the affected bondary
5671 // placed on top of the current one.
5672 xNew = affectedBoundary.index > boundary.index ?
5673 affectedBoundary.x1New : boundary.x1;
5674 } else if (affectedBoundary.x2New === undefined) {
5675 // We have some space in between, new x in middle will be a fair
5676 // choice.
5677 xNew = (affectedBoundary.x2 + boundary.x1) / 2;
5678 } else {
5679 // Affected boundary has x2new set, using it as new x.
5680 xNew = affectedBoundary.x2New;
5681 }
5682 if (xNew > maxXNew) {
5683 maxXNew = xNew;
5684 }
5685 }
5686
5687 // Set new x1 for current boundary.
5688 boundary.x1New = maxXNew;
5689
5690 // Adjusts new x2 for the affected boundaries.
5691 for (q = i; q <= j; q++) {
5692 horizonPart = horizon[q];
5693 affectedBoundary = horizonPart.boundary;
5694 if (affectedBoundary.x2New === undefined) {
5695 // Was not set yet, choosing new x if possible.
5696 if (affectedBoundary.x2 > boundary.x1) {
5697 // Current and affected boundaries intersect. If affected boundary
5698 // is placed on top of the current, shrinking the affected.
5699 if (affectedBoundary.index > boundary.index) {
5700 affectedBoundary.x2New = affectedBoundary.x2;
5701 }
5702 } else {
5703 affectedBoundary.x2New = maxXNew;
5704 }
5705 } else if (affectedBoundary.x2New > maxXNew) {
5706 // Affected boundary is touching new x, pushing it back.
5707 affectedBoundary.x2New = Math.max(maxXNew, affectedBoundary.x2);
5708 }
5709 }
5710
5711 // Fixing the horizon.
5712 var changedHorizon = [], lastBoundary = null;
5713 for (q = i; q <= j; q++) {
5714 horizonPart = horizon[q];
5715 affectedBoundary = horizonPart.boundary;
5716 // Checking which boundary will be visible.
5717 var useBoundary = affectedBoundary.x2 > boundary.x2 ?
5718 affectedBoundary : boundary;
5719 if (lastBoundary === useBoundary) {
5720 // Merging with previous.
5721 changedHorizon[changedHorizon.length - 1].end = horizonPart.end;
5722 } else {
5723 changedHorizon.push({
5724 start: horizonPart.start,
5725 end: horizonPart.end,
5726 boundary: useBoundary
5727 });
5728 lastBoundary = useBoundary;
5729 }
5730 }
5731 if (horizon[i].start < boundary.y1) {
5732 changedHorizon[0].start = boundary.y1;
5733 changedHorizon.unshift({
5734 start: horizon[i].start,
5735 end: boundary.y1,
5736 boundary: horizon[i].boundary
5737 });
5738 }
5739 if (boundary.y2 < horizon[j].end) {
5740 changedHorizon[changedHorizon.length - 1].end = boundary.y2;
5741 changedHorizon.push({
5742 start: boundary.y2,
5743 end: horizon[j].end,
5744 boundary: horizon[j].boundary
5745 });
5746 }
5747
5748 // Set x2 new of boundary that is no longer visible (see overlapping case
5749 // above).
5750 // TODO more efficient, e.g. via reference counting.
5751 for (q = i; q <= j; q++) {
5752 horizonPart = horizon[q];
5753 affectedBoundary = horizonPart.boundary;
5754 if (affectedBoundary.x2New !== undefined) {
5755 continue;
5756 }
5757 var used = false;
5758 for (k = i - 1; !used && k >= 0 &&
5759 horizon[k].start >= affectedBoundary.y1; k--) {
5760 used = horizon[k].boundary === affectedBoundary;
5761 }
5762 for (k = j + 1; !used && k < horizon.length &&
5763 horizon[k].end <= affectedBoundary.y2; k++) {
5764 used = horizon[k].boundary === affectedBoundary;
5765 }
5766 for (k = 0; !used && k < changedHorizon.length; k++) {
5767 used = changedHorizon[k].boundary === affectedBoundary;
5768 }
5769 if (!used) {
5770 affectedBoundary.x2New = maxXNew;
5771 }
5772 }
5773
5774 Array.prototype.splice.apply(horizon,
5775 [i, j - i + 1].concat(changedHorizon));
5776 });
5777
5778 // Set new x2 for all unset boundaries.
5779 horizon.forEach(function (horizonPart) {
5780 var affectedBoundary = horizonPart.boundary;
5781 if (affectedBoundary.x2New === undefined) {
5782 affectedBoundary.x2New = Math.max(width, affectedBoundary.x2);
5783 }
5784 });
5785 }
5786
5787 /**
5788 * Text layer rendering task.
5789 *
5790 * @param {TextContent} textContent
5791 * @param {HTMLElement} container
5792 * @param {PageViewport} viewport
5793 * @param {Array} textDivs
5794 * @param {boolean} enhanceTextSelection
5795 * @private
5796 */
5797 function TextLayerRenderTask(textContent, container, viewport, textDivs,
5798 enhanceTextSelection) {
5799 this._textContent = textContent;
5800 this._container = container;
5801 this._viewport = viewport;
5802 this._textDivs = textDivs || [];
5803 this._textDivProperties = new WeakMap();
5804 this._renderingDone = false;
5805 this._canceled = false;
5806 this._capability = createPromiseCapability();
5807 this._renderTimer = null;
5808 this._bounds = [];
5809 this._enhanceTextSelection = !!enhanceTextSelection;
5810 }
5811 TextLayerRenderTask.prototype = {
5812 get promise() {
5813 return this._capability.promise;
5814 },
5815
5816 cancel: function TextLayer_cancel() {
5817 this._canceled = true;
5818 if (this._renderTimer !== null) {
5819 clearTimeout(this._renderTimer);
5820 this._renderTimer = null;
5821 }
5822 this._capability.reject('canceled');
5823 },
5824
5825 _render: function TextLayer_render(timeout) {
5826 var textItems = this._textContent.items;
5827 var textStyles = this._textContent.styles;
5828 for (var i = 0, len = textItems.length; i < len; i++) {
5829 appendText(this, textItems[i], textStyles);
5830 }
5831
5832 if (!timeout) { // Render right away
5833 render(this);
5834 } else { // Schedule
5835 var self = this;
5836 this._renderTimer = setTimeout(function() {
5837 render(self);
5838 self._renderTimer = null;
5839 }, timeout);
5840 }
5841 },
5842
5843 expandTextDivs: function TextLayer_expandTextDivs(expandDivs) {
5844 if (!this._enhanceTextSelection || !this._renderingDone) {
5845 return;
5846 }
5847 if (this._bounds !== null) {
5848 expand(this);
5849 this._bounds = null;
5850 }
5851
5852 for (var i = 0, ii = this._textDivs.length; i < ii; i++) {
5853 var div = this._textDivs[i];
5854 var divProperties = this._textDivProperties.get(div);
5855
5856 if (divProperties.isWhitespace) {
5857 continue;
5858 }
5859 if (expandDivs) {
5860 var transform = '', padding = '';
5861
5862 if (divProperties.scale !== 1) {
5863 transform = 'scaleX(' + divProperties.scale + ')';
5864 }
5865 if (divProperties.angle !== 0) {
5866 transform = 'rotate(' + divProperties.angle + 'deg) ' + transform;
5867 }
5868 if (divProperties.paddingLeft !== 0) {
5869 padding += ' padding-left: ' +
5870 (divProperties.paddingLeft / divProperties.scale) + 'px;';
5871 transform += ' translateX(' +
5872 (-divProperties.paddingLeft / divProperties.scale) + 'px)';
5873 }
5874 if (divProperties.paddingTop !== 0) {
5875 padding += ' padding-top: ' + divProperties.paddingTop + 'px;';
5876 transform += ' translateY(' + (-divProperties.paddingTop) + 'px)';
5877 }
5878 if (divProperties.paddingRight !== 0) {
5879 padding += ' padding-right: ' +
5880 (divProperties.paddingRight / divProperties.scale) + 'px;';
5881 }
5882 if (divProperties.paddingBottom !== 0) {
5883 padding += ' padding-bottom: ' +
5884 divProperties.paddingBottom + 'px;';
5885 }
5886
5887 if (padding !== '') {
5888 div.setAttribute('style', divProperties.style + padding);
5889 }
5890 if (transform !== '') {
5891 CustomStyle.setProp('transform', div, transform);
5892 }
5893 } else {
5894 div.style.padding = 0;
5895 CustomStyle.setProp('transform', div,
5896 divProperties.originalTransform || '');
5897 }
5898 }
5899 },
5900 };
5901
5902 /**
5903 * Starts rendering of the text layer.
5904 *
5905 * @param {TextLayerRenderParameters} renderParameters
5906 * @returns {TextLayerRenderTask}
5907 */
5908 function renderTextLayer(renderParameters) {
5909 var task = new TextLayerRenderTask(renderParameters.textContent,
5910 renderParameters.container,
5911 renderParameters.viewport,
5912 renderParameters.textDivs,
5913 renderParameters.enhanceTextSelection);
5914 task._render(renderParameters.timeout);
5915 return task;
5916 }
5917
5918 return renderTextLayer;
5919})();
5920
5921exports.renderTextLayer = renderTextLayer;
5922}));
5923
5924
5925(function (root, factory) {
5926 {
5927 factory((root.pdfjsDisplayWebGL = {}), root.pdfjsSharedUtil,
5928 root.pdfjsDisplayDOMUtils);
5929 }
5930}(this, function (exports, sharedUtil, displayDOMUtils) {
5931
5932var shadow = sharedUtil.shadow;
5933var getDefaultSetting = displayDOMUtils.getDefaultSetting;
5934
5935var WebGLUtils = (function WebGLUtilsClosure() {
5936 function loadShader(gl, code, shaderType) {
5937 var shader = gl.createShader(shaderType);
5938 gl.shaderSource(shader, code);
5939 gl.compileShader(shader);
5940 var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
5941 if (!compiled) {
5942 var errorMsg = gl.getShaderInfoLog(shader);
5943 throw new Error('Error during shader compilation: ' + errorMsg);
5944 }
5945 return shader;
5946 }
5947 function createVertexShader(gl, code) {
5948 return loadShader(gl, code, gl.VERTEX_SHADER);
5949 }
5950 function createFragmentShader(gl, code) {
5951 return loadShader(gl, code, gl.FRAGMENT_SHADER);
5952 }
5953 function createProgram(gl, shaders) {
5954 var program = gl.createProgram();
5955 for (var i = 0, ii = shaders.length; i < ii; ++i) {
5956 gl.attachShader(program, shaders[i]);
5957 }
5958 gl.linkProgram(program);
5959 var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
5960 if (!linked) {
5961 var errorMsg = gl.getProgramInfoLog(program);
5962 throw new Error('Error during program linking: ' + errorMsg);
5963 }
5964 return program;
5965 }
5966 function createTexture(gl, image, textureId) {
5967 gl.activeTexture(textureId);
5968 var texture = gl.createTexture();
5969 gl.bindTexture(gl.TEXTURE_2D, texture);
5970
5971 // Set the parameters so we can render any size image.
5972 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
5973 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
5974 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
5975 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
5976
5977 // Upload the image into the texture.
5978 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
5979 return texture;
5980 }
5981
5982 var currentGL, currentCanvas;
5983 function generateGL() {
5984 if (currentGL) {
5985 return;
5986 }
5987 currentCanvas = document.createElement('canvas');
5988 currentGL = currentCanvas.getContext('webgl',
5989 { premultipliedalpha: false });
5990 }
5991
5992 var smaskVertexShaderCode = '\
5993 attribute vec2 a_position; \
5994 attribute vec2 a_texCoord; \
5995 \
5996 uniform vec2 u_resolution; \
5997 \
5998 varying vec2 v_texCoord; \
5999 \
6000 void main() { \
6001 vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; \
6002 gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \
6003 \
6004 v_texCoord = a_texCoord; \
6005 } ';
6006
6007 var smaskFragmentShaderCode = '\
6008 precision mediump float; \
6009 \
6010 uniform vec4 u_backdrop; \
6011 uniform int u_subtype; \
6012 uniform sampler2D u_image; \
6013 uniform sampler2D u_mask; \
6014 \
6015 varying vec2 v_texCoord; \
6016 \
6017 void main() { \
6018 vec4 imageColor = texture2D(u_image, v_texCoord); \
6019 vec4 maskColor = texture2D(u_mask, v_texCoord); \
6020 if (u_backdrop.a > 0.0) { \
6021 maskColor.rgb = maskColor.rgb * maskColor.a + \
6022 u_backdrop.rgb * (1.0 - maskColor.a); \
6023 } \
6024 float lum; \
6025 if (u_subtype == 0) { \
6026 lum = maskColor.a; \
6027 } else { \
6028 lum = maskColor.r * 0.3 + maskColor.g * 0.59 + \
6029 maskColor.b * 0.11; \
6030 } \
6031 imageColor.a *= lum; \
6032 imageColor.rgb *= imageColor.a; \
6033 gl_FragColor = imageColor; \
6034 } ';
6035
6036 var smaskCache = null;
6037
6038 function initSmaskGL() {
6039 var canvas, gl;
6040
6041 generateGL();
6042 canvas = currentCanvas;
6043 currentCanvas = null;
6044 gl = currentGL;
6045 currentGL = null;
6046
6047 // setup a GLSL program
6048 var vertexShader = createVertexShader(gl, smaskVertexShaderCode);
6049 var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
6050 var program = createProgram(gl, [vertexShader, fragmentShader]);
6051 gl.useProgram(program);
6052
6053 var cache = {};
6054 cache.gl = gl;
6055 cache.canvas = canvas;
6056 cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
6057 cache.positionLocation = gl.getAttribLocation(program, 'a_position');
6058 cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop');
6059 cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype');
6060
6061 var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
6062 var texLayerLocation = gl.getUniformLocation(program, 'u_image');
6063 var texMaskLocation = gl.getUniformLocation(program, 'u_mask');
6064
6065 // provide texture coordinates for the rectangle.
6066 var texCoordBuffer = gl.createBuffer();
6067 gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
6068 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
6069 0.0, 0.0,
6070 1.0, 0.0,
6071 0.0, 1.0,
6072 0.0, 1.0,
6073 1.0, 0.0,
6074 1.0, 1.0]), gl.STATIC_DRAW);
6075 gl.enableVertexAttribArray(texCoordLocation);
6076 gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
6077
6078 gl.uniform1i(texLayerLocation, 0);
6079 gl.uniform1i(texMaskLocation, 1);
6080
6081 smaskCache = cache;
6082 }
6083
6084 function composeSMask(layer, mask, properties) {
6085 var width = layer.width, height = layer.height;
6086
6087 if (!smaskCache) {
6088 initSmaskGL();
6089 }
6090 var cache = smaskCache,canvas = cache.canvas, gl = cache.gl;
6091 canvas.width = width;
6092 canvas.height = height;
6093 gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
6094 gl.uniform2f(cache.resolutionLocation, width, height);
6095
6096 if (properties.backdrop) {
6097 gl.uniform4f(cache.resolutionLocation, properties.backdrop[0],
6098 properties.backdrop[1], properties.backdrop[2], 1);
6099 } else {
6100 gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0);
6101 }
6102 gl.uniform1i(cache.subtypeLocation,
6103 properties.subtype === 'Luminosity' ? 1 : 0);
6104
6105 // Create a textures
6106 var texture = createTexture(gl, layer, gl.TEXTURE0);
6107 var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
6108
6109
6110 // Create a buffer and put a single clipspace rectangle in
6111 // it (2 triangles)
6112 var buffer = gl.createBuffer();
6113 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
6114 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
6115 0, 0,
6116 width, 0,
6117 0, height,
6118 0, height,
6119 width, 0,
6120 width, height]), gl.STATIC_DRAW);
6121 gl.enableVertexAttribArray(cache.positionLocation);
6122 gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
6123
6124 // draw
6125 gl.clearColor(0, 0, 0, 0);
6126 gl.enable(gl.BLEND);
6127 gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
6128 gl.clear(gl.COLOR_BUFFER_BIT);
6129
6130 gl.drawArrays(gl.TRIANGLES, 0, 6);
6131
6132 gl.flush();
6133
6134 gl.deleteTexture(texture);
6135 gl.deleteTexture(maskTexture);
6136 gl.deleteBuffer(buffer);
6137
6138 return canvas;
6139 }
6140
6141 var figuresVertexShaderCode = '\
6142 attribute vec2 a_position; \
6143 attribute vec3 a_color; \
6144 \
6145 uniform vec2 u_resolution; \
6146 uniform vec2 u_scale; \
6147 uniform vec2 u_offset; \
6148 \
6149 varying vec4 v_color; \
6150 \
6151 void main() { \
6152 vec2 position = (a_position + u_offset) * u_scale; \
6153 vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0; \
6154 gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \
6155 \
6156 v_color = vec4(a_color / 255.0, 1.0); \
6157 } ';
6158
6159 var figuresFragmentShaderCode = '\
6160 precision mediump float; \
6161 \
6162 varying vec4 v_color; \
6163 \
6164 void main() { \
6165 gl_FragColor = v_color; \
6166 } ';
6167
6168 var figuresCache = null;
6169
6170 function initFiguresGL() {
6171 var canvas, gl;
6172
6173 generateGL();
6174 canvas = currentCanvas;
6175 currentCanvas = null;
6176 gl = currentGL;
6177 currentGL = null;
6178
6179 // setup a GLSL program
6180 var vertexShader = createVertexShader(gl, figuresVertexShaderCode);
6181 var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
6182 var program = createProgram(gl, [vertexShader, fragmentShader]);
6183 gl.useProgram(program);
6184
6185 var cache = {};
6186 cache.gl = gl;
6187 cache.canvas = canvas;
6188 cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
6189 cache.scaleLocation = gl.getUniformLocation(program, 'u_scale');
6190 cache.offsetLocation = gl.getUniformLocation(program, 'u_offset');
6191 cache.positionLocation = gl.getAttribLocation(program, 'a_position');
6192 cache.colorLocation = gl.getAttribLocation(program, 'a_color');
6193
6194 figuresCache = cache;
6195 }
6196
6197 function drawFigures(width, height, backgroundColor, figures, context) {
6198 if (!figuresCache) {
6199 initFiguresGL();
6200 }
6201 var cache = figuresCache, canvas = cache.canvas, gl = cache.gl;
6202
6203 canvas.width = width;
6204 canvas.height = height;
6205 gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
6206 gl.uniform2f(cache.resolutionLocation, width, height);
6207
6208 // count triangle points
6209 var count = 0;
6210 var i, ii, rows;
6211 for (i = 0, ii = figures.length; i < ii; i++) {
6212 switch (figures[i].type) {
6213 case 'lattice':
6214 rows = (figures[i].coords.length / figures[i].verticesPerRow) | 0;
6215 count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6;
6216 break;
6217 case 'triangles':
6218 count += figures[i].coords.length;
6219 break;
6220 }
6221 }
6222 // transfer data
6223 var coords = new Float32Array(count * 2);
6224 var colors = new Uint8Array(count * 3);
6225 var coordsMap = context.coords, colorsMap = context.colors;
6226 var pIndex = 0, cIndex = 0;
6227 for (i = 0, ii = figures.length; i < ii; i++) {
6228 var figure = figures[i], ps = figure.coords, cs = figure.colors;
6229 switch (figure.type) {
6230 case 'lattice':
6231 var cols = figure.verticesPerRow;
6232 rows = (ps.length / cols) | 0;
6233 for (var row = 1; row < rows; row++) {
6234 var offset = row * cols + 1;
6235 for (var col = 1; col < cols; col++, offset++) {
6236 coords[pIndex] = coordsMap[ps[offset - cols - 1]];
6237 coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1];
6238 coords[pIndex + 2] = coordsMap[ps[offset - cols]];
6239 coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1];
6240 coords[pIndex + 4] = coordsMap[ps[offset - 1]];
6241 coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1];
6242 colors[cIndex] = colorsMap[cs[offset - cols - 1]];
6243 colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1];
6244 colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2];
6245 colors[cIndex + 3] = colorsMap[cs[offset - cols]];
6246 colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1];
6247 colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2];
6248 colors[cIndex + 6] = colorsMap[cs[offset - 1]];
6249 colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1];
6250 colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2];
6251
6252 coords[pIndex + 6] = coords[pIndex + 2];
6253 coords[pIndex + 7] = coords[pIndex + 3];
6254 coords[pIndex + 8] = coords[pIndex + 4];
6255 coords[pIndex + 9] = coords[pIndex + 5];
6256 coords[pIndex + 10] = coordsMap[ps[offset]];
6257 coords[pIndex + 11] = coordsMap[ps[offset] + 1];
6258 colors[cIndex + 9] = colors[cIndex + 3];
6259 colors[cIndex + 10] = colors[cIndex + 4];
6260 colors[cIndex + 11] = colors[cIndex + 5];
6261 colors[cIndex + 12] = colors[cIndex + 6];
6262 colors[cIndex + 13] = colors[cIndex + 7];
6263 colors[cIndex + 14] = colors[cIndex + 8];
6264 colors[cIndex + 15] = colorsMap[cs[offset]];
6265 colors[cIndex + 16] = colorsMap[cs[offset] + 1];
6266 colors[cIndex + 17] = colorsMap[cs[offset] + 2];
6267 pIndex += 12;
6268 cIndex += 18;
6269 }
6270 }
6271 break;
6272 case 'triangles':
6273 for (var j = 0, jj = ps.length; j < jj; j++) {
6274 coords[pIndex] = coordsMap[ps[j]];
6275 coords[pIndex + 1] = coordsMap[ps[j] + 1];
6276 colors[cIndex] = colorsMap[cs[j]];
6277 colors[cIndex + 1] = colorsMap[cs[j] + 1];
6278 colors[cIndex + 2] = colorsMap[cs[j] + 2];
6279 pIndex += 2;
6280 cIndex += 3;
6281 }
6282 break;
6283 }
6284 }
6285
6286 // draw
6287 if (backgroundColor) {
6288 gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255,
6289 backgroundColor[2] / 255, 1.0);
6290 } else {
6291 gl.clearColor(0, 0, 0, 0);
6292 }
6293 gl.clear(gl.COLOR_BUFFER_BIT);
6294
6295 var coordsBuffer = gl.createBuffer();
6296 gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer);
6297 gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
6298 gl.enableVertexAttribArray(cache.positionLocation);
6299 gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
6300
6301 var colorsBuffer = gl.createBuffer();
6302 gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
6303 gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
6304 gl.enableVertexAttribArray(cache.colorLocation);
6305 gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false,
6306 0, 0);
6307
6308 gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY);
6309 gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY);
6310
6311 gl.drawArrays(gl.TRIANGLES, 0, count);
6312
6313 gl.flush();
6314
6315 gl.deleteBuffer(coordsBuffer);
6316 gl.deleteBuffer(colorsBuffer);
6317
6318 return canvas;
6319 }
6320
6321 function cleanup() {
6322 if (smaskCache && smaskCache.canvas) {
6323 smaskCache.canvas.width = 0;
6324 smaskCache.canvas.height = 0;
6325 }
6326 if (figuresCache && figuresCache.canvas) {
6327 figuresCache.canvas.width = 0;
6328 figuresCache.canvas.height = 0;
6329 }
6330 smaskCache = null;
6331 figuresCache = null;
6332 }
6333
6334 return {
6335 get isEnabled() {
6336 if (getDefaultSetting('disableWebGL')) {
6337 return false;
6338 }
6339 var enabled = false;
6340 try {
6341 generateGL();
6342 enabled = !!currentGL;
6343 } catch (e) { }
6344 return shadow(this, 'isEnabled', enabled);
6345 },
6346 composeSMask: composeSMask,
6347 drawFigures: drawFigures,
6348 clear: cleanup
6349 };
6350})();
6351
6352exports.WebGLUtils = WebGLUtils;
6353}));
6354
6355
6356(function (root, factory) {
6357 {
6358 factory((root.pdfjsDisplayPatternHelper = {}), root.pdfjsSharedUtil,
6359 root.pdfjsDisplayWebGL);
6360 }
6361}(this, function (exports, sharedUtil, displayWebGL) {
6362
6363var Util = sharedUtil.Util;
6364var info = sharedUtil.info;
6365var isArray = sharedUtil.isArray;
6366var error = sharedUtil.error;
6367var WebGLUtils = displayWebGL.WebGLUtils;
6368
6369var ShadingIRs = {};
6370
6371ShadingIRs.RadialAxial = {
6372 fromIR: function RadialAxial_fromIR(raw) {
6373 var type = raw[1];
6374 var colorStops = raw[2];
6375 var p0 = raw[3];
6376 var p1 = raw[4];
6377 var r0 = raw[5];
6378 var r1 = raw[6];
6379 return {
6380 type: 'Pattern',
6381 getPattern: function RadialAxial_getPattern(ctx) {
6382 var grad;
6383 if (type === 'axial') {
6384 grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
6385 } else if (type === 'radial') {
6386 grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
6387 }
6388
6389 for (var i = 0, ii = colorStops.length; i < ii; ++i) {
6390 var c = colorStops[i];
6391 grad.addColorStop(c[0], c[1]);
6392 }
6393 return grad;
6394 }
6395 };
6396 }
6397};
6398
6399var createMeshCanvas = (function createMeshCanvasClosure() {
6400 function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
6401 // Very basic Gouraud-shaded triangle rasterization algorithm.
6402 var coords = context.coords, colors = context.colors;
6403 var bytes = data.data, rowSize = data.width * 4;
6404 var tmp;
6405 if (coords[p1 + 1] > coords[p2 + 1]) {
6406 tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp;
6407 }
6408 if (coords[p2 + 1] > coords[p3 + 1]) {
6409 tmp = p2; p2 = p3; p3 = tmp; tmp = c2; c2 = c3; c3 = tmp;
6410 }
6411 if (coords[p1 + 1] > coords[p2 + 1]) {
6412 tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp;
6413 }
6414 var x1 = (coords[p1] + context.offsetX) * context.scaleX;
6415 var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
6416 var x2 = (coords[p2] + context.offsetX) * context.scaleX;
6417 var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
6418 var x3 = (coords[p3] + context.offsetX) * context.scaleX;
6419 var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
6420 if (y1 >= y3) {
6421 return;
6422 }
6423 var c1r = colors[c1], c1g = colors[c1 + 1], c1b = colors[c1 + 2];
6424 var c2r = colors[c2], c2g = colors[c2 + 1], c2b = colors[c2 + 2];
6425 var c3r = colors[c3], c3g = colors[c3 + 1], c3b = colors[c3 + 2];
6426
6427 var minY = Math.round(y1), maxY = Math.round(y3);
6428 var xa, car, cag, cab;
6429 var xb, cbr, cbg, cbb;
6430 var k;
6431 for (var y = minY; y <= maxY; y++) {
6432 if (y < y2) {
6433 k = y < y1 ? 0 : y1 === y2 ? 1 : (y1 - y) / (y1 - y2);
6434 xa = x1 - (x1 - x2) * k;
6435 car = c1r - (c1r - c2r) * k;
6436 cag = c1g - (c1g - c2g) * k;
6437 cab = c1b - (c1b - c2b) * k;
6438 } else {
6439 k = y > y3 ? 1 : y2 === y3 ? 0 : (y2 - y) / (y2 - y3);
6440 xa = x2 - (x2 - x3) * k;
6441 car = c2r - (c2r - c3r) * k;
6442 cag = c2g - (c2g - c3g) * k;
6443 cab = c2b - (c2b - c3b) * k;
6444 }
6445 k = y < y1 ? 0 : y > y3 ? 1 : (y1 - y) / (y1 - y3);
6446 xb = x1 - (x1 - x3) * k;
6447 cbr = c1r - (c1r - c3r) * k;
6448 cbg = c1g - (c1g - c3g) * k;
6449 cbb = c1b - (c1b - c3b) * k;
6450 var x1_ = Math.round(Math.min(xa, xb));
6451 var x2_ = Math.round(Math.max(xa, xb));
6452 var j = rowSize * y + x1_ * 4;
6453 for (var x = x1_; x <= x2_; x++) {
6454 k = (xa - x) / (xa - xb);
6455 k = k < 0 ? 0 : k > 1 ? 1 : k;
6456 bytes[j++] = (car - (car - cbr) * k) | 0;
6457 bytes[j++] = (cag - (cag - cbg) * k) | 0;
6458 bytes[j++] = (cab - (cab - cbb) * k) | 0;
6459 bytes[j++] = 255;
6460 }
6461 }
6462 }
6463
6464 function drawFigure(data, figure, context) {
6465 var ps = figure.coords;
6466 var cs = figure.colors;
6467 var i, ii;
6468 switch (figure.type) {
6469 case 'lattice':
6470 var verticesPerRow = figure.verticesPerRow;
6471 var rows = Math.floor(ps.length / verticesPerRow) - 1;
6472 var cols = verticesPerRow - 1;
6473 for (i = 0; i < rows; i++) {
6474 var q = i * verticesPerRow;
6475 for (var j = 0; j < cols; j++, q++) {
6476 drawTriangle(data, context,
6477 ps[q], ps[q + 1], ps[q + verticesPerRow],
6478 cs[q], cs[q + 1], cs[q + verticesPerRow]);
6479 drawTriangle(data, context,
6480 ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow],
6481 cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
6482 }
6483 }
6484 break;
6485 case 'triangles':
6486 for (i = 0, ii = ps.length; i < ii; i += 3) {
6487 drawTriangle(data, context,
6488 ps[i], ps[i + 1], ps[i + 2],
6489 cs[i], cs[i + 1], cs[i + 2]);
6490 }
6491 break;
6492 default:
6493 error('illigal figure');
6494 break;
6495 }
6496 }
6497
6498 function createMeshCanvas(bounds, combinesScale, coords, colors, figures,
6499 backgroundColor, cachedCanvases) {
6500 // we will increase scale on some weird factor to let antialiasing take
6501 // care of "rough" edges
6502 var EXPECTED_SCALE = 1.1;
6503 // MAX_PATTERN_SIZE is used to avoid OOM situation.
6504 var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
6505 // We need to keep transparent border around our pattern for fill():
6506 // createPattern with 'no-repeat' will bleed edges across entire area.
6507 var BORDER_SIZE = 2;
6508
6509 var offsetX = Math.floor(bounds[0]);
6510 var offsetY = Math.floor(bounds[1]);
6511 var boundsWidth = Math.ceil(bounds[2]) - offsetX;
6512 var boundsHeight = Math.ceil(bounds[3]) - offsetY;
6513
6514 var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] *
6515 EXPECTED_SCALE)), MAX_PATTERN_SIZE);
6516 var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] *
6517 EXPECTED_SCALE)), MAX_PATTERN_SIZE);
6518 var scaleX = boundsWidth / width;
6519 var scaleY = boundsHeight / height;
6520
6521 var context = {
6522 coords: coords,
6523 colors: colors,
6524 offsetX: -offsetX,
6525 offsetY: -offsetY,
6526 scaleX: 1 / scaleX,
6527 scaleY: 1 / scaleY
6528 };
6529
6530 var paddedWidth = width + BORDER_SIZE * 2;
6531 var paddedHeight = height + BORDER_SIZE * 2;
6532
6533 var canvas, tmpCanvas, i, ii;
6534 if (WebGLUtils.isEnabled) {
6535 canvas = WebGLUtils.drawFigures(width, height, backgroundColor,
6536 figures, context);
6537
6538 // https://bugzilla.mozilla.org/show_bug.cgi?id=972126
6539 tmpCanvas = cachedCanvases.getCanvas('mesh', paddedWidth, paddedHeight,
6540 false);
6541 tmpCanvas.context.drawImage(canvas, BORDER_SIZE, BORDER_SIZE);
6542 canvas = tmpCanvas.canvas;
6543 } else {
6544 tmpCanvas = cachedCanvases.getCanvas('mesh', paddedWidth, paddedHeight,
6545 false);
6546 var tmpCtx = tmpCanvas.context;
6547
6548 var data = tmpCtx.createImageData(width, height);
6549 if (backgroundColor) {
6550 var bytes = data.data;
6551 for (i = 0, ii = bytes.length; i < ii; i += 4) {
6552 bytes[i] = backgroundColor[0];
6553 bytes[i + 1] = backgroundColor[1];
6554 bytes[i + 2] = backgroundColor[2];
6555 bytes[i + 3] = 255;
6556 }
6557 }
6558 for (i = 0; i < figures.length; i++) {
6559 drawFigure(data, figures[i], context);
6560 }
6561 tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
6562 canvas = tmpCanvas.canvas;
6563 }
6564
6565 return {canvas: canvas,
6566 offsetX: offsetX - BORDER_SIZE * scaleX,
6567 offsetY: offsetY - BORDER_SIZE * scaleY,
6568 scaleX: scaleX, scaleY: scaleY};
6569 }
6570 return createMeshCanvas;
6571})();
6572
6573ShadingIRs.Mesh = {
6574 fromIR: function Mesh_fromIR(raw) {
6575 //var type = raw[1];
6576 var coords = raw[2];
6577 var colors = raw[3];
6578 var figures = raw[4];
6579 var bounds = raw[5];
6580 var matrix = raw[6];
6581 //var bbox = raw[7];
6582 var background = raw[8];
6583 return {
6584 type: 'Pattern',
6585 getPattern: function Mesh_getPattern(ctx, owner, shadingFill) {
6586 var scale;
6587 if (shadingFill) {
6588 scale = Util.singularValueDecompose2dScale(ctx.mozCurrentTransform);
6589 } else {
6590 // Obtain scale from matrix and current transformation matrix.
6591 scale = Util.singularValueDecompose2dScale(owner.baseTransform);
6592 if (matrix) {
6593 var matrixScale = Util.singularValueDecompose2dScale(matrix);
6594 scale = [scale[0] * matrixScale[0],
6595 scale[1] * matrixScale[1]];
6596 }
6597 }
6598
6599
6600 // Rasterizing on the main thread since sending/queue large canvases
6601 // might cause OOM.
6602 var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords,
6603 colors, figures, shadingFill ? null : background,
6604 owner.cachedCanvases);
6605
6606 if (!shadingFill) {
6607 ctx.setTransform.apply(ctx, owner.baseTransform);
6608 if (matrix) {
6609 ctx.transform.apply(ctx, matrix);
6610 }
6611 }
6612
6613 ctx.translate(temporaryPatternCanvas.offsetX,
6614 temporaryPatternCanvas.offsetY);
6615 ctx.scale(temporaryPatternCanvas.scaleX,
6616 temporaryPatternCanvas.scaleY);
6617
6618 return ctx.createPattern(temporaryPatternCanvas.canvas, 'no-repeat');
6619 }
6620 };
6621 }
6622};
6623
6624ShadingIRs.Dummy = {
6625 fromIR: function Dummy_fromIR() {
6626 return {
6627 type: 'Pattern',
6628 getPattern: function Dummy_fromIR_getPattern() {
6629 return 'hotpink';
6630 }
6631 };
6632 }
6633};
6634
6635function getShadingPatternFromIR(raw) {
6636 var shadingIR = ShadingIRs[raw[0]];
6637 if (!shadingIR) {
6638 error('Unknown IR type: ' + raw[0]);
6639 }
6640 return shadingIR.fromIR(raw);
6641}
6642
6643var TilingPattern = (function TilingPatternClosure() {
6644 var PaintType = {
6645 COLORED: 1,
6646 UNCOLORED: 2
6647 };
6648
6649 var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
6650
6651 function TilingPattern(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
6652 this.operatorList = IR[2];
6653 this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
6654 this.bbox = IR[4];
6655 this.xstep = IR[5];
6656 this.ystep = IR[6];
6657 this.paintType = IR[7];
6658 this.tilingType = IR[8];
6659 this.color = color;
6660 this.canvasGraphicsFactory = canvasGraphicsFactory;
6661 this.baseTransform = baseTransform;
6662 this.type = 'Pattern';
6663 this.ctx = ctx;
6664 }
6665
6666 TilingPattern.prototype = {
6667 createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
6668 var operatorList = this.operatorList;
6669 var bbox = this.bbox;
6670 var xstep = this.xstep;
6671 var ystep = this.ystep;
6672 var paintType = this.paintType;
6673 var tilingType = this.tilingType;
6674 var color = this.color;
6675 var canvasGraphicsFactory = this.canvasGraphicsFactory;
6676
6677 info('TilingType: ' + tilingType);
6678
6679 var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
6680
6681 var topLeft = [x0, y0];
6682 // we want the canvas to be as large as the step size
6683 var botRight = [x0 + xstep, y0 + ystep];
6684
6685 var width = botRight[0] - topLeft[0];
6686 var height = botRight[1] - topLeft[1];
6687
6688 // Obtain scale from matrix and current transformation matrix.
6689 var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
6690 var curMatrixScale = Util.singularValueDecompose2dScale(
6691 this.baseTransform);
6692 var combinedScale = [matrixScale[0] * curMatrixScale[0],
6693 matrixScale[1] * curMatrixScale[1]];
6694
6695 // MAX_PATTERN_SIZE is used to avoid OOM situation.
6696 // Use width and height values that are as close as possible to the end
6697 // result when the pattern is used. Too low value makes the pattern look
6698 // blurry. Too large value makes it look too crispy.
6699 width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])),
6700 MAX_PATTERN_SIZE);
6701
6702 height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
6703 MAX_PATTERN_SIZE);
6704
6705 var tmpCanvas = owner.cachedCanvases.getCanvas('pattern',
6706 width, height, true);
6707 var tmpCtx = tmpCanvas.context;
6708 var graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
6709 graphics.groupLevel = owner.groupLevel;
6710
6711 this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color);
6712
6713 this.setScale(width, height, xstep, ystep);
6714 this.transformToScale(graphics);
6715
6716 // transform coordinates to pattern space
6717 var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
6718 graphics.transform.apply(graphics, tmpTranslate);
6719
6720 this.clipBbox(graphics, bbox, x0, y0, x1, y1);
6721
6722 graphics.executeOperatorList(operatorList);
6723 return tmpCanvas.canvas;
6724 },
6725
6726 setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
6727 this.scale = [width / xstep, height / ystep];
6728 },
6729
6730 transformToScale: function TilingPattern_transformToScale(graphics) {
6731 var scale = this.scale;
6732 var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
6733 graphics.transform.apply(graphics, tmpScale);
6734 },
6735
6736 scaleToContext: function TilingPattern_scaleToContext() {
6737 var scale = this.scale;
6738 this.ctx.scale(1 / scale[0], 1 / scale[1]);
6739 },
6740
6741 clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
6742 if (bbox && isArray(bbox) && bbox.length === 4) {
6743 var bboxWidth = x1 - x0;
6744 var bboxHeight = y1 - y0;
6745 graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
6746 graphics.clip();
6747 graphics.endPath();
6748 }
6749 },
6750
6751 setFillAndStrokeStyleToContext:
6752 function setFillAndStrokeStyleToContext(context, paintType, color) {
6753 switch (paintType) {
6754 case PaintType.COLORED:
6755 var ctx = this.ctx;
6756 context.fillStyle = ctx.fillStyle;
6757 context.strokeStyle = ctx.strokeStyle;
6758 break;
6759 case PaintType.UNCOLORED:
6760 var cssColor = Util.makeCssRgb(color[0], color[1], color[2]);
6761 context.fillStyle = cssColor;
6762 context.strokeStyle = cssColor;
6763 break;
6764 default:
6765 error('Unsupported paint type: ' + paintType);
6766 }
6767 },
6768
6769 getPattern: function TilingPattern_getPattern(ctx, owner) {
6770 var temporaryPatternCanvas = this.createPatternCanvas(owner);
6771
6772 ctx = this.ctx;
6773 ctx.setTransform.apply(ctx, this.baseTransform);
6774 ctx.transform.apply(ctx, this.matrix);
6775 this.scaleToContext();
6776
6777 return ctx.createPattern(temporaryPatternCanvas, 'repeat');
6778 }
6779 };
6780
6781 return TilingPattern;
6782})();
6783
6784exports.getShadingPatternFromIR = getShadingPatternFromIR;
6785exports.TilingPattern = TilingPattern;
6786}));
6787
6788
6789(function (root, factory) {
6790 {
6791 factory((root.pdfjsDisplayCanvas = {}), root.pdfjsSharedUtil,
6792 root.pdfjsDisplayDOMUtils, root.pdfjsDisplayPatternHelper,
6793 root.pdfjsDisplayWebGL);
6794 }
6795}(this, function (exports, sharedUtil, displayDOMUtils, displayPatternHelper,
6796 displayWebGL) {
6797
6798var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
6799var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX;
6800var ImageKind = sharedUtil.ImageKind;
6801var OPS = sharedUtil.OPS;
6802var TextRenderingMode = sharedUtil.TextRenderingMode;
6803var Uint32ArrayView = sharedUtil.Uint32ArrayView;
6804var Util = sharedUtil.Util;
6805var assert = sharedUtil.assert;
6806var info = sharedUtil.info;
6807var isNum = sharedUtil.isNum;
6808var isArray = sharedUtil.isArray;
6809var isLittleEndian = sharedUtil.isLittleEndian;
6810var error = sharedUtil.error;
6811var shadow = sharedUtil.shadow;
6812var warn = sharedUtil.warn;
6813var TilingPattern = displayPatternHelper.TilingPattern;
6814var getShadingPatternFromIR = displayPatternHelper.getShadingPatternFromIR;
6815var WebGLUtils = displayWebGL.WebGLUtils;
6816var hasCanvasTypedArrays = displayDOMUtils.hasCanvasTypedArrays;
6817
6818// <canvas> contexts store most of the state we need natively.
6819// However, PDF needs a bit more state, which we store here.
6820
6821// Minimal font size that would be used during canvas fillText operations.
6822var MIN_FONT_SIZE = 16;
6823// Maximum font size that would be used during canvas fillText operations.
6824var MAX_FONT_SIZE = 100;
6825var MAX_GROUP_SIZE = 4096;
6826
6827// Heuristic value used when enforcing minimum line widths.
6828var MIN_WIDTH_FACTOR = 0.65;
6829
6830var COMPILE_TYPE3_GLYPHS = true;
6831var MAX_SIZE_TO_COMPILE = 1000;
6832
6833var FULL_CHUNK_HEIGHT = 16;
6834
6835var HasCanvasTypedArraysCached = {
6836 get value() {
6837 return shadow(HasCanvasTypedArraysCached, 'value', hasCanvasTypedArrays());
6838 }
6839};
6840
6841var IsLittleEndianCached = {
6842 get value() {
6843 return shadow(IsLittleEndianCached, 'value', isLittleEndian());
6844 }
6845};
6846
6847function createScratchCanvas(width, height) {
6848 var canvas = document.createElement('canvas');
6849 canvas.width = width;
6850 canvas.height = height;
6851 return canvas;
6852}
6853
6854function addContextCurrentTransform(ctx) {
6855 // If the context doesn't expose a `mozCurrentTransform`, add a JS based one.
6856 if (!ctx.mozCurrentTransform) {
6857 ctx._originalSave = ctx.save;
6858 ctx._originalRestore = ctx.restore;
6859 ctx._originalRotate = ctx.rotate;
6860 ctx._originalScale = ctx.scale;
6861 ctx._originalTranslate = ctx.translate;
6862 ctx._originalTransform = ctx.transform;
6863 ctx._originalSetTransform = ctx.setTransform;
6864
6865 ctx._transformMatrix = ctx._transformMatrix || [1, 0, 0, 1, 0, 0];
6866 ctx._transformStack = [];
6867
6868 Object.defineProperty(ctx, 'mozCurrentTransform', {
6869 get: function getCurrentTransform() {
6870 return this._transformMatrix;
6871 }
6872 });
6873
6874 Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
6875 get: function getCurrentTransformInverse() {
6876 // Calculation done using WolframAlpha:
6877 // http://www.wolframalpha.com/input/?
6878 // i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
6879
6880 var m = this._transformMatrix;
6881 var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
6882
6883 var ad_bc = a * d - b * c;
6884 var bc_ad = b * c - a * d;
6885
6886 return [
6887 d / ad_bc,
6888 b / bc_ad,
6889 c / bc_ad,
6890 a / ad_bc,
6891 (d * e - c * f) / bc_ad,
6892 (b * e - a * f) / ad_bc
6893 ];
6894 }
6895 });
6896
6897 ctx.save = function ctxSave() {
6898 var old = this._transformMatrix;
6899 this._transformStack.push(old);
6900 this._transformMatrix = old.slice(0, 6);
6901
6902 this._originalSave();
6903 };
6904
6905 ctx.restore = function ctxRestore() {
6906 var prev = this._transformStack.pop();
6907 if (prev) {
6908 this._transformMatrix = prev;
6909 this._originalRestore();
6910 }
6911 };
6912
6913 ctx.translate = function ctxTranslate(x, y) {
6914 var m = this._transformMatrix;
6915 m[4] = m[0] * x + m[2] * y + m[4];
6916 m[5] = m[1] * x + m[3] * y + m[5];
6917
6918 this._originalTranslate(x, y);
6919 };
6920
6921 ctx.scale = function ctxScale(x, y) {
6922 var m = this._transformMatrix;
6923 m[0] = m[0] * x;
6924 m[1] = m[1] * x;
6925 m[2] = m[2] * y;
6926 m[3] = m[3] * y;
6927
6928 this._originalScale(x, y);
6929 };
6930
6931 ctx.transform = function ctxTransform(a, b, c, d, e, f) {
6932 var m = this._transformMatrix;
6933 this._transformMatrix = [
6934 m[0] * a + m[2] * b,
6935 m[1] * a + m[3] * b,
6936 m[0] * c + m[2] * d,
6937 m[1] * c + m[3] * d,
6938 m[0] * e + m[2] * f + m[4],
6939 m[1] * e + m[3] * f + m[5]
6940 ];
6941
6942 ctx._originalTransform(a, b, c, d, e, f);
6943 };
6944
6945 ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
6946 this._transformMatrix = [a, b, c, d, e, f];
6947
6948 ctx._originalSetTransform(a, b, c, d, e, f);
6949 };
6950
6951 ctx.rotate = function ctxRotate(angle) {
6952 var cosValue = Math.cos(angle);
6953 var sinValue = Math.sin(angle);
6954
6955 var m = this._transformMatrix;
6956 this._transformMatrix = [
6957 m[0] * cosValue + m[2] * sinValue,
6958 m[1] * cosValue + m[3] * sinValue,
6959 m[0] * (-sinValue) + m[2] * cosValue,
6960 m[1] * (-sinValue) + m[3] * cosValue,
6961 m[4],
6962 m[5]
6963 ];
6964
6965 this._originalRotate(angle);
6966 };
6967 }
6968}
6969
6970var CachedCanvases = (function CachedCanvasesClosure() {
6971 function CachedCanvases() {
6972 this.cache = Object.create(null);
6973 }
6974 CachedCanvases.prototype = {
6975 getCanvas: function CachedCanvases_getCanvas(id, width, height,
6976 trackTransform) {
6977 var canvasEntry;
6978 if (this.cache[id] !== undefined) {
6979 canvasEntry = this.cache[id];
6980 canvasEntry.canvas.width = width;
6981 canvasEntry.canvas.height = height;
6982 // reset canvas transform for emulated mozCurrentTransform, if needed
6983 canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0);
6984 } else {
6985 var canvas = createScratchCanvas(width, height);
6986 var ctx = canvas.getContext('2d');
6987 if (trackTransform) {
6988 addContextCurrentTransform(ctx);
6989 }
6990 this.cache[id] = canvasEntry = {canvas: canvas, context: ctx};
6991 }
6992 return canvasEntry;
6993 },
6994 clear: function () {
6995 for (var id in this.cache) {
6996 var canvasEntry = this.cache[id];
6997 // Zeroing the width and height causes Firefox to release graphics
6998 // resources immediately, which can greatly reduce memory consumption.
6999 canvasEntry.canvas.width = 0;
7000 canvasEntry.canvas.height = 0;
7001 delete this.cache[id];
7002 }
7003 }
7004 };
7005 return CachedCanvases;
7006})();
7007
7008function compileType3Glyph(imgData) {
7009 var POINT_TO_PROCESS_LIMIT = 1000;
7010
7011 var width = imgData.width, height = imgData.height;
7012 var i, j, j0, width1 = width + 1;
7013 var points = new Uint8Array(width1 * (height + 1));
7014 var POINT_TYPES =
7015 new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
7016
7017 // decodes bit-packed mask data
7018 var lineSize = (width + 7) & ~7, data0 = imgData.data;
7019 var data = new Uint8Array(lineSize * height), pos = 0, ii;
7020 for (i = 0, ii = data0.length; i < ii; i++) {
7021 var mask = 128, elem = data0[i];
7022 while (mask > 0) {
7023 data[pos++] = (elem & mask) ? 0 : 255;
7024 mask >>= 1;
7025 }
7026 }
7027
7028 // finding iteresting points: every point is located between mask pixels,
7029 // so there will be points of the (width + 1)x(height + 1) grid. Every point
7030 // will have flags assigned based on neighboring mask pixels:
7031 // 4 | 8
7032 // --P--
7033 // 2 | 1
7034 // We are interested only in points with the flags:
7035 // - outside corners: 1, 2, 4, 8;
7036 // - inside corners: 7, 11, 13, 14;
7037 // - and, intersections: 5, 10.
7038 var count = 0;
7039 pos = 0;
7040 if (data[pos] !== 0) {
7041 points[0] = 1;
7042 ++count;
7043 }
7044 for (j = 1; j < width; j++) {
7045 if (data[pos] !== data[pos + 1]) {
7046 points[j] = data[pos] ? 2 : 1;
7047 ++count;
7048 }
7049 pos++;
7050 }
7051 if (data[pos] !== 0) {
7052 points[j] = 2;
7053 ++count;
7054 }
7055 for (i = 1; i < height; i++) {
7056 pos = i * lineSize;
7057 j0 = i * width1;
7058 if (data[pos - lineSize] !== data[pos]) {
7059 points[j0] = data[pos] ? 1 : 8;
7060 ++count;
7061 }
7062 // 'sum' is the position of the current pixel configuration in the 'TYPES'
7063 // array (in order 8-1-2-4, so we can use '>>2' to shift the column).
7064 var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
7065 for (j = 1; j < width; j++) {
7066 sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) +
7067 (data[pos - lineSize + 1] ? 8 : 0);
7068 if (POINT_TYPES[sum]) {
7069 points[j0 + j] = POINT_TYPES[sum];
7070 ++count;
7071 }
7072 pos++;
7073 }
7074 if (data[pos - lineSize] !== data[pos]) {
7075 points[j0 + j] = data[pos] ? 2 : 4;
7076 ++count;
7077 }
7078
7079 if (count > POINT_TO_PROCESS_LIMIT) {
7080 return null;
7081 }
7082 }
7083
7084 pos = lineSize * (height - 1);
7085 j0 = i * width1;
7086 if (data[pos] !== 0) {
7087 points[j0] = 8;
7088 ++count;
7089 }
7090 for (j = 1; j < width; j++) {
7091 if (data[pos] !== data[pos + 1]) {
7092 points[j0 + j] = data[pos] ? 4 : 8;
7093 ++count;
7094 }
7095 pos++;
7096 }
7097 if (data[pos] !== 0) {
7098 points[j0 + j] = 4;
7099 ++count;
7100 }
7101 if (count > POINT_TO_PROCESS_LIMIT) {
7102 return null;
7103 }
7104
7105 // building outlines
7106 var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
7107 var outlines = [];
7108 for (i = 0; count && i <= height; i++) {
7109 var p = i * width1;
7110 var end = p + width;
7111 while (p < end && !points[p]) {
7112 p++;
7113 }
7114 if (p === end) {
7115 continue;
7116 }
7117 var coords = [p % width1, i];
7118
7119 var type = points[p], p0 = p, pp;
7120 do {
7121 var step = steps[type];
7122 do {
7123 p += step;
7124 } while (!points[p]);
7125
7126 pp = points[p];
7127 if (pp !== 5 && pp !== 10) {
7128 // set new direction
7129 type = pp;
7130 // delete mark
7131 points[p] = 0;
7132 } else { // type is 5 or 10, ie, a crossing
7133 // set new direction
7134 type = pp & ((0x33 * type) >> 4);
7135 // set new type for "future hit"
7136 points[p] &= (type >> 2 | type << 2);
7137 }
7138
7139 coords.push(p % width1);
7140 coords.push((p / width1) | 0);
7141 --count;
7142 } while (p0 !== p);
7143 outlines.push(coords);
7144 --i;
7145 }
7146
7147 var drawOutline = function(c) {
7148 c.save();
7149 // the path shall be painted in [0..1]x[0..1] space
7150 c.scale(1 / width, -1 / height);
7151 c.translate(0, -height);
7152 c.beginPath();
7153 for (var i = 0, ii = outlines.length; i < ii; i++) {
7154 var o = outlines[i];
7155 c.moveTo(o[0], o[1]);
7156 for (var j = 2, jj = o.length; j < jj; j += 2) {
7157 c.lineTo(o[j], o[j+1]);
7158 }
7159 }
7160 c.fill();
7161 c.beginPath();
7162 c.restore();
7163 };
7164
7165 return drawOutline;
7166}
7167
7168var CanvasExtraState = (function CanvasExtraStateClosure() {
7169 function CanvasExtraState(old) {
7170 // Are soft masks and alpha values shapes or opacities?
7171 this.alphaIsShape = false;
7172 this.fontSize = 0;
7173 this.fontSizeScale = 1;
7174 this.textMatrix = IDENTITY_MATRIX;
7175 this.textMatrixScale = 1;
7176 this.fontMatrix = FONT_IDENTITY_MATRIX;
7177 this.leading = 0;
7178 // Current point (in user coordinates)
7179 this.x = 0;
7180 this.y = 0;
7181 // Start of text line (in text coordinates)
7182 this.lineX = 0;
7183 this.lineY = 0;
7184 // Character and word spacing
7185 this.charSpacing = 0;
7186 this.wordSpacing = 0;
7187 this.textHScale = 1;
7188 this.textRenderingMode = TextRenderingMode.FILL;
7189 this.textRise = 0;
7190 // Default fore and background colors
7191 this.fillColor = '#000000';
7192 this.strokeColor = '#000000';
7193 this.patternFill = false;
7194 // Note: fill alpha applies to all non-stroking operations
7195 this.fillAlpha = 1;
7196 this.strokeAlpha = 1;
7197 this.lineWidth = 1;
7198 this.activeSMask = null;
7199 this.resumeSMaskCtx = null; // nonclonable field (see the save method below)
7200
7201 this.old = old;
7202 }
7203
7204 CanvasExtraState.prototype = {
7205 clone: function CanvasExtraState_clone() {
7206 return Object.create(this);
7207 },
7208 setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) {
7209 this.x = x;
7210 this.y = y;
7211 }
7212 };
7213 return CanvasExtraState;
7214})();
7215
7216var CanvasGraphics = (function CanvasGraphicsClosure() {
7217 // Defines the time the executeOperatorList is going to be executing
7218 // before it stops and shedules a continue of execution.
7219 var EXECUTION_TIME = 15;
7220 // Defines the number of steps before checking the execution time
7221 var EXECUTION_STEPS = 10;
7222
7223 function CanvasGraphics(canvasCtx, commonObjs, objs, imageLayer) {
7224 this.ctx = canvasCtx;
7225 this.current = new CanvasExtraState();
7226 this.stateStack = [];
7227 this.pendingClip = null;
7228 this.pendingEOFill = false;
7229 this.res = null;
7230 this.xobjs = null;
7231 this.commonObjs = commonObjs;
7232 this.objs = objs;
7233 this.imageLayer = imageLayer;
7234 this.groupStack = [];
7235 this.processingType3 = null;
7236 // Patterns are painted relative to the initial page/form transform, see pdf
7237 // spec 8.7.2 NOTE 1.
7238 this.baseTransform = null;
7239 this.baseTransformStack = [];
7240 this.groupLevel = 0;
7241 this.smaskStack = [];
7242 this.smaskCounter = 0;
7243 this.tempSMask = null;
7244 this.cachedCanvases = new CachedCanvases();
7245 if (canvasCtx) {
7246 // NOTE: if mozCurrentTransform is polyfilled, then the current state of
7247 // the transformation must already be set in canvasCtx._transformMatrix.
7248 addContextCurrentTransform(canvasCtx);
7249 }
7250 this.cachedGetSinglePixelWidth = null;
7251 }
7252
7253 function putBinaryImageData(ctx, imgData) {
7254 if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
7255 ctx.putImageData(imgData, 0, 0);
7256 return;
7257 }
7258
7259 // Put the image data to the canvas in chunks, rather than putting the
7260 // whole image at once. This saves JS memory, because the ImageData object
7261 // is smaller. It also possibly saves C++ memory within the implementation
7262 // of putImageData(). (E.g. in Firefox we make two short-lived copies of
7263 // the data passed to putImageData()). |n| shouldn't be too small, however,
7264 // because too many putImageData() calls will slow things down.
7265 //
7266 // Note: as written, if the last chunk is partial, the putImageData() call
7267 // will (conceptually) put pixels past the bounds of the canvas. But
7268 // that's ok; any such pixels are ignored.
7269
7270 var height = imgData.height, width = imgData.width;
7271 var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
7272 var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
7273 var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
7274
7275 var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
7276 var srcPos = 0, destPos;
7277 var src = imgData.data;
7278 var dest = chunkImgData.data;
7279 var i, j, thisChunkHeight, elemsInThisChunk;
7280
7281 // There are multiple forms in which the pixel data can be passed, and
7282 // imgData.kind tells us which one this is.
7283 if (imgData.kind === ImageKind.GRAYSCALE_1BPP) {
7284 // Grayscale, 1 bit per pixel (i.e. black-and-white).
7285 var srcLength = src.byteLength;
7286 var dest32 = HasCanvasTypedArraysCached.value ?
7287 new Uint32Array(dest.buffer) : new Uint32ArrayView(dest);
7288 var dest32DataLength = dest32.length;
7289 var fullSrcDiff = (width + 7) >> 3;
7290 var white = 0xFFFFFFFF;
7291 var black = (IsLittleEndianCached.value ||
7292 !HasCanvasTypedArraysCached.value) ? 0xFF000000 : 0x000000FF;
7293 for (i = 0; i < totalChunks; i++) {
7294 thisChunkHeight =
7295 (i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight;
7296 destPos = 0;
7297 for (j = 0; j < thisChunkHeight; j++) {
7298 var srcDiff = srcLength - srcPos;
7299 var k = 0;
7300 var kEnd = (srcDiff > fullSrcDiff) ? width : srcDiff * 8 - 7;
7301 var kEndUnrolled = kEnd & ~7;
7302 var mask = 0;
7303 var srcByte = 0;
7304 for (; k < kEndUnrolled; k += 8) {
7305 srcByte = src[srcPos++];
7306 dest32[destPos++] = (srcByte & 128) ? white : black;
7307 dest32[destPos++] = (srcByte & 64) ? white : black;
7308 dest32[destPos++] = (srcByte & 32) ? white : black;
7309 dest32[destPos++] = (srcByte & 16) ? white : black;
7310 dest32[destPos++] = (srcByte & 8) ? white : black;
7311 dest32[destPos++] = (srcByte & 4) ? white : black;
7312 dest32[destPos++] = (srcByte & 2) ? white : black;
7313 dest32[destPos++] = (srcByte & 1) ? white : black;
7314 }
7315 for (; k < kEnd; k++) {
7316 if (mask === 0) {
7317 srcByte = src[srcPos++];
7318 mask = 128;
7319 }
7320
7321 dest32[destPos++] = (srcByte & mask) ? white : black;
7322 mask >>= 1;
7323 }
7324 }
7325 // We ran out of input. Make all remaining pixels transparent.
7326 while (destPos < dest32DataLength) {
7327 dest32[destPos++] = 0;
7328 }
7329
7330 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
7331 }
7332 } else if (imgData.kind === ImageKind.RGBA_32BPP) {
7333 // RGBA, 32-bits per pixel.
7334
7335 j = 0;
7336 elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
7337 for (i = 0; i < fullChunks; i++) {
7338 dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
7339 srcPos += elemsInThisChunk;
7340
7341 ctx.putImageData(chunkImgData, 0, j);
7342 j += FULL_CHUNK_HEIGHT;
7343 }
7344 if (i < totalChunks) {
7345 elemsInThisChunk = width * partialChunkHeight * 4;
7346 dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
7347 ctx.putImageData(chunkImgData, 0, j);
7348 }
7349
7350 } else if (imgData.kind === ImageKind.RGB_24BPP) {
7351 // RGB, 24-bits per pixel.
7352 thisChunkHeight = FULL_CHUNK_HEIGHT;
7353 elemsInThisChunk = width * thisChunkHeight;
7354 for (i = 0; i < totalChunks; i++) {
7355 if (i >= fullChunks) {
7356 thisChunkHeight = partialChunkHeight;
7357 elemsInThisChunk = width * thisChunkHeight;
7358 }
7359
7360 destPos = 0;
7361 for (j = elemsInThisChunk; j--;) {
7362 dest[destPos++] = src[srcPos++];
7363 dest[destPos++] = src[srcPos++];
7364 dest[destPos++] = src[srcPos++];
7365 dest[destPos++] = 255;
7366 }
7367 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
7368 }
7369 } else {
7370 error('bad image kind: ' + imgData.kind);
7371 }
7372 }
7373
7374 function putBinaryImageMask(ctx, imgData) {
7375 var height = imgData.height, width = imgData.width;
7376 var partialChunkHeight = height % FULL_CHUNK_HEIGHT;
7377 var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
7378 var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
7379
7380 var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
7381 var srcPos = 0;
7382 var src = imgData.data;
7383 var dest = chunkImgData.data;
7384
7385 for (var i = 0; i < totalChunks; i++) {
7386 var thisChunkHeight =
7387 (i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight;
7388
7389 // Expand the mask so it can be used by the canvas. Any required
7390 // inversion has already been handled.
7391 var destPos = 3; // alpha component offset
7392 for (var j = 0; j < thisChunkHeight; j++) {
7393 var mask = 0;
7394 for (var k = 0; k < width; k++) {
7395 if (!mask) {
7396 var elem = src[srcPos++];
7397 mask = 128;
7398 }
7399 dest[destPos] = (elem & mask) ? 0 : 255;
7400 destPos += 4;
7401 mask >>= 1;
7402 }
7403 }
7404 ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
7405 }
7406 }
7407
7408 function copyCtxState(sourceCtx, destCtx) {
7409 var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha',
7410 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
7411 'globalCompositeOperation', 'font'];
7412 for (var i = 0, ii = properties.length; i < ii; i++) {
7413 var property = properties[i];
7414 if (sourceCtx[property] !== undefined) {
7415 destCtx[property] = sourceCtx[property];
7416 }
7417 }
7418 if (sourceCtx.setLineDash !== undefined) {
7419 destCtx.setLineDash(sourceCtx.getLineDash());
7420 destCtx.lineDashOffset = sourceCtx.lineDashOffset;
7421 }
7422 }
7423
7424 function composeSMaskBackdrop(bytes, r0, g0, b0) {
7425 var length = bytes.length;
7426 for (var i = 3; i < length; i += 4) {
7427 var alpha = bytes[i];
7428 if (alpha === 0) {
7429 bytes[i - 3] = r0;
7430 bytes[i - 2] = g0;
7431 bytes[i - 1] = b0;
7432 } else if (alpha < 255) {
7433 var alpha_ = 255 - alpha;
7434 bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) >> 8;
7435 bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) >> 8;
7436 bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) >> 8;
7437 }
7438 }
7439 }
7440
7441 function composeSMaskAlpha(maskData, layerData, transferMap) {
7442 var length = maskData.length;
7443 var scale = 1 / 255;
7444 for (var i = 3; i < length; i += 4) {
7445 var alpha = transferMap ? transferMap[maskData[i]] : maskData[i];
7446 layerData[i] = (layerData[i] * alpha * scale) | 0;
7447 }
7448 }
7449
7450 function composeSMaskLuminosity(maskData, layerData, transferMap) {
7451 var length = maskData.length;
7452 for (var i = 3; i < length; i += 4) {
7453 var y = (maskData[i - 3] * 77) + // * 0.3 / 255 * 0x10000
7454 (maskData[i - 2] * 152) + // * 0.59 ....
7455 (maskData[i - 1] * 28); // * 0.11 ....
7456 layerData[i] = transferMap ?
7457 (layerData[i] * transferMap[y >> 8]) >> 8 :
7458 (layerData[i] * y) >> 16;
7459 }
7460 }
7461
7462 function genericComposeSMask(maskCtx, layerCtx, width, height,
7463 subtype, backdrop, transferMap) {
7464 var hasBackdrop = !!backdrop;
7465 var r0 = hasBackdrop ? backdrop[0] : 0;
7466 var g0 = hasBackdrop ? backdrop[1] : 0;
7467 var b0 = hasBackdrop ? backdrop[2] : 0;
7468
7469 var composeFn;
7470 if (subtype === 'Luminosity') {
7471 composeFn = composeSMaskLuminosity;
7472 } else {
7473 composeFn = composeSMaskAlpha;
7474 }
7475
7476 // processing image in chunks to save memory
7477 var PIXELS_TO_PROCESS = 1048576;
7478 var chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));
7479 for (var row = 0; row < height; row += chunkSize) {
7480 var chunkHeight = Math.min(chunkSize, height - row);
7481 var maskData = maskCtx.getImageData(0, row, width, chunkHeight);
7482 var layerData = layerCtx.getImageData(0, row, width, chunkHeight);
7483
7484 if (hasBackdrop) {
7485 composeSMaskBackdrop(maskData.data, r0, g0, b0);
7486 }
7487 composeFn(maskData.data, layerData.data, transferMap);
7488
7489 maskCtx.putImageData(layerData, 0, row);
7490 }
7491 }
7492
7493 function composeSMask(ctx, smask, layerCtx) {
7494 var mask = smask.canvas;
7495 var maskCtx = smask.context;
7496
7497 ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY,
7498 smask.offsetX, smask.offsetY);
7499
7500 var backdrop = smask.backdrop || null;
7501 if (!smask.transferMap && WebGLUtils.isEnabled) {
7502 var composed = WebGLUtils.composeSMask(layerCtx.canvas, mask,
7503 {subtype: smask.subtype, backdrop: backdrop});
7504 ctx.setTransform(1, 0, 0, 1, 0, 0);
7505 ctx.drawImage(composed, smask.offsetX, smask.offsetY);
7506 return;
7507 }
7508 genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height,
7509 smask.subtype, backdrop, smask.transferMap);
7510 ctx.drawImage(mask, 0, 0);
7511 }
7512
7513 var LINE_CAP_STYLES = ['butt', 'round', 'square'];
7514 var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
7515 var NORMAL_CLIP = {};
7516 var EO_CLIP = {};
7517
7518 CanvasGraphics.prototype = {
7519
7520 beginDrawing: function CanvasGraphics_beginDrawing(transform, viewport,
7521 transparency) {
7522 // For pdfs that use blend modes we have to clear the canvas else certain
7523 // blend modes can look wrong since we'd be blending with a white
7524 // backdrop. The problem with a transparent backdrop though is we then
7525 // don't get sub pixel anti aliasing on text, creating temporary
7526 // transparent canvas when we have blend modes.
7527 var width = this.ctx.canvas.width;
7528 var height = this.ctx.canvas.height;
7529
7530 this.ctx.save();
7531 this.ctx.fillStyle = 'rgb(255, 255, 255)';
7532 this.ctx.fillRect(0, 0, width, height);
7533 this.ctx.restore();
7534
7535 if (transparency) {
7536 var transparentCanvas = this.cachedCanvases.getCanvas(
7537 'transparent', width, height, true);
7538 this.compositeCtx = this.ctx;
7539 this.transparentCanvas = transparentCanvas.canvas;
7540 this.ctx = transparentCanvas.context;
7541 this.ctx.save();
7542 // The transform can be applied before rendering, transferring it to
7543 // the new canvas.
7544 this.ctx.transform.apply(this.ctx,
7545 this.compositeCtx.mozCurrentTransform);
7546 }
7547
7548 this.ctx.save();
7549 if (transform) {
7550 this.ctx.transform.apply(this.ctx, transform);
7551 }
7552 this.ctx.transform.apply(this.ctx, viewport.transform);
7553
7554 this.baseTransform = this.ctx.mozCurrentTransform.slice();
7555
7556 if (this.imageLayer) {
7557 this.imageLayer.beginLayout();
7558 }
7559 },
7560
7561 executeOperatorList: function CanvasGraphics_executeOperatorList(
7562 operatorList,
7563 executionStartIdx, continueCallback,
7564 stepper) {
7565 var argsArray = operatorList.argsArray;
7566 var fnArray = operatorList.fnArray;
7567 var i = executionStartIdx || 0;
7568 var argsArrayLen = argsArray.length;
7569
7570 // Sometimes the OperatorList to execute is empty.
7571 if (argsArrayLen === i) {
7572 return i;
7573 }
7574
7575 var chunkOperations = (argsArrayLen - i > EXECUTION_STEPS &&
7576 typeof continueCallback === 'function');
7577 var endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
7578 var steps = 0;
7579
7580 var commonObjs = this.commonObjs;
7581 var objs = this.objs;
7582 var fnId;
7583
7584 while (true) {
7585 if (stepper !== undefined && i === stepper.nextBreakPoint) {
7586 stepper.breakIt(i, continueCallback);
7587 return i;
7588 }
7589
7590 fnId = fnArray[i];
7591
7592 if (fnId !== OPS.dependency) {
7593 this[fnId].apply(this, argsArray[i]);
7594 } else {
7595 var deps = argsArray[i];
7596 for (var n = 0, nn = deps.length; n < nn; n++) {
7597 var depObjId = deps[n];
7598 var common = depObjId[0] === 'g' && depObjId[1] === '_';
7599 var objsPool = common ? commonObjs : objs;
7600
7601 // If the promise isn't resolved yet, add the continueCallback
7602 // to the promise and bail out.
7603 if (!objsPool.isResolved(depObjId)) {
7604 objsPool.get(depObjId, continueCallback);
7605 return i;
7606 }
7607 }
7608 }
7609
7610 i++;
7611
7612 // If the entire operatorList was executed, stop as were done.
7613 if (i === argsArrayLen) {
7614 return i;
7615 }
7616
7617 // If the execution took longer then a certain amount of time and
7618 // `continueCallback` is specified, interrupt the execution.
7619 if (chunkOperations && ++steps > EXECUTION_STEPS) {
7620 if (Date.now() > endTime) {
7621 continueCallback();
7622 return i;
7623 }
7624 steps = 0;
7625 }
7626
7627 // If the operatorList isn't executed completely yet OR the execution
7628 // time was short enough, do another execution round.
7629 }
7630 },
7631
7632 endDrawing: function CanvasGraphics_endDrawing() {
7633 // Finishing all opened operations such as SMask group painting.
7634 if (this.current.activeSMask !== null) {
7635 this.endSMaskGroup();
7636 }
7637
7638 this.ctx.restore();
7639
7640 if (this.transparentCanvas) {
7641 this.ctx = this.compositeCtx;
7642 this.ctx.save();
7643 this.ctx.setTransform(1, 0, 0, 1, 0, 0); // Avoid apply transform twice
7644 this.ctx.drawImage(this.transparentCanvas, 0, 0);
7645 this.ctx.restore();
7646 this.transparentCanvas = null;
7647 }
7648
7649 this.cachedCanvases.clear();
7650 WebGLUtils.clear();
7651
7652 if (this.imageLayer) {
7653 this.imageLayer.endLayout();
7654 }
7655 },
7656
7657 // Graphics state
7658 setLineWidth: function CanvasGraphics_setLineWidth(width) {
7659 this.current.lineWidth = width;
7660 this.ctx.lineWidth = width;
7661 },
7662 setLineCap: function CanvasGraphics_setLineCap(style) {
7663 this.ctx.lineCap = LINE_CAP_STYLES[style];
7664 },
7665 setLineJoin: function CanvasGraphics_setLineJoin(style) {
7666 this.ctx.lineJoin = LINE_JOIN_STYLES[style];
7667 },
7668 setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
7669 this.ctx.miterLimit = limit;
7670 },
7671 setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
7672 var ctx = this.ctx;
7673 if (ctx.setLineDash !== undefined) {
7674 ctx.setLineDash(dashArray);
7675 ctx.lineDashOffset = dashPhase;
7676 }
7677 },
7678 setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
7679 // Maybe if we one day fully support color spaces this will be important
7680 // for now we can ignore.
7681 // TODO set rendering intent?
7682 },
7683 setFlatness: function CanvasGraphics_setFlatness(flatness) {
7684 // There's no way to control this with canvas, but we can safely ignore.
7685 // TODO set flatness?
7686 },
7687 setGState: function CanvasGraphics_setGState(states) {
7688 for (var i = 0, ii = states.length; i < ii; i++) {
7689 var state = states[i];
7690 var key = state[0];
7691 var value = state[1];
7692
7693 switch (key) {
7694 case 'LW':
7695 this.setLineWidth(value);
7696 break;
7697 case 'LC':
7698 this.setLineCap(value);
7699 break;
7700 case 'LJ':
7701 this.setLineJoin(value);
7702 break;
7703 case 'ML':
7704 this.setMiterLimit(value);
7705 break;
7706 case 'D':
7707 this.setDash(value[0], value[1]);
7708 break;
7709 case 'RI':
7710 this.setRenderingIntent(value);
7711 break;
7712 case 'FL':
7713 this.setFlatness(value);
7714 break;
7715 case 'Font':
7716 this.setFont(value[0], value[1]);
7717 break;
7718 case 'CA':
7719 this.current.strokeAlpha = state[1];
7720 break;
7721 case 'ca':
7722 this.current.fillAlpha = state[1];
7723 this.ctx.globalAlpha = state[1];
7724 break;
7725 case 'BM':
7726 if (value && value.name && (value.name !== 'Normal')) {
7727 var mode = value.name.replace(/([A-Z])/g,
7728 function(c) {
7729 return '-' + c.toLowerCase();
7730 }
7731 ).substring(1);
7732 this.ctx.globalCompositeOperation = mode;
7733 if (this.ctx.globalCompositeOperation !== mode) {
7734 warn('globalCompositeOperation "' + mode +
7735 '" is not supported');
7736 }
7737 } else {
7738 this.ctx.globalCompositeOperation = 'source-over';
7739 }
7740 break;
7741 case 'SMask':
7742 if (this.current.activeSMask) {
7743 // If SMask is currrenly used, it needs to be suspended or
7744 // finished. Suspend only makes sense when at least one save()
7745 // was performed and state needs to be reverted on restore().
7746 if (this.stateStack.length > 0 &&
7747 (this.stateStack[this.stateStack.length - 1].activeSMask ===
7748 this.current.activeSMask)) {
7749 this.suspendSMaskGroup();
7750 } else {
7751 this.endSMaskGroup();
7752 }
7753 }
7754 this.current.activeSMask = value ? this.tempSMask : null;
7755 if (this.current.activeSMask) {
7756 this.beginSMaskGroup();
7757 }
7758 this.tempSMask = null;
7759 break;
7760 }
7761 }
7762 },
7763 beginSMaskGroup: function CanvasGraphics_beginSMaskGroup() {
7764
7765 var activeSMask = this.current.activeSMask;
7766 var drawnWidth = activeSMask.canvas.width;
7767 var drawnHeight = activeSMask.canvas.height;
7768 var cacheId = 'smaskGroupAt' + this.groupLevel;
7769 var scratchCanvas = this.cachedCanvases.getCanvas(
7770 cacheId, drawnWidth, drawnHeight, true);
7771
7772 var currentCtx = this.ctx;
7773 var currentTransform = currentCtx.mozCurrentTransform;
7774 this.ctx.save();
7775
7776 var groupCtx = scratchCanvas.context;
7777 groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY);
7778 groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY);
7779 groupCtx.transform.apply(groupCtx, currentTransform);
7780
7781 activeSMask.startTransformInverse = groupCtx.mozCurrentTransformInverse;
7782
7783 copyCtxState(currentCtx, groupCtx);
7784 this.ctx = groupCtx;
7785 this.setGState([
7786 ['BM', 'Normal'],
7787 ['ca', 1],
7788 ['CA', 1]
7789 ]);
7790 this.groupStack.push(currentCtx);
7791 this.groupLevel++;
7792 },
7793 suspendSMaskGroup: function CanvasGraphics_endSMaskGroup() {
7794 // Similar to endSMaskGroup, the intermediate canvas has to be composed
7795 // and future ctx state restored.
7796 var groupCtx = this.ctx;
7797 this.groupLevel--;
7798 this.ctx = this.groupStack.pop();
7799
7800 composeSMask(this.ctx, this.current.activeSMask, groupCtx);
7801 this.ctx.restore();
7802 this.ctx.save(); // save is needed since SMask will be resumed.
7803 copyCtxState(groupCtx, this.ctx);
7804
7805 // Saving state for resuming.
7806 this.current.resumeSMaskCtx = groupCtx;
7807 // Transform was changed in the SMask canvas, reflecting this change on
7808 // this.ctx.
7809 var deltaTransform = Util.transform(
7810 this.current.activeSMask.startTransformInverse,
7811 groupCtx.mozCurrentTransform);
7812 this.ctx.transform.apply(this.ctx, deltaTransform);
7813
7814 // SMask was composed, the results at the groupCtx can be cleared.
7815 groupCtx.save();
7816 groupCtx.setTransform(1, 0, 0, 1, 0, 0);
7817 groupCtx.clearRect(0, 0, groupCtx.canvas.width, groupCtx.canvas.height);
7818 groupCtx.restore();
7819 },
7820 resumeSMaskGroup: function CanvasGraphics_endSMaskGroup() {
7821 // Resuming state saved by suspendSMaskGroup. We don't need to restore
7822 // any groupCtx state since restore() command (the only caller) will do
7823 // that for us. See also beginSMaskGroup.
7824 var groupCtx = this.current.resumeSMaskCtx;
7825 var currentCtx = this.ctx;
7826 this.ctx = groupCtx;
7827 this.groupStack.push(currentCtx);
7828 this.groupLevel++;
7829 },
7830 endSMaskGroup: function CanvasGraphics_endSMaskGroup() {
7831 var groupCtx = this.ctx;
7832 this.groupLevel--;
7833 this.ctx = this.groupStack.pop();
7834
7835 composeSMask(this.ctx, this.current.activeSMask, groupCtx);
7836 this.ctx.restore();
7837 copyCtxState(groupCtx, this.ctx);
7838 // Transform was changed in the SMask canvas, reflecting this change on
7839 // this.ctx.
7840 var deltaTransform = Util.transform(
7841 this.current.activeSMask.startTransformInverse,
7842 groupCtx.mozCurrentTransform);
7843 this.ctx.transform.apply(this.ctx, deltaTransform);
7844 },
7845 save: function CanvasGraphics_save() {
7846 this.ctx.save();
7847 var old = this.current;
7848 this.stateStack.push(old);
7849 this.current = old.clone();
7850 this.current.resumeSMaskCtx = null;
7851 },
7852 restore: function CanvasGraphics_restore() {
7853 // SMask was suspended, we just need to resume it.
7854 if (this.current.resumeSMaskCtx) {
7855 this.resumeSMaskGroup();
7856 }
7857 // SMask has to be finished once there is no states that are using the
7858 // same SMask.
7859 if (this.current.activeSMask !== null && (this.stateStack.length === 0 ||
7860 this.stateStack[this.stateStack.length - 1].activeSMask !==
7861 this.current.activeSMask)) {
7862 this.endSMaskGroup();
7863 }
7864
7865 if (this.stateStack.length !== 0) {
7866 this.current = this.stateStack.pop();
7867 this.ctx.restore();
7868
7869 // Ensure that the clipping path is reset (fixes issue6413.pdf).
7870 this.pendingClip = null;
7871
7872 this.cachedGetSinglePixelWidth = null;
7873 }
7874 },
7875 transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
7876 this.ctx.transform(a, b, c, d, e, f);
7877
7878 this.cachedGetSinglePixelWidth = null;
7879 },
7880
7881 // Path
7882 constructPath: function CanvasGraphics_constructPath(ops, args) {
7883 var ctx = this.ctx;
7884 var current = this.current;
7885 var x = current.x, y = current.y;
7886 for (var i = 0, j = 0, ii = ops.length; i < ii; i++) {
7887 switch (ops[i] | 0) {
7888 case OPS.rectangle:
7889 x = args[j++];
7890 y = args[j++];
7891 var width = args[j++];
7892 var height = args[j++];
7893 if (width === 0) {
7894 width = this.getSinglePixelWidth();
7895 }
7896 if (height === 0) {
7897 height = this.getSinglePixelWidth();
7898 }
7899 var xw = x + width;
7900 var yh = y + height;
7901 this.ctx.moveTo(x, y);
7902 this.ctx.lineTo(xw, y);
7903 this.ctx.lineTo(xw, yh);
7904 this.ctx.lineTo(x, yh);
7905 this.ctx.lineTo(x, y);
7906 this.ctx.closePath();
7907 break;
7908 case OPS.moveTo:
7909 x = args[j++];
7910 y = args[j++];
7911 ctx.moveTo(x, y);
7912 break;
7913 case OPS.lineTo:
7914 x = args[j++];
7915 y = args[j++];
7916 ctx.lineTo(x, y);
7917 break;
7918 case OPS.curveTo:
7919 x = args[j + 4];
7920 y = args[j + 5];
7921 ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3],
7922 x, y);
7923 j += 6;
7924 break;
7925 case OPS.curveTo2:
7926 ctx.bezierCurveTo(x, y, args[j], args[j + 1],
7927 args[j + 2], args[j + 3]);
7928 x = args[j + 2];
7929 y = args[j + 3];
7930 j += 4;
7931 break;
7932 case OPS.curveTo3:
7933 x = args[j + 2];
7934 y = args[j + 3];
7935 ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
7936 j += 4;
7937 break;
7938 case OPS.closePath:
7939 ctx.closePath();
7940 break;
7941 }
7942 }
7943 current.setCurrentPoint(x, y);
7944 },
7945 closePath: function CanvasGraphics_closePath() {
7946 this.ctx.closePath();
7947 },
7948 stroke: function CanvasGraphics_stroke(consumePath) {
7949 consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
7950 var ctx = this.ctx;
7951 var strokeColor = this.current.strokeColor;
7952 // Prevent drawing too thin lines by enforcing a minimum line width.
7953 ctx.lineWidth = Math.max(this.getSinglePixelWidth() * MIN_WIDTH_FACTOR,
7954 this.current.lineWidth);
7955 // For stroke we want to temporarily change the global alpha to the
7956 // stroking alpha.
7957 ctx.globalAlpha = this.current.strokeAlpha;
7958 if (strokeColor && strokeColor.hasOwnProperty('type') &&
7959 strokeColor.type === 'Pattern') {
7960 // for patterns, we transform to pattern space, calculate
7961 // the pattern, call stroke, and restore to user space
7962 ctx.save();
7963 ctx.strokeStyle = strokeColor.getPattern(ctx, this);
7964 ctx.stroke();
7965 ctx.restore();
7966 } else {
7967 ctx.stroke();
7968 }
7969 if (consumePath) {
7970 this.consumePath();
7971 }
7972 // Restore the global alpha to the fill alpha
7973 ctx.globalAlpha = this.current.fillAlpha;
7974 },
7975 closeStroke: function CanvasGraphics_closeStroke() {
7976 this.closePath();
7977 this.stroke();
7978 },
7979 fill: function CanvasGraphics_fill(consumePath) {
7980 consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
7981 var ctx = this.ctx;
7982 var fillColor = this.current.fillColor;
7983 var isPatternFill = this.current.patternFill;
7984 var needRestore = false;
7985
7986 if (isPatternFill) {
7987 ctx.save();
7988 if (this.baseTransform) {
7989 ctx.setTransform.apply(ctx, this.baseTransform);
7990 }
7991 ctx.fillStyle = fillColor.getPattern(ctx, this);
7992 needRestore = true;
7993 }
7994
7995 if (this.pendingEOFill) {
7996 if (ctx.mozFillRule !== undefined) {
7997 ctx.mozFillRule = 'evenodd';
7998 ctx.fill();
7999 ctx.mozFillRule = 'nonzero';
8000 } else {
8001 ctx.fill('evenodd');
8002 }
8003 this.pendingEOFill = false;
8004 } else {
8005 ctx.fill();
8006 }
8007
8008 if (needRestore) {
8009 ctx.restore();
8010 }
8011 if (consumePath) {
8012 this.consumePath();
8013 }
8014 },
8015 eoFill: function CanvasGraphics_eoFill() {
8016 this.pendingEOFill = true;
8017 this.fill();
8018 },
8019 fillStroke: function CanvasGraphics_fillStroke() {
8020 this.fill(false);
8021 this.stroke(false);
8022
8023 this.consumePath();
8024 },
8025 eoFillStroke: function CanvasGraphics_eoFillStroke() {
8026 this.pendingEOFill = true;
8027 this.fillStroke();
8028 },
8029 closeFillStroke: function CanvasGraphics_closeFillStroke() {
8030 this.closePath();
8031 this.fillStroke();
8032 },
8033 closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() {
8034 this.pendingEOFill = true;
8035 this.closePath();
8036 this.fillStroke();
8037 },
8038 endPath: function CanvasGraphics_endPath() {
8039 this.consumePath();
8040 },
8041
8042 // Clipping
8043 clip: function CanvasGraphics_clip() {
8044 this.pendingClip = NORMAL_CLIP;
8045 },
8046 eoClip: function CanvasGraphics_eoClip() {
8047 this.pendingClip = EO_CLIP;
8048 },
8049
8050 // Text
8051 beginText: function CanvasGraphics_beginText() {
8052 this.current.textMatrix = IDENTITY_MATRIX;
8053 this.current.textMatrixScale = 1;
8054 this.current.x = this.current.lineX = 0;
8055 this.current.y = this.current.lineY = 0;
8056 },
8057 endText: function CanvasGraphics_endText() {
8058 var paths = this.pendingTextPaths;
8059 var ctx = this.ctx;
8060 if (paths === undefined) {
8061 ctx.beginPath();
8062 return;
8063 }
8064
8065 ctx.save();
8066 ctx.beginPath();
8067 for (var i = 0; i < paths.length; i++) {
8068 var path = paths[i];
8069 ctx.setTransform.apply(ctx, path.transform);
8070 ctx.translate(path.x, path.y);
8071 path.addToPath(ctx, path.fontSize);
8072 }
8073 ctx.restore();
8074 ctx.clip();
8075 ctx.beginPath();
8076 delete this.pendingTextPaths;
8077 },
8078 setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
8079 this.current.charSpacing = spacing;
8080 },
8081 setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
8082 this.current.wordSpacing = spacing;
8083 },
8084 setHScale: function CanvasGraphics_setHScale(scale) {
8085 this.current.textHScale = scale / 100;
8086 },
8087 setLeading: function CanvasGraphics_setLeading(leading) {
8088 this.current.leading = -leading;
8089 },
8090 setFont: function CanvasGraphics_setFont(fontRefName, size) {
8091 var fontObj = this.commonObjs.get(fontRefName);
8092 var current = this.current;
8093
8094 if (!fontObj) {
8095 error('Can\'t find font for ' + fontRefName);
8096 }
8097
8098 current.fontMatrix = (fontObj.fontMatrix ?
8099 fontObj.fontMatrix : FONT_IDENTITY_MATRIX);
8100
8101 // A valid matrix needs all main diagonal elements to be non-zero
8102 // This also ensures we bypass FF bugzilla bug #719844.
8103 if (current.fontMatrix[0] === 0 ||
8104 current.fontMatrix[3] === 0) {
8105 warn('Invalid font matrix for font ' + fontRefName);
8106 }
8107
8108 // The spec for Tf (setFont) says that 'size' specifies the font 'scale',
8109 // and in some docs this can be negative (inverted x-y axes).
8110 if (size < 0) {
8111 size = -size;
8112 current.fontDirection = -1;
8113 } else {
8114 current.fontDirection = 1;
8115 }
8116
8117 this.current.font = fontObj;
8118 this.current.fontSize = size;
8119
8120 if (fontObj.isType3Font) {
8121 return; // we don't need ctx.font for Type3 fonts
8122 }
8123
8124 var name = fontObj.loadedName || 'sans-serif';
8125 var bold = fontObj.black ? (fontObj.bold ? '900' : 'bold') :
8126 (fontObj.bold ? 'bold' : 'normal');
8127
8128 var italic = fontObj.italic ? 'italic' : 'normal';
8129 var typeface = '"' + name + '", ' + fontObj.fallbackName;
8130
8131 // Some font backends cannot handle fonts below certain size.
8132 // Keeping the font at minimal size and using the fontSizeScale to change
8133 // the current transformation matrix before the fillText/strokeText.
8134 // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
8135 var browserFontSize = size < MIN_FONT_SIZE ? MIN_FONT_SIZE :
8136 size > MAX_FONT_SIZE ? MAX_FONT_SIZE : size;
8137 this.current.fontSizeScale = size / browserFontSize;
8138
8139 var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
8140 this.ctx.font = rule;
8141 },
8142 setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
8143 this.current.textRenderingMode = mode;
8144 },
8145 setTextRise: function CanvasGraphics_setTextRise(rise) {
8146 this.current.textRise = rise;
8147 },
8148 moveText: function CanvasGraphics_moveText(x, y) {
8149 this.current.x = this.current.lineX += x;
8150 this.current.y = this.current.lineY += y;
8151 },
8152 setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
8153 this.setLeading(-y);
8154 this.moveText(x, y);
8155 },
8156 setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) {
8157 this.current.textMatrix = [a, b, c, d, e, f];
8158 this.current.textMatrixScale = Math.sqrt(a * a + b * b);
8159
8160 this.current.x = this.current.lineX = 0;
8161 this.current.y = this.current.lineY = 0;
8162 },
8163 nextLine: function CanvasGraphics_nextLine() {
8164 this.moveText(0, this.current.leading);
8165 },
8166
8167 paintChar: function CanvasGraphics_paintChar(character, x, y) {
8168 var ctx = this.ctx;
8169 var current = this.current;
8170 var font = current.font;
8171 var textRenderingMode = current.textRenderingMode;
8172 var fontSize = current.fontSize / current.fontSizeScale;
8173 var fillStrokeMode = textRenderingMode &
8174 TextRenderingMode.FILL_STROKE_MASK;
8175 var isAddToPathSet = !!(textRenderingMode &
8176 TextRenderingMode.ADD_TO_PATH_FLAG);
8177
8178 var addToPath;
8179 if (font.disableFontFace || isAddToPathSet) {
8180 addToPath = font.getPathGenerator(this.commonObjs, character);
8181 }
8182
8183 if (font.disableFontFace) {
8184 ctx.save();
8185 ctx.translate(x, y);
8186 ctx.beginPath();
8187 addToPath(ctx, fontSize);
8188 if (fillStrokeMode === TextRenderingMode.FILL ||
8189 fillStrokeMode === TextRenderingMode.FILL_STROKE) {
8190 ctx.fill();
8191 }
8192 if (fillStrokeMode === TextRenderingMode.STROKE ||
8193 fillStrokeMode === TextRenderingMode.FILL_STROKE) {
8194 ctx.stroke();
8195 }
8196 ctx.restore();
8197 } else {
8198 if (fillStrokeMode === TextRenderingMode.FILL ||
8199 fillStrokeMode === TextRenderingMode.FILL_STROKE) {
8200 ctx.fillText(character, x, y);
8201 }
8202 if (fillStrokeMode === TextRenderingMode.STROKE ||
8203 fillStrokeMode === TextRenderingMode.FILL_STROKE) {
8204 ctx.strokeText(character, x, y);
8205 }
8206 }
8207
8208 if (isAddToPathSet) {
8209 var paths = this.pendingTextPaths || (this.pendingTextPaths = []);
8210 paths.push({
8211 transform: ctx.mozCurrentTransform,
8212 x: x,
8213 y: y,
8214 fontSize: fontSize,
8215 addToPath: addToPath
8216 });
8217 }
8218 },
8219
8220 get isFontSubpixelAAEnabled() {
8221 // Checks if anti-aliasing is enabled when scaled text is painted.
8222 // On Windows GDI scaled fonts looks bad.
8223 var ctx = document.createElement('canvas').getContext('2d');
8224 ctx.scale(1.5, 1);
8225 ctx.fillText('I', 0, 10);
8226 var data = ctx.getImageData(0, 0, 10, 10).data;
8227 var enabled = false;
8228 for (var i = 3; i < data.length; i += 4) {
8229 if (data[i] > 0 && data[i] < 255) {
8230 enabled = true;
8231 break;
8232 }
8233 }
8234 return shadow(this, 'isFontSubpixelAAEnabled', enabled);
8235 },
8236
8237 showText: function CanvasGraphics_showText(glyphs) {
8238 var current = this.current;
8239 var font = current.font;
8240 if (font.isType3Font) {
8241 return this.showType3Text(glyphs);
8242 }
8243
8244 var fontSize = current.fontSize;
8245 if (fontSize === 0) {
8246 return;
8247 }
8248
8249 var ctx = this.ctx;
8250 var fontSizeScale = current.fontSizeScale;
8251 var charSpacing = current.charSpacing;
8252 var wordSpacing = current.wordSpacing;
8253 var fontDirection = current.fontDirection;
8254 var textHScale = current.textHScale * fontDirection;
8255 var glyphsLength = glyphs.length;
8256 var vertical = font.vertical;
8257 var spacingDir = vertical ? 1 : -1;
8258 var defaultVMetrics = font.defaultVMetrics;
8259 var widthAdvanceScale = fontSize * current.fontMatrix[0];
8260
8261 var simpleFillText =
8262 current.textRenderingMode === TextRenderingMode.FILL &&
8263 !font.disableFontFace;
8264
8265 ctx.save();
8266 ctx.transform.apply(ctx, current.textMatrix);
8267 ctx.translate(current.x, current.y + current.textRise);
8268
8269 if (current.patternFill) {
8270 // TODO: Some shading patterns are not applied correctly to text,
8271 // e.g. issues 3988 and 5432, and ShowText-ShadingPattern.pdf.
8272 ctx.fillStyle = current.fillColor.getPattern(ctx, this);
8273 }
8274
8275 if (fontDirection > 0) {
8276 ctx.scale(textHScale, -1);
8277 } else {
8278 ctx.scale(textHScale, 1);
8279 }
8280
8281 var lineWidth = current.lineWidth;
8282 var scale = current.textMatrixScale;
8283 if (scale === 0 || lineWidth === 0) {
8284 var fillStrokeMode = current.textRenderingMode &
8285 TextRenderingMode.FILL_STROKE_MASK;
8286 if (fillStrokeMode === TextRenderingMode.STROKE ||
8287 fillStrokeMode === TextRenderingMode.FILL_STROKE) {
8288 this.cachedGetSinglePixelWidth = null;
8289 lineWidth = this.getSinglePixelWidth() * MIN_WIDTH_FACTOR;
8290 }
8291 } else {
8292 lineWidth /= scale;
8293 }
8294
8295 if (fontSizeScale !== 1.0) {
8296 ctx.scale(fontSizeScale, fontSizeScale);
8297 lineWidth /= fontSizeScale;
8298 }
8299
8300 ctx.lineWidth = lineWidth;
8301
8302 var x = 0, i;
8303 for (i = 0; i < glyphsLength; ++i) {
8304 var glyph = glyphs[i];
8305 if (isNum(glyph)) {
8306 x += spacingDir * glyph * fontSize / 1000;
8307 continue;
8308 }
8309
8310 var restoreNeeded = false;
8311 var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
8312 var character = glyph.fontChar;
8313 var accent = glyph.accent;
8314 var scaledX, scaledY, scaledAccentX, scaledAccentY;
8315 var width = glyph.width;
8316 if (vertical) {
8317 var vmetric, vx, vy;
8318 vmetric = glyph.vmetric || defaultVMetrics;
8319 vx = glyph.vmetric ? vmetric[1] : width * 0.5;
8320 vx = -vx * widthAdvanceScale;
8321 vy = vmetric[2] * widthAdvanceScale;
8322
8323 width = vmetric ? -vmetric[0] : width;
8324 scaledX = vx / fontSizeScale;
8325 scaledY = (x + vy) / fontSizeScale;
8326 } else {
8327 scaledX = x / fontSizeScale;
8328 scaledY = 0;
8329 }
8330
8331 if (font.remeasure && width > 0) {
8332 // Some standard fonts may not have the exact width: rescale per
8333 // character if measured width is greater than expected glyph width
8334 // and subpixel-aa is enabled, otherwise just center the glyph.
8335 var measuredWidth = ctx.measureText(character).width * 1000 /
8336 fontSize * fontSizeScale;
8337 if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
8338 var characterScaleX = width / measuredWidth;
8339 restoreNeeded = true;
8340 ctx.save();
8341 ctx.scale(characterScaleX, 1);
8342 scaledX /= characterScaleX;
8343 } else if (width !== measuredWidth) {
8344 scaledX += (width - measuredWidth) / 2000 *
8345 fontSize / fontSizeScale;
8346 }
8347 }
8348
8349 // Only attempt to draw the glyph if it is actually in the embedded font
8350 // file or if there isn't a font file so the fallback font is shown.
8351 if (glyph.isInFont || font.missingFile) {
8352 if (simpleFillText && !accent) {
8353 // common case
8354 ctx.fillText(character, scaledX, scaledY);
8355 } else {
8356 this.paintChar(character, scaledX, scaledY);
8357 if (accent) {
8358 scaledAccentX = scaledX + accent.offset.x / fontSizeScale;
8359 scaledAccentY = scaledY - accent.offset.y / fontSizeScale;
8360 this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY);
8361 }
8362 }
8363 }
8364
8365 var charWidth = width * widthAdvanceScale + spacing * fontDirection;
8366 x += charWidth;
8367
8368 if (restoreNeeded) {
8369 ctx.restore();
8370 }
8371 }
8372 if (vertical) {
8373 current.y -= x * textHScale;
8374 } else {
8375 current.x += x * textHScale;
8376 }
8377 ctx.restore();
8378 },
8379
8380 showType3Text: function CanvasGraphics_showType3Text(glyphs) {
8381 // Type3 fonts - each glyph is a "mini-PDF"
8382 var ctx = this.ctx;
8383 var current = this.current;
8384 var font = current.font;
8385 var fontSize = current.fontSize;
8386 var fontDirection = current.fontDirection;
8387 var spacingDir = font.vertical ? 1 : -1;
8388 var charSpacing = current.charSpacing;
8389 var wordSpacing = current.wordSpacing;
8390 var textHScale = current.textHScale * fontDirection;
8391 var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
8392 var glyphsLength = glyphs.length;
8393 var isTextInvisible =
8394 current.textRenderingMode === TextRenderingMode.INVISIBLE;
8395 var i, glyph, width, spacingLength;
8396
8397 if (isTextInvisible || fontSize === 0) {
8398 return;
8399 }
8400 this.cachedGetSinglePixelWidth = null;
8401
8402 ctx.save();
8403 ctx.transform.apply(ctx, current.textMatrix);
8404 ctx.translate(current.x, current.y);
8405
8406 ctx.scale(textHScale, fontDirection);
8407
8408 for (i = 0; i < glyphsLength; ++i) {
8409 glyph = glyphs[i];
8410 if (isNum(glyph)) {
8411 spacingLength = spacingDir * glyph * fontSize / 1000;
8412 this.ctx.translate(spacingLength, 0);
8413 current.x += spacingLength * textHScale;
8414 continue;
8415 }
8416
8417 var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
8418 var operatorList = font.charProcOperatorList[glyph.operatorListId];
8419 if (!operatorList) {
8420 warn('Type3 character \"' + glyph.operatorListId +
8421 '\" is not available');
8422 continue;
8423 }
8424 this.processingType3 = glyph;
8425 this.save();
8426 ctx.scale(fontSize, fontSize);
8427 ctx.transform.apply(ctx, fontMatrix);
8428 this.executeOperatorList(operatorList);
8429 this.restore();
8430
8431 var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
8432 width = transformed[0] * fontSize + spacing;
8433
8434 ctx.translate(width, 0);
8435 current.x += width * textHScale;
8436 }
8437 ctx.restore();
8438 this.processingType3 = null;
8439 },
8440
8441 // Type3 fonts
8442 setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) {
8443 // We can safely ignore this since the width should be the same
8444 // as the width in the Widths array.
8445 },
8446 setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth,
8447 yWidth,
8448 llx,
8449 lly,
8450 urx,
8451 ury) {
8452 // TODO According to the spec we're also suppose to ignore any operators
8453 // that set color or include images while processing this type3 font.
8454 this.ctx.rect(llx, lly, urx - llx, ury - lly);
8455 this.clip();
8456 this.endPath();
8457 },
8458
8459 // Color
8460 getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR) {
8461 var pattern;
8462 if (IR[0] === 'TilingPattern') {
8463 var color = IR[1];
8464 var baseTransform = this.baseTransform ||
8465 this.ctx.mozCurrentTransform.slice();
8466 var self = this;
8467 var canvasGraphicsFactory = {
8468 createCanvasGraphics: function (ctx) {
8469 return new CanvasGraphics(ctx, self.commonObjs, self.objs);
8470 }
8471 };
8472 pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory,
8473 baseTransform);
8474 } else {
8475 pattern = getShadingPatternFromIR(IR);
8476 }
8477 return pattern;
8478 },
8479 setStrokeColorN: function CanvasGraphics_setStrokeColorN(/*...*/) {
8480 this.current.strokeColor = this.getColorN_Pattern(arguments);
8481 },
8482 setFillColorN: function CanvasGraphics_setFillColorN(/*...*/) {
8483 this.current.fillColor = this.getColorN_Pattern(arguments);
8484 this.current.patternFill = true;
8485 },
8486 setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) {
8487 var color = Util.makeCssRgb(r, g, b);
8488 this.ctx.strokeStyle = color;
8489 this.current.strokeColor = color;
8490 },
8491 setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) {
8492 var color = Util.makeCssRgb(r, g, b);
8493 this.ctx.fillStyle = color;
8494 this.current.fillColor = color;
8495 this.current.patternFill = false;
8496 },
8497
8498 shadingFill: function CanvasGraphics_shadingFill(patternIR) {
8499 var ctx = this.ctx;
8500
8501 this.save();
8502 var pattern = getShadingPatternFromIR(patternIR);
8503 ctx.fillStyle = pattern.getPattern(ctx, this, true);
8504
8505 var inv = ctx.mozCurrentTransformInverse;
8506 if (inv) {
8507 var canvas = ctx.canvas;
8508 var width = canvas.width;
8509 var height = canvas.height;
8510
8511 var bl = Util.applyTransform([0, 0], inv);
8512 var br = Util.applyTransform([0, height], inv);
8513 var ul = Util.applyTransform([width, 0], inv);
8514 var ur = Util.applyTransform([width, height], inv);
8515
8516 var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
8517 var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
8518 var x1 = Math.max(bl[0], br[0], ul[0], ur[0]);
8519 var y1 = Math.max(bl[1], br[1], ul[1], ur[1]);
8520
8521 this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
8522 } else {
8523 // HACK to draw the gradient onto an infinite rectangle.
8524 // PDF gradients are drawn across the entire image while
8525 // Canvas only allows gradients to be drawn in a rectangle
8526 // The following bug should allow us to remove this.
8527 // https://bugzilla.mozilla.org/show_bug.cgi?id=664884
8528
8529 this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
8530 }
8531
8532 this.restore();
8533 },
8534
8535 // Images
8536 beginInlineImage: function CanvasGraphics_beginInlineImage() {
8537 error('Should not call beginInlineImage');
8538 },
8539 beginImageData: function CanvasGraphics_beginImageData() {
8540 error('Should not call beginImageData');
8541 },
8542
8543 paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix,
8544 bbox) {
8545 this.save();
8546 this.baseTransformStack.push(this.baseTransform);
8547
8548 if (isArray(matrix) && 6 === matrix.length) {
8549 this.transform.apply(this, matrix);
8550 }
8551
8552 this.baseTransform = this.ctx.mozCurrentTransform;
8553
8554 if (isArray(bbox) && 4 === bbox.length) {
8555 var width = bbox[2] - bbox[0];
8556 var height = bbox[3] - bbox[1];
8557 this.ctx.rect(bbox[0], bbox[1], width, height);
8558 this.clip();
8559 this.endPath();
8560 }
8561 },
8562
8563 paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
8564 this.restore();
8565 this.baseTransform = this.baseTransformStack.pop();
8566 },
8567
8568 beginGroup: function CanvasGraphics_beginGroup(group) {
8569 this.save();
8570 var currentCtx = this.ctx;
8571 // TODO non-isolated groups - according to Rik at adobe non-isolated
8572 // group results aren't usually that different and they even have tools
8573 // that ignore this setting. Notes from Rik on implementing:
8574 // - When you encounter an transparency group, create a new canvas with
8575 // the dimensions of the bbox
8576 // - copy the content from the previous canvas to the new canvas
8577 // - draw as usual
8578 // - remove the backdrop alpha:
8579 // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha
8580 // value of your transparency group and 'alphaBackdrop' the alpha of the
8581 // backdrop
8582 // - remove background color:
8583 // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)
8584 if (!group.isolated) {
8585 info('TODO: Support non-isolated groups.');
8586 }
8587
8588 // TODO knockout - supposedly possible with the clever use of compositing
8589 // modes.
8590 if (group.knockout) {
8591 warn('Knockout groups not supported.');
8592 }
8593
8594 var currentTransform = currentCtx.mozCurrentTransform;
8595 if (group.matrix) {
8596 currentCtx.transform.apply(currentCtx, group.matrix);
8597 }
8598 assert(group.bbox, 'Bounding box is required.');
8599
8600 // Based on the current transform figure out how big the bounding box
8601 // will actually be.
8602 var bounds = Util.getAxialAlignedBoundingBox(
8603 group.bbox,
8604 currentCtx.mozCurrentTransform);
8605 // Clip the bounding box to the current canvas.
8606 var canvasBounds = [0,
8607 0,
8608 currentCtx.canvas.width,
8609 currentCtx.canvas.height];
8610 bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
8611 // Use ceil in case we're between sizes so we don't create canvas that is
8612 // too small and make the canvas at least 1x1 pixels.
8613 var offsetX = Math.floor(bounds[0]);
8614 var offsetY = Math.floor(bounds[1]);
8615 var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
8616 var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
8617 var scaleX = 1, scaleY = 1;
8618 if (drawnWidth > MAX_GROUP_SIZE) {
8619 scaleX = drawnWidth / MAX_GROUP_SIZE;
8620 drawnWidth = MAX_GROUP_SIZE;
8621 }
8622 if (drawnHeight > MAX_GROUP_SIZE) {
8623 scaleY = drawnHeight / MAX_GROUP_SIZE;
8624 drawnHeight = MAX_GROUP_SIZE;
8625 }
8626
8627 var cacheId = 'groupAt' + this.groupLevel;
8628 if (group.smask) {
8629 // Using two cache entries is case if masks are used one after another.
8630 cacheId += '_smask_' + ((this.smaskCounter++) % 2);
8631 }
8632 var scratchCanvas = this.cachedCanvases.getCanvas(
8633 cacheId, drawnWidth, drawnHeight, true);
8634 var groupCtx = scratchCanvas.context;
8635
8636 // Since we created a new canvas that is just the size of the bounding box
8637 // we have to translate the group ctx.
8638 groupCtx.scale(1 / scaleX, 1 / scaleY);
8639 groupCtx.translate(-offsetX, -offsetY);
8640 groupCtx.transform.apply(groupCtx, currentTransform);
8641
8642 if (group.smask) {
8643 // Saving state and cached mask to be used in setGState.
8644 this.smaskStack.push({
8645 canvas: scratchCanvas.canvas,
8646 context: groupCtx,
8647 offsetX: offsetX,
8648 offsetY: offsetY,
8649 scaleX: scaleX,
8650 scaleY: scaleY,
8651 subtype: group.smask.subtype,
8652 backdrop: group.smask.backdrop,
8653 transferMap: group.smask.transferMap || null,
8654 startTransformInverse: null, // used during suspend operation
8655 });
8656 } else {
8657 // Setup the current ctx so when the group is popped we draw it at the
8658 // right location.
8659 currentCtx.setTransform(1, 0, 0, 1, 0, 0);
8660 currentCtx.translate(offsetX, offsetY);
8661 currentCtx.scale(scaleX, scaleY);
8662 }
8663 // The transparency group inherits all off the current graphics state
8664 // except the blend mode, soft mask, and alpha constants.
8665 copyCtxState(currentCtx, groupCtx);
8666 this.ctx = groupCtx;
8667 this.setGState([
8668 ['BM', 'Normal'],
8669 ['ca', 1],
8670 ['CA', 1]
8671 ]);
8672 this.groupStack.push(currentCtx);
8673 this.groupLevel++;
8674
8675 // Reseting mask state, masks will be applied on restore of the group.
8676 this.current.activeSMask = null;
8677 },
8678
8679 endGroup: function CanvasGraphics_endGroup(group) {
8680 this.groupLevel--;
8681 var groupCtx = this.ctx;
8682 this.ctx = this.groupStack.pop();
8683 // Turn off image smoothing to avoid sub pixel interpolation which can
8684 // look kind of blurry for some pdfs.
8685 if (this.ctx.imageSmoothingEnabled !== undefined) {
8686 this.ctx.imageSmoothingEnabled = false;
8687 } else {
8688 this.ctx.mozImageSmoothingEnabled = false;
8689 }
8690 if (group.smask) {
8691 this.tempSMask = this.smaskStack.pop();
8692 } else {
8693 this.ctx.drawImage(groupCtx.canvas, 0, 0);
8694 }
8695 this.restore();
8696 },
8697
8698 beginAnnotations: function CanvasGraphics_beginAnnotations() {
8699 this.save();
8700 this.current = new CanvasExtraState();
8701
8702 if (this.baseTransform) {
8703 this.ctx.setTransform.apply(this.ctx, this.baseTransform);
8704 }
8705 },
8706
8707 endAnnotations: function CanvasGraphics_endAnnotations() {
8708 this.restore();
8709 },
8710
8711 beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform,
8712 matrix) {
8713 this.save();
8714
8715 if (isArray(rect) && 4 === rect.length) {
8716 var width = rect[2] - rect[0];
8717 var height = rect[3] - rect[1];
8718 this.ctx.rect(rect[0], rect[1], width, height);
8719 this.clip();
8720 this.endPath();
8721 }
8722
8723 this.transform.apply(this, transform);
8724 this.transform.apply(this, matrix);
8725 },
8726
8727 endAnnotation: function CanvasGraphics_endAnnotation() {
8728 this.restore();
8729 },
8730
8731 paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
8732 var domImage = this.objs.get(objId);
8733 if (!domImage) {
8734 warn('Dependent image isn\'t ready yet');
8735 return;
8736 }
8737
8738 this.save();
8739
8740 var ctx = this.ctx;
8741 // scale the image to the unit square
8742 ctx.scale(1 / w, -1 / h);
8743
8744 ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
8745 0, -h, w, h);
8746 if (this.imageLayer) {
8747 var currentTransform = ctx.mozCurrentTransformInverse;
8748 var position = this.getCanvasPosition(0, 0);
8749 this.imageLayer.appendImage({
8750 objId: objId,
8751 left: position[0],
8752 top: position[1],
8753 width: w / currentTransform[0],
8754 height: h / currentTransform[3]
8755 });
8756 }
8757 this.restore();
8758 },
8759
8760 paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) {
8761 var ctx = this.ctx;
8762 var width = img.width, height = img.height;
8763 var fillColor = this.current.fillColor;
8764 var isPatternFill = this.current.patternFill;
8765
8766 var glyph = this.processingType3;
8767
8768 if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) {
8769 if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) {
8770 glyph.compiled =
8771 compileType3Glyph({data: img.data, width: width, height: height});
8772 } else {
8773 glyph.compiled = null;
8774 }
8775 }
8776
8777 if (glyph && glyph.compiled) {
8778 glyph.compiled(ctx);
8779 return;
8780 }
8781
8782 var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
8783 width, height);
8784 var maskCtx = maskCanvas.context;
8785 maskCtx.save();
8786
8787 putBinaryImageMask(maskCtx, img);
8788
8789 maskCtx.globalCompositeOperation = 'source-in';
8790
8791 maskCtx.fillStyle = isPatternFill ?
8792 fillColor.getPattern(maskCtx, this) : fillColor;
8793 maskCtx.fillRect(0, 0, width, height);
8794
8795 maskCtx.restore();
8796
8797 this.paintInlineImageXObject(maskCanvas.canvas);
8798 },
8799
8800 paintImageMaskXObjectRepeat:
8801 function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX,
8802 scaleY, positions) {
8803 var width = imgData.width;
8804 var height = imgData.height;
8805 var fillColor = this.current.fillColor;
8806 var isPatternFill = this.current.patternFill;
8807
8808 var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
8809 width, height);
8810 var maskCtx = maskCanvas.context;
8811 maskCtx.save();
8812
8813 putBinaryImageMask(maskCtx, imgData);
8814
8815 maskCtx.globalCompositeOperation = 'source-in';
8816
8817 maskCtx.fillStyle = isPatternFill ?
8818 fillColor.getPattern(maskCtx, this) : fillColor;
8819 maskCtx.fillRect(0, 0, width, height);
8820
8821 maskCtx.restore();
8822
8823 var ctx = this.ctx;
8824 for (var i = 0, ii = positions.length; i < ii; i += 2) {
8825 ctx.save();
8826 ctx.transform(scaleX, 0, 0, scaleY, positions[i], positions[i + 1]);
8827 ctx.scale(1, -1);
8828 ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
8829 0, -1, 1, 1);
8830 ctx.restore();
8831 }
8832 },
8833
8834 paintImageMaskXObjectGroup:
8835 function CanvasGraphics_paintImageMaskXObjectGroup(images) {
8836 var ctx = this.ctx;
8837
8838 var fillColor = this.current.fillColor;
8839 var isPatternFill = this.current.patternFill;
8840 for (var i = 0, ii = images.length; i < ii; i++) {
8841 var image = images[i];
8842 var width = image.width, height = image.height;
8843
8844 var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas',
8845 width, height);
8846 var maskCtx = maskCanvas.context;
8847 maskCtx.save();
8848
8849 putBinaryImageMask(maskCtx, image);
8850
8851 maskCtx.globalCompositeOperation = 'source-in';
8852
8853 maskCtx.fillStyle = isPatternFill ?
8854 fillColor.getPattern(maskCtx, this) : fillColor;
8855 maskCtx.fillRect(0, 0, width, height);
8856
8857 maskCtx.restore();
8858
8859 ctx.save();
8860 ctx.transform.apply(ctx, image.transform);
8861 ctx.scale(1, -1);
8862 ctx.drawImage(maskCanvas.canvas, 0, 0, width, height,
8863 0, -1, 1, 1);
8864 ctx.restore();
8865 }
8866 },
8867
8868 paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
8869 var imgData = this.objs.get(objId);
8870 if (!imgData) {
8871 warn('Dependent image isn\'t ready yet');
8872 return;
8873 }
8874
8875 this.paintInlineImageXObject(imgData);
8876 },
8877
8878 paintImageXObjectRepeat:
8879 function CanvasGraphics_paintImageXObjectRepeat(objId, scaleX, scaleY,
8880 positions) {
8881 var imgData = this.objs.get(objId);
8882 if (!imgData) {
8883 warn('Dependent image isn\'t ready yet');
8884 return;
8885 }
8886
8887 var width = imgData.width;
8888 var height = imgData.height;
8889 var map = [];
8890 for (var i = 0, ii = positions.length; i < ii; i += 2) {
8891 map.push({transform: [scaleX, 0, 0, scaleY, positions[i],
8892 positions[i + 1]], x: 0, y: 0, w: width, h: height});
8893 }
8894 this.paintInlineImageXObjectGroup(imgData, map);
8895 },
8896
8897 paintInlineImageXObject:
8898 function CanvasGraphics_paintInlineImageXObject(imgData) {
8899 var width = imgData.width;
8900 var height = imgData.height;
8901 var ctx = this.ctx;
8902
8903 this.save();
8904 // scale the image to the unit square
8905 ctx.scale(1 / width, -1 / height);
8906
8907 var currentTransform = ctx.mozCurrentTransformInverse;
8908 var a = currentTransform[0], b = currentTransform[1];
8909 var widthScale = Math.max(Math.sqrt(a * a + b * b), 1);
8910 var c = currentTransform[2], d = currentTransform[3];
8911 var heightScale = Math.max(Math.sqrt(c * c + d * d), 1);
8912
8913 var imgToPaint, tmpCanvas;
8914 // instanceof HTMLElement does not work in jsdom node.js module
8915 if (imgData instanceof HTMLElement || !imgData.data) {
8916 imgToPaint = imgData;
8917 } else {
8918 tmpCanvas = this.cachedCanvases.getCanvas('inlineImage',
8919 width, height);
8920 var tmpCtx = tmpCanvas.context;
8921 putBinaryImageData(tmpCtx, imgData);
8922 imgToPaint = tmpCanvas.canvas;
8923 }
8924
8925 var paintWidth = width, paintHeight = height;
8926 var tmpCanvasId = 'prescale1';
8927 // Vertial or horizontal scaling shall not be more than 2 to not loose the
8928 // pixels during drawImage operation, painting on the temporary canvas(es)
8929 // that are twice smaller in size
8930 while ((widthScale > 2 && paintWidth > 1) ||
8931 (heightScale > 2 && paintHeight > 1)) {
8932 var newWidth = paintWidth, newHeight = paintHeight;
8933 if (widthScale > 2 && paintWidth > 1) {
8934 newWidth = Math.ceil(paintWidth / 2);
8935 widthScale /= paintWidth / newWidth;
8936 }
8937 if (heightScale > 2 && paintHeight > 1) {
8938 newHeight = Math.ceil(paintHeight / 2);
8939 heightScale /= paintHeight / newHeight;
8940 }
8941 tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId,
8942 newWidth, newHeight);
8943 tmpCtx = tmpCanvas.context;
8944 tmpCtx.clearRect(0, 0, newWidth, newHeight);
8945 tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
8946 0, 0, newWidth, newHeight);
8947 imgToPaint = tmpCanvas.canvas;
8948 paintWidth = newWidth;
8949 paintHeight = newHeight;
8950 tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1';
8951 }
8952 ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight,
8953 0, -height, width, height);
8954
8955 if (this.imageLayer) {
8956 var position = this.getCanvasPosition(0, -height);
8957 this.imageLayer.appendImage({
8958 imgData: imgData,
8959 left: position[0],
8960 top: position[1],
8961 width: width / currentTransform[0],
8962 height: height / currentTransform[3]
8963 });
8964 }
8965 this.restore();
8966 },
8967
8968 paintInlineImageXObjectGroup:
8969 function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
8970 var ctx = this.ctx;
8971 var w = imgData.width;
8972 var h = imgData.height;
8973
8974 var tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', w, h);
8975 var tmpCtx = tmpCanvas.context;
8976 putBinaryImageData(tmpCtx, imgData);
8977
8978 for (var i = 0, ii = map.length; i < ii; i++) {
8979 var entry = map[i];
8980 ctx.save();
8981 ctx.transform.apply(ctx, entry.transform);
8982 ctx.scale(1, -1);
8983 ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h,
8984 0, -1, 1, 1);
8985 if (this.imageLayer) {
8986 var position = this.getCanvasPosition(entry.x, entry.y);
8987 this.imageLayer.appendImage({
8988 imgData: imgData,
8989 left: position[0],
8990 top: position[1],
8991 width: w,
8992 height: h
8993 });
8994 }
8995 ctx.restore();
8996 }
8997 },
8998
8999 paintSolidColorImageMask:
9000 function CanvasGraphics_paintSolidColorImageMask() {
9001 this.ctx.fillRect(0, 0, 1, 1);
9002 },
9003
9004 paintXObject: function CanvasGraphics_paintXObject() {
9005 warn('Unsupported \'paintXObject\' command.');
9006 },
9007
9008 // Marked content
9009
9010 markPoint: function CanvasGraphics_markPoint(tag) {
9011 // TODO Marked content.
9012 },
9013 markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
9014 // TODO Marked content.
9015 },
9016 beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
9017 // TODO Marked content.
9018 },
9019 beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
9020 tag, properties) {
9021 // TODO Marked content.
9022 },
9023 endMarkedContent: function CanvasGraphics_endMarkedContent() {
9024 // TODO Marked content.
9025 },
9026
9027 // Compatibility
9028
9029 beginCompat: function CanvasGraphics_beginCompat() {
9030 // TODO ignore undefined operators (should we do that anyway?)
9031 },
9032 endCompat: function CanvasGraphics_endCompat() {
9033 // TODO stop ignoring undefined operators
9034 },
9035
9036 // Helper functions
9037
9038 consumePath: function CanvasGraphics_consumePath() {
9039 var ctx = this.ctx;
9040 if (this.pendingClip) {
9041 if (this.pendingClip === EO_CLIP) {
9042 if (ctx.mozFillRule !== undefined) {
9043 ctx.mozFillRule = 'evenodd';
9044 ctx.clip();
9045 ctx.mozFillRule = 'nonzero';
9046 } else {
9047 ctx.clip('evenodd');
9048 }
9049 } else {
9050 ctx.clip();
9051 }
9052 this.pendingClip = null;
9053 }
9054 ctx.beginPath();
9055 },
9056 getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
9057 if (this.cachedGetSinglePixelWidth === null) {
9058 // NOTE: The `save` and `restore` commands used below is a workaround
9059 // that is necessary in order to prevent `mozCurrentTransformInverse`
9060 // from intermittently returning incorrect values in Firefox, see:
9061 // https://github.com/mozilla/pdf.js/issues/7188.
9062 this.ctx.save();
9063 var inverse = this.ctx.mozCurrentTransformInverse;
9064 this.ctx.restore();
9065 // max of the current horizontal and vertical scale
9066 this.cachedGetSinglePixelWidth = Math.sqrt(Math.max(
9067 (inverse[0] * inverse[0] + inverse[1] * inverse[1]),
9068 (inverse[2] * inverse[2] + inverse[3] * inverse[3])));
9069 }
9070 return this.cachedGetSinglePixelWidth;
9071 },
9072 getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) {
9073 var transform = this.ctx.mozCurrentTransform;
9074 return [
9075 transform[0] * x + transform[2] * y + transform[4],
9076 transform[1] * x + transform[3] * y + transform[5]
9077 ];
9078 }
9079 };
9080
9081 for (var op in OPS) {
9082 CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
9083 }
9084
9085 return CanvasGraphics;
9086})();
9087
9088exports.CanvasGraphics = CanvasGraphics;
9089exports.createScratchCanvas = createScratchCanvas;
9090}));
9091
9092
9093(function (root, factory) {
9094 {
9095 factory((root.pdfjsDisplayAPI = {}), root.pdfjsSharedUtil,
9096 root.pdfjsDisplayFontLoader, root.pdfjsDisplayCanvas,
9097 root.pdfjsDisplayMetadata, root.pdfjsDisplayDOMUtils);
9098 }
9099}(this, function (exports, sharedUtil, displayFontLoader, displayCanvas,
9100 displayMetadata, displayDOMUtils, amdRequire) {
9101
9102var InvalidPDFException = sharedUtil.InvalidPDFException;
9103var MessageHandler = sharedUtil.MessageHandler;
9104var MissingPDFException = sharedUtil.MissingPDFException;
9105var PageViewport = sharedUtil.PageViewport;
9106var PasswordResponses = sharedUtil.PasswordResponses;
9107var PasswordException = sharedUtil.PasswordException;
9108var StatTimer = sharedUtil.StatTimer;
9109var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
9110var UnknownErrorException = sharedUtil.UnknownErrorException;
9111var Util = sharedUtil.Util;
9112var createPromiseCapability = sharedUtil.createPromiseCapability;
9113var error = sharedUtil.error;
9114var deprecated = sharedUtil.deprecated;
9115var getVerbosityLevel = sharedUtil.getVerbosityLevel;
9116var info = sharedUtil.info;
9117var isInt = sharedUtil.isInt;
9118var isArray = sharedUtil.isArray;
9119var isArrayBuffer = sharedUtil.isArrayBuffer;
9120var isSameOrigin = sharedUtil.isSameOrigin;
9121var loadJpegStream = sharedUtil.loadJpegStream;
9122var stringToBytes = sharedUtil.stringToBytes;
9123var globalScope = sharedUtil.globalScope;
9124var warn = sharedUtil.warn;
9125var FontFaceObject = displayFontLoader.FontFaceObject;
9126var FontLoader = displayFontLoader.FontLoader;
9127var CanvasGraphics = displayCanvas.CanvasGraphics;
9128var createScratchCanvas = displayCanvas.createScratchCanvas;
9129var Metadata = displayMetadata.Metadata;
9130var getDefaultSetting = displayDOMUtils.getDefaultSetting;
9131
9132var DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536
9133
9134var isWorkerDisabled = false;
9135var workerSrc;
9136var isPostMessageTransfersDisabled = false;
9137
9138
9139var useRequireEnsure = false;
9140if (typeof window === 'undefined') {
9141 // node.js - disable worker and set require.ensure.
9142 isWorkerDisabled = true;
9143 if (typeof require.ensure === 'undefined') {
9144 require.ensure = require('node-ensure');
9145 }
9146 useRequireEnsure = true;
9147}
9148if (typeof __webpack_require__ !== 'undefined') {
9149 useRequireEnsure = true;
9150}
9151if (typeof requirejs !== 'undefined' && requirejs.toUrl) {
9152 workerSrc = requirejs.toUrl('pdfjs-dist/build/pdf.worker.js');
9153}
9154var dynamicLoaderSupported = typeof requirejs !== 'undefined' && requirejs.load;
9155var fakeWorkerFilesLoader = useRequireEnsure ? (function (callback) {
9156 require.ensure([], function () {
9157 var worker = require('./pdf.worker.js');
9158 callback(worker.WorkerMessageHandler);
9159 });
9160}) : dynamicLoaderSupported ? (function (callback) {
9161 requirejs(['pdfjs-dist/build/pdf.worker'], function (worker) {
9162 callback(worker.WorkerMessageHandler);
9163 });
9164}) : null;
9165
9166
9167/**
9168 * Document initialization / loading parameters object.
9169 *
9170 * @typedef {Object} DocumentInitParameters
9171 * @property {string} url - The URL of the PDF.
9172 * @property {TypedArray|Array|string} data - Binary PDF data. Use typed arrays
9173 * (Uint8Array) to improve the memory usage. If PDF data is BASE64-encoded,
9174 * use atob() to convert it to a binary string first.
9175 * @property {Object} httpHeaders - Basic authentication headers.
9176 * @property {boolean} withCredentials - Indicates whether or not cross-site
9177 * Access-Control requests should be made using credentials such as cookies
9178 * or authorization headers. The default is false.
9179 * @property {string} password - For decrypting password-protected PDFs.
9180 * @property {TypedArray} initialData - A typed array with the first portion or
9181 * all of the pdf data. Used by the extension since some data is already
9182 * loaded before the switch to range requests.
9183 * @property {number} length - The PDF file length. It's used for progress
9184 * reports and range requests operations.
9185 * @property {PDFDataRangeTransport} range
9186 * @property {number} rangeChunkSize - Optional parameter to specify
9187 * maximum number of bytes fetched per range request. The default value is
9188 * 2^16 = 65536.
9189 * @property {PDFWorker} worker - The worker that will be used for the loading
9190 * and parsing of the PDF data.
9191 */
9192
9193/**
9194 * @typedef {Object} PDFDocumentStats
9195 * @property {Array} streamTypes - Used stream types in the document (an item
9196 * is set to true if specific stream ID was used in the document).
9197 * @property {Array} fontTypes - Used font type in the document (an item is set
9198 * to true if specific font ID was used in the document).
9199 */
9200
9201/**
9202 * This is the main entry point for loading a PDF and interacting with it.
9203 * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
9204 * is used, which means it must follow the same origin rules that any XHR does
9205 * e.g. No cross domain requests without CORS.
9206 *
9207 * @param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src
9208 * Can be a url to where a PDF is located, a typed array (Uint8Array)
9209 * already populated with data or parameter object.
9210 *
9211 * @param {PDFDataRangeTransport} pdfDataRangeTransport (deprecated) It is used
9212 * if you want to manually serve range requests for data in the PDF.
9213 *
9214 * @param {function} passwordCallback (deprecated) It is used to request a
9215 * password if wrong or no password was provided. The callback receives two
9216 * parameters: function that needs to be called with new password and reason
9217 * (see {PasswordResponses}).
9218 *
9219 * @param {function} progressCallback (deprecated) It is used to be able to
9220 * monitor the loading progress of the PDF file (necessary to implement e.g.
9221 * a loading bar). The callback receives an {Object} with the properties:
9222 * {number} loaded and {number} total.
9223 *
9224 * @return {PDFDocumentLoadingTask}
9225 */
9226function getDocument(src, pdfDataRangeTransport,
9227 passwordCallback, progressCallback) {
9228 var task = new PDFDocumentLoadingTask();
9229
9230 // Support of the obsolete arguments (for compatibility with API v1.0)
9231 if (arguments.length > 1) {
9232 deprecated('getDocument is called with pdfDataRangeTransport, ' +
9233 'passwordCallback or progressCallback argument');
9234 }
9235 if (pdfDataRangeTransport) {
9236 if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) {
9237 // Not a PDFDataRangeTransport instance, trying to add missing properties.
9238 pdfDataRangeTransport = Object.create(pdfDataRangeTransport);
9239 pdfDataRangeTransport.length = src.length;
9240 pdfDataRangeTransport.initialData = src.initialData;
9241 if (!pdfDataRangeTransport.abort) {
9242 pdfDataRangeTransport.abort = function () {};
9243 }
9244 }
9245 src = Object.create(src);
9246 src.range = pdfDataRangeTransport;
9247 }
9248 task.onPassword = passwordCallback || null;
9249 task.onProgress = progressCallback || null;
9250
9251 var source;
9252 if (typeof src === 'string') {
9253 source = { url: src };
9254 } else if (isArrayBuffer(src)) {
9255 source = { data: src };
9256 } else if (src instanceof PDFDataRangeTransport) {
9257 source = { range: src };
9258 } else {
9259 if (typeof src !== 'object') {
9260 error('Invalid parameter in getDocument, need either Uint8Array, ' +
9261 'string or a parameter object');
9262 }
9263 if (!src.url && !src.data && !src.range) {
9264 error('Invalid parameter object: need either .data, .range or .url');
9265 }
9266
9267 source = src;
9268 }
9269
9270 var params = {};
9271 var rangeTransport = null;
9272 var worker = null;
9273 for (var key in source) {
9274 if (key === 'url' && typeof window !== 'undefined') {
9275 // The full path is required in the 'url' field.
9276 params[key] = new URL(source[key], window.location).href;
9277 continue;
9278 } else if (key === 'range') {
9279 rangeTransport = source[key];
9280 continue;
9281 } else if (key === 'worker') {
9282 worker = source[key];
9283 continue;
9284 } else if (key === 'data' && !(source[key] instanceof Uint8Array)) {
9285 // Converting string or array-like data to Uint8Array.
9286 var pdfBytes = source[key];
9287 if (typeof pdfBytes === 'string') {
9288 params[key] = stringToBytes(pdfBytes);
9289 } else if (typeof pdfBytes === 'object' && pdfBytes !== null &&
9290 !isNaN(pdfBytes.length)) {
9291 params[key] = new Uint8Array(pdfBytes);
9292 } else if (isArrayBuffer(pdfBytes)) {
9293 params[key] = new Uint8Array(pdfBytes);
9294 } else {
9295 error('Invalid PDF binary data: either typed array, string or ' +
9296 'array-like object is expected in the data property.');
9297 }
9298 continue;
9299 }
9300 params[key] = source[key];
9301 }
9302
9303 params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
9304
9305 if (!worker) {
9306 // Worker was not provided -- creating and owning our own.
9307 worker = new PDFWorker();
9308 task._worker = worker;
9309 }
9310 var docId = task.docId;
9311 worker.promise.then(function () {
9312 if (task.destroyed) {
9313 throw new Error('Loading aborted');
9314 }
9315 return _fetchDocument(worker, params, rangeTransport, docId).then(
9316 function (workerId) {
9317 if (task.destroyed) {
9318 throw new Error('Loading aborted');
9319 }
9320 var messageHandler = new MessageHandler(docId, workerId, worker.port);
9321 var transport = new WorkerTransport(messageHandler, task, rangeTransport);
9322 task._transport = transport;
9323 messageHandler.send('Ready', null);
9324 });
9325 }).catch(task._capability.reject);
9326
9327 return task;
9328}
9329
9330/**
9331 * Starts fetching of specified PDF document/data.
9332 * @param {PDFWorker} worker
9333 * @param {Object} source
9334 * @param {PDFDataRangeTransport} pdfDataRangeTransport
9335 * @param {string} docId Unique document id, used as MessageHandler id.
9336 * @returns {Promise} The promise, which is resolved when worker id of
9337 * MessageHandler is known.
9338 * @private
9339 */
9340function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
9341 if (worker.destroyed) {
9342 return Promise.reject(new Error('Worker was destroyed'));
9343 }
9344
9345 source.disableAutoFetch = getDefaultSetting('disableAutoFetch');
9346 source.disableStream = getDefaultSetting('disableStream');
9347 source.chunkedViewerLoading = !!pdfDataRangeTransport;
9348 if (pdfDataRangeTransport) {
9349 source.length = pdfDataRangeTransport.length;
9350 source.initialData = pdfDataRangeTransport.initialData;
9351 }
9352 return worker.messageHandler.sendWithPromise('GetDocRequest', {
9353 docId: docId,
9354 source: source,
9355 disableRange: getDefaultSetting('disableRange'),
9356 maxImageSize: getDefaultSetting('maxImageSize'),
9357 cMapUrl: getDefaultSetting('cMapUrl'),
9358 cMapPacked: getDefaultSetting('cMapPacked'),
9359 disableFontFace: getDefaultSetting('disableFontFace'),
9360 disableCreateObjectURL: getDefaultSetting('disableCreateObjectURL'),
9361 postMessageTransfers: getDefaultSetting('postMessageTransfers') &&
9362 !isPostMessageTransfersDisabled,
9363 }).then(function (workerId) {
9364 if (worker.destroyed) {
9365 throw new Error('Worker was destroyed');
9366 }
9367 return workerId;
9368 });
9369}
9370
9371/**
9372 * PDF document loading operation.
9373 * @class
9374 * @alias PDFDocumentLoadingTask
9375 */
9376var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
9377 var nextDocumentId = 0;
9378
9379 /** @constructs PDFDocumentLoadingTask */
9380 function PDFDocumentLoadingTask() {
9381 this._capability = createPromiseCapability();
9382 this._transport = null;
9383 this._worker = null;
9384
9385 /**
9386 * Unique document loading task id -- used in MessageHandlers.
9387 * @type {string}
9388 */
9389 this.docId = 'd' + (nextDocumentId++);
9390
9391 /**
9392 * Shows if loading task is destroyed.
9393 * @type {boolean}
9394 */
9395 this.destroyed = false;
9396
9397 /**
9398 * Callback to request a password if wrong or no password was provided.
9399 * The callback receives two parameters: function that needs to be called
9400 * with new password and reason (see {PasswordResponses}).
9401 */
9402 this.onPassword = null;
9403
9404 /**
9405 * Callback to be able to monitor the loading progress of the PDF file
9406 * (necessary to implement e.g. a loading bar). The callback receives
9407 * an {Object} with the properties: {number} loaded and {number} total.
9408 */
9409 this.onProgress = null;
9410
9411 /**
9412 * Callback to when unsupported feature is used. The callback receives
9413 * an {UNSUPPORTED_FEATURES} argument.
9414 */
9415 this.onUnsupportedFeature = null;
9416 }
9417
9418 PDFDocumentLoadingTask.prototype =
9419 /** @lends PDFDocumentLoadingTask.prototype */ {
9420 /**
9421 * @return {Promise}
9422 */
9423 get promise() {
9424 return this._capability.promise;
9425 },
9426
9427 /**
9428 * Aborts all network requests and destroys worker.
9429 * @return {Promise} A promise that is resolved after destruction activity
9430 * is completed.
9431 */
9432 destroy: function () {
9433 this.destroyed = true;
9434
9435 var transportDestroyed = !this._transport ? Promise.resolve() :
9436 this._transport.destroy();
9437 return transportDestroyed.then(function () {
9438 this._transport = null;
9439 if (this._worker) {
9440 this._worker.destroy();
9441 this._worker = null;
9442 }
9443 }.bind(this));
9444 },
9445
9446 /**
9447 * Registers callbacks to indicate the document loading completion.
9448 *
9449 * @param {function} onFulfilled The callback for the loading completion.
9450 * @param {function} onRejected The callback for the loading failure.
9451 * @return {Promise} A promise that is resolved after the onFulfilled or
9452 * onRejected callback.
9453 */
9454 then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) {
9455 return this.promise.then.apply(this.promise, arguments);
9456 }
9457 };
9458
9459 return PDFDocumentLoadingTask;
9460})();
9461
9462/**
9463 * Abstract class to support range requests file loading.
9464 * @class
9465 * @alias PDFDataRangeTransport
9466 * @param {number} length
9467 * @param {Uint8Array} initialData
9468 */
9469var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() {
9470 function PDFDataRangeTransport(length, initialData) {
9471 this.length = length;
9472 this.initialData = initialData;
9473
9474 this._rangeListeners = [];
9475 this._progressListeners = [];
9476 this._progressiveReadListeners = [];
9477 this._readyCapability = createPromiseCapability();
9478 }
9479 PDFDataRangeTransport.prototype =
9480 /** @lends PDFDataRangeTransport.prototype */ {
9481 addRangeListener:
9482 function PDFDataRangeTransport_addRangeListener(listener) {
9483 this._rangeListeners.push(listener);
9484 },
9485
9486 addProgressListener:
9487 function PDFDataRangeTransport_addProgressListener(listener) {
9488 this._progressListeners.push(listener);
9489 },
9490
9491 addProgressiveReadListener:
9492 function PDFDataRangeTransport_addProgressiveReadListener(listener) {
9493 this._progressiveReadListeners.push(listener);
9494 },
9495
9496 onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) {
9497 var listeners = this._rangeListeners;
9498 for (var i = 0, n = listeners.length; i < n; ++i) {
9499 listeners[i](begin, chunk);
9500 }
9501 },
9502
9503 onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) {
9504 this._readyCapability.promise.then(function () {
9505 var listeners = this._progressListeners;
9506 for (var i = 0, n = listeners.length; i < n; ++i) {
9507 listeners[i](loaded);
9508 }
9509 }.bind(this));
9510 },
9511
9512 onDataProgressiveRead:
9513 function PDFDataRangeTransport_onDataProgress(chunk) {
9514 this._readyCapability.promise.then(function () {
9515 var listeners = this._progressiveReadListeners;
9516 for (var i = 0, n = listeners.length; i < n; ++i) {
9517 listeners[i](chunk);
9518 }
9519 }.bind(this));
9520 },
9521
9522 transportReady: function PDFDataRangeTransport_transportReady() {
9523 this._readyCapability.resolve();
9524 },
9525
9526 requestDataRange:
9527 function PDFDataRangeTransport_requestDataRange(begin, end) {
9528 throw new Error('Abstract method PDFDataRangeTransport.requestDataRange');
9529 },
9530
9531 abort: function PDFDataRangeTransport_abort() {
9532 }
9533 };
9534 return PDFDataRangeTransport;
9535})();
9536
9537/**
9538 * Proxy to a PDFDocument in the worker thread. Also, contains commonly used
9539 * properties that can be read synchronously.
9540 * @class
9541 * @alias PDFDocumentProxy
9542 */
9543var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
9544 function PDFDocumentProxy(pdfInfo, transport, loadingTask) {
9545 this.pdfInfo = pdfInfo;
9546 this.transport = transport;
9547 this.loadingTask = loadingTask;
9548 }
9549 PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ {
9550 /**
9551 * @return {number} Total number of pages the PDF contains.
9552 */
9553 get numPages() {
9554 return this.pdfInfo.numPages;
9555 },
9556 /**
9557 * @return {string} A unique ID to identify a PDF. Not guaranteed to be
9558 * unique.
9559 */
9560 get fingerprint() {
9561 return this.pdfInfo.fingerprint;
9562 },
9563 /**
9564 * @param {number} pageNumber The page number to get. The first page is 1.
9565 * @return {Promise} A promise that is resolved with a {@link PDFPageProxy}
9566 * object.
9567 */
9568 getPage: function PDFDocumentProxy_getPage(pageNumber) {
9569 return this.transport.getPage(pageNumber);
9570 },
9571 /**
9572 * @param {{num: number, gen: number}} ref The page reference. Must have
9573 * the 'num' and 'gen' properties.
9574 * @return {Promise} A promise that is resolved with the page index that is
9575 * associated with the reference.
9576 */
9577 getPageIndex: function PDFDocumentProxy_getPageIndex(ref) {
9578 return this.transport.getPageIndex(ref);
9579 },
9580 /**
9581 * @return {Promise} A promise that is resolved with a lookup table for
9582 * mapping named destinations to reference numbers.
9583 *
9584 * This can be slow for large documents: use getDestination instead
9585 */
9586 getDestinations: function PDFDocumentProxy_getDestinations() {
9587 return this.transport.getDestinations();
9588 },
9589 /**
9590 * @param {string} id The named destination to get.
9591 * @return {Promise} A promise that is resolved with all information
9592 * of the given named destination.
9593 */
9594 getDestination: function PDFDocumentProxy_getDestination(id) {
9595 return this.transport.getDestination(id);
9596 },
9597 /**
9598 * @return {Promise} A promise that is resolved with:
9599 * an Array containing the pageLabels that correspond to the pageIndexes,
9600 * or `null` when no pageLabels are present in the PDF file.
9601 */
9602 getPageLabels: function PDFDocumentProxy_getPageLabels() {
9603 return this.transport.getPageLabels();
9604 },
9605 /**
9606 * @return {Promise} A promise that is resolved with a lookup table for
9607 * mapping named attachments to their content.
9608 */
9609 getAttachments: function PDFDocumentProxy_getAttachments() {
9610 return this.transport.getAttachments();
9611 },
9612 /**
9613 * @return {Promise} A promise that is resolved with an array of all the
9614 * JavaScript strings in the name tree.
9615 */
9616 getJavaScript: function PDFDocumentProxy_getJavaScript() {
9617 return this.transport.getJavaScript();
9618 },
9619 /**
9620 * @return {Promise} A promise that is resolved with an {Array} that is a
9621 * tree outline (if it has one) of the PDF. The tree is in the format of:
9622 * [
9623 * {
9624 * title: string,
9625 * bold: boolean,
9626 * italic: boolean,
9627 * color: rgb Uint8Array,
9628 * dest: dest obj,
9629 * url: string,
9630 * items: array of more items like this
9631 * },
9632 * ...
9633 * ].
9634 */
9635 getOutline: function PDFDocumentProxy_getOutline() {
9636 return this.transport.getOutline();
9637 },
9638 /**
9639 * @return {Promise} A promise that is resolved with an {Object} that has
9640 * info and metadata properties. Info is an {Object} filled with anything
9641 * available in the information dictionary and similarly metadata is a
9642 * {Metadata} object with information from the metadata section of the PDF.
9643 */
9644 getMetadata: function PDFDocumentProxy_getMetadata() {
9645 return this.transport.getMetadata();
9646 },
9647 /**
9648 * @return {Promise} A promise that is resolved with a TypedArray that has
9649 * the raw data from the PDF.
9650 */
9651 getData: function PDFDocumentProxy_getData() {
9652 return this.transport.getData();
9653 },
9654 /**
9655 * @return {Promise} A promise that is resolved when the document's data
9656 * is loaded. It is resolved with an {Object} that contains the length
9657 * property that indicates size of the PDF data in bytes.
9658 */
9659 getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() {
9660 return this.transport.downloadInfoCapability.promise;
9661 },
9662 /**
9663 * @return {Promise} A promise this is resolved with current stats about
9664 * document structures (see {@link PDFDocumentStats}).
9665 */
9666 getStats: function PDFDocumentProxy_getStats() {
9667 return this.transport.getStats();
9668 },
9669 /**
9670 * Cleans up resources allocated by the document, e.g. created @font-face.
9671 */
9672 cleanup: function PDFDocumentProxy_cleanup() {
9673 this.transport.startCleanup();
9674 },
9675 /**
9676 * Destroys current document instance and terminates worker.
9677 */
9678 destroy: function PDFDocumentProxy_destroy() {
9679 return this.loadingTask.destroy();
9680 }
9681 };
9682 return PDFDocumentProxy;
9683})();
9684
9685/**
9686 * Page getTextContent parameters.
9687 *
9688 * @typedef {Object} getTextContentParameters
9689 * @param {boolean} normalizeWhitespace - replaces all occurrences of
9690 * whitespace with standard spaces (0x20). The default value is `false`.
9691 * @param {boolean} disableCombineTextItems - do not attempt to combine
9692 * same line {@link TextItem}'s. The default value is `false`.
9693 */
9694
9695/**
9696 * Page text content.
9697 *
9698 * @typedef {Object} TextContent
9699 * @property {array} items - array of {@link TextItem}
9700 * @property {Object} styles - {@link TextStyles} objects, indexed by font
9701 * name.
9702 */
9703
9704/**
9705 * Page text content part.
9706 *
9707 * @typedef {Object} TextItem
9708 * @property {string} str - text content.
9709 * @property {string} dir - text direction: 'ttb', 'ltr' or 'rtl'.
9710 * @property {array} transform - transformation matrix.
9711 * @property {number} width - width in device space.
9712 * @property {number} height - height in device space.
9713 * @property {string} fontName - font name used by pdf.js for converted font.
9714 */
9715
9716/**
9717 * Text style.
9718 *
9719 * @typedef {Object} TextStyle
9720 * @property {number} ascent - font ascent.
9721 * @property {number} descent - font descent.
9722 * @property {boolean} vertical - text is in vertical mode.
9723 * @property {string} fontFamily - possible font family
9724 */
9725
9726/**
9727 * Page annotation parameters.
9728 *
9729 * @typedef {Object} GetAnnotationsParameters
9730 * @param {string} intent - Determines the annotations that will be fetched,
9731 * can be either 'display' (viewable annotations) or 'print'
9732 * (printable annotations).
9733 * If the parameter is omitted, all annotations are fetched.
9734 */
9735
9736/**
9737 * Page render parameters.
9738 *
9739 * @typedef {Object} RenderParameters
9740 * @property {Object} canvasContext - A 2D context of a DOM Canvas object.
9741 * @property {PageViewport} viewport - Rendering viewport obtained by
9742 * calling of PDFPage.getViewport method.
9743 * @property {string} intent - Rendering intent, can be 'display' or 'print'
9744 * (default value is 'display').
9745 * @property {boolean} renderInteractiveForms - (optional) Whether or not
9746 * interactive form elements are rendered in the display
9747 * layer. If so, we do not render them on canvas as well.
9748 * @property {Array} transform - (optional) Additional transform, applied
9749 * just before viewport transform.
9750 * @property {Object} imageLayer - (optional) An object that has beginLayout,
9751 * endLayout and appendImage functions.
9752 * @property {function} continueCallback - (deprecated) A function that will be
9753 * called each time the rendering is paused. To continue
9754 * rendering call the function that is the first argument
9755 * to the callback.
9756 */
9757
9758/**
9759 * PDF page operator list.
9760 *
9761 * @typedef {Object} PDFOperatorList
9762 * @property {Array} fnArray - Array containing the operator functions.
9763 * @property {Array} argsArray - Array containing the arguments of the
9764 * functions.
9765 */
9766
9767/**
9768 * Proxy to a PDFPage in the worker thread.
9769 * @class
9770 * @alias PDFPageProxy
9771 */
9772var PDFPageProxy = (function PDFPageProxyClosure() {
9773 function PDFPageProxy(pageIndex, pageInfo, transport) {
9774 this.pageIndex = pageIndex;
9775 this.pageInfo = pageInfo;
9776 this.transport = transport;
9777 this.stats = new StatTimer();
9778 this.stats.enabled = getDefaultSetting('enableStats');
9779 this.commonObjs = transport.commonObjs;
9780 this.objs = new PDFObjects();
9781 this.cleanupAfterRender = false;
9782 this.pendingCleanup = false;
9783 this.intentStates = Object.create(null);
9784 this.destroyed = false;
9785 }
9786 PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ {
9787 /**
9788 * @return {number} Page number of the page. First page is 1.
9789 */
9790 get pageNumber() {
9791 return this.pageIndex + 1;
9792 },
9793 /**
9794 * @return {number} The number of degrees the page is rotated clockwise.
9795 */
9796 get rotate() {
9797 return this.pageInfo.rotate;
9798 },
9799 /**
9800 * @return {Object} The reference that points to this page. It has 'num' and
9801 * 'gen' properties.
9802 */
9803 get ref() {
9804 return this.pageInfo.ref;
9805 },
9806 /**
9807 * @return {Array} An array of the visible portion of the PDF page in the
9808 * user space units - [x1, y1, x2, y2].
9809 */
9810 get view() {
9811 return this.pageInfo.view;
9812 },
9813 /**
9814 * @param {number} scale The desired scale of the viewport.
9815 * @param {number} rotate Degrees to rotate the viewport. If omitted this
9816 * defaults to the page rotation.
9817 * @return {PageViewport} Contains 'width' and 'height' properties
9818 * along with transforms required for rendering.
9819 */
9820 getViewport: function PDFPageProxy_getViewport(scale, rotate) {
9821 if (arguments.length < 2) {
9822 rotate = this.rotate;
9823 }
9824 return new PageViewport(this.view, scale, rotate, 0, 0);
9825 },
9826 /**
9827 * @param {GetAnnotationsParameters} params - Annotation parameters.
9828 * @return {Promise} A promise that is resolved with an {Array} of the
9829 * annotation objects.
9830 */
9831 getAnnotations: function PDFPageProxy_getAnnotations(params) {
9832 var intent = (params && params.intent) || null;
9833
9834 if (!this.annotationsPromise || this.annotationsIntent !== intent) {
9835 this.annotationsPromise = this.transport.getAnnotations(this.pageIndex,
9836 intent);
9837 this.annotationsIntent = intent;
9838 }
9839 return this.annotationsPromise;
9840 },
9841 /**
9842 * Begins the process of rendering a page to the desired context.
9843 * @param {RenderParameters} params Page render parameters.
9844 * @return {RenderTask} An object that contains the promise, which
9845 * is resolved when the page finishes rendering.
9846 */
9847 render: function PDFPageProxy_render(params) {
9848 var stats = this.stats;
9849 stats.time('Overall');
9850
9851 // If there was a pending destroy cancel it so no cleanup happens during
9852 // this call to render.
9853 this.pendingCleanup = false;
9854
9855 var renderingIntent = (params.intent === 'print' ? 'print' : 'display');
9856 var renderInteractiveForms = (params.renderInteractiveForms === true ?
9857 true : /* Default */ false);
9858
9859 if (!this.intentStates[renderingIntent]) {
9860 this.intentStates[renderingIntent] = Object.create(null);
9861 }
9862 var intentState = this.intentStates[renderingIntent];
9863
9864 // If there's no displayReadyCapability yet, then the operatorList
9865 // was never requested before. Make the request and create the promise.
9866 if (!intentState.displayReadyCapability) {
9867 intentState.receivingOperatorList = true;
9868 intentState.displayReadyCapability = createPromiseCapability();
9869 intentState.operatorList = {
9870 fnArray: [],
9871 argsArray: [],
9872 lastChunk: false
9873 };
9874
9875 this.stats.time('Page Request');
9876 this.transport.messageHandler.send('RenderPageRequest', {
9877 pageIndex: this.pageNumber - 1,
9878 intent: renderingIntent,
9879 renderInteractiveForms: renderInteractiveForms,
9880 });
9881 }
9882
9883 var internalRenderTask = new InternalRenderTask(complete, params,
9884 this.objs,
9885 this.commonObjs,
9886 intentState.operatorList,
9887 this.pageNumber);
9888 internalRenderTask.useRequestAnimationFrame = renderingIntent !== 'print';
9889 if (!intentState.renderTasks) {
9890 intentState.renderTasks = [];
9891 }
9892 intentState.renderTasks.push(internalRenderTask);
9893 var renderTask = internalRenderTask.task;
9894
9895 // Obsolete parameter support
9896 if (params.continueCallback) {
9897 deprecated('render is used with continueCallback parameter');
9898 renderTask.onContinue = params.continueCallback;
9899 }
9900
9901 var self = this;
9902 intentState.displayReadyCapability.promise.then(
9903 function pageDisplayReadyPromise(transparency) {
9904 if (self.pendingCleanup) {
9905 complete();
9906 return;
9907 }
9908 stats.time('Rendering');
9909 internalRenderTask.initializeGraphics(transparency);
9910 internalRenderTask.operatorListChanged();
9911 },
9912 function pageDisplayReadPromiseError(reason) {
9913 complete(reason);
9914 }
9915 );
9916
9917 function complete(error) {
9918 var i = intentState.renderTasks.indexOf(internalRenderTask);
9919 if (i >= 0) {
9920 intentState.renderTasks.splice(i, 1);
9921 }
9922
9923 if (self.cleanupAfterRender) {
9924 self.pendingCleanup = true;
9925 }
9926 self._tryCleanup();
9927
9928 if (error) {
9929 internalRenderTask.capability.reject(error);
9930 } else {
9931 internalRenderTask.capability.resolve();
9932 }
9933 stats.timeEnd('Rendering');
9934 stats.timeEnd('Overall');
9935 }
9936
9937 return renderTask;
9938 },
9939
9940 /**
9941 * @return {Promise} A promise resolved with an {@link PDFOperatorList}
9942 * object that represents page's operator list.
9943 */
9944 getOperatorList: function PDFPageProxy_getOperatorList() {
9945 function operatorListChanged() {
9946 if (intentState.operatorList.lastChunk) {
9947 intentState.opListReadCapability.resolve(intentState.operatorList);
9948
9949 var i = intentState.renderTasks.indexOf(opListTask);
9950 if (i >= 0) {
9951 intentState.renderTasks.splice(i, 1);
9952 }
9953 }
9954 }
9955
9956 var renderingIntent = 'oplist';
9957 if (!this.intentStates[renderingIntent]) {
9958 this.intentStates[renderingIntent] = Object.create(null);
9959 }
9960 var intentState = this.intentStates[renderingIntent];
9961 var opListTask;
9962
9963 if (!intentState.opListReadCapability) {
9964 opListTask = {};
9965 opListTask.operatorListChanged = operatorListChanged;
9966 intentState.receivingOperatorList = true;
9967 intentState.opListReadCapability = createPromiseCapability();
9968 intentState.renderTasks = [];
9969 intentState.renderTasks.push(opListTask);
9970 intentState.operatorList = {
9971 fnArray: [],
9972 argsArray: [],
9973 lastChunk: false
9974 };
9975
9976 this.transport.messageHandler.send('RenderPageRequest', {
9977 pageIndex: this.pageIndex,
9978 intent: renderingIntent
9979 });
9980 }
9981 return intentState.opListReadCapability.promise;
9982 },
9983
9984 /**
9985 * @param {getTextContentParameters} params - getTextContent parameters.
9986 * @return {Promise} That is resolved a {@link TextContent}
9987 * object that represent the page text content.
9988 */
9989 getTextContent: function PDFPageProxy_getTextContent(params) {
9990 return this.transport.messageHandler.sendWithPromise('GetTextContent', {
9991 pageIndex: this.pageNumber - 1,
9992 normalizeWhitespace: (params && params.normalizeWhitespace === true ?
9993 true : /* Default */ false),
9994 combineTextItems: (params && params.disableCombineTextItems === true ?
9995 false : /* Default */ true),
9996 });
9997 },
9998
9999 /**
10000 * Destroys page object.
10001 */
10002 _destroy: function PDFPageProxy_destroy() {
10003 this.destroyed = true;
10004 this.transport.pageCache[this.pageIndex] = null;
10005
10006 var waitOn = [];
10007 Object.keys(this.intentStates).forEach(function(intent) {
10008 if (intent === 'oplist') {
10009 // Avoid errors below, since the renderTasks are just stubs.
10010 return;
10011 }
10012 var intentState = this.intentStates[intent];
10013 intentState.renderTasks.forEach(function(renderTask) {
10014 var renderCompleted = renderTask.capability.promise.
10015 catch(function () {}); // ignoring failures
10016 waitOn.push(renderCompleted);
10017 renderTask.cancel();
10018 });
10019 }, this);
10020 this.objs.clear();
10021 this.annotationsPromise = null;
10022 this.pendingCleanup = false;
10023 return Promise.all(waitOn);
10024 },
10025
10026 /**
10027 * Cleans up resources allocated by the page. (deprecated)
10028 */
10029 destroy: function() {
10030 deprecated('page destroy method, use cleanup() instead');
10031 this.cleanup();
10032 },
10033
10034 /**
10035 * Cleans up resources allocated by the page.
10036 */
10037 cleanup: function PDFPageProxy_cleanup() {
10038 this.pendingCleanup = true;
10039 this._tryCleanup();
10040 },
10041 /**
10042 * For internal use only. Attempts to clean up if rendering is in a state
10043 * where that's possible.
10044 * @ignore
10045 */
10046 _tryCleanup: function PDFPageProxy_tryCleanup() {
10047 if (!this.pendingCleanup ||
10048 Object.keys(this.intentStates).some(function(intent) {
10049 var intentState = this.intentStates[intent];
10050 return (intentState.renderTasks.length !== 0 ||
10051 intentState.receivingOperatorList);
10052 }, this)) {
10053 return;
10054 }
10055
10056 Object.keys(this.intentStates).forEach(function(intent) {
10057 delete this.intentStates[intent];
10058 }, this);
10059 this.objs.clear();
10060 this.annotationsPromise = null;
10061 this.pendingCleanup = false;
10062 },
10063 /**
10064 * For internal use only.
10065 * @ignore
10066 */
10067 _startRenderPage: function PDFPageProxy_startRenderPage(transparency,
10068 intent) {
10069 var intentState = this.intentStates[intent];
10070 // TODO Refactor RenderPageRequest to separate rendering
10071 // and operator list logic
10072 if (intentState.displayReadyCapability) {
10073 intentState.displayReadyCapability.resolve(transparency);
10074 }
10075 },
10076 /**
10077 * For internal use only.
10078 * @ignore
10079 */
10080 _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk,
10081 intent) {
10082 var intentState = this.intentStates[intent];
10083 var i, ii;
10084 // Add the new chunk to the current operator list.
10085 for (i = 0, ii = operatorListChunk.length; i < ii; i++) {
10086 intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
10087 intentState.operatorList.argsArray.push(
10088 operatorListChunk.argsArray[i]);
10089 }
10090 intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
10091
10092 // Notify all the rendering tasks there are more operators to be consumed.
10093 for (i = 0; i < intentState.renderTasks.length; i++) {
10094 intentState.renderTasks[i].operatorListChanged();
10095 }
10096
10097 if (operatorListChunk.lastChunk) {
10098 intentState.receivingOperatorList = false;
10099 this._tryCleanup();
10100 }
10101 }
10102 };
10103 return PDFPageProxy;
10104})();
10105
10106/**
10107 * PDF.js web worker abstraction, it controls instantiation of PDF documents and
10108 * WorkerTransport for them. If creation of a web worker is not possible,
10109 * a "fake" worker will be used instead.
10110 * @class
10111 */
10112var PDFWorker = (function PDFWorkerClosure() {
10113 var nextFakeWorkerId = 0;
10114
10115 function getWorkerSrc() {
10116 if (typeof workerSrc !== 'undefined') {
10117 return workerSrc;
10118 }
10119 if (getDefaultSetting('workerSrc')) {
10120 return getDefaultSetting('workerSrc');
10121 }
10122 if (pdfjsFilePath) {
10123 return pdfjsFilePath.replace(/\.js$/i, '.worker.js');
10124 }
10125 error('No PDFJS.workerSrc specified');
10126 }
10127
10128 var fakeWorkerFilesLoadedCapability;
10129
10130 // Loads worker code into main thread.
10131 function setupFakeWorkerGlobal() {
10132 var WorkerMessageHandler;
10133 if (!fakeWorkerFilesLoadedCapability) {
10134 fakeWorkerFilesLoadedCapability = createPromiseCapability();
10135 // In the developer build load worker_loader which in turn loads all the
10136 // other files and resolves the promise. In production only the
10137 // pdf.worker.js file is needed.
10138 var loader = fakeWorkerFilesLoader || function (callback) {
10139 Util.loadScript(getWorkerSrc(), function () {
10140 callback(window.pdfjsDistBuildPdfWorker.WorkerMessageHandler);
10141 });
10142 };
10143 loader(fakeWorkerFilesLoadedCapability.resolve);
10144 }
10145 return fakeWorkerFilesLoadedCapability.promise;
10146 }
10147
10148 function FakeWorkerPort(defer) {
10149 this._listeners = [];
10150 this._defer = defer;
10151 this._deferred = Promise.resolve(undefined);
10152 }
10153 FakeWorkerPort.prototype = {
10154 postMessage: function (obj, transfers) {
10155 function cloneValue(value) {
10156 // Trying to perform a structured clone close to the spec, including
10157 // transfers.
10158 if (typeof value !== 'object' || value === null) {
10159 return value;
10160 }
10161 if (cloned.has(value)) { // already cloned the object
10162 return cloned.get(value);
10163 }
10164 var result;
10165 var buffer;
10166 if ((buffer = value.buffer) && isArrayBuffer(buffer)) {
10167 // We found object with ArrayBuffer (typed array).
10168 var transferable = transfers && transfers.indexOf(buffer) >= 0;
10169 if (value === buffer) {
10170 // Special case when we are faking typed arrays in compatibility.js.
10171 result = value;
10172 } else if (transferable) {
10173 result = new value.constructor(buffer, value.byteOffset,
10174 value.byteLength);
10175 } else {
10176 result = new value.constructor(value);
10177 }
10178 cloned.set(value, result);
10179 return result;
10180 }
10181 result = isArray(value) ? [] : {};
10182 cloned.set(value, result); // adding to cache now for cyclic references
10183 // Cloning all value and object properties, however ignoring properties
10184 // defined via getter.
10185 for (var i in value) {
10186 var desc, p = value;
10187 while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
10188 p = Object.getPrototypeOf(p);
10189 }
10190 if (typeof desc.value === 'undefined' ||
10191 typeof desc.value === 'function') {
10192 continue;
10193 }
10194 result[i] = cloneValue(desc.value);
10195 }
10196 return result;
10197 }
10198
10199 if (!this._defer) {
10200 this._listeners.forEach(function (listener) {
10201 listener.call(this, {data: obj});
10202 }, this);
10203 return;
10204 }
10205
10206 var cloned = new WeakMap();
10207 var e = {data: cloneValue(obj)};
10208 this._deferred.then(function () {
10209 this._listeners.forEach(function (listener) {
10210 listener.call(this, e);
10211 }, this);
10212 }.bind(this));
10213 },
10214 addEventListener: function (name, listener) {
10215 this._listeners.push(listener);
10216 },
10217 removeEventListener: function (name, listener) {
10218 var i = this._listeners.indexOf(listener);
10219 this._listeners.splice(i, 1);
10220 },
10221 terminate: function () {
10222 this._listeners = [];
10223 }
10224 };
10225
10226 function createCDNWrapper(url) {
10227 // We will rely on blob URL's property to specify origin.
10228 // We want this function to fail in case if createObjectURL or Blob do not
10229 // exist or fail for some reason -- our Worker creation will fail anyway.
10230 var wrapper = 'importScripts(\'' + url + '\');';
10231 return URL.createObjectURL(new Blob([wrapper]));
10232 }
10233
10234 function PDFWorker(name) {
10235 this.name = name;
10236 this.destroyed = false;
10237
10238 this._readyCapability = createPromiseCapability();
10239 this._port = null;
10240 this._webWorker = null;
10241 this._messageHandler = null;
10242 this._initialize();
10243 }
10244
10245 PDFWorker.prototype = /** @lends PDFWorker.prototype */ {
10246 get promise() {
10247 return this._readyCapability.promise;
10248 },
10249
10250 get port() {
10251 return this._port;
10252 },
10253
10254 get messageHandler() {
10255 return this._messageHandler;
10256 },
10257
10258 _initialize: function PDFWorker_initialize() {
10259 // If worker support isn't disabled explicit and the browser has worker
10260 // support, create a new web worker and test if it/the browser fulfills
10261 // all requirements to run parts of pdf.js in a web worker.
10262 // Right now, the requirement is, that an Uint8Array is still an
10263 // Uint8Array as it arrives on the worker. (Chrome added this with v.15.)
10264 if (!isWorkerDisabled && !getDefaultSetting('disableWorker') &&
10265 typeof Worker !== 'undefined') {
10266 var workerSrc = getWorkerSrc();
10267
10268 try {
10269 // Wraps workerSrc path into blob URL, if the former does not belong
10270 // to the same origin.
10271 if (!isSameOrigin(window.location.href, workerSrc)) {
10272 workerSrc = createCDNWrapper(
10273 new URL(workerSrc, window.location).href);
10274 }
10275 // Some versions of FF can't create a worker on localhost, see:
10276 // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
10277 var worker = new Worker(workerSrc);
10278 var messageHandler = new MessageHandler('main', 'worker', worker);
10279 var terminateEarly = function() {
10280 worker.removeEventListener('error', onWorkerError);
10281 messageHandler.destroy();
10282 worker.terminate();
10283 if (this.destroyed) {
10284 this._readyCapability.reject(new Error('Worker was destroyed'));
10285 } else {
10286 // Fall back to fake worker if the termination is caused by an
10287 // error (e.g. NetworkError / SecurityError).
10288 this._setupFakeWorker();
10289 }
10290 }.bind(this);
10291
10292 var onWorkerError = function(event) {
10293 if (!this._webWorker) {
10294 // Worker failed to initialize due to an error. Clean up and fall
10295 // back to the fake worker.
10296 terminateEarly();
10297 }
10298 }.bind(this);
10299 worker.addEventListener('error', onWorkerError);
10300
10301 messageHandler.on('test', function PDFWorker_test(data) {
10302 worker.removeEventListener('error', onWorkerError);
10303 if (this.destroyed) {
10304 terminateEarly();
10305 return; // worker was destroyed
10306 }
10307 var supportTypedArray = data && data.supportTypedArray;
10308 if (supportTypedArray) {
10309 this._messageHandler = messageHandler;
10310 this._port = worker;
10311 this._webWorker = worker;
10312 if (!data.supportTransfers) {
10313 isPostMessageTransfersDisabled = true;
10314 }
10315 this._readyCapability.resolve();
10316 // Send global setting, e.g. verbosity level.
10317 messageHandler.send('configure', {
10318 verbosity: getVerbosityLevel()
10319 });
10320 } else {
10321 this._setupFakeWorker();
10322 messageHandler.destroy();
10323 worker.terminate();
10324 }
10325 }.bind(this));
10326
10327 messageHandler.on('console_log', function (data) {
10328 console.log.apply(console, data);
10329 });
10330 messageHandler.on('console_error', function (data) {
10331 console.error.apply(console, data);
10332 });
10333
10334 messageHandler.on('ready', function (data) {
10335 worker.removeEventListener('error', onWorkerError);
10336 if (this.destroyed) {
10337 terminateEarly();
10338 return; // worker was destroyed
10339 }
10340 try {
10341 sendTest();
10342 } catch (e) {
10343 // We need fallback to a faked worker.
10344 this._setupFakeWorker();
10345 }
10346 }.bind(this));
10347
10348 var sendTest = function () {
10349 var postMessageTransfers =
10350 getDefaultSetting('postMessageTransfers') &&
10351 !isPostMessageTransfersDisabled;
10352 var testObj = new Uint8Array([postMessageTransfers ? 255 : 0]);
10353 // Some versions of Opera throw a DATA_CLONE_ERR on serializing the
10354 // typed array. Also, checking if we can use transfers.
10355 try {
10356 messageHandler.send('test', testObj, [testObj.buffer]);
10357 } catch (ex) {
10358 info('Cannot use postMessage transfers');
10359 testObj[0] = 0;
10360 messageHandler.send('test', testObj);
10361 }
10362 };
10363
10364 // It might take time for worker to initialize (especially when AMD
10365 // loader is used). We will try to send test immediately, and then
10366 // when 'ready' message will arrive. The worker shall process only
10367 // first received 'test'.
10368 sendTest();
10369 return;
10370 } catch (e) {
10371 info('The worker has been disabled.');
10372 }
10373 }
10374 // Either workers are disabled, not supported or have thrown an exception.
10375 // Thus, we fallback to a faked worker.
10376 this._setupFakeWorker();
10377 },
10378
10379 _setupFakeWorker: function PDFWorker_setupFakeWorker() {
10380 if (!isWorkerDisabled && !getDefaultSetting('disableWorker')) {
10381 warn('Setting up fake worker.');
10382 isWorkerDisabled = true;
10383 }
10384
10385 setupFakeWorkerGlobal().then(function (WorkerMessageHandler) {
10386 if (this.destroyed) {
10387 this._readyCapability.reject(new Error('Worker was destroyed'));
10388 return;
10389 }
10390
10391 // We cannot turn on proper fake port simulation (this includes
10392 // structured cloning) when typed arrays are not supported. Relying
10393 // on a chance that messages will be sent in proper order.
10394 var isTypedArraysPresent = Uint8Array !== Float32Array;
10395 var port = new FakeWorkerPort(isTypedArraysPresent);
10396 this._port = port;
10397
10398 // All fake workers use the same port, making id unique.
10399 var id = 'fake' + (nextFakeWorkerId++);
10400
10401 // If the main thread is our worker, setup the handling for the
10402 // messages -- the main thread sends to it self.
10403 var workerHandler = new MessageHandler(id + '_worker', id, port);
10404 WorkerMessageHandler.setup(workerHandler, port);
10405
10406 var messageHandler = new MessageHandler(id, id + '_worker', port);
10407 this._messageHandler = messageHandler;
10408 this._readyCapability.resolve();
10409 }.bind(this));
10410 },
10411
10412 /**
10413 * Destroys the worker instance.
10414 */
10415 destroy: function PDFWorker_destroy() {
10416 this.destroyed = true;
10417 if (this._webWorker) {
10418 // We need to terminate only web worker created resource.
10419 this._webWorker.terminate();
10420 this._webWorker = null;
10421 }
10422 this._port = null;
10423 if (this._messageHandler) {
10424 this._messageHandler.destroy();
10425 this._messageHandler = null;
10426 }
10427 }
10428 };
10429
10430 return PDFWorker;
10431})();
10432
10433/**
10434 * For internal use only.
10435 * @ignore
10436 */
10437var WorkerTransport = (function WorkerTransportClosure() {
10438 function WorkerTransport(messageHandler, loadingTask, pdfDataRangeTransport) {
10439 this.messageHandler = messageHandler;
10440 this.loadingTask = loadingTask;
10441 this.pdfDataRangeTransport = pdfDataRangeTransport;
10442 this.commonObjs = new PDFObjects();
10443 this.fontLoader = new FontLoader(loadingTask.docId);
10444
10445 this.destroyed = false;
10446 this.destroyCapability = null;
10447
10448 this.pageCache = [];
10449 this.pagePromises = [];
10450 this.downloadInfoCapability = createPromiseCapability();
10451
10452 this.setupMessageHandler();
10453 }
10454 WorkerTransport.prototype = {
10455 destroy: function WorkerTransport_destroy() {
10456 if (this.destroyCapability) {
10457 return this.destroyCapability.promise;
10458 }
10459
10460 this.destroyed = true;
10461 this.destroyCapability = createPromiseCapability();
10462
10463 var waitOn = [];
10464 // We need to wait for all renderings to be completed, e.g.
10465 // timeout/rAF can take a long time.
10466 this.pageCache.forEach(function (page) {
10467 if (page) {
10468 waitOn.push(page._destroy());
10469 }
10470 });
10471 this.pageCache = [];
10472 this.pagePromises = [];
10473 var self = this;
10474 // We also need to wait for the worker to finish its long running tasks.
10475 var terminated = this.messageHandler.sendWithPromise('Terminate', null);
10476 waitOn.push(terminated);
10477 Promise.all(waitOn).then(function () {
10478 self.fontLoader.clear();
10479 if (self.pdfDataRangeTransport) {
10480 self.pdfDataRangeTransport.abort();
10481 self.pdfDataRangeTransport = null;
10482 }
10483 if (self.messageHandler) {
10484 self.messageHandler.destroy();
10485 self.messageHandler = null;
10486 }
10487 self.destroyCapability.resolve();
10488 }, this.destroyCapability.reject);
10489 return this.destroyCapability.promise;
10490 },
10491
10492 setupMessageHandler:
10493 function WorkerTransport_setupMessageHandler() {
10494 var messageHandler = this.messageHandler;
10495
10496 function updatePassword(password) {
10497 messageHandler.send('UpdatePassword', password);
10498 }
10499
10500 var pdfDataRangeTransport = this.pdfDataRangeTransport;
10501 if (pdfDataRangeTransport) {
10502 pdfDataRangeTransport.addRangeListener(function(begin, chunk) {
10503 messageHandler.send('OnDataRange', {
10504 begin: begin,
10505 chunk: chunk
10506 });
10507 });
10508
10509 pdfDataRangeTransport.addProgressListener(function(loaded) {
10510 messageHandler.send('OnDataProgress', {
10511 loaded: loaded
10512 });
10513 });
10514
10515 pdfDataRangeTransport.addProgressiveReadListener(function(chunk) {
10516 messageHandler.send('OnDataRange', {
10517 chunk: chunk
10518 });
10519 });
10520
10521 messageHandler.on('RequestDataRange',
10522 function transportDataRange(data) {
10523 pdfDataRangeTransport.requestDataRange(data.begin, data.end);
10524 }, this);
10525 }
10526
10527 messageHandler.on('GetDoc', function transportDoc(data) {
10528 var pdfInfo = data.pdfInfo;
10529 this.numPages = data.pdfInfo.numPages;
10530 var loadingTask = this.loadingTask;
10531 var pdfDocument = new PDFDocumentProxy(pdfInfo, this, loadingTask);
10532 this.pdfDocument = pdfDocument;
10533 loadingTask._capability.resolve(pdfDocument);
10534 }, this);
10535
10536 messageHandler.on('NeedPassword',
10537 function transportNeedPassword(exception) {
10538 var loadingTask = this.loadingTask;
10539 if (loadingTask.onPassword) {
10540 return loadingTask.onPassword(updatePassword,
10541 PasswordResponses.NEED_PASSWORD);
10542 }
10543 loadingTask._capability.reject(
10544 new PasswordException(exception.message, exception.code));
10545 }, this);
10546
10547 messageHandler.on('IncorrectPassword',
10548 function transportIncorrectPassword(exception) {
10549 var loadingTask = this.loadingTask;
10550 if (loadingTask.onPassword) {
10551 return loadingTask.onPassword(updatePassword,
10552 PasswordResponses.INCORRECT_PASSWORD);
10553 }
10554 loadingTask._capability.reject(
10555 new PasswordException(exception.message, exception.code));
10556 }, this);
10557
10558 messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) {
10559 this.loadingTask._capability.reject(
10560 new InvalidPDFException(exception.message));
10561 }, this);
10562
10563 messageHandler.on('MissingPDF', function transportMissingPDF(exception) {
10564 this.loadingTask._capability.reject(
10565 new MissingPDFException(exception.message));
10566 }, this);
10567
10568 messageHandler.on('UnexpectedResponse',
10569 function transportUnexpectedResponse(exception) {
10570 this.loadingTask._capability.reject(
10571 new UnexpectedResponseException(exception.message, exception.status));
10572 }, this);
10573
10574 messageHandler.on('UnknownError',
10575 function transportUnknownError(exception) {
10576 this.loadingTask._capability.reject(
10577 new UnknownErrorException(exception.message, exception.details));
10578 }, this);
10579
10580 messageHandler.on('DataLoaded', function transportPage(data) {
10581 this.downloadInfoCapability.resolve(data);
10582 }, this);
10583
10584 messageHandler.on('PDFManagerReady', function transportPage(data) {
10585 if (this.pdfDataRangeTransport) {
10586 this.pdfDataRangeTransport.transportReady();
10587 }
10588 }, this);
10589
10590 messageHandler.on('StartRenderPage', function transportRender(data) {
10591 if (this.destroyed) {
10592 return; // Ignore any pending requests if the worker was terminated.
10593 }
10594 var page = this.pageCache[data.pageIndex];
10595
10596 page.stats.timeEnd('Page Request');
10597 page._startRenderPage(data.transparency, data.intent);
10598 }, this);
10599
10600 messageHandler.on('RenderPageChunk', function transportRender(data) {
10601 if (this.destroyed) {
10602 return; // Ignore any pending requests if the worker was terminated.
10603 }
10604 var page = this.pageCache[data.pageIndex];
10605
10606 page._renderPageChunk(data.operatorList, data.intent);
10607 }, this);
10608
10609 messageHandler.on('commonobj', function transportObj(data) {
10610 if (this.destroyed) {
10611 return; // Ignore any pending requests if the worker was terminated.
10612 }
10613
10614 var id = data[0];
10615 var type = data[1];
10616 if (this.commonObjs.hasData(id)) {
10617 return;
10618 }
10619
10620 switch (type) {
10621 case 'Font':
10622 var exportedData = data[2];
10623
10624 if ('error' in exportedData) {
10625 var exportedError = exportedData.error;
10626 warn('Error during font loading: ' + exportedError);
10627 this.commonObjs.resolve(id, exportedError);
10628 break;
10629 }
10630 var fontRegistry = null;
10631 if (getDefaultSetting('pdfBug') && globalScope.FontInspector &&
10632 globalScope['FontInspector'].enabled) {
10633 fontRegistry = {
10634 registerFont: function (font, url) {
10635 globalScope['FontInspector'].fontAdded(font, url);
10636 }
10637 };
10638 }
10639 var font = new FontFaceObject(exportedData, {
10640 isEvalSuported: getDefaultSetting('isEvalSupported'),
10641 disableFontFace: getDefaultSetting('disableFontFace'),
10642 fontRegistry: fontRegistry
10643 });
10644
10645 this.fontLoader.bind(
10646 [font],
10647 function fontReady(fontObjs) {
10648 this.commonObjs.resolve(id, font);
10649 }.bind(this)
10650 );
10651 break;
10652 case 'FontPath':
10653 this.commonObjs.resolve(id, data[2]);
10654 break;
10655 default:
10656 error('Got unknown common object type ' + type);
10657 }
10658 }, this);
10659
10660 messageHandler.on('obj', function transportObj(data) {
10661 if (this.destroyed) {
10662 return; // Ignore any pending requests if the worker was terminated.
10663 }
10664
10665 var id = data[0];
10666 var pageIndex = data[1];
10667 var type = data[2];
10668 var pageProxy = this.pageCache[pageIndex];
10669 var imageData;
10670 if (pageProxy.objs.hasData(id)) {
10671 return;
10672 }
10673
10674 switch (type) {
10675 case 'JpegStream':
10676 imageData = data[3];
10677 loadJpegStream(id, imageData, pageProxy.objs);
10678 break;
10679 case 'Image':
10680 imageData = data[3];
10681 pageProxy.objs.resolve(id, imageData);
10682
10683 // heuristics that will allow not to store large data
10684 var MAX_IMAGE_SIZE_TO_STORE = 8000000;
10685 if (imageData && 'data' in imageData &&
10686 imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
10687 pageProxy.cleanupAfterRender = true;
10688 }
10689 break;
10690 default:
10691 error('Got unknown object type ' + type);
10692 }
10693 }, this);
10694
10695 messageHandler.on('DocProgress', function transportDocProgress(data) {
10696 if (this.destroyed) {
10697 return; // Ignore any pending requests if the worker was terminated.
10698 }
10699
10700 var loadingTask = this.loadingTask;
10701 if (loadingTask.onProgress) {
10702 loadingTask.onProgress({
10703 loaded: data.loaded,
10704 total: data.total
10705 });
10706 }
10707 }, this);
10708
10709 messageHandler.on('PageError', function transportError(data) {
10710 if (this.destroyed) {
10711 return; // Ignore any pending requests if the worker was terminated.
10712 }
10713
10714 var page = this.pageCache[data.pageNum - 1];
10715 var intentState = page.intentStates[data.intent];
10716
10717 if (intentState.displayReadyCapability) {
10718 intentState.displayReadyCapability.reject(data.error);
10719 } else {
10720 error(data.error);
10721 }
10722
10723 if (intentState.operatorList) {
10724 // Mark operator list as complete.
10725 intentState.operatorList.lastChunk = true;
10726 for (var i = 0; i < intentState.renderTasks.length; i++) {
10727 intentState.renderTasks[i].operatorListChanged();
10728 }
10729 }
10730 }, this);
10731
10732 messageHandler.on('UnsupportedFeature',
10733 function transportUnsupportedFeature(data) {
10734 if (this.destroyed) {
10735 return; // Ignore any pending requests if the worker was terminated.
10736 }
10737 var featureId = data.featureId;
10738 var loadingTask = this.loadingTask;
10739 if (loadingTask.onUnsupportedFeature) {
10740 loadingTask.onUnsupportedFeature(featureId);
10741 }
10742 _UnsupportedManager.notify(featureId);
10743 }, this);
10744
10745 messageHandler.on('JpegDecode', function(data) {
10746 if (this.destroyed) {
10747 return Promise.reject(new Error('Worker was destroyed'));
10748 }
10749
10750 var imageUrl = data[0];
10751 var components = data[1];
10752 if (components !== 3 && components !== 1) {
10753 return Promise.reject(
10754 new Error('Only 3 components or 1 component can be returned'));
10755 }
10756
10757 return new Promise(function (resolve, reject) {
10758 var img = new Image();
10759 img.onload = function () {
10760 var width = img.width;
10761 var height = img.height;
10762 var size = width * height;
10763 var rgbaLength = size * 4;
10764 var buf = new Uint8Array(size * components);
10765 var tmpCanvas = createScratchCanvas(width, height);
10766 var tmpCtx = tmpCanvas.getContext('2d');
10767 tmpCtx.drawImage(img, 0, 0);
10768 var data = tmpCtx.getImageData(0, 0, width, height).data;
10769 var i, j;
10770
10771 if (components === 3) {
10772 for (i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
10773 buf[j] = data[i];
10774 buf[j + 1] = data[i + 1];
10775 buf[j + 2] = data[i + 2];
10776 }
10777 } else if (components === 1) {
10778 for (i = 0, j = 0; i < rgbaLength; i += 4, j++) {
10779 buf[j] = data[i];
10780 }
10781 }
10782 resolve({ data: buf, width: width, height: height});
10783 };
10784 img.onerror = function () {
10785 reject(new Error('JpegDecode failed to load image'));
10786 };
10787 img.src = imageUrl;
10788 });
10789 }, this);
10790 },
10791
10792 getData: function WorkerTransport_getData() {
10793 return this.messageHandler.sendWithPromise('GetData', null);
10794 },
10795
10796 getPage: function WorkerTransport_getPage(pageNumber, capability) {
10797 if (!isInt(pageNumber) || pageNumber <= 0 || pageNumber > this.numPages) {
10798 return Promise.reject(new Error('Invalid page request'));
10799 }
10800
10801 var pageIndex = pageNumber - 1;
10802 if (pageIndex in this.pagePromises) {
10803 return this.pagePromises[pageIndex];
10804 }
10805 var promise = this.messageHandler.sendWithPromise('GetPage', {
10806 pageIndex: pageIndex
10807 }).then(function (pageInfo) {
10808 if (this.destroyed) {
10809 throw new Error('Transport destroyed');
10810 }
10811 var page = new PDFPageProxy(pageIndex, pageInfo, this);
10812 this.pageCache[pageIndex] = page;
10813 return page;
10814 }.bind(this));
10815 this.pagePromises[pageIndex] = promise;
10816 return promise;
10817 },
10818
10819 getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
10820 return this.messageHandler.sendWithPromise('GetPageIndex', {
10821 ref: ref,
10822 }).catch(function (reason) {
10823 return Promise.reject(new Error(reason));
10824 });
10825 },
10826
10827 getAnnotations: function WorkerTransport_getAnnotations(pageIndex, intent) {
10828 return this.messageHandler.sendWithPromise('GetAnnotations', {
10829 pageIndex: pageIndex,
10830 intent: intent,
10831 });
10832 },
10833
10834 getDestinations: function WorkerTransport_getDestinations() {
10835 return this.messageHandler.sendWithPromise('GetDestinations', null);
10836 },
10837
10838 getDestination: function WorkerTransport_getDestination(id) {
10839 return this.messageHandler.sendWithPromise('GetDestination', { id: id });
10840 },
10841
10842 getPageLabels: function WorkerTransport_getPageLabels() {
10843 return this.messageHandler.sendWithPromise('GetPageLabels', null);
10844 },
10845
10846 getAttachments: function WorkerTransport_getAttachments() {
10847 return this.messageHandler.sendWithPromise('GetAttachments', null);
10848 },
10849
10850 getJavaScript: function WorkerTransport_getJavaScript() {
10851 return this.messageHandler.sendWithPromise('GetJavaScript', null);
10852 },
10853
10854 getOutline: function WorkerTransport_getOutline() {
10855 return this.messageHandler.sendWithPromise('GetOutline', null);
10856 },
10857
10858 getMetadata: function WorkerTransport_getMetadata() {
10859 return this.messageHandler.sendWithPromise('GetMetadata', null).
10860 then(function transportMetadata(results) {
10861 return {
10862 info: results[0],
10863 metadata: (results[1] ? new Metadata(results[1]) : null)
10864 };
10865 });
10866 },
10867
10868 getStats: function WorkerTransport_getStats() {
10869 return this.messageHandler.sendWithPromise('GetStats', null);
10870 },
10871
10872 startCleanup: function WorkerTransport_startCleanup() {
10873 this.messageHandler.sendWithPromise('Cleanup', null).
10874 then(function endCleanup() {
10875 for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
10876 var page = this.pageCache[i];
10877 if (page) {
10878 page.cleanup();
10879 }
10880 }
10881 this.commonObjs.clear();
10882 this.fontLoader.clear();
10883 }.bind(this));
10884 }
10885 };
10886 return WorkerTransport;
10887
10888})();
10889
10890/**
10891 * A PDF document and page is built of many objects. E.g. there are objects
10892 * for fonts, images, rendering code and such. These objects might get processed
10893 * inside of a worker. The `PDFObjects` implements some basic functions to
10894 * manage these objects.
10895 * @ignore
10896 */
10897var PDFObjects = (function PDFObjectsClosure() {
10898 function PDFObjects() {
10899 this.objs = Object.create(null);
10900 }
10901
10902 PDFObjects.prototype = {
10903 /**
10904 * Internal function.
10905 * Ensures there is an object defined for `objId`.
10906 */
10907 ensureObj: function PDFObjects_ensureObj(objId) {
10908 if (this.objs[objId]) {
10909 return this.objs[objId];
10910 }
10911
10912 var obj = {
10913 capability: createPromiseCapability(),
10914 data: null,
10915 resolved: false
10916 };
10917 this.objs[objId] = obj;
10918
10919 return obj;
10920 },
10921
10922 /**
10923 * If called *without* callback, this returns the data of `objId` but the
10924 * object needs to be resolved. If it isn't, this function throws.
10925 *
10926 * If called *with* a callback, the callback is called with the data of the
10927 * object once the object is resolved. That means, if you call this
10928 * function and the object is already resolved, the callback gets called
10929 * right away.
10930 */
10931 get: function PDFObjects_get(objId, callback) {
10932 // If there is a callback, then the get can be async and the object is
10933 // not required to be resolved right now
10934 if (callback) {
10935 this.ensureObj(objId).capability.promise.then(callback);
10936 return null;
10937 }
10938
10939 // If there isn't a callback, the user expects to get the resolved data
10940 // directly.
10941 var obj = this.objs[objId];
10942
10943 // If there isn't an object yet or the object isn't resolved, then the
10944 // data isn't ready yet!
10945 if (!obj || !obj.resolved) {
10946 error('Requesting object that isn\'t resolved yet ' + objId);
10947 }
10948
10949 return obj.data;
10950 },
10951
10952 /**
10953 * Resolves the object `objId` with optional `data`.
10954 */
10955 resolve: function PDFObjects_resolve(objId, data) {
10956 var obj = this.ensureObj(objId);
10957
10958 obj.resolved = true;
10959 obj.data = data;
10960 obj.capability.resolve(data);
10961 },
10962
10963 isResolved: function PDFObjects_isResolved(objId) {
10964 var objs = this.objs;
10965
10966 if (!objs[objId]) {
10967 return false;
10968 } else {
10969 return objs[objId].resolved;
10970 }
10971 },
10972
10973 hasData: function PDFObjects_hasData(objId) {
10974 return this.isResolved(objId);
10975 },
10976
10977 /**
10978 * Returns the data of `objId` if object exists, null otherwise.
10979 */
10980 getData: function PDFObjects_getData(objId) {
10981 var objs = this.objs;
10982 if (!objs[objId] || !objs[objId].resolved) {
10983 return null;
10984 } else {
10985 return objs[objId].data;
10986 }
10987 },
10988
10989 clear: function PDFObjects_clear() {
10990 this.objs = Object.create(null);
10991 }
10992 };
10993 return PDFObjects;
10994})();
10995
10996/**
10997 * Allows controlling of the rendering tasks.
10998 * @class
10999 * @alias RenderTask
11000 */
11001var RenderTask = (function RenderTaskClosure() {
11002 function RenderTask(internalRenderTask) {
11003 this._internalRenderTask = internalRenderTask;
11004
11005 /**
11006 * Callback for incremental rendering -- a function that will be called
11007 * each time the rendering is paused. To continue rendering call the
11008 * function that is the first argument to the callback.
11009 * @type {function}
11010 */
11011 this.onContinue = null;
11012 }
11013
11014 RenderTask.prototype = /** @lends RenderTask.prototype */ {
11015 /**
11016 * Promise for rendering task completion.
11017 * @return {Promise}
11018 */
11019 get promise() {
11020 return this._internalRenderTask.capability.promise;
11021 },
11022
11023 /**
11024 * Cancels the rendering task. If the task is currently rendering it will
11025 * not be cancelled until graphics pauses with a timeout. The promise that
11026 * this object extends will resolved when cancelled.
11027 */
11028 cancel: function RenderTask_cancel() {
11029 this._internalRenderTask.cancel();
11030 },
11031
11032 /**
11033 * Registers callbacks to indicate the rendering task completion.
11034 *
11035 * @param {function} onFulfilled The callback for the rendering completion.
11036 * @param {function} onRejected The callback for the rendering failure.
11037 * @return {Promise} A promise that is resolved after the onFulfilled or
11038 * onRejected callback.
11039 */
11040 then: function RenderTask_then(onFulfilled, onRejected) {
11041 return this.promise.then.apply(this.promise, arguments);
11042 }
11043 };
11044
11045 return RenderTask;
11046})();
11047
11048/**
11049 * For internal use only.
11050 * @ignore
11051 */
11052var InternalRenderTask = (function InternalRenderTaskClosure() {
11053
11054 function InternalRenderTask(callback, params, objs, commonObjs, operatorList,
11055 pageNumber) {
11056 this.callback = callback;
11057 this.params = params;
11058 this.objs = objs;
11059 this.commonObjs = commonObjs;
11060 this.operatorListIdx = null;
11061 this.operatorList = operatorList;
11062 this.pageNumber = pageNumber;
11063 this.running = false;
11064 this.graphicsReadyCallback = null;
11065 this.graphicsReady = false;
11066 this.useRequestAnimationFrame = false;
11067 this.cancelled = false;
11068 this.capability = createPromiseCapability();
11069 this.task = new RenderTask(this);
11070 // caching this-bound methods
11071 this._continueBound = this._continue.bind(this);
11072 this._scheduleNextBound = this._scheduleNext.bind(this);
11073 this._nextBound = this._next.bind(this);
11074 }
11075
11076 InternalRenderTask.prototype = {
11077
11078 initializeGraphics:
11079 function InternalRenderTask_initializeGraphics(transparency) {
11080
11081 if (this.cancelled) {
11082 return;
11083 }
11084 if (getDefaultSetting('pdfBug') && globalScope.StepperManager &&
11085 globalScope.StepperManager.enabled) {
11086 this.stepper = globalScope.StepperManager.create(this.pageNumber - 1);
11087 this.stepper.init(this.operatorList);
11088 this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
11089 }
11090
11091 var params = this.params;
11092 this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs,
11093 this.objs, params.imageLayer);
11094
11095 this.gfx.beginDrawing(params.transform, params.viewport, transparency);
11096 this.operatorListIdx = 0;
11097 this.graphicsReady = true;
11098 if (this.graphicsReadyCallback) {
11099 this.graphicsReadyCallback();
11100 }
11101 },
11102
11103 cancel: function InternalRenderTask_cancel() {
11104 this.running = false;
11105 this.cancelled = true;
11106 this.callback('cancelled');
11107 },
11108
11109 operatorListChanged: function InternalRenderTask_operatorListChanged() {
11110 if (!this.graphicsReady) {
11111 if (!this.graphicsReadyCallback) {
11112 this.graphicsReadyCallback = this._continueBound;
11113 }
11114 return;
11115 }
11116
11117 if (this.stepper) {
11118 this.stepper.updateOperatorList(this.operatorList);
11119 }
11120
11121 if (this.running) {
11122 return;
11123 }
11124 this._continue();
11125 },
11126
11127 _continue: function InternalRenderTask__continue() {
11128 this.running = true;
11129 if (this.cancelled) {
11130 return;
11131 }
11132 if (this.task.onContinue) {
11133 this.task.onContinue.call(this.task, this._scheduleNextBound);
11134 } else {
11135 this._scheduleNext();
11136 }
11137 },
11138
11139 _scheduleNext: function InternalRenderTask__scheduleNext() {
11140 if (this.useRequestAnimationFrame && typeof window !== 'undefined') {
11141 window.requestAnimationFrame(this._nextBound);
11142 } else {
11143 Promise.resolve(undefined).then(this._nextBound);
11144 }
11145 },
11146
11147 _next: function InternalRenderTask__next() {
11148 if (this.cancelled) {
11149 return;
11150 }
11151 this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList,
11152 this.operatorListIdx,
11153 this._continueBound,
11154 this.stepper);
11155 if (this.operatorListIdx === this.operatorList.argsArray.length) {
11156 this.running = false;
11157 if (this.operatorList.lastChunk) {
11158 this.gfx.endDrawing();
11159 this.callback();
11160 }
11161 }
11162 }
11163
11164 };
11165
11166 return InternalRenderTask;
11167})();
11168
11169/**
11170 * (Deprecated) Global observer of unsupported feature usages. Use
11171 * onUnsupportedFeature callback of the {PDFDocumentLoadingTask} instance.
11172 */
11173var _UnsupportedManager = (function UnsupportedManagerClosure() {
11174 var listeners = [];
11175 return {
11176 listen: function (cb) {
11177 deprecated('Global UnsupportedManager.listen is used: ' +
11178 ' use PDFDocumentLoadingTask.onUnsupportedFeature instead');
11179 listeners.push(cb);
11180 },
11181 notify: function (featureId) {
11182 for (var i = 0, ii = listeners.length; i < ii; i++) {
11183 listeners[i](featureId);
11184 }
11185 }
11186 };
11187})();
11188
11189if (typeof pdfjsVersion !== 'undefined') {
11190 exports.version = pdfjsVersion;
11191}
11192if (typeof pdfjsBuild !== 'undefined') {
11193 exports.build = pdfjsBuild;
11194}
11195
11196exports.getDocument = getDocument;
11197exports.PDFDataRangeTransport = PDFDataRangeTransport;
11198exports.PDFWorker = PDFWorker;
11199exports.PDFDocumentProxy = PDFDocumentProxy;
11200exports.PDFPageProxy = PDFPageProxy;
11201exports._UnsupportedManager = _UnsupportedManager;
11202}));
11203
11204
11205(function (root, factory) {
11206 {
11207 factory((root.pdfjsDisplayGlobal = {}), root.pdfjsSharedUtil,
11208 root.pdfjsDisplayDOMUtils, root.pdfjsDisplayAPI,
11209 root.pdfjsDisplayAnnotationLayer, root.pdfjsDisplayTextLayer,
11210 root.pdfjsDisplayMetadata, root.pdfjsDisplaySVG);
11211 }
11212}(this, function (exports, sharedUtil, displayDOMUtils, displayAPI,
11213 displayAnnotationLayer, displayTextLayer, displayMetadata,
11214 displaySVG) {
11215
11216 var globalScope = sharedUtil.globalScope;
11217 var deprecated = sharedUtil.deprecated;
11218 var warn = sharedUtil.warn;
11219 var LinkTarget = displayDOMUtils.LinkTarget;
11220
11221 var isWorker = (typeof window === 'undefined');
11222
11223 // The global PDFJS object is now deprecated and will not be supported in
11224 // the future. The members below are maintained for backward compatibility
11225 // and shall not be extended or modified. If the global.js is included as
11226 // a module, we will create a global PDFJS object instance or use existing.
11227 if (!globalScope.PDFJS) {
11228 globalScope.PDFJS = {};
11229 }
11230 var PDFJS = globalScope.PDFJS;
11231
11232 if (typeof pdfjsVersion !== 'undefined') {
11233 PDFJS.version = pdfjsVersion;
11234 }
11235 if (typeof pdfjsBuild !== 'undefined') {
11236 PDFJS.build = pdfjsBuild;
11237 }
11238
11239 PDFJS.pdfBug = false;
11240
11241 if (PDFJS.verbosity !== undefined) {
11242 sharedUtil.setVerbosityLevel(PDFJS.verbosity);
11243 }
11244 delete PDFJS.verbosity;
11245 Object.defineProperty(PDFJS, 'verbosity', {
11246 get: function () { return sharedUtil.getVerbosityLevel(); },
11247 set: function (level) { sharedUtil.setVerbosityLevel(level); },
11248 enumerable: true,
11249 configurable: true
11250 });
11251
11252 PDFJS.VERBOSITY_LEVELS = sharedUtil.VERBOSITY_LEVELS;
11253 PDFJS.OPS = sharedUtil.OPS;
11254 PDFJS.UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
11255 PDFJS.isValidUrl = sharedUtil.isValidUrl;
11256 PDFJS.shadow = sharedUtil.shadow;
11257 PDFJS.createBlob = sharedUtil.createBlob;
11258 PDFJS.createObjectURL = function PDFJS_createObjectURL(data, contentType) {
11259 return sharedUtil.createObjectURL(data, contentType,
11260 PDFJS.disableCreateObjectURL);
11261 };
11262 Object.defineProperty(PDFJS, 'isLittleEndian', {
11263 configurable: true,
11264 get: function PDFJS_isLittleEndian() {
11265 var value = sharedUtil.isLittleEndian();
11266 return sharedUtil.shadow(PDFJS, 'isLittleEndian', value);
11267 }
11268 });
11269 PDFJS.removeNullCharacters = sharedUtil.removeNullCharacters;
11270 PDFJS.PasswordResponses = sharedUtil.PasswordResponses;
11271 PDFJS.PasswordException = sharedUtil.PasswordException;
11272 PDFJS.UnknownErrorException = sharedUtil.UnknownErrorException;
11273 PDFJS.InvalidPDFException = sharedUtil.InvalidPDFException;
11274 PDFJS.MissingPDFException = sharedUtil.MissingPDFException;
11275 PDFJS.UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
11276 PDFJS.Util = sharedUtil.Util;
11277 PDFJS.PageViewport = sharedUtil.PageViewport;
11278 PDFJS.createPromiseCapability = sharedUtil.createPromiseCapability;
11279
11280 /**
11281 * The maximum allowed image size in total pixels e.g. width * height. Images
11282 * above this value will not be drawn. Use -1 for no limit.
11283 * @var {number}
11284 */
11285 PDFJS.maxImageSize = (PDFJS.maxImageSize === undefined ?
11286 -1 : PDFJS.maxImageSize);
11287
11288 /**
11289 * The url of where the predefined Adobe CMaps are located. Include trailing
11290 * slash.
11291 * @var {string}
11292 */
11293 PDFJS.cMapUrl = (PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl);
11294
11295 /**
11296 * Specifies if CMaps are binary packed.
11297 * @var {boolean}
11298 */
11299 PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked;
11300
11301 /**
11302 * By default fonts are converted to OpenType fonts and loaded via font face
11303 * rules. If disabled, the font will be rendered using a built in font
11304 * renderer that constructs the glyphs with primitive path commands.
11305 * @var {boolean}
11306 */
11307 PDFJS.disableFontFace = (PDFJS.disableFontFace === undefined ?
11308 false : PDFJS.disableFontFace);
11309
11310 /**
11311 * Path for image resources, mainly for annotation icons. Include trailing
11312 * slash.
11313 * @var {string}
11314 */
11315 PDFJS.imageResourcesPath = (PDFJS.imageResourcesPath === undefined ?
11316 '' : PDFJS.imageResourcesPath);
11317
11318 /**
11319 * Disable the web worker and run all code on the main thread. This will
11320 * happen automatically if the browser doesn't support workers or sending
11321 * typed arrays to workers.
11322 * @var {boolean}
11323 */
11324 PDFJS.disableWorker = (PDFJS.disableWorker === undefined ?
11325 false : PDFJS.disableWorker);
11326
11327 /**
11328 * Path and filename of the worker file. Required when the worker is enabled
11329 * in development mode. If unspecified in the production build, the worker
11330 * will be loaded based on the location of the pdf.js file. It is recommended
11331 * that the workerSrc is set in a custom application to prevent issues caused
11332 * by third-party frameworks and libraries.
11333 * @var {string}
11334 */
11335 PDFJS.workerSrc = (PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc);
11336
11337 /**
11338 * Disable range request loading of PDF files. When enabled and if the server
11339 * supports partial content requests then the PDF will be fetched in chunks.
11340 * Enabled (false) by default.
11341 * @var {boolean}
11342 */
11343 PDFJS.disableRange = (PDFJS.disableRange === undefined ?
11344 false : PDFJS.disableRange);
11345
11346 /**
11347 * Disable streaming of PDF file data. By default PDF.js attempts to load PDF
11348 * in chunks. This default behavior can be disabled.
11349 * @var {boolean}
11350 */
11351 PDFJS.disableStream = (PDFJS.disableStream === undefined ?
11352 false : PDFJS.disableStream);
11353
11354 /**
11355 * Disable pre-fetching of PDF file data. When range requests are enabled
11356 * PDF.js will automatically keep fetching more data even if it isn't needed
11357 * to display the current page. This default behavior can be disabled.
11358 *
11359 * NOTE: It is also necessary to disable streaming, see above,
11360 * in order for disabling of pre-fetching to work correctly.
11361 * @var {boolean}
11362 */
11363 PDFJS.disableAutoFetch = (PDFJS.disableAutoFetch === undefined ?
11364 false : PDFJS.disableAutoFetch);
11365
11366 /**
11367 * Enables special hooks for debugging PDF.js.
11368 * @var {boolean}
11369 */
11370 PDFJS.pdfBug = (PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug);
11371
11372 /**
11373 * Enables transfer usage in postMessage for ArrayBuffers.
11374 * @var {boolean}
11375 */
11376 PDFJS.postMessageTransfers = (PDFJS.postMessageTransfers === undefined ?
11377 true : PDFJS.postMessageTransfers);
11378
11379 /**
11380 * Disables URL.createObjectURL usage.
11381 * @var {boolean}
11382 */
11383 PDFJS.disableCreateObjectURL = (PDFJS.disableCreateObjectURL === undefined ?
11384 false : PDFJS.disableCreateObjectURL);
11385
11386 /**
11387 * Disables WebGL usage.
11388 * @var {boolean}
11389 */
11390 PDFJS.disableWebGL = (PDFJS.disableWebGL === undefined ?
11391 true : PDFJS.disableWebGL);
11392
11393 /**
11394 * Specifies the |target| attribute for external links.
11395 * The constants from PDFJS.LinkTarget should be used:
11396 * - NONE [default]
11397 * - SELF
11398 * - BLANK
11399 * - PARENT
11400 * - TOP
11401 * @var {number}
11402 */
11403 PDFJS.externalLinkTarget = (PDFJS.externalLinkTarget === undefined ?
11404 LinkTarget.NONE : PDFJS.externalLinkTarget);
11405
11406 /**
11407 * Specifies the |rel| attribute for external links. Defaults to stripping
11408 * the referrer.
11409 * @var {string}
11410 */
11411 PDFJS.externalLinkRel = (PDFJS.externalLinkRel === undefined ?
11412 'noreferrer' : PDFJS.externalLinkRel);
11413
11414 /**
11415 * Determines if we can eval strings as JS. Primarily used to improve
11416 * performance for font rendering.
11417 * @var {boolean}
11418 */
11419 PDFJS.isEvalSupported = (PDFJS.isEvalSupported === undefined ?
11420 true : PDFJS.isEvalSupported);
11421
11422 var savedOpenExternalLinksInNewWindow = PDFJS.openExternalLinksInNewWindow;
11423 delete PDFJS.openExternalLinksInNewWindow;
11424 Object.defineProperty(PDFJS, 'openExternalLinksInNewWindow', {
11425 get: function () {
11426 return PDFJS.externalLinkTarget === LinkTarget.BLANK;
11427 },
11428 set: function (value) {
11429 if (value) {
11430 deprecated('PDFJS.openExternalLinksInNewWindow, please use ' +
11431 '"PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK" instead.');
11432 }
11433 if (PDFJS.externalLinkTarget !== LinkTarget.NONE) {
11434 warn('PDFJS.externalLinkTarget is already initialized');
11435 return;
11436 }
11437 PDFJS.externalLinkTarget = value ? LinkTarget.BLANK : LinkTarget.NONE;
11438 },
11439 enumerable: true,
11440 configurable: true
11441 });
11442 if (savedOpenExternalLinksInNewWindow) {
11443 /**
11444 * (Deprecated) Opens external links in a new window if enabled.
11445 * The default behavior opens external links in the PDF.js window.
11446 *
11447 * NOTE: This property has been deprecated, please use
11448 * `PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK` instead.
11449 * @var {boolean}
11450 */
11451 PDFJS.openExternalLinksInNewWindow = savedOpenExternalLinksInNewWindow;
11452 }
11453
11454 PDFJS.getDocument = displayAPI.getDocument;
11455 PDFJS.PDFDataRangeTransport = displayAPI.PDFDataRangeTransport;
11456 PDFJS.PDFWorker = displayAPI.PDFWorker;
11457
11458 Object.defineProperty(PDFJS, 'hasCanvasTypedArrays', {
11459 configurable: true,
11460 get: function PDFJS_hasCanvasTypedArrays() {
11461 var value = displayDOMUtils.hasCanvasTypedArrays();
11462 return sharedUtil.shadow(PDFJS, 'hasCanvasTypedArrays', value);
11463 }
11464 });
11465 PDFJS.CustomStyle = displayDOMUtils.CustomStyle;
11466 PDFJS.LinkTarget = LinkTarget;
11467 PDFJS.addLinkAttributes = displayDOMUtils.addLinkAttributes;
11468 PDFJS.getFilenameFromUrl = displayDOMUtils.getFilenameFromUrl;
11469 PDFJS.isExternalLinkTargetSet = displayDOMUtils.isExternalLinkTargetSet;
11470
11471 PDFJS.AnnotationLayer = displayAnnotationLayer.AnnotationLayer;
11472
11473 PDFJS.renderTextLayer = displayTextLayer.renderTextLayer;
11474
11475 PDFJS.Metadata = displayMetadata.Metadata;
11476
11477 PDFJS.SVGGraphics = displaySVG.SVGGraphics;
11478
11479 PDFJS.UnsupportedManager = displayAPI._UnsupportedManager;
11480
11481 exports.globalScope = globalScope;
11482 exports.isWorker = isWorker;
11483 exports.PDFJS = globalScope.PDFJS;
11484}));
11485 }).call(pdfjsLibs);
11486
11487 exports.PDFJS = pdfjsLibs.pdfjsDisplayGlobal.PDFJS;
11488 exports.build = pdfjsLibs.pdfjsDisplayAPI.build;
11489 exports.version = pdfjsLibs.pdfjsDisplayAPI.version;
11490 exports.getDocument = pdfjsLibs.pdfjsDisplayAPI.getDocument;
11491 exports.PDFDataRangeTransport =
11492 pdfjsLibs.pdfjsDisplayAPI.PDFDataRangeTransport;
11493 exports.PDFWorker = pdfjsLibs.pdfjsDisplayAPI.PDFWorker;
11494 exports.renderTextLayer = pdfjsLibs.pdfjsDisplayTextLayer.renderTextLayer;
11495 exports.AnnotationLayer =
11496 pdfjsLibs.pdfjsDisplayAnnotationLayer.AnnotationLayer;
11497 exports.CustomStyle = pdfjsLibs.pdfjsDisplayDOMUtils.CustomStyle;
11498 exports.PasswordResponses = pdfjsLibs.pdfjsSharedUtil.PasswordResponses;
11499 exports.InvalidPDFException = pdfjsLibs.pdfjsSharedUtil.InvalidPDFException;
11500 exports.MissingPDFException = pdfjsLibs.pdfjsSharedUtil.MissingPDFException;
11501 exports.SVGGraphics = pdfjsLibs.pdfjsDisplaySVG.SVGGraphics;
11502 exports.UnexpectedResponseException =
11503 pdfjsLibs.pdfjsSharedUtil.UnexpectedResponseException;
11504 exports.OPS = pdfjsLibs.pdfjsSharedUtil.OPS;
11505 exports.UNSUPPORTED_FEATURES = pdfjsLibs.pdfjsSharedUtil.UNSUPPORTED_FEATURES;
11506 exports.isValidUrl = pdfjsLibs.pdfjsSharedUtil.isValidUrl;
11507 exports.createObjectURL = pdfjsLibs.pdfjsSharedUtil.createObjectURL;
11508 exports.removeNullCharacters = pdfjsLibs.pdfjsSharedUtil.removeNullCharacters;
11509 exports.shadow = pdfjsLibs.pdfjsSharedUtil.shadow;
11510 exports.createBlob = pdfjsLibs.pdfjsSharedUtil.createBlob;
11511 exports.getFilenameFromUrl =
11512 pdfjsLibs.pdfjsDisplayDOMUtils.getFilenameFromUrl;
11513 exports.addLinkAttributes = pdfjsLibs.pdfjsDisplayDOMUtils.addLinkAttributes;
11514}));
11515