javascriptjqueryhtmlsvgcanvg

How to properly generate canvas from complex svg


I'm trying to export a complex SVG block (generated with c3) from my website as an image (be it png, svg, pdf, at this point I'm open to anything that solves it, although vector format would be ideal). I have tried html2canvas, canvg, jsPDF, and all the cool kids from that gang. The problem is: one of my plots gets all screwed up. Line becomes area, areas get inverted, colors are ruined, ... you name it.

I'm pretty far from being js expert. I've just got here and I'm finding my way around, some please bear with me.

I don't know if this is a CSS issue or what. Yep, we do have CSS behind the html.

My temporary solution is to use jQuery.print.js to call a print of my div. This is far from ideal for many reasons:

So, one question is:

The function doing the print call for now is:

function getscreenshot(div) {
  // Hide row pair:
  $('#map-card').addClass('hidden');

  // Temporarily change class attr to spawn all row:
  var divClass = $(div).attr('class');
  $(div).attr('class', 'col');

  // ****PROBLEM****
  // Div size is not upated before calling print()
  // causing the print to have the size as displayed on user screen
  // How to refresh it before print?
  // ********

  // jQuery.print solves it in a non-ideal way, since user has to set save as file and so on
  $(div).print();

  // This solution with jsPDF produces **ugly as hell** image:
  // var pdf = new jsPDF('landscape');
  // pdf.addHTML(document.getElementById(div), function() {
  //     pdf.save(name + 'pdf');
  // });

  // Recover original size after print:
  // Restore row pair and div original state:
  $('#map-card').removeClass('hidden');
  $(div).attr('class', divClass);
}

Here is the row of cards as shown on the webpage: Cards row

The plot on the right is the one I'm focusing my try-outs, and the one that is getting completing unformatted. Check what comes out using html2canvas, jsPDF and the like results in the same misconstruction as seem in the fiddle with SVG pasted, using canvg.js

PS: Yep, I did search a lot. That's how I ended up trying html2canvas, canvg, jsPDF, jqprint, ...

Cheers!


Solution

  • The final result makes use of @tgiachetti answer, with a few tweaks. The problem with the original svg-crowbar-2 is that it is a booklet and opens up download buttons to all SVGs on the website, which is not the desired end result, and only works on Chrome.

    The solution presented here works on both Chrome and Firefox.

    So I ended up applying a few modifications on svg-crowbar-2.js from https://github.com/NYTimes/svg-crowbar :

    Changes can be found as js file singledownload-svg-crowbar-2.js at: https://github.com/FluVigilanciaBR/fludashboard/blob/development/fludashboard/static/libs/svg-crowbar-2/singledownload-svg-crowbar-2.js

    For usage:

    First off, inserted tweaked svg-crowbar-2 as source:

    <script src="./static/libs/svg-crowbar-2/singledownload-svg-crowbar-2.js"></script>
    

    Then, at each plot card I inserted a button with a call to function getscreenshot passing the sequential number of the desired SVG:

    <div class="container">
     <button type="button" class="btn btn-link btn-sm no-print" onclick="getscreenshot(3);">Salvar imagem</button>
    </div>
    

    This function, placed on index.html itself, basically defines SVG file name based on plot sequential number and calls the single SVG downloader function singleSVGcrowbar, defined on singledownload-svg-crowbar-2.js:

    <script language="JavaScript" type="text/javascript">
      function getscreenshot(myplot) {
        var plotName = ['mapa', 'mapa-legenda', 'serie-temporal', 'distribuicao-etaria'];
        var myplotObj = {SVGid: myplot, SVGname: plotName[myplot]};
    
        singleSVGcrowbar(myplotObj);
      }
    </script>
    

    If you want to insert this on your own website, first of all identify the sequential number of the desired SVG block by running the original svg-crowbar-2. This is the number inserted on getscreenshot(myplot) call.