1

I have an app (using Vue) that renders a number of different graphs on different pages. I am adding a PDF download feature that allows on to download all of the graphs in a single PDF. For Pdf generation I am using JsPDF.

Currently all the charts are being rendered on the client side, when the page containing the graph is opened. Within Chart.js you can save the base64 encoded string data of any chart. I am trying to figure out how to get this base64 string data without actually rendering the graph. For chart.js to create a new graph, you need to provide it with a context, an html element. When I set this element's css display property to none, the base64 string data is nothing. When I try turning the visibility of the element to hidden, I see a massive mount of blank space show up beneath the download pdf button where the graph element exists.

Is it possible to "draw" the chart.js graph (for the purpose of the getting the base64 string) without it rendering or showing up at all on my screen? Or is this a wrong approach to turning a Chart.js graph into a PDF?

Any help is appreciated.

Brian
  • 385
  • 1
  • 5
  • 23
  • 1
    You could try rendering it at coordinates _way_ outside the bounds of your screen; that way, it would keep its relative size for the `base64` string, but be rendered in a place no human could ever see it. Or potentially at a different `z-index`, behind all other on-screen elements. – Tim Lewis Jan 25 '21 at 17:41
  • 1
    @TimLewis I haven't tried this, but it's a good idea. Let me see if it works. – Brian Jan 25 '21 at 17:43
  • 1
    I only know of this trick from "honey-potting"; rendering human-non-clickable elements that if clicked by a bot (since viewports are meaningless to a bot) trigger anti-spam measures :) But the same logic applies; if the human can't see it, it doesn't mean it hasn't been rendered. – Tim Lewis Jan 25 '21 at 17:45
  • Might be able to use the of screen canvas API to do it – LeeLenalee Jan 25 '21 at 18:02
  • 1
    I tried adding this to the css. position: absolute; left: -100000px; It does render off screen, but the base64 is either just a few pixels or massively distorted. All I see on the pdf is a squiggly gray square. – Brian Jan 25 '21 at 18:02
  • @LeeLenalee I will look into this. Thanks. – Brian Jan 25 '21 at 18:03

1 Answers1

1

I found a similair thread to this, only change that was needed to his example was telling chartjs to not animate the chart. Then you can render it out of the screen in a div. You will have to make the div big enough so your chart is also large enough that you can use it.

Original thread: Render Chartjs on hidden DIV

var canvas = document.getElementById('myChart');
var ctx = canvas.getContext('2d');
var chart = new Chart(ctx, {
    // The type of chart we want to create
    type: 'line',
    // The data for our dataset
    data: {
      labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
      datasets: [{
            label: 'My First dataset',
            backgroundColor: 'rgb(255, 99, 132)',
            borderColor: 'rgb(255, 99, 132)',
            data: [0, 10, 5, 2, 20, 30, 45]
          }]
        },

        options: {
        animation: {
        duration: 0
        }
        }
      });

for (var id in Chart.instances) {
    Chart.instances[id].resize();
    Chart.instances[id].draw('1s');
    Chart.instances[id].render('1s');
    console.log(Chart.instances[id].toBase64Image());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js" integrity="sha512-hZf9Qhp3rlDJBvAKvmiG+goaaKRZA6LKUO35oK6EsM0/kjPK32Yw7URqrq3Q+Nvbbt8Usss+IekL7CRn83dYmw==" crossorigin="anonymous"></script>

<div style="position:absolute;left:-9999px;top:-9999px">
  <canvas id="myChart"></canvas>
</div>
LeeLenalee
  • 27,463
  • 6
  • 45
  • 69