JS導出PDF插件(支持中文、圖片使用路徑)

在WEB上想作一個導出PDF的功能,發現jsPDF比較多人推薦,遺憾的是不支持中文,最後找到pdfmake,很好地解決了此問題。它的效果能夠先到http://pdfmake.org/playground.html查看。在使用過程當中,還發現圖片的插入是相對繁瑣的一件事。javascript

針對這些問題,本文的主要內容可分爲三部分:html

  • pdfmake的基本使用方法;
  • 如何解決中文問題;
  • 如何經過指定圖片地址插入圖片。

pdfmake的基本使用方法

1.包含如下兩個文件java

<script src="build/pdfmake.min.js"></script>
    <script src="build/vfs_fonts.js"></script>

2.在JS代碼中聲明一個Document-definition對象,這個是pdfmake本身的術語。簡單點說,就是建立一個至少包含content屬性的對象。而後就能夠調用pdfMake的方法導出PDF,具體見以下代碼:node

<script type="text/javascript">
 //建立Document-definition對象 
 var dd = {
            content: [
             'One paragraph',
             'Another paragraph, this time a little bit longer to make  sure, this line will be divided into at least two lines'
             ]
        };
  //導出PDF
  pdfMake.createPdf(dd).download();
  </script>

按照上例操做,就能夠看到提示文件下載了。關於pdfmake的完整教程請登錄pdfmake項目查看。git

pdfmake支持中文

三個步驟:github

1.到pdfmake項目網站,把工程都下載下來,而後進入工程目錄將字體文件(好比微軟雅黑.ttf)放到examples/fonts目錄下,而後使用grunt dump_dir生成新的vfs_fonts.js文件;canvas

從上面描述可知該工程是經過grunt管理的,若是無相關知識,請上網先補習下。數組

grunt dump_dir命令會將fonts目錄下全部文件都打包,所以無用文件請別放進去。瀏覽器

微軟雅黑.ttf網上一搜一大把,WINDOWS電腦系統盤下存放字體的目錄也找獲得。服務器

2.回到本身的例子代碼中,JS代碼中修改pdfMake的fonts對象,聲明當前要用到字體:

pdfMake.fonts = {
            Roboto: {
                normal: 'Roboto-Regular.ttf',
                bold: 'Roboto-Medium.ttf',
                italics: 'Roboto-Italic.ttf',
                bolditalics: 'Roboto-Italic.ttf'
            },
            微軟雅黑: {
                normal: '微軟雅黑.ttf',
                bold: '微軟雅黑.ttf',
                italics: '微軟雅黑.ttf',
                bolditalics: '微軟雅黑.ttf',
            }
        };

3.Document-definition對象中聲明默認要使用的字體,具體來講:就是聲明一個對象,除了content屬性,還要有一個defaultStyle屬性,該defaultStyle下面還有再有一個font屬性:

var dd = {
            content: [
             '中英文測試',
             'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
             ],
             defaultStyle: {
                 font: '微軟雅黑'
             }
        };

如下爲根據如上步驟作的一個完整例子源碼:

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
    <meta charset="utf-8">
    <title>my first export PDF</title>
    <script src="build/pdfmake.min.js"></script>
    <script src="build/vfs_fonts.js"></script>
    <script>
    function down() {
        var dd = {
            content: [
             '中英文測試',
             'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
             ],
             defaultStyle: {
                 font: '微軟雅黑'
             }
        };
        pdfMake.fonts = {
            Roboto: {
                normal: 'Roboto-Regular.ttf',
                bold: 'Roboto-Medium.ttf',
                italics: 'Roboto-Italic.ttf',
                bolditalics: 'Roboto-Italic.ttf'
            },
            微軟雅黑: {
                normal: '微軟雅黑.ttf',
                bold: '微軟雅黑.ttf',
                italics: '微軟雅黑.ttf',
                bolditalics: '微軟雅黑.ttf',
            }
        };
        pdfMake.createPdf(dd).download();
    }
    </script>
    </head>
    <body>
    <button onclick="down()">下載</button>
    </body>
</html>

插入圖片

在插入圖片方面,jsPDF要求先將圖片轉換成Data URL才行,而pdfmake容許直接指定路徑,看起來是很方便,但這是有條件的,必須是以node.js做爲服務器,或者將圖片放到vfs_fonts.js中,因此總的來講,用處不大,仍是同樣得將圖片轉換成Data URL形式才行。

爲解決此問題,我寫了一個ImageDataURL的函數對象,可同時傳入多個圖片地址。在圖片都加載完成後,ImageDataURL.oncomplete將被觸發,在回調中經過this.imgdata取出各個圖片的Data URL,根據pdfmake的要求組織下,就可正確生成pdf了。

ImageDataURL的原理是經過H5的canvas標籤,將圖片繪製在canvas上,而後經過canvas的toDataURL獲得圖像的Data URL。使用時請注意瀏覽器兼容性問題。

如下爲將sampleImage.jpg, sampleage.jpg, sampleImage.jpg依次寫入PDF的例子,測試時sampleage.jpg不存在,PDF直接忽略。

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
    <meta charset="utf-8">
    <title>my second export PDF</title>
    <script src="build/pdfmake.min.js"></script>
    <script src="build/vfs_fonts.js"></script>
    <script>
    
    function down() {
        var x = new ImageDataURL(["sampleImage.jpg", "samplage.jpg", "sampleImage.jpg"]);
        x.oncomplete = function() {
            var imgs = new Array();
            console.log("complete");
            for (key in this.imgdata) {
                if (this.imgdata[key] == this.emptyobj)//不存在的圖片直接忽略
                    continue;
                imgs.push({image:this.imgdata[key]});//pdfmake的圖片格式{image:image dataurl}
            }
            var dd = {
                content: [
                 'Title',
                 imgs,
                 ],
            };
            pdfMake.createPdf(dd).download();
        }
    }
    </script>
    </head>
    <body>
    <button onclick="down()">下載</button>
    <script>
    function ImageDataURL(urls) {//urls必須是字符串或字符串數組
        this.completenum = 0;
        this.totalnum = 0;
        this.imgdata = new Array();
        this.emptyobj = new Object();
        this.oncomplete = function(){};
        this.getDataURL = function(url, index) {
            var c = document.createElement("canvas");
            var cxt = c.getContext("2d");
            var img = new Image();
            var dataurl;
            var p;
            p = this;
            img.src = url;
            img.onload = function() {
                var i;
                var maxwidth = 500;
                var scale = 1.0;
                if (img.width > maxwidth) {
                    scale = maxwidth / img.width;
                    c.width = maxwidth;
                    c.height = Math.floor(img.height * scale);
                } else {
                    c.width= img.width;
                    c.height= img.height;
                }
                cxt.drawImage(img, 0, 0, c.width, c.height);

                p.imgdata[index] = c.toDataURL('image/jpeg');
                for (i = 0; i < p.totalnum; ++i) {
                    if (p.imgdata[i] == null)
                        break;
                }
                if (i == p.totalnum) {
                    p.oncomplete();
                }
            };
            img.onerror = function() {
                p.imgdata[index] = p.emptyobj;
                for (i = 0; i < p.totalnum; ++i) {
                    if (p.imgdata[i] == null)
                        break;
                }
                if (i == p.totalnum) {
                    p.oncomplete();
                }
            };
        }
        if (urls instanceof Array) {
            this.totalnum = urls.length; 
            this.imgdata = new Array(this.totalnum);
            for (key in urls) {
                this.getDataURL(urls[key], key);
            }
        } else {
            this.imgdata = new Array(1);
            this.totalnum = 1;
            this.getDataURL(urls, 0);
        }
    }

    </script>
    </body>
</html>

參考

相關文章
相關標籤/搜索