近期项目需要实现将 html 页面转换成 pdf 报告的需求,经过一番调研以及结合过往经验,发现了有如下三种技术方案,本篇文章主要内容是基于这三种不同方案的 demo 实现及对比。
方案选型
- html2canvas + jspdf
- wkhtmltopdf
- node + puppeteer
准备工作
利用 chatgpt 生成一个报告的 html 模板,每页包含了一个表格和一个由 echarts 生成的柱状图。实际项目中基本也是这样的内容,文字 + charts,所以用这个来测试具有一定的参考意义,生成的 html 模板如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | < title >Report Page</ title > body { font-family: Arial, sans-serif; margin: 20px; } .page { margin-bottom: 40px; } .chart { width: 600px; height: 400px; margin-top: 20px; } table { width: 100%; border-collapse: collapse; } th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; } < div class = "page" id = "page1" > < h2 >Page 1 - Data and Chart</ h2 > < table > < tbody >< tr > < th >Item</ th > < th >Value</ th > </ tr > < tr > < td >Item 1</ td > < td >100</ td > </ tr > < tr > < td >Item 2</ td > < td >200</ td > </ tr > </ tbody ></ table > < div id = "chart1" class = "chart" ></ div > </ div > < div class = "page" id = "page2" > < table > < tbody >< tr > < th >Item</ th > < th >Value</ th > </ tr > < tr > < td >Item 1</ td > < td >100</ td > </ tr > < tr > < td >Item 2</ td > < td >200</ td > </ tr > </ tbody ></ table > < h2 >Page 2 - Data and Chart</ h2 > < div id = "chart2" class = "chart" ></ div > </ div > < div class = "page" id = "page3" > < table > < tbody >< tr > < th >Item</ th > < th >Value</ th > </ tr > < tr > < td >Item 1</ td > < td >100</ td > </ tr > < tr > < td >Item 2</ td > < td >200</ td > </ tr > </ tbody ></ table > < h2 >Page 3 - Data and Chart</ h2 > < div id = "chart3" class = "chart" ></ div > </ div > // ECharts 图表示例 var chart1 = echarts.init(document.getElementById('chart1')); var option1 = { title: {text: 'ECharts 示例 1'}, tooltip: {}, xAxis: {data: ['类别1', '类别2', '类别3', '类别4']}, yAxis: {}, series: [{type: 'bar', data: [5, 20, 36, 10]}] }; chart1.setOption(option1); // 为page2和page3重复上面的过程,设置不同的option配置 var chart2 = echarts.init(document.getElementById('chart2')); chart2.setOption(option1); var chart3 = echarts.init(document.getElementById('chart3')); chart3.setOption(option1); |
使用 http-server 在 html 页面目录下启动一个 web 服务器(没有 http-server, 参考这里安装 www.npmjs.com/package/http-server)
1 | http-server . -p 9090 |
访问 http://127.0.0.1:9090/report.html
,即可看到对应的报告模板页面,如下:
wkhtmltopdf
到这里 wkhtmltopdf.org/downloads.html 下载系统对应版本的安装包,然后安装好
执行如下命令,即可生成 pdf
1 | wkhtmltopdf http: //127 .0.0.1:9090 /report .html report.pdf |
查看生成的 pdf 内容,能正常显示,看起来也没啥问题
实际报告肯定是有多页的,接下来我们尝试一下看看如何分页。在 page1 和 page2 元素后分别加入一个
元素,对应的 class 如下:
1 2 3 | .page-break-after { page-break-after : always ; } |
然后再次执行转换命令,得到的 pdf 如下:
问题
偶尔会卡在某个进度不动,不知道为啥
执行时间不稳定,时快时慢
putteteer
安装就不多说明了,自行 google。安装好后,同样让 chatgpt 生成一段测试的代码,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const puppeteer = require( 'puppeteer' ); async function savePageAsPDF(url, outputPath) { // 启动浏览器 const browser = await puppeteer.launch(); // 打开新页面 const page = await browser.newPage(); // 导航到指定URL await page.goto(url, { waitUntil: 'networkidle2' }); // 保存页面为PDF await page.pdf({ path: outputPath, format: 'A4' }); // 关闭浏览器 await browser.close(); console.log(`PDF已保存到:${outputPath}`); } // 调用函数保存网页为PDF |
执行转换脚本
1 | node index.js |
得到的 pdf 内容如下,看起来也没啥问题
html2canvas + jspdf
引入 html2canvas 和 jspdf 相关脚本
1 |
添加下载按钮及转换函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | < button >Convert to PDF</ button > async function addPage(id, pdf) { const element = document.getElementById(id); // 使用 html2canvas 渲染指定元素 const canvas = await html2canvas(element, {scale: 2}); const imgData = canvas.toDataURL('image/png'); // 使用 jsPDF 创建PDF文档 const imgProps = pdf.getImageProperties(imgData); const pdfWidth = pdf.internal.pageSize.getWidth(); // const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; const pdfHeight = (imgProps.height / imgProps.width) * pdfWidth; // const pdfHeight = pdf.internal.pageSize.getHeight(); console.log(imgProps.width, pdfWidth, pdfHeight, pdf.internal.pageSize.getHeight()); pdf.addImage(imgData, 'JPEG', 0, 0, pdfWidth, pdfHeight, 'NONE', 'FAST'); } async function convertToPDF() { const pdf = new window.jspdf.jsPDF(); await addPage('page1', pdf); pdf.addPage(); await addPage('page2', pdf); pdf.addPage(); await addPage('page3', pdf); pdf.save('converted.pdf'); } |
访问页面,点击按钮即可下载 pdf 了,内容如下:
方案对比
wkhtmltopdf,依赖后端,配置比较灵活,但是速度好像不稳定
pupeteer,依赖后端,生成的 pdf 比较准确,质量也还可以
html2canvas + jspdf,纯前端实现,依赖少
以上就是JavaScript实现html转pdf的三种方法详解的详细内容,更多关于JavaScript html转pdf的资料请关注IT俱乐部其它相关文章!