在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>