Skip to content

Commit

Permalink
Merge pull request #109 from neuroglia-io/feat-dagre-save-to-png
Browse files Browse the repository at this point in the history
Added the ability to save the graph as PNG in Blazor.Dagre
  • Loading branch information
JBBianchi authored Aug 26, 2024
2 parents fba32c9 + 14cca16 commit ad1b6fd
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/Neuroglia.Blazor.Dagre/DagreGraph.razor
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
<polygon points="22 16 24 16 24 8 16 8 16 10 22 10 22 16"/>
<polygon points="8 24 16 24 16 22 10 22 10 16 8 16 8 24"/>
</svg>
<svg id="save" viewBox="0 0 24 24">
<!-- from https://www.svgrepo.com/svg/432286/save-down-2 -->
<path d="M18.437,20.948H5.563a2.372,2.372,0,0,1-2.5-2.21v-11a2.372,2.372,0,0,1,2.5-2.211h.462a.5.5,0,0,1,0,1H5.563a1.38,1.38,0,0,0-1.5,1.211v11a1.38,1.38,0,0,0,1.5,1.21H18.437a1.38,1.38,0,0,0,1.5-1.21v-11a1.38,1.38,0,0,0-1.5-1.211h-.462a.5.5,0,0,1,0-1h.462a2.372,2.372,0,0,1,2.5,2.211v11A2.372,2.372,0,0,1,18.437,20.948Z" />
<path d="M15.355,10.592l-3,3a.5.5,0,0,1-.35.15.508.508,0,0,1-.36-.15l-3-3a.5.5,0,0,1,.71-.71l2.14,2.139V3.552a.508.508,0,0,1,.5-.5.5.5,0,0,1,.5.5v8.49l2.15-2.16a.5.5,0,0,1,.71.71Z" />
</svg>
<svg id="toggle-orientation" viewBox="0 0 16 16">
<!-- from https://www.svgrepo.com/svg/371431/orientation -->
<path d="M11 2.1c2 0 3 1.3 3 2.9h-1l1.5 2 1.5-2h-1c0-2.2-2-3.9-4-3.9v-1.1l-2 1.5 2 1.5v-0.9z"></path>
Expand Down Expand Up @@ -100,6 +105,11 @@
<use href="#fit" />
</svg>
</button>
<button class="btn" type="button" @onclick="SaveAsPngAsync" title="save as png">
<svg>
<use href="#save" />
</svg>
</button>
<!--<button class="btn" type="button" @onclick="ToggleOrientationAsync" title="toggle orientation">
<svg>
<use href="#toggle-orientation" />
Expand Down Expand Up @@ -277,6 +287,13 @@
await this.CenterAsync();
}

public virtual async Task SaveAsPngAsync()
{
if (this.graph == null)
return;
await this.JSRuntime.InvokeVoidAsync("neuroglia.blazor.saveAsPng", this.graphReference);
}

public virtual async Task ToggleOrientationAsync()
{
if (this.graph == null || this.options == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
if (dagre == null) {
throw 'dagre needs to be loaded first';
}

function copyStyle(element) {
const style = getComputedStyle(element);
Object.entries(style).forEach(([key, value]) => {
const styleName = key.replace(/\-([a-z])/g, match => match[1].toUpperCase());
element.style[styleName] = value;
});
Array.from(element.childNodes)
.filter(node => node.nodeType == Node.ELEMENT_NODE)
.forEach(node => copyStyle(node));
}

window.dagre = dagre;
window.neuroglia = window.neuroglia || {};
window.neuroglia.blazor = window.neuroglia.blazor || {};
Expand Down Expand Up @@ -46,4 +58,40 @@
}
return wScale;
}
window.neuroglia.blazor.saveAsPng = (graphElement) => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const boundingBox = graphElement.querySelector('g.graph').getBBox();
canvas.setAttribute('width', boundingBox.width + 'px');
canvas.setAttribute('height', boundingBox.height + 'px');
canvas.setAttribute('style', 'display: none;');
const img = new Image(boundingBox.width, boundingBox.height);
img.onerror = (err) => {
console.error('There was an error loading the SVG as base64.');
};
img.onload = async () => {
context.drawImage(img, 0, 0, img.width, img.height);
const downloadLink = document.createElement('a');
downloadLink.download = 'diagram.png';
downloadLink.href = canvas.toDataURL('image/png', 1);
downloadLink.click();
document.body.removeChild(canvas);
document.body.removeChild(img);
document.body.removeChild(svgClone);
};
const svgClone = graphElement.cloneNode(true);
const defsEl = svgClone.querySelector('defs');
const graphElClone = svgClone.querySelector('g.graph');
graphElClone.setAttribute('transform', 'scale(1)');
document.body.appendChild(svgClone);
Array.from(document.querySelectorAll('.svg-definitions defs *')).forEach(def => {
defsEl.appendChild(def.cloneNode(true));
});
copyStyle(svgClone);
const svg = new XMLSerializer().serializeToString(svgClone);
const base64 = btoa(unescape(encodeURIComponent(svg)));
document.body.appendChild(canvas);
document.body.appendChild(img);
img.src = `data:image/svg+xml;charset=utf-8;base64,${base64}`;
}
})();

0 comments on commit ad1b6fd

Please sign in to comment.