1. 程式人生 > >純js實現html轉pdf

純js實現html轉pdf

專案開發中遇到了一個變態需求,需要把一整個頁面匯出為pdf格式,而且要保留頁面上的所有的表格、svg圖片和樣式。
簡而言之,就是希望像截圖一樣,把整個頁面截下來,然後儲存成pdf。
咋不上天呢……
查了一下,能夠實現html轉pdf的方法還是挺多的,大概有以下幾種:
1、大部分瀏覽器就有這個功能。然而我們客戶要的可不是這個,人家要的是能夠在系統中主動觸發的匯出為pdf功能,所以這種方案pass。
2、利用第三方工具。我找到了一種利用wkhtmltopdf這種工具來匯出的方案,自己在我們的專案中試了一下,效果不好,而且對svg圖片的支援也不行。pass。
3、還有一種是利用iText類後臺生成java檔案。但因為需要匯出的這個頁面是動態頁面,而且直接把頁面傳給後臺會丟失大量樣式,所以還是pass。
最後沒什麼好的辦法,只能退而求其次,想著要不先把html頁面轉成圖片,再把圖片匯出為pdf。因為要支援使用者匯出下載,而且要保留樣式,所以最好是純js前端實現。
html轉canvas的話,就用html2canvas這個js,這個網上介紹比較多了,這裡就不廢話了。
比較麻煩的是svg圖片,直接用html2canvas無法把svg標籤的內容轉成canvas,最後查了一圈資料後,鎖定了canvg這個js。canvg是谷歌的一個外掛,可以將svg標籤內容轉成canvas。具體到我們的專案,還有一個難點,就是如何把glyphicons這種字型圖示也轉成canvas,因為在不同瀏覽器下對這種字型圖示的支援是完全不一樣的。最後找到的方法是用char code來替換這些字型圖示,重新繪製成canvas。由canvas生成圖片不用廢話。由圖片生成pdf用jsPDF實現。 折騰了大半天,總算把整個流程打通了,接下來一步一步貼上程式碼。

第一步:把對應dom節點裡所有的svg元素替換成canvas

 1 svg2canvas: function(targetElem) {
 2   var svgElem = targetElem.find('svg');
 3   svgElem.each(function(index, node) {
 4     var parentNode = node.parentNode;
 5     //由於現在的IE不支援直接對svg標籤node取內容,所以需要在當前標籤外面套一層div,通過外層div的innerHTML屬性來獲取
 6     var tempNode = document.createElement('div');
7 tempNode.appendChild(node); 8 var svg = tempNode.innerHTML; 9 var canvas = document.createElement('canvas'); 10 //轉換 11 canvg(canvas, svg); 12 parentNode.appendChild(canvas); 13 }); 14 }

第二步:把glyphicons字型轉成canvas。如果專案中沒有用到glyphicons字型圖示,可忽略這一步

 1 glyphicons2canvas: function
(targetElem, fontClassName, fontFamilyName) { 2 var iconElems = targetElem.find('.' + fontClassName); 3 iconElems.each(function(index, inconNode) { 4 var fontSize = $(inconNode).css("font-size"); 5 var iconColor = $(inconNode).css("color"); 6 var styleContent = $(inconNode).attr('style'); 7 //去掉"px" 8 fontSize = fontSize.replace("px", ""); 9 var charCode = getCharCodeByGlyphiconsName(iconName); 10 var myCanvas = document.createElement('canvas'); 11 //把canva寬高各增加2是為了顯示圖示完整 12 myCanvas.width = parseInt(fontSize) + 2; 13 myCanvas.height = parseInt(fontSize) + 2; 14 myCanvas.style = styleContent; 15 var ctx = myCanvas.getContext('2d'); 16 //設定繪圖內容的顏色 17 ctx.fillStyle = iconColor; 18 //設定繪圖的字型大小以及font-family的名字 19 ctx.font = fontSize + 'px ' + fontFamilyName; 20 ctx.fillText(String.fromCharCode(charCode), 1, parseInt(fontSize) + 1); 21 $(inconNode).replaceWith(myCanvas); 22 }); 23 } 24 //根據glyphicons/glyphicon圖示的類名獲取到對應的char code 25 getCharCodeByGlyphiconsName: function(iconName) { 26 switch (iconName) { 27 case("glyphicons-resize-full"): 28 return "0xE216"; 29 case ("glyphicons-chevron-left"): 30 return "0xE225"; 31 default: 32 return ""; 33 } 34 }

第三步:html轉canvas轉圖片再轉pdf

 1 html2canvas($("#myExportArea"), {
 2   onrendered: function(canvas) {
 3     var imgData = canvas.toDataURL('image/jpeg');
 4     var img = new Image();
 5     img.src = imgData;
 6     //根據圖片的尺寸設定pdf的規格,要在圖片載入成功時執行,之所以要*0.225是因為比例問題
 7     img.onload = function() {
 8       //此處需要注意,pdf橫置和豎置兩個屬性,需要根據寬高的比例來調整,不然會出現顯示不完全的問題
 9       if (this.width > this.height) {
10         var doc = new jsPDF('l', 'mm', [this.width * 0.225, this.height * 0.225]);
11       } else {
12         var doc = new jsPDF('p', 'mm', [this.width * 0.225, this.height * 0.225]);
13       }
14       doc.addImage(imgData, 'jpeg', 0, 0, this.width * 0.225, this.height * 0.225);
15       //根據下載儲存成不同的檔名
16       doc.save('report_pdf_' + new Date().getTime() + '.pdf');
17     }
18   },
19   background: "#fff",
20   //這裡給生成的圖片預設背景,不然的話,如果你的html根節點沒設定背景的話,會用黑色填充。
21   allowTaint: true //避免一些不識別的圖片干擾,預設為false,遇到不識別的圖片干擾則會停止處理html2canvas
22 });

雖然最後勉強完成了客戶的要求,但是生成的pdf效果明顯不如正常截圖來的清晰……水平所限,暫時只能想到這種方法,如果大家有更好的辦法,歡迎指點。