6

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:

  • No bbox. It generates a PDF with page size defined by user, not image size;

  • I'm using bootstrap cards with auto resize. Whenever the "print image" is pressed, the print uses the current sizing. I've tried hiding cards to rescale the target one, but the resizing will only take place AFTER the print call, for reasons unknown to me. It this issue is solved, this temporary solution would be better, although still a temp.

So, one question is:

  • how to get the SVG as shown?
  • Alternatively, how to resize the card BEFORE the print is called?
  • Or, how to generate raster (png/jpeg) without the formatting errors obtained from canvg/jsPDF?

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!

  • The project related to this website is stored here: https://github.com/FluVigilanciaBR/fludashboard The snapshot is from the current development branch. The main html code is here: https://github.com/FluVigilanciaBR/fludashboard/blob/development/fludashboard/templates/index.html – Marcelo do Pagode Jun 07 '18 at 21:55
  • I find it rather peculiar that we cannot yet grab SVG's as bitmap images. After all, the browser renders it as pixels in the final step so I'm quite baffled why there's no way for us to grab that image. – nicholaswmin Jun 08 '18 at 16:20
  • If you truly just want a raster of the image, you could use screen capture/ snipping tool – tgiachetti Jun 08 '18 at 16:26
  • 1
    @tgiachetti : Screen capture was my first answer when asked "how can we download the plots from your website". Unfortunately for me, it was frowned upon based on the potential final users of the platform... they've explicitly asked for a "download button". PS: the fact that plot.ly has this functionality built in drives me crazy! How come there's no easy way out with c3 while plot.ly offers it out-of-the-box? Unfortunately, at the moment we don't have time to migrate all the plots to plot.ly structure. This might happen on future versions of the project. – Marcelo do Pagode Jun 08 '18 at 18:51
  • 1
    I got rid of the black fill problem and readded axis ticks by adding this line into your jsfiddle ``. I inserted it into your svg string right after the `` opening tag. – tgiachetti Jun 12 '18 at 15:45

2 Answers2

1

You can solve the problem with Mike Bostock's (or maybe it's NY Times's) SVG Crowbar. Use SVG Crowbar 2. You drag it to the bookmarks bar then click on it when you're on the page where you want to save the SVG. If there is more than one SVG, you will have to choose which one.

I tested SVG Crowbar 1 with c3.js and it didn't save the styles correctly, so make sure you use SVG Crowbar 2. I tested SVG Crowbar 2 with c3.js and it worked for me.

Once you have an SVG, you can use Inkscape to make a PNG out of it. Click: file > export PNG Image. Then choose your desired dimensions for output. Then click Export. This site has a step by step of how to do this

There are also sites online that convert SVG to PNG also, but there are a few so I won't post the links. A google search should find what you need.

If you want to do all of this in a programmatic way, that's another story.

Update: SVG Crowbar (1 and 2) only work in chrome.

tgiachetti
  • 344
  • 1
  • 5
  • 1
    Thanks for the tip regarding crowbar v1/v2. I've also encountered some issues with Crowbar 1 on my plots. I'll give it a try with v2 and comment on that ASAP. Unfortunately, to be restricted to Chrome will still be an issue for the end product. But if it works, I'll register here for reference. – Marcelo do Pagode Jun 08 '18 at 18:53
1

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 :

  • Used as a function, not booklet;
  • Cycles only up to 4th SVG source, since the remaining ones are not needed;
  • Receives SVG sequential number for single plot download;
  • Reload page after download.

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.