Subversion-Projekte lars-tiefland.cienc

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
9 lars 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 'a3', 'a4' (Default),'a5' ,'letter' ,'legal'
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
                'a3': [841.89, 1190.55],
348
                'a4': [595.28, 841.89],
349
                'a5': [420.94, 595.28],
350
                'letter': [612, 792],
351
                'legal': [612, 1008]
352
            },
353
            textColor = '0 g',
354
            drawColor = '0 G',
355
            page = 0,
356
            pages = [],
357
            objectNumber = 2, // 'n' Current object number
358
            outToPages = false, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
359
            offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
360
            fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font.
361
            fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
362
            activeFontSize = 16,
363
            activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle
364
            lineWidth = 0.200025, // 2mm
365
            pageHeight,
366
            pageWidth,
367
            k, // Scale factor
368
            documentProperties = {'title': '', 'subject': '', 'author': '', 'keywords': '', 'creator': ''},
369
            lineCapID = 0,
370
            lineJoinID = 0,
371
            API = {},
372
            events = new PubSub(API),
373
            tmp,
374
            plugin,
375
            /////////////////////
376
            // Private functions
377
            /////////////////////
378
            // simplified (speedier) replacement for sprintf's %.2f conversion
379
            f2 = function (number) {
380
                return number.toFixed(2);
381
            },
382
            // simplified (speedier) replacement for sprintf's %.3f conversion
383
            f3 = function (number) {
384
                return number.toFixed(3);
385
            },
386
            // simplified (speedier) replacement for sprintf's %02d
387
            padd2 = function (number) {
388
                var n = (number).toFixed(0);
389
                if (number < 10) {
390
                    return '0' + n;
391
                } else {
392
                    return n;
393
                }
394
            },
395
            // simplified (speedier) replacement for sprintf's %02d
396
            padd10 = function (number) {
397
                var n = (number).toFixed(0);
398
                if (n.length < 10) {
399
                    return new Array( 11 - n.length ).join('0') + n;
400
                } else {
401
                    return n;
402
                }
403
            },
404
            out = function (string) {
405
                if (outToPages) { /* set by beginPage */
406
                    pages[page].push(string);
407
                } else {
408
                    content.push(string);
409
                    content_length += string.length + 1; // +1 is for '\n' that will be used to join contents of content
410
                }
411
            },
412
            newObject = function () {
413
                // Begin a new object
414
                objectNumber++;
415
                offsets[objectNumber] = content_length;
416
                out(objectNumber + ' 0 obj');
417
                return objectNumber;
418
            },
419
            putStream = function (str) {
420
                out('stream');
421
                out(str);
422
                out('endstream');
423
            },
424
            wPt,
425
            hPt,
426
            kids,
427
            i,
428
            putPages = function () {
429
                wPt = pageWidth * k;
430
                hPt = pageHeight * k;
431
 
432
                // outToPages = false as set in endDocument(). out() writes to content.
433
 
434
                var n, p, arr, uint, i, deflater, adler32;
435
                for (n = 1; n <= page; n++) {
436
                    newObject();
437
                    out('<</Type /Page');
438
                    out('/Parent 1 0 R');
439
                    out('/Resources 2 0 R');
440
                    out('/Contents ' + (objectNumber + 1) + ' 0 R>>');
441
                    out('endobj');
442
 
443
                    // Page content
444
                    p = pages[n].join('\n');
445
                    newObject();
446
                    if (compress) {
447
                        arr = [];
448
                        for (i = 0; i < p.length; ++i) {
449
                            arr[i] = p.charCodeAt(i);
450
                        }
451
                        adler32 = adler32cs.from(p);
452
                        deflater = new Deflater(6);
453
                        deflater.append(new Uint8Array(arr));
454
                        p = deflater.flush();
455
                        arr = [new Uint8Array([120, 156]), new Uint8Array(p),
456
                               new Uint8Array([adler32 & 0xFF, (adler32 >> 8) & 0xFF, (adler32 >> 16) & 0xFF, (adler32 >> 24) & 0xFF])];
457
                        p = '';
458
                        for (i in arr) {
459
                            if (arr.hasOwnProperty(i)) {
460
                                p += String.fromCharCode.apply(null, arr[i]);
461
                            }
462
                        }
463
                        out('<</Length ' + p.length  + ' /Filter [/FlateDecode]>>');
464
                    } else {
465
                        out('<</Length ' + p.length  + '>>');
466
                    }
467
                    putStream(p);
468
                    out('endobj');
469
                }
470
                offsets[1] = content_length;
471
                out('1 0 obj');
472
                out('<</Type /Pages');
473
                kids = '/Kids [';
474
                for (i = 0; i < page; i++) {
475
                    kids += (3 + 2 * i) + ' 0 R ';
476
                }
477
                out(kids + ']');
478
                out('/Count ' + page);
479
                out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
480
                out('>>');
481
                out('endobj');
482
            },
483
            putFont = function (font) {
484
                font.objectNumber = newObject();
485
                out('<</BaseFont/' + font.PostScriptName + '/Type/Font');
486
                if (typeof font.encoding === 'string') {
487
                    out('/Encoding/' + font.encoding);
488
                }
489
                out('/Subtype/Type1>>');
490
                out('endobj');
491
            },
492
            putFonts = function () {
493
                var fontKey;
494
                for (fontKey in fonts) {
495
                    if (fonts.hasOwnProperty(fontKey)) {
496
                        putFont(fonts[fontKey]);
497
                    }
498
                }
499
            },
500
            putXobjectDict = function () {
501
                // Loop through images, or other data objects
502
                events.publish('putXobjectDict');
503
            },
504
            putResourceDictionary = function () {
505
                out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
506
                out('/Font <<');
507
                // Do this for each font, the '1' bit is the index of the font
508
                var fontKey;
509
                for (fontKey in fonts) {
510
                    if (fonts.hasOwnProperty(fontKey)) {
511
                        out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
512
                    }
513
                }
514
                out('>>');
515
                out('/XObject <<');
516
                putXobjectDict();
517
                out('>>');
518
            },
519
            putResources = function () {
520
                putFonts();
521
                events.publish('putResources');
522
                // Resource dictionary
523
                offsets[2] = content_length;
524
                out('2 0 obj');
525
                out('<<');
526
                putResourceDictionary();
527
                out('>>');
528
                out('endobj');
529
                events.publish('postPutResources');
530
            },
531
            addToFontDictionary = function (fontKey, fontName, fontStyle) {
532
                // this is mapping structure for quick font key lookup.
533
                // returns the KEY of the font (ex: "F1") for a given pair of font name and type (ex: "Arial". "Italic")
534
                var undef;
535
                if (fontmap[fontName] === undef) {
536
                    fontmap[fontName] = {}; // fontStyle is a var interpreted and converted to appropriate string. don't wrap in quotes.
537
                }
538
                fontmap[fontName][fontStyle] = fontKey;
539
            },
540
            /**
541
            FontObject describes a particular font as member of an instnace of jsPDF
542
 
543
            It's a collection of properties like 'id' (to be used in PDF stream),
544
            'fontName' (font's family name), 'fontStyle' (font's style variant label)
545
 
546
            @class
547
            @public
548
            @property id {String} PDF-document-instance-specific label assinged to the font.
549
            @property PostScriptName {String} PDF specification full name for the font
550
            @property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
551
            @name FontObject
552
            */
553
            FontObject = {},
554
            addFont = function (PostScriptName, fontName, fontStyle, encoding) {
555
                var fontKey = 'F' + (getObjectLength(fonts) + 1).toString(10),
556
                    // This is FontObject
557
                    font = fonts[fontKey] = {
558
                        'id': fontKey,
559
                        // , 'objectNumber':   will be set by putFont()
560
                        'PostScriptName': PostScriptName,
561
                        'fontName': fontName,
562
                        'fontStyle': fontStyle,
563
                        'encoding': encoding,
564
                        'metadata': {}
565
                    };
566
 
567
                addToFontDictionary(fontKey, fontName, fontStyle);
568
 
569
                events.publish('addFont', font);
570
 
571
                return fontKey;
572
            },
573
            addFonts = function () {
574
 
575
                var HELVETICA = "helvetica",
576
                    TIMES = "times",
577
                    COURIER = "courier",
578
                    NORMAL = "normal",
579
                    BOLD = "bold",
580
                    ITALIC = "italic",
581
                    BOLD_ITALIC = "bolditalic",
582
                    encoding = 'StandardEncoding',
583
                    standardFonts = [
584
                        ['Helvetica', HELVETICA, NORMAL],
585
                        ['Helvetica-Bold', HELVETICA, BOLD],
586
                        ['Helvetica-Oblique', HELVETICA, ITALIC],
587
                        ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC],
588
                        ['Courier', COURIER, NORMAL],
589
                        ['Courier-Bold', COURIER, BOLD],
590
                        ['Courier-Oblique', COURIER, ITALIC],
591
                        ['Courier-BoldOblique', COURIER, BOLD_ITALIC],
592
                        ['Times-Roman', TIMES, NORMAL],
593
                        ['Times-Bold', TIMES, BOLD],
594
                        ['Times-Italic', TIMES, ITALIC],
595
                        ['Times-BoldItalic', TIMES, BOLD_ITALIC]
596
                    ],
597
                    i,
598
                    l,
599
                    fontKey,
600
                    parts;
601
                for (i = 0, l = standardFonts.length; i < l; i++) {
602
                    fontKey = addFont(
603
                        standardFonts[i][0],
604
                        standardFonts[i][1],
605
                        standardFonts[i][2],
606
                        encoding
607
                    );
608
 
609
                    // adding aliases for standard fonts, this time matching the capitalization
610
                    parts = standardFonts[i][0].split('-');
611
                    addToFontDictionary(fontKey, parts[0], parts[1] || '');
612
                }
613
 
614
                events.publish('addFonts', {'fonts': fonts, 'dictionary': fontmap});
615
            },
616
            /**
617
 
618
            @public
619
            @function
620
            @param text {String}
621
            @param flags {Object} Encoding flags.
622
            @returns {String} Encoded string
623
            */
624
            to8bitStream = function (text, flags) {
625
                /* PDF 1.3 spec:
626
                "For text strings encoded in Unicode, the first two bytes must be 254 followed by
627
                255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
628
                with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
629
                to be a meaningful beginning of a word or phrase.) The remainder of the
630
                string consists of Unicode character codes, according to the UTF-16 encoding
631
                specified in the Unicode standard, version 2.0. Commonly used Unicode values
632
                are represented as 2 bytes per character, with the high-order byte appearing first
633
                in the string."
634
 
635
                In other words, if there are chars in a string with char code above 255, we
636
                recode the string to UCS2 BE - string doubles in length and BOM is prepended.
637
 
638
                HOWEVER!
639
                Actual *content* (body) text (as opposed to strings used in document properties etc)
640
                does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
641
 
642
                Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
643
                a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
644
                fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
645
                code page. There, however, all characters in the stream are treated as GIDs,
646
                including BOM, which is the reason we need to skip BOM in content text (i.e. that
647
                that is tied to a font).
648
 
649
                To signal this "special" PDFEscape / to8bitStream handling mode,
650
                API.text() function sets (unless you overwrite it with manual values
651
                given to API.text(.., flags) )
652
                    flags.autoencode = true
653
                    flags.noBOM = true
654
 
655
                */
656
 
657
                /*
658
                `flags` properties relied upon:
659
                .sourceEncoding = string with encoding label.
660
                    "Unicode" by default. = encoding of the incoming text.
661
                    pass some non-existing encoding name
662
                    (ex: 'Do not touch my strings! I know what I am doing.')
663
                    to make encoding code skip the encoding step.
664
                .outputEncoding = Either valid PDF encoding name
665
                    (must be supported by jsPDF font metrics, otherwise no encoding)
666
                    or a JS object, where key = sourceCharCode, value = outputCharCode
667
                    missing keys will be treated as: sourceCharCode === outputCharCode
668
                .noBOM
669
                    See comment higher above for explanation for why this is important
670
                .autoencode
671
                    See comment higher above for explanation for why this is important
672
                */
673
 
674
                var i, l, undef, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch;
675
 
676
                if (flags === undef) {
677
                    flags = {};
678
                }
679
 
680
                sourceEncoding = flags.sourceEncoding ? sourceEncoding : 'Unicode';
681
 
682
                outputEncoding = flags.outputEncoding;
683
 
684
                // This 'encoding' section relies on font metrics format
685
                // attached to font objects by, among others,
686
                // "Willow Systems' standard_font_metrics plugin"
687
                // see jspdf.plugin.standard_font_metrics.js for format
688
                // of the font.metadata.encoding Object.
689
                // It should be something like
690
                //   .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
691
                //   .widths = {0:width, code:width, ..., 'fof':divisor}
692
                //   .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
693
                if ((flags.autoencode || outputEncoding) &&
694
                        fonts[activeFontKey].metadata &&
695
                        fonts[activeFontKey].metadata[sourceEncoding] &&
696
                        fonts[activeFontKey].metadata[sourceEncoding].encoding
697
                        ) {
698
                    encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding;
699
 
700
                    // each font has default encoding. Some have it clearly defined.
701
                    if (!outputEncoding && fonts[activeFontKey].encoding) {
702
                        outputEncoding = fonts[activeFontKey].encoding;
703
                    }
704
 
705
                    // Hmmm, the above did not work? Let's try again, in different place.
706
                    if (!outputEncoding && encodingBlock.codePages) {
707
                        outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
708
                    }
709
 
710
                    if (typeof outputEncoding === 'string') {
711
                        outputEncoding = encodingBlock[outputEncoding];
712
                    }
713
                    // we want output encoding to be a JS Object, where
714
                    // key = sourceEncoding's character code and
715
                    // value = outputEncoding's character code.
716
                    if (outputEncoding) {
717
                        isUnicode = false;
718
                        newtext = [];
719
                        for (i = 0, l = text.length; i < l; i++) {
720
                            ch = outputEncoding[text.charCodeAt(i)];
721
                            if (ch) {
722
                                newtext.push(
723
                                    String.fromCharCode(ch)
724
                                );
725
                            } else {
726
                                newtext.push(
727
                                    text[i]
728
                                );
729
                            }
730
 
731
                            // since we are looping over chars anyway, might as well
732
                            // check for residual unicodeness
733
                            if (newtext[i].charCodeAt(0) >> 8) { /* more than 255 */
734
                                isUnicode = true;
735
                            }
736
                        }
737
                        text = newtext.join('');
738
                    }
739
                }
740
 
741
                i = text.length;
742
                // isUnicode may be set to false above. Hence the triple-equal to undefined
743
                while (isUnicode === undef && i !== 0) {
744
                    if (text.charCodeAt(i - 1) >> 8) { /* more than 255 */
745
                        isUnicode = true;
746
                    }
747
                    i--;
748
                }
749
                if (!isUnicode) {
750
                    return text;
751
                } else {
752
                    newtext = flags.noBOM ? [] : [254, 255];
753
                    for (i = 0, l = text.length; i < l; i++) {
754
                        ch = text.charCodeAt(i);
755
                        bch = ch >> 8; // divide by 256
756
                        if (bch >> 8) { /* something left after dividing by 256 second time */
757
                            throw new Error("Character at position " + i.toString(10) + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
758
                        }
759
                        newtext.push(bch);
760
                        newtext.push(ch - (bch << 8));
761
                    }
762
                    return String.fromCharCode.apply(undef, newtext);
763
                }
764
            },
765
            // Replace '/', '(', and ')' with pdf-safe versions
766
            pdfEscape = function (text, flags) {
767
                // doing to8bitStream does NOT make this PDF display unicode text. For that
768
                // we also need to reference a unicode font and embed it - royal pain in the rear.
769
 
770
                // There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
771
                // which JavaScript Strings are happy to provide. So, while we still cannot display
772
                // 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
773
                // 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
774
                // is still parseable.
775
                // This will allow immediate support for unicode in document properties strings.
776
                return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
777
            },
778
            putInfo = function () {
779
                out('/Producer (jsPDF ' + version + ')');
780
                if (documentProperties.title) {
781
                    out('/Title (' + pdfEscape(documentProperties.title) + ')');
782
                }
783
                if (documentProperties.subject) {
784
                    out('/Subject (' + pdfEscape(documentProperties.subject) + ')');
785
                }
786
                if (documentProperties.author) {
787
                    out('/Author (' + pdfEscape(documentProperties.author) + ')');
788
                }
789
                if (documentProperties.keywords) {
790
                    out('/Keywords (' + pdfEscape(documentProperties.keywords) + ')');
791
                }
792
                if (documentProperties.creator) {
793
                    out('/Creator (' + pdfEscape(documentProperties.creator) + ')');
794
                }
795
                var created = new Date();
796
                out('/CreationDate (D:' +
797
                    [
798
                        created.getFullYear(),
799
                        padd2(created.getMonth() + 1),
800
                        padd2(created.getDate()),
801
                        padd2(created.getHours()),
802
                        padd2(created.getMinutes()),
803
                        padd2(created.getSeconds())
804
                    ].join('') +
805
                    ')'
806
                    );
807
            },
808
            putCatalog = function () {
809
                out('/Type /Catalog');
810
                out('/Pages 1 0 R');
811
                // @TODO: Add zoom and layout modes
812
                out('/OpenAction [3 0 R /FitH null]');
813
                out('/PageLayout /OneColumn');
814
                events.publish('putCatalog');
815
            },
816
            putTrailer = function () {
817
                out('/Size ' + (objectNumber + 1));
818
                out('/Root ' + objectNumber + ' 0 R');
819
                out('/Info ' + (objectNumber - 1) + ' 0 R');
820
            },
821
            beginPage = function () {
822
                page++;
823
                // Do dimension stuff
824
                outToPages = true;
825
                pages[page] = [];
826
            },
827
            _addPage = function () {
828
                beginPage();
829
                // Set line width
830
                out(f2(lineWidth * k) + ' w');
831
                // Set draw color
832
                out(drawColor);
833
                // resurrecting non-default line caps, joins
834
                if (lineCapID !== 0) {
835
                    out(lineCapID.toString(10) + ' J');
836
                }
837
                if (lineJoinID !== 0) {
838
                    out(lineJoinID.toString(10) + ' j');
839
                }
840
                events.publish('addPage', {'pageNumber': page});
841
            },
842
            /**
843
            Returns a document-specific font key - a label assigned to a
844
            font name + font type combination at the time the font was added
845
            to the font inventory.
846
 
847
            Font key is used as label for the desired font for a block of text
848
            to be added to the PDF document stream.
849
            @private
850
            @function
851
            @param fontName {String} can be undefined on "falthy" to indicate "use current"
852
            @param fontStyle {String} can be undefined on "falthy" to indicate "use current"
853
            @returns {String} Font key.
854
            */
855
            getFont = function (fontName, fontStyle) {
856
                var key, undef;
857
 
858
                if (fontName === undef) {
859
                    fontName = fonts[activeFontKey].fontName;
860
                }
861
                if (fontStyle === undef) {
862
                    fontStyle = fonts[activeFontKey].fontStyle;
863
                }
864
 
865
                try {
866
                    key = fontmap[fontName][fontStyle]; // returns a string like 'F3' - the KEY corresponding tot he font + type combination.
867
                } catch (e) {
868
                    key = undef;
869
                }
870
                if (!key) {
871
                    throw new Error("Unable to look up font label for font '" + fontName + "', '" + fontStyle + "'. Refer to getFontList() for available fonts.");
872
                }
873
 
874
                return key;
875
            },
876
            buildDocument = function () {
877
 
878
                outToPages = false; // switches out() to content
879
                content = [];
880
                offsets = [];
881
 
882
                // putHeader()
883
                out('%PDF-' + pdfVersion);
884
 
885
                putPages();
886
 
887
                putResources();
888
 
889
                // Info
890
                newObject();
891
                out('<<');
892
                putInfo();
893
                out('>>');
894
                out('endobj');
895
 
896
                // Catalog
897
                newObject();
898
                out('<<');
899
                putCatalog();
900
                out('>>');
901
                out('endobj');
902
 
903
                // Cross-ref
904
                var o = content_length, i;
905
                out('xref');
906
                out('0 ' + (objectNumber + 1));
907
                out('0000000000 65535 f ');
908
                for (i = 1; i <= objectNumber; i++) {
909
                    out(padd10(offsets[i]) + ' 00000 n ');
910
                }
911
                // Trailer
912
                out('trailer');
913
                out('<<');
914
                putTrailer();
915
                out('>>');
916
                out('startxref');
917
                out(o);
918
                out('%%EOF');
919
 
920
                outToPages = true;
921
 
922
                return content.join('\n');
923
            },
924
            getStyle = function (style) {
925
                // see Path-Painting Operators of PDF spec
926
                var op = 'S'; // stroke
927
                if (style === 'F') {
928
                    op = 'f'; // fill
929
                } else if (style === 'FD' || style === 'DF') {
930
                    op = 'B'; // both
931
                }
932
                return op;
933
            },
934
 
935
            /**
936
            Generates the PDF document.
937
            Possible values:
938
                datauristring (alias dataurlstring) - Data-Url-formatted data returned as string.
939
                datauri (alias datauri) - Data-Url-formatted data pushed into current window's location (effectively reloading the window with contents of the PDF).
940
 
941
            If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
942
 
943
            @param {String} type A string identifying one of the possible output types.
944
            @param {Object} options An object providing some additional signalling to PDF generator.
945
            @function
946
            @returns {jsPDF}
947
            @methodOf jsPDF#
948
            @name output
949
            */
950
            output = function (type, options) {
951
                var undef, data, length, array, i, blob;
952
                switch (type) {
953
                case undef:
954
                    return buildDocument();
955
                case 'save':
956
                    if (navigator.getUserMedia) {
957
                        if (window.URL === undefined) {
958
                            return API.output('dataurlnewwindow');
959
                        } else if (window.URL.createObjectURL === undefined) {
960
                            return API.output('dataurlnewwindow');
961
                        }
962
                    }
963
                    data = buildDocument();
964
 
965
                    // Need to add the file to BlobBuilder as a Uint8Array
966
                    length = data.length;
967
                    array = new Uint8Array(new ArrayBuffer(length));
968
 
969
                    for (i = 0; i < length; i++) {
970
                        array[i] = data.charCodeAt(i);
971
                    }
972
 
973
                    blob = new Blob([array], {type: "application/pdf"});
974
 
975
                    saveAs(blob, options);
976
                    break;
977
                case 'datauristring':
978
                case 'dataurlstring':
979
                    return 'data:application/pdf;base64,' + btoa(buildDocument());
980
                case 'datauri':
981
                case 'dataurl':
982
                    document.location.href = 'data:application/pdf;base64,' + btoa(buildDocument());
983
                    break;
984
                case 'dataurlnewwindow':
985
                    window.open('data:application/pdf;base64,' + btoa(buildDocument()));
986
                    break;
987
                default:
988
                    throw new Error('Output type "' + type + '" is not supported.');
989
                }
990
                // @TODO: Add different output options
991
            };
992
 
993
        if (unit === 'pt') {
994
            k = 1;
995
        } else if (unit === 'mm') {
996
            k = 72 / 25.4;
997
        } else if (unit === 'cm') {
998
            k = 72 / 2.54;
999
        } else if (unit === 'in') {
1000
            k = 72;
1001
        } else {
1002
            throw ('Invalid unit: ' + unit);
1003
        }
1004
 
1005
        // Dimensions are stored as user units and converted to points on output
1006
        if (pageFormats.hasOwnProperty(format_as_string)) {
1007
            pageHeight = pageFormats[format_as_string][1] / k;
1008
            pageWidth = pageFormats[format_as_string][0] / k;
1009
        } else {
1010
            try {
1011
                pageHeight = format[1];
1012
                pageWidth = format[0];
1013
            } catch (err) {
1014
                throw ('Invalid format: ' + format);
1015
            }
1016
        }
1017
 
1018
        if (orientation === 'p' || orientation === 'portrait') {
1019
            orientation = 'p';
1020
            if (pageWidth > pageHeight) {
1021
                tmp = pageWidth;
1022
                pageWidth = pageHeight;
1023
                pageHeight = tmp;
1024
            }
1025
        } else if (orientation === 'l' || orientation === 'landscape') {
1026
            orientation = 'l';
1027
            if (pageHeight > pageWidth) {
1028
                tmp = pageWidth;
1029
                pageWidth = pageHeight;
1030
                pageHeight = tmp;
1031
            }
1032
        } else {
1033
            throw ('Invalid orientation: ' + orientation);
1034
        }
1035
 
1036
 
1037
 
1038
        //---------------------------------------
1039
        // Public API
1040
 
1041
        /*
1042
        Object exposing internal API to plugins
1043
        @public
1044
        */
1045
        API.internal = {
1046
            'pdfEscape': pdfEscape,
1047
            'getStyle': getStyle,
1048
            /**
1049
            Returns {FontObject} describing a particular font.
1050
            @public
1051
            @function
1052
            @param fontName {String} (Optional) Font's family name
1053
            @param fontStyle {String} (Optional) Font's style variation name (Example:"Italic")
1054
            @returns {FontObject}
1055
            */
1056
            'getFont': function () { return fonts[getFont.apply(API, arguments)]; },
1057
            'getFontSize': function () { return activeFontSize;    },
1058
            'btoa': btoa,
1059
            'write': function (string1, string2, string3, etc) {
1060
                out(
1061
                    arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' ')
1062
                );
1063
            },
1064
            'getCoordinateString': function (value) {
1065
                return f2(value * k);
1066
            },
1067
            'getVerticalCoordinateString': function (value) {
1068
                return f2((pageHeight - value) * k);
1069
            },
1070
            'collections': {},
1071
            'newObject': newObject,
1072
            'putStream': putStream,
1073
            'events': events,
1074
            // ratio that you use in multiplication of a given "size" number to arrive to 'point'
1075
            // units of measurement.
1076
            // scaleFactor is set at initialization of the document and calculated against the stated
1077
            // default measurement units for the document.
1078
            // If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
1079
            // through multiplication.
1080
            'scaleFactor': k,
1081
            'pageSize': {'width': pageWidth, 'height': pageHeight},
1082
            'output': function (type, options) {
1083
                return output(type, options);
1084
            }
1085
        };
1086
 
1087
        /**
1088
        Adds (and transfers the focus to) new page to the PDF document.
1089
        @function
1090
        @returns {jsPDF}
1091
 
1092
        @methodOf jsPDF#
1093
        @name addPage
1094
         */
1095
        API.addPage = function () {
1096
            _addPage();
1097
            return this;
1098
        };
1099
 
1100
        /**
1101
        Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
1102
        @function
1103
        @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.
1104
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1105
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1106
        @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.
1107
        @returns {jsPDF}
1108
        @methodOf jsPDF#
1109
        @name text
1110
         */
1111
        API.text = function (text, x, y, flags) {
1112
            /**
1113
             * Inserts something like this into PDF
1114
                BT
1115
                /F1 16 Tf  % Font name + size
1116
                16 TL % How many units down for next line in multiline text
1117
 
1118
                28.35 813.54 Td % position
1119
                (line one) Tj
1120
                T* (line two) Tj
1121
                T* (line three) Tj
1122
                ET
1123
            */
1124
 
1125
            var undef, _first, _second, _third, newtext, str, i;
1126
            // Pre-August-2012 the order of arguments was function(x, y, text, flags)
1127
            // in effort to make all calls have similar signature like
1128
            //   function(data, coordinates... , miscellaneous)
1129
            // this method had its args flipped.
1130
            // code below allows backward compatibility with old arg order.
1131
            if (typeof text === 'number') {
1132
                _first = y;
1133
                _second = text;
1134
                _third = x;
1135
 
1136
                text = _first;
1137
                x = _second;
1138
                y = _third;
1139
            }
1140
 
1141
            // If there are any newlines in text, we assume
1142
            // the user wanted to print multiple lines, so break the
1143
            // text up into an array.  If the text is already an array,
1144
            // we assume the user knows what they are doing.
1145
            if (typeof text === 'string' && text.match(/[\n\r]/)) {
1146
                text = text.split(/\r\n|\r|\n/g);
1147
            }
1148
 
1149
            if (typeof flags === 'undefined') {
1150
                flags = {'noBOM': true, 'autoencode': true};
1151
            } else {
1152
 
1153
                if (flags.noBOM === undef) {
1154
                    flags.noBOM = true;
1155
                }
1156
 
1157
                if (flags.autoencode === undef) {
1158
                    flags.autoencode = true;
1159
                }
1160
 
1161
            }
1162
 
1163
            if (typeof text === 'string') {
1164
                str = pdfEscape(text, flags);
1165
            } else if (text instanceof Array) {  /* Array */
1166
                // we don't want to destroy  original text array, so cloning it
1167
                newtext = text.concat();
1168
                // we do array.join('text that must not be PDFescaped")
1169
                // thus, pdfEscape each component separately
1170
                for (i = newtext.length - 1; i !== -1; i--) {
1171
                    newtext[i] = pdfEscape(newtext[i], flags);
1172
                }
1173
                str = newtext.join(") Tj\nT* (");
1174
            } else {
1175
                throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.');
1176
            }
1177
            // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
1178
 
1179
            // BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET
1180
            // if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations)
1181
            // Thus, there is NO useful, *reliable* concept of "default" font for a page.
1182
            // The fact that "default" (reuse font used before) font worked before in basic cases is an accident
1183
            // - readers dealing smartly with brokenness of jsPDF's markup.
1184
            out(
1185
                'BT\n/' +
1186
                    activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size
1187
                    activeFontSize + ' TL\n' + // line spacing
1188
                    textColor +
1189
                    '\n' + f2(x * k) + ' ' + f2((pageHeight - y) * k) + ' Td\n(' +
1190
                    str +
1191
                    ') Tj\nET'
1192
            );
1193
            return this;
1194
        };
1195
 
1196
        API.line = function (x1, y1, x2, y2) {
1197
            out(
1198
                f2(x1 * k) + ' ' + f2((pageHeight - y1) * k) + ' m ' +
1199
                    f2(x2 * k) + ' ' + f2((pageHeight - y2) * k) + ' l S'
1200
            );
1201
            return this;
1202
        };
1203
 
1204
        /**
1205
        Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
1206
        All data points in `lines` are relative to last line origin.
1207
        `x`, `y` become x1,y1 for first line / curve in the set.
1208
        For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
1209
        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.
1210
 
1211
        @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
1212
        @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
1213
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1214
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1215
        @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.
1216
        @function
1217
        @returns {jsPDF}
1218
        @methodOf jsPDF#
1219
        @name lines
1220
         */
1221
        API.lines = function (lines, x, y, scale, style) {
1222
            var undef, _first, _second, _third, scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4;
1223
 
1224
            // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
1225
            // in effort to make all calls have similar signature like
1226
            //   function(content, coordinateX, coordinateY , miscellaneous)
1227
            // this method had its args flipped.
1228
            // code below allows backward compatibility with old arg order.
1229
            if (typeof lines === 'number') {
1230
                _first = y;
1231
                _second = lines;
1232
                _third = x;
1233
 
1234
                lines = _first;
1235
                x = _second;
1236
                y = _third;
1237
            }
1238
 
1239
            style = getStyle(style);
1240
            scale = scale === undef ? [1, 1] : scale;
1241
 
1242
            // starting point
1243
            out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m ');
1244
 
1245
            scalex = scale[0];
1246
            scaley = scale[1];
1247
            l = lines.length;
1248
            //, x2, y2 // bezier only. In page default measurement "units", *after* scaling
1249
            //, x3, y3 // bezier only. In page default measurement "units", *after* scaling
1250
            // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
1251
            x4 = x; // last / ending point = starting point for first item.
1252
            y4 = y; // last / ending point = starting point for first item.
1253
 
1254
            for (i = 0; i < l; i++) {
1255
                leg = lines[i];
1256
                if (leg.length === 2) {
1257
                    // simple line
1258
                    x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
1259
                    y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
1260
                    out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l');
1261
                } else {
1262
                    // bezier curve
1263
                    x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
1264
                    y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
1265
                    x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
1266
                    y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
1267
                    x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
1268
                    y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
1269
                    out(
1270
                        f3(x2 * k) + ' ' +
1271
                            f3((pageHeight - y2) * k) + ' ' +
1272
                            f3(x3 * k) + ' ' +
1273
                            f3((pageHeight - y3) * k) + ' ' +
1274
                            f3(x4 * k) + ' ' +
1275
                            f3((pageHeight - y4) * k) + ' c'
1276
                    );
1277
                }
1278
            }
1279
            // stroking / filling / both the path
1280
            out(style);
1281
            return this;
1282
        };
1283
 
1284
        /**
1285
        Adds a rectangle to PDF
1286
 
1287
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1288
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1289
        @param {Number} w Width (in units declared at inception of PDF document)
1290
        @param {Number} h Height (in units declared at inception of PDF document)
1291
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1292
        @function
1293
        @returns {jsPDF}
1294
        @methodOf jsPDF#
1295
        @name rect
1296
         */
1297
        API.rect = function (x, y, w, h, style) {
1298
            var op = getStyle(style);
1299
            out([
1300
                f2(x * k),
1301
                f2((pageHeight - y) * k),
1302
                f2(w * k),
1303
                f2(-h * k),
1304
                're',
1305
                op
1306
            ].join(' '));
1307
            return this;
1308
        };
1309
 
1310
        /**
1311
        Adds a triangle to PDF
1312
 
1313
        @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
1314
        @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
1315
        @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
1316
        @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
1317
        @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
1318
        @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
1319
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1320
        @function
1321
        @returns {jsPDF}
1322
        @methodOf jsPDF#
1323
        @name triangle
1324
         */
1325
        API.triangle = function (x1, y1, x2, y2, x3, y3, style) {
1326
            this.lines(
1327
                [
1328
                    [ x2 - x1, y2 - y1 ], // vector to point 2
1329
                    [ x3 - x2, y3 - y2 ], // vector to point 3
1330
                    [ x1 - x3, y1 - y3 ] // closing vector back to point 1
1331
                ],
1332
                x1,
1333
                y1, // start of path
1334
                [1, 1],
1335
                style
1336
            );
1337
            return this;
1338
        };
1339
 
1340
        /**
1341
        Adds a rectangle with rounded corners to PDF
1342
 
1343
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1344
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1345
        @param {Number} w Width (in units declared at inception of PDF document)
1346
        @param {Number} h Height (in units declared at inception of PDF document)
1347
        @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
1348
        @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
1349
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1350
        @function
1351
        @returns {jsPDF}
1352
        @methodOf jsPDF#
1353
        @name roundedRect
1354
        */
1355
        API.roundedRect = function (x, y, w, h, rx, ry, style) {
1356
            var MyArc = 4 / 3 * (Math.SQRT2 - 1);
1357
            this.lines(
1358
                [
1359
                    [ (w - 2 * rx), 0 ],
1360
                    [ (rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry ],
1361
                    [ 0, (h - 2 * ry) ],
1362
                    [ 0, (ry * MyArc), -(rx * MyArc), ry, -rx, ry],
1363
                    [ (-w + 2 * rx), 0],
1364
                    [ -(rx * MyArc), 0, -rx, -(ry * MyArc), -rx, -ry],
1365
                    [ 0, (-h + 2 * ry)],
1366
                    [ 0, -(ry * MyArc), (rx * MyArc), -ry, rx, -ry]
1367
                ],
1368
                x + rx,
1369
                y, // start of path
1370
                [1, 1],
1371
                style
1372
            );
1373
            return this;
1374
        };
1375
 
1376
        /**
1377
        Adds an ellipse to PDF
1378
 
1379
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1380
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1381
        @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
1382
        @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
1383
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1384
        @function
1385
        @returns {jsPDF}
1386
        @methodOf jsPDF#
1387
        @name ellipse
1388
         */
1389
        API.ellipse = function (x, y, rx, ry, style) {
1390
            var op = getStyle(style),
1391
                lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
1392
                ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
1393
 
1394
            out([
1395
                f2((x + rx) * k),
1396
                f2((pageHeight - y) * k),
1397
                'm',
1398
                f2((x + rx) * k),
1399
                f2((pageHeight - (y - ly)) * k),
1400
                f2((x + lx) * k),
1401
                f2((pageHeight - (y - ry)) * k),
1402
                f2(x * k),
1403
                f2((pageHeight - (y - ry)) * k),
1404
                'c'
1405
            ].join(' '));
1406
            out([
1407
                f2((x - lx) * k),
1408
                f2((pageHeight - (y - ry)) * k),
1409
                f2((x - rx) * k),
1410
                f2((pageHeight - (y - ly)) * k),
1411
                f2((x - rx) * k),
1412
                f2((pageHeight - y) * k),
1413
                'c'
1414
            ].join(' '));
1415
            out([
1416
                f2((x - rx) * k),
1417
                f2((pageHeight - (y + ly)) * k),
1418
                f2((x - lx) * k),
1419
                f2((pageHeight - (y + ry)) * k),
1420
                f2(x * k),
1421
                f2((pageHeight - (y + ry)) * k),
1422
                'c'
1423
            ].join(' '));
1424
            out([
1425
                f2((x + lx) * k),
1426
                f2((pageHeight - (y + ry)) * k),
1427
                f2((x + rx) * k),
1428
                f2((pageHeight - (y + ly)) * k),
1429
                f2((x + rx) * k),
1430
                f2((pageHeight - y) * k),
1431
                'c',
1432
                op
1433
            ].join(' '));
1434
            return this;
1435
        };
1436
 
1437
        /**
1438
        Adds an circle to PDF
1439
 
1440
        @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1441
        @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1442
        @param {Number} r Radius (in units declared at inception of PDF document)
1443
        @param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
1444
        @function
1445
        @returns {jsPDF}
1446
        @methodOf jsPDF#
1447
        @name circle
1448
         */
1449
        API.circle = function (x, y, r, style) {
1450
            return this.ellipse(x, y, r, r, style);
1451
        };
1452
 
1453
        /**
1454
        Adds a properties to the PDF document
1455
 
1456
        @param {Object} A property_name-to-property_value object structure.
1457
        @function
1458
        @returns {jsPDF}
1459
        @methodOf jsPDF#
1460
        @name setProperties
1461
         */
1462
        API.setProperties = function (properties) {
1463
            // copying only those properties we can render.
1464
            var property;
1465
            for (property in documentProperties) {
1466
                if (documentProperties.hasOwnProperty(property) && properties[property]) {
1467
                    documentProperties[property] = properties[property];
1468
                }
1469
            }
1470
            return this;
1471
        };
1472
 
1473
        /**
1474
        Sets font size for upcoming text elements.
1475
 
1476
        @param {Number} size Font size in points.
1477
        @function
1478
        @returns {jsPDF}
1479
        @methodOf jsPDF#
1480
        @name setFontSize
1481
         */
1482
        API.setFontSize = function (size) {
1483
            activeFontSize = size;
1484
            return this;
1485
        };
1486
 
1487
        /**
1488
        Sets text font face, variant for upcoming text elements.
1489
        See output of jsPDF.getFontList() for possible font names, styles.
1490
 
1491
        @param {String} fontName Font name or family. Example: "times"
1492
        @param {String} fontStyle Font style or variant. Example: "italic"
1493
        @function
1494
        @returns {jsPDF}
1495
        @methodOf jsPDF#
1496
        @name setFont
1497
         */
1498
        API.setFont = function (fontName, fontStyle) {
1499
            activeFontKey = getFont(fontName, fontStyle);
1500
            // if font is not found, the above line blows up and we never go further
1501
            return this;
1502
        };
1503
 
1504
        /**
1505
        Switches font style or variant for upcoming text elements,
1506
        while keeping the font face or family same.
1507
        See output of jsPDF.getFontList() for possible font names, styles.
1508
 
1509
        @param {String} style Font style or variant. Example: "italic"
1510
        @function
1511
        @returns {jsPDF}
1512
        @methodOf jsPDF#
1513
        @name setFontStyle
1514
         */
1515
        API.setFontStyle = API.setFontType = function (style) {
1516
            var undef;
1517
            activeFontKey = getFont(undef, style);
1518
            // if font is not found, the above line blows up and we never go further
1519
            return this;
1520
        };
1521
 
1522
        /**
1523
        Returns an object - a tree of fontName to fontStyle relationships available to
1524
        active PDF document.
1525
 
1526
        @public
1527
        @function
1528
        @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
1529
        @methodOf jsPDF#
1530
        @name getFontList
1531
        */
1532
        API.getFontList = function () {
1533
            // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
1534
            var list = {},
1535
                fontName,
1536
                fontStyle,
1537
                tmp;
1538
 
1539
            for (fontName in fontmap) {
1540
                if (fontmap.hasOwnProperty(fontName)) {
1541
                    list[fontName] = tmp = [];
1542
                    for (fontStyle in fontmap[fontName]) {
1543
                        if (fontmap[fontName].hasOwnProperty(fontStyle)) {
1544
                            tmp.push(fontStyle);
1545
                        }
1546
                    }
1547
                }
1548
            }
1549
 
1550
            return list;
1551
        };
1552
 
1553
        /**
1554
        Sets line width for upcoming lines.
1555
 
1556
        @param {Number} width Line width (in units declared at inception of PDF document)
1557
        @function
1558
        @returns {jsPDF}
1559
        @methodOf jsPDF#
1560
        @name setLineWidth
1561
         */
1562
        API.setLineWidth = function (width) {
1563
            out((width * k).toFixed(2) + ' w');
1564
            return this;
1565
        };
1566
 
1567
        /**
1568
        Sets the stroke color for upcoming elements.
1569
 
1570
        Depending on the number of arguments given, Gray, RGB, or CMYK
1571
        color space is implied.
1572
 
1573
        When only ch1 is given, "Gray" color space is implied and it
1574
        must be a value in the range from 0.00 (solid black) to to 1.00 (white)
1575
        if values are communicated as String types, or in range from 0 (black)
1576
        to 255 (white) if communicated as Number type.
1577
        The RGB-like 0-255 range is provided for backward compatibility.
1578
 
1579
        When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
1580
        value must be in the range from 0.00 (minimum intensity) to to 1.00
1581
        (max intensity) if values are communicated as String types, or
1582
        from 0 (min intensity) to to 255 (max intensity) if values are communicated
1583
        as Number types.
1584
        The RGB-like 0-255 range is provided for backward compatibility.
1585
 
1586
        When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
1587
        value must be a in the range from 0.00 (0% concentration) to to
1588
        1.00 (100% concentration)
1589
 
1590
        Because JavaScript treats fixed point numbers badly (rounds to
1591
        floating point nearest to binary representation) it is highly advised to
1592
        communicate the fractional numbers as String types, not JavaScript Number type.
1593
 
1594
        @param {Number|String} ch1 Color channel value
1595
        @param {Number|String} ch2 Color channel value
1596
        @param {Number|String} ch3 Color channel value
1597
        @param {Number|String} ch4 Color channel value
1598
 
1599
        @function
1600
        @returns {jsPDF}
1601
        @methodOf jsPDF#
1602
        @name setDrawColor
1603
         */
1604
        API.setDrawColor = function (ch1, ch2, ch3, ch4) {
1605
            var color;
1606
            if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
1607
                // Gray color space.
1608
                if (typeof ch1 === 'string') {
1609
                    color = ch1 + ' G';
1610
                } else {
1611
                    color = f2(ch1 / 255) + ' G';
1612
                }
1613
            } else if (ch4 === undefined) {
1614
                // RGB
1615
                if (typeof ch1 === 'string') {
1616
                    color = [ch1, ch2, ch3, 'RG'].join(' ');
1617
                } else {
1618
                    color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' ');
1619
                }
1620
            } else {
1621
                // CMYK
1622
                if (typeof ch1 === 'string') {
1623
                    color = [ch1, ch2, ch3, ch4, 'K'].join(' ');
1624
                } else {
1625
                    color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' ');
1626
                }
1627
            }
1628
 
1629
            out(color);
1630
            return this;
1631
        };
1632
 
1633
        /**
1634
        Sets the fill color for upcoming elements.
1635
 
1636
        Depending on the number of arguments given, Gray, RGB, or CMYK
1637
        color space is implied.
1638
 
1639
        When only ch1 is given, "Gray" color space is implied and it
1640
        must be a value in the range from 0.00 (solid black) to to 1.00 (white)
1641
        if values are communicated as String types, or in range from 0 (black)
1642
        to 255 (white) if communicated as Number type.
1643
        The RGB-like 0-255 range is provided for backward compatibility.
1644
 
1645
        When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
1646
        value must be in the range from 0.00 (minimum intensity) to to 1.00
1647
        (max intensity) if values are communicated as String types, or
1648
        from 0 (min intensity) to to 255 (max intensity) if values are communicated
1649
        as Number types.
1650
        The RGB-like 0-255 range is provided for backward compatibility.
1651
 
1652
        When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
1653
        value must be a in the range from 0.00 (0% concentration) to to
1654
        1.00 (100% concentration)
1655
 
1656
        Because JavaScript treats fixed point numbers badly (rounds to
1657
        floating point nearest to binary representation) it is highly advised to
1658
        communicate the fractional numbers as String types, not JavaScript Number type.
1659
 
1660
        @param {Number|String} ch1 Color channel value
1661
        @param {Number|String} ch2 Color channel value
1662
        @param {Number|String} ch3 Color channel value
1663
        @param {Number|String} ch4 Color channel value
1664
 
1665
        @function
1666
        @returns {jsPDF}
1667
        @methodOf jsPDF#
1668
        @name setFillColor
1669
         */
1670
        API.setFillColor = function (ch1, ch2, ch3, ch4) {
1671
            var color;
1672
 
1673
            if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
1674
                // Gray color space.
1675
                if (typeof ch1 === 'string') {
1676
                    color = ch1 + ' g';
1677
                } else {
1678
                    color = f2(ch1 / 255) + ' g';
1679
                }
1680
            } else if (ch4 === undefined) {
1681
                // RGB
1682
                if (typeof ch1 === 'string') {
1683
                    color = [ch1, ch2, ch3, 'rg'].join(' ');
1684
                } else {
1685
                    color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' ');
1686
                }
1687
            } else {
1688
                // CMYK
1689
                if (typeof ch1 === 'string') {
1690
                    color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
1691
                } else {
1692
                    color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
1693
                }
1694
            }
1695
 
1696
            out(color);
1697
            return this;
1698
        };
1699
 
1700
        /**
1701
        Sets the text color for upcoming elements.
1702
        If only one, first argument is given,
1703
        treats the value as gray-scale color value.
1704
 
1705
        @param {Number} r Red channel color value in range 0-255
1706
        @param {Number} g Green channel color value in range 0-255
1707
        @param {Number} b Blue channel color value in range 0-255
1708
        @function
1709
        @returns {jsPDF}
1710
        @methodOf jsPDF#
1711
        @name setTextColor
1712
        */
1713
        API.setTextColor = function (r, g, b) {
1714
            if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
1715
                textColor = f3(r / 255) + ' g';
1716
            } else {
1717
                textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
1718
            }
1719
            return this;
1720
        };
1721
 
1722
        /**
1723
        Is an Object providing a mapping from human-readable to
1724
        integer flag values designating the varieties of line cap
1725
        and join styles.
1726
 
1727
        @returns {Object}
1728
        @fieldOf jsPDF#
1729
        @name CapJoinStyles
1730
        */
1731
        API.CapJoinStyles = {
1732
            0: 0,
1733
            'butt': 0,
1734
            'but': 0,
1735
            'bevel': 0,
1736
            1: 1,
1737
            'round': 1,
1738
            'rounded': 1,
1739
            'circle': 1,
1740
            2: 2,
1741
            'projecting': 2,
1742
            'project': 2,
1743
            'square': 2,
1744
            'milter': 2
1745
        };
1746
 
1747
        /**
1748
        Sets the line cap styles
1749
        See {jsPDF.CapJoinStyles} for variants
1750
 
1751
        @param {String|Number} style A string or number identifying the type of line cap
1752
        @function
1753
        @returns {jsPDF}
1754
        @methodOf jsPDF#
1755
        @name setLineCap
1756
        */
1757
        API.setLineCap = function (style) {
1758
            var id = this.CapJoinStyles[style];
1759
            if (id === undefined) {
1760
                throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
1761
            }
1762
            lineCapID = id;
1763
            out(id.toString(10) + ' J');
1764
 
1765
            return this;
1766
        };
1767
 
1768
        /**
1769
        Sets the line join styles
1770
        See {jsPDF.CapJoinStyles} for variants
1771
 
1772
        @param {String|Number} style A string or number identifying the type of line join
1773
        @function
1774
        @returns {jsPDF}
1775
        @methodOf jsPDF#
1776
        @name setLineJoin
1777
        */
1778
        API.setLineJoin = function (style) {
1779
            var id = this.CapJoinStyles[style];
1780
            if (id === undefined) {
1781
                throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
1782
            }
1783
            lineJoinID = id;
1784
            out(id.toString(10) + ' j');
1785
 
1786
            return this;
1787
        };
1788
 
1789
        // Output is both an internal (for plugins) and external function
1790
        API.output = output;
1791
 
1792
        /**
1793
         * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
1794
         * @param  {String} filename The filename including extension.
1795
         *
1796
         * @function
1797
         * @returns {jsPDF}
1798
         * @methodOf jsPDF#
1799
         * @name save
1800
         */
1801
        API.save = function (filename) {
1802
            API.output('save', filename);
1803
        };
1804
 
1805
        // applying plugins (more methods) ON TOP of built-in API.
1806
        // this is intentional as we allow plugins to override
1807
        // built-ins
1808
        for (plugin in jsPDF.API) {
1809
            if (jsPDF.API.hasOwnProperty(plugin)) {
1810
                if (plugin === 'events' && jsPDF.API.events.length) {
1811
                    (function (events, newEvents) {
1812
 
1813
                        // jsPDF.API.events is a JS Array of Arrays
1814
                        // where each Array is a pair of event name, handler
1815
                        // Events were added by plugins to the jsPDF instantiator.
1816
                        // These are always added to the new instance and some ran
1817
                        // during instantiation.
1818
 
1819
                        var eventname, handler_and_args, i;
1820
 
1821
                        for (i = newEvents.length - 1; i !== -1; i--) {
1822
                            // subscribe takes 3 args: 'topic', function, runonce_flag
1823
                            // if undefined, runonce is false.
1824
                            // users can attach callback directly,
1825
                            // or they can attach an array with [callback, runonce_flag]
1826
                            // that's what the "apply" magic is for below.
1827
                            eventname = newEvents[i][0];
1828
                            handler_and_args = newEvents[i][1];
1829
                            events.subscribe.apply(
1830
                                events,
1831
                                [eventname].concat(
1832
                                    typeof handler_and_args === 'function' ?
1833
                                            [ handler_and_args ] :
1834
                                            handler_and_args
1835
                                )
1836
                            );
1837
                        }
1838
                    }(events, jsPDF.API.events));
1839
                } else {
1840
                    API[plugin] = jsPDF.API[plugin];
1841
                }
1842
            }
1843
        }
1844
 
1845
        /////////////////////////////////////////
1846
        // continuing initilisation of jsPDF Document object
1847
        /////////////////////////////////////////
1848
 
1849
 
1850
        // Add the first page automatically
1851
        addFonts();
1852
        activeFontKey = 'F1';
1853
        _addPage();
1854
 
1855
        events.publish('initialized');
1856
 
1857
        return API;
1858
    }
1859
 
1860
/**
1861
jsPDF.API is a STATIC property of jsPDF class.
1862
jsPDF.API is an object you can add methods and properties to.
1863
The methods / properties you add will show up in new jsPDF objects.
1864
 
1865
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.
1866
Examples:
1867
    jsPDF.API.events['initialized'] = function(){ 'this' is API object }
1868
    jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
1869
 
1870
@static
1871
@public
1872
@memberOf jsPDF
1873
@name API
1874
 
1875
@example
1876
    jsPDF.API.mymethod = function(){
1877
        // 'this' will be ref to internal API object. see jsPDF source
1878
        // , so you can refer to built-in methods like so:
1879
        //     this.line(....)
1880
        //     this.text(....)
1881
    }
1882
    var pdfdoc = new jsPDF()
1883
    pdfdoc.mymethod() // <- !!!!!!
1884
*/
1885
    jsPDF.API = {'events': []};
1886
 
1887
    return jsPDF;
1888
}());