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