Reputation: 31895
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)
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.
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.
Upvotes: 9
Views: 4399
Reputation: 4287
If you are using puppeteer for taking the screenshot in image or pdf format then you need to scale the image using deviceScaleFactor
option, by default it is set to 1. Its min value is 1 and the max is 5. By setting it, both the image as well as pdf would be scaled up in quality. Check out this issue for more details on this. Attaching the code below:
const page = await browser.newPage();
await page.setViewport({ width: 800, height: 800, deviceScaleFactor: 3 });
P.S. Setting width and height to viewport wouldn't have any effect on the size of image/pdf.
Upvotes: 1
Reputation: 31895
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:
A couple of features:
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:
Many thanks to Weasyprint for making generating PDF documents with HTML and CSS productive! Here's the PDF I have been generated:
Upvotes: 1
Reputation: 744
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();
Upvotes: 2
Reputation: 34117
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.
Upvotes: 2