9
votes

I'm trying to render a report via PhantomJS 2.1.1 where the HTML page contains a chart generated by Chart.js. I have full control over that page. The resulting PDF should be a printable A4. As you can see in the screenshot down below, the chart is very blurry.

Is there any way I can make either Chart.js or PhantomJS render the chart/page at a higher DPI so that the drawn canvas appears nice and sharp?

PhantomJS:

page.property('paperSize', {
  format: 'A4',
  orientation: 'portrait',
  border: '2cm'
});

Chart.js:

var lineChart = new Chart(ctx).Line(data, {
  animation: false,
  responsive: true,
  pointDot: false,
  scaleShowLabels: true,
  showScale: true,
  showTooltips: false,
  bezierCurve : false,
  scaleShowVerticalLines: false
});

blurrychart

5
What is the height and widh of canvas element? - Rahul R.
@RahulR. I've set it to 100% using CSS. I think Chart.js is figuring that out on its own because of responsive: true - dislick
did you found a solution for this issue? - aldo.roman.nurena
No solution yet. @aldo.roman.nurena - dislick
I found this github.com/ariya/phantomjs/pull/14085 It seems they solved it applying a DPI setting but it has not been published yet. It's on master branch, though - aldo.roman.nurena

5 Answers

8
votes

Add viewportSize and zoomFactor in your phantomjs page:

await page.property('viewportSize', { height: 1600, width: 3600 });
await page.property('zoomFactor', 4);

and add in your html head template

<script>
  window.devicePixelRatio = 4;
</script>
2
votes

Try setting the zoom factor using a higher DPI for paper in relation to screen DPI:

page.zoomFactor = 300 / 96;   // or use / 72

Must be set after page size is defined.

You could also check out this answer:
Poor quality of png images drawn into html canvas

2
votes

For Phantom 2, I rendered the charts with a large canvas to get the resolution up and then converted it into a png finally destroying and removing the old canvas and replacing them with an image with responsive CSS classes. Adjusting the knobes on the canvas width and height in addition to Chart.js options will get you a perfect render. We were able to get our rendering speed up with the approach (alternative to SVG renders) and the file size down.

HTML:

<div class="container">
  <!-- generated images -->
  <img id="someIdImage" class="img-responsive"></img>

  <!-- temporary canvas -->
  <canvas id="someId" width="2000" height="600"></canvas>    
</div>

Javascript:

/**
 * _plot() plot with Chart.js
 *
 * @param {Function} callback
 */
 function _plot(callback) {
  var config = {}; // some Chart.js config
  var id = 'someId';
  var el = document.querySelector('#' + id);
  var el2d = el.getContext('2d');

  // plot instance
  var instance = new Chart(el2d, config);

  // generate and append image
  document.querySelector('#' + id + 'Image').setAttribute('src', el.toDataURL('image/png'));

  // destroy instance
  instance.destroy();
  el.parentElement.removeChild(el);

  // callback
  if (callback) {
    callback();
  }
}
0
votes

Chart.js now has the parameter "devicePixelRatio". This allows you to increase the resolution directly in Chart.js. (normal 96dpi. target 300dpi; 300/96 = 3.125)

options:{
      devicePixelRatio: 3
}

Documentation: https://www.chartjs.org/docs/3.0.2/configuration/device-pixel-ratio.html

-1
votes
I can confirm that the @DevTrong response is working with Phantomjs 2.1.1

the only difference is that i set in my settings file:

    page.viewportSize = { width: 3600, height: 1600 };
    page.zoomFactor = 4;


Note: Its very important to set in you html this part:

<//script>
  window.devicePixelRatio = 4;
<//script> (fix the script tag)