| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- 'use strict';
- (function () {
- var out$ = typeof exports != 'undefined' && exports || typeof define != 'undefined' && {} || this || window;
- if (typeof define !== 'undefined') define('save-svg-as-png', [], function () {
- return out$;
- });
- out$.default = out$;
- var xmlNs = 'http://www.w3.org/2000/xmlns/';
- var xhtmlNs = 'http://www.w3.org/1999/xhtml';
- var svgNs = 'http://www.w3.org/2000/svg';
- 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 " ">]>';
- var urlRegex = /url\(["']?(.+?)["']?\)/;
- var fontFormats = {
- woff2: 'font/woff2',
- woff: 'font/woff',
- otf: 'application/x-font-opentype',
- ttf: 'application/x-font-ttf',
- eot: 'application/vnd.ms-fontobject',
- sfnt: 'application/font-sfnt',
- svg: 'image/svg+xml'
- };
- var isElement = function isElement(obj) {
- return obj instanceof HTMLElement || obj instanceof SVGElement;
- };
- var requireDomNode = function requireDomNode(el) {
- if (!isElement(el)) throw new Error('an HTMLElement or SVGElement is required; got ' + el);
- };
- var requireDomNodePromise = function requireDomNodePromise(el) {
- return new Promise(function (resolve, reject) {
- if (isElement(el)) resolve(el);else reject(new Error('an HTMLElement or SVGElement is required; got ' + el));
- });
- };
- var isExternal = function isExternal(url) {
- return url && url.lastIndexOf('http', 0) === 0 && url.lastIndexOf(window.location.host) === -1;
- };
- var getFontMimeTypeFromUrl = function getFontMimeTypeFromUrl(fontUrl) {
- var formats = Object.keys(fontFormats).filter(function (extension) {
- return fontUrl.indexOf('.' + extension) > 0;
- }).map(function (extension) {
- return fontFormats[extension];
- });
- if (formats) return formats[0];
- console.error('Unknown font format for ' + fontUrl + '. Fonts may not be working correctly.');
- return 'application/octet-stream';
- };
- var arrayBufferToBase64 = function arrayBufferToBase64(buffer) {
- var binary = '';
- var bytes = new Uint8Array(buffer);
- for (var i = 0; i < bytes.byteLength; i++) {
- binary += String.fromCharCode(bytes[i]);
- }return window.btoa(binary);
- };
- var getDimension = function getDimension(el, clone, dim) {
- 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));
- return typeof v === 'undefined' || v === null || isNaN(parseFloat(v)) ? 0 : v;
- };
- var getDimensions = function getDimensions(el, clone, width, height) {
- if (el.tagName === 'svg') return {
- width: width || getDimension(el, clone, 'width'),
- height: height || getDimension(el, clone, 'height')
- };else if (el.getBBox) {
- var _el$getBBox = el.getBBox(),
- x = _el$getBBox.x,
- y = _el$getBBox.y,
- _width = _el$getBBox.width,
- _height = _el$getBBox.height;
- return {
- width: x + _width,
- height: y + _height
- };
- }
- };
- var reEncode = function reEncode(data) {
- return decodeURIComponent(encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, function (match, p1) {
- var c = String.fromCharCode('0x' + p1);
- return c === '%' ? '%25' : c;
- }));
- };
- var uriToBlob = function uriToBlob(uri) {
- var byteString = window.atob(uri.split(',')[1]);
- var mimeString = uri.split(',')[0].split(':')[1].split(';')[0];
- var buffer = new ArrayBuffer(byteString.length);
- var intArray = new Uint8Array(buffer);
- for (var i = 0; i < byteString.length; i++) {
- intArray[i] = byteString.charCodeAt(i);
- }
- return new Blob([buffer], { type: mimeString });
- };
- var query = function query(el, selector) {
- if (!selector) return;
- try {
- return el.querySelector(selector) || el.parentNode && el.parentNode.querySelector(selector);
- } catch (err) {
- console.warn('Invalid CSS selector "' + selector + '"', err);
- }
- };
- var detectCssFont = function detectCssFont(rule, href) {
- // Match CSS font-face rules to external links.
- // @font-face {
- // src: local('Abel'), url(https://fonts.gstatic.com/s/abel/v6/UzN-iejR1VoXU2Oc-7LsbvesZW2xOQ-xsNqO47m55DA.woff2);
- // }
- var match = rule.cssText.match(urlRegex);
- var url = match && match[1] || '';
- if (!url || url.match(/^data:/) || url === 'about:blank') return;
- var fullUrl = url.startsWith('../') ? href + '/../' + url : url.startsWith('./') ? href + '/.' + url : url;
- return {
- text: rule.cssText,
- format: getFontMimeTypeFromUrl(fullUrl),
- url: fullUrl
- };
- };
- var inlineImages = function inlineImages(el) {
- return Promise.all(Array.from(el.querySelectorAll('image')).map(function (image) {
- var href = image.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || image.getAttribute('href');
- if (!href) return Promise.resolve(null);
- if (isExternal(href)) {
- href += (href.indexOf('?') === -1 ? '?' : '&') + 't=' + new Date().valueOf();
- }
- return new Promise(function (resolve, reject) {
- var canvas = document.createElement('canvas');
- var img = new Image();
- img.crossOrigin = 'anonymous';
- img.src = href;
- img.onerror = function () {
- return reject(new Error('Could not load ' + href));
- };
- img.onload = function () {
- canvas.width = img.width;
- canvas.height = img.height;
- canvas.getContext('2d').drawImage(img, 0, 0);
- image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', canvas.toDataURL('image/png'));
- resolve(true);
- };
- });
- }));
- };
- var cachedFonts = {};
- var inlineFonts = function inlineFonts(fonts) {
- return Promise.all(fonts.map(function (font) {
- return new Promise(function (resolve, reject) {
- if (cachedFonts[font.url]) return resolve(cachedFonts[font.url]);
- var req = new XMLHttpRequest();
- req.addEventListener('load', function () {
- // TODO: it may also be worth it to wait until fonts are fully loaded before
- // attempting to rasterize them. (e.g. use https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet)
- var fontInBase64 = arrayBufferToBase64(req.response);
- var fontUri = font.text.replace(urlRegex, 'url("data:' + font.format + ';base64,' + fontInBase64 + '")') + '\n';
- cachedFonts[font.url] = fontUri;
- resolve(fontUri);
- });
- req.addEventListener('error', function (e) {
- console.warn('Failed to load font from: ' + font.url, e);
- cachedFonts[font.url] = null;
- resolve(null);
- });
- req.addEventListener('abort', function (e) {
- console.warn('Aborted loading font from: ' + font.url, e);
- resolve(null);
- });
- req.open('GET', font.url);
- req.responseType = 'arraybuffer';
- req.send();
- });
- })).then(function (fontCss) {
- return fontCss.filter(function (x) {
- return x;
- }).join('');
- });
- };
- var cachedRules = null;
- var styleSheetRules = function styleSheetRules() {
- if (cachedRules) return cachedRules;
- return cachedRules = Array.from(document.styleSheets).map(function (sheet) {
- try {
- return { rules: sheet.cssRules, href: sheet.href };
- } catch (e) {
- console.warn('Stylesheet could not be loaded: ' + sheet.href, e);
- return {};
- }
- });
- };
- var inlineCss = function inlineCss(el, options) {
- var _ref = options || {},
- selectorRemap = _ref.selectorRemap,
- modifyStyle = _ref.modifyStyle,
- modifyCss = _ref.modifyCss,
- fonts = _ref.fonts,
- excludeUnusedCss = _ref.excludeUnusedCss;
- var generateCss = modifyCss || function (selector, properties) {
- var sel = selectorRemap ? selectorRemap(selector) : selector;
- var props = modifyStyle ? modifyStyle(properties) : properties;
- return sel + '{' + props + '}\n';
- };
- var css = [];
- var detectFonts = typeof fonts === 'undefined';
- var fontList = fonts || [];
- styleSheetRules().forEach(function (_ref2) {
- var rules = _ref2.rules,
- href = _ref2.href;
- if (!rules) return;
- Array.from(rules).forEach(function (rule) {
- if (typeof rule.style != 'undefined') {
- if (query(el, rule.selectorText)) css.push(generateCss(rule.selectorText, rule.style.cssText));else if (detectFonts && rule.cssText.match(/^@font-face/)) {
- var font = detectCssFont(rule, href);
- if (font) fontList.push(font);
- } else if (!excludeUnusedCss) {
- css.push(rule.cssText);
- }
- }
- });
- });
- return inlineFonts(fontList).then(function (fontCss) {
- return css.join('\n') + fontCss;
- });
- };
- var downloadOptions = function downloadOptions() {
- if (!navigator.msSaveOrOpenBlob && !('download' in document.createElement('a'))) {
- return { popup: window.open() };
- }
- };
- out$.prepareSvg = function (el, options, done) {
- requireDomNode(el);
- var _ref3 = options || {},
- _ref3$left = _ref3.left,
- left = _ref3$left === undefined ? 0 : _ref3$left,
- _ref3$top = _ref3.top,
- top = _ref3$top === undefined ? 0 : _ref3$top,
- w = _ref3.width,
- h = _ref3.height,
- _ref3$scale = _ref3.scale,
- scale = _ref3$scale === undefined ? 1 : _ref3$scale,
- _ref3$responsive = _ref3.responsive,
- responsive = _ref3$responsive === undefined ? false : _ref3$responsive,
- _ref3$excludeCss = _ref3.excludeCss,
- excludeCss = _ref3$excludeCss === undefined ? false : _ref3$excludeCss;
- return inlineImages(el).then(function () {
- var clone = el.cloneNode(true);
- clone.style.backgroundColor = (options || {}).backgroundColor || el.style.backgroundColor;
- var _getDimensions = getDimensions(el, clone, w, h),
- width = _getDimensions.width,
- height = _getDimensions.height;
- if (el.tagName !== 'svg') {
- if (el.getBBox) {
- if (clone.getAttribute('transform') != null) {
- clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate\(.*?\)/, ''));
- }
- var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.appendChild(clone);
- clone = svg;
- } else {
- console.error('Attempted to render non-SVG element', el);
- return;
- }
- }
- clone.setAttribute('version', '1.1');
- clone.setAttribute('viewBox', [left, top, width, height].join(' '));
- if (!clone.getAttribute('xmlns')) clone.setAttributeNS(xmlNs, 'xmlns', svgNs);
- if (!clone.getAttribute('xmlns:xlink')) clone.setAttributeNS(xmlNs, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
- if (responsive) {
- clone.removeAttribute('width');
- clone.removeAttribute('height');
- clone.setAttribute('preserveAspectRatio', 'xMinYMin meet');
- } else {
- clone.setAttribute('width', width * scale);
- clone.setAttribute('height', height * scale);
- }
- Array.from(clone.querySelectorAll('foreignObject > *')).forEach(function (foreignObject) {
- foreignObject.setAttributeNS(xmlNs, 'xmlns', foreignObject.tagName === 'svg' ? svgNs : xhtmlNs);
- });
- if (excludeCss) {
- var outer = document.createElement('div');
- outer.appendChild(clone);
- var src = outer.innerHTML;
- if (typeof done === 'function') done(src, width, height);else return { src: src, width: width, height: height };
- } else {
- return inlineCss(el, options).then(function (css) {
- var style = document.createElement('style');
- style.setAttribute('type', 'text/css');
- style.innerHTML = '<![CDATA[\n' + css + '\n]]>';
- var defs = document.createElement('defs');
- defs.appendChild(style);
- clone.insertBefore(defs, clone.firstChild);
- var outer = document.createElement('div');
- outer.appendChild(clone);
- var src = outer.innerHTML.replace(/NS\d+:href/gi, 'xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href');
- if (typeof done === 'function') done(src, width, height);else return { src: src, width: width, height: height };
- });
- }
- });
- };
- out$.svgAsDataUri = function (el, options, done) {
- requireDomNode(el);
- return out$.prepareSvg(el, options).then(function (_ref4) {
- var src = _ref4.src,
- width = _ref4.width,
- height = _ref4.height;
- var svgXml = 'data:image/svg+xml;base64,' + window.btoa(reEncode(doctype + src));
- if (typeof done === 'function') {
- done(svgXml, width, height);
- }
- return svgXml;
- });
- };
- out$.svgAsPngUri = function (el, options, done) {
- requireDomNode(el);
- var _ref5 = options || {},
- _ref5$encoderType = _ref5.encoderType,
- encoderType = _ref5$encoderType === undefined ? 'image/png' : _ref5$encoderType,
- _ref5$encoderOptions = _ref5.encoderOptions,
- encoderOptions = _ref5$encoderOptions === undefined ? 0.8 : _ref5$encoderOptions,
- canvg = _ref5.canvg;
- var convertToPng = function convertToPng(_ref6) {
- var src = _ref6.src,
- width = _ref6.width,
- height = _ref6.height;
- var canvas = document.createElement('canvas');
- var context = canvas.getContext('2d');
- var pixelRatio = window.devicePixelRatio || 1;
- canvas.width = width * pixelRatio;
- canvas.height = height * pixelRatio;
- canvas.style.width = canvas.width + 'px';
- canvas.style.height = canvas.height + 'px';
- context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
- if (canvg) canvg(canvas, src);else context.drawImage(src, 0, 0);
- var png = void 0;
- try {
- png = canvas.toDataURL(encoderType, encoderOptions);
- } catch (e) {
- if (typeof SecurityError !== 'undefined' && e instanceof SecurityError || e.name === 'SecurityError') {
- console.error('Rendered SVG images cannot be downloaded in this browser.');
- return;
- } else throw e;
- }
- if (typeof done === 'function') done(png, canvas.width, canvas.height);
- return Promise.resolve(png);
- };
- if (canvg) return out$.prepareSvg(el, options).then(convertToPng);else return out$.svgAsDataUri(el, options).then(function (uri) {
- return new Promise(function (resolve, reject) {
- var image = new Image();
- image.onload = function () {
- return resolve(convertToPng({
- src: image,
- width: image.width,
- height: image.height
- }));
- };
- image.onerror = function () {
- 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);
- };
- image.src = uri;
- });
- });
- };
- out$.download = function (name, uri, options) {
- if (navigator.msSaveOrOpenBlob) navigator.msSaveOrOpenBlob(uriToBlob(uri), name);else {
- var saveLink = document.createElement('a');
- if ('download' in saveLink) {
- saveLink.download = name;
- saveLink.style.display = 'none';
- document.body.appendChild(saveLink);
- try {
- var blob = uriToBlob(uri);
- var url = URL.createObjectURL(blob);
- saveLink.href = url;
- saveLink.onclick = function () {
- return requestAnimationFrame(function () {
- return URL.revokeObjectURL(url);
- });
- };
- } catch (e) {
- console.error(e);
- console.warn('Error while getting object URL. Falling back to string URL.');
- saveLink.href = uri;
- }
- saveLink.click();
- document.body.removeChild(saveLink);
- } else if (options && options.popup) {
- options.popup.document.title = name;
- options.popup.location.replace(uri);
- }
- }
- };
- out$.saveSvg = function (el, name, options) {
- var downloadOpts = downloadOptions(); // don't inline, can't be async
- return requireDomNodePromise(el).then(function (el) {
- return out$.svgAsDataUri(el, options || {});
- }).then(function (uri) {
- return out$.download(name, uri, downloadOpts);
- });
- };
- out$.saveSvgAsPng = function (el, name, options) {
- var downloadOpts = downloadOptions(); // don't inline, can't be async
- return requireDomNodePromise(el).then(function (el) {
- return out$.svgAsPngUri(el, options || {});
- }).then(function (uri) {
- return out$.download(name, uri, downloadOpts);
- });
- };
- })();
|