We have angular js application which uses highchart js library and it render chart as SVG. We have made several widget which display combination of data, let say in some widget we have only SVG chart, in some we have only tabular data rendered with angular directive, while in some widget we have combination of both i.e. some chart + some HTML (it can be simple bullet list or let say tabular data rendered with angular). Now we want to give functionality so that each widget can be exported as image.
We came across this example jsfiddle.net/8ypxW/3/ which use html2canvas library and work well with HTML only data, but SVG chart is not exported with this.
We thought to generate image from server side through C#, but each widget is generated with js library (highchart & angular), so in this case we do not have any direct support on server side.
What are the solution for above scenario? or what can be other approach to achieve similar.
I think dom-to-image.js is a bit more accurate than html to canvas, here are some tricks:
dom-to-image let you generate from html svg data url format, yes I know why would you do that, well it's more accurate, with that svg a image is painted inside a canvas (what you need in your case, or something more complex if you need entire page printed in an image as in my example). From that canvas you can pull the image, resize it, download it.
Copyrights: http://jsfiddle.net/gregfedorov/Qh9X5/9/ and http://jsfiddle.net/christopheviau/v6VMf/
///dom to svg module
document.getElementById('downloadbody').onclick= function(){
var wrapper = document.getElementsByTagName("BODY")[0];
//dom to image
domtoimage.toSvg(wrapper).then(function (svgDataUrl) {
//download function
downloadPNGFromAnyImageSrc(svgDataUrl);
});
}
document.getElementById('downloadcircle').onclick= function(){
var wrapper = document.getElementById('circle');
//dom to image
domtoimage.toSvg(wrapper).then(function (svgDataUrl) {
//download function
downloadPNGFromAnyImageSrc(svgDataUrl);
});
}
document.getElementById('downloadtable').onclick= function(){
var wrapper = document.getElementById('table');
//dom to image
domtoimage.toSvg(wrapper).then(function (svgDataUrl) {
//download function
downloadPNGFromAnyImageSrc(svgDataUrl);
});
}
function downloadPNGFromAnyImageSrc(src)
{
//recreate the image with src recieved
var img = new Image;
//when image loaded (to know width and height)
img.onload = function(){
//drow image inside a canvas
var canvas = convertImageToCanvas(img);
//get image/png from convas
var pngImage = convertCanvasToImage(canvas);
//download
var anchor = document.createElement('a');
anchor.setAttribute('href', pngImage.src);
anchor.setAttribute('download', 'image.png');
anchor.click();
};
img.src = src;
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
}
// Converts canvas to an image
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
}
////// svg's generations
////CIrcle Modeule///////
var dataset = {
apples: [53245, 28479, 19697, 24037, 40245],
};
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
var svg = d3.select("#circle").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var path = svg.selectAll("path")
.data(pie(dataset.apples))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc);
// Table module ////////////////////////////////////
var Table = function module() {
var opts = {
width: 200,
height: 200,
margins: {top: 20, right: 20, bottom: 20, left: 20}
};
function exports(selection) {
selection.each(function (dataset) {
//________________________________________________
// Data
//________________________________________________
var columnLabel = dataset.columnLabel;
var rowLabel = dataset.rowLabel;
var value = dataset.value;
//________________________________________________
// DOM preparation
//________________________________________________
// Size
var chartW = Math.max(opts.width - opts.margins.left - opts.margins.right, 0.1);
var chartH = Math.max(opts.height - opts.margins.top - opts.margins.bottom, 0.1);
// SVG
var parentDiv = d3.select(this).html('');
var svg = parentDiv.append('svg').attr('width', opts.width).attr('height', opts.height);
var visSvg = svg.append('g').attr('class', 'vis-group').attr('transform', 'translate(' + opts.margins.left + ',' + opts.margins.top + ')');
var tableBodySvg = visSvg.append('g').attr('class', 'chart-group');
var tableHeaderSvg = visSvg.append('g').attr('class', 'chart-group');
var rowHeaderSvg = tableHeaderSvg.append('g').attr('class', 'row-header');
var colHeaderSvg = tableHeaderSvg.append('g').attr('class', 'col-header');
//________________________________________________
// Table
//________________________________________________
var rowHeaderLevelNum = 1;
var colHeaderLevelNum = 1;
var cellH = chartH / (value.length + rowHeaderLevelNum);
var cellW = chartW / (value[0].length + colHeaderLevelNum);
// Row header
var rowHeaderCell = rowHeaderSvg.selectAll('rect.row-header-cell')
.data(rowLabel);
rowHeaderCell.enter().append('rect')
.attr({
class:'row-header-cell',
width:cellW, height:cellH,
x: 0,
y: function(d, i){return i * cellH + (cellH * colHeaderLevelNum)}
})
.style({fill:'#eee', stroke:'silver'});
// Row header text
rowHeaderCell.enter().append('text')
.attr({
class:'row-header-content',
x: 0,
y: function(d, i){return i * cellH + (cellH * colHeaderLevelNum)},
dx: cellW/2,
dy: cellH/2
})
.style({fill:'black', 'text-anchor':'middle'})
.text(function(d, i){return d;});
// Col header
var colHeaderCell = colHeaderSvg.selectAll('rect.col-header-cell')
.data(columnLabel);
colHeaderCell.enter().append('rect')
.attr({
class:'col-header-cell',
width:cellW, height:cellH,
x: function(d, i){return i * cellW + (cellW * rowHeaderLevelNum)},
y: 0
})
.style({fill:'#eee', stroke:'silver'});
// Col header text
colHeaderCell.enter().append('text')
.attr({
class:'col-header-content',
x: function(d, i){return i * cellW + (cellW * rowHeaderLevelNum)},
y: 0,
dx: cellW/2,
dy: cellH/2
})
.style({fill:'black', 'text-anchor':'middle'})
.text(function(d, i){return d;});
// Body
var row = tableBodySvg.selectAll('g.row')
.data(value);
row.enter().append('g')
.attr('class', 'cell row')
.each(function(pD, pI){
// Cells
var cell = d3.select(this)
.selectAll('rect.cell')
.data(pD);
cell.enter().append('rect')
.attr({
class:'cell', width:cellW, height:cellH,
x: function(d, i){return i * cellW + (cellW * rowHeaderLevelNum)},
y: function(d, i){return pI * cellH + cellH}
})
.style({fill:'white', stroke:'silver'});
// Text
cell.enter().append('text')
.attr({
class:'cell-content', width:cellW, height:cellH,
x: function(d, i){return i * cellW + (cellW * rowHeaderLevelNum)},
y: function(d, i){return pI * cellH + cellH},
dx: cellW/2,
dy: cellH/2
})
.style({fill:'black', 'text-anchor':'middle'})
.text(function(d, i){return d;});
});
});
}
exports.opts = opts;
createAccessors(exports, opts);
return exports;
};
// Helper function ////////////////////////////////////
var createAccessors = function(visExport) {
for (var n in visExport.opts) {
if (!visExport.opts.hasOwnProperty(n)) continue;
visExport[n] = (function(n) {
return function(v) {
return arguments.length ? (visExport.opts[n] = v, this) : visExport.opts[n];
}
})(n);
}
};
// Usage ////////////////////////////////////
var dataset = {
rowLabel: ['A', 'B', 'C', 'D', 'E'],
columnLabel: ['P', 'Q', 'R', 'S'],
value: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19, 20]]
};
var width = 400;
var height = 300;
var table = Table().width(width).height(height);
d3.select('#table')
.datum(dataset)
.call(table);
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
background-color:blue;
}
text {
font: 10px sans-serif;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
<script src="https://mbostock.github.io/d3/d3.js"></script>
<script src="https://rawgit.com/tsayen/dom-to-image/master/src/dom-to-image.js"></script>
<div>
Using dom-to-image.js transform anny given html to a image (svg - no quality loss)<br>
1. tansform html into svg<br>
2. drow svg inside hidden canvas<br>
3. export canvas jpg or png<br>
4. I you like donwload the thing<br>
</div>
<button id='downloadbody'>Download body</button>
<button id='downloadcircle'>Download Circle</button>
<button id='downloadtable'>Download Table</button>
<div id="circle">
</div>
<div id="table">
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With