123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- // adapted from https://github.com/NYTimes/svg-crowbar
- import { each } from 'lodash';
- const 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">';
- const prefix = {
- svg: 'http://www.w3.org/2000/svg',
- xlink: 'http://www.w3.org/1999/xlink',
- xmlns: 'http://www.w3.org/2000/xmlns/'
- };
- const cssSkipValues = {
- '0px 0px': true,
- auto: true,
- pointer: true,
- visible: true
- };
- function setInlineStyles(svg, target, emptySvgDeclarationComputed) {
- function explicitlySetStyle(element, targetEl) {
- const cSSStyleDeclarationComputed = getComputedStyle(element);
- let computedStyleStr = '';
- each(cSSStyleDeclarationComputed, (key) => {
- const value = cSSStyleDeclarationComputed.getPropertyValue(key);
- if (value !== emptySvgDeclarationComputed.getPropertyValue(key) && !cssSkipValues[value]) {
- computedStyleStr += `${key}:${value};`;
- }
- });
- targetEl.setAttribute('style', computedStyleStr);
- targetEl.removeAttribute('data-reactid');
- }
- function traverse(obj) {
- const tree = [];
- function visit(node) {
- if (node && node.hasChildNodes()) {
- let child = node.firstChild;
- while (child) {
- if (child.nodeType === 1 && child.nodeName !== 'SCRIPT') {
- tree.push(child);
- visit(child);
- }
- child = child.nextSibling;
- }
- }
- }
- tree.push(obj);
- visit(obj);
- return tree;
- }
- // make sure logo shows up
- svg.setAttribute('class', 'exported');
- // hardcode computed css styles inside svg
- const allElements = traverse(svg);
- const allTargetElements = traverse(target);
- for (let i = allElements.length - 1; i >= 0; i -= 1) {
- explicitlySetStyle(allElements[i], allTargetElements[i]);
- }
- // set font
- target.setAttribute('style', 'font-family: Arial;');
- // set view box
- target.setAttribute('width', svg.clientWidth);
- target.setAttribute('height', svg.clientHeight);
- }
- function download(source, name) {
- let filename = 'untitled';
- if (name) {
- filename = name;
- } else if (window.document.title) {
- filename = `${window.document.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}-${(+new Date())}`;
- }
- const url = window.URL.createObjectURL(new Blob(
- source,
- { type: 'text/xml' }
- ));
- const a = document.createElement('a');
- document.body.appendChild(a);
- a.setAttribute('class', 'svg-crowbar');
- a.setAttribute('download', `${filename}.svg`);
- a.setAttribute('href', url);
- a.style.display = 'none';
- a.click();
- setTimeout(() => {
- window.URL.revokeObjectURL(url);
- }, 10);
- }
- function getSVGElement() {
- return document.getElementById('canvas');
- }
- function getSVG(doc, emptySvgDeclarationComputed) {
- const svg = getSVGElement();
- const target = svg.cloneNode(true);
- target.setAttribute('version', '1.1');
- // removing attributes so they aren't doubled up
- target.removeAttribute('xmlns');
- target.removeAttribute('xlink');
- // These are needed for the svg
- if (!target.hasAttributeNS(prefix.xmlns, 'xmlns')) {
- target.setAttributeNS(prefix.xmlns, 'xmlns', prefix.svg);
- }
- if (!target.hasAttributeNS(prefix.xmlns, 'xmlns:xlink')) {
- target.setAttributeNS(prefix.xmlns, 'xmlns:xlink', prefix.xlink);
- }
- setInlineStyles(svg, target, emptySvgDeclarationComputed);
- const source = (new XMLSerializer()).serializeToString(target);
- return [doctype + source];
- }
- function cleanup() {
- const crowbarElements = document.querySelectorAll('.svg-crowbar');
- [].forEach.call(crowbarElements, (el) => {
- el.parentNode.removeChild(el);
- });
- // hide embedded logo
- const svg = getSVGElement();
- svg.setAttribute('class', '');
- }
- export function saveGraph(filename) {
- window.URL = (window.URL || window.webkitURL);
- // add empty svg element
- const emptySvg = window.document.createElementNS(prefix.svg, 'svg');
- window.document.body.appendChild(emptySvg);
- const emptySvgDeclarationComputed = getComputedStyle(emptySvg);
- const svgSource = getSVG(document, emptySvgDeclarationComputed);
- download(svgSource, filename);
- cleanup();
- }
|