最近一朋友 研究使用js 导出pdf 研究了3 天多无果 我刚好看到了 就用半天时间 谷歌了下 发现了这个好东西 pdfMake
它可以使用自定义字体 支持图片等格式 不过添加的时候 要使用 node js grunt 这耗了几个小时的时间
这是继 我使用 php 导出 pdf word Excel text cvs json 后 又一个突破使用 js 在前端导出 支持中文的pdf 手动滑稽

pdfmake https://github.com/bpampuch/pdfmake
此教程主要包含
pdfmake 的使用
导入字体文件 并生成js
成功生成的pdf 示例
image-FZS3JW-带图-方正宋三简体
table-msyh-表格微软雅黑
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的完整教程请登陆pdfmake项目查看。
pdfmake支持中文
跟多相关信息 请看 文章末尾的 中文乱码 问题 的解决方案以及 node 生成 vfs_fonts.js 方法
三个步骤:
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>
中文乱码 问题 的解决方案以及 node 生成 vfs_fonts.js 方法
PDF乱码解决方法
因为DataTables调用的是pdfMake进行导出PDF,而pdfMake自带的字体无法支持中文,需要我们自己生成vfs_fonts.js进行替换,步骤如下:
安装Grunt及模块
首先运行如下命令:
npm install -g grunt-cli
再到pdfMake源代码目录运行:
npm install grunt --save-dev
之后再安装需要用到的模块:
npm install grunt-dump-dir --save-dev
建议使用淘宝的cnpm速度会快很多。
编译自定义vfs_fonts.js文件
将下载的ttf字体文件放到项目examples\fonts目录下,在pdfMake根目录下运行如下命令:
grunt dump_dir
如果运行成功将显示如下消息:
File “build/vfs_fonts.js” created. Done, without errors.
现在可以将build/vfs_fonts.js文件引到自己的项目中来,再进行注册自定义字体的设置。
在项目中注册自定义字体
这里需要对pdfMake.fonts进行注册自己的自定义字体,在页面头部进行javascript载入初始化
pdfMake.fonts = {
msyh: {
normal: 'msyh.ttf'
}
};
msyh是我自定义的字体名称,使用的是微软雅黑字体,在你的docDefinition中将默认字体设置成你前面向pdfMake注册过的字体就可以了。
var docDefinition = {
content: '测试pdfMake中文导出乱码',
defaultStyle: {
font: 'msyh'
}
};
node js 官网
在线编辑器 以及官网
Custom-Fonts—client-side
https://github.com/bpampuch/pdfmake