具有多个chart.js图表​​的页面为pdf

时间:2017-03-31 12:16:43

标签: javascript pdf charts chart.js

我使用chart.js生成一个包含多个图表的报表页面。我需要将此报告导出为PDF。通过搜索有许多解决方案,但我找不到具有多个canvas元素的解决方案。

唯一可用的解决方案似乎是遍历所有图像,然后使用图像重新创建报告,然后将其作为pdf下载。

有没有更简单/更有效的方法来实现这一目标?

<body>
<h1> Chart 1 </h1>
<div style="width:800px; height:400px;">
<canvas id="chart_1" width="50" height="50"></canvas>
</div>

<h1> Chart 2 </h1>
<div style="width:800px; height:400px;">
<canvas id="chart_2" width="50" height="50"></canvas>
</div>

<h1> Chart 3 </h1>
<div style="width:800px; height:400px;">
<canvas id="chart_3" width="50" height="50"></canvas>
</div>
</body>

2 个答案:

答案 0 :(得分:9)

老实说,似乎最简单的方法就是提供一个“下载到PDF”链接,弹出浏览器的打印页面并指示用户选择“打印为pdf”。

如果这种方法对您(或您的用户)不起作用,那么这是一种粗略的方法。

基本上,我们会创建一个与报告页面大小相同的新canvas元素,并将现有chart.js canvas图表中的像素逐渐绘制到新的canvas中。完成后,您可以使用jsPDF将新画布作为图像添加到pdf文档中并下载文件。

这是一个实现此功能的示例实现。

$('#downloadPdf').click(function(event) {
  // get size of report page
  var reportPageHeight = $('#reportPage').innerHeight();
  var reportPageWidth = $('#reportPage').innerWidth();

  // create a new canvas object that we will populate with all other canvas objects
  var pdfCanvas = $('<canvas />').attr({
    id: "canvaspdf",
    width: reportPageWidth,
    height: reportPageHeight
  });

  // keep track canvas position
  var pdfctx = $(pdfCanvas)[0].getContext('2d');
  var pdfctxX = 0;
  var pdfctxY = 0;
  var buffer = 100;

  // for each chart.js chart
  $("canvas").each(function(index) {
    // get the chart height/width
    var canvasHeight = $(this).innerHeight();
    var canvasWidth = $(this).innerWidth();

    // draw the chart into the new canvas
    pdfctx.drawImage($(this)[0], pdfctxX, pdfctxY, canvasWidth, canvasHeight);
    pdfctxX += canvasWidth + buffer;

    // our report page is in a grid pattern so replicate that in the new canvas
    if (index % 2 === 1) {
      pdfctxX = 0;
      pdfctxY += canvasHeight + buffer;
    }
  });

  // create new pdf and add our new canvas as an image
  var pdf = new jsPDF('l', 'pt', [reportPageWidth, reportPageHeight]);
  pdf.addImage($(pdfCanvas)[0], 'PNG', 0, 0);

  // download the pdf
  pdf.save('filename.pdf');
});

您可以在此codepen看到它的实际效果。

现在让我们谈谈这种方法的一些问题。首先,您必须控制新canvas对象中每个chart.js canvas的位置。唯一的方法是了解报表页面的结构并实现相同的结构。在我的例子中,我的图表是2x2网格,逻辑处理相应的。如果您有3x2网格或不同的网格,则必须更改定位逻辑。

最后,最终的pdf输出文件尺寸远大于原始图表页面(来自网络)。我认为原因是因为我的图表“容器”div延伸到整个页面。因此,您可能希望使用其他方法来设置新canvas的大小。

长话短说,上面的例子是为了展示一种方法而不是你的最终解决方案。

祝你好运!

答案 1 :(得分:0)

我在 vanilla javascript 中有一个可行的解决方案(尽管我使用了 ts 类型)并使用了 lib jsPdf,其中每个 pdf 页面都需要一个绘图:

let index = 1;
// create new pdf object
// if don't choose compress as true you will end up with a large pdf file
let pdf = new jsPDF({
  orientation: 'landscape',
  unit: 'px',
  format: 'a4',
  compress: true,
})
// search for the html element(s) you need
const canvas = document.querySelectorAll("canvas");
// here my size are in pixels since I configured that in the obj instance
let pageWidth = 400; 
let pageHeight = 400;
let index = 1;
// traverse the array of canvas 
canvas.forEach( (canva:HTMLCanvasElement) => {

  // I added some options among others I added the type of the compression
  // method: FAST
  pdf.addImage(canva, 'PNG', 10, 10, pageWidth, pageHeight, `img${index}`, "FAST");
  // so as to not end up with an extra pdf page at the end of the iteration
  if (index < canvas.length) {
    pdf.addPage();
  }
  index++;
});

// download the pdf
pdf.save('Reporte.pdf');