Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - Export Div with SVG chart + HTML as and Image

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.

like image 243
user6096856 Avatar asked Nov 15 '16 05:11

user6096856


1 Answers

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>
like image 81
SilentTremor Avatar answered Oct 08 '22 01:10

SilentTremor