Chartbreaker
    Preparing search index...
    • Add the canvas module (~3.1.0) to your dependency list in package.json

    To use charts in your server, you can import the bundleName.js file just like any other module. The API is exactly the same as in the browser, with the exception of a few small differences:

    • Redraws must be manually requested.
    • The canvas cannot be resized after the first rendering pass
    • Different output formats may be selected by passing a "_backend" object to the Controller's constructor
    • The value of the "id" option passed to the Controller's constructor is completely arbitrary
    • Controller or Sparkline needs to be passed a createCanvas function in the ControllerOptions-constructor parameters.

    Currently, the library supports 3 different formats: PNG, SVG, and PDF. Both SVG and PDF generate true 100% scalable vector images. By default, Chartbreaker will generate PNG images.

    import { Controller } from '@stock3/charting-bundle-chartbreaker';
    import { createCanvas } from 'canvas'; // https://www.npmjs.com/package/canvas

    const chart = new Controller({
    id: 'chart',
    createCanvas,
    _backend: { // optional
    format: 'pdf',
    }
    });

    Similar to in the browser, the language data must be made available to Chartbreaker by using the setLanguageData method.

    import { setLanguageData } from '@stock3/charting-bundle-chartbreaker';
    import de from '@stock3/charting-bundle-chartbreaker/de.js';

    setLanguageData('de', de);

    By default, the contents of a chart are updated automatically by the library in the next tick following any changes. In a server environment, it can make sense to disable this behaviour by calling:

    controller.update(false);
    

    To manually trigger an update afterwards, you can then call:

    controller.update();
    

    In the browser, the chart is automatically rendered to the canvas when an update happens, and you can not manually redraw it. In a NodeJS environment, however, the ChartSet object exposes an additional method render. Whenever you want to render the chart, you will have to manually call this method.

    The Image data can be accessed via the Canvas object exposed by ChartSet::getCanvas, either as a Buffer, or as a Stream.

    Once a Controller is no longer needed, make sure to call destroy; this will release the rendering context, unsubscribe from push data, etc.

    To obtain a stream, you can use the createPNGStream method on the Canvas object. Asynchronous data access via Streams is only available when rendering PNGs; for PDF or SVG you have to use the synchronous Buffers.

    const out = fs.createWriteStream('chart.png');
    const data = controller.getChartSet().getCanvas().createPNGStream();

    data.pipe(out);
    data.once('end', function() {
    // destroy the chart once rendering is finished
    controller.destroy();

    // wait until data is flushed to file then exit
    out.once('finish', function(){
    process.exit(0);
    });
    });
    const data = controller.getChartSet().getCanvas().toBuffer();
    fs.writeFileSync("chart.png", data);
    controller.destroy();
    import { Controller, setLanguageData, setClazz, Random } from '@stock3/charting-bundle-chartbreaker';
    import bundleDe from '@stock3/charting-bundle-chartbreaker/de.js'
    import fs from 'node:fs';
    import { createCanvas } from 'canvas'; // https://www.npmjs.com/package/canvas

    setLanguageData('de', bundleDe);

    // reguster random loader
    setClazz(plugins.Random);

    // create a new controller
    const controller = new Controller({
    id: 'chart',
    createCanvas,
    _backend: {
    format: 'image' // 'pdf' and 'svg' are also possible
    }
    });

    // disable automatic updating - chart will not update content until update() is called manually
    controller.update(false);

    // add a timeseries using the previously registered loader
    controller.addTimeSeries("Random()");

    /*
    * subscribe to `loaded` event which is triggered once all data has been loaded and chart is "ready"
    */
    controller.once("loaded", function() {
    const chartSet = controller.getChartSet();

    /*
    * rendering needs to be requested manually in the node.js environment
    */
    chartSet.render();

    /*
    * when using the image backend, `createPNGStream` will provide a stream object
    * alternatively, you can use `toBuffer` (only option for pdf and svg)
    */
    const out = fs.createWriteStream('chart.png');
    const data = chartSet.getCanvas().createPNGStream();
    data.pipe(out);
    data.once('end', function() {
    // destroy the chart once rendering is finished
    controller.destroy();

    // wait until data is flushed to file then exit
    out.once('finish', function(){
    process.exit(0);
    });
    });

    });

    // update chart
    controller.update();

    Error Handling

    Because the library uses asynchronous data loading, you can not handle all errors with a simple try/catch block. Errors could occur in various places and at arbitrary times - e.g. in a custom loader after a fetch request. However, almost all of these places can be traced back to a callback listening for an event on some object. The custom EventEmitter implementation used in the library therefore allows you to provide your own error handler which will receive all errors occuring in any listener callback.

    import { EventEmitter } from '@stock3/charting-bundle-chartbreaker';

    EventEmitter.guard(function(error) {
    console.log("An error occured:", error);
    });
    obj.on("event", function() {
    throw "x"; // will be forwarded to function above
    });

    Changing the error handler has no effect on previously registered listeners. They will still forward errors to the handler that was set when they were first registered.

    EventEmitter.guard(function handlerA(error) {
    console.log("Error handled by A");
    });
    object.on("eventA", function() {
    throw "x";
    });
    EventEmitter.guard(function handlerB(error) {
    console.log("Error handled by B");
    });
    obj.on("eventB", function() {
    throw "x";
    });
    obj.emit("eventB"); // prints "Error handled by B"
    obj.emit("eventA"); // prints "Error handled by A"

    Depending on the type of your server, you might also require some context (e.g. an http request) to process an error. This can be achieved by creating individual error handlers for each context. All functions calling any library methods should then always reapply the correct error handler exactly once at the beginning, before using Chartbreaker.

    For example, a server that calls processRequest for every http request could implement the following:

    function processRequest(req) {
    let failed = false;
    function guard(request) {
    EventEmitter.guard(function(error) {
    failed = true;
    request.writeHead(500);
    request.end("An error occured");
    });
    }

    // apply error handling before creating chart
    guard(req);

    const controller = new Controller(...);

    // ... configure chart

    controller.on("example", function() {
    // another request might have started in the meantime
    // => we need to reapply the correct context
    // if we intend to do any additional asynchronous operations
    guard(req);

    // ... do something else
    });

    controller.on("loaded", function() {
    if(failed) return; // do nothing if we already responded with an error code

    controller.getChartSet().render();

    const data = chartSet.getCanvas().createPNGStream();
    data.pipe(req);
    data.once('end', function() {
    // destroy the chart once rendering is finished
    chartSet.destroy();
    });
    })
    };