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