JS導出PDF插件(支持中文、圖片使用路徑)
在WEB上想做一個導出PDF的功能,發現jsPDF比較多人推薦,遺憾的是不支持中文,最後找到pdfmake,很好地解決了此問題。它的效果可以先到http://pdfmake.org/playground.html查看。在使用過程中,還發現圖片的插入是相對繁瑣的一件事。
針對這些問題,本文的主要內容可分為三部分:
- 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的完整教程請登陸pdfmake項目查看。
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>
參考
- pdfmake如何使用自定義的字體
- pdfmake的項目
- pdfmake演示
JS導出PDF插件(支持中文、圖片使用路徑)