用js生成PDF的方案
在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 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>
引入依赖脚本
原理上需要先把需要保存的节点渲染到一个canvas,然后利用这个canvas制作pdf,所以先引入以下两个依赖代码文件:
<script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js"></script>
<script src="https://cdn.bootcss.com/jspdf/1.3.4/jspdf.debug.js"></script>
- 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
。