項目當中通常會有生成pdf的需求,pdf的排版尤為重要!通過html生成,最為方便.
1. 依賴
工具使用freemarker模板進行數據渲染
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.18</version>
</dependency>
2. 工具類
import java.io.*;
import java.util.Locale;
import java.util.Map;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
public class PdfUtil {
/**
* 通過模板導出pdf文件
* @param data 數據
* @param templateFileName 模板文件名
* @throws Exception
*/
public static ByteArrayOutputStream createPDF(Map<String,Object> data, String templateFileName) throws Exception {
// 創建一個FreeMarker實例, 負責管理FreeMarker模板的Configuration實例
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
cfg.setClassForTemplateLoading(PdfUtil.class,"/templates");
ITextRenderer renderer = new ITextRenderer();
OutputStream out = new ByteArrayOutputStream();
try {
// 設置 css中 的字體樣式(暫時僅支持宋體和黑體) 必須,不然中文不顯示
renderer.getFontResolver().addFont("/static/font/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 設置模板的編碼格式
cfg.setEncoding(Locale.CHINA, "UTF-8");
// 獲取模板文件
Template template = cfg.getTemplate(templateFileName, "UTF-8");
StringWriter writer = new StringWriter();
// 將數據輸出到html中
template.process(data, writer);
writer.flush();
String html = writer.toString();
// 把html代碼傳入渲染器中
renderer.setDocumentFromString(html);
// 設置模板中的圖片路徑 (這里的images在resources目錄下) 模板中img標簽src路徑需要相對路徑加圖片名 如<img src="images/xh.jpg"/>
String url = PdfUtil.class.getClassLoader().getResource("static/images").toURI().toString();
renderer.getSharedContext().setBaseURL(url);
renderer.layout();
renderer.createPDF(out, false);
renderer.finishPDF();
out.flush();
return (ByteArrayOutputStream)out;
} finally {
if(out != null){
out.close();
}
}
}
}
代碼中需要注意路徑設置,否則會導致css和圖片引入無效
-
cfg.setClassForTemplateLoading(PdfUtil.class,"/templates");
指定FreeMarker模板文件的位置 -
renderer.getFontResolver().addFont("/static/font/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
指定字體文件,否則中文不顯示 -
PdfUtil.class.getClassLoader().getResource("static/images").toURI().toString();
指定模板中圖片路徑
靜態資源目錄結構:
3. 使用
建議使用時,先寫一個html靜態頁面,調試好了再復制到ftl文件中,保存成模板
靜態index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<style>
.center{
width: 380px;
height: 538px;
background: url("images/zs.png") center no-repeat;
margin: 10% auto;
font-family: SimSun;
position: relative;
}
.name{
position: absolute;
top: 216px;
left: 60px;
font-size: 20px;
width: 74px;
text-align: center;
display: block;
}
</style>
</head>
<body>
<div class="center">
<span class="name">李逍遙</span>
</div>
</body>
</html>
模板zhengshu.ftl
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title></title>
<style>
.center{
width: 380px;
height: 538px;
background: url("images/zs.png") center no-repeat;
margin: 15% auto;
font-family: SimSun;
position: relative;
}
.name{
position: absolute;
top: 216px;
left: 60px;
font-size: 20px;
width: 74px;
text-align: center;
display: block;
}
</style>
</head>
<body>
<div class="center">
<span class="name">${name}</span>
</div>
</body>
</html>
把需要設置數據的地方,用freemarker語法進行占位${}
單元測試
@Test
public void pdf() throws IOException {
ByteArrayOutputStream baos = null;
FileOutputStream out = null;
try {
Map<String,Object> data = new HashMap<>();
data.put("name", "李逍遙");
baos = PdfUtil.createPDF(data, "zhengshu.ftl");
String fileName = "獲獎證書.pdf";
File file = new File(fileName);
out = new FileOutputStream(file);
baos.writeTo(out);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(baos!=null){
baos.close();
}
if(out != null){
out.close();
}
}
}
使用controller
mport java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/pdf")
public class PdfController {
@RequestMapping("/export")
public void exportPdf(HttpServletResponse response) throws Exception{
ByteArrayOutputStream baos = null;
OutputStream out = null;
try {
// 模板中的數據,實際運用從數據庫中查詢
Map<String,Object> data = new HashMap<>();
data.put("name", "李逍遙");
baos = PdfUtil.createPDF(data, "zhengshu.ftl");;
// 設置響應消息頭,告訴瀏覽器當前響應是一個下載文件
response.setContentType( "application/x-msdownload");
// 告訴瀏覽器,當前響應數據要求用戶干預保存到文件中,以及文件名是什么 如果文件名有中文,必須URL編碼
String fileName = URLEncoder.encode("獲獎證書.pdf", "UTF-8");
response.setHeader( "Content-Disposition", "attachment;filename=" + fileName);
out = response.getOutputStream();
baos.writeTo(out);
baos.close();
} catch (Exception e) {
e.printStackTrace();
throw new Exception("導出失敗:" + e.getMessage());
} finally{
if(baos != null){
baos.close();
}
if(out != null){
out.close();
}
}
}
}
4. 橫向打印
有時網頁比較寬時,生成的pdf寬度不夠,導致顯示內容不完整,可以通過在模板css設置@page
控制
/*設置頁面寬高 A4大小*/
@page{size:297mm 210mm;}
參考: