PDF导出工具有itext,但是itext对中文支持不好,还有样式CSS支持也不好,使用IReport比较复杂,上手不太容易,怎么办?
幸好有Flying-Saucer这个项目,帮助我们解决了以上问题!Flying-Saucer最重要的是很方便,不需要使用IReport的复杂操作,只会写html就能够做PDF导出的模板。
使用freemarker的思路
1、编写ftl模板
2、使用freemarker生成html
3、根据生成的html在生成PDF
来一张图片,否则无图无真相
项目依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<
properties
>
<
servlet
>3.1.0</
servlet
>
<
freemarker
>2.3.22</
freemarker
>
<
flying-saucer
>9.0.3</
flying-saucer
>
</
properties
>
<
dependencies
>
<
dependency
>
<
groupId
>javax.servlet</
groupId
>
<
artifactId
>javax.servlet-api</
artifactId
>
<
version
>${servlet}</
version
>
<
scope
>provided</
scope
>
</
dependency
>
<
dependency
>
<
groupId
>org.freemarker</
groupId
>
<
artifactId
>freemarker</
artifactId
>
<
version
>${freemarker}</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.xhtmlrenderer</
groupId
>
<
artifactId
>flying-saucer-pdf</
artifactId
>
<
version
>${flying-saucer}</
version
>
</
dependency
>
</
dependencies
>
|
依赖关系图
项目结构图
主要的操作类
PdfUtils
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
package
org.xdemo.example.pdf;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.OutputStream;
import
java.util.HashMap;
import
java.util.Map;
import
javax.servlet.http.HttpServletResponse;
import
org.xhtmlrenderer.pdf.ITextRenderer;
import
com.lowagie.text.DocumentException;
import
freemarker.core.ParseException;
import
freemarker.template.MalformedTemplateNameException;
import
freemarker.template.TemplateException;
import
freemarker.template.TemplateNotFoundException;
/**
* PDF生成工具类
* @author Goofy <a href="http://www.xdemo.org">http://www.xdemo.org</a>
*
*/
public
class
PdfUtils {
public
static
void
main(String[] args) {
try
{
Map<Object, Object> o=
new
HashMap<Object, Object>();
o.put(
"name"
,
"http://www.xdemo.org/"
);
String path=PdfHelper.getPath();
generateToFile(path,
"resources/tpl.ftl"
,path+
"resources/"
, o,
"D:\\xdemo.pdf"
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
/**
* 生成PDF到文件
* @param ftlPath 模板文件路径(不含文件名)
* @param ftlName 模板文件吗(不含路径)
* @param imageDiskPath 图片的磁盘路径
* @param data 数据
* @param outputFile 目标文件(全路径名称)
* @throws Exception
*/
public
static
void
generateToFile(String ftlPath,String ftlName,String imageDiskPath,Object data,String outputFile)
throws
Exception {
String html=PdfHelper.getPdfContent(ftlPath, ftlName, data);
OutputStream out =
null
;
ITextRenderer render =
null
;
out =
new
FileOutputStream(outputFile);
render = PdfHelper.getRender();
render.setDocumentFromString(html);
if
(imageDiskPath!=
null
&&!imageDiskPath.equals(
""
)){
//html中如果有图片,图片的路径则使用这里设置的路径的相对路径,这个是作为根路径
render.getSharedContext().setBaseURL(
"file:/"
+imageDiskPath);
}
render.layout();
render.createPDF(out);
render.finishPDF();
render =
null
;
out.close();
}
/**
* 生成PDF到输出流中(ServletOutputStream用于下载PDF)
* @param ftlPath ftl模板文件的路径(不含文件名)
* @param ftlName ftl模板文件的名称(不含路径)
* @param imageDiskPath 如果PDF中要求图片,那么需要传入图片所在位置的磁盘路径
* @param data 输入到FTL中的数据
* @param response HttpServletResponse
* @return
* @throws TemplateNotFoundException
* @throws MalformedTemplateNameException
* @throws ParseException
* @throws IOException
* @throws TemplateException
* @throws DocumentException
*/
public
static
OutputStream generateToServletOutputStream(String ftlPath,String ftlName,String imageDiskPath,Object data,HttpServletResponse response)
throws
TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException, DocumentException{
String html=PdfHelper.getPdfContent(ftlPath, ftlName, data);
OutputStream out =
null
;
ITextRenderer render =
null
;
out = response.getOutputStream();
render = PdfHelper.getRender();
render.setDocumentFromString(html);
if
(imageDiskPath!=
null
&&!imageDiskPath.equals(
""
)){
//html中如果有图片,图片的路径则使用这里设置的路径的相对路径,这个是作为根路径
render.getSharedContext().setBaseURL(
"file:/"
+imageDiskPath);
}
render.layout();
render.createPDF(out);
render.finishPDF();
render =
null
;
return
out;
}
}
|
辅助类
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package
org.xdemo.example.pdf;
import
java.io.File;
import
java.io.IOException;
import
java.io.StringWriter;
import
java.util.Locale;
import
org.xhtmlrenderer.pdf.ITextRenderer;
import
com.lowagie.text.DocumentException;
import
com.lowagie.text.pdf.BaseFont;
import
freemarker.core.ParseException;
import
freemarker.template.Configuration;
import
freemarker.template.MalformedTemplateNameException;
import
freemarker.template.Template;
import
freemarker.template.TemplateException;
import
freemarker.template.TemplateNotFoundException;
/**
* PDF生成辅助类
* @author Goofy <a href="http://www.xdemo.org">http://www.xdemo.org</a>
*
*/
@SuppressWarnings
(
"deprecation"
)
public
class
PdfHelper {
public
static
ITextRenderer getRender()
throws
DocumentException, IOException {
ITextRenderer render =
new
ITextRenderer();
String path = getPath();
//添加字体,以支持中文
render.getFontResolver().addFont(path +
"resources/ARIALUNI.TTF"
, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
render.getFontResolver().addFont(path +
"resources/SIMSUN.TTC"
, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
return
render;
}
//获取要写入PDF的内容
public
static
String getPdfContent(String ftlPath, String ftlName, Object o)
throws
TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException {
return
useTemplate(ftlPath, ftlName, o);
}
//使用freemarker得到html内容
public
static
String useTemplate(String ftlPath, String ftlName, Object o)
throws
TemplateNotFoundException, MalformedTemplateNameException, ParseException, IOException, TemplateException {
String html =
null
;
Template tpl = getFreemarkerConfig(ftlPath).getTemplate(ftlName);
tpl.setEncoding(
"UTF-8"
);
StringWriter writer =
new
StringWriter();
tpl.process(o, writer);
writer.flush();
html = writer.toString();
return
html;
}
/**
* 获取Freemarker配置
* @param templatePath
* @return
* @throws IOException
*/
private
static
Configuration getFreemarkerConfig(String templatePath)
throws
IOException {
Configuration config =
new
Configuration();
config.setDirectoryForTemplateLoading(
new
File(templatePath));
config.setEncoding(Locale.CHINA,
"utf-8"
);
return
config;
}
/**
* 获取类路径
* @return
*/
public
static
String getPath(){
return
PdfHelper.
class
.getResource(
""
).getPath().substring(
1
);
}
}
|
模板文件
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
<!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"
>
<
head
>
<
title
></
title
>
<
style
type
=
"text/css"
>
body {
margin-left: 45px;
margin-right: 45px;
font-family: Arial Unicode MS;
font-size: 10px;
}
table {
margin: auto;
width: 100%;
border-collapse: collapse;
border: 1px solid #444444;
}
th,td {
border: 1px solid #444444;
font-size: 10px;
margin-left: 5px;
}
.mcContent {
line-height: 180%;
padding: 20px;
}
.logo {
text-align: center;
}
.title {
text-align: center;
font-weight: bold;
font-size: 20px;
}
.notes {
font-weight: normal;
margin-left: 5px;
margin-right: 5px;
line-height: 18px;
}
.text_content {
margin-left: 5px;
margin-right: 5px;
line-height: 18px;
}
.sum_insured_first_row {
width: 20%;
}
.sum_insured_span {
font-size: 10px;
}
.special_agreements_div {
page-break-before: always;
font-size: 14px;
margin-top: 20px;
}
.special_agreements_div .special_agreements {
font-size: 18px;
font-weight: bold;
}
.title_right {
width: 100%;
margin: 0 auto;
}
.title_right p {
text-align: left;
margin: 0;
margin-left: 50%;
padding: 0;
}
@page {
size: 8.5in 11in;
@
bottom-center
{
content
:
"page "
counter(
page
)
" of "
counter(
pages
);
}
.signature {
margin: 0 auto;
clear: both;
font-size: 16px;
font-weight: bold;
}
.signature_table {
/* font-size: 16px; */
font-weight: bold;
}
</
style
>
</
head
>
<
body
>
作者:<
a
href
=
"http://www.xdemo.org/"
>http://www.xdemo.org/</
a
>
<
div
>
<
p
>你好:${name}</
p
>
<
div
class
=
"logo"
>
<!--这里的图片使用相对与ITextRenderer.getSharedContext().setBaseURL("file:/"+imageDiskPath);的路径-->
图片支持<
img
src
=
"logo1.png"
/>
</
div
>
<
div
>
<
p
>Hello PDF: 中文支持</
p
>
<
div
style
=
"border:1px solid red;color:red;"
>
样式支持,红边框,红字
</
div
>
<
div
style
=
"border:10px solid blue;color:blue;"
>
样式支持,蓝色10像素的边框,蓝字
</
div
>
<
hr
/>
<
table
>
<
tr
style
=
"background:gray;"
>
<
th
>A</
th
>
<
th
>B</
th
>
<
th
>C</
th
>
<
th
>D</
th
>
</
tr
>
<
tr
>
<
td
>100</
td
>
<
td
>29</
td
>
<
td
>32</
td
>
<
td
>43</
td
>
</
tr
>
<
tr
>
<
td
>100</
td
>
<
td
>29</
td
>
<
td
>32</
td
>
<
td
>43</
td
>
</
tr
>
<
tr
>
<
td
>100</
td
>
<
td
>29</
td
>
<
td
>32</
td
>
<
td
>43</
td
>
</
tr
>
<
tr
>
<
td
>100</
td
>
<
td
>29</
td
>
<
td
>32</
td
>
<
td
>43</
td
>
</
tr
>
<
tr
>
<
td
>100</
td
>
<
td
>29</
td
>
<
td
>32</
td
>
<
td
>43</
td
>
</
tr
>
</
table
>
</
div
>
</
div
>
</
body
>
</
html
>
|
注意
本工具类提供了两种字体支持中文,所以定义样式的时候字体只能用:simsun或者arial unicode MS,否则中文显示不出来,如需其他字体另行添加),另外请注意图片的路径问题,html中z如果有图片,图片的路径则使用这里设置的路径的相对路径,这个是作为根路径,如
1
2
3
4
|
<
div
class
=
"logo"
>
<!--这里的图片使用相对与ITextRenderer.getSharedContext().setBaseURL("file:/"+imageDiskPath);的路径-->
图片支持<
img
src
=
"logo1.png"
/>
</
div
>
|
如果需要PDF的下载,可以通过generateToServletOutputStream这个方法来获取PDF的输出流,然后通过response写到客户端去
转载请注明来源: http://www.xdemo.org/flying-saucer-html-freemarker-pdf/