html頁面導出爲pdf(jsPDF、iText、wkhtmltopdf)

html頁面導出pdf,本來是一件很簡單的事情,在瀏覽器直接打印(Mac快捷鍵爲⌘+p;Windows快捷鍵爲ctrl+p),就可以把頁面另存爲pdf文件,但對於要經常把頁面導出爲pdf的用戶來說並不友好,一個合格程序員的標準就是:做出來的軟件豬都要會用,否則你就是豬。

調研了幾種html導出pdf的實現方式,這裏把要點記錄下來分享下。

上面三種是着重調研的三種方式,下面進行簡單介紹。


一、html2canvas+jsPDF

這種方式的原理是利用html2canvas遍歷頁面中的dom節點,渲染成canvas image,再用jsPDF把canvas image轉化爲pdf,最後轉化的pdf的內容都是圖片形式,類似於把整個網頁截圖、切割,再一頁一頁拼接成一個完整的pdf。

代碼樣例
html:

<button id="exportToPdf">導出爲PDF</button>
<div id="export_content">
    這裏是要導出爲pdf中的內容
</div>

 

javascript(需要依賴jspdf和html2canvas相關js):

<script src="js/jspdf.debug.js"></script>
<script src="js/html2canvas.js"></script>
<script type="text/javascript">
    var downPdf = document.getElementById("exportToPdf");
    downPdf.onclick = function () {
        html2canvas(
                document.getElementById("export_content"),
                {
                    dpi: 172,//導出pdf清晰度
                    onrendered: function (canvas) {
                        var contentWidth = canvas.width;
                        var contentHeight = canvas.height;

                        //一頁pdf顯示html頁面生成的canvas高度;
                        var pageHeight = contentWidth / 592.28 * 841.89;
                        //未生成pdf的html頁面高度
                        var leftHeight = contentHeight;
                        //pdf頁面偏移
                        var position = 0;
                        //html頁面生成的canvas在pdf中圖片的寬高(a4紙的尺寸[595.28,841.89])
                        var imgWidth = 595.28;
                        var imgHeight = 592.28 / contentWidth * contentHeight;

                        var pageData = canvas.toDataURL('image/jpeg', 1.0);
                        var pdf = new jsPDF('', 'pt', 'a4');

                        //有兩個高度需要區分,一個是html頁面的實際高度,和生成pdf的頁面高度(841.89)
                        //當內容未超過pdf一頁顯示的範圍,無需分頁
                        if (leftHeight < pageHeight) {
                            pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
                        } else {
                            while (leftHeight > 0) {
                                pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                                leftHeight -= pageHeight;
                                position -= 841.89;
                                //避免添加空白頁
                                if (leftHeight > 0) {
                                    pdf.addPage();
                                }
                            }
                        }
                        pdf.save('content.pdf');
                    },
                    //背景設爲白色(默認爲黑色)
                    background: "#fff"  
                })
    }
</script>

   

這種方法的優點是所有的過程都由js在客戶端完成,不需要依賴服務器。
目前發現的兩個比較明顯的缺點:
1、生成的pdf質量不高,失真比較嚴重(不過在github上這個方法可以適當提高下生成pdf的清晰度https://github.com/niklasvh/html2canvas/pull/1087);
2、在分頁處如果有圖片的話,不會自動識別隔頁處理(甚至一行文字也能給你上下一分爲二),而是無情地把圖片一分爲二,滿滿的違和感~如下圖:

github上有一篇文章說明比較詳細,還有具體的demo:https://github.com/linwalker/render-html-to-pdf


二、iText

iText是一個第三方報表java插件,可以在後端利用java隨意生成、轉化pdf文件,提供了很多api,比較靈活。

代碼樣例
pom依賴:

<dependency>
    <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
    <artifactId>com.lowagie.text</artifactId>
    <version>2.1.7</version>
</dependency>
<dependency>
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.0.8</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.4.2</version>
</dependency>

java實現:

ITextRenderer renderer = new ITextRenderer();
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("/Users/hehe/share/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
OutputStream os = new FileOutputStream("/Users/hehe/Desktop/iTextPDF.pdf");
String htmlstr = HttpHandler.sendGet("http://localhost:10086/test/iTextPDF.html");//HttpHandler.sendGet只是單純獲得指定網頁的html字符串內容
renderer.setDocumentFromString(htmlstr);
renderer.layout();
renderer.createPDF(os);

 

以上只是簡單利用html字符串來生成pdf,需要注意的是:
1、如果頁面中有中文,服務器端需要下載字體庫simsun.ttc,在後臺進行引用,同時在頁面的樣式中加入對應字體的定義,如:body{font-family: SimSun;},否則中文無法渲染(中文處渲染出來的效果是空白);
2、頁面中如果有圖片,如果圖片引用是絕對路徑或者base64則不用考慮,如果是相對路徑,需要在後臺用renderer.getSharedContext().setBaseURL("圖片絕對路徑目錄");來指定圖片路徑,否則圖片無法渲染。
3、要轉化的頁面必須是標準的XHTML頁面,有一處不符合規範就會報錯,小編再試的時候就經常報諸如org.xml.sax.SAXParseException;lineNumber: 24; columnNumber: 6;元素類型 "span" 必須由匹配的結束標記 "</span> 終止"之類的錯誤,所以如果要用iText來大量爬取網絡中的頁面的話,還是放棄吧,畢竟網上很多頁面都是不標準的~


三、wkhtmltopdf

wkhtmltopdf是一個可以把html轉爲pdf的插件,有windows、linux等平臺的版本,最大的特點就是使用簡單,語言無關性。
1、下載:官網下載 https://wkhtmltopdf.org/downloads.html
2、執行:該插件是“綠色版”,無需編譯安裝,下載解壓後,在bin目錄下有wkhtmltoimage和wkhtmltopdf兩個文件,生成pdf可以直接運行wkhtmltopdf(也可以把bin目錄配置到環境變量),執行wkhtmltopdf -V查看是否可以執行。
執行的時候可能會報錯wkhtmltopdf: error while loading shared libraries: libXrender.so.1 或者 ./wkhtmltopdf: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory 具體解決方法可參考https://www.svennd.be/wkhtmltopdf-error-while-loading-shared-libraries-libxrender-so-1/

如果執行完打印出wkhtmltopdf的版本號,則說明OK了,下面來一個打印html頁面的例子試試看,就把本頁面轉化成pdf吧:

wkhtmltopdf --disable-smart-shrinking https://blog.csdn.net/huyuyang6688/article/details/79710704 myBlog.pdf

執行完之後,就會在當前目錄生成一個pdf(當然生成pdf的目錄可以指定),--disable-smart-shrinking 這個參數是關閉縮放,如果不加的話,生成的pdf內容會特別“瘦”,不造爲啥這個命令在mac環境下不是很有效,不敢在linux環境生成的PDF是正常的。具體更詳細的用法可以參考如下文章:
1、HTML 轉 PDF 之 wkhtmltopdf 工具簡介
2、HTML 轉 PDF 之 wkhtmltopdf 工具精講
3、wkhtmltopdf參數詳解
4、解決wkhtmltopdf支持中文和縮放問題:wkhtmltopdf折騰記

與之類似的還有一個叫Phantomjs的插件,效果差不多,還沒深入研究。
---------------------
作者:dannyhoo6688
來源:CSDN
原文:https://blog.csdn.net/huyuyang6688/article/details/79710704
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章