Total Complexity | 1339 |
Complexity/F | 2.52 |
Lines of Code | 8031 |
Function Count | 531 |
Duplicated Lines | 143 |
Ratio | 1.78 % |
Changes | 3 | ||
Bugs | 0 | Features | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like laraview/pdf.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
||
21 | if (typeof PDFJS === 'undefined') { |
||
22 | (typeof window !== 'undefined' ? window : this).PDFJS = {}; |
||
23 | } |
||
24 | |||
25 | PDFJS.version = '1.1.114'; |
||
26 | PDFJS.build = '3fd44fd'; |
||
27 | |||
28 | (function pdfjsWrapper() { |
||
29 | // Use strict in our context only - users might not want it |
||
30 | 'use strict'; |
||
31 | |||
32 | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
||
33 | /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
||
34 | /* Copyright 2012 Mozilla Foundation |
||
35 | * |
||
36 | * Licensed under the Apache License, Version 2.0 (the "License"); |
||
37 | * you may not use this file except in compliance with the License. |
||
38 | * You may obtain a copy of the License at |
||
39 | * |
||
40 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
41 | * |
||
42 | * Unless required by applicable law or agreed to in writing, software |
||
43 | * distributed under the License is distributed on an "AS IS" BASIS, |
||
44 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||
45 | * See the License for the specific language governing permissions and |
||
46 | * limitations under the License. |
||
47 | */ |
||
48 | /* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL, |
||
49 | Promise */ |
||
50 | |||
51 | 'use strict'; |
||
52 | |||
53 | var globalScope = (typeof window === 'undefined') ? this : window; |
||
54 | |||
55 | var isWorker = (typeof window === 'undefined'); |
||
56 | |||
57 | var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; |
||
58 | |||
59 | var TextRenderingMode = { |
||
60 | FILL: 0, |
||
61 | STROKE: 1, |
||
62 | FILL_STROKE: 2, |
||
63 | INVISIBLE: 3, |
||
64 | FILL_ADD_TO_PATH: 4, |
||
65 | STROKE_ADD_TO_PATH: 5, |
||
66 | FILL_STROKE_ADD_TO_PATH: 6, |
||
67 | ADD_TO_PATH: 7, |
||
68 | FILL_STROKE_MASK: 3, |
||
69 | ADD_TO_PATH_FLAG: 4 |
||
70 | }; |
||
71 | |||
72 | var ImageKind = { |
||
73 | GRAYSCALE_1BPP: 1, |
||
74 | RGB_24BPP: 2, |
||
75 | RGBA_32BPP: 3 |
||
76 | }; |
||
77 | |||
78 | var AnnotationType = { |
||
|
|||
79 | WIDGET: 1, |
||
80 | TEXT: 2, |
||
81 | LINK: 3 |
||
82 | }; |
||
83 | |||
84 | var StreamType = { |
||
85 | UNKNOWN: 0, |
||
86 | FLATE: 1, |
||
87 | LZW: 2, |
||
88 | DCT: 3, |
||
89 | JPX: 4, |
||
90 | JBIG: 5, |
||
91 | A85: 6, |
||
92 | AHX: 7, |
||
93 | CCF: 8, |
||
94 | RL: 9 |
||
95 | }; |
||
96 | |||
97 | var FontType = { |
||
98 | UNKNOWN: 0, |
||
99 | TYPE1: 1, |
||
100 | TYPE1C: 2, |
||
101 | CIDFONTTYPE0: 3, |
||
102 | CIDFONTTYPE0C: 4, |
||
103 | TRUETYPE: 5, |
||
104 | CIDFONTTYPE2: 6, |
||
105 | TYPE3: 7, |
||
106 | OPENTYPE: 8, |
||
107 | TYPE0: 9, |
||
108 | MMTYPE1: 10 |
||
109 | }; |
||
110 | |||
111 | // The global PDFJS object exposes the API |
||
112 | // In production, it will be declared outside a global wrapper |
||
113 | // In development, it will be declared here |
||
114 | if (!globalScope.PDFJS) { |
||
115 | globalScope.PDFJS = {}; |
||
116 | } |
||
117 | |||
118 | globalScope.PDFJS.pdfBug = false; |
||
119 | |||
120 | PDFJS.VERBOSITY_LEVELS = { |
||
121 | errors: 0, |
||
122 | warnings: 1, |
||
123 | infos: 5 |
||
124 | }; |
||
125 | |||
126 | // All the possible operations for an operator list. |
||
127 | var OPS = PDFJS.OPS = { |
||
128 | // Intentionally start from 1 so it is easy to spot bad operators that will be |
||
129 | // 0's. |
||
130 | dependency: 1, |
||
131 | setLineWidth: 2, |
||
132 | setLineCap: 3, |
||
133 | setLineJoin: 4, |
||
134 | setMiterLimit: 5, |
||
135 | setDash: 6, |
||
136 | setRenderingIntent: 7, |
||
137 | setFlatness: 8, |
||
138 | setGState: 9, |
||
139 | save: 10, |
||
140 | restore: 11, |
||
141 | transform: 12, |
||
142 | moveTo: 13, |
||
143 | lineTo: 14, |
||
144 | curveTo: 15, |
||
145 | curveTo2: 16, |
||
146 | curveTo3: 17, |
||
147 | closePath: 18, |
||
148 | rectangle: 19, |
||
149 | stroke: 20, |
||
150 | closeStroke: 21, |
||
151 | fill: 22, |
||
152 | eoFill: 23, |
||
153 | fillStroke: 24, |
||
154 | eoFillStroke: 25, |
||
155 | closeFillStroke: 26, |
||
156 | closeEOFillStroke: 27, |
||
157 | endPath: 28, |
||
158 | clip: 29, |
||
159 | eoClip: 30, |
||
160 | beginText: 31, |
||
161 | endText: 32, |
||
162 | setCharSpacing: 33, |
||
163 | setWordSpacing: 34, |
||
164 | setHScale: 35, |
||
165 | setLeading: 36, |
||
166 | setFont: 37, |
||
167 | setTextRenderingMode: 38, |
||
168 | setTextRise: 39, |
||
169 | moveText: 40, |
||
170 | setLeadingMoveText: 41, |
||
171 | setTextMatrix: 42, |
||
172 | nextLine: 43, |
||
173 | showText: 44, |
||
174 | showSpacedText: 45, |
||
175 | nextLineShowText: 46, |
||
176 | nextLineSetSpacingShowText: 47, |
||
177 | setCharWidth: 48, |
||
178 | setCharWidthAndBounds: 49, |
||
179 | setStrokeColorSpace: 50, |
||
180 | setFillColorSpace: 51, |
||
181 | setStrokeColor: 52, |
||
182 | setStrokeColorN: 53, |
||
183 | setFillColor: 54, |
||
184 | setFillColorN: 55, |
||
185 | setStrokeGray: 56, |
||
186 | setFillGray: 57, |
||
187 | setStrokeRGBColor: 58, |
||
188 | setFillRGBColor: 59, |
||
189 | setStrokeCMYKColor: 60, |
||
190 | setFillCMYKColor: 61, |
||
191 | shadingFill: 62, |
||
192 | beginInlineImage: 63, |
||
193 | beginImageData: 64, |
||
194 | endInlineImage: 65, |
||
195 | paintXObject: 66, |
||
196 | markPoint: 67, |
||
197 | markPointProps: 68, |
||
198 | beginMarkedContent: 69, |
||
199 | beginMarkedContentProps: 70, |
||
200 | endMarkedContent: 71, |
||
201 | beginCompat: 72, |
||
202 | endCompat: 73, |
||
203 | paintFormXObjectBegin: 74, |
||
204 | paintFormXObjectEnd: 75, |
||
205 | beginGroup: 76, |
||
206 | endGroup: 77, |
||
207 | beginAnnotations: 78, |
||
208 | endAnnotations: 79, |
||
209 | beginAnnotation: 80, |
||
210 | endAnnotation: 81, |
||
211 | paintJpegXObject: 82, |
||
212 | paintImageMaskXObject: 83, |
||
213 | paintImageMaskXObjectGroup: 84, |
||
214 | paintImageXObject: 85, |
||
215 | paintInlineImageXObject: 86, |
||
216 | paintInlineImageXObjectGroup: 87, |
||
217 | paintImageXObjectRepeat: 88, |
||
218 | paintImageMaskXObjectRepeat: 89, |
||
219 | paintSolidColorImageMask: 90, |
||
220 | constructPath: 91 |
||
221 | }; |
||
222 | |||
223 | // A notice for devs. These are good for things that are helpful to devs, such |
||
224 | // as warning that Workers were disabled, which is important to devs but not |
||
225 | // end users. |
||
226 | function info(msg) { |
||
227 | if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) { |
||
228 | console.log('Info: ' + msg); |
||
229 | } |
||
230 | } |
||
231 | |||
232 | // Non-fatal warnings. |
||
233 | function warn(msg) { |
||
234 | if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) { |
||
235 | console.log('Warning: ' + msg); |
||
236 | } |
||
237 | } |
||
238 | |||
239 | // Fatal errors that should trigger the fallback UI and halt execution by |
||
240 | // throwing an exception. |
||
241 | function error(msg) { |
||
242 | if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.errors) { |
||
243 | console.log('Error: ' + msg); |
||
244 | console.log(backtrace()); |
||
245 | } |
||
246 | UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown); |
||
247 | throw new Error(msg); |
||
248 | } |
||
249 | |||
250 | function backtrace() { |
||
251 | try { |
||
252 | throw new Error(); |
||
253 | } catch (e) { |
||
254 | return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; |
||
255 | } |
||
256 | } |
||
257 | |||
258 | function assert(cond, msg) { |
||
259 | if (!cond) { |
||
260 | error(msg); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = { |
||
265 | unknown: 'unknown', |
||
266 | forms: 'forms', |
||
267 | javaScript: 'javaScript', |
||
268 | smask: 'smask', |
||
269 | shadingPattern: 'shadingPattern', |
||
270 | font: 'font' |
||
271 | }; |
||
272 | |||
273 | var UnsupportedManager = PDFJS.UnsupportedManager = |
||
274 | (function UnsupportedManagerClosure() { |
||
275 | var listeners = []; |
||
276 | return { |
||
277 | listen: function (cb) { |
||
278 | listeners.push(cb); |
||
279 | }, |
||
280 | notify: function (featureId) { |
||
281 | warn('Unsupported feature "' + featureId + '"'); |
||
282 | for (var i = 0, ii = listeners.length; i < ii; i++) { |
||
283 | listeners[i](featureId); |
||
284 | } |
||
285 | } |
||
286 | }; |
||
287 | })(); |
||
288 | |||
289 | // Combines two URLs. The baseUrl shall be absolute URL. If the url is an |
||
290 | // absolute URL, it will be returned as is. |
||
291 | function combineUrl(baseUrl, url) { |
||
292 | if (!url) { |
||
293 | return baseUrl; |
||
294 | } |
||
295 | if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) { |
||
296 | return url; |
||
297 | } |
||
298 | var i; |
||
299 | if (url.charAt(0) === '/') { |
||
300 | // absolute path |
||
301 | i = baseUrl.indexOf('://'); |
||
302 | if (url.charAt(1) === '/') { |
||
303 | ++i; |
||
304 | } else { |
||
305 | i = baseUrl.indexOf('/', i + 3); |
||
306 | } |
||
307 | return baseUrl.substring(0, i) + url; |
||
308 | } else { |
||
309 | // relative path |
||
310 | var pathLength = baseUrl.length; |
||
311 | i = baseUrl.lastIndexOf('#'); |
||
312 | pathLength = i >= 0 ? i : pathLength; |
||
313 | i = baseUrl.lastIndexOf('?', pathLength); |
||
314 | pathLength = i >= 0 ? i : pathLength; |
||
315 | var prefixLength = baseUrl.lastIndexOf('/', pathLength); |
||
316 | return baseUrl.substring(0, prefixLength + 1) + url; |
||
317 | } |
||
318 | } |
||
319 | |||
320 | // Validates if URL is safe and allowed, e.g. to avoid XSS. |
||
321 | function isValidUrl(url, allowRelative) { |
||
322 | if (!url) { |
||
323 | return false; |
||
324 | } |
||
325 | // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) |
||
326 | // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) |
||
327 | var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); |
||
328 | if (!protocol) { |
||
329 | return allowRelative; |
||
330 | } |
||
331 | protocol = protocol[0].toLowerCase(); |
||
332 | switch (protocol) { |
||
333 | case 'http': |
||
334 | case 'https': |
||
335 | case 'ftp': |
||
336 | case 'mailto': |
||
337 | case 'tel': |
||
338 | return true; |
||
339 | default: |
||
340 | return false; |
||
341 | } |
||
342 | } |
||
343 | PDFJS.isValidUrl = isValidUrl; |
||
344 | |||
345 | function shadow(obj, prop, value) { |
||
346 | Object.defineProperty(obj, prop, { value: value, |
||
347 | enumerable: true, |
||
348 | configurable: true, |
||
349 | writable: false }); |
||
350 | return value; |
||
351 | } |
||
352 | PDFJS.shadow = shadow; |
||
353 | |||
354 | var PasswordResponses = PDFJS.PasswordResponses = { |
||
355 | NEED_PASSWORD: 1, |
||
356 | INCORRECT_PASSWORD: 2 |
||
357 | }; |
||
358 | |||
359 | var PasswordException = (function PasswordExceptionClosure() { |
||
360 | function PasswordException(msg, code) { |
||
361 | this.name = 'PasswordException'; |
||
362 | this.message = msg; |
||
363 | this.code = code; |
||
364 | } |
||
365 | |||
366 | PasswordException.prototype = new Error(); |
||
367 | PasswordException.constructor = PasswordException; |
||
368 | |||
369 | return PasswordException; |
||
370 | })(); |
||
371 | PDFJS.PasswordException = PasswordException; |
||
372 | |||
373 | var UnknownErrorException = (function UnknownErrorExceptionClosure() { |
||
374 | function UnknownErrorException(msg, details) { |
||
375 | this.name = 'UnknownErrorException'; |
||
376 | this.message = msg; |
||
377 | this.details = details; |
||
378 | } |
||
379 | |||
380 | UnknownErrorException.prototype = new Error(); |
||
381 | UnknownErrorException.constructor = UnknownErrorException; |
||
382 | |||
383 | return UnknownErrorException; |
||
384 | })(); |
||
385 | PDFJS.UnknownErrorException = UnknownErrorException; |
||
386 | |||
387 | var InvalidPDFException = (function InvalidPDFExceptionClosure() { |
||
388 | function InvalidPDFException(msg) { |
||
389 | this.name = 'InvalidPDFException'; |
||
390 | this.message = msg; |
||
391 | } |
||
392 | |||
393 | InvalidPDFException.prototype = new Error(); |
||
394 | InvalidPDFException.constructor = InvalidPDFException; |
||
395 | |||
396 | return InvalidPDFException; |
||
397 | })(); |
||
398 | PDFJS.InvalidPDFException = InvalidPDFException; |
||
399 | |||
400 | var MissingPDFException = (function MissingPDFExceptionClosure() { |
||
401 | function MissingPDFException(msg) { |
||
402 | this.name = 'MissingPDFException'; |
||
403 | this.message = msg; |
||
404 | } |
||
405 | |||
406 | MissingPDFException.prototype = new Error(); |
||
407 | MissingPDFException.constructor = MissingPDFException; |
||
408 | |||
409 | return MissingPDFException; |
||
410 | })(); |
||
411 | PDFJS.MissingPDFException = MissingPDFException; |
||
412 | |||
413 | var UnexpectedResponseException = |
||
414 | (function UnexpectedResponseExceptionClosure() { |
||
415 | function UnexpectedResponseException(msg, status) { |
||
416 | this.name = 'UnexpectedResponseException'; |
||
417 | this.message = msg; |
||
418 | this.status = status; |
||
419 | } |
||
420 | |||
421 | UnexpectedResponseException.prototype = new Error(); |
||
422 | UnexpectedResponseException.constructor = UnexpectedResponseException; |
||
423 | |||
424 | return UnexpectedResponseException; |
||
425 | })(); |
||
426 | PDFJS.UnexpectedResponseException = UnexpectedResponseException; |
||
427 | |||
428 | var NotImplementedException = (function NotImplementedExceptionClosure() { |
||
429 | function NotImplementedException(msg) { |
||
430 | this.message = msg; |
||
431 | } |
||
432 | |||
433 | NotImplementedException.prototype = new Error(); |
||
434 | NotImplementedException.prototype.name = 'NotImplementedException'; |
||
435 | NotImplementedException.constructor = NotImplementedException; |
||
436 | |||
437 | return NotImplementedException; |
||
438 | })(); |
||
439 | |||
440 | var MissingDataException = (function MissingDataExceptionClosure() { |
||
441 | function MissingDataException(begin, end) { |
||
442 | this.begin = begin; |
||
443 | this.end = end; |
||
444 | this.message = 'Missing data [' + begin + ', ' + end + ')'; |
||
445 | } |
||
446 | |||
447 | MissingDataException.prototype = new Error(); |
||
448 | MissingDataException.prototype.name = 'MissingDataException'; |
||
449 | MissingDataException.constructor = MissingDataException; |
||
450 | |||
451 | return MissingDataException; |
||
452 | })(); |
||
453 | |||
454 | var XRefParseException = (function XRefParseExceptionClosure() { |
||
455 | function XRefParseException(msg) { |
||
456 | this.message = msg; |
||
457 | } |
||
458 | |||
459 | XRefParseException.prototype = new Error(); |
||
460 | XRefParseException.prototype.name = 'XRefParseException'; |
||
461 | XRefParseException.constructor = XRefParseException; |
||
462 | |||
463 | return XRefParseException; |
||
464 | })(); |
||
465 | |||
466 | |||
467 | function bytesToString(bytes) { |
||
468 | assert(bytes !== null && typeof bytes === 'object' && |
||
469 | bytes.length !== undefined, 'Invalid argument for bytesToString'); |
||
470 | var length = bytes.length; |
||
471 | var MAX_ARGUMENT_COUNT = 8192; |
||
472 | if (length < MAX_ARGUMENT_COUNT) { |
||
473 | return String.fromCharCode.apply(null, bytes); |
||
474 | } |
||
475 | var strBuf = []; |
||
476 | for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) { |
||
477 | var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); |
||
478 | var chunk = bytes.subarray(i, chunkEnd); |
||
479 | strBuf.push(String.fromCharCode.apply(null, chunk)); |
||
480 | } |
||
481 | return strBuf.join(''); |
||
482 | } |
||
483 | |||
484 | function stringToBytes(str) { |
||
485 | assert(typeof str === 'string', 'Invalid argument for stringToBytes'); |
||
486 | var length = str.length; |
||
487 | var bytes = new Uint8Array(length); |
||
488 | for (var i = 0; i < length; ++i) { |
||
489 | bytes[i] = str.charCodeAt(i) & 0xFF; |
||
490 | } |
||
491 | return bytes; |
||
492 | } |
||
493 | |||
494 | function string32(value) { |
||
495 | return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff, |
||
496 | (value >> 8) & 0xff, value & 0xff); |
||
497 | } |
||
498 | |||
499 | function log2(x) { |
||
500 | var n = 1, i = 0; |
||
501 | while (x > n) { |
||
502 | n <<= 1; |
||
503 | i++; |
||
504 | } |
||
505 | return i; |
||
506 | } |
||
507 | |||
508 | function readInt8(data, start) { |
||
509 | return (data[start] << 24) >> 24; |
||
510 | } |
||
511 | |||
512 | function readUint16(data, offset) { |
||
513 | return (data[offset] << 8) | data[offset + 1]; |
||
514 | } |
||
515 | |||
516 | function readUint32(data, offset) { |
||
517 | return ((data[offset] << 24) | (data[offset + 1] << 16) | |
||
518 | (data[offset + 2] << 8) | data[offset + 3]) >>> 0; |
||
519 | } |
||
520 | |||
521 | // Lazy test the endianness of the platform |
||
522 | // NOTE: This will be 'true' for simulated TypedArrays |
||
523 | function isLittleEndian() { |
||
524 | var buffer8 = new Uint8Array(2); |
||
525 | buffer8[0] = 1; |
||
526 | var buffer16 = new Uint16Array(buffer8.buffer); |
||
527 | return (buffer16[0] === 1); |
||
528 | } |
||
529 | |||
530 | Object.defineProperty(PDFJS, 'isLittleEndian', { |
||
531 | configurable: true, |
||
532 | get: function PDFJS_isLittleEndian() { |
||
533 | return shadow(PDFJS, 'isLittleEndian', isLittleEndian()); |
||
534 | } |
||
535 | }); |
||
536 | |||
537 | //#if !(FIREFOX || MOZCENTRAL || B2G || CHROME) |
||
538 | //// Lazy test if the userAgant support CanvasTypedArrays |
||
539 | function hasCanvasTypedArrays() { |
||
540 | var canvas = document.createElement('canvas'); |
||
541 | canvas.width = canvas.height = 1; |
||
542 | var ctx = canvas.getContext('2d'); |
||
543 | var imageData = ctx.createImageData(1, 1); |
||
544 | return (typeof imageData.data.buffer !== 'undefined'); |
||
545 | } |
||
546 | |||
547 | Object.defineProperty(PDFJS, 'hasCanvasTypedArrays', { |
||
548 | configurable: true, |
||
549 | get: function PDFJS_hasCanvasTypedArrays() { |
||
550 | return shadow(PDFJS, 'hasCanvasTypedArrays', hasCanvasTypedArrays()); |
||
551 | } |
||
552 | }); |
||
553 | |||
554 | var Uint32ArrayView = (function Uint32ArrayViewClosure() { |
||
555 | |||
556 | function Uint32ArrayView(buffer, length) { |
||
557 | this.buffer = buffer; |
||
558 | this.byteLength = buffer.length; |
||
559 | this.length = length === undefined ? (this.byteLength >> 2) : length; |
||
560 | ensureUint32ArrayViewProps(this.length); |
||
561 | } |
||
562 | Uint32ArrayView.prototype = Object.create(null); |
||
563 | |||
564 | var uint32ArrayViewSetters = 0; |
||
565 | function createUint32ArrayProp(index) { |
||
566 | return { |
||
567 | get: function () { |
||
568 | var buffer = this.buffer, offset = index << 2; |
||
569 | return (buffer[offset] | (buffer[offset + 1] << 8) | |
||
570 | (buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0; |
||
571 | }, |
||
572 | set: function (value) { |
||
573 | var buffer = this.buffer, offset = index << 2; |
||
574 | buffer[offset] = value & 255; |
||
575 | buffer[offset + 1] = (value >> 8) & 255; |
||
576 | buffer[offset + 2] = (value >> 16) & 255; |
||
577 | buffer[offset + 3] = (value >>> 24) & 255; |
||
578 | } |
||
579 | }; |
||
580 | } |
||
581 | |||
582 | function ensureUint32ArrayViewProps(length) { |
||
583 | while (uint32ArrayViewSetters < length) { |
||
584 | Object.defineProperty(Uint32ArrayView.prototype, |
||
585 | uint32ArrayViewSetters, |
||
586 | createUint32ArrayProp(uint32ArrayViewSetters)); |
||
587 | uint32ArrayViewSetters++; |
||
588 | } |
||
589 | } |
||
590 | |||
591 | return Uint32ArrayView; |
||
592 | })(); |
||
593 | //#else |
||
594 | //PDFJS.hasCanvasTypedArrays = true; |
||
595 | //#endif |
||
596 | |||
597 | var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; |
||
598 | |||
599 | var Util = PDFJS.Util = (function UtilClosure() { |
||
600 | function Util() {} |
||
601 | |||
602 | var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')']; |
||
603 | |||
604 | // makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids |
||
605 | // creating many intermediate strings. |
||
606 | Util.makeCssRgb = function Util_makeCssRgb(r, g, b) { |
||
607 | rgbBuf[1] = r; |
||
608 | rgbBuf[3] = g; |
||
609 | rgbBuf[5] = b; |
||
610 | return rgbBuf.join(''); |
||
611 | }; |
||
612 | |||
613 | // Concatenates two transformation matrices together and returns the result. |
||
614 | Util.transform = function Util_transform(m1, m2) { |
||
615 | return [ |
||
616 | m1[0] * m2[0] + m1[2] * m2[1], |
||
617 | m1[1] * m2[0] + m1[3] * m2[1], |
||
618 | m1[0] * m2[2] + m1[2] * m2[3], |
||
619 | m1[1] * m2[2] + m1[3] * m2[3], |
||
620 | m1[0] * m2[4] + m1[2] * m2[5] + m1[4], |
||
621 | m1[1] * m2[4] + m1[3] * m2[5] + m1[5] |
||
622 | ]; |
||
623 | }; |
||
624 | |||
625 | // For 2d affine transforms |
||
626 | Util.applyTransform = function Util_applyTransform(p, m) { |
||
627 | var xt = p[0] * m[0] + p[1] * m[2] + m[4]; |
||
628 | var yt = p[0] * m[1] + p[1] * m[3] + m[5]; |
||
629 | return [xt, yt]; |
||
630 | }; |
||
631 | |||
632 | Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { |
||
633 | var d = m[0] * m[3] - m[1] * m[2]; |
||
634 | var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; |
||
635 | var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; |
||
636 | return [xt, yt]; |
||
637 | }; |
||
638 | |||
639 | // Applies the transform to the rectangle and finds the minimum axially |
||
640 | // aligned bounding box. |
||
641 | Util.getAxialAlignedBoundingBox = |
||
642 | function Util_getAxialAlignedBoundingBox(r, m) { |
||
643 | |||
644 | var p1 = Util.applyTransform(r, m); |
||
645 | var p2 = Util.applyTransform(r.slice(2, 4), m); |
||
646 | var p3 = Util.applyTransform([r[0], r[3]], m); |
||
647 | var p4 = Util.applyTransform([r[2], r[1]], m); |
||
648 | return [ |
||
649 | Math.min(p1[0], p2[0], p3[0], p4[0]), |
||
650 | Math.min(p1[1], p2[1], p3[1], p4[1]), |
||
651 | Math.max(p1[0], p2[0], p3[0], p4[0]), |
||
652 | Math.max(p1[1], p2[1], p3[1], p4[1]) |
||
653 | ]; |
||
654 | }; |
||
655 | |||
656 | Util.inverseTransform = function Util_inverseTransform(m) { |
||
657 | var d = m[0] * m[3] - m[1] * m[2]; |
||
658 | return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, |
||
659 | (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; |
||
660 | }; |
||
661 | |||
662 | // Apply a generic 3d matrix M on a 3-vector v: |
||
663 | // | a b c | | X | |
||
664 | // | d e f | x | Y | |
||
665 | // | g h i | | Z | |
||
666 | // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], |
||
667 | // with v as [X,Y,Z] |
||
668 | Util.apply3dTransform = function Util_apply3dTransform(m, v) { |
||
669 | return [ |
||
670 | m[0] * v[0] + m[1] * v[1] + m[2] * v[2], |
||
671 | m[3] * v[0] + m[4] * v[1] + m[5] * v[2], |
||
672 | m[6] * v[0] + m[7] * v[1] + m[8] * v[2] |
||
673 | ]; |
||
674 | }; |
||
675 | |||
676 | // This calculation uses Singular Value Decomposition. |
||
677 | // The SVD can be represented with formula A = USV. We are interested in the |
||
678 | // matrix S here because it represents the scale values. |
||
679 | Util.singularValueDecompose2dScale = |
||
680 | function Util_singularValueDecompose2dScale(m) { |
||
681 | |||
682 | var transpose = [m[0], m[2], m[1], m[3]]; |
||
683 | |||
684 | // Multiply matrix m with its transpose. |
||
685 | var a = m[0] * transpose[0] + m[1] * transpose[2]; |
||
686 | var b = m[0] * transpose[1] + m[1] * transpose[3]; |
||
687 | var c = m[2] * transpose[0] + m[3] * transpose[2]; |
||
688 | var d = m[2] * transpose[1] + m[3] * transpose[3]; |
||
689 | |||
690 | // Solve the second degree polynomial to get roots. |
||
691 | var first = (a + d) / 2; |
||
692 | var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2; |
||
693 | var sx = first + second || 1; |
||
694 | var sy = first - second || 1; |
||
695 | |||
696 | // Scale values are the square roots of the eigenvalues. |
||
697 | return [Math.sqrt(sx), Math.sqrt(sy)]; |
||
698 | }; |
||
699 | |||
700 | // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2) |
||
701 | // For coordinate systems whose origin lies in the bottom-left, this |
||
702 | // means normalization to (BL,TR) ordering. For systems with origin in the |
||
703 | // top-left, this means (TL,BR) ordering. |
||
704 | Util.normalizeRect = function Util_normalizeRect(rect) { |
||
705 | var r = rect.slice(0); // clone rect |
||
706 | if (rect[0] > rect[2]) { |
||
707 | r[0] = rect[2]; |
||
708 | r[2] = rect[0]; |
||
709 | } |
||
710 | if (rect[1] > rect[3]) { |
||
711 | r[1] = rect[3]; |
||
712 | r[3] = rect[1]; |
||
713 | } |
||
714 | return r; |
||
715 | }; |
||
716 | |||
717 | // Returns a rectangle [x1, y1, x2, y2] corresponding to the |
||
718 | // intersection of rect1 and rect2. If no intersection, returns 'false' |
||
719 | // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2] |
||
720 | Util.intersect = function Util_intersect(rect1, rect2) { |
||
721 | function compare(a, b) { |
||
722 | return a - b; |
||
723 | } |
||
724 | |||
725 | // Order points along the axes |
||
726 | var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare), |
||
727 | orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare), |
||
728 | result = []; |
||
729 | |||
730 | rect1 = Util.normalizeRect(rect1); |
||
731 | rect2 = Util.normalizeRect(rect2); |
||
732 | |||
733 | // X: first and second points belong to different rectangles? |
||
734 | if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) || |
||
735 | (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) { |
||
736 | // Intersection must be between second and third points |
||
737 | result[0] = orderedX[1]; |
||
738 | result[2] = orderedX[2]; |
||
739 | } else { |
||
740 | return false; |
||
741 | } |
||
742 | |||
743 | // Y: first and second points belong to different rectangles? |
||
744 | if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) || |
||
745 | (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) { |
||
746 | // Intersection must be between second and third points |
||
747 | result[1] = orderedY[1]; |
||
748 | result[3] = orderedY[2]; |
||
749 | } else { |
||
750 | return false; |
||
751 | } |
||
752 | |||
753 | return result; |
||
754 | }; |
||
755 | |||
756 | Util.sign = function Util_sign(num) { |
||
757 | return num < 0 ? -1 : 1; |
||
758 | }; |
||
759 | |||
760 | Util.appendToArray = function Util_appendToArray(arr1, arr2) { |
||
761 | Array.prototype.push.apply(arr1, arr2); |
||
762 | }; |
||
763 | |||
764 | Util.prependToArray = function Util_prependToArray(arr1, arr2) { |
||
765 | Array.prototype.unshift.apply(arr1, arr2); |
||
766 | }; |
||
767 | |||
768 | Util.extendObj = function extendObj(obj1, obj2) { |
||
769 | for (var key in obj2) { |
||
770 | obj1[key] = obj2[key]; |
||
771 | } |
||
772 | }; |
||
773 | |||
774 | Util.getInheritableProperty = function Util_getInheritableProperty(dict, |
||
775 | name) { |
||
776 | while (dict && !dict.has(name)) { |
||
777 | dict = dict.get('Parent'); |
||
778 | } |
||
779 | if (!dict) { |
||
780 | return null; |
||
781 | } |
||
782 | return dict.get(name); |
||
783 | }; |
||
784 | |||
785 | Util.inherit = function Util_inherit(sub, base, prototype) { |
||
786 | sub.prototype = Object.create(base.prototype); |
||
787 | sub.prototype.constructor = sub; |
||
788 | for (var prop in prototype) { |
||
789 | sub.prototype[prop] = prototype[prop]; |
||
790 | } |
||
791 | }; |
||
792 | |||
793 | Util.loadScript = function Util_loadScript(src, callback) { |
||
794 | var script = document.createElement('script'); |
||
795 | var loaded = false; |
||
796 | script.setAttribute('src', src); |
||
797 | if (callback) { |
||
798 | script.onload = function() { |
||
799 | if (!loaded) { |
||
800 | callback(); |
||
801 | } |
||
802 | loaded = true; |
||
803 | }; |
||
804 | } |
||
805 | document.getElementsByTagName('head')[0].appendChild(script); |
||
806 | }; |
||
807 | |||
808 | return Util; |
||
809 | })(); |
||
810 | |||
811 | /** |
||
812 | * PDF page viewport created based on scale, rotation and offset. |
||
813 | * @class |
||
814 | * @alias PDFJS.PageViewport |
||
815 | */ |
||
816 | var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { |
||
817 | /** |
||
818 | * @constructor |
||
819 | * @private |
||
820 | * @param viewBox {Array} xMin, yMin, xMax and yMax coordinates. |
||
821 | * @param scale {number} scale of the viewport. |
||
822 | * @param rotation {number} rotations of the viewport in degrees. |
||
823 | * @param offsetX {number} offset X |
||
824 | * @param offsetY {number} offset Y |
||
825 | * @param dontFlip {boolean} if true, axis Y will not be flipped. |
||
826 | */ |
||
827 | function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) { |
||
828 | this.viewBox = viewBox; |
||
829 | this.scale = scale; |
||
830 | this.rotation = rotation; |
||
831 | this.offsetX = offsetX; |
||
832 | this.offsetY = offsetY; |
||
833 | |||
834 | // creating transform to convert pdf coordinate system to the normal |
||
835 | // canvas like coordinates taking in account scale and rotation |
||
836 | var centerX = (viewBox[2] + viewBox[0]) / 2; |
||
837 | var centerY = (viewBox[3] + viewBox[1]) / 2; |
||
838 | var rotateA, rotateB, rotateC, rotateD; |
||
839 | rotation = rotation % 360; |
||
840 | rotation = rotation < 0 ? rotation + 360 : rotation; |
||
841 | switch (rotation) { |
||
842 | case 180: |
||
843 | rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; |
||
844 | break; |
||
845 | case 90: |
||
846 | rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; |
||
847 | break; |
||
848 | case 270: |
||
849 | rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; |
||
850 | break; |
||
851 | //case 0: |
||
852 | default: |
||
853 | rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; |
||
854 | break; |
||
855 | } |
||
856 | |||
857 | if (dontFlip) { |
||
858 | rotateC = -rotateC; rotateD = -rotateD; |
||
859 | } |
||
860 | |||
861 | var offsetCanvasX, offsetCanvasY; |
||
862 | var width, height; |
||
863 | if (rotateA === 0) { |
||
864 | offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; |
||
865 | offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; |
||
866 | width = Math.abs(viewBox[3] - viewBox[1]) * scale; |
||
867 | height = Math.abs(viewBox[2] - viewBox[0]) * scale; |
||
868 | } else { |
||
869 | offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; |
||
870 | offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; |
||
871 | width = Math.abs(viewBox[2] - viewBox[0]) * scale; |
||
872 | height = Math.abs(viewBox[3] - viewBox[1]) * scale; |
||
873 | } |
||
874 | // creating transform for the following operations: |
||
875 | // translate(-centerX, -centerY), rotate and flip vertically, |
||
876 | // scale, and translate(offsetCanvasX, offsetCanvasY) |
||
877 | this.transform = [ |
||
878 | rotateA * scale, |
||
879 | rotateB * scale, |
||
880 | rotateC * scale, |
||
881 | rotateD * scale, |
||
882 | offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, |
||
883 | offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY |
||
884 | ]; |
||
885 | |||
886 | this.width = width; |
||
887 | this.height = height; |
||
888 | this.fontScale = scale; |
||
889 | } |
||
890 | PageViewport.prototype = /** @lends PDFJS.PageViewport.prototype */ { |
||
891 | /** |
||
892 | * Clones viewport with additional properties. |
||
893 | * @param args {Object} (optional) If specified, may contain the 'scale' or |
||
894 | * 'rotation' properties to override the corresponding properties in |
||
895 | * the cloned viewport. |
||
896 | * @returns {PDFJS.PageViewport} Cloned viewport. |
||
897 | */ |
||
898 | clone: function PageViewPort_clone(args) { |
||
899 | args = args || {}; |
||
900 | var scale = 'scale' in args ? args.scale : this.scale; |
||
901 | var rotation = 'rotation' in args ? args.rotation : this.rotation; |
||
902 | return new PageViewport(this.viewBox.slice(), scale, rotation, |
||
903 | this.offsetX, this.offsetY, args.dontFlip); |
||
904 | }, |
||
905 | /** |
||
906 | * Converts PDF point to the viewport coordinates. For examples, useful for |
||
907 | * converting PDF location into canvas pixel coordinates. |
||
908 | * @param x {number} X coordinate. |
||
909 | * @param y {number} Y coordinate. |
||
910 | * @returns {Object} Object that contains 'x' and 'y' properties of the |
||
911 | * point in the viewport coordinate space. |
||
912 | * @see {@link convertToPdfPoint} |
||
913 | * @see {@link convertToViewportRectangle} |
||
914 | */ |
||
915 | convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { |
||
916 | return Util.applyTransform([x, y], this.transform); |
||
917 | }, |
||
918 | /** |
||
919 | * Converts PDF rectangle to the viewport coordinates. |
||
920 | * @param rect {Array} xMin, yMin, xMax and yMax coordinates. |
||
921 | * @returns {Array} Contains corresponding coordinates of the rectangle |
||
922 | * in the viewport coordinate space. |
||
923 | * @see {@link convertToViewportPoint} |
||
924 | */ |
||
925 | convertToViewportRectangle: |
||
926 | function PageViewport_convertToViewportRectangle(rect) { |
||
927 | var tl = Util.applyTransform([rect[0], rect[1]], this.transform); |
||
928 | var br = Util.applyTransform([rect[2], rect[3]], this.transform); |
||
929 | return [tl[0], tl[1], br[0], br[1]]; |
||
930 | }, |
||
931 | /** |
||
932 | * Converts viewport coordinates to the PDF location. For examples, useful |
||
933 | * for converting canvas pixel location into PDF one. |
||
934 | * @param x {number} X coordinate. |
||
935 | * @param y {number} Y coordinate. |
||
936 | * @returns {Object} Object that contains 'x' and 'y' properties of the |
||
937 | * point in the PDF coordinate space. |
||
938 | * @see {@link convertToViewportPoint} |
||
939 | */ |
||
940 | convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { |
||
941 | return Util.applyInverseTransform([x, y], this.transform); |
||
942 | } |
||
943 | }; |
||
944 | return PageViewport; |
||
945 | })(); |
||
946 | |||
947 | var PDFStringTranslateTable = [ |
||
948 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||
949 | 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, |
||
950 | 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, |
||
951 | 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, |
||
952 | 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, |
||
953 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, |
||
954 | 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, |
||
955 | 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, |
||
956 | 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC |
||
957 | ]; |
||
958 | |||
959 | function stringToPDFString(str) { |
||
960 | var i, n = str.length, strBuf = []; |
||
961 | if (str[0] === '\xFE' && str[1] === '\xFF') { |
||
962 | // UTF16BE BOM |
||
963 | for (i = 2; i < n; i += 2) { |
||
964 | strBuf.push(String.fromCharCode( |
||
965 | (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1))); |
||
966 | } |
||
967 | } else { |
||
968 | for (i = 0; i < n; ++i) { |
||
969 | var code = PDFStringTranslateTable[str.charCodeAt(i)]; |
||
970 | strBuf.push(code ? String.fromCharCode(code) : str.charAt(i)); |
||
971 | } |
||
972 | } |
||
973 | return strBuf.join(''); |
||
974 | } |
||
975 | |||
976 | function stringToUTF8String(str) { |
||
977 | return decodeURIComponent(escape(str)); |
||
978 | } |
||
979 | |||
980 | function isEmptyObj(obj) { |
||
981 | for (var key in obj) { |
||
982 | return false; |
||
983 | } |
||
984 | return true; |
||
985 | } |
||
986 | |||
987 | function isBool(v) { |
||
988 | return typeof v === 'boolean'; |
||
989 | } |
||
990 | |||
991 | function isInt(v) { |
||
992 | return typeof v === 'number' && ((v | 0) === v); |
||
993 | } |
||
994 | |||
995 | function isNum(v) { |
||
996 | return typeof v === 'number'; |
||
997 | } |
||
998 | |||
999 | function isString(v) { |
||
1000 | return typeof v === 'string'; |
||
1001 | } |
||
1002 | |||
1003 | function isName(v) { |
||
1004 | return v instanceof Name; |
||
1005 | } |
||
1006 | |||
1007 | function isCmd(v, cmd) { |
||
1008 | return v instanceof Cmd && (cmd === undefined || v.cmd === cmd); |
||
1009 | } |
||
1010 | |||
1011 | function isDict(v, type) { |
||
1012 | if (!(v instanceof Dict)) { |
||
1013 | return false; |
||
1014 | } |
||
1015 | if (!type) { |
||
1016 | return true; |
||
1017 | } |
||
1018 | var dictType = v.get('Type'); |
||
1019 | return isName(dictType) && dictType.name === type; |
||
1020 | } |
||
1021 | |||
1022 | function isArray(v) { |
||
1023 | return v instanceof Array; |
||
1024 | } |
||
1025 | |||
1026 | function isStream(v) { |
||
1027 | return typeof v === 'object' && v !== null && v.getBytes !== undefined; |
||
1028 | } |
||
1029 | |||
1030 | function isArrayBuffer(v) { |
||
1031 | return typeof v === 'object' && v !== null && v.byteLength !== undefined; |
||
1032 | } |
||
1033 | |||
1034 | function isRef(v) { |
||
1035 | return v instanceof Ref; |
||
1036 | } |
||
1037 | |||
1038 | /** |
||
1039 | * Promise Capability object. |
||
1040 | * |
||
1041 | * @typedef {Object} PromiseCapability |
||
1042 | * @property {Promise} promise - A promise object. |
||
1043 | * @property {function} resolve - Fullfills the promise. |
||
1044 | * @property {function} reject - Rejects the promise. |
||
1045 | */ |
||
1046 | |||
1047 | /** |
||
1048 | * Creates a promise capability object. |
||
1049 | * @alias PDFJS.createPromiseCapability |
||
1050 | * |
||
1051 | * @return {PromiseCapability} A capability object contains: |
||
1052 | * - a Promise, resolve and reject methods. |
||
1053 | */ |
||
1054 | function createPromiseCapability() { |
||
1055 | var capability = {}; |
||
1056 | capability.promise = new Promise(function (resolve, reject) { |
||
1057 | capability.resolve = resolve; |
||
1058 | capability.reject = reject; |
||
1059 | }); |
||
1060 | return capability; |
||
1061 | } |
||
1062 | |||
1063 | PDFJS.createPromiseCapability = createPromiseCapability; |
||
1064 | |||
1065 | /** |
||
1066 | * Polyfill for Promises: |
||
1067 | * The following promise implementation tries to generally implement the |
||
1068 | * Promise/A+ spec. Some notable differences from other promise libaries are: |
||
1069 | * - There currently isn't a seperate deferred and promise object. |
||
1070 | * - Unhandled rejections eventually show an error if they aren't handled. |
||
1071 | * |
||
1072 | * Based off of the work in: |
||
1073 | * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 |
||
1074 | */ |
||
1075 | (function PromiseClosure() { |
||
1076 | if (globalScope.Promise) { |
||
1077 | // Promises existing in the DOM/Worker, checking presence of all/resolve |
||
1078 | if (typeof globalScope.Promise.all !== 'function') { |
||
1079 | globalScope.Promise.all = function (iterable) { |
||
1080 | var count = 0, results = [], resolve, reject; |
||
1081 | var promise = new globalScope.Promise(function (resolve_, reject_) { |
||
1082 | resolve = resolve_; |
||
1083 | reject = reject_; |
||
1084 | }); |
||
1085 | iterable.forEach(function (p, i) { |
||
1086 | count++; |
||
1087 | p.then(function (result) { |
||
1088 | results[i] = result; |
||
1089 | count--; |
||
1090 | if (count === 0) { |
||
1091 | resolve(results); |
||
1092 | } |
||
1093 | }, reject); |
||
1094 | }); |
||
1095 | if (count === 0) { |
||
1096 | resolve(results); |
||
1097 | } |
||
1098 | return promise; |
||
1099 | }; |
||
1100 | } |
||
1101 | if (typeof globalScope.Promise.resolve !== 'function') { |
||
1102 | globalScope.Promise.resolve = function (value) { |
||
1103 | return new globalScope.Promise(function (resolve) { resolve(value); }); |
||
1104 | }; |
||
1105 | } |
||
1106 | if (typeof globalScope.Promise.reject !== 'function') { |
||
1107 | globalScope.Promise.reject = function (reason) { |
||
1108 | return new globalScope.Promise(function (resolve, reject) { |
||
1109 | reject(reason); |
||
1110 | }); |
||
1111 | }; |
||
1112 | } |
||
1113 | if (typeof globalScope.Promise.prototype.catch !== 'function') { |
||
1114 | globalScope.Promise.prototype.catch = function (onReject) { |
||
1115 | return globalScope.Promise.prototype.then(undefined, onReject); |
||
1116 | }; |
||
1117 | } |
||
1118 | return; |
||
1119 | } |
||
1120 | //#if !MOZCENTRAL |
||
1121 | var STATUS_PENDING = 0; |
||
1122 | var STATUS_RESOLVED = 1; |
||
1123 | var STATUS_REJECTED = 2; |
||
1124 | |||
1125 | // In an attempt to avoid silent exceptions, unhandled rejections are |
||
1126 | // tracked and if they aren't handled in a certain amount of time an |
||
1127 | // error is logged. |
||
1128 | var REJECTION_TIMEOUT = 500; |
||
1129 | |||
1130 | var HandlerManager = { |
||
1131 | handlers: [], |
||
1132 | running: false, |
||
1133 | unhandledRejections: [], |
||
1134 | pendingRejectionCheck: false, |
||
1135 | |||
1136 | scheduleHandlers: function scheduleHandlers(promise) { |
||
1137 | if (promise._status === STATUS_PENDING) { |
||
1138 | return; |
||
1139 | } |
||
1140 | |||
1141 | this.handlers = this.handlers.concat(promise._handlers); |
||
1142 | promise._handlers = []; |
||
1143 | |||
1144 | if (this.running) { |
||
1145 | return; |
||
1146 | } |
||
1147 | this.running = true; |
||
1148 | |||
1149 | setTimeout(this.runHandlers.bind(this), 0); |
||
1150 | }, |
||
1151 | |||
1152 | runHandlers: function runHandlers() { |
||
1153 | var RUN_TIMEOUT = 1; // ms |
||
1154 | var timeoutAt = Date.now() + RUN_TIMEOUT; |
||
1155 | while (this.handlers.length > 0) { |
||
1156 | var handler = this.handlers.shift(); |
||
1157 | |||
1158 | var nextStatus = handler.thisPromise._status; |
||
1159 | var nextValue = handler.thisPromise._value; |
||
1160 | |||
1161 | try { |
||
1162 | if (nextStatus === STATUS_RESOLVED) { |
||
1163 | if (typeof handler.onResolve === 'function') { |
||
1164 | nextValue = handler.onResolve(nextValue); |
||
1165 | } |
||
1166 | } else if (typeof handler.onReject === 'function') { |
||
1167 | nextValue = handler.onReject(nextValue); |
||
1168 | nextStatus = STATUS_RESOLVED; |
||
1169 | |||
1170 | if (handler.thisPromise._unhandledRejection) { |
||
1171 | this.removeUnhandeledRejection(handler.thisPromise); |
||
1172 | } |
||
1173 | } |
||
1174 | } catch (ex) { |
||
1175 | nextStatus = STATUS_REJECTED; |
||
1176 | nextValue = ex; |
||
1177 | } |
||
1178 | |||
1179 | handler.nextPromise._updateStatus(nextStatus, nextValue); |
||
1180 | if (Date.now() >= timeoutAt) { |
||
1181 | break; |
||
1182 | } |
||
1183 | } |
||
1184 | |||
1185 | if (this.handlers.length > 0) { |
||
1186 | setTimeout(this.runHandlers.bind(this), 0); |
||
1187 | return; |
||
1188 | } |
||
1189 | |||
1190 | this.running = false; |
||
1191 | }, |
||
1192 | |||
1193 | addUnhandledRejection: function addUnhandledRejection(promise) { |
||
1194 | this.unhandledRejections.push({ |
||
1195 | promise: promise, |
||
1196 | time: Date.now() |
||
1197 | }); |
||
1198 | this.scheduleRejectionCheck(); |
||
1199 | }, |
||
1200 | |||
1201 | removeUnhandeledRejection: function removeUnhandeledRejection(promise) { |
||
1202 | promise._unhandledRejection = false; |
||
1203 | for (var i = 0; i < this.unhandledRejections.length; i++) { |
||
1204 | if (this.unhandledRejections[i].promise === promise) { |
||
1205 | this.unhandledRejections.splice(i); |
||
1206 | i--; |
||
1207 | } |
||
1208 | } |
||
1209 | }, |
||
1210 | |||
1211 | scheduleRejectionCheck: function scheduleRejectionCheck() { |
||
1212 | if (this.pendingRejectionCheck) { |
||
1213 | return; |
||
1214 | } |
||
1215 | this.pendingRejectionCheck = true; |
||
1216 | setTimeout(function rejectionCheck() { |
||
1217 | this.pendingRejectionCheck = false; |
||
1218 | var now = Date.now(); |
||
1219 | for (var i = 0; i < this.unhandledRejections.length; i++) { |
||
1220 | if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { |
||
1221 | var unhandled = this.unhandledRejections[i].promise._value; |
||
1222 | var msg = 'Unhandled rejection: ' + unhandled; |
||
1223 | if (unhandled.stack) { |
||
1224 | msg += '\n' + unhandled.stack; |
||
1225 | } |
||
1226 | warn(msg); |
||
1227 | this.unhandledRejections.splice(i); |
||
1228 | i--; |
||
1229 | } |
||
1230 | } |
||
1231 | if (this.unhandledRejections.length) { |
||
1232 | this.scheduleRejectionCheck(); |
||
1233 | } |
||
1234 | }.bind(this), REJECTION_TIMEOUT); |
||
1235 | } |
||
1236 | }; |
||
1237 | |||
1238 | function Promise(resolver) { |
||
1239 | this._status = STATUS_PENDING; |
||
1240 | this._handlers = []; |
||
1241 | try { |
||
1242 | resolver.call(this, this._resolve.bind(this), this._reject.bind(this)); |
||
1243 | } catch (e) { |
||
1244 | this._reject(e); |
||
1245 | } |
||
1246 | } |
||
1247 | /** |
||
1248 | * Builds a promise that is resolved when all the passed in promises are |
||
1249 | * resolved. |
||
1250 | * @param {array} array of data and/or promises to wait for. |
||
1251 | * @return {Promise} New dependant promise. |
||
1252 | */ |
||
1253 | Promise.all = function Promise_all(promises) { |
||
1254 | var resolveAll, rejectAll; |
||
1255 | var deferred = new Promise(function (resolve, reject) { |
||
1256 | resolveAll = resolve; |
||
1257 | rejectAll = reject; |
||
1258 | }); |
||
1259 | var unresolved = promises.length; |
||
1260 | var results = []; |
||
1261 | if (unresolved === 0) { |
||
1262 | resolveAll(results); |
||
1263 | return deferred; |
||
1264 | } |
||
1265 | function reject(reason) { |
||
1266 | if (deferred._status === STATUS_REJECTED) { |
||
1267 | return; |
||
1268 | } |
||
1269 | results = []; |
||
1270 | rejectAll(reason); |
||
1271 | } |
||
1272 | for (var i = 0, ii = promises.length; i < ii; ++i) { |
||
1273 | var promise = promises[i]; |
||
1274 | var resolve = (function(i) { |
||
1275 | return function(value) { |
||
1276 | if (deferred._status === STATUS_REJECTED) { |
||
1277 | return; |
||
1278 | } |
||
1279 | results[i] = value; |
||
1280 | unresolved--; |
||
1281 | if (unresolved === 0) { |
||
1282 | resolveAll(results); |
||
1283 | } |
||
1284 | }; |
||
1285 | })(i); |
||
1286 | if (Promise.isPromise(promise)) { |
||
1287 | promise.then(resolve, reject); |
||
1288 | } else { |
||
1289 | resolve(promise); |
||
1290 | } |
||
1291 | } |
||
1292 | return deferred; |
||
1293 | }; |
||
1294 | |||
1295 | /** |
||
1296 | * Checks if the value is likely a promise (has a 'then' function). |
||
1297 | * @return {boolean} true if value is thenable |
||
1298 | */ |
||
1299 | Promise.isPromise = function Promise_isPromise(value) { |
||
1300 | return value && typeof value.then === 'function'; |
||
1301 | }; |
||
1302 | |||
1303 | /** |
||
1304 | * Creates resolved promise |
||
1305 | * @param value resolve value |
||
1306 | * @returns {Promise} |
||
1307 | */ |
||
1308 | Promise.resolve = function Promise_resolve(value) { |
||
1309 | return new Promise(function (resolve) { resolve(value); }); |
||
1310 | }; |
||
1311 | |||
1312 | /** |
||
1313 | * Creates rejected promise |
||
1314 | * @param reason rejection value |
||
1315 | * @returns {Promise} |
||
1316 | */ |
||
1317 | Promise.reject = function Promise_reject(reason) { |
||
1318 | return new Promise(function (resolve, reject) { reject(reason); }); |
||
1319 | }; |
||
1320 | |||
1321 | Promise.prototype = { |
||
1322 | _status: null, |
||
1323 | _value: null, |
||
1324 | _handlers: null, |
||
1325 | _unhandledRejection: null, |
||
1326 | |||
1327 | _updateStatus: function Promise__updateStatus(status, value) { |
||
1328 | if (this._status === STATUS_RESOLVED || |
||
1329 | this._status === STATUS_REJECTED) { |
||
1330 | return; |
||
1331 | } |
||
1332 | |||
1333 | if (status === STATUS_RESOLVED && |
||
1334 | Promise.isPromise(value)) { |
||
1335 | value.then(this._updateStatus.bind(this, STATUS_RESOLVED), |
||
1336 | this._updateStatus.bind(this, STATUS_REJECTED)); |
||
1337 | return; |
||
1338 | } |
||
1339 | |||
1340 | this._status = status; |
||
1341 | this._value = value; |
||
1342 | |||
1343 | if (status === STATUS_REJECTED && this._handlers.length === 0) { |
||
1344 | this._unhandledRejection = true; |
||
1345 | HandlerManager.addUnhandledRejection(this); |
||
1346 | } |
||
1347 | |||
1348 | HandlerManager.scheduleHandlers(this); |
||
1349 | }, |
||
1350 | |||
1351 | _resolve: function Promise_resolve(value) { |
||
1352 | this._updateStatus(STATUS_RESOLVED, value); |
||
1353 | }, |
||
1354 | |||
1355 | _reject: function Promise_reject(reason) { |
||
1356 | this._updateStatus(STATUS_REJECTED, reason); |
||
1357 | }, |
||
1358 | |||
1359 | then: function Promise_then(onResolve, onReject) { |
||
1360 | var nextPromise = new Promise(function (resolve, reject) { |
||
1361 | this.resolve = resolve; |
||
1362 | this.reject = reject; |
||
1363 | }); |
||
1364 | this._handlers.push({ |
||
1365 | thisPromise: this, |
||
1366 | onResolve: onResolve, |
||
1367 | onReject: onReject, |
||
1368 | nextPromise: nextPromise |
||
1369 | }); |
||
1370 | HandlerManager.scheduleHandlers(this); |
||
1371 | return nextPromise; |
||
1372 | }, |
||
1373 | |||
1374 | catch: function Promise_catch(onReject) { |
||
1375 | return this.then(undefined, onReject); |
||
1376 | } |
||
1377 | }; |
||
1378 | |||
1379 | globalScope.Promise = Promise; |
||
1380 | //#else |
||
1381 | //throw new Error('DOM Promise is not present'); |
||
1382 | //#endif |
||
1383 | })(); |
||
1384 | |||
1385 | var StatTimer = (function StatTimerClosure() { |
||
1386 | function rpad(str, pad, length) { |
||
1387 | while (str.length < length) { |
||
1388 | str += pad; |
||
1389 | } |
||
1390 | return str; |
||
1391 | } |
||
1392 | function StatTimer() { |
||
1393 | this.started = {}; |
||
1394 | this.times = []; |
||
1395 | this.enabled = true; |
||
1396 | } |
||
1397 | StatTimer.prototype = { |
||
1398 | time: function StatTimer_time(name) { |
||
1399 | if (!this.enabled) { |
||
1400 | return; |
||
1401 | } |
||
1402 | if (name in this.started) { |
||
1403 | warn('Timer is already running for ' + name); |
||
1404 | } |
||
1405 | this.started[name] = Date.now(); |
||
1406 | }, |
||
1407 | timeEnd: function StatTimer_timeEnd(name) { |
||
1408 | if (!this.enabled) { |
||
1409 | return; |
||
1410 | } |
||
1411 | if (!(name in this.started)) { |
||
1412 | warn('Timer has not been started for ' + name); |
||
1413 | } |
||
1414 | this.times.push({ |
||
1415 | 'name': name, |
||
1416 | 'start': this.started[name], |
||
1417 | 'end': Date.now() |
||
1418 | }); |
||
1419 | // Remove timer from started so it can be called again. |
||
1420 | delete this.started[name]; |
||
1421 | }, |
||
1422 | toString: function StatTimer_toString() { |
||
1423 | var i, ii; |
||
1424 | var times = this.times; |
||
1425 | var out = ''; |
||
1426 | // Find the longest name for padding purposes. |
||
1427 | var longest = 0; |
||
1428 | for (i = 0, ii = times.length; i < ii; ++i) { |
||
1429 | var name = times[i]['name']; |
||
1430 | if (name.length > longest) { |
||
1431 | longest = name.length; |
||
1432 | } |
||
1433 | } |
||
1434 | for (i = 0, ii = times.length; i < ii; ++i) { |
||
1435 | var span = times[i]; |
||
1436 | var duration = span.end - span.start; |
||
1437 | out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n'; |
||
1438 | } |
||
1439 | return out; |
||
1440 | } |
||
1441 | }; |
||
1442 | return StatTimer; |
||
1443 | })(); |
||
1444 | |||
1445 | PDFJS.createBlob = function createBlob(data, contentType) { |
||
1446 | if (typeof Blob !== 'undefined') { |
||
1447 | return new Blob([data], { type: contentType }); |
||
1448 | } |
||
1449 | // Blob builder is deprecated in FF14 and removed in FF18. |
||
1450 | var bb = new MozBlobBuilder(); |
||
1451 | bb.append(data); |
||
1452 | return bb.getBlob(contentType); |
||
1453 | }; |
||
1454 | |||
1455 | PDFJS.createObjectURL = (function createObjectURLClosure() { |
||
1456 | // Blob/createObjectURL is not available, falling back to data schema. |
||
1457 | var digits = |
||
1458 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; |
||
1459 | |||
1460 | return function createObjectURL(data, contentType) { |
||
1461 | if (!PDFJS.disableCreateObjectURL && |
||
1462 | typeof URL !== 'undefined' && URL.createObjectURL) { |
||
1463 | var blob = PDFJS.createBlob(data, contentType); |
||
1464 | return URL.createObjectURL(blob); |
||
1465 | } |
||
1466 | |||
1467 | var buffer = 'data:' + contentType + ';base64,'; |
||
1468 | for (var i = 0, ii = data.length; i < ii; i += 3) { |
||
1469 | var b1 = data[i] & 0xFF; |
||
1470 | var b2 = data[i + 1] & 0xFF; |
||
1471 | var b3 = data[i + 2] & 0xFF; |
||
1472 | var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); |
||
1473 | var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; |
||
1474 | var d4 = i + 2 < ii ? (b3 & 0x3F) : 64; |
||
1475 | buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4]; |
||
1476 | } |
||
1477 | return buffer; |
||
1478 | }; |
||
1479 | })(); |
||
1480 | |||
1481 | function MessageHandler(name, comObj) { |
||
1482 | this.name = name; |
||
1483 | this.comObj = comObj; |
||
1484 | this.callbackIndex = 1; |
||
1485 | this.postMessageTransfers = true; |
||
1486 | var callbacksCapabilities = this.callbacksCapabilities = {}; |
||
1487 | var ah = this.actionHandler = {}; |
||
1488 | |||
1489 | ah['console_log'] = [function ahConsoleLog(data) { |
||
1490 | console.log.apply(console, data); |
||
1491 | }]; |
||
1492 | ah['console_error'] = [function ahConsoleError(data) { |
||
1493 | console.error.apply(console, data); |
||
1494 | }]; |
||
1495 | ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) { |
||
1496 | UnsupportedManager.notify(data); |
||
1497 | }]; |
||
1498 | |||
1499 | comObj.onmessage = function messageHandlerComObjOnMessage(event) { |
||
1500 | var data = event.data; |
||
1501 | if (data.isReply) { |
||
1502 | var callbackId = data.callbackId; |
||
1503 | if (data.callbackId in callbacksCapabilities) { |
||
1504 | var callback = callbacksCapabilities[callbackId]; |
||
1505 | delete callbacksCapabilities[callbackId]; |
||
1506 | if ('error' in data) { |
||
1507 | callback.reject(data.error); |
||
1508 | } else { |
||
1509 | callback.resolve(data.data); |
||
1510 | } |
||
1511 | } else { |
||
1512 | error('Cannot resolve callback ' + callbackId); |
||
1513 | } |
||
1514 | } else if (data.action in ah) { |
||
1515 | var action = ah[data.action]; |
||
1516 | if (data.callbackId) { |
||
1517 | Promise.resolve().then(function () { |
||
1518 | return action[0].call(action[1], data.data); |
||
1519 | }).then(function (result) { |
||
1520 | comObj.postMessage({ |
||
1521 | isReply: true, |
||
1522 | callbackId: data.callbackId, |
||
1523 | data: result |
||
1524 | }); |
||
1525 | }, function (reason) { |
||
1526 | comObj.postMessage({ |
||
1527 | isReply: true, |
||
1528 | callbackId: data.callbackId, |
||
1529 | error: reason |
||
1530 | }); |
||
1531 | }); |
||
1532 | } else { |
||
1533 | action[0].call(action[1], data.data); |
||
1534 | } |
||
1535 | } else { |
||
1536 | error('Unknown action from worker: ' + data.action); |
||
1537 | } |
||
1538 | }; |
||
1539 | } |
||
1540 | |||
1541 | MessageHandler.prototype = { |
||
1542 | on: function messageHandlerOn(actionName, handler, scope) { |
||
1543 | var ah = this.actionHandler; |
||
1544 | if (ah[actionName]) { |
||
1545 | error('There is already an actionName called "' + actionName + '"'); |
||
1546 | } |
||
1547 | ah[actionName] = [handler, scope]; |
||
1548 | }, |
||
1549 | /** |
||
1550 | * Sends a message to the comObj to invoke the action with the supplied data. |
||
1551 | * @param {String} actionName Action to call. |
||
1552 | * @param {JSON} data JSON data to send. |
||
1553 | * @param {Array} [transfers] Optional list of transfers/ArrayBuffers |
||
1554 | */ |
||
1555 | send: function messageHandlerSend(actionName, data, transfers) { |
||
1556 | var message = { |
||
1557 | action: actionName, |
||
1558 | data: data |
||
1559 | }; |
||
1560 | this.postMessage(message, transfers); |
||
1561 | }, |
||
1562 | /** |
||
1563 | * Sends a message to the comObj to invoke the action with the supplied data. |
||
1564 | * Expects that other side will callback with the response. |
||
1565 | * @param {String} actionName Action to call. |
||
1566 | * @param {JSON} data JSON data to send. |
||
1567 | * @param {Array} [transfers] Optional list of transfers/ArrayBuffers. |
||
1568 | * @returns {Promise} Promise to be resolved with response data. |
||
1569 | */ |
||
1570 | sendWithPromise: |
||
1571 | function messageHandlerSendWithPromise(actionName, data, transfers) { |
||
1572 | var callbackId = this.callbackIndex++; |
||
1573 | var message = { |
||
1574 | action: actionName, |
||
1575 | data: data, |
||
1576 | callbackId: callbackId |
||
1577 | }; |
||
1578 | var capability = createPromiseCapability(); |
||
1579 | this.callbacksCapabilities[callbackId] = capability; |
||
1580 | try { |
||
1581 | this.postMessage(message, transfers); |
||
1582 | } catch (e) { |
||
1583 | capability.reject(e); |
||
1584 | } |
||
1585 | return capability.promise; |
||
1586 | }, |
||
1587 | /** |
||
1588 | * Sends raw message to the comObj. |
||
1589 | * @private |
||
1590 | * @param message {Object} Raw message. |
||
1591 | * @param transfers List of transfers/ArrayBuffers, or undefined. |
||
1592 | */ |
||
1593 | postMessage: function (message, transfers) { |
||
1594 | if (transfers && this.postMessageTransfers) { |
||
1595 | this.comObj.postMessage(message, transfers); |
||
1596 | } else { |
||
1597 | this.comObj.postMessage(message); |
||
1598 | } |
||
1599 | } |
||
1600 | }; |
||
1601 | |||
1602 | function loadJpegStream(id, imageUrl, objs) { |
||
1603 | var img = new Image(); |
||
1604 | img.onload = (function loadJpegStream_onloadClosure() { |
||
1605 | objs.resolve(id, img); |
||
1606 | }); |
||
1607 | img.onerror = (function loadJpegStream_onerrorClosure() { |
||
1608 | objs.resolve(id, null); |
||
1609 | warn('Error during JPEG image loading'); |
||
1610 | }); |
||
1611 | img.src = imageUrl; |
||
1612 | } |
||
1613 | |||
1614 | |||
1615 | /** |
||
1616 | * The maximum allowed image size in total pixels e.g. width * height. Images |
||
1617 | * above this value will not be drawn. Use -1 for no limit. |
||
1618 | * @var {number} |
||
1619 | */ |
||
1620 | PDFJS.maxImageSize = (PDFJS.maxImageSize === undefined ? |
||
1621 | -1 : PDFJS.maxImageSize); |
||
1622 | |||
1623 | /** |
||
1624 | * The url of where the predefined Adobe CMaps are located. Include trailing |
||
1625 | * slash. |
||
1626 | * @var {string} |
||
1627 | */ |
||
1628 | PDFJS.cMapUrl = (PDFJS.cMapUrl === undefined ? null : PDFJS.cMapUrl); |
||
1629 | |||
1630 | /** |
||
1631 | * Specifies if CMaps are binary packed. |
||
1632 | * @var {boolean} |
||
1633 | */ |
||
1634 | PDFJS.cMapPacked = PDFJS.cMapPacked === undefined ? false : PDFJS.cMapPacked; |
||
1635 | |||
1636 | /** |
||
1637 | * By default fonts are converted to OpenType fonts and loaded via font face |
||
1638 | * rules. If disabled, the font will be rendered using a built in font renderer |
||
1639 | * that constructs the glyphs with primitive path commands. |
||
1640 | * @var {boolean} |
||
1641 | */ |
||
1642 | PDFJS.disableFontFace = (PDFJS.disableFontFace === undefined ? |
||
1643 | false : PDFJS.disableFontFace); |
||
1644 | |||
1645 | /** |
||
1646 | * Path for image resources, mainly for annotation icons. Include trailing |
||
1647 | * slash. |
||
1648 | * @var {string} |
||
1649 | */ |
||
1650 | PDFJS.imageResourcesPath = (PDFJS.imageResourcesPath === undefined ? |
||
1651 | '' : PDFJS.imageResourcesPath); |
||
1652 | |||
1653 | /** |
||
1654 | * Disable the web worker and run all code on the main thread. This will happen |
||
1655 | * automatically if the browser doesn't support workers or sending typed arrays |
||
1656 | * to workers. |
||
1657 | * @var {boolean} |
||
1658 | */ |
||
1659 | PDFJS.disableWorker = (PDFJS.disableWorker === undefined ? |
||
1660 | false : PDFJS.disableWorker); |
||
1661 | |||
1662 | /** |
||
1663 | * Path and filename of the worker file. Required when the worker is enabled in |
||
1664 | * development mode. If unspecified in the production build, the worker will be |
||
1665 | * loaded based on the location of the pdf.js file. |
||
1666 | * @var {string} |
||
1667 | */ |
||
1668 | PDFJS.workerSrc = (PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc); |
||
1669 | |||
1670 | /** |
||
1671 | * Disable range request loading of PDF files. When enabled and if the server |
||
1672 | * supports partial content requests then the PDF will be fetched in chunks. |
||
1673 | * Enabled (false) by default. |
||
1674 | * @var {boolean} |
||
1675 | */ |
||
1676 | PDFJS.disableRange = (PDFJS.disableRange === undefined ? |
||
1677 | false : PDFJS.disableRange); |
||
1678 | |||
1679 | /** |
||
1680 | * Disable streaming of PDF file data. By default PDF.js attempts to load PDF |
||
1681 | * in chunks. This default behavior can be disabled. |
||
1682 | * @var {boolean} |
||
1683 | */ |
||
1684 | PDFJS.disableStream = (PDFJS.disableStream === undefined ? |
||
1685 | false : PDFJS.disableStream); |
||
1686 | |||
1687 | /** |
||
1688 | * Disable pre-fetching of PDF file data. When range requests are enabled PDF.js |
||
1689 | * will automatically keep fetching more data even if it isn't needed to display |
||
1690 | * the current page. This default behavior can be disabled. |
||
1691 | * |
||
1692 | * NOTE: It is also necessary to disable streaming, see above, |
||
1693 | * in order for disabling of pre-fetching to work correctly. |
||
1694 | * @var {boolean} |
||
1695 | */ |
||
1696 | PDFJS.disableAutoFetch = (PDFJS.disableAutoFetch === undefined ? |
||
1697 | false : PDFJS.disableAutoFetch); |
||
1698 | |||
1699 | /** |
||
1700 | * Enables special hooks for debugging PDF.js. |
||
1701 | * @var {boolean} |
||
1702 | */ |
||
1703 | PDFJS.pdfBug = (PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug); |
||
1704 | |||
1705 | /** |
||
1706 | * Enables transfer usage in postMessage for ArrayBuffers. |
||
1707 | * @var {boolean} |
||
1708 | */ |
||
1709 | PDFJS.postMessageTransfers = (PDFJS.postMessageTransfers === undefined ? |
||
1710 | true : PDFJS.postMessageTransfers); |
||
1711 | |||
1712 | /** |
||
1713 | * Disables URL.createObjectURL usage. |
||
1714 | * @var {boolean} |
||
1715 | */ |
||
1716 | PDFJS.disableCreateObjectURL = (PDFJS.disableCreateObjectURL === undefined ? |
||
1717 | false : PDFJS.disableCreateObjectURL); |
||
1718 | |||
1719 | /** |
||
1720 | * Disables WebGL usage. |
||
1721 | * @var {boolean} |
||
1722 | */ |
||
1723 | PDFJS.disableWebGL = (PDFJS.disableWebGL === undefined ? |
||
1724 | true : PDFJS.disableWebGL); |
||
1725 | |||
1726 | /** |
||
1727 | * Disables fullscreen support, and by extension Presentation Mode, |
||
1728 | * in browsers which support the fullscreen API. |
||
1729 | * @var {boolean} |
||
1730 | */ |
||
1731 | PDFJS.disableFullscreen = (PDFJS.disableFullscreen === undefined ? |
||
1732 | false : PDFJS.disableFullscreen); |
||
1733 | |||
1734 | /** |
||
1735 | * Enables CSS only zooming. |
||
1736 | * @var {boolean} |
||
1737 | */ |
||
1738 | PDFJS.useOnlyCssZoom = (PDFJS.useOnlyCssZoom === undefined ? |
||
1739 | false : PDFJS.useOnlyCssZoom); |
||
1740 | |||
1741 | /** |
||
1742 | * Controls the logging level. |
||
1743 | * The constants from PDFJS.VERBOSITY_LEVELS should be used: |
||
1744 | * - errors |
||
1745 | * - warnings [default] |
||
1746 | * - infos |
||
1747 | * @var {number} |
||
1748 | */ |
||
1749 | PDFJS.verbosity = (PDFJS.verbosity === undefined ? |
||
1750 | PDFJS.VERBOSITY_LEVELS.warnings : PDFJS.verbosity); |
||
1751 | |||
1752 | /** |
||
1753 | * The maximum supported canvas size in total pixels e.g. width * height. |
||
1754 | * The default value is 4096 * 4096. Use -1 for no limit. |
||
1755 | * @var {number} |
||
1756 | */ |
||
1757 | PDFJS.maxCanvasPixels = (PDFJS.maxCanvasPixels === undefined ? |
||
1758 | 16777216 : PDFJS.maxCanvasPixels); |
||
1759 | |||
1760 | /** |
||
1761 | * Opens external links in a new window if enabled. The default behavior opens |
||
1762 | * external links in the PDF.js window. |
||
1763 | * @var {boolean} |
||
1764 | */ |
||
1765 | PDFJS.openExternalLinksInNewWindow = ( |
||
1766 | PDFJS.openExternalLinksInNewWindow === undefined ? |
||
1767 | false : PDFJS.openExternalLinksInNewWindow); |
||
1768 | |||
1769 | /** |
||
1770 | * Document initialization / loading parameters object. |
||
1771 | * |
||
1772 | * @typedef {Object} DocumentInitParameters |
||
1773 | * @property {string} url - The URL of the PDF. |
||
1774 | * @property {TypedArray|Array|string} data - Binary PDF data. Use typed arrays |
||
1775 | * (Uint8Array) to improve the memory usage. If PDF data is BASE64-encoded, |
||
1776 | * use atob() to convert it to a binary string first. |
||
1777 | * @property {Object} httpHeaders - Basic authentication headers. |
||
1778 | * @property {boolean} withCredentials - Indicates whether or not cross-site |
||
1779 | * Access-Control requests should be made using credentials such as cookies |
||
1780 | * or authorization headers. The default is false. |
||
1781 | * @property {string} password - For decrypting password-protected PDFs. |
||
1782 | * @property {TypedArray} initialData - A typed array with the first portion or |
||
1783 | * all of the pdf data. Used by the extension since some data is already |
||
1784 | * loaded before the switch to range requests. |
||
1785 | * @property {number} length - The PDF file length. It's used for progress |
||
1786 | * reports and range requests operations. |
||
1787 | * @property {PDFDataRangeTransport} range |
||
1788 | */ |
||
1789 | |||
1790 | /** |
||
1791 | * @typedef {Object} PDFDocumentStats |
||
1792 | * @property {Array} streamTypes - Used stream types in the document (an item |
||
1793 | * is set to true if specific stream ID was used in the document). |
||
1794 | * @property {Array} fontTypes - Used font type in the document (an item is set |
||
1795 | * to true if specific font ID was used in the document). |
||
1796 | */ |
||
1797 | |||
1798 | /** |
||
1799 | * This is the main entry point for loading a PDF and interacting with it. |
||
1800 | * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR) |
||
1801 | * is used, which means it must follow the same origin rules that any XHR does |
||
1802 | * e.g. No cross domain requests without CORS. |
||
1803 | * |
||
1804 | * @param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src |
||
1805 | * Can be a url to where a PDF is located, a typed array (Uint8Array) |
||
1806 | * already populated with data or parameter object. |
||
1807 | * |
||
1808 | * @param {PDFDataRangeTransport} pdfDataRangeTransport (deprecated) It is used |
||
1809 | * if you want to manually serve range requests for data in the PDF. |
||
1810 | * |
||
1811 | * @param {function} passwordCallback (deprecated) It is used to request a |
||
1812 | * password if wrong or no password was provided. The callback receives two |
||
1813 | * parameters: function that needs to be called with new password and reason |
||
1814 | * (see {PasswordResponses}). |
||
1815 | * |
||
1816 | * @param {function} progressCallback (deprecated) It is used to be able to |
||
1817 | * monitor the loading progress of the PDF file (necessary to implement e.g. |
||
1818 | * a loading bar). The callback receives an {Object} with the properties: |
||
1819 | * {number} loaded and {number} total. |
||
1820 | * |
||
1821 | * @return {PDFDocumentLoadingTask} |
||
1822 | */ |
||
1823 | PDFJS.getDocument = function getDocument(src, |
||
1824 | pdfDataRangeTransport, |
||
1825 | passwordCallback, |
||
1826 | progressCallback) { |
||
1827 | var task = new PDFDocumentLoadingTask(); |
||
1828 | |||
1829 | // Support of the obsolete arguments (for compatibility with API v1.0) |
||
1830 | if (pdfDataRangeTransport) { |
||
1831 | if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) { |
||
1832 | // Not a PDFDataRangeTransport instance, trying to add missing properties. |
||
1833 | pdfDataRangeTransport = Object.create(pdfDataRangeTransport); |
||
1834 | pdfDataRangeTransport.length = src.length; |
||
1835 | pdfDataRangeTransport.initialData = src.initialData; |
||
1836 | } |
||
1837 | src = Object.create(src); |
||
1838 | src.range = pdfDataRangeTransport; |
||
1839 | } |
||
1840 | task.onPassword = passwordCallback || null; |
||
1841 | task.onProgress = progressCallback || null; |
||
1842 | |||
1843 | var workerInitializedCapability, transport; |
||
1844 | var source; |
||
1845 | if (typeof src === 'string') { |
||
1846 | source = { url: src }; |
||
1847 | } else if (isArrayBuffer(src)) { |
||
1848 | source = { data: src }; |
||
1849 | } else if (src instanceof PDFDataRangeTransport) { |
||
1850 | source = { range: src }; |
||
1851 | } else { |
||
1852 | if (typeof src !== 'object') { |
||
1853 | error('Invalid parameter in getDocument, need either Uint8Array, ' + |
||
1854 | 'string or a parameter object'); |
||
1855 | } |
||
1856 | if (!src.url && !src.data && !src.range) { |
||
1857 | error('Invalid parameter object: need either .data, .range or .url'); |
||
1858 | } |
||
1859 | |||
1860 | source = src; |
||
1861 | } |
||
1862 | |||
1863 | var params = {}; |
||
1864 | for (var key in source) { |
||
1865 | if (key === 'url' && typeof window !== 'undefined') { |
||
1866 | // The full path is required in the 'url' field. |
||
1867 | params[key] = combineUrl(window.location.href, source[key]); |
||
1868 | continue; |
||
1869 | } else if (key === 'range') { |
||
1870 | continue; |
||
1871 | } else if (key === 'data' && !(source[key] instanceof Uint8Array)) { |
||
1872 | // Converting string or array-like data to Uint8Array. |
||
1873 | var pdfBytes = source[key]; |
||
1874 | if (typeof pdfBytes === 'string') { |
||
1875 | params[key] = stringToBytes(pdfBytes); |
||
1876 | } else if (typeof pdfBytes === 'object' && pdfBytes !== null && |
||
1877 | !isNaN(pdfBytes.length)) { |
||
1878 | params[key] = new Uint8Array(pdfBytes); |
||
1879 | } else { |
||
1880 | error('Invalid PDF binary data: either typed array, string or ' + |
||
1881 | 'array-like object is expected in the data property.'); |
||
1882 | } |
||
1883 | continue; |
||
1884 | } |
||
1885 | params[key] = source[key]; |
||
1886 | } |
||
1887 | |||
1888 | workerInitializedCapability = createPromiseCapability(); |
||
1889 | transport = new WorkerTransport(workerInitializedCapability, source.range); |
||
1890 | workerInitializedCapability.promise.then(function transportInitialized() { |
||
1891 | transport.fetchDocument(task, params); |
||
1892 | }); |
||
1893 | |||
1894 | return task; |
||
1895 | }; |
||
1896 | |||
1897 | /** |
||
1898 | * PDF document loading operation. |
||
1899 | * @class |
||
1900 | */ |
||
1901 | var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() { |
||
1902 | /** @constructs PDFDocumentLoadingTask */ |
||
1903 | function PDFDocumentLoadingTask() { |
||
1904 | this._capability = createPromiseCapability(); |
||
1905 | |||
1906 | /** |
||
1907 | * Callback to request a password if wrong or no password was provided. |
||
1908 | * The callback receives two parameters: function that needs to be called |
||
1909 | * with new password and reason (see {PasswordResponses}). |
||
1910 | */ |
||
1911 | this.onPassword = null; |
||
1912 | |||
1913 | /** |
||
1914 | * Callback to be able to monitor the loading progress of the PDF file |
||
1915 | * (necessary to implement e.g. a loading bar). The callback receives |
||
1916 | * an {Object} with the properties: {number} loaded and {number} total. |
||
1917 | */ |
||
1918 | this.onProgress = null; |
||
1919 | } |
||
1920 | |||
1921 | PDFDocumentLoadingTask.prototype = |
||
1922 | /** @lends PDFDocumentLoadingTask.prototype */ { |
||
1923 | /** |
||
1924 | * @return {Promise} |
||
1925 | */ |
||
1926 | get promise() { |
||
1927 | return this._capability.promise; |
||
1928 | }, |
||
1929 | |||
1930 | // TODO add cancel or abort method |
||
1931 | |||
1932 | /** |
||
1933 | * Registers callbacks to indicate the document loading completion. |
||
1934 | * |
||
1935 | * @param {function} onFulfilled The callback for the loading completion. |
||
1936 | * @param {function} onRejected The callback for the loading failure. |
||
1937 | * @return {Promise} A promise that is resolved after the onFulfilled or |
||
1938 | * onRejected callback. |
||
1939 | */ |
||
1940 | then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) { |
||
1941 | return this.promise.then.apply(this.promise, arguments); |
||
1942 | } |
||
1943 | }; |
||
1944 | |||
1945 | return PDFDocumentLoadingTask; |
||
1946 | })(); |
||
1947 | |||
1948 | /** |
||
1949 | * Abstract class to support range requests file loading. |
||
1950 | * @class |
||
1951 | */ |
||
1952 | var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() { |
||
1953 | /** |
||
1954 | * @constructs PDFDataRangeTransport |
||
1955 | * @param {number} length |
||
1956 | * @param {Uint8Array} initialData |
||
1957 | */ |
||
1958 | function PDFDataRangeTransport(length, initialData) { |
||
1959 | this.length = length; |
||
1960 | this.initialData = initialData; |
||
1961 | |||
1962 | this._rangeListeners = []; |
||
1963 | this._progressListeners = []; |
||
1964 | this._progressiveReadListeners = []; |
||
1965 | this._readyCapability = createPromiseCapability(); |
||
1966 | } |
||
1967 | PDFDataRangeTransport.prototype = |
||
1968 | /** @lends PDFDataRangeTransport.prototype */ { |
||
1969 | addRangeListener: |
||
1970 | function PDFDataRangeTransport_addRangeListener(listener) { |
||
1971 | this._rangeListeners.push(listener); |
||
1972 | }, |
||
1973 | |||
1974 | addProgressListener: |
||
1975 | function PDFDataRangeTransport_addProgressListener(listener) { |
||
1976 | this._progressListeners.push(listener); |
||
1977 | }, |
||
1978 | |||
1979 | addProgressiveReadListener: |
||
1980 | function PDFDataRangeTransport_addProgressiveReadListener(listener) { |
||
1981 | this._progressiveReadListeners.push(listener); |
||
1982 | }, |
||
1983 | |||
1984 | onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) { |
||
1985 | var listeners = this._rangeListeners; |
||
1986 | for (var i = 0, n = listeners.length; i < n; ++i) { |
||
1987 | listeners[i](begin, chunk); |
||
1988 | } |
||
1989 | }, |
||
1990 | |||
1991 | onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) { |
||
1992 | this._readyCapability.promise.then(function () { |
||
1993 | var listeners = this._progressListeners; |
||
1994 | for (var i = 0, n = listeners.length; i < n; ++i) { |
||
1995 | listeners[i](loaded); |
||
1996 | } |
||
1997 | }.bind(this)); |
||
1998 | }, |
||
1999 | |||
2000 | onDataProgressiveRead: |
||
2001 | function PDFDataRangeTransport_onDataProgress(chunk) { |
||
2002 | this._readyCapability.promise.then(function () { |
||
2003 | var listeners = this._progressiveReadListeners; |
||
2004 | for (var i = 0, n = listeners.length; i < n; ++i) { |
||
2005 | listeners[i](chunk); |
||
2006 | } |
||
2007 | }.bind(this)); |
||
2008 | }, |
||
2009 | |||
2010 | transportReady: function PDFDataRangeTransport_transportReady() { |
||
2011 | this._readyCapability.resolve(); |
||
2012 | }, |
||
2013 | |||
2014 | requestDataRange: |
||
2015 | function PDFDataRangeTransport_requestDataRange(begin, end) { |
||
2016 | throw new Error('Abstract method PDFDataRangeTransport.requestDataRange'); |
||
2017 | } |
||
2018 | }; |
||
2019 | return PDFDataRangeTransport; |
||
2020 | })(); |
||
2021 | |||
2022 | PDFJS.PDFDataRangeTransport = PDFDataRangeTransport; |
||
2023 | |||
2024 | /** |
||
2025 | * Proxy to a PDFDocument in the worker thread. Also, contains commonly used |
||
2026 | * properties that can be read synchronously. |
||
2027 | * @class |
||
2028 | */ |
||
2029 | var PDFDocumentProxy = (function PDFDocumentProxyClosure() { |
||
2030 | function PDFDocumentProxy(pdfInfo, transport) { |
||
2031 | this.pdfInfo = pdfInfo; |
||
2032 | this.transport = transport; |
||
2033 | } |
||
2034 | PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ { |
||
2035 | /** |
||
2036 | * @return {number} Total number of pages the PDF contains. |
||
2037 | */ |
||
2038 | get numPages() { |
||
2039 | return this.pdfInfo.numPages; |
||
2040 | }, |
||
2041 | /** |
||
2042 | * @return {string} A unique ID to identify a PDF. Not guaranteed to be |
||
2043 | * unique. |
||
2044 | */ |
||
2045 | get fingerprint() { |
||
2046 | return this.pdfInfo.fingerprint; |
||
2047 | }, |
||
2048 | /** |
||
2049 | * @param {number} pageNumber The page number to get. The first page is 1. |
||
2050 | * @return {Promise} A promise that is resolved with a {@link PDFPageProxy} |
||
2051 | * object. |
||
2052 | */ |
||
2053 | getPage: function PDFDocumentProxy_getPage(pageNumber) { |
||
2054 | return this.transport.getPage(pageNumber); |
||
2055 | }, |
||
2056 | /** |
||
2057 | * @param {{num: number, gen: number}} ref The page reference. Must have |
||
2058 | * the 'num' and 'gen' properties. |
||
2059 | * @return {Promise} A promise that is resolved with the page index that is |
||
2060 | * associated with the reference. |
||
2061 | */ |
||
2062 | getPageIndex: function PDFDocumentProxy_getPageIndex(ref) { |
||
2063 | return this.transport.getPageIndex(ref); |
||
2064 | }, |
||
2065 | /** |
||
2066 | * @return {Promise} A promise that is resolved with a lookup table for |
||
2067 | * mapping named destinations to reference numbers. |
||
2068 | * |
||
2069 | * This can be slow for large documents: use getDestination instead |
||
2070 | */ |
||
2071 | getDestinations: function PDFDocumentProxy_getDestinations() { |
||
2072 | return this.transport.getDestinations(); |
||
2073 | }, |
||
2074 | /** |
||
2075 | * @param {string} id The named destination to get. |
||
2076 | * @return {Promise} A promise that is resolved with all information |
||
2077 | * of the given named destination. |
||
2078 | */ |
||
2079 | getDestination: function PDFDocumentProxy_getDestination(id) { |
||
2080 | return this.transport.getDestination(id); |
||
2081 | }, |
||
2082 | /** |
||
2083 | * @return {Promise} A promise that is resolved with a lookup table for |
||
2084 | * mapping named attachments to their content. |
||
2085 | */ |
||
2086 | getAttachments: function PDFDocumentProxy_getAttachments() { |
||
2087 | return this.transport.getAttachments(); |
||
2088 | }, |
||
2089 | /** |
||
2090 | * @return {Promise} A promise that is resolved with an array of all the |
||
2091 | * JavaScript strings in the name tree. |
||
2092 | */ |
||
2093 | getJavaScript: function PDFDocumentProxy_getJavaScript() { |
||
2094 | return this.transport.getJavaScript(); |
||
2095 | }, |
||
2096 | /** |
||
2097 | * @return {Promise} A promise that is resolved with an {Array} that is a |
||
2098 | * tree outline (if it has one) of the PDF. The tree is in the format of: |
||
2099 | * [ |
||
2100 | * { |
||
2101 | * title: string, |
||
2102 | * bold: boolean, |
||
2103 | * italic: boolean, |
||
2104 | * color: rgb array, |
||
2105 | * dest: dest obj, |
||
2106 | * items: array of more items like this |
||
2107 | * }, |
||
2108 | * ... |
||
2109 | * ]. |
||
2110 | */ |
||
2111 | getOutline: function PDFDocumentProxy_getOutline() { |
||
2112 | return this.transport.getOutline(); |
||
2113 | }, |
||
2114 | /** |
||
2115 | * @return {Promise} A promise that is resolved with an {Object} that has |
||
2116 | * info and metadata properties. Info is an {Object} filled with anything |
||
2117 | * available in the information dictionary and similarly metadata is a |
||
2118 | * {Metadata} object with information from the metadata section of the PDF. |
||
2119 | */ |
||
2120 | getMetadata: function PDFDocumentProxy_getMetadata() { |
||
2121 | return this.transport.getMetadata(); |
||
2122 | }, |
||
2123 | /** |
||
2124 | * @return {Promise} A promise that is resolved with a TypedArray that has |
||
2125 | * the raw data from the PDF. |
||
2126 | */ |
||
2127 | getData: function PDFDocumentProxy_getData() { |
||
2128 | return this.transport.getData(); |
||
2129 | }, |
||
2130 | /** |
||
2131 | * @return {Promise} A promise that is resolved when the document's data |
||
2132 | * is loaded. It is resolved with an {Object} that contains the length |
||
2133 | * property that indicates size of the PDF data in bytes. |
||
2134 | */ |
||
2135 | getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() { |
||
2136 | return this.transport.downloadInfoCapability.promise; |
||
2137 | }, |
||
2138 | /** |
||
2139 | * @return {Promise} A promise this is resolved with current stats about |
||
2140 | * document structures (see {@link PDFDocumentStats}). |
||
2141 | */ |
||
2142 | getStats: function PDFDocumentProxy_getStats() { |
||
2143 | return this.transport.getStats(); |
||
2144 | }, |
||
2145 | /** |
||
2146 | * Cleans up resources allocated by the document, e.g. created @font-face. |
||
2147 | */ |
||
2148 | cleanup: function PDFDocumentProxy_cleanup() { |
||
2149 | this.transport.startCleanup(); |
||
2150 | }, |
||
2151 | /** |
||
2152 | * Destroys current document instance and terminates worker. |
||
2153 | */ |
||
2154 | destroy: function PDFDocumentProxy_destroy() { |
||
2155 | this.transport.destroy(); |
||
2156 | } |
||
2157 | }; |
||
2158 | return PDFDocumentProxy; |
||
2159 | })(); |
||
2160 | |||
2161 | /** |
||
2162 | * Page text content. |
||
2163 | * |
||
2164 | * @typedef {Object} TextContent |
||
2165 | * @property {array} items - array of {@link TextItem} |
||
2166 | * @property {Object} styles - {@link TextStyles} objects, indexed by font |
||
2167 | * name. |
||
2168 | */ |
||
2169 | |||
2170 | /** |
||
2171 | * Page text content part. |
||
2172 | * |
||
2173 | * @typedef {Object} TextItem |
||
2174 | * @property {string} str - text content. |
||
2175 | * @property {string} dir - text direction: 'ttb', 'ltr' or 'rtl'. |
||
2176 | * @property {array} transform - transformation matrix. |
||
2177 | * @property {number} width - width in device space. |
||
2178 | * @property {number} height - height in device space. |
||
2179 | * @property {string} fontName - font name used by pdf.js for converted font. |
||
2180 | */ |
||
2181 | |||
2182 | /** |
||
2183 | * Text style. |
||
2184 | * |
||
2185 | * @typedef {Object} TextStyle |
||
2186 | * @property {number} ascent - font ascent. |
||
2187 | * @property {number} descent - font descent. |
||
2188 | * @property {boolean} vertical - text is in vertical mode. |
||
2189 | * @property {string} fontFamily - possible font family |
||
2190 | */ |
||
2191 | |||
2192 | /** |
||
2193 | * Page render parameters. |
||
2194 | * |
||
2195 | * @typedef {Object} RenderParameters |
||
2196 | * @property {Object} canvasContext - A 2D context of a DOM Canvas object. |
||
2197 | * @property {PDFJS.PageViewport} viewport - Rendering viewport obtained by |
||
2198 | * calling of PDFPage.getViewport method. |
||
2199 | * @property {string} intent - Rendering intent, can be 'display' or 'print' |
||
2200 | * (default value is 'display'). |
||
2201 | * @property {Object} imageLayer - (optional) An object that has beginLayout, |
||
2202 | * endLayout and appendImage functions. |
||
2203 | * @property {function} continueCallback - (deprecated) A function that will be |
||
2204 | * called each time the rendering is paused. To continue |
||
2205 | * rendering call the function that is the first argument |
||
2206 | * to the callback. |
||
2207 | */ |
||
2208 | |||
2209 | /** |
||
2210 | * PDF page operator list. |
||
2211 | * |
||
2212 | * @typedef {Object} PDFOperatorList |
||
2213 | * @property {Array} fnArray - Array containing the operator functions. |
||
2214 | * @property {Array} argsArray - Array containing the arguments of the |
||
2215 | * functions. |
||
2216 | */ |
||
2217 | |||
2218 | /** |
||
2219 | * Proxy to a PDFPage in the worker thread. |
||
2220 | * @class |
||
2221 | */ |
||
2222 | var PDFPageProxy = (function PDFPageProxyClosure() { |
||
2223 | function PDFPageProxy(pageIndex, pageInfo, transport) { |
||
2224 | this.pageIndex = pageIndex; |
||
2225 | this.pageInfo = pageInfo; |
||
2226 | this.transport = transport; |
||
2227 | this.stats = new StatTimer(); |
||
2228 | this.stats.enabled = !!globalScope.PDFJS.enableStats; |
||
2229 | this.commonObjs = transport.commonObjs; |
||
2230 | this.objs = new PDFObjects(); |
||
2231 | this.cleanupAfterRender = false; |
||
2232 | this.pendingDestroy = false; |
||
2233 | this.intentStates = {}; |
||
2234 | } |
||
2235 | PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ { |
||
2236 | /** |
||
2237 | * @return {number} Page number of the page. First page is 1. |
||
2238 | */ |
||
2239 | get pageNumber() { |
||
2240 | return this.pageIndex + 1; |
||
2241 | }, |
||
2242 | /** |
||
2243 | * @return {number} The number of degrees the page is rotated clockwise. |
||
2244 | */ |
||
2245 | get rotate() { |
||
2246 | return this.pageInfo.rotate; |
||
2247 | }, |
||
2248 | /** |
||
2249 | * @return {Object} The reference that points to this page. It has 'num' and |
||
2250 | * 'gen' properties. |
||
2251 | */ |
||
2252 | get ref() { |
||
2253 | return this.pageInfo.ref; |
||
2254 | }, |
||
2255 | /** |
||
2256 | * @return {Array} An array of the visible portion of the PDF page in the |
||
2257 | * user space units - [x1, y1, x2, y2]. |
||
2258 | */ |
||
2259 | get view() { |
||
2260 | return this.pageInfo.view; |
||
2261 | }, |
||
2262 | /** |
||
2263 | * @param {number} scale The desired scale of the viewport. |
||
2264 | * @param {number} rotate Degrees to rotate the viewport. If omitted this |
||
2265 | * defaults to the page rotation. |
||
2266 | * @return {PDFJS.PageViewport} Contains 'width' and 'height' properties |
||
2267 | * along with transforms required for rendering. |
||
2268 | */ |
||
2269 | getViewport: function PDFPageProxy_getViewport(scale, rotate) { |
||
2270 | if (arguments.length < 2) { |
||
2271 | rotate = this.rotate; |
||
2272 | } |
||
2273 | return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); |
||
2274 | }, |
||
2275 | /** |
||
2276 | * @return {Promise} A promise that is resolved with an {Array} of the |
||
2277 | * annotation objects. |
||
2278 | */ |
||
2279 | getAnnotations: function PDFPageProxy_getAnnotations() { |
||
2280 | if (this.annotationsPromise) { |
||
2281 | return this.annotationsPromise; |
||
2282 | } |
||
2283 | |||
2284 | var promise = this.transport.getAnnotations(this.pageIndex); |
||
2285 | this.annotationsPromise = promise; |
||
2286 | return promise; |
||
2287 | }, |
||
2288 | /** |
||
2289 | * Begins the process of rendering a page to the desired context. |
||
2290 | * @param {RenderParameters} params Page render parameters. |
||
2291 | * @return {RenderTask} An object that contains the promise, which |
||
2292 | * is resolved when the page finishes rendering. |
||
2293 | */ |
||
2294 | render: function PDFPageProxy_render(params) { |
||
2295 | var stats = this.stats; |
||
2296 | stats.time('Overall'); |
||
2297 | |||
2298 | // If there was a pending destroy cancel it so no cleanup happens during |
||
2299 | // this call to render. |
||
2300 | this.pendingDestroy = false; |
||
2301 | |||
2302 | var renderingIntent = (params.intent === 'print' ? 'print' : 'display'); |
||
2303 | |||
2304 | if (!this.intentStates[renderingIntent]) { |
||
2305 | this.intentStates[renderingIntent] = {}; |
||
2306 | } |
||
2307 | var intentState = this.intentStates[renderingIntent]; |
||
2308 | |||
2309 | // If there's no displayReadyCapability yet, then the operatorList |
||
2310 | // was never requested before. Make the request and create the promise. |
||
2311 | if (!intentState.displayReadyCapability) { |
||
2312 | intentState.receivingOperatorList = true; |
||
2313 | intentState.displayReadyCapability = createPromiseCapability(); |
||
2314 | intentState.operatorList = { |
||
2315 | fnArray: [], |
||
2316 | argsArray: [], |
||
2317 | lastChunk: false |
||
2318 | }; |
||
2319 | |||
2320 | this.stats.time('Page Request'); |
||
2321 | this.transport.messageHandler.send('RenderPageRequest', { |
||
2322 | pageIndex: this.pageNumber - 1, |
||
2323 | intent: renderingIntent |
||
2324 | }); |
||
2325 | } |
||
2326 | |||
2327 | var internalRenderTask = new InternalRenderTask(complete, params, |
||
2328 | this.objs, |
||
2329 | this.commonObjs, |
||
2330 | intentState.operatorList, |
||
2331 | this.pageNumber); |
||
2332 | if (!intentState.renderTasks) { |
||
2333 | intentState.renderTasks = []; |
||
2334 | } |
||
2335 | intentState.renderTasks.push(internalRenderTask); |
||
2336 | var renderTask = internalRenderTask.task; |
||
2337 | |||
2338 | // Obsolete parameter support |
||
2339 | if (params.continueCallback) { |
||
2340 | renderTask.onContinue = params.continueCallback; |
||
2341 | } |
||
2342 | |||
2343 | var self = this; |
||
2344 | intentState.displayReadyCapability.promise.then( |
||
2345 | function pageDisplayReadyPromise(transparency) { |
||
2346 | if (self.pendingDestroy) { |
||
2347 | complete(); |
||
2348 | return; |
||
2349 | } |
||
2350 | stats.time('Rendering'); |
||
2351 | internalRenderTask.initalizeGraphics(transparency); |
||
2352 | internalRenderTask.operatorListChanged(); |
||
2353 | }, |
||
2354 | function pageDisplayReadPromiseError(reason) { |
||
2355 | complete(reason); |
||
2356 | } |
||
2357 | ); |
||
2358 | |||
2359 | function complete(error) { |
||
2360 | var i = intentState.renderTasks.indexOf(internalRenderTask); |
||
2361 | if (i >= 0) { |
||
2362 | intentState.renderTasks.splice(i, 1); |
||
2363 | } |
||
2364 | |||
2365 | if (self.cleanupAfterRender) { |
||
2366 | self.pendingDestroy = true; |
||
2367 | } |
||
2368 | self._tryDestroy(); |
||
2369 | |||
2370 | if (error) { |
||
2371 | internalRenderTask.capability.reject(error); |
||
2372 | } else { |
||
2373 | internalRenderTask.capability.resolve(); |
||
2374 | } |
||
2375 | stats.timeEnd('Rendering'); |
||
2376 | stats.timeEnd('Overall'); |
||
2377 | } |
||
2378 | |||
2379 | return renderTask; |
||
2380 | }, |
||
2381 | |||
2382 | /** |
||
2383 | * @return {Promise} A promise resolved with an {@link PDFOperatorList} |
||
2384 | * object that represents page's operator list. |
||
2385 | */ |
||
2386 | getOperatorList: function PDFPageProxy_getOperatorList() { |
||
2387 | function operatorListChanged() { |
||
2388 | if (intentState.operatorList.lastChunk) { |
||
2389 | intentState.opListReadCapability.resolve(intentState.operatorList); |
||
2390 | } |
||
2391 | } |
||
2392 | |||
2393 | var renderingIntent = 'oplist'; |
||
2394 | if (!this.intentStates[renderingIntent]) { |
||
2395 | this.intentStates[renderingIntent] = {}; |
||
2396 | } |
||
2397 | var intentState = this.intentStates[renderingIntent]; |
||
2398 | |||
2399 | if (!intentState.opListReadCapability) { |
||
2400 | var opListTask = {}; |
||
2401 | opListTask.operatorListChanged = operatorListChanged; |
||
2402 | intentState.receivingOperatorList = true; |
||
2403 | intentState.opListReadCapability = createPromiseCapability(); |
||
2404 | intentState.renderTasks = []; |
||
2405 | intentState.renderTasks.push(opListTask); |
||
2406 | intentState.operatorList = { |
||
2407 | fnArray: [], |
||
2408 | argsArray: [], |
||
2409 | lastChunk: false |
||
2410 | }; |
||
2411 | |||
2412 | this.transport.messageHandler.send('RenderPageRequest', { |
||
2413 | pageIndex: this.pageIndex, |
||
2414 | intent: renderingIntent |
||
2415 | }); |
||
2416 | } |
||
2417 | return intentState.opListReadCapability.promise; |
||
2418 | }, |
||
2419 | |||
2420 | /** |
||
2421 | * @return {Promise} That is resolved a {@link TextContent} |
||
2422 | * object that represent the page text content. |
||
2423 | */ |
||
2424 | getTextContent: function PDFPageProxy_getTextContent() { |
||
2425 | return this.transport.messageHandler.sendWithPromise('GetTextContent', { |
||
2426 | pageIndex: this.pageNumber - 1 |
||
2427 | }); |
||
2428 | }, |
||
2429 | /** |
||
2430 | * Destroys resources allocated by the page. |
||
2431 | */ |
||
2432 | destroy: function PDFPageProxy_destroy() { |
||
2433 | this.pendingDestroy = true; |
||
2434 | this._tryDestroy(); |
||
2435 | }, |
||
2436 | /** |
||
2437 | * For internal use only. Attempts to clean up if rendering is in a state |
||
2438 | * where that's possible. |
||
2439 | * @ignore |
||
2440 | */ |
||
2441 | _tryDestroy: function PDFPageProxy__destroy() { |
||
2442 | if (!this.pendingDestroy || |
||
2443 | Object.keys(this.intentStates).some(function(intent) { |
||
2444 | var intentState = this.intentStates[intent]; |
||
2445 | return (intentState.renderTasks.length !== 0 || |
||
2446 | intentState.receivingOperatorList); |
||
2447 | }, this)) { |
||
2448 | return; |
||
2449 | } |
||
2450 | |||
2451 | Object.keys(this.intentStates).forEach(function(intent) { |
||
2452 | delete this.intentStates[intent]; |
||
2453 | }, this); |
||
2454 | this.objs.clear(); |
||
2455 | this.annotationsPromise = null; |
||
2456 | this.pendingDestroy = false; |
||
2457 | }, |
||
2458 | /** |
||
2459 | * For internal use only. |
||
2460 | * @ignore |
||
2461 | */ |
||
2462 | _startRenderPage: function PDFPageProxy_startRenderPage(transparency, |
||
2463 | intent) { |
||
2464 | var intentState = this.intentStates[intent]; |
||
2465 | // TODO Refactor RenderPageRequest to separate rendering |
||
2466 | // and operator list logic |
||
2467 | if (intentState.displayReadyCapability) { |
||
2468 | intentState.displayReadyCapability.resolve(transparency); |
||
2469 | } |
||
2470 | }, |
||
2471 | /** |
||
2472 | * For internal use only. |
||
2473 | * @ignore |
||
2474 | */ |
||
2475 | _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk, |
||
2476 | intent) { |
||
2477 | var intentState = this.intentStates[intent]; |
||
2478 | var i, ii; |
||
2479 | // Add the new chunk to the current operator list. |
||
2480 | for (i = 0, ii = operatorListChunk.length; i < ii; i++) { |
||
2481 | intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); |
||
2482 | intentState.operatorList.argsArray.push( |
||
2483 | operatorListChunk.argsArray[i]); |
||
2484 | } |
||
2485 | intentState.operatorList.lastChunk = operatorListChunk.lastChunk; |
||
2486 | |||
2487 | // Notify all the rendering tasks there are more operators to be consumed. |
||
2488 | for (i = 0; i < intentState.renderTasks.length; i++) { |
||
2489 | intentState.renderTasks[i].operatorListChanged(); |
||
2490 | } |
||
2491 | |||
2492 | if (operatorListChunk.lastChunk) { |
||
2493 | intentState.receivingOperatorList = false; |
||
2494 | this._tryDestroy(); |
||
2495 | } |
||
2496 | } |
||
2497 | }; |
||
2498 | return PDFPageProxy; |
||
2499 | })(); |
||
2500 | |||
2501 | /** |
||
2502 | * For internal use only. |
||
2503 | * @ignore |
||
2504 | */ |
||
2505 | var WorkerTransport = (function WorkerTransportClosure() { |
||
2506 | function WorkerTransport(workerInitializedCapability, pdfDataRangeTransport) { |
||
2507 | this.pdfDataRangeTransport = pdfDataRangeTransport; |
||
2508 | this.workerInitializedCapability = workerInitializedCapability; |
||
2509 | this.commonObjs = new PDFObjects(); |
||
2510 | |||
2511 | this.loadingTask = null; |
||
2512 | |||
2513 | this.pageCache = []; |
||
2514 | this.pagePromises = []; |
||
2515 | this.downloadInfoCapability = createPromiseCapability(); |
||
2516 | |||
2517 | // If worker support isn't disabled explicit and the browser has worker |
||
2518 | // support, create a new web worker and test if it/the browser fullfills |
||
2519 | // all requirements to run parts of pdf.js in a web worker. |
||
2520 | // Right now, the requirement is, that an Uint8Array is still an Uint8Array |
||
2521 | // as it arrives on the worker. Chrome added this with version 15. |
||
2522 | //#if !SINGLE_FILE |
||
2523 | if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { |
||
2524 | var workerSrc = PDFJS.workerSrc; |
||
2525 | if (!workerSrc) { |
||
2526 | error('No PDFJS.workerSrc specified'); |
||
2527 | } |
||
2528 | |||
2529 | try { |
||
2530 | // Some versions of FF can't create a worker on localhost, see: |
||
2531 | // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 |
||
2532 | var worker = new Worker(workerSrc); |
||
2533 | var messageHandler = new MessageHandler('main', worker); |
||
2534 | this.messageHandler = messageHandler; |
||
2535 | |||
2536 | messageHandler.on('test', function transportTest(data) { |
||
2537 | var supportTypedArray = data && data.supportTypedArray; |
||
2538 | if (supportTypedArray) { |
||
2539 | this.worker = worker; |
||
2540 | if (!data.supportTransfers) { |
||
2541 | PDFJS.postMessageTransfers = false; |
||
2542 | } |
||
2543 | this.setupMessageHandler(messageHandler); |
||
2544 | workerInitializedCapability.resolve(); |
||
2545 | } else { |
||
2546 | this.setupFakeWorker(); |
||
2547 | } |
||
2548 | }.bind(this)); |
||
2549 | |||
2550 | var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); |
||
2551 | // Some versions of Opera throw a DATA_CLONE_ERR on serializing the |
||
2552 | // typed array. Also, checking if we can use transfers. |
||
2553 | try { |
||
2554 | messageHandler.send('test', testObj, [testObj.buffer]); |
||
2555 | } catch (ex) { |
||
2556 | info('Cannot use postMessage transfers'); |
||
2557 | testObj[0] = 0; |
||
2558 | messageHandler.send('test', testObj); |
||
2559 | } |
||
2560 | return; |
||
2561 | } catch (e) { |
||
2562 | info('The worker has been disabled.'); |
||
2563 | } |
||
2564 | } |
||
2565 | //#endif |
||
2566 | // Either workers are disabled, not supported or have thrown an exception. |
||
2567 | // Thus, we fallback to a faked worker. |
||
2568 | this.setupFakeWorker(); |
||
2569 | } |
||
2570 | WorkerTransport.prototype = { |
||
2571 | destroy: function WorkerTransport_destroy() { |
||
2572 | this.pageCache = []; |
||
2573 | this.pagePromises = []; |
||
2574 | var self = this; |
||
2575 | this.messageHandler.sendWithPromise('Terminate', null).then(function () { |
||
2576 | FontLoader.clear(); |
||
2577 | if (self.worker) { |
||
2578 | self.worker.terminate(); |
||
2579 | } |
||
2580 | }); |
||
2581 | }, |
||
2582 | |||
2583 | setupFakeWorker: function WorkerTransport_setupFakeWorker() { |
||
2584 | globalScope.PDFJS.disableWorker = true; |
||
2585 | |||
2586 | if (!PDFJS.fakeWorkerFilesLoadedCapability) { |
||
2587 | PDFJS.fakeWorkerFilesLoadedCapability = createPromiseCapability(); |
||
2588 | // In the developer build load worker_loader which in turn loads all the |
||
2589 | // other files and resolves the promise. In production only the |
||
2590 | // pdf.worker.js file is needed. |
||
2591 | //#if !PRODUCTION |
||
2592 | Util.loadScript(PDFJS.workerSrc); |
||
2593 | //#endif |
||
2594 | //#if PRODUCTION && SINGLE_FILE |
||
2595 | // PDFJS.fakeWorkerFilesLoadedCapability.resolve(); |
||
2596 | //#endif |
||
2597 | //#if PRODUCTION && !SINGLE_FILE |
||
2598 | // Util.loadScript(PDFJS.workerSrc, function() { |
||
2599 | // PDFJS.fakeWorkerFilesLoadedCapability.resolve(); |
||
2600 | // }); |
||
2601 | //#endif |
||
2602 | } |
||
2603 | PDFJS.fakeWorkerFilesLoadedCapability.promise.then(function () { |
||
2604 | warn('Setting up fake worker.'); |
||
2605 | // If we don't use a worker, just post/sendMessage to the main thread. |
||
2606 | var fakeWorker = { |
||
2607 | postMessage: function WorkerTransport_postMessage(obj) { |
||
2608 | fakeWorker.onmessage({data: obj}); |
||
2609 | }, |
||
2610 | terminate: function WorkerTransport_terminate() {} |
||
2611 | }; |
||
2612 | |||
2613 | var messageHandler = new MessageHandler('main', fakeWorker); |
||
2614 | this.setupMessageHandler(messageHandler); |
||
2615 | |||
2616 | // If the main thread is our worker, setup the handling for the messages |
||
2617 | // the main thread sends to it self. |
||
2618 | PDFJS.WorkerMessageHandler.setup(messageHandler); |
||
2619 | |||
2620 | this.workerInitializedCapability.resolve(); |
||
2621 | }.bind(this)); |
||
2622 | }, |
||
2623 | |||
2624 | setupMessageHandler: |
||
2625 | function WorkerTransport_setupMessageHandler(messageHandler) { |
||
2626 | this.messageHandler = messageHandler; |
||
2627 | |||
2628 | function updatePassword(password) { |
||
2629 | messageHandler.send('UpdatePassword', password); |
||
2630 | } |
||
2631 | |||
2632 | var pdfDataRangeTransport = this.pdfDataRangeTransport; |
||
2633 | if (pdfDataRangeTransport) { |
||
2634 | pdfDataRangeTransport.addRangeListener(function(begin, chunk) { |
||
2635 | messageHandler.send('OnDataRange', { |
||
2636 | begin: begin, |
||
2637 | chunk: chunk |
||
2638 | }); |
||
2639 | }); |
||
2640 | |||
2641 | pdfDataRangeTransport.addProgressListener(function(loaded) { |
||
2642 | messageHandler.send('OnDataProgress', { |
||
2643 | loaded: loaded |
||
2644 | }); |
||
2645 | }); |
||
2646 | |||
2647 | pdfDataRangeTransport.addProgressiveReadListener(function(chunk) { |
||
2648 | messageHandler.send('OnDataRange', { |
||
2649 | chunk: chunk |
||
2650 | }); |
||
2651 | }); |
||
2652 | |||
2653 | messageHandler.on('RequestDataRange', |
||
2654 | function transportDataRange(data) { |
||
2655 | pdfDataRangeTransport.requestDataRange(data.begin, data.end); |
||
2656 | }, this); |
||
2657 | } |
||
2658 | |||
2659 | messageHandler.on('GetDoc', function transportDoc(data) { |
||
2660 | var pdfInfo = data.pdfInfo; |
||
2661 | this.numPages = data.pdfInfo.numPages; |
||
2662 | var pdfDocument = new PDFDocumentProxy(pdfInfo, this); |
||
2663 | this.pdfDocument = pdfDocument; |
||
2664 | this.loadingTask._capability.resolve(pdfDocument); |
||
2665 | }, this); |
||
2666 | |||
2667 | messageHandler.on('NeedPassword', |
||
2668 | function transportNeedPassword(exception) { |
||
2669 | var loadingTask = this.loadingTask; |
||
2670 | if (loadingTask.onPassword) { |
||
2671 | return loadingTask.onPassword(updatePassword, |
||
2672 | PasswordResponses.NEED_PASSWORD); |
||
2673 | } |
||
2674 | loadingTask._capability.reject( |
||
2675 | new PasswordException(exception.message, exception.code)); |
||
2676 | }, this); |
||
2677 | |||
2678 | messageHandler.on('IncorrectPassword', |
||
2679 | function transportIncorrectPassword(exception) { |
||
2680 | var loadingTask = this.loadingTask; |
||
2681 | if (loadingTask.onPassword) { |
||
2682 | return loadingTask.onPassword(updatePassword, |
||
2683 | PasswordResponses.INCORRECT_PASSWORD); |
||
2684 | } |
||
2685 | loadingTask._capability.reject( |
||
2686 | new PasswordException(exception.message, exception.code)); |
||
2687 | }, this); |
||
2688 | |||
2689 | messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) { |
||
2690 | this.loadingTask._capability.reject( |
||
2691 | new InvalidPDFException(exception.message)); |
||
2692 | }, this); |
||
2693 | |||
2694 | messageHandler.on('MissingPDF', function transportMissingPDF(exception) { |
||
2695 | this.loadingTask._capability.reject( |
||
2696 | new MissingPDFException(exception.message)); |
||
2697 | }, this); |
||
2698 | |||
2699 | messageHandler.on('UnexpectedResponse', |
||
2700 | function transportUnexpectedResponse(exception) { |
||
2701 | this.loadingTask._capability.reject( |
||
2702 | new UnexpectedResponseException(exception.message, exception.status)); |
||
2703 | }, this); |
||
2704 | |||
2705 | messageHandler.on('UnknownError', |
||
2706 | function transportUnknownError(exception) { |
||
2707 | this.loadingTask._capability.reject( |
||
2708 | new UnknownErrorException(exception.message, exception.details)); |
||
2709 | }, this); |
||
2710 | |||
2711 | messageHandler.on('DataLoaded', function transportPage(data) { |
||
2712 | this.downloadInfoCapability.resolve(data); |
||
2713 | }, this); |
||
2714 | |||
2715 | messageHandler.on('PDFManagerReady', function transportPage(data) { |
||
2716 | if (this.pdfDataRangeTransport) { |
||
2717 | this.pdfDataRangeTransport.transportReady(); |
||
2718 | } |
||
2719 | }, this); |
||
2720 | |||
2721 | messageHandler.on('StartRenderPage', function transportRender(data) { |
||
2722 | var page = this.pageCache[data.pageIndex]; |
||
2723 | |||
2724 | page.stats.timeEnd('Page Request'); |
||
2725 | page._startRenderPage(data.transparency, data.intent); |
||
2726 | }, this); |
||
2727 | |||
2728 | messageHandler.on('RenderPageChunk', function transportRender(data) { |
||
2729 | var page = this.pageCache[data.pageIndex]; |
||
2730 | |||
2731 | page._renderPageChunk(data.operatorList, data.intent); |
||
2732 | }, this); |
||
2733 | |||
2734 | messageHandler.on('commonobj', function transportObj(data) { |
||
2735 | var id = data[0]; |
||
2736 | var type = data[1]; |
||
2737 | if (this.commonObjs.hasData(id)) { |
||
2738 | return; |
||
2739 | } |
||
2740 | |||
2741 | switch (type) { |
||
2742 | case 'Font': |
||
2743 | var exportedData = data[2]; |
||
2744 | |||
2745 | var font; |
||
2746 | if ('error' in exportedData) { |
||
2747 | var error = exportedData.error; |
||
2748 | warn('Error during font loading: ' + error); |
||
2749 | this.commonObjs.resolve(id, error); |
||
2750 | break; |
||
2751 | } else { |
||
2752 | font = new FontFaceObject(exportedData); |
||
2753 | } |
||
2754 | |||
2755 | FontLoader.bind( |
||
2756 | [font], |
||
2757 | function fontReady(fontObjs) { |
||
2758 | this.commonObjs.resolve(id, font); |
||
2759 | }.bind(this) |
||
2760 | ); |
||
2761 | break; |
||
2762 | case 'FontPath': |
||
2763 | this.commonObjs.resolve(id, data[2]); |
||
2764 | break; |
||
2765 | default: |
||
2766 | error('Got unknown common object type ' + type); |
||
2767 | } |
||
2768 | }, this); |
||
2769 | |||
2770 | messageHandler.on('obj', function transportObj(data) { |
||
2771 | var id = data[0]; |
||
2772 | var pageIndex = data[1]; |
||
2773 | var type = data[2]; |
||
2774 | var pageProxy = this.pageCache[pageIndex]; |
||
2775 | var imageData; |
||
2776 | if (pageProxy.objs.hasData(id)) { |
||
2777 | return; |
||
2778 | } |
||
2779 | |||
2780 | switch (type) { |
||
2781 | case 'JpegStream': |
||
2782 | imageData = data[3]; |
||
2783 | loadJpegStream(id, imageData, pageProxy.objs); |
||
2784 | break; |
||
2785 | case 'Image': |
||
2786 | imageData = data[3]; |
||
2787 | pageProxy.objs.resolve(id, imageData); |
||
2788 | |||
2789 | // heuristics that will allow not to store large data |
||
2790 | var MAX_IMAGE_SIZE_TO_STORE = 8000000; |
||
2791 | if (imageData && 'data' in imageData && |
||
2792 | imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) { |
||
2793 | pageProxy.cleanupAfterRender = true; |
||
2794 | } |
||
2795 | break; |
||
2796 | default: |
||
2797 | error('Got unknown object type ' + type); |
||
2798 | } |
||
2799 | }, this); |
||
2800 | |||
2801 | messageHandler.on('DocProgress', function transportDocProgress(data) { |
||
2802 | var loadingTask = this.loadingTask; |
||
2803 | if (loadingTask.onProgress) { |
||
2804 | loadingTask.onProgress({ |
||
2805 | loaded: data.loaded, |
||
2806 | total: data.total |
||
2807 | }); |
||
2808 | } |
||
2809 | }, this); |
||
2810 | |||
2811 | messageHandler.on('PageError', function transportError(data) { |
||
2812 | var page = this.pageCache[data.pageNum - 1]; |
||
2813 | var intentState = page.intentStates[data.intent]; |
||
2814 | if (intentState.displayReadyCapability) { |
||
2815 | intentState.displayReadyCapability.reject(data.error); |
||
2816 | } else { |
||
2817 | error(data.error); |
||
2818 | } |
||
2819 | }, this); |
||
2820 | |||
2821 | messageHandler.on('JpegDecode', function(data) { |
||
2822 | var imageUrl = data[0]; |
||
2823 | var components = data[1]; |
||
2824 | if (components !== 3 && components !== 1) { |
||
2825 | return Promise.reject( |
||
2826 | new Error('Only 3 components or 1 component can be returned')); |
||
2827 | } |
||
2828 | |||
2829 | return new Promise(function (resolve, reject) { |
||
2830 | var img = new Image(); |
||
2831 | img.onload = function () { |
||
2832 | var width = img.width; |
||
2833 | var height = img.height; |
||
2834 | var size = width * height; |
||
2835 | var rgbaLength = size * 4; |
||
2836 | var buf = new Uint8Array(size * components); |
||
2837 | var tmpCanvas = createScratchCanvas(width, height); |
||
2838 | var tmpCtx = tmpCanvas.getContext('2d'); |
||
2839 | tmpCtx.drawImage(img, 0, 0); |
||
2840 | var data = tmpCtx.getImageData(0, 0, width, height).data; |
||
2841 | var i, j; |
||
2842 | |||
2843 | if (components === 3) { |
||
2844 | for (i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { |
||
2845 | buf[j] = data[i]; |
||
2846 | buf[j + 1] = data[i + 1]; |
||
2847 | buf[j + 2] = data[i + 2]; |
||
2848 | } |
||
2849 | } else if (components === 1) { |
||
2850 | for (i = 0, j = 0; i < rgbaLength; i += 4, j++) { |
||
2851 | buf[j] = data[i]; |
||
2852 | } |
||
2853 | } |
||
2854 | resolve({ data: buf, width: width, height: height}); |
||
2855 | }; |
||
2856 | img.onerror = function () { |
||
2857 | reject(new Error('JpegDecode failed to load image')); |
||
2858 | }; |
||
2859 | img.src = imageUrl; |
||
2860 | }); |
||
2861 | }); |
||
2862 | }, |
||
2863 | |||
2864 | fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) { |
||
2865 | this.loadingTask = loadingTask; |
||
2866 | |||
2867 | source.disableAutoFetch = PDFJS.disableAutoFetch; |
||
2868 | source.disableStream = PDFJS.disableStream; |
||
2869 | source.chunkedViewerLoading = !!this.pdfDataRangeTransport; |
||
2870 | if (this.pdfDataRangeTransport) { |
||
2871 | source.length = this.pdfDataRangeTransport.length; |
||
2872 | source.initialData = this.pdfDataRangeTransport.initialData; |
||
2873 | } |
||
2874 | this.messageHandler.send('GetDocRequest', { |
||
2875 | source: source, |
||
2876 | disableRange: PDFJS.disableRange, |
||
2877 | maxImageSize: PDFJS.maxImageSize, |
||
2878 | cMapUrl: PDFJS.cMapUrl, |
||
2879 | cMapPacked: PDFJS.cMapPacked, |
||
2880 | disableFontFace: PDFJS.disableFontFace, |
||
2881 | disableCreateObjectURL: PDFJS.disableCreateObjectURL, |
||
2882 | verbosity: PDFJS.verbosity |
||
2883 | }); |
||
2884 | }, |
||
2885 | |||
2886 | getData: function WorkerTransport_getData() { |
||
2887 | return this.messageHandler.sendWithPromise('GetData', null); |
||
2888 | }, |
||
2889 | |||
2890 | getPage: function WorkerTransport_getPage(pageNumber, capability) { |
||
2891 | if (pageNumber <= 0 || pageNumber > this.numPages || |
||
2892 | (pageNumber|0) !== pageNumber) { |
||
2893 | return Promise.reject(new Error('Invalid page request')); |
||
2894 | } |
||
2895 | |||
2896 | var pageIndex = pageNumber - 1; |
||
2897 | if (pageIndex in this.pagePromises) { |
||
2898 | return this.pagePromises[pageIndex]; |
||
2899 | } |
||
2900 | var promise = this.messageHandler.sendWithPromise('GetPage', { |
||
2901 | pageIndex: pageIndex |
||
2902 | }).then(function (pageInfo) { |
||
2903 | var page = new PDFPageProxy(pageIndex, pageInfo, this); |
||
2904 | this.pageCache[pageIndex] = page; |
||
2905 | return page; |
||
2906 | }.bind(this)); |
||
2907 | this.pagePromises[pageIndex] = promise; |
||
2908 | return promise; |
||
2909 | }, |
||
2910 | |||
2911 | getPageIndex: function WorkerTransport_getPageIndexByRef(ref) { |
||
2912 | return this.messageHandler.sendWithPromise('GetPageIndex', { ref: ref }); |
||
2913 | }, |
||
2914 | |||
2915 | getAnnotations: function WorkerTransport_getAnnotations(pageIndex) { |
||
2916 | return this.messageHandler.sendWithPromise('GetAnnotations', |
||
2917 | { pageIndex: pageIndex }); |
||
2918 | }, |
||
2919 | |||
2920 | getDestinations: function WorkerTransport_getDestinations() { |
||
2921 | return this.messageHandler.sendWithPromise('GetDestinations', null); |
||
2922 | }, |
||
2923 | |||
2924 | getDestination: function WorkerTransport_getDestination(id) { |
||
2925 | return this.messageHandler.sendWithPromise('GetDestination', { id: id } ); |
||
2926 | }, |
||
2927 | |||
2928 | getAttachments: function WorkerTransport_getAttachments() { |
||
2929 | return this.messageHandler.sendWithPromise('GetAttachments', null); |
||
2930 | }, |
||
2931 | |||
2932 | getJavaScript: function WorkerTransport_getJavaScript() { |
||
2933 | return this.messageHandler.sendWithPromise('GetJavaScript', null); |
||
2934 | }, |
||
2935 | |||
2936 | getOutline: function WorkerTransport_getOutline() { |
||
2937 | return this.messageHandler.sendWithPromise('GetOutline', null); |
||
2938 | }, |
||
2939 | |||
2940 | getMetadata: function WorkerTransport_getMetadata() { |
||
2941 | return this.messageHandler.sendWithPromise('GetMetadata', null). |
||
2942 | then(function transportMetadata(results) { |
||
2943 | return { |
||
2944 | info: results[0], |
||
2945 | metadata: (results[1] ? new PDFJS.Metadata(results[1]) : null) |
||
2946 | }; |
||
2947 | }); |
||
2948 | }, |
||
2949 | |||
2950 | getStats: function WorkerTransport_getStats() { |
||
2951 | return this.messageHandler.sendWithPromise('GetStats', null); |
||
2952 | }, |
||
2953 | |||
2954 | startCleanup: function WorkerTransport_startCleanup() { |
||
2955 | this.messageHandler.sendWithPromise('Cleanup', null). |
||
2956 | then(function endCleanup() { |
||
2957 | for (var i = 0, ii = this.pageCache.length; i < ii; i++) { |
||
2958 | var page = this.pageCache[i]; |
||
2959 | if (page) { |
||
2960 | page.destroy(); |
||
2961 | } |
||
2962 | } |
||
2963 | this.commonObjs.clear(); |
||
2964 | FontLoader.clear(); |
||
2965 | }.bind(this)); |
||
2966 | } |
||
2967 | }; |
||
2968 | return WorkerTransport; |
||
2969 | |||
2970 | })(); |
||
2971 | |||
2972 | /** |
||
2973 | * A PDF document and page is built of many objects. E.g. there are objects |
||
2974 | * for fonts, images, rendering code and such. These objects might get processed |
||
2975 | * inside of a worker. The `PDFObjects` implements some basic functions to |
||
2976 | * manage these objects. |
||
2977 | * @ignore |
||
2978 | */ |
||
2979 | var PDFObjects = (function PDFObjectsClosure() { |
||
2980 | function PDFObjects() { |
||
2981 | this.objs = {}; |
||
2982 | } |
||
2983 | |||
2984 | PDFObjects.prototype = { |
||
2985 | /** |
||
2986 | * Internal function. |
||
2987 | * Ensures there is an object defined for `objId`. |
||
2988 | */ |
||
2989 | ensureObj: function PDFObjects_ensureObj(objId) { |
||
2990 | if (this.objs[objId]) { |
||
2991 | return this.objs[objId]; |
||
2992 | } |
||
2993 | |||
2994 | var obj = { |
||
2995 | capability: createPromiseCapability(), |
||
2996 | data: null, |
||
2997 | resolved: false |
||
2998 | }; |
||
2999 | this.objs[objId] = obj; |
||
3000 | |||
3001 | return obj; |
||
3002 | }, |
||
3003 | |||
3004 | /** |
||
3005 | * If called *without* callback, this returns the data of `objId` but the |
||
3006 | * object needs to be resolved. If it isn't, this function throws. |
||
3007 | * |
||
3008 | * If called *with* a callback, the callback is called with the data of the |
||
3009 | * object once the object is resolved. That means, if you call this |
||
3010 | * function and the object is already resolved, the callback gets called |
||
3011 | * right away. |
||
3012 | */ |
||
3013 | get: function PDFObjects_get(objId, callback) { |
||
3014 | // If there is a callback, then the get can be async and the object is |
||
3015 | // not required to be resolved right now |
||
3016 | if (callback) { |
||
3017 | this.ensureObj(objId).capability.promise.then(callback); |
||
3018 | return null; |
||
3019 | } |
||
3020 | |||
3021 | // If there isn't a callback, the user expects to get the resolved data |
||
3022 | // directly. |
||
3023 | var obj = this.objs[objId]; |
||
3024 | |||
3025 | // If there isn't an object yet or the object isn't resolved, then the |
||
3026 | // data isn't ready yet! |
||
3027 | if (!obj || !obj.resolved) { |
||
3028 | error('Requesting object that isn\'t resolved yet ' + objId); |
||
3029 | } |
||
3030 | |||
3031 | return obj.data; |
||
3032 | }, |
||
3033 | |||
3034 | /** |
||
3035 | * Resolves the object `objId` with optional `data`. |
||
3036 | */ |
||
3037 | resolve: function PDFObjects_resolve(objId, data) { |
||
3038 | var obj = this.ensureObj(objId); |
||
3039 | |||
3040 | obj.resolved = true; |
||
3041 | obj.data = data; |
||
3042 | obj.capability.resolve(data); |
||
3043 | }, |
||
3044 | |||
3045 | isResolved: function PDFObjects_isResolved(objId) { |
||
3046 | var objs = this.objs; |
||
3047 | |||
3048 | if (!objs[objId]) { |
||
3049 | return false; |
||
3050 | } else { |
||
3051 | return objs[objId].resolved; |
||
3052 | } |
||
3053 | }, |
||
3054 | |||
3055 | hasData: function PDFObjects_hasData(objId) { |
||
3056 | return this.isResolved(objId); |
||
3057 | }, |
||
3058 | |||
3059 | /** |
||
3060 | * Returns the data of `objId` if object exists, null otherwise. |
||
3061 | */ |
||
3062 | getData: function PDFObjects_getData(objId) { |
||
3063 | var objs = this.objs; |
||
3064 | if (!objs[objId] || !objs[objId].resolved) { |
||
3065 | return null; |
||
3066 | } else { |
||
3067 | return objs[objId].data; |
||
3068 | } |
||
3069 | }, |
||
3070 | |||
3071 | clear: function PDFObjects_clear() { |
||
3072 | this.objs = {}; |
||
3073 | } |
||
3074 | }; |
||
3075 | return PDFObjects; |
||
3076 | })(); |
||
3077 | |||
3078 | /** |
||
3079 | * Allows controlling of the rendering tasks. |
||
3080 | * @class |
||
3081 | */ |
||
3082 | var RenderTask = (function RenderTaskClosure() { |
||
3083 | function RenderTask(internalRenderTask) { |
||
3084 | this._internalRenderTask = internalRenderTask; |
||
3085 | |||
3086 | /** |
||
3087 | * Callback for incremental rendering -- a function that will be called |
||
3088 | * each time the rendering is paused. To continue rendering call the |
||
3089 | * function that is the first argument to the callback. |
||
3090 | * @type {function} |
||
3091 | */ |
||
3092 | this.onContinue = null; |
||
3093 | } |
||
3094 | |||
3095 | RenderTask.prototype = /** @lends RenderTask.prototype */ { |
||
3096 | /** |
||
3097 | * Promise for rendering task completion. |
||
3098 | * @return {Promise} |
||
3099 | */ |
||
3100 | get promise() { |
||
3101 | return this._internalRenderTask.capability.promise; |
||
3102 | }, |
||
3103 | |||
3104 | /** |
||
3105 | * Cancels the rendering task. If the task is currently rendering it will |
||
3106 | * not be cancelled until graphics pauses with a timeout. The promise that |
||
3107 | * this object extends will resolved when cancelled. |
||
3108 | */ |
||
3109 | cancel: function RenderTask_cancel() { |
||
3110 | this._internalRenderTask.cancel(); |
||
3111 | }, |
||
3112 | |||
3113 | /** |
||
3114 | * Registers callbacks to indicate the rendering task completion. |
||
3115 | * |
||
3116 | * @param {function} onFulfilled The callback for the rendering completion. |
||
3117 | * @param {function} onRejected The callback for the rendering failure. |
||
3118 | * @return {Promise} A promise that is resolved after the onFulfilled or |
||
3119 | * onRejected callback. |
||
3120 | */ |
||
3121 | then: function RenderTask_then(onFulfilled, onRejected) { |
||
3122 | return this.promise.then.apply(this.promise, arguments); |
||
3123 | } |
||
3124 | }; |
||
3125 | |||
3126 | return RenderTask; |
||
3127 | })(); |
||
3128 | |||
3129 | /** |
||
3130 | * For internal use only. |
||
3131 | * @ignore |
||
3132 | */ |
||
3133 | var InternalRenderTask = (function InternalRenderTaskClosure() { |
||
3134 | |||
3135 | function InternalRenderTask(callback, params, objs, commonObjs, operatorList, |
||
3136 | pageNumber) { |
||
3137 | this.callback = callback; |
||
3138 | this.params = params; |
||
3139 | this.objs = objs; |
||
3140 | this.commonObjs = commonObjs; |
||
3141 | this.operatorListIdx = null; |
||
3142 | this.operatorList = operatorList; |
||
3143 | this.pageNumber = pageNumber; |
||
3144 | this.running = false; |
||
3145 | this.graphicsReadyCallback = null; |
||
3146 | this.graphicsReady = false; |
||
3147 | this.cancelled = false; |
||
3148 | this.capability = createPromiseCapability(); |
||
3149 | this.task = new RenderTask(this); |
||
3150 | // caching this-bound methods |
||
3151 | this._continueBound = this._continue.bind(this); |
||
3152 | this._scheduleNextBound = this._scheduleNext.bind(this); |
||
3153 | this._nextBound = this._next.bind(this); |
||
3154 | } |
||
3155 | |||
3156 | InternalRenderTask.prototype = { |
||
3157 | |||
3158 | initalizeGraphics: |
||
3159 | function InternalRenderTask_initalizeGraphics(transparency) { |
||
3160 | |||
3161 | if (this.cancelled) { |
||
3162 | return; |
||
3163 | } |
||
3164 | if (PDFJS.pdfBug && 'StepperManager' in globalScope && |
||
3165 | globalScope.StepperManager.enabled) { |
||
3166 | this.stepper = globalScope.StepperManager.create(this.pageNumber - 1); |
||
3167 | this.stepper.init(this.operatorList); |
||
3168 | this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); |
||
3169 | } |
||
3170 | |||
3171 | var params = this.params; |
||
3172 | this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs, |
||
3173 | this.objs, params.imageLayer); |
||
3174 | |||
3175 | this.gfx.beginDrawing(params.viewport, transparency); |
||
3176 | this.operatorListIdx = 0; |
||
3177 | this.graphicsReady = true; |
||
3178 | if (this.graphicsReadyCallback) { |
||
3179 | this.graphicsReadyCallback(); |
||
3180 | } |
||
3181 | }, |
||
3182 | |||
3183 | cancel: function InternalRenderTask_cancel() { |
||
3184 | this.running = false; |
||
3185 | this.cancelled = true; |
||
3186 | this.callback('cancelled'); |
||
3187 | }, |
||
3188 | |||
3189 | operatorListChanged: function InternalRenderTask_operatorListChanged() { |
||
3190 | if (!this.graphicsReady) { |
||
3191 | if (!this.graphicsReadyCallback) { |
||
3192 | this.graphicsReadyCallback = this._continueBound; |
||
3193 | } |
||
3194 | return; |
||
3195 | } |
||
3196 | |||
3197 | if (this.stepper) { |
||
3198 | this.stepper.updateOperatorList(this.operatorList); |
||
3199 | } |
||
3200 | |||
3201 | if (this.running) { |
||
3202 | return; |
||
3203 | } |
||
3204 | this._continue(); |
||
3205 | }, |
||
3206 | |||
3207 | _continue: function InternalRenderTask__continue() { |
||
3208 | this.running = true; |
||
3209 | if (this.cancelled) { |
||
3210 | return; |
||
3211 | } |
||
3212 | if (this.task.onContinue) { |
||
3213 | this.task.onContinue.call(this.task, this._scheduleNextBound); |
||
3214 | } else { |
||
3215 | this._scheduleNext(); |
||
3216 | } |
||
3217 | }, |
||
3218 | |||
3219 | _scheduleNext: function InternalRenderTask__scheduleNext() { |
||
3220 | window.requestAnimationFrame(this._nextBound); |
||
3221 | }, |
||
3222 | |||
3223 | _next: function InternalRenderTask__next() { |
||
3224 | if (this.cancelled) { |
||
3225 | return; |
||
3226 | } |
||
3227 | this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, |
||
3228 | this.operatorListIdx, |
||
3229 | this._continueBound, |
||
3230 | this.stepper); |
||
3231 | if (this.operatorListIdx === this.operatorList.argsArray.length) { |
||
3232 | this.running = false; |
||
3233 | if (this.operatorList.lastChunk) { |
||
3234 | this.gfx.endDrawing(); |
||
3235 | this.callback(); |
||
3236 | } |
||
3237 | } |
||
3238 | } |
||
3239 | |||
3240 | }; |
||
3241 | |||
3242 | return InternalRenderTask; |
||
3243 | })(); |
||
3244 | |||
3245 | |||
3246 | var Metadata = PDFJS.Metadata = (function MetadataClosure() { |
||
3247 | function fixMetadata(meta) { |
||
3248 | return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) { |
||
3249 | var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g, |
||
3250 | function(code, d1, d2, d3) { |
||
3251 | return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1); |
||
3252 | }); |
||
3253 | var chars = ''; |
||
3254 | for (var i = 0; i < bytes.length; i += 2) { |
||
3255 | var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1); |
||
3256 | chars += code >= 32 && code < 127 && code !== 60 && code !== 62 && |
||
3257 | code !== 38 && false ? String.fromCharCode(code) : |
||
3258 | '&#x' + (0x10000 + code).toString(16).substring(1) + ';'; |
||
3259 | } |
||
3260 | return '>' + chars; |
||
3261 | }); |
||
3262 | } |
||
3263 | |||
3264 | function Metadata(meta) { |
||
3265 | if (typeof meta === 'string') { |
||
3266 | // Ghostscript produces invalid metadata |
||
3267 | meta = fixMetadata(meta); |
||
3268 | |||
3269 | var parser = new DOMParser(); |
||
3270 | meta = parser.parseFromString(meta, 'application/xml'); |
||
3271 | } else if (!(meta instanceof Document)) { |
||
3272 | error('Metadata: Invalid metadata object'); |
||
3273 | } |
||
3274 | |||
3275 | this.metaDocument = meta; |
||
3276 | this.metadata = {}; |
||
3277 | this.parse(); |
||
3278 | } |
||
3279 | |||
3280 | Metadata.prototype = { |
||
3281 | parse: function Metadata_parse() { |
||
3282 | var doc = this.metaDocument; |
||
3283 | var rdf = doc.documentElement; |
||
3284 | |||
3285 | if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta> |
||
3286 | rdf = rdf.firstChild; |
||
3287 | while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') { |
||
3288 | rdf = rdf.nextSibling; |
||
3289 | } |
||
3290 | } |
||
3291 | |||
3292 | var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null; |
||
3293 | if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) { |
||
3294 | return; |
||
3295 | } |
||
3296 | |||
3297 | var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength; |
||
3298 | for (i = 0, length = children.length; i < length; i++) { |
||
3299 | desc = children[i]; |
||
3300 | if (desc.nodeName.toLowerCase() !== 'rdf:description') { |
||
3301 | continue; |
||
3302 | } |
||
3303 | |||
3304 | for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) { |
||
3305 | if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') { |
||
3306 | entry = desc.childNodes[ii]; |
||
3307 | name = entry.nodeName.toLowerCase(); |
||
3308 | this.metadata[name] = entry.textContent.trim(); |
||
3309 | } |
||
3310 | } |
||
3311 | } |
||
3312 | }, |
||
3313 | |||
3314 | get: function Metadata_get(name) { |
||
3315 | return this.metadata[name] || null; |
||
3316 | }, |
||
3317 | |||
3318 | has: function Metadata_has(name) { |
||
3319 | return typeof this.metadata[name] !== 'undefined'; |
||
3320 | } |
||
3321 | }; |
||
3322 | |||
3323 | return Metadata; |
||
3324 | })(); |
||
3325 | |||
3326 | |||
3327 | // <canvas> contexts store most of the state we need natively. |
||
3328 | // However, PDF needs a bit more state, which we store here. |
||
3329 | |||
3330 | // Minimal font size that would be used during canvas fillText operations. |
||
3331 | var MIN_FONT_SIZE = 16; |
||
3332 | // Maximum font size that would be used during canvas fillText operations. |
||
3333 | var MAX_FONT_SIZE = 100; |
||
3334 | var MAX_GROUP_SIZE = 4096; |
||
3335 | |||
3336 | // Heuristic value used when enforcing minimum line widths. |
||
3337 | var MIN_WIDTH_FACTOR = 0.65; |
||
3338 | |||
3339 | var COMPILE_TYPE3_GLYPHS = true; |
||
3340 | var MAX_SIZE_TO_COMPILE = 1000; |
||
3341 | |||
3342 | var FULL_CHUNK_HEIGHT = 16; |
||
3343 | |||
3344 | function createScratchCanvas(width, height) { |
||
3345 | var canvas = document.createElement('canvas'); |
||
3346 | canvas.width = width; |
||
3347 | canvas.height = height; |
||
3348 | return canvas; |
||
3349 | } |
||
3350 | |||
3351 | function addContextCurrentTransform(ctx) { |
||
3352 | // If the context doesn't expose a `mozCurrentTransform`, add a JS based one. |
||
3353 | if (!ctx.mozCurrentTransform) { |
||
3354 | ctx._originalSave = ctx.save; |
||
3355 | ctx._originalRestore = ctx.restore; |
||
3356 | ctx._originalRotate = ctx.rotate; |
||
3357 | ctx._originalScale = ctx.scale; |
||
3358 | ctx._originalTranslate = ctx.translate; |
||
3359 | ctx._originalTransform = ctx.transform; |
||
3360 | ctx._originalSetTransform = ctx.setTransform; |
||
3361 | |||
3362 | ctx._transformMatrix = ctx._transformMatrix || [1, 0, 0, 1, 0, 0]; |
||
3363 | ctx._transformStack = []; |
||
3364 | |||
3365 | Object.defineProperty(ctx, 'mozCurrentTransform', { |
||
3366 | get: function getCurrentTransform() { |
||
3367 | return this._transformMatrix; |
||
3368 | } |
||
3369 | }); |
||
3370 | |||
3371 | Object.defineProperty(ctx, 'mozCurrentTransformInverse', { |
||
3372 | get: function getCurrentTransformInverse() { |
||
3373 | // Calculation done using WolframAlpha: |
||
3374 | // http://www.wolframalpha.com/input/? |
||
3375 | // i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}} |
||
3376 | |||
3377 | var m = this._transformMatrix; |
||
3378 | var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5]; |
||
3379 | |||
3380 | var ad_bc = a * d - b * c; |
||
3381 | var bc_ad = b * c - a * d; |
||
3382 | |||
3383 | return [ |
||
3384 | d / ad_bc, |
||
3385 | b / bc_ad, |
||
3386 | c / bc_ad, |
||
3387 | a / ad_bc, |
||
3388 | (d * e - c * f) / bc_ad, |
||
3389 | (b * e - a * f) / ad_bc |
||
3390 | ]; |
||
3391 | } |
||
3392 | }); |
||
3393 | |||
3394 | ctx.save = function ctxSave() { |
||
3395 | var old = this._transformMatrix; |
||
3396 | this._transformStack.push(old); |
||
3397 | this._transformMatrix = old.slice(0, 6); |
||
3398 | |||
3399 | this._originalSave(); |
||
3400 | }; |
||
3401 | |||
3402 | ctx.restore = function ctxRestore() { |
||
3403 | var prev = this._transformStack.pop(); |
||
3404 | if (prev) { |
||
3405 | this._transformMatrix = prev; |
||
3406 | this._originalRestore(); |
||
3407 | } |
||
3408 | }; |
||
3409 | |||
3410 | ctx.translate = function ctxTranslate(x, y) { |
||
3411 | var m = this._transformMatrix; |
||
3412 | m[4] = m[0] * x + m[2] * y + m[4]; |
||
3413 | m[5] = m[1] * x + m[3] * y + m[5]; |
||
3414 | |||
3415 | this._originalTranslate(x, y); |
||
3416 | }; |
||
3417 | |||
3418 | ctx.scale = function ctxScale(x, y) { |
||
3419 | var m = this._transformMatrix; |
||
3420 | m[0] = m[0] * x; |
||
3421 | m[1] = m[1] * x; |
||
3422 | m[2] = m[2] * y; |
||
3423 | m[3] = m[3] * y; |
||
3424 | |||
3425 | this._originalScale(x, y); |
||
3426 | }; |
||
3427 | |||
3428 | ctx.transform = function ctxTransform(a, b, c, d, e, f) { |
||
3429 | var m = this._transformMatrix; |
||
3430 | this._transformMatrix = [ |
||
3431 | m[0] * a + m[2] * b, |
||
3432 | m[1] * a + m[3] * b, |
||
3433 | m[0] * c + m[2] * d, |
||
3434 | m[1] * c + m[3] * d, |
||
3435 | m[0] * e + m[2] * f + m[4], |
||
3436 | m[1] * e + m[3] * f + m[5] |
||
3437 | ]; |
||
3438 | |||
3439 | ctx._originalTransform(a, b, c, d, e, f); |
||
3440 | }; |
||
3441 | |||
3442 | ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) { |
||
3443 | this._transformMatrix = [a, b, c, d, e, f]; |
||
3444 | |||
3445 | ctx._originalSetTransform(a, b, c, d, e, f); |
||
3446 | }; |
||
3447 | |||
3448 | ctx.rotate = function ctxRotate(angle) { |
||
3449 | var cosValue = Math.cos(angle); |
||
3450 | var sinValue = Math.sin(angle); |
||
3451 | |||
3452 | var m = this._transformMatrix; |
||
3453 | this._transformMatrix = [ |
||
3454 | m[0] * cosValue + m[2] * sinValue, |
||
3455 | m[1] * cosValue + m[3] * sinValue, |
||
3456 | m[0] * (-sinValue) + m[2] * cosValue, |
||
3457 | m[1] * (-sinValue) + m[3] * cosValue, |
||
3458 | m[4], |
||
3459 | m[5] |
||
3460 | ]; |
||
3461 | |||
3462 | this._originalRotate(angle); |
||
3463 | }; |
||
3464 | } |
||
3465 | } |
||
3466 | |||
3467 | var CachedCanvases = (function CachedCanvasesClosure() { |
||
3468 | var cache = {}; |
||
3469 | return { |
||
3470 | getCanvas: function CachedCanvases_getCanvas(id, width, height, |
||
3471 | trackTransform) { |
||
3472 | var canvasEntry; |
||
3473 | if (cache[id] !== undefined) { |
||
3474 | canvasEntry = cache[id]; |
||
3475 | canvasEntry.canvas.width = width; |
||
3476 | canvasEntry.canvas.height = height; |
||
3477 | // reset canvas transform for emulated mozCurrentTransform, if needed |
||
3478 | canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0); |
||
3479 | } else { |
||
3480 | var canvas = createScratchCanvas(width, height); |
||
3481 | var ctx = canvas.getContext('2d'); |
||
3482 | if (trackTransform) { |
||
3483 | addContextCurrentTransform(ctx); |
||
3484 | } |
||
3485 | cache[id] = canvasEntry = {canvas: canvas, context: ctx}; |
||
3486 | } |
||
3487 | return canvasEntry; |
||
3488 | }, |
||
3489 | clear: function () { |
||
3490 | for (var id in cache) { |
||
3491 | var canvasEntry = cache[id]; |
||
3492 | // Zeroing the width and height causes Firefox to release graphics |
||
3493 | // resources immediately, which can greatly reduce memory consumption. |
||
3494 | canvasEntry.canvas.width = 0; |
||
3495 | canvasEntry.canvas.height = 0; |
||
3496 | delete cache[id]; |
||
3497 | } |
||
3498 | } |
||
3499 | }; |
||
3500 | })(); |
||
3501 | |||
3502 | function compileType3Glyph(imgData) { |
||
3503 | var POINT_TO_PROCESS_LIMIT = 1000; |
||
3504 | |||
3505 | var width = imgData.width, height = imgData.height; |
||
3506 | var i, j, j0, width1 = width + 1; |
||
3507 | var points = new Uint8Array(width1 * (height + 1)); |
||
3508 | var POINT_TYPES = |
||
3509 | new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); |
||
3510 | |||
3511 | // decodes bit-packed mask data |
||
3512 | var lineSize = (width + 7) & ~7, data0 = imgData.data; |
||
3513 | var data = new Uint8Array(lineSize * height), pos = 0, ii; |
||
3514 | for (i = 0, ii = data0.length; i < ii; i++) { |
||
3515 | var mask = 128, elem = data0[i]; |
||
3516 | while (mask > 0) { |
||
3517 | data[pos++] = (elem & mask) ? 0 : 255; |
||
3518 | mask >>= 1; |
||
3519 | } |
||
3520 | } |
||
3521 | |||
3522 | // finding iteresting points: every point is located between mask pixels, |
||
3523 | // so there will be points of the (width + 1)x(height + 1) grid. Every point |
||
3524 | // will have flags assigned based on neighboring mask pixels: |
||
3525 | // 4 | 8 |
||
3526 | // --P-- |
||
3527 | // 2 | 1 |
||
3528 | // We are interested only in points with the flags: |
||
3529 | // - outside corners: 1, 2, 4, 8; |
||
3530 | // - inside corners: 7, 11, 13, 14; |
||
3531 | // - and, intersections: 5, 10. |
||
3532 | var count = 0; |
||
3533 | pos = 0; |
||
3534 | if (data[pos] !== 0) { |
||
3535 | points[0] = 1; |
||
3536 | ++count; |
||
3537 | } |
||
3538 | for (j = 1; j < width; j++) { |
||
3539 | if (data[pos] !== data[pos + 1]) { |
||
3540 | points[j] = data[pos] ? 2 : 1; |
||
3541 | ++count; |
||
3542 | } |
||
3543 | pos++; |
||
3544 | } |
||
3545 | if (data[pos] !== 0) { |
||
3546 | points[j] = 2; |
||
3547 | ++count; |
||
3548 | } |
||
3549 | for (i = 1; i < height; i++) { |
||
3550 | pos = i * lineSize; |
||
3551 | j0 = i * width1; |
||
3552 | if (data[pos - lineSize] !== data[pos]) { |
||
3553 | points[j0] = data[pos] ? 1 : 8; |
||
3554 | ++count; |
||
3555 | } |
||
3556 | // 'sum' is the position of the current pixel configuration in the 'TYPES' |
||
3557 | // array (in order 8-1-2-4, so we can use '>>2' to shift the column). |
||
3558 | var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0); |
||
3559 | for (j = 1; j < width; j++) { |
||
3560 | sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + |
||
3561 | (data[pos - lineSize + 1] ? 8 : 0); |
||
3562 | if (POINT_TYPES[sum]) { |
||
3563 | points[j0 + j] = POINT_TYPES[sum]; |
||
3564 | ++count; |
||
3565 | } |
||
3566 | pos++; |
||
3567 | } |
||
3568 | if (data[pos - lineSize] !== data[pos]) { |
||
3569 | points[j0 + j] = data[pos] ? 2 : 4; |
||
3570 | ++count; |
||
3571 | } |
||
3572 | |||
3573 | if (count > POINT_TO_PROCESS_LIMIT) { |
||
3574 | return null; |
||
3575 | } |
||
3576 | } |
||
3577 | |||
3578 | pos = lineSize * (height - 1); |
||
3579 | j0 = i * width1; |
||
3580 | if (data[pos] !== 0) { |
||
3581 | points[j0] = 8; |
||
3582 | ++count; |
||
3583 | } |
||
3584 | for (j = 1; j < width; j++) { |
||
3585 | if (data[pos] !== data[pos + 1]) { |
||
3586 | points[j0 + j] = data[pos] ? 4 : 8; |
||
3587 | ++count; |
||
3588 | } |
||
3589 | pos++; |
||
3590 | } |
||
3591 | if (data[pos] !== 0) { |
||
3592 | points[j0 + j] = 4; |
||
3593 | ++count; |
||
3594 | } |
||
3595 | if (count > POINT_TO_PROCESS_LIMIT) { |
||
3596 | return null; |
||
3597 | } |
||
3598 | |||
3599 | // building outlines |
||
3600 | var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]); |
||
3601 | var outlines = []; |
||
3602 | for (i = 0; count && i <= height; i++) { |
||
3603 | var p = i * width1; |
||
3604 | var end = p + width; |
||
3605 | while (p < end && !points[p]) { |
||
3606 | p++; |
||
3607 | } |
||
3608 | if (p === end) { |
||
3609 | continue; |
||
3610 | } |
||
3611 | var coords = [p % width1, i]; |
||
3612 | |||
3613 | var type = points[p], p0 = p, pp; |
||
3614 | do { |
||
3615 | var step = steps[type]; |
||
3616 | do { |
||
3617 | p += step; |
||
3618 | } while (!points[p]); |
||
3619 | |||
3620 | pp = points[p]; |
||
3621 | if (pp !== 5 && pp !== 10) { |
||
3622 | // set new direction |
||
3623 | type = pp; |
||
3624 | // delete mark |
||
3625 | points[p] = 0; |
||
3626 | } else { // type is 5 or 10, ie, a crossing |
||
3627 | // set new direction |
||
3628 | type = pp & ((0x33 * type) >> 4); |
||
3629 | // set new type for "future hit" |
||
3630 | points[p] &= (type >> 2 | type << 2); |
||
3631 | } |
||
3632 | |||
3633 | coords.push(p % width1); |
||
3634 | coords.push((p / width1) | 0); |
||
3635 | --count; |
||
3636 | } while (p0 !== p); |
||
3637 | outlines.push(coords); |
||
3638 | --i; |
||
3639 | } |
||
3640 | |||
3641 | var drawOutline = function(c) { |
||
3642 | c.save(); |
||
3643 | // the path shall be painted in [0..1]x[0..1] space |
||
3644 | c.scale(1 / width, -1 / height); |
||
3645 | c.translate(0, -height); |
||
3646 | c.beginPath(); |
||
3647 | for (var i = 0, ii = outlines.length; i < ii; i++) { |
||
3648 | var o = outlines[i]; |
||
3649 | c.moveTo(o[0], o[1]); |
||
3650 | for (var j = 2, jj = o.length; j < jj; j += 2) { |
||
3651 | c.lineTo(o[j], o[j+1]); |
||
3652 | } |
||
3653 | } |
||
3654 | c.fill(); |
||
3655 | c.beginPath(); |
||
3656 | c.restore(); |
||
3657 | }; |
||
3658 | |||
3659 | return drawOutline; |
||
3660 | } |
||
3661 | |||
3662 | var CanvasExtraState = (function CanvasExtraStateClosure() { |
||
3663 | function CanvasExtraState(old) { |
||
3664 | // Are soft masks and alpha values shapes or opacities? |
||
3665 | this.alphaIsShape = false; |
||
3666 | this.fontSize = 0; |
||
3667 | this.fontSizeScale = 1; |
||
3668 | this.textMatrix = IDENTITY_MATRIX; |
||
3669 | this.textMatrixScale = 1; |
||
3670 | this.fontMatrix = FONT_IDENTITY_MATRIX; |
||
3671 | this.leading = 0; |
||
3672 | // Current point (in user coordinates) |
||
3673 | this.x = 0; |
||
3674 | this.y = 0; |
||
3675 | // Start of text line (in text coordinates) |
||
3676 | this.lineX = 0; |
||
3677 | this.lineY = 0; |
||
3678 | // Character and word spacing |
||
3679 | this.charSpacing = 0; |
||
3680 | this.wordSpacing = 0; |
||
3681 | this.textHScale = 1; |
||
3682 | this.textRenderingMode = TextRenderingMode.FILL; |
||
3683 | this.textRise = 0; |
||
3684 | // Default fore and background colors |
||
3685 | this.fillColor = '#000000'; |
||
3686 | this.strokeColor = '#000000'; |
||
3687 | this.patternFill = false; |
||
3688 | // Note: fill alpha applies to all non-stroking operations |
||
3689 | this.fillAlpha = 1; |
||
3690 | this.strokeAlpha = 1; |
||
3691 | this.lineWidth = 1; |
||
3692 | this.activeSMask = null; // nonclonable field (see the save method below) |
||
3693 | |||
3694 | this.old = old; |
||
3695 | } |
||
3696 | |||
3697 | CanvasExtraState.prototype = { |
||
3698 | clone: function CanvasExtraState_clone() { |
||
3699 | return Object.create(this); |
||
3700 | }, |
||
3701 | setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) { |
||
3702 | this.x = x; |
||
3703 | this.y = y; |
||
3704 | } |
||
3705 | }; |
||
3706 | return CanvasExtraState; |
||
3707 | })(); |
||
3708 | |||
3709 | var CanvasGraphics = (function CanvasGraphicsClosure() { |
||
3710 | // Defines the time the executeOperatorList is going to be executing |
||
3711 | // before it stops and shedules a continue of execution. |
||
3712 | var EXECUTION_TIME = 15; |
||
3713 | // Defines the number of steps before checking the execution time |
||
3714 | var EXECUTION_STEPS = 10; |
||
3715 | |||
3716 | function CanvasGraphics(canvasCtx, commonObjs, objs, imageLayer) { |
||
3717 | this.ctx = canvasCtx; |
||
3718 | this.current = new CanvasExtraState(); |
||
3719 | this.stateStack = []; |
||
3720 | this.pendingClip = null; |
||
3721 | this.pendingEOFill = false; |
||
3722 | this.res = null; |
||
3723 | this.xobjs = null; |
||
3724 | this.commonObjs = commonObjs; |
||
3725 | this.objs = objs; |
||
3726 | this.imageLayer = imageLayer; |
||
3727 | this.groupStack = []; |
||
3728 | this.processingType3 = null; |
||
3729 | // Patterns are painted relative to the initial page/form transform, see pdf |
||
3730 | // spec 8.7.2 NOTE 1. |
||
3731 | this.baseTransform = null; |
||
3732 | this.baseTransformStack = []; |
||
3733 | this.groupLevel = 0; |
||
3734 | this.smaskStack = []; |
||
3735 | this.smaskCounter = 0; |
||
3736 | this.tempSMask = null; |
||
3737 | if (canvasCtx) { |
||
3738 | // NOTE: if mozCurrentTransform is polyfilled, then the current state of |
||
3739 | // the transformation must already be set in canvasCtx._transformMatrix. |
||
3740 | addContextCurrentTransform(canvasCtx); |
||
3741 | } |
||
3742 | this.cachedGetSinglePixelWidth = null; |
||
3743 | } |
||
3744 | |||
3745 | function putBinaryImageData(ctx, imgData) { |
||
3746 | if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) { |
||
3747 | ctx.putImageData(imgData, 0, 0); |
||
3748 | return; |
||
3749 | } |
||
3750 | |||
3751 | // Put the image data to the canvas in chunks, rather than putting the |
||
3752 | // whole image at once. This saves JS memory, because the ImageData object |
||
3753 | // is smaller. It also possibly saves C++ memory within the implementation |
||
3754 | // of putImageData(). (E.g. in Firefox we make two short-lived copies of |
||
3755 | // the data passed to putImageData()). |n| shouldn't be too small, however, |
||
3756 | // because too many putImageData() calls will slow things down. |
||
3757 | // |
||
3758 | // Note: as written, if the last chunk is partial, the putImageData() call |
||
3759 | // will (conceptually) put pixels past the bounds of the canvas. But |
||
3760 | // that's ok; any such pixels are ignored. |
||
3761 | |||
3762 | var height = imgData.height, width = imgData.width; |
||
3763 | var partialChunkHeight = height % FULL_CHUNK_HEIGHT; |
||
3764 | var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; |
||
3765 | var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; |
||
3766 | |||
3767 | var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); |
||
3768 | var srcPos = 0, destPos; |
||
3769 | var src = imgData.data; |
||
3770 | var dest = chunkImgData.data; |
||
3771 | var i, j, thisChunkHeight, elemsInThisChunk; |
||
3772 | |||
3773 | // There are multiple forms in which the pixel data can be passed, and |
||
3774 | // imgData.kind tells us which one this is. |
||
3775 | if (imgData.kind === ImageKind.GRAYSCALE_1BPP) { |
||
3776 | // Grayscale, 1 bit per pixel (i.e. black-and-white). |
||
3777 | var srcLength = src.byteLength; |
||
3778 | var dest32 = PDFJS.hasCanvasTypedArrays ? new Uint32Array(dest.buffer) : |
||
3779 | new Uint32ArrayView(dest); |
||
3780 | var dest32DataLength = dest32.length; |
||
3781 | var fullSrcDiff = (width + 7) >> 3; |
||
3782 | var white = 0xFFFFFFFF; |
||
3783 | var black = (PDFJS.isLittleEndian || !PDFJS.hasCanvasTypedArrays) ? |
||
3784 | 0xFF000000 : 0x000000FF; |
||
3785 | for (i = 0; i < totalChunks; i++) { |
||
3786 | thisChunkHeight = |
||
3787 | (i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight; |
||
3788 | destPos = 0; |
||
3789 | for (j = 0; j < thisChunkHeight; j++) { |
||
3790 | var srcDiff = srcLength - srcPos; |
||
3791 | var k = 0; |
||
3792 | var kEnd = (srcDiff > fullSrcDiff) ? width : srcDiff * 8 - 7; |
||
3793 | var kEndUnrolled = kEnd & ~7; |
||
3794 | var mask = 0; |
||
3795 | var srcByte = 0; |
||
3796 | for (; k < kEndUnrolled; k += 8) { |
||
3797 | srcByte = src[srcPos++]; |
||
3798 | dest32[destPos++] = (srcByte & 128) ? white : black; |
||
3799 | dest32[destPos++] = (srcByte & 64) ? white : black; |
||
3800 | dest32[destPos++] = (srcByte & 32) ? white : black; |
||
3801 | dest32[destPos++] = (srcByte & 16) ? white : black; |
||
3802 | dest32[destPos++] = (srcByte & 8) ? white : black; |
||
3803 | dest32[destPos++] = (srcByte & 4) ? white : black; |
||
3804 | dest32[destPos++] = (srcByte & 2) ? white : black; |
||
3805 | dest32[destPos++] = (srcByte & 1) ? white : black; |
||
3806 | } |
||
3807 | for (; k < kEnd; k++) { |
||
3808 | if (mask === 0) { |
||
3809 | srcByte = src[srcPos++]; |
||
3810 | mask = 128; |
||
3811 | } |
||
3812 | |||
3813 | dest32[destPos++] = (srcByte & mask) ? white : black; |
||
3814 | mask >>= 1; |
||
3815 | } |
||
3816 | } |
||
3817 | // We ran out of input. Make all remaining pixels transparent. |
||
3818 | while (destPos < dest32DataLength) { |
||
3819 | dest32[destPos++] = 0; |
||
3820 | } |
||
3821 | |||
3822 | ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); |
||
3823 | } |
||
3824 | } else if (imgData.kind === ImageKind.RGBA_32BPP) { |
||
3825 | // RGBA, 32-bits per pixel. |
||
3826 | |||
3827 | j = 0; |
||
3828 | elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; |
||
3829 | for (i = 0; i < fullChunks; i++) { |
||
3830 | dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); |
||
3831 | srcPos += elemsInThisChunk; |
||
3832 | |||
3833 | ctx.putImageData(chunkImgData, 0, j); |
||
3834 | j += FULL_CHUNK_HEIGHT; |
||
3835 | } |
||
3836 | if (i < totalChunks) { |
||
3837 | elemsInThisChunk = width * partialChunkHeight * 4; |
||
3838 | dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); |
||
3839 | ctx.putImageData(chunkImgData, 0, j); |
||
3840 | } |
||
3841 | |||
3842 | } else if (imgData.kind === ImageKind.RGB_24BPP) { |
||
3843 | // RGB, 24-bits per pixel. |
||
3844 | thisChunkHeight = FULL_CHUNK_HEIGHT; |
||
3845 | elemsInThisChunk = width * thisChunkHeight; |
||
3846 | for (i = 0; i < totalChunks; i++) { |
||
3847 | if (i >= fullChunks) { |
||
3848 | thisChunkHeight = partialChunkHeight; |
||
3849 | elemsInThisChunk = width * thisChunkHeight; |
||
3850 | } |
||
3851 | |||
3852 | destPos = 0; |
||
3853 | for (j = elemsInThisChunk; j--;) { |
||
3854 | dest[destPos++] = src[srcPos++]; |
||
3855 | dest[destPos++] = src[srcPos++]; |
||
3856 | dest[destPos++] = src[srcPos++]; |
||
3857 | dest[destPos++] = 255; |
||
3858 | } |
||
3859 | ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); |
||
3860 | } |
||
3861 | } else { |
||
3862 | error('bad image kind: ' + imgData.kind); |
||
3863 | } |
||
3864 | } |
||
3865 | |||
3866 | function putBinaryImageMask(ctx, imgData) { |
||
3867 | var height = imgData.height, width = imgData.width; |
||
3868 | var partialChunkHeight = height % FULL_CHUNK_HEIGHT; |
||
3869 | var fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT; |
||
3870 | var totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1; |
||
3871 | |||
3872 | var chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT); |
||
3873 | var srcPos = 0; |
||
3874 | var src = imgData.data; |
||
3875 | var dest = chunkImgData.data; |
||
3876 | |||
3877 | for (var i = 0; i < totalChunks; i++) { |
||
3878 | var thisChunkHeight = |
||
3879 | (i < fullChunks) ? FULL_CHUNK_HEIGHT : partialChunkHeight; |
||
3880 | |||
3881 | // Expand the mask so it can be used by the canvas. Any required |
||
3882 | // inversion has already been handled. |
||
3883 | var destPos = 3; // alpha component offset |
||
3884 | for (var j = 0; j < thisChunkHeight; j++) { |
||
3885 | var mask = 0; |
||
3886 | for (var k = 0; k < width; k++) { |
||
3887 | if (!mask) { |
||
3888 | var elem = src[srcPos++]; |
||
3889 | mask = 128; |
||
3890 | } |
||
3891 | dest[destPos] = (elem & mask) ? 0 : 255; |
||
3892 | destPos += 4; |
||
3893 | mask >>= 1; |
||
3894 | } |
||
3895 | } |
||
3896 | ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); |
||
3897 | } |
||
3898 | } |
||
3899 | |||
3900 | function copyCtxState(sourceCtx, destCtx) { |
||
3901 | var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha', |
||
3902 | 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', |
||
3903 | 'globalCompositeOperation', 'font']; |
||
3904 | for (var i = 0, ii = properties.length; i < ii; i++) { |
||
3905 | var property = properties[i]; |
||
3906 | if (sourceCtx[property] !== undefined) { |
||
3907 | destCtx[property] = sourceCtx[property]; |
||
3908 | } |
||
3909 | } |
||
3910 | if (sourceCtx.setLineDash !== undefined) { |
||
3911 | destCtx.setLineDash(sourceCtx.getLineDash()); |
||
3912 | destCtx.lineDashOffset = sourceCtx.lineDashOffset; |
||
3913 | } else if (sourceCtx.mozDashOffset !== undefined) { |
||
3914 | destCtx.mozDash = sourceCtx.mozDash; |
||
3915 | destCtx.mozDashOffset = sourceCtx.mozDashOffset; |
||
3916 | } |
||
3917 | } |
||
3918 | |||
3919 | function composeSMaskBackdrop(bytes, r0, g0, b0) { |
||
3920 | var length = bytes.length; |
||
3921 | for (var i = 3; i < length; i += 4) { |
||
3922 | var alpha = bytes[i]; |
||
3923 | if (alpha === 0) { |
||
3924 | bytes[i - 3] = r0; |
||
3925 | bytes[i - 2] = g0; |
||
3926 | bytes[i - 1] = b0; |
||
3927 | } else if (alpha < 255) { |
||
3928 | var alpha_ = 255 - alpha; |
||
3929 | bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) >> 8; |
||
3930 | bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) >> 8; |
||
3931 | bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) >> 8; |
||
3932 | } |
||
3933 | } |
||
3934 | } |
||
3935 | |||
3936 | function composeSMaskAlpha(maskData, layerData) { |
||
3937 | var length = maskData.length; |
||
3938 | var scale = 1 / 255; |
||
3939 | for (var i = 3; i < length; i += 4) { |
||
3940 | var alpha = maskData[i]; |
||
3941 | layerData[i] = (layerData[i] * alpha * scale) | 0; |
||
3942 | } |
||
3943 | } |
||
3944 | |||
3945 | function composeSMaskLuminosity(maskData, layerData) { |
||
3946 | var length = maskData.length; |
||
3947 | for (var i = 3; i < length; i += 4) { |
||
3948 | var y = (maskData[i - 3] * 77) + // * 0.3 / 255 * 0x10000 |
||
3949 | (maskData[i - 2] * 152) + // * 0.59 .... |
||
3950 | (maskData[i - 1] * 28); // * 0.11 .... |
||
3951 | layerData[i] = (layerData[i] * y) >> 16; |
||
3952 | } |
||
3953 | } |
||
3954 | |||
3955 | function genericComposeSMask(maskCtx, layerCtx, width, height, |
||
3956 | subtype, backdrop) { |
||
3957 | var hasBackdrop = !!backdrop; |
||
3958 | var r0 = hasBackdrop ? backdrop[0] : 0; |
||
3959 | var g0 = hasBackdrop ? backdrop[1] : 0; |
||
3960 | var b0 = hasBackdrop ? backdrop[2] : 0; |
||
3961 | |||
3962 | var composeFn; |
||
3963 | if (subtype === 'Luminosity') { |
||
3964 | composeFn = composeSMaskLuminosity; |
||
3965 | } else { |
||
3966 | composeFn = composeSMaskAlpha; |
||
3967 | } |
||
3968 | |||
3969 | // processing image in chunks to save memory |
||
3970 | var PIXELS_TO_PROCESS = 1048576; |
||
3971 | var chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width)); |
||
3972 | for (var row = 0; row < height; row += chunkSize) { |
||
3973 | var chunkHeight = Math.min(chunkSize, height - row); |
||
3974 | var maskData = maskCtx.getImageData(0, row, width, chunkHeight); |
||
3975 | var layerData = layerCtx.getImageData(0, row, width, chunkHeight); |
||
3976 | |||
3977 | if (hasBackdrop) { |
||
3978 | composeSMaskBackdrop(maskData.data, r0, g0, b0); |
||
3979 | } |
||
3980 | composeFn(maskData.data, layerData.data); |
||
3981 | |||
3982 | maskCtx.putImageData(layerData, 0, row); |
||
3983 | } |
||
3984 | } |
||
3985 | |||
3986 | function composeSMask(ctx, smask, layerCtx) { |
||
3987 | var mask = smask.canvas; |
||
3988 | var maskCtx = smask.context; |
||
3989 | |||
3990 | ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY, |
||
3991 | smask.offsetX, smask.offsetY); |
||
3992 | |||
3993 | var backdrop = smask.backdrop || null; |
||
3994 | if (WebGLUtils.isEnabled) { |
||
3995 | var composed = WebGLUtils.composeSMask(layerCtx.canvas, mask, |
||
3996 | {subtype: smask.subtype, backdrop: backdrop}); |
||
3997 | ctx.setTransform(1, 0, 0, 1, 0, 0); |
||
3998 | ctx.drawImage(composed, smask.offsetX, smask.offsetY); |
||
3999 | return; |
||
4000 | } |
||
4001 | genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height, |
||
4002 | smask.subtype, backdrop); |
||
4003 | ctx.drawImage(mask, 0, 0); |
||
4004 | } |
||
4005 | |||
4006 | var LINE_CAP_STYLES = ['butt', 'round', 'square']; |
||
4007 | var LINE_JOIN_STYLES = ['miter', 'round', 'bevel']; |
||
4008 | var NORMAL_CLIP = {}; |
||
4009 | var EO_CLIP = {}; |
||
4010 | |||
4011 | CanvasGraphics.prototype = { |
||
4012 | |||
4013 | beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) { |
||
4014 | // For pdfs that use blend modes we have to clear the canvas else certain |
||
4015 | // blend modes can look wrong since we'd be blending with a white |
||
4016 | // backdrop. The problem with a transparent backdrop though is we then |
||
4017 | // don't get sub pixel anti aliasing on text, so we fill with white if |
||
4018 | // we can. |
||
4019 | var width = this.ctx.canvas.width; |
||
4020 | var height = this.ctx.canvas.height; |
||
4021 | if (transparency) { |
||
4022 | this.ctx.clearRect(0, 0, width, height); |
||
4023 | } else { |
||
4024 | this.ctx.mozOpaque = true; |
||
4025 | this.ctx.save(); |
||
4026 | this.ctx.fillStyle = 'rgb(255, 255, 255)'; |
||
4027 | this.ctx.fillRect(0, 0, width, height); |
||
4028 | this.ctx.restore(); |
||
4029 | } |
||
4030 | |||
4031 | var transform = viewport.transform; |
||
4032 | |||
4033 | this.ctx.save(); |
||
4034 | this.ctx.transform.apply(this.ctx, transform); |
||
4035 | |||
4036 | this.baseTransform = this.ctx.mozCurrentTransform.slice(); |
||
4037 | |||
4038 | if (this.imageLayer) { |
||
4039 | this.imageLayer.beginLayout(); |
||
4040 | } |
||
4041 | }, |
||
4042 | |||
4043 | executeOperatorList: function CanvasGraphics_executeOperatorList( |
||
4044 | operatorList, |
||
4045 | executionStartIdx, continueCallback, |
||
4046 | stepper) { |
||
4047 | var argsArray = operatorList.argsArray; |
||
4048 | var fnArray = operatorList.fnArray; |
||
4049 | var i = executionStartIdx || 0; |
||
4050 | var argsArrayLen = argsArray.length; |
||
4051 | |||
4052 | // Sometimes the OperatorList to execute is empty. |
||
4053 | if (argsArrayLen === i) { |
||
4054 | return i; |
||
4055 | } |
||
4056 | |||
4057 | var chunkOperations = (argsArrayLen - i > EXECUTION_STEPS && |
||
4058 | typeof continueCallback === 'function'); |
||
4059 | var endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0; |
||
4060 | var steps = 0; |
||
4061 | |||
4062 | var commonObjs = this.commonObjs; |
||
4063 | var objs = this.objs; |
||
4064 | var fnId; |
||
4065 | |||
4066 | while (true) { |
||
4067 | if (stepper !== undefined && i === stepper.nextBreakPoint) { |
||
4068 | stepper.breakIt(i, continueCallback); |
||
4069 | return i; |
||
4070 | } |
||
4071 | |||
4072 | fnId = fnArray[i]; |
||
4073 | |||
4074 | if (fnId !== OPS.dependency) { |
||
4075 | this[fnId].apply(this, argsArray[i]); |
||
4076 | } else { |
||
4077 | var deps = argsArray[i]; |
||
4078 | for (var n = 0, nn = deps.length; n < nn; n++) { |
||
4079 | var depObjId = deps[n]; |
||
4080 | var common = depObjId[0] === 'g' && depObjId[1] === '_'; |
||
4081 | var objsPool = common ? commonObjs : objs; |
||
4082 | |||
4083 | // If the promise isn't resolved yet, add the continueCallback |
||
4084 | // to the promise and bail out. |
||
4085 | if (!objsPool.isResolved(depObjId)) { |
||
4086 | objsPool.get(depObjId, continueCallback); |
||
4087 | return i; |
||
4088 | } |
||
4089 | } |
||
4090 | } |
||
4091 | |||
4092 | i++; |
||
4093 | |||
4094 | // If the entire operatorList was executed, stop as were done. |
||
4095 | if (i === argsArrayLen) { |
||
4096 | return i; |
||
4097 | } |
||
4098 | |||
4099 | // If the execution took longer then a certain amount of time and |
||
4100 | // `continueCallback` is specified, interrupt the execution. |
||
4101 | if (chunkOperations && ++steps > EXECUTION_STEPS) { |
||
4102 | if (Date.now() > endTime) { |
||
4103 | continueCallback(); |
||
4104 | return i; |
||
4105 | } |
||
4106 | steps = 0; |
||
4107 | } |
||
4108 | |||
4109 | // If the operatorList isn't executed completely yet OR the execution |
||
4110 | // time was short enough, do another execution round. |
||
4111 | } |
||
4112 | }, |
||
4113 | |||
4114 | endDrawing: function CanvasGraphics_endDrawing() { |
||
4115 | this.ctx.restore(); |
||
4116 | CachedCanvases.clear(); |
||
4117 | WebGLUtils.clear(); |
||
4118 | |||
4119 | if (this.imageLayer) { |
||
4120 | this.imageLayer.endLayout(); |
||
4121 | } |
||
4122 | }, |
||
4123 | |||
4124 | // Graphics state |
||
4125 | setLineWidth: function CanvasGraphics_setLineWidth(width) { |
||
4126 | this.current.lineWidth = width; |
||
4127 | this.ctx.lineWidth = width; |
||
4128 | }, |
||
4129 | setLineCap: function CanvasGraphics_setLineCap(style) { |
||
4130 | this.ctx.lineCap = LINE_CAP_STYLES[style]; |
||
4131 | }, |
||
4132 | setLineJoin: function CanvasGraphics_setLineJoin(style) { |
||
4133 | this.ctx.lineJoin = LINE_JOIN_STYLES[style]; |
||
4134 | }, |
||
4135 | setMiterLimit: function CanvasGraphics_setMiterLimit(limit) { |
||
4136 | this.ctx.miterLimit = limit; |
||
4137 | }, |
||
4138 | setDash: function CanvasGraphics_setDash(dashArray, dashPhase) { |
||
4139 | var ctx = this.ctx; |
||
4140 | if (ctx.setLineDash !== undefined) { |
||
4141 | ctx.setLineDash(dashArray); |
||
4142 | ctx.lineDashOffset = dashPhase; |
||
4143 | } else { |
||
4144 | ctx.mozDash = dashArray; |
||
4145 | ctx.mozDashOffset = dashPhase; |
||
4146 | } |
||
4147 | }, |
||
4148 | setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) { |
||
4149 | // Maybe if we one day fully support color spaces this will be important |
||
4150 | // for now we can ignore. |
||
4151 | // TODO set rendering intent? |
||
4152 | }, |
||
4153 | setFlatness: function CanvasGraphics_setFlatness(flatness) { |
||
4154 | // There's no way to control this with canvas, but we can safely ignore. |
||
4155 | // TODO set flatness? |
||
4156 | }, |
||
4157 | setGState: function CanvasGraphics_setGState(states) { |
||
4158 | for (var i = 0, ii = states.length; i < ii; i++) { |
||
4159 | var state = states[i]; |
||
4160 | var key = state[0]; |
||
4161 | var value = state[1]; |
||
4162 | |||
4163 | switch (key) { |
||
4164 | case 'LW': |
||
4165 | this.setLineWidth(value); |
||
4166 | break; |
||
4167 | case 'LC': |
||
4168 | this.setLineCap(value); |
||
4169 | break; |
||
4170 | case 'LJ': |
||
4171 | this.setLineJoin(value); |
||
4172 | break; |
||
4173 | case 'ML': |
||
4174 | this.setMiterLimit(value); |
||
4175 | break; |
||
4176 | case 'D': |
||
4177 | this.setDash(value[0], value[1]); |
||
4178 | break; |
||
4179 | case 'RI': |
||
4180 | this.setRenderingIntent(value); |
||
4181 | break; |
||
4182 | case 'FL': |
||
4183 | this.setFlatness(value); |
||
4184 | break; |
||
4185 | case 'Font': |
||
4186 | this.setFont(value[0], value[1]); |
||
4187 | break; |
||
4188 | case 'CA': |
||
4189 | this.current.strokeAlpha = state[1]; |
||
4190 | break; |
||
4191 | case 'ca': |
||
4192 | this.current.fillAlpha = state[1]; |
||
4193 | this.ctx.globalAlpha = state[1]; |
||
4194 | break; |
||
4195 | case 'BM': |
||
4196 | if (value && value.name && (value.name !== 'Normal')) { |
||
4197 | var mode = value.name.replace(/([A-Z])/g, |
||
4198 | function(c) { |
||
4199 | return '-' + c.toLowerCase(); |
||
4200 | } |
||
4201 | ).substring(1); |
||
4202 | this.ctx.globalCompositeOperation = mode; |
||
4203 | if (this.ctx.globalCompositeOperation !== mode) { |
||
4204 | warn('globalCompositeOperation "' + mode + |
||
4205 | '" is not supported'); |
||
4206 | } |
||
4207 | } else { |
||
4208 | this.ctx.globalCompositeOperation = 'source-over'; |
||
4209 | } |
||
4210 | break; |
||
4211 | case 'SMask': |
||
4212 | if (this.current.activeSMask) { |
||
4213 | this.endSMaskGroup(); |
||
4214 | } |
||
4215 | this.current.activeSMask = value ? this.tempSMask : null; |
||
4216 | if (this.current.activeSMask) { |
||
4217 | this.beginSMaskGroup(); |
||
4218 | } |
||
4219 | this.tempSMask = null; |
||
4220 | break; |
||
4221 | } |
||
4222 | } |
||
4223 | }, |
||
4224 | beginSMaskGroup: function CanvasGraphics_beginSMaskGroup() { |
||
4225 | |||
4226 | var activeSMask = this.current.activeSMask; |
||
4227 | var drawnWidth = activeSMask.canvas.width; |
||
4228 | var drawnHeight = activeSMask.canvas.height; |
||
4229 | var cacheId = 'smaskGroupAt' + this.groupLevel; |
||
4230 | var scratchCanvas = CachedCanvases.getCanvas( |
||
4231 | cacheId, drawnWidth, drawnHeight, true); |
||
4232 | |||
4233 | var currentCtx = this.ctx; |
||
4234 | var currentTransform = currentCtx.mozCurrentTransform; |
||
4235 | this.ctx.save(); |
||
4236 | |||
4237 | var groupCtx = scratchCanvas.context; |
||
4238 | groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY); |
||
4239 | groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY); |
||
4240 | groupCtx.transform.apply(groupCtx, currentTransform); |
||
4241 | |||
4242 | copyCtxState(currentCtx, groupCtx); |
||
4243 | this.ctx = groupCtx; |
||
4244 | this.setGState([ |
||
4245 | ['BM', 'Normal'], |
||
4246 | ['ca', 1], |
||
4247 | ['CA', 1] |
||
4248 | ]); |
||
4249 | this.groupStack.push(currentCtx); |
||
4250 | this.groupLevel++; |
||
4251 | }, |
||
4252 | endSMaskGroup: function CanvasGraphics_endSMaskGroup() { |
||
4253 | var groupCtx = this.ctx; |
||
4254 | this.groupLevel--; |
||
4255 | this.ctx = this.groupStack.pop(); |
||
4256 | |||
4257 | composeSMask(this.ctx, this.current.activeSMask, groupCtx); |
||
4258 | this.ctx.restore(); |
||
4259 | }, |
||
4260 | save: function CanvasGraphics_save() { |
||
4261 | this.ctx.save(); |
||
4262 | var old = this.current; |
||
4263 | this.stateStack.push(old); |
||
4264 | this.current = old.clone(); |
||
4265 | this.current.activeSMask = null; |
||
4266 | }, |
||
4267 | restore: function CanvasGraphics_restore() { |
||
4268 | if (this.stateStack.length !== 0) { |
||
4269 | if (this.current.activeSMask !== null) { |
||
4270 | this.endSMaskGroup(); |
||
4271 | } |
||
4272 | |||
4273 | this.current = this.stateStack.pop(); |
||
4274 | this.ctx.restore(); |
||
4275 | |||
4276 | this.cachedGetSinglePixelWidth = null; |
||
4277 | } |
||
4278 | }, |
||
4279 | transform: function CanvasGraphics_transform(a, b, c, d, e, f) { |
||
4280 | this.ctx.transform(a, b, c, d, e, f); |
||
4281 | |||
4282 | this.cachedGetSinglePixelWidth = null; |
||
4283 | }, |
||
4284 | |||
4285 | // Path |
||
4286 | View Code Duplication | constructPath: function CanvasGraphics_constructPath(ops, args) { |
|
4287 | var ctx = this.ctx; |
||
4288 | var current = this.current; |
||
4289 | var x = current.x, y = current.y; |
||
4290 | for (var i = 0, j = 0, ii = ops.length; i < ii; i++) { |
||
4291 | switch (ops[i] | 0) { |
||
4292 | case OPS.rectangle: |
||
4293 | x = args[j++]; |
||
4294 | y = args[j++]; |
||
4295 | var width = args[j++]; |
||
4296 | var height = args[j++]; |
||
4297 | if (width === 0) { |
||
4298 | width = this.getSinglePixelWidth(); |
||
4299 | } |
||
4300 | if (height === 0) { |
||
4301 | height = this.getSinglePixelWidth(); |
||
4302 | } |
||
4303 | var xw = x + width; |
||
4304 | var yh = y + height; |
||
4305 | this.ctx.moveTo(x, y); |
||
4306 | this.ctx.lineTo(xw, y); |
||
4307 | this.ctx.lineTo(xw, yh); |
||
4308 | this.ctx.lineTo(x, yh); |
||
4309 | this.ctx.lineTo(x, y); |
||
4310 | this.ctx.closePath(); |
||
4311 | break; |
||
4312 | case OPS.moveTo: |
||
4313 | x = args[j++]; |
||
4314 | y = args[j++]; |
||
4315 | ctx.moveTo(x, y); |
||
4316 | break; |
||
4317 | case OPS.lineTo: |
||
4318 | x = args[j++]; |
||
4319 | y = args[j++]; |
||
4320 | ctx.lineTo(x, y); |
||
4321 | break; |
||
4322 | case OPS.curveTo: |
||
4323 | x = args[j + 4]; |
||
4324 | y = args[j + 5]; |
||
4325 | ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], |
||
4326 | x, y); |
||
4327 | j += 6; |
||
4328 | break; |
||
4329 | case OPS.curveTo2: |
||
4330 | ctx.bezierCurveTo(x, y, args[j], args[j + 1], |
||
4331 | args[j + 2], args[j + 3]); |
||
4332 | x = args[j + 2]; |
||
4333 | y = args[j + 3]; |
||
4334 | j += 4; |
||
4335 | break; |
||
4336 | case OPS.curveTo3: |
||
4337 | x = args[j + 2]; |
||
4338 | y = args[j + 3]; |
||
4339 | ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y); |
||
4340 | j += 4; |
||
4341 | break; |
||
4342 | case OPS.closePath: |
||
4343 | ctx.closePath(); |
||
4344 | break; |
||
4345 | } |
||
4346 | } |
||
4347 | current.setCurrentPoint(x, y); |
||
4348 | }, |
||
4349 | closePath: function CanvasGraphics_closePath() { |
||
4350 | this.ctx.closePath(); |
||
4351 | }, |
||
4352 | stroke: function CanvasGraphics_stroke(consumePath) { |
||
4353 | consumePath = typeof consumePath !== 'undefined' ? consumePath : true; |
||
4354 | var ctx = this.ctx; |
||
4355 | var strokeColor = this.current.strokeColor; |
||
4356 | // Prevent drawing too thin lines by enforcing a minimum line width. |
||
4357 | ctx.lineWidth = Math.max(this.getSinglePixelWidth() * MIN_WIDTH_FACTOR, |
||
4358 | this.current.lineWidth); |
||
4359 | // For stroke we want to temporarily change the global alpha to the |
||
4360 | // stroking alpha. |
||
4361 | ctx.globalAlpha = this.current.strokeAlpha; |
||
4362 | if (strokeColor && strokeColor.hasOwnProperty('type') && |
||
4363 | strokeColor.type === 'Pattern') { |
||
4364 | // for patterns, we transform to pattern space, calculate |
||
4365 | // the pattern, call stroke, and restore to user space |
||
4366 | ctx.save(); |
||
4367 | ctx.strokeStyle = strokeColor.getPattern(ctx, this); |
||
4368 | ctx.stroke(); |
||
4369 | ctx.restore(); |
||
4370 | } else { |
||
4371 | ctx.stroke(); |
||
4372 | } |
||
4373 | if (consumePath) { |
||
4374 | this.consumePath(); |
||
4375 | } |
||
4376 | // Restore the global alpha to the fill alpha |
||
4377 | ctx.globalAlpha = this.current.fillAlpha; |
||
4378 | }, |
||
4379 | closeStroke: function CanvasGraphics_closeStroke() { |
||
4380 | this.closePath(); |
||
4381 | this.stroke(); |
||
4382 | }, |
||
4383 | fill: function CanvasGraphics_fill(consumePath) { |
||
4384 | consumePath = typeof consumePath !== 'undefined' ? consumePath : true; |
||
4385 | var ctx = this.ctx; |
||
4386 | var fillColor = this.current.fillColor; |
||
4387 | var isPatternFill = this.current.patternFill; |
||
4388 | var needRestore = false; |
||
4389 | |||
4390 | if (isPatternFill) { |
||
4391 | ctx.save(); |
||
4392 | ctx.fillStyle = fillColor.getPattern(ctx, this); |
||
4393 | needRestore = true; |
||
4394 | } |
||
4395 | |||
4396 | if (this.pendingEOFill) { |
||
4397 | if (ctx.mozFillRule !== undefined) { |
||
4398 | ctx.mozFillRule = 'evenodd'; |
||
4399 | ctx.fill(); |
||
4400 | ctx.mozFillRule = 'nonzero'; |
||
4401 | } else { |
||
4402 | try { |
||
4403 | ctx.fill('evenodd'); |
||
4404 | } catch (ex) { |
||
4405 | // shouldn't really happen, but browsers might think differently |
||
4406 | ctx.fill(); |
||
4407 | } |
||
4408 | } |
||
4409 | this.pendingEOFill = false; |
||
4410 | } else { |
||
4411 | ctx.fill(); |
||
4412 | } |
||
4413 | |||
4414 | if (needRestore) { |
||
4415 | ctx.restore(); |
||
4416 | } |
||
4417 | if (consumePath) { |
||
4418 | this.consumePath(); |
||
4419 | } |
||
4420 | }, |
||
4421 | eoFill: function CanvasGraphics_eoFill() { |
||
4422 | this.pendingEOFill = true; |
||
4423 | this.fill(); |
||
4424 | }, |
||
4425 | fillStroke: function CanvasGraphics_fillStroke() { |
||
4426 | this.fill(false); |
||
4427 | this.stroke(false); |
||
4428 | |||
4429 | this.consumePath(); |
||
4430 | }, |
||
4431 | eoFillStroke: function CanvasGraphics_eoFillStroke() { |
||
4432 | this.pendingEOFill = true; |
||
4433 | this.fillStroke(); |
||
4434 | }, |
||
4435 | closeFillStroke: function CanvasGraphics_closeFillStroke() { |
||
4436 | this.closePath(); |
||
4437 | this.fillStroke(); |
||
4438 | }, |
||
4439 | closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() { |
||
4440 | this.pendingEOFill = true; |
||
4441 | this.closePath(); |
||
4442 | this.fillStroke(); |
||
4443 | }, |
||
4444 | endPath: function CanvasGraphics_endPath() { |
||
4445 | this.consumePath(); |
||
4446 | }, |
||
4447 | |||
4448 | // Clipping |
||
4449 | clip: function CanvasGraphics_clip() { |
||
4450 | this.pendingClip = NORMAL_CLIP; |
||
4451 | }, |
||
4452 | eoClip: function CanvasGraphics_eoClip() { |
||
4453 | this.pendingClip = EO_CLIP; |
||
4454 | }, |
||
4455 | |||
4456 | // Text |
||
4457 | beginText: function CanvasGraphics_beginText() { |
||
4458 | this.current.textMatrix = IDENTITY_MATRIX; |
||
4459 | this.current.textMatrixScale = 1; |
||
4460 | this.current.x = this.current.lineX = 0; |
||
4461 | this.current.y = this.current.lineY = 0; |
||
4462 | }, |
||
4463 | endText: function CanvasGraphics_endText() { |
||
4464 | var paths = this.pendingTextPaths; |
||
4465 | var ctx = this.ctx; |
||
4466 | if (paths === undefined) { |
||
4467 | ctx.beginPath(); |
||
4468 | return; |
||
4469 | } |
||
4470 | |||
4471 | ctx.save(); |
||
4472 | ctx.beginPath(); |
||
4473 | for (var i = 0; i < paths.length; i++) { |
||
4474 | var path = paths[i]; |
||
4475 | ctx.setTransform.apply(ctx, path.transform); |
||
4476 | ctx.translate(path.x, path.y); |
||
4477 | path.addToPath(ctx, path.fontSize); |
||
4478 | } |
||
4479 | ctx.restore(); |
||
4480 | ctx.clip(); |
||
4481 | ctx.beginPath(); |
||
4482 | delete this.pendingTextPaths; |
||
4483 | }, |
||
4484 | setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) { |
||
4485 | this.current.charSpacing = spacing; |
||
4486 | }, |
||
4487 | setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) { |
||
4488 | this.current.wordSpacing = spacing; |
||
4489 | }, |
||
4490 | setHScale: function CanvasGraphics_setHScale(scale) { |
||
4491 | this.current.textHScale = scale / 100; |
||
4492 | }, |
||
4493 | setLeading: function CanvasGraphics_setLeading(leading) { |
||
4494 | this.current.leading = -leading; |
||
4495 | }, |
||
4496 | setFont: function CanvasGraphics_setFont(fontRefName, size) { |
||
4497 | var fontObj = this.commonObjs.get(fontRefName); |
||
4498 | var current = this.current; |
||
4499 | |||
4500 | if (!fontObj) { |
||
4501 | error('Can\'t find font for ' + fontRefName); |
||
4502 | } |
||
4503 | |||
4504 | current.fontMatrix = (fontObj.fontMatrix ? |
||
4505 | fontObj.fontMatrix : FONT_IDENTITY_MATRIX); |
||
4506 | |||
4507 | // A valid matrix needs all main diagonal elements to be non-zero |
||
4508 | // This also ensures we bypass FF bugzilla bug #719844. |
||
4509 | if (current.fontMatrix[0] === 0 || |
||
4510 | current.fontMatrix[3] === 0) { |
||
4511 | warn('Invalid font matrix for font ' + fontRefName); |
||
4512 | } |
||
4513 | |||
4514 | // The spec for Tf (setFont) says that 'size' specifies the font 'scale', |
||
4515 | // and in some docs this can be negative (inverted x-y axes). |
||
4516 | if (size < 0) { |
||
4517 | size = -size; |
||
4518 | current.fontDirection = -1; |
||
4519 | } else { |
||
4520 | current.fontDirection = 1; |
||
4521 | } |
||
4522 | |||
4523 | this.current.font = fontObj; |
||
4524 | this.current.fontSize = size; |
||
4525 | |||
4526 | if (fontObj.isType3Font) { |
||
4527 | return; // we don't need ctx.font for Type3 fonts |
||
4528 | } |
||
4529 | |||
4530 | var name = fontObj.loadedName || 'sans-serif'; |
||
4531 | var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : |
||
4532 | (fontObj.bold ? 'bold' : 'normal'); |
||
4533 | |||
4534 | var italic = fontObj.italic ? 'italic' : 'normal'; |
||
4535 | var typeface = '"' + name + '", ' + fontObj.fallbackName; |
||
4536 | |||
4537 | // Some font backends cannot handle fonts below certain size. |
||
4538 | // Keeping the font at minimal size and using the fontSizeScale to change |
||
4539 | // the current transformation matrix before the fillText/strokeText. |
||
4540 | // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227 |
||
4541 | var browserFontSize = size < MIN_FONT_SIZE ? MIN_FONT_SIZE : |
||
4542 | size > MAX_FONT_SIZE ? MAX_FONT_SIZE : size; |
||
4543 | this.current.fontSizeScale = size / browserFontSize; |
||
4544 | |||
4545 | var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface; |
||
4546 | this.ctx.font = rule; |
||
4547 | }, |
||
4548 | setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) { |
||
4549 | this.current.textRenderingMode = mode; |
||
4550 | }, |
||
4551 | setTextRise: function CanvasGraphics_setTextRise(rise) { |
||
4552 | this.current.textRise = rise; |
||
4553 | }, |
||
4554 | moveText: function CanvasGraphics_moveText(x, y) { |
||
4555 | this.current.x = this.current.lineX += x; |
||
4556 | this.current.y = this.current.lineY += y; |
||
4557 | }, |
||
4558 | setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) { |
||
4559 | this.setLeading(-y); |
||
4560 | this.moveText(x, y); |
||
4561 | }, |
||
4562 | setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) { |
||
4563 | this.current.textMatrix = [a, b, c, d, e, f]; |
||
4564 | this.current.textMatrixScale = Math.sqrt(a * a + b * b); |
||
4565 | |||
4566 | this.current.x = this.current.lineX = 0; |
||
4567 | this.current.y = this.current.lineY = 0; |
||
4568 | }, |
||
4569 | nextLine: function CanvasGraphics_nextLine() { |
||
4570 | this.moveText(0, this.current.leading); |
||
4571 | }, |
||
4572 | |||
4573 | paintChar: function CanvasGraphics_paintChar(character, x, y) { |
||
4574 | var ctx = this.ctx; |
||
4575 | var current = this.current; |
||
4576 | var font = current.font; |
||
4577 | var textRenderingMode = current.textRenderingMode; |
||
4578 | var fontSize = current.fontSize / current.fontSizeScale; |
||
4579 | var fillStrokeMode = textRenderingMode & |
||
4580 | TextRenderingMode.FILL_STROKE_MASK; |
||
4581 | var isAddToPathSet = !!(textRenderingMode & |
||
4582 | TextRenderingMode.ADD_TO_PATH_FLAG); |
||
4583 | |||
4584 | var addToPath; |
||
4585 | if (font.disableFontFace || isAddToPathSet) { |
||
4586 | addToPath = font.getPathGenerator(this.commonObjs, character); |
||
4587 | } |
||
4588 | |||
4589 | if (font.disableFontFace) { |
||
4590 | ctx.save(); |
||
4591 | ctx.translate(x, y); |
||
4592 | ctx.beginPath(); |
||
4593 | addToPath(ctx, fontSize); |
||
4594 | if (fillStrokeMode === TextRenderingMode.FILL || |
||
4595 | fillStrokeMode === TextRenderingMode.FILL_STROKE) { |
||
4596 | ctx.fill(); |
||
4597 | } |
||
4598 | if (fillStrokeMode === TextRenderingMode.STROKE || |
||
4599 | fillStrokeMode === TextRenderingMode.FILL_STROKE) { |
||
4600 | ctx.stroke(); |
||
4601 | } |
||
4602 | ctx.restore(); |
||
4603 | } else { |
||
4604 | if (fillStrokeMode === TextRenderingMode.FILL || |
||
4605 | fillStrokeMode === TextRenderingMode.FILL_STROKE) { |
||
4606 | ctx.fillText(character, x, y); |
||
4607 | } |
||
4608 | if (fillStrokeMode === TextRenderingMode.STROKE || |
||
4609 | fillStrokeMode === TextRenderingMode.FILL_STROKE) { |
||
4610 | ctx.strokeText(character, x, y); |
||
4611 | } |
||
4612 | } |
||
4613 | |||
4614 | if (isAddToPathSet) { |
||
4615 | var paths = this.pendingTextPaths || (this.pendingTextPaths = []); |
||
4616 | paths.push({ |
||
4617 | transform: ctx.mozCurrentTransform, |
||
4618 | x: x, |
||
4619 | y: y, |
||
4620 | fontSize: fontSize, |
||
4621 | addToPath: addToPath |
||
4622 | }); |
||
4623 | } |
||
4624 | }, |
||
4625 | |||
4626 | get isFontSubpixelAAEnabled() { |
||
4627 | // Checks if anti-aliasing is enabled when scaled text is painted. |
||
4628 | // On Windows GDI scaled fonts looks bad. |
||
4629 | var ctx = document.createElement('canvas').getContext('2d'); |
||
4630 | ctx.scale(1.5, 1); |
||
4631 | ctx.fillText('I', 0, 10); |
||
4632 | var data = ctx.getImageData(0, 0, 10, 10).data; |
||
4633 | var enabled = false; |
||
4634 | for (var i = 3; i < data.length; i += 4) { |
||
4635 | if (data[i] > 0 && data[i] < 255) { |
||
4636 | enabled = true; |
||
4637 | break; |
||
4638 | } |
||
4639 | } |
||
4640 | return shadow(this, 'isFontSubpixelAAEnabled', enabled); |
||
4641 | }, |
||
4642 | |||
4643 | showText: function CanvasGraphics_showText(glyphs) { |
||
4644 | var current = this.current; |
||
4645 | var font = current.font; |
||
4646 | if (font.isType3Font) { |
||
4647 | return this.showType3Text(glyphs); |
||
4648 | } |
||
4649 | |||
4650 | var fontSize = current.fontSize; |
||
4651 | if (fontSize === 0) { |
||
4652 | return; |
||
4653 | } |
||
4654 | |||
4655 | var ctx = this.ctx; |
||
4656 | var fontSizeScale = current.fontSizeScale; |
||
4657 | var charSpacing = current.charSpacing; |
||
4658 | var wordSpacing = current.wordSpacing; |
||
4659 | var fontDirection = current.fontDirection; |
||
4660 | var textHScale = current.textHScale * fontDirection; |
||
4661 | var glyphsLength = glyphs.length; |
||
4662 | var vertical = font.vertical; |
||
4663 | var defaultVMetrics = font.defaultVMetrics; |
||
4664 | var widthAdvanceScale = fontSize * current.fontMatrix[0]; |
||
4665 | |||
4666 | var simpleFillText = |
||
4667 | current.textRenderingMode === TextRenderingMode.FILL && |
||
4668 | !font.disableFontFace; |
||
4669 | |||
4670 | ctx.save(); |
||
4671 | ctx.transform.apply(ctx, current.textMatrix); |
||
4672 | ctx.translate(current.x, current.y + current.textRise); |
||
4673 | |||
4674 | if (fontDirection > 0) { |
||
4675 | ctx.scale(textHScale, -1); |
||
4676 | } else { |
||
4677 | ctx.scale(textHScale, 1); |
||
4678 | } |
||
4679 | |||
4680 | var lineWidth = current.lineWidth; |
||
4681 | var scale = current.textMatrixScale; |
||
4682 | if (scale === 0 || lineWidth === 0) { |
||
4683 | var fillStrokeMode = current.textRenderingMode & |
||
4684 | TextRenderingMode.FILL_STROKE_MASK; |
||
4685 | if (fillStrokeMode === TextRenderingMode.STROKE || |
||
4686 | fillStrokeMode === TextRenderingMode.FILL_STROKE) { |
||
4687 | this.cachedGetSinglePixelWidth = null; |
||
4688 | lineWidth = this.getSinglePixelWidth() * MIN_WIDTH_FACTOR; |
||
4689 | } |
||
4690 | } else { |
||
4691 | lineWidth /= scale; |
||
4692 | } |
||
4693 | |||
4694 | if (fontSizeScale !== 1.0) { |
||
4695 | ctx.scale(fontSizeScale, fontSizeScale); |
||
4696 | lineWidth /= fontSizeScale; |
||
4697 | } |
||
4698 | |||
4699 | ctx.lineWidth = lineWidth; |
||
4700 | |||
4701 | var x = 0, i; |
||
4702 | for (i = 0; i < glyphsLength; ++i) { |
||
4703 | var glyph = glyphs[i]; |
||
4704 | if (glyph === null) { |
||
4705 | // word break |
||
4706 | x += fontDirection * wordSpacing; |
||
4707 | continue; |
||
4708 | } else if (isNum(glyph)) { |
||
4709 | x += -glyph * fontSize * 0.001; |
||
4710 | continue; |
||
4711 | } |
||
4712 | |||
4713 | var restoreNeeded = false; |
||
4714 | var character = glyph.fontChar; |
||
4715 | var accent = glyph.accent; |
||
4716 | var scaledX, scaledY, scaledAccentX, scaledAccentY; |
||
4717 | var width = glyph.width; |
||
4718 | if (vertical) { |
||
4719 | var vmetric, vx, vy; |
||
4720 | vmetric = glyph.vmetric || defaultVMetrics; |
||
4721 | vx = glyph.vmetric ? vmetric[1] : width * 0.5; |
||
4722 | vx = -vx * widthAdvanceScale; |
||
4723 | vy = vmetric[2] * widthAdvanceScale; |
||
4724 | |||
4725 | width = vmetric ? -vmetric[0] : width; |
||
4726 | scaledX = vx / fontSizeScale; |
||
4727 | scaledY = (x + vy) / fontSizeScale; |
||
4728 | } else { |
||
4729 | scaledX = x / fontSizeScale; |
||
4730 | scaledY = 0; |
||
4731 | } |
||
4732 | |||
4733 | if (font.remeasure && width > 0 && this.isFontSubpixelAAEnabled) { |
||
4734 | // some standard fonts may not have the exact width, trying to |
||
4735 | // rescale per character |
||
4736 | var measuredWidth = ctx.measureText(character).width * 1000 / |
||
4737 | fontSize * fontSizeScale; |
||
4738 | var characterScaleX = width / measuredWidth; |
||
4739 | restoreNeeded = true; |
||
4740 | ctx.save(); |
||
4741 | ctx.scale(characterScaleX, 1); |
||
4742 | scaledX /= characterScaleX; |
||
4743 | } |
||
4744 | |||
4745 | if (simpleFillText && !accent) { |
||
4746 | // common case |
||
4747 | ctx.fillText(character, scaledX, scaledY); |
||
4748 | } else { |
||
4749 | this.paintChar(character, scaledX, scaledY); |
||
4750 | if (accent) { |
||
4751 | scaledAccentX = scaledX + accent.offset.x / fontSizeScale; |
||
4752 | scaledAccentY = scaledY - accent.offset.y / fontSizeScale; |
||
4753 | this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY); |
||
4754 | } |
||
4755 | } |
||
4756 | |||
4757 | var charWidth = width * widthAdvanceScale + charSpacing * fontDirection; |
||
4758 | x += charWidth; |
||
4759 | |||
4760 | if (restoreNeeded) { |
||
4761 | ctx.restore(); |
||
4762 | } |
||
4763 | } |
||
4764 | if (vertical) { |
||
4765 | current.y -= x * textHScale; |
||
4766 | } else { |
||
4767 | current.x += x * textHScale; |
||
4768 | } |
||
4769 | ctx.restore(); |
||
4770 | }, |
||
4771 | |||
4772 | showType3Text: function CanvasGraphics_showType3Text(glyphs) { |
||
4773 | // Type3 fonts - each glyph is a "mini-PDF" |
||
4774 | var ctx = this.ctx; |
||
4775 | var current = this.current; |
||
4776 | var font = current.font; |
||
4777 | var fontSize = current.fontSize; |
||
4778 | var fontDirection = current.fontDirection; |
||
4779 | var charSpacing = current.charSpacing; |
||
4780 | var wordSpacing = current.wordSpacing; |
||
4781 | var textHScale = current.textHScale * fontDirection; |
||
4782 | var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; |
||
4783 | var glyphsLength = glyphs.length; |
||
4784 | var isTextInvisible = |
||
4785 | current.textRenderingMode === TextRenderingMode.INVISIBLE; |
||
4786 | var i, glyph, width; |
||
4787 | |||
4788 | if (isTextInvisible || fontSize === 0) { |
||
4789 | return; |
||
4790 | } |
||
4791 | |||
4792 | ctx.save(); |
||
4793 | ctx.transform.apply(ctx, current.textMatrix); |
||
4794 | ctx.translate(current.x, current.y); |
||
4795 | |||
4796 | ctx.scale(textHScale, fontDirection); |
||
4797 | |||
4798 | for (i = 0; i < glyphsLength; ++i) { |
||
4799 | glyph = glyphs[i]; |
||
4800 | if (glyph === null) { |
||
4801 | // word break |
||
4802 | this.ctx.translate(wordSpacing, 0); |
||
4803 | current.x += wordSpacing * textHScale; |
||
4804 | continue; |
||
4805 | } else if (isNum(glyph)) { |
||
4806 | var spacingLength = -glyph * 0.001 * fontSize; |
||
4807 | this.ctx.translate(spacingLength, 0); |
||
4808 | current.x += spacingLength * textHScale; |
||
4809 | continue; |
||
4810 | } |
||
4811 | |||
4812 | var operatorList = font.charProcOperatorList[glyph.operatorListId]; |
||
4813 | if (!operatorList) { |
||
4814 | warn('Type3 character \"' + glyph.operatorListId + |
||
4815 | '\" is not available'); |
||
4816 | continue; |
||
4817 | } |
||
4818 | this.processingType3 = glyph; |
||
4819 | this.save(); |
||
4820 | ctx.scale(fontSize, fontSize); |
||
4821 | ctx.transform.apply(ctx, fontMatrix); |
||
4822 | this.executeOperatorList(operatorList); |
||
4823 | this.restore(); |
||
4824 | |||
4825 | var transformed = Util.applyTransform([glyph.width, 0], fontMatrix); |
||
4826 | width = transformed[0] * fontSize + charSpacing; |
||
4827 | |||
4828 | ctx.translate(width, 0); |
||
4829 | current.x += width * textHScale; |
||
4830 | } |
||
4831 | ctx.restore(); |
||
4832 | this.processingType3 = null; |
||
4833 | }, |
||
4834 | |||
4835 | // Type3 fonts |
||
4836 | setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) { |
||
4837 | // We can safely ignore this since the width should be the same |
||
4838 | // as the width in the Widths array. |
||
4839 | }, |
||
4840 | setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth, |
||
4841 | yWidth, |
||
4842 | llx, |
||
4843 | lly, |
||
4844 | urx, |
||
4845 | ury) { |
||
4846 | // TODO According to the spec we're also suppose to ignore any operators |
||
4847 | // that set color or include images while processing this type3 font. |
||
4848 | this.ctx.rect(llx, lly, urx - llx, ury - lly); |
||
4849 | this.clip(); |
||
4850 | this.endPath(); |
||
4851 | }, |
||
4852 | |||
4853 | // Color |
||
4854 | getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR) { |
||
4855 | var pattern; |
||
4856 | if (IR[0] === 'TilingPattern') { |
||
4857 | var color = IR[1]; |
||
4858 | pattern = new TilingPattern(IR, color, this.ctx, this.objs, |
||
4859 | this.commonObjs, this.baseTransform); |
||
4860 | } else { |
||
4861 | pattern = getShadingPatternFromIR(IR); |
||
4862 | } |
||
4863 | return pattern; |
||
4864 | }, |
||
4865 | setStrokeColorN: function CanvasGraphics_setStrokeColorN(/*...*/) { |
||
4866 | this.current.strokeColor = this.getColorN_Pattern(arguments); |
||
4867 | }, |
||
4868 | setFillColorN: function CanvasGraphics_setFillColorN(/*...*/) { |
||
4869 | this.current.fillColor = this.getColorN_Pattern(arguments); |
||
4870 | this.current.patternFill = true; |
||
4871 | }, |
||
4872 | setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) { |
||
4873 | var color = Util.makeCssRgb(r, g, b); |
||
4874 | this.ctx.strokeStyle = color; |
||
4875 | this.current.strokeColor = color; |
||
4876 | }, |
||
4877 | setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) { |
||
4878 | var color = Util.makeCssRgb(r, g, b); |
||
4879 | this.ctx.fillStyle = color; |
||
4880 | this.current.fillColor = color; |
||
4881 | this.current.patternFill = false; |
||
4882 | }, |
||
4883 | |||
4884 | shadingFill: function CanvasGraphics_shadingFill(patternIR) { |
||
4885 | var ctx = this.ctx; |
||
4886 | |||
4887 | this.save(); |
||
4888 | var pattern = getShadingPatternFromIR(patternIR); |
||
4889 | ctx.fillStyle = pattern.getPattern(ctx, this, true); |
||
4890 | |||
4891 | var inv = ctx.mozCurrentTransformInverse; |
||
4892 | if (inv) { |
||
4893 | var canvas = ctx.canvas; |
||
4894 | var width = canvas.width; |
||
4895 | var height = canvas.height; |
||
4896 | |||
4897 | var bl = Util.applyTransform([0, 0], inv); |
||
4898 | var br = Util.applyTransform([0, height], inv); |
||
4899 | var ul = Util.applyTransform([width, 0], inv); |
||
4900 | var ur = Util.applyTransform([width, height], inv); |
||
4901 | |||
4902 | var x0 = Math.min(bl[0], br[0], ul[0], ur[0]); |
||
4903 | var y0 = Math.min(bl[1], br[1], ul[1], ur[1]); |
||
4904 | var x1 = Math.max(bl[0], br[0], ul[0], ur[0]); |
||
4905 | var y1 = Math.max(bl[1], br[1], ul[1], ur[1]); |
||
4906 | |||
4907 | this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0); |
||
4908 | } else { |
||
4909 | // HACK to draw the gradient onto an infinite rectangle. |
||
4910 | // PDF gradients are drawn across the entire image while |
||
4911 | // Canvas only allows gradients to be drawn in a rectangle |
||
4912 | // The following bug should allow us to remove this. |
||
4913 | // https://bugzilla.mozilla.org/show_bug.cgi?id=664884 |
||
4914 | |||
4915 | this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); |
||
4916 | } |
||
4917 | |||
4918 | this.restore(); |
||
4919 | }, |
||
4920 | |||
4921 | // Images |
||
4922 | beginInlineImage: function CanvasGraphics_beginInlineImage() { |
||
4923 | error('Should not call beginInlineImage'); |
||
4924 | }, |
||
4925 | beginImageData: function CanvasGraphics_beginImageData() { |
||
4926 | error('Should not call beginImageData'); |
||
4927 | }, |
||
4928 | |||
4929 | paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix, |
||
4930 | bbox) { |
||
4931 | this.save(); |
||
4932 | this.baseTransformStack.push(this.baseTransform); |
||
4933 | |||
4934 | if (isArray(matrix) && 6 === matrix.length) { |
||
4935 | this.transform.apply(this, matrix); |
||
4936 | } |
||
4937 | |||
4938 | this.baseTransform = this.ctx.mozCurrentTransform; |
||
4939 | |||
4940 | if (isArray(bbox) && 4 === bbox.length) { |
||
4941 | var width = bbox[2] - bbox[0]; |
||
4942 | var height = bbox[3] - bbox[1]; |
||
4943 | this.ctx.rect(bbox[0], bbox[1], width, height); |
||
4944 | this.clip(); |
||
4945 | this.endPath(); |
||
4946 | } |
||
4947 | }, |
||
4948 | |||
4949 | paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() { |
||
4950 | this.restore(); |
||
4951 | this.baseTransform = this.baseTransformStack.pop(); |
||
4952 | }, |
||
4953 | |||
4954 | beginGroup: function CanvasGraphics_beginGroup(group) { |
||
4955 | this.save(); |
||
4956 | var currentCtx = this.ctx; |
||
4957 | // TODO non-isolated groups - according to Rik at adobe non-isolated |
||
4958 | // group results aren't usually that different and they even have tools |
||
4959 | // that ignore this setting. Notes from Rik on implmenting: |
||
4960 | // - When you encounter an transparency group, create a new canvas with |
||
4961 | // the dimensions of the bbox |
||
4962 | // - copy the content from the previous canvas to the new canvas |
||
4963 | // - draw as usual |
||
4964 | // - remove the backdrop alpha: |
||
4965 | // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha |
||
4966 | // value of your transparency group and 'alphaBackdrop' the alpha of the |
||
4967 | // backdrop |
||
4968 | // - remove background color: |
||
4969 | // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew) |
||
4970 | if (!group.isolated) { |
||
4971 | info('TODO: Support non-isolated groups.'); |
||
4972 | } |
||
4973 | |||
4974 | // TODO knockout - supposedly possible with the clever use of compositing |
||
4975 | // modes. |
||
4976 | if (group.knockout) { |
||
4977 | warn('Knockout groups not supported.'); |
||
4978 | } |
||
4979 | |||
4980 | var currentTransform = currentCtx.mozCurrentTransform; |
||
4981 | if (group.matrix) { |
||
4982 | currentCtx.transform.apply(currentCtx, group.matrix); |
||
4983 | } |
||
4984 | assert(group.bbox, 'Bounding box is required.'); |
||
4985 | |||
4986 | // Based on the current transform figure out how big the bounding box |
||
4987 | // will actually be. |
||
4988 | var bounds = Util.getAxialAlignedBoundingBox( |
||
4989 | group.bbox, |
||
4990 | currentCtx.mozCurrentTransform); |
||
4991 | // Clip the bounding box to the current canvas. |
||
4992 | var canvasBounds = [0, |
||
4993 | 0, |
||
4994 | currentCtx.canvas.width, |
||
4995 | currentCtx.canvas.height]; |
||
4996 | bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; |
||
4997 | // Use ceil in case we're between sizes so we don't create canvas that is |
||
4998 | // too small and make the canvas at least 1x1 pixels. |
||
4999 | var offsetX = Math.floor(bounds[0]); |
||
5000 | var offsetY = Math.floor(bounds[1]); |
||
5001 | var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1); |
||
5002 | var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1); |
||
5003 | var scaleX = 1, scaleY = 1; |
||
5004 | if (drawnWidth > MAX_GROUP_SIZE) { |
||
5005 | scaleX = drawnWidth / MAX_GROUP_SIZE; |
||
5006 | drawnWidth = MAX_GROUP_SIZE; |
||
5007 | } |
||
5008 | if (drawnHeight > MAX_GROUP_SIZE) { |
||
5009 | scaleY = drawnHeight / MAX_GROUP_SIZE; |
||
5010 | drawnHeight = MAX_GROUP_SIZE; |
||
5011 | } |
||
5012 | |||
5013 | var cacheId = 'groupAt' + this.groupLevel; |
||
5014 | if (group.smask) { |
||
5015 | // Using two cache entries is case if masks are used one after another. |
||
5016 | cacheId += '_smask_' + ((this.smaskCounter++) % 2); |
||
5017 | } |
||
5018 | var scratchCanvas = CachedCanvases.getCanvas( |
||
5019 | cacheId, drawnWidth, drawnHeight, true); |
||
5020 | var groupCtx = scratchCanvas.context; |
||
5021 | |||
5022 | // Since we created a new canvas that is just the size of the bounding box |
||
5023 | // we have to translate the group ctx. |
||
5024 | groupCtx.scale(1 / scaleX, 1 / scaleY); |
||
5025 | groupCtx.translate(-offsetX, -offsetY); |
||
5026 | groupCtx.transform.apply(groupCtx, currentTransform); |
||
5027 | |||
5028 | if (group.smask) { |
||
5029 | // Saving state and cached mask to be used in setGState. |
||
5030 | this.smaskStack.push({ |
||
5031 | canvas: scratchCanvas.canvas, |
||
5032 | context: groupCtx, |
||
5033 | offsetX: offsetX, |
||
5034 | offsetY: offsetY, |
||
5035 | scaleX: scaleX, |
||
5036 | scaleY: scaleY, |
||
5037 | subtype: group.smask.subtype, |
||
5038 | backdrop: group.smask.backdrop |
||
5039 | }); |
||
5040 | } else { |
||
5041 | // Setup the current ctx so when the group is popped we draw it at the |
||
5042 | // right location. |
||
5043 | currentCtx.setTransform(1, 0, 0, 1, 0, 0); |
||
5044 | currentCtx.translate(offsetX, offsetY); |
||
5045 | currentCtx.scale(scaleX, scaleY); |
||
5046 | } |
||
5047 | // The transparency group inherits all off the current graphics state |
||
5048 | // except the blend mode, soft mask, and alpha constants. |
||
5049 | copyCtxState(currentCtx, groupCtx); |
||
5050 | this.ctx = groupCtx; |
||
5051 | this.setGState([ |
||
5052 | ['BM', 'Normal'], |
||
5053 | ['ca', 1], |
||
5054 | ['CA', 1] |
||
5055 | ]); |
||
5056 | this.groupStack.push(currentCtx); |
||
5057 | this.groupLevel++; |
||
5058 | }, |
||
5059 | |||
5060 | endGroup: function CanvasGraphics_endGroup(group) { |
||
5061 | this.groupLevel--; |
||
5062 | var groupCtx = this.ctx; |
||
5063 | this.ctx = this.groupStack.pop(); |
||
5064 | // Turn off image smoothing to avoid sub pixel interpolation which can |
||
5065 | // look kind of blurry for some pdfs. |
||
5066 | if (this.ctx.imageSmoothingEnabled !== undefined) { |
||
5067 | this.ctx.imageSmoothingEnabled = false; |
||
5068 | } else { |
||
5069 | this.ctx.mozImageSmoothingEnabled = false; |
||
5070 | } |
||
5071 | if (group.smask) { |
||
5072 | this.tempSMask = this.smaskStack.pop(); |
||
5073 | } else { |
||
5074 | this.ctx.drawImage(groupCtx.canvas, 0, 0); |
||
5075 | } |
||
5076 | this.restore(); |
||
5077 | }, |
||
5078 | |||
5079 | beginAnnotations: function CanvasGraphics_beginAnnotations() { |
||
5080 | this.save(); |
||
5081 | this.current = new CanvasExtraState(); |
||
5082 | }, |
||
5083 | |||
5084 | endAnnotations: function CanvasGraphics_endAnnotations() { |
||
5085 | this.restore(); |
||
5086 | }, |
||
5087 | |||
5088 | beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform, |
||
5089 | matrix) { |
||
5090 | this.save(); |
||
5091 | |||
5092 | if (isArray(rect) && 4 === rect.length) { |
||
5093 | var width = rect[2] - rect[0]; |
||
5094 | var height = rect[3] - rect[1]; |
||
5095 | this.ctx.rect(rect[0], rect[1], width, height); |
||
5096 | this.clip(); |
||
5097 | this.endPath(); |
||
5098 | } |
||
5099 | |||
5100 | this.transform.apply(this, transform); |
||
5101 | this.transform.apply(this, matrix); |
||
5102 | }, |
||
5103 | |||
5104 | endAnnotation: function CanvasGraphics_endAnnotation() { |
||
5105 | this.restore(); |
||
5106 | }, |
||
5107 | |||
5108 | paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) { |
||
5109 | var domImage = this.objs.get(objId); |
||
5110 | if (!domImage) { |
||
5111 | warn('Dependent image isn\'t ready yet'); |
||
5112 | return; |
||
5113 | } |
||
5114 | |||
5115 | this.save(); |
||
5116 | |||
5117 | var ctx = this.ctx; |
||
5118 | // scale the image to the unit square |
||
5119 | ctx.scale(1 / w, -1 / h); |
||
5120 | |||
5121 | ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, |
||
5122 | 0, -h, w, h); |
||
5123 | if (this.imageLayer) { |
||
5124 | var currentTransform = ctx.mozCurrentTransformInverse; |
||
5125 | var position = this.getCanvasPosition(0, 0); |
||
5126 | this.imageLayer.appendImage({ |
||
5127 | objId: objId, |
||
5128 | left: position[0], |
||
5129 | top: position[1], |
||
5130 | width: w / currentTransform[0], |
||
5131 | height: h / currentTransform[3] |
||
5132 | }); |
||
5133 | } |
||
5134 | this.restore(); |
||
5135 | }, |
||
5136 | |||
5137 | paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) { |
||
5138 | var ctx = this.ctx; |
||
5139 | var width = img.width, height = img.height; |
||
5140 | var fillColor = this.current.fillColor; |
||
5141 | var isPatternFill = this.current.patternFill; |
||
5142 | |||
5143 | var glyph = this.processingType3; |
||
5144 | |||
5145 | if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) { |
||
5146 | if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) { |
||
5147 | glyph.compiled = |
||
5148 | compileType3Glyph({data: img.data, width: width, height: height}); |
||
5149 | } else { |
||
5150 | glyph.compiled = null; |
||
5151 | } |
||
5152 | } |
||
5153 | |||
5154 | if (glyph && glyph.compiled) { |
||
5155 | glyph.compiled(ctx); |
||
5156 | return; |
||
5157 | } |
||
5158 | |||
5159 | var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); |
||
5160 | var maskCtx = maskCanvas.context; |
||
5161 | maskCtx.save(); |
||
5162 | |||
5163 | putBinaryImageMask(maskCtx, img); |
||
5164 | |||
5165 | maskCtx.globalCompositeOperation = 'source-in'; |
||
5166 | |||
5167 | maskCtx.fillStyle = isPatternFill ? |
||
5168 | fillColor.getPattern(maskCtx, this) : fillColor; |
||
5169 | maskCtx.fillRect(0, 0, width, height); |
||
5170 | |||
5171 | maskCtx.restore(); |
||
5172 | |||
5173 | this.paintInlineImageXObject(maskCanvas.canvas); |
||
5174 | }, |
||
5175 | |||
5176 | paintImageMaskXObjectRepeat: |
||
5177 | function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX, |
||
5178 | scaleY, positions) { |
||
5179 | var width = imgData.width; |
||
5180 | var height = imgData.height; |
||
5181 | var fillColor = this.current.fillColor; |
||
5182 | var isPatternFill = this.current.patternFill; |
||
5183 | |||
5184 | var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); |
||
5185 | var maskCtx = maskCanvas.context; |
||
5186 | maskCtx.save(); |
||
5187 | |||
5188 | putBinaryImageMask(maskCtx, imgData); |
||
5189 | |||
5190 | maskCtx.globalCompositeOperation = 'source-in'; |
||
5191 | |||
5192 | maskCtx.fillStyle = isPatternFill ? |
||
5193 | fillColor.getPattern(maskCtx, this) : fillColor; |
||
5194 | maskCtx.fillRect(0, 0, width, height); |
||
5195 | |||
5196 | maskCtx.restore(); |
||
5197 | |||
5198 | var ctx = this.ctx; |
||
5199 | for (var i = 0, ii = positions.length; i < ii; i += 2) { |
||
5200 | ctx.save(); |
||
5201 | ctx.transform(scaleX, 0, 0, scaleY, positions[i], positions[i + 1]); |
||
5202 | ctx.scale(1, -1); |
||
5203 | ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, |
||
5204 | 0, -1, 1, 1); |
||
5205 | ctx.restore(); |
||
5206 | } |
||
5207 | }, |
||
5208 | |||
5209 | paintImageMaskXObjectGroup: |
||
5210 | function CanvasGraphics_paintImageMaskXObjectGroup(images) { |
||
5211 | var ctx = this.ctx; |
||
5212 | |||
5213 | var fillColor = this.current.fillColor; |
||
5214 | var isPatternFill = this.current.patternFill; |
||
5215 | for (var i = 0, ii = images.length; i < ii; i++) { |
||
5216 | var image = images[i]; |
||
5217 | var width = image.width, height = image.height; |
||
5218 | |||
5219 | var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); |
||
5220 | var maskCtx = maskCanvas.context; |
||
5221 | maskCtx.save(); |
||
5222 | |||
5223 | putBinaryImageMask(maskCtx, image); |
||
5224 | |||
5225 | maskCtx.globalCompositeOperation = 'source-in'; |
||
5226 | |||
5227 | maskCtx.fillStyle = isPatternFill ? |
||
5228 | fillColor.getPattern(maskCtx, this) : fillColor; |
||
5229 | maskCtx.fillRect(0, 0, width, height); |
||
5230 | |||
5231 | maskCtx.restore(); |
||
5232 | |||
5233 | ctx.save(); |
||
5234 | ctx.transform.apply(ctx, image.transform); |
||
5235 | ctx.scale(1, -1); |
||
5236 | ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, |
||
5237 | 0, -1, 1, 1); |
||
5238 | ctx.restore(); |
||
5239 | } |
||
5240 | }, |
||
5241 | |||
5242 | paintImageXObject: function CanvasGraphics_paintImageXObject(objId) { |
||
5243 | var imgData = this.objs.get(objId); |
||
5244 | if (!imgData) { |
||
5245 | warn('Dependent image isn\'t ready yet'); |
||
5246 | return; |
||
5247 | } |
||
5248 | |||
5249 | this.paintInlineImageXObject(imgData); |
||
5250 | }, |
||
5251 | |||
5252 | paintImageXObjectRepeat: |
||
5253 | function CanvasGraphics_paintImageXObjectRepeat(objId, scaleX, scaleY, |
||
5254 | positions) { |
||
5255 | var imgData = this.objs.get(objId); |
||
5256 | if (!imgData) { |
||
5257 | warn('Dependent image isn\'t ready yet'); |
||
5258 | return; |
||
5259 | } |
||
5260 | |||
5261 | var width = imgData.width; |
||
5262 | var height = imgData.height; |
||
5263 | var map = []; |
||
5264 | for (var i = 0, ii = positions.length; i < ii; i += 2) { |
||
5265 | map.push({transform: [scaleX, 0, 0, scaleY, positions[i], |
||
5266 | positions[i + 1]], x: 0, y: 0, w: width, h: height}); |
||
5267 | } |
||
5268 | this.paintInlineImageXObjectGroup(imgData, map); |
||
5269 | }, |
||
5270 | |||
5271 | paintInlineImageXObject: |
||
5272 | function CanvasGraphics_paintInlineImageXObject(imgData) { |
||
5273 | var width = imgData.width; |
||
5274 | var height = imgData.height; |
||
5275 | var ctx = this.ctx; |
||
5276 | |||
5277 | this.save(); |
||
5278 | // scale the image to the unit square |
||
5279 | ctx.scale(1 / width, -1 / height); |
||
5280 | |||
5281 | var currentTransform = ctx.mozCurrentTransformInverse; |
||
5282 | var a = currentTransform[0], b = currentTransform[1]; |
||
5283 | var widthScale = Math.max(Math.sqrt(a * a + b * b), 1); |
||
5284 | var c = currentTransform[2], d = currentTransform[3]; |
||
5285 | var heightScale = Math.max(Math.sqrt(c * c + d * d), 1); |
||
5286 | |||
5287 | var imgToPaint, tmpCanvas; |
||
5288 | // instanceof HTMLElement does not work in jsdom node.js module |
||
5289 | if (imgData instanceof HTMLElement || !imgData.data) { |
||
5290 | imgToPaint = imgData; |
||
5291 | } else { |
||
5292 | tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height); |
||
5293 | var tmpCtx = tmpCanvas.context; |
||
5294 | putBinaryImageData(tmpCtx, imgData); |
||
5295 | imgToPaint = tmpCanvas.canvas; |
||
5296 | } |
||
5297 | |||
5298 | var paintWidth = width, paintHeight = height; |
||
5299 | var tmpCanvasId = 'prescale1'; |
||
5300 | // Vertial or horizontal scaling shall not be more than 2 to not loose the |
||
5301 | // pixels during drawImage operation, painting on the temporary canvas(es) |
||
5302 | // that are twice smaller in size |
||
5303 | while ((widthScale > 2 && paintWidth > 1) || |
||
5304 | (heightScale > 2 && paintHeight > 1)) { |
||
5305 | var newWidth = paintWidth, newHeight = paintHeight; |
||
5306 | if (widthScale > 2 && paintWidth > 1) { |
||
5307 | newWidth = Math.ceil(paintWidth / 2); |
||
5308 | widthScale /= paintWidth / newWidth; |
||
5309 | } |
||
5310 | if (heightScale > 2 && paintHeight > 1) { |
||
5311 | newHeight = Math.ceil(paintHeight / 2); |
||
5312 | heightScale /= paintHeight / newHeight; |
||
5313 | } |
||
5314 | tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); |
||
5315 | tmpCtx = tmpCanvas.context; |
||
5316 | tmpCtx.clearRect(0, 0, newWidth, newHeight); |
||
5317 | tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, |
||
5318 | 0, 0, newWidth, newHeight); |
||
5319 | imgToPaint = tmpCanvas.canvas; |
||
5320 | paintWidth = newWidth; |
||
5321 | paintHeight = newHeight; |
||
5322 | tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1'; |
||
5323 | } |
||
5324 | ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, |
||
5325 | 0, -height, width, height); |
||
5326 | |||
5327 | if (this.imageLayer) { |
||
5328 | var position = this.getCanvasPosition(0, -height); |
||
5329 | this.imageLayer.appendImage({ |
||
5330 | imgData: imgData, |
||
5331 | left: position[0], |
||
5332 | top: position[1], |
||
5333 | width: width / currentTransform[0], |
||
5334 | height: height / currentTransform[3] |
||
5335 | }); |
||
5336 | } |
||
5337 | this.restore(); |
||
5338 | }, |
||
5339 | |||
5340 | paintInlineImageXObjectGroup: |
||
5341 | function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) { |
||
5342 | var ctx = this.ctx; |
||
5343 | var w = imgData.width; |
||
5344 | var h = imgData.height; |
||
5345 | |||
5346 | var tmpCanvas = CachedCanvases.getCanvas('inlineImage', w, h); |
||
5347 | var tmpCtx = tmpCanvas.context; |
||
5348 | putBinaryImageData(tmpCtx, imgData); |
||
5349 | |||
5350 | for (var i = 0, ii = map.length; i < ii; i++) { |
||
5351 | var entry = map[i]; |
||
5352 | ctx.save(); |
||
5353 | ctx.transform.apply(ctx, entry.transform); |
||
5354 | ctx.scale(1, -1); |
||
5355 | ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h, |
||
5356 | 0, -1, 1, 1); |
||
5357 | if (this.imageLayer) { |
||
5358 | var position = this.getCanvasPosition(entry.x, entry.y); |
||
5359 | this.imageLayer.appendImage({ |
||
5360 | imgData: imgData, |
||
5361 | left: position[0], |
||
5362 | top: position[1], |
||
5363 | width: w, |
||
5364 | height: h |
||
5365 | }); |
||
5366 | } |
||
5367 | ctx.restore(); |
||
5368 | } |
||
5369 | }, |
||
5370 | |||
5371 | paintSolidColorImageMask: |
||
5372 | function CanvasGraphics_paintSolidColorImageMask() { |
||
5373 | this.ctx.fillRect(0, 0, 1, 1); |
||
5374 | }, |
||
5375 | |||
5376 | // Marked content |
||
5377 | |||
5378 | markPoint: function CanvasGraphics_markPoint(tag) { |
||
5379 | // TODO Marked content. |
||
5380 | }, |
||
5381 | markPointProps: function CanvasGraphics_markPointProps(tag, properties) { |
||
5382 | // TODO Marked content. |
||
5383 | }, |
||
5384 | beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) { |
||
5385 | // TODO Marked content. |
||
5386 | }, |
||
5387 | beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps( |
||
5388 | tag, properties) { |
||
5389 | // TODO Marked content. |
||
5390 | }, |
||
5391 | endMarkedContent: function CanvasGraphics_endMarkedContent() { |
||
5392 | // TODO Marked content. |
||
5393 | }, |
||
5394 | |||
5395 | // Compatibility |
||
5396 | |||
5397 | beginCompat: function CanvasGraphics_beginCompat() { |
||
5398 | // TODO ignore undefined operators (should we do that anyway?) |
||
5399 | }, |
||
5400 | endCompat: function CanvasGraphics_endCompat() { |
||
5401 | // TODO stop ignoring undefined operators |
||
5402 | }, |
||
5403 | |||
5404 | // Helper functions |
||
5405 | |||
5406 | consumePath: function CanvasGraphics_consumePath() { |
||
5407 | var ctx = this.ctx; |
||
5408 | if (this.pendingClip) { |
||
5409 | if (this.pendingClip === EO_CLIP) { |
||
5410 | if (ctx.mozFillRule !== undefined) { |
||
5411 | ctx.mozFillRule = 'evenodd'; |
||
5412 | ctx.clip(); |
||
5413 | ctx.mozFillRule = 'nonzero'; |
||
5414 | } else { |
||
5415 | try { |
||
5416 | ctx.clip('evenodd'); |
||
5417 | } catch (ex) { |
||
5418 | // shouldn't really happen, but browsers might think differently |
||
5419 | ctx.clip(); |
||
5420 | } |
||
5421 | } |
||
5422 | } else { |
||
5423 | ctx.clip(); |
||
5424 | } |
||
5425 | this.pendingClip = null; |
||
5426 | } |
||
5427 | ctx.beginPath(); |
||
5428 | }, |
||
5429 | getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) { |
||
5430 | if (this.cachedGetSinglePixelWidth === null) { |
||
5431 | var inverse = this.ctx.mozCurrentTransformInverse; |
||
5432 | // max of the current horizontal and vertical scale |
||
5433 | this.cachedGetSinglePixelWidth = Math.sqrt(Math.max( |
||
5434 | (inverse[0] * inverse[0] + inverse[1] * inverse[1]), |
||
5435 | (inverse[2] * inverse[2] + inverse[3] * inverse[3]))); |
||
5436 | } |
||
5437 | return this.cachedGetSinglePixelWidth; |
||
5438 | }, |
||
5439 | getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) { |
||
5440 | var transform = this.ctx.mozCurrentTransform; |
||
5441 | return [ |
||
5442 | transform[0] * x + transform[2] * y + transform[4], |
||
5443 | transform[1] * x + transform[3] * y + transform[5] |
||
5444 | ]; |
||
5445 | } |
||
5446 | }; |
||
5447 | |||
5448 | for (var op in OPS) { |
||
5449 | CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; |
||
5450 | } |
||
5451 | |||
5452 | return CanvasGraphics; |
||
5453 | })(); |
||
5454 | |||
5455 | |||
5456 | var WebGLUtils = (function WebGLUtilsClosure() { |
||
5457 | function loadShader(gl, code, shaderType) { |
||
5458 | var shader = gl.createShader(shaderType); |
||
5459 | gl.shaderSource(shader, code); |
||
5460 | gl.compileShader(shader); |
||
5461 | var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); |
||
5462 | if (!compiled) { |
||
5463 | var errorMsg = gl.getShaderInfoLog(shader); |
||
5464 | throw new Error('Error during shader compilation: ' + errorMsg); |
||
5465 | } |
||
5466 | return shader; |
||
5467 | } |
||
5468 | function createVertexShader(gl, code) { |
||
5469 | return loadShader(gl, code, gl.VERTEX_SHADER); |
||
5470 | } |
||
5471 | function createFragmentShader(gl, code) { |
||
5472 | return loadShader(gl, code, gl.FRAGMENT_SHADER); |
||
5473 | } |
||
5474 | function createProgram(gl, shaders) { |
||
5475 | var program = gl.createProgram(); |
||
5476 | for (var i = 0, ii = shaders.length; i < ii; ++i) { |
||
5477 | gl.attachShader(program, shaders[i]); |
||
5478 | } |
||
5479 | gl.linkProgram(program); |
||
5480 | var linked = gl.getProgramParameter(program, gl.LINK_STATUS); |
||
5481 | if (!linked) { |
||
5482 | var errorMsg = gl.getProgramInfoLog(program); |
||
5483 | throw new Error('Error during program linking: ' + errorMsg); |
||
5484 | } |
||
5485 | return program; |
||
5486 | } |
||
5487 | function createTexture(gl, image, textureId) { |
||
5488 | gl.activeTexture(textureId); |
||
5489 | var texture = gl.createTexture(); |
||
5490 | gl.bindTexture(gl.TEXTURE_2D, texture); |
||
5491 | |||
5492 | // Set the parameters so we can render any size image. |
||
5493 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); |
||
5494 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); |
||
5495 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); |
||
5496 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); |
||
5497 | |||
5498 | // Upload the image into the texture. |
||
5499 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); |
||
5500 | return texture; |
||
5501 | } |
||
5502 | |||
5503 | var currentGL, currentCanvas; |
||
5504 | function generateGL() { |
||
5505 | if (currentGL) { |
||
5506 | return; |
||
5507 | } |
||
5508 | currentCanvas = document.createElement('canvas'); |
||
5509 | currentGL = currentCanvas.getContext('webgl', |
||
5510 | { premultipliedalpha: false }); |
||
5511 | } |
||
5512 | |||
5513 | var smaskVertexShaderCode = '\ |
||
5514 | attribute vec2 a_position; \ |
||
5515 | attribute vec2 a_texCoord; \ |
||
5516 | \ |
||
5517 | uniform vec2 u_resolution; \ |
||
5518 | \ |
||
5519 | varying vec2 v_texCoord; \ |
||
5520 | \ |
||
5521 | void main() { \ |
||
5522 | vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; \ |
||
5523 | gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \ |
||
5524 | \ |
||
5525 | v_texCoord = a_texCoord; \ |
||
5526 | } '; |
||
5527 | |||
5528 | var smaskFragmentShaderCode = '\ |
||
5529 | precision mediump float; \ |
||
5530 | \ |
||
5531 | uniform vec4 u_backdrop; \ |
||
5532 | uniform int u_subtype; \ |
||
5533 | uniform sampler2D u_image; \ |
||
5534 | uniform sampler2D u_mask; \ |
||
5535 | \ |
||
5536 | varying vec2 v_texCoord; \ |
||
5537 | \ |
||
5538 | void main() { \ |
||
5539 | vec4 imageColor = texture2D(u_image, v_texCoord); \ |
||
5540 | vec4 maskColor = texture2D(u_mask, v_texCoord); \ |
||
5541 | if (u_backdrop.a > 0.0) { \ |
||
5542 | maskColor.rgb = maskColor.rgb * maskColor.a + \ |
||
5543 | u_backdrop.rgb * (1.0 - maskColor.a); \ |
||
5544 | } \ |
||
5545 | float lum; \ |
||
5546 | if (u_subtype == 0) { \ |
||
5547 | lum = maskColor.a; \ |
||
5548 | } else { \ |
||
5549 | lum = maskColor.r * 0.3 + maskColor.g * 0.59 + \ |
||
5550 | maskColor.b * 0.11; \ |
||
5551 | } \ |
||
5552 | imageColor.a *= lum; \ |
||
5553 | imageColor.rgb *= imageColor.a; \ |
||
5554 | gl_FragColor = imageColor; \ |
||
5555 | } '; |
||
5556 | |||
5557 | var smaskCache = null; |
||
5558 | |||
5559 | function initSmaskGL() { |
||
5560 | var canvas, gl; |
||
5561 | |||
5562 | generateGL(); |
||
5563 | canvas = currentCanvas; |
||
5564 | currentCanvas = null; |
||
5565 | gl = currentGL; |
||
5566 | currentGL = null; |
||
5567 | |||
5568 | // setup a GLSL program |
||
5569 | var vertexShader = createVertexShader(gl, smaskVertexShaderCode); |
||
5570 | var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode); |
||
5571 | var program = createProgram(gl, [vertexShader, fragmentShader]); |
||
5572 | gl.useProgram(program); |
||
5573 | |||
5574 | var cache = {}; |
||
5575 | cache.gl = gl; |
||
5576 | cache.canvas = canvas; |
||
5577 | cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); |
||
5578 | cache.positionLocation = gl.getAttribLocation(program, 'a_position'); |
||
5579 | cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop'); |
||
5580 | cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype'); |
||
5581 | |||
5582 | var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord'); |
||
5583 | var texLayerLocation = gl.getUniformLocation(program, 'u_image'); |
||
5584 | var texMaskLocation = gl.getUniformLocation(program, 'u_mask'); |
||
5585 | |||
5586 | // provide texture coordinates for the rectangle. |
||
5587 | var texCoordBuffer = gl.createBuffer(); |
||
5588 | gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); |
||
5589 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ |
||
5590 | 0.0, 0.0, |
||
5591 | 1.0, 0.0, |
||
5592 | 0.0, 1.0, |
||
5593 | 0.0, 1.0, |
||
5594 | 1.0, 0.0, |
||
5595 | 1.0, 1.0]), gl.STATIC_DRAW); |
||
5596 | gl.enableVertexAttribArray(texCoordLocation); |
||
5597 | gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); |
||
5598 | |||
5599 | gl.uniform1i(texLayerLocation, 0); |
||
5600 | gl.uniform1i(texMaskLocation, 1); |
||
5601 | |||
5602 | smaskCache = cache; |
||
5603 | } |
||
5604 | |||
5605 | function composeSMask(layer, mask, properties) { |
||
5606 | var width = layer.width, height = layer.height; |
||
5607 | |||
5608 | if (!smaskCache) { |
||
5609 | initSmaskGL(); |
||
5610 | } |
||
5611 | var cache = smaskCache,canvas = cache.canvas, gl = cache.gl; |
||
5612 | canvas.width = width; |
||
5613 | canvas.height = height; |
||
5614 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); |
||
5615 | gl.uniform2f(cache.resolutionLocation, width, height); |
||
5616 | |||
5617 | if (properties.backdrop) { |
||
5618 | gl.uniform4f(cache.resolutionLocation, properties.backdrop[0], |
||
5619 | properties.backdrop[1], properties.backdrop[2], 1); |
||
5620 | } else { |
||
5621 | gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0); |
||
5622 | } |
||
5623 | gl.uniform1i(cache.subtypeLocation, |
||
5624 | properties.subtype === 'Luminosity' ? 1 : 0); |
||
5625 | |||
5626 | // Create a textures |
||
5627 | var texture = createTexture(gl, layer, gl.TEXTURE0); |
||
5628 | var maskTexture = createTexture(gl, mask, gl.TEXTURE1); |
||
5629 | |||
5630 | |||
5631 | // Create a buffer and put a single clipspace rectangle in |
||
5632 | // it (2 triangles) |
||
5633 | var buffer = gl.createBuffer(); |
||
5634 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); |
||
5635 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ |
||
5636 | 0, 0, |
||
5637 | width, 0, |
||
5638 | 0, height, |
||
5639 | 0, height, |
||
5640 | width, 0, |
||
5641 | width, height]), gl.STATIC_DRAW); |
||
5642 | gl.enableVertexAttribArray(cache.positionLocation); |
||
5643 | gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0); |
||
5644 | |||
5645 | // draw |
||
5646 | gl.clearColor(0, 0, 0, 0); |
||
5647 | gl.enable(gl.BLEND); |
||
5648 | gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); |
||
5649 | gl.clear(gl.COLOR_BUFFER_BIT); |
||
5650 | |||
5651 | gl.drawArrays(gl.TRIANGLES, 0, 6); |
||
5652 | |||
5653 | gl.flush(); |
||
5654 | |||
5655 | gl.deleteTexture(texture); |
||
5656 | gl.deleteTexture(maskTexture); |
||
5657 | gl.deleteBuffer(buffer); |
||
5658 | |||
5659 | return canvas; |
||
5660 | } |
||
5661 | |||
5662 | var figuresVertexShaderCode = '\ |
||
5663 | attribute vec2 a_position; \ |
||
5664 | attribute vec3 a_color; \ |
||
5665 | \ |
||
5666 | uniform vec2 u_resolution; \ |
||
5667 | uniform vec2 u_scale; \ |
||
5668 | uniform vec2 u_offset; \ |
||
5669 | \ |
||
5670 | varying vec4 v_color; \ |
||
5671 | \ |
||
5672 | void main() { \ |
||
5673 | vec2 position = (a_position + u_offset) * u_scale; \ |
||
5674 | vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0; \ |
||
5675 | gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \ |
||
5676 | \ |
||
5677 | v_color = vec4(a_color / 255.0, 1.0); \ |
||
5678 | } '; |
||
5679 | |||
5680 | var figuresFragmentShaderCode = '\ |
||
5681 | precision mediump float; \ |
||
5682 | \ |
||
5683 | varying vec4 v_color; \ |
||
5684 | \ |
||
5685 | void main() { \ |
||
5686 | gl_FragColor = v_color; \ |
||
5687 | } '; |
||
5688 | |||
5689 | var figuresCache = null; |
||
5690 | |||
5691 | function initFiguresGL() { |
||
5692 | var canvas, gl; |
||
5693 | |||
5694 | generateGL(); |
||
5695 | canvas = currentCanvas; |
||
5696 | currentCanvas = null; |
||
5697 | gl = currentGL; |
||
5698 | currentGL = null; |
||
5699 | |||
5700 | // setup a GLSL program |
||
5701 | var vertexShader = createVertexShader(gl, figuresVertexShaderCode); |
||
5702 | var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode); |
||
5703 | var program = createProgram(gl, [vertexShader, fragmentShader]); |
||
5704 | gl.useProgram(program); |
||
5705 | |||
5706 | var cache = {}; |
||
5707 | cache.gl = gl; |
||
5708 | cache.canvas = canvas; |
||
5709 | cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); |
||
5710 | cache.scaleLocation = gl.getUniformLocation(program, 'u_scale'); |
||
5711 | cache.offsetLocation = gl.getUniformLocation(program, 'u_offset'); |
||
5712 | cache.positionLocation = gl.getAttribLocation(program, 'a_position'); |
||
5713 | cache.colorLocation = gl.getAttribLocation(program, 'a_color'); |
||
5714 | |||
5715 | figuresCache = cache; |
||
5716 | } |
||
5717 | |||
5718 | function drawFigures(width, height, backgroundColor, figures, context) { |
||
5719 | if (!figuresCache) { |
||
5720 | initFiguresGL(); |
||
5721 | } |
||
5722 | var cache = figuresCache, canvas = cache.canvas, gl = cache.gl; |
||
5723 | |||
5724 | canvas.width = width; |
||
5725 | canvas.height = height; |
||
5726 | gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); |
||
5727 | gl.uniform2f(cache.resolutionLocation, width, height); |
||
5728 | |||
5729 | // count triangle points |
||
5730 | var count = 0; |
||
5731 | var i, ii, rows; |
||
5732 | for (i = 0, ii = figures.length; i < ii; i++) { |
||
5733 | switch (figures[i].type) { |
||
5734 | case 'lattice': |
||
5735 | rows = (figures[i].coords.length / figures[i].verticesPerRow) | 0; |
||
5736 | count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6; |
||
5737 | break; |
||
5738 | case 'triangles': |
||
5739 | count += figures[i].coords.length; |
||
5740 | break; |
||
5741 | } |
||
5742 | } |
||
5743 | // transfer data |
||
5744 | var coords = new Float32Array(count * 2); |
||
5745 | var colors = new Uint8Array(count * 3); |
||
5746 | var coordsMap = context.coords, colorsMap = context.colors; |
||
5747 | var pIndex = 0, cIndex = 0; |
||
5748 | for (i = 0, ii = figures.length; i < ii; i++) { |
||
5749 | var figure = figures[i], ps = figure.coords, cs = figure.colors; |
||
5750 | switch (figure.type) { |
||
5751 | case 'lattice': |
||
5752 | var cols = figure.verticesPerRow; |
||
5753 | rows = (ps.length / cols) | 0; |
||
5754 | for (var row = 1; row < rows; row++) { |
||
5755 | var offset = row * cols + 1; |
||
5756 | for (var col = 1; col < cols; col++, offset++) { |
||
5757 | coords[pIndex] = coordsMap[ps[offset - cols - 1]]; |
||
5758 | coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1]; |
||
5759 | coords[pIndex + 2] = coordsMap[ps[offset - cols]]; |
||
5760 | coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1]; |
||
5761 | coords[pIndex + 4] = coordsMap[ps[offset - 1]]; |
||
5762 | coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1]; |
||
5763 | colors[cIndex] = colorsMap[cs[offset - cols - 1]]; |
||
5764 | colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1]; |
||
5765 | colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2]; |
||
5766 | colors[cIndex + 3] = colorsMap[cs[offset - cols]]; |
||
5767 | colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1]; |
||
5768 | colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2]; |
||
5769 | colors[cIndex + 6] = colorsMap[cs[offset - 1]]; |
||
5770 | colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1]; |
||
5771 | colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2]; |
||
5772 | |||
5773 | coords[pIndex + 6] = coords[pIndex + 2]; |
||
5774 | coords[pIndex + 7] = coords[pIndex + 3]; |
||
5775 | coords[pIndex + 8] = coords[pIndex + 4]; |
||
5776 | coords[pIndex + 9] = coords[pIndex + 5]; |
||
5777 | coords[pIndex + 10] = coordsMap[ps[offset]]; |
||
5778 | coords[pIndex + 11] = coordsMap[ps[offset] + 1]; |
||
5779 | colors[cIndex + 9] = colors[cIndex + 3]; |
||
5780 | colors[cIndex + 10] = colors[cIndex + 4]; |
||
5781 | colors[cIndex + 11] = colors[cIndex + 5]; |
||
5782 | colors[cIndex + 12] = colors[cIndex + 6]; |
||
5783 | colors[cIndex + 13] = colors[cIndex + 7]; |
||
5784 | colors[cIndex + 14] = colors[cIndex + 8]; |
||
5785 | colors[cIndex + 15] = colorsMap[cs[offset]]; |
||
5786 | colors[cIndex + 16] = colorsMap[cs[offset] + 1]; |
||
5787 | colors[cIndex + 17] = colorsMap[cs[offset] + 2]; |
||
5788 | pIndex += 12; |
||
5789 | cIndex += 18; |
||
5790 | } |
||
5791 | } |
||
5792 | break; |
||
5793 | case 'triangles': |
||
5794 | for (var j = 0, jj = ps.length; j < jj; j++) { |
||
5795 | coords[pIndex] = coordsMap[ps[j]]; |
||
5796 | coords[pIndex + 1] = coordsMap[ps[j] + 1]; |
||
5797 | colors[cIndex] = colorsMap[cs[i]]; |
||
5798 | colors[cIndex + 1] = colorsMap[cs[j] + 1]; |
||
5799 | colors[cIndex + 2] = colorsMap[cs[j] + 2]; |
||
5800 | pIndex += 2; |
||
5801 | cIndex += 3; |
||
5802 | } |
||
5803 | break; |
||
5804 | } |
||
5805 | } |
||
5806 | |||
5807 | // draw |
||
5808 | if (backgroundColor) { |
||
5809 | gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255, |
||
5810 | backgroundColor[2] / 255, 1.0); |
||
5811 | } else { |
||
5812 | gl.clearColor(0, 0, 0, 0); |
||
5813 | } |
||
5814 | gl.clear(gl.COLOR_BUFFER_BIT); |
||
5815 | |||
5816 | var coordsBuffer = gl.createBuffer(); |
||
5817 | gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer); |
||
5818 | gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW); |
||
5819 | gl.enableVertexAttribArray(cache.positionLocation); |
||
5820 | gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0); |
||
5821 | |||
5822 | var colorsBuffer = gl.createBuffer(); |
||
5823 | gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer); |
||
5824 | gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); |
||
5825 | gl.enableVertexAttribArray(cache.colorLocation); |
||
5826 | gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false, |
||
5827 | 0, 0); |
||
5828 | |||
5829 | gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY); |
||
5830 | gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY); |
||
5831 | |||
5832 | gl.drawArrays(gl.TRIANGLES, 0, count); |
||
5833 | |||
5834 | gl.flush(); |
||
5835 | |||
5836 | gl.deleteBuffer(coordsBuffer); |
||
5837 | gl.deleteBuffer(colorsBuffer); |
||
5838 | |||
5839 | return canvas; |
||
5840 | } |
||
5841 | |||
5842 | function cleanup() { |
||
5843 | if (smaskCache && smaskCache.canvas) { |
||
5844 | smaskCache.canvas.width = 0; |
||
5845 | smaskCache.canvas.height = 0; |
||
5846 | } |
||
5847 | if (figuresCache && figuresCache.canvas) { |
||
5848 | figuresCache.canvas.width = 0; |
||
5849 | figuresCache.canvas.height = 0; |
||
5850 | } |
||
5851 | smaskCache = null; |
||
5852 | figuresCache = null; |
||
5853 | } |
||
5854 | |||
5855 | return { |
||
5856 | get isEnabled() { |
||
5857 | if (PDFJS.disableWebGL) { |
||
5858 | return false; |
||
5859 | } |
||
5860 | var enabled = false; |
||
5861 | try { |
||
5862 | generateGL(); |
||
5863 | enabled = !!currentGL; |
||
5864 | } catch (e) { } |
||
5865 | return shadow(this, 'isEnabled', enabled); |
||
5866 | }, |
||
5867 | composeSMask: composeSMask, |
||
5868 | drawFigures: drawFigures, |
||
5869 | clear: cleanup |
||
5870 | }; |
||
5871 | })(); |
||
5872 | |||
5873 | |||
5874 | var ShadingIRs = {}; |
||
5875 | |||
5876 | ShadingIRs.RadialAxial = { |
||
5877 | fromIR: function RadialAxial_fromIR(raw) { |
||
5878 | var type = raw[1]; |
||
5879 | var colorStops = raw[2]; |
||
5880 | var p0 = raw[3]; |
||
5881 | var p1 = raw[4]; |
||
5882 | var r0 = raw[5]; |
||
5883 | var r1 = raw[6]; |
||
5884 | return { |
||
5885 | type: 'Pattern', |
||
5886 | getPattern: function RadialAxial_getPattern(ctx) { |
||
5887 | var grad; |
||
5888 | if (type === 'axial') { |
||
5889 | grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); |
||
5890 | } else if (type === 'radial') { |
||
5891 | grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); |
||
5892 | } |
||
5893 | |||
5894 | for (var i = 0, ii = colorStops.length; i < ii; ++i) { |
||
5895 | var c = colorStops[i]; |
||
5896 | grad.addColorStop(c[0], c[1]); |
||
5897 | } |
||
5898 | return grad; |
||
5899 | } |
||
5900 | }; |
||
5901 | } |
||
5902 | }; |
||
5903 | |||
5904 | var createMeshCanvas = (function createMeshCanvasClosure() { |
||
5905 | function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) { |
||
5906 | // Very basic Gouraud-shaded triangle rasterization algorithm. |
||
5907 | var coords = context.coords, colors = context.colors; |
||
5908 | var bytes = data.data, rowSize = data.width * 4; |
||
5909 | var tmp; |
||
5910 | if (coords[p1 + 1] > coords[p2 + 1]) { |
||
5911 | tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp; |
||
5912 | } |
||
5913 | if (coords[p2 + 1] > coords[p3 + 1]) { |
||
5914 | tmp = p2; p2 = p3; p3 = tmp; tmp = c2; c2 = c3; c3 = tmp; |
||
5915 | } |
||
5916 | if (coords[p1 + 1] > coords[p2 + 1]) { |
||
5917 | tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp; |
||
5918 | } |
||
5919 | var x1 = (coords[p1] + context.offsetX) * context.scaleX; |
||
5920 | var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY; |
||
5921 | var x2 = (coords[p2] + context.offsetX) * context.scaleX; |
||
5922 | var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY; |
||
5923 | var x3 = (coords[p3] + context.offsetX) * context.scaleX; |
||
5924 | var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY; |
||
5925 | if (y1 >= y3) { |
||
5926 | return; |
||
5927 | } |
||
5928 | var c1r = colors[c1], c1g = colors[c1 + 1], c1b = colors[c1 + 2]; |
||
5929 | var c2r = colors[c2], c2g = colors[c2 + 1], c2b = colors[c2 + 2]; |
||
5930 | var c3r = colors[c3], c3g = colors[c3 + 1], c3b = colors[c3 + 2]; |
||
5931 | |||
5932 | var minY = Math.round(y1), maxY = Math.round(y3); |
||
5933 | var xa, car, cag, cab; |
||
5934 | var xb, cbr, cbg, cbb; |
||
5935 | var k; |
||
5936 | for (var y = minY; y <= maxY; y++) { |
||
5937 | if (y < y2) { |
||
5938 | k = y < y1 ? 0 : y1 === y2 ? 1 : (y1 - y) / (y1 - y2); |
||
5939 | xa = x1 - (x1 - x2) * k; |
||
5940 | car = c1r - (c1r - c2r) * k; |
||
5941 | cag = c1g - (c1g - c2g) * k; |
||
5942 | cab = c1b - (c1b - c2b) * k; |
||
5943 | } else { |
||
5944 | k = y > y3 ? 1 : y2 === y3 ? 0 : (y2 - y) / (y2 - y3); |
||
5945 | xa = x2 - (x2 - x3) * k; |
||
5946 | car = c2r - (c2r - c3r) * k; |
||
5947 | cag = c2g - (c2g - c3g) * k; |
||
5948 | cab = c2b - (c2b - c3b) * k; |
||
5949 | } |
||
5950 | k = y < y1 ? 0 : y > y3 ? 1 : (y1 - y) / (y1 - y3); |
||
5951 | xb = x1 - (x1 - x3) * k; |
||
5952 | cbr = c1r - (c1r - c3r) * k; |
||
5953 | cbg = c1g - (c1g - c3g) * k; |
||
5954 | cbb = c1b - (c1b - c3b) * k; |
||
5955 | var x1_ = Math.round(Math.min(xa, xb)); |
||
5956 | var x2_ = Math.round(Math.max(xa, xb)); |
||
5957 | var j = rowSize * y + x1_ * 4; |
||
5958 | for (var x = x1_; x <= x2_; x++) { |
||
5959 | k = (xa - x) / (xa - xb); |
||
5960 | k = k < 0 ? 0 : k > 1 ? 1 : k; |
||
5961 | bytes[j++] = (car - (car - cbr) * k) | 0; |
||
5962 | bytes[j++] = (cag - (cag - cbg) * k) | 0; |
||
5963 | bytes[j++] = (cab - (cab - cbb) * k) | 0; |
||
5964 | bytes[j++] = 255; |
||
5965 | } |
||
5966 | } |
||
5967 | } |
||
5968 | |||
5969 | function drawFigure(data, figure, context) { |
||
5970 | var ps = figure.coords; |
||
5971 | var cs = figure.colors; |
||
5972 | var i, ii; |
||
5973 | switch (figure.type) { |
||
5974 | case 'lattice': |
||
5975 | var verticesPerRow = figure.verticesPerRow; |
||
5976 | var rows = Math.floor(ps.length / verticesPerRow) - 1; |
||
5977 | var cols = verticesPerRow - 1; |
||
5978 | for (i = 0; i < rows; i++) { |
||
5979 | var q = i * verticesPerRow; |
||
5980 | for (var j = 0; j < cols; j++, q++) { |
||
5981 | drawTriangle(data, context, |
||
5982 | ps[q], ps[q + 1], ps[q + verticesPerRow], |
||
5983 | cs[q], cs[q + 1], cs[q + verticesPerRow]); |
||
5984 | drawTriangle(data, context, |
||
5985 | ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], |
||
5986 | cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]); |
||
5987 | } |
||
5988 | } |
||
5989 | break; |
||
5990 | case 'triangles': |
||
5991 | for (i = 0, ii = ps.length; i < ii; i += 3) { |
||
5992 | drawTriangle(data, context, |
||
5993 | ps[i], ps[i + 1], ps[i + 2], |
||
5994 | cs[i], cs[i + 1], cs[i + 2]); |
||
5995 | } |
||
5996 | break; |
||
5997 | default: |
||
5998 | error('illigal figure'); |
||
5999 | break; |
||
6000 | } |
||
6001 | } |
||
6002 | |||
6003 | function createMeshCanvas(bounds, combinesScale, coords, colors, figures, |
||
6004 | backgroundColor) { |
||
6005 | // we will increase scale on some weird factor to let antialiasing take |
||
6006 | // care of "rough" edges |
||
6007 | var EXPECTED_SCALE = 1.1; |
||
6008 | // MAX_PATTERN_SIZE is used to avoid OOM situation. |
||
6009 | var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough |
||
6010 | |||
6011 | var offsetX = Math.floor(bounds[0]); |
||
6012 | var offsetY = Math.floor(bounds[1]); |
||
6013 | var boundsWidth = Math.ceil(bounds[2]) - offsetX; |
||
6014 | var boundsHeight = Math.ceil(bounds[3]) - offsetY; |
||
6015 | |||
6016 | var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] * |
||
6017 | EXPECTED_SCALE)), MAX_PATTERN_SIZE); |
||
6018 | var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] * |
||
6019 | EXPECTED_SCALE)), MAX_PATTERN_SIZE); |
||
6020 | var scaleX = boundsWidth / width; |
||
6021 | var scaleY = boundsHeight / height; |
||
6022 | |||
6023 | var context = { |
||
6024 | coords: coords, |
||
6025 | colors: colors, |
||
6026 | offsetX: -offsetX, |
||
6027 | offsetY: -offsetY, |
||
6028 | scaleX: 1 / scaleX, |
||
6029 | scaleY: 1 / scaleY |
||
6030 | }; |
||
6031 | |||
6032 | var canvas, tmpCanvas, i, ii; |
||
6033 | if (WebGLUtils.isEnabled) { |
||
6034 | canvas = WebGLUtils.drawFigures(width, height, backgroundColor, |
||
6035 | figures, context); |
||
6036 | |||
6037 | // https://bugzilla.mozilla.org/show_bug.cgi?id=972126 |
||
6038 | tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); |
||
6039 | tmpCanvas.context.drawImage(canvas, 0, 0); |
||
6040 | canvas = tmpCanvas.canvas; |
||
6041 | } else { |
||
6042 | tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); |
||
6043 | var tmpCtx = tmpCanvas.context; |
||
6044 | |||
6045 | var data = tmpCtx.createImageData(width, height); |
||
6046 | if (backgroundColor) { |
||
6047 | var bytes = data.data; |
||
6048 | for (i = 0, ii = bytes.length; i < ii; i += 4) { |
||
6049 | bytes[i] = backgroundColor[0]; |
||
6050 | bytes[i + 1] = backgroundColor[1]; |
||
6051 | bytes[i + 2] = backgroundColor[2]; |
||
6052 | bytes[i + 3] = 255; |
||
6053 | } |
||
6054 | } |
||
6055 | for (i = 0; i < figures.length; i++) { |
||
6056 | drawFigure(data, figures[i], context); |
||
6057 | } |
||
6058 | tmpCtx.putImageData(data, 0, 0); |
||
6059 | canvas = tmpCanvas.canvas; |
||
6060 | } |
||
6061 | |||
6062 | return {canvas: canvas, offsetX: offsetX, offsetY: offsetY, |
||
6063 | scaleX: scaleX, scaleY: scaleY}; |
||
6064 | } |
||
6065 | return createMeshCanvas; |
||
6066 | })(); |
||
6067 | |||
6068 | ShadingIRs.Mesh = { |
||
6069 | fromIR: function Mesh_fromIR(raw) { |
||
6070 | //var type = raw[1]; |
||
6071 | var coords = raw[2]; |
||
6072 | var colors = raw[3]; |
||
6073 | var figures = raw[4]; |
||
6074 | var bounds = raw[5]; |
||
6075 | var matrix = raw[6]; |
||
6076 | //var bbox = raw[7]; |
||
6077 | var background = raw[8]; |
||
6078 | return { |
||
6079 | type: 'Pattern', |
||
6080 | getPattern: function Mesh_getPattern(ctx, owner, shadingFill) { |
||
6081 | var scale; |
||
6082 | if (shadingFill) { |
||
6083 | scale = Util.singularValueDecompose2dScale(ctx.mozCurrentTransform); |
||
6084 | } else { |
||
6085 | // Obtain scale from matrix and current transformation matrix. |
||
6086 | scale = Util.singularValueDecompose2dScale(owner.baseTransform); |
||
6087 | if (matrix) { |
||
6088 | var matrixScale = Util.singularValueDecompose2dScale(matrix); |
||
6089 | scale = [scale[0] * matrixScale[0], |
||
6090 | scale[1] * matrixScale[1]]; |
||
6091 | } |
||
6092 | } |
||
6093 | |||
6094 | |||
6095 | // Rasterizing on the main thread since sending/queue large canvases |
||
6096 | // might cause OOM. |
||
6097 | var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords, |
||
6098 | colors, figures, shadingFill ? null : background); |
||
6099 | |||
6100 | if (!shadingFill) { |
||
6101 | ctx.setTransform.apply(ctx, owner.baseTransform); |
||
6102 | if (matrix) { |
||
6103 | ctx.transform.apply(ctx, matrix); |
||
6104 | } |
||
6105 | } |
||
6106 | |||
6107 | ctx.translate(temporaryPatternCanvas.offsetX, |
||
6108 | temporaryPatternCanvas.offsetY); |
||
6109 | ctx.scale(temporaryPatternCanvas.scaleX, |
||
6110 | temporaryPatternCanvas.scaleY); |
||
6111 | |||
6112 | return ctx.createPattern(temporaryPatternCanvas.canvas, 'no-repeat'); |
||
6113 | } |
||
6114 | }; |
||
6115 | } |
||
6116 | }; |
||
6117 | |||
6118 | ShadingIRs.Dummy = { |
||
6119 | fromIR: function Dummy_fromIR() { |
||
6120 | return { |
||
6121 | type: 'Pattern', |
||
6122 | getPattern: function Dummy_fromIR_getPattern() { |
||
6123 | return 'hotpink'; |
||
6124 | } |
||
6125 | }; |
||
6126 | } |
||
6127 | }; |
||
6128 | |||
6129 | function getShadingPatternFromIR(raw) { |
||
6130 | var shadingIR = ShadingIRs[raw[0]]; |
||
6131 | if (!shadingIR) { |
||
6132 | error('Unknown IR type: ' + raw[0]); |
||
6133 | } |
||
6134 | return shadingIR.fromIR(raw); |
||
6135 | } |
||
6136 | |||
6137 | var TilingPattern = (function TilingPatternClosure() { |
||
6138 | var PaintType = { |
||
6139 | COLORED: 1, |
||
6140 | UNCOLORED: 2 |
||
6141 | }; |
||
6142 | |||
6143 | var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough |
||
6144 | |||
6145 | function TilingPattern(IR, color, ctx, objs, commonObjs, baseTransform) { |
||
6146 | this.operatorList = IR[2]; |
||
6147 | this.matrix = IR[3] || [1, 0, 0, 1, 0, 0]; |
||
6148 | this.bbox = IR[4]; |
||
6149 | this.xstep = IR[5]; |
||
6150 | this.ystep = IR[6]; |
||
6151 | this.paintType = IR[7]; |
||
6152 | this.tilingType = IR[8]; |
||
6153 | this.color = color; |
||
6154 | this.objs = objs; |
||
6155 | this.commonObjs = commonObjs; |
||
6156 | this.baseTransform = baseTransform; |
||
6157 | this.type = 'Pattern'; |
||
6158 | this.ctx = ctx; |
||
6159 | } |
||
6160 | |||
6161 | TilingPattern.prototype = { |
||
6162 | createPatternCanvas: function TilinPattern_createPatternCanvas(owner) { |
||
6163 | var operatorList = this.operatorList; |
||
6164 | var bbox = this.bbox; |
||
6165 | var xstep = this.xstep; |
||
6166 | var ystep = this.ystep; |
||
6167 | var paintType = this.paintType; |
||
6168 | var tilingType = this.tilingType; |
||
6169 | var color = this.color; |
||
6170 | var objs = this.objs; |
||
6171 | var commonObjs = this.commonObjs; |
||
6172 | |||
6173 | info('TilingType: ' + tilingType); |
||
6174 | |||
6175 | var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; |
||
6176 | |||
6177 | var topLeft = [x0, y0]; |
||
6178 | // we want the canvas to be as large as the step size |
||
6179 | var botRight = [x0 + xstep, y0 + ystep]; |
||
6180 | |||
6181 | var width = botRight[0] - topLeft[0]; |
||
6182 | var height = botRight[1] - topLeft[1]; |
||
6183 | |||
6184 | // Obtain scale from matrix and current transformation matrix. |
||
6185 | var matrixScale = Util.singularValueDecompose2dScale(this.matrix); |
||
6186 | var curMatrixScale = Util.singularValueDecompose2dScale( |
||
6187 | this.baseTransform); |
||
6188 | var combinedScale = [matrixScale[0] * curMatrixScale[0], |
||
6189 | matrixScale[1] * curMatrixScale[1]]; |
||
6190 | |||
6191 | // MAX_PATTERN_SIZE is used to avoid OOM situation. |
||
6192 | // Use width and height values that are as close as possible to the end |
||
6193 | // result when the pattern is used. Too low value makes the pattern look |
||
6194 | // blurry. Too large value makes it look too crispy. |
||
6195 | width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])), |
||
6196 | MAX_PATTERN_SIZE); |
||
6197 | |||
6198 | height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])), |
||
6199 | MAX_PATTERN_SIZE); |
||
6200 | |||
6201 | var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true); |
||
6202 | var tmpCtx = tmpCanvas.context; |
||
6203 | var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs); |
||
6204 | graphics.groupLevel = owner.groupLevel; |
||
6205 | |||
6206 | this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color); |
||
6207 | |||
6208 | this.setScale(width, height, xstep, ystep); |
||
6209 | this.transformToScale(graphics); |
||
6210 | |||
6211 | // transform coordinates to pattern space |
||
6212 | var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]]; |
||
6213 | graphics.transform.apply(graphics, tmpTranslate); |
||
6214 | |||
6215 | this.clipBbox(graphics, bbox, x0, y0, x1, y1); |
||
6216 | |||
6217 | graphics.executeOperatorList(operatorList); |
||
6218 | return tmpCanvas.canvas; |
||
6219 | }, |
||
6220 | |||
6221 | setScale: function TilingPattern_setScale(width, height, xstep, ystep) { |
||
6222 | this.scale = [width / xstep, height / ystep]; |
||
6223 | }, |
||
6224 | |||
6225 | transformToScale: function TilingPattern_transformToScale(graphics) { |
||
6226 | var scale = this.scale; |
||
6227 | var tmpScale = [scale[0], 0, 0, scale[1], 0, 0]; |
||
6228 | graphics.transform.apply(graphics, tmpScale); |
||
6229 | }, |
||
6230 | |||
6231 | scaleToContext: function TilingPattern_scaleToContext() { |
||
6232 | var scale = this.scale; |
||
6233 | this.ctx.scale(1 / scale[0], 1 / scale[1]); |
||
6234 | }, |
||
6235 | |||
6236 | clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) { |
||
6237 | if (bbox && isArray(bbox) && bbox.length === 4) { |
||
6238 | var bboxWidth = x1 - x0; |
||
6239 | var bboxHeight = y1 - y0; |
||
6240 | graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight); |
||
6241 | graphics.clip(); |
||
6242 | graphics.endPath(); |
||
6243 | } |
||
6244 | }, |
||
6245 | |||
6246 | setFillAndStrokeStyleToContext: |
||
6247 | function setFillAndStrokeStyleToContext(context, paintType, color) { |
||
6248 | switch (paintType) { |
||
6249 | case PaintType.COLORED: |
||
6250 | var ctx = this.ctx; |
||
6251 | context.fillStyle = ctx.fillStyle; |
||
6252 | context.strokeStyle = ctx.strokeStyle; |
||
6253 | break; |
||
6254 | case PaintType.UNCOLORED: |
||
6255 | var cssColor = Util.makeCssRgb(color[0], color[1], color[2]); |
||
6256 | context.fillStyle = cssColor; |
||
6257 | context.strokeStyle = cssColor; |
||
6258 | break; |
||
6259 | default: |
||
6260 | error('Unsupported paint type: ' + paintType); |
||
6261 | } |
||
6262 | }, |
||
6263 | |||
6264 | getPattern: function TilingPattern_getPattern(ctx, owner) { |
||
6265 | var temporaryPatternCanvas = this.createPatternCanvas(owner); |
||
6266 | |||
6267 | ctx = this.ctx; |
||
6268 | ctx.setTransform.apply(ctx, this.baseTransform); |
||
6269 | ctx.transform.apply(ctx, this.matrix); |
||
6270 | this.scaleToContext(); |
||
6271 | |||
6272 | return ctx.createPattern(temporaryPatternCanvas, 'repeat'); |
||
6273 | } |
||
6274 | }; |
||
6275 | |||
6276 | return TilingPattern; |
||
6277 | })(); |
||
6278 | |||
6279 | |||
6280 | PDFJS.disableFontFace = false; |
||
6281 | |||
6282 | var FontLoader = { |
||
6283 | insertRule: function fontLoaderInsertRule(rule) { |
||
6284 | var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG'); |
||
6285 | if (!styleElement) { |
||
6286 | styleElement = document.createElement('style'); |
||
6287 | styleElement.id = 'PDFJS_FONT_STYLE_TAG'; |
||
6288 | document.documentElement.getElementsByTagName('head')[0].appendChild( |
||
6289 | styleElement); |
||
6290 | } |
||
6291 | |||
6292 | var styleSheet = styleElement.sheet; |
||
6293 | styleSheet.insertRule(rule, styleSheet.cssRules.length); |
||
6294 | }, |
||
6295 | |||
6296 | clear: function fontLoaderClear() { |
||
6297 | var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG'); |
||
6298 | if (styleElement) { |
||
6299 | styleElement.parentNode.removeChild(styleElement); |
||
6300 | } |
||
6301 | //#if !(MOZCENTRAL) |
||
6302 | this.nativeFontFaces.forEach(function(nativeFontFace) { |
||
6303 | document.fonts.delete(nativeFontFace); |
||
6304 | }); |
||
6305 | this.nativeFontFaces.length = 0; |
||
6306 | //#endif |
||
6307 | }, |
||
6308 | //#if !(MOZCENTRAL) |
||
6309 | get loadTestFont() { |
||
6310 | // This is a CFF font with 1 glyph for '.' that fills its entire width and |
||
6311 | // height. |
||
6312 | return shadow(this, 'loadTestFont', atob( |
||
6313 | 'T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQ' + |
||
6314 | 'AABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwA' + |
||
6315 | 'AAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbm' + |
||
6316 | 'FtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAA' + |
||
6317 | 'AADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6A' + |
||
6318 | 'ABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAA' + |
||
6319 | 'MQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAA' + |
||
6320 | 'AAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAA' + |
||
6321 | 'AAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQ' + |
||
6322 | 'AAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMA' + |
||
6323 | 'AQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAA' + |
||
6324 | 'EAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAA' + |
||
6325 | 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAA' + |
||
6326 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + |
||
6327 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + |
||
6328 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + |
||
6329 | 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA' + |
||
6330 | 'AAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgc' + |
||
6331 | 'A/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF' + |
||
6332 | 'hYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQA' + |
||
6333 | 'AAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAg' + |
||
6334 | 'ABAAAAAAAAAAAD6AAAAAAAAA==' |
||
6335 | )); |
||
6336 | }, |
||
6337 | |||
6338 | loadTestFontId: 0, |
||
6339 | |||
6340 | loadingContext: { |
||
6341 | requests: [], |
||
6342 | nextRequestId: 0 |
||
6343 | }, |
||
6344 | |||
6345 | isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() { |
||
6346 | if (isWorker) { |
||
6347 | return false; |
||
6348 | } |
||
6349 | |||
6350 | // User agent string sniffing is bad, but there is no reliable way to tell |
||
6351 | // if font is fully loaded and ready to be used with canvas. |
||
6352 | var userAgent = window.navigator.userAgent; |
||
6353 | var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent); |
||
6354 | if (m && m[1] >= 14) { |
||
6355 | return true; |
||
6356 | } |
||
6357 | // TODO other browsers |
||
6358 | if (userAgent === 'node') { |
||
6359 | return true; |
||
6360 | } |
||
6361 | return false; |
||
6362 | })(), |
||
6363 | |||
6364 | nativeFontFaces: [], |
||
6365 | |||
6366 | isFontLoadingAPISupported: (!isWorker && typeof document !== 'undefined' && |
||
6367 | !!document.fonts), |
||
6368 | |||
6369 | addNativeFontFace: function fontLoader_addNativeFontFace(nativeFontFace) { |
||
6370 | this.nativeFontFaces.push(nativeFontFace); |
||
6371 | document.fonts.add(nativeFontFace); |
||
6372 | }, |
||
6373 | |||
6374 | bind: function fontLoaderBind(fonts, callback) { |
||
6375 | assert(!isWorker, 'bind() shall be called from main thread'); |
||
6376 | |||
6377 | var rules = []; |
||
6378 | var fontsToLoad = []; |
||
6379 | var fontLoadPromises = []; |
||
6380 | for (var i = 0, ii = fonts.length; i < ii; i++) { |
||
6381 | var font = fonts[i]; |
||
6382 | |||
6383 | // Add the font to the DOM only once or skip if the font |
||
6384 | // is already loaded. |
||
6385 | if (font.attached || font.loading === false) { |
||
6386 | continue; |
||
6387 | } |
||
6388 | font.attached = true; |
||
6389 | |||
6390 | if (this.isFontLoadingAPISupported) { |
||
6391 | var nativeFontFace = font.createNativeFontFace(); |
||
6392 | if (nativeFontFace) { |
||
6393 | fontLoadPromises.push(nativeFontFace.loaded); |
||
6394 | } |
||
6395 | } else { |
||
6396 | var rule = font.bindDOM(); |
||
6397 | if (rule) { |
||
6398 | rules.push(rule); |
||
6399 | fontsToLoad.push(font); |
||
6400 | } |
||
6401 | } |
||
6402 | } |
||
6403 | |||
6404 | var request = FontLoader.queueLoadingCallback(callback); |
||
6405 | if (this.isFontLoadingAPISupported) { |
||
6406 | Promise.all(fontsToLoad).then(function() { |
||
6407 | request.complete(); |
||
6408 | }); |
||
6409 | } else if (rules.length > 0 && !this.isSyncFontLoadingSupported) { |
||
6410 | FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); |
||
6411 | } else { |
||
6412 | request.complete(); |
||
6413 | } |
||
6414 | }, |
||
6415 | |||
6416 | queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) { |
||
6417 | function LoadLoader_completeRequest() { |
||
6418 | assert(!request.end, 'completeRequest() cannot be called twice'); |
||
6419 | request.end = Date.now(); |
||
6420 | |||
6421 | // sending all completed requests in order how they were queued |
||
6422 | while (context.requests.length > 0 && context.requests[0].end) { |
||
6423 | var otherRequest = context.requests.shift(); |
||
6424 | setTimeout(otherRequest.callback, 0); |
||
6425 | } |
||
6426 | } |
||
6427 | |||
6428 | var context = FontLoader.loadingContext; |
||
6429 | var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++); |
||
6430 | var request = { |
||
6431 | id: requestId, |
||
6432 | complete: LoadLoader_completeRequest, |
||
6433 | callback: callback, |
||
6434 | started: Date.now() |
||
6435 | }; |
||
6436 | context.requests.push(request); |
||
6437 | return request; |
||
6438 | }, |
||
6439 | |||
6440 | prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, |
||
6441 | fonts, |
||
6442 | request) { |
||
6443 | /** Hack begin */ |
||
6444 | // There's currently no event when a font has finished downloading so the |
||
6445 | // following code is a dirty hack to 'guess' when a font is |
||
6446 | // ready. It's assumed fonts are loaded in order, so add a known test |
||
6447 | // font after the desired fonts and then test for the loading of that |
||
6448 | // test font. |
||
6449 | |||
6450 | function int32(data, offset) { |
||
6451 | return (data.charCodeAt(offset) << 24) | |
||
6452 | (data.charCodeAt(offset + 1) << 16) | |
||
6453 | (data.charCodeAt(offset + 2) << 8) | |
||
6454 | (data.charCodeAt(offset + 3) & 0xff); |
||
6455 | } |
||
6456 | |||
6457 | function spliceString(s, offset, remove, insert) { |
||
6458 | var chunk1 = s.substr(0, offset); |
||
6459 | var chunk2 = s.substr(offset + remove); |
||
6460 | return chunk1 + insert + chunk2; |
||
6461 | } |
||
6462 | |||
6463 | var i, ii; |
||
6464 | |||
6465 | var canvas = document.createElement('canvas'); |
||
6466 | canvas.width = 1; |
||
6467 | canvas.height = 1; |
||
6468 | var ctx = canvas.getContext('2d'); |
||
6469 | |||
6470 | var called = 0; |
||
6471 | function isFontReady(name, callback) { |
||
6472 | called++; |
||
6473 | // With setTimeout clamping this gives the font ~100ms to load. |
||
6474 | if(called > 30) { |
||
6475 | warn('Load test font never loaded.'); |
||
6476 | callback(); |
||
6477 | return; |
||
6478 | } |
||
6479 | ctx.font = '30px ' + name; |
||
6480 | ctx.fillText('.', 0, 20); |
||
6481 | var imageData = ctx.getImageData(0, 0, 1, 1); |
||
6482 | if (imageData.data[3] > 0) { |
||
6483 | callback(); |
||
6484 | return; |
||
6485 | } |
||
6486 | setTimeout(isFontReady.bind(null, name, callback)); |
||
6487 | } |
||
6488 | |||
6489 | var loadTestFontId = 'lt' + Date.now() + this.loadTestFontId++; |
||
6490 | // Chromium seems to cache fonts based on a hash of the actual font data, |
||
6491 | // so the font must be modified for each load test else it will appear to |
||
6492 | // be loaded already. |
||
6493 | // TODO: This could maybe be made faster by avoiding the btoa of the full |
||
6494 | // font by splitting it in chunks before hand and padding the font id. |
||
6495 | var data = this.loadTestFont; |
||
6496 | var COMMENT_OFFSET = 976; // has to be on 4 byte boundary (for checksum) |
||
6497 | data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, |
||
6498 | loadTestFontId); |
||
6499 | // CFF checksum is important for IE, adjusting it |
||
6500 | var CFF_CHECKSUM_OFFSET = 16; |
||
6501 | var XXXX_VALUE = 0x58585858; // the "comment" filled with 'X' |
||
6502 | var checksum = int32(data, CFF_CHECKSUM_OFFSET); |
||
6503 | for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) { |
||
6504 | checksum = (checksum - XXXX_VALUE + int32(loadTestFontId, i)) | 0; |
||
6505 | } |
||
6506 | if (i < loadTestFontId.length) { // align to 4 bytes boundary |
||
6507 | checksum = (checksum - XXXX_VALUE + |
||
6508 | int32(loadTestFontId + 'XXX', i)) | 0; |
||
6509 | } |
||
6510 | data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum)); |
||
6511 | |||
6512 | var url = 'url(data:font/opentype;base64,' + btoa(data) + ');'; |
||
6513 | var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' + |
||
6514 | url + '}'; |
||
6515 | FontLoader.insertRule(rule); |
||
6516 | |||
6517 | var names = []; |
||
6518 | for (i = 0, ii = fonts.length; i < ii; i++) { |
||
6519 | names.push(fonts[i].loadedName); |
||
6520 | } |
||
6521 | names.push(loadTestFontId); |
||
6522 | |||
6523 | var div = document.createElement('div'); |
||
6524 | div.setAttribute('style', |
||
6525 | 'visibility: hidden;' + |
||
6526 | 'width: 10px; height: 10px;' + |
||
6527 | 'position: absolute; top: 0px; left: 0px;'); |
||
6528 | for (i = 0, ii = names.length; i < ii; ++i) { |
||
6529 | var span = document.createElement('span'); |
||
6530 | span.textContent = 'Hi'; |
||
6531 | span.style.fontFamily = names[i]; |
||
6532 | div.appendChild(span); |
||
6533 | } |
||
6534 | document.body.appendChild(div); |
||
6535 | |||
6536 | isFontReady(loadTestFontId, function() { |
||
6537 | document.body.removeChild(div); |
||
6538 | request.complete(); |
||
6539 | }); |
||
6540 | /** Hack end */ |
||
6541 | } |
||
6542 | //#else |
||
6543 | //bind: function fontLoaderBind(fonts, callback) { |
||
6544 | // assert(!isWorker, 'bind() shall be called from main thread'); |
||
6545 | // |
||
6546 | // for (var i = 0, ii = fonts.length; i < ii; i++) { |
||
6547 | // var font = fonts[i]; |
||
6548 | // if (font.attached) { |
||
6549 | // continue; |
||
6550 | // } |
||
6551 | // |
||
6552 | // font.attached = true; |
||
6553 | // font.bindDOM() |
||
6554 | // } |
||
6555 | // |
||
6556 | // setTimeout(callback); |
||
6557 | //} |
||
6558 | //#endif |
||
6559 | }; |
||
6560 | |||
6561 | var FontFaceObject = (function FontFaceObjectClosure() { |
||
6562 | function FontFaceObject(name, file, properties) { |
||
6563 | this.compiledGlyphs = {}; |
||
6564 | if (arguments.length === 1) { |
||
6565 | // importing translated data |
||
6566 | var data = arguments[0]; |
||
6567 | for (var i in data) { |
||
6568 | this[i] = data[i]; |
||
6569 | } |
||
6570 | return; |
||
6571 | } |
||
6572 | } |
||
6573 | FontFaceObject.prototype = { |
||
6574 | //#if !(MOZCENTRAL) |
||
6575 | createNativeFontFace: function FontFaceObject_createNativeFontFace() { |
||
6576 | if (!this.data) { |
||
6577 | return null; |
||
6578 | } |
||
6579 | |||
6580 | if (PDFJS.disableFontFace) { |
||
6581 | this.disableFontFace = true; |
||
6582 | return null; |
||
6583 | } |
||
6584 | |||
6585 | var nativeFontFace = new FontFace(this.loadedName, this.data, {}); |
||
6586 | |||
6587 | FontLoader.addNativeFontFace(nativeFontFace); |
||
6588 | |||
6589 | if (PDFJS.pdfBug && 'FontInspector' in globalScope && |
||
6590 | globalScope['FontInspector'].enabled) { |
||
6591 | globalScope['FontInspector'].fontAdded(this); |
||
6592 | } |
||
6593 | return nativeFontFace; |
||
6594 | }, |
||
6595 | //#endif |
||
6596 | |||
6597 | bindDOM: function FontFaceObject_bindDOM() { |
||
6598 | if (!this.data) { |
||
6599 | return null; |
||
6600 | } |
||
6601 | |||
6602 | if (PDFJS.disableFontFace) { |
||
6603 | this.disableFontFace = true; |
||
6604 | return null; |
||
6605 | } |
||
6606 | |||
6607 | var data = bytesToString(new Uint8Array(this.data)); |
||
6608 | var fontName = this.loadedName; |
||
6609 | |||
6610 | // Add the font-face rule to the document |
||
6611 | var url = ('url(data:' + this.mimetype + ';base64,' + |
||
6612 | window.btoa(data) + ');'); |
||
6613 | var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}'; |
||
6614 | FontLoader.insertRule(rule); |
||
6615 | |||
6616 | if (PDFJS.pdfBug && 'FontInspector' in globalScope && |
||
6617 | globalScope['FontInspector'].enabled) { |
||
6618 | globalScope['FontInspector'].fontAdded(this, url); |
||
6619 | } |
||
6620 | |||
6621 | return rule; |
||
6622 | }, |
||
6623 | |||
6624 | getPathGenerator: function FontLoader_getPathGenerator(objs, character) { |
||
6625 | if (!(character in this.compiledGlyphs)) { |
||
6626 | var js = objs.get(this.loadedName + '_path_' + character); |
||
6627 | /*jshint -W054 */ |
||
6628 | this.compiledGlyphs[character] = new Function('c', 'size', js); |
||
6629 | } |
||
6630 | return this.compiledGlyphs[character]; |
||
6631 | } |
||
6632 | }; |
||
6633 | return FontFaceObject; |
||
6634 | })(); |
||
6635 | |||
6636 | |||
6637 | var ANNOT_MIN_SIZE = 10; // px |
||
6638 | |||
6639 | var AnnotationUtils = (function AnnotationUtilsClosure() { |
||
6640 | // TODO(mack): This dupes some of the logic in CanvasGraphics.setFont() |
||
6641 | function setTextStyles(element, item, fontObj) { |
||
6642 | |||
6643 | var style = element.style; |
||
6644 | style.fontSize = item.fontSize + 'px'; |
||
6645 | style.direction = item.fontDirection < 0 ? 'rtl': 'ltr'; |
||
6646 | |||
6647 | if (!fontObj) { |
||
6648 | return; |
||
6649 | } |
||
6650 | |||
6651 | style.fontWeight = fontObj.black ? |
||
6652 | (fontObj.bold ? 'bolder' : 'bold') : |
||
6653 | (fontObj.bold ? 'bold' : 'normal'); |
||
6654 | style.fontStyle = fontObj.italic ? 'italic' : 'normal'; |
||
6655 | |||
6656 | var fontName = fontObj.loadedName; |
||
6657 | var fontFamily = fontName ? '"' + fontName + '", ' : ''; |
||
6658 | // Use a reasonable default font if the font doesn't specify a fallback |
||
6659 | var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif'; |
||
6660 | style.fontFamily = fontFamily + fallbackName; |
||
6661 | } |
||
6662 | |||
6663 | function initContainer(item, drawBorder) { |
||
6664 | var container = document.createElement('section'); |
||
6665 | var cstyle = container.style; |
||
6666 | var width = item.rect[2] - item.rect[0]; |
||
6667 | var height = item.rect[3] - item.rect[1]; |
||
6668 | |||
6669 | var bWidth = item.borderWidth || 0; |
||
6670 | if (bWidth) { |
||
6671 | width = width - 2 * bWidth; |
||
6672 | height = height - 2 * bWidth; |
||
6673 | cstyle.borderWidth = bWidth + 'px'; |
||
6674 | var color = item.color; |
||
6675 | if (drawBorder && color) { |
||
6676 | cstyle.borderStyle = 'solid'; |
||
6677 | cstyle.borderColor = Util.makeCssRgb(Math.round(color[0] * 255), |
||
6678 | Math.round(color[1] * 255), |
||
6679 | Math.round(color[2] * 255)); |
||
6680 | } |
||
6681 | } |
||
6682 | cstyle.width = width + 'px'; |
||
6683 | cstyle.height = height + 'px'; |
||
6684 | return container; |
||
6685 | } |
||
6686 | |||
6687 | function getHtmlElementForTextWidgetAnnotation(item, commonObjs) { |
||
6688 | var element = document.createElement('div'); |
||
6689 | var width = item.rect[2] - item.rect[0]; |
||
6690 | var height = item.rect[3] - item.rect[1]; |
||
6691 | element.style.width = width + 'px'; |
||
6692 | element.style.height = height + 'px'; |
||
6693 | element.style.display = 'table'; |
||
6694 | |||
6695 | var content = document.createElement('div'); |
||
6696 | content.textContent = item.fieldValue; |
||
6697 | var textAlignment = item.textAlignment; |
||
6698 | content.style.textAlign = ['left', 'center', 'right'][textAlignment]; |
||
6699 | content.style.verticalAlign = 'middle'; |
||
6700 | content.style.display = 'table-cell'; |
||
6701 | |||
6702 | var fontObj = item.fontRefName ? |
||
6703 | commonObjs.getData(item.fontRefName) : null; |
||
6704 | setTextStyles(content, item, fontObj); |
||
6705 | |||
6706 | element.appendChild(content); |
||
6707 | |||
6708 | return element; |
||
6709 | } |
||
6710 | |||
6711 | function getHtmlElementForTextAnnotation(item) { |
||
6712 | var rect = item.rect; |
||
6713 | |||
6714 | // sanity check because of OOo-generated PDFs |
||
6715 | if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) { |
||
6716 | rect[3] = rect[1] + ANNOT_MIN_SIZE; |
||
6717 | } |
||
6718 | if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) { |
||
6719 | rect[2] = rect[0] + (rect[3] - rect[1]); // make it square |
||
6720 | } |
||
6721 | |||
6722 | var container = initContainer(item, false); |
||
6723 | container.className = 'annotText'; |
||
6724 | |||
6725 | var image = document.createElement('img'); |
||
6726 | image.style.height = container.style.height; |
||
6727 | image.style.width = container.style.width; |
||
6728 | var iconName = item.name; |
||
6729 | image.src = PDFJS.imageResourcesPath + 'annotation-' + |
||
6730 | iconName.toLowerCase() + '.svg'; |
||
6731 | image.alt = '[{{type}} Annotation]'; |
||
6732 | image.dataset.l10nId = 'text_annotation_type'; |
||
6733 | image.dataset.l10nArgs = JSON.stringify({type: iconName}); |
||
6734 | |||
6735 | var contentWrapper = document.createElement('div'); |
||
6736 | contentWrapper.className = 'annotTextContentWrapper'; |
||
6737 | contentWrapper.style.left = Math.floor(rect[2] - rect[0] + 5) + 'px'; |
||
6738 | contentWrapper.style.top = '-10px'; |
||
6739 | |||
6740 | var content = document.createElement('div'); |
||
6741 | content.className = 'annotTextContent'; |
||
6742 | content.setAttribute('hidden', true); |
||
6743 | |||
6744 | var i, ii; |
||
6745 | if (item.hasBgColor) { |
||
6746 | var color = item.color; |
||
6747 | |||
6748 | // Enlighten the color (70%) |
||
6749 | var BACKGROUND_ENLIGHT = 0.7; |
||
6750 | var r = BACKGROUND_ENLIGHT * (1.0 - color[0]) + color[0]; |
||
6751 | var g = BACKGROUND_ENLIGHT * (1.0 - color[1]) + color[1]; |
||
6752 | var b = BACKGROUND_ENLIGHT * (1.0 - color[2]) + color[2]; |
||
6753 | content.style.backgroundColor = Util.makeCssRgb((r * 255) | 0, |
||
6754 | (g * 255) | 0, |
||
6755 | (b * 255) | 0); |
||
6756 | } |
||
6757 | |||
6758 | var title = document.createElement('h1'); |
||
6759 | var text = document.createElement('p'); |
||
6760 | title.textContent = item.title; |
||
6761 | |||
6762 | if (!item.content && !item.title) { |
||
6763 | content.setAttribute('hidden', true); |
||
6764 | } else { |
||
6765 | var e = document.createElement('span'); |
||
6766 | var lines = item.content.split(/(?:\r\n?|\n)/); |
||
6767 | for (i = 0, ii = lines.length; i < ii; ++i) { |
||
6768 | var line = lines[i]; |
||
6769 | e.appendChild(document.createTextNode(line)); |
||
6770 | if (i < (ii - 1)) { |
||
6771 | e.appendChild(document.createElement('br')); |
||
6772 | } |
||
6773 | } |
||
6774 | text.appendChild(e); |
||
6775 | |||
6776 | var pinned = false; |
||
6777 | |||
6778 | var showAnnotation = function showAnnotation(pin) { |
||
6779 | if (pin) { |
||
6780 | pinned = true; |
||
6781 | } |
||
6782 | if (content.hasAttribute('hidden')) { |
||
6783 | container.style.zIndex += 1; |
||
6784 | content.removeAttribute('hidden'); |
||
6785 | } |
||
6786 | }; |
||
6787 | |||
6788 | var hideAnnotation = function hideAnnotation(unpin) { |
||
6789 | if (unpin) { |
||
6790 | pinned = false; |
||
6791 | } |
||
6792 | if (!content.hasAttribute('hidden') && !pinned) { |
||
6793 | container.style.zIndex -= 1; |
||
6794 | content.setAttribute('hidden', true); |
||
6795 | } |
||
6796 | }; |
||
6797 | |||
6798 | var toggleAnnotation = function toggleAnnotation() { |
||
6799 | if (pinned) { |
||
6800 | hideAnnotation(true); |
||
6801 | } else { |
||
6802 | showAnnotation(true); |
||
6803 | } |
||
6804 | }; |
||
6805 | |||
6806 | image.addEventListener('click', function image_clickHandler() { |
||
6807 | toggleAnnotation(); |
||
6808 | }, false); |
||
6809 | image.addEventListener('mouseover', function image_mouseOverHandler() { |
||
6810 | showAnnotation(); |
||
6811 | }, false); |
||
6812 | image.addEventListener('mouseout', function image_mouseOutHandler() { |
||
6813 | hideAnnotation(); |
||
6814 | }, false); |
||
6815 | |||
6816 | content.addEventListener('click', function content_clickHandler() { |
||
6817 | hideAnnotation(true); |
||
6818 | }, false); |
||
6819 | } |
||
6820 | |||
6821 | content.appendChild(title); |
||
6822 | content.appendChild(text); |
||
6823 | contentWrapper.appendChild(content); |
||
6824 | container.appendChild(image); |
||
6825 | container.appendChild(contentWrapper); |
||
6826 | |||
6827 | return container; |
||
6828 | } |
||
6829 | |||
6830 | function getHtmlElementForLinkAnnotation(item) { |
||
6831 | var container = initContainer(item, true); |
||
6832 | container.className = 'annotLink'; |
||
6833 | |||
6834 | var link = document.createElement('a'); |
||
6835 | link.href = link.title = item.url || ''; |
||
6836 | if (item.url && PDFJS.openExternalLinksInNewWindow) { |
||
6837 | link.target = '_blank'; |
||
6838 | } |
||
6839 | |||
6840 | container.appendChild(link); |
||
6841 | |||
6842 | return container; |
||
6843 | } |
||
6844 | |||
6845 | function getHtmlElement(data, objs) { |
||
6846 | switch (data.annotationType) { |
||
6847 | case AnnotationType.WIDGET: |
||
6848 | return getHtmlElementForTextWidgetAnnotation(data, objs); |
||
6849 | case AnnotationType.TEXT: |
||
6850 | return getHtmlElementForTextAnnotation(data); |
||
6851 | case AnnotationType.LINK: |
||
6852 | return getHtmlElementForLinkAnnotation(data); |
||
6853 | default: |
||
6854 | throw new Error('Unsupported annotationType: ' + data.annotationType); |
||
6855 | } |
||
6856 | } |
||
6857 | |||
6858 | return { |
||
6859 | getHtmlElement: getHtmlElement |
||
6860 | }; |
||
6861 | })(); |
||
6862 | PDFJS.AnnotationUtils = AnnotationUtils; |
||
6863 | |||
6864 | |||
6865 | //#if (GENERIC || SINGLE_FILE) |
||
6866 | var SVG_DEFAULTS = { |
||
6867 | fontStyle: 'normal', |
||
6868 | fontWeight: 'normal', |
||
6869 | fillColor: '#000000' |
||
6870 | }; |
||
6871 | |||
6872 | var convertImgDataToPng = (function convertImgDataToPngClosure() { |
||
6873 | var PNG_HEADER = |
||
6874 | new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); |
||
6875 | |||
6876 | var CHUNK_WRAPPER_SIZE = 12; |
||
6877 | |||
6878 | var crcTable = new Int32Array(256); |
||
6879 | for (var i = 0; i < 256; i++) { |
||
6880 | var c = i; |
||
6881 | for (var h = 0; h < 8; h++) { |
||
6882 | if (c & 1) { |
||
6883 | c = 0xedB88320 ^ ((c >> 1) & 0x7fffffff); |
||
6884 | } else { |
||
6885 | c = (c >> 1) & 0x7fffffff; |
||
6886 | } |
||
6887 | } |
||
6888 | crcTable[i] = c; |
||
6889 | } |
||
6890 | |||
6891 | function crc32(data, start, end) { |
||
6892 | var crc = -1; |
||
6893 | for (var i = start; i < end; i++) { |
||
6894 | var a = (crc ^ data[i]) & 0xff; |
||
6895 | var b = crcTable[a]; |
||
6896 | crc = (crc >>> 8) ^ b; |
||
6897 | } |
||
6898 | return crc ^ -1; |
||
6899 | } |
||
6900 | |||
6901 | function writePngChunk(type, body, data, offset) { |
||
6902 | var p = offset; |
||
6903 | var len = body.length; |
||
6904 | |||
6905 | data[p] = len >> 24 & 0xff; |
||
6906 | data[p + 1] = len >> 16 & 0xff; |
||
6907 | data[p + 2] = len >> 8 & 0xff; |
||
6908 | data[p + 3] = len & 0xff; |
||
6909 | p += 4; |
||
6910 | |||
6911 | data[p] = type.charCodeAt(0) & 0xff; |
||
6912 | data[p + 1] = type.charCodeAt(1) & 0xff; |
||
6913 | data[p + 2] = type.charCodeAt(2) & 0xff; |
||
6914 | data[p + 3] = type.charCodeAt(3) & 0xff; |
||
6915 | p += 4; |
||
6916 | |||
6917 | data.set(body, p); |
||
6918 | p += body.length; |
||
6919 | |||
6920 | var crc = crc32(data, offset + 4, p); |
||
6921 | |||
6922 | data[p] = crc >> 24 & 0xff; |
||
6923 | data[p + 1] = crc >> 16 & 0xff; |
||
6924 | data[p + 2] = crc >> 8 & 0xff; |
||
6925 | data[p + 3] = crc & 0xff; |
||
6926 | } |
||
6927 | |||
6928 | function adler32(data, start, end) { |
||
6929 | var a = 1; |
||
6930 | var b = 0; |
||
6931 | for (var i = start; i < end; ++i) { |
||
6932 | a = (a + (data[i] & 0xff)) % 65521; |
||
6933 | b = (b + a) % 65521; |
||
6934 | } |
||
6935 | return (b << 16) | a; |
||
6936 | } |
||
6937 | |||
6938 | function encode(imgData, kind) { |
||
6939 | var width = imgData.width; |
||
6940 | var height = imgData.height; |
||
6941 | var bitDepth, colorType, lineSize; |
||
6942 | var bytes = imgData.data; |
||
6943 | |||
6944 | switch (kind) { |
||
6945 | case ImageKind.GRAYSCALE_1BPP: |
||
6946 | colorType = 0; |
||
6947 | bitDepth = 1; |
||
6948 | lineSize = (width + 7) >> 3; |
||
6949 | break; |
||
6950 | case ImageKind.RGB_24BPP: |
||
6951 | colorType = 2; |
||
6952 | bitDepth = 8; |
||
6953 | lineSize = width * 3; |
||
6954 | break; |
||
6955 | case ImageKind.RGBA_32BPP: |
||
6956 | colorType = 6; |
||
6957 | bitDepth = 8; |
||
6958 | lineSize = width * 4; |
||
6959 | break; |
||
6960 | default: |
||
6961 | throw new Error('invalid format'); |
||
6962 | } |
||
6963 | |||
6964 | // prefix every row with predictor 0 |
||
6965 | var literals = new Uint8Array((1 + lineSize) * height); |
||
6966 | var offsetLiterals = 0, offsetBytes = 0; |
||
6967 | var y, i; |
||
6968 | for (y = 0; y < height; ++y) { |
||
6969 | literals[offsetLiterals++] = 0; // no prediction |
||
6970 | literals.set(bytes.subarray(offsetBytes, offsetBytes + lineSize), |
||
6971 | offsetLiterals); |
||
6972 | offsetBytes += lineSize; |
||
6973 | offsetLiterals += lineSize; |
||
6974 | } |
||
6975 | |||
6976 | if (kind === ImageKind.GRAYSCALE_1BPP) { |
||
6977 | // inverting for B/W |
||
6978 | offsetLiterals = 0; |
||
6979 | for (y = 0; y < height; y++) { |
||
6980 | offsetLiterals++; // skipping predictor |
||
6981 | for (i = 0; i < lineSize; i++) { |
||
6982 | literals[offsetLiterals++] ^= 0xFF; |
||
6983 | } |
||
6984 | } |
||
6985 | } |
||
6986 | |||
6987 | var ihdr = new Uint8Array([ |
||
6988 | width >> 24 & 0xff, |
||
6989 | width >> 16 & 0xff, |
||
6990 | width >> 8 & 0xff, |
||
6991 | width & 0xff, |
||
6992 | height >> 24 & 0xff, |
||
6993 | height >> 16 & 0xff, |
||
6994 | height >> 8 & 0xff, |
||
6995 | height & 0xff, |
||
6996 | bitDepth, // bit depth |
||
6997 | colorType, // color type |
||
6998 | 0x00, // compression method |
||
6999 | 0x00, // filter method |
||
7000 | 0x00 // interlace method |
||
7001 | ]); |
||
7002 | |||
7003 | var len = literals.length; |
||
7004 | var maxBlockLength = 0xFFFF; |
||
7005 | |||
7006 | var deflateBlocks = Math.ceil(len / maxBlockLength); |
||
7007 | var idat = new Uint8Array(2 + len + deflateBlocks * 5 + 4); |
||
7008 | var pi = 0; |
||
7009 | idat[pi++] = 0x78; // compression method and flags |
||
7010 | idat[pi++] = 0x9c; // flags |
||
7011 | |||
7012 | var pos = 0; |
||
7013 | while (len > maxBlockLength) { |
||
7014 | // writing non-final DEFLATE blocks type 0 and length of 65535 |
||
7015 | idat[pi++] = 0x00; |
||
7016 | idat[pi++] = 0xff; |
||
7017 | idat[pi++] = 0xff; |
||
7018 | idat[pi++] = 0x00; |
||
7019 | idat[pi++] = 0x00; |
||
7020 | idat.set(literals.subarray(pos, pos + maxBlockLength), pi); |
||
7021 | pi += maxBlockLength; |
||
7022 | pos += maxBlockLength; |
||
7023 | len -= maxBlockLength; |
||
7024 | } |
||
7025 | |||
7026 | // writing non-final DEFLATE blocks type 0 |
||
7027 | idat[pi++] = 0x01; |
||
7028 | idat[pi++] = len & 0xff; |
||
7029 | idat[pi++] = len >> 8 & 0xff; |
||
7030 | idat[pi++] = (~len & 0xffff) & 0xff; |
||
7031 | idat[pi++] = (~len & 0xffff) >> 8 & 0xff; |
||
7032 | idat.set(literals.subarray(pos), pi); |
||
7033 | pi += literals.length - pos; |
||
7034 | |||
7035 | var adler = adler32(literals, 0, literals.length); // checksum |
||
7036 | idat[pi++] = adler >> 24 & 0xff; |
||
7037 | idat[pi++] = adler >> 16 & 0xff; |
||
7038 | idat[pi++] = adler >> 8 & 0xff; |
||
7039 | idat[pi++] = adler & 0xff; |
||
7040 | |||
7041 | // PNG will consists: header, IHDR+data, IDAT+data, and IEND. |
||
7042 | var pngLength = PNG_HEADER.length + (CHUNK_WRAPPER_SIZE * 3) + |
||
7043 | ihdr.length + idat.length; |
||
7044 | var data = new Uint8Array(pngLength); |
||
7045 | var offset = 0; |
||
7046 | data.set(PNG_HEADER, offset); |
||
7047 | offset += PNG_HEADER.length; |
||
7048 | writePngChunk('IHDR', ihdr, data, offset); |
||
7049 | offset += CHUNK_WRAPPER_SIZE + ihdr.length; |
||
7050 | writePngChunk('IDATA', idat, data, offset); |
||
7051 | offset += CHUNK_WRAPPER_SIZE + idat.length; |
||
7052 | writePngChunk('IEND', new Uint8Array(0), data, offset); |
||
7053 | |||
7054 | return PDFJS.createObjectURL(data, 'image/png'); |
||
7055 | } |
||
7056 | |||
7057 | return function convertImgDataToPng(imgData) { |
||
7058 | var kind = (imgData.kind === undefined ? |
||
7059 | ImageKind.GRAYSCALE_1BPP : imgData.kind); |
||
7060 | return encode(imgData, kind); |
||
7061 | }; |
||
7062 | })(); |
||
7063 | |||
7064 | var SVGExtraState = (function SVGExtraStateClosure() { |
||
7065 | function SVGExtraState() { |
||
7066 | this.fontSizeScale = 1; |
||
7067 | this.fontWeight = SVG_DEFAULTS.fontWeight; |
||
7068 | this.fontSize = 0; |
||
7069 | |||
7070 | this.textMatrix = IDENTITY_MATRIX; |
||
7071 | this.fontMatrix = FONT_IDENTITY_MATRIX; |
||
7072 | this.leading = 0; |
||
7073 | |||
7074 | // Current point (in user coordinates) |
||
7075 | this.x = 0; |
||
7076 | this.y = 0; |
||
7077 | |||
7078 | // Start of text line (in text coordinates) |
||
7079 | this.lineX = 0; |
||
7080 | this.lineY = 0; |
||
7081 | |||
7082 | // Character and word spacing |
||
7083 | this.charSpacing = 0; |
||
7084 | this.wordSpacing = 0; |
||
7085 | this.textHScale = 1; |
||
7086 | this.textRise = 0; |
||
7087 | |||
7088 | // Default foreground and background colors |
||
7089 | this.fillColor = SVG_DEFAULTS.fillColor; |
||
7090 | this.strokeColor = '#000000'; |
||
7091 | |||
7092 | this.fillAlpha = 1; |
||
7093 | this.strokeAlpha = 1; |
||
7094 | this.lineWidth = 1; |
||
7095 | this.lineJoin = ''; |
||
7096 | this.lineCap = ''; |
||
7097 | this.miterLimit = 0; |
||
7098 | |||
7099 | this.dashArray = []; |
||
7100 | this.dashPhase = 0; |
||
7101 | |||
7102 | this.dependencies = []; |
||
7103 | |||
7104 | // Clipping |
||
7105 | this.clipId = ''; |
||
7106 | this.pendingClip = false; |
||
7107 | |||
7108 | this.maskId = ''; |
||
7109 | } |
||
7110 | |||
7111 | SVGExtraState.prototype = { |
||
7112 | clone: function SVGExtraState_clone() { |
||
7113 | return Object.create(this); |
||
7114 | }, |
||
7115 | setCurrentPoint: function SVGExtraState_setCurrentPoint(x, y) { |
||
7116 | this.x = x; |
||
7117 | this.y = y; |
||
7118 | } |
||
7119 | }; |
||
7120 | return SVGExtraState; |
||
7121 | })(); |
||
7122 | |||
7123 | var SVGGraphics = (function SVGGraphicsClosure() { |
||
7124 | function createScratchSVG(width, height) { |
||
7125 | var NS = 'http://www.w3.org/2000/svg'; |
||
7126 | var svg = document.createElementNS(NS, 'svg:svg'); |
||
7127 | svg.setAttributeNS(null, 'version', '1.1'); |
||
7128 | svg.setAttributeNS(null, 'width', width + 'px'); |
||
7129 | svg.setAttributeNS(null, 'height', height + 'px'); |
||
7130 | svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height); |
||
7131 | return svg; |
||
7132 | } |
||
7133 | |||
7134 | function opListToTree(opList) { |
||
7135 | var opTree = []; |
||
7136 | var tmp = []; |
||
7137 | var opListLen = opList.length; |
||
7138 | |||
7139 | for (var x = 0; x < opListLen; x++) { |
||
7140 | if (opList[x].fn === 'save') { |
||
7141 | opTree.push({'fnId': 92, 'fn': 'group', 'items': []}); |
||
7142 | tmp.push(opTree); |
||
7143 | opTree = opTree[opTree.length - 1].items; |
||
7144 | continue; |
||
7145 | } |
||
7146 | |||
7147 | if(opList[x].fn === 'restore') { |
||
7148 | opTree = tmp.pop(); |
||
7149 | } else { |
||
7150 | opTree.push(opList[x]); |
||
7151 | } |
||
7152 | } |
||
7153 | return opTree; |
||
7154 | } |
||
7155 | |||
7156 | /** |
||
7157 | * Formats float number. |
||
7158 | * @param value {number} number to format. |
||
7159 | * @returns {string} |
||
7160 | */ |
||
7161 | function pf(value) { |
||
7162 | if (value === (value | 0)) { // integer number |
||
7163 | return value.toString(); |
||
7164 | } |
||
7165 | var s = value.toFixed(10); |
||
7166 | var i = s.length - 1; |
||
7167 | if (s[i] !== '0') { |
||
7168 | return s; |
||
7169 | } |
||
7170 | // removing trailing zeros |
||
7171 | do { |
||
7172 | i--; |
||
7173 | } while (s[i] === '0'); |
||
7174 | return s.substr(0, s[i] === '.' ? i : i + 1); |
||
7175 | } |
||
7176 | |||
7177 | /** |
||
7178 | * Formats transform matrix. The standard rotation, scale and translate |
||
7179 | * matrices are replaced by their shorter forms, and for identity matrix |
||
7180 | * returns empty string to save the memory. |
||
7181 | * @param m {Array} matrix to format. |
||
7182 | * @returns {string} |
||
7183 | */ |
||
7184 | function pm(m) { |
||
7185 | if (m[4] === 0 && m[5] === 0) { |
||
7186 | if (m[1] === 0 && m[2] === 0) { |
||
7187 | if (m[0] === 1 && m[3] === 1) { |
||
7188 | return ''; |
||
7189 | } |
||
7190 | return 'scale(' + pf(m[0]) + ' ' + pf(m[3]) + ')'; |
||
7191 | } |
||
7192 | if (m[0] === m[3] && m[1] === -m[2]) { |
||
7193 | var a = Math.acos(m[0]) * 180 / Math.PI; |
||
7194 | return 'rotate(' + pf(a) + ')'; |
||
7195 | } |
||
7196 | } else { |
||
7197 | if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1) { |
||
7198 | return 'translate(' + pf(m[4]) + ' ' + pf(m[5]) + ')'; |
||
7199 | } |
||
7200 | } |
||
7201 | return 'matrix(' + pf(m[0]) + ' ' + pf(m[1]) + ' ' + pf(m[2]) + ' ' + |
||
7202 | pf(m[3]) + ' ' + pf(m[4]) + ' ' + pf(m[5]) + ')'; |
||
7203 | } |
||
7204 | |||
7205 | function SVGGraphics(commonObjs, objs) { |
||
7206 | this.current = new SVGExtraState(); |
||
7207 | this.transformMatrix = IDENTITY_MATRIX; // Graphics state matrix |
||
7208 | this.transformStack = []; |
||
7209 | this.extraStack = []; |
||
7210 | this.commonObjs = commonObjs; |
||
7211 | this.objs = objs; |
||
7212 | this.pendingEOFill = false; |
||
7213 | |||
7214 | this.embedFonts = false; |
||
7215 | this.embeddedFonts = {}; |
||
7216 | this.cssStyle = null; |
||
7217 | } |
||
7218 | |||
7219 | var NS = 'http://www.w3.org/2000/svg'; |
||
7220 | var XML_NS = 'http://www.w3.org/XML/1998/namespace'; |
||
7221 | var XLINK_NS = 'http://www.w3.org/1999/xlink'; |
||
7222 | var LINE_CAP_STYLES = ['butt', 'round', 'square']; |
||
7223 | var LINE_JOIN_STYLES = ['miter', 'round', 'bevel']; |
||
7224 | var clipCount = 0; |
||
7225 | var maskCount = 0; |
||
7226 | |||
7227 | SVGGraphics.prototype = { |
||
7228 | save: function SVGGraphics_save() { |
||
7229 | this.transformStack.push(this.transformMatrix); |
||
7230 | var old = this.current; |
||
7231 | this.extraStack.push(old); |
||
7232 | this.current = old.clone(); |
||
7233 | }, |
||
7234 | |||
7235 | restore: function SVGGraphics_restore() { |
||
7236 | this.transformMatrix = this.transformStack.pop(); |
||
7237 | this.current = this.extraStack.pop(); |
||
7238 | |||
7239 | this.tgrp = document.createElementNS(NS, 'svg:g'); |
||
7240 | this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix)); |
||
7241 | this.pgrp.appendChild(this.tgrp); |
||
7242 | }, |
||
7243 | |||
7244 | group: function SVGGraphics_group(items) { |
||
7245 | this.save(); |
||
7246 | this.executeOpTree(items); |
||
7247 | this.restore(); |
||
7248 | }, |
||
7249 | |||
7250 | loadDependencies: function SVGGraphics_loadDependencies(operatorList) { |
||
7251 | var fnArray = operatorList.fnArray; |
||
7252 | var fnArrayLen = fnArray.length; |
||
7253 | var argsArray = operatorList.argsArray; |
||
7254 | |||
7255 | var self = this; |
||
7256 | for (var i = 0; i < fnArrayLen; i++) { |
||
7257 | if (OPS.dependency === fnArray[i]) { |
||
7258 | var deps = argsArray[i]; |
||
7259 | for (var n = 0, nn = deps.length; n < nn; n++) { |
||
7260 | var obj = deps[n]; |
||
7261 | var common = obj.substring(0, 2) === 'g_'; |
||
7262 | var promise; |
||
7263 | if (common) { |
||
7264 | promise = new Promise(function(resolve) { |
||
7265 | self.commonObjs.get(obj, resolve); |
||
7266 | }); |
||
7267 | } else { |
||
7268 | promise = new Promise(function(resolve) { |
||
7269 | self.objs.get(obj, resolve); |
||
7270 | }); |
||
7271 | } |
||
7272 | this.current.dependencies.push(promise); |
||
7273 | } |
||
7274 | } |
||
7275 | } |
||
7276 | return Promise.all(this.current.dependencies); |
||
7277 | }, |
||
7278 | |||
7279 | transform: function SVGGraphics_transform(a, b, c, d, e, f) { |
||
7280 | var transformMatrix = [a, b, c, d, e, f]; |
||
7281 | this.transformMatrix = PDFJS.Util.transform(this.transformMatrix, |
||
7282 | transformMatrix); |
||
7283 | |||
7284 | this.tgrp = document.createElementNS(NS, 'svg:g'); |
||
7285 | this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix)); |
||
7286 | }, |
||
7287 | |||
7288 | getSVG: function SVGGraphics_getSVG(operatorList, viewport) { |
||
7289 | this.svg = createScratchSVG(viewport.width, viewport.height); |
||
7290 | this.viewport = viewport; |
||
7291 | |||
7292 | return this.loadDependencies(operatorList).then(function () { |
||
7293 | this.transformMatrix = IDENTITY_MATRIX; |
||
7294 | this.pgrp = document.createElementNS(NS, 'svg:g'); // Parent group |
||
7295 | this.pgrp.setAttributeNS(null, 'transform', pm(viewport.transform)); |
||
7296 | this.tgrp = document.createElementNS(NS, 'svg:g'); // Transform group |
||
7297 | this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix)); |
||
7298 | this.defs = document.createElementNS(NS, 'svg:defs'); |
||
7299 | this.pgrp.appendChild(this.defs); |
||
7300 | this.pgrp.appendChild(this.tgrp); |
||
7301 | this.svg.appendChild(this.pgrp); |
||
7302 | var opTree = this.convertOpList(operatorList); |
||
7303 | this.executeOpTree(opTree); |
||
7304 | return this.svg; |
||
7305 | }.bind(this)); |
||
7306 | }, |
||
7307 | |||
7308 | convertOpList: function SVGGraphics_convertOpList(operatorList) { |
||
7309 | var argsArray = operatorList.argsArray; |
||
7310 | var fnArray = operatorList.fnArray; |
||
7311 | var fnArrayLen = fnArray.length; |
||
7312 | var REVOPS = []; |
||
7313 | var opList = []; |
||
7314 | |||
7315 | for (var op in OPS) { |
||
7316 | REVOPS[OPS[op]] = op; |
||
7317 | } |
||
7318 | |||
7319 | for (var x = 0; x < fnArrayLen; x++) { |
||
7320 | var fnId = fnArray[x]; |
||
7321 | opList.push({'fnId' : fnId, 'fn': REVOPS[fnId], 'args': argsArray[x]}); |
||
7322 | } |
||
7323 | return opListToTree(opList); |
||
7324 | }, |
||
7325 | |||
7326 | executeOpTree: function SVGGraphics_executeOpTree(opTree) { |
||
7327 | var opTreeLen = opTree.length; |
||
7328 | for(var x = 0; x < opTreeLen; x++) { |
||
7329 | var fn = opTree[x].fn; |
||
7330 | var fnId = opTree[x].fnId; |
||
7331 | var args = opTree[x].args; |
||
7332 | |||
7333 | switch (fnId | 0) { |
||
7334 | case OPS.beginText: |
||
7335 | this.beginText(); |
||
7336 | break; |
||
7337 | case OPS.setLeading: |
||
7338 | this.setLeading(args); |
||
7339 | break; |
||
7340 | case OPS.setLeadingMoveText: |
||
7341 | this.setLeadingMoveText(args[0], args[1]); |
||
7342 | break; |
||
7343 | case OPS.setFont: |
||
7344 | this.setFont(args); |
||
7345 | break; |
||
7346 | case OPS.showText: |
||
7347 | this.showText(args[0]); |
||
7348 | break; |
||
7349 | case OPS.showSpacedText: |
||
7350 | this.showText(args[0]); |
||
7351 | break; |
||
7352 | case OPS.endText: |
||
7353 | this.endText(); |
||
7354 | break; |
||
7355 | case OPS.moveText: |
||
7356 | this.moveText(args[0], args[1]); |
||
7357 | break; |
||
7358 | case OPS.setCharSpacing: |
||
7359 | this.setCharSpacing(args[0]); |
||
7360 | break; |
||
7361 | case OPS.setWordSpacing: |
||
7362 | this.setWordSpacing(args[0]); |
||
7363 | break; |
||
7364 | case OPS.setHScale: |
||
7365 | this.setHScale(args[0]); |
||
7366 | break; |
||
7367 | case OPS.setTextMatrix: |
||
7368 | this.setTextMatrix(args[0], args[1], args[2], |
||
7369 | args[3], args[4], args[5]); |
||
7370 | break; |
||
7371 | case OPS.setLineWidth: |
||
7372 | this.setLineWidth(args[0]); |
||
7373 | break; |
||
7374 | case OPS.setLineJoin: |
||
7375 | this.setLineJoin(args[0]); |
||
7376 | break; |
||
7377 | case OPS.setLineCap: |
||
7378 | this.setLineCap(args[0]); |
||
7379 | break; |
||
7380 | case OPS.setMiterLimit: |
||
7381 | this.setMiterLimit(args[0]); |
||
7382 | break; |
||
7383 | case OPS.setFillRGBColor: |
||
7384 | this.setFillRGBColor(args[0], args[1], args[2]); |
||
7385 | break; |
||
7386 | case OPS.setStrokeRGBColor: |
||
7387 | this.setStrokeRGBColor(args[0], args[1], args[2]); |
||
7388 | break; |
||
7389 | case OPS.setDash: |
||
7390 | this.setDash(args[0], args[1]); |
||
7391 | break; |
||
7392 | case OPS.setGState: |
||
7393 | this.setGState(args[0]); |
||
7394 | break; |
||
7395 | case OPS.fill: |
||
7396 | this.fill(); |
||
7397 | break; |
||
7398 | case OPS.eoFill: |
||
7399 | this.eoFill(); |
||
7400 | break; |
||
7401 | case OPS.stroke: |
||
7402 | this.stroke(); |
||
7403 | break; |
||
7404 | case OPS.fillStroke: |
||
7405 | this.fillStroke(); |
||
7406 | break; |
||
7407 | case OPS.eoFillStroke: |
||
7408 | this.eoFillStroke(); |
||
7409 | break; |
||
7410 | case OPS.clip: |
||
7411 | this.clip('nonzero'); |
||
7412 | break; |
||
7413 | case OPS.eoClip: |
||
7414 | this.clip('evenodd'); |
||
7415 | break; |
||
7416 | case OPS.paintSolidColorImageMask: |
||
7417 | this.paintSolidColorImageMask(); |
||
7418 | break; |
||
7419 | case OPS.paintJpegXObject: |
||
7420 | this.paintJpegXObject(args[0], args[1], args[2]); |
||
7421 | break; |
||
7422 | case OPS.paintImageXObject: |
||
7423 | this.paintImageXObject(args[0]); |
||
7424 | break; |
||
7425 | case OPS.paintInlineImageXObject: |
||
7426 | this.paintInlineImageXObject(args[0]); |
||
7427 | break; |
||
7428 | case OPS.paintImageMaskXObject: |
||
7429 | this.paintImageMaskXObject(args[0]); |
||
7430 | break; |
||
7431 | case OPS.paintFormXObjectBegin: |
||
7432 | this.paintFormXObjectBegin(args[0], args[1]); |
||
7433 | break; |
||
7434 | case OPS.paintFormXObjectEnd: |
||
7435 | this.paintFormXObjectEnd(); |
||
7436 | break; |
||
7437 | case OPS.closePath: |
||
7438 | this.closePath(); |
||
7439 | break; |
||
7440 | case OPS.closeStroke: |
||
7441 | this.closeStroke(); |
||
7442 | break; |
||
7443 | case OPS.closeFillStroke: |
||
7444 | this.closeFillStroke(); |
||
7445 | break; |
||
7446 | case OPS.nextLine: |
||
7447 | this.nextLine(); |
||
7448 | break; |
||
7449 | case OPS.transform: |
||
7450 | this.transform(args[0], args[1], args[2], args[3], |
||
7451 | args[4], args[5]); |
||
7452 | break; |
||
7453 | case OPS.constructPath: |
||
7454 | this.constructPath(args[0], args[1]); |
||
7455 | break; |
||
7456 | case OPS.endPath: |
||
7457 | this.endPath(); |
||
7458 | break; |
||
7459 | case 92: |
||
7460 | this.group(opTree[x].items); |
||
7461 | break; |
||
7462 | default: |
||
7463 | warn('Unimplemented method '+ fn); |
||
7464 | break; |
||
7465 | } |
||
7466 | } |
||
7467 | }, |
||
7468 | |||
7469 | setWordSpacing: function SVGGraphics_setWordSpacing(wordSpacing) { |
||
7470 | this.current.wordSpacing = wordSpacing; |
||
7471 | }, |
||
7472 | |||
7473 | setCharSpacing: function SVGGraphics_setCharSpacing(charSpacing) { |
||
7474 | this.current.charSpacing = charSpacing; |
||
7475 | }, |
||
7476 | |||
7477 | nextLine: function SVGGraphics_nextLine() { |
||
7478 | this.moveText(0, this.current.leading); |
||
7479 | }, |
||
7480 | |||
7481 | setTextMatrix: function SVGGraphics_setTextMatrix(a, b, c, d, e, f) { |
||
7482 | var current = this.current; |
||
7483 | this.current.textMatrix = this.current.lineMatrix = [a, b, c, d, e, f]; |
||
7484 | |||
7485 | this.current.x = this.current.lineX = 0; |
||
7486 | this.current.y = this.current.lineY = 0; |
||
7487 | |||
7488 | current.xcoords = []; |
||
7489 | current.tspan = document.createElementNS(NS, 'svg:tspan'); |
||
7490 | current.tspan.setAttributeNS(null, 'font-family', current.fontFamily); |
||
7491 | current.tspan.setAttributeNS(null, 'font-size', |
||
7492 | pf(current.fontSize) + 'px'); |
||
7493 | current.tspan.setAttributeNS(null, 'y', pf(-current.y)); |
||
7494 | |||
7495 | current.txtElement = document.createElementNS(NS, 'svg:text'); |
||
7496 | current.txtElement.appendChild(current.tspan); |
||
7497 | }, |
||
7498 | |||
7499 | beginText: function SVGGraphics_beginText() { |
||
7500 | this.current.x = this.current.lineX = 0; |
||
7501 | this.current.y = this.current.lineY = 0; |
||
7502 | this.current.textMatrix = IDENTITY_MATRIX; |
||
7503 | this.current.lineMatrix = IDENTITY_MATRIX; |
||
7504 | this.current.tspan = document.createElementNS(NS, 'svg:tspan'); |
||
7505 | this.current.txtElement = document.createElementNS(NS, 'svg:text'); |
||
7506 | this.current.txtgrp = document.createElementNS(NS, 'svg:g'); |
||
7507 | this.current.xcoords = []; |
||
7508 | }, |
||
7509 | |||
7510 | moveText: function SVGGraphics_moveText(x, y) { |
||
7511 | var current = this.current; |
||
7512 | this.current.x = this.current.lineX += x; |
||
7513 | this.current.y = this.current.lineY += y; |
||
7514 | |||
7515 | current.xcoords = []; |
||
7516 | current.tspan = document.createElementNS(NS, 'svg:tspan'); |
||
7517 | current.tspan.setAttributeNS(null, 'font-family', current.fontFamily); |
||
7518 | current.tspan.setAttributeNS(null, 'font-size', |
||
7519 | pf(current.fontSize) + 'px'); |
||
7520 | current.tspan.setAttributeNS(null, 'y', pf(-current.y)); |
||
7521 | }, |
||
7522 | |||
7523 | showText: function SVGGraphics_showText(glyphs) { |
||
7524 | var current = this.current; |
||
7525 | var font = current.font; |
||
7526 | var fontSize = current.fontSize; |
||
7527 | |||
7528 | if (fontSize === 0) { |
||
7529 | return; |
||
7530 | } |
||
7531 | |||
7532 | var charSpacing = current.charSpacing; |
||
7533 | var wordSpacing = current.wordSpacing; |
||
7534 | var fontDirection = current.fontDirection; |
||
7535 | var textHScale = current.textHScale * fontDirection; |
||
7536 | var glyphsLength = glyphs.length; |
||
7537 | var vertical = font.vertical; |
||
7538 | var widthAdvanceScale = fontSize * current.fontMatrix[0]; |
||
7539 | |||
7540 | var x = 0, i; |
||
7541 | for (i = 0; i < glyphsLength; ++i) { |
||
7542 | var glyph = glyphs[i]; |
||
7543 | if (glyph === null) { |
||
7544 | // word break |
||
7545 | x += fontDirection * wordSpacing; |
||
7546 | continue; |
||
7547 | } else if (isNum(glyph)) { |
||
7548 | x += -glyph * fontSize * 0.001; |
||
7549 | continue; |
||
7550 | } |
||
7551 | current.xcoords.push(current.x + x * textHScale); |
||
7552 | |||
7553 | var width = glyph.width; |
||
7554 | var character = glyph.fontChar; |
||
7555 | var charWidth = width * widthAdvanceScale + charSpacing * fontDirection; |
||
7556 | x += charWidth; |
||
7557 | |||
7558 | current.tspan.textContent += character; |
||
7559 | } |
||
7560 | if (vertical) { |
||
7561 | current.y -= x * textHScale; |
||
7562 | } else { |
||
7563 | current.x += x * textHScale; |
||
7564 | } |
||
7565 | |||
7566 | current.tspan.setAttributeNS(null, 'x', |
||
7567 | current.xcoords.map(pf).join(' ')); |
||
7568 | current.tspan.setAttributeNS(null, 'y', pf(-current.y)); |
||
7569 | current.tspan.setAttributeNS(null, 'font-family', current.fontFamily); |
||
7570 | current.tspan.setAttributeNS(null, 'font-size', |
||
7571 | pf(current.fontSize) + 'px'); |
||
7572 | if (current.fontStyle !== SVG_DEFAULTS.fontStyle) { |
||
7573 | current.tspan.setAttributeNS(null, 'font-style', current.fontStyle); |
||
7574 | } |
||
7575 | if (current.fontWeight !== SVG_DEFAULTS.fontWeight) { |
||
7576 | current.tspan.setAttributeNS(null, 'font-weight', current.fontWeight); |
||
7577 | } |
||
7578 | if (current.fillColor !== SVG_DEFAULTS.fillColor) { |
||
7579 | current.tspan.setAttributeNS(null, 'fill', current.fillColor); |
||
7580 | } |
||
7581 | |||
7582 | current.txtElement.setAttributeNS(null, 'transform', |
||
7583 | pm(current.textMatrix) + |
||
7584 | ' scale(1, -1)' ); |
||
7585 | current.txtElement.setAttributeNS(XML_NS, 'xml:space', 'preserve'); |
||
7586 | current.txtElement.appendChild(current.tspan); |
||
7587 | current.txtgrp.appendChild(current.txtElement); |
||
7588 | |||
7589 | this.tgrp.appendChild(current.txtElement); |
||
7590 | |||
7591 | }, |
||
7592 | |||
7593 | setLeadingMoveText: function SVGGraphics_setLeadingMoveText(x, y) { |
||
7594 | this.setLeading(-y); |
||
7595 | this.moveText(x, y); |
||
7596 | }, |
||
7597 | |||
7598 | addFontStyle: function SVGGraphics_addFontStyle(fontObj) { |
||
7599 | if (!this.cssStyle) { |
||
7600 | this.cssStyle = document.createElementNS(NS, 'svg:style'); |
||
7601 | this.cssStyle.setAttributeNS(null, 'type', 'text/css'); |
||
7602 | this.defs.appendChild(this.cssStyle); |
||
7603 | } |
||
7604 | |||
7605 | var url = PDFJS.createObjectURL(fontObj.data, fontObj.mimetype); |
||
7606 | this.cssStyle.textContent += |
||
7607 | '@font-face { font-family: "' + fontObj.loadedName + '";' + |
||
7608 | ' src: url(' + url + '); }\n'; |
||
7609 | }, |
||
7610 | |||
7611 | setFont: function SVGGraphics_setFont(details) { |
||
7612 | var current = this.current; |
||
7613 | var fontObj = this.commonObjs.get(details[0]); |
||
7614 | var size = details[1]; |
||
7615 | this.current.font = fontObj; |
||
7616 | |||
7617 | if (this.embedFonts && fontObj.data && |
||
7618 | !this.embeddedFonts[fontObj.loadedName]) { |
||
7619 | this.addFontStyle(fontObj); |
||
7620 | this.embeddedFonts[fontObj.loadedName] = fontObj; |
||
7621 | } |
||
7622 | |||
7623 | current.fontMatrix = (fontObj.fontMatrix ? |
||
7624 | fontObj.fontMatrix : FONT_IDENTITY_MATRIX); |
||
7625 | |||
7626 | var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : |
||
7627 | (fontObj.bold ? 'bold' : 'normal'); |
||
7628 | var italic = fontObj.italic ? 'italic' : 'normal'; |
||
7629 | |||
7630 | if (size < 0) { |
||
7631 | size = -size; |
||
7632 | current.fontDirection = -1; |
||
7633 | } else { |
||
7634 | current.fontDirection = 1; |
||
7635 | } |
||
7636 | current.fontSize = size; |
||
7637 | current.fontFamily = fontObj.loadedName; |
||
7638 | current.fontWeight = bold; |
||
7639 | current.fontStyle = italic; |
||
7640 | |||
7641 | current.tspan = document.createElementNS(NS, 'svg:tspan'); |
||
7642 | current.tspan.setAttributeNS(null, 'y', pf(-current.y)); |
||
7643 | current.xcoords = []; |
||
7644 | }, |
||
7645 | |||
7646 | endText: function SVGGraphics_endText() { |
||
7647 | if (this.current.pendingClip) { |
||
7648 | this.cgrp.appendChild(this.tgrp); |
||
7649 | this.pgrp.appendChild(this.cgrp); |
||
7650 | } else { |
||
7651 | this.pgrp.appendChild(this.tgrp); |
||
7652 | } |
||
7653 | this.tgrp = document.createElementNS(NS, 'svg:g'); |
||
7654 | this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix)); |
||
7655 | }, |
||
7656 | |||
7657 | // Path properties |
||
7658 | setLineWidth: function SVGGraphics_setLineWidth(width) { |
||
7659 | this.current.lineWidth = width; |
||
7660 | }, |
||
7661 | setLineCap: function SVGGraphics_setLineCap(style) { |
||
7662 | this.current.lineCap = LINE_CAP_STYLES[style]; |
||
7663 | }, |
||
7664 | setLineJoin: function SVGGraphics_setLineJoin(style) { |
||
7665 | this.current.lineJoin = LINE_JOIN_STYLES[style]; |
||
7666 | }, |
||
7667 | setMiterLimit: function SVGGraphics_setMiterLimit(limit) { |
||
7668 | this.current.miterLimit = limit; |
||
7669 | }, |
||
7670 | setStrokeRGBColor: function SVGGraphics_setStrokeRGBColor(r, g, b) { |
||
7671 | var color = Util.makeCssRgb(r, g, b); |
||
7672 | this.current.strokeColor = color; |
||
7673 | }, |
||
7674 | setFillRGBColor: function SVGGraphics_setFillRGBColor(r, g, b) { |
||
7675 | var color = Util.makeCssRgb(r, g, b); |
||
7676 | this.current.fillColor = color; |
||
7677 | this.current.tspan = document.createElementNS(NS, 'svg:tspan'); |
||
7678 | this.current.xcoords = []; |
||
7679 | }, |
||
7680 | setDash: function SVGGraphics_setDash(dashArray, dashPhase) { |
||
7681 | this.current.dashArray = dashArray; |
||
7682 | this.current.dashPhase = dashPhase; |
||
7683 | }, |
||
7684 | |||
7685 | View Code Duplication | constructPath: function SVGGraphics_constructPath(ops, args) { |
|
7686 | var current = this.current; |
||
7687 | var x = current.x, y = current.y; |
||
7688 | current.path = document.createElementNS(NS, 'svg:path'); |
||
7689 | var d = []; |
||
7690 | var opLength = ops.length; |
||
7691 | |||
7692 | for (var i = 0, j = 0; i < opLength; i++) { |
||
7693 | switch (ops[i] | 0) { |
||
7694 | case OPS.rectangle: |
||
7695 | x = args[j++]; |
||
7696 | y = args[j++]; |
||
7697 | var width = args[j++]; |
||
7698 | var height = args[j++]; |
||
7699 | var xw = x + width; |
||
7700 | var yh = y + height; |
||
7701 | d.push('M', pf(x), pf(y), 'L', pf(xw) , pf(y), 'L', pf(xw), pf(yh), |
||
7702 | 'L', pf(x), pf(yh), 'Z'); |
||
7703 | break; |
||
7704 | case OPS.moveTo: |
||
7705 | x = args[j++]; |
||
7706 | y = args[j++]; |
||
7707 | d.push('M', pf(x), pf(y)); |
||
7708 | break; |
||
7709 | case OPS.lineTo: |
||
7710 | x = args[j++]; |
||
7711 | y = args[j++]; |
||
7712 | d.push('L', pf(x) , pf(y)); |
||
7713 | break; |
||
7714 | case OPS.curveTo: |
||
7715 | x = args[j + 4]; |
||
7716 | y = args[j + 5]; |
||
7717 | d.push('C', pf(args[j]), pf(args[j + 1]), pf(args[j + 2]), |
||
7718 | pf(args[j + 3]), pf(x), pf(y)); |
||
7719 | j += 6; |
||
7720 | break; |
||
7721 | case OPS.curveTo2: |
||
7722 | x = args[j + 2]; |
||
7723 | y = args[j + 3]; |
||
7724 | d.push('C', pf(x), pf(y), pf(args[j]), pf(args[j + 1]), |
||
7725 | pf(args[j + 2]), pf(args[j + 3])); |
||
7726 | j += 4; |
||
7727 | break; |
||
7728 | case OPS.curveTo3: |
||
7729 | x = args[j + 2]; |
||
7730 | y = args[j + 3]; |
||
7731 | d.push('C', pf(args[j]), pf(args[j + 1]), pf(x), pf(y), |
||
7732 | pf(x), pf(y)); |
||
7733 | j += 4; |
||
7734 | break; |
||
7735 | case OPS.closePath: |
||
7736 | d.push('Z'); |
||
7737 | break; |
||
7738 | } |
||
7739 | } |
||
7740 | current.path.setAttributeNS(null, 'd', d.join(' ')); |
||
7741 | current.path.setAttributeNS(null, 'stroke-miterlimit', |
||
7742 | pf(current.miterLimit)); |
||
7743 | current.path.setAttributeNS(null, 'stroke-linecap', current.lineCap); |
||
7744 | current.path.setAttributeNS(null, 'stroke-linejoin', current.lineJoin); |
||
7745 | current.path.setAttributeNS(null, 'stroke-width', |
||
7746 | pf(current.lineWidth) + 'px'); |
||
7747 | current.path.setAttributeNS(null, 'stroke-dasharray', |
||
7748 | current.dashArray.map(pf).join(' ')); |
||
7749 | current.path.setAttributeNS(null, 'stroke-dashoffset', |
||
7750 | pf(current.dashPhase) + 'px'); |
||
7751 | current.path.setAttributeNS(null, 'fill', 'none'); |
||
7752 | |||
7753 | this.tgrp.appendChild(current.path); |
||
7754 | if (current.pendingClip) { |
||
7755 | this.cgrp.appendChild(this.tgrp); |
||
7756 | this.pgrp.appendChild(this.cgrp); |
||
7757 | } else { |
||
7758 | this.pgrp.appendChild(this.tgrp); |
||
7759 | } |
||
7760 | // Saving a reference in current.element so that it can be addressed |
||
7761 | // in 'fill' and 'stroke' |
||
7762 | current.element = current.path; |
||
7763 | current.setCurrentPoint(x, y); |
||
7764 | }, |
||
7765 | |||
7766 | endPath: function SVGGraphics_endPath() { |
||
7767 | var current = this.current; |
||
7768 | if (current.pendingClip) { |
||
7769 | this.cgrp.appendChild(this.tgrp); |
||
7770 | this.pgrp.appendChild(this.cgrp); |
||
7771 | } else { |
||
7772 | this.pgrp.appendChild(this.tgrp); |
||
7773 | } |
||
7774 | this.tgrp = document.createElementNS(NS, 'svg:g'); |
||
7775 | this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix)); |
||
7776 | }, |
||
7777 | |||
7778 | clip: function SVGGraphics_clip(type) { |
||
7779 | var current = this.current; |
||
7780 | // Add current path to clipping path |
||
7781 | current.clipId = 'clippath' + clipCount; |
||
7782 | clipCount++; |
||
7783 | this.clippath = document.createElementNS(NS, 'svg:clipPath'); |
||
7784 | this.clippath.setAttributeNS(null, 'id', current.clipId); |
||
7785 | var clipElement = current.element.cloneNode(); |
||
7786 | if (type === 'evenodd') { |
||
7787 | clipElement.setAttributeNS(null, 'clip-rule', 'evenodd'); |
||
7788 | } else { |
||
7789 | clipElement.setAttributeNS(null, 'clip-rule', 'nonzero'); |
||
7790 | } |
||
7791 | this.clippath.setAttributeNS(null, 'transform', pm(this.transformMatrix)); |
||
7792 | this.clippath.appendChild(clipElement); |
||
7793 | this.defs.appendChild(this.clippath); |
||
7794 | |||
7795 | // Create a new group with that attribute |
||
7796 | current.pendingClip = true; |
||
7797 | this.cgrp = document.createElementNS(NS, 'svg:g'); |
||
7798 | this.cgrp.setAttributeNS(null, 'clip-path', |
||
7799 | 'url(#' + current.clipId + ')'); |
||
7800 | this.pgrp.appendChild(this.cgrp); |
||
7801 | }, |
||
7802 | |||
7803 | closePath: function SVGGraphics_closePath() { |
||
7804 | var current = this.current; |
||
7805 | var d = current.path.getAttributeNS(null, 'd'); |
||
7806 | d += 'Z'; |
||
7807 | current.path.setAttributeNS(null, 'd', d); |
||
7808 | }, |
||
7809 | |||
7810 | setLeading: function SVGGraphics_setLeading(leading) { |
||
7811 | this.current.leading = -leading; |
||
7812 | }, |
||
7813 | |||
7814 | setTextRise: function SVGGraphics_setTextRise(textRise) { |
||
7815 | this.current.textRise = textRise; |
||
7816 | }, |
||
7817 | |||
7818 | setHScale: function SVGGraphics_setHScale(scale) { |
||
7819 | this.current.textHScale = scale / 100; |
||
7820 | }, |
||
7821 | |||
7822 | setGState: function SVGGraphics_setGState(states) { |
||
7823 | for (var i = 0, ii = states.length; i < ii; i++) { |
||
7824 | var state = states[i]; |
||
7825 | var key = state[0]; |
||
7826 | var value = state[1]; |
||
7827 | |||
7828 | switch (key) { |
||
7829 | case 'LW': |
||
7830 | this.setLineWidth(value); |
||
7831 | break; |
||
7832 | case 'LC': |
||
7833 | this.setLineCap(value); |
||
7834 | break; |
||
7835 | case 'LJ': |
||
7836 | this.setLineJoin(value); |
||
7837 | break; |
||
7838 | case 'ML': |
||
7839 | this.setMiterLimit(value); |
||
7840 | break; |
||
7841 | case 'D': |
||
7842 | this.setDash(value[0], value[1]); |
||
7843 | break; |
||
7844 | case 'RI': |
||
7845 | break; |
||
7846 | case 'FL': |
||
7847 | break; |
||
7848 | case 'Font': |
||
7849 | this.setFont(value); |
||
7850 | break; |
||
7851 | case 'CA': |
||
7852 | break; |
||
7853 | case 'ca': |
||
7854 | break; |
||
7855 | case 'BM': |
||
7856 | break; |
||
7857 | case 'SMask': |
||
7858 | break; |
||
7859 | } |
||
7860 | } |
||
7861 | }, |
||
7862 | |||
7863 | fill: function SVGGraphics_fill() { |
||
7864 | var current = this.current; |
||
7865 | current.element.setAttributeNS(null, 'fill', current.fillColor); |
||
7866 | }, |
||
7867 | |||
7868 | stroke: function SVGGraphics_stroke() { |
||
7869 | var current = this.current; |
||
7870 | current.element.setAttributeNS(null, 'stroke', current.strokeColor); |
||
7871 | current.element.setAttributeNS(null, 'fill', 'none'); |
||
7872 | }, |
||
7873 | |||
7874 | eoFill: function SVGGraphics_eoFill() { |
||
7875 | var current = this.current; |
||
7876 | current.element.setAttributeNS(null, 'fill', current.fillColor); |
||
7877 | current.element.setAttributeNS(null, 'fill-rule', 'evenodd'); |
||
7878 | }, |
||
7879 | |||
7880 | fillStroke: function SVGGraphics_fillStroke() { |
||
7881 | // Order is important since stroke wants fill to be none. |
||
7882 | // First stroke, then if fill needed, it will be overwritten. |
||
7883 | this.stroke(); |
||
7884 | this.fill(); |
||
7885 | }, |
||
7886 | |||
7887 | eoFillStroke: function SVGGraphics_eoFillStroke() { |
||
7888 | this.current.element.setAttributeNS(null, 'fill-rule', 'evenodd'); |
||
7889 | this.fillStroke(); |
||
7890 | }, |
||
7891 | |||
7892 | closeStroke: function SVGGraphics_closeStroke() { |
||
7893 | this.closePath(); |
||
7894 | this.stroke(); |
||
7895 | }, |
||
7896 | |||
7897 | closeFillStroke: function SVGGraphics_closeFillStroke() { |
||
7898 | this.closePath(); |
||
7899 | this.fillStroke(); |
||
7900 | }, |
||
7901 | |||
7902 | paintSolidColorImageMask: |
||
7903 | function SVGGraphics_paintSolidColorImageMask() { |
||
7904 | var current = this.current; |
||
7905 | var rect = document.createElementNS(NS, 'svg:rect'); |
||
7906 | rect.setAttributeNS(null, 'x', '0'); |
||
7907 | rect.setAttributeNS(null, 'y', '0'); |
||
7908 | rect.setAttributeNS(null, 'width', '1px'); |
||
7909 | rect.setAttributeNS(null, 'height', '1px'); |
||
7910 | rect.setAttributeNS(null, 'fill', current.fillColor); |
||
7911 | this.tgrp.appendChild(rect); |
||
7912 | }, |
||
7913 | |||
7914 | paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) { |
||
7915 | var current = this.current; |
||
7916 | var imgObj = this.objs.get(objId); |
||
7917 | var imgEl = document.createElementNS(NS, 'svg:image'); |
||
7918 | imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src); |
||
7919 | imgEl.setAttributeNS(null, 'width', imgObj.width + 'px'); |
||
7920 | imgEl.setAttributeNS(null, 'height', imgObj.height + 'px'); |
||
7921 | imgEl.setAttributeNS(null, 'x', '0'); |
||
7922 | imgEl.setAttributeNS(null, 'y', pf(-h)); |
||
7923 | imgEl.setAttributeNS(null, 'transform', |
||
7924 | 'scale(' + pf(1 / w) + ' ' + pf(-1 / h) + ')'); |
||
7925 | |||
7926 | this.tgrp.appendChild(imgEl); |
||
7927 | if (current.pendingClip) { |
||
7928 | this.cgrp.appendChild(this.tgrp); |
||
7929 | this.pgrp.appendChild(this.cgrp); |
||
7930 | } else { |
||
7931 | this.pgrp.appendChild(this.tgrp); |
||
7932 | } |
||
7933 | }, |
||
7934 | |||
7935 | paintImageXObject: function SVGGraphics_paintImageXObject(objId) { |
||
7936 | var imgData = this.objs.get(objId); |
||
7937 | if (!imgData) { |
||
7938 | warn('Dependent image isn\'t ready yet'); |
||
7939 | return; |
||
7940 | } |
||
7941 | this.paintInlineImageXObject(imgData); |
||
7942 | }, |
||
7943 | |||
7944 | paintInlineImageXObject: |
||
7945 | function SVGGraphics_paintInlineImageXObject(imgData, mask) { |
||
7946 | var current = this.current; |
||
7947 | var width = imgData.width; |
||
7948 | var height = imgData.height; |
||
7949 | |||
7950 | var imgSrc = convertImgDataToPng(imgData); |
||
7951 | var cliprect = document.createElementNS(NS, 'svg:rect'); |
||
7952 | cliprect.setAttributeNS(null, 'x', '0'); |
||
7953 | cliprect.setAttributeNS(null, 'y', '0'); |
||
7954 | cliprect.setAttributeNS(null, 'width', pf(width)); |
||
7955 | cliprect.setAttributeNS(null, 'height', pf(height)); |
||
7956 | current.element = cliprect; |
||
7957 | this.clip('nonzero'); |
||
7958 | var imgEl = document.createElementNS(NS, 'svg:image'); |
||
7959 | imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgSrc); |
||
7960 | imgEl.setAttributeNS(null, 'x', '0'); |
||
7961 | imgEl.setAttributeNS(null, 'y', pf(-height)); |
||
7962 | imgEl.setAttributeNS(null, 'width', pf(width) + 'px'); |
||
7963 | imgEl.setAttributeNS(null, 'height', pf(height) + 'px'); |
||
7964 | imgEl.setAttributeNS(null, 'transform', |
||
7965 | 'scale(' + pf(1 / width) + ' ' + |
||
7966 | pf(-1 / height) + ')'); |
||
7967 | if (mask) { |
||
7968 | mask.appendChild(imgEl); |
||
7969 | } else { |
||
7970 | this.tgrp.appendChild(imgEl); |
||
7971 | } |
||
7972 | if (current.pendingClip) { |
||
7973 | this.cgrp.appendChild(this.tgrp); |
||
7974 | this.pgrp.appendChild(this.cgrp); |
||
7975 | } else { |
||
7976 | this.pgrp.appendChild(this.tgrp); |
||
7977 | } |
||
7978 | }, |
||
7979 | |||
7980 | paintImageMaskXObject: |
||
7981 | function SVGGraphics_paintImageMaskXObject(imgData) { |
||
7982 | var current = this.current; |
||
7983 | var width = imgData.width; |
||
7984 | var height = imgData.height; |
||
7985 | var fillColor = current.fillColor; |
||
7986 | |||
7987 | current.maskId = 'mask' + maskCount++; |
||
7988 | var mask = document.createElementNS(NS, 'svg:mask'); |
||
7989 | mask.setAttributeNS(null, 'id', current.maskId); |
||
7990 | |||
7991 | var rect = document.createElementNS(NS, 'svg:rect'); |
||
7992 | rect.setAttributeNS(null, 'x', '0'); |
||
7993 | rect.setAttributeNS(null, 'y', '0'); |
||
7994 | rect.setAttributeNS(null, 'width', pf(width)); |
||
7995 | rect.setAttributeNS(null, 'height', pf(height)); |
||
7996 | rect.setAttributeNS(null, 'fill', fillColor); |
||
7997 | rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')'); |
||
7998 | this.defs.appendChild(mask); |
||
7999 | this.tgrp.appendChild(rect); |
||
8000 | |||
8001 | this.paintInlineImageXObject(imgData, mask); |
||
8002 | }, |
||
8003 | |||
8004 | paintFormXObjectBegin: |
||
8005 | function SVGGraphics_paintFormXObjectBegin(matrix, bbox) { |
||
8006 | this.save(); |
||
8007 | |||
8008 | if (isArray(matrix) && matrix.length === 6) { |
||
8009 | this.transform(matrix[0], matrix[1], matrix[2], |
||
8010 | matrix[3], matrix[4], matrix[5]); |
||
8011 | } |
||
8012 | |||
8013 | if (isArray(bbox) && bbox.length === 4) { |
||
8014 | var width = bbox[2] - bbox[0]; |
||
8015 | var height = bbox[3] - bbox[1]; |
||
8016 | |||
8017 | var cliprect = document.createElementNS(NS, 'svg:rect'); |
||
8018 | cliprect.setAttributeNS(null, 'x', bbox[0]); |
||
8019 | cliprect.setAttributeNS(null, 'y', bbox[1]); |
||
8020 | cliprect.setAttributeNS(null, 'width', pf(width)); |
||
8021 | cliprect.setAttributeNS(null, 'height', pf(height)); |
||
8022 | this.current.element = cliprect; |
||
8023 | this.clip('nonzero'); |
||
8024 | this.endPath(); |
||
8025 | } |
||
8026 | }, |
||
8027 | |||
8028 | paintFormXObjectEnd: |
||
8029 | function SVGGraphics_paintFormXObjectEnd() { |
||
8030 | this.restore(); |
||
8031 | } |
||
8032 | }; |
||
8033 | return SVGGraphics; |
||
8034 | })(); |
||
8035 | |||
8036 | PDFJS.SVGGraphics = SVGGraphics; |
||
8037 | //#endif |
||
8038 | |||
8039 | |||
8040 | }).call((typeof window === 'undefined') ? this : window); |
||
8041 | |||
8042 | if (!PDFJS.workerSrc && typeof document !== 'undefined') { |
||
8043 | // workerSrc is not set -- using last script url to define default location |
||
8044 | PDFJS.workerSrc = (function () { |
||
8045 | 'use strict'; |
||
8046 | var scriptTagContainer = document.body || |
||
8047 | document.getElementsByTagName('head')[0]; |
||
8048 | var pdfjsSrc = scriptTagContainer.lastChild.src; |
||
8049 | return pdfjsSrc && pdfjsSrc.replace(/\.js$/i, '.worker.js'); |
||
8050 | })(); |
||
8051 | } |
||
8052 | |||
8053 |