3

I've been working for a few weeks now on a Discord bot that basically compiles stats on the server and deduces patterns. In order to improve it, I wanted to make it generate graphs as PNGs in order to send them back to the user - in short, no DOM. In order to achieve this, I'm currenlty using vega (version 5.10.1 - latest) and node-canvas (version 2.6.1 - latest), with nodejs v12.16.1.

I've been scouring the web for help on vega usage, and found a couple contradicting sources. I've been using the example code provided here : https://vega.github.io/vega/usage/

The thing is that I keep getting this error :

TypeError: Cannot read property 'getContext' of null
message:"Cannot read property 'getContext' of null"
stack:"TypeError: Cannot read property 'getContext' of null
    at resize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3665:28)
    at CanvasRenderer.prototype$6.resize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3714:5)
    at CanvasRenderer.prototype$4.initialize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3294:17)
    at CanvasRenderer.prototype$6.initialize (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-scenegraph\build\vega-scenegraph.js:3709:28)
    at initializeRenderer (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-view\build\vega-view.js:657:8)
    at renderHeadless (e:\DEV\GIT REPOS\GITHUB\PERSO\JS\test-StatBot\node_modules\vega-view\build\vega-view.js:780:12)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async View.renderToCanvas [as toCanvas] (e:\DEV\GIT REPOS\GITHUB\P...

Here is the code which is giving me trouble :

// Imports
const vega = require('vega');


// Render image from given graph spec (statsObject)
async function graphToImage (statsObject) {

    graphObject = new vega.View(vega.parse(statsObject), { renderer: 'none'});

    const pngName = generateFileName(10);

    removeExistingFile(pngName);

    graphObject.toCanvas().then(canvas => {

        console.log('Writing PNG to file...');
        writeFile(`../../../../generated/${pngName}.png`, canvas.toBuffer());

    }).catch(err => {

        console.log("Error writing PNG to file:");
        console.error(err);

    });


    return pngName;
}

I don't really know how canvas or vega work, and so I have no idea what could be causing this issue and how to fix it... However, the problem seems to be located inside of the toCanvas() method. Any help is much appreciated !

Thanks in advance !

Ruben N
  • 43
  • 8

2 Answers2

4
// Using view.toSVG() along with the npm package sharp worked well for me 


const view = new vega.View(vega.parse(templateObject), {renderer: 'none'});

view.toSVG().then(async function (svg) {

    await sharp(Buffer.from(svg))
        .toFormat('png')
        .toFile('fileName.png')

}).catch(function(err) {
    console.error(err);
});
  • Thanks for the answer ! That's what I ended up going with too, but your code is way cleaner than mine... – Ruben N Jun 15 '20 at 06:00
1

Edit : I managed to fix my issue, and I am posting the anwser here for future notice :

I succeeded to actually generate a graph picture by rendering the View object straight to an SVG string, by using view.toSVG() instead of the buggy view.toCanvas(), which worked great.

Then, all that was left to do was to convert the obtained SVG string into a PNG file, and that was it.

Here is the updated, working code :

// Imports
const vega = require('vega');


// Render image from given graph object
async function graphToImage (statsObject) {

    // Generate a new 10-char hex string
    const pngName = generateHexStringName(10);


    // Remove any existing file with the same name in order to prevent confusion
    removeExistingFile(pngName);


    var view = new vega.View(vega.parse(statsObject), {renderer: 'none'});


    // Generate an SVG string
    view.toSVG().then(async function (svg) {

        // Working SVG string
        console.log(svg);

        // Process obtained SVG string, e. g. write it to PNG file


    }).catch(function(err) {

        console.error(err);

    });


    // Return the name of the generated PNG file
    return pngName;
}
Ruben N
  • 43
  • 8