JavaScript實現(xiàn)html轉pdf的三種方法詳解
近期項目需要實現(xiàn)將 html 頁面轉換成 pdf 報告的需求,經過一番調研以及結合過往經驗,發(fā)現(xiàn)了有如下三種技術方案,本篇文章主要內容是基于這三種不同方案的 demo 實現(xiàn)及對比。
方案選型
- html2canvas + jspdf
- wkhtmltopdf
- node + puppeteer
準備工作
利用 chatgpt 生成一個報告的 html 模板,每頁包含了一個表格和一個由 echarts 生成的柱狀圖。實際項目中基本也是這樣的內容,文字 + charts,所以用這個來測試具有一定的參考意義,生成的 html 模板如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Report Page</title> <!-- ECharts庫 --> <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script> <style> 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; } </style> </head> <body> <div class="page" id="page1"> <h2>Page 1 - Data and Chart</h2> <table> <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> <!-- 更多列表數(shù)據(jù) --> </table> <div id="chart1" class="chart"></div> </div> <div class="page" id="page2"> <table> <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> <!-- 更多列表數(shù)據(jù) --> </table> <h2>Page 2 - Data and Chart</h2> <!-- 類似的列表和圖表占位符 --> <div id="chart2" class="chart"></div> </div> <div class="page" id="page3"> <table> <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> <!-- 更多列表數(shù)據(jù) --> </table> <h2>Page 3 - Data and Chart</h2> <!-- 類似的列表和圖表占位符 --> <div id="chart3" class="chart"></div> </div> <script> // 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); </script> </body> </html>
使用 http-server 在 html 頁面目錄下啟動一個 web 服務器(沒有 http-server, 參考這里安裝 www.npmjs.com/package/http-server)
http-server . -p 9090
訪問 http://127.0.0.1:9090/report.html
,即可看到對應的報告模板頁面,如下:
wkhtmltopdf
到這里 wkhtmltopdf.org/downloads.html 下載系統(tǒng)對應版本的安裝包,然后安裝好
執(zhí)行如下命令,即可生成 pdf
wkhtmltopdf http://127.0.0.1:9090/report.html report.pdf
查看生成的 pdf 內容,能正常顯示,看起來也沒啥問題
實際報告肯定是有多頁的,接下來我們嘗試一下看看如何分頁。在 page1 和 page2 元素后分別加入一個
元素,對應的 class 如下:
.page-break-after { page-break-after: always; }
然后再次執(zhí)行轉換命令,得到的 pdf 如下:
問題
偶爾會卡在某個進度不動,不知道為啥
執(zhí)行時間不穩(wěn)定,時快時慢
putteteer
安裝就不多說明了,自行 google。安裝好后,同樣讓 chatgpt 生成一段測試的代碼,如下:
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}`); } // 調用函數(shù)保存網頁為PDF savePageAsPDF('http://127.0.0.1:9090/report.html', 'report1.pdf').catch(console.error);
執(zhí)行轉換腳本
node index.js
得到的 pdf 內容如下,看起來也沒啥問題
html2canvas + jspdf
引入 html2canvas 和 jspdf 相關腳本
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.3.1/jspdf.umd.min.js"></script>
添加下載按鈕及轉換函數(shù)
<button onclick="convertToPDF()">Convert to PDF</button> <script> 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 創(chuàng)建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'); } </script>
訪問頁面,點擊按鈕即可下載 pdf 了,內容如下:
方案對比
wkhtmltopdf,依賴后端,配置比較靈活,但是速度好像不穩(wěn)定
pupeteer,依賴后端,生成的 pdf 比較準確,質量也還可以
html2canvas + jspdf,純前端實現(xiàn),依賴少
以上就是JavaScript實現(xiàn)html轉pdf的三種方法詳解的詳細內容,更多關于JavaScript html轉pdf的資料請關注腳本之家其它相關文章!
相關文章
ECMAScript 6即將帶給我們新的數(shù)組操作方法前瞻
這篇文章主要介紹了ECMAScript 6即將帶給我們新的數(shù)組操作方法前瞻,需要的朋友可以參考下2015-01-01Firefox/Chrome/Safari的中可直接使用$/$$函數(shù)進行調試
偶然發(fā)現(xiàn)的,頁面中沒有引入Prototype和jQuery??刂婆_中敲$卻發(fā)現(xiàn)是一個函數(shù)。又試著敲$$,也是個function2012-02-02JavaScript中setInterval()和setTimeout()的用法及區(qū)別
這篇文章主要給大家介紹了關于JavaScript中setInterval()和setTimeout()用法及區(qū)別的相關資料,Javascript的setTimeOut和setInterval函數(shù)應用非常廣泛,它們都用來處理延時和定時任務,需要的朋友可以參考下2023-11-11