const { createCanvas } = require("canvas"); const { Canvas, FontLibrary } = require("skia-canvas"); const Chart = require("chart.js/auto"); const { backgroundColors, borderColors } = require("./canvas-colors"); const { defaultsDeep, isNumber } = require("lodash"); try { FontLibrary.use("Montserrat", [ "/usr/share/fonts/Montserrat-Regular.ttf", "/usr/share/fonts/Montserrat-Bold.ttf", "/usr/share/fonts/Montserrat-Italic.ttf" ]); } catch (error) { console.error( "Error loading fonts Skia Canvas Fonts, please be sure to install Montserrat font package", error.message ); } // Utility to create a chart configuration const getChartConfiguration = (keys, values, override) => { const defaultConfiguration = { type: "doughnut", data: { labels: keys, datasets: [ { data: values, backgroundColor: backgroundColors, borderColor: borderColors, borderWidth: 1 } ] }, options: { devicePixelRatio: 4, responsive: false, animation: false, maintainAspectRatio: true, circumference: 180, rotation: -90, plugins: { legend: { labels: { boxWidth: 20, font: { family: "'Montserrat'", size: 10, style: "normal", weight: "normal" } }, position: "left" } } } }; return defaultsDeep(override || {}, defaultConfiguration); }; exports.canvastest = function (req, res) { res.status(200).send("OK"); }; exports.canvas = function (req, res) { const { logger } = req; const { w, h, values, keys, override } = req.body; logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override }); // Set the default Width and Height let [width, height] = [500, 275]; // Allow for custom width and height if (isNumber(w)) { width = w; } if (isNumber(h)) { height = h; } const configuration = getChartConfiguration(keys, values, override); res.status(200).send( (() => { const canvas = createCanvas(width, height); const ctx = canvas.getContext("2d"); new Chart(ctx, configuration); return canvas.toDataURL(); })() ); }; exports.canvasSkia = async function (req, res) { const { logger } = req; const { w, h, values, keys, override } = req.body; // Log incoming request for debugging logger.log("inbound-canvas-creation", "debug", "jsr", null, { w, h, values, keys, override }); // Default width and height const width = typeof w === "number" && w > 0 ? w : 500; const height = typeof h === "number" && h > 0 ? h : 275; const configuration = getChartConfiguration(keys, values, override); try { // Create a canvas and get the 2D rendering context const canvas = new Canvas(width, height); const ctx = canvas.getContext("2d"); // Render the chart new Chart(ctx, configuration); // Convert the canvas to a Base64-encoded image const chartImage = (await canvas.toBuffer("image/png")).toString("base64"); const dataURL = `data:image/png;base64,${chartImage}`; // Send the Base64-encoded image as the response res.status(200).send(dataURL); } catch (error) { // Log and handle rendering errors logger.log("canvas-error", "error", "jsr", null, { error: error.message }); res.status(500).send("Failed to generate canvas."); } };