highcharts-rails 5.0.14 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +60 -0
  3. data/Rakefile +54 -5
  4. data/app/assets/images/highcharts/earth.svg +432 -0
  5. data/app/assets/javascripts/highcharts.js +5103 -3147
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +930 -277
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +1374 -249
  8. data/app/assets/javascripts/highcharts/lib/canvg.js +3073 -0
  9. data/app/assets/javascripts/highcharts/lib/jspdf.js +16624 -0
  10. data/app/assets/javascripts/highcharts/lib/rgbcolor.js +299 -0
  11. data/app/assets/javascripts/highcharts/lib/svg2pdf.js +3488 -0
  12. data/app/assets/javascripts/highcharts/modules/accessibility.js +654 -212
  13. data/app/assets/javascripts/highcharts/modules/annotations.js +1552 -274
  14. data/app/assets/javascripts/highcharts/modules/boost-canvas.js +773 -0
  15. data/app/assets/javascripts/highcharts/modules/boost.js +636 -210
  16. data/app/assets/javascripts/highcharts/modules/broken-axis.js +2 -2
  17. data/app/assets/javascripts/highcharts/modules/bullet.js +364 -0
  18. data/app/assets/javascripts/highcharts/modules/data.js +766 -38
  19. data/app/assets/javascripts/highcharts/modules/drag-panes.js +588 -0
  20. data/app/assets/javascripts/highcharts/modules/drilldown.js +106 -36
  21. data/app/assets/javascripts/highcharts/modules/export-data.js +597 -0
  22. data/app/assets/javascripts/highcharts/modules/exporting.js +424 -162
  23. data/app/assets/javascripts/highcharts/modules/funnel.js +144 -22
  24. data/app/assets/javascripts/highcharts/modules/gantt.js +1154 -0
  25. data/app/assets/javascripts/highcharts/modules/grid-axis.js +1 -1
  26. data/app/assets/javascripts/highcharts/modules/heatmap.js +406 -80
  27. data/app/assets/javascripts/highcharts/modules/histogram-bellcurve.js +513 -0
  28. data/app/assets/javascripts/highcharts/modules/item-series.js +126 -0
  29. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +31 -13
  30. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +179 -57
  31. data/app/assets/javascripts/highcharts/modules/oldie.js +1378 -0
  32. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +8 -6
  33. data/app/assets/javascripts/highcharts/modules/parallel-coordinates.js +494 -0
  34. data/app/assets/javascripts/highcharts/modules/pareto.js +275 -0
  35. data/app/assets/javascripts/highcharts/modules/sankey.js +641 -0
  36. data/app/assets/javascripts/highcharts/modules/series-label.js +355 -145
  37. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +122 -1
  38. data/app/assets/javascripts/highcharts/modules/static-scale.js +64 -0
  39. data/app/assets/javascripts/highcharts/modules/stock.js +1944 -676
  40. data/app/assets/javascripts/highcharts/modules/streamgraph.js +139 -0
  41. data/app/assets/javascripts/highcharts/modules/sunburst.js +2403 -0
  42. data/app/assets/javascripts/highcharts/modules/tilemap.js +1199 -0
  43. data/app/assets/javascripts/highcharts/modules/treemap.js +538 -134
  44. data/app/assets/javascripts/highcharts/modules/variable-pie.js +490 -0
  45. data/app/assets/javascripts/highcharts/modules/variwide.js +283 -0
  46. data/app/assets/javascripts/highcharts/modules/vector.js +294 -0
  47. data/app/assets/javascripts/highcharts/modules/windbarb.js +490 -0
  48. data/app/assets/javascripts/highcharts/modules/wordcloud.js +681 -0
  49. data/app/assets/javascripts/highcharts/modules/xrange.js +615 -0
  50. data/app/assets/javascripts/highcharts/themes/avocado.js +54 -0
  51. data/app/assets/javascripts/highcharts/themes/dark-blue.js +6 -6
  52. data/app/assets/javascripts/highcharts/themes/dark-green.js +6 -6
  53. data/app/assets/javascripts/highcharts/themes/dark-unica.js +6 -6
  54. data/app/assets/javascripts/highcharts/themes/gray.js +14 -10
  55. data/app/assets/javascripts/highcharts/themes/grid-light.js +6 -6
  56. data/app/assets/javascripts/highcharts/themes/grid.js +7 -5
  57. data/app/assets/javascripts/highcharts/themes/sand-signika.js +8 -7
  58. data/app/assets/javascripts/highcharts/themes/skies.js +15 -9
  59. data/app/assets/javascripts/highcharts/themes/sunset.js +53 -0
  60. data/app/assets/stylesheets/highcharts/highcharts.css +802 -0
  61. data/app/assets/stylesheets/highcharts/highcharts.scss +665 -0
  62. data/lib/highcharts/version.rb +1 -1
  63. metadata +31 -1
@@ -0,0 +1,299 @@
1
+ /** @preserve
2
+ * A class to parse color values
3
+ * @author Stoyan Stefanov <sstoo@gmail.com>
4
+ * @link http://www.phpied.com/rgb-color-parser-in-javascript/
5
+ * @license Use it if you like it
6
+ */
7
+ (function (global) {
8
+ function RGBColor(color_string)
9
+ {
10
+ this.ok = false;
11
+
12
+ // strip any leading #
13
+ if (color_string.charAt(0) == '#') { // remove # if any
14
+ color_string = color_string.substr(1,6);
15
+ }
16
+
17
+ color_string = color_string.replace(/ /g,'');
18
+ color_string = color_string.toLowerCase();
19
+
20
+ // before getting into regexps, try simple matches
21
+ // and overwrite the input
22
+ var simple_colors = {
23
+ aliceblue: 'f0f8ff',
24
+ antiquewhite: 'faebd7',
25
+ aqua: '00ffff',
26
+ aquamarine: '7fffd4',
27
+ azure: 'f0ffff',
28
+ beige: 'f5f5dc',
29
+ bisque: 'ffe4c4',
30
+ black: '000000',
31
+ blanchedalmond: 'ffebcd',
32
+ blue: '0000ff',
33
+ blueviolet: '8a2be2',
34
+ brown: 'a52a2a',
35
+ burlywood: 'deb887',
36
+ cadetblue: '5f9ea0',
37
+ chartreuse: '7fff00',
38
+ chocolate: 'd2691e',
39
+ coral: 'ff7f50',
40
+ cornflowerblue: '6495ed',
41
+ cornsilk: 'fff8dc',
42
+ crimson: 'dc143c',
43
+ cyan: '00ffff',
44
+ darkblue: '00008b',
45
+ darkcyan: '008b8b',
46
+ darkgoldenrod: 'b8860b',
47
+ darkgray: 'a9a9a9',
48
+ darkgreen: '006400',
49
+ darkkhaki: 'bdb76b',
50
+ darkmagenta: '8b008b',
51
+ darkolivegreen: '556b2f',
52
+ darkorange: 'ff8c00',
53
+ darkorchid: '9932cc',
54
+ darkred: '8b0000',
55
+ darksalmon: 'e9967a',
56
+ darkseagreen: '8fbc8f',
57
+ darkslateblue: '483d8b',
58
+ darkslategray: '2f4f4f',
59
+ darkturquoise: '00ced1',
60
+ darkviolet: '9400d3',
61
+ deeppink: 'ff1493',
62
+ deepskyblue: '00bfff',
63
+ dimgray: '696969',
64
+ dodgerblue: '1e90ff',
65
+ feldspar: 'd19275',
66
+ firebrick: 'b22222',
67
+ floralwhite: 'fffaf0',
68
+ forestgreen: '228b22',
69
+ fuchsia: 'ff00ff',
70
+ gainsboro: 'dcdcdc',
71
+ ghostwhite: 'f8f8ff',
72
+ gold: 'ffd700',
73
+ goldenrod: 'daa520',
74
+ gray: '808080',
75
+ green: '008000',
76
+ greenyellow: 'adff2f',
77
+ honeydew: 'f0fff0',
78
+ hotpink: 'ff69b4',
79
+ indianred : 'cd5c5c',
80
+ indigo : '4b0082',
81
+ ivory: 'fffff0',
82
+ khaki: 'f0e68c',
83
+ lavender: 'e6e6fa',
84
+ lavenderblush: 'fff0f5',
85
+ lawngreen: '7cfc00',
86
+ lemonchiffon: 'fffacd',
87
+ lightblue: 'add8e6',
88
+ lightcoral: 'f08080',
89
+ lightcyan: 'e0ffff',
90
+ lightgoldenrodyellow: 'fafad2',
91
+ lightgrey: 'd3d3d3',
92
+ lightgreen: '90ee90',
93
+ lightpink: 'ffb6c1',
94
+ lightsalmon: 'ffa07a',
95
+ lightseagreen: '20b2aa',
96
+ lightskyblue: '87cefa',
97
+ lightslateblue: '8470ff',
98
+ lightslategray: '778899',
99
+ lightsteelblue: 'b0c4de',
100
+ lightyellow: 'ffffe0',
101
+ lime: '00ff00',
102
+ limegreen: '32cd32',
103
+ linen: 'faf0e6',
104
+ magenta: 'ff00ff',
105
+ maroon: '800000',
106
+ mediumaquamarine: '66cdaa',
107
+ mediumblue: '0000cd',
108
+ mediumorchid: 'ba55d3',
109
+ mediumpurple: '9370d8',
110
+ mediumseagreen: '3cb371',
111
+ mediumslateblue: '7b68ee',
112
+ mediumspringgreen: '00fa9a',
113
+ mediumturquoise: '48d1cc',
114
+ mediumvioletred: 'c71585',
115
+ midnightblue: '191970',
116
+ mintcream: 'f5fffa',
117
+ mistyrose: 'ffe4e1',
118
+ moccasin: 'ffe4b5',
119
+ navajowhite: 'ffdead',
120
+ navy: '000080',
121
+ oldlace: 'fdf5e6',
122
+ olive: '808000',
123
+ olivedrab: '6b8e23',
124
+ orange: 'ffa500',
125
+ orangered: 'ff4500',
126
+ orchid: 'da70d6',
127
+ palegoldenrod: 'eee8aa',
128
+ palegreen: '98fb98',
129
+ paleturquoise: 'afeeee',
130
+ palevioletred: 'd87093',
131
+ papayawhip: 'ffefd5',
132
+ peachpuff: 'ffdab9',
133
+ peru: 'cd853f',
134
+ pink: 'ffc0cb',
135
+ plum: 'dda0dd',
136
+ powderblue: 'b0e0e6',
137
+ purple: '800080',
138
+ red: 'ff0000',
139
+ rosybrown: 'bc8f8f',
140
+ royalblue: '4169e1',
141
+ saddlebrown: '8b4513',
142
+ salmon: 'fa8072',
143
+ sandybrown: 'f4a460',
144
+ seagreen: '2e8b57',
145
+ seashell: 'fff5ee',
146
+ sienna: 'a0522d',
147
+ silver: 'c0c0c0',
148
+ skyblue: '87ceeb',
149
+ slateblue: '6a5acd',
150
+ slategray: '708090',
151
+ snow: 'fffafa',
152
+ springgreen: '00ff7f',
153
+ steelblue: '4682b4',
154
+ tan: 'd2b48c',
155
+ teal: '008080',
156
+ thistle: 'd8bfd8',
157
+ tomato: 'ff6347',
158
+ turquoise: '40e0d0',
159
+ violet: 'ee82ee',
160
+ violetred: 'd02090',
161
+ wheat: 'f5deb3',
162
+ white: 'ffffff',
163
+ whitesmoke: 'f5f5f5',
164
+ yellow: 'ffff00',
165
+ yellowgreen: '9acd32'
166
+ };
167
+ for (var key in simple_colors) {
168
+ if (color_string == key) {
169
+ color_string = simple_colors[key];
170
+ }
171
+ }
172
+ // emd of simple type-in colors
173
+
174
+ // array of color definition objects
175
+ var color_defs = [
176
+ {
177
+ re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
178
+ example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
179
+ process: function (bits){
180
+ return [
181
+ parseInt(bits[1]),
182
+ parseInt(bits[2]),
183
+ parseInt(bits[3])
184
+ ];
185
+ }
186
+ },
187
+ {
188
+ re: /^(\w{2})(\w{2})(\w{2})$/,
189
+ example: ['#00ff00', '336699'],
190
+ process: function (bits){
191
+ return [
192
+ parseInt(bits[1], 16),
193
+ parseInt(bits[2], 16),
194
+ parseInt(bits[3], 16)
195
+ ];
196
+ }
197
+ },
198
+ {
199
+ re: /^(\w{1})(\w{1})(\w{1})$/,
200
+ example: ['#fb0', 'f0f'],
201
+ process: function (bits){
202
+ return [
203
+ parseInt(bits[1] + bits[1], 16),
204
+ parseInt(bits[2] + bits[2], 16),
205
+ parseInt(bits[3] + bits[3], 16)
206
+ ];
207
+ }
208
+ }
209
+ ];
210
+
211
+ // search through the definitions to find a match
212
+ for (var i = 0; i < color_defs.length; i++) {
213
+ var re = color_defs[i].re;
214
+ var processor = color_defs[i].process;
215
+ var bits = re.exec(color_string);
216
+ if (bits) {
217
+ channels = processor(bits);
218
+ this.r = channels[0];
219
+ this.g = channels[1];
220
+ this.b = channels[2];
221
+ this.ok = true;
222
+ }
223
+
224
+ }
225
+
226
+ // validate/cleanup values
227
+ this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
228
+ this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
229
+ this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
230
+
231
+ // some getters
232
+ this.toRGB = function () {
233
+ return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
234
+ }
235
+ this.toHex = function () {
236
+ var r = this.r.toString(16);
237
+ var g = this.g.toString(16);
238
+ var b = this.b.toString(16);
239
+ if (r.length == 1) r = '0' + r;
240
+ if (g.length == 1) g = '0' + g;
241
+ if (b.length == 1) b = '0' + b;
242
+ return '#' + r + g + b;
243
+ }
244
+
245
+ // help
246
+ this.getHelpXML = function () {
247
+
248
+ var examples = new Array();
249
+ // add regexps
250
+ for (var i = 0; i < color_defs.length; i++) {
251
+ var example = color_defs[i].example;
252
+ for (var j = 0; j < example.length; j++) {
253
+ examples[examples.length] = example[j];
254
+ }
255
+ }
256
+ // add type-in colors
257
+ for (var sc in simple_colors) {
258
+ examples[examples.length] = sc;
259
+ }
260
+
261
+ var xml = document.createElement('ul');
262
+ xml.setAttribute('id', 'rgbcolor-examples');
263
+ for (var i = 0; i < examples.length; i++) {
264
+ try {
265
+ var list_item = document.createElement('li');
266
+ var list_color = new RGBColor(examples[i]);
267
+ var example_div = document.createElement('div');
268
+ example_div.style.cssText =
269
+ 'margin: 3px; '
270
+ + 'border: 1px solid black; '
271
+ + 'background:' + list_color.toHex() + '; '
272
+ + 'color:' + list_color.toHex()
273
+ ;
274
+ example_div.appendChild(document.createTextNode('test'));
275
+ var list_item_value = document.createTextNode(
276
+ ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
277
+ );
278
+ list_item.appendChild(example_div);
279
+ list_item.appendChild(list_item_value);
280
+ xml.appendChild(list_item);
281
+
282
+ } catch(e){}
283
+ }
284
+ return xml;
285
+
286
+ }
287
+
288
+ }
289
+ if (typeof define === "function" && define.amd) {
290
+ define(function () {
291
+ return RGBColor;
292
+ });
293
+ } else if (typeof module !== "undefined" && module.exports) {
294
+ module.exports = RGBColor;
295
+ } else {
296
+ global.RGBColor = RGBColor;
297
+ }
298
+ return RGBColor;
299
+ })(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this);
@@ -0,0 +1,3488 @@
1
+ /**
2
+ * Modules in this bundle
3
+ * @license
4
+ *
5
+ * svg2pdf.js:
6
+ * license: MIT (http://opensource.org/licenses/MIT)
7
+ * author: yFiles for HTML Support Team <yfileshtml@yworks.com>
8
+ * homepage: https://github.com/yWorks/svg2pdf.js#readme
9
+ * version: 1.1.0
10
+ *
11
+ * svgpath:
12
+ * license: MIT (http://opensource.org/licenses/MIT)
13
+ * maintainers: vitaly <vitaly@rcdesign.ru>
14
+ * homepage: https://github.com/fontello/svgpath#readme
15
+ * version: 2.2.1
16
+ *
17
+ * This header is generated by licensify (https://github.com/twada/licensify)
18
+ */
19
+ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.svg2pdf = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
20
+ 'use strict';
21
+
22
+ module.exports = require('./lib/svgpath');
23
+
24
+ },{"./lib/svgpath":6}],2:[function(require,module,exports){
25
+ // Convert an arc to a sequence of cubic bézier curves
26
+ //
27
+ 'use strict';
28
+
29
+
30
+ var TAU = Math.PI * 2;
31
+
32
+
33
+ /* eslint-disable space-infix-ops */
34
+
35
+ // Calculate an angle between two vectors
36
+ //
37
+ function vector_angle(ux, uy, vx, vy) {
38
+ var sign = (ux * vy - uy * vx < 0) ? -1 : 1;
39
+ var umag = Math.sqrt(ux * ux + uy * uy);
40
+ var vmag = Math.sqrt(ux * ux + uy * uy);
41
+ var dot = ux * vx + uy * vy;
42
+ var div = dot / (umag * vmag);
43
+
44
+ // rounding errors, e.g. -1.0000000000000002 can screw up this
45
+ if (div > 1.0) { div = 1.0; }
46
+ if (div < -1.0) { div = -1.0; }
47
+
48
+ return sign * Math.acos(div);
49
+ }
50
+
51
+
52
+ // Convert from endpoint to center parameterization,
53
+ // see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
54
+ //
55
+ // Return [cx, cy, theta1, delta_theta]
56
+ //
57
+ function get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) {
58
+ // Step 1.
59
+ //
60
+ // Moving an ellipse so origin will be the middlepoint between our two
61
+ // points. After that, rotate it to line up ellipse axes with coordinate
62
+ // axes.
63
+ //
64
+ var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
65
+ var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
66
+
67
+ var rx_sq = rx * rx;
68
+ var ry_sq = ry * ry;
69
+ var x1p_sq = x1p * x1p;
70
+ var y1p_sq = y1p * y1p;
71
+
72
+ // Step 2.
73
+ //
74
+ // Compute coordinates of the centre of this ellipse (cx', cy')
75
+ // in the new coordinate system.
76
+ //
77
+ var radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq);
78
+
79
+ if (radicant < 0) {
80
+ // due to rounding errors it might be e.g. -1.3877787807814457e-17
81
+ radicant = 0;
82
+ }
83
+
84
+ radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq);
85
+ radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
86
+
87
+ var cxp = radicant * rx/ry * y1p;
88
+ var cyp = radicant * -ry/rx * x1p;
89
+
90
+ // Step 3.
91
+ //
92
+ // Transform back to get centre coordinates (cx, cy) in the original
93
+ // coordinate system.
94
+ //
95
+ var cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2;
96
+ var cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2;
97
+
98
+ // Step 4.
99
+ //
100
+ // Compute angles (theta1, delta_theta).
101
+ //
102
+ var v1x = (x1p - cxp) / rx;
103
+ var v1y = (y1p - cyp) / ry;
104
+ var v2x = (-x1p - cxp) / rx;
105
+ var v2y = (-y1p - cyp) / ry;
106
+
107
+ var theta1 = vector_angle(1, 0, v1x, v1y);
108
+ var delta_theta = vector_angle(v1x, v1y, v2x, v2y);
109
+
110
+ if (fs === 0 && delta_theta > 0) {
111
+ delta_theta -= TAU;
112
+ }
113
+ if (fs === 1 && delta_theta < 0) {
114
+ delta_theta += TAU;
115
+ }
116
+
117
+ return [ cx, cy, theta1, delta_theta ];
118
+ }
119
+
120
+ //
121
+ // Approximate one unit arc segment with bézier curves,
122
+ // see http://math.stackexchange.com/questions/873224
123
+ //
124
+ function approximate_unit_arc(theta1, delta_theta) {
125
+ var alpha = 4/3 * Math.tan(delta_theta/4);
126
+
127
+ var x1 = Math.cos(theta1);
128
+ var y1 = Math.sin(theta1);
129
+ var x2 = Math.cos(theta1 + delta_theta);
130
+ var y2 = Math.sin(theta1 + delta_theta);
131
+
132
+ return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
133
+ }
134
+
135
+ module.exports = function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
136
+ var sin_phi = Math.sin(phi * TAU / 360);
137
+ var cos_phi = Math.cos(phi * TAU / 360);
138
+
139
+ // Make sure radii are valid
140
+ //
141
+ var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
142
+ var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
143
+
144
+ if (x1p === 0 && y1p === 0) {
145
+ // we're asked to draw line to itself
146
+ return [];
147
+ }
148
+
149
+ if (rx === 0 || ry === 0) {
150
+ // one of the radii is zero
151
+ return [];
152
+ }
153
+
154
+
155
+ // Compensate out-of-range radii
156
+ //
157
+ rx = Math.abs(rx);
158
+ ry = Math.abs(ry);
159
+
160
+ var lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
161
+ if (lambda > 1) {
162
+ rx *= Math.sqrt(lambda);
163
+ ry *= Math.sqrt(lambda);
164
+ }
165
+
166
+
167
+ // Get center parameters (cx, cy, theta1, delta_theta)
168
+ //
169
+ var cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi);
170
+
171
+ var result = [];
172
+ var theta1 = cc[2];
173
+ var delta_theta = cc[3];
174
+
175
+ // Split an arc to multiple segments, so each segment
176
+ // will be less than τ/4 (= 90°)
177
+ //
178
+ var segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1);
179
+ delta_theta /= segments;
180
+
181
+ for (var i = 0; i < segments; i++) {
182
+ result.push(approximate_unit_arc(theta1, delta_theta));
183
+ theta1 += delta_theta;
184
+ }
185
+
186
+ // We have a bezier approximation of a unit circle,
187
+ // now need to transform back to the original ellipse
188
+ //
189
+ return result.map(function (curve) {
190
+ for (var i = 0; i < curve.length; i += 2) {
191
+ var x = curve[i + 0];
192
+ var y = curve[i + 1];
193
+
194
+ // scale
195
+ x *= rx;
196
+ y *= ry;
197
+
198
+ // rotate
199
+ var xp = cos_phi*x - sin_phi*y;
200
+ var yp = sin_phi*x + cos_phi*y;
201
+
202
+ // translate
203
+ curve[i + 0] = xp + cc[0];
204
+ curve[i + 1] = yp + cc[1];
205
+ }
206
+
207
+ return curve;
208
+ });
209
+ };
210
+
211
+ },{}],3:[function(require,module,exports){
212
+ 'use strict';
213
+
214
+ /* eslint-disable space-infix-ops */
215
+
216
+ // The precision used to consider an ellipse as a circle
217
+ //
218
+ var epsilon = 0.0000000001;
219
+
220
+ // To convert degree in radians
221
+ //
222
+ var torad = Math.PI / 180;
223
+
224
+ // Class constructor :
225
+ // an ellipse centred at 0 with radii rx,ry and x - axis - angle ax.
226
+ //
227
+ function Ellipse(rx, ry, ax) {
228
+ if (!(this instanceof Ellipse)) { return new Ellipse(rx, ry, ax); }
229
+ this.rx = rx;
230
+ this.ry = ry;
231
+ this.ax = ax;
232
+ }
233
+
234
+ // Apply a linear transform m to the ellipse
235
+ // m is an array representing a matrix :
236
+ // - -
237
+ // | m[0] m[2] |
238
+ // | m[1] m[3] |
239
+ // - -
240
+ //
241
+ Ellipse.prototype.transform = function (m) {
242
+ // We consider the current ellipse as image of the unit circle
243
+ // by first scale(rx,ry) and then rotate(ax) ...
244
+ // So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
245
+ var c = Math.cos(this.ax * torad), s = Math.sin(this.ax * torad);
246
+ var ma = [
247
+ this.rx * (m[0]*c + m[2]*s),
248
+ this.rx * (m[1]*c + m[3]*s),
249
+ this.ry * (-m[0]*s + m[2]*c),
250
+ this.ry * (-m[1]*s + m[3]*c)
251
+ ];
252
+
253
+ // ma * transpose(ma) = [ J L ]
254
+ // [ L K ]
255
+ // L is calculated later (if the image is not a circle)
256
+ var J = ma[0]*ma[0] + ma[2]*ma[2],
257
+ K = ma[1]*ma[1] + ma[3]*ma[3];
258
+
259
+ // the discriminant of the characteristic polynomial of ma * transpose(ma)
260
+ var D = ((ma[0]-ma[3])*(ma[0]-ma[3]) + (ma[2]+ma[1])*(ma[2]+ma[1])) *
261
+ ((ma[0]+ma[3])*(ma[0]+ma[3]) + (ma[2]-ma[1])*(ma[2]-ma[1]));
262
+
263
+ // the "mean eigenvalue"
264
+ var JK = (J + K) / 2;
265
+
266
+ // check if the image is (almost) a circle
267
+ if (D < epsilon * JK) {
268
+ // if it is
269
+ this.rx = this.ry = Math.sqrt(JK);
270
+ this.ax = 0;
271
+ return this;
272
+ }
273
+
274
+ // if it is not a circle
275
+ var L = ma[0]*ma[1] + ma[2]*ma[3];
276
+
277
+ D = Math.sqrt(D);
278
+
279
+ // {l1,l2} = the two eigen values of ma * transpose(ma)
280
+ var l1 = JK + D/2,
281
+ l2 = JK - D/2;
282
+ // the x - axis - rotation angle is the argument of the l1 - eigenvector
283
+ this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ?
284
+ 90
285
+ :
286
+ Math.atan(Math.abs(L) > Math.abs(l1 - K) ?
287
+ (l1 - J) / L
288
+ :
289
+ L / (l1 - K)
290
+ ) * 180 / Math.PI;
291
+
292
+ // if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
293
+ if (this.ax >= 0) {
294
+ // if ax in [0,90]
295
+ this.rx = Math.sqrt(l1);
296
+ this.ry = Math.sqrt(l2);
297
+ } else {
298
+ // if ax in ]-90,0[ => exchange axes
299
+ this.ax += 90;
300
+ this.rx = Math.sqrt(l2);
301
+ this.ry = Math.sqrt(l1);
302
+ }
303
+
304
+ return this;
305
+ };
306
+
307
+ // Check if the ellipse is (almost) degenerate, i.e. rx = 0 or ry = 0
308
+ //
309
+ Ellipse.prototype.isDegenerate = function () {
310
+ return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx);
311
+ };
312
+
313
+ module.exports = Ellipse;
314
+
315
+ },{}],4:[function(require,module,exports){
316
+ 'use strict';
317
+
318
+ // combine 2 matrixes
319
+ // m1, m2 - [a, b, c, d, e, g]
320
+ //
321
+ function combine(m1, m2) {
322
+ return [
323
+ m1[0] * m2[0] + m1[2] * m2[1],
324
+ m1[1] * m2[0] + m1[3] * m2[1],
325
+ m1[0] * m2[2] + m1[2] * m2[3],
326
+ m1[1] * m2[2] + m1[3] * m2[3],
327
+ m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
328
+ m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
329
+ ];
330
+ }
331
+
332
+
333
+ function Matrix() {
334
+ if (!(this instanceof Matrix)) { return new Matrix(); }
335
+ this.queue = []; // list of matrixes to apply
336
+ this.cache = null; // combined matrix cache
337
+ }
338
+
339
+
340
+ Matrix.prototype.matrix = function (m) {
341
+ if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) {
342
+ return this;
343
+ }
344
+ this.cache = null;
345
+ this.queue.push(m);
346
+ return this;
347
+ };
348
+
349
+
350
+ Matrix.prototype.translate = function (tx, ty) {
351
+ if (tx !== 0 || ty !== 0) {
352
+ this.cache = null;
353
+ this.queue.push([ 1, 0, 0, 1, tx, ty ]);
354
+ }
355
+ return this;
356
+ };
357
+
358
+
359
+ Matrix.prototype.scale = function (sx, sy) {
360
+ if (sx !== 1 || sy !== 1) {
361
+ this.cache = null;
362
+ this.queue.push([ sx, 0, 0, sy, 0, 0 ]);
363
+ }
364
+ return this;
365
+ };
366
+
367
+
368
+ Matrix.prototype.rotate = function (angle, rx, ry) {
369
+ var rad, cos, sin;
370
+
371
+ if (angle !== 0) {
372
+ this.translate(rx, ry);
373
+
374
+ rad = angle * Math.PI / 180;
375
+ cos = Math.cos(rad);
376
+ sin = Math.sin(rad);
377
+
378
+ this.queue.push([ cos, sin, -sin, cos, 0, 0 ]);
379
+ this.cache = null;
380
+
381
+ this.translate(-rx, -ry);
382
+ }
383
+ return this;
384
+ };
385
+
386
+
387
+ Matrix.prototype.skewX = function (angle) {
388
+ if (angle !== 0) {
389
+ this.cache = null;
390
+ this.queue.push([ 1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0 ]);
391
+ }
392
+ return this;
393
+ };
394
+
395
+
396
+ Matrix.prototype.skewY = function (angle) {
397
+ if (angle !== 0) {
398
+ this.cache = null;
399
+ this.queue.push([ 1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0 ]);
400
+ }
401
+ return this;
402
+ };
403
+
404
+
405
+ // Flatten queue
406
+ //
407
+ Matrix.prototype.toArray = function () {
408
+ if (this.cache) {
409
+ return this.cache;
410
+ }
411
+
412
+ if (!this.queue.length) {
413
+ this.cache = [ 1, 0, 0, 1, 0, 0 ];
414
+ return this.cache;
415
+ }
416
+
417
+ this.cache = this.queue[0];
418
+
419
+ if (this.queue.length === 1) {
420
+ return this.cache;
421
+ }
422
+
423
+ for (var i = 1; i < this.queue.length; i++) {
424
+ this.cache = combine(this.cache, this.queue[i]);
425
+ }
426
+
427
+ return this.cache;
428
+ };
429
+
430
+
431
+ // Apply list of matrixes to (x,y) point.
432
+ // If `isRelative` set, `translate` component of matrix will be skipped
433
+ //
434
+ Matrix.prototype.calc = function (x, y, isRelative) {
435
+ var m;
436
+
437
+ // Don't change point on empty transforms queue
438
+ if (!this.queue.length) { return [ x, y ]; }
439
+
440
+ // Calculate final matrix, if not exists
441
+ //
442
+ // NB. if you deside to apply transforms to point one-by-one,
443
+ // they should be taken in reverse order
444
+
445
+ if (!this.cache) {
446
+ this.cache = this.toArray();
447
+ }
448
+
449
+ m = this.cache;
450
+
451
+ // Apply matrix to point
452
+ return [
453
+ x * m[0] + y * m[2] + (isRelative ? 0 : m[4]),
454
+ x * m[1] + y * m[3] + (isRelative ? 0 : m[5])
455
+ ];
456
+ };
457
+
458
+
459
+ module.exports = Matrix;
460
+
461
+ },{}],5:[function(require,module,exports){
462
+ 'use strict';
463
+
464
+
465
+ var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
466
+
467
+ var SPECIAL_SPACES = [
468
+ 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
469
+ 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
470
+ ];
471
+
472
+ function isSpace(ch) {
473
+ return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) || // Line terminators
474
+ // White spaces
475
+ (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
476
+ (ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0);
477
+ }
478
+
479
+ function isCommand(code) {
480
+ /*eslint-disable no-bitwise*/
481
+ switch (code | 0x20) {
482
+ case 0x6D/* m */:
483
+ case 0x7A/* z */:
484
+ case 0x6C/* l */:
485
+ case 0x68/* h */:
486
+ case 0x76/* v */:
487
+ case 0x63/* c */:
488
+ case 0x73/* s */:
489
+ case 0x71/* q */:
490
+ case 0x74/* t */:
491
+ case 0x61/* a */:
492
+ case 0x72/* r */:
493
+ return true;
494
+ }
495
+ return false;
496
+ }
497
+
498
+ function isDigit(code) {
499
+ return (code >= 48 && code <= 57); // 0..9
500
+ }
501
+
502
+ function isDigitStart(code) {
503
+ return (code >= 48 && code <= 57) || /* 0..9 */
504
+ code === 0x2B || /* + */
505
+ code === 0x2D || /* - */
506
+ code === 0x2E; /* . */
507
+ }
508
+
509
+
510
+ function State(path) {
511
+ this.index = 0;
512
+ this.path = path;
513
+ this.max = path.length;
514
+ this.result = [];
515
+ this.param = 0.0;
516
+ this.err = '';
517
+ this.segmentStart = 0;
518
+ this.data = [];
519
+ }
520
+
521
+ function skipSpaces(state) {
522
+ while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) {
523
+ state.index++;
524
+ }
525
+ }
526
+
527
+
528
+ function scanParam(state) {
529
+ var start = state.index,
530
+ index = start,
531
+ max = state.max,
532
+ zeroFirst = false,
533
+ hasCeiling = false,
534
+ hasDecimal = false,
535
+ hasDot = false,
536
+ ch;
537
+
538
+ if (index >= max) {
539
+ state.err = 'SvgPath: missed param (at pos ' + index + ')';
540
+ return;
541
+ }
542
+ ch = state.path.charCodeAt(index);
543
+
544
+ if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
545
+ index++;
546
+ ch = (index < max) ? state.path.charCodeAt(index) : 0;
547
+ }
548
+
549
+ // This logic is shamelessly borrowed from Esprima
550
+ // https://github.com/ariya/esprimas
551
+ //
552
+ if (!isDigit(ch) && ch !== 0x2E/* . */) {
553
+ state.err = 'SvgPath: param should start with 0..9 or `.` (at pos ' + index + ')';
554
+ return;
555
+ }
556
+
557
+ if (ch !== 0x2E/* . */) {
558
+ zeroFirst = (ch === 0x30/* 0 */);
559
+ index++;
560
+
561
+ ch = (index < max) ? state.path.charCodeAt(index) : 0;
562
+
563
+ if (zeroFirst && index < max) {
564
+ // decimal number starts with '0' such as '09' is illegal.
565
+ if (ch && isDigit(ch)) {
566
+ state.err = 'SvgPath: numbers started with `0` such as `09` are ilegal (at pos ' + start + ')';
567
+ return;
568
+ }
569
+ }
570
+
571
+ while (index < max && isDigit(state.path.charCodeAt(index))) {
572
+ index++;
573
+ hasCeiling = true;
574
+ }
575
+ ch = (index < max) ? state.path.charCodeAt(index) : 0;
576
+ }
577
+
578
+ if (ch === 0x2E/* . */) {
579
+ hasDot = true;
580
+ index++;
581
+ while (isDigit(state.path.charCodeAt(index))) {
582
+ index++;
583
+ hasDecimal = true;
584
+ }
585
+ ch = (index < max) ? state.path.charCodeAt(index) : 0;
586
+ }
587
+
588
+ if (ch === 0x65/* e */ || ch === 0x45/* E */) {
589
+ if (hasDot && !hasCeiling && !hasDecimal) {
590
+ state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
591
+ return;
592
+ }
593
+
594
+ index++;
595
+
596
+ ch = (index < max) ? state.path.charCodeAt(index) : 0;
597
+ if (ch === 0x2B/* + */ || ch === 0x2D/* - */) {
598
+ index++;
599
+ }
600
+ if (index < max && isDigit(state.path.charCodeAt(index))) {
601
+ while (index < max && isDigit(state.path.charCodeAt(index))) {
602
+ index++;
603
+ }
604
+ } else {
605
+ state.err = 'SvgPath: invalid float exponent (at pos ' + index + ')';
606
+ return;
607
+ }
608
+ }
609
+
610
+ state.index = index;
611
+ state.param = parseFloat(state.path.slice(start, index)) + 0.0;
612
+ }
613
+
614
+
615
+ function finalizeSegment(state) {
616
+ var cmd, cmdLC;
617
+
618
+ // Process duplicated commands (without comand name)
619
+
620
+ // This logic is shamelessly borrowed from Raphael
621
+ // https://github.com/DmitryBaranovskiy/raphael/
622
+ //
623
+ cmd = state.path[state.segmentStart];
624
+ cmdLC = cmd.toLowerCase();
625
+
626
+ var params = state.data;
627
+
628
+ if (cmdLC === 'm' && params.length > 2) {
629
+ state.result.push([ cmd, params[0], params[1] ]);
630
+ params = params.slice(2);
631
+ cmdLC = 'l';
632
+ cmd = (cmd === 'm') ? 'l' : 'L';
633
+ }
634
+
635
+ if (cmdLC === 'r') {
636
+ state.result.push([ cmd ].concat(params));
637
+ } else {
638
+
639
+ while (params.length >= paramCounts[cmdLC]) {
640
+ state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
641
+ if (!paramCounts[cmdLC]) {
642
+ break;
643
+ }
644
+ }
645
+ }
646
+ }
647
+
648
+
649
+ function scanSegment(state) {
650
+ var max = state.max,
651
+ cmdCode, comma_found, need_params, i;
652
+
653
+ state.segmentStart = state.index;
654
+ cmdCode = state.path.charCodeAt(state.index);
655
+
656
+ if (!isCommand(cmdCode)) {
657
+ state.err = 'SvgPath: bad command ' + state.path[state.index] + ' (at pos ' + state.index + ')';
658
+ return;
659
+ }
660
+
661
+ need_params = paramCounts[state.path[state.index].toLowerCase()];
662
+
663
+ state.index++;
664
+ skipSpaces(state);
665
+
666
+ state.data = [];
667
+
668
+ if (!need_params) {
669
+ // Z
670
+ finalizeSegment(state);
671
+ return;
672
+ }
673
+
674
+ comma_found = false;
675
+
676
+ for (;;) {
677
+ for (i = need_params; i > 0; i--) {
678
+ scanParam(state);
679
+ if (state.err.length) {
680
+ return;
681
+ }
682
+ state.data.push(state.param);
683
+
684
+ skipSpaces(state);
685
+ comma_found = false;
686
+
687
+ if (state.index < max && state.path.charCodeAt(state.index) === 0x2C/* , */) {
688
+ state.index++;
689
+ skipSpaces(state);
690
+ comma_found = true;
691
+ }
692
+ }
693
+
694
+ // after ',' param is mandatory
695
+ if (comma_found) {
696
+ continue;
697
+ }
698
+
699
+ if (state.index >= state.max) {
700
+ break;
701
+ }
702
+
703
+ // Stop on next segment
704
+ if (!isDigitStart(state.path.charCodeAt(state.index))) {
705
+ break;
706
+ }
707
+ }
708
+
709
+ finalizeSegment(state);
710
+ }
711
+
712
+
713
+ /* Returns array of segments:
714
+ *
715
+ * [
716
+ * [ command, coord1, coord2, ... ]
717
+ * ]
718
+ */
719
+ module.exports = function pathParse(svgPath) {
720
+ var state = new State(svgPath);
721
+ var max = state.max;
722
+
723
+ skipSpaces(state);
724
+
725
+ while (state.index < max && !state.err.length) {
726
+ scanSegment(state);
727
+ }
728
+
729
+ if (state.err.length) {
730
+ state.result = [];
731
+
732
+ } else if (state.result.length) {
733
+
734
+ if ('mM'.indexOf(state.result[0][0]) < 0) {
735
+ state.err = 'SvgPath: string should start with `M` or `m`';
736
+ state.result = [];
737
+ } else {
738
+ state.result[0][0] = 'M';
739
+ }
740
+ }
741
+
742
+ return {
743
+ err: state.err,
744
+ segments: state.result
745
+ };
746
+ };
747
+
748
+ },{}],6:[function(require,module,exports){
749
+ // SVG Path transformations library
750
+ //
751
+ // Usage:
752
+ //
753
+ // SvgPath('...')
754
+ // .translate(-150, -100)
755
+ // .scale(0.5)
756
+ // .translate(-150, -100)
757
+ // .toFixed(1)
758
+ // .toString()
759
+ //
760
+
761
+ 'use strict';
762
+
763
+
764
+ var pathParse = require('./path_parse');
765
+ var transformParse = require('./transform_parse');
766
+ var matrix = require('./matrix');
767
+ var a2c = require('./a2c');
768
+ var ellipse = require('./ellipse');
769
+
770
+
771
+ // Class constructor
772
+ //
773
+ function SvgPath(path) {
774
+ if (!(this instanceof SvgPath)) { return new SvgPath(path); }
775
+
776
+ var pstate = pathParse(path);
777
+
778
+ // Array of path segments.
779
+ // Each segment is array [command, param1, param2, ...]
780
+ this.segments = pstate.segments;
781
+
782
+ // Error message on parse error.
783
+ this.err = pstate.err;
784
+
785
+ // Transforms stack for lazy evaluation
786
+ this.__stack = [];
787
+ }
788
+
789
+
790
+ SvgPath.prototype.__matrix = function (m) {
791
+ var self = this, i;
792
+
793
+ // Quick leave for empty matrix
794
+ if (!m.queue.length) { return; }
795
+
796
+ this.iterate(function (s, index, x, y) {
797
+ var p, result, name, isRelative;
798
+
799
+ switch (s[0]) {
800
+
801
+ // Process 'assymetric' commands separately
802
+ case 'v':
803
+ p = m.calc(0, s[1], true);
804
+ result = (p[0] === 0) ? [ 'v', p[1] ] : [ 'l', p[0], p[1] ];
805
+ break;
806
+
807
+ case 'V':
808
+ p = m.calc(x, s[1], false);
809
+ result = (p[0] === m.calc(x, y, false)[0]) ? [ 'V', p[1] ] : [ 'L', p[0], p[1] ];
810
+ break;
811
+
812
+ case 'h':
813
+ p = m.calc(s[1], 0, true);
814
+ result = (p[1] === 0) ? [ 'h', p[0] ] : [ 'l', p[0], p[1] ];
815
+ break;
816
+
817
+ case 'H':
818
+ p = m.calc(s[1], y, false);
819
+ result = (p[1] === m.calc(x, y, false)[1]) ? [ 'H', p[0] ] : [ 'L', p[0], p[1] ];
820
+ break;
821
+
822
+ case 'a':
823
+ case 'A':
824
+ // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
825
+
826
+ // Drop segment if arc is empty (end point === start point)
827
+ /*if ((s[0] === 'A' && s[6] === x && s[7] === y) ||
828
+ (s[0] === 'a' && s[6] === 0 && s[7] === 0)) {
829
+ return [];
830
+ }*/
831
+
832
+ // Transform rx, ry and the x-axis-rotation
833
+ var ma = m.toArray();
834
+ var e = ellipse(s[1], s[2], s[3]).transform(ma);
835
+
836
+ // flip sweep-flag if matrix is not orientation-preserving
837
+ if (ma[0] * ma[3] - ma[1] * ma[2] < 0) {
838
+ s[5] = s[5] ? '0' : '1';
839
+ }
840
+
841
+ // Transform end point as usual (without translation for relative notation)
842
+ p = m.calc(s[6], s[7], s[0] === 'a');
843
+
844
+ // Empty arcs can be ignored by renderer, but should not be dropped
845
+ // to avoid collisions with `S A S` and so on. Replace with empty line.
846
+ if ((s[0] === 'A' && s[6] === x && s[7] === y) ||
847
+ (s[0] === 'a' && s[6] === 0 && s[7] === 0)) {
848
+ result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ];
849
+ break;
850
+ }
851
+
852
+ // if the resulting ellipse is (almost) a segment ...
853
+ if (e.isDegenerate()) {
854
+ // replace the arc by a line
855
+ result = [ s[0] === 'a' ? 'l' : 'L', p[0], p[1] ];
856
+ } else {
857
+ // if it is a real ellipse
858
+ // s[0], s[4] and s[5] are not modified
859
+ result = [ s[0], e.rx, e.ry, e.ax, s[4], s[5], p[0], p[1] ];
860
+ }
861
+
862
+ break;
863
+
864
+ case 'm':
865
+ // Edge case. The very first `m` should be processed as absolute, if happens.
866
+ // Make sense for coord shift transforms.
867
+ isRelative = index > 0;
868
+
869
+ p = m.calc(s[1], s[2], isRelative);
870
+ result = [ 'm', p[0], p[1] ];
871
+ break;
872
+
873
+ default:
874
+ name = s[0];
875
+ result = [ name ];
876
+ isRelative = (name.toLowerCase() === name);
877
+
878
+ // Apply transformations to the segment
879
+ for (i = 1; i < s.length; i += 2) {
880
+ p = m.calc(s[i], s[i + 1], isRelative);
881
+ result.push(p[0], p[1]);
882
+ }
883
+ }
884
+
885
+ self.segments[index] = result;
886
+ }, true);
887
+ };
888
+
889
+
890
+ // Apply stacked commands
891
+ //
892
+ SvgPath.prototype.__evaluateStack = function () {
893
+ var m, i;
894
+
895
+ if (!this.__stack.length) { return; }
896
+
897
+ if (this.__stack.length === 1) {
898
+ this.__matrix(this.__stack[0]);
899
+ this.__stack = [];
900
+ return;
901
+ }
902
+
903
+ m = matrix();
904
+ i = this.__stack.length;
905
+
906
+ while (--i >= 0) {
907
+ m.matrix(this.__stack[i].toArray());
908
+ }
909
+
910
+ this.__matrix(m);
911
+ this.__stack = [];
912
+ };
913
+
914
+
915
+ // Convert processed SVG Path back to string
916
+ //
917
+ SvgPath.prototype.toString = function () {
918
+ var elements = [], skipCmd, cmd;
919
+
920
+ this.__evaluateStack();
921
+
922
+ for (var i = 0; i < this.segments.length; i++) {
923
+ // remove repeating commands names
924
+ cmd = this.segments[i][0];
925
+ skipCmd = i > 0 && cmd !== 'm' && cmd !== 'M' && cmd === this.segments[i - 1][0];
926
+ elements = elements.concat(skipCmd ? this.segments[i].slice(1) : this.segments[i]);
927
+ }
928
+
929
+ return elements.join(' ')
930
+ // Optimizations: remove spaces around commands & before `-`
931
+ //
932
+ // We could also remove leading zeros for `0.5`-like values,
933
+ // but their count is too small to spend time for.
934
+ .replace(/ ?([achlmqrstvz]) ?/gi, '$1')
935
+ .replace(/ \-/g, '-')
936
+ // workaround for FontForge SVG importing bug
937
+ .replace(/zm/g, 'z m');
938
+ };
939
+
940
+
941
+ // Translate path to (x [, y])
942
+ //
943
+ SvgPath.prototype.translate = function (x, y) {
944
+ this.__stack.push(matrix().translate(x, y || 0));
945
+ return this;
946
+ };
947
+
948
+
949
+ // Scale path to (sx [, sy])
950
+ // sy = sx if not defined
951
+ //
952
+ SvgPath.prototype.scale = function (sx, sy) {
953
+ this.__stack.push(matrix().scale(sx, (!sy && (sy !== 0)) ? sx : sy));
954
+ return this;
955
+ };
956
+
957
+
958
+ // Rotate path around point (sx [, sy])
959
+ // sy = sx if not defined
960
+ //
961
+ SvgPath.prototype.rotate = function (angle, rx, ry) {
962
+ this.__stack.push(matrix().rotate(angle, rx || 0, ry || 0));
963
+ return this;
964
+ };
965
+
966
+
967
+ // Skew path along the X axis by `degrees` angle
968
+ //
969
+ SvgPath.prototype.skewX = function (degrees) {
970
+ this.__stack.push(matrix().skewX(degrees));
971
+ return this;
972
+ };
973
+
974
+
975
+ // Skew path along the Y axis by `degrees` angle
976
+ //
977
+ SvgPath.prototype.skewY = function (degrees) {
978
+ this.__stack.push(matrix().skewY(degrees));
979
+ return this;
980
+ };
981
+
982
+
983
+ // Apply matrix transform (array of 6 elements)
984
+ //
985
+ SvgPath.prototype.matrix = function (m) {
986
+ this.__stack.push(matrix().matrix(m));
987
+ return this;
988
+ };
989
+
990
+
991
+ // Transform path according to "transform" attr of SVG spec
992
+ //
993
+ SvgPath.prototype.transform = function (transformString) {
994
+ if (!transformString.trim()) {
995
+ return this;
996
+ }
997
+ this.__stack.push(transformParse(transformString));
998
+ return this;
999
+ };
1000
+
1001
+
1002
+ // Round coords with given decimal precition.
1003
+ // 0 by default (to integers)
1004
+ //
1005
+ SvgPath.prototype.round = function (d) {
1006
+ var contourStartDeltaX = 0, contourStartDeltaY = 0, deltaX = 0, deltaY = 0, l;
1007
+
1008
+ d = d || 0;
1009
+
1010
+ this.__evaluateStack();
1011
+
1012
+ this.segments.forEach(function (s) {
1013
+ var isRelative = (s[0].toLowerCase() === s[0]);
1014
+
1015
+ switch (s[0]) {
1016
+ case 'H':
1017
+ case 'h':
1018
+ if (isRelative) { s[1] += deltaX; }
1019
+ deltaX = s[1] - s[1].toFixed(d);
1020
+ s[1] = +s[1].toFixed(d);
1021
+ return;
1022
+
1023
+ case 'V':
1024
+ case 'v':
1025
+ if (isRelative) { s[1] += deltaY; }
1026
+ deltaY = s[1] - s[1].toFixed(d);
1027
+ s[1] = +s[1].toFixed(d);
1028
+ return;
1029
+
1030
+ case 'Z':
1031
+ case 'z':
1032
+ deltaX = contourStartDeltaX;
1033
+ deltaY = contourStartDeltaY;
1034
+ return;
1035
+
1036
+ case 'M':
1037
+ case 'm':
1038
+ if (isRelative) {
1039
+ s[1] += deltaX;
1040
+ s[2] += deltaY;
1041
+ }
1042
+
1043
+ deltaX = s[1] - s[1].toFixed(d);
1044
+ deltaY = s[2] - s[2].toFixed(d);
1045
+
1046
+ contourStartDeltaX = deltaX;
1047
+ contourStartDeltaY = deltaY;
1048
+
1049
+ s[1] = +s[1].toFixed(d);
1050
+ s[2] = +s[2].toFixed(d);
1051
+ return;
1052
+
1053
+ case 'A':
1054
+ case 'a':
1055
+ // [cmd, rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
1056
+ if (isRelative) {
1057
+ s[6] += deltaX;
1058
+ s[7] += deltaY;
1059
+ }
1060
+
1061
+ deltaX = s[6] - s[6].toFixed(d);
1062
+ deltaY = s[7] - s[7].toFixed(d);
1063
+
1064
+ s[1] = +s[1].toFixed(d);
1065
+ s[2] = +s[2].toFixed(d);
1066
+ s[3] = +s[3].toFixed(d + 2); // better precision for rotation
1067
+ s[6] = +s[6].toFixed(d);
1068
+ s[7] = +s[7].toFixed(d);
1069
+ return;
1070
+
1071
+ default:
1072
+ // a c l q s t
1073
+ l = s.length;
1074
+
1075
+ if (isRelative) {
1076
+ s[l - 2] += deltaX;
1077
+ s[l - 1] += deltaY;
1078
+ }
1079
+
1080
+ deltaX = s[l - 2] - s[l - 2].toFixed(d);
1081
+ deltaY = s[l - 1] - s[l - 1].toFixed(d);
1082
+
1083
+ s.forEach(function (val, i) {
1084
+ if (!i) { return; }
1085
+ s[i] = +s[i].toFixed(d);
1086
+ });
1087
+ return;
1088
+ }
1089
+ });
1090
+
1091
+ return this;
1092
+ };
1093
+
1094
+
1095
+ // Apply iterator function to all segments. If function returns result,
1096
+ // current segment will be replaced to array of returned segments.
1097
+ // If empty array is returned, current regment will be deleted.
1098
+ //
1099
+ SvgPath.prototype.iterate = function (iterator, keepLazyStack) {
1100
+ var segments = this.segments,
1101
+ replacements = {},
1102
+ needReplace = false,
1103
+ lastX = 0,
1104
+ lastY = 0,
1105
+ countourStartX = 0,
1106
+ countourStartY = 0;
1107
+ var i, j, newSegments;
1108
+
1109
+ if (!keepLazyStack) {
1110
+ this.__evaluateStack();
1111
+ }
1112
+
1113
+ segments.forEach(function (s, index) {
1114
+
1115
+ var res = iterator(s, index, lastX, lastY);
1116
+
1117
+ if (Array.isArray(res)) {
1118
+ replacements[index] = res;
1119
+ needReplace = true;
1120
+ }
1121
+
1122
+ var isRelative = (s[0] === s[0].toLowerCase());
1123
+
1124
+ // calculate absolute X and Y
1125
+ switch (s[0]) {
1126
+ case 'm':
1127
+ case 'M':
1128
+ lastX = s[1] + (isRelative ? lastX : 0);
1129
+ lastY = s[2] + (isRelative ? lastY : 0);
1130
+ countourStartX = lastX;
1131
+ countourStartY = lastY;
1132
+ return;
1133
+
1134
+ case 'h':
1135
+ case 'H':
1136
+ lastX = s[1] + (isRelative ? lastX : 0);
1137
+ return;
1138
+
1139
+ case 'v':
1140
+ case 'V':
1141
+ lastY = s[1] + (isRelative ? lastY : 0);
1142
+ return;
1143
+
1144
+ case 'z':
1145
+ case 'Z':
1146
+ // That make sence for multiple contours
1147
+ lastX = countourStartX;
1148
+ lastY = countourStartY;
1149
+ return;
1150
+
1151
+ default:
1152
+ lastX = s[s.length - 2] + (isRelative ? lastX : 0);
1153
+ lastY = s[s.length - 1] + (isRelative ? lastY : 0);
1154
+ }
1155
+ });
1156
+
1157
+ // Replace segments if iterator return results
1158
+
1159
+ if (!needReplace) { return this; }
1160
+
1161
+ newSegments = [];
1162
+
1163
+ for (i = 0; i < segments.length; i++) {
1164
+ if (typeof replacements[i] !== 'undefined') {
1165
+ for (j = 0; j < replacements[i].length; j++) {
1166
+ newSegments.push(replacements[i][j]);
1167
+ }
1168
+ } else {
1169
+ newSegments.push(segments[i]);
1170
+ }
1171
+ }
1172
+
1173
+ this.segments = newSegments;
1174
+
1175
+ return this;
1176
+ };
1177
+
1178
+
1179
+ // Converts segments from relative to absolute
1180
+ //
1181
+ SvgPath.prototype.abs = function () {
1182
+
1183
+ this.iterate(function (s, index, x, y) {
1184
+ var name = s[0],
1185
+ nameUC = name.toUpperCase(),
1186
+ i;
1187
+
1188
+ // Skip absolute commands
1189
+ if (name === nameUC) { return; }
1190
+
1191
+ s[0] = nameUC;
1192
+
1193
+ switch (name) {
1194
+ case 'v':
1195
+ // v has shifted coords parity
1196
+ s[1] += y;
1197
+ return;
1198
+
1199
+ case 'a':
1200
+ // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
1201
+ // touch x, y only
1202
+ s[6] += x;
1203
+ s[7] += y;
1204
+ return;
1205
+
1206
+ default:
1207
+ for (i = 1; i < s.length; i++) {
1208
+ s[i] += i % 2 ? x : y; // odd values are X, even - Y
1209
+ }
1210
+ }
1211
+ }, true);
1212
+
1213
+ return this;
1214
+ };
1215
+
1216
+
1217
+ // Converts segments from absolute to relative
1218
+ //
1219
+ SvgPath.prototype.rel = function () {
1220
+
1221
+ this.iterate(function (s, index, x, y) {
1222
+ var name = s[0],
1223
+ nameLC = name.toLowerCase(),
1224
+ i;
1225
+
1226
+ // Skip relative commands
1227
+ if (name === nameLC) { return; }
1228
+
1229
+ // Don't touch the first M to avoid potential confusions.
1230
+ if (index === 0 && name === 'M') { return; }
1231
+
1232
+ s[0] = nameLC;
1233
+
1234
+ switch (name) {
1235
+ case 'V':
1236
+ // V has shifted coords parity
1237
+ s[1] -= y;
1238
+ return;
1239
+
1240
+ case 'A':
1241
+ // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
1242
+ // touch x, y only
1243
+ s[6] -= x;
1244
+ s[7] -= y;
1245
+ return;
1246
+
1247
+ default:
1248
+ for (i = 1; i < s.length; i++) {
1249
+ s[i] -= i % 2 ? x : y; // odd values are X, even - Y
1250
+ }
1251
+ }
1252
+ }, true);
1253
+
1254
+ return this;
1255
+ };
1256
+
1257
+
1258
+ // Converts arcs to cubic bézier curves
1259
+ //
1260
+ SvgPath.prototype.unarc = function () {
1261
+ this.iterate(function (s, index, x, y) {
1262
+ var new_segments, nextX, nextY, result = [], name = s[0];
1263
+
1264
+ // Skip anything except arcs
1265
+ if (name !== 'A' && name !== 'a') { return null; }
1266
+
1267
+ if (name === 'a') {
1268
+ // convert relative arc coordinates to absolute
1269
+ nextX = x + s[6];
1270
+ nextY = y + s[7];
1271
+ } else {
1272
+ nextX = s[6];
1273
+ nextY = s[7];
1274
+ }
1275
+
1276
+ new_segments = a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]);
1277
+
1278
+ // Degenerated arcs can be ignored by renderer, but should not be dropped
1279
+ // to avoid collisions with `S A S` and so on. Replace with empty line.
1280
+ if (new_segments.length === 0) {
1281
+ return [ [ s[0] === 'a' ? 'l' : 'L', s[6], s[7] ] ];
1282
+ }
1283
+
1284
+ new_segments.forEach(function (s) {
1285
+ result.push([ 'C', s[2], s[3], s[4], s[5], s[6], s[7] ]);
1286
+ });
1287
+
1288
+ return result;
1289
+ });
1290
+
1291
+ return this;
1292
+ };
1293
+
1294
+
1295
+ // Converts smooth curves (with missed control point) to generic curves
1296
+ //
1297
+ SvgPath.prototype.unshort = function () {
1298
+ var segments = this.segments;
1299
+ var prevControlX, prevControlY, prevSegment;
1300
+ var curControlX, curControlY;
1301
+
1302
+ // TODO: add lazy evaluation flag when relative commands supported
1303
+
1304
+ this.iterate(function (s, idx, x, y) {
1305
+ var name = s[0], nameUC = name.toUpperCase(), isRelative;
1306
+
1307
+ // First command MUST be M|m, it's safe to skip.
1308
+ // Protect from access to [-1] for sure.
1309
+ if (!idx) { return; }
1310
+
1311
+ if (nameUC === 'T') { // quadratic curve
1312
+ isRelative = (name === 't');
1313
+
1314
+ prevSegment = segments[idx - 1];
1315
+
1316
+ if (prevSegment[0] === 'Q') {
1317
+ prevControlX = prevSegment[1] - x;
1318
+ prevControlY = prevSegment[2] - y;
1319
+ } else if (prevSegment[0] === 'q') {
1320
+ prevControlX = prevSegment[1] - prevSegment[3];
1321
+ prevControlY = prevSegment[2] - prevSegment[4];
1322
+ } else {
1323
+ prevControlX = 0;
1324
+ prevControlY = 0;
1325
+ }
1326
+
1327
+ curControlX = -prevControlX;
1328
+ curControlY = -prevControlY;
1329
+
1330
+ if (!isRelative) {
1331
+ curControlX += x;
1332
+ curControlY += y;
1333
+ }
1334
+
1335
+ segments[idx] = [
1336
+ isRelative ? 'q' : 'Q',
1337
+ curControlX, curControlY,
1338
+ s[1], s[2]
1339
+ ];
1340
+
1341
+ } else if (nameUC === 'S') { // cubic curve
1342
+ isRelative = (name === 's');
1343
+
1344
+ prevSegment = segments[idx - 1];
1345
+
1346
+ if (prevSegment[0] === 'C') {
1347
+ prevControlX = prevSegment[3] - x;
1348
+ prevControlY = prevSegment[4] - y;
1349
+ } else if (prevSegment[0] === 'c') {
1350
+ prevControlX = prevSegment[3] - prevSegment[5];
1351
+ prevControlY = prevSegment[4] - prevSegment[6];
1352
+ } else {
1353
+ prevControlX = 0;
1354
+ prevControlY = 0;
1355
+ }
1356
+
1357
+ curControlX = -prevControlX;
1358
+ curControlY = -prevControlY;
1359
+
1360
+ if (!isRelative) {
1361
+ curControlX += x;
1362
+ curControlY += y;
1363
+ }
1364
+
1365
+ segments[idx] = [
1366
+ isRelative ? 'c' : 'C',
1367
+ curControlX, curControlY,
1368
+ s[1], s[2], s[3], s[4]
1369
+ ];
1370
+ }
1371
+ });
1372
+
1373
+ return this;
1374
+ };
1375
+
1376
+
1377
+ module.exports = SvgPath;
1378
+
1379
+ },{"./a2c":2,"./ellipse":3,"./matrix":4,"./path_parse":5,"./transform_parse":7}],7:[function(require,module,exports){
1380
+ 'use strict';
1381
+
1382
+
1383
+ var Matrix = require('./matrix');
1384
+
1385
+ var operations = {
1386
+ matrix: true,
1387
+ scale: true,
1388
+ rotate: true,
1389
+ translate: true,
1390
+ skewX: true,
1391
+ skewY: true
1392
+ };
1393
+
1394
+ var CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
1395
+ var PARAMS_SPLIT_RE = /[\s,]+/;
1396
+
1397
+
1398
+ module.exports = function transformParse(transformString) {
1399
+ var matrix = new Matrix();
1400
+ var cmd, params;
1401
+
1402
+ // Split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
1403
+ transformString.split(CMD_SPLIT_RE).forEach(function (item) {
1404
+
1405
+ // Skip empty elements
1406
+ if (!item.length) { return; }
1407
+
1408
+ // remember operation
1409
+ if (typeof operations[item] !== 'undefined') {
1410
+ cmd = item;
1411
+ return;
1412
+ }
1413
+
1414
+ // extract params & att operation to matrix
1415
+ params = item.split(PARAMS_SPLIT_RE).map(function (i) {
1416
+ return +i || 0;
1417
+ });
1418
+
1419
+ // If params count is not correct - ignore command
1420
+ switch (cmd) {
1421
+ case 'matrix':
1422
+ if (params.length === 6) {
1423
+ matrix.matrix(params);
1424
+ }
1425
+ return;
1426
+
1427
+ case 'scale':
1428
+ if (params.length === 1) {
1429
+ matrix.scale(params[0], params[0]);
1430
+ } else if (params.length === 2) {
1431
+ matrix.scale(params[0], params[1]);
1432
+ }
1433
+ return;
1434
+
1435
+ case 'rotate':
1436
+ if (params.length === 1) {
1437
+ matrix.rotate(params[0], 0, 0);
1438
+ } else if (params.length === 3) {
1439
+ matrix.rotate(params[0], params[1], params[2]);
1440
+ }
1441
+ return;
1442
+
1443
+ case 'translate':
1444
+ if (params.length === 1) {
1445
+ matrix.translate(params[0], 0);
1446
+ } else if (params.length === 2) {
1447
+ matrix.translate(params[0], params[1]);
1448
+ }
1449
+ return;
1450
+
1451
+ case 'skewX':
1452
+ if (params.length === 1) {
1453
+ matrix.skewX(params[0]);
1454
+ }
1455
+ return;
1456
+
1457
+ case 'skewY':
1458
+ if (params.length === 1) {
1459
+ matrix.skewY(params[0]);
1460
+ }
1461
+ return;
1462
+ }
1463
+ });
1464
+
1465
+ return matrix;
1466
+ };
1467
+
1468
+ },{"./matrix":4}],8:[function(require,module,exports){
1469
+ /**
1470
+ * A class to parse color values
1471
+ * @author Stoyan Stefanov <sstoo@gmail.com>
1472
+ * @link http://www.phpied.com/rgb-color-parser-in-javascript/
1473
+ * @license Use it if you like it
1474
+ */
1475
+ (function (global) {
1476
+ function RGBColor(color_string)
1477
+ {
1478
+ this.ok = false;
1479
+
1480
+ // strip any leading #
1481
+ if (color_string.charAt(0) == '#') { // remove # if any
1482
+ color_string = color_string.substr(1,6);
1483
+ }
1484
+
1485
+ color_string = color_string.replace(/ /g,'');
1486
+ color_string = color_string.toLowerCase();
1487
+
1488
+ // before getting into regexps, try simple matches
1489
+ // and overwrite the input
1490
+ var simple_colors = {
1491
+ aliceblue: 'f0f8ff',
1492
+ antiquewhite: 'faebd7',
1493
+ aqua: '00ffff',
1494
+ aquamarine: '7fffd4',
1495
+ azure: 'f0ffff',
1496
+ beige: 'f5f5dc',
1497
+ bisque: 'ffe4c4',
1498
+ black: '000000',
1499
+ blanchedalmond: 'ffebcd',
1500
+ blue: '0000ff',
1501
+ blueviolet: '8a2be2',
1502
+ brown: 'a52a2a',
1503
+ burlywood: 'deb887',
1504
+ cadetblue: '5f9ea0',
1505
+ chartreuse: '7fff00',
1506
+ chocolate: 'd2691e',
1507
+ coral: 'ff7f50',
1508
+ cornflowerblue: '6495ed',
1509
+ cornsilk: 'fff8dc',
1510
+ crimson: 'dc143c',
1511
+ cyan: '00ffff',
1512
+ darkblue: '00008b',
1513
+ darkcyan: '008b8b',
1514
+ darkgoldenrod: 'b8860b',
1515
+ darkgray: 'a9a9a9',
1516
+ darkgreen: '006400',
1517
+ darkkhaki: 'bdb76b',
1518
+ darkmagenta: '8b008b',
1519
+ darkolivegreen: '556b2f',
1520
+ darkorange: 'ff8c00',
1521
+ darkorchid: '9932cc',
1522
+ darkred: '8b0000',
1523
+ darksalmon: 'e9967a',
1524
+ darkseagreen: '8fbc8f',
1525
+ darkslateblue: '483d8b',
1526
+ darkslategray: '2f4f4f',
1527
+ darkturquoise: '00ced1',
1528
+ darkviolet: '9400d3',
1529
+ deeppink: 'ff1493',
1530
+ deepskyblue: '00bfff',
1531
+ dimgray: '696969',
1532
+ dodgerblue: '1e90ff',
1533
+ feldspar: 'd19275',
1534
+ firebrick: 'b22222',
1535
+ floralwhite: 'fffaf0',
1536
+ forestgreen: '228b22',
1537
+ fuchsia: 'ff00ff',
1538
+ gainsboro: 'dcdcdc',
1539
+ ghostwhite: 'f8f8ff',
1540
+ gold: 'ffd700',
1541
+ goldenrod: 'daa520',
1542
+ gray: '808080',
1543
+ green: '008000',
1544
+ greenyellow: 'adff2f',
1545
+ honeydew: 'f0fff0',
1546
+ hotpink: 'ff69b4',
1547
+ indianred : 'cd5c5c',
1548
+ indigo : '4b0082',
1549
+ ivory: 'fffff0',
1550
+ khaki: 'f0e68c',
1551
+ lavender: 'e6e6fa',
1552
+ lavenderblush: 'fff0f5',
1553
+ lawngreen: '7cfc00',
1554
+ lemonchiffon: 'fffacd',
1555
+ lightblue: 'add8e6',
1556
+ lightcoral: 'f08080',
1557
+ lightcyan: 'e0ffff',
1558
+ lightgoldenrodyellow: 'fafad2',
1559
+ lightgrey: 'd3d3d3',
1560
+ lightgreen: '90ee90',
1561
+ lightpink: 'ffb6c1',
1562
+ lightsalmon: 'ffa07a',
1563
+ lightseagreen: '20b2aa',
1564
+ lightskyblue: '87cefa',
1565
+ lightslateblue: '8470ff',
1566
+ lightslategray: '778899',
1567
+ lightsteelblue: 'b0c4de',
1568
+ lightyellow: 'ffffe0',
1569
+ lime: '00ff00',
1570
+ limegreen: '32cd32',
1571
+ linen: 'faf0e6',
1572
+ magenta: 'ff00ff',
1573
+ maroon: '800000',
1574
+ mediumaquamarine: '66cdaa',
1575
+ mediumblue: '0000cd',
1576
+ mediumorchid: 'ba55d3',
1577
+ mediumpurple: '9370d8',
1578
+ mediumseagreen: '3cb371',
1579
+ mediumslateblue: '7b68ee',
1580
+ mediumspringgreen: '00fa9a',
1581
+ mediumturquoise: '48d1cc',
1582
+ mediumvioletred: 'c71585',
1583
+ midnightblue: '191970',
1584
+ mintcream: 'f5fffa',
1585
+ mistyrose: 'ffe4e1',
1586
+ moccasin: 'ffe4b5',
1587
+ navajowhite: 'ffdead',
1588
+ navy: '000080',
1589
+ oldlace: 'fdf5e6',
1590
+ olive: '808000',
1591
+ olivedrab: '6b8e23',
1592
+ orange: 'ffa500',
1593
+ orangered: 'ff4500',
1594
+ orchid: 'da70d6',
1595
+ palegoldenrod: 'eee8aa',
1596
+ palegreen: '98fb98',
1597
+ paleturquoise: 'afeeee',
1598
+ palevioletred: 'd87093',
1599
+ papayawhip: 'ffefd5',
1600
+ peachpuff: 'ffdab9',
1601
+ peru: 'cd853f',
1602
+ pink: 'ffc0cb',
1603
+ plum: 'dda0dd',
1604
+ powderblue: 'b0e0e6',
1605
+ purple: '800080',
1606
+ red: 'ff0000',
1607
+ rosybrown: 'bc8f8f',
1608
+ royalblue: '4169e1',
1609
+ saddlebrown: '8b4513',
1610
+ salmon: 'fa8072',
1611
+ sandybrown: 'f4a460',
1612
+ seagreen: '2e8b57',
1613
+ seashell: 'fff5ee',
1614
+ sienna: 'a0522d',
1615
+ silver: 'c0c0c0',
1616
+ skyblue: '87ceeb',
1617
+ slateblue: '6a5acd',
1618
+ slategray: '708090',
1619
+ snow: 'fffafa',
1620
+ springgreen: '00ff7f',
1621
+ steelblue: '4682b4',
1622
+ tan: 'd2b48c',
1623
+ teal: '008080',
1624
+ thistle: 'd8bfd8',
1625
+ tomato: 'ff6347',
1626
+ turquoise: '40e0d0',
1627
+ violet: 'ee82ee',
1628
+ violetred: 'd02090',
1629
+ wheat: 'f5deb3',
1630
+ white: 'ffffff',
1631
+ whitesmoke: 'f5f5f5',
1632
+ yellow: 'ffff00',
1633
+ yellowgreen: '9acd32'
1634
+ };
1635
+ for (var key in simple_colors) {
1636
+ if (color_string == key) {
1637
+ color_string = simple_colors[key];
1638
+ }
1639
+ }
1640
+ // emd of simple type-in colors
1641
+
1642
+ // array of color definition objects
1643
+ var color_defs = [
1644
+ {
1645
+ re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/,
1646
+ example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
1647
+ process: function (bits){
1648
+ return [
1649
+ parseInt(bits[1]),
1650
+ parseInt(bits[2]),
1651
+ parseInt(bits[3])
1652
+ ];
1653
+ }
1654
+ },
1655
+ {
1656
+ re: /^(\w{2})(\w{2})(\w{2})$/,
1657
+ example: ['#00ff00', '336699'],
1658
+ process: function (bits){
1659
+ return [
1660
+ parseInt(bits[1], 16),
1661
+ parseInt(bits[2], 16),
1662
+ parseInt(bits[3], 16)
1663
+ ];
1664
+ }
1665
+ },
1666
+ {
1667
+ re: /^(\w{1})(\w{1})(\w{1})$/,
1668
+ example: ['#fb0', 'f0f'],
1669
+ process: function (bits){
1670
+ return [
1671
+ parseInt(bits[1] + bits[1], 16),
1672
+ parseInt(bits[2] + bits[2], 16),
1673
+ parseInt(bits[3] + bits[3], 16)
1674
+ ];
1675
+ }
1676
+ }
1677
+ ];
1678
+
1679
+ // search through the definitions to find a match
1680
+ for (var i = 0; i < color_defs.length; i++) {
1681
+ var re = color_defs[i].re;
1682
+ var processor = color_defs[i].process;
1683
+ var bits = re.exec(color_string);
1684
+ if (bits) {
1685
+ var channels = processor(bits);
1686
+ this.r = channels[0];
1687
+ this.g = channels[1];
1688
+ this.b = channels[2];
1689
+ this.ok = true;
1690
+ }
1691
+
1692
+ }
1693
+
1694
+ // validate/cleanup values
1695
+ this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r);
1696
+ this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g);
1697
+ this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b);
1698
+
1699
+ // some getters
1700
+ this.toRGB = function () {
1701
+ return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')';
1702
+ }
1703
+ this.toHex = function () {
1704
+ var r = this.r.toString(16);
1705
+ var g = this.g.toString(16);
1706
+ var b = this.b.toString(16);
1707
+ if (r.length == 1) r = '0' + r;
1708
+ if (g.length == 1) g = '0' + g;
1709
+ if (b.length == 1) b = '0' + b;
1710
+ return '#' + r + g + b;
1711
+ }
1712
+
1713
+ // help
1714
+ this.getHelpXML = function () {
1715
+
1716
+ var examples = new Array();
1717
+ // add regexps
1718
+ for (var i = 0; i < color_defs.length; i++) {
1719
+ var example = color_defs[i].example;
1720
+ for (var j = 0; j < example.length; j++) {
1721
+ examples[examples.length] = example[j];
1722
+ }
1723
+ }
1724
+ // add type-in colors
1725
+ for (var sc in simple_colors) {
1726
+ examples[examples.length] = sc;
1727
+ }
1728
+
1729
+ var xml = document.createElement('ul');
1730
+ xml.setAttribute('id', 'rgbcolor-examples');
1731
+ for (var i = 0; i < examples.length; i++) {
1732
+ try {
1733
+ var list_item = document.createElement('li');
1734
+ var list_color = new RGBColor(examples[i]);
1735
+ var example_div = document.createElement('div');
1736
+ example_div.style.cssText =
1737
+ 'margin: 3px; '
1738
+ + 'border: 1px solid black; '
1739
+ + 'background:' + list_color.toHex() + '; '
1740
+ + 'color:' + list_color.toHex()
1741
+ ;
1742
+ example_div.appendChild(document.createTextNode('test'));
1743
+ var list_item_value = document.createTextNode(
1744
+ ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex()
1745
+ );
1746
+ list_item.appendChild(example_div);
1747
+ list_item.appendChild(list_item_value);
1748
+ xml.appendChild(list_item);
1749
+
1750
+ } catch(e){}
1751
+ }
1752
+ return xml;
1753
+
1754
+ }
1755
+
1756
+ }
1757
+ if (typeof define === "function" && define.amd) {
1758
+ define(function () {
1759
+ return RGBColor;
1760
+ });
1761
+ } else if (typeof module !== "undefined" && module.exports) {
1762
+ module.exports = RGBColor;
1763
+ } else {
1764
+ global.RGBColor = RGBColor;
1765
+ }
1766
+ return RGBColor;
1767
+ })(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this);
1768
+
1769
+ },{}],9:[function(require,module,exports){
1770
+ /*
1771
+ The MIT License (MIT)
1772
+
1773
+ Copyright (c) 2015-2016 yWorks GmbH
1774
+
1775
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1776
+ of this software and associated documentation files (the "Software"), to deal
1777
+ in the Software without restriction, including without limitation the rights
1778
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1779
+ copies of the Software, and to permit persons to whom the Software is
1780
+ furnished to do so, subject to the following conditions:
1781
+
1782
+ The above copyright notice and this permission notice shall be included in all
1783
+ copies or substantial portions of the Software.
1784
+
1785
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1786
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1787
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1788
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1789
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1790
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1791
+ SOFTWARE.
1792
+ */
1793
+
1794
+ /**
1795
+ * Renders an svg element to a jsPDF document.
1796
+ * For accurate results a DOM document is required (mainly used for text size measurement and image format conversion)
1797
+ * @param element {HTMLElement} The svg element, which will be cloned, so the original stays unchanged.
1798
+ * @param pdf {jsPDF} The jsPDF object.
1799
+ * @param options {object} An object that may contain render options. Currently supported are:
1800
+ * scale: The global factor by which everything is scaled.
1801
+ * xOffset, yOffset: Offsets that are added to every coordinate AFTER scaling (They are not
1802
+ * influenced by the scale attribute).
1803
+ */
1804
+ (function (global) {
1805
+ var RGBColor;
1806
+ var SvgPath;
1807
+
1808
+ var _pdf; // jsPDF pdf-document
1809
+
1810
+ var cToQ = 2 / 3; // ratio to convert quadratic bezier curves to cubic ones
1811
+
1812
+ var iriReference = /url\(#([^)]+)\)/;
1813
+
1814
+
1815
+ // pathSegList is marked deprecated in chrome, so parse the d attribute manually if necessary
1816
+ var getPathSegList = function (node) {
1817
+ var d = node.getAttribute("d");
1818
+
1819
+ // Replace arcs before path segment list is handled
1820
+ if (SvgPath) {
1821
+ d = SvgPath(d).unshort().unarc().abs().toString();
1822
+ node.setAttribute('d', d);
1823
+ }
1824
+
1825
+ var pathSegList = node.pathSegList;
1826
+
1827
+ if (pathSegList) {
1828
+ return pathSegList;
1829
+ }
1830
+
1831
+ pathSegList = [];
1832
+
1833
+ var regex = /([a-df-zA-DF-Z])([^a-df-zA-DF-Z]*)/g,
1834
+ match;
1835
+ while (match = regex.exec(d)) {
1836
+ var coords = parseFloats(match[2]);
1837
+
1838
+ var type = match[1];
1839
+ var length = "zZ".indexOf(type) >= 0 ? 0 :
1840
+ "hHvV".indexOf(type) >= 0 ? 1 :
1841
+ "mMlLtT".indexOf(type) >= 0 ? 2 :
1842
+ "sSqQ".indexOf(type) >= 0 ? 4 :
1843
+ "aA".indexOf(type) >= 0 ? 7 :
1844
+ "cC".indexOf(type) >= 0 ? 6 : -1;
1845
+
1846
+ var i = 0;
1847
+ do {
1848
+ var pathSeg = {pathSegTypeAsLetter: type};
1849
+ switch (type) {
1850
+ case "h":
1851
+ case "H":
1852
+ pathSeg.x = coords[i];
1853
+ break;
1854
+
1855
+ case "v":
1856
+ case "V":
1857
+ pathSeg.y = coords[i];
1858
+ break;
1859
+
1860
+ case "c":
1861
+ case "C":
1862
+ pathSeg.x1 = coords[i + length - 6];
1863
+ pathSeg.y1 = coords[i + length - 5];
1864
+ case "s":
1865
+ case "S":
1866
+ pathSeg.x2 = coords[i + length - 4];
1867
+ pathSeg.y2 = coords[i + length - 3];
1868
+ case "t":
1869
+ case "T":
1870
+ case "l":
1871
+ case "L":
1872
+ case "m":
1873
+ case "M":
1874
+ pathSeg.x = coords[i + length - 2];
1875
+ pathSeg.y = coords[i + length - 1];
1876
+ break;
1877
+
1878
+ case "q":
1879
+ case "Q":
1880
+ pathSeg.x1 = coords[i];
1881
+ pathSeg.y1 = coords[i + 1];
1882
+ pathSeg.x = coords[i + 2];
1883
+ pathSeg.y = coords[i + 3];
1884
+ break;
1885
+ case "a":
1886
+ case "A":
1887
+ throw new Error("Cannot convert Arcs without SvgPath package");
1888
+ }
1889
+
1890
+ pathSegList.push(pathSeg);
1891
+
1892
+ // "If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit
1893
+ // lineto commands"
1894
+ if (type === "m") {
1895
+ type = "l";
1896
+ } else if (type === "M") {
1897
+ type = "L";
1898
+ }
1899
+
1900
+ i += length;
1901
+ } while(i < coords.length);
1902
+ }
1903
+
1904
+ pathSegList.getItem = function (i) {
1905
+ return this[i]
1906
+ };
1907
+ pathSegList.numberOfItems = pathSegList.length;
1908
+
1909
+ return pathSegList;
1910
+ };
1911
+
1912
+ // returns an attribute of a node, either from the node directly or from css
1913
+ var getAttribute = function (node, propertyNode, propertyCss) {
1914
+ propertyCss = propertyCss || propertyNode;
1915
+ return node.getAttribute(propertyNode) || node.style[propertyCss];
1916
+ };
1917
+
1918
+ var nodeIs = function (node, tagsString) {
1919
+ return tagsString.split(",").indexOf(node.tagName.toLowerCase()) >= 0;
1920
+ };
1921
+
1922
+ var forEachChild = function (node, fn) {
1923
+ // copy list of children, as the original might be modified
1924
+ var children = [];
1925
+ for (var i = 0; i < node.childNodes.length; i++) {
1926
+ var childNode = node.childNodes[i];
1927
+ if (childNode.nodeName.charAt(0) !== "#")
1928
+ children.push(childNode);
1929
+ }
1930
+ for (i = 0; i < children.length; i++) {
1931
+ fn(i, children[i]);
1932
+ }
1933
+ };
1934
+
1935
+ var getAngle = function (from, to) {
1936
+ return Math.atan2(to[1] - from[1], to[0] - from[0]);
1937
+ };
1938
+
1939
+ function normalize(v) {
1940
+ var length = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
1941
+ return [v[0] / length, v[1] / length];
1942
+ }
1943
+
1944
+ function getDirectionVector(from, to) {
1945
+ var v = [to[0] - from[0], to[1] - from[1]];
1946
+ return normalize(v);
1947
+ }
1948
+
1949
+ function addVectors(v1, v2) {
1950
+ return [v1[0] + v2[0], v1[1] + v2[1]];
1951
+ }
1952
+
1953
+ // mirrors p1 at p2
1954
+ var mirrorPoint = function (p1, p2) {
1955
+ var dx = p2[0] - p1[0];
1956
+ var dy = p2[1] - p1[1];
1957
+
1958
+ return [p1[0] + 2 * dx, p1[1] + 2 * dy];
1959
+ };
1960
+
1961
+ // transforms a cubic bezier control point to a quadratic one: returns from + (2/3) * (to - from)
1962
+ var toCubic = function (from, to) {
1963
+ return [cToQ * (to[0] - from[0]) + from[0], cToQ * (to[1] - from[1]) + from[1]];
1964
+ };
1965
+
1966
+ // extracts a control point from a previous path segment (for t,T,s,S segments)
1967
+ var getControlPointFromPrevious = function (i, from, list, prevX, prevY) {
1968
+ var prev = list.getItem(i - 1);
1969
+ var p2;
1970
+ if (i > 0 && (prev.pathSegTypeAsLetter === "C" || prev.pathSegTypeAsLetter === "S")) {
1971
+ p2 = mirrorPoint([prev.x2, prev.y2], from);
1972
+ } else if (i > 0 && (prev.pathSegTypeAsLetter === "c" || prev.pathSegTypeAsLetter === "s")) {
1973
+ p2 = mirrorPoint([prev.x2 + prevX, prev.y2 + prevY], from);
1974
+ } else {
1975
+ p2 = [from[0], from[1]];
1976
+ }
1977
+ return p2;
1978
+ };
1979
+
1980
+ // an id prefix to handle duplicate ids
1981
+ var SvgPrefix = function (prefix) {
1982
+ this.prefix = prefix;
1983
+ this.id = 0;
1984
+ this.nextChild = function () {
1985
+ return new SvgPrefix("_" + this.id++ + "_" + this.get());
1986
+ };
1987
+ this.get = function () {
1988
+ return this.prefix;
1989
+ }
1990
+ };
1991
+
1992
+ var AttributeState = function () {
1993
+ this.fillMode = "F";
1994
+ this.strokeMode = "";
1995
+
1996
+ this.color = new RGBColor("rgb(0, 0, 0)");
1997
+ this.fill = new RGBColor("rgb(0, 0, 0)");
1998
+ this.fillOpacity = 1.0;
1999
+ // this.fillRule = "nonzero";
2000
+ this.fontFamily = "times";
2001
+ this.fontSize = 16;
2002
+ // this.fontStyle = "normal";
2003
+ // this.fontVariant = "normal";
2004
+ // this.fontWeight = "normal";
2005
+ this.opacity = 1.0;
2006
+ this.stroke = null;
2007
+ this.strokeDasharray = null;
2008
+ this.strokeDashoffset = null;
2009
+ this.strokeLinecap = "butt";
2010
+ this.strokeLinejoin = "miter";
2011
+ this.strokeMiterlimit = 4.0;
2012
+ this.strokeOpacity = 1.0;
2013
+ this.strokeWidth = 1.0;
2014
+ // this.textAlign = "start";
2015
+ this.textAnchor = "start";
2016
+ this.visibility = "visible";
2017
+ };
2018
+
2019
+ AttributeState.prototype.clone = function () {
2020
+ var clone = new AttributeState();
2021
+ Object.getOwnPropertyNames(this).forEach(function (name) {
2022
+ clone[name] = this[name];
2023
+ }, this);
2024
+ return clone;
2025
+ };
2026
+
2027
+ /**
2028
+ * @constructor
2029
+ * @property {Marker[]} markers
2030
+ */
2031
+ function MarkerList() {
2032
+ this.markers = [];
2033
+ }
2034
+
2035
+ /**
2036
+ * @param {Marker} marker
2037
+ */
2038
+ MarkerList.prototype.addMarker = function addMarker(marker) {
2039
+ this.markers.push(marker);
2040
+ };
2041
+
2042
+ MarkerList.prototype.draw = function (tfMatrix, attributeState) {
2043
+ for (var i = 0; i < this.markers.length; i++) {
2044
+ var marker = this.markers[i];
2045
+
2046
+ var tf;
2047
+ var angle = marker.angle, anchor = marker.anchor;
2048
+ var cos = Math.cos(angle);
2049
+ var sin = Math.sin(angle);
2050
+ // position at and rotate around anchor
2051
+ tf = new _pdf.Matrix(cos, sin, -sin, cos, anchor[0], anchor[1]);
2052
+ // scale with stroke-width
2053
+ tf = _pdf.matrixMult(new _pdf.Matrix(attributeState.strokeWidth, 0, 0, attributeState.strokeWidth, 0, 0), tf);
2054
+
2055
+ tf = _pdf.matrixMult(tf, tfMatrix);
2056
+
2057
+ // as the marker is already scaled by the current line width we must not apply the line width twice!
2058
+ _pdf.saveGraphicsState();
2059
+ _pdf.setLineWidth(1.0);
2060
+ _pdf.doFormObject(marker.id, tf);
2061
+ _pdf.restoreGraphicsState();
2062
+ }
2063
+ };
2064
+
2065
+ /**
2066
+ * @param {string} id
2067
+ * @param {[number,number]} anchor
2068
+ * @param {number} angle
2069
+ */
2070
+ function Marker(id, anchor, angle) {
2071
+ this.id = id;
2072
+ this.anchor = anchor;
2073
+ this.angle = angle;
2074
+ }
2075
+
2076
+ // returns the node for the specified id or incrementally removes prefixes to search "higher" levels
2077
+ var getFromDefs = function (id, defs) {
2078
+ var regExp = /_\d+_/;
2079
+ while (!defs[id] && regExp.exec(id)) {
2080
+ id = id.replace(regExp, "");
2081
+ }
2082
+ return defs[id];
2083
+ };
2084
+
2085
+ // replace any newline characters by space and trim
2086
+ var removeNewlinesAndTrim = function (str) {
2087
+ return str.replace(/[\n\s\r]+/, " ").trim();
2088
+ };
2089
+
2090
+ // clones the defs object (or basically any object)
2091
+ var cloneDefs = function (defs) {
2092
+ var clone = {};
2093
+ for (var key in defs) {
2094
+ if (defs.hasOwnProperty(key)) {
2095
+ clone[key] = defs[key];
2096
+ }
2097
+ }
2098
+ return clone;
2099
+ };
2100
+
2101
+ function computeViewBoxTransform(node, bounds, eX, eY, eWidth, eHeight) {
2102
+ var vbX = bounds[0];
2103
+ var vbY = bounds[1];
2104
+ var vbWidth = bounds[2];
2105
+ var vbHeight = bounds[3];
2106
+
2107
+ var scaleX = eWidth / vbWidth;
2108
+ var scaleY = eHeight / vbHeight;
2109
+
2110
+ var align, meetOrSlice;
2111
+ var preserveAspectRatio = node.getAttribute("preserveAspectRatio");
2112
+ if (preserveAspectRatio) {
2113
+ var alignAndMeetOrSlice = preserveAspectRatio.split(" ");
2114
+ align = alignAndMeetOrSlice[0];
2115
+ meetOrSlice = alignAndMeetOrSlice[1] || "meet";
2116
+ } else {
2117
+ align = "xMidYMid";
2118
+ meetOrSlice = "meet"
2119
+ }
2120
+
2121
+ if (align !== "none") {
2122
+ if (meetOrSlice === "meet") {
2123
+ // uniform scaling with min scale
2124
+ scaleX = scaleY = Math.min(scaleX, scaleY);
2125
+ } else if (meetOrSlice === "slice") {
2126
+ // uniform scaling with max scale
2127
+ scaleX = scaleY = Math.max(scaleX, scaleY);
2128
+ }
2129
+ }
2130
+
2131
+ var translateX = eX - (vbX * scaleX);
2132
+ var translateY = eY - (vbY * scaleY);
2133
+
2134
+ if (align.indexOf("xMid") >= 0) {
2135
+ translateX += (eWidth - vbWidth * scaleX) / 2;
2136
+ } else if (align.indexOf("xMax") >= 0) {
2137
+ translateX += eWidth - vbWidth * scaleX;
2138
+ }
2139
+
2140
+ if (align.indexOf("yMid") >= 0) {
2141
+ translateY += (eHeight - vbHeight * scaleY) / 2;
2142
+ } else if (align.indexOf("yMax") >= 0) {
2143
+ translateY += (eHeight - vbHeight * scaleY);
2144
+ }
2145
+
2146
+ var translate = new _pdf.Matrix(1, 0, 0, 1, translateX, translateY);
2147
+ var scale = new _pdf.Matrix(scaleX, 0, 0, scaleY, 0, 0);
2148
+
2149
+ return _pdf.matrixMult(scale, translate);
2150
+ }
2151
+
2152
+ // computes the transform directly applied at the node (such as viewbox scaling and the "transform" atrribute)
2153
+ // x,y,cx,cy,r,... are omitted
2154
+ var computeNodeTransform = function (node) {
2155
+ var viewBox, x, y;
2156
+ var nodeTransform = _pdf.unitMatrix;
2157
+ if (nodeIs(node, "svg,g")) {
2158
+ x = parseFloat(node.getAttribute("x")) || 0;
2159
+ y = parseFloat(node.getAttribute("y")) || 0;
2160
+
2161
+ viewBox = node.getAttribute("viewBox");
2162
+ if (viewBox) {
2163
+ var width = parseFloat(node.getAttribute("width"));
2164
+ var height = parseFloat(node.getAttribute("height"));
2165
+ nodeTransform = computeViewBoxTransform(node, parseFloats(viewBox), x, y, width, height)
2166
+ } else {
2167
+ nodeTransform = new _pdf.Matrix(1, 0, 0, 1, x, y);
2168
+ }
2169
+ } else if (nodeIs(node, "marker")) {
2170
+ x = parseFloat(node.getAttribute("refX")) || 0;
2171
+ y = parseFloat(node.getAttribute("refY")) || 0;
2172
+
2173
+ viewBox = node.getAttribute("viewBox");
2174
+ if (viewBox) {
2175
+ var bounds = parseFloats(viewBox);
2176
+ bounds[0] = bounds[1] = 0; // for some reason vbX anc vbY seem to be ignored for markers
2177
+ nodeTransform = computeViewBoxTransform(node, bounds, 0, 0, node.getAttribute("markerWidth"), node.getAttribute("markerHeight"));
2178
+ nodeTransform = _pdf.matrixMult(new _pdf.Matrix(1, 0, 0, 1, -x, -y), nodeTransform);
2179
+ } else {
2180
+ nodeTransform = new _pdf.Matrix(1, 0, 0, 1, -x, -y);
2181
+ }
2182
+ }
2183
+
2184
+ var transformString = node.getAttribute("transform");
2185
+ if (!transformString)
2186
+ return nodeTransform;
2187
+ else
2188
+ return _pdf.matrixMult(nodeTransform, parseTransform(transformString));
2189
+ };
2190
+
2191
+ // parses the "points" string used by polygons and returns an array of points
2192
+ var parsePointsString = function (string) {
2193
+ var floats = parseFloats(string);
2194
+ var result = [];
2195
+ for (var i = 0; i < floats.length - 1; i += 2) {
2196
+ var x = floats[i];
2197
+ var y = floats[i + 1];
2198
+ result.push([x, y]);
2199
+ }
2200
+ return result;
2201
+ };
2202
+
2203
+ // parses the "transform" string
2204
+ var parseTransform = function (transformString) {
2205
+ if (!transformString)
2206
+ return _pdf.unitMatrix;
2207
+
2208
+ var mRegex = /^\s*matrix\(([^\)]+)\)\s*/,
2209
+ tRegex = /^\s*translate\(([^\)]+)\)\s*/,
2210
+ rRegex = /^\s*rotate\(([^\)]+)\)\s*/,
2211
+ sRegex = /^\s*scale\(([^\)]+)\)\s*/,
2212
+ sXRegex = /^\s*skewX\(([^\)]+)\)\s*/,
2213
+ sYRegex = /^\s*skewY\(([^\)]+)\)\s*/;
2214
+
2215
+ var resultMatrix = _pdf.unitMatrix, m;
2216
+
2217
+ while (transformString.length > 0) {
2218
+ var match = mRegex.exec(transformString);
2219
+ if (match) {
2220
+ m = parseFloats(match[1]);
2221
+ resultMatrix = _pdf.matrixMult(new _pdf.Matrix(m[0], m[1], m[2], m[3], m[4], m[5]), resultMatrix);
2222
+ transformString = transformString.substr(match[0].length);
2223
+ }
2224
+ match = rRegex.exec(transformString);
2225
+ if (match) {
2226
+ m = parseFloats(match[1]);
2227
+ var a = Math.PI * m[0] / 180;
2228
+ resultMatrix = _pdf.matrixMult(new _pdf.Matrix(Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0), resultMatrix);
2229
+ if (m[1] && m[2]) {
2230
+ var t1 = new _pdf.Matrix(1, 0, 0, 1, m[1], m[2]);
2231
+ var t2 = new _pdf.Matrix(1, 0, 0, 1, -m[1], -m[2]);
2232
+ resultMatrix = _pdf.matrixMult(t2, _pdf.matrixMult(resultMatrix, t1));
2233
+ }
2234
+ transformString = transformString.substr(match[0].length);
2235
+ }
2236
+ match = tRegex.exec(transformString);
2237
+ if (match) {
2238
+ m = parseFloats(match[1]);
2239
+ resultMatrix = _pdf.matrixMult(new _pdf.Matrix(1, 0, 0, 1, m[0], m[1] || 0), resultMatrix);
2240
+ transformString = transformString.substr(match[0].length);
2241
+ }
2242
+ match = sRegex.exec(transformString);
2243
+ if (match) {
2244
+ m = parseFloats(match[1]);
2245
+ if (!m[1])
2246
+ m[1] = m[0];
2247
+ resultMatrix = _pdf.matrixMult(new _pdf.Matrix(m[0], 0, 0, m[1], 0, 0), resultMatrix);
2248
+ transformString = transformString.substr(match[0].length);
2249
+ }
2250
+ match = sXRegex.exec(transformString);
2251
+ if (match) {
2252
+ m = parseFloat(match[1]);
2253
+ resultMatrix = _pdf.matrixMult(new _pdf.Matrix(1, 0, Math.tan(m), 1, 0, 0), resultMatrix);
2254
+ transformString = transformString.substr(match[0].length);
2255
+ }
2256
+ match = sYRegex.exec(transformString);
2257
+ if (match) {
2258
+ m = parseFloat(match[1]);
2259
+ resultMatrix = _pdf.matrixMult(new _pdf.Matrix(1, Math.tan(m), 0, 1, 0, 0), resultMatrix);
2260
+ transformString = transformString.substr(match[0].length);
2261
+ }
2262
+ }
2263
+ return resultMatrix;
2264
+ };
2265
+
2266
+ // parses a comma, sign and/or whitespace separated string of floats and returns the single floats in an array
2267
+ var parseFloats = function (str) {
2268
+ var floats = [], match,
2269
+ regex = /[+-]?(?:(?:\d+\.?\d*)|(?:\d*\.?\d+))(?:[eE][+-]?\d+)?/g;
2270
+ while(match = regex.exec(str)) {
2271
+ floats.push(parseFloat(match[0]));
2272
+ }
2273
+ return floats;
2274
+ };
2275
+
2276
+ // extends RGBColor by rgba colors as RGBColor is not capable of it
2277
+ var parseColor = function (colorString) {
2278
+ var match = /\s*rgba\(((?:[^,\)]*,){3}[^,\)]*)\)\s*/.exec(colorString);
2279
+ if (match) {
2280
+ var floats = parseFloats(match[1]);
2281
+ var color = new RGBColor("rgb(" + floats.slice(0,3).join(",") + ")");
2282
+ color.a = floats[3];
2283
+ return color;
2284
+ } else {
2285
+ return new RGBColor(colorString);
2286
+ }
2287
+ };
2288
+
2289
+ // multiplies a vector with a matrix: vec' = vec * matrix
2290
+ var multVecMatrix = function (vec, matrix) {
2291
+ var x = vec[0];
2292
+ var y = vec[1];
2293
+ return [
2294
+ matrix.a * x + matrix.c * y + matrix.e,
2295
+ matrix.b * x + matrix.d * y + matrix.f
2296
+ ];
2297
+ };
2298
+
2299
+ // returns the untransformed bounding box [x, y, width, height] of an svg element (quite expensive for path and polygon objects, as
2300
+ // the whole points/d-string has to be processed)
2301
+ var getUntransformedBBox = function (node) {
2302
+ if (getAttribute(node, "display") === "none") {
2303
+ return [0, 0, 0, 0];
2304
+ }
2305
+
2306
+ var i, minX, minY, maxX, maxY, viewBox, vb, boundingBox;
2307
+ var pf = parseFloat;
2308
+
2309
+ if (nodeIs(node, "polygon")) {
2310
+ var points = parsePointsString(node.getAttribute("points"));
2311
+ minX = Number.POSITIVE_INFINITY;
2312
+ minY = Number.POSITIVE_INFINITY;
2313
+ maxX = Number.NEGATIVE_INFINITY;
2314
+ maxY = Number.NEGATIVE_INFINITY;
2315
+ for (i = 0; i < points.length; i++) {
2316
+ var point = points[i];
2317
+ minX = Math.min(minX, point[0]);
2318
+ maxX = Math.max(maxX, point[0]);
2319
+ minY = Math.min(minY, point[1]);
2320
+ maxY = Math.max(maxY, point[1]);
2321
+ }
2322
+ boundingBox = [
2323
+ minX,
2324
+ minY,
2325
+ maxX - minX,
2326
+ maxY - minY
2327
+ ];
2328
+ } else if (nodeIs(node, "path")) {
2329
+ var list = getPathSegList(node);
2330
+ minX = Number.POSITIVE_INFINITY;
2331
+ minY = Number.POSITIVE_INFINITY;
2332
+ maxX = Number.NEGATIVE_INFINITY;
2333
+ maxY = Number.NEGATIVE_INFINITY;
2334
+ var x = 0, y = 0;
2335
+ var prevX, prevY, newX, newY;
2336
+ var p2, p3, to;
2337
+ for (i = 0; i < list.numberOfItems; i++) {
2338
+ var seg = list.getItem(i);
2339
+ var cmd = seg.pathSegTypeAsLetter;
2340
+ switch (cmd) {
2341
+ case "H":
2342
+ newX = seg.x;
2343
+ newY = y;
2344
+ break;
2345
+ case "h":
2346
+ newX = seg.x + x;
2347
+ newY = y;
2348
+ break;
2349
+ case "V":
2350
+ newX = x;
2351
+ newY = seg.y;
2352
+ break;
2353
+ case "v":
2354
+ newX = x;
2355
+ newY = seg.y + y;
2356
+ break;
2357
+ case "C":
2358
+ p2 = [seg.x1, seg.y1];
2359
+ p3 = [seg.x2, seg.y2];
2360
+ to = [seg.x, seg.y];
2361
+ break;
2362
+ case "c":
2363
+ p2 = [seg.x1 + x, seg.y1 + y];
2364
+ p3 = [seg.x2 + x, seg.y2 + y];
2365
+ to = [seg.x + x, seg.y + y];
2366
+ break;
2367
+ case "S":
2368
+ p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2369
+ p3 = [seg.x2, seg.y2];
2370
+ to = [seg.x, seg.y];
2371
+ break;
2372
+ case "s":
2373
+ p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2374
+ p3 = [seg.x2 + x, seg.y2 + y];
2375
+ to = [seg.x + x, seg.y + y];
2376
+ break;
2377
+ case "Q":
2378
+ pf = [seg.x1, seg.y1];
2379
+ p2 = toCubic([x, y], pf);
2380
+ p3 = toCubic([seg.x, seg.y], pf);
2381
+ to = [seg.x, seg.y];
2382
+ break;
2383
+ case "q":
2384
+ pf = [seg.x1 + x, seg.y1 + y];
2385
+ p2 = toCubic([x, y], pf);
2386
+ p3 = toCubic([x + seg.x, y + seg.y], pf);
2387
+ to = [seg.x + x, seg.y + y];
2388
+ break;
2389
+ case "T":
2390
+ p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2391
+ p2 = toCubic([x, y], pf);
2392
+ p3 = toCubic([seg.x, seg.y], pf);
2393
+ to = [seg.x, seg.y];
2394
+ break;
2395
+ case "t":
2396
+ pf = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2397
+ p2 = toCubic([x, y], pf);
2398
+ p3 = toCubic([x + seg.x, y + seg.y], pf);
2399
+ to = [seg.x + x, seg.y + y];
2400
+ break;
2401
+ // TODO: A,a
2402
+ }
2403
+ if ("sScCqQtT".indexOf(cmd) >= 0) {
2404
+ prevX = x;
2405
+ prevY = y;
2406
+ }
2407
+ if ("MLCSQT".indexOf(cmd) >= 0) {
2408
+ x = seg.x;
2409
+ y = seg.y;
2410
+ } else if ("mlcsqt".indexOf(cmd) >= 0) {
2411
+ x = seg.x + x;
2412
+ y = seg.y + y;
2413
+ } else if ("zZ".indexOf(cmd) < 0) {
2414
+ x = newX;
2415
+ y = newY;
2416
+ }
2417
+ if ("CSQTcsqt".indexOf(cmd) >= 0) {
2418
+ minX = Math.min(minX, x, p2[0], p3[0], to[0]);
2419
+ maxX = Math.max(maxX, x, p2[0], p3[0], to[0]);
2420
+ minY = Math.min(minY, y, p2[1], p3[1], to[1]);
2421
+ maxY = Math.max(maxY, y, p2[1], p3[1], to[1]);
2422
+ } else {
2423
+ minX = Math.min(minX, x);
2424
+ maxX = Math.max(maxX, x);
2425
+ minY = Math.min(minY, y);
2426
+ maxY = Math.max(maxY, y);
2427
+ }
2428
+ }
2429
+ boundingBox = [
2430
+ minX,
2431
+ minY,
2432
+ maxX - minX,
2433
+ maxY - minY
2434
+ ];
2435
+ } else if (nodeIs(node, "svg")) {
2436
+ viewBox = node.getAttribute("viewBox");
2437
+ if (viewBox) {
2438
+ vb = parseFloats(viewBox);
2439
+ }
2440
+ return [
2441
+ pf(node.getAttribute("x")) || (vb && vb[0]) || 0,
2442
+ pf(node.getAttribute("y")) || (vb && vb[1]) || 0,
2443
+ pf(node.getAttribute("width")) || (vb && vb[2]) || 0,
2444
+ pf(node.getAttribute("height")) || (vb && vb[3]) || 0
2445
+ ];
2446
+ } else if (nodeIs(node, "g")) {
2447
+ boundingBox = [0, 0, 0, 0];
2448
+ forEachChild(node, function (i, node) {
2449
+ var nodeBox = getUntransformedBBox(node);
2450
+ boundingBox = [
2451
+ Math.min(boundingBox[0], nodeBox[0]),
2452
+ Math.min(boundingBox[1], nodeBox[1]),
2453
+ Math.max(boundingBox[0] + boundingBox[2], nodeBox[0] + nodeBox[2]) - Math.min(boundingBox[0], nodeBox[0]),
2454
+ Math.max(boundingBox[1] + boundingBox[3], nodeBox[1] + nodeBox[3]) - Math.min(boundingBox[1], nodeBox[1])
2455
+ ];
2456
+ });
2457
+ } else if (nodeIs(node, "marker")) {
2458
+ viewBox = node.getAttribute("viewBox");
2459
+ if (viewBox) {
2460
+ vb = parseFloats(viewBox);
2461
+ }
2462
+ return [
2463
+ (vb && vb[0]) || 0,
2464
+ (vb && vb[1]) || 0,
2465
+ (vb && vb[2]) || pf(node.getAttribute("marker-width")) || 0,
2466
+ (vb && vb[3]) || pf(node.getAttribute("marker-height")) || 0
2467
+ ];
2468
+ } else if (nodeIs(node, "pattern")) {
2469
+ return [
2470
+ pf(node.getAttribute("x")) || 0,
2471
+ pf(node.getAttribute("y")) || 0,
2472
+ pf(node.getAttribute("width")) || 0,
2473
+ pf(node.getAttribute("height")) || 0
2474
+ ]
2475
+ } else {
2476
+ // TODO: check if there are other possible coordinate attributes
2477
+ var x1 = pf(node.getAttribute("x1")) || pf(node.getAttribute("x")) || pf((node.getAttribute("cx")) - pf(node.getAttribute("r"))) || 0;
2478
+ var x2 = pf(node.getAttribute("x2")) || (x1 + pf(node.getAttribute("width"))) || (pf(node.getAttribute("cx")) + pf(node.getAttribute("r"))) || 0;
2479
+ var y1 = pf(node.getAttribute("y1")) || pf(node.getAttribute("y")) || (pf(node.getAttribute("cy")) - pf(node.getAttribute("r"))) || 0;
2480
+ var y2 = pf(node.getAttribute("y2")) || (y1 + pf(node.getAttribute("height"))) || (pf(node.getAttribute("cy")) + pf(node.getAttribute("r"))) || 0;
2481
+ boundingBox = [
2482
+ Math.min(x1, x2),
2483
+ Math.min(y1, y2),
2484
+ Math.max(x1, x2) - Math.min(x1, x2),
2485
+ Math.max(y1, y2) - Math.min(y1, y2)
2486
+ ];
2487
+ }
2488
+
2489
+ if (!nodeIs(node, "marker,svg,g")) {
2490
+ // add line-width
2491
+ var lineWidth = getAttribute(node, "stroke-width") || 1;
2492
+ var miterLimit = getAttribute(node, "stroke-miterlimit");
2493
+ // miterLength / lineWidth = 1 / sin(phi / 2)
2494
+ miterLimit && (lineWidth *= 0.5 / (Math.sin(Math.PI / 12)));
2495
+ return [
2496
+ boundingBox[0] - lineWidth,
2497
+ boundingBox[1] - lineWidth,
2498
+ boundingBox[2] + 2 * lineWidth,
2499
+ boundingBox[3] + 2 * lineWidth
2500
+ ];
2501
+ }
2502
+
2503
+ return boundingBox;
2504
+ };
2505
+
2506
+ // transforms a bounding box and returns a new rect that contains it
2507
+ var transformBBox = function (box, matrix) {
2508
+ var bl = multVecMatrix([box[0], box[1]], matrix);
2509
+ var br = multVecMatrix([box[0] + box[2], box[1]], matrix);
2510
+ var tl = multVecMatrix([box[0], box[1] + box[3]], matrix);
2511
+ var tr = multVecMatrix([box[0] + box[2], box[1] + box[3]], matrix);
2512
+
2513
+ var bottom = Math.min(bl[1], br[1], tl[1], tr[1]);
2514
+ var left = Math.min(bl[0], br[0], tl[0], tr[0]);
2515
+ var top = Math.max(bl[1], br[1], tl[1], tr[1]);
2516
+ var right = Math.max(bl[0], br[0], tl[0], tr[0]);
2517
+
2518
+ return [
2519
+ left,
2520
+ bottom,
2521
+ right - left,
2522
+ top - bottom
2523
+ ]
2524
+ };
2525
+
2526
+ // draws a polygon
2527
+ var polygon = function (node, tfMatrix, colorMode, gradient, gradientMatrix, svgIdPrefix, attributeState) {
2528
+ var points = parsePointsString(node.getAttribute("points"));
2529
+ var lines = [{op: "m", c: multVecMatrix(points[0], tfMatrix)}];
2530
+ var i, angle;
2531
+ for (i = 1; i < points.length; i++) {
2532
+ var p = points[i];
2533
+ var to = multVecMatrix(p, tfMatrix);
2534
+ lines.push({op: "l", c: to});
2535
+ }
2536
+ lines.push({op: "h"});
2537
+ _pdf.path(lines, colorMode, gradient, gradientMatrix);
2538
+
2539
+ var markerEnd = node.getAttribute("marker-end"),
2540
+ markerStart = node.getAttribute("marker-start"),
2541
+ markerMid = node.getAttribute("marker-mid");
2542
+
2543
+ if (markerStart || markerMid || markerEnd) {
2544
+ var length = lines.length;
2545
+ var markers = new MarkerList();
2546
+ if (markerStart) {
2547
+ markerStart = svgIdPrefix.get() + iriReference.exec(markerStart)[1];
2548
+ angle = addVectors(getDirectionVector(lines[0].c, lines[1].c), getDirectionVector(lines[length - 2].c, lines[0].c));
2549
+ markers.addMarker(new Marker(markerStart, lines[0].c, Math.atan2(angle[1], angle[0])));
2550
+ }
2551
+
2552
+ if (markerMid) {
2553
+ markerMid = svgIdPrefix.get() + iriReference.exec(markerMid)[1];
2554
+ var prevAngle = getDirectionVector(lines[0].c, lines[1].c), curAngle;
2555
+ for (i = 1; i < lines.length - 2; i++) {
2556
+ curAngle = getDirectionVector(lines[i].c, lines[i + 1].c);
2557
+ angle = addVectors(prevAngle, curAngle);
2558
+ markers.addMarker(new Marker(markerMid, lines[i].c, Math.atan2(angle[1], angle[0])));
2559
+ prevAngle = curAngle;
2560
+ }
2561
+
2562
+ curAngle = getDirectionVector(lines[length - 2].c, lines[0].c);
2563
+ angle = addVectors(prevAngle, curAngle);
2564
+ markers.addMarker(new Marker(markerMid, lines[length - 2].c, Math.atan2(angle[1], angle[0])));
2565
+ }
2566
+
2567
+ if (markerEnd) {
2568
+ markerEnd = svgIdPrefix.get() + iriReference.exec(markerEnd)[1];
2569
+ angle = addVectors(getDirectionVector(lines[0].c, lines[1].c), getDirectionVector(lines[length - 2].c, lines[0].c));
2570
+ markers.addMarker(new Marker(markerEnd, lines[0].c, Math.atan2(angle[1], angle[0])));
2571
+ }
2572
+
2573
+ markers.draw(_pdf.unitMatrix, attributeState);
2574
+ }
2575
+ };
2576
+
2577
+ // draws an image (converts it to jpeg first, as jsPDF doesn't support png or other formats)
2578
+ var image = function (node) {
2579
+ // convert image to jpeg
2580
+ var imageUrl = node.getAttribute("xlink:href") || node.getAttribute("href");
2581
+ var image = new Image();
2582
+ image.src = imageUrl;
2583
+
2584
+ var canvas = document.createElement("canvas");
2585
+ var width = parseFloat(node.getAttribute("width")),
2586
+ height = parseFloat(node.getAttribute("height")),
2587
+ x = parseFloat(node.getAttribute("x") || 0),
2588
+ y = parseFloat(node.getAttribute("y") || 0);
2589
+ canvas.width = width;
2590
+ canvas.height = height;
2591
+ var context = canvas.getContext("2d");
2592
+ context.fillStyle = "#fff";
2593
+ context.fillRect(0, 0, width, height);
2594
+ context.drawImage(image, 0, 0, width, height);
2595
+
2596
+ try {
2597
+ var jpegUrl = canvas.toDataURL("image/jpeg");
2598
+
2599
+ _pdf.addImage(jpegUrl,
2600
+ "jpeg",
2601
+ x,
2602
+ y,
2603
+ width,
2604
+ height
2605
+ );
2606
+ } catch (e) {
2607
+ (typeof console === "object"
2608
+ && console.warn
2609
+ && console.warn('svg2pdfjs: Images with external resource link are not supported! ("' + imageUrl + '")'));
2610
+ }
2611
+ };
2612
+
2613
+ // draws a path
2614
+ var path = function (node, tfMatrix, svgIdPrefix, colorMode, gradient, gradientMatrix, attributeState) {
2615
+ var list = getPathSegList(node);
2616
+ var markerEnd = node.getAttribute("marker-end"),
2617
+ markerStart = node.getAttribute("marker-start"),
2618
+ markerMid = node.getAttribute("marker-mid");
2619
+
2620
+ markerEnd && (markerEnd = svgIdPrefix.get() + iriReference.exec(markerEnd)[1]);
2621
+ markerStart && (markerStart = svgIdPrefix.get() + iriReference.exec(markerStart)[1]);
2622
+ markerMid && (markerMid = svgIdPrefix.get() + iriReference.exec(markerMid)[1]);
2623
+
2624
+ var getLinesFromPath = function (pathSegList, tfMatrix) {
2625
+ var x = 0, y = 0;
2626
+ var x0 = x, y0 = y;
2627
+ var prevX, prevY, newX, newY;
2628
+ var to, p, p2, p3;
2629
+ var lines = [];
2630
+ var markers = new MarkerList();
2631
+ var op;
2632
+ var prevAngle = [0, 0], curAngle;
2633
+
2634
+ for (var i = 0; i < list.numberOfItems; i++) {
2635
+ var seg = list.getItem(i);
2636
+ var cmd = seg.pathSegTypeAsLetter;
2637
+ switch (cmd) {
2638
+ case "M":
2639
+ x0 = x;
2640
+ y0 = y;
2641
+ to = [seg.x, seg.y];
2642
+ op = "m";
2643
+ break;
2644
+ case "m":
2645
+ x0 = x;
2646
+ y0 = y;
2647
+ to = [seg.x + x, seg.y + y];
2648
+ op = "m";
2649
+ break;
2650
+ case "L":
2651
+ to = [seg.x, seg.y];
2652
+ op = "l";
2653
+ break;
2654
+ case "l":
2655
+ to = [seg.x + x, seg.y + y];
2656
+ op = "l";
2657
+ break;
2658
+ case "H":
2659
+ to = [seg.x, y];
2660
+ op = "l";
2661
+ newX = seg.x;
2662
+ newY = y;
2663
+ break;
2664
+ case "h":
2665
+ to = [seg.x + x, y];
2666
+ op = "l";
2667
+ newX = seg.x + x;
2668
+ newY = y;
2669
+ break;
2670
+ case "V":
2671
+ to = [x, seg.y];
2672
+ op = "l";
2673
+ newX = x;
2674
+ newY = seg.y;
2675
+ break;
2676
+ case "v":
2677
+ to = [x, seg.y + y];
2678
+ op = "l";
2679
+ newX = x;
2680
+ newY = seg.y + y;
2681
+ break;
2682
+ case "C":
2683
+ p2 = [seg.x1, seg.y1];
2684
+ p3 = [seg.x2, seg.y2];
2685
+ to = [seg.x, seg.y];
2686
+ break;
2687
+ case "c":
2688
+ p2 = [seg.x1 + x, seg.y1 + y];
2689
+ p3 = [seg.x2 + x, seg.y2 + y];
2690
+ to = [seg.x + x, seg.y + y];
2691
+ break;
2692
+ case "S":
2693
+ p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2694
+ p3 = [seg.x2, seg.y2];
2695
+ to = [seg.x, seg.y];
2696
+ break;
2697
+ case "s":
2698
+ p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2699
+ p3 = [seg.x2 + x, seg.y2 + y];
2700
+ to = [seg.x + x, seg.y + y];
2701
+ break;
2702
+ case "Q":
2703
+ p = [seg.x1, seg.y1];
2704
+ p2 = toCubic([x, y], p);
2705
+ p3 = toCubic([seg.x, seg.y], p);
2706
+ to = [seg.x, seg.y];
2707
+ break;
2708
+ case "q":
2709
+ p = [seg.x1 + x, seg.y1 + y];
2710
+ p2 = toCubic([x, y], p);
2711
+ p3 = toCubic([x + seg.x, y + seg.y], p);
2712
+ to = [seg.x + x, seg.y + y];
2713
+ break;
2714
+ case "T":
2715
+ p2 = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2716
+ p2 = toCubic([x, y], p);
2717
+ p3 = toCubic([seg.x, seg.y], p);
2718
+ to = [seg.x, seg.y];
2719
+ break;
2720
+ case "t":
2721
+ p = getControlPointFromPrevious(i, [x, y], list, prevX, prevY);
2722
+ p2 = toCubic([x, y], p);
2723
+ p3 = toCubic([x + seg.x, y + seg.y], p);
2724
+ to = [seg.x + x, seg.y + y];
2725
+ break;
2726
+ // TODO: A,a
2727
+ case "Z":
2728
+ case "z":
2729
+ x = x0;
2730
+ y = y0;
2731
+ lines.push({op: "h"});
2732
+ break;
2733
+ }
2734
+
2735
+ var hasStartMarker = markerStart
2736
+ && (i === 1
2737
+ || ("mM".indexOf(cmd) < 0 && "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0));
2738
+ var hasEndMarker = markerEnd
2739
+ && (i === list.numberOfItems - 1
2740
+ || ("mM".indexOf(cmd) < 0 && "mM".indexOf(list.getItem(i + 1).pathSegTypeAsLetter) >= 0));
2741
+ var hasMidMarker = markerMid
2742
+ && i > 0
2743
+ && !(i === 1 && "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0);
2744
+
2745
+ if ("sScCqQtT".indexOf(cmd) >= 0) {
2746
+ hasStartMarker && markers.addMarker(new Marker(markerStart, [x, y], getAngle([x, y], p2)));
2747
+ hasEndMarker && markers.addMarker(new Marker(markerEnd, to, getAngle(p3, to)));
2748
+ if (hasMidMarker) {
2749
+ curAngle = getDirectionVector([x, y], p2);
2750
+ curAngle = "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0 ?
2751
+ curAngle : normalize(addVectors(prevAngle, curAngle));
2752
+ markers.addMarker(new Marker(markerMid, [x, y], Math.atan2(curAngle[1], curAngle[0])));
2753
+ }
2754
+
2755
+ prevAngle = getDirectionVector(p3, to);
2756
+
2757
+ prevX = x;
2758
+ prevY = y;
2759
+ p2 = multVecMatrix(p2, tfMatrix);
2760
+ p3 = multVecMatrix(p3, tfMatrix);
2761
+ p = multVecMatrix(to, tfMatrix);
2762
+ lines.push({
2763
+ op: "c", c: [
2764
+ p2[0], p2[1],
2765
+ p3[0], p3[1],
2766
+ p[0], p[1]
2767
+ ]
2768
+ });
2769
+ } else if ("lLhHvVmM".indexOf(cmd) >= 0) {
2770
+ curAngle = getDirectionVector([x, y], to);
2771
+ hasStartMarker && markers.addMarker(new Marker(markerStart, [x, y], Math.atan2(curAngle[1], curAngle[0])));
2772
+ hasEndMarker && markers.addMarker(new Marker(markerEnd, to, Math.atan2(curAngle[1], curAngle[0])));
2773
+ if (hasMidMarker) {
2774
+ var angle = "mM".indexOf(cmd) >= 0 ?
2775
+ prevAngle : "mM".indexOf(list.getItem(i - 1).pathSegTypeAsLetter) >= 0 ?
2776
+ curAngle : normalize(addVectors(prevAngle, curAngle));
2777
+ markers.addMarker(new Marker(markerMid, [x, y], Math.atan2(angle[1], angle[0])));
2778
+ }
2779
+ prevAngle = curAngle;
2780
+
2781
+ p = multVecMatrix(to, tfMatrix);
2782
+ lines.push({op: op, c: p});
2783
+ }
2784
+
2785
+ if ("MLCSQT".indexOf(cmd) >= 0) {
2786
+ x = seg.x;
2787
+ y = seg.y;
2788
+ } else if ("mlcsqt".indexOf(cmd) >= 0) {
2789
+ x = seg.x + x;
2790
+ y = seg.y + y;
2791
+ } else if ("zZ".indexOf(cmd) < 0) {
2792
+ x = newX;
2793
+ y = newY;
2794
+ }
2795
+ }
2796
+
2797
+ return {lines: lines, markers: markers};
2798
+ };
2799
+ var lines = getLinesFromPath(list, tfMatrix);
2800
+
2801
+ if (lines.lines.length > 0) {
2802
+ _pdf.path(lines.lines, colorMode, gradient, gradientMatrix);
2803
+ }
2804
+
2805
+ if (markerEnd || markerStart || markerMid) {
2806
+ lines.markers.draw(tfMatrix, attributeState);
2807
+ }
2808
+ };
2809
+
2810
+ // draws the element referenced by a use node, makes use of pdf's XObjects/FormObjects so nodes are only written once
2811
+ // to the pdf document. This highly reduces the file size and computation time.
2812
+ var use = function (node, tfMatrix, svgIdPrefix) {
2813
+ var url = (node.getAttribute("href") || node.getAttribute("xlink:href"));
2814
+ // just in case someone has the idea to use empty use-tags, wtf???
2815
+ if (!url)
2816
+ return;
2817
+
2818
+ // get the size of the referenced form object (to apply the correct scaling)
2819
+ var formObject = _pdf.getFormObject(svgIdPrefix.get() + url.substring(1));
2820
+
2821
+ // scale and position it right
2822
+ var x = node.getAttribute("x") || 0;
2823
+ var y = node.getAttribute("y") || 0;
2824
+ var width = node.getAttribute("width") || formObject.width;
2825
+ var height = node.getAttribute("height") || formObject.height;
2826
+ var t = new _pdf.Matrix(width / formObject.width || 0, 0, 0, height / formObject.height || 0, x, y);
2827
+ t = _pdf.matrixMult(t, tfMatrix);
2828
+ _pdf.doFormObject(svgIdPrefix.get() + url.substring(1), t);
2829
+ };
2830
+
2831
+ // draws a line
2832
+ var line = function (node, tfMatrix, svgIdPrefix, attributeState) {
2833
+ var p1 = multVecMatrix([parseFloat(node.getAttribute('x1')), parseFloat(node.getAttribute('y1'))], tfMatrix);
2834
+ var p2 = multVecMatrix([parseFloat(node.getAttribute('x2')), parseFloat(node.getAttribute('y2'))], tfMatrix);
2835
+
2836
+ if (attributeState.strokeMode === "D"){
2837
+ _pdf.line(p1[0], p1[1], p2[0], p2[1]);
2838
+ }
2839
+
2840
+ var markerStart = node.getAttribute("marker-start"),
2841
+ markerEnd = node.getAttribute("marker-end");
2842
+
2843
+ if (markerStart || markerEnd) {
2844
+ var markers = new MarkerList();
2845
+ var angle = getAngle(p1, p2);
2846
+ if (markerStart) {
2847
+ markers.addMarker(new Marker(svgIdPrefix.get() + iriReference.exec(markerStart)[1], p1, angle));
2848
+ }
2849
+ if (markerEnd) {
2850
+ markers.addMarker(new Marker(svgIdPrefix.get() + iriReference.exec(markerEnd)[1], p2, angle));
2851
+ }
2852
+ markers.draw(_pdf.unitMatrix, attributeState);
2853
+ }
2854
+ };
2855
+
2856
+ // draws a rect
2857
+ var rect = function (node, colorMode, gradient, gradientMatrix) {
2858
+ _pdf.roundedRect(
2859
+ parseFloat(node.getAttribute('x')) || 0,
2860
+ parseFloat(node.getAttribute('y')) || 0,
2861
+ parseFloat(node.getAttribute('width')),
2862
+ parseFloat(node.getAttribute('height')),
2863
+ parseFloat(node.getAttribute('rx')) || 0,
2864
+ parseFloat(node.getAttribute('ry')) || 0,
2865
+ colorMode,
2866
+ gradient,
2867
+ gradientMatrix
2868
+ );
2869
+ };
2870
+
2871
+ // draws an ellipse
2872
+ var ellipse = function (node, colorMode, gradient, gradientMatrix) {
2873
+ _pdf.ellipse(
2874
+ parseFloat(node.getAttribute('cx')) || 0,
2875
+ parseFloat(node.getAttribute('cy')) || 0,
2876
+ parseFloat(node.getAttribute('rx')),
2877
+ parseFloat(node.getAttribute('ry')),
2878
+ colorMode,
2879
+ gradient,
2880
+ gradientMatrix
2881
+ );
2882
+ };
2883
+
2884
+ // draws a circle
2885
+ var circle = function (node, colorMode, gradient, gradientMatrix) {
2886
+ var radius = parseFloat(node.getAttribute('r')) || 0;
2887
+ _pdf.ellipse(
2888
+ parseFloat(node.getAttribute('cx')) || 0,
2889
+ parseFloat(node.getAttribute('cy')) || 0,
2890
+ radius,
2891
+ radius,
2892
+ colorMode,
2893
+ gradient,
2894
+ gradientMatrix
2895
+ );
2896
+ };
2897
+
2898
+ // applies text transformations to a text node
2899
+ var transformText = function (node, text) {
2900
+ var textTransform = getAttribute(node, "text-transform");
2901
+ switch (textTransform) {
2902
+ case "uppercase": return text.toUpperCase();
2903
+ case "lowercase": return text.toLowerCase();
2904
+ default: return text;
2905
+ // TODO: capitalize, full-width
2906
+ }
2907
+ };
2908
+
2909
+ // draws a text element and its tspan children
2910
+ var text = function (node, tfMatrix, hasFillColor, fillRGB, attributeState) {
2911
+ _pdf.saveGraphicsState();
2912
+ setTextProperties(node, fillRGB);
2913
+
2914
+ var getTextOffset = function (textAnchor, width) {
2915
+ var xOffset = 0;
2916
+ switch (textAnchor) {
2917
+ case 'end':
2918
+ xOffset = width;
2919
+ break;
2920
+ case 'middle':
2921
+ xOffset = width / 2;
2922
+ break;
2923
+ case 'start':
2924
+ break;
2925
+ }
2926
+ return xOffset;
2927
+ };
2928
+
2929
+ /**
2930
+ * Convert em, px and bare number attributes to pixel values
2931
+ */
2932
+ var toPixels = function (value, pdfFontSize) {
2933
+ var match;
2934
+
2935
+ // em
2936
+ match = value && value.toString().match(/^([\-0-9.]+)em$/);
2937
+ if (match) {
2938
+ return parseFloat(match[1]) * pdfFontSize;
2939
+ }
2940
+
2941
+ // pixels
2942
+ match = value && value.toString().match(/^([\-0-9.]+)(px|)$/);
2943
+ if (match) {
2944
+ return parseFloat(match[1]);
2945
+ }
2946
+ return 0;
2947
+ };
2948
+
2949
+ // creates an svg element and append the text node to properly measure the text size
2950
+ var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
2951
+ svg.appendChild(node);
2952
+ svg.setAttribute("visibility", "hidden");
2953
+ document.body.appendChild(svg);
2954
+
2955
+ var box = node.getBBox();
2956
+ var x, y, xOffset = 0;
2957
+ var textAnchor = getAttribute(node, "text-anchor");
2958
+ if (textAnchor) {
2959
+ xOffset = getTextOffset(textAnchor, box.width);
2960
+ }
2961
+
2962
+ var pdfFontSize = _pdf.getFontSize();
2963
+ var textX = toPixels(node.getAttribute('x'), pdfFontSize);
2964
+ var textY = toPixels(node.getAttribute('y'), pdfFontSize);
2965
+ var m = _pdf.matrixMult(new _pdf.Matrix(1, 0, 0, 1, textX, textY), tfMatrix);
2966
+
2967
+ x = toPixels(node.getAttribute("dx"), pdfFontSize);
2968
+ y = toPixels(node.getAttribute("dy"), pdfFontSize);
2969
+
2970
+ var visibility = getAttribute(node, "visibility") || attributeState.visibility;
2971
+ // when there are no tspans draw the text directly
2972
+ if (node.childElementCount === 0) {
2973
+ if (visibility === "visible") {
2974
+ _pdf.text(
2975
+ (x - xOffset),
2976
+ y,
2977
+ transformText(node, removeNewlinesAndTrim(node.textContent)),
2978
+ void 0,
2979
+ m
2980
+ );
2981
+ }
2982
+ } else {
2983
+ // otherwise loop over tspans and position each relative to the previous one
2984
+ forEachChild(node, function (i, tSpan) {
2985
+ if (!tSpan.textContent || nodeIs(tSpan, 'title,desc,metadata')) {
2986
+ return;
2987
+ }
2988
+ _pdf.saveGraphicsState();
2989
+ var tSpanColor = getAttribute(tSpan, "fill");
2990
+ setTextProperties(tSpan, tSpanColor && new RGBColor(tSpanColor));
2991
+ var extent = tSpan.getExtentOfChar(0);
2992
+ var tSpanVisibility = getAttribute(tSpan, "visibility") || visibility;
2993
+ if (tSpanVisibility === "visible") {
2994
+ _pdf.text(
2995
+ extent.x - textX,//x - xOffset,
2996
+ extent.y + extent.height * 0.7 - textY, // 0.7 roughly mimicks the text baseline
2997
+ transformText(node, removeNewlinesAndTrim(tSpan.textContent)),
2998
+ void 0,
2999
+ m
3000
+ );
3001
+ }
3002
+
3003
+ _pdf.restoreGraphicsState();
3004
+ });
3005
+
3006
+ }
3007
+
3008
+ document.body.removeChild(svg);
3009
+ _pdf.restoreGraphicsState();
3010
+ };
3011
+
3012
+ // As defs elements are allowed to appear after they are referenced, we search for them first
3013
+ var findAndRenderDefs = function (node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState) {
3014
+ forEachChild(node, function (i, child) {
3015
+ if (child.tagName.toLowerCase() === "defs") {
3016
+ renderNode(child, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState);
3017
+ // prevent defs from being evaluated twice // TODO: make this better
3018
+ child.parentNode.removeChild(child);
3019
+ }
3020
+ });
3021
+ };
3022
+
3023
+ // processes a svg node
3024
+ var svg = function (node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState) {
3025
+ // create a new prefix and clone the defs, as defs within the svg should not be visible outside
3026
+ var newSvgIdPrefix = svgIdPrefix.nextChild();
3027
+ var newDefs = cloneDefs(defs);
3028
+ findAndRenderDefs(node, tfMatrix, newDefs, newSvgIdPrefix, withinDefs, attributeState);
3029
+ renderChildren(node, tfMatrix, newDefs, newSvgIdPrefix, withinDefs, attributeState);
3030
+ };
3031
+
3032
+ // renders all children of a node
3033
+ var renderChildren = function (node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState) {
3034
+ forEachChild(node, function (i, node) {
3035
+ renderNode(node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState);
3036
+ });
3037
+ };
3038
+
3039
+ // adds a gradient to defs and the pdf document for later use, type is either "axial" or "radial"
3040
+ // opacity is only supported rudimentary by averaging over all stops
3041
+ // transforms are applied on use
3042
+ var putGradient = function (node, type, coords, defs, svgIdPrefix) {
3043
+ var colors = [];
3044
+ var opacitySum = 0;
3045
+ var hasOpacity = false;
3046
+ var gState;
3047
+ forEachChild(node, function (i, element) {
3048
+ // since opacity gradients are hard to realize, average the opacity over the control points
3049
+ if (element.tagName.toLowerCase() === "stop") {
3050
+ var color = new RGBColor(getAttribute(element, "stop-color"));
3051
+ colors.push({
3052
+ offset: parseFloat(element.getAttribute("offset")),
3053
+ color: [color.r, color.g, color.b]
3054
+ });
3055
+ var opacity = getAttribute(element, "stop-opacity");
3056
+ if (opacity && opacity != 1) {
3057
+ opacitySum += parseFloat(opacity);
3058
+ hasOpacity = true;
3059
+ }
3060
+ }
3061
+ });
3062
+
3063
+ if (hasOpacity) {
3064
+ gState = new _pdf.GState({opacity: opacitySum / coords.length});
3065
+ }
3066
+
3067
+ var pattern = new _pdf.ShadingPattern(type, coords, colors, gState);
3068
+ var id = svgIdPrefix.get() + node.getAttribute("id");
3069
+ _pdf.addShadingPattern(id, pattern);
3070
+ defs[id] = node;
3071
+ };
3072
+
3073
+ var pattern = function (node, defs, svgIdPrefix, attributeState) {
3074
+ var id = svgIdPrefix.get() + node.getAttribute("id");
3075
+ defs[id] = node;
3076
+
3077
+ // the transformations directly at the node are written to the pattern transformation matrix
3078
+ var bBox = getUntransformedBBox(node);
3079
+ var pattern = new _pdf.TilingPattern([bBox[0], bBox[1], bBox[0] + bBox[2], bBox[1] + bBox[3]], bBox[2], bBox[3],
3080
+ null, computeNodeTransform(node));
3081
+
3082
+ _pdf.beginTilingPattern(pattern);
3083
+ // continue without transformation
3084
+ renderChildren(node, _pdf.unitMatrix, defs, svgIdPrefix, false, attributeState);
3085
+ _pdf.endTilingPattern(id, pattern);
3086
+ };
3087
+
3088
+ function setTextProperties(node, fillRGB) {
3089
+ var fontFamily = getAttribute(node, "font-family");
3090
+ if (fontFamily) {
3091
+ _pdf.setFont(fontFamily);
3092
+ }
3093
+
3094
+ if (fillRGB && fillRGB.ok) {
3095
+ _pdf.setTextColor(fillRGB.r, fillRGB.g, fillRGB.b);
3096
+ }
3097
+
3098
+ var fontType;
3099
+ var fontWeight = getAttribute(node, "font-weight");
3100
+ if (fontWeight) {
3101
+ if (fontWeight === "bold") {
3102
+ fontType = "bold";
3103
+ }
3104
+ }
3105
+
3106
+ var fontStyle = getAttribute(node, "font-style");
3107
+ if (fontStyle) {
3108
+ if (fontStyle === "italic") {
3109
+ fontType += "italic";
3110
+ }
3111
+ }
3112
+ _pdf.setFontType(fontType);
3113
+
3114
+ var pdfFontSize = 16;
3115
+ var fontSize = getAttribute(node, "font-size");
3116
+ if (fontSize) {
3117
+ pdfFontSize = parseFloat(fontSize);
3118
+ _pdf.setFontSize(pdfFontSize);
3119
+ }
3120
+ }
3121
+
3122
+
3123
+ /**
3124
+ * Renders a svg node.
3125
+ * @param node The svg element
3126
+ * @param contextTransform The current transformation matrix
3127
+ * @param defs The defs map holding all svg nodes that can be referenced
3128
+ * @param svgIdPrefix The current id prefix
3129
+ * @param withinDefs True iff we are top-level within a defs node, so the target can be switched to an pdf form object
3130
+ * @param {AttributeState} attributeState Keeps track of parent attributes that are inherited automatically
3131
+ */
3132
+ var renderNode = function (node, contextTransform, defs, svgIdPrefix, withinDefs, attributeState) {
3133
+ attributeState = attributeState.clone();
3134
+
3135
+ if (getAttribute(node, "display") === "none") {
3136
+ return;
3137
+ }
3138
+
3139
+ var visibility = attributeState.visibility = getAttribute(node, "visibility") || attributeState.visibility;
3140
+ if (visibility === "hidden" && !nodeIs(node, "svg,g,marker,a,pattern,defs,text")) {
3141
+ return;
3142
+ }
3143
+
3144
+ var tfMatrix,
3145
+ hasFillColor = false,
3146
+ fillRGB = null,
3147
+ fillMode = "inherit",
3148
+ strokeMode = "inherit",
3149
+ fillUrl = null,
3150
+ fillData = null,
3151
+ bBox;
3152
+
3153
+ //
3154
+ // Decide about the render target and set the correct transformation
3155
+ //
3156
+
3157
+ // if we are within a defs node, start a new pdf form object and draw this node and all children on that instead
3158
+ // of the top-level page
3159
+ var targetIsFormObject = withinDefs && !nodeIs(node, "lineargradient,radialgradient,pattern");
3160
+ if (targetIsFormObject) {
3161
+
3162
+ // the transformations directly at the node are written to the pdf form object transformation matrix
3163
+ tfMatrix = computeNodeTransform(node);
3164
+ bBox = getUntransformedBBox(node);
3165
+
3166
+ _pdf.beginFormObject(bBox[0], bBox[1], bBox[2], bBox[3], tfMatrix);
3167
+
3168
+ // continue without transformation and set withinDefs to false to prevent child nodes from starting new form objects
3169
+ tfMatrix = _pdf.unitMatrix;
3170
+ withinDefs = false;
3171
+
3172
+ } else {
3173
+ tfMatrix = _pdf.matrixMult(computeNodeTransform(node), contextTransform);
3174
+ _pdf.saveGraphicsState();
3175
+ }
3176
+
3177
+ //
3178
+ // extract fill and stroke mode
3179
+ //
3180
+
3181
+ // fill mode
3182
+ if (nodeIs(node, "g,path,rect,text,ellipse,line,circle,polygon")) {
3183
+ function setDefaultColor() {
3184
+ fillRGB = new RGBColor("rgb(0, 0, 0)");
3185
+ hasFillColor = true;
3186
+ fillMode = "F";
3187
+ }
3188
+
3189
+ var fillColor = getAttribute(node, "fill");
3190
+ if (fillColor) {
3191
+ var url = iriReference.exec(fillColor);
3192
+ if (url) {
3193
+ // probably a gradient (or something unsupported)
3194
+ fillUrl = svgIdPrefix.get() + url[1];
3195
+ var fill = getFromDefs(fillUrl, defs);
3196
+ if (fill && nodeIs(fill, "lineargradient,radialgradient")) {
3197
+
3198
+ // matrix to convert between gradient space and user space
3199
+ // for "userSpaceOnUse" this is the current transformation: tfMatrix
3200
+ // for "objectBoundingBox" or default, the gradient gets scaled and transformed to the bounding box
3201
+ var gradientUnitsMatrix = tfMatrix;
3202
+ if (!fill.hasAttribute("gradientUnits")
3203
+ || fill.getAttribute("gradientUnits").toLowerCase() === "objectboundingbox") {
3204
+ bBox || (bBox = getUntransformedBBox(node));
3205
+ gradientUnitsMatrix = new _pdf.Matrix(bBox[2], 0, 0, bBox[3], bBox[0], bBox[1]);
3206
+
3207
+ var nodeTransform = computeNodeTransform(node);
3208
+ gradientUnitsMatrix = _pdf.matrixMult(gradientUnitsMatrix, nodeTransform);
3209
+ }
3210
+
3211
+ // matrix that is applied to the gradient before any other transformations
3212
+ var gradientTransform = parseTransform(fill.getAttribute("gradientTransform"));
3213
+
3214
+ fillData = _pdf.matrixMult(gradientTransform, gradientUnitsMatrix);
3215
+
3216
+ fillMode = "";
3217
+ } else if (fill && nodeIs(fill, "pattern")) {
3218
+ var fillBBox, y, width, height, x;
3219
+ fillData = {};
3220
+
3221
+ var patternUnitsMatrix = _pdf.unitMatrix;
3222
+ if (!fill.hasAttribute("patternUnits")
3223
+ || fill.getAttribute("patternUnits").toLowerCase() === "objectboundingbox") {
3224
+ bBox || (bBox = getUntransformedBBox(node));
3225
+ patternUnitsMatrix = new _pdf.Matrix(1, 0, 0, 1, bBox[0], bBox[1]);
3226
+
3227
+ // TODO: slightly inaccurate (rounding errors? line width bBoxes?)
3228
+ fillBBox = getUntransformedBBox(fill);
3229
+ x = fillBBox[0] * bBox[0];
3230
+ y = fillBBox[1] * bBox[1];
3231
+ width = fillBBox[2] * bBox[2];
3232
+ height = fillBBox[3] * bBox[3];
3233
+ fillData.boundingBox = [x, y, x + width, y + height];
3234
+ fillData.xStep = width;
3235
+ fillData.yStep = height;
3236
+ }
3237
+
3238
+ var patternContentUnitsMatrix = _pdf.unitMatrix;
3239
+ if (fill.hasAttribute("patternContentUnits")
3240
+ && fill.getAttribute("patternContentUnits").toLowerCase() === "objectboundingbox") {
3241
+ bBox || (bBox = getUntransformedBBox(node));
3242
+ patternContentUnitsMatrix = new _pdf.Matrix(bBox[2], 0, 0, bBox[3], 0, 0);
3243
+
3244
+ fillBBox = fillData.boundingBox || getUntransformedBBox(fill);
3245
+ x = fillBBox[0] / bBox[0];
3246
+ y = fillBBox[1] / bBox[1];
3247
+ width = fillBBox[2] / bBox[2];
3248
+ height = fillBBox[3] / bBox[3];
3249
+ fillData.boundingBox = [x, y, x + width, y + height];
3250
+ fillData.xStep = width;
3251
+ fillData.yStep = height;
3252
+ }
3253
+
3254
+ fillData.matrix = _pdf.matrixMult(
3255
+ _pdf.matrixMult(patternContentUnitsMatrix, patternUnitsMatrix), tfMatrix);
3256
+
3257
+ fillMode = "F";
3258
+ } else {
3259
+ // unsupported fill argument -> fill black
3260
+ fillUrl = fill = null;
3261
+ setDefaultColor();
3262
+ }
3263
+ } else {
3264
+ // plain color
3265
+ fillRGB = parseColor(fillColor);
3266
+ if (fillRGB.ok) {
3267
+ hasFillColor = true;
3268
+ fillMode = "F";
3269
+ } else {
3270
+ fillMode = "";
3271
+ }
3272
+ }
3273
+ }
3274
+
3275
+ // opacity is realized via a pdf graphics state
3276
+ var opacity = 1.0;
3277
+ var nodeOpacity = getAttribute(node, "opacity")
3278
+ || getAttribute(node, "fill-opacity")
3279
+ // this is a quickfix: pdf can't handle different opacity values for stroke and fill
3280
+ // we could split the primitive into two parts, however
3281
+ || getAttribute(node, "stroke-opacity");
3282
+ if (nodeOpacity) {
3283
+ opacity *= parseFloat(nodeOpacity);
3284
+ }
3285
+ if (fillRGB && typeof fillRGB.a === "number") {
3286
+ opacity *= fillRGB.a;
3287
+ }
3288
+ _pdf.setGState(new _pdf.GState({opacity: opacity}));
3289
+ }
3290
+
3291
+ if (nodeIs(node, "g,path,rect,ellipse,line,circle,polygon")) {
3292
+ // text has no fill color, so don't apply it until here
3293
+ if (hasFillColor) {
3294
+ attributeState.fill = fillRGB;
3295
+ _pdf.setFillColor(fillRGB.r, fillRGB.g, fillRGB.b);
3296
+ }
3297
+
3298
+ // stroke mode
3299
+ var strokeColor = getAttribute(node, "stroke");
3300
+ if (strokeColor) {
3301
+ var strokeWidth = getAttribute(node, "stroke-width");
3302
+ if (strokeWidth !== void 0 && strokeWidth !== "") {
3303
+ strokeWidth = Math.abs(parseFloat(strokeWidth));
3304
+ attributeState.strokeWidth = strokeWidth;
3305
+ _pdf.setLineWidth(strokeWidth);
3306
+ }
3307
+ var strokeRGB = new RGBColor(strokeColor);
3308
+ if (strokeRGB.ok) {
3309
+ attributeState.color = strokeRGB;
3310
+ _pdf.setDrawColor(strokeRGB.r, strokeRGB.g, strokeRGB.b);
3311
+ if (strokeWidth !== 0) {
3312
+ // pdf spec states: "A line width of 0 denotes the thinnest line that can be rendered at device resolution:
3313
+ // 1 device pixel wide". SVG, however, does not draw zero width lines.
3314
+ strokeMode = "D";
3315
+ } else {
3316
+ strokeMode = "";
3317
+ }
3318
+ }
3319
+ var lineCap = getAttribute(node, "stroke-linecap");
3320
+ if (lineCap) {
3321
+ _pdf.setLineCap(attributeState.strokeLinecap = lineCap);
3322
+ }
3323
+ var lineJoin = getAttribute(node, "stroke-linejoin");
3324
+ if (lineJoin) {
3325
+ _pdf.setLineJoin(attributeState.strokeLinejoin = lineJoin);
3326
+ }
3327
+ var dashArray = getAttribute(node, "stroke-dasharray");
3328
+ if (dashArray) {
3329
+ dashArray = parseFloats(dashArray);
3330
+ var dashOffset = parseInt(getAttribute(node, "stroke-dashoffset")) || 0;
3331
+ attributeState.strokeDasharray = dashArray;
3332
+ attributeState.strokeDashoffset = dashOffset;
3333
+ _pdf.setLineDashPattern(dashArray, dashOffset);
3334
+ }
3335
+ var miterLimit = getAttribute(node, "stroke-miterlimit");
3336
+ if (miterLimit !== void 0 && miterLimit !== "") {
3337
+ _pdf.setLineMiterLimit(attributeState.strokeMiterlimit = parseFloat(miterLimit));
3338
+ }
3339
+ }
3340
+ }
3341
+
3342
+ // inherit fill and stroke mode if not specified at this node
3343
+ fillMode = attributeState.fillMode = fillMode === "inherit" ? attributeState.fillMode : fillMode;
3344
+ strokeMode = attributeState.strokeMode = strokeMode === "inherit" ? attributeState.strokeMode : strokeMode;
3345
+
3346
+ var colorMode = fillMode + strokeMode;
3347
+
3348
+ setTextProperties(node, fillRGB);
3349
+
3350
+ // do the actual drawing
3351
+ switch (node.tagName.toLowerCase()) {
3352
+ case 'svg':
3353
+ svg(node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState);
3354
+ break;
3355
+ case 'g':
3356
+ findAndRenderDefs(node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState);
3357
+ case 'a':
3358
+ case "marker":
3359
+ renderChildren(node, tfMatrix, defs, svgIdPrefix, withinDefs, attributeState);
3360
+ break;
3361
+
3362
+ case 'defs':
3363
+ renderChildren(node, tfMatrix, defs, svgIdPrefix, true, attributeState);
3364
+ break;
3365
+
3366
+ case 'use':
3367
+ use(node, tfMatrix, svgIdPrefix);
3368
+ break;
3369
+
3370
+ case 'line':
3371
+ line(node, tfMatrix, svgIdPrefix, attributeState);
3372
+ break;
3373
+
3374
+ case 'rect':
3375
+ _pdf.setCurrentTransformationMatrix(tfMatrix);
3376
+ rect(node, colorMode, fillUrl, fillData);
3377
+ break;
3378
+
3379
+ case 'ellipse':
3380
+ _pdf.setCurrentTransformationMatrix(tfMatrix);
3381
+ ellipse(node, colorMode, fillUrl, fillData);
3382
+ break;
3383
+
3384
+ case 'circle':
3385
+ _pdf.setCurrentTransformationMatrix(tfMatrix);
3386
+ circle(node, colorMode, fillUrl, fillData);
3387
+ break;
3388
+ case 'text':
3389
+ text(node, tfMatrix, hasFillColor, fillRGB, attributeState);
3390
+ break;
3391
+
3392
+ case 'path':
3393
+ path(node, tfMatrix, svgIdPrefix, colorMode, fillUrl, fillData, attributeState);
3394
+ break;
3395
+
3396
+ case 'polygon':
3397
+ polygon(node, tfMatrix, colorMode, fillUrl, fillData, svgIdPrefix, attributeState);
3398
+ break;
3399
+
3400
+ case 'image':
3401
+ _pdf.setCurrentTransformationMatrix(tfMatrix);
3402
+ image(node);
3403
+ break;
3404
+
3405
+ case "lineargradient":
3406
+ putGradient(node, "axial", [
3407
+ node.getAttribute("x1"),
3408
+ node.getAttribute("y1"),
3409
+ node.getAttribute("x2"),
3410
+ node.getAttribute("y2")
3411
+ ], defs, svgIdPrefix);
3412
+ break;
3413
+
3414
+ case "radialgradient":
3415
+ putGradient(node, "radial", [
3416
+ node.getAttribute("fx") || node.getAttribute("cx"),
3417
+ node.getAttribute("fy") || node.getAttribute("cy"),
3418
+ 0,
3419
+ node.getAttribute("cx") || 0,
3420
+ node.getAttribute("cy") || 0,
3421
+ node.getAttribute("r") || 0
3422
+ ], defs, svgIdPrefix);
3423
+ break;
3424
+
3425
+ case "pattern":
3426
+ pattern(node, defs, svgIdPrefix, attributeState);
3427
+ break;
3428
+ }
3429
+
3430
+ // close either the formObject or the graphics context
3431
+ if (targetIsFormObject) {
3432
+ _pdf.endFormObject(svgIdPrefix.get() + node.getAttribute("id"));
3433
+ } else {
3434
+ _pdf.restoreGraphicsState();
3435
+ }
3436
+ };
3437
+
3438
+ // the actual svgToPdf function (see above)
3439
+ var svg2pdf = function (element, pdf, options) {
3440
+ _pdf = pdf;
3441
+
3442
+ var k = options.scale || 1.0,
3443
+ xOffset = options.xOffset || 0.0,
3444
+ yOffset = options.yOffset || 0.0;
3445
+
3446
+ // set offsets and scale everything by k
3447
+ _pdf.saveGraphicsState();
3448
+ _pdf.setCurrentTransformationMatrix(new _pdf.Matrix(k, 0, 0, k, xOffset, yOffset));
3449
+
3450
+ // set default values that differ from pdf defaults
3451
+ var attributeState = new AttributeState();
3452
+ _pdf.setLineWidth(attributeState.strokeWidth);
3453
+ var fill = attributeState.fill;
3454
+ _pdf.setFillColor(fill.r, fill.g, fill.b);
3455
+ _pdf.setFont(attributeState.fontFamily);
3456
+ _pdf.setFontSize(attributeState.fontSize);
3457
+
3458
+ // start rendering
3459
+ renderNode(element.cloneNode(true), _pdf.unitMatrix, {}, new SvgPrefix(""), false, attributeState);
3460
+
3461
+ _pdf.restoreGraphicsState();
3462
+
3463
+ return _pdf;
3464
+ };
3465
+
3466
+ if (typeof define === "function" && define.amd) {
3467
+ define(["./rgbcolor", "SvgPath"], function (rgbcolor, svgpath) {
3468
+ RGBColor = rgbcolor;
3469
+ SvgPath = svgpath;
3470
+ return svg2pdf;
3471
+ });
3472
+ } else if (typeof module !== "undefined" && module.exports) {
3473
+ RGBColor = require("./rgbcolor.js");
3474
+ SvgPath = require("SvgPath");
3475
+ module.exports = svg2pdf;
3476
+ } else {
3477
+ SvgPath = global.SvgPath;
3478
+ RGBColor = global.RGBColor;
3479
+ global.svg2pdf = svg2pdf;
3480
+ // for compatibility reasons
3481
+ global.svgElementToPdf = svg2pdf;
3482
+ }
3483
+ return svg2pdf;
3484
+ }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this));
3485
+
3486
+ },{"./rgbcolor.js":8,"SvgPath":1}]},{},[9])(9)
3487
+ });
3488
+ //# sourceMappingURL=svg2pdf.js.map