Reputation: 1728
I am trying to figure ou the proper way to combine Puppeteer and the GoogleCharts library to render Bar charts and export a PNG image of the chart.
The basic layout, mostly inspired by the Puppeteer documentation seems to be something like that, to create a new page with a canvas element.
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(`
<!DOCTYPE html>
<html>
<body>
<canvas id="chart" width="580" height="400"></canvas>
</body>
</html>
`);
await browser.close();
})();
I have found this npm package to work with Google Charts: https://www.npmjs.com/package/google-charts. The chart.draw
method seems to accept a DOM element in which it will render the chart.
According to the documentation of this package, the usage looks pretty simple.
const pie_1_chart = new GoogleCharts.api.visualization.PieChart(document.getElementById('chart1'));
pie_1_chart.draw(data);
How can I execute the draw method so that it render the chart inside the #canvas
DOM element of the Puppeteer's page ?
Also, if you could give me an example of the proper way to export the page/canvas as a PNG image, that would be very appreciated.
NOTE: I browsed a lot to find an existing library/package that would do what I am trying to achieve, but the closest I could found is a TypeScript package for Chart.JS: https://github.com/SeanSobey/ChartjsNodePuppeteer. I am not familiar with TypeScript/ES6, so I don't know how I could adapt this library to make it work with the Google Charts library instead of Chart.JS.
Thanks
Upvotes: 1
Views: 2769
Reputation: 209
Here is my code to use the charts in the PDF file, resolved the issue to get the full width for charts in the PDF file as similar to application view
Options of defaultViewport: null and args: ['--window-size=1920,1080'] works for me in the application and give the full view to create the pdf of charts
const browser = await puppeteer.launch({
headless: true,
defaultViewport: null,
args: [
'--window-size=1366,768'
],
});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(0);
await page.goto(reportUrl, {
waitUntil: ['load', 'domcontentloaded', 'networkidle0'],
});
Upvotes: 0
Reputation: 51
I have used the below lines of code to setup the puppeteer in the node.js application. It is working fine to display the charts in the pdf file.
For me the combination of defaultViewport: null and args: ['--window-size=1920,1080'] gave me full-screen with view fitting to screensize and charts shows with proper width as shown in the application:
const browser = await puppeteer.launch({
headless: true,
defaultViewport: null,
args: [
'--window-size=1366,768',
'--no-sandbox',
'--disable-setuid-sandbox',
],
});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(0);
await page.goto(reportUrl, {
waitUntil: ['load', 'domcontentloaded', 'networkidle0'],
});
Upvotes: 0
Reputation: 11132
I had this same problem and baked it into an open-source library called Google Charts Node.
Here's an example usage:
const GoogleChartsNode = require('google-charts-node');
// Define your chart drawing function
function drawChart() {
const data = google.visualization.arrayToDataTable([
['City', '2010 Population',],
['New York City, NY', 8175000],
['Los Angeles, CA', 3792000],
['Chicago, IL', 2695000],
['Houston, TX', 2099000],
['Philadelphia, PA', 1526000]
]);
const options = {
title: 'Population of Largest U.S. Cities',
chartArea: {width: '50%'},
hAxis: {
title: 'Total Population',
minValue: 0
},
vAxis: {
title: 'City'
}
};
const chart = new google.visualization.BarChart(container);
chart.draw(data, options);
}
// Render the chart to image
const image = await GoogleChartsNode.render(drawChart, {
width: 400,
height: 300,
});
The render
method returns a buffer that contains this image:
It's available on NPM as google-charts-node.
Regarding the general problem of rendering with Puppeteer, I found it to be fairly straightforward. I try to use the charts-provided method getImageURI
whenever possible, but not all charts support it. In that case, I take a screenshot using Puppeteer.
Here is the basic logic (also check out the full source code):
async function render() {
// Puppeteer setup
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Add the chart
await page.setContent(`...Insert your Google Charts code here...`);
// Use getImageURI if available (not all charts support)
const imageBase64 = await page.evaluate(() => {
if (!window.chart || typeof window.chart.getImageURI === 'undefined') {
return null;
}
return window.chart.getImageURI();
});
let buf;
if (imageBase64) {
buf = Buffer.from(imageBase64.slice('data:image/png;base64,'.length), 'base64');
} else {
// getImageURI was not available - take a screenshot using puppeteer
const elt = await page.$('#chart_div');
buf = await elt.screenshot();
}
await browser.close();
return buf;
}
Upvotes: 1
Reputation: 2509
I've set up a puppeteer code which loading Google Chart example page's Iframe and then screenshot the elementHandle
#chart_div
.
This is way too much faster and simpler than loading another library.
Next you can create HTML DOM with a div element and set up a Google Chart with it and take screenshot the div element.
const page = await browser.newPage()
page.setDefaultNavigationTimeout(0);
const goto = await page.goto('https://developers-dot-devsite-v2-prod.appspot.com/chart/interactive/docs/gallery/barchart_9a8ce4c79b8da3fa2e73bee14869e01b6f7fb5150dc7540172d60b5680259b48.frame')
const wait = await page.waitForSelector('#chart_div > div', {'visible' : true, 'timeout' : 0})
const elem = await page.$('#chart_div')
const save = await elem.screenshot({path : 'googlechart.png' })
Upvotes: 0