forked from enviPath/enviPy
[Feature] Pathway SVG Export (#102)
Co-authored-by: Tim Lorsbach <tim@lorsba.ch> Reviewed-on: enviPath/enviPy#102
This commit is contained in:
@ -384,12 +384,31 @@ function draw(pathway, elem) {
|
||||
.style("fill", "#e8e8e8");
|
||||
|
||||
// Add image only for non pseudo nodes
|
||||
node.filter(d => !d.pseudo).append("image")
|
||||
.attr("xlink:href", d => d.image)
|
||||
.attr("x", -nodeRadius)
|
||||
.attr("y", -nodeRadius)
|
||||
.attr("width", nodeRadius * 2)
|
||||
.attr("height", nodeRadius * 2);
|
||||
node.filter(d => !d.pseudo).each(function (d) {
|
||||
const g = d3.select(this).append("g");
|
||||
|
||||
// Parse the SVG string
|
||||
const parser = new DOMParser();
|
||||
const svgDoc = parser.parseFromString(d.image_svg, "image/svg+xml");
|
||||
const svgContent = svgDoc.documentElement;
|
||||
|
||||
// Remove width/height so scaling works
|
||||
svgContent.removeAttribute("width");
|
||||
svgContent.removeAttribute("height");
|
||||
|
||||
// Move all children of the parsed SVG into our <g>
|
||||
while (svgContent.firstChild) {
|
||||
g.node().appendChild(svgContent.firstChild);
|
||||
}
|
||||
|
||||
// Get viewBox dimensions for scaling
|
||||
const vb = svgContent.viewBox.baseVal;
|
||||
const svgWidth = vb.width || 40;
|
||||
const svgHeight = vb.height || 40;
|
||||
|
||||
// Center and scale
|
||||
g.attr("transform", `translate(${-svgWidth/2},${-svgHeight/2}) scale(${(nodeRadius*2)/svgWidth})`);
|
||||
});
|
||||
|
||||
// add element to nodes array
|
||||
node.each(function (d) {
|
||||
@ -397,4 +416,38 @@ function draw(pathway, elem) {
|
||||
});
|
||||
|
||||
pop_add(node, "Compound", node_popup);
|
||||
}
|
||||
}
|
||||
|
||||
function serializeSVG(svgElement) {
|
||||
|
||||
svgElement.querySelectorAll("line.link").forEach(line => {
|
||||
const style = getComputedStyle(line);
|
||||
line.setAttribute("stroke", style.stroke);
|
||||
line.setAttribute("stroke-width", style.strokeWidth);
|
||||
line.setAttribute("fill", style.fill);
|
||||
});
|
||||
|
||||
const serializer = new XMLSerializer();
|
||||
let svgString = serializer.serializeToString(svgElement);
|
||||
|
||||
// Add namespace if missing
|
||||
if (!svgString.includes('xmlns="http://www.w3.org/2000/svg"')) {
|
||||
svgString = svgString.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
|
||||
}
|
||||
|
||||
return svgString;
|
||||
}
|
||||
|
||||
function downloadSVG(svgElement, filename = 'chart.svg') {
|
||||
const svgString = serializeSVG(svgElement);
|
||||
const blob = new Blob([svgString], {type: 'image/svg+xml;charset=utf-8'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user