Skip to content

Instantly share code, notes, and snippets.

@nstrayer
Forked from gka/multi-crowbar.js
Last active September 9, 2016 20:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nstrayer/6c702f7f57c08b01f3edfd7474cf77ba to your computer and use it in GitHub Desktop.
Save nstrayer/6c702f7f57c08b01f3edfd7474cf77ba to your computer and use it in GitHub Desktop.
like svg-crowbar, but for multiple svg elements!
  1. Paste multi-crowbar.js into the browser developer console
  2. Call multiCrowbar function and pass a selector for the container div
multiCrowbar(".my-chart")

if you want to include html labels or captions you can pass another selector as second argument

multiCrowbar(".my-chart", "h2.my-caption")
var multiCrowbar = (function() {
/*
* SVG Export
* converts html labels to svg text nodes
* will produce incorrect results when used with multi-line html texts
*
* Author: Gregor Aisch
* based on https://github.com/NYTimes/svg-crowbar/blob/gh-pages/svg-crowbar-2.js
*/
window.d3 = null;
var s = document.createElement('script');
s.src = 'https://d3js.org/d3.v3.min.js';
document.getElementsByTagName('head')[0].appendChild(s);
function check() {
if ( !window.d3) return setTimeout(check, 200);
console.log('ready...');
run_multi_crowbar()
}
check();
var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "https://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
return function(cont, label_selector) {
var container = prompt("Enter a holder ID or Class for your vis. \nE.g. '#vizDiv' (Leave blank for the whole page)", "");
container = container == "" ? "body": container
var parent = d3.select(container),
parent_n = parent.node();
var out_w = parent_n.nodeName == 'body' ? parent_n.scrollWidth : parent_n.clientWidth,
out_h = parent_n.nodeName == 'body' ? parent_n.scrollHeight: parent_n.clientHeight;
var labels = label_selector ? parent.selectAll(label_selector) : null,
nodes = parent.selectAll('path, line, rect, circle, text');
// divs = parent.selectAll('.export-rect,.rect'),
// circles = parent.selectAll('.circle');
var svgNodes = parent.selectAll('svg');
// 1. create a new svg container of the size of the page
var out = parent.append('svg');
// empty css declaration
var emptyCSS = window.getComputedStyle(out.node());
out.attr({ width: out_w, height: out_h })
.style({ position: 'absolute', left: 0, top: 0 });
var offsetTop = parent_n.getBoundingClientRect().top - parent_n.parentNode.getBoundingClientRect().top;
var out_g = out.append('g').attr('id', 'svg');
nodes.each(function() {
var el = this,
cur = el,
curCSS,
bbox,
transforms = [];
while (cur) {
curCSS = getComputedStyle(cur);
if (cur.nodeName == 'defs') return;
if (cur.nodeName != 'svg') {
// check node visibility
transforms.push(attr(cur, 'transform'));
cur = cur.parentNode;
} else {
bbox = cur.getBoundingClientRect();
transforms.push('translate('+[bbox.left, bbox.top]+')');
cur = null;
}
if (isHidden(curCSS)) return;
}
transforms = _.filter(transforms, _.identity).reverse();
var cloned = el.cloneNode(true);
cloned.setAttribute('transform', transforms.join(' '));
// copy all computed style attributes
explicitlySetStyle(el, cloned);
out_g.node().appendChild(cloned);
});
if (labels) {
out_g = out.append('g').attr('id', 'text');
labels.each(function() {
// create a text node for each label
var el = this,
cur = el,
bbox = el.getBoundingClientRect(),
align = 'left',
content = el.innerText,
transforms = [];
var txt = out_g.append('text')
.text(content)
.attr({ x: bbox.left });
copyTextStyles(el, txt.node());
txt.attr('y', bbox.top)
.style('dominant-baseline', 'text-before-edge');
bbox = txt.node().getBoundingClientRect();
txt.attr('y', bbox.top+bbox.height).style('dominant-baseline', 'text-after-edge');
});
}
download(out.node(), 'export');
out.remove();
// labels.remove();
// svgNodes.remove();
function isHidden(css) {
return css.display == 'none' ||
css.visibility == 'hidden' ||
+css.opacity === 0 ||
(+css.fillOpacity === 0 || css.fill == 'none') &&
(css.stroke == 'none' || !css.stroke || +css.strokeOpacity === 0);
}
function explicitlySetStyle(element, target) {
var elCSS = getComputedStyle(element),
i, len, key, value,
computedStyleStr = "";
for (i=0, len=elCSS.length; i<len; i++) {
key=elCSS[i];
value=elCSS.getPropertyValue(key);
if (value!==emptyCSS.getPropertyValue(key)) {
computedStyleStr+=key+":"+value+";";
}
}
target.setAttribute('style', computedStyleStr);
}
function copyTextStyles(element, target) {
var elCSS = getComputedStyle(element),
i, len, key, value,
computedStyleStr = "";
for (i=0, len=elCSS.length; i<len; i++) {
key=elCSS[i];
if (key.substr(0,4) == 'font' || key.substr(0,4) == 'text' || key == 'color') {
value=elCSS.getPropertyValue(key);
if (key == 'color') key = 'fill';
if (value!==emptyCSS.getPropertyValue(key)) {
computedStyleStr+=key+":"+value+";";
}
}
}
target.setAttribute('style', computedStyleStr);
}
function download(svg, filename) {
var source = (new XMLSerializer()).serializeToString(svg);
var url = window.URL.createObjectURL(new Blob([doctype + source], { "type" : "text\/xml" }));
var 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(function() {
window.URL.revokeObjectURL(url);
}, 10);
}
function attr(n, v) { return n.getAttribute(v); }
};
})();
function run_multi_crowbar(){
multiCrowbar()
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Multi-Crowbar</title>
</head>
<style>
.bookmarklet {
padding: 3px 8px;
font-size: 12px;
font-weight: bold;
text-decoration: none;
border-radius: 1em;
background: #2b8cbe;
color: white;
}
.center {
padding: 70px 0;
text-align: center;
}
</style>
<body>
<p class = "center">
<a class="bookmarklet" href="javascript:(function(){s=document.createElement('script');s.type='text/javascript';s.src='https://cdn.rawgit.com/nstrayer/6c702f7f57c08b01f3edfd7474cf77ba/raw/ca571c282fb14922916b3281f72bba0442c528de/bookmarklet.js?v='+parseInt(Math.random()*99999999);document.body.appendChild(s);})();">Multi-Crowbar</a>&nbsp; <span>← Drag me to your bookmarks bar.</span>
<p>
</body>
</html>
var multiCrowbar = (function() {
/*
* SVG Export
* converts html labels to svg text nodes
* will produce incorrect results when used with multi-line html texts
*
* Author: Gregor Aisch
* based on https://github.com/NYTimes/svg-crowbar/blob/gh-pages/svg-crowbar-2.js
*/
window.d3 = null;
var s = document.createElement('script');
s.src = 'https://d3js.org/d3.v3.min.js';
document.getElementsByTagName('head')[0].appendChild(s);
function check() {
if (!window.d3) return setTimeout(check, 200);
console.log('ready...');
// run('body');
}
check();
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">';
return function(cont, label_selector) {
var parent = d3.select(cont),
parent_n = parent.node();
var out_w = parent_n.nodeName == 'body' ? parent_n.scrollWidth : parent_n.clientWidth,
out_h = parent_n.nodeName == 'body' ? parent_n.scrollHeight: parent_n.clientHeight;
var labels = label_selector ? parent.selectAll(label_selector) : null,
nodes = parent.selectAll('path, line, rect, circle, text');
// divs = parent.selectAll('.export-rect,.rect'),
// circles = parent.selectAll('.circle');
var svgNodes = parent.selectAll('svg');
// 1. create a new svg container of the size of the page
var out = parent.append('svg');
// empty css declaration
var emptyCSS = window.getComputedStyle(out.node());
out.attr({ width: out_w, height: out_h })
.style({ position: 'absolute', left: 0, top: 0 });
var offsetTop = parent_n.getBoundingClientRect().top - parent_n.parentNode.getBoundingClientRect().top;
var out_g = out.append('g').attr('id', 'svg');
nodes.each(function() {
var el = this,
cur = el,
curCSS,
bbox,
transforms = [];
while (cur) {
curCSS = getComputedStyle(cur);
if (cur.nodeName == 'defs') return;
if (cur.nodeName != 'svg') {
// check node visibility
transforms.push(attr(cur, 'transform'));
cur = cur.parentNode;
} else {
bbox = cur.getBoundingClientRect();
transforms.push('translate('+[bbox.left, bbox.top]+')');
cur = null;
}
if (isHidden(curCSS)) return;
}
transforms = _.filter(transforms, _.identity).reverse();
var cloned = el.cloneNode(true);
cloned.setAttribute('transform', transforms.join(' '));
// copy all computed style attributes
explicitlySetStyle(el, cloned);
out_g.node().appendChild(cloned);
});
if (labels) {
out_g = out.append('g').attr('id', 'text');
labels.each(function() {
// create a text node for each label
var el = this,
cur = el,
bbox = el.getBoundingClientRect(),
align = 'left',
content = el.innerText,
transforms = [];
var txt = out_g.append('text')
.text(content)
.attr({ x: bbox.left });
copyTextStyles(el, txt.node());
txt.attr('y', bbox.top)
.style('dominant-baseline', 'text-before-edge');
bbox = txt.node().getBoundingClientRect();
txt.attr('y', bbox.top+bbox.height).style('dominant-baseline', 'text-after-edge');
});
}
download(out.node(), 'export');
out.remove();
// labels.remove();
// svgNodes.remove();
function isHidden(css) {
return css.display == 'none' ||
css.visibility == 'hidden' ||
+css.opacity === 0 ||
(+css.fillOpacity === 0 || css.fill == 'none') &&
(css.stroke == 'none' || !css.stroke || +css.strokeOpacity === 0);
}
function explicitlySetStyle(element, target) {
var elCSS = getComputedStyle(element),
i, len, key, value,
computedStyleStr = "";
for (i=0, len=elCSS.length; i<len; i++) {
key=elCSS[i];
value=elCSS.getPropertyValue(key);
if (value!==emptyCSS.getPropertyValue(key)) {
computedStyleStr+=key+":"+value+";";
}
}
target.setAttribute('style', computedStyleStr);
}
function copyTextStyles(element, target) {
var elCSS = getComputedStyle(element),
i, len, key, value,
computedStyleStr = "";
for (i=0, len=elCSS.length; i<len; i++) {
key=elCSS[i];
if (key.substr(0,4) == 'font' || key.substr(0,4) == 'text' || key == 'color') {
value=elCSS.getPropertyValue(key);
if (key == 'color') key = 'fill';
if (value!==emptyCSS.getPropertyValue(key)) {
computedStyleStr+=key+":"+value+";";
}
}
}
target.setAttribute('style', computedStyleStr);
}
function download(svg, filename) {
var source = (new XMLSerializer()).serializeToString(svg);
var url = window.URL.createObjectURL(new Blob([doctype + source], { "type" : "text\/xml" }));
var 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(function() {
window.URL.revokeObjectURL(url);
}, 10);
}
function attr(n, v) { return n.getAttribute(v); }
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment