Flying Saucer生成pdf报表

时间:2021-11-01 21:07:04

使用Flying Saucer生成pdf报表:


1.首先看一下Flying Saucer的官方介绍:

https://github.com/flyingsaucerproject/flyingsaucer

OVERVIEW

Flying Saucer is a pure-Java library for rendering arbitrary well-formed XML (or XHTML) using CSS 2.1 for layout and formatting, output to Swing panels, PDF, and images.

Comprehensive documentation available in our user's guide, linked from our website at http://code.google.com/p/flying-saucer/ For information on our development releases, please contact us on our mailing lists.

If you end up using Flying Saucer for your own projects, please drop us an email and tell us about it; it helps inform where we go next, and is interesting and inspiring to other developers.

可以看到Flying Saucer可以完美的解析html,并且可以使用css2.1的样式,添加图片等功能。


2.简单的使用:

业务上经常有这样的场景:

将用户信息查询出来并且显示在页面上设计好的table上。并且可以选择下载。

解决方法:

1.)使用java模板(velocity、freemarker等)定义好表格样式,这里我们使用freemarker

引入freemarker

<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>

创建ftl模板

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.tg {
border-collapse: collapse;
border-spacing: 0;
}

.tg td {
font-family: 'Arial Unicode MS';
font-size: 14px;
padding: 16px 5px;
border-style: solid;
border-width: 1px;
overflow: hidden;
word-br/eak: normal;
}

.tg th {
font-family:'Arial Unicode MS';
font-size: 14px;
font-weight: normal;
padding: 16px 5px;
border-style: solid;
border-width: 1px;
overflow: hidden;
word-break: normal;
}

.tg .tg-s6z2 {
text-align: center
}

.tg .tg-baqh {
text-align: center;
vertical-align: top
}

.tg .tg-yw4l {
vertical-align: center;
}

.tg .tg-noborder {
border:none;
}

</style>
</head>

<body style="font-family:'Arial Unicode MS'">
<div style="width: 700px;margin: 0 auto;">
<h4 style="text-align: center;">Interview Assessment Form</h4>
<table class="tg" style="width: 100%;table-layout: fixed; word-break:break-strict;">
<tr>
<td class="tg-031e" colspan="5">Candidate:<span>${candidate}</span></td>
<td class="tg-031e" colspan="5">Position:<span>${position}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="5">Source:<span>${source}</span></td>
<td class="tg-031e" colspan="5">Last Interview Date:<span>${lastInterviewDate}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="2">Paper Test</td>
<td class="tg-031e" colspan="8"><span>${testPaper}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="2">Test Date</td>
<td class="tg-s6z2">IQ<br/>Single<br/>choice</td>
<td class="tg-s6z2">IQ<br/>Multiple<br/>choice</td>
<td class="tg-s6z2">Logical<br/>Single<br/>choice</td>
<td class="tg-s6z2">Logical<br/>Multiple<br/>choices</td>
<td class="tg-s6z2">English<br/>short<br/>answer</td>
<td class="tg-s6z2">JAVA<br/>program-<br/>ming</td>
<td class="tg-s6z2">.NET<br/>program-<br/>ming</td>
<td class="tg-baqh">Total<br/>Score</td>
</tr>
<tr>
<td class="tg-s6z2" colspan="2" rowspan="3"><span>${testDate}</span></td>
<td class="tg-s6z2"><span>${iqSingleScore}</span></td>
<td class="tg-s6z2"><span>${iqMultipleScore}</span></td>
<td class="tg-s6z2"><span>${logicalSingleScore}</span></td>
<td class="tg-s6z2"><span>${logicalMultipleScore}</span></td>
<td class="tg-s6z2"><span>${englishScore}</span></td>
<td class="tg-s6z2"><span>${javaProScore}</span></td>
<td class="tg-s6z2"><span>${netProScore}</span></td>
<td class="tg-s6z2" rowspan="3"><span>${totalScore}</span></td>
</tr>
<tr>
<td class="tg-s6z2">Comment</td>
<td class="tg-s6z2">Comment</td>
<td class="tg-s6z2">Comment</td>
<td class="tg-s6z2">Comment</td>
<td class="tg-s6z2">Comment</td>
<td class="tg-s6z2">Comment</td>
<td class="tg-s6z2">Comment</td>
</tr>
<tr>
<td class="tg-s6z2"><span>${iqSingleCom}</span></td>
<td class="tg-s6z2"><span>${iqMultipleCom}</span></td>
<td class="tg-s6z2"><span>${logicalSingleCom}</span></td>
<td class="tg-s6z2"><span>${logicalMultipleCom}</span></td>
<td class="tg-s6z2"><span>${englishCom}</span></td>
<td class="tg-s6z2"><span>${javaProCom}</span></td>
<td class="tg-s6z2"><span>${netProCom}</span></td>
</tr>
<tr>
<td class="tg-s6z2" colspan="2" rowspan="6">First interview</td>
<td class="tg-031e" colspan="4">Verbal English -40</td>
<td class="tg-yw4l" colspan="4"><span>${verbalEngScore}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="4">Optional -40</td>
<td class="tg-yw4l" colspan="4"><span>${optionalScore}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="4">MAP -30</td>
<td class="tg-yw4l" colspan="4"><span>${mapScore}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="4">Total Score -110</td>
<td class="tg-yw4l" colspan="4"><span>${firstIntTotalScore}</span></td>
</tr>
<tr>
<td class="tg-031e" colspan="8" rowspan="2">Comments:<span>${firstIntComment}</span><br/><br/>
<span style="vertical-align: bottom;float: right;">Interviewer:${firstInterviewer}</span></td>
</tr>
<tr>
</tr>
<tr>
<td class="tg-s6z2" colspan="2" rowspan="3">Second interview</td>
<td class="tg-031e" colspan="8" rowspan="3">Comments:<span>${secondIntComment}</span><br/><br/>
<span style="vertical-align: bottom;float: right;">Interviewer:${secondInterviewer}</span>
</td>
</tr>
<tr>
</tr>
<tr>
</tr>
<tr>
<td class="tg-s6z2" colspan="2" rowspan="3">Third interview</td>
<td class="tg-031e" colspan="8" rowspan="3">Comments:<span>${thirdIntComment}</span><br/><br/>
<span style="vertical-align: bottom;float: right;">Interviewer:${thirdInterviewer}</span>
</td>
</tr>
<tr>
</tr>
<tr>
</tr>
<tr>
<td class="tg-031e" colspan="10"> Final Suggestion:<span>${finalSuggestion}</span><br/></td>
</tr>
<tr>
<td class="tg-noborder" colspan="5">Applicant decision:<span>${decision}</span></td>
<td class="tg-noborder" colspan="5">Reason:<span>${reason}</span></td>
</tr>
</table>
</div>
</body>
</html>
通过freemarker在模板中注入需要填写的变量

将模板放在项目中的template包中。

2)使用Flying Saucer

引入jar包

点击下载项目所需Jar包

创建导出方法:

public void convert() throws Exception {
FileOutputStream os = null;
try {
// 创建一个freemarker.template.Configuration实例,它是存储 FreeMarker
// 应用级设置的核心部分
// 指定版本号
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
// 设置模板目录
cfg.setDirectoryForTemplateLoading(new File("src/main/java/templates"));
// 设置默认编码格式
cfg.setDefaultEncoding("UTF-8");

// 数据
Map<String, Object> product = new HashMap<String, Object>();
product.put("candidate", "");
product.put("position", "");
product.put("source", "");
product.put("lastInterviewDate", "");
product.put("testPaper", "");
product.put("testDate", "");
product.put("iqSingleScore", "");
product.put("iqMultipleScore", "");
product.put("logicalSingleScore", "");
product.put("logicalMultipleScore", "");
product.put("englishScore", "");
product.put("javaProScore", "");
product.put("netProScore", "");
product.put("totalScore", "");
product.put("iqSingleCom", "");
product.put("iqMultipleCom", "");
product.put("logicalSingleCom", "");
product.put("logicalMultipleCom", "");
product.put("englishCom", "");
product.put("javaProCom", "");
product.put("netProCom", "");
product.put("verbalEngScore", "");
product.put("optionalScore", "");
product.put("mapScore", "");
product.put("firstIntTotalScore", "");
product.put("firstIntComment", "");
product.put("firstInterviewer", "");
product.put("secondIntComment", "");
product.put("secondInterviewer", "");
product.put("thirdIntComment", "");
product.put("thirdInterviewer", "");
product.put("finalSuggestion", "");
product.put("decision", "");
product.put("reason", "");

// 从设置的目录中获得模板
Template temp = cfg.getTemplate("internInterview.ftl");
//从模板生成html文件
String file1 = "test.html";
File file = new File(file1);
if (!file.exists()) {
file.createNewFile();
}
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));
temp.process(product, out);

// 关闭
out.close();

ITextRenderer renderer = new ITextRenderer();
os = new FileOutputStream("interview.pdf");
renderer.setDocument(new File(file1).toURI().toURL().toString());
//获取中文字体
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

renderer.layout();
renderer.createPDF(os);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null)
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}




这里需要注意的地方: 

为了能够正确的显示中文:

// 解决中文支持问题    
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
这里引用了arialuni.ttf字体,它位于C盘windows/fonts文件夹下,将它引用后,还需要在页面上使用这个字体
同时
<body style="font-family:'Arial Unicode MS'">  
许多人有这样一个问题——按照以上两个步骤做了之后,页面中还是没有中文,这时,请检查你引用的css文件,其中一定设置了其它字体,只需将它去掉即可  


3)注意:

a.上述代码纯粹测试,使用者自行封装。

b.如果模板引入css,请引入css在系统中的绝对路径

c.linux显示中文:

fontResolver.addFont("/usr/share/fonts/TTF/ARIALUNI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 

d.如果引入图片,链接中输入图片在系统中的绝对路径

e.欢迎交流