Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas charts is blurry when download as PDF

2nd Update:

Thanks again for everyone who commented/answered my question

Problem solved: I am not able to make canvas image looks better on the generated PDF and the due date of the project is closing, finally, I decided to move the "PDF Report" feature from Front-End(Angular) to Back-End(python), I have posted an answer(not really an answer for my own question, but more like a solution)

Update after a couple of days search:

I have replaced Chart.js with Echarts but the blurry issue still exists. I did a lot of search on jsPDF and Canvas blurry online, it seems like the canvas settings need to be customized, unfortunately, I have no clue how to achieve it.

How looks in broswer: enter image description here

When download as PDF: enter image description here

Original Question:

I have an Angular project which is required to download PDF for graph data. I choose Chart.js and jspdf which works well. When I print 2 charts in one page, the resolution is okay but when I try to print 3 charts on the same page, the texts are blurry. I have read this similar question but not figured out how to make resolution better when having multiple charts.

HTML Code:

  <div class="col-md-4">
    <div class="card card-chart">
      <div class="card-header">
        <h5 class="card-title">Cervical Lateral Flexion</h5>
        <!--<p class="card-category">Line Chart</p>-->
      </div>
      <div class="card-body">
        <canvas id="cervicalLateralFlexion"></canvas>
      </div>
    </div>
  </div>

Typescript Code:

  public static buildChart(doc, selector, title, yHeight) {
    let height = yHeight;
    const canvas = document.querySelector(selector) as HTMLCanvasElement;
    doc.setFontSize(h5);
    doc.text(leftMargin, yHeight, title);
    const canvasImage = canvas.toDataURL('image/png');
    height += margin;
    doc.addImage(canvasImage, 'PNG', leftMargin, height);
  }

Can anyone help? How to solve this issue? Thanks in advance! Image 1 is how it looks on web page and image 2 is PDF file.

enter image description here

enter image description here

like image 835
Haifeng Zhang Avatar asked Nov 12 '18 05:11

Haifeng Zhang


3 Answers

You need to set the height and width in addImage

doc.addImage(canvasImage, 'PNG', leftMargin, height, "IMAGE-WIDTH-HERE", "IMAGE-HEIGHT-HERE");

If you don't know the image dimensions try something like this.

function getImageDimensions(file) {
  return new Promise (function (resolved, rejected) {
    var i = new Image()
    i.onload = function(){
      resolved({w: i.width, h: i.height})
    };
    i.src = file
  })
}

public static buildChart(doc, selector, title, yHeight) {
    let height = yHeight;
    const canvas = document.querySelector(selector) as HTMLCanvasElement;
    doc.setFontSize(h5);
    doc.text(leftMargin, yHeight, title);

    const canvasImage = canvas.toDataURL('image/png, 1.0');

    let dimensions = await getImageDimensions(canvasImage);

    height += margin;
    doc.addImage(canvasImage, 'PNG', leftMargin, height, dimensions.w, dimensions.h);
  }

getImageDimensions function copied from here, this function uses Promises since we need synchronous behavior in your scenario.

like image 76
kiranvj Avatar answered Oct 07 '22 07:10

kiranvj


This is a workaround, not a direct solution to the jsPDF issues described. I was faced with the exact same issue: jsPDF, blurriness for charts and angular.

Hopefully, I'm not straying too far off topic but I thought this would be valuable for some.

The workaround: node + puppeteer

puppeteer is a headless chrome browser that works under any environment (even linux terminal) and can render a web page into an image or PDF.

    import * as puppeteer from "puppeteer";

    const browser = await puppeteer.launch({});
    const page = await browser.newPage();

    await page.goto("https://google.com", { waitUntil: 'networkidle2' });
    await page.setViewport({ width: 1250, height: 3950 });
    await page.screenshot({ path: 'hourly_report.png' });
    await browser.close();
like image 21
dean grande Avatar answered Oct 07 '22 06:10

dean grande


Update on May 10th, 2020 I have recorded a couple of videos on how to build PDF report using Weasyprint and Plotly, you can find details here: https://www.youtube.com/watch?v=hjWTCYPy7oU&t=1s

Update on Feb 3rd, 2020:

I have found that Plotly is really amazing when export static images, highly recommended. See images I have built and you can compare it with my original question post images: enter image description here A couple of features:

  • Images can be saved as SVG or PNG. No worry about the blurry issue anymore!
  • it is easy to customize the theme/style
  • You can add any shape on the graph, see the blue vertical line, you can draw triangle, rectangle(see the filled gray area) and any other shapes you want to.
  • It supports multiple languages: JS, Python, and R.

Original Post in 2019:

Thanks all for whom answered my question, I really appreciate your help I spent more than 2 weeks on jsPDF + Chart.js but still not figuring out how to print PDF with canvas images properly.

This project is new, the languages/tools/dependencies are not restricted. To make the project done on time I switch the PDF report from Front-End(Angular) to Back-End(Python). Now it took me 2 days to complete the PDF.

sources I used to generate PDF report and Charts:

  • Weasyprint https://weasyprint.org/
  • Pygal http://pygal.org/en/stable/

Many thanks to Weasyprint for making generating PDF documents with HTML and CSS productive! Here's the PDF I have been generated:

enter image description here

enter image description here

like image 1
Haifeng Zhang Avatar answered Oct 07 '22 06:10

Haifeng Zhang