diff options
Diffstat (limited to 'static/dist/js/pdf.js')
-rw-r--r-- | static/dist/js/pdf.js | 11515 |
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) { | ||
21 | define('pdfjs-dist/build/pdf', ['exports'], factory); | ||
22 | } else if (typeof exports !== 'undefined') { | ||
23 | factory(exports); | ||
24 | } else { | ||
25 | factory((root.pdfjsDistBuildPdf = {})); | ||
26 | } | ||
27 | }(this, function (exports) { | ||
28 | // Use strict in our context only - users might not want it | ||
29 | 'use strict'; | ||
30 | |||
31 | var pdfjsVersion = '1.6.210'; | ||
32 | var 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 | |||
50 | var globalScope = (typeof window !== 'undefined') ? window : | ||
51 | (typeof global !== 'undefined') ? global : | ||
52 | (typeof self !== 'undefined') ? self : this; | ||
53 | |||
54 | var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; | ||
55 | |||
56 | var 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 | |||
69 | var ImageKind = { | ||
70 | GRAYSCALE_1BPP: 1, | ||
71 | RGB_24BPP: 2, | ||
72 | RGBA_32BPP: 3 | ||
73 | }; | ||
74 | |||
75 | var 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 | |||
104 | var 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 | |||
117 | var 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 | |||
139 | var AnnotationBorderStyleType = { | ||
140 | SOLID: 1, | ||
141 | DASHED: 2, | ||
142 | BEVELED: 3, | ||
143 | INSET: 4, | ||
144 | UNDERLINE: 5 | ||
145 | }; | ||
146 | |||
147 | var 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 | |||
160 | var 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 | |||
174 | var VERBOSITY_LEVELS = { | ||
175 | errors: 0, | ||
176 | warnings: 1, | ||
177 | infos: 5 | ||
178 | }; | ||
179 | |||
180 | // All the possible operations for an operator list. | ||
181 | var 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 | |||
277 | var verbosity = VERBOSITY_LEVELS.warnings; | ||
278 | |||
279 | function setVerbosityLevel(level) { | ||
280 | verbosity = level; | ||
281 | } | ||
282 | |||
283 | function 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. | ||
290 | function info(msg) { | ||
291 | if (verbosity >= VERBOSITY_LEVELS.infos) { | ||
292 | console.log('Info: ' + msg); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | // Non-fatal warnings. | ||
297 | function 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. | ||
304 | function 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. | ||
310 | function 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 | |||
318 | function 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 | |||
326 | function assert(cond, msg) { | ||
327 | if (!cond) { | ||
328 | error(msg); | ||
329 | } | ||
330 | } | ||
331 | |||
332 | var 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. | ||
342 | function 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. | ||
357 | function 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 | |||
380 | function 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 | |||
388 | function 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 | |||
400 | var PasswordResponses = { | ||
401 | NEED_PASSWORD: 1, | ||
402 | INCORRECT_PASSWORD: 2 | ||
403 | }; | ||
404 | |||
405 | var 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 | |||
418 | var 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 | |||
431 | var 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 | |||
443 | var 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 | |||
455 | var 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 | |||
469 | var 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 | |||
481 | var 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 | |||
495 | var 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 | |||
507 | var NullCharactersRegExp = /\x00/g; | ||
508 | |||
509 | function 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 | |||
517 | function 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 | |||
534 | function 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 | */ | ||
549 | function 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 | */ | ||
562 | function 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 | |||
593 | function string32(value) { | ||
594 | return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff, | ||
595 | (value >> 8) & 0xff, value & 0xff); | ||
596 | } | ||
597 | |||
598 | function log2(x) { | ||
599 | var n = 1, i = 0; | ||
600 | while (x > n) { | ||
601 | n <<= 1; | ||
602 | i++; | ||
603 | } | ||
604 | return i; | ||
605 | } | ||
606 | |||
607 | function readInt8(data, start) { | ||
608 | return (data[start] << 24) >> 24; | ||
609 | } | ||
610 | |||
611 | function readUint16(data, offset) { | ||
612 | return (data[offset] << 8) | data[offset + 1]; | ||
613 | } | ||
614 | |||
615 | function 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 | ||
622 | function 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. | ||
630 | function isEvalSupported() { | ||
631 | try { | ||
632 | /* jshint evil: true */ | ||
633 | new Function(''); | ||
634 | return true; | ||
635 | } catch (e) { | ||
636 | return false; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | var 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 | |||
680 | exports.Uint32ArrayView = Uint32ArrayView; | ||
681 | |||
682 | var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; | ||
683 | |||
684 | var 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 | */ | ||
937 | var 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 | |||
1068 | var 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 | |||
1080 | function 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 | |||
1097 | function stringToUTF8String(str) { | ||
1098 | return decodeURIComponent(escape(str)); | ||
1099 | } | ||
1100 | |||
1101 | function utf8StringToString(str) { | ||
1102 | return unescape(encodeURIComponent(str)); | ||
1103 | } | ||
1104 | |||
1105 | function isEmptyObj(obj) { | ||
1106 | for (var key in obj) { | ||
1107 | return false; | ||
1108 | } | ||
1109 | return true; | ||
1110 | } | ||
1111 | |||
1112 | function isBool(v) { | ||
1113 | return typeof v === 'boolean'; | ||
1114 | } | ||
1115 | |||
1116 | function isInt(v) { | ||
1117 | return typeof v === 'number' && ((v | 0) === v); | ||
1118 | } | ||
1119 | |||
1120 | function isNum(v) { | ||
1121 | return typeof v === 'number'; | ||
1122 | } | ||
1123 | |||
1124 | function isString(v) { | ||
1125 | return typeof v === 'string'; | ||
1126 | } | ||
1127 | |||
1128 | function isArray(v) { | ||
1129 | return v instanceof Array; | ||
1130 | } | ||
1131 | |||
1132 | function 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. | ||
1137 | function 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 | */ | ||
1157 | function 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 | |||
1513 | var 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 | |||
1573 | var 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 | |||
1580 | var 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 | |||
1606 | function 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 | |||
1671 | MessageHandler.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 | |||
1740 | function 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 | |||
2394 | exports.FONT_IDENTITY_MATRIX = FONT_IDENTITY_MATRIX; | ||
2395 | exports.IDENTITY_MATRIX = IDENTITY_MATRIX; | ||
2396 | exports.OPS = OPS; | ||
2397 | exports.VERBOSITY_LEVELS = VERBOSITY_LEVELS; | ||
2398 | exports.UNSUPPORTED_FEATURES = UNSUPPORTED_FEATURES; | ||
2399 | exports.AnnotationBorderStyleType = AnnotationBorderStyleType; | ||
2400 | exports.AnnotationFieldFlag = AnnotationFieldFlag; | ||
2401 | exports.AnnotationFlag = AnnotationFlag; | ||
2402 | exports.AnnotationType = AnnotationType; | ||
2403 | exports.FontType = FontType; | ||
2404 | exports.ImageKind = ImageKind; | ||
2405 | exports.InvalidPDFException = InvalidPDFException; | ||
2406 | exports.MessageHandler = MessageHandler; | ||
2407 | exports.MissingDataException = MissingDataException; | ||
2408 | exports.MissingPDFException = MissingPDFException; | ||
2409 | exports.NotImplementedException = NotImplementedException; | ||
2410 | exports.PageViewport = PageViewport; | ||
2411 | exports.PasswordException = PasswordException; | ||
2412 | exports.PasswordResponses = PasswordResponses; | ||
2413 | exports.StatTimer = StatTimer; | ||
2414 | exports.StreamType = StreamType; | ||
2415 | exports.TextRenderingMode = TextRenderingMode; | ||
2416 | exports.UnexpectedResponseException = UnexpectedResponseException; | ||
2417 | exports.UnknownErrorException = UnknownErrorException; | ||
2418 | exports.Util = Util; | ||
2419 | exports.XRefParseException = XRefParseException; | ||
2420 | exports.arrayByteLength = arrayByteLength; | ||
2421 | exports.arraysToBytes = arraysToBytes; | ||
2422 | exports.assert = assert; | ||
2423 | exports.bytesToString = bytesToString; | ||
2424 | exports.createBlob = createBlob; | ||
2425 | exports.createPromiseCapability = createPromiseCapability; | ||
2426 | exports.createObjectURL = createObjectURL; | ||
2427 | exports.deprecated = deprecated; | ||
2428 | exports.error = error; | ||
2429 | exports.getLookupTableFactory = getLookupTableFactory; | ||
2430 | exports.getVerbosityLevel = getVerbosityLevel; | ||
2431 | exports.globalScope = globalScope; | ||
2432 | exports.info = info; | ||
2433 | exports.isArray = isArray; | ||
2434 | exports.isArrayBuffer = isArrayBuffer; | ||
2435 | exports.isBool = isBool; | ||
2436 | exports.isEmptyObj = isEmptyObj; | ||
2437 | exports.isInt = isInt; | ||
2438 | exports.isNum = isNum; | ||
2439 | exports.isString = isString; | ||
2440 | exports.isSpace = isSpace; | ||
2441 | exports.isSameOrigin = isSameOrigin; | ||
2442 | exports.isValidUrl = isValidUrl; | ||
2443 | exports.isLittleEndian = isLittleEndian; | ||
2444 | exports.isEvalSupported = isEvalSupported; | ||
2445 | exports.loadJpegStream = loadJpegStream; | ||
2446 | exports.log2 = log2; | ||
2447 | exports.readInt8 = readInt8; | ||
2448 | exports.readUint16 = readUint16; | ||
2449 | exports.readUint32 = readUint32; | ||
2450 | exports.removeNullCharacters = removeNullCharacters; | ||
2451 | exports.setVerbosityLevel = setVerbosityLevel; | ||
2452 | exports.shadow = shadow; | ||
2453 | exports.string32 = string32; | ||
2454 | exports.stringToBytes = stringToBytes; | ||
2455 | exports.stringToPDFString = stringToPDFString; | ||
2456 | exports.stringToUTF8String = stringToUTF8String; | ||
2457 | exports.utf8StringToString = utf8StringToString; | ||
2458 | exports.warn = warn; | ||
2459 | })); | ||
2460 | |||
2461 | |||
2462 | (function (root, factory) { | ||
2463 | { | ||
2464 | factory((root.pdfjsDisplayDOMUtils = {}), root.pdfjsSharedUtil); | ||
2465 | } | ||
2466 | }(this, function (exports, sharedUtil) { | ||
2467 | |||
2468 | var removeNullCharacters = sharedUtil.removeNullCharacters; | ||
2469 | var warn = sharedUtil.warn; | ||
2470 | |||
2471 | /** | ||
2472 | * Optimised CSS custom property getter/setter. | ||
2473 | * @class | ||
2474 | */ | ||
2475 | var 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 | |||
2525 | function 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 | |||
2533 | var LinkTarget = { | ||
2534 | NONE: 0, // Default value. | ||
2535 | SELF: 1, | ||
2536 | BLANK: 2, | ||
2537 | PARENT: 3, | ||
2538 | TOP: 4, | ||
2539 | }; | ||
2540 | |||
2541 | var 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 | */ | ||
2562 | function 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. | ||
2582 | function 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 | |||
2591 | function 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 | |||
2652 | function 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 | |||
2665 | exports.CustomStyle = CustomStyle; | ||
2666 | exports.addLinkAttributes = addLinkAttributes; | ||
2667 | exports.isExternalLinkTargetSet = isExternalLinkTargetSet; | ||
2668 | exports.getFilenameFromUrl = getFilenameFromUrl; | ||
2669 | exports.LinkTarget = LinkTarget; | ||
2670 | exports.hasCanvasTypedArrays = hasCanvasTypedArrays; | ||
2671 | exports.getDefaultSetting = getDefaultSetting; | ||
2672 | })); | ||
2673 | |||
2674 | |||
2675 | (function (root, factory) { | ||
2676 | { | ||
2677 | factory((root.pdfjsDisplayFontLoader = {}), root.pdfjsSharedUtil); | ||
2678 | } | ||
2679 | }(this, function (exports, sharedUtil) { | ||
2680 | |||
2681 | var assert = sharedUtil.assert; | ||
2682 | var bytesToString = sharedUtil.bytesToString; | ||
2683 | var string32 = sharedUtil.string32; | ||
2684 | var shadow = sharedUtil.shadow; | ||
2685 | var warn = sharedUtil.warn; | ||
2686 | |||
2687 | function 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 | } | ||
2697 | FontLoader.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 | }; | ||
2932 | FontLoader.isFontLoadingAPISupported = typeof document !== 'undefined' && | ||
2933 | !!document.fonts; | ||
2934 | Object.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 | |||
2956 | var IsEvalSupportedCached = { | ||
2957 | get value() { | ||
2958 | return shadow(this, 'value', sharedUtil.isEvalSupported()); | ||
2959 | } | ||
2960 | }; | ||
2961 | |||
2962 | var 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 | |||
3058 | exports.FontFaceObject = FontFaceObject; | ||
3059 | exports.FontLoader = FontLoader; | ||
3060 | })); | ||
3061 | |||
3062 | |||
3063 | (function (root, factory) { | ||
3064 | { | ||
3065 | factory((root.pdfjsDisplayMetadata = {}), root.pdfjsSharedUtil); | ||
3066 | } | ||
3067 | }(this, function (exports, sharedUtil) { | ||
3068 | |||
3069 | var 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 | |||
3147 | exports.Metadata = Metadata; | ||
3148 | })); | ||
3149 | |||
3150 | |||
3151 | (function (root, factory) { | ||
3152 | { | ||
3153 | factory((root.pdfjsDisplaySVG = {}), root.pdfjsSharedUtil); | ||
3154 | } | ||
3155 | }(this, function (exports, sharedUtil) { | ||
3156 | var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX; | ||
3157 | var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX; | ||
3158 | var ImageKind = sharedUtil.ImageKind; | ||
3159 | var OPS = sharedUtil.OPS; | ||
3160 | var Util = sharedUtil.Util; | ||
3161 | var isNum = sharedUtil.isNum; | ||
3162 | var isArray = sharedUtil.isArray; | ||
3163 | var warn = sharedUtil.warn; | ||
3164 | var createObjectURL = sharedUtil.createObjectURL; | ||
3165 | |||
3166 | var SVG_DEFAULTS = { | ||
3167 | fontStyle: 'normal', | ||
3168 | fontWeight: 'normal', | ||
3169 | fillColor: '#000000' | ||
3170 | }; | ||
3171 | |||
3172 | var 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 | |||
3364 | var 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 | |||
3423 | var 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 | |||
4338 | exports.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 | |||
4349 | var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType; | ||
4350 | var AnnotationType = sharedUtil.AnnotationType; | ||
4351 | var Util = sharedUtil.Util; | ||
4352 | var addLinkAttributes = displayDOMUtils.addLinkAttributes; | ||
4353 | var LinkTarget = displayDOMUtils.LinkTarget; | ||
4354 | var getFilenameFromUrl = displayDOMUtils.getFilenameFromUrl; | ||
4355 | var warn = sharedUtil.warn; | ||
4356 | var CustomStyle = displayDOMUtils.CustomStyle; | ||
4357 | var 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 | */ | ||
4375 | function AnnotationElementFactory() {} | ||
4376 | AnnotationElementFactory.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 | */ | ||
4429 | var 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 | */ | ||
4593 | var 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 | */ | ||
4678 | var 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 | */ | ||
4721 | var 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 | */ | ||
4749 | var 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 | */ | ||
4854 | var 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 | */ | ||
4906 | var 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 | */ | ||
5044 | var 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 | */ | ||
5078 | var 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 | */ | ||
5112 | var 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 | */ | ||
5145 | var 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 | */ | ||
5179 | var 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 | */ | ||
5246 | var 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 | |||
5304 | exports.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 | |||
5315 | var Util = sharedUtil.Util; | ||
5316 | var createPromiseCapability = sharedUtil.createPromiseCapability; | ||
5317 | var CustomStyle = displayDOMUtils.CustomStyle; | ||
5318 | var 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 | */ | ||
5337 | var 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 | |||
5921 | exports.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 | |||
5932 | var shadow = sharedUtil.shadow; | ||
5933 | var getDefaultSetting = displayDOMUtils.getDefaultSetting; | ||
5934 | |||
5935 | var 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 | |||
6352 | exports.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 | |||
6363 | var Util = sharedUtil.Util; | ||
6364 | var info = sharedUtil.info; | ||
6365 | var isArray = sharedUtil.isArray; | ||
6366 | var error = sharedUtil.error; | ||
6367 | var WebGLUtils = displayWebGL.WebGLUtils; | ||
6368 | |||
6369 | var ShadingIRs = {}; | ||
6370 | |||
6371 | ShadingIRs.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 | |||
6399 | var 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 | |||
6573 | ShadingIRs.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 | |||
6624 | ShadingIRs.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 | |||
6635 | function 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 | |||
6643 | var 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 | |||
6784 | exports.getShadingPatternFromIR = getShadingPatternFromIR; | ||
6785 | exports.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 | |||
6798 | var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX; | ||
6799 | var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX; | ||
6800 | var ImageKind = sharedUtil.ImageKind; | ||
6801 | var OPS = sharedUtil.OPS; | ||
6802 | var TextRenderingMode = sharedUtil.TextRenderingMode; | ||
6803 | var Uint32ArrayView = sharedUtil.Uint32ArrayView; | ||
6804 | var Util = sharedUtil.Util; | ||
6805 | var assert = sharedUtil.assert; | ||
6806 | var info = sharedUtil.info; | ||
6807 | var isNum = sharedUtil.isNum; | ||
6808 | var isArray = sharedUtil.isArray; | ||
6809 | var isLittleEndian = sharedUtil.isLittleEndian; | ||
6810 | var error = sharedUtil.error; | ||
6811 | var shadow = sharedUtil.shadow; | ||
6812 | var warn = sharedUtil.warn; | ||
6813 | var TilingPattern = displayPatternHelper.TilingPattern; | ||
6814 | var getShadingPatternFromIR = displayPatternHelper.getShadingPatternFromIR; | ||
6815 | var WebGLUtils = displayWebGL.WebGLUtils; | ||
6816 | var 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. | ||
6822 | var MIN_FONT_SIZE = 16; | ||
6823 | // Maximum font size that would be used during canvas fillText operations. | ||
6824 | var MAX_FONT_SIZE = 100; | ||
6825 | var MAX_GROUP_SIZE = 4096; | ||
6826 | |||
6827 | // Heuristic value used when enforcing minimum line widths. | ||
6828 | var MIN_WIDTH_FACTOR = 0.65; | ||
6829 | |||
6830 | var COMPILE_TYPE3_GLYPHS = true; | ||
6831 | var MAX_SIZE_TO_COMPILE = 1000; | ||
6832 | |||
6833 | var FULL_CHUNK_HEIGHT = 16; | ||
6834 | |||
6835 | var HasCanvasTypedArraysCached = { | ||
6836 | get value() { | ||
6837 | return shadow(HasCanvasTypedArraysCached, 'value', hasCanvasTypedArrays()); | ||
6838 | } | ||
6839 | }; | ||
6840 | |||
6841 | var IsLittleEndianCached = { | ||
6842 | get value() { | ||
6843 | return shadow(IsLittleEndianCached, 'value', isLittleEndian()); | ||
6844 | } | ||
6845 | }; | ||
6846 | |||
6847 | function createScratchCanvas(width, height) { | ||
6848 | var canvas = document.createElement('canvas'); | ||
6849 | canvas.width = width; | ||
6850 | canvas.height = height; | ||
6851 | return canvas; | ||
6852 | } | ||
6853 | |||
6854 | function 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 | |||
6970 | var 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 | |||
7008 | function 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 | |||
7168 | var 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 | |||
7216 | var 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 | |||
9088 | exports.CanvasGraphics = CanvasGraphics; | ||
9089 | exports.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 | |||
9102 | var InvalidPDFException = sharedUtil.InvalidPDFException; | ||
9103 | var MessageHandler = sharedUtil.MessageHandler; | ||
9104 | var MissingPDFException = sharedUtil.MissingPDFException; | ||
9105 | var PageViewport = sharedUtil.PageViewport; | ||
9106 | var PasswordResponses = sharedUtil.PasswordResponses; | ||
9107 | var PasswordException = sharedUtil.PasswordException; | ||
9108 | var StatTimer = sharedUtil.StatTimer; | ||
9109 | var UnexpectedResponseException = sharedUtil.UnexpectedResponseException; | ||
9110 | var UnknownErrorException = sharedUtil.UnknownErrorException; | ||
9111 | var Util = sharedUtil.Util; | ||
9112 | var createPromiseCapability = sharedUtil.createPromiseCapability; | ||
9113 | var error = sharedUtil.error; | ||
9114 | var deprecated = sharedUtil.deprecated; | ||
9115 | var getVerbosityLevel = sharedUtil.getVerbosityLevel; | ||
9116 | var info = sharedUtil.info; | ||
9117 | var isInt = sharedUtil.isInt; | ||
9118 | var isArray = sharedUtil.isArray; | ||
9119 | var isArrayBuffer = sharedUtil.isArrayBuffer; | ||
9120 | var isSameOrigin = sharedUtil.isSameOrigin; | ||
9121 | var loadJpegStream = sharedUtil.loadJpegStream; | ||
9122 | var stringToBytes = sharedUtil.stringToBytes; | ||
9123 | var globalScope = sharedUtil.globalScope; | ||
9124 | var warn = sharedUtil.warn; | ||
9125 | var FontFaceObject = displayFontLoader.FontFaceObject; | ||
9126 | var FontLoader = displayFontLoader.FontLoader; | ||
9127 | var CanvasGraphics = displayCanvas.CanvasGraphics; | ||
9128 | var createScratchCanvas = displayCanvas.createScratchCanvas; | ||
9129 | var Metadata = displayMetadata.Metadata; | ||
9130 | var getDefaultSetting = displayDOMUtils.getDefaultSetting; | ||
9131 | |||
9132 | var DEFAULT_RANGE_CHUNK_SIZE = 65536; // 2^16 = 65536 | ||
9133 | |||
9134 | var isWorkerDisabled = false; | ||
9135 | var workerSrc; | ||
9136 | var isPostMessageTransfersDisabled = false; | ||
9137 | |||
9138 | |||
9139 | var useRequireEnsure = false; | ||
9140 | if (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 | } | ||
9148 | if (typeof __webpack_require__ !== 'undefined') { | ||
9149 | useRequireEnsure = true; | ||
9150 | } | ||
9151 | if (typeof requirejs !== 'undefined' && requirejs.toUrl) { | ||
9152 | workerSrc = requirejs.toUrl('pdfjs-dist/build/pdf.worker.js'); | ||
9153 | } | ||
9154 | var dynamicLoaderSupported = typeof requirejs !== 'undefined' && requirejs.load; | ||
9155 | var 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 | */ | ||
9226 | function 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 | */ | ||
9340 | function _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 | */ | ||
9376 | var 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 | */ | ||
9469 | var 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 | */ | ||
9543 | var 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 | */ | ||
9772 | var 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 | */ | ||
10112 | var 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 | */ | ||
10437 | var 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 | */ | ||
10897 | var 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 | */ | ||
11001 | var 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 | */ | ||
11052 | var 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 | */ | ||
11173 | var _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 | |||
11189 | if (typeof pdfjsVersion !== 'undefined') { | ||
11190 | exports.version = pdfjsVersion; | ||
11191 | } | ||
11192 | if (typeof pdfjsBuild !== 'undefined') { | ||
11193 | exports.build = pdfjsBuild; | ||
11194 | } | ||
11195 | |||
11196 | exports.getDocument = getDocument; | ||
11197 | exports.PDFDataRangeTransport = PDFDataRangeTransport; | ||
11198 | exports.PDFWorker = PDFWorker; | ||
11199 | exports.PDFDocumentProxy = PDFDocumentProxy; | ||
11200 | exports.PDFPageProxy = PDFPageProxy; | ||
11201 | exports._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 | |||