1 | (function() {
|
2 | var out$ = typeof exports != 'undefined' && exports || typeof define != 'undefined' && {} || this;
|
3 |
|
4 | var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [<!ENTITY nbsp " ">]>';
|
5 |
|
6 | function isElement(obj) {
|
7 | return obj instanceof HTMLElement || obj instanceof SVGElement;
|
8 | }
|
9 |
|
10 | function requireDomNode(el) {
|
11 | if (!isElement(el)) {
|
12 | throw new Error('an HTMLElement or SVGElement is required; got ' + el);
|
13 | }
|
14 | }
|
15 |
|
16 | function isExternal(url) {
|
17 | return url && url.lastIndexOf('http',0) == 0 && url.lastIndexOf(window.location.host) == -1;
|
18 | }
|
19 |
|
20 | function inlineImages(el, callback) {
|
21 | requireDomNode(el);
|
22 |
|
23 | var images = el.querySelectorAll('image'),
|
24 | left = images.length,
|
25 | checkDone = function() {
|
26 | if (left === 0) {
|
27 | callback();
|
28 | }
|
29 | };
|
30 |
|
31 | checkDone();
|
32 | for (var i = 0; i < images.length; i++) {
|
33 | (function(image) {
|
34 | var href = image.getAttributeNS("http://www.w3.org/1999/xlink", "href");
|
35 | if (href) {
|
36 | if (isExternal(href.value)) {
|
37 | console.warn("Cannot render embedded images linking to external hosts: "+href.value);
|
38 | return;
|
39 | }
|
40 | }
|
41 | var canvas = document.createElement('canvas');
|
42 | var ctx = canvas.getContext('2d');
|
43 | var img = new Image();
|
44 | img.crossOrigin="anonymous";
|
45 | href = href || image.getAttribute('href');
|
46 | if (href) {
|
47 | img.src = href;
|
48 | img.onload = function() {
|
49 | canvas.width = img.width;
|
50 | canvas.height = img.height;
|
51 | ctx.drawImage(img, 0, 0);
|
52 | image.setAttributeNS("http://www.w3.org/1999/xlink", "href", canvas.toDataURL('image/png'));
|
53 | left--;
|
54 | checkDone();
|
55 | }
|
56 | img.onerror = function() {
|
57 | console.log("Could not load "+href);
|
58 | left--;
|
59 | checkDone();
|
60 | }
|
61 | } else {
|
62 | left--;
|
63 | checkDone();
|
64 | }
|
65 | })(images[i]);
|
66 | }
|
67 | }
|
68 |
|
69 | function styles(el, options, cssLoadedCallback) {
|
70 | var selectorRemap = options.selectorRemap;
|
71 | var modifyStyle = options.modifyStyle;
|
72 | var modifyCss = options.modifyCss || function(selector, properties) {
|
73 | var selector = selectorRemap ? selectorRemap(selector) : selector;
|
74 | var cssText = modifyStyle ? modifyStyle(properties) : properties;
|
75 | return selector + " { " + cssText + " }\n";
|
76 | };
|
77 | var css = "";
|
78 |
|
79 |
|
80 | var fontsQueue = [];
|
81 | var sheets = document.styleSheets;
|
82 | for (var i = 0; i < sheets.length; i++) {
|
83 | try {
|
84 | var rules = sheets[i].cssRules;
|
85 | } catch (e) {
|
86 | console.warn("Stylesheet could not be loaded: "+sheets[i].href);
|
87 | continue;
|
88 | }
|
89 |
|
90 | if (rules != null) {
|
91 | for (var j = 0, match; j < rules.length; j++, match = null) {
|
92 | var rule = rules[j];
|
93 | if (typeof(rule.style) != "undefined") {
|
94 | var selectorText;
|
95 |
|
96 | try {
|
97 | selectorText = rule.selectorText;
|
98 | } catch(err) {
|
99 | console.warn('The following CSS rule has an invalid selector: "' + rule + '"', err);
|
100 | }
|
101 |
|
102 | try {
|
103 | if (selectorText) {
|
104 | match = el.querySelector(selectorText) || (el.parentNode && el.parentNode.querySelector(selectorText));
|
105 | }
|
106 | } catch(err) {
|
107 | console.warn('Invalid CSS selector "' + selectorText + '"', err);
|
108 | }
|
109 |
|
110 | if (match) {
|
111 | css += modifyCss(rule.selectorText, rule.style.cssText);
|
112 | } else if(rule.cssText.match(/^@font-face/)) {
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | var fontUrlRegexp = /url\(["']?(.+?)["']?\)/;
|
121 |
|
122 | var fontUrlMatch = rule.cssText.match(fontUrlRegexp);
|
123 |
|
124 | var externalFontUrl = (fontUrlMatch && fontUrlMatch[1]) || '';
|
125 | var fontUrlIsDataURI = externalFontUrl.match(/^data:/);
|
126 | if (fontUrlIsDataURI) {
|
127 |
|
128 | externalFontUrl = '';
|
129 | }
|
130 |
|
131 | if (externalFontUrl === 'about:blank') {
|
132 |
|
133 | externalFontUrl = '';
|
134 | }
|
135 |
|
136 | if (externalFontUrl) {
|
137 |
|
138 |
|
139 |
|
140 | if (externalFontUrl.startsWith('../')) {
|
141 | externalFontUrl = sheets[i].href + '/../' + externalFontUrl
|
142 | } else if (externalFontUrl.startsWith('./')) {
|
143 | externalFontUrl = sheets[i].href + '/.' + externalFontUrl
|
144 | }
|
145 |
|
146 | fontsQueue.push({
|
147 | text: rule.cssText,
|
148 |
|
149 | fontUrlRegexp: fontUrlRegexp,
|
150 | format: getFontMimeTypeFromUrl(externalFontUrl),
|
151 | url: externalFontUrl
|
152 | });
|
153 | } else {
|
154 |
|
155 | css += rule.cssText + '\n';
|
156 | }
|
157 | }
|
158 | }
|
159 | }
|
160 | }
|
161 | }
|
162 |
|
163 |
|
164 | processFontQueue(fontsQueue);
|
165 |
|
166 | function getFontMimeTypeFromUrl(fontUrl) {
|
167 | var supportedFormats = {
|
168 | 'woff2': 'font/woff2',
|
169 | 'woff': 'font/woff',
|
170 | 'otf': 'application/x-font-opentype',
|
171 | 'ttf': 'application/x-font-ttf',
|
172 | 'eot': 'application/vnd.ms-fontobject',
|
173 | 'sfnt': 'application/font-sfnt',
|
174 | 'svg': 'image/svg+xml'
|
175 | };
|
176 | var extensions = Object.keys(supportedFormats);
|
177 | for (var i = 0; i < extensions.length; ++i) {
|
178 | var extension = extensions[i];
|
179 |
|
180 | if (fontUrl.indexOf('.' + extension) > 0) {
|
181 | return supportedFormats[extension];
|
182 | }
|
183 | }
|
184 |
|
185 |
|
186 | console.error('Unknown font format for ' + fontUrl+ '; Fonts may not be working correctly');
|
187 | return 'application/octet-stream';
|
188 | }
|
189 |
|
190 | function processFontQueue(queue) {
|
191 | if (queue.length > 0) {
|
192 |
|
193 | var font = queue.pop();
|
194 | processNext(font);
|
195 | } else {
|
196 |
|
197 | cssLoadedCallback(css);
|
198 | }
|
199 |
|
200 | function processNext(font) {
|
201 |
|
202 | var oReq = new XMLHttpRequest();
|
203 | oReq.addEventListener('load', fontLoaded);
|
204 | oReq.addEventListener('error', transferFailed);
|
205 | oReq.addEventListener('abort', transferFailed);
|
206 | oReq.open('GET', font.url);
|
207 | oReq.responseType = 'arraybuffer';
|
208 | oReq.send();
|
209 |
|
210 | function fontLoaded() {
|
211 |
|
212 |
|
213 | var fontBits = oReq.response;
|
214 | var fontInBase64 = arrayBufferToBase64(fontBits);
|
215 | updateFontStyle(font, fontInBase64);
|
216 | }
|
217 |
|
218 | function transferFailed(e) {
|
219 | console.warn('Failed to load font from: ' + font.url);
|
220 | console.warn(e)
|
221 | css += font.text + '\n';
|
222 | processFontQueue(queue);
|
223 | }
|
224 |
|
225 | function updateFontStyle(font, fontInBase64) {
|
226 | var dataUrl = 'url("data:' + font.format + ';base64,' + fontInBase64 + '")';
|
227 | css += font.text.replace(font.fontUrlRegexp, dataUrl) + '\n';
|
228 |
|
229 |
|
230 | setTimeout(function() {
|
231 | processFontQueue(queue)
|
232 | }, 0);
|
233 | }
|
234 |
|
235 | }
|
236 | }
|
237 |
|
238 | function arrayBufferToBase64(buffer) {
|
239 | var binary = '';
|
240 | var bytes = new Uint8Array(buffer);
|
241 | var len = bytes.byteLength;
|
242 |
|
243 | for (var i = 0; i < len; i++) {
|
244 | binary += String.fromCharCode(bytes[i]);
|
245 | }
|
246 |
|
247 | return window.btoa(binary);
|
248 | }
|
249 | }
|
250 |
|
251 | function getDimension(el, clone, dim) {
|
252 | var v = (el.viewBox && el.viewBox.baseVal && el.viewBox.baseVal[dim]) ||
|
253 | (clone.getAttribute(dim) !== null && !clone.getAttribute(dim).match(/%$/) && parseInt(clone.getAttribute(dim))) ||
|
254 | el.getBoundingClientRect()[dim] ||
|
255 | parseInt(clone.style[dim]) ||
|
256 | parseInt(window.getComputedStyle(el).getPropertyValue(dim));
|
257 | return (typeof v === 'undefined' || v === null || isNaN(parseFloat(v))) ? 0 : v;
|
258 | }
|
259 |
|
260 | function reEncode(data) {
|
261 | data = encodeURIComponent(data);
|
262 | data = data.replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
263 | var c = String.fromCharCode('0x'+p1);
|
264 | return c === '%' ? '%25' : c;
|
265 | });
|
266 | return decodeURIComponent(data);
|
267 | }
|
268 |
|
269 | out$.prepareSvg = function(el, options, cb) {
|
270 | requireDomNode(el);
|
271 |
|
272 | options = options || {};
|
273 | options.scale = options.scale || 1;
|
274 | options.responsive = options.responsive || false;
|
275 | var xmlns = "http://www.w3.org/2000/xmlns/";
|
276 |
|
277 | inlineImages(el, function() {
|
278 | var outer = document.createElement("div");
|
279 | var clone = el.cloneNode(true);
|
280 | var width, height;
|
281 | if(el.tagName == 'svg') {
|
282 | width = options.width || getDimension(el, clone, 'width');
|
283 | height = options.height || getDimension(el, clone, 'height');
|
284 | } else if(el.getBBox) {
|
285 | var box = el.getBBox();
|
286 | width = box.x + box.width;
|
287 | height = box.y + box.height;
|
288 | clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate\(.*?\)/, ''));
|
289 |
|
290 | var svg = document.createElementNS('http://www.w3.org/2000/svg','svg')
|
291 | svg.appendChild(clone)
|
292 | clone = svg;
|
293 | } else {
|
294 | console.error('Attempted to render non-SVG element', el);
|
295 | return;
|
296 | }
|
297 |
|
298 | clone.setAttribute("version", "1.1");
|
299 | if (!clone.getAttribute('xmlns')) {
|
300 | clone.setAttributeNS(xmlns, "xmlns", "http://www.w3.org/2000/svg");
|
301 | }
|
302 | if (!clone.getAttribute('xmlns:xlink')) {
|
303 | clone.setAttributeNS(xmlns, "xmlns:xlink", "http://www.w3.org/1999/xlink");
|
304 | }
|
305 |
|
306 | if (options.responsive) {
|
307 | clone.removeAttribute('width');
|
308 | clone.removeAttribute('height');
|
309 | clone.setAttribute('preserveAspectRatio', 'xMinYMin meet');
|
310 | } else {
|
311 | clone.setAttribute("width", width * options.scale);
|
312 | clone.setAttribute("height", height * options.scale);
|
313 | }
|
314 |
|
315 | clone.setAttribute("viewBox", [
|
316 | options.left || 0,
|
317 | options.top || 0,
|
318 | width,
|
319 | height
|
320 | ].join(" "));
|
321 |
|
322 | var fos = clone.querySelectorAll('foreignObject > *');
|
323 | for (var i = 0; i < fos.length; i++) {
|
324 | if (!fos[i].getAttribute('xmlns')) {
|
325 | fos[i].setAttributeNS(xmlns, "xmlns", "http://www.w3.org/1999/xhtml");
|
326 | }
|
327 | }
|
328 |
|
329 | outer.appendChild(clone);
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 | styles(el, options, cssLoadedCallback);
|
336 |
|
337 | function cssLoadedCallback(css) {
|
338 |
|
339 | var s = document.createElement('style');
|
340 | s.setAttribute('type', 'text/css');
|
341 | s.innerHTML = "<![CDATA[\n" + css + "\n]]>";
|
342 | var defs = document.createElement('defs');
|
343 | defs.appendChild(s);
|
344 | clone.insertBefore(defs, clone.firstChild);
|
345 |
|
346 | if (cb) {
|
347 | var outHtml = outer.innerHTML;
|
348 | outHtml = outHtml.replace(/NS\d+:href/gi, 'xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href');
|
349 | cb(outHtml, width, height);
|
350 | }
|
351 | }
|
352 | });
|
353 | }
|
354 |
|
355 | out$.svgAsDataUri = function(el, options, cb) {
|
356 | out$.prepareSvg(el, options, function(svg) {
|
357 | var uri = 'data:image/svg+xml;base64,' + window.btoa(reEncode(doctype + svg));
|
358 | if (cb) {
|
359 | cb(uri);
|
360 | }
|
361 | });
|
362 | }
|
363 |
|
364 | out$.svgAsPngUri = function(el, options, cb) {
|
365 | requireDomNode(el);
|
366 |
|
367 | options = options || {};
|
368 | options.encoderType = options.encoderType || 'image/png';
|
369 | options.encoderOptions = options.encoderOptions || 0.8;
|
370 |
|
371 | var convertToPng = function(src, w, h) {
|
372 | var canvas = document.createElement('canvas');
|
373 | var context = canvas.getContext('2d');
|
374 | canvas.width = w;
|
375 | canvas.height = h;
|
376 |
|
377 | var pixelRatio = window.devicePixelRatio || 1;
|
378 |
|
379 | canvas.style.width = canvas.width+'px';
|
380 | canvas.style.height = canvas.height+'px';
|
381 | canvas.width *= pixelRatio;
|
382 | canvas.height *= pixelRatio;
|
383 |
|
384 | context.setTransform(pixelRatio,0,0,pixelRatio,0,0);
|
385 |
|
386 | if(options.canvg) {
|
387 | options.canvg(canvas, src);
|
388 | } else {
|
389 | context.drawImage(src, 0, 0);
|
390 | }
|
391 |
|
392 | if(options.backgroundColor){
|
393 | context.globalCompositeOperation = 'destination-over';
|
394 | context.fillStyle = options.backgroundColor;
|
395 | context.fillRect(0, 0, canvas.width, canvas.height);
|
396 | }
|
397 |
|
398 | var png;
|
399 | try {
|
400 | png = canvas.toDataURL(options.encoderType, options.encoderOptions);
|
401 | } catch (e) {
|
402 | if ((typeof SecurityError !== 'undefined' && e instanceof SecurityError) || e.name == "SecurityError") {
|
403 | console.error("Rendered SVG images cannot be downloaded in this browser.");
|
404 | return;
|
405 | } else {
|
406 | throw e;
|
407 | }
|
408 | }
|
409 | cb(png);
|
410 | }
|
411 |
|
412 | if(options.canvg) {
|
413 | out$.prepareSvg(el, options, convertToPng);
|
414 | } else {
|
415 | out$.svgAsDataUri(el, options, function(uri) {
|
416 | var image = new Image();
|
417 |
|
418 | image.onload = function() {
|
419 | convertToPng(image, image.width, image.height);
|
420 | }
|
421 |
|
422 | image.onerror = function() {
|
423 | console.error(
|
424 | 'There was an error loading the data URI as an image on the following SVG\n',
|
425 | window.atob(uri.slice(26)), '\n',
|
426 | "Open the following link to see browser's diagnosis\n",
|
427 | uri);
|
428 | }
|
429 |
|
430 | image.src = uri;
|
431 | });
|
432 | }
|
433 | }
|
434 |
|
435 | out$.download = function(name, uri) {
|
436 | if (navigator.msSaveOrOpenBlob) {
|
437 | navigator.msSaveOrOpenBlob(uriToBlob(uri), name);
|
438 | } else {
|
439 | var saveLink = document.createElement('a');
|
440 | var downloadSupported = 'download' in saveLink;
|
441 | if (downloadSupported) {
|
442 | saveLink.download = name;
|
443 | saveLink.style.display = 'none';
|
444 | document.body.appendChild(saveLink);
|
445 | try {
|
446 | var blob = uriToBlob(uri);
|
447 | var url = URL.createObjectURL(blob);
|
448 | saveLink.href = url;
|
449 | saveLink.onclick = function() {
|
450 | requestAnimationFrame(function() {
|
451 | URL.revokeObjectURL(url);
|
452 | })
|
453 | };
|
454 | } catch (e) {
|
455 | console.warn('This browser does not support object URLs. Falling back to string URL.');
|
456 | saveLink.href = uri;
|
457 | }
|
458 | saveLink.click();
|
459 | document.body.removeChild(saveLink);
|
460 | }
|
461 | else {
|
462 | window.open(uri, '_temp', 'menubar=no,toolbar=no,status=no');
|
463 | }
|
464 | }
|
465 | }
|
466 |
|
467 | function uriToBlob(uri) {
|
468 | var byteString = window.atob(uri.split(',')[1]);
|
469 | var mimeString = uri.split(',')[0].split(':')[1].split(';')[0]
|
470 | var buffer = new ArrayBuffer(byteString.length);
|
471 | var intArray = new Uint8Array(buffer);
|
472 | for (var i = 0; i < byteString.length; i++) {
|
473 | intArray[i] = byteString.charCodeAt(i);
|
474 | }
|
475 | return new Blob([buffer], {type: mimeString});
|
476 | }
|
477 |
|
478 | out$.saveSvg = function(el, name, options) {
|
479 | requireDomNode(el);
|
480 |
|
481 | options = options || {};
|
482 | out$.svgAsDataUri(el, options, function(uri) {
|
483 | out$.download(name, uri);
|
484 | });
|
485 | }
|
486 |
|
487 | out$.saveSvgAsPng = function(el, name, options) {
|
488 | requireDomNode(el);
|
489 |
|
490 | options = options || {};
|
491 | out$.svgAsPngUri(el, options, function(uri) {
|
492 | out$.download(name, uri);
|
493 | });
|
494 | }
|
495 |
|
496 |
|
497 | if (typeof define !== 'undefined') {
|
498 | define(function() {
|
499 | return out$;
|
500 | });
|
501 | }
|
502 |
|
503 | })();
|