用js生成PDF的方案


在java里,我們常用Itext來生成pdf,在pdf文件里組合圖片,文字,畫表格,畫線等操作,還會遇到中文支持的問題。

那好,現在想直接在web前端就生成pdf怎么辦,目前有以下幾個解決方案

1:JSPDF.js

這個庫支持不同類型的PDF文件格式,包括:文本,數字,圖形,圖片,同時你可以自由的編輯標題或者其它類型元素。

還支持互動的內容制作,例如,你可以輸入文字或者數字,然后jsPDF幫助生成最后的PDF內容。

缺點,不支持中文,不支持svg導入(解決方案:svg+html轉換成canvas)

http://jspdf.com/

示例代碼如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Downloadify</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<style type="text/css" media="screen">
body {background: #fff; width: 500px; margin: 20px auto;}
input, textarea, p { font-family: 宋體, 黑體; font-size: 12pt;}
input, textarea { border: solid 1px #aaa; padding: 4px; width: 98%;}
</style>
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/downloadify.js"></script>
<script type="text/javascript" src="js/jspdf.js"></script>
<!-- <script type="text/javascript" src="js/downloadify.min.js"></script> -->
<script type="text/javascript">
window.load=function(){
Downloadify.create('downloadify',{
filename: function(){
return document.getElementById('filename').value;
},
data: function(){
var doc = new jsPDF();
doc.text(20, 20, document.getElementById('data').value);
doc.addPage();
doc.text(20, 20, document.getElementById('data').value);
return doc.output();
},
onComplete: function(){ alert('成功保存文件!'); },
onCancel: function(){ alert('您已經取消保存文件'); },
onError: function(){ alert('出現錯誤了'); },
swf: 'js/downloadify.swf',
downloadImage: 'js/download.png',
width: 100,
height: 30,
transparent: true,
append: false
});
}
</script>
</head>
<body onload="load();">
<input type="text" name="filename" value="文件名.pdf" id="filename" /><br />
<textarea cols="60" rows="10" name="data" id="data">it seem do not support to Chinese</textarea>
<p id="downloadify">You must have Flash 10 installed to download this file.</p>
</body>
</html>

2:pdfmake

pdfmake的基本使用方法

1.包含以下兩個文件

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

2.在JS代碼中聲明一個Document-definition對象,這個是pdfmake自己的術語。簡單點說,就是創建一個至少包含content屬性的對象。然后就可以調用pdfMake的方法導出PDF,具體見如下代碼:

1
2
3
4
5
6
7
8
9
10
11
<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支持中文

三個步驟:

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

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

grunt dump_dir命令會將fonts目錄下所有文件都打包,因此無用文件請別放進去。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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屬性:

1
2
3
4
5
6
7
8
9
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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM