Search code examples
node.jspdf-generationecharts

How to generate a PDF with eCharts content in NodeJS API


I have a NodeJS API and I need to create an endpoint which creates a richly-featured dynamic PDF report and saves it to an S3 bucket. I've been working with PDFKit (https://pdfkit.org/) and have most of what I need from there, but I've come up across a stumbling block because the latest design I've been given includes a donut chart generated with eCharts and I can't figure out how to include this using PDFKit. It needs to be generated dynamically for each call. There is no visible HTML page which displays the report, this must all be achieved within the NodeJS API.

Are there any pre-existing solutions I'm overlooking that would make this possible?

NB I have some flexibility so it doesn't have to be specifically an eCharts solution, as long as I can include a stacked donut chart with labels.

Thanks.


Solution

  • This is totally doable. I've put together an example that adds a chart to the PDFKit document.

    const echarts = require("echarts");
    const Canvas = require("canvas-prebuilt");
    const { JSDOM } = require("jsdom");
    const fs = require("fs");
    const PDFDocument = require("pdfkit");
    const SVGtoPDF = require("svg-to-pdfkit");
    
    const option = {
      tooltip: {
        trigger: "item"
      },
      legend: {
        top: "5%",
        left: "center"
      },
      series: [
        {
          name: "Pie Chart",
          type: "pie",
          radius: ["40%", "70%"],
          avoidLabelOverlap: false,
          itemStyle: {
            borderRadius: 10,
            borderColor: "#fff",
            borderWidth: 2
          },
          label: {
            show: false,
            position: "center"
          },
          emphasis: {
            label: {
              show: true,
              fontSize: "40",
              fontWeight: "bold"
            }
          },
          labelLine: {
            show: false
          },
          data: [
            { value: 1048, name: "A" },
            { value: 735, name: "B" },
            { value: 580, name: "C" },
            { value: 484, name: "D" },
            { value: 300, name: "E" }
          ]
        }
      ]
    };
    
    const getChartSVG = (option) => {
      echarts.setCanvasCreator(() => {
        return Canvas.createCanvas(100, 100);
      });
    
      const { window } = new JSDOM();
      global.window = window;
      global.navigator = window.navigator;
      global.document = window.document;
    
      const root = document.createElement("div");
      root.style.cssText = "width: 500px; height: 500px;";
    
      const chart = echarts.init(root, null, {
        renderer: "svg"
      });
    
      chart.setOption(option);
    
      const chartSvg = root.querySelector("svg").outerHTML;
      chart.dispose();
    
      return chartSvg;
    };
    
    const makePDF = (svg) => {
      const doc = new PDFDocument();
      const stream = fs.createWriteStream("file.pdf");
    
      SVGtoPDF(doc, svg, 0, 0);
    
      stream.on("finish", function() {
        console.log("Done");
      });
    
      doc.pipe(stream);
      doc.end();
    };
    
    const run = () => {
      const svg = getChartSVG(option);
      makePDF(svg);
    };
    
    run();
    

    Here svg-to-pdfkit library plays an important role of adding the svg content to the pdf document or else it would have been quite difficult to implement this.

    Find the code repository here: https://github.com/muraliprajapati/echarts-node-pdf