import { Readable } from 'stream'; // @ts-expect-error moduleResolution:nodenext issue 54523 import { Chart as ChartJS, ChartConfiguration, ChartComponentLike } from 'chart.js/auto'; import { ChartJSNodeCanvasBase, MimeType, Canvas } from './chartJSNodeCanvasBase'; export class ChartJSNodeCanvas extends ChartJSNodeCanvasBase { /** * Render to a data url. * @see https://github.com/Automattic/node-canvas#canvastodataurl * * @param configuration The Chart JS configuration for the chart to render. * @param mimeType The image format, `image/png` or `image/jpeg`. */ public renderToDataURL(configuration: ChartConfiguration, mimeType: MimeType = 'image/png'): Promise { const chart = this.renderChart(configuration); return new Promise((resolve, reject) => { if (!chart.canvas) { return reject(new Error('Canvas is null')); } const canvas = chart.canvas as Canvas; canvas.toDataURL(mimeType, (error: Error | null, png: string) => { chart.destroy(); if (error) { return reject(error); } return resolve(png); }); }); } /** * Render to a data url synchronously. * @see https://github.com/Automattic/node-canvas#canvastodataurl * * @param configuration The Chart JS configuration for the chart to render. * @param mimeType The image format, `image/png` or `image/jpeg`. */ public renderToDataURLSync(configuration: ChartConfiguration, mimeType: MimeType = 'image/png'): string { const chart = this.renderChart(configuration); if (!chart.canvas) { throw new Error('Canvas is null'); } const canvas = chart.canvas as Canvas; const dataUrl = canvas.toDataURL(mimeType); // Use this to destroy any chart instances that are created. // This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js. // This must be called before the canvas is reused for a new chart. chart.destroy(); return dataUrl; } /** * Render to a buffer. * @see https://github.com/Automattic/node-canvas#canvastobuffer * * @param configuration The Chart JS configuration for the chart to render. * @param mimeType A string indicating the image format. Valid options are `image/png`, `image/jpeg` (if node-canvas was built with JPEG support) or `raw` (unencoded ARGB32 data in native-endian byte order, top-to-bottom). Defaults to `image/png` for image canvases, or the corresponding type for PDF or SVG canvas. */ public renderToBuffer(configuration: ChartConfiguration, mimeType: MimeType = 'image/png'): Promise { const chart = this.renderChart(configuration); return new Promise((resolve, reject) => { if (!chart.canvas) { throw new Error('Canvas is null'); } const canvas = chart.canvas as Canvas; canvas.toBuffer((error: Error | null, buffer: Buffer) => { chart.destroy(); if (error) { return reject(error); } return resolve(buffer); }, mimeType); }); } /** * Render to a buffer synchronously. * @see https://github.com/Automattic/node-canvas#canvastobuffer * * @param configuration The Chart JS configuration for the chart to render. * @param mimeType A string indicating the image format. Valid options are `image/png`, `image/jpeg` (if node-canvas was built with JPEG support), `raw` (unencoded ARGB32 data in native-endian byte order, top-to-bottom), `application/pdf` (for PDF canvases) and image/svg+xml (for SVG canvases). Defaults to `image/png` for image canvases, or the corresponding type for PDF or SVG canvas. */ public renderToBufferSync(configuration: ChartConfiguration, mimeType: MimeType | 'application/pdf' | 'image/svg+xml' = 'image/png'): Buffer { const chart = this.renderChart(configuration); if (!chart.canvas) { throw new Error('Canvas is null'); } const canvas = chart.canvas as Canvas; const buffer = canvas.toBuffer(mimeType); chart.destroy(); return buffer; } /** * Render to a stream. * @see https://github.com/Automattic/node-canvas#canvascreatepngstream * * @param configuration The Chart JS configuration for the chart to render. * @param mimeType A string indicating the image format. Valid options are `image/png`, `image/jpeg` (if node-canvas was built with JPEG support), `application/pdf` (for PDF canvases) and image/svg+xml (for SVG canvases). Defaults to `image/png` for image canvases, or the corresponding type for PDF or SVG canvas. */ public renderToStream(configuration: ChartConfiguration, mimeType: MimeType | 'application/pdf' = 'image/png'): Readable { const chart = this.renderChart(configuration); if (!chart.canvas) { throw new Error('Canvas is null'); } const canvas = chart.canvas as Canvas; setImmediate(() => chart.destroy()); switch (mimeType) { case 'image/png': return canvas.createPNGStream(); case 'image/jpeg': return canvas.createJPEGStream(); case 'application/pdf': return canvas.createPDFStream(); default: throw new Error(`Un-handled mimeType: ${mimeType}`); } } private renderChart(configuration: ChartConfiguration): ChartJS { const canvas = this._createCanvas(this._width, this._height, this._type); (canvas as any).style = (canvas as any).style || {}; configuration.options = configuration.options || {}; configuration.options.responsive = false; // Disable animation (otherwise charts will throw exceptions) configuration.options.animation = false; const context = canvas.getContext('2d'); (global as any).Image = this._image; // Some plugins use this API const chart = new this._chartJs((context as any), configuration); delete (global as any).Image; return chart; } }