A topics that pops up frequently in the Google Charts forums is about serializing the contents of an interactive chart
- which is normally rendered as an svg object - as a plain old image. The current Google Charts API do not offer this feature (at the time of this writing) but a solution working on all modern browsers can be quickly written. This article demonstrates how.
Note that this article discusses how to serialize charts to images in a completely client-side fashion. If you are willing to do server-side image generation, there are plenty of alternatives (such as getting screenshots of headless webkit processes).
Also note that there are domains in which Google Charts can be converted to images, for example when you are printing Google spreadsheets containing charts, exporting charts contained in Google spreadsheets (they offer a ‘Publish as image’ option) or generating charts using Google Apps Script Chart services. Depending on your case, you might not need the solution described here, but you may have to resort to it if you are using Google Charts directly via their public APIs.
The main trick to make image serialization work is to first convert the SVG chart into a
canvas element, extract its contents as base64 encoded image using
canvas.toDataURL, then use this payload as the
src attribute for an
img tag generated on the fly.
There are various downsides that make this technique suboptimal (but hey, better than having nothing, right?):
- It works only for SVG charts. You won’t be able to save an AnnotatedTimeline, or other flash-based charts, to image.
- It requires the browser to support svg, canvas and data URLs. This cuts out Internet Explorer 8 and lower. For these cases, there are not that many alternatives, aside from installing Chrome frame or relying on ActiveX or Silverlight embeds to take screenshots of the relevant portion of the page (whose discussion is out of scope here).
- Some browsers impose limitations on data urls sizes (for example, Opera 11) which may limit the size/complexity of the chart you can serialize
- Serializing complex charts (for example, Geocharts) takes a noticeable amount of time (depending on the browser and speed of the machine you are using) and may freeze the browser for a few seconds. Most of the basic charts are serialized almost instantaneously though.
So this solution will work on Chrome, Safari, Firefox (tested on 3.6+), Internet Explorer 9+ and, partially, in Opera.
How it works
The serialization code works as follows. First, you extract the svg code for the chart you want to serialize (assuming
chartContainer points to the html element containing the chart):
var chartArea = chartContainer.getElementsByTagName('iframe'). contentDocument.getElementById('chartArea'); var svg = chartArea.innerHTML;
Then you create a
canvas element and position it offscreen for the user not to see it:
var canvas = doc.createElement('canvas'); canvas.setAttribute('width', chartArea.offsetWidth); canvas.setAttribute('height', chartArea.offsetHeight); canvas.setAttribute( 'style', 'position: absolute; ' + 'top: ' + (-chartArea.offsetHeight * 2) + 'px;' + 'left: ' + (-chartArea.offsetWidth * 2) + 'px;'); doc.body.appendChild(canvas);
Using the canvg svg-to-canvas converter, you transform the svg instructions into a canvas rendering. You then extract the image data (as a base64 encoded string) and assign it to a local variable:
canvg(canvas, svg); var imgData = canvas.toDataURL("image/png");
You can then use the image data to populate an
img tag elsewhere in the page, or redirect the user to download the generated image:
// Populate img tag var img = document.createElement('img'); img.src = imgData; document.getElementById('imageContainer').appendChild(img);
// Download image window.location = imgData.replace("image/png", "image/octet-stream");
Note how we force a different mime-type on the image data for the download case, to force the browser to trigger a download rather than displaying the image in the browser window.
Forcing the image download does not work in all browsers and it doesn’t behave that well: the downloaded image doesn’t have an assigned filename and every browser will use its own defaults in assigning it, often resulting in a confusing name for the user. For example, Chrome saves the image with the filename of
download, with no extension, making it difficult for the user to understand that the file is indeed a PNG image.
It’s probably better to present the image in a separate window or page location, and ask the user to save it from there.
- canvg library: http://code.google.com/p/canvg/
- more ways to save canvas to images: http://www.nihilogic.dk/labs/canvas2image/