1 | 'use strict';
|
2 |
|
3 | (function () {
|
4 | var out$ = typeof exports != 'undefined' && exports || typeof define != 'undefined' && {} || this || window;
|
5 | if (typeof define !== 'undefined') define('save-svg-as-png', [], function () {
|
6 | return out$;
|
7 | });
|
8 | out$.default = out$;
|
9 |
|
10 | var xmlns = 'http://www.w3.org/2000/xmlns/';
|
11 | 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 " ">]>';
|
12 | var urlRegex = /url\(["']?(.+?)["']?\)/;
|
13 | var fontFormats = {
|
14 | woff2: 'font/woff2',
|
15 | woff: 'font/woff',
|
16 | otf: 'application/x-font-opentype',
|
17 | ttf: 'application/x-font-ttf',
|
18 | eot: 'application/vnd.ms-fontobject',
|
19 | sfnt: 'application/font-sfnt',
|
20 | svg: 'image/svg+xml'
|
21 | };
|
22 |
|
23 | var isElement = function isElement(obj) {
|
24 | return obj instanceof HTMLElement || obj instanceof SVGElement;
|
25 | };
|
26 | var requireDomNode = function requireDomNode(el) {
|
27 | if (!isElement(el)) throw new Error('an HTMLElement or SVGElement is required; got ' + el);
|
28 | };
|
29 | var requireDomNodePromise = function requireDomNodePromise(el) {
|
30 | return new Promise(function (resolve, reject) {
|
31 | if (isElement(el)) resolve(el);else reject(new Error('an HTMLElement or SVGElement is required; got ' + el));
|
32 | });
|
33 | };
|
34 | var isExternal = function isExternal(url) {
|
35 | return url && url.lastIndexOf('http', 0) === 0 && url.lastIndexOf(window.location.host) === -1;
|
36 | };
|
37 |
|
38 | var getFontMimeTypeFromUrl = function getFontMimeTypeFromUrl(fontUrl) {
|
39 | var formats = Object.keys(fontFormats).filter(function (extension) {
|
40 | return fontUrl.indexOf('.' + extension) > 0;
|
41 | }).map(function (extension) {
|
42 | return fontFormats[extension];
|
43 | });
|
44 | if (formats) return formats[0];
|
45 | console.error('Unknown font format for ' + fontUrl + '. Fonts may not be working correctly.');
|
46 | return 'application/octet-stream';
|
47 | };
|
48 |
|
49 | var arrayBufferToBase64 = function arrayBufferToBase64(buffer) {
|
50 | var binary = '';
|
51 | var bytes = new Uint8Array(buffer);
|
52 | for (var i = 0; i < bytes.byteLength; i++) {
|
53 | binary += String.fromCharCode(bytes[i]);
|
54 | }return window.btoa(binary);
|
55 | };
|
56 |
|
57 | var getDimension = function getDimension(el, clone, dim) {
|
58 | var v = el.viewBox && el.viewBox.baseVal && el.viewBox.baseVal[dim] || clone.getAttribute(dim) !== null && !clone.getAttribute(dim).match(/%$/) && parseInt(clone.getAttribute(dim)) || el.getBoundingClientRect()[dim] || parseInt(clone.style[dim]) || parseInt(window.getComputedStyle(el).getPropertyValue(dim));
|
59 | return typeof v === 'undefined' || v === null || isNaN(parseFloat(v)) ? 0 : v;
|
60 | };
|
61 |
|
62 | var getDimensions = function getDimensions(el, clone, width, height) {
|
63 | if (el.tagName === 'svg') return {
|
64 | width: width || getDimension(el, clone, 'width'),
|
65 | height: height || getDimension(el, clone, 'height')
|
66 | };else if (el.getBBox) {
|
67 | var _el$getBBox = el.getBBox(),
|
68 | x = _el$getBBox.x,
|
69 | y = _el$getBBox.y,
|
70 | _width = _el$getBBox.width,
|
71 | _height = _el$getBBox.height;
|
72 |
|
73 | return {
|
74 | width: x + _width,
|
75 | height: y + _height
|
76 | };
|
77 | }
|
78 | };
|
79 |
|
80 | var reEncode = function reEncode(data) {
|
81 | return decodeURIComponent(encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
82 | var c = String.fromCharCode('0x' + p1);
|
83 | return c === '%' ? '%25' : c;
|
84 | }));
|
85 | };
|
86 |
|
87 | var uriToBlob = function uriToBlob(uri) {
|
88 | var byteString = window.atob(uri.split(',')[1]);
|
89 | var mimeString = uri.split(',')[0].split(':')[1].split(';')[0];
|
90 | var buffer = new ArrayBuffer(byteString.length);
|
91 | var intArray = new Uint8Array(buffer);
|
92 | for (var i = 0; i < byteString.length; i++) {
|
93 | intArray[i] = byteString.charCodeAt(i);
|
94 | }
|
95 | return new Blob([buffer], { type: mimeString });
|
96 | };
|
97 |
|
98 | var query = function query(el, selector) {
|
99 | if (!selector) return;
|
100 | try {
|
101 | return el.querySelector(selector) || el.parentNode && el.parentNode.querySelector(selector);
|
102 | } catch (err) {
|
103 | console.warn('Invalid CSS selector "' + selector + '"', err);
|
104 | }
|
105 | };
|
106 |
|
107 | var detectCssFont = function detectCssFont(rule, href) {
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | var match = rule.cssText.match(urlRegex);
|
113 | var url = match && match[1] || '';
|
114 | if (!url || url.match(/^data:/) || url === 'about:blank') return;
|
115 | var fullUrl = url.startsWith('../') ? href + '/../' + url : url.startsWith('./') ? href + '/.' + url : url;
|
116 | return {
|
117 | text: rule.cssText,
|
118 | format: getFontMimeTypeFromUrl(fullUrl),
|
119 | url: fullUrl
|
120 | };
|
121 | };
|
122 |
|
123 | var inlineImages = function inlineImages(el) {
|
124 | return Promise.all(Array.from(el.querySelectorAll('image')).map(function (image) {
|
125 | var href = image.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || image.getAttribute('href');
|
126 | if (!href) return Promise.resolve(null);
|
127 | if (isExternal(href)) {
|
128 | href += (href.indexOf('?') === -1 ? '?' : '&') + 't=' + new Date().valueOf();
|
129 | }
|
130 | return new Promise(function (resolve, reject) {
|
131 | var canvas = document.createElement('canvas');
|
132 | var img = new Image();
|
133 | img.crossOrigin = 'anonymous';
|
134 | img.src = href;
|
135 | img.onerror = function () {
|
136 | return reject(new Error('Could not load ' + href));
|
137 | };
|
138 | img.onload = function () {
|
139 | canvas.width = img.width;
|
140 | canvas.height = img.height;
|
141 | canvas.getContext('2d').drawImage(img, 0, 0);
|
142 | image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', canvas.toDataURL('image/png'));
|
143 | resolve(true);
|
144 | };
|
145 | });
|
146 | }));
|
147 | };
|
148 |
|
149 | var cachedFonts = {};
|
150 | var inlineFonts = function inlineFonts(fonts) {
|
151 | return Promise.all(fonts.map(function (font) {
|
152 | return new Promise(function (resolve, reject) {
|
153 | if (cachedFonts[font.url]) return resolve(cachedFonts[font.url]);
|
154 |
|
155 | var req = new XMLHttpRequest();
|
156 | req.addEventListener('load', function () {
|
157 |
|
158 |
|
159 | var fontInBase64 = arrayBufferToBase64(req.response);
|
160 | var fontUri = font.text.replace(urlRegex, 'url("data:' + font.format + ';base64,' + fontInBase64 + '")') + '\n';
|
161 | cachedFonts[font.url] = fontUri;
|
162 | resolve(fontUri);
|
163 | });
|
164 | req.addEventListener('error', function (e) {
|
165 | console.warn('Failed to load font from: ' + font.url, e);
|
166 | cachedFonts[font.url] = null;
|
167 | resolve(null);
|
168 | });
|
169 | req.addEventListener('abort', function (e) {
|
170 | console.warn('Aborted loading font from: ' + font.url, e);
|
171 | resolve(null);
|
172 | });
|
173 | req.open('GET', font.url);
|
174 | req.responseType = 'arraybuffer';
|
175 | req.send();
|
176 | });
|
177 | })).then(function (fontCss) {
|
178 | return fontCss.filter(function (x) {
|
179 | return x;
|
180 | }).join('');
|
181 | });
|
182 | };
|
183 |
|
184 | var cachedRules = null;
|
185 | var styleSheetRules = function styleSheetRules() {
|
186 | if (cachedRules) return cachedRules;
|
187 | return cachedRules = Array.from(document.styleSheets).map(function (sheet) {
|
188 | try {
|
189 | return { rules: sheet.cssRules, href: sheet.href };
|
190 | } catch (e) {
|
191 | console.warn('Stylesheet could not be loaded: ' + sheet.href, e);
|
192 | return {};
|
193 | }
|
194 | });
|
195 | };
|
196 |
|
197 | var inlineCss = function inlineCss(el, options) {
|
198 | var _ref = options || {},
|
199 | selectorRemap = _ref.selectorRemap,
|
200 | modifyStyle = _ref.modifyStyle,
|
201 | modifyCss = _ref.modifyCss,
|
202 | fonts = _ref.fonts;
|
203 |
|
204 | var generateCss = modifyCss || function (selector, properties) {
|
205 | var sel = selectorRemap ? selectorRemap(selector) : selector;
|
206 | var props = modifyStyle ? modifyStyle(properties) : properties;
|
207 | return sel + '{' + props + '}\n';
|
208 | };
|
209 | var css = [];
|
210 | var detectFonts = typeof fonts === 'undefined';
|
211 | var fontList = fonts || [];
|
212 | styleSheetRules().forEach(function (_ref2) {
|
213 | var rules = _ref2.rules,
|
214 | href = _ref2.href;
|
215 |
|
216 | if (!rules) return;
|
217 | Array.from(rules).forEach(function (rule) {
|
218 | if (typeof rule.style != 'undefined') {
|
219 | if (query(el, rule.selectorText)) css.push(generateCss(rule.selectorText, rule.style.cssText));else if (detectFonts && rule.cssText.match(/^@font-face/)) {
|
220 | var font = detectCssFont(rule, href);
|
221 | if (font) fontList.push(font);
|
222 | } else css.push(rule.cssText);
|
223 | }
|
224 | });
|
225 | });
|
226 |
|
227 | return inlineFonts(fontList).then(function (fontCss) {
|
228 | return css.join('\n') + fontCss;
|
229 | });
|
230 | };
|
231 |
|
232 | out$.prepareSvg = function (el, options, done) {
|
233 | requireDomNode(el);
|
234 |
|
235 | var _ref3 = options || {},
|
236 | _ref3$left = _ref3.left,
|
237 | left = _ref3$left === undefined ? 0 : _ref3$left,
|
238 | _ref3$top = _ref3.top,
|
239 | top = _ref3$top === undefined ? 0 : _ref3$top,
|
240 | w = _ref3.width,
|
241 | h = _ref3.height,
|
242 | _ref3$scale = _ref3.scale,
|
243 | scale = _ref3$scale === undefined ? 1 : _ref3$scale,
|
244 | _ref3$responsive = _ref3.responsive,
|
245 | responsive = _ref3$responsive === undefined ? false : _ref3$responsive;
|
246 |
|
247 | return inlineImages(el).then(function () {
|
248 | var clone = el.cloneNode(true);
|
249 | clone.style.backgroundColor = (options || {}).backgroundColor || el.style.backgroundColor;
|
250 |
|
251 | var _getDimensions = getDimensions(el, clone, w, h),
|
252 | width = _getDimensions.width,
|
253 | height = _getDimensions.height;
|
254 |
|
255 | if (el.tagName !== 'svg') {
|
256 | if (el.getBBox) {
|
257 | clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate\(.*?\)/, ''));
|
258 | var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
259 | svg.appendChild(clone);
|
260 | clone = svg;
|
261 | } else {
|
262 | console.error('Attempted to render non-SVG element', el);
|
263 | return;
|
264 | }
|
265 | }
|
266 |
|
267 | clone.setAttribute('version', '1.1');
|
268 | clone.setAttribute('viewBox', [left, top, width, height].join(' '));
|
269 | if (!clone.getAttribute('xmlns')) clone.setAttributeNS(xmlns, 'xmlns', 'http://www.w3.org/2000/svg');
|
270 | if (!clone.getAttribute('xmlns:xlink')) clone.setAttributeNS(xmlns, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
271 |
|
272 | if (responsive) {
|
273 | clone.removeAttribute('width');
|
274 | clone.removeAttribute('height');
|
275 | clone.setAttribute('preserveAspectRatio', 'xMinYMin meet');
|
276 | } else {
|
277 | clone.setAttribute('width', width * scale);
|
278 | clone.setAttribute('height', height * scale);
|
279 | }
|
280 |
|
281 | Array.from(clone.querySelectorAll('foreignObject > *')).forEach(function (foreignObject) {
|
282 | if (!foreignObject.getAttribute('xmlns')) foreignObject.setAttributeNS(xmlns, 'xmlns', 'http://www.w3.org/1999/xhtml');
|
283 | });
|
284 |
|
285 | return inlineCss(el, options).then(function (css) {
|
286 | var style = document.createElement('style');
|
287 | style.setAttribute('type', 'text/css');
|
288 | style.innerHTML = '<![CDATA[\n' + css + '\n]]>';
|
289 |
|
290 | var defs = document.createElement('defs');
|
291 | defs.appendChild(style);
|
292 | clone.insertBefore(defs, clone.firstChild);
|
293 |
|
294 | var outer = document.createElement('div');
|
295 | outer.appendChild(clone);
|
296 | var src = outer.innerHTML.replace(/NS\d+:href/gi, 'xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href');
|
297 |
|
298 | if (typeof done === 'function') done(src, width, height);else return { src: src, width: width, height: height };
|
299 | });
|
300 | });
|
301 | };
|
302 |
|
303 | out$.svgAsDataUri = function (el, options, done) {
|
304 | requireDomNode(el);
|
305 | return out$.prepareSvg(el, options).then(function (_ref4) {
|
306 | var src = _ref4.src,
|
307 | width = _ref4.width,
|
308 | height = _ref4.height;
|
309 |
|
310 | var svgXml = 'data:image/svg+xml;base64,' + window.btoa(reEncode(doctype + src));
|
311 | if (typeof done === 'function') {
|
312 | done(svgXml, width, height);
|
313 | }
|
314 | return svgXml;
|
315 | });
|
316 | };
|
317 |
|
318 | out$.svgAsPngUri = function (el, options, done) {
|
319 | requireDomNode(el);
|
320 |
|
321 | var _ref5 = options || {},
|
322 | _ref5$encoderType = _ref5.encoderType,
|
323 | encoderType = _ref5$encoderType === undefined ? 'image/png' : _ref5$encoderType,
|
324 | _ref5$encoderOptions = _ref5.encoderOptions,
|
325 | encoderOptions = _ref5$encoderOptions === undefined ? 0.8 : _ref5$encoderOptions,
|
326 | canvg = _ref5.canvg;
|
327 |
|
328 | var convertToPng = function convertToPng(_ref6) {
|
329 | var src = _ref6.src,
|
330 | width = _ref6.width,
|
331 | height = _ref6.height;
|
332 |
|
333 | var canvas = document.createElement('canvas');
|
334 | var context = canvas.getContext('2d');
|
335 | var pixelRatio = window.devicePixelRatio || 1;
|
336 |
|
337 | canvas.width = width * pixelRatio;
|
338 | canvas.height = height * pixelRatio;
|
339 | canvas.style.width = canvas.width + 'px';
|
340 | canvas.style.height = canvas.height + 'px';
|
341 | context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
342 |
|
343 | if (canvg) canvg(canvas, src);else context.drawImage(src, 0, 0);
|
344 |
|
345 | var png = void 0;
|
346 | try {
|
347 | png = canvas.toDataURL(encoderType, encoderOptions);
|
348 | } catch (e) {
|
349 | if (typeof SecurityError !== 'undefined' && e instanceof SecurityError || e.name === 'SecurityError') {
|
350 | console.error('Rendered SVG images cannot be downloaded in this browser.');
|
351 | return;
|
352 | } else throw e;
|
353 | }
|
354 | if (typeof done === 'function') done(png, canvas.width, canvas.height);
|
355 | return Promise.resolve(png);
|
356 | };
|
357 |
|
358 | if (canvg) return out$.prepareSvg(el, options).then(convertToPng);else return out$.svgAsDataUri(el, options).then(function (uri) {
|
359 | return new Promise(function (resolve, reject) {
|
360 | var image = new Image();
|
361 | image.onload = function () {
|
362 | return resolve(convertToPng({
|
363 | src: image,
|
364 | width: image.width,
|
365 | height: image.height
|
366 | }));
|
367 | };
|
368 | image.onerror = function () {
|
369 | reject('There was an error loading the data URI as an image on the following SVG\n' + window.atob(uri.slice(26)) + 'Open the following link to see browser\'s diagnosis\n' + uri);
|
370 | };
|
371 | image.src = uri;
|
372 | });
|
373 | });
|
374 | };
|
375 |
|
376 | out$.download = function (name, uri) {
|
377 | if (navigator.msSaveOrOpenBlob) navigator.msSaveOrOpenBlob(uriToBlob(uri), name);else {
|
378 | var saveLink = document.createElement('a');
|
379 | if ('download' in saveLink) {
|
380 | saveLink.download = name;
|
381 | saveLink.style.display = 'none';
|
382 | document.body.appendChild(saveLink);
|
383 | try {
|
384 | var blob = uriToBlob(uri);
|
385 | var url = URL.createObjectURL(blob);
|
386 | saveLink.href = url;
|
387 | saveLink.onclick = function () {
|
388 | return requestAnimationFrame(function () {
|
389 | return URL.revokeObjectURL(url);
|
390 | });
|
391 | };
|
392 | } catch (e) {
|
393 | console.error(e);
|
394 | console.warn('Error while getting object URL. Falling back to string URL.');
|
395 | saveLink.href = uri;
|
396 | }
|
397 | saveLink.click();
|
398 | document.body.removeChild(saveLink);
|
399 | } else {
|
400 | window.open(uri, '_temp', 'menubar=no,toolbar=no,status=no');
|
401 | }
|
402 | }
|
403 | };
|
404 |
|
405 | out$.saveSvg = function (el, name, options) {
|
406 | return requireDomNodePromise(el).then(function (el) {
|
407 | return out$.svgAsDataUri(el, options || {});
|
408 | }).then(function (uri) {
|
409 | return out$.download(name, uri);
|
410 | });
|
411 | };
|
412 |
|
413 | out$.saveSvgAsPng = function (el, name, options) {
|
414 | return requireDomNodePromise(el).then(function (el) {
|
415 | return out$.svgAsPngUri(el, options || {});
|
416 | }).then(function (uri) {
|
417 | return out$.download(name, uri);
|
418 | });
|
419 | };
|
420 | })(); |
\ | No newline at end of file |