Java通过Html(freemarker模板)生成PDF实战, 可支持商用
技术架构
springboot + freemarker + [pdfbox] + flying-saucer-pdf
生成流程:
- freemarker: 根据数据填充ftl模板文件,得到包含有效数据的html文件(包含页眉页脚页码的处理,和解决中文渲染等问题)。
- flying-saucer-pdf: 将html转换成PDF文件。
- pdfbox: 操作PDF文件,完成加解密等操作。
依赖包
<!-- springboot版本, 2.2.x也是支持的 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.3.0</version>
</dependency>
<!-- load freemarker template file -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!-- convert html to pdf -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.4.1</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-core</artifactId>
<version>9.4.1</version>
</dependency>
<!-- operate pdf, such as encypt/decypt,不做加解密可不引用 -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.24</version>
</dependency>
<!-- encrypt/decrypt zip -->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.11.5</version>
</dependency>
PS:网上很多文章使用 itext5/7来生成PDF的,用于个人学习或者开源项目确实没问题,但是不方便用于公司商业项目,因为itext是基于AGPL 的开源协议,商用是需要收费的,当然贵司愿意付费也是okay的。
1. 准备PDF的模板文件(freemarker文件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>PDF Demo Title</title>
<style>
@page {
size: A4;
margin: 35mm 10mm 23mm 10mm;
@top-center {
content: element(headerTop);
}
@bottom-center {
content: element(footerBottom);
}
}
#pagenumber:before {
content: counter(page);
}
#pagecount:before {
content: counter(pages);
}
.headerTop {
position: running(headerTop);
color: white;
}
.footerBottom {
color: #777E90;
position: running(footerBottom);
margin-top: 10mm;
}
* {
padding: 0;
margin: 0;
}
html,
body {
/*优先加载 Poppins英文字体,无法渲染则使用PingFang中文字体*/
font-family: Poppins-Medium, PingFang, sans-serif;
margin: 0 auto;
}
.content {
color: #23262F;
font-size: 8px;
padding: 0 0;
margin-top: 0;
}
.size16 {
font-size: 16px;
}
.size12 {
font-size: 12px;
}
.size10 {
font-size: 10px;
}
.size8 {
font-size: 8px;
}
.lineHeight18 {
line-height: 18px;
}
.weight600 {
font-weight: 600;
}
.weight500 {
font-weight: 500;
}
.padding18 {
padding: 18px;
}
.marginBottom8 {
margin-bottom: 8px;
}
.marginLeft6 {
margin-left: 6px;
}
.marginLeft24 {
margin-left: 24px;
}
.marginLeft16 {
margin-left: 16px;
}
.marginLeft13 {
margin-left: 13px;
}
.marginLeft4 {
margin-left: 4px;
}
.marginTop4 {
margin-top: 4px;
}
.marginTop8 {
margin-top: 8px;
}
.width140 {
width: 140px;
}
.widthFull {
width: 100%;
}
.heightFull {
height: 100%;
}
.textAlignCenter {
text-align: center;
}
.inlineCenter {
display: inline-block;
vertical-align: top;
}
.backGray {
background-color: #F7F7F7;
}
.backBlue {
background-color: #0E59F0;
}
.border {
border: 1px solid #E5E5E5;
}
.colorBlue {
color: #6E9BF6;
}
.colorDark {
color: #777E90;
}
.colorBlack {
color: #23262F;
}
.colorBottomLine {
background-color: #6E9BF6;
}
.colorGray {
color: #838CA4;
}
.table {
border-spacing: 0;
/*跨页表格标题*/
-fs-table-paginate: paginate;
}
.table>thead>tr>th,
.table>tbody>tr>td {
padding: 16px 4px;
text-align: left;
word-break: break-all;
word-wrap: break-word;
white-space: normal;
page-break-inside: avoid;
page-break-after: auto;
}
.table>thead>tr>th {
color: #838CA4;
font-weight: 400;
size: 12px;
}
.positionAbsolute {
position: absolute;
}
.rightBottom8 {
top: 80px;
right: 30px;
}
.height8 {
height: 8px;
}
.height6 {
height: 6px;
}
.height16 {
height: 16px;
}
.right0 {
right: 0;
}
.lineGary {
height: 1px;
background-color: #E5E5E5;
}
</style>
</head>
<body>
<!-- 页眉 -->
<div class="headerTop">
<img class="widthFull" style="margin-left: -1px" width="716px"
src="data:image/jpeg;base64,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" />
<div class="positionAbsolute rightBottom8 size12 weight500">${headerDate}</div>
</div>
<!-- 页脚 -->
<div class="footerBottom">
<div class="height6 colorBottomLine"></div>
<div class="marginTop8 size10">
<div class="inlineCenter">footer name
<span class="marginLeft4 colorBlack">
<![CDATA[${footerName}]]>
</span>
</div>
<!-- 页码 -->
<div class="inlineCenter colorBlack positionAbsolute right0">
<span id="pagenumber"></span>/<span id="pagecount"></span>
</div>
</div>
</div>
<div class="content">
<!-- 用户信息 -->
<div class="border padding18">
<div class="colorBlue marginBottom8 weight500 size16">
<![CDATA[${userName}]]>
</div>
<div class="">
<span>Account Name</span>
<span class="colorBlue marginLeft6 weight500">
<![CDATA[${accountName}]]>
</span>
<span class="marginLeft24">Account ID</span>
<span class="colorBlue marginLeft6 weight500">
<![CDATA[${accountId}]]>
</span>
</div>
</div>
<div class="height8"></div>
<!-- 表格 -->
<section style="display: ${display}">
<div class="height16"></div>
<div class="">
<span class="colorBlack size16 weight600 marginLeft4 inlineCenter">Deposit</span>
</div>
<div class="marginTop8 weight500 colorBlack size10">Deposit History</div>
<div class="height16"></div>
<div class="lineGary"></div>
<div class="marginTop8">
<table class="widthFull table">
<thead>
<tr class="backGray">
<th>Currency</th>
<th>Request ID</th>
<th>Bank Name</th>
<th>Bank Number</th>
<th>Amount</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<#if list?? && (list?size> 0)>
<#list list as detail>
<tr>
<td>${detail.tokenCode}</td>
<td>${detail.orderNo}</td>
<td>
<#if detail.bankName??>
<![CDATA[${detail.bankName}]]>
<#else>-
</#if>
</td>
<td>
<#if detail.bankNumber??>
<#-- 处理特殊字符的渲染 -->
<![CDATA[${detail.bankNumber}]]>
<#else>-
</#if>
</td>
<td>
<#if detail.qty??>${detail.qty}<#else>-</#if>
</td>
<td>
<#if detail.updateTime??>${detail.updateTime}<#else>-</#if>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
</div>
<div class="height16"></div>
<div class="lineGary"></div>
</section>
</div>
</body>
</html>
freemarker模板注意事项:
- 图片需要通过base64的方式加载,直接加载图片路径可能无法渲染
- 字体名称需要和Java代码中加载的字体名称保持一致,中文无法渲染可能是没有设置别名
- 替换的变量,如果有null值需要在模板中判断
- 如果填充的变量中存在特殊字符,通过<![CDATA[${变量名}]]> 方式设置
- 部分高级的CSS样式或者标签可能不支持
- 页眉页脚采用running的方式处理
2. Java Code
2.1 FreeMarkerUtils
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@Slf4j
public class FreeMarkerUtils {
private static Template getTemplate(String templateFileName) {
Configuration configuration = new Configuration(Configuration.VERSION_2_3_29);
Template template = null;
try {
configuration.setObjectWrapper(new DefaultObjectWrapper());
//设置编码格式
configuration.setDefaultEncoding("UTF-8");
//模板文件
configuration.setClassForTemplateLoading(FreeMarkerUtils.class, "/templates");
template = configuration.getTemplate(templateFileName + ".ftl", StandardCharsets.UTF_8.toString());
} catch (IOException e) {
e.printStackTrace();
log.error("get template file failed, fileName:{}", templateFileName);
}
return template;
}
public static String generateHtmlStr(Map<String, Object> variables, String templateFileName) {
Template template = getTemplate(templateFileName);
StringWriter stringWriter = new StringWriter();
template.setEncoding("UTF-8");
try (BufferedWriter writer = new BufferedWriter(stringWriter)) {
template.process(variables, writer);
String htmlStr = stringWriter.toString();
writer.flush();
return htmlStr;
} catch (TemplateException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 删除xml无法识别的非法字符
* @param content
* @return
*/
public static String removeIllegalChar(String content) {
return content.replaceAll("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", "");
}
}
2.2 PdfTest Code
import com.janche.pdf.utils.FileUtil;
import com.janche.pdf.utils.FreeMarkerUtils;
import com.janche.pdf.vo.PdfVo;
import com.lowagie.text.pdf.BaseFont;
import freemarker.cache.ClassTemplateLoader;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
/**
* @Description:
* @Auther: lirong
* @Date: 2024/04/16
*/
@Slf4j
public class PdfTest {
public static void main(String[] args) {
ITextRenderer renderer = new ITextRenderer();
try {
addPdfFont(renderer);
String htmlStr = FreeMarkerUtils.generateHtmlStr(loadPdfData(), "pdfTemplate");
renderer.setDocumentFromString(FreeMarkerUtils.removeIllegalChar(htmlStr));
renderer.layout();
} catch (IOException e) {
throw new RuntimeException(e);
}
File pdfFile = new File("output/demo.pdf");
File encryptPdfFile = new File("output/demo_encrypt.pdf");
File zipFile = new File("output/demo.zip");
try (OutputStream os = new FileOutputStream(pdfFile)) {
renderer.createPDF(os);
// encrypt pdf, if needn't encrypt pdf file, can remove pdfBox dependency
PDDocument document = PDDocument.load(pdfFile);
StandardProtectionPolicy policy = new StandardProtectionPolicy("123456", "1234", new AccessPermission());
policy.setEncryptionKeyLength(128);
policy.setPermissions(new AccessPermission());
document.protect(policy);
document.save(encryptPdfFile);
document.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
log.info("PDF file generated successfully!");
}
private static void addPdfFont(ITextRenderer renderer) throws IOException {
// add English font
ClassTemplateLoader classTemplateLoader = new ClassTemplateLoader(FreeMarkerUtils.class, "/static/font");
String enFontPath = classTemplateLoader.getBasePackagePath() + "Poppins-Medium.ttf";
ITextFontResolver fontResolver = renderer.getFontResolver();
// the first font needn't set alias
fontResolver.addFont(enFontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// add Chinese font, the second font must set alias
String chFontPath = classTemplateLoader.getBasePackagePath() + "PingFang-Regular.ttf";
fontResolver.addFont(chFontPath, "PingFang", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED, null);
}
public static Map<String, Object> loadPdfData() {
Map<String, Object> data = new HashMap<>();
// 启用
data.put("display", "block");
// 隐藏
// data.put("display", "none");
data.put("footerName", "footer-龍");
data.put("userName", "龍年發财");
data.put("accountName", "Jackson-龍");
data.put("accountId", "234324");
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
data.put("headerDate", now.format(dateFormatter));
ArrayList<PdfVo> tableList = new ArrayList<>();
IntStream.range(1, 20).forEach(i -> {
LocalDateTime dateTime = now.plusDays(i);
PdfVo pdfVo = PdfVo.builder()
.tokenCode("USD" + i)
.bankName("HK Bank" + i)
.bankNumber("&23**94&" + i)
.qty(BigDecimal.TEN.add(new BigDecimal(i)))
.orderNo("ab123445" + i)
.updateTime(dateTime.format(dateFormatter))
.build();
tableList.add(pdfVo);
});
data.put("list", tableList);
return data;
}
}
PS:
- 关于中文字体不显示的问题: 一般是字体未正确加载,或者读取时字体名称不正确,完全不需要去更改什么源码class文件,高版本的flying-saucer-pdf 早已支持。
- 加载多个字体时,后面的字体需要设置别名,ftl模板中也需要使用设置的别名。
字体文件:(可在文章底部的github项目中获取)
- PingFang-Regular.ttf 中文字体
- Poppins-Medium.ttf 英文字体
3. PDF展示
Github源码下载:https://github.com/Janche/springboot-html2pdf-demo