使用 canvas 生成 Puppeteer 和 PDF
Puppeteer and PDF generation with canvas
我有一个带有 puppeteer 的 js 文件,可以生成 pdf 格式的大型报告。除了 canvas 之外,一切都很好。在反应文件中,这是 chartjs 的基本逻辑:
useEffect((): void => {
if (refChart && refChart.current) {
const newChartInstance = new Chart(refChart.current, chartConfig);
newChartInstance.options.animation = {
onComplete: (): void => {
if (refImage && refImage.current) {
refImage.current.src = newChartInstance.toBase64Image();
}
},
};
}
}, [refChart]);
return (
<>
<canvas ref={refChart} style={{ display: (printing) ? 'none' : 'block' }} width="100%" />
<img
alt="printing chart"
ref={refImage}
className={classes.chartImage}
style={{
display: (printing) ? 'block' : 'none',
}}
/>
</>
);
如果我打开 url 或打印它 (CTRL + P),图像将代替 canvas 显示,但在 puppeteer 中,我的 pdf 有(损坏的)canvas尺寸错误(即使打印锁定为真)。
这是我所期望的,我可以在浏览器和打印中看到:
这就是我从 puppeteer 那里得到的:
有人可以帮我解决这个问题吗?提前致谢。
我找到了在 puppeteer 中添加 page.setViewport()
的解决方案。这是我的最终代码:
const puppeteer = require('puppeteer');
module.exports = async (callback, url) => {
const browser = await puppeteer.launch({
headless: true,
executablePath: (process.platform === 'win32') ? null : '/usr/bin/chromium-browser',
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage'],
})
.catch((err) => {
// eslint-disable-next-line no-console
console.log('lauch: ', err);
});
const page = await browser.newPage()
.catch((err) => {
// eslint-disable-next-line no-console
console.log('page: ', err);
});
await page.setViewport({
width: 1280,
height: 1024,
deviceScaleFactor: 1,
});
await page.goto(`${url}/pdf`, {
waitUntil: ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'],
timeout: 0,
})
.catch((err) => {
// eslint-disable-next-line no-console
console.log('goto: ', err);
});
await page.evaluate(async () => {
let scrollPosition = 0;
let documentHeight = document.body.scrollHeight;
while (documentHeight > scrollPosition) {
window.scrollBy(0, documentHeight);
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
scrollPosition = documentHeight;
documentHeight = document.body.scrollHeight;
}
});
// await page.evaluate(async () => {
// const matches = document.querySelectorAll('img');
// matches.forEach((canv) => {
// // eslint-disable-next-line no-param-reassign
// canv.style.maxWidth = '80%';
// });
// });
const buffer = await page.pdf({
// path: 'hn.pdf',
format: 'a4',
margin: {
top: '1cm',
bottom: '1.5cm',
},
})
.catch((err) => {
// eslint-disable-next-line no-console
console.log('page.pdf: ', err);
});
await browser.close()
.catch((err) => {
// eslint-disable-next-line no-console
console.log('close: ', err);
});
const base64 = buffer.toString('base64');
return callback(null, base64);
};
这是我的最终结果:
我有一个带有 puppeteer 的 js 文件,可以生成 pdf 格式的大型报告。除了 canvas 之外,一切都很好。在反应文件中,这是 chartjs 的基本逻辑:
useEffect((): void => {
if (refChart && refChart.current) {
const newChartInstance = new Chart(refChart.current, chartConfig);
newChartInstance.options.animation = {
onComplete: (): void => {
if (refImage && refImage.current) {
refImage.current.src = newChartInstance.toBase64Image();
}
},
};
}
}, [refChart]);
return (
<>
<canvas ref={refChart} style={{ display: (printing) ? 'none' : 'block' }} width="100%" />
<img
alt="printing chart"
ref={refImage}
className={classes.chartImage}
style={{
display: (printing) ? 'block' : 'none',
}}
/>
</>
);
如果我打开 url 或打印它 (CTRL + P),图像将代替 canvas 显示,但在 puppeteer 中,我的 pdf 有(损坏的)canvas尺寸错误(即使打印锁定为真)。
这是我所期望的,我可以在浏览器和打印中看到:
这就是我从 puppeteer 那里得到的:
有人可以帮我解决这个问题吗?提前致谢。
我找到了在 puppeteer 中添加 page.setViewport()
的解决方案。这是我的最终代码:
const puppeteer = require('puppeteer');
module.exports = async (callback, url) => {
const browser = await puppeteer.launch({
headless: true,
executablePath: (process.platform === 'win32') ? null : '/usr/bin/chromium-browser',
args: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage'],
})
.catch((err) => {
// eslint-disable-next-line no-console
console.log('lauch: ', err);
});
const page = await browser.newPage()
.catch((err) => {
// eslint-disable-next-line no-console
console.log('page: ', err);
});
await page.setViewport({
width: 1280,
height: 1024,
deviceScaleFactor: 1,
});
await page.goto(`${url}/pdf`, {
waitUntil: ['load', 'domcontentloaded', 'networkidle0', 'networkidle2'],
timeout: 0,
})
.catch((err) => {
// eslint-disable-next-line no-console
console.log('goto: ', err);
});
await page.evaluate(async () => {
let scrollPosition = 0;
let documentHeight = document.body.scrollHeight;
while (documentHeight > scrollPosition) {
window.scrollBy(0, documentHeight);
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
scrollPosition = documentHeight;
documentHeight = document.body.scrollHeight;
}
});
// await page.evaluate(async () => {
// const matches = document.querySelectorAll('img');
// matches.forEach((canv) => {
// // eslint-disable-next-line no-param-reassign
// canv.style.maxWidth = '80%';
// });
// });
const buffer = await page.pdf({
// path: 'hn.pdf',
format: 'a4',
margin: {
top: '1cm',
bottom: '1.5cm',
},
})
.catch((err) => {
// eslint-disable-next-line no-console
console.log('page.pdf: ', err);
});
await browser.close()
.catch((err) => {
// eslint-disable-next-line no-console
console.log('close: ', err);
});
const base64 = buffer.toString('base64');
return callback(null, base64);
};
这是我的最终结果: