在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 οnlοad="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.包含以下两个文件

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


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

2.在JS代码中声明一个Document-definition对象,这个是pdfmake自己的术语。简单点说,就是创建一个至少包含content属性的对象。然后就可以调用pdfMake的方法导出PDF,具体见如下代码:

<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对象,声明当前要用到字体:

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>