純js實現html轉pdf

項目開發中遇到了一個變態需求,須要把一整個頁面導出爲pdf格式,並且要保留頁面上的全部的表格、svg圖片和樣式。css

簡而言之,就是但願像截圖同樣,把整個頁面截下來,而後保存成pdf。html

咋不上天呢……前端

查了一下,可以實現html轉pdf的方法仍是挺多的,大概有如下幾種:java

一、大部分瀏覽器就有這個功能。然而咱們客戶要的可不是這個,人家要的是可以在系統中主動觸發的導出爲pdf功能,因此這種方案pass。node

二、利用第三方工具。我找到了一種利用wkhtmltopdf這種工具來導出的方案,本身在咱們的項目中試了一下,效果很差,並且對svg圖片的支持也不行。pass。git

三、還有一種是利用iText類後臺生成java文件。但由於須要導出的這個頁面是動態頁面,並且直接把頁面傳給後臺會丟失大量樣式,因此仍是pass。github

最後沒什麼好的辦法,只能退而求其次,想着要不先把html頁面轉成圖片,再把圖片導出爲pdf。由於要支持用戶導出下載,並且要保留樣式,因此最好是純js前端實現。chrome

html轉canvas的話,就用html2canvas這個js,這個網上介紹比較多了,這裏就不廢話了。canvas

比較麻煩的是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效果明顯不如正常截圖來的清晰……水平所限,暫時只能想到這種方法,若是你們有更好的辦法,歡迎指點。

一個簡單的demo:https://github.com/SuperNaturalGit/HtmlToPdf

使用方法:

使用Git克隆項目到本地:git clone https://github.com/SuperNaturalGit/HtmlToPdf.git

使用chrome瀏覽器打開index.html便可。其餘瀏覽器的兼容性沒測試。

若是不用git,直接把幾個文件所有copy到本地,只要相對路徑沒問題,也能夠運行的。

ps:評論裏冷秋月同窗對導出流程作了改良,能夠解決閃爍以及背景的問題,你們能夠參考。

相關文章
相關標籤/搜索