介紹
本篇博客主要是為了介紹如何使用:flying-saucer+itext+freemark實現導出復雜點的pdf文件。
思路
- 先把pdf的內容以html形式准備好
- 使用freemarker將html中的動態內容替換掉
- 使用flying-saucer生成pdf文件
下載jar包
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.5</version>
</dependency>
下載中文字體
搜索:simsun.ttc
宋體(對應css中的 屬性 font-family: SimSun; /宋體/)
准備好pdf的模板
把這個模板放到

<?xml version="1.0" encoding="UTF-8"?>
<!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" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>准考證</title>
<style>
@page { size: 13.8in 10.4in; }
</style>
<!--@page { size: 13.8in 10.4in; } 這個可以調整pdf頁面的大小很好用-->
</head>
<body>
<div style="width: 1200px; margin:0 auto; background: #fff; position: relative">
<img src="/isuyang/images/steam/students.jpg" width="1200px;" alt=""/>
<span style="color:#333; font-size:12.0pt; font-family: ; position: absolute; top: 63px; left:154px;">${certificateNumber}</span>
<span style="color:#333; font-size:20.0pt; font-family: SimSun; font-weight: bold; position: absolute; top: 320px; left:236px;width: 160px; text-align: center">${realName}</span>
<span style="color:#333; font-size:20.0pt; font-family: SimSun; font-weight: bold; position: absolute; top: 426px; left:236px;width: 140px; text-align: center">${division}</span>
<span style="color:#333; font-size:20.0pt; font-family: SimSun; font-weight: bold; position: absolute; top: 426px; left:428px;width: 140px; text-align: center">${grade}</span>
<span style="color:#333; font-size:20.0pt; font-family: SimSun; font-weight: bold; position: absolute; top: 426px; left:612px;width: 140px; text-align: center">${prize}</span>
</div>
</body>
</html>
配置Freemaker
添加引用
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
在src/main/resource目錄下創建ftl.properties文件

這個文件不一定非得放在這里,只是maven項目約定資源文件的目錄是在這。
ftl的文件內容:
classic_compatible=true
##如果變量為null,轉化為空字符串,比如做比較的時候按照空字符做比較
whitespace_stripping=true
##去掉多余的空格,非常有用
##模版更新事件,設置為1秒,正式環境設置為3600秒
#template_update_delay=3600
template_update_delay=1
##模版更新時間,這里配置是1秒更新一次,正式環境,模版不會改變,可以將這個值設很大,提高效率
##locale=zh_CN
##中國
default_encoding=utf-8
##編碼utf8
url_escaping_charset=utf-8
##url編碼utf8
date_format=yyyy-MM-dd
##顯示日期格式
time_format=HH:mm:ss
##顯示時間格式
datetime_format=yyyy-MM-dd HH:mm:ss
##顯示日期格式
number_format=0.######
##數字顯示格式
boolean_format=是,否
## boolean顯示格式
tag_syntax=auto_detect
##設置標簽類型 兩種:[] 和 <> 。[] 這種標記解析要快些
在ftl.xml文件里配置freemarker.template.Configuration

記得在spring-context.xml文件里引用這個文件,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"
default-lazy-init="true">
<description>ftl Configuration</description>
<!-- 加載配置屬性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:config/config.properties" />
<bean id="configuration" class="freemarker.template.Configuration"/>
</beans>
在啟動的時候加載配置
/**
* 啟動
* <p>
* 應用的啟動入口
*
* @author ZhuangJunxiang(529272571@qq.com)
* @Date 2017年4月10日
*/
@Component
public class Setup implements ServletContextAware {
@Override
public void setServletContext(final ServletContext context) {
WebApplicationContext act = ContextLoader.getCurrentWebApplicationContext();
Configuration ftlConf = (Configuration) act.getBean("configuration");
FtlUtil.initConfiguration(ftlConf, context, "/", "config/ftl.properties");
}
}
編寫Controller
/**
* 下載初賽的pdf證書
*
*/
@Void //這個是自定義注解,可以刪除
@RequestMapping
@ResponseBody
public void downloadChuSaiPrizePdf() {
stemViewService.downloadPdf();
}
編寫steamViewService
public void downloadPdf() {
String pdfPath = generatorPdf(getSteamSignup(), "student.ftl");
boolean exist = FileUtil.isExist(pdfPath);
if (!exist) {
throw ExceptionUtil.bEx("PDF不存在");
}
try {
DownloadUtil.download(MvcUtil.getResponse(), pdfPath);
} catch (Exception e) {
e.printStackTrace();
} finally {
FileUtil.deleteFile(pdfPath);
}
}
/**
* 生成pdf
*
* @param parameter
* @return TODO(這里描述每個參數,如果有返回值描述返回值,如果有異常描述異常)
*/
public String generatorPdf(Map<String, Object> parameter, String ftlPath) {
//Pdf的存放路徑:"G:\\pdf\\" 這里的路徑我是通過配置獲取的
String pdfPath = pdfConfig.getValue("pdfPath") + parameter.get("certificateNumber") + ".pdf";
//字體的存放路徑 這里也是通過配置獲取的:可以寫:G:\\pdf\\simsun.ttc
String font1 = pdfConfig.getValue("fontPath");
OutputStream os = null;
try {
os = new FileOutputStream(pdfPath);
ITextRenderer renderer = new ITextRenderer();
ResourceLoaderUserAgent callback = new ResourceLoaderUserAgent(renderer.getOutputDevice());
renderer.getSharedContext().setUserAgentCallback(callback);
callback.setSharedContext(renderer.getSharedContext());
//添加中文字體
renderer.getFontResolver().addFont(font1, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//這里的ftl路徑存放在WEB-INF/steam/下
String repAfterHtml = FtlUtil.build(configuration, "steam/" + ftlPath, parameter);
renderer.setDocumentFromString(repAfterHtml);
// 解決圖片的相對路徑問題,這里也可以寫:http://localhost:8080/的形式
renderer.getSharedContext().setBaseURL("http://static.isuyang.cn/");
renderer.layout();
renderer.createPDF(os);
os.close();
os = null;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// ignore
}
}
}
return pdfPath;
}
/**
* 資源加載代理
*
*/
private static class ResourceLoaderUserAgent extends ITextUserAgent {
public ResourceLoaderUserAgent(ITextOutputDevice outputDevice) {
super(outputDevice);
}
protected InputStream resolveAndOpenStream(String uri) {
InputStream is = super.resolveAndOpenStream(uri);
System.out.println("加載資源文件: " + uri);
return is;
}
}
DownloadUtil.java
package com.we.core.web.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import com.we.core.common.util.ExceptionUtil;
import com.we.core.common.util.FileUtil;
/**
* 下載方法
* @author lq
* @Date 2014-7-15
*/
public class DownloadUtil {
public static HttpServletResponse download(final HttpServletResponse response, final String path) throws Exception {
return download(response, path, null);
}
public static HttpServletResponse download(final HttpServletResponse response, final String path,
final String fileName) throws Exception {
File file = new File(path);
String fn = fileName;
if (com.we.core.common.util.Util.isEmpty(fileName)) {
fn = file.getName();
} else {
if (fileName.indexOf(".") == -1) {
fn = fileName + "." + FileUtil.getSuffix(file.getName());
}
}
InputStream fis = new BufferedInputStream(new FileInputStream(path));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
response.addHeader("Content-Disposition", "attachment;filename="
+ new String(fn.getBytes("gb2312"), "ISO8859-1"));
response.addHeader("Content-Length", "" + file.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
return response;
}
public static HttpServletResponse viewWord(final HttpServletResponse response, final String path) throws Exception {
try {
File file = new File(path);
String filename = file.getName();
InputStream fis = new BufferedInputStream(new FileInputStream(path));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
response.addHeader("Content-Disposition", "inline;filename="
+ new String(filename.getBytes("gb2312"), "ISO8859-1"));
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/pdf");
toClient.write(buffer);
toClient.flush();
toClient.close();
} catch (Exception e) {
throw ExceptionUtil.bEx("文件損壞,預覽失敗");
}
return response;
}
public HttpServletResponse viewTxt(final HttpServletResponse response, final String path) throws Exception {
try {
BufferedReader bis = null;
File file = new File(path);
InputStream in = new FileInputStream(file);
bis = new BufferedReader(new InputStreamReader(in));
StringBuffer buf = new StringBuffer();
String temp;
while ((temp = bis.readLine()) != null) {
buf.append(temp);
response.getWriter().write(temp);
if (buf.length() >= 1000) {
break;
}
}
bis.close();
} catch (Exception e) {
throw ExceptionUtil.bEx("文件損壞,預覽失敗");
}
return response;
}
}
效果

建議
最好參考官方文檔,因為好多寫的都是不全,還有就是在搜索百度的時候一定要搜索博客靠前的文章,可以使用百度的搜索工具過濾一下。
https://developers.itextpdf.com/examples/xml-worker-itext5/html-images
