VGTech is a blog where the developers and devops of Norways most visited website share code and tricks of the trade… Read more



Are you brilliant? We're hiring. Read more

Server-side Highcharts rendering

PhantomJS

Highcharts is a fantastic tool for generating javascript-based charts. It’s flexible, fast and can be extensively customized. Recently, I had to find a way to render Highcharts as static images on the server as we wanted to attach charts in emails.

The reason why we wanted to stick with Highcharts even on the server is to still maintain a single code-base and to have a unified feel of the charts we present to our users.

What I wanted to achieve was simple:

  • Run a POST request against a webserver with the same JSON-data you would construct a regular Highcharts instance with
  • Get back a PNG-image as the HTTP-response

There are a number of options already available:

  • You can use a combination of PHP and Java with Batik
  • You can use the Java server with Batik
  • You can use a PhantomJS script

For various reasons, these did not match what I wanted to achieve or they did it in a way that were not optimal in regards to our server setup or comfort zone. The closest to what I wanted to achieve was the PhantomJS script, but it had a limitation where it would output the chart to local files.

I ended up writing a fairly simple set of scripts that runs on PhantomJS and have made it available on Github. It is by no means thoroughly tested or as flexible as the official solutions, but in my opinion it is slightly cleaner and easier to set up. Basically, you just clone the repository, configure the config.json file (normally just have to change the port to one of your choice) and run:

phantomjs run.js

You can then POST your JSON-options to it (the JSON-parser is strict, so you might want to run your JSON through JSONLint if it fails):

Show code
{
    "chart": {
        "type": "line"
    },
    "title": null,
    "xAxis": {
        "categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
    },
    "yAxis": {
        "title": {
            "text": "Temperature (°C)"
        }
    },
    "series": [{
        "name": "Tokyo",
        "data": [7.0, 6.9, 9.5, 14.5, 18.2, 21.5,
                 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
    }, {
        "name": "New York",
        "data": [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0,
                 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]
    }, {
        "name": "Berlin",
        "data": [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0,
                 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]
    }, {
        "name": "London",
        "data": [3.9, 4.2, 5.7, 8.5, 11.9, 15.2,
                 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]
    }]
}

Will give you back this image:
Output of Highcharts renderer

To see that it actually works, you can perform a simple request against the server once it is up and running:

Show code
curl \
-H "Content-Type: application/json" \
-X POST \
-d '{"series":[{"data":[7.0,6.9,9.5]}]}' \
http://some.host:11942  > /var/www/simple-chart.png

If you want to try it out yourself, get the code on Github.

Developer at VG with a passion for Node.js, React, PHP and the web platform as a whole. espen.codes - @rexxars


10 comments

  • Jeff Frederich

    Hello,

    This is a great post regarding working with Highcharts and Phantom.js. Using Phantom.js you are rendering charts in a headless browser, not on the server. This does provide a similar outcome, but you do get some performance penalties. You might want to checkout ZingChart (full disclosure I am on the team). We do offer a node.js build of our library which pulls from the same code base and can render html5 charts directly on servers. We also have a Phantom.js build we include in our free branded trial and a Rhino build for the Java users.


    • Espen Hovlandsdal

      I guess you could argue it's not really "on the server", but it's really just a matter of definition. The end result is the same: the chart can be rendered to an image file, on the server, without any user-interaction. Thanks for providing an alternative :-)

  • Anil Gautam

    Hi I need to know if I have some custom functions to create the input data. How can I use them?


    • Espen Hovlandsdal

      I'm not sure I understand your question...

      This is simply a webservice - the input data can be built by anything you want, be it PHP, Javascript, Python, Java... Anything that can produce JSON.

      Simply run your logic to generate the input data, then get the entire JSON-blob you would normally pass to the highcharts constructor and send it as a POST-request to the webservice.

  • Stefan

    Hi Espen,
    thanks for such an amazing script. I t works quite well.
    But I've got one serious question. If i create an image serverside i pass a valid JSON array (like the above one) to the curl execution. But how about if the data in the JSON array are javascript, the script return that its not valid JSON.

    Like the PieChart, here i need to do some pre calculation and just pass the js variables to the options.

    Maybee you have an solution for that.

    Thanks Stefan


    • Espen Hovlandsdal

      Good question. It sort of depends on what you are trying to achieve. If you can, I would try and do the calculations first and send raw JSON. This is the safe option.

      I realize that for some use cases, such as specifying formatters and similar, it would be nice to send actual JS functions instead. I've updated the Github repository now with a new version, which introduces an "allowUnsafeEvaluation"-option in the configuration file. If you enable this, you can send a string of Javascript instead of JSON. An example:


      curl \
      -H "Content-Type: application/json" \
      -X POST \
      -d '{"series":[{"data":[7.0,6.9,9.5]}],"yAxis":{"labels":{"formatter": function() { return "wat"; }}}}' \
      http://some.host:11942 > /var/www/chart.png


      Would set all labels on the yAxis to "wat".

      Does that solve your problem?

  • tio00

    Hi Espen!

    Thank you for your fantastic work! Especially the final option "allowUnsafeEvaluation" saved me with regard to some label formatting issues...

    I've set up "phantomjs run.js" to run as a service under ubuntu, so that I'm always sure its available and starts at boot, or can turn it off and on at will through the service command.

    I use this for batch plotting from a php function using php "exec" and your curl command, like so:

    $exec_cmd = '/usr/bin/curl -H "Content-Type: application/json" -X POST -d \''.$hcData2.'\' http://localhost:11942 > /var/www/htdocs/tst/images/'.$pFilename.'.png';
    exec($exec_cmd);

    Have you used this from php yourself and if so, how do you call it?

    Now that everything works OK, I need to optimize my implementation, since I'll be generateing about 12000 plots pr. night and I was wondering if you had a method with better performance?

    Again, thanks for your hard work!


    • Espen Hovlandsdal

      Hello there - glad you found it useful.

      I guess the first thing you could do to optimize the speed is to perform multiple renders in parallel. This can be done either by forking multiple processes on the command line without waiting for a reply.

      Another option is to do it through PHP. I put together a quick sample where I combine the parallel requests trick with the renderer. See the sample on Github.

      Maybe that helps you on your way?

      If you just want to do one request from PHP, you can also just use file_get_contents, like the example in this gist.

  • rishi

    Hi , i am facing one problem related to highchart export .
    1- i set up my local export server using java & phantomjs , it is working fine .But when i giving request from client side to the local export server for (PNG or JPEG or PDF) it automatically saves in local system directory , instead of showing pop up to user for saving or opening .
    2- where local export server creates the image in local system .
    Any help is appreciated.


  • Nikhil Gopinath

    Thank you Espen for the super helpful post and scripts. This saved me a lot of work in setting up a highcharts export server.


Leave your comment