Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/** @preserve jsPDF 0.9.0rc2 ( ${buildDate} ${commitID} )
2
Copyright (c) 2010-2012 James Hall, james@snapshotmedia.co.uk, https://github.com/MrRio/jsPDF
3
Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
4
MIT license.
5
*/
6
 
7
/*
8
 * Permission is hereby granted, free of charge, to any person obtaining
9
 * a copy of this software and associated documentation files (the
10
 * "Software"), to deal in the Software without restriction, including
11
 * without limitation the rights to use, copy, modify, merge, publish,
12
 * distribute, sublicense, and/or sell copies of the Software, and to
13
 * permit persons to whom the Software is furnished to do so, subject to
14
 * the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be
17
 * included in all copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
 * ====================================================================
27
 */
28
 
29
 
30
/**
31
Creates new jsPDF document object instance
32
@class
33
@param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
34
@param unit Measurement unit to be used when coordinates are specified. One of "pt" (points), "mm" (Default), "cm", "in"
35
@param format One of 'a0', 'a1', 'a2', 'a3', 'a4' (Default) etc to 'a10', 'b0' to 'b10', 'c0' to 'c10', 'letter', 'government-letter', 'legal', 'junior-legal', 'ledger' or 'tabloid'
36
@returns {jsPDF}
37
@name jsPDF
38
*/
39
var jsPDF = (function () {
40
    'use strict';
41
    /*jslint browser:true, plusplus: true, bitwise: true, nomen: true */
42
    /*global document: false, btoa, atob, zpipe, Uint8Array, ArrayBuffer, Blob, saveAs, adler32cs, Deflater */
43
 
44
// this will run on <=IE9, possibly some niche browsers
45
// new webkit-based, FireFox, IE10 already have native version of this.
46
    if (typeof btoa === 'undefined') {
47
        window.btoa = function (data) {
48
        // DO NOT ADD UTF8 ENCODING CODE HERE!!!!
49
 
50
        // UTF8 encoding encodes bytes over char code 128
51
        // and, essentially, turns an 8-bit binary streams
52
        // (that base64 can deal with) into 7-bit binary streams.
53
        // (by default server does not know that and does not recode the data back to 8bit)
54
        // You destroy your data.
55
 
56
        // binary streams like jpeg image data etc, while stored in JavaScript strings,
57
        // (which are 16bit arrays) are in 8bit format already.
58
        // You do NOT need to char-encode that before base64 encoding.
59
 
60
        // if you, by act of fate
61
        // have string which has individual characters with code
62
        // above 255 (pure unicode chars), encode that BEFORE you base64 here.
63
        // you can use absolutely any approch there, as long as in the end,
64
        // base64 gets an 8bit (char codes 0 - 255) stream.
65
        // when you get it on the server after un-base64, you must
66
        // UNencode it too, to get back to 16, 32bit or whatever original bin stream.
67
 
68
        // Note, Yes, JavaScript strings are, in most cases UCS-2 -
69
        // 16-bit character arrays. This does not mean, however,
70
        // that you always have to UTF8 it before base64.
71
        // it means that if you have actual characters anywhere in
72
        // that string that have char code above 255, you need to
73
        // recode *entire* string from 16-bit (or 32bit) to 8-bit array.
74
        // You can do binary split to UTF16 (BE or LE)
75
        // you can do utf8, you can split the thing by hand and prepend BOM to it,
76
        // but whatever you do, make sure you mirror the opposite on
77
        // the server. If server does not expect to post-process un-base64
78
        // 8-bit binary stream, think very very hard about messing around with encoding.
79
 
80
        // so, long story short:
81
        // DO NOT ADD UTF8 ENCODING CODE HERE!!!!
82
 
83
        /* @preserve
84
        ====================================================================
85
        base64 encoder
86
        MIT, GPL
87
 
88
        version: 1109.2015
89
        discuss at: http://phpjs.org/functions/base64_encode
90
        +   original by: Tyler Akins (http://rumkin.com)
91
        +   improved by: Bayron Guevara
92
        +   improved by: Thunder.m
93
        +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
94
        +   bugfixed by: Pellentesque Malesuada
95
        +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
96
        +   improved by: Rafal Kukawski (http://kukawski.pl)
97
        +                Daniel Dotsenko, Willow Systems Corp, willow-systems.com
98
        ====================================================================
99
        */
100
 
101
            var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
102
                b64a = b64.split(''),
103
                o1,
104
                o2,
105
                o3,
106
                h1,
107
                h2,
108
                h3,
109
                h4,
110
                bits,
111
                i = 0,
112
                ac = 0,
113
                enc = "",
114
                tmp_arr = [],
115
                r;
116
 
117
            do { // pack three octets into four hexets
118
                o1 = data.charCodeAt(i++);
119
                o2 = data.charCodeAt(i++);
120
                o3 = data.charCodeAt(i++);
121
 
122
                bits = o1 << 16 | o2 << 8 | o3;
123
 
124
                h1 = bits >> 18 & 0x3f;
125
                h2 = bits >> 12 & 0x3f;
126
                h3 = bits >> 6 & 0x3f;
127
                h4 = bits & 0x3f;
128
 
129
                // use hexets to index into b64, and append result to encoded string
130
                tmp_arr[ac++] = b64a[h1] + b64a[h2] + b64a[h3] + b64a[h4];
131
            } while (i < data.length);
132
 
133
            enc = tmp_arr.join('');
134
            r = data.length % 3;
135
            return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
136
            // end of base64 encoder MIT, GPL
137
        };
138
    }
139
 
140
    if (typeof atob === 'undefined') {
141
        window.atob = function (data) {
142
        // http://kevin.vanzonneveld.net
143
        // +   original by: Tyler Akins (http://rumkin.com)
144
        // +   improved by: Thunder.m
145
        // +      input by: Aman Gupta
146
        // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
147
        // +   bugfixed by: Onno Marsman
148
        // +   bugfixed by: Pellentesque Malesuada
149
        // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
150
        // +      input by: Brett Zamir (http://brett-zamir.me)
151
        // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
152
        // *     example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
153
        // *     returns 1: 'Kevin van Zonneveld'
154
        // mozilla has this native
155
        // - but breaks in 2.0.0.12!
156
        //if (typeof this.window['atob'] == 'function') {
157
        //    return atob(data);
158
        //}
159
            var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
160
                o1,
161
                o2,
162
                o3,
163
                h1,
164
                h2,
165
                h3,
166
                h4,
167
                bits,
168
                i = 0,
169
                ac = 0,
170
                dec = "",
171
                tmp_arr = [];
172
 
173
            if (!data) {
174
                return data;
175
            }
176
 
177
            data += '';
178
 
179
            do { // unpack four hexets into three octets using index points in b64
180
                h1 = b64.indexOf(data.charAt(i++));
181
                h2 = b64.indexOf(data.charAt(i++));
182
                h3 = b64.indexOf(data.charAt(i++));
183
                h4 = b64.indexOf(data.charAt(i++));
184
 
185
                bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
186
 
187
                o1 = bits >> 16 & 0xff;
188
                o2 = bits >> 8 & 0xff;
189
                o3 = bits & 0xff;
190
 
191
                if (h3 === 64) {
192
                    tmp_arr[ac++] = String.fromCharCode(o1);
193
                } else if (h4 === 64) {
194
                    tmp_arr[ac++] = String.fromCharCode(o1, o2);
195
                } else {
196
                    tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
197
                }
198
            } while (i < data.length);
199
            dec = tmp_arr.join('');
200
            return dec;
201
        };
202
    }
203
 
204
    var getObjectLength = typeof Object.keys === 'function' ?
205
                function (object) {
206
                    return Object.keys(object).length;
207
                } :
208
                function (object) {
209
                    var i = 0, e;
210
                    for (e in object) {
211
                        if (object.hasOwnProperty(e)) {
212
                            i++;
213
                        }
214
                    }
215
                    return i;
216
                },
217
 
218
/**
219
PubSub implementation
220
 
221
@class
222
@name PubSub
223
*/
224
        PubSub = function (context) {
225
            /**  @preserve
226
            -----------------------------------------------------------------------------------------------
227
            JavaScript PubSub library
228
            2012 (c) ddotsenko@willowsystems.com
229
            based on Peter Higgins (dante@dojotoolkit.org)
230
            Loosely based on Dojo publish/subscribe API, limited in scope. Rewritten blindly.
231
            Original is (c) Dojo Foundation 2004-2010. Released under either AFL or new BSD, see:
232
            http://dojofoundation.org/license for more information.
233
            -----------------------------------------------------------------------------------------------
234
            */
235
            /**
236
            @private
237
            @fieldOf PubSub
238
            */
239
            this.topics = {};
240
            /**
241
            Stores what will be `this` within the callback functions.
242
 
243
            @private
244
            @fieldOf PubSub#
245
            */
246
            this.context = context;
247
            /**
248
            Allows caller to emit an event and pass arguments to event listeners.
249
            @public
250
            @function
251
            @param topic {String} Name of the channel on which to voice this event
252
            @param args Any number of arguments you want to pass to the listeners of this event.
253
            @methodOf PubSub#
254
            @name publish
255
            */
256
            this.publish = function (topic, args) {
257
                if (this.topics[topic]) {
258
                    var currentTopic = this.topics[topic],
259
                        toremove = [],
260
                        fn,
261
                        i,
262
                        l,
263
                        pair,
264
                        emptyFunc = function () {};
265
                    args = Array.prototype.slice.call(arguments, 1);
266
                    for (i = 0, l = currentTopic.length; i < l; i++) {
267
                        pair = currentTopic[i]; // this is a [function, once_flag] array
268
                        fn = pair[0];
269
                        if (pair[1]) { /* 'run once' flag set */
270
                            pair[0] = emptyFunc;
271
                            toremove.push(i);
272
                        }
273
                        fn.apply(this.context, args);
274
                    }
275
                    for (i = 0, l = toremove.length; i < l; i++) {
276
                        currentTopic.splice(toremove[i], 1);
277
                    }
278
                }
279
            };
280
            /**
281
            Allows listener code to subscribe to channel and be called when data is available
282
            @public
283
            @function
284
            @param topic {String} Name of the channel on which to voice this event
285
            @param callback {Function} Executable (function pointer) that will be ran when event is voiced on this channel.
286
            @param once {Boolean} (optional. False by default) Flag indicating if the function is to be triggered only once.
287
            @returns {Object} A token object that cen be used for unsubscribing.
288
            @methodOf PubSub#
289
            @name subscribe
290
            */
291
            this.subscribe = function (topic, callback, once) {
292
                if (!this.topics[topic]) {
293
                    this.topics[topic] = [[callback, once]];
294
                } else {
295
                    this.topics[topic].push([callback, once]);
296
                }
297
                return {
298
                    "topic": topic,
299
                    "callback": callback
300
                };
301
            };
302
            /**
303
            Allows listener code to unsubscribe from a channel
304
            @public
305
            @function
306
            @param token {Object} A token object that was returned by `subscribe` method
307
            @methodOf PubSub#
308
            @name unsubscribe
309
            */
310
            this.unsubscribe = function (token) {
311
                if (this.topics[token.topic]) {
312
                    var currentTopic = this.topics[token.topic], i, l;
313
 
314
                    for (i = 0, l = currentTopic.length; i < l; i++) {
315
                        if (currentTopic[i][0] === token.callback) {
316
                            currentTopic.splice(i, 1);
317
                        }
318
                    }
319
                }
320
            };
321
        };
322
 
323
 
324
/**
325
@constructor
326
@private
327
*/
328
    function jsPDF(orientation, unit, format, compressPdf) { /** String orientation, String unit, String format, Boolean compressed */
329
 
330
        // Default parameter values
331
        if (typeof orientation === 'undefined') {
332
            orientation = 'p';
333
        } else {
334
            orientation = orientation.toString().toLowerCase();
335
        }
336
        if (typeof unit === 'undefined') { unit = 'mm'; }
337
        if (typeof format === 'undefined') { format = 'a4'; }
338
        if (typeof compressPdf === 'undefined' && typeof zpipe === 'undefined') { compressPdf = false; }
339
 
340
        var format_as_string = format.toString().toLowerCase(),
341
            version = '0.9.0rc2',
342
            content = [],
343
            content_length = 0,
344
            compress = compressPdf,
345
            pdfVersion = '1.3', // PDF Version
346
            pageFormats = { // Size in pt of various paper formats
347
                'a0': [2383.94, 3370.39],
348
                'a1': [1683.78, 2383.94],
349
                'a2': [1190.55, 1683.78],
350
                'a3': [841.89,  1190.55],
351
                'a4': [595.28,  841.89],
352
                'a5': [419.53,  595.28],
353
                'a6': [297.64,  419.53],
354
                'a7': [209.76,  297.64],
355
                'a8': [147.4 ,  209.76],
356
                'a9': [104.88,  147.4],
357
                'a10': [73.7,  104.88],
358
                'b0': [2834.65, 4008.19],
359
                'b1': [2004.09, 2834.65],
360
                'b2': [1417.32, 2004.09],
361
                'b3': [1000.63, 1417.32],
362
                'b4': [708.66,  1000.63],
363
                'b5': [498.9,  708.66],
364
                'b6': [354.33,  498.9],
365
                'b7': [249.45,  354.33],
366
                'b8': [175.75,  249.45],
367
                'b9': [124.72,  175.75],
368
                'b10': [87.87,  124.72],
369
                'c0': [2599.37, 3676.54],
370
                'c1': [1836.85, 2599.37],
371
                'c2': [1298.27, 1836.85],
372
                'c3': [918.43,  1298.27],
373
                'c4': [649.13,  918.43],
374
                'c5': [459.21,  649.13],
375
                'c6': [323.15,  459.21],
376
                'c7': [229.61,  323.15],
377
                'c8': [161.57,  229.61],
378
                'c9': [113.39,  161.57],
379
                'c10': [79.37,   113.39],
380
                'letter': [612, 792],
381
                'government-letter': [576, 756],
382
                'legal': [612, 1008],
383
                'junior-legal': [576, 360],
384
                'ledger': [1224, 792],
385
                'tabloid': [792, 1224]
386
            },
387
            textColor = '0 g',
388
            drawColor = '0 G',
389
            page = 0,
390
            pages = [],
391
            objectNumber = 2, // 'n' Current object number
392
            outToPages = false, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
393
            offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
394
            fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font.
395
            fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
396
            activeFontSize = 16,
397
            activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle
398
            lineWidth = 0.200025, // 2mm
399
            lineHeightProportion = 1.15,
400
            pageHeight,
401
            pageWidth,
402
            k, // Scale factor
403
            documentProperties = {'title': '', 'subject': '', 'author': '', 'keywords': '', 'creator': ''},
404
            lineCapID = 0,
405
            lineJoinID = 0,
406
            API = {},
407
            events = new PubSub(API),
408
            tmp,
409
            plugin,
410
            /////////////////////
411
            // Private functions
412
            /////////////////////
413
            // simplified (speedier) replacement for sprintf's %.2f conversion
414
            f2 = function (number) {
415
                return number.toFixed(2);
416
            },
417
            // simplified (speedier) replacement for sprintf's %.3f conversion
418
            f3 = function (number) {
419
                return number.toFixed(3);
420
            },
421
            // simplified (speedier) replacement for sprintf's %02d
422
            padd2 = function (number) {
423
                var n = (number).toFixed(0);
424
                if (number < 10) {
425
                    return '0' + n;
426
                } else {
427
                    return n;
428
                }
429
            },
430
            // simplified (speedier) replacement for sprintf's %02d
431
            padd10 = function (number) {
432
                var n = (number).toFixed(0);
433
                if (n.length < 10) {
434
                    return new Array( 11 - n.length ).join('0') + n;
435
                } else {
436
                    return n;
437
                }
438
            },
439
            out = function (string) {
440
                if (outToPages) { /* set by beginPage */
441
                    pages[page].push(string);
442
                } else {
443
                    content.push(string);
444
                    content_length += string.length + 1; // +1 is for '\n' that will be used to join contents of content
445
                }
446
            },
447
            newObject = function () {
448
                // Begin a new object
449
                objectNumber++;
450
                offsets[objectNumber] = content_length;
451
                out(objectNumber + ' 0 obj');
452
                return objectNumber;
453
            },
454
            putStream = function (str) {
455
                out('stream');
456
                out(str);
457
                out('endstream');
458
            },
459
            wPt,
460
            hPt,
461
            kids,
462
            i,
463
            putPages = function () {
464
                wPt = pageWidth * k;
465
                hPt = pageHeight * k;
466
 
467
                // outToPages = false as set in endDocument(). out() writes to content.
468
 
469
                var n, p, arr, uint, i, deflater, adler32;
470
                for (n = 1; n <= page; n++) {
471
                    newObject();
472
                    out('<</Type /Page');
473
                    out('/Parent 1 0 R');
474
                    out('/Resources 2 0 R');
475
                    out('/Contents ' + (objectNumber + 1) + ' 0 R>>');
476
                    out('endobj');
477
 
478
                    // Page content
479
                    p = pages[n].join('\n');
480
                    newObject();
481
                    if (compress) {
482
                        arr = [];
483
                        for (i = 0; i < p.length; ++i) {
484
                            arr[i] = p.charCodeAt(i);
485
                        }
486
                        adler32 = adler32cs.from(p);
487
                        deflater = new Deflater(6);
488
                        deflater.append(new Uint8Array(arr));
489
                        p = deflater.flush();
490
                        arr = [new Uint8Array([120, 156]), new Uint8Array(p),
491
                               new Uint8Array([adler32 & 0xFF, (adler32 >> 8) & 0xFF, (adler32 >> 16) & 0xFF, (adler32 >> 24) & 0xFF])];
492
                        p = '';
493
                        for (i in arr) {
494
                            if (arr.hasOwnProperty(i)) {
495
                                p += String.fromCharCode.apply(null, arr[i]);
496
                            }
497
                        }
498
                        out('<</Length ' + p.length  + ' /Filter [/FlateDecode]>>');
499
                    } else {
500
                        out('<</Length ' + p.length  + '>>');
501
                    }
502
                    putStream(p);
503
                    out('endobj');
504
                }
505
                offsets[1] = content_length;
506
                out('1 0 obj');
507
                out('<</Type /Pages');
508
                kids = '/Kids [';
509
                for (i = 0; i < page; i++) {
510
                    kids += (3 + 2 * i) + ' 0 R ';
511
                }
512
                out(kids + ']');
513
                out('/Count ' + page);
514
                out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
515
                out('>>');
516
                out('endobj');
517
            },
518
            putFont = function (font) {
519
                font.objectNumber = newObject();
520
                out('<</BaseFont/' + font.PostScriptName + '/Type/Font');
521
                if (typeof font.encoding === 'string') {
522
                    out('/Encoding/' + font.encoding);
523
                }
524
                out('/Subtype/Type1>>');
525
                out('endobj');
526
            },
527
            putFonts = function () {
528
                var fontKey;
529
                for (fontKey in fonts) {
530
                    if (fonts.hasOwnProperty(fontKey)) {
531
                        putFont(fonts[fontKey]);
532
                    }
533
                }
534
            },
535
            putXobjectDict = function () {
536
                // Loop through images, or other data objects
537
                events.publish('putXobjectDict');
538
            },
539
            putResourceDictionary = function () {
540
                out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
541
                out('/Font <<');
542
                // Do this for each font, the '1' bit is the index of the font
543
                var fontKey;
544
                for (fontKey in fonts) {
545
                    if (fonts.hasOwnProperty(fontKey)) {
546
                        out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
547
                    }
548
                }
549
                out('>>');
550
                out('/XObject <<');
551
                putXobjectDict();
552
                out('>>');
553
            },
554
            putResources = function () {
555
                putFonts();
556
                events.publish('putResources');
557
                // Resource dictionary
558
                offsets[2] = content_length;
559
                out('2 0 obj');
560
                out('<<');
561
                putResourceDictionary();
562
                out('>>');
563
                out('endobj');
564
                events.publish('postPutResources');
565
            },
566
            addToFontDictionary = function (fontKey, fontName, fontStyle) {
567
                // this is mapping structure for quick font key lookup.
568
                // returns the KEY of the font (ex: "F1") for a given pair of font name and type (ex: "Arial". "Italic")
569
                var undef;
570
                if (fontmap[fontName] === undef) {
571
                    fontmap[fontName] = {}; // fontStyle is a var interpreted and converted to appropriate string. don't wrap in quotes.
572
                }
573
                fontmap[fontName][fontStyle] = fontKey;
574
            },
575
            /**
576
            FontObject describes a particular font as member of an instnace of jsPDF
577
 
578
            It's a collection of properties like 'id' (to be used in PDF stream),
579
            'fontName' (font's family name), 'fontStyle' (font's style variant label)
580
 
581
            @class
582
            @public
583
            @property id {String} PDF-document-instance-specific label assinged to the font.
584
            @property PostScriptName {String} PDF specification full name for the font
585
            @property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
586
            @name FontObject
587
            */
588
            FontObject = {},
589
            addFont = function (PostScriptName, fontName, fontStyle, encoding) {
590
                var fontKey = 'F' + (getObjectLength(fonts) + 1).toString(10),
591
                    // This is FontObject
592
                    font = fonts[fontKey] = {
593
                        'id': fontKey,
594
                        // , 'objectNumber':   will be set by putFont()
595
                        'PostScriptName': PostScriptName,
596
                        'fontName': fontName,
597
                        'fontStyle': fontStyle,
598
                        'encoding': encoding,
599
                        'metadata': {}
600
                    };
601
 
602
                addToFontDictionary(fontKey, fontName, fontStyle);
603
 
604
                events.publish('addFont', font);
605
 
606
                return fontKey;
607
            },
608
            addFonts = function () {
609
 
610
                var HELVETICA = "helvetica",
611
                    TIMES = "times",
612
                    COURIER = "courier",
613
                    NORMAL = "normal",
614
                    BOLD = "bold",
615
                    ITALIC = "italic",
616
                    BOLD_ITALIC = "bolditalic",
617
                    encoding = 'StandardEncoding',
618
                    standardFonts = [
619
                        ['Helvetica', HELVETICA, NORMAL],
620
                        ['Helvetica-Bold', HELVETICA, BOLD],
621
                        ['Helvetica-Oblique', HELVETICA, ITALIC],
622
                        ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC],
623
                        ['Courier', COURIER, NORMAL],
624
                        ['Courier-Bold', COURIER, BOLD],
625
                        ['Courier-Oblique', COURIER, ITALIC],
626
                        ['Courier-BoldOblique', COURIER, BOLD_ITALIC],
627
                        ['Times-Roman', TIMES, NORMAL],
628
                        ['Times-Bold', TIMES, BOLD],
629
                        ['Times-Italic', TIMES, ITALIC],
630
                        ['Times-BoldItalic', TIMES, BOLD_ITALIC]
631
                    ],
632
                    i,
633
                    l,
634
                    fontKey,
635
                    parts;
636
                for (i = 0, l = standardFonts.length; i < l; i++) {
637
                    fontKey = addFont(
638
                        standardFonts[i][0],
639
                        standardFonts[i][1],
640
                        standardFonts[i][2],
641
                        encoding
642
                    );
643
 
644
                    // adding aliases for standard fonts, this time matching the capitalization
645
                    parts = standardFonts[i][0].split('-');
646
                    addToFontDictionary(fontKey, parts[0], parts[1] || '');
647
                }
648
 
649
                events.publish('addFonts', {'fonts': fonts, 'dictionary': fontmap});
650
            },
651
            /**
652
 
653
            @public
654
            @function
655
            @param text {String}
656
            @param flags {Object} Encoding flags.
657
            @returns {String} Encoded string
658
            */
659
            to8bitStream = function (text, flags) {
660
                /* PDF 1.3 spec:
661
                "For text strings encoded in Unicode, the first two bytes must be 254 followed by
662
                255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
663
                with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
664
                to be a meaningful beginning of a word or phrase.) The remainder of the
665
                string consists of Unicode character codes, according to the UTF-16 encoding
666
                specified in the Unicode standard, version 2.0. Commonly used Unicode values
667
                are represented as 2 bytes per character, with the high-order byte appearing first
668
                in the string."
669
 
670
                In other words, if there are chars in a string with char code above 255, we
671
                recode the string to UCS2 BE - string doubles in length and BOM is prepended.
672
 
673
                HOWEVER!
674
                Actual *content* (body) text (as opposed to strings used in document properties etc)
675
                does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
676
 
677
                Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
678
                a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
679
                fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
680
                code page. There, however, all characters in the stream are treated as GIDs,
681
                including BOM, which is the reason we need to skip BOM in content text (i.e. that
682
                that is tied to a font).
683
 
684
                To signal this "special" PDFEscape / to8bitStream handling mode,
685
                API.text() function sets (unless you overwrite it with manual values
686
                given to API.text(.., flags) )
687
                    flags.autoencode = true
688
                    flags.noBOM = true
689
 
690
                */
691
 
692
                /*
693
                `flags` properties relied upon:
694
                .sourceEncoding = string with encoding label.
695
                    "Unicode" by default. = encoding of the incoming text.
696
                    pass some non-existing encoding name
697
                    (ex: 'Do not touch my strings! I know what I am doing.')
698
                    to make encoding code skip the encoding step.
699
                .outputEncoding = Either valid PDF encoding name
700
                    (must be supported by jsPDF font metrics, otherwise no encoding)
701
                    or a JS object, where key = sourceCharCode, value = outputCharCode
702
                    missing keys will be treated as: sourceCharCode === outputCharCode
703
                .noBOM
704
                    See comment higher above for explanation for why this is important
705
                .autoencode
706
                    See comment higher above for explanation for why this is important
707
                */
708
 
709
                var i, l, undef, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch;
710
 
711
                if (flags === undef) {
712
                    flags = {};
713
                }
714
 
715
                sourceEncoding = flags.sourceEncoding ? sourceEncoding : 'Unicode';
716
 
717
                outputEncoding = flags.outputEncoding;
718
 
719
                // This 'encoding' section relies on font metrics format
720
                // attached to font objects by, among others,
721
                // "Willow Systems' standard_font_metrics plugin"
722
                // see jspdf.plugin.standard_font_metrics.js for format
723
                // of the font.metadata.encoding Object.
724
                // It should be something like
725
                //   .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
726
                //   .widths = {0:width, code:width, ..., 'fof':divisor}
727
                //   .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
728
                if ((flags.autoencode || outputEncoding) &&
729
                        fonts[activeFontKey].metadata &&
730
                        fonts[activeFontKey].metadata[sourceEncoding] &&
731
                        fonts[activeFontKey].metadata[sourceEncoding].encoding
732
                        ) {
733
                    encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding;
734
 
735
                    // each font has default encoding. Some have it clearly defined.
736
                    if (!outputEncoding && fonts[activeFontKey].encoding) {
737
                        outputEncoding = fonts[activeFontKey].encoding;
738
                    }
739
 
740
                    // Hmmm, the above did not work? Let's try again, in different place.
741
                    if (!outputEncoding && encodingBlock.codePages) {
742
                        outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
743
                    }
744
 
745
                    if (typeof outputEncoding === 'string') {
746
                        outputEncoding = encodingBlock[outputEncoding];
747
                    }
748
                    // we want output encoding to be a JS Object, where
749
                    // key = sourceEncoding's character code and
750
                    // value = outputEncoding's character code.
751
                    if (outputEncoding) {
752
                        isUnicode = false;
753
                        newtext = [];
754
                        for (i = 0, l = text.length; i < l; i++) {
755
                            ch = outputEncoding[text.charCodeAt(i)];
756
                            if (ch) {
757
                                newtext.push(
758
                                    String.fromCharCode(ch)
759
                                );
760
                            } else {
761
                                newtext.push(
762
                                    text[i]
763
                                );
764
                            }
765
 
766
                            // since we are looping over chars anyway, might as well
767
                            // check for residual unicodeness
768
                            if (newtext[i].charCodeAt(0) >> 8) { /* more than 255 */
769
                                isUnicode = true;
770
                            }
771
                        }
772
                        text = newtext.join('');
773
                    }
774
                }
775
 
776
                i = text.length;
777
                // isUnicode may be set to false above. Hence the triple-equal to undefined
778
                while (isUnicode === undef && i !== 0) {
779
                    if (text.charCodeAt(i - 1) >> 8) { /* more than 255 */
780
                        isUnicode = true;
781
                    }
782
                    i--;
783
                }
784
                if (!isUnicode) {
785
                    return text;
786
                } else {
787
                    newtext = flags.noBOM ? [] : [254, 255];
788
                    for (i = 0, l = text.length; i < l; i++) {
789
                        ch = text.charCodeAt(i);
790
                        bch = ch >> 8; // divide by 256
791
                        if (bch >> 8) { /* something left after dividing by 256 second time */
792
                            throw new Error("Character at position " + i.toString(10) + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
793
                        }
794
                        newtext.push(bch);
795
                        newtext.push(ch - (bch << 8));
796
                    }
797
                    return String.fromCharCode.apply(undef, newtext);
798
                }
799
            },
800
            // Replace '/', '(', and ')' with pdf-safe versions
801
            pdfEscape = function (text, flags) {
802
                // doing to8bitStream does NOT make this PDF display unicode text. For that
803
                // we also need to reference a unicode font and embed it - royal pain in the rear.
804
 
805
                // There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
806
                // which JavaScript Strings are happy to provide. So, while we still cannot display
807
                // 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
808
                // 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
809
                // is still parseable.
810
                // This will allow immediate support for unicode in document properties strings.
811
                return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
812
            },
813
            putInfo = function () {
814
                out('/Producer (jsPDF ' + version + ')');
815
                if (documentProperties.title) {
816
                    out('/Title (' + pdfEscape(documentProperties.title) + ')');
817
                }
818
                if (documentProperties.subject) {
819
                    out('/Subject (' + pdfEscape(documentProperties.subject) + ')');
820
                }
821
                if (documentProperties.author) {
822
                    out('/Author (' + pdfEscape(documentProperties.author) + ')');
823
                }
824
                if (documentProperties.keywords) {
825
                    out('/Keywords (' + pdfEscape(documentProperties.keywords) + ')');
826
                }
827
                if (documentProperties.creator) {
828
                    out('/Creator (' + pdfEscape(documentProperties.creator) + ')');
829
                }
830
                var created = new Date();
831
                out('/CreationDate (D:' +
832
                    [
833
                        created.getFullYear(),
834
                        padd2(created.getMonth() + 1),
835
                        padd2(created.getDate()),
836
                        padd2(created.getHours()),
837
                        padd2(created.getMinutes()),
838
                        padd2(created.getSeconds())
839
                    ].join('') +
840
                    ')'
841
                    );
842
            },
843
            putCatalog = function () {
844
                out('/Type /Catalog');
845
                out('/Pages 1 0 R');
846
                // @TODO: Add zoom and layout modes
847
                out('/OpenAction [3 0 R /FitH null]');
848
                out('/PageLayout /OneColumn');
849
                events.publish('putCatalog');
850
            },
851
            putTrailer = function () {
852
                out('/Size ' + (objectNumber + 1));
853
                out('/Root ' + objectNumber + ' 0 R');
854
                out('/Info ' + (objectNumber - 1) + ' 0 R');
855
            },
856
            beginPage = function () {
857
                page++;
858
                // Do dimension stuff
859
                outToPages = true;
860
                pages[page] = [];
861
            },
862
            _addPage = function () {
863
                beginPage();
864
                // Set line width
865
                out(f2(lineWidth * k) + ' w');
866
                // Set draw color
867
                out(drawColor);
868
                // resurrecting non-default line caps, joins
869
                if (lineCapID !== 0) {
870
                    out(lineCapID.toString(10) + ' J');
871
                }
872
                if (lineJoinID !== 0) {
873
                    out(lineJoinID.toString(10) + ' j');
874
                }
875
                events.publish('addPage', {'pageNumber': page});
876
            },
877
            /**
878
            Returns a document-specific font key - a label assigned to a
879
            font name + font type combination at the time the font was added
880
            to the font inventory.
881
 
882
            Font key is used as label for the desired font for a block of text
883
            to be added to the PDF document stream.
884
            @private
885
            @function
886
            @param fontName {String} can be undefined on "falthy" to indicate "use current"
887
            @param fontStyle {String} can be undefined on "falthy" to indicate "use current"
888
            @returns {String} Font key.
889
            */
890
            getFont = function (fontName, fontStyle) {
891
                var key, undef;
892
 
893
                if (fontName === undef) {
894
                    fontName = fonts[activeFontKey].fontName;
895
                }
896
                if (fontStyle === undef) {
897
                    fontStyle = fonts[activeFontKey].fontStyle;
898
                }
899
 
900
                try {
901
                    key = fontmap[fontName][fontStyle]; // returns a string like 'F3' - the KEY corresponding tot he font + type combination.
902
                } catch (e) {
903
                    key = undef;
904
                }
905
                if (!key) {
906
                    throw new Error("Unable to look up font label for font '" + fontName + "', '" + fontStyle + "'. Refer to getFontList() for available fonts.");
907
                }
908
 
909
                return key;
910
            },
911
            buildDocument = function () {
912
 
913
                outToPages = false; // switches out() to content
914
                content = [];
915
                offsets = [];
916
 
917
                // putHeader()
918
                out('%PDF-' + pdfVersion);
919
 
920
                putPages();
921
 
922
                putResources();
923
 
924
                // Info
925
                newObject();
926
                out('<<');
927
                putInfo();
928
                out('>>');
929
                out('endobj');
930
 
931
                // Catalog
932
                newObject();
933
                out('<<');
934
                putCatalog();
935
                out('>>');
936
                out('endobj');
937
 
938
                // Cross-ref
939
                var o = content_length, i;
940
                out('xref');
941
                out('0 ' + (objectNumber + 1));
942
                out('0000000000 65535 f ');
943
                for (i = 1; i <= objectNumber; i++) {
944
                    out(padd10(offsets[i]) + ' 00000 n ');
945
                }
946
                // Trailer
947
                out('trailer');
948
                out('<<');
949
                putTrailer();
950
                out('>>');
951
                out('startxref');
952
                out(o);
953
                out('%%EOF');
954
 
955
                outToPages = true;
956
 
957
                return content.join('\n');
958
            },
959
            getStyle = function (style) {
960
                // see Path-Painting Operators of PDF spec
961
                var op = 'S'; // stroke
962
                if (style === 'F') {
963
                    op = 'f'; // fill
964
                } else if (style === 'FD' || style === 'DF') {
965
                    op = 'B'; // both
966
                }
967
                return op;
968
            },
969
 
970
            /**
971
            Generates the PDF document.
972
            Possible values:
973
                datauristring (alias dataurlstring) - Data-Url-formatted data returned as string.
974
                datauri (alias datauri) - Data-Url-formatted data pushed into current window's location (effectively reloading the window with contents of the PDF).
975
 
976
            If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
977
 
978
            @param {String} type A string identifying one of the possible output types.
979
            @param {Object} options An object providing some additional signalling to PDF generator.
980
            @function
981
            @returns {jsPDF}
982
            @methodOf jsPDF#
983
            @name output
984
            */
985
            output = function (type, options) {
986
                var undef, data, length, array, i, blob;
987
                switch (type) {
988
                case undef:
989
                    return buildDocument();
990
                case 'save':
991
                    if (navigator.getUserMedia) {
992
                        if (window.URL === undefined) {
993
                            return API.output('dataurlnewwindow');
994
                        } else if (window.URL.createObjectURL === undefined) {
995
                            return API.output('dataurlnewwindow');
996
                        }
997
                    }
998
                    data = buildDocument();
999
 
1000
                    // Need to add the file to BlobBuilder as a Uint8Array
1001
                    length = data.length;
1002
                    array = new Uint8Array(new ArrayBuffer(length));
1003
 
1004
                    for (i = 0; i < length; i++) {
1005
                        array[i] = data.charCodeAt(i);
1006
                    }
1007
 
1008
                    blob = new Blob([array], {type: "application/pdf"});
1009
 
1010
                    saveAs(blob, options);
1011
                    break;
1012
                case 'datauristring':
1013
                case 'dataurlstring':
1014
                    return 'data:application/pdf;base64,' + btoa(buildDocument());
1015
                case 'datauri':
1016
                case 'dataurl':
1017
                    document.location.href = 'data:application/pdf;base64,' + btoa(buildDocument());
1018
                    break;
1019
                case 'dataurlnewwindow':
1020
                    window.open('data:application/pdf;base64,' + btoa(buildDocument()));
1021
                    break;
1022
                default:
1023
                    throw new Error('Output type "' + type + '" is not supported.');
1024
                }
1025
                // @TODO: Add different output options
1026
            };
1027
 
1028
        if (unit === 'pt') {
1029
            k = 1;
1030
        } else if (unit === 'mm') {
1031
            k = 72 / 25.4;
1032
        } else if (unit === 'cm') {
1033
            k = 72 / 2.54;
1034
        } else if (unit === 'in') {
1035
            k = 72;
1036
        } else {
1037
            throw ('Invalid unit: ' + unit);
1038
        }
1039
 
1040
        // Dimensions are stored as user units and converted to points on output
1041
        if (pageFormats.hasOwnProperty(format_as_string)) {
1042
            pageHeight = pageFormats[format_as_string][1] / k;
1043
            pageWidth = pageFormats[format_as_string][0] / k;
1044
        } else {
1045
            try {
1046
                pageHeight = format[1];
1047
                pageWidth = format[0];
1048
            } catch (err) {
1049
                throw ('Invalid format: ' + format);
1050
            }
1051
        }
1052
 
1053
        if (orientation === 'p' || orientation === 'portrait') {
1054
            orientation = 'p';
1055
            if (pageWidth > pageHeight) {
1056
                tmp = pageWidth;
1057
                pageWidth = pageHeight;
1058
                pageHeight = tmp;
1059
            }
1060
        } else if (orientation === 'l' || orientation === 'landscape') {
1061
            orientation = 'l';
1062
            if (pageHeight > pageWidth) {
1063
                tmp = pageWidth;
1064
                pageWidth = pageHeight;
1065
                pageHeight = tmp;
1066
            }
1067
        } else {
1068
            throw ('Invalid orientation: ' + orientation);
1069
        }
1070
 
1071
 
1072
 
1073
        //---------------------------------------
1074
        // Public API
1075
 
1076
        /*
1077
        Object exposing internal API to plugins
1078
        @public
1079
        */
1080
        API.internal = {
1081
            'pdfEscape': pdfEscape,
1082
            'getStyle': getStyle,
1083
            /**
1084
            Returns {FontObject} describing a particular font.
1085
            @public
1086
            @function
1087
            @param fontName {String} (Optional) Font's family name
1088
            @param fontStyle {String} (Optional) Font's style variation name (Example:"Italic")
1089
            @returns {FontObject}
1090
            */
1091
            'getFont': function () { return fonts[getFont.apply(API, arguments)]; },
1092
            'getFontSize': function () { return activeFontSize;    },
1093
            'getLineHeight': function () { return activeFontSize * lineHeightProportion;    },
1094
            'btoa': btoa,
1095
            'write': function (string1, string2, string3, etc) {
1096
                out(
1097
                    arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' ')
1098
                );
1099
            },
1100
            'getCoordinateString': function (value) {
1101
                return f2(value * k);
1102
            },
1103
            'getVerticalCoordinateString': function (value) {
1104
                return f2((pageHeight - value) * k);
1105
            },
1106
            'collections': {},
1107
            'newObject': newObject,
1108
            'putStream': putStream,
1109
            'events': events,
1110
            // ratio that you use in multiplication of a given "size" number to arrive to 'point'
1111
            // units of measurement.
1112
            // scaleFactor is set at initialization of the document and calculated against the stated
1113
            // default measurement units for the document.
1114
            // If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
1115
            // through multiplication.
1116
            'scaleFactor': k,
1117
            'pageSize': {'width': pageWidth, 'height': pageHeight},
1118
            'output': function (type, options) {
1119
                return output(type, options);
1120
            },
1121
            'getNumberOfPages': function () {return pages.length - 1; },
1122
            'pages': pages
1123
        };
1124
 
1125
        /**
1126
        Adds (and transfers the focus to) new page to the PDF document.
1127
        @function
1128
        @returns {jsPDF}
1129
 
1130
        @methodOf jsPDF#
1131
        @name addPage
1132
         */
1133
        API.addPage = function () {
1134
            _addPage();
1135
            return this;
1136
        };
1137
 
1138
        /**
1139
        Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
1140
        @function
1141
        @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down per font, spacing settings declared before this call.
1142
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1143
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1144
        @param {Object} flags Collection of settings signalling how the text must be encoded. Defaults are sane. If you think you want to pass some flags, you likely can read the source.
1145
        @returns {jsPDF}
1146
        @methodOf jsPDF#
1147
        @name text
1148
         */
1149
        API.text = function (text, x, y, flags) {
1150
            /**
1151
             * Inserts something like this into PDF
1152
                BT
1153
                /F1 16 Tf  % Font name + size
1154
                16 TL % How many units down for next line in multiline text
1155
 
1156
                28.35 813.54 Td % position
1157
                (line one) Tj
1158
                T* (line two) Tj
1159
                T* (line three) Tj
1160
                ET
1161
            */
1162
 
1163
            var undef, _first, _second, _third, newtext, str, i;
1164
            // Pre-August-2012 the order of arguments was function(x, y, text, flags)
1165
            // in effort to make all calls have similar signature like
1166
            //   function(data, coordinates... , miscellaneous)
1167
            // this method had its args flipped.
1168
            // code below allows backward compatibility with old arg order.
1169
            if (typeof text === 'number') {
1170
                _first = y;
1171
                _second = text;
1172
                _third = x;
1173
 
1174
                text = _first;
1175
                x = _second;
1176
                y = _third;
1177
            }
1178
 
1179
            // If there are any newlines in text, we assume
1180
            // the user wanted to print multiple lines, so break the
1181
            // text up into an array.  If the text is already an array,
1182
            // we assume the user knows what they are doing.
1183
            if (typeof text === 'string' && text.match(/[\n\r]/)) {
1184
                text = text.split(/\r\n|\r|\n/g);
1185
            }
1186
 
1187
            if (typeof flags === 'undefined') {
1188
                flags = {'noBOM': true, 'autoencode': true};
1189
            } else {
1190
 
1191
                if (flags.noBOM === undef) {
1192
                    flags.noBOM = true;
1193
                }
1194
 
1195
                if (flags.autoencode === undef) {
1196
                    flags.autoencode = true;
1197
                }
1198
 
1199
            }
1200
 
1201
            if (typeof text === 'string') {
1202
                str = pdfEscape(text, flags);
1203
            } else if (text instanceof Array) {  /* Array */
1204
                // we don't want to destroy  original text array, so cloning it
1205
                newtext = text.concat();
1206
                // we do array.join('text that must not be PDFescaped")
1207
                // thus, pdfEscape each component separately
1208
                for (i = newtext.length - 1; i !== -1; i--) {
1209
                    newtext[i] = pdfEscape(newtext[i], flags);
1210
                }
1211
                str = newtext.join(") Tj\nT* (");
1212
            } else {
1213
                throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.');
1214
            }
1215
            // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
1216
 
1217
            // BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET
1218
            // if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations)
1219
            // Thus, there is NO useful, *reliable* concept of "default" font for a page.
1220
            // The fact that "default" (reuse font used before) font worked before in basic cases is an accident
1221
            // - readers dealing smartly with brokenness of jsPDF's markup.
1222
            out(
1223
                'BT\n/' +
1224
                    activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size
1225
                    (activeFontSize * lineHeightProportion) + ' TL\n' + // line spacing
1226
                    textColor +
1227
                    '\n' + f2(x * k) + ' ' + f2((pageHeight - y) * k) + ' Td\n(' +
1228
                    str +
1229
                    ') Tj\nET'
1230
            );
1231
            return this;
1232
        };
1233
 
1234
        API.line = function (x1, y1, x2, y2) {
1235
            out(
1236
                f2(x1 * k) + ' ' + f2((pageHeight - y1) * k) + ' m ' +
1237
                    f2(x2 * k) + ' ' + f2((pageHeight - y2) * k) + ' l S'
1238
            );
1239
            return this;
1240
        };
1241
 
1242
        /**
1243
        Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
1244
        All data points in `lines` are relative to last line origin.
1245
        `x`, `y` become x1,y1 for first line / curve in the set.
1246
        For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
1247
        For bezier curves you need to specify [x2,y2,x3,y3,x4,y4] - vectors to control points 1, 2, ending point. All vectors are against the start of the curve - x1,y1.
1248
 
1249
        @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
1250
        @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
1251
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1252
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1253
        @param {Number} scale (Defaults to [1.0,1.0]) x,y Scaling factor for all vectors. Elements can be any floating number Sub-one makes drawing smaller. Over-one grows the drawing. Negative flips the direction.
1254
        @param {String} style One of 'S' (the default), 'F', 'FD' or 'DF'.  'S' draws just the curve. 'F' fills the region defined by the curves. 'DF' or 'FD' draws the curves and fills the region.
1255
        @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point.
1256
        @function
1257
        @returns {jsPDF}
1258
        @methodOf jsPDF#
1259
        @name lines
1260
         */
1261
        API.lines = function (lines, x, y, scale, style, closed) {
1262
            var undef, _first, _second, _third, scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4;
1263
 
1264
            // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
1265
            // in effort to make all calls have similar signature like
1266
            //   function(content, coordinateX, coordinateY , miscellaneous)
1267
            // this method had its args flipped.
1268
            // code below allows backward compatibility with old arg order.
1269
            if (typeof lines === 'number') {
1270
                _first = y;
1271
                _second = lines;
1272
                _third = x;
1273
 
1274
                lines = _first;
1275
                x = _second;
1276
                y = _third;
1277
            }
1278
 
1279
            style = getStyle(style);
1280
            scale = scale === undef ? [1, 1] : scale;
1281
 
1282
            // starting point
1283
            out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m ');
1284
 
1285
            scalex = scale[0];
1286
            scaley = scale[1];
1287
            l = lines.length;
1288
            //, x2, y2 // bezier only. In page default measurement "units", *after* scaling
1289
            //, x3, y3 // bezier only. In page default measurement "units", *after* scaling
1290
            // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
1291
            x4 = x; // last / ending point = starting point for first item.
1292
            y4 = y; // last / ending point = starting point for first item.
1293
 
1294
            for (i = 0; i < l; i++) {
1295
                leg = lines[i];
1296
                if (leg.length === 2) {
1297
                    // simple line
1298
                    x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
1299
                    y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
1300
                    out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l');
1301
                } else {
1302
                    // bezier curve
1303
                    x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
1304
                    y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
1305
                    x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
1306
                    y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
1307
                    x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
1308
                    y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
1309
                    out(
1310
                        f3(x2 * k) + ' ' +
1311
                            f3((pageHeight - y2) * k) + ' ' +
1312
                            f3(x3 * k) + ' ' +
1313
                            f3((pageHeight - y3) * k) + ' ' +
1314
                            f3(x4 * k) + ' ' +
1315
                            f3((pageHeight - y4) * k) + ' c'
1316
                    );
1317
                }
1318
            }
1319
 
1320
            if (closed == true) {
1321
                out(' h');
1322
            }
1323
 
1324
            // stroking / filling / both the path
1325
            out(style);
1326
            return this;
1327
        };
1328
 
1329
        /**
1330
        Adds a rectangle to PDF
1331
 
1332
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1333
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1334
        @param {Number} w Width (in units declared at inception of PDF document)
1335
        @param {Number} h Height (in units declared at inception of PDF document)
1336
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1337
        @function
1338
        @returns {jsPDF}
1339
        @methodOf jsPDF#
1340
        @name rect
1341
         */
1342
        API.rect = function (x, y, w, h, style) {
1343
            var op = getStyle(style);
1344
            out([
1345
                f2(x * k),
1346
                f2((pageHeight - y) * k),
1347
                f2(w * k),
1348
                f2(-h * k),
1349
                're',
1350
                op
1351
            ].join(' '));
1352
            return this;
1353
        };
1354
 
1355
        /**
1356
        Adds a triangle to PDF
1357
 
1358
        @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
1359
        @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
1360
        @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
1361
        @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
1362
        @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
1363
        @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
1364
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1365
        @function
1366
        @returns {jsPDF}
1367
        @methodOf jsPDF#
1368
        @name triangle
1369
         */
1370
        API.triangle = function (x1, y1, x2, y2, x3, y3, style) {
1371
            this.lines(
1372
                [
1373
                    [ x2 - x1, y2 - y1 ], // vector to point 2
1374
                    [ x3 - x2, y3 - y2 ], // vector to point 3
1375
                    [ x1 - x3, y1 - y3 ] // closing vector back to point 1
1376
                ],
1377
                x1,
1378
                y1, // start of path
1379
                [1, 1],
1380
                style,
1381
                true
1382
            );
1383
            return this;
1384
        };
1385
 
1386
        /**
1387
        Adds a rectangle with rounded corners to PDF
1388
 
1389
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1390
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1391
        @param {Number} w Width (in units declared at inception of PDF document)
1392
        @param {Number} h Height (in units declared at inception of PDF document)
1393
        @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
1394
        @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
1395
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1396
        @function
1397
        @returns {jsPDF}
1398
        @methodOf jsPDF#
1399
        @name roundedRect
1400
        */
1401
        API.roundedRect = function (x, y, w, h, rx, ry, style) {
1402
            var MyArc = 4 / 3 * (Math.SQRT2 - 1);
1403
            this.lines(
1404
                [
1405
                    [ (w - 2 * rx), 0 ],
1406
                    [ (rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry ],
1407
                    [ 0, (h - 2 * ry) ],
1408
                    [ 0, (ry * MyArc), -(rx * MyArc), ry, -rx, ry],
1409
                    [ (-w + 2 * rx), 0],
1410
                    [ -(rx * MyArc), 0, -rx, -(ry * MyArc), -rx, -ry],
1411
                    [ 0, (-h + 2 * ry)],
1412
                    [ 0, -(ry * MyArc), (rx * MyArc), -ry, rx, -ry]
1413
                ],
1414
                x + rx,
1415
                y, // start of path
1416
                [1, 1],
1417
                style
1418
            );
1419
            return this;
1420
        };
1421
 
1422
        /**
1423
        Adds an ellipse to PDF
1424
 
1425
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1426
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1427
        @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
1428
        @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
1429
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1430
        @function
1431
        @returns {jsPDF}
1432
        @methodOf jsPDF#
1433
        @name ellipse
1434
         */
1435
        API.ellipse = function (x, y, rx, ry, style) {
1436
            var op = getStyle(style),
1437
                lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
1438
                ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
1439
 
1440
            out([
1441
                f2((x + rx) * k),
1442
                f2((pageHeight - y) * k),
1443
                'm',
1444
                f2((x + rx) * k),
1445
                f2((pageHeight - (y - ly)) * k),
1446
                f2((x + lx) * k),
1447
                f2((pageHeight - (y - ry)) * k),
1448
                f2(x * k),
1449
                f2((pageHeight - (y - ry)) * k),
1450
                'c'
1451
            ].join(' '));
1452
            out([
1453
                f2((x - lx) * k),
1454
                f2((pageHeight - (y - ry)) * k),
1455
                f2((x - rx) * k),
1456
                f2((pageHeight - (y - ly)) * k),
1457
                f2((x - rx) * k),
1458
                f2((pageHeight - y) * k),
1459
                'c'
1460
            ].join(' '));
1461
            out([
1462
                f2((x - rx) * k),
1463
                f2((pageHeight - (y + ly)) * k),
1464
                f2((x - lx) * k),
1465
                f2((pageHeight - (y + ry)) * k),
1466
                f2(x * k),
1467
                f2((pageHeight - (y + ry)) * k),
1468
                'c'
1469
            ].join(' '));
1470
            out([
1471
                f2((x + lx) * k),
1472
                f2((pageHeight - (y + ry)) * k),
1473
                f2((x + rx) * k),
1474
                f2((pageHeight - (y + ly)) * k),
1475
                f2((x + rx) * k),
1476
                f2((pageHeight - y) * k),
1477
                'c',
1478
                op
1479
            ].join(' '));
1480
            return this;
1481
        };
1482
 
1483
        /**
1484
        Adds an circle to PDF
1485
 
1486
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1487
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1488
        @param {Number} r Radius (in units declared at inception of PDF document)
1489
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1490
        @function
1491
        @returns {jsPDF}
1492
        @methodOf jsPDF#
1493
        @name circle
1494
         */
1495
        API.circle = function (x, y, r, style) {
1496
            return this.ellipse(x, y, r, r, style);
1497
        };
1498
 
1499
        /**
1500
        Adds a properties to the PDF document
1501
 
1502
        @param {Object} A property_name-to-property_value object structure.
1503
        @function
1504
        @returns {jsPDF}
1505
        @methodOf jsPDF#
1506
        @name setProperties
1507
         */
1508
        API.setProperties = function (properties) {
1509
            // copying only those properties we can render.
1510
            var property;
1511
            for (property in documentProperties) {
1512
                if (documentProperties.hasOwnProperty(property) && properties[property]) {
1513
                    documentProperties[property] = properties[property];
1514
                }
1515
            }
1516
            return this;
1517
        };
1518
 
1519
        /**
1520
        Sets font size for upcoming text elements.
1521
 
1522
        @param {Number} size Font size in points.
1523
        @function
1524
        @returns {jsPDF}
1525
        @methodOf jsPDF#
1526
        @name setFontSize
1527
         */
1528
        API.setFontSize = function (size) {
1529
            activeFontSize = size;
1530
            return this;
1531
        };
1532
 
1533
        /**
1534
        Sets text font face, variant for upcoming text elements.
1535
        See output of jsPDF.getFontList() for possible font names, styles.
1536
 
1537
        @param {String} fontName Font name or family. Example: "times"
1538
        @param {String} fontStyle Font style or variant. Example: "italic"
1539
        @function
1540
        @returns {jsPDF}
1541
        @methodOf jsPDF#
1542
        @name setFont
1543
         */
1544
        API.setFont = function (fontName, fontStyle) {
1545
            activeFontKey = getFont(fontName, fontStyle);
1546
            // if font is not found, the above line blows up and we never go further
1547
            return this;
1548
        };
1549
 
1550
        /**
1551
        Switches font style or variant for upcoming text elements,
1552
        while keeping the font face or family same.
1553
        See output of jsPDF.getFontList() for possible font names, styles.
1554
 
1555
        @param {String} style Font style or variant. Example: "italic"
1556
        @function
1557
        @returns {jsPDF}
1558
        @methodOf jsPDF#
1559
        @name setFontStyle
1560
         */
1561
        API.setFontStyle = API.setFontType = function (style) {
1562
            var undef;
1563
            activeFontKey = getFont(undef, style);
1564
            // if font is not found, the above line blows up and we never go further
1565
            return this;
1566
        };
1567
 
1568
        /**
1569
        Returns an object - a tree of fontName to fontStyle relationships available to
1570
        active PDF document.
1571
 
1572
        @public
1573
        @function
1574
        @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
1575
        @methodOf jsPDF#
1576
        @name getFontList
1577
        */
1578
        API.getFontList = function () {
1579
            // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
1580
            var list = {},
1581
                fontName,
1582
                fontStyle,
1583
                tmp;
1584
 
1585
            for (fontName in fontmap) {
1586
                if (fontmap.hasOwnProperty(fontName)) {
1587
                    list[fontName] = tmp = [];
1588
                    for (fontStyle in fontmap[fontName]) {
1589
                        if (fontmap[fontName].hasOwnProperty(fontStyle)) {
1590
                            tmp.push(fontStyle);
1591
                        }
1592
                    }
1593
                }
1594
            }
1595
 
1596
            return list;
1597
        };
1598
 
1599
        /**
1600
        Sets line width for upcoming lines.
1601
 
1602
        @param {Number} width Line width (in units declared at inception of PDF document)
1603
        @function
1604
        @returns {jsPDF}
1605
        @methodOf jsPDF#
1606
        @name setLineWidth
1607
         */
1608
        API.setLineWidth = function (width) {
1609
            out((width * k).toFixed(2) + ' w');
1610
            return this;
1611
        };
1612
 
1613
        /**
1614
        Sets the stroke color for upcoming elements.
1615
 
1616
        Depending on the number of arguments given, Gray, RGB, or CMYK
1617
        color space is implied.
1618
 
1619
        When only ch1 is given, "Gray" color space is implied and it
1620
        must be a value in the range from 0.00 (solid black) to to 1.00 (white)
1621
        if values are communicated as String types, or in range from 0 (black)
1622
        to 255 (white) if communicated as Number type.
1623
        The RGB-like 0-255 range is provided for backward compatibility.
1624
 
1625
        When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
1626
        value must be in the range from 0.00 (minimum intensity) to to 1.00
1627
        (max intensity) if values are communicated as String types, or
1628
        from 0 (min intensity) to to 255 (max intensity) if values are communicated
1629
        as Number types.
1630
        The RGB-like 0-255 range is provided for backward compatibility.
1631
 
1632
        When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
1633
        value must be a in the range from 0.00 (0% concentration) to to
1634
        1.00 (100% concentration)
1635
 
1636
        Because JavaScript treats fixed point numbers badly (rounds to
1637
        floating point nearest to binary representation) it is highly advised to
1638
        communicate the fractional numbers as String types, not JavaScript Number type.
1639
 
1640
        @param {Number|String} ch1 Color channel value
1641
        @param {Number|String} ch2 Color channel value
1642
        @param {Number|String} ch3 Color channel value
1643
        @param {Number|String} ch4 Color channel value
1644
 
1645
        @function
1646
        @returns {jsPDF}
1647
        @methodOf jsPDF#
1648
        @name setDrawColor
1649
         */
1650
        API.setDrawColor = function (ch1, ch2, ch3, ch4) {
1651
            var color;
1652
            if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
1653
                // Gray color space.
1654
                if (typeof ch1 === 'string') {
1655
                    color = ch1 + ' G';
1656
                } else {
1657
                    color = f2(ch1 / 255) + ' G';
1658
                }
1659
            } else if (ch4 === undefined) {
1660
                // RGB
1661
                if (typeof ch1 === 'string') {
1662
                    color = [ch1, ch2, ch3, 'RG'].join(' ');
1663
                } else {
1664
                    color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' ');
1665
                }
1666
            } else {
1667
                // CMYK
1668
                if (typeof ch1 === 'string') {
1669
                    color = [ch1, ch2, ch3, ch4, 'K'].join(' ');
1670
                } else {
1671
                    color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' ');
1672
                }
1673
            }
1674
 
1675
            out(color);
1676
            return this;
1677
        };
1678
 
1679
        /**
1680
        Sets the fill color for upcoming elements.
1681
 
1682
        Depending on the number of arguments given, Gray, RGB, or CMYK
1683
        color space is implied.
1684
 
1685
        When only ch1 is given, "Gray" color space is implied and it
1686
        must be a value in the range from 0.00 (solid black) to to 1.00 (white)
1687
        if values are communicated as String types, or in range from 0 (black)
1688
        to 255 (white) if communicated as Number type.
1689
        The RGB-like 0-255 range is provided for backward compatibility.
1690
 
1691
        When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
1692
        value must be in the range from 0.00 (minimum intensity) to to 1.00
1693
        (max intensity) if values are communicated as String types, or
1694
        from 0 (min intensity) to to 255 (max intensity) if values are communicated
1695
        as Number types.
1696
        The RGB-like 0-255 range is provided for backward compatibility.
1697
 
1698
        When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
1699
        value must be a in the range from 0.00 (0% concentration) to to
1700
        1.00 (100% concentration)
1701
 
1702
        Because JavaScript treats fixed point numbers badly (rounds to
1703
        floating point nearest to binary representation) it is highly advised to
1704
        communicate the fractional numbers as String types, not JavaScript Number type.
1705
 
1706
        @param {Number|String} ch1 Color channel value
1707
        @param {Number|String} ch2 Color channel value
1708
        @param {Number|String} ch3 Color channel value
1709
        @param {Number|String} ch4 Color channel value
1710
 
1711
        @function
1712
        @returns {jsPDF}
1713
        @methodOf jsPDF#
1714
        @name setFillColor
1715
         */
1716
        API.setFillColor = function (ch1, ch2, ch3, ch4) {
1717
            var color;
1718
 
1719
            if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
1720
                // Gray color space.
1721
                if (typeof ch1 === 'string') {
1722
                    color = ch1 + ' g';
1723
                } else {
1724
                    color = f2(ch1 / 255) + ' g';
1725
                }
1726
            } else if (ch4 === undefined) {
1727
                // RGB
1728
                if (typeof ch1 === 'string') {
1729
                    color = [ch1, ch2, ch3, 'rg'].join(' ');
1730
                } else {
1731
                    color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' ');
1732
                }
1733
            } else {
1734
                // CMYK
1735
                if (typeof ch1 === 'string') {
1736
                    color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
1737
                } else {
1738
                    color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
1739
                }
1740
            }
1741
 
1742
            out(color);
1743
            return this;
1744
        };
1745
 
1746
        /**
1747
        Sets the text color for upcoming elements.
1748
        If only one, first argument is given,
1749
        treats the value as gray-scale color value.
1750
 
1751
        @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF'
1752
        @param {Number} g Green channel color value in range 0-255
1753
        @param {Number} b Blue channel color value in range 0-255
1754
        @function
1755
        @returns {jsPDF}
1756
        @methodOf jsPDF#
1757
        @name setTextColor
1758
        */
1759
        API.setTextColor = function (r, g, b) {
1760
            var patt = /#[0-9A-Fa-f]{6}/;
1761
            if ((typeof r == 'string') && patt.test(r)) {
1762
                var hex = r.replace('#','');
1763
                var bigint = parseInt(hex, 16);
1764
                r = (bigint >> 16) & 255;
1765
                g = (bigint >> 8) & 255;
1766
                b = bigint & 255;
1767
            }
1768
 
1769
            if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
1770
                textColor = f3(r / 255) + ' g';
1771
            } else {
1772
                textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
1773
            }
1774
            return this;
1775
        };
1776
 
1777
        /**
1778
        Is an Object providing a mapping from human-readable to
1779
        integer flag values designating the varieties of line cap
1780
        and join styles.
1781
 
1782
        @returns {Object}
1783
        @fieldOf jsPDF#
1784
        @name CapJoinStyles
1785
        */
1786
        API.CapJoinStyles = {
1787
            0: 0,
1788
            'butt': 0,
1789
            'but': 0,
1790
            'miter': 0,
1791
            1: 1,
1792
            'round': 1,
1793
            'rounded': 1,
1794
            'circle': 1,
1795
            2: 2,
1796
            'projecting': 2,
1797
            'project': 2,
1798
            'square': 2,
1799
            'bevel': 2
1800
        };
1801
 
1802
        /**
1803
        Sets the line cap styles
1804
        See {jsPDF.CapJoinStyles} for variants
1805
 
1806
        @param {String|Number} style A string or number identifying the type of line cap
1807
        @function
1808
        @returns {jsPDF}
1809
        @methodOf jsPDF#
1810
        @name setLineCap
1811
        */
1812
        API.setLineCap = function (style) {
1813
            var id = this.CapJoinStyles[style];
1814
            if (id === undefined) {
1815
                throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
1816
            }
1817
            lineCapID = id;
1818
            out(id.toString(10) + ' J');
1819
 
1820
            return this;
1821
        };
1822
 
1823
        /**
1824
        Sets the line join styles
1825
        See {jsPDF.CapJoinStyles} for variants
1826
 
1827
        @param {String|Number} style A string or number identifying the type of line join
1828
        @function
1829
        @returns {jsPDF}
1830
        @methodOf jsPDF#
1831
        @name setLineJoin
1832
        */
1833
        API.setLineJoin = function (style) {
1834
            var id = this.CapJoinStyles[style];
1835
            if (id === undefined) {
1836
                throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
1837
            }
1838
            lineJoinID = id;
1839
            out(id.toString(10) + ' j');
1840
 
1841
            return this;
1842
        };
1843
 
1844
        // Output is both an internal (for plugins) and external function
1845
        API.output = output;
1846
 
1847
        /**
1848
         * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
1849
         * @param  {String} filename The filename including extension.
1850
         *
1851
         * @function
1852
         * @returns {jsPDF}
1853
         * @methodOf jsPDF#
1854
         * @name save
1855
         */
1856
        API.save = function (filename) {
1857
            API.output('save', filename);
1858
        };
1859
 
1860
        // applying plugins (more methods) ON TOP of built-in API.
1861
        // this is intentional as we allow plugins to override
1862
        // built-ins
1863
        for (plugin in jsPDF.API) {
1864
            if (jsPDF.API.hasOwnProperty(plugin)) {
1865
                if (plugin === 'events' && jsPDF.API.events.length) {
1866
                    (function (events, newEvents) {
1867
 
1868
                        // jsPDF.API.events is a JS Array of Arrays
1869
                        // where each Array is a pair of event name, handler
1870
                        // Events were added by plugins to the jsPDF instantiator.
1871
                        // These are always added to the new instance and some ran
1872
                        // during instantiation.
1873
 
1874
                        var eventname, handler_and_args, i;
1875
 
1876
                        for (i = newEvents.length - 1; i !== -1; i--) {
1877
                            // subscribe takes 3 args: 'topic', function, runonce_flag
1878
                            // if undefined, runonce is false.
1879
                            // users can attach callback directly,
1880
                            // or they can attach an array with [callback, runonce_flag]
1881
                            // that's what the "apply" magic is for below.
1882
                            eventname = newEvents[i][0];
1883
                            handler_and_args = newEvents[i][1];
1884
                            events.subscribe.apply(
1885
                                events,
1886
                                [eventname].concat(
1887
                                    typeof handler_and_args === 'function' ?
1888
                                            [ handler_and_args ] :
1889
                                            handler_and_args
1890
                                )
1891
                            );
1892
                        }
1893
                    }(events, jsPDF.API.events));
1894
                } else {
1895
                    API[plugin] = jsPDF.API[plugin];
1896
                }
1897
            }
1898
        }
1899
 
1900
        /////////////////////////////////////////
1901
        // continuing initilisation of jsPDF Document object
1902
        /////////////////////////////////////////
1903
 
1904
 
1905
        // Add the first page automatically
1906
        addFonts();
1907
        activeFontKey = 'F1';
1908
        _addPage();
1909
 
1910
        events.publish('initialized');
1911
 
1912
        return API;
1913
    }
1914
 
1915
/**
1916
jsPDF.API is a STATIC property of jsPDF class.
1917
jsPDF.API is an object you can add methods and properties to.
1918
The methods / properties you add will show up in new jsPDF objects.
1919
 
1920
One property is prepopulated. It is the 'events' Object. Plugin authors can add topics, callbacks to this object. These will be reassigned to all new instances of jsPDF.
1921
Examples:
1922
    jsPDF.API.events['initialized'] = function(){ 'this' is API object }
1923
    jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
1924
 
1925
@static
1926
@public
1927
@memberOf jsPDF
1928
@name API
1929
 
1930
@example
1931
    jsPDF.API.mymethod = function(){
1932
        // 'this' will be ref to internal API object. see jsPDF source
1933
        // , so you can refer to built-in methods like so:
1934
        //     this.line(....)
1935
        //     this.text(....)
1936
    }
1937
    var pdfdoc = new jsPDF()
1938
    pdfdoc.mymethod() // <- !!!!!!
1939
*/
1940
    jsPDF.API = {'events': []};
1941
 
1942
    return jsPDF;
1943
}());