How to create a chart without a browser using PhantomJS (command line rendering)

This HOWTO demonstrates how to use the PhantomJS command-line tool for rendering a webpage without using a web browser like Google Chrome or Microsoft Internet Explorer. By doing this you could setup a script that automatically saves an image of the same chart as that that's used on your website which you can then use in a PDF (for example). Note: This HOWTO guide is in the canvas section of the website but applies to SVG charts as well.

Introduction

A common request is to be able to use charts in PDF files (or similar) or to be able to download the chart as an image. Some browsers (such as Mozilla Firefox allow to right-click a canvas and save it as an image - but this is a far cry from having it done automatically (eg from a scheduled task or cron job).

Get hold of PhantomJS

The PhantomJS project appears to be stalled and not actively developed so I've added the downloads to the RGraph website and there are links below to the files.

Downloadable pre-compiled binary packages

FreeBSD

Binary packages are available via pkg:

$ sudo pkg install phantomjs

Source code

You can get hold of the source code using the GitHub PhantomJS repository .

The HTML file that creates the chart

This is the webpage that you're fetching with PhantomJS. It's a regular web page and just produces the chart - be it a canvas based chart or SVG chart. PhantomJS will create both. Here's the HTML code for the whole page:

SVG version:
<!DOCTYPE html>
<html>
<head>
    <!-- These paths should be relative to wherever you put this page on your website -->
    <script src="/libraries/RGraph.svg.common.core.js" ></script>
    <script src="/libraries/RGraph.svg.line.js" ></script>
    
    <!-- PhantomJS renders the page with a black/transparent background so set it to white and get rid of margins/padding -->
    <style>
        body {
            background-color: white;
            margin: 0 0 0 0;
            padding: 0 0 0 0;
        }
    </style>
    
    <!-- Don't want this page indexed by search engines -->
    <meta name="robots" content="noindex, nofollow" />
</head>
<body>

    <!-- This is the div tag where the chart will appear -->
    <div id="cc" style="width: 950; height: 300px"></div>
    
    <!--
        This is the script that generates the chart. Like any other page on your server this could integrate with anything
        else on your server - for example MySQL
    -->
    <script>
        new RGraph.SVG.Line({
            id: 'cc',
            data: [8,4,6,3,5,8,9,8,4,6,3,5,2,4,8,6],
            options: {
                xaxisLabels: ['Barry','Charles','Olga','Lou','Fred','Hoolio','Gary','Mia','Rich','Kev','John','David','Paul','Fred','Lewis','John'],
                linewidth: 5,
                spline: true,
                backgroundGridVlines: false,
                backgroundGridBorder: false,
                yaxis: false
            }
        }).draw();
    </script>

</body>
</html>
Canvas version:
<html>
<head>
    <!-- These paths should be relative to wherever you put this page on your website -->
    <script src="/libraries/RGraph.common.core.js" ></script>
    <script src="/libraries/RGraph.line.js" ></script>
    
    <!-- PhantomJS renders the page with a black/transparent background so set it to white and get rid of margins/padding -->
    <style>
        body {
            background-color: white;
            background-color: white;
            margin: 0 0 0 0;
            padding: 0 0 0 0;
        }
    </style>
    
    <!-- Don't want this page indexed by search engines -->
    <meta name="robots" content="noindex" />
</head>
<body>

    <!-- This is the canvas tag where the chart will appear -->
    <canvas id="cvs" width="850" height="300">[No canvas support]</canvas>
    
    <!--
        This is the script that generates the chart. Like any other page on your server this could integrate with anything
        else on your server - for example MySQL
    -->
    <script>
        new RGraph.Line({
            id: 'cvs',
            data: [8,4,6,3,5,8,9,8,4,6,3,5,2,4,8,6],
            options: {
                xaxisLabels: ['Barry','Charles','Olga','Lou','Fred','Hoolio','Gary','Mia','Rich','Kev','John','David','Paul','Fred','Lewis','John'],
                linewidth: 5,
                spline: true,
                backgroundGridVlines: false,
                backgroundGridBorder: false,
                yaxis: false,
                shadow: true
            }
        }).draw();
    </script>

</body>
</html>

The JavaScript file that makes the request for the webpage

This is a little JavaScript snippet (it can get larger!) that is run by PhantomJS which creates the "headless" browser instance and then requests the page that you tell it to (in this case it's the page shown just above). PhantomJS renders the page and the chart that we've put on it (regardless of whether it's canvas or SVG based) and saves an image of it locally. The configuration that's used here saves the top left corner of the page - which in this case is just the chart.

SVG version:
// This creates an instance of a page. The require() function is provided by PhantomJS
var page = require('webpage').create();

// Open our test webpage (this is a real page that you can view in your browser)
page.open('https://www.rgraph.net/tests/svg.line/phantomjs.html', function()
{
    // This sets the area of the virtual browser to be saved. The chart is positioned
    // in the top right corner so that's the bit we want
    page.viewportSize = {
        width: 850,
        height: 300
    };
    
    // Render the page to this file
    page.render('rgraph-svg.png');
    
    // Exit PhantomJS cleanly
    phantom.exit(0);
});
Canvas version:
// This creates an instance of a page. The require() function is provided by PhantomJS
var page = require('webpage').create();

// Open our test webpage (this is a real page that you can view in your browser)
page.open('https://www.rgraph.net/tests/canvas.line/phantomjs.html', function()
{
    // This sets the area of the virtual browser to be saved. The chart is positioned
    // in the top right corner so that's the bit we want
    page.viewportSize = {
        width: 850,
        height: 300
    };
    
    // Render the page to this file
    page.render('rgraph-canvas.png');
    
    // Exit PhantomJS cleanly
    phantom.exit(0);
});

Making the request

Making the request is just a case of running the PhantomJS binary and giving it the filename of the JavaScript file that we created (which is shown just above).

The JavaScript file then makes a request for the page that shows the chart (which is also shown above). The image of the page is saved locally using the filename that's specified in the JavaScript file.

The commands that were used to run PhantomJS from the Windows command line was this (I'll assume that the Mac/Linux versions are quite similar)):

c:\PhantomJS\> .\bin\phantomjs.exe .\rgraph-svg.js
c:\PhantomJS\> .\bin\phantomjs.exe .\rgraph-canvas.js

Creating the chart automatically

By adding the script to Windows Task Scheduler (or on Mac/Linux you can use a cron task) you can make the generation of the chart automatic - so it happens at whatever interval that you specify.

This would obviously make things much easier and essentially remove the need to initiate the command yourself. It could be a daily task or you could have it produce the chart every minute so that it's always up-to-date.