highcharts_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +106 -0
  9. data/Rakefile +6 -0
  10. data/highcharts_rails.gemspec +27 -0
  11. data/lib/highcharts_rails/version.rb +3 -0
  12. data/lib/highcharts_rails.rb +8 -0
  13. data/vendor/assets/javascripts/highcharts-3d.src.js +2139 -0
  14. data/vendor/assets/javascripts/highcharts-more.src.js +2982 -0
  15. data/vendor/assets/javascripts/highcharts.src.js +22947 -0
  16. data/vendor/assets/javascripts/js/highcharts-3d.src.js +2085 -0
  17. data/vendor/assets/javascripts/js/highcharts-more.src.js +2820 -0
  18. data/vendor/assets/javascripts/js/highcharts.src.js +20917 -0
  19. data/vendor/assets/javascripts/js/modules/accessibility.src.js +1072 -0
  20. data/vendor/assets/javascripts/js/modules/annotations.src.js +408 -0
  21. data/vendor/assets/javascripts/js/modules/boost.src.js +652 -0
  22. data/vendor/assets/javascripts/js/modules/broken-axis.src.js +338 -0
  23. data/vendor/assets/javascripts/js/modules/data.src.js +981 -0
  24. data/vendor/assets/javascripts/js/modules/drilldown.src.js +756 -0
  25. data/vendor/assets/javascripts/js/modules/exporting.src.js +953 -0
  26. data/vendor/assets/javascripts/js/modules/funnel.src.js +290 -0
  27. data/vendor/assets/javascripts/js/modules/gantt.src.js +791 -0
  28. data/vendor/assets/javascripts/js/modules/grid-axis.src.js +545 -0
  29. data/vendor/assets/javascripts/js/modules/heatmap.src.js +798 -0
  30. data/vendor/assets/javascripts/js/modules/no-data-to-display.src.js +150 -0
  31. data/vendor/assets/javascripts/js/modules/offline-exporting.src.js +492 -0
  32. data/vendor/assets/javascripts/js/modules/overlapping-datalabels.src.js +164 -0
  33. data/vendor/assets/javascripts/js/modules/series-label.src.js +606 -0
  34. data/vendor/assets/javascripts/js/modules/solid-gauge.src.js +305 -0
  35. data/vendor/assets/javascripts/js/modules/treemap.src.js +881 -0
  36. data/vendor/assets/javascripts/js/modules/xrange-series.src.js +254 -0
  37. data/vendor/assets/javascripts/js/themes/dark-blue.js +317 -0
  38. data/vendor/assets/javascripts/js/themes/dark-green.js +314 -0
  39. data/vendor/assets/javascripts/js/themes/dark-unica.js +243 -0
  40. data/vendor/assets/javascripts/js/themes/gray.js +326 -0
  41. data/vendor/assets/javascripts/js/themes/grid-light.js +99 -0
  42. data/vendor/assets/javascripts/js/themes/grid.js +131 -0
  43. data/vendor/assets/javascripts/js/themes/sand-signika.js +129 -0
  44. data/vendor/assets/javascripts/js/themes/skies.js +112 -0
  45. data/vendor/assets/javascripts/lib/canvg.src.js +3073 -0
  46. data/vendor/assets/javascripts/lib/jspdf.src.js +3031 -0
  47. data/vendor/assets/javascripts/lib/rgbcolor.src.js +299 -0
  48. data/vendor/assets/javascripts/lib/svg2pdf.src.js +1451 -0
  49. data/vendor/assets/javascripts/modules/accessibility.src.js +1072 -0
  50. data/vendor/assets/javascripts/modules/annotations.src.js +408 -0
  51. data/vendor/assets/javascripts/modules/boost.src.js +652 -0
  52. data/vendor/assets/javascripts/modules/broken-axis.src.js +338 -0
  53. data/vendor/assets/javascripts/modules/data.src.js +981 -0
  54. data/vendor/assets/javascripts/modules/drilldown.src.js +797 -0
  55. data/vendor/assets/javascripts/modules/exporting.src.js +882 -0
  56. data/vendor/assets/javascripts/modules/funnel.src.js +304 -0
  57. data/vendor/assets/javascripts/modules/gantt.src.js +815 -0
  58. data/vendor/assets/javascripts/modules/grid-axis.src.js +547 -0
  59. data/vendor/assets/javascripts/modules/heatmap.src.js +810 -0
  60. data/vendor/assets/javascripts/modules/no-data-to-display.src.js +161 -0
  61. data/vendor/assets/javascripts/modules/offline-exporting.src.js +492 -0
  62. data/vendor/assets/javascripts/modules/overlapping-datalabels.src.js +164 -0
  63. data/vendor/assets/javascripts/modules/series-label.src.js +606 -0
  64. data/vendor/assets/javascripts/modules/solid-gauge.src.js +316 -0
  65. data/vendor/assets/javascripts/modules/treemap.src.js +935 -0
  66. data/vendor/assets/javascripts/modules/xrange-series.src.js +276 -0
  67. data/vendor/assets/javascripts/themes/dark-blue.js +317 -0
  68. data/vendor/assets/javascripts/themes/dark-green.js +314 -0
  69. data/vendor/assets/javascripts/themes/dark-unica.js +243 -0
  70. data/vendor/assets/javascripts/themes/gray.js +326 -0
  71. data/vendor/assets/javascripts/themes/grid-light.js +99 -0
  72. data/vendor/assets/javascripts/themes/grid.js +131 -0
  73. data/vendor/assets/javascripts/themes/sand-signika.js +129 -0
  74. data/vendor/assets/javascripts/themes/skies.js +112 -0
  75. data/vendor/assets/stylesheets/highcharts.scss +610 -0
  76. metadata +161 -0
@@ -0,0 +1,3031 @@
1
+ /** @preserve
2
+ * jsPDF - PDF Document creation from JavaScript
3
+ * Version ${versionID}
4
+ * CommitID ${commitID}
5
+ *
6
+ * Copyright (c) 2010-2014 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF
7
+ * 2010 Aaron Spike, https://github.com/acspike
8
+ * 2012 Willow Systems Corporation, willow-systems.com
9
+ * 2012 Pablo Hess, https://github.com/pablohess
10
+ * 2012 Florian Jenett, https://github.com/fjenett
11
+ * 2013 Warren Weckesser, https://github.com/warrenweckesser
12
+ * 2013 Youssef Beddad, https://github.com/lifof
13
+ * 2013 Lee Driscoll, https://github.com/lsdriscoll
14
+ * 2013 Stefan Slonevskiy, https://github.com/stefslon
15
+ * 2013 Jeremy Morel, https://github.com/jmorel
16
+ * 2013 Christoph Hartmann, https://github.com/chris-rock
17
+ * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
18
+ * 2014 James Makes, https://github.com/dollaruw
19
+ * 2014 Diego Casorran, https://github.com/diegocr
20
+ * 2014 Steven Spungin, https://github.com/Flamenco
21
+ * 2014 Kenneth Glassey, https://github.com/Gavvers
22
+ *
23
+ * Permission is hereby granted, free of charge, to any person obtaining
24
+ * a copy of this software and associated documentation files (the
25
+ * "Software"), to deal in the Software without restriction, including
26
+ * without limitation the rights to use, copy, modify, merge, publish,
27
+ * distribute, sublicense, and/or sell copies of the Software, and to
28
+ * permit persons to whom the Software is furnished to do so, subject to
29
+ * the following conditions:
30
+ *
31
+ * The above copyright notice and this permission notice shall be
32
+ * included in all copies or substantial portions of the Software.
33
+ *
34
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
38
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
39
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41
+ *
42
+ * Contributor(s):
43
+ * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
44
+ * kim3er, mfo, alnorth, Flamenco
45
+ */
46
+
47
+ /**
48
+ * Creates new jsPDF document object instance.
49
+ *
50
+ * @class
51
+ * @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
52
+ * @param unit Measurement unit to be used when coordinates are specified.
53
+ * One of "pt" (points), "mm" (Default), "cm", "in"
54
+ * @param format One of 'pageFormats' as shown below, default: a4
55
+ * @returns {jsPDF}
56
+ * @name jsPDF
57
+ */
58
+ var jsPDF = (function(global) {
59
+ 'use strict';
60
+ var pdfVersion = '1.3',
61
+ pageFormats = { // Size in pt of various paper formats
62
+ 'a0' : [2383.94, 3370.39], 'a1' : [1683.78, 2383.94],
63
+ 'a2' : [1190.55, 1683.78], 'a3' : [ 841.89, 1190.55],
64
+ 'a4' : [ 595.28, 841.89], 'a5' : [ 419.53, 595.28],
65
+ 'a6' : [ 297.64, 419.53], 'a7' : [ 209.76, 297.64],
66
+ 'a8' : [ 147.40, 209.76], 'a9' : [ 104.88, 147.40],
67
+ 'a10' : [ 73.70, 104.88], 'b0' : [2834.65, 4008.19],
68
+ 'b1' : [2004.09, 2834.65], 'b2' : [1417.32, 2004.09],
69
+ 'b3' : [1000.63, 1417.32], 'b4' : [ 708.66, 1000.63],
70
+ 'b5' : [ 498.90, 708.66], 'b6' : [ 354.33, 498.90],
71
+ 'b7' : [ 249.45, 354.33], 'b8' : [ 175.75, 249.45],
72
+ 'b9' : [ 124.72, 175.75], 'b10' : [ 87.87, 124.72],
73
+ 'c0' : [2599.37, 3676.54], 'c1' : [1836.85, 2599.37],
74
+ 'c2' : [1298.27, 1836.85], 'c3' : [ 918.43, 1298.27],
75
+ 'c4' : [ 649.13, 918.43], 'c5' : [ 459.21, 649.13],
76
+ 'c6' : [ 323.15, 459.21], 'c7' : [ 229.61, 323.15],
77
+ 'c8' : [ 161.57, 229.61], 'c9' : [ 113.39, 161.57],
78
+ 'c10' : [ 79.37, 113.39], 'dl' : [ 311.81, 623.62],
79
+ 'letter' : [612, 792],
80
+ 'government-letter' : [576, 756],
81
+ 'legal' : [612, 1008],
82
+ 'junior-legal' : [576, 360],
83
+ 'ledger' : [1224, 792],
84
+ 'tabloid' : [792, 1224],
85
+ 'credit-card' : [153, 243]
86
+ };
87
+
88
+ /**
89
+ * jsPDF's Internal PubSub Implementation.
90
+ * See mrrio.github.io/jsPDF/doc/symbols/PubSub.html
91
+ * Backward compatible rewritten on 2014 by
92
+ * Diego Casorran, https://github.com/diegocr
93
+ *
94
+ * @class
95
+ * @name PubSub
96
+ */
97
+ function PubSub(context) {
98
+ var topics = {};
99
+
100
+ this.subscribe = function(topic, callback, once) {
101
+ if(typeof callback !== 'function') {
102
+ return false;
103
+ }
104
+
105
+ if(!topics.hasOwnProperty(topic)) {
106
+ topics[topic] = {};
107
+ }
108
+
109
+ var id = Math.random().toString(35);
110
+ topics[topic][id] = [callback,!!once];
111
+
112
+ return id;
113
+ };
114
+
115
+ this.unsubscribe = function(token) {
116
+ for(var topic in topics) {
117
+ if(topics[topic][token]) {
118
+ delete topics[topic][token];
119
+ return true;
120
+ }
121
+ }
122
+ return false;
123
+ };
124
+
125
+ this.publish = function(topic) {
126
+ if(topics.hasOwnProperty(topic)) {
127
+ var args = Array.prototype.slice.call(arguments, 1), idr = [];
128
+
129
+ for(var id in topics[topic]) {
130
+ var sub = topics[topic][id];
131
+ try {
132
+ sub[0].apply(context, args);
133
+ } catch(ex) {
134
+ if(global.console) {
135
+ console.error('jsPDF PubSub Error', ex.message, ex);
136
+ }
137
+ }
138
+ if(sub[1]) idr.push(id);
139
+ }
140
+ if(idr.length) idr.forEach(this.unsubscribe);
141
+ }
142
+ };
143
+ }
144
+
145
+ /**
146
+ * @constructor
147
+ * @private
148
+ */
149
+ function jsPDF(orientation, unit, format, compressPdf) {
150
+ var options = {};
151
+
152
+ if (typeof orientation === 'object') {
153
+ options = orientation;
154
+
155
+ orientation = options.orientation;
156
+ unit = options.unit || unit;
157
+ format = options.format || format;
158
+ compressPdf = options.compress || options.compressPdf || compressPdf;
159
+ }
160
+
161
+ // Default options
162
+ unit = unit || 'mm';
163
+ format = format || 'a4';
164
+ orientation = ('' + (orientation || 'P')).toLowerCase();
165
+
166
+ var format_as_string = ('' + format).toLowerCase(),
167
+ compress = !!compressPdf && typeof Uint8Array === 'function',
168
+ textColor = options.textColor || '0 g',
169
+ drawColor = options.drawColor || '0 G',
170
+ activeFontSize = options.fontSize || 16,
171
+ lineHeightProportion = options.lineHeight || 1.15,
172
+ lineWidth = options.lineWidth || 0.200025, // 2mm
173
+ objectNumber = 2, // 'n' Current object number
174
+ outToPages = !1, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
175
+ offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
176
+ fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font.
177
+ fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
178
+ activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle
179
+
180
+ fontStateStack = [], //
181
+
182
+ patterns = {}, // collection of pattern objects
183
+ patternMap = {}, // see fonts
184
+
185
+ gStates = {}, // collection of graphic state objects
186
+ gStatesMap = {}, // see fonts
187
+ activeGState = null,
188
+
189
+ k, // Scale factor
190
+ tmp,
191
+ page = 0,
192
+ currentPage,
193
+ pages = [],
194
+ pagesContext = [], // same index as pages and pagedim
195
+ pagedim = [],
196
+ content = [],
197
+ additionalObjects = [],
198
+ lineCapID = 0,
199
+ lineJoinID = 0,
200
+ content_length = 0,
201
+
202
+ renderTargets = {},
203
+ renderTargetMap = {},
204
+ renderTargetStack = [],
205
+
206
+ pageX, pageY, pageMatrix, // only used for FormObjects
207
+ pageWidth,
208
+ pageHeight,
209
+ pageMode,
210
+ zoomMode,
211
+ layoutMode,
212
+ documentProperties = {
213
+ 'title' : '',
214
+ 'subject' : '',
215
+ 'author' : '',
216
+ 'keywords' : '',
217
+ 'creator' : ''
218
+ },
219
+ API = {},
220
+ events = new PubSub(API),
221
+
222
+ /////////////////////
223
+ // Private functions
224
+ /////////////////////
225
+ f2 = function(number) {
226
+ return number.toFixed(2); // Ie, %.2f
227
+ },
228
+ f3 = function(number) {
229
+ return number.toFixed(3); // Ie, %.3f
230
+ },
231
+ padd2 = function(number) {
232
+ return ('0' + parseInt(number)).slice(-2);
233
+ },
234
+ padd2Hex = function (hexString) {
235
+ var s = "00" + hexString;
236
+ return s.substr(s.length - 2);
237
+ },
238
+ out = function(string) {
239
+ if (outToPages) {
240
+ /* set by beginPage */
241
+ pages[currentPage].push(string);
242
+ } else {
243
+ // +1 for '\n' that will be used to join 'content'
244
+ content_length += string.length + 1;
245
+ content.push(string);
246
+ }
247
+ },
248
+ newObject = function() {
249
+ // Begin a new object
250
+ objectNumber++;
251
+ offsets[objectNumber] = content_length;
252
+ out(objectNumber + ' 0 obj');
253
+ return objectNumber;
254
+ },
255
+ // Does not output the object until after the pages have been output.
256
+ // Returns an object containing the objectId and content.
257
+ // All pages have been added so the object ID can be estimated to start right after.
258
+ // This does not modify the current objectNumber; It must be updated after the newObjects are output.
259
+ newAdditionalObject = function() {
260
+ var objId = pages.length * 2 + 1;
261
+ objId += additionalObjects.length;
262
+ var obj = {objId:objId, content:''};
263
+ additionalObjects.push(obj);
264
+ return obj;
265
+ },
266
+ // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
267
+ newObjectDeferred = function() {
268
+ objectNumber++;
269
+ offsets[objectNumber] = function(){
270
+ return content_length;
271
+ };
272
+ return objectNumber;
273
+ },
274
+ newObjectDeferredBegin = function(oid) {
275
+ offsets[oid] = content_length;
276
+ },
277
+ putStream = function(str) {
278
+ out('stream');
279
+ out(str);
280
+ out('endstream');
281
+ },
282
+ putPages = function() {
283
+ var n,p,arr,i,deflater,adler32,adler32cs,wPt,hPt;
284
+
285
+ adler32cs = global.adler32cs || jsPDF.adler32cs;
286
+ if (compress && typeof adler32cs === 'undefined') {
287
+ compress = false;
288
+ }
289
+
290
+ // outToPages = false as set in endDocument(). out() writes to content.
291
+
292
+ for (n = 1; n <= page; n++) {
293
+ newObject();
294
+ wPt = (pageWidth = pagedim[n].width) * k;
295
+ hPt = (pageHeight = pagedim[n].height) * k;
296
+ out('<</Type /Page');
297
+ out('/Parent 1 0 R');
298
+ out('/Resources 2 0 R');
299
+ out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
300
+ // Added for annotation plugin
301
+ events.publish('putPage', {pageNumber: n, page: pages[n]});
302
+ out('/Contents ' + (objectNumber + 1) + ' 0 R');
303
+ out('>>');
304
+ out('endobj');
305
+
306
+ // Page content
307
+ p = pages[n].join('\n');
308
+
309
+ // prepend global change of basis matrix
310
+ // (Now, instead of converting every coordinate to the pdf coordinate system, we apply a matrix
311
+ // that does this job for us (however, texts, images and similar objects must be drawn bottom up))
312
+ p = new Matrix(k, 0, 0, -k, 0, pageHeight).toString() + " cm\n" + p;
313
+
314
+ newObject();
315
+ if (compress) {
316
+ arr = [];
317
+ i = p.length;
318
+ while(i--) {
319
+ arr[i] = p.charCodeAt(i);
320
+ }
321
+ adler32 = adler32cs.from(p);
322
+ deflater = new Deflater(6);
323
+ deflater.append(new Uint8Array(arr));
324
+ p = deflater.flush();
325
+ arr = new Uint8Array(p.length + 6);
326
+ arr.set(new Uint8Array([120, 156]));
327
+ arr.set(p, 2);
328
+ arr.set(new Uint8Array([adler32 & 0xFF, (adler32 >> 8) & 0xFF, (adler32 >> 16) & 0xFF, (adler32 >> 24) & 0xFF]), p.length+2);
329
+ p = String.fromCharCode.apply(null, arr);
330
+ out('<</Length ' + p.length + ' /Filter [/FlateDecode]>>');
331
+ } else {
332
+ out('<</Length ' + p.length + '>>');
333
+ }
334
+ putStream(p);
335
+ out('endobj');
336
+ }
337
+ offsets[1] = content_length;
338
+ out('1 0 obj');
339
+ out('<</Type /Pages');
340
+ var kids = '/Kids [';
341
+ for (i = 0; i < page; i++) {
342
+ kids += (3 + 2 * i) + ' 0 R ';
343
+ }
344
+ out(kids + ']');
345
+ out('/Count ' + page);
346
+ out('>>');
347
+ out('endobj');
348
+ events.publish('postPutPages');
349
+ },
350
+ putFont = function(font) {
351
+ font.objectNumber = newObject();
352
+ out('<</BaseFont/' + font.PostScriptName + '/Type/Font');
353
+ if (typeof font.encoding === 'string') {
354
+ out('/Encoding/' + font.encoding);
355
+ }
356
+ out('/Subtype/Type1>>');
357
+ out('endobj');
358
+ },
359
+ putFonts = function() {
360
+ for (var fontKey in fonts) {
361
+ if (fonts.hasOwnProperty(fontKey)) {
362
+ putFont(fonts[fontKey]);
363
+ }
364
+ }
365
+ },
366
+ putXObject = function (xObject) {
367
+ xObject.objectNumber = newObject();
368
+ out("<<");
369
+ out("/Type /XObject");
370
+ out("/Subtype /Form");
371
+ out("/BBox [" + [
372
+ f2(xObject.x),
373
+ f2(xObject.y),
374
+ f2(xObject.x + xObject.width),
375
+ f2(xObject.y + xObject.height)
376
+ ].join(" ") + "]");
377
+ out("/Matrix [" + xObject.matrix.toString() + "]");
378
+ // TODO: /Resources
379
+
380
+ var p = xObject.pages[1].join("\n");
381
+ out("/Length " + p.length);
382
+
383
+ out(">>");
384
+ putStream(p);
385
+ out("endobj");
386
+ },
387
+ putXObjects = function () {
388
+ for (var xObjectKey in renderTargets) {
389
+ if (renderTargets.hasOwnProperty(xObjectKey)) {
390
+ putXObject(renderTargets[xObjectKey]);
391
+ }
392
+ }
393
+ },
394
+
395
+ interpolateAndEncodeRGBStream = function (colors, numberSamples) {
396
+ var tValues = [];
397
+ var t;
398
+ var dT = 1.0 / (numberSamples - 1);
399
+ for (t = 0.0; t < 1.0; t += dT) {
400
+ tValues.push(t);
401
+ }
402
+ tValues.push(1.0);
403
+
404
+ // add first and last control point if not present
405
+ if (colors[0].offset != 0.0) {
406
+ var c0 = {
407
+ offset: 0.0,
408
+ color: colors[0].color
409
+ };
410
+ colors.unshift(c0)
411
+ }
412
+ if (colors[colors.length - 1].offset != 1.0) {
413
+ var c1 = {
414
+ offset: 1.0,
415
+ color: colors[colors.length - 1].color
416
+ };
417
+ colors.push(c1);
418
+ }
419
+
420
+ var out = "";
421
+ var index = 0;
422
+
423
+ for (var i = 0; i < tValues.length; i++) {
424
+ t = tValues[i];
425
+
426
+ while (t > colors[index + 1].offset)
427
+ index++;
428
+
429
+ var a = colors[index].offset;
430
+ var b = colors[index + 1].offset;
431
+ var d = (t - a) / (b - a);
432
+
433
+ var aColor = colors[index].color;
434
+ var bColor = colors[index + 1].color;
435
+
436
+ out += padd2Hex((Math.round((1 - d) * aColor[0] + d * bColor[0])).toString(16))
437
+ + padd2Hex((Math.round((1 - d) * aColor[1] + d * bColor[1])).toString(16))
438
+ + padd2Hex((Math.round((1 - d) * aColor[2] + d * bColor[2])).toString(16));
439
+ }
440
+ return out.trim();
441
+ },
442
+ putShadingPattern = function (pattern, numberSamples) {
443
+ /*
444
+ Axial patterns shade between the two points specified in coords, radial patterns between the inner
445
+ and outer circle.
446
+
447
+ The user can specify an array (colors) that maps t-Values in [0, 1] to RGB colors. These are now
448
+ interpolated to equidistant samples and written to pdf as a sample (type 0) function.
449
+ */
450
+
451
+ // The number of color samples that should be used to describe the shading.
452
+ // The higher, the more accurate the gradient will be.
453
+ numberSamples || (numberSamples = 21);
454
+
455
+ var funcObjectNumber = newObject();
456
+ var stream = interpolateAndEncodeRGBStream(pattern.colors, numberSamples);
457
+ out("<< /FunctionType 0");
458
+ out("/Domain [0.0 1.0]");
459
+ out("/Size [" + numberSamples + "]");
460
+ out("/BitsPerSample 8");
461
+ out("/Range [0.0 1.0 0.0 1.0 0.0 1.0]");
462
+ out("/Decode [0.0 1.0 0.0 1.0 0.0 1.0]");
463
+ out("/Length " + stream.length);
464
+ // The stream is Hex encoded
465
+ out("/Filter /ASCIIHexDecode");
466
+ out(">>");
467
+ putStream(stream);
468
+ out("endobj");
469
+
470
+ pattern.objectNumber = newObject();
471
+ out("<< /ShadingType " + pattern.type);
472
+ out("/ColorSpace /DeviceRGB");
473
+
474
+ var coords = "/Coords ["
475
+ + f3(parseFloat(pattern.coords[0])) + " "// x1
476
+ + f3(parseFloat(pattern.coords[1])) + " "; // y1
477
+ if (pattern.type === 2) {
478
+ // axial
479
+ coords += f3(parseFloat(pattern.coords[2])) + " " // x2
480
+ + f3(parseFloat(pattern.coords[3])); // y2
481
+ } else {
482
+ // radial
483
+ coords += f3(parseFloat(pattern.coords[2])) + " "// r1
484
+ + f3(parseFloat(pattern.coords[3])) + " " // x2
485
+ + f3(parseFloat(pattern.coords[4])) + " " // y2
486
+ + f3(parseFloat(pattern.coords[5])); // r2
487
+ }
488
+ coords += "]";
489
+ out(coords);
490
+
491
+ if (pattern.matrix) {
492
+ out("/Matrix [" + pattern.matrix.toString() + "]");
493
+ }
494
+
495
+ out("/Function " + funcObjectNumber + " 0 R");
496
+ out("/Extend [true true]");
497
+ out(">>");
498
+ out("endobj");
499
+ },
500
+ putTilingPattern = function (pattern) {
501
+ var resourcesObjectNumber = newObject();
502
+ out("<<");
503
+ putResourceDictionary();
504
+ out(">>");
505
+ out("endobj");
506
+
507
+ pattern.objectNumber = newObject();
508
+ out("<< /Type /Pattern");
509
+ out("/PatternType 1"); // tiling pattern
510
+ out("/PaintType 1"); // colored tiling pattern
511
+ out("/TilingType 1"); // constant spacing
512
+ out("/BBox [" + pattern.boundingBox.map(f3).join(" ") + "]");
513
+ out("/XStep " + f3(pattern.xStep));
514
+ out("/YStep " + f3(pattern.yStep));
515
+ out("/Length " + pattern.stream.length);
516
+ out("/Resources " + resourcesObjectNumber + " 0 R"); // TODO: resources
517
+ pattern.matrix && out("/Matrix [" + pattern.matrix.toString() + "]");
518
+
519
+ out(">>");
520
+
521
+ putStream(pattern.stream);
522
+
523
+ out("endobj");
524
+ },
525
+ putPatterns = function () {
526
+ var patternKey;
527
+ for (patternKey in patterns) {
528
+ if (patterns.hasOwnProperty(patternKey)) {
529
+ if (patterns[patternKey] instanceof API.ShadingPattern) {
530
+ putShadingPattern(patterns[patternKey]);
531
+ } else if (patterns[patternKey] instanceof API.TilingPattern) {
532
+ putTilingPattern(patterns[patternKey]);
533
+ }
534
+ }
535
+ }
536
+ },
537
+
538
+ putGState = function (gState) {
539
+ gState.objectNumber = newObject();
540
+ out("<<");
541
+ for (var p in gState) {
542
+ switch (p) {
543
+ case "opacity":
544
+ out("/ca " + f2(gState[p]));
545
+ break;
546
+ }
547
+ }
548
+ out(">>");
549
+ out("endobj");
550
+ },
551
+ putGStates = function () {
552
+ var gStateKey;
553
+ for (gStateKey in gStates) {
554
+ if (gStates.hasOwnProperty(gStateKey)) {
555
+ putGState(gStates[gStateKey]);
556
+ }
557
+ }
558
+ },
559
+
560
+ putXobjectDict = function () {
561
+ for (var xObjectKey in renderTargets) {
562
+ if (renderTargets.hasOwnProperty(xObjectKey) && renderTargets[xObjectKey].objectNumber >= 0) {
563
+ out("/" + xObjectKey + " " + renderTargets[xObjectKey].objectNumber + " 0 R");
564
+ }
565
+ }
566
+
567
+ events.publish('putXobjectDict');
568
+ },
569
+
570
+ putShadingPatternDict = function () {
571
+ for (var patternKey in patterns) {
572
+ if (patterns.hasOwnProperty(patternKey) && patterns[patternKey] instanceof API.ShadingPattern && patterns[patternKey].objectNumber >= 0) {
573
+ out("/" + patternKey + " " + patterns[patternKey].objectNumber + " 0 R");
574
+ }
575
+ }
576
+
577
+ events.publish("putShadingPatternDict");
578
+ },
579
+
580
+ putTilingPatternDict = function () {
581
+ for (var patternKey in patterns) {
582
+ if (patterns.hasOwnProperty(patternKey) && patterns[patternKey] instanceof API.TilingPattern && patterns[patternKey].objectNumber >= 0) {
583
+ out("/" + patternKey + " " + patterns[patternKey].objectNumber + " 0 R");
584
+ }
585
+ }
586
+
587
+ events.publish("putTilingPatternDict");
588
+ },
589
+
590
+ putGStatesDict = function () {
591
+ var gStateKey;
592
+ for (gStateKey in gStates) {
593
+ if (gStates.hasOwnProperty(gStateKey) && gStates[gStateKey].objectNumber >= 0) {
594
+ out("/" + gStateKey + " " + gStates[gStateKey].objectNumber + " 0 R");
595
+ }
596
+ }
597
+
598
+ events.publish("putGStateDict");
599
+ },
600
+ putResourceDictionary = function() {
601
+ out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
602
+ out('/Font <<');
603
+ // Do this for each font, the '1' bit is the index of the font
604
+ for (var fontKey in fonts) {
605
+ if (fonts.hasOwnProperty(fontKey)) {
606
+ out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
607
+ }
608
+ }
609
+ out('>>');
610
+
611
+ out("/Shading <<");
612
+ putShadingPatternDict();
613
+ out(">>");
614
+
615
+ out("/Pattern <<");
616
+ putTilingPatternDict();
617
+ out(">>");
618
+
619
+ out("/ExtGState <<");
620
+ putGStatesDict();
621
+ out('>>');
622
+
623
+ out('/XObject <<');
624
+ putXobjectDict();
625
+ out('>>');
626
+ },
627
+ putResources = function() {
628
+ putFonts();
629
+ putGStates();
630
+ putXObjects();
631
+ putPatterns();
632
+ events.publish('putResources');
633
+ // Resource dictionary
634
+ offsets[2] = content_length;
635
+ out('2 0 obj');
636
+ out('<<');
637
+ putResourceDictionary();
638
+ out('>>');
639
+ out('endobj');
640
+ events.publish('postPutResources');
641
+ },
642
+ putAdditionalObjects = function() {
643
+ events.publish('putAdditionalObjects');
644
+ for (var i=0; i<additionalObjects.length; i++){
645
+ var obj = additionalObjects[i];
646
+ offsets[obj.objId] = content_length;
647
+ out( obj.objId + ' 0 obj');
648
+ out(obj.content);
649
+ out('endobj');
650
+ }
651
+ objectNumber += additionalObjects.length;
652
+ events.publish('postPutAdditionalObjects');
653
+ },
654
+ addToFontDictionary = function(fontKey, fontName, fontStyle) {
655
+ // this is mapping structure for quick font key lookup.
656
+ // returns the KEY of the font (ex: "F1") for a given
657
+ // pair of font name and type (ex: "Arial". "Italic")
658
+ if (!fontmap.hasOwnProperty(fontName)) {
659
+ fontmap[fontName] = {};
660
+ }
661
+ fontmap[fontName][fontStyle] = fontKey;
662
+ },
663
+ /**
664
+ * FontObject describes a particular font as member of an instnace of jsPDF
665
+ *
666
+ * It's a collection of properties like 'id' (to be used in PDF stream),
667
+ * 'fontName' (font's family name), 'fontStyle' (font's style variant label)
668
+ *
669
+ * @public
670
+ * @property id {String} PDF-document-instance-specific label assinged to the font.
671
+ * @property PostScriptName {String} PDF specification full name for the font
672
+ * @property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
673
+ * @name FontObject
674
+ */
675
+ addFont = function(PostScriptName, fontName, fontStyle, encoding) {
676
+ var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10),
677
+ // This is FontObject
678
+ font = fonts[fontKey] = {
679
+ 'id' : fontKey,
680
+ 'PostScriptName' : PostScriptName,
681
+ 'fontName' : fontName,
682
+ 'fontStyle' : fontStyle,
683
+ 'encoding' : encoding,
684
+ 'metadata' : {}
685
+ };
686
+ addToFontDictionary(fontKey, fontName, fontStyle);
687
+ events.publish('addFont', font);
688
+
689
+ return fontKey;
690
+ },
691
+ addFonts = function() {
692
+
693
+ var HELVETICA = "helvetica",
694
+ TIMES = "times",
695
+ COURIER = "courier",
696
+ NORMAL = "normal",
697
+ BOLD = "bold",
698
+ ITALIC = "italic",
699
+ BOLD_ITALIC = "bolditalic",
700
+ encoding = 'StandardEncoding',
701
+ ZAPF = "zapfdingbats",
702
+ standardFonts = [
703
+ ['Helvetica', HELVETICA, NORMAL],
704
+ ['Helvetica-Bold', HELVETICA, BOLD],
705
+ ['Helvetica-Oblique', HELVETICA, ITALIC],
706
+ ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC],
707
+ ['Courier', COURIER, NORMAL],
708
+ ['Courier-Bold', COURIER, BOLD],
709
+ ['Courier-Oblique', COURIER, ITALIC],
710
+ ['Courier-BoldOblique', COURIER, BOLD_ITALIC],
711
+ ['Times-Roman', TIMES, NORMAL],
712
+ ['Times-Bold', TIMES, BOLD],
713
+ ['Times-Italic', TIMES, ITALIC],
714
+ ['Times-BoldItalic', TIMES, BOLD_ITALIC],
715
+ ['ZapfDingbats',ZAPF ]
716
+ ];
717
+
718
+ for (var i = 0, l = standardFonts.length; i < l; i++) {
719
+ var fontKey = addFont(
720
+ standardFonts[i][0],
721
+ standardFonts[i][1],
722
+ standardFonts[i][2],
723
+ encoding);
724
+
725
+ // adding aliases for standard fonts, this time matching the capitalization
726
+ var parts = standardFonts[i][0].split('-');
727
+ addToFontDictionary(fontKey, parts[0], parts[1] || '');
728
+ }
729
+ events.publish('addFonts', { fonts : fonts, dictionary : fontmap });
730
+ },
731
+ matrixMult = function (m1, m2) {
732
+ return new Matrix(
733
+ m1.a * m2.a + m1.b * m2.c,
734
+ m1.a * m2.b + m1.b * m2.d,
735
+ m1.c * m2.a + m1.d * m2.c,
736
+ m1.c * m2.b + m1.d * m2.d,
737
+ m1.e * m2.a + m1.f * m2.c + m2.e,
738
+ m1.e * m2.b + m1.f * m2.d + m2.f
739
+ );
740
+ },
741
+ Matrix = function (a, b, c, d, e, f) {
742
+ this.a = a;
743
+ this.b = b;
744
+ this.c = c;
745
+ this.d = d;
746
+ this.e = e;
747
+ this.f = f;
748
+ };
749
+
750
+ Matrix.prototype = {
751
+ toString: function () {
752
+ return [
753
+ f3(this.a),
754
+ f3(this.b),
755
+ f3(this.c),
756
+ f3(this.d),
757
+ f3(this.e),
758
+ f3(this.f)
759
+ ].join(" ");
760
+ }
761
+ };
762
+
763
+ var unitMatrix = new Matrix(1, 0, 0, 1, 0, 0),
764
+
765
+ // Used (1) to save the current stream state to the XObjects stack and (2) to save completed form
766
+ // objects in the xObjects map.
767
+ RenderTarget = function () {
768
+ this.page = page;
769
+ this.currentPage = currentPage;
770
+ this.pages = pages.slice(0);
771
+ this.pagedim = pagedim.slice(0);
772
+ this.pagesContext = pagesContext.slice(0);
773
+ this.x = pageX;
774
+ this.y = pageY;
775
+ this.matrix = pageMatrix;
776
+ this.width = pageWidth;
777
+ this.height = pageHeight;
778
+
779
+ this.id = ""; // set by endFormObject()
780
+ this.objectNumber = -1; // will be set by putXObject()
781
+ };
782
+
783
+ RenderTarget.prototype = {
784
+ restore: function () {
785
+ page = this.page;
786
+ currentPage = this.currentPage;
787
+ pagesContext = this.pagesContext;
788
+ pagedim = this.pagedim;
789
+ pages = this.pages;
790
+ pageX = this.x;
791
+ pageY = this.y;
792
+ pageMatrix = this.matrix;
793
+ pageWidth = this.width;
794
+ pageHeight = this.height;
795
+ }
796
+ };
797
+
798
+ var beginNewRenderTarget = function (x, y, width, height, matrix) {
799
+ // save current state
800
+ renderTargetStack.push(new RenderTarget());
801
+
802
+ // clear pages
803
+ page = currentPage = 0;
804
+ pages = [];
805
+ pageX = x;
806
+ pageY = y;
807
+
808
+ pageMatrix = matrix;
809
+
810
+ beginPage(width, height);
811
+ },
812
+
813
+ endFormObject = function (key) {
814
+ // only add it if it is not already present (the keys provided by the user must be unique!)
815
+ if (renderTargetMap[key])
816
+ return;
817
+
818
+ // save the created xObject
819
+ var newXObject = new RenderTarget();
820
+
821
+ var xObjectId = 'Xo' + (Object.keys(renderTargets).length + 1).toString(10);
822
+ newXObject.id = xObjectId;
823
+
824
+ renderTargetMap[key] = xObjectId;
825
+ renderTargets[xObjectId] = newXObject;
826
+
827
+ events.publish('addFormObject', newXObject);
828
+
829
+ // restore state from stack
830
+ renderTargetStack.pop().restore();
831
+ },
832
+
833
+ /**
834
+ * Adds a new pattern for later use.
835
+ * @param {String} key The key by it can be referenced later. The keys must be unique!
836
+ * @param {API.Pattern} pattern The pattern
837
+ */
838
+ addPattern = function (key, pattern) {
839
+ // only add it if it is not already present (the keys provided by the user must be unique!)
840
+ if (patternMap[key])
841
+ return;
842
+
843
+ var prefix = pattern instanceof API.ShadingPattern ? "Sh" : "P";
844
+ var patternKey = prefix + (Object.keys(patterns).length + 1).toString(10);
845
+ pattern.id = patternKey;
846
+
847
+ patternMap[key] = patternKey;
848
+ patterns[patternKey] = pattern;
849
+
850
+ events.publish('addPattern', pattern);
851
+ },
852
+
853
+ /**
854
+ * Adds a new Graphics State. Duplicates are automatically eliminated.
855
+ * @param {String} key Might also be null, if no later reference to this gState is needed
856
+ * @param {Object} gState The gState object
857
+ */
858
+ addGState = function (key, gState) {
859
+ // only add it if it is not already present (the keys provided by the user must be unique!)
860
+ if (key && gStatesMap[key])
861
+ return;
862
+
863
+ var duplicate = false;
864
+ for (var s in gStates) {
865
+ if (gStates.hasOwnProperty(s)) {
866
+ if (gStates[s].equals(gState)) {
867
+ duplicate = true;
868
+ break;
869
+ }
870
+ }
871
+ }
872
+
873
+ if (duplicate) {
874
+ gState = gStates[s];
875
+ } else {
876
+ var gStateKey = 'GS' + (Object.keys(gStates).length + 1).toString(10);
877
+ gStates[gStateKey] = gState;
878
+ gState.id = gStateKey;
879
+ }
880
+
881
+ // several user keys may point to the same GState object
882
+ key && (gStatesMap[key] = gState.id);
883
+
884
+ events.publish('addGState', gState);
885
+
886
+ return gState;
887
+ },
888
+ SAFE = function __safeCall(fn) {
889
+ fn.foo = function __safeCallWrapper() {
890
+ try {
891
+ return fn.apply(this, arguments);
892
+ } catch (e) {
893
+ var stack = e.stack || '';
894
+ if(~stack.indexOf(' at ')) stack = stack.split(" at ")[1];
895
+ var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message;
896
+ if(global.console) {
897
+ global.console.error(m, e);
898
+ if(global.alert) alert(m);
899
+ } else {
900
+ throw new Error(m);
901
+ }
902
+ }
903
+ };
904
+ fn.foo.bar = fn;
905
+ return fn.foo;
906
+ },
907
+ to8bitStream = function(text, flags) {
908
+ /**
909
+ * PDF 1.3 spec:
910
+ * "For text strings encoded in Unicode, the first two bytes must be 254 followed by
911
+ * 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
912
+ * with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
913
+ * to be a meaningful beginning of a word or phrase.) The remainder of the
914
+ * string consists of Unicode character codes, according to the UTF-16 encoding
915
+ * specified in the Unicode standard, version 2.0. Commonly used Unicode values
916
+ * are represented as 2 bytes per character, with the high-order byte appearing first
917
+ * in the string."
918
+ *
919
+ * In other words, if there are chars in a string with char code above 255, we
920
+ * recode the string to UCS2 BE - string doubles in length and BOM is prepended.
921
+ *
922
+ * HOWEVER!
923
+ * Actual *content* (body) text (as opposed to strings used in document properties etc)
924
+ * does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
925
+ *
926
+ * Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
927
+ * a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
928
+ * fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
929
+ * code page. There, however, all characters in the stream are treated as GIDs,
930
+ * including BOM, which is the reason we need to skip BOM in content text (i.e. that
931
+ * that is tied to a font).
932
+ *
933
+ * To signal this "special" PDFEscape / to8bitStream handling mode,
934
+ * API.text() function sets (unless you overwrite it with manual values
935
+ * given to API.text(.., flags) )
936
+ * flags.autoencode = true
937
+ * flags.noBOM = true
938
+ *
939
+ * ===================================================================================
940
+ * `flags` properties relied upon:
941
+ * .sourceEncoding = string with encoding label.
942
+ * "Unicode" by default. = encoding of the incoming text.
943
+ * pass some non-existing encoding name
944
+ * (ex: 'Do not touch my strings! I know what I am doing.')
945
+ * to make encoding code skip the encoding step.
946
+ * .outputEncoding = Either valid PDF encoding name
947
+ * (must be supported by jsPDF font metrics, otherwise no encoding)
948
+ * or a JS object, where key = sourceCharCode, value = outputCharCode
949
+ * missing keys will be treated as: sourceCharCode === outputCharCode
950
+ * .noBOM
951
+ * See comment higher above for explanation for why this is important
952
+ * .autoencode
953
+ * See comment higher above for explanation for why this is important
954
+ */
955
+
956
+ var i,l,sourceEncoding,encodingBlock,outputEncoding,newtext,isUnicode,ch,bch;
957
+
958
+ flags = flags || {};
959
+ sourceEncoding = flags.sourceEncoding || 'Unicode';
960
+ outputEncoding = flags.outputEncoding;
961
+
962
+ // This 'encoding' section relies on font metrics format
963
+ // attached to font objects by, among others,
964
+ // "Willow Systems' standard_font_metrics plugin"
965
+ // see jspdf.plugin.standard_font_metrics.js for format
966
+ // of the font.metadata.encoding Object.
967
+ // It should be something like
968
+ // .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
969
+ // .widths = {0:width, code:width, ..., 'fof':divisor}
970
+ // .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
971
+ if ((flags.autoencode || outputEncoding) &&
972
+ fonts[activeFontKey].metadata &&
973
+ fonts[activeFontKey].metadata[sourceEncoding] &&
974
+ fonts[activeFontKey].metadata[sourceEncoding].encoding) {
975
+ encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding;
976
+
977
+ // each font has default encoding. Some have it clearly defined.
978
+ if (!outputEncoding && fonts[activeFontKey].encoding) {
979
+ outputEncoding = fonts[activeFontKey].encoding;
980
+ }
981
+
982
+ // Hmmm, the above did not work? Let's try again, in different place.
983
+ if (!outputEncoding && encodingBlock.codePages) {
984
+ outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
985
+ }
986
+
987
+ if (typeof outputEncoding === 'string') {
988
+ outputEncoding = encodingBlock[outputEncoding];
989
+ }
990
+ // we want output encoding to be a JS Object, where
991
+ // key = sourceEncoding's character code and
992
+ // value = outputEncoding's character code.
993
+ if (outputEncoding) {
994
+ isUnicode = false;
995
+ newtext = [];
996
+ for (i = 0, l = text.length; i < l; i++) {
997
+ ch = outputEncoding[text.charCodeAt(i)];
998
+ if (ch) {
999
+ newtext.push(
1000
+ String.fromCharCode(ch));
1001
+ } else {
1002
+ newtext.push(
1003
+ text[i]);
1004
+ }
1005
+
1006
+ // since we are looping over chars anyway, might as well
1007
+ // check for residual unicodeness
1008
+ if (newtext[i].charCodeAt(0) >> 8) {
1009
+ /* more than 255 */
1010
+ isUnicode = true;
1011
+ }
1012
+ }
1013
+ text = newtext.join('');
1014
+ }
1015
+ }
1016
+
1017
+ i = text.length;
1018
+ // isUnicode may be set to false above. Hence the triple-equal to undefined
1019
+ while (isUnicode === undefined && i !== 0) {
1020
+ if (text.charCodeAt(i - 1) >> 8) {
1021
+ /* more than 255 */
1022
+ isUnicode = true;
1023
+ }
1024
+ i--;
1025
+ }
1026
+ if (!isUnicode) {
1027
+ return text;
1028
+ }
1029
+
1030
+ newtext = flags.noBOM ? [] : [254, 255];
1031
+ for (i = 0, l = text.length; i < l; i++) {
1032
+ ch = text.charCodeAt(i);
1033
+ bch = ch >> 8; // divide by 256
1034
+ if (bch >> 8) {
1035
+ /* something left after dividing by 256 second time */
1036
+ throw new Error("Character at position " + i + " of string '"
1037
+ + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
1038
+ }
1039
+ newtext.push(bch);
1040
+ newtext.push(ch - (bch << 8));
1041
+ }
1042
+ return String.fromCharCode.apply(undefined, newtext);
1043
+ },
1044
+ pdfEscape = function(text, flags) {
1045
+ /**
1046
+ * Replace '/', '(', and ')' with pdf-safe versions
1047
+ *
1048
+ * Doing to8bitStream does NOT make this PDF display unicode text. For that
1049
+ * we also need to reference a unicode font and embed it - royal pain in the rear.
1050
+ *
1051
+ * There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
1052
+ * which JavaScript Strings are happy to provide. So, while we still cannot display
1053
+ * 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
1054
+ * 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
1055
+ * is still parseable.
1056
+ * This will allow immediate support for unicode in document properties strings.
1057
+ */
1058
+ return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
1059
+ },
1060
+ putInfo = function() {
1061
+ out('/Producer (jsPDF ' + jsPDF.version + ')');
1062
+ for(var key in documentProperties) {
1063
+ if(documentProperties.hasOwnProperty(key) && documentProperties[key]) {
1064
+ out('/'+key.substr(0,1).toUpperCase() + key.substr(1)
1065
+ +' (' + pdfEscape(documentProperties[key]) + ')');
1066
+ }
1067
+ }
1068
+ var created = new Date(),
1069
+ tzoffset = created.getTimezoneOffset(),
1070
+ tzsign = tzoffset < 0 ? '+' : '-',
1071
+ tzhour = Math.floor(Math.abs(tzoffset / 60)),
1072
+ tzmin = Math.abs(tzoffset % 60),
1073
+ tzstr = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join('');
1074
+ out(['/CreationDate (D:',
1075
+ created.getFullYear(),
1076
+ padd2(created.getMonth() + 1),
1077
+ padd2(created.getDate()),
1078
+ padd2(created.getHours()),
1079
+ padd2(created.getMinutes()),
1080
+ padd2(created.getSeconds()), tzstr, ')'].join(''));
1081
+ },
1082
+ putCatalog = function() {
1083
+ out('/Type /Catalog');
1084
+ out('/Pages 1 0 R');
1085
+ // PDF13ref Section 7.2.1
1086
+ if (!zoomMode) zoomMode = 'fullwidth';
1087
+ switch(zoomMode) {
1088
+ case 'fullwidth' : out('/OpenAction [3 0 R /FitH null]'); break;
1089
+ case 'fullheight' : out('/OpenAction [3 0 R /FitV null]'); break;
1090
+ case 'fullpage' : out('/OpenAction [3 0 R /Fit]'); break;
1091
+ case 'original' : out('/OpenAction [3 0 R /XYZ null null 1]'); break;
1092
+ default:
1093
+ var pcn = '' + zoomMode;
1094
+ if (pcn.substr(pcn.length-1) === '%')
1095
+ zoomMode = parseInt(zoomMode) / 100;
1096
+ if (typeof zoomMode === 'number') {
1097
+ out('/OpenAction [3 0 R /XYZ null null '+f2(zoomMode)+']');
1098
+ }
1099
+ }
1100
+ if (!layoutMode) layoutMode = 'continuous';
1101
+ switch(layoutMode) {
1102
+ case 'continuous' : out('/PageLayout /OneColumn'); break;
1103
+ case 'single' : out('/PageLayout /SinglePage'); break;
1104
+ case 'two':
1105
+ case 'twoleft' : out('/PageLayout /TwoColumnLeft'); break;
1106
+ case 'tworight' : out('/PageLayout /TwoColumnRight'); break;
1107
+ }
1108
+ if (pageMode) {
1109
+ /**
1110
+ * A name object specifying how the document should be displayed when opened:
1111
+ * UseNone : Neither document outline nor thumbnail images visible -- DEFAULT
1112
+ * UseOutlines : Document outline visible
1113
+ * UseThumbs : Thumbnail images visible
1114
+ * FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible
1115
+ */
1116
+ out('/PageMode /' + pageMode);
1117
+ }
1118
+ events.publish('putCatalog');
1119
+ },
1120
+ putTrailer = function() {
1121
+ out('/Size ' + (objectNumber + 1));
1122
+ out('/Root ' + objectNumber + ' 0 R');
1123
+ out('/Info ' + (objectNumber - 1) + ' 0 R');
1124
+ },
1125
+ beginPage = function(width,height) {
1126
+ // Dimensions are stored as user units and converted to points on output
1127
+ var orientation = typeof height === 'string' && height.toLowerCase();
1128
+ if (typeof width === 'string') {
1129
+ var format = width.toLowerCase();
1130
+ if (pageFormats.hasOwnProperty(format)) {
1131
+ width = pageFormats[format][0] / k;
1132
+ height = pageFormats[format][1] / k;
1133
+ }
1134
+ }
1135
+ if (Array.isArray(width)) {
1136
+ height = width[1];
1137
+ width = width[0];
1138
+ }
1139
+ //if (orientation) {
1140
+ // switch(orientation.substr(0,1)) {
1141
+ // case 'l': if (height > width ) orientation = 's'; break;
1142
+ // case 'p': if (width > height ) orientation = 's'; break;
1143
+ // }
1144
+ // TODO: What is the reason for this (for me it only seems to raise bugs)?
1145
+ // if (orientation === 's') { tmp = width; width = height; height = tmp; }
1146
+ //}
1147
+ outToPages = true;
1148
+ pages[++page] = [];
1149
+ pagedim[page] = {
1150
+ width : Number(width) || pageWidth,
1151
+ height : Number(height) || pageHeight
1152
+ };
1153
+ pagesContext[page] = {};
1154
+ _setPage(page);
1155
+ },
1156
+ _addPage = function() {
1157
+ beginPage.apply(this, arguments);
1158
+ // Set line width
1159
+ out(f2(lineWidth) + ' w');
1160
+ // Set draw color
1161
+ out(drawColor);
1162
+ // resurrecting non-default line caps, joins
1163
+ if (lineCapID !== 0) {
1164
+ out(lineCapID + ' J');
1165
+ }
1166
+ if (lineJoinID !== 0) {
1167
+ out(lineJoinID + ' j');
1168
+ }
1169
+ events.publish('addPage', { pageNumber : page });
1170
+ },
1171
+ _deletePage = function( n ) {
1172
+ if (n > 0 && n <= page) {
1173
+ pages.splice(n, 1);
1174
+ pagedim.splice(n, 1);
1175
+ page--;
1176
+ if (currentPage > page){
1177
+ currentPage = page;
1178
+ }
1179
+ this.setPage(currentPage);
1180
+ }
1181
+ },
1182
+ _setPage = function(n) {
1183
+ if (n > 0 && n <= page) {
1184
+ currentPage = n;
1185
+ pageWidth = pagedim[n].width;
1186
+ pageHeight = pagedim[n].height;
1187
+ }
1188
+ },
1189
+ /**
1190
+ * Returns a document-specific font key - a label assigned to a
1191
+ * font name + font type combination at the time the font was added
1192
+ * to the font inventory.
1193
+ *
1194
+ * Font key is used as label for the desired font for a block of text
1195
+ * to be added to the PDF document stream.
1196
+ * @private
1197
+ * @function
1198
+ * @param {String} fontName can be undefined on "falthy" to indicate "use current"
1199
+ * @param {String} fontStyle can be undefined on "falthy" to indicate "use current"
1200
+ * @returns {String} Font key.
1201
+ */
1202
+ getFont = function(fontName, fontStyle) {
1203
+ var key;
1204
+
1205
+ fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName;
1206
+ fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle;
1207
+
1208
+ if (fontName !== undefined){
1209
+ fontName = fontName.toLowerCase();
1210
+ }
1211
+ switch(fontName){
1212
+ case 'sans-serif':
1213
+ case 'verdana':
1214
+ case 'arial':
1215
+ case 'helvetica':
1216
+ fontName = 'helvetica';
1217
+ break;
1218
+ case 'fixed':
1219
+ case 'monospace':
1220
+ case 'terminal':
1221
+ case 'courier':
1222
+ fontName = 'courier';
1223
+ break;
1224
+ case 'serif':
1225
+ case 'cursive':
1226
+ case 'fantasy':
1227
+ default:
1228
+ fontName = 'times';
1229
+ break;
1230
+ }
1231
+
1232
+ try {
1233
+ // get a string like 'F3' - the KEY corresponding tot he font + type combination.
1234
+ key = fontmap[fontName][fontStyle];
1235
+ } catch (e) {}
1236
+
1237
+ if (!key) {
1238
+ //throw new Error("Unable to look up font label for font '" + fontName + "', '"
1239
+ //+ fontStyle + "'. Refer to getFontList() for available fonts.");
1240
+ key = fontmap['times'][fontStyle];
1241
+ if (key == null){
1242
+ key = fontmap['times']['normal'];
1243
+ }
1244
+ }
1245
+ return key;
1246
+ },
1247
+ buildDocument = function() {
1248
+
1249
+ outToPages = false; // switches out() to content
1250
+ objectNumber = 2;
1251
+ content = [];
1252
+ offsets = [];
1253
+ additionalObjects = [];
1254
+
1255
+ // putHeader()
1256
+ out('%PDF-' + pdfVersion);
1257
+
1258
+ putPages();
1259
+
1260
+ // Must happen after putPages
1261
+ // Modifies current object Id
1262
+ putAdditionalObjects();
1263
+
1264
+ putResources();
1265
+
1266
+ // Info
1267
+ newObject();
1268
+ out('<<');
1269
+ putInfo();
1270
+ out('>>');
1271
+ out('endobj');
1272
+
1273
+ // Catalog
1274
+ newObject();
1275
+ out('<<');
1276
+ putCatalog();
1277
+ out('>>');
1278
+ out('endobj');
1279
+
1280
+ // Cross-ref
1281
+ var o = content_length, i, p = "0000000000";
1282
+ out('xref');
1283
+ out('0 ' + (objectNumber + 1));
1284
+ out(p+' 65535 f ');
1285
+ for (i = 1; i <= objectNumber; i++) {
1286
+ var offset = offsets[i];
1287
+ if (typeof offset === 'function'){
1288
+ out((p + offsets[i]()).slice(-10) + ' 00000 n ');
1289
+ }else{
1290
+ out((p + offsets[i]).slice(-10) + ' 00000 n ');
1291
+ }
1292
+ }
1293
+ // Trailer
1294
+ out('trailer');
1295
+ out('<<');
1296
+ putTrailer();
1297
+ out('>>');
1298
+ out('startxref');
1299
+ out(o);
1300
+ out('%%EOF');
1301
+
1302
+ outToPages = true;
1303
+
1304
+ return content.join('\n');
1305
+ },
1306
+
1307
+ getStyle = function(style) {
1308
+ // see path-painting operators in PDF spec
1309
+ var op = 'n'; // none
1310
+ if (style === "D") {
1311
+ op = 'S'; // stroke
1312
+ } else if (style === 'F') {
1313
+ op = 'f'; // fill
1314
+ } else if (style === 'FD' || style === 'DF') {
1315
+ op = 'B'; // both
1316
+ } else if (style === 'f' || style === 'f*' || style === 'B' || style === 'B*') {
1317
+ /*
1318
+ Allow direct use of these PDF path-painting operators:
1319
+ - f fill using nonzero winding number rule
1320
+ - f* fill using even-odd rule
1321
+ - B fill then stroke with fill using non-zero winding number rule
1322
+ - B* fill then stroke with fill using even-odd rule
1323
+ */
1324
+ op = style;
1325
+ }
1326
+ return op;
1327
+ },
1328
+ // puts the style for the previously drawn path. If a patternKey is provided, the pattern is used to fill
1329
+ // the path. Use patternMatrix to transform the pattern to rhe right location.
1330
+ putStyle = function (style, patternKey, patternData) {
1331
+ style = getStyle(style);
1332
+
1333
+ // stroking / filling / both the path
1334
+ if (!patternKey) {
1335
+ out(style);
1336
+ return;
1337
+ }
1338
+
1339
+ patternData || (patternData = unitMatrix);
1340
+
1341
+ var patternId = patternMap[patternKey];
1342
+ var pattern = patterns[patternId];
1343
+
1344
+ if (pattern instanceof API.ShadingPattern) {
1345
+ out("q");
1346
+ out("W " + style);
1347
+
1348
+ if (pattern.gState) {
1349
+ API.setGState(pattern.gState);
1350
+ }
1351
+
1352
+ out(patternData.toString() + " cm");
1353
+ out("/" + patternId + " sh");
1354
+ out("Q");
1355
+ } else if (pattern instanceof API.TilingPattern) {
1356
+ // pdf draws patterns starting at the bottom left corner and they are not affected by the global transformation,
1357
+ // so we must flip them
1358
+ var matrix = new Matrix(1, 0, 0, -1, 0, pageHeight);
1359
+
1360
+ if (patternData.matrix) {
1361
+ matrix = matrixMult(patternData.matrix || unitMatrix, matrix);
1362
+
1363
+ // we cannot apply a matrix to the pattern on use so we must abuse the pattern matrix and create new instances
1364
+ // for each use
1365
+ patternId = pattern.createClone(patternKey, patternData.boundingBox, patternData.xStep, patternData.yStep, matrix).id;
1366
+ }
1367
+
1368
+ out("q");
1369
+ out("/Pattern cs");
1370
+ out("/" + patternId + " scn");
1371
+
1372
+ if (pattern.gState) {
1373
+ API.setGState(pattern.gState);
1374
+ }
1375
+
1376
+ out(style);
1377
+ out("Q");
1378
+ }
1379
+ },
1380
+
1381
+ getArrayBuffer = function() {
1382
+ var data = buildDocument(), len = data.length,
1383
+ ab = new ArrayBuffer(len), u8 = new Uint8Array(ab);
1384
+
1385
+ while(len--) u8[len] = data.charCodeAt(len);
1386
+ return ab;
1387
+ },
1388
+ getBlob = function() {
1389
+ return new Blob([getArrayBuffer()], { type : "application/pdf" });
1390
+ },
1391
+ /**
1392
+ * Generates the PDF document.
1393
+ *
1394
+ * If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
1395
+ *
1396
+ * @param {String} type A string identifying one of the possible output types.
1397
+ * @param {Object} options An object providing some additional signalling to PDF generator.
1398
+ * @function
1399
+ * @returns {jsPDF}
1400
+ * @methodOf jsPDF#
1401
+ * @name output
1402
+ */
1403
+ output = SAFE(function(type, options) {
1404
+ var datauri = ('' + type).substr(0,6) === 'dataur'
1405
+ ? 'data:application/pdf;base64,'+btoa(buildDocument()):0;
1406
+
1407
+ switch (type) {
1408
+ case undefined:
1409
+ return buildDocument();
1410
+ case 'save':
1411
+ if (navigator.getUserMedia) {
1412
+ if (global.URL === undefined
1413
+ || global.URL.createObjectURL === undefined) {
1414
+ return API.output('dataurlnewwindow');
1415
+ }
1416
+ }
1417
+ saveAs(getBlob(), options);
1418
+ if(typeof saveAs.unload === 'function') {
1419
+ if(global.setTimeout) {
1420
+ setTimeout(saveAs.unload,911);
1421
+ }
1422
+ }
1423
+ break;
1424
+ case 'arraybuffer':
1425
+ return getArrayBuffer();
1426
+ case 'blob':
1427
+ return getBlob();
1428
+ case 'bloburi':
1429
+ case 'bloburl':
1430
+ // User is responsible of calling revokeObjectURL
1431
+ return global.URL && global.URL.createObjectURL(getBlob()) || void 0;
1432
+ case 'datauristring':
1433
+ case 'dataurlstring':
1434
+ return datauri;
1435
+ case 'dataurlnewwindow':
1436
+ var nW = global.open(datauri);
1437
+ if (nW || typeof safari === "undefined") return nW;
1438
+ /* pass through */
1439
+ case 'datauri':
1440
+ case 'dataurl':
1441
+ return global.document.location.href = datauri;
1442
+ default:
1443
+ throw new Error('Output type "' + type + '" is not supported.');
1444
+ }
1445
+ // @TODO: Add different output options
1446
+ });
1447
+
1448
+ switch (unit) {
1449
+ case 'pt': k = 1; break;
1450
+ case 'mm': k = 72 / 25.4000508; break;
1451
+ case 'cm': k = 72 / 2.54000508; break;
1452
+ case 'in': k = 72; break;
1453
+ case 'px': k = 96 / 72; break;
1454
+ case 'pc': k = 12; break;
1455
+ case 'em': k = 12; break;
1456
+ case 'ex': k = 6; break;
1457
+ default:
1458
+ throw ('Invalid unit: ' + unit);
1459
+ }
1460
+
1461
+ //---------------------------------------
1462
+ // Public API
1463
+
1464
+ /**
1465
+ * Object exposing internal API to plugins
1466
+ * @public
1467
+ */
1468
+ API.internal = {
1469
+ 'pdfEscape' : pdfEscape,
1470
+ 'getStyle' : getStyle,
1471
+ /**
1472
+ * Returns {FontObject} describing a particular font.
1473
+ * @public
1474
+ * @function
1475
+ * @param {String} fontName (Optional) Font's family name
1476
+ * @param {String} fontStyle (Optional) Font's style variation name (Example:"Italic")
1477
+ * @returns {FontObject}
1478
+ */
1479
+ 'getFont' : function() {
1480
+ return fonts[getFont.apply(API, arguments)];
1481
+ },
1482
+ 'getFontSize' : function() {
1483
+ return activeFontSize;
1484
+ },
1485
+ 'getLineHeight' : function() {
1486
+ return activeFontSize * lineHeightProportion;
1487
+ },
1488
+ 'write' : function(string1 /*, string2, string3, etc */) {
1489
+ out(arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' '));
1490
+ },
1491
+ 'getCoordinateString' : function(value) {
1492
+ return f2(value);
1493
+ },
1494
+ 'getVerticalCoordinateString' : function(value) {
1495
+ return f2(value);
1496
+ },
1497
+ 'collections' : {},
1498
+ 'newObject' : newObject,
1499
+ 'newAdditionalObject' : newAdditionalObject,
1500
+ 'newObjectDeferred' : newObjectDeferred,
1501
+ 'newObjectDeferredBegin' : newObjectDeferredBegin,
1502
+ 'putStream' : putStream,
1503
+ 'events' : events,
1504
+ // ratio that you use in multiplication of a given "size" number to arrive to 'point'
1505
+ // units of measurement.
1506
+ // scaleFactor is set at initialization of the document and calculated against the stated
1507
+ // default measurement units for the document.
1508
+ // If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
1509
+ // through multiplication.
1510
+ 'scaleFactor' : k,
1511
+ 'pageSize' : {
1512
+ get width() {
1513
+ return pageWidth
1514
+ },
1515
+ get height() {
1516
+ return pageHeight
1517
+ }
1518
+ },
1519
+ 'output' : function(type, options) {
1520
+ return output(type, options);
1521
+ },
1522
+ 'getNumberOfPages' : function() {
1523
+ return pages.length - 1;
1524
+ },
1525
+ 'pages' : pages,
1526
+ 'out' : out,
1527
+ 'f2' : f2,
1528
+ 'getPageInfo' : function(pageNumberOneBased){
1529
+ var objId = (pageNumberOneBased - 1) * 2 + 3;
1530
+ return {objId:objId, pageNumber:pageNumberOneBased, pageContext:pagesContext[pageNumberOneBased]};
1531
+ },
1532
+ 'getCurrentPageInfo' : function(){
1533
+ var objId = (currentPage - 1) * 2 + 3;
1534
+ return {objId:objId, pageNumber:currentPage, pageContext:pagesContext[currentPage]};
1535
+ },
1536
+ 'getPDFVersion': function () {
1537
+ return pdfVersion;
1538
+ }
1539
+ };
1540
+
1541
+ /**
1542
+ * An object representing a pdf graphics state.
1543
+ * @param parameters A parameter object that contains all properties this graphics state wants to set.
1544
+ * Supported are: opacity
1545
+ * @constructor
1546
+ */
1547
+ API.GState = function (parameters) {
1548
+ var supported = "opacity";
1549
+ for (var p in parameters) {
1550
+ if (parameters.hasOwnProperty(p) && supported.indexOf(p) >= 0) {
1551
+ this[p] = parameters[p];
1552
+ }
1553
+ }
1554
+ this.id = ""; // set by addGState()
1555
+ this.objectNumber = -1; // will be set by putGState()
1556
+
1557
+ this.equals = function (other) {
1558
+ var ignore = "id,objectNumber,equals";
1559
+ if (!other || typeof other !== typeof this)
1560
+ return false;
1561
+ var count = 0;
1562
+ for (var p in this) {
1563
+ if (ignore.indexOf(p) >= 0)
1564
+ continue;
1565
+ if (this.hasOwnProperty(p) && !other.hasOwnProperty(p))
1566
+ return false;
1567
+ if (this[p] !== other[p])
1568
+ return false;
1569
+ count++;
1570
+ }
1571
+ for (var p in other) {
1572
+ if (other.hasOwnProperty(p) && ignore.indexOf(p) < 0)
1573
+ count--;
1574
+ }
1575
+ return count === 0;
1576
+ }
1577
+ };
1578
+
1579
+ /**
1580
+ * Adds a new {@link GState} for later use {@see setGState}.
1581
+ * @param {String} key
1582
+ * @param {GState} gState
1583
+ * @function
1584
+ * @returns {jsPDF}
1585
+ * @methodOf jsPDF#
1586
+ * @name addGState
1587
+ */
1588
+ API.addGState = function (key, gState) {
1589
+ addGState(key, gState);
1590
+ return this;
1591
+ };
1592
+
1593
+ /**
1594
+ * Adds (and transfers the focus to) new page to the PDF document.
1595
+ * @function
1596
+ * @returns {jsPDF}
1597
+ *
1598
+ * @methodOf jsPDF#
1599
+ * @name addPage
1600
+ */
1601
+ API.addPage = function() {
1602
+ _addPage.apply(this, arguments);
1603
+ return this;
1604
+ };
1605
+ API.setPage = function() {
1606
+ _setPage.apply(this, arguments);
1607
+ return this;
1608
+ };
1609
+ API.insertPage = function(beforePage) {
1610
+ this.addPage();
1611
+ this.movePage(currentPage, beforePage);
1612
+ return this;
1613
+ };
1614
+ API.movePage = function(targetPage, beforePage) {
1615
+ var tmpPagesContext, tmpPagedim, tmpPages, i;
1616
+ if (targetPage > beforePage){
1617
+ tmpPages = pages[targetPage];
1618
+ tmpPagedim = pagedim[targetPage];
1619
+ tmpPagesContext = pagesContext[targetPage];
1620
+ for (i = targetPage; i > beforePage; i--){
1621
+ pages[i] = pages[i-1];
1622
+ pagedim[i] = pagedim[i-1];
1623
+ pagesContext[i] = pagesContext[i-1];
1624
+ }
1625
+ pages[beforePage] = tmpPages;
1626
+ pagedim[beforePage] = tmpPagedim;
1627
+ pagesContext[beforePage] = tmpPagesContext;
1628
+ this.setPage(beforePage);
1629
+ } else if (targetPage < beforePage){
1630
+ tmpPages = pages[targetPage];
1631
+ tmpPagedim = pagedim[targetPage];
1632
+ tmpPagesContext = pagesContext[targetPage];
1633
+ for (i = targetPage; i < beforePage; i++){
1634
+ pages[i] = pages[i+1];
1635
+ pagedim[i] = pagedim[i+1];
1636
+ pagesContext[i] = pagesContext[i+1];
1637
+ }
1638
+ pages[beforePage] = tmpPages;
1639
+ pagedim[beforePage] = tmpPagedim;
1640
+ pagesContext[beforePage] = tmpPagesContext;
1641
+ this.setPage(beforePage);
1642
+ }
1643
+ return this;
1644
+ };
1645
+
1646
+ API.deletePage = function() {
1647
+ _deletePage.apply( this, arguments );
1648
+ return this;
1649
+ };
1650
+ API.setDisplayMode = function(zoom, layout, pmode) {
1651
+ zoomMode = zoom;
1652
+ layoutMode = layout;
1653
+ pageMode = pmode;
1654
+ return this;
1655
+ };
1656
+
1657
+ /**
1658
+ * Saves the current graphics state ("pushes it on the stack"). It can be restored by {@link restoreGraphicsState}
1659
+ * later. Here, the general pdf graphics state is meant, also including the current transformation matrix,
1660
+ * fill and stroke colors etc.
1661
+ * @function
1662
+ * @returns {jsPDF}
1663
+ * @methodOf jsPDF#
1664
+ * @name saveGraphicsState
1665
+ */
1666
+ API.saveGraphicsState = function () {
1667
+ out("q");
1668
+ // as we cannot set font key and size independently we must keep track of both
1669
+ fontStateStack.push({
1670
+ key: activeFontKey,
1671
+ size: activeFontSize
1672
+ });
1673
+ return this;
1674
+ };
1675
+
1676
+ /**
1677
+ * Restores a previously saved graphics state saved by {@link saveGraphicsState} ("pops the stack").
1678
+ * @function
1679
+ * @returns {jsPDF}
1680
+ * @methodOf jsPDF#
1681
+ * @name restoreGraphicsState
1682
+ */
1683
+ API.restoreGraphicsState = function () {
1684
+ out("Q");
1685
+
1686
+ // restore previous font state
1687
+ var fontState = fontStateStack.pop();
1688
+ activeFontKey = fontState.key;
1689
+ activeFontSize = fontState.size;
1690
+
1691
+ return this;
1692
+ };
1693
+
1694
+ /**
1695
+ * Appends this matrix to the left of all previously applied matrices.
1696
+ * @param {Matrix} matrix
1697
+ * @function
1698
+ * @returns {jsPDF}
1699
+ * @methodOf jsPDF#
1700
+ * @name setCurrentTransformationMatrix
1701
+ */
1702
+ API.setCurrentTransformationMatrix = function (matrix) {
1703
+ out(matrix.toString() + " cm");
1704
+ return this;
1705
+ };
1706
+
1707
+ /**
1708
+ * Starts a new pdf form object, which means that all conseequent draw calls target a new independent object
1709
+ * until {@link endFormObject} is called. The created object can be referenced and drawn later using
1710
+ * {@link doFormObject}. Nested form objects are possible.
1711
+ * x, y, width, height set the bounding box that is used to clip the content.
1712
+ * @param {number} x
1713
+ * @param {number} y
1714
+ * @param {number} width
1715
+ * @param {number} height
1716
+ * @param {Matrix} matrix The matrix that will be applied to convert the form objects coordinate system to
1717
+ * the parent's.
1718
+ * @function
1719
+ * @returns {jsPDF}
1720
+ * @methodOf jsPDF#
1721
+ */
1722
+ API.beginFormObject = function (x, y, width, height, matrix) {
1723
+ // The user can set the output target to a new form object. Nested form objects are possible.
1724
+ // Currently, they use the resource dictionary of the surrounding stream. This should be changed, as
1725
+ // the PDF-Spec states:
1726
+ // "In PDF 1.2 and later versions, form XObjects may be independent of the content streams in which
1727
+ // they appear, and this is strongly recommended although not requiredIn PDF 1.2 and later versions,
1728
+ // form XObjects may be independent of the content streams in which they appear, and this is strongly
1729
+ // recommended although not required"
1730
+ beginNewRenderTarget(x, y, width, height, matrix);
1731
+ return this;
1732
+ };
1733
+
1734
+ /**
1735
+ * Completes and saves the form object.
1736
+ * @param {String} key The key by which this form object can be referenced.
1737
+ * @function
1738
+ * @returns {jsPDF}
1739
+ * @methodOf jsPDF#
1740
+ * @name endFormObject
1741
+ */
1742
+ API.endFormObject = function (key) {
1743
+ endFormObject(key);
1744
+ return this;
1745
+ };
1746
+
1747
+ /**
1748
+ * Draws the specified form object by referencing to the respective pdf XObject created with
1749
+ * {@link API.beginFormObject} and {@link endFormObject}.
1750
+ * The location is determined by matrix.
1751
+ * @param {String} key The key to the form object.
1752
+ * @param {Matrix} matrix The matrix applied before drawing the form object.
1753
+ * @function
1754
+ * @returns {jsPDF}
1755
+ * @methodOf jsPDF#
1756
+ * @name doFormObject
1757
+ */
1758
+ API.doFormObject = function (key, matrix) {
1759
+ var xObject = renderTargets[renderTargetMap[key]];
1760
+ out("q");
1761
+ out(matrix.toString() + " cm");
1762
+ out("/" + xObject.id + " Do");
1763
+ out("Q");
1764
+ return this;
1765
+ };
1766
+
1767
+ /**
1768
+ * Returns the form object specified by key.
1769
+ * @param key {String}
1770
+ * @returns {{x: number, y: number, width: number, height: number, matrix: Matrix}}
1771
+ * @function
1772
+ * @returns {jsPDF}
1773
+ * @methodOf jsPDF#
1774
+ * @name getFormObject
1775
+ */
1776
+ API.getFormObject = function (key) {
1777
+ var xObject = renderTargets[renderTargetMap[key]];
1778
+ return {
1779
+ x: xObject.x,
1780
+ y: xObject.y,
1781
+ width: xObject.width,
1782
+ height: xObject.height,
1783
+ matrix: xObject.matrix
1784
+ };
1785
+ };
1786
+
1787
+ /**
1788
+ * A matrix object for 2D homogenous transformations:
1789
+ * | a b 0 |
1790
+ * | c d 0 |
1791
+ * | e f 1 |
1792
+ * pdf multiplies matrices righthand: v' = v x m1 x m2 x ...
1793
+ * @param {number} a
1794
+ * @param {number} b
1795
+ * @param {number} c
1796
+ * @param {number} d
1797
+ * @param {number} e
1798
+ * @param {number} f
1799
+ * @constructor
1800
+ */
1801
+ API.Matrix = Matrix;
1802
+
1803
+ /**
1804
+ * Multiplies two matrices. (see {@link Matrix})
1805
+ * @param {Matrix} m1
1806
+ * @param {Matrix} m2
1807
+ */
1808
+ API.matrixMult = matrixMult;
1809
+
1810
+ /**
1811
+ * The unit matrix (equal to new Matrix(1, 0, 0, 1, 0, 0).
1812
+ * @type {Matrix}
1813
+ */
1814
+ API.unitMatrix = unitMatrix;
1815
+
1816
+ var Pattern = function (gState, matrix) {
1817
+ this.gState = gState;
1818
+ this.matrix = matrix;
1819
+
1820
+ this.id = ""; // set by addPattern()
1821
+ this.objectNumber = -1; // will be set by putPattern()
1822
+ };
1823
+
1824
+ /**
1825
+ * A pattern describing a shading pattern.
1826
+ * @param {String} type One of "axial" or "radial"
1827
+ * @param {Array<Number>} coords Either [x1, y1, x2, y2] for "axial" type describing the two interpolation points
1828
+ * or [x1, y1, r, x2, y2, r2] for "radial" describing inner and the outer circle.
1829
+ * @param {Array<Object>} colors An array of objects with the fields "offset" and "color". "offset" describes
1830
+ * the offset in parameter space [0, 1]. "color" is an array of length 3 describing RGB values in [0, 255].
1831
+ * @param {GState=} gState An additional graphics state that gets applied to the pattern (optional).
1832
+ * @param {Matrix=} matrix A matrix that describes the transformation between the pattern coordinate system
1833
+ * and the use coordinate system (optional).
1834
+ * @constructor
1835
+ * @extends API.Pattern
1836
+ */
1837
+ API.ShadingPattern = function (type, coords, colors, gState, matrix) {
1838
+ // see putPattern() for information how they are realized
1839
+ this.type = type === "axial" ? 2 : 3;
1840
+ this.coords = coords;
1841
+ this.colors = colors;
1842
+
1843
+ Pattern.call(this, gState, matrix);
1844
+ };
1845
+
1846
+ /**
1847
+ * A PDF Tiling pattern.
1848
+ * @param {Array.<Number>} boundingBox The bounding box at which one pattern cell gets clipped.
1849
+ * @param {Number} xStep Horizontal spacing between pattern cells.
1850
+ * @param {Number} yStep Vertical spacing between pattern cells.
1851
+ * @param {API.GState=} gState An additional graphics state that gets applied to the pattern (optional).
1852
+ * @param {Matrix=} matrix A matrix that describes the transformation between the pattern coordinate system
1853
+ * and the use coordinate system (optional).
1854
+ * @constructor
1855
+ * @extends API.Pattern
1856
+ */
1857
+ API.TilingPattern = function (boundingBox, xStep, yStep, gState, matrix) {
1858
+ this.boundingBox = boundingBox;
1859
+ this.xStep = xStep;
1860
+ this.yStep = yStep;
1861
+
1862
+ this.stream = ""; // set by endTilingPattern();
1863
+
1864
+ this.cloneIndex = 0;
1865
+
1866
+ Pattern.call(this, gState, matrix);
1867
+ };
1868
+
1869
+ API.TilingPattern.prototype = {
1870
+ createClone: function (patternKey, boundingBox, xStep, yStep, matrix) {
1871
+ var clone = new API.TilingPattern(boundingBox || this.boundingBox, xStep || this.xStep, yStep || this.yStep,
1872
+ this.gState, matrix || this.matrix);
1873
+ clone.stream = this.stream;
1874
+ var key = patternKey + "$$" + this.cloneIndex++ + "$$";
1875
+ addPattern(key, clone);
1876
+ return clone;
1877
+ }
1878
+ };
1879
+
1880
+ /**
1881
+ * Adds a new {@link API.ShadingPattern} for later use.
1882
+ * @param {String} key
1883
+ * @param {Pattern} pattern
1884
+ * @function
1885
+ * @returns {jsPDF}
1886
+ * @methodOf jsPDF#
1887
+ * @name addPattern
1888
+ */
1889
+ API.addShadingPattern = function (key, pattern) {
1890
+ addPattern(key, pattern);
1891
+ return this;
1892
+ };
1893
+
1894
+ /**
1895
+ * Begins a new tiling pattern. All subsequent render calls are drawn to this pattern until {@link API.endTilingPattern}
1896
+ * gets called.
1897
+ * @param {API.Pattern} pattern
1898
+ */
1899
+ API.beginTilingPattern = function (pattern) {
1900
+ beginNewRenderTarget(pattern.boundingBox[0], pattern.boundingBox[1],
1901
+ pattern.boundingBox[2] - pattern.boundingBox[0], pattern.boundingBox[3] - pattern.boundingBox[1], pattern.matrix);
1902
+ };
1903
+
1904
+ /**
1905
+ * Ends a tiling pattern and sets the render target to the one active before {@link API.beginTilingPattern} has been called.
1906
+ * @param {string} key A unique key that is used to reference this pattern at later use.
1907
+ * @param {API.Pattern} pattern The pattern to end.
1908
+ */
1909
+ API.endTilingPattern = function (key, pattern) {
1910
+ // retrieve the stream
1911
+ pattern.stream = pages[currentPage].join("\n");
1912
+
1913
+ addPattern(key, pattern);
1914
+
1915
+ events.publish("endTilingPattern", pattern);
1916
+
1917
+ // restore state from stack
1918
+ renderTargetStack.pop().restore();
1919
+ };
1920
+
1921
+ /**
1922
+ * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
1923
+ *
1924
+ * @function
1925
+ * @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down
1926
+ * per font, spacing settings declared before this call.
1927
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
1928
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
1929
+ * @param {Object} flags Collection of settings signalling how the text must be encoded. Defaults are sane. If you
1930
+ * think you want to pass some flags, you likely can read the source.
1931
+ * @param {number|Matrix} transform If transform is a number the text will be rotated by this value. If it is a Matrix,
1932
+ * this matrix gets directly applied to the text, which allows shearing effects etc.
1933
+ * @param align {string}
1934
+ * @returns {jsPDF}
1935
+ * @methodOf jsPDF#
1936
+ */
1937
+ API.text = function(text, x, y, flags, transform, align) {
1938
+ /**
1939
+ * Inserts something like this into PDF
1940
+ * BT
1941
+ * /F1 16 Tf % Font name + size
1942
+ * 16 TL % How many units down for next line in multiline text
1943
+ * 0 g % color
1944
+ * 28.35 813.54 Td % position
1945
+ * (line one) Tj
1946
+ * T* (line two) Tj
1947
+ * T* (line three) Tj
1948
+ * ET
1949
+ */
1950
+ function ESC(s) {
1951
+ s = s.split("\t").join(Array(options.TabLen||9).join(" "));
1952
+ return pdfEscape(s, flags);
1953
+ }
1954
+
1955
+ // Pre-August-2012 the order of arguments was function(x, y, text, flags)
1956
+ // in effort to make all calls have similar signature like
1957
+ // function(data, coordinates... , miscellaneous)
1958
+ // this method had its args flipped.
1959
+ // code below allows backward compatibility with old arg order.
1960
+ if (typeof text === 'number') {
1961
+ var tmp = y;
1962
+ y = x;
1963
+ x = text;
1964
+ text = tmp;
1965
+ }
1966
+
1967
+ // If there are any newlines in text, we assume
1968
+ // the user wanted to print multiple lines, so break the
1969
+ // text up into an array. If the text is already an array,
1970
+ // we assume the user knows what they are doing.
1971
+ // Convert text into an array anyway to simplify
1972
+ // later code.
1973
+ if (typeof text === 'string') {
1974
+ if(text.match(/[\n\r]/)) {
1975
+ text = text.split( /\r\n|\r|\n/g);
1976
+ } else {
1977
+ text = [text];
1978
+ }
1979
+ }
1980
+ if (typeof transform === 'string') {
1981
+ align = transform;
1982
+ transform = null;
1983
+ }
1984
+ if (typeof flags === 'string') {
1985
+ align = flags;
1986
+ flags = null;
1987
+ }
1988
+ if (typeof flags === 'number') {
1989
+ transform = flags;
1990
+ flags = null;
1991
+ }
1992
+
1993
+ var todo;
1994
+ if (transform && typeof transform === "number") {
1995
+ transform *= (Math.PI / 180);
1996
+ var c = Math.cos(transform),
1997
+ s = Math.sin(transform);
1998
+ transform = new Matrix(c, s , -s, c, 0, 0);
1999
+ } else if (!transform) {
2000
+ transform = unitMatrix;
2001
+ }
2002
+
2003
+ flags = flags || {};
2004
+ if (!('noBOM' in flags))
2005
+ flags.noBOM = true;
2006
+ if (!('autoencode' in flags))
2007
+ flags.autoencode = true;
2008
+
2009
+ var strokeOption = '';
2010
+ var pageContext = this.internal.getCurrentPageInfo().pageContext;
2011
+ if (true === flags.stroke){
2012
+ if (pageContext.lastTextWasStroke !== true){
2013
+ strokeOption = '1 Tr\n';
2014
+ pageContext.lastTextWasStroke = true;
2015
+ }
2016
+ }
2017
+ else{
2018
+ if (pageContext.lastTextWasStroke){
2019
+ strokeOption = '0 Tr\n';
2020
+ }
2021
+ pageContext.lastTextWasStroke = false;
2022
+ }
2023
+
2024
+ if (typeof this._runningPageHeight === 'undefined'){
2025
+ this._runningPageHeight = 0;
2026
+ }
2027
+
2028
+ if (typeof text === 'string') {
2029
+ text = ESC(text);
2030
+ } else if (Object.prototype.toString.call(text) === '[object Array]') {
2031
+ // we don't want to destroy original text array, so cloning it
2032
+ var sa = text.concat(), da = [], len = sa.length;
2033
+ // we do array.join('text that must not be PDFescaped")
2034
+ // thus, pdfEscape each component separately
2035
+ while (len--) {
2036
+ da.push(ESC(sa.shift()));
2037
+ }
2038
+ var linesLeft = Math.ceil((y - this._runningPageHeight) / (activeFontSize * lineHeightProportion));
2039
+ if (0 <= linesLeft && linesLeft < da.length + 1) {
2040
+ //todo = da.splice(linesLeft-1);
2041
+ }
2042
+
2043
+ if( align ) {
2044
+ var left,
2045
+ prevX,
2046
+ maxLineLength,
2047
+ leading = activeFontSize * lineHeightProportion,
2048
+ lineWidths = text.map( function( v ) {
2049
+ return this.getStringUnitWidth( v ) * activeFontSize;
2050
+ }, this );
2051
+ maxLineLength = Math.max.apply( Math, lineWidths );
2052
+ // The first line uses the "main" Td setting,
2053
+ // and the subsequent lines are offset by the
2054
+ // previous line's x coordinate.
2055
+ if( align === "center" ) {
2056
+ // The passed in x coordinate defines
2057
+ // the center point.
2058
+ left = x - maxLineLength / 2;
2059
+ x -= lineWidths[0] / 2;
2060
+ } else if ( align === "right" ) {
2061
+ // The passed in x coordinate defines the
2062
+ // rightmost point of the text.
2063
+ left = x - maxLineLength;
2064
+ x -= lineWidths[0];
2065
+ } else {
2066
+ throw new Error('Unrecognized alignment option, use "center" or "right".');
2067
+ }
2068
+ prevX = x;
2069
+ text = da[0] + ") Tj\n";
2070
+ for ( i = 1, len = da.length ; i < len; i++ ) {
2071
+ var delta = maxLineLength - lineWidths[i];
2072
+ if( align === "center" ) delta /= 2;
2073
+ // T* = x-offset leading Td ( text )
2074
+ text += ( ( left - prevX ) + delta ) + " -" + leading + " Td (" + da[i];
2075
+ prevX = left + delta;
2076
+ if( i < len - 1 ) {
2077
+ text += ") Tj\n";
2078
+ }
2079
+ }
2080
+ } else {
2081
+ text = da.join(") Tj\nT* (");
2082
+ }
2083
+ } else {
2084
+ throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.');
2085
+ }
2086
+ // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
2087
+
2088
+ // BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET
2089
+ // if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations)
2090
+ // Thus, there is NO useful, *reliable* concept of "default" font for a page.
2091
+ // The fact that "default" (reuse font used before) font worked before in basic cases is an accident
2092
+ // - readers dealing smartly with brokenness of jsPDF's markup.
2093
+
2094
+ var curY;
2095
+
2096
+ if (todo){
2097
+ //this.addPage();
2098
+ //this._runningPageHeight += y - (activeFontSize * 1.7);
2099
+ //curY = f2(activeFontSize * 1.7);
2100
+ } else {
2101
+ curY = f2(y);
2102
+ }
2103
+ //curY = f2(((y - this._runningPageHeight));
2104
+
2105
+ // if (curY < 0){
2106
+ // console.log('auto page break');
2107
+ // this.addPage();
2108
+ // this._runningPageHeight = y - (activeFontSize * 1.7);
2109
+ // curY = f2(activeFontSize * 1.7);
2110
+ // }
2111
+
2112
+ var translate = new Matrix(1, 0, 0, -1, x, curY);
2113
+ transform = matrixMult(translate, transform);
2114
+ var position = transform.toString() + " Tm";
2115
+
2116
+ out(
2117
+ 'BT\n' +
2118
+ (activeFontSize * lineHeightProportion) + ' TL\n' + // line spacing
2119
+ strokeOption +// stroke option
2120
+ position + '\n(' +
2121
+ text +
2122
+ ') Tj\nET');
2123
+
2124
+ if (todo) {
2125
+ //this.text( todo, x, activeFontSize * 1.7);
2126
+ //this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7));
2127
+ this.text( todo, x, y);// + (activeFontSize * 1.7));
2128
+ }
2129
+
2130
+ return this;
2131
+ };
2132
+
2133
+
2134
+ API.lstext = function(text, x, y, spacing) {
2135
+ for (var i = 0, len = text.length ; i < len; i++, x += spacing) this.text(text[i], x, y);
2136
+ };
2137
+
2138
+ /**
2139
+ * Draw a line
2140
+ * @param {number} x1
2141
+ * @param {number} y1
2142
+ * @param {number} x2
2143
+ * @param {number} y2
2144
+ * @function
2145
+ * @returns {jsPDF}
2146
+ * @methodOf jsPDF#
2147
+ * @name line
2148
+ */
2149
+ API.line = function(x1, y1, x2, y2) {
2150
+ return this.lines([[x2 - x1, y2 - y1]], x1, y1, [1, 1], "D");
2151
+ };
2152
+
2153
+ API.clip = function() {
2154
+ // By patrick-roberts, github.com/MrRio/jsPDF/issues/328
2155
+ // Call .clip() after calling .rect() with a style argument of null
2156
+ out('W'); // clip
2157
+ out('S'); // stroke path; necessary for clip to work
2158
+ };
2159
+
2160
+
2161
+ /**
2162
+ * @typedef {Object} PatternData
2163
+ * {Matrix|undefined} matrix
2164
+ * {Number|undefined} xStep
2165
+ * {Number|undefined} yStep
2166
+ * {Array.<Number>|undefined} boundingBox
2167
+ */
2168
+
2169
+ /**
2170
+ * Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
2171
+ * All data points in `lines` are relative to last line origin.
2172
+ * `x`, `y` become x1,y1 for first line / curve in the set.
2173
+ * For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
2174
+ * 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.
2175
+ *
2176
+ * @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
2177
+ * @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
2178
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
2179
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
2180
+ * @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.
2181
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
2182
+ * @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point.
2183
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the path.
2184
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2185
+ * will modify the pattern on use.
2186
+ * @function
2187
+ * @returns {jsPDF}
2188
+ * @methodOf jsPDF#
2189
+ * @name lines
2190
+ */
2191
+ API.lines = function(lines, x, y, scale, style, closed, patternKey, patternData) {
2192
+ var scalex,scaley,i,l,leg,x2,y2,x3,y3,x4,y4;
2193
+
2194
+ // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
2195
+ // in effort to make all calls have similar signature like
2196
+ // function(content, coordinateX, coordinateY , miscellaneous)
2197
+ // this method had its args flipped.
2198
+ // code below allows backward compatibility with old arg order.
2199
+ if (typeof lines === 'number') {
2200
+ var tmp = y;
2201
+ y = x;
2202
+ x = lines;
2203
+ lines = tmp;
2204
+ }
2205
+
2206
+ scale = scale || [1, 1];
2207
+
2208
+ // starting point
2209
+ out(f3(x) + ' ' + f3(y) + ' m ');
2210
+
2211
+ scalex = scale[0];
2212
+ scaley = scale[1];
2213
+ l = lines.length;
2214
+ //, x2, y2 // bezier only. In page default measurement "units", *after* scaling
2215
+ //, x3, y3 // bezier only. In page default measurement "units", *after* scaling
2216
+ // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
2217
+ x4 = x; // last / ending point = starting point for first item.
2218
+ y4 = y; // last / ending point = starting point for first item.
2219
+
2220
+ for (i = 0; i < l; i++) {
2221
+ leg = lines[i];
2222
+ if (leg.length === 2) {
2223
+ // simple line
2224
+ x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
2225
+ y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
2226
+ out(f3(x4) + ' ' + f3(y4) + ' l');
2227
+ } else {
2228
+ // bezier curve
2229
+ x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
2230
+ y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
2231
+ x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
2232
+ y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
2233
+ x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
2234
+ y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
2235
+ out(
2236
+ f3(x2) + ' ' +
2237
+ f3(y2) + ' ' +
2238
+ f3(x3) + ' ' +
2239
+ f3(y3) + ' ' +
2240
+ f3(x4) + ' ' +
2241
+ f3(y4) + ' c');
2242
+ }
2243
+ }
2244
+
2245
+ if (closed) {
2246
+ out('h');
2247
+ }
2248
+
2249
+ putStyle(style, patternKey, patternData);
2250
+
2251
+ return this;
2252
+ };
2253
+
2254
+ /**
2255
+ * Similar to {@link API.lines} but all coordinates are interpreted as absolute coordinates instead of relative.
2256
+ * @param {Array<Object>} lines An array of {op: operator, c: coordinates} object, where op is one of "m" (move to), "l" (line to)
2257
+ * "c" (cubic bezier curve) and "h" (close (sub)path)). c is an array of coordinates. "m" and "l" expect two, "c"
2258
+ * six and "h" an empty array (or undefined).
2259
+ * @param {String} style The style
2260
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the path.
2261
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2262
+ * will modify the pattern on use.
2263
+ * @function
2264
+ * @returns {jsPDF}
2265
+ * @methodOf jsPDF#
2266
+ * @name path
2267
+ */
2268
+ API.path = function (lines, style, patternKey, patternData) {
2269
+
2270
+ for (var i = 0; i < lines.length; i++) {
2271
+ var leg = lines[i];
2272
+ var coords = leg.c;
2273
+ switch (leg.op) {
2274
+ case "m":
2275
+ // move
2276
+ out(f3(coords[0]) + ' ' + f3(coords[1]) + ' m');
2277
+ break;
2278
+ case "l":
2279
+ // simple line
2280
+ out(f3(coords[0]) + ' ' + f3(coords[1]) + ' l');
2281
+ break;
2282
+ case "c":
2283
+ // bezier curve
2284
+ out([
2285
+ f3(coords[0]),
2286
+ f3(coords[1]),
2287
+ f3(coords[2]),
2288
+ f3(coords[3]),
2289
+ f3(coords[4]),
2290
+ f3(coords[5]),
2291
+ "c"
2292
+ ].join(" "));
2293
+ break;
2294
+ case "h":
2295
+ // close path
2296
+ out("h");
2297
+ }
2298
+
2299
+
2300
+ }
2301
+
2302
+ putStyle(style, patternKey, patternData);
2303
+
2304
+ return this;
2305
+ };
2306
+
2307
+ /**
2308
+ * Adds a rectangle to PDF
2309
+ *
2310
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
2311
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
2312
+ * @param {Number} w Width (in units declared at inception of PDF document)
2313
+ * @param {Number} h Height (in units declared at inception of PDF document)
2314
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
2315
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
2316
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2317
+ * will modify the pattern on use.
2318
+ * @function
2319
+ * @returns {jsPDF}
2320
+ * @methodOf jsPDF#
2321
+ * @name rect
2322
+ */
2323
+ API.rect = function(x, y, w, h, style, patternKey, patternData) {
2324
+ out([
2325
+ f2(x),
2326
+ f2(y),
2327
+ f2(w),
2328
+ f2(-h),
2329
+ 're'
2330
+ ].join(' '));
2331
+
2332
+ putStyle(style, patternKey, patternData);
2333
+
2334
+ return this;
2335
+ };
2336
+
2337
+ /**
2338
+ * Adds a triangle to PDF
2339
+ *
2340
+ * @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
2341
+ * @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
2342
+ * @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
2343
+ * @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
2344
+ * @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
2345
+ * @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
2346
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
2347
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
2348
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2349
+ * will modify the pattern on use.
2350
+ * @function
2351
+ * @returns {jsPDF}
2352
+ * @methodOf jsPDF#
2353
+ * @name triangle
2354
+ */
2355
+ API.triangle = function(x1, y1, x2, y2, x3, y3, style, patternKey, patternData) {
2356
+ this.lines(
2357
+ [
2358
+ [x2 - x1, y2 - y1], // vector to point 2
2359
+ [x3 - x2, y3 - y2], // vector to point 3
2360
+ [x1 - x3, y1 - y3]// closing vector back to point 1
2361
+ ],
2362
+ x1,
2363
+ y1, // start of path
2364
+ [1, 1],
2365
+ style,
2366
+ true,
2367
+ patternKey,
2368
+ patternData
2369
+ );
2370
+ return this;
2371
+ };
2372
+
2373
+ /**
2374
+ * Adds a rectangle with rounded corners to PDF
2375
+ *
2376
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
2377
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
2378
+ * @param {Number} w Width (in units declared at inception of PDF document)
2379
+ * @param {Number} h Height (in units declared at inception of PDF document)
2380
+ * @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
2381
+ * @param {Number} ry Radius along y axis (in units declared at inception of PDF document)
2382
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
2383
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
2384
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2385
+ * will modify the pattern on use.
2386
+ * @function
2387
+ * @returns {jsPDF}
2388
+ * @methodOf jsPDF#
2389
+ * @name roundedRect
2390
+ */
2391
+ API.roundedRect = function(x, y, w, h, rx, ry, style, patternKey, patternData) {
2392
+ var MyArc = 4 / 3 * (Math.SQRT2 - 1);
2393
+
2394
+ rx = Math.min(rx, w * 0.5);
2395
+ ry = Math.min(ry, h * 0.5);
2396
+
2397
+ this.lines(
2398
+ [
2399
+ [(w - 2 * rx), 0],
2400
+ [(rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry],
2401
+ [0, (h - 2 * ry)],
2402
+ [0, (ry * MyArc), - (rx * MyArc), ry, -rx, ry],
2403
+ [(-w + 2 * rx), 0],
2404
+ [ - (rx * MyArc), 0, -rx, - (ry * MyArc), -rx, -ry],
2405
+ [0, (-h + 2 * ry)],
2406
+ [0, - (ry * MyArc), (rx * MyArc), -ry, rx, -ry]
2407
+ ],
2408
+ x + rx,
2409
+ y, // start of path
2410
+ [1, 1],
2411
+ style,
2412
+ true,
2413
+ patternKey,
2414
+ patternData
2415
+ );
2416
+ return this;
2417
+ };
2418
+
2419
+ /**
2420
+ * Adds an ellipse to PDF
2421
+ *
2422
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
2423
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
2424
+ * @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
2425
+ * @param {Number} ry Radius along y axis (in units declared at inception of PDF document)
2426
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
2427
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
2428
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2429
+ * will modify the pattern on use.
2430
+ * @function
2431
+ * @returns {jsPDF}
2432
+ * @methodOf jsPDF#
2433
+ * @name ellipse
2434
+ */
2435
+ API.ellipse = function(x, y, rx, ry, style, patternKey, patternData) {
2436
+ var lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
2437
+ ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
2438
+
2439
+ out([
2440
+ f2(x + rx),
2441
+ f2(y),
2442
+ 'm',
2443
+ f2(x + rx),
2444
+ f2(y - ly),
2445
+ f2(x + lx),
2446
+ f2(y - ry),
2447
+ f2(x),
2448
+ f2(y - ry),
2449
+ 'c'
2450
+ ].join(' '));
2451
+ out([
2452
+ f2(x - lx),
2453
+ f2(y - ry),
2454
+ f2(x - rx),
2455
+ f2(y - ly),
2456
+ f2(x - rx),
2457
+ f2(y),
2458
+ 'c'
2459
+ ].join(' '));
2460
+ out([
2461
+ f2(x - rx),
2462
+ f2(y + ly),
2463
+ f2(x - lx),
2464
+ f2(y + ry),
2465
+ f2(x),
2466
+ f2(y + ry),
2467
+ 'c'
2468
+ ].join(' '));
2469
+ out([
2470
+ f2(x + lx),
2471
+ f2(y + ry),
2472
+ f2(x + rx),
2473
+ f2(y + ly),
2474
+ f2(x + rx),
2475
+ f2(y),
2476
+ 'c'
2477
+ ].join(' '));
2478
+
2479
+ putStyle(style, patternKey, patternData);
2480
+
2481
+ return this;
2482
+ };
2483
+
2484
+ /**
2485
+ * Adds an circle to PDF
2486
+ *
2487
+ * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
2488
+ * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
2489
+ * @param {Number} r Radius (in units declared at inception of PDF document)
2490
+ * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
2491
+ * @param {String} patternKey The pattern key for the pattern that should be used to fill the primitive.
2492
+ * @param {Matrix|PatternData} patternData The matrix that transforms the pattern into user space, or an object that
2493
+ * will modify the pattern on use.
2494
+ * @function
2495
+ * @returns {jsPDF}
2496
+ * @methodOf jsPDF#
2497
+ * @name circle
2498
+ */
2499
+ API.circle = function(x, y, r, style, patternKey, patternData) {
2500
+ return this.ellipse(x, y, r, r, style, patternKey, patternData);
2501
+ };
2502
+
2503
+ /**
2504
+ * Adds a properties to the PDF document
2505
+ *
2506
+ * @param {Object} properties A property_name-to-property_value object structure.
2507
+ * @function
2508
+ * @returns {jsPDF}
2509
+ * @methodOf jsPDF#
2510
+ * @name setProperties
2511
+ */
2512
+ API.setProperties = function(properties) {
2513
+ // copying only those properties we can render.
2514
+ for (var property in documentProperties) {
2515
+ if (documentProperties.hasOwnProperty(property) && properties[property]) {
2516
+ documentProperties[property] = properties[property];
2517
+ }
2518
+ }
2519
+ return this;
2520
+ };
2521
+
2522
+ /**
2523
+ * Sets font size for upcoming text elements.
2524
+ *
2525
+ * @param {Number} size Font size in points.
2526
+ * @function
2527
+ * @returns {jsPDF}
2528
+ * @methodOf jsPDF#
2529
+ * @name setFontSize
2530
+ */
2531
+ API.setFontSize = function(size) {
2532
+ activeFontSize = size;
2533
+ out("/" + activeFontKey + " " + activeFontSize + " Tf");
2534
+ return this;
2535
+ };
2536
+
2537
+ API.getFontSize = function () {
2538
+ return activeFontSize;
2539
+ };
2540
+
2541
+ /**
2542
+ * Sets text font face, variant for upcoming text elements.
2543
+ * See output of jsPDF.getFontList() for possible font names, styles.
2544
+ *
2545
+ * @param {String} fontName Font name or family. Example: "times"
2546
+ * @param {String} fontStyle Font style or variant. Example: "italic"
2547
+ * @function
2548
+ * @returns {jsPDF}
2549
+ * @methodOf jsPDF#
2550
+ * @name setFont
2551
+ */
2552
+ API.setFont = function(fontName, fontStyle) {
2553
+ activeFontKey = getFont(fontName, fontStyle);
2554
+ // if font is not found, the above line blows up and we never go further
2555
+ out("/" + activeFontKey + " " + activeFontSize + " Tf");
2556
+ return this;
2557
+ };
2558
+
2559
+ /**
2560
+ * Switches font style or variant for upcoming text elements,
2561
+ * while keeping the font face or family same.
2562
+ * See output of jsPDF.getFontList() for possible font names, styles.
2563
+ *
2564
+ * @param {String} style Font style or variant. Example: "italic"
2565
+ * @function
2566
+ * @returns {jsPDF}
2567
+ * @methodOf jsPDF#
2568
+ * @name setFontStyle
2569
+ */
2570
+ API.setFontStyle = API.setFontType = function(style) {
2571
+ activeFontKey = getFont(undefined, style);
2572
+ // if font is not found, the above line blows up and we never go further
2573
+ return this;
2574
+ };
2575
+
2576
+ /**
2577
+ * Returns an object - a tree of fontName to fontStyle relationships available to
2578
+ * active PDF document.
2579
+ *
2580
+ * @public
2581
+ * @function
2582
+ * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
2583
+ * @methodOf jsPDF#
2584
+ * @name getFontList
2585
+ */
2586
+ API.getFontList = function() {
2587
+ // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
2588
+ var list = {},fontName,fontStyle,tmp;
2589
+
2590
+ for (fontName in fontmap) {
2591
+ if (fontmap.hasOwnProperty(fontName)) {
2592
+ list[fontName] = tmp = [];
2593
+ for (fontStyle in fontmap[fontName]) {
2594
+ if (fontmap[fontName].hasOwnProperty(fontStyle)) {
2595
+ tmp.push(fontStyle);
2596
+ }
2597
+ }
2598
+ }
2599
+ }
2600
+
2601
+ return list;
2602
+ };
2603
+
2604
+ /**
2605
+ * Add a custom font.
2606
+ *
2607
+ * @param {String} postScriptName name of the Font. Example: "Menlo-Regular"
2608
+ * @param {String} fontName of font-family from @font-face definition. Example: "Menlo Regular"
2609
+ * @param {String} fontStyle style. Example: "normal"
2610
+ * @function
2611
+ * @returns the {fontKey} (same as the internal method)
2612
+ * @methodOf jsPDF#
2613
+ * @name addFont
2614
+ */
2615
+ API.addFont = function(postScriptName, fontName, fontStyle) {
2616
+ addFont(postScriptName, fontName, fontStyle, 'StandardEncoding');
2617
+ };
2618
+
2619
+ /**
2620
+ * Sets line width for upcoming lines.
2621
+ *
2622
+ * @param {Number} width Line width (in units declared at inception of PDF document)
2623
+ * @function
2624
+ * @returns {jsPDF}
2625
+ * @methodOf jsPDF#
2626
+ * @name setLineWidth
2627
+ */
2628
+ API.setLineWidth = function(width) {
2629
+ out(width.toFixed(2) + ' w');
2630
+ return this;
2631
+ };
2632
+
2633
+ /**
2634
+ * Sets the stroke color for upcoming elements.
2635
+ *
2636
+ * Depending on the number of arguments given, Gray, RGB, or CMYK
2637
+ * color space is implied.
2638
+ *
2639
+ * When only ch1 is given, "Gray" color space is implied and it
2640
+ * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
2641
+ * if values are communicated as String types, or in range from 0 (black)
2642
+ * to 255 (white) if communicated as Number type.
2643
+ * The RGB-like 0-255 range is provided for backward compatibility.
2644
+ *
2645
+ * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
2646
+ * value must be in the range from 0.00 (minimum intensity) to to 1.00
2647
+ * (max intensity) if values are communicated as String types, or
2648
+ * from 0 (min intensity) to to 255 (max intensity) if values are communicated
2649
+ * as Number types.
2650
+ * The RGB-like 0-255 range is provided for backward compatibility.
2651
+ *
2652
+ * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
2653
+ * value must be a in the range from 0.00 (0% concentration) to to
2654
+ * 1.00 (100% concentration)
2655
+ *
2656
+ * Because JavaScript treats fixed point numbers badly (rounds to
2657
+ * floating point nearest to binary representation) it is highly advised to
2658
+ * communicate the fractional numbers as String types, not JavaScript Number type.
2659
+ *
2660
+ * @param {Number|String} ch1 Color channel value
2661
+ * @param {Number|String} ch2 Color channel value
2662
+ * @param {Number|String} ch3 Color channel value
2663
+ * @param {Number|String} ch4 Color channel value
2664
+ *
2665
+ * @function
2666
+ * @returns {jsPDF}
2667
+ * @methodOf jsPDF#
2668
+ * @name setDrawColor
2669
+ */
2670
+ API.setDrawColor = function(ch1, ch2, ch3, ch4) {
2671
+ var color;
2672
+ if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
2673
+ // Gray color space.
2674
+ if (typeof ch1 === 'string') {
2675
+ color = ch1 + ' G';
2676
+ } else {
2677
+ color = f2(ch1 / 255) + ' G';
2678
+ }
2679
+ } else if (ch4 === undefined) {
2680
+ // RGB
2681
+ if (typeof ch1 === 'string') {
2682
+ color = [ch1, ch2, ch3, 'RG'].join(' ');
2683
+ } else {
2684
+ color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' ');
2685
+ }
2686
+ } else {
2687
+ // CMYK
2688
+ if (typeof ch1 === 'string') {
2689
+ color = [ch1, ch2, ch3, ch4, 'K'].join(' ');
2690
+ } else {
2691
+ color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' ');
2692
+ }
2693
+ }
2694
+
2695
+ out(color);
2696
+ return this;
2697
+ };
2698
+
2699
+ /**
2700
+ * Sets the fill color for upcoming elements.
2701
+ *
2702
+ * Depending on the number of arguments given, Gray, RGB, or CMYK
2703
+ * color space is implied.
2704
+ *
2705
+ * When only ch1 is given, "Gray" color space is implied and it
2706
+ * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
2707
+ * if values are communicated as String types, or in range from 0 (black)
2708
+ * to 255 (white) if communicated as Number type.
2709
+ * The RGB-like 0-255 range is provided for backward compatibility.
2710
+ *
2711
+ * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
2712
+ * value must be in the range from 0.00 (minimum intensity) to to 1.00
2713
+ * (max intensity) if values are communicated as String types, or
2714
+ * from 0 (min intensity) to to 255 (max intensity) if values are communicated
2715
+ * as Number types.
2716
+ * The RGB-like 0-255 range is provided for backward compatibility.
2717
+ *
2718
+ * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
2719
+ * value must be a in the range from 0.00 (0% concentration) to to
2720
+ * 1.00 (100% concentration)
2721
+ *
2722
+ * Because JavaScript treats fixed point numbers badly (rounds to
2723
+ * floating point nearest to binary representation) it is highly advised to
2724
+ * communicate the fractional numbers as String types, not JavaScript Number type.
2725
+ *
2726
+ * @param {Number|String} ch1 Color channel value
2727
+ * @param {Number|String} ch2 Color channel value
2728
+ * @param {Number|String} ch3 Color channel value
2729
+ * @param {Number|String} ch4 Color channel value
2730
+ *
2731
+ * @function
2732
+ * @returns {jsPDF}
2733
+ * @methodOf jsPDF#
2734
+ * @name setFillColor
2735
+ */
2736
+ API.setFillColor = function(ch1, ch2, ch3, ch4) {
2737
+ var color;
2738
+
2739
+ if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
2740
+ // Gray color space.
2741
+ if (typeof ch1 === 'string') {
2742
+ color = ch1 + ' g';
2743
+ } else {
2744
+ color = f2(ch1 / 255) + ' g';
2745
+ }
2746
+ } else if (ch4 === undefined || typeof ch4 === 'object') {
2747
+ // RGB
2748
+ if (typeof ch1 === 'string') {
2749
+ color = [ch1, ch2, ch3, 'rg'].join(' ');
2750
+ } else {
2751
+ color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' ');
2752
+ }
2753
+ if (ch4 && ch4.a === 0){
2754
+ //TODO Implement transparency.
2755
+ //WORKAROUND use white for now
2756
+ color = ['255', '255', '255', 'rg'].join(' ');
2757
+ }
2758
+ } else {
2759
+ // CMYK
2760
+ if (typeof ch1 === 'string') {
2761
+ color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
2762
+ } else {
2763
+ color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
2764
+ }
2765
+ }
2766
+
2767
+ out(color);
2768
+ return this;
2769
+ };
2770
+
2771
+ /**
2772
+ * Sets the text color for upcoming elements.
2773
+ * If only one, first argument is given,
2774
+ * treats the value as gray-scale color value.
2775
+ *
2776
+ * @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF'
2777
+ * @param {Number} g Green channel color value in range 0-255
2778
+ * @param {Number} b Blue channel color value in range 0-255
2779
+ * @function
2780
+ * @returns {jsPDF}
2781
+ * @methodOf jsPDF#
2782
+ * @name setTextColor
2783
+ */
2784
+ API.setTextColor = function(r, g, b) {
2785
+ if ((typeof r === 'string') && /^#[0-9A-Fa-f]{6}$/.test(r)) {
2786
+ var hex = parseInt(r.substr(1), 16);
2787
+ r = (hex >> 16) & 255;
2788
+ g = (hex >> 8) & 255;
2789
+ b = (hex & 255);
2790
+ }
2791
+
2792
+ if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
2793
+ textColor = f3(r / 255) + ' g';
2794
+ } else {
2795
+ textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
2796
+ }
2797
+
2798
+ out(textColor);
2799
+
2800
+ return this;
2801
+ };
2802
+
2803
+ /**
2804
+ * Sets a either previously added {@link GState} (via {@link addGState}) or a new {@link GState}.
2805
+ * @param {String|GState} gState If type is string, a previously added GState is used, if type is GState
2806
+ * it will be added before use.
2807
+ * @function
2808
+ * @returns {jsPDF}
2809
+ * @methodOf jsPDF#
2810
+ * @name setGState
2811
+ */
2812
+ API.setGState = function (gState) {
2813
+ if (typeof gState === "string") {
2814
+ gState = gStates[gStatesMap[gState]];
2815
+ } else {
2816
+ gState = addGState(null, gState);
2817
+ }
2818
+
2819
+ if (!gState.equals(activeGState)) {
2820
+ out("/" + gState.id + " gs");
2821
+ activeGState = gState;
2822
+ }
2823
+ };
2824
+
2825
+ /**
2826
+ * Is an Object providing a mapping from human-readable to
2827
+ * integer flag values designating the varieties of line cap
2828
+ * and join styles.
2829
+ *
2830
+ * @returns {Object}
2831
+ * @fieldOf jsPDF#
2832
+ * @name CapJoinStyles
2833
+ */
2834
+ API.CapJoinStyles = {
2835
+ 0 : 0,
2836
+ 'butt' : 0,
2837
+ 'but' : 0,
2838
+ 'miter' : 0,
2839
+ 1 : 1,
2840
+ 'round' : 1,
2841
+ 'rounded' : 1,
2842
+ 'circle' : 1,
2843
+ 2 : 2,
2844
+ 'projecting' : 2,
2845
+ 'project' : 2,
2846
+ 'square' : 2,
2847
+ 'bevel' : 2
2848
+ };
2849
+
2850
+ /**
2851
+ * Sets the line cap styles
2852
+ * See {jsPDF.CapJoinStyles} for variants
2853
+ *
2854
+ * @param {String|Number} style A string or number identifying the type of line cap
2855
+ * @function
2856
+ * @returns {jsPDF}
2857
+ * @methodOf jsPDF#
2858
+ * @name setLineCap
2859
+ */
2860
+ API.setLineCap = function(style) {
2861
+ var id = this.CapJoinStyles[style];
2862
+ if (id === undefined) {
2863
+ throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
2864
+ }
2865
+ lineCapID = id;
2866
+ out(id + ' J');
2867
+
2868
+ return this;
2869
+ };
2870
+
2871
+ /**
2872
+ * Sets the line join styles
2873
+ * See {jsPDF.CapJoinStyles} for variants
2874
+ *
2875
+ * @param {String|Number} style A string or number identifying the type of line join
2876
+ * @function
2877
+ * @returns {jsPDF}
2878
+ * @methodOf jsPDF#
2879
+ * @name setLineJoin
2880
+ */
2881
+ API.setLineJoin = function(style) {
2882
+ var id = this.CapJoinStyles[style];
2883
+ if (id === undefined) {
2884
+ throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
2885
+ }
2886
+ lineJoinID = id;
2887
+ out(id + ' j');
2888
+
2889
+ return this;
2890
+ };
2891
+
2892
+ /**
2893
+ * Sets the miter limit.
2894
+ * @param {number} miterLimit
2895
+ * @function
2896
+ * @returns {jsPDF}
2897
+ * @methodOf jsPDF#
2898
+ * @name setMiterLimit
2899
+ */
2900
+ API.setLineMiterLimit = function (miterLimit) {
2901
+ out(f2(miterLimit) + " M");
2902
+
2903
+ return this;
2904
+ };
2905
+
2906
+ /**
2907
+ * Sets the line dash pattern.
2908
+ * @param {Array<number>} array An array containing 0-2 numbers. The first number sets the length of the
2909
+ * dashes, the second number the length of the gaps. If the second number is missing, the gaps are considered
2910
+ * to be as long as the dashes. An empty array means solid, unbroken lines.
2911
+ * @param phase The phase lines start with.
2912
+ * @function
2913
+ * @returns {jsPDF}
2914
+ * @methodOf jsPDF#
2915
+ * @name setLineDashPattern
2916
+ */
2917
+ API.setLineDashPattern = function (array, phase) {
2918
+ out([
2919
+ "[" + (array[0] !== undefined ? array[0] : ""),
2920
+ (array[1] !== undefined ? array[1] : "" ) + "]",
2921
+ phase,
2922
+ "d"
2923
+ ].join(" "));
2924
+
2925
+ return this;
2926
+ };
2927
+
2928
+ // Output is both an internal (for plugins) and external function
2929
+ API.output = output;
2930
+
2931
+ /**
2932
+ * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
2933
+ * @param {String} filename The filename including extension.
2934
+ *
2935
+ * @function
2936
+ * @returns {jsPDF}
2937
+ * @methodOf jsPDF#
2938
+ * @name save
2939
+ */
2940
+ API.save = function(filename) {
2941
+ API.output('save', filename);
2942
+ };
2943
+
2944
+ // applying plugins (more methods) ON TOP of built-in API.
2945
+ // this is intentional as we allow plugins to override
2946
+ // built-ins
2947
+ for (var plugin in jsPDF.API) {
2948
+ if (jsPDF.API.hasOwnProperty(plugin)) {
2949
+ if (plugin === 'events' && jsPDF.API.events.length) {
2950
+ (function(events, newEvents) {
2951
+
2952
+ // jsPDF.API.events is a JS Array of Arrays
2953
+ // where each Array is a pair of event name, handler
2954
+ // Events were added by plugins to the jsPDF instantiator.
2955
+ // These are always added to the new instance and some ran
2956
+ // during instantiation.
2957
+ var eventname,handler_and_args,i;
2958
+
2959
+ for (i = newEvents.length - 1; i !== -1; i--) {
2960
+ // subscribe takes 3 args: 'topic', function, runonce_flag
2961
+ // if undefined, runonce is false.
2962
+ // users can attach callback directly,
2963
+ // or they can attach an array with [callback, runonce_flag]
2964
+ // that's what the "apply" magic is for below.
2965
+ eventname = newEvents[i][0];
2966
+ handler_and_args = newEvents[i][1];
2967
+ events.subscribe.apply(
2968
+ events,
2969
+ [eventname].concat(
2970
+ typeof handler_and_args === 'function' ?
2971
+ [handler_and_args] : handler_and_args));
2972
+ }
2973
+ }(events, jsPDF.API.events));
2974
+ } else {
2975
+ API[plugin] = jsPDF.API[plugin];
2976
+ }
2977
+ }
2978
+ }
2979
+
2980
+ //////////////////////////////////////////////////////
2981
+ // continuing initialization of jsPDF Document object
2982
+ //////////////////////////////////////////////////////
2983
+ // Add the first page automatically
2984
+ addFonts();
2985
+ activeFontKey = 'F1';
2986
+ _addPage(format, orientation);
2987
+
2988
+ events.publish('initialized');
2989
+ return API;
2990
+ }
2991
+
2992
+ /**
2993
+ * jsPDF.API is a STATIC property of jsPDF class.
2994
+ * jsPDF.API is an object you can add methods and properties to.
2995
+ * The methods / properties you add will show up in new jsPDF objects.
2996
+ *
2997
+ * One property is prepopulated. It is the 'events' Object. Plugin authors can add topics,
2998
+ * callbacks to this object. These will be reassigned to all new instances of jsPDF.
2999
+ * Examples:
3000
+ * jsPDF.API.events['initialized'] = function(){ 'this' is API object }
3001
+ * jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
3002
+ *
3003
+ * @static
3004
+ * @public
3005
+ * @memberOf jsPDF
3006
+ * @name API
3007
+ *
3008
+ * @example
3009
+ * jsPDF.API.mymethod = function(){
3010
+ * // 'this' will be ref to internal API object. see jsPDF source
3011
+ * // , so you can refer to built-in methods like so:
3012
+ * // this.line(....)
3013
+ * // this.text(....)
3014
+ * }
3015
+ * var pdfdoc = new jsPDF()
3016
+ * pdfdoc.mymethod() // <- !!!!!!
3017
+ */
3018
+ jsPDF.API = {events:[]};
3019
+ jsPDF.version = "1.0.0-trunk";
3020
+
3021
+ if (typeof define === 'function' && define.amd) {
3022
+ define('jsPDF', function() {
3023
+ return jsPDF;
3024
+ });
3025
+ } else if (typeof module !== 'undefined' && module.exports) {
3026
+ module.exports = jsPDF;
3027
+ } else {
3028
+ global.jsPDF = jsPDF;
3029
+ }
3030
+ return jsPDF;
3031
+ }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this));