博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用js生成PDF的方案
阅读量:6689 次
发布时间:2019-06-25

本文共 8061 字,大约阅读时间需要 26 分钟。

在java里,我们常用Itext来生成pdf,在pdf文件里组合图片,文字,画表格,画线等操作,还会遇到中文支持的问题。

那好,现在想直接在web前端就生成pdf怎么办,目前有以下几个解决方案

1:JSPDF.js

这个库支持不同类型的PDF文件格式,包括:文本,数字,图形,图片,同时你可以自由的编辑标题或者其它类型元素。

还支持互动的内容制作,例如,你可以输入文字或者数字,然后jsPDF帮助生成最后的PDF内容。

缺点,不支持中文,不支持svg导入(解决方案:svg+html转换成canvas)

示例代码如下:

<!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.包含以下两个文件

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>
 
 
 
 
 

引入依赖脚本

原理上需要先把需要保存的节点渲染到一个canvas,然后利用这个canvas制作pdf,所以先引入以下两个依赖代码文件:

 
  • 1
  • 2

编写所需代码

假设所需要打印的dom的父容器的class名是right-aside,就可以写这样一个函数:

function () {  var target = document.getElementsByClassName("right-aside")[0]; target.style.background = "#FFFFFF"; html2canvas(target, { 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; //页面偏移 var position = 0; //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 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"); } }) }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

以上代码将会以a4纸的尺寸来生成pdf文件,可以分页,代码来自于网络!

坑点提示

jsPDF打印成pdf文件时,注意页面要回到打印区域的顶部,我在该demo设置了先回到页面顶部,再打印,这样就不会出现黑块了。

 

jsPDF打印成pdf文件时,注意设置打印区域dom的背景色为白色,即#FFFFFF

转载地址:http://djkoo.baihongyu.com/

你可能感兴趣的文章
40.C#--面对对象,类的继承和构造函数继承的使用
查看>>
列表,元组,集合
查看>>
jquery easyui滚动条部分设置介绍
查看>>
cannot find -lxxx问题
查看>>
预防云端开源项目打包 Redis Labs再更改模块
查看>>
超惊人!去年发生的身分外泄安全事件是2017的4倍
查看>>
oracle sqlplus免安装的配置instantclient-basiclite
查看>>
Java开发GUI之选择列表
查看>>
一、分布式商城架构逻辑图
查看>>
find命令详解
查看>>
2018年的“核心期刊陷阱”已开启,你知道吗?2018年的“核心期刊陷阱”已开启,你知道吗?...
查看>>
rsync+shell脚本完成自动化备份
查看>>
如何衡量机器人用激光雷达的实用性和可靠性
查看>>
机器人是如何完成避障的?机器人避障解决方案解读
查看>>
利用思维导图软件绘制鱼骨图怎样做
查看>>
mac os 安装maven
查看>>
结构体数组存放学生信息并排序输出
查看>>
UI设计师有什么实用价值?
查看>>
默认路由与静态的配置
查看>>
通过错误堆栈信息和源码分析错误来源
查看>>