jsPdf是一個可以把html轉成pdf的插件,有人多人在用。
但是老外做的很多東西沒考慮過英文之外的語言,這個也不例外,不支持中文,而且頁面還原度也不是很好。
網上實例導出的是 A4 大小,用到的是 addImage,以及需要 html2canvas 庫。其實就是把需要導出的網頁,利用 html2canvas 生成圖片,在 addImage 至 PDF 中。至於為什么需要先轉成圖片呢,直接網頁轉不是很好嗎,還能文字復制。因為不支中文,而且,樣式還原度也不高。當然,執意於文本復制,網上也有解決方式(搜索關鍵字:jsPDF 中文)。
所以常用的解決方案就是:曲線救國: html2canvas + jsPdf
既然你不支持中文,那我把頁面轉成圖片,怕不怕,圖片再導出PDF照樣中!這種方式很常見、很省事,問題也很多圖片拉伸、模糊,最重要這樣導出的PDF是沒有靈魂的,因為他里面的內容都是圖片,不能復制。對於pdf操作要求比較高的,這種方案就不大適合。如果你喜歡這種可以參照這篇文章Javascript 將html轉成pdf,下載,支持多頁哦(html2canvas 和 jsPDF),寫的很詳細。
采用這種方案也有一些問題需要處理,這里記錄下,給后來人避坑。
1、html2canvas生成圖片模糊的問題
這個問題大多數都會遇到,可以看我之前的博客處理:html轉為圖片插件:html2canvas保存圖片模糊問題解決
2、html2canvas偶爾生成大量空白漏掉一些元素問題
在使用過程我發現有時候當滾動到底部時,生成的圖片就會存在大量空白,漏掉很多元素
解決方案就是在生成圖片前滾動至頂部使生成圖片的元素進入渲染區域內。添加如下代碼即可:
document.documentElement.scrollTop = 0
3、html2canvas生成圖片里包含跨域圖片的處理
這個問題也比較奇怪,在很多同版本瀏覽器上均可以生成跨域圖片,但是在個別電腦上同版本瀏覽器也不行。會報跨域的錯誤,因為我們圖片都是存儲在阿里雲的oss上
Access to image at 'https://......oss-cn-...aliyuncs.com/image/base/be6f..f.jpeg'
from origin 'http://...:3001' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
網上很多各種解決方案也不靠譜。所以最后還是自己看源碼,在html2canvas官網:http://html2canvas.hertzen.com/,下載源碼,這里先看一下之前寫的博客,涉及到在線圖片跨域轉base64格式的問題:將在線圖片轉換成base64踩坑記錄及靜態資源跨域及緩存的處理
通過之前這篇博客就可以看到問題,那么其實我們只要改下面2段代碼即可
if (isInlineBase64Image(src) || useCORS) { // img.crossOrigin = 'anonymous';
img.crossOrigin = '*'; } // img.src = src;
img.src = src + '?v=' + Math.random();
注釋的為原代碼,下面的為修改后的代碼即可。
4、jsPdf分頁問題
頁面過長,就涉及分頁問題。因為 A4 是有固定頁面大小的(寬:595.28, 高:841.89),保存的內容高度(縮放至A4寬度)超過該長度,則會出現截斷問題。
解決方式其實不多,分情況而定。其實最簡單的方式是不分頁。不是嗎?不分頁,啥問題都沒。這個問題就是,為什么要分頁?講真,沒有分頁需求,也沒有打印需求,沒必要分頁,不是嗎?整個頁面導成圖片就行。
不分頁就是將元素寬高作為pdf文件的寬高就行
const pdf = new jsPDF('', 'pt', [contentWidth, contentHeight])
如果確實要分頁的話,如果是純靜態頁面,還比較簡單,根據 A4 的尺寸比例,預留好間隔,這樣,裁剪的時候,便不會出現文本或者其他內容被裁成兩半。
如果是動態頁面的話就比較復雜,前端實現方案就並不太合適了,建議采用后端實現方案。
5、實例代碼
downPdf () { let _this = this document.documentElement.scrollTop = 0 let canvas = document.createElement("canvas") let context = canvas.getContext("2d") let _articleHtml = document.getElementById('article-content') let _w = _articleHtml.clientWidth let _h = _articleHtml.clientHeight let scale = 3 canvas.width = _w * scale canvas.height = _h * scale context.scale(scale, scale) let opts = { scale: 1, width: _w, height: _h, canvas: canvas, useCORS: true } html2canvas(_articleHtml, opts).then(canvas => { _this.createPdfAll(canvas, scale) }) }, createPdfAll (canvas, scale) { let contentWidth = canvas.width / scale let contentHeight = canvas.height / scale let pdf = new jsPDF('', 'pt', [contentWidth, contentHeight]) let pageData = canvas.toDataURL('image/jpeg', 1.0) pdf.addImage(pageData, 'JPEG', -9, 0, contentWidth, contentHeight) pdf.save(`${this.detail.title}.pdf`) },
此種方案純前端實現,無需后端配合,並且頁面還原度比較高,對於pdf操作要求不高的需求,還是比較合適的解決方案。
缺點就是無法復制,對pdf操作不大兼容,對於分頁也不大友好,容易出現分割。其實后端實現方案也有蠻多,並且對於分頁更友好,可以分情況選擇不同方案。