1、引入依赖

<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.22</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>

2、模板文件 test.ftl

<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title></title>
<style>
body {
font-family: SimSun;
padding: 30px 20px 0;
}
.outTable{
margin-bottom: 20px;
}
table {
border-collapse: collapse;
table-layout: fixed;
width: 100%;
font-size: 13px;
/* margin: 20px 0px; */
text-align: center;
word-wrap: break-word;

page-break-inside:auto;
-fs-table-paginate:paginate;
border-spacing:0;
table-layout:fixed;
word-break:break-strict;
cellspacing:0;cellpadding:0;
border: solid 1px #ccc;
padding: 2px 2px;
}
tr {
page-break-inside:avoid;
page-break-after:auto;
height: 25px;
}
</style>
</head>
<body>
<h1>First Page</h1>
<div style="page-break-after:always;"></div>
<h1>Second Page</h1>
<div style="page-break-after:always;"></div>

<#if dataList ??>
<table class="outTable" border="1" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td>日期</td>
<td>摘要</td>
<td>凭证种类</td>
<td>凭证号码</td>
<td>借方金额 </td>
<td>贷方金额</td>
<td>余额方向</td>
<td>余额</td>
</tr>
<#list dataList as item>
<tr>
<td>${(item.c1)!''}</td>
<td>${(item.c2)!''}</td>
<td>${(item.c3)!''}</td>
<td>${(item.c4)!''}</td>
<td>${(item.c5)!''}</td>
<td>${(item.c6)!''}</td>
<td>${(item.c7)!''}</td>
<td>${(item.c8)!''}</td>
</tr>
</#list>
</table>
</#if>
</body>
</html>

3、完整Java测试代码

import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import com.spire.pdf.FileFormat;
import com.spire.pdf.PdfDocument;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.junit.jupiter.api.Test;
import org.ofdrw.layout.DocContentReplace;
import org.ofdrw.layout.OFDDoc;
import org.ofdrw.reader.OFDReader;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* freemarker 转 pdf
*
* @author shanhy
* @date 2022-09-21 13:50
*/
public class Ftl2PdfTest {

/**
* 转换 pdf 文档为 ofd 文档
*/
@Test
public void convertFtl2Pdf() throws IOException {
long start = System.currentTimeMillis();

ByteArrayOutputStream baos = null;
OutputStream out = null;
try {
// 模板中的数据,实际运用从数据库中查询
Map<String,Object> data = new HashMap<>();
List<Map<String, String>> dataList = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
Map<String, String> itemMap = new HashMap<>();
itemMap.put("c1","20220927");
itemMap.put("c2","跨行转入");
itemMap.put("c3","999");
itemMap.put("c4","13123123");
itemMap.put("c5",String.valueOf(1000 + i));
itemMap.put("c6","0.00");
itemMap.put("c7","贷");
itemMap.put("c8","31312.00");
dataList.add(itemMap);
}
data.put("dataList", dataList);

baos = createPDF(data, "test.ftl");
out = Files.newOutputStream(Paths.get("D:\\Downloads\\test.pdf"));
baos.writeTo(out);
baos.close();
} catch (Exception e) {
e.printStackTrace();
} finally{
if(baos != null){
baos.close();
}
if(out != null){
out.close();
}
}

System.out.println("累计耗时="+(System.currentTimeMillis() - start)+"ms");
}

/**
* 通过模板导出pdf文件
* @param data 数据
* @param templateFileName 模板文件名
* @throws Exception
*/
public ByteArrayOutputStream createPDF(Map<String,Object> data, String templateFileName) throws Exception {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
cfg.setDirectoryForTemplateLoading(Paths.get("D:\\Downloads\\templates").toFile());
// cfg.setClassForTemplateLoading(Ftl2PdfTest.class,"/templates");
ITextRenderer renderer = new ITextRenderer();
try(ByteArrayOutputStream out = new ByteArrayOutputStream();) {
// 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示
renderer.getFontResolver().addFont("D:\\Downloads\\templates\\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"/>
// URI images = PDFTemplateUtil.class.getClassLoader().getResource("images").toURI();
// if (images != null) {
// String url = images.toString();
// renderer.getSharedContext().setBaseURL(url);
// }
renderer.layout();

renderer.createPDF(out, false);
renderer.finishPDF();
out.flush();
return out;
}
}
}

(END)