Server-Side HighStock charts generation with NodeJS

Problem

I'm using HighStock to generate some charts in browser. But now I want to store some of them at the server. So I know that HighCharts can be exported to the server but I'd rather use some other way if possible. The thing is to run HighStock at the server and convert the svg to some image format and then store it there.

Quick googling gives me this page. Combining HighCharts and NodeJS seems to be the right way but this solution does not work for newer versions of HighCharts. More precisely, using jsdom module (v0.2.10 - the latest) in NodeJS with HighStock v1.0.2 (look at the following code):

var jsdom = require('jsdom');
var fs = require('fs');
var jQuery = fs.readFileSync('./js/jquery-1.7.min.js').toString();
var Highstock = fs.readFileSync('./js/highstock.js').toString();
jsdom.env({
    html: '<div id="container"></div>',
    src: [ jQuery, Highstock ],
    done: function(errors, window) {
        if (errors) return console.log(errors);
        var Highcharts = window.Highcharts;
        var chart = new Highcharts.Chart(options);
    }
});

throws an exception:

Error: Invalid character: Invalid character in tag name: <

Somehow these two libraries does not seem to work together. So this may work with older versions of HighStock but actually I need HighStock v1.0.2.

Is there any solution for this problem? Some better library that jsdom? Or some strange yet working tricks? Any idea appreciated. :)

// EDIT

Thanks to ReCoder I've managed to get it working. Main thing was to add forExport flag to the options (preventing exceptions). Unfortunetly this generated the chart but did not update the holder. I've managed to get it working after adding exporting module (part of HighStock package). The full working code looks like this:

var jsdom = require('jsdom');
var fs = require('fs');
var jQuery = fs.readFileSync('./js/jquery-1.7.min.js').toString();
var Highstock = fs.readFileSync('./js/highstock.src.js').toString();
var Exporting = fs.readFileSync('./js/exporting.src.js').toString();

jsdom.env({
    html: '<div id="container"></div>',
    src: [ jQuery, Highstock, Exporting ],
    done: function(errors, window) {
        if (errors) return console.log(errors);
        var Highcharts = window.Highcharts;
        var chart = new Highcharts.Chart({
              chart: {
                 renderTo: 'container',
                 animation: false,
                 forExport: true       
              },
              exporting: { enabled: false },
              xAxis: {
                 categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
                    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
              },
              series: [{
                 animation: false,
                 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]
              }, {
                 animation: false,      
                 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]
              }, {
                 animation: false,      
                 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]
              }, {
                 animation: false,      
                 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]
              }]
        });

        var svg = chart.getSVG();

        fs.writeFile("./test.svg", svg, function(err) {
            if(err) {
                console.log(err);
            } else {
                console.log("The file was saved!");
            }            
        }); 
    }
});

The chart is not as good as it should be (for example labels badly placed), but maybe it's a matter of tuning options. At least it works!

Problem courtesy of: freakish

Solution

That can be fixed by raising the "forExport" flag in options object:

options.chart.forExport = true;
Solution courtesy of: ReCoder

Discussion

I replaced node.js with phantomJs which did a good job, as described in Generating HTML Canvas image data server-side? (example is from jQplot, but works for highchart as well.)

Discussion courtesy of: FtLie

I tried to recreate your example and I get jQuery and Highcharts loaded with jsdom. It looks like Highcharts tries to call createElement with an html fragment:

createElement('<div filled="f" stroked="f" style="position: absolute;left:0;top:0;width:10px;height:10px;"/>')

This looks fishy. I'm not sure how it works in the browser - I can't call createElement with that in my browser either (Chromium).

Discussion courtesy of: Linus Gustav Larsson Thiel

This recipe can be found in it's original form on Stack Over Flow.