Convert a chart created with D3.js into an image (PNG)

13,277 views
Skip to first unread message

lionel Lioninho

unread,
Jun 11, 2013, 11:21:35 AM6/11/13
to d3...@googlegroups.com
Hi guys,

I created a line chart using D3.js and I need to provide an export feature, that enable the user to export the graph as an image 

do you have any idea of how to do it well ? I tried the process of svg -> canvas -> PNG using (Canvg and Canvas2Image) but

it doesn't feet my needs. 

Thanks in advance

Aaron Reabow

unread,
Jun 12, 2013, 12:21:26 PM6/12/13
to d3...@googlegroups.com
hi.

what i do is copy the svg tag into inkscape.

from there you can export in whatever format you want.

(plus do amendments)

hope that helps

lionel Lioninho

unread,
Jun 12, 2013, 1:02:23 PM6/12/13
to d3...@googlegroups.com
You're right, but I need to propose it as a feature in a web application :-)
Any idea ?


2013/6/12 Aaron Reabow <rea...@gmail.com>

--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/qQN1yb6XQcI/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
lionel Bastian LONKAP TSAMBA

Étudiant 5e année Génie Informatique et Statistiques à Polytech-lille

Site: http://first-developer.fr

Email: lionel.fir...@gmail.com

Tel: +336 12 48 03 01





-- 
lionel Bastian LONKAP TSAMBA

Fifth year student in Software Engineering and Statistics 

at the Lille University Graduate School of Engineers (Polytech-Lille)

Site: http://first-developer.fr

Email: lionel.fir...@gmail.com

Phone: +336 12 48 03 01

Ian Johnson

unread,
Jun 12, 2013, 1:07:01 PM6/12/13
to d3...@googlegroups.com
have you searched the mailling list and stackoverflow?
there are at least 5 threads that have discussed this. I think the consensus is PhantomJS at the moment, but I saw Marc post something recently evaluating various techniques.


--
You received this message because you are subscribed to the Google Groups "d3-js" group.
To unsubscribe from this group and stop receiving emails from it, send an email to d3-js+un...@googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Ian Johnson - 周彦

marc fawzi

unread,
Jun 12, 2013, 3:23:50 PM6/12/13
to d3...@googlegroups.com
I actually didn't do more than mentally investigate the NodeJS approaches by browsing the open issues (bugs, missing features) for each library (namely, jsdom and canvg for NodeJS and libsvg for Imagemagik + NodeJS)  They all issues that undermined my confidence in their ability to render sophisticated visualizations that make use of somewhat obscure SVG features... or at least that was my concern.

I picked PhantomJS because none of the open issues affect its ability to output a raster version of whatever is in the frame buffer (with the exception of WebGL: https://github.com/ariya/phantomjs/issues/10273)  

The cost of the PhantomJS approach is that you're running a heavier process, but having PhantomJS run as a "webserver" allows you to continuously send it D3 code and data via HTTP POST method and have it spit back base64 PNGs, so it can certainly work as part of a web app rendering pipeline. However, due to unknowns around scalability and stability of PhantomJS in this setup (much to be investigated and learned) I would recommend creating the PNG assets via a cron job or creating them once and reusing them for all users. So this approach should work well when all the charts/maps are reusable across all users, not so much when every user has their own chart/map. The latter scenario raises concerns around scalability and stability of PhantomJS, much of which remains unknown (especially with many reports of it crashing, seg faults, etc)

Look for the PhantomJS approach on stackoverflow and improve on it/customize it to your liking. 

I've updated it so that I can send data (as JSON) separate from the D3-based component configuration and loading code >> 



Stephen Bannasch

unread,
Jun 13, 2013, 12:32:48 PM6/13/13
to d3...@googlegroups.com
At 7:02 PM +0200 6/12/13, lionel Lioninho wrote:
You're right, but I need to propose it as a feature in a web application :-)
Any idea ?

The following is preliminary WIP (work-in-progress) but may be interesting to you.

We are doing a research spike into how to generate images from pages or sections of pages that include among other content SVG documents generated with D3.

We are generating png images by extracting the dom, re-writing image links, packaging this up with the css resources and generating the image on the server with phantomjs.

The work is going on in the 'wip_noah_screenshot' branch here:
https://github.com/concord-consortium/lab/tree/wip_noah_screenshot  (all code for Lab available under attribution-only open-source licenses)

To test the early WIP implementation this open this page:


Turn off the "render in iframe" checkbox:


The image generation is for the resource in the center of the page we call an Interactive:


To see the image generation work go at the bottom of the page click either the "png for model" or "png for interactive"


The DOM resources are extracted, sent to to the server, an image generated with phantomjs and then sent back to the dome. The image will appear directly under these buttons.
P720801AF.png
P720801AF 1.png
P720801AF 2.png

lionel Lioninho

unread,
Jun 13, 2013, 12:41:27 PM6/13/13
to d3...@googlegroups.com
OMG marc fawzi, and stepheneb thanks a lot  :-) 
Photo du profil de marc fawzi


2013/6/13 Stephen Bannasch <stephen....@deanbrook.org>
At 7:02 PM +0200 6/12/13, lionel Lioninho wrote:
You're right, but I need to propose it as a feature in a web application :-)
Any idea ?

The following is preliminary WIP (work-in-progress) but may be interesting to you.

We are doing a research spike into how to generate images from pages or sections of pages that include among other content SVG documents generated with D3.

We are generating png images by extracting the dom, re-writing image links, packaging this up with the css resources and generating the image on the server with phantomjs.

The work is going on in the 'wip_noah_screenshot' branch here:
https://github.com/concord-consortium/lab/tree/wip_noah_screenshot  (all code for Lab available under attribution-only open-source licenses)

To test the early WIP implementation this open this page:


Turn off the "render in iframe" checkbox:


The image generation is for the resource in the center of the page we call an Interactive:


To see the image generation work go at the bottom of the page click either the "png for model" or "png for interactive"


The DOM resources are extracted, sent to to the server, an image generated with phantomjs and then sent back to the dome. The image will appear directly under these buttons.

--
You received this message because you are subscribed to a topic in the Google Groups "d3-js" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/d3-js/qQN1yb6XQcI/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to d3-js+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 
P720801AF 2.png
P720801AF 1.png
P720801AF.png

Stephen Bannasch

unread,
Jun 13, 2013, 3:13:14 PM6/13/13
to d3...@googlegroups.com
I forgot to mention that one of the reasons we are going through the complication of extracting the content is that we want to make images of pages after people have played with them and explored the Interactives.

If you are just trying to get a png image from a static existing page with content generated by D3 then you can just setup phantomjs on a server and have it load the page -- then use the existing examples for how to get a rastered version of the image.

Maggie Lee

unread,
Jun 13, 2013, 10:35:05 PM6/13/13
to d3...@googlegroups.com


 I heard second-hand that IE 10 on Windows lets you save .html as .svg or .bmp

VJ

unread,
Nov 13, 2013, 11:06:40 AM11/13/13
to d3...@googlegroups.com
Marc, Ian,

I need this as well. I have D3 line chart generated from server side data file. At same time d3 (svg) chart is rendered in browser, I want to save off a .png on client machine to be used in a word document.

Six months later... Is this still the best way to do this? D3 in browser to png/jpg? Will D3 ever add this support?
Thanks for any help,
VJ

Ian Johnson

unread,
Nov 13, 2013, 2:08:26 PM11/13/13
to d3...@googlegroups.com
I've been using http://html2canvas.hertzen.com/examples.html
with pretty decent success (it doesn't work on all svg features, but its pretty good)

d3 will most likely never support this since it falls out of the scope of the library (and there are other libraries dedicated to it). a plugin maybe.

VJ

unread,
Nov 13, 2013, 2:21:09 PM11/13/13
to d3...@googlegroups.com
I will check it out thanks Ian.

Just so i understand. 
I render my data using D3.js to html page with <svg>all the cool d3 stuff I created</svg>
Then I do html2canvas to get the svg into canvas. 
Then I do img = canvas.toDataURL("image/jpg"); to get it to a jpg (or png)?
Then I can save it localy?

The only thing other than d3.js I will need is html2Canvas library? or is there other libs I need.

Thanks!

Ian Johnson

unread,
Nov 13, 2013, 3:12:49 PM11/13/13
to d3...@googlegroups.com
yep, pretty much. you can set that string as a link, or as the src for an image which can be right click -> save as.

Chris Viau

unread,
Nov 13, 2013, 3:19:12 PM11/13/13
to d3...@googlegroups.com
I use canvg instead of html2Canvas, but it's lacking a lot of features too. In fact, we could write the SVG directly to canvas and a canvas to png. But we have to use canvg or html2Canvas to parse the SVG and redraw it using canvas commands just because of a "security" feature in Chrome preventing a tainted canvas to be written as an image. So we are stuck using these parser that can't replicate all features.
Chris

Jim V

unread,
Nov 14, 2013, 1:50:35 PM11/14/13
to d3...@googlegroups.com
There is a great post on Mozilla Source - that deals with this issue in a slightly different manner:

They have a stand-alone server on AWS that uses a tool that wraps phantomJS and provides an easy way to get a screenshot. The site calls the server, passing in URL, configuration, and which selector to capture - then the server returns a screenshot.

i'm building a server right now to try this process out. While its a slightly different approach then full client-side, it might address some of the shortcomings of the current approaches. 

Thoughts?

marc fawzi

unread,
Nov 14, 2013, 2:56:53 PM11/14/13
to d3...@googlegroups.com
Jim

I have a homemade version of something similar to that, not quite the same. 

I'm not sure this applies, but just in case, one thing to watch out for is that you don't want the DOM to persist between requests so page.open should be called (I assume on your d3 skeleton page) for each request so as to start with a brand new DOM. Otherwise, if you open the D3 skeleton page and evaluate data and code in it that does different things each time (based on the code and data passed in the request) then you will have to reset the DOM to virgin state each time, including setting width/height of all container elements (whatever D3 uses to append svg) to 'auto' or else I found that under moderate to heavy request volume the page.evaluate will finish and your call to render would kick in before the DOM update (running in the PhantomJS Qt frame buffer) has finished. I was mistaken to assume the page.evaluate method blocked till the DOM was fully updated. So I was getting left-over height/width properties from the previous request when the request frequency was around 2/sec. I solved it initially by explicitly resetting the width/height of all container elements in the loaded D3 skeleton page to auto in page.evaluate before evaluating the code/data sent in the request and before calling render but this approach was gonna get ugly when the complexity of the D3 skeleton page increased (e.g. complex page wit many nested container elements) So the best way is to re-open the D3 skeleton page each time, on each request, and execute the code/data in it then render.

Unfortunately, I've been swamped with work that I haven't had any time to write that blog post :/   But I figured someone should know about this particular issue. Also, remember to use Web safe colors for your d3 vis :) or else you may run into PNG rendering issue common to all browsers.

Also, the PhantomJS team is working on 2.0 release which will have a different rendering engine/architecture, which may introduce it's own opportunities and set of challenges. 
 
Good luck and I still hope to go in depth on our experience running a site with 200K+ hits a day with PhantomJS supplying the static data vis assets.

Marc

  

Chris Viau

unread,
Nov 14, 2013, 4:03:58 PM11/14/13
to d3...@googlegroups.com
One trick if you only need to export the chart or part of the page, instead of sending the url for Phantomjs to rerun the whole thing, is to serialize the current state of the SVG
    var svgString = new XMLSerializer().serializeToString(d3.select('svg').node());
and add it to an html template with your stylesheets for Phantomjs to render. Here is an example for loading html as a string with Phantomjs.
Does it make sense?
Chris

marc fawzi

unread,
Nov 14, 2013, 10:23:38 PM11/14/13
to d3...@googlegroups.com

The trick you mention is interesting and I assume you mean it for the Amazon service

or in a homegrown scheme you could also use the webserver and page components of phantomjs and send in the d3 code that generates the chart to phantomjs along with the data and it would then open up a shell/skeleton d3 page and executes it in that page then render the results as base64 (or set mime type and return as PNG) and send back to you.



arunkjn

unread,
Nov 15, 2013, 8:15:08 AM11/15/13
to d3...@googlegroups.com
If you want to follow the phantomJS approach, I created a small utility sometime back https://github.com/arunkjn/screenshot-service
With little effort, you can extend it to specify a particular element to grab. I am very occupied right now. Would love to see a PR if anyone is interested in extending it.

Alex

unread,
Nov 15, 2013, 4:19:54 PM11/15/13
to d3...@googlegroups.com
To solve this, I use a web form is hooked up to a button. The end user presses the button, activating the form, which sends the SVG as a POST variable to a server-side (Perl) script.

The server-side Perl script runs Imagemagick on the SVG, converting it to PDF or PNG, depending on which button is pressed.

As noted, this is very flexible — I can spit out whatever I want, so long as there is a routine available to convert from SVG to something else. I also have control over the server, so I can install modules and libraries to do image processing with Imagemagick.

One drawback is that I cannot use external CSS to style the SVG. All style attributes must be included with the SVG elements for the PDF/PNG/whatever output to look like what the browser renders. But d3 makes inline styling easy enough: I import some style globals from a master global JS file and use those to programmatically style my SVG elements. 

Styling is a bit more work, but the web form and service do the rest.

-Alex

Phuoc Do

unread,
Dec 2, 2013, 1:29:30 PM12/2/13
to d3...@googlegroups.com
I followed Jim Vallandingham's suggestion and made some tweaks to Al Jazeera banquo server. We put together a stand alone service if anyone wants to use:


We use it to generate thumbnails for viz on vida.io. It's still experimental. Feel free to send me an email if you have any problem.

Phuoc Do
Reply all
Reply to author
Forward
0 new messages