SpringBoot自定义HTML生成PDF,支持水印文本及图片
根据HTML实现数据渲染填充PDF动态列表,表格,图片
步骤1、Pom.xml 引入
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.1.22</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
步骤2、编写工具类
@Component
@Slf4j
public class FreeMarkUtils {
private FreeMarkUtils() {
}
private volatile static Configuration configuration;
static {
if (configuration == null) {
synchronized (FreeMarkUtils.class) {
if (configuration == null) {
configuration = new Configuration(Configuration.VERSION_2_3_28);
}
}
}
}
/**
* 使用 Freemarker 引擎渲染 HTML
*
* @param dataMap 传入 HTML 模板的 Map 数据
* @param fileName 模板文件名称 (例如 "templates/template.ftl")
* @return 渲染后的 HTML 字符串
*/
public static String freemarkerRender(Map<String, Object> dataMap, String fileName) {
// 创建 FreeMarker 配置实例
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 设置默认字符集和处理异常方法
configuration.setDefaultEncoding("UTF-8");
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
try {
// 使用 SpringTemplateLoader 读取资源文件目录
final SpringTemplateLoader templateLoader = new SpringTemplateLoader(new DefaultResourceLoader(), "classpath:templates");
configuration.setTemplateLoader(templateLoader);
// 配置 FreeMarker 日志和错误处理
configuration.setLogTemplateExceptions(false);
configuration.setWrapUncheckedExceptions(true);
// 获取模板
Template template = configuration.getTemplate(fileName);
// 填充数据并返回结果字符串
return FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
// 如果出现异常或者其他错误,返回 null
return null;
}
/**
* 将HTML转换为带水印的PDF文档
*
* @param htmlTmpStr HTML字符串
* @param fontFile 字体文件路径
* @return 带水印的PDF文档字节数组
*/
public static byte[] createHtml2Pdf(String htmlTmpStr, String fontFile) {
ByteArrayOutputStream outputStream = null; // 创建一个字节数组输出流
byte[] result = null; // 初始化返回结果为null
try {
outputStream = new ByteArrayOutputStream(); // 实例化字节数组输出流
ITextRenderer renderer = new ITextRenderer(); // 创建ITextRenderer对象
renderer.setDocumentFromString(htmlTmpStr); // 将HTML字符串解析为文档对象
ITextFontResolver fontResolver = renderer.getFontResolver(); // 获取字体解析器
final File tempFile = getTempFile(fontFile); // 获取临时字体文件
fontResolver.addFont(tempFile.getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 将字体文件添加到字体解析器中
renderer.layout(); // 计算文档的布局
renderer.createPDF(outputStream); // 将文档转换成PDF并写入字节数组输出流中
result = outputStream.toByteArray(); // 将生成的PDF字节数组赋值给返回结果
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.flush(); // 刷新字节数组输出流
outputStream.close(); // 关闭字节数组输出流
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result; // 返回生成的PDF字节数组,如果出现异常则返回null
}
/**
* @Author Fuzp
* @Date 11:11 2023/10/10
* @Content 添加文字水印
**/
public static byte[] PDFAddWatermark(byte[] inputFile, String waterMarkName) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
// 水印的高和宽(参数可调节)
int textH = 75; // 水印的高度
int textW = 170; // 水印的宽度
// 间隔距离(参数可调节)
int interval = 60; // 水印之间的间隔距离
PdfReader reader = new PdfReader(inputFile); // 创建PdfReader对象,读取输入文件
PdfStamper stamper = new PdfStamper(reader, outputStream); // 创建PdfStamper对象,用于修改PDF
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); // 创建字体对象,指定字体样式和编码方式
PdfGState gs = new PdfGState(); // 创建PdfGState对象,用于设置图形状态
gs.setFillOpacity(0.2f); // 设置透明度
gs.setStrokeOpacity(0.4f); // 设置边框透明度
int total = reader.getNumberOfPages() + 1; // 获取PDF总页数
JLabel label = new JLabel(); // 创建JLabel对象,用于获取字体的相关信息
label.setText(waterMarkName); // 设置水印文字
PdfContentByte under;
// 可添加多个水印
Rectangle pageRect = null; // 创建Rectangle对象,存储页面的尺寸信息
FontMetrics metrics; // 创建FontMetrics对象,用于获取字体的度量信息
label.setText(waterMarkName); // 设置水印文字
metrics = label.getFontMetrics(label.getFont()); // 获取字体的度量信息
for (int i = 1; i < total; i++) {
pageRect = reader.getPageSizeWithRotation(i); // 获取指定页面的尺寸信息(包括旋转角度)
// 在内容上方加水印
under = stamper.getOverContent(i); // 获取内容上方的PdfContentByte对象,用于绘制水印
// 在内容下方加水印
// under = stamper.getUnderContent(i);
under.saveState(); // 保存当前图形状态
under.setGState(gs); // 设置图形状态
under.beginText(); // 开始绘制文本内容
//设置字体颜色为蓝色
under.setColorFill(BaseColor.BLUE);
under.setFontAndSize(base, 20); // 设置字体和字号
// 水印文字成30度角倾斜
for (int height = interval + textH; height < pageRect.getHeight();
height = height + textH * 3) { // 按行循环绘制水印
for (int width = interval + textW; width < pageRect.getWidth() + textW;
width = width + textW * 2) { // 按列循环绘制水印
under.showTextAligned(
Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 30); // 绘制水印文字
}
}
under.endText(); // 结束绘制文本内容
}
stamper.close(); // 关闭PdfStamper对象
reader.close(); // 关闭PdfReader对象
} catch (Exception e) {
e.printStackTrace();
}
return outputStream.toByteArray(); // 返回输出流的字节数组
}
/**
* @Author Fuzp
* @Date 11:30 2023/10/10
* @Content 添加水印图片
**/
public static byte[] PFAddWatermarkImage(byte[] inputFile, String watermarkImagePath) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
// 水印的高和宽(参数可调节)
int imgH = 170; // 水印图片的高度
int imgW = 170; // 水印图片的宽度
// 间隔距离(参数可调节)
int interval = 60; // 水印之间的间隔距离
PdfReader reader = new PdfReader(inputFile); // 创建PdfReader对象,读取输入文件
PdfStamper stamper = new PdfStamper(reader, outputStream); // 创建PdfStamper对象,用于修改PDF
int total = reader.getNumberOfPages() + 1; // 获取PDF总页数
Image image = Image.getInstance(watermarkImagePath); // 创建Image对象,读取水印图片
image.scaleAbsolute(imgW, imgH); // 设置水印图片大小
image.setAbsolutePosition(0, 0); // 设置水印图片位置
PdfContentByte under;
Rectangle pageRect = null; // 创建Rectangle对象,存储页面的尺寸信息
for (int i = 1; i < total; i++) {
pageRect = reader.getPageSizeWithRotation(i); // 获取指定页面的尺寸信息(包括旋转角度)
// 在内容上方加水印
under = stamper.getOverContent(i); // 获取内容上方的PdfContentByte对象,用于绘制水印
// 在内容下方加水印
// under = stamper.getUnderContent(i);
float x = pageRect.getRight() - imgW - interval; // 计算水印图片的横坐标位置
float y = pageRect.getTop() - imgH - interval; // 计算水印图片的纵坐标位置
PdfGState gState = new PdfGState();
gState.setFillOpacity(0.5f); // 设置透明度为0.5
under.setGState(gState); // 应用透明度
under.addImage(image, imgW, 0, 0, imgH, x, y); // 绘制水印图片并设置透明度
}
stamper.close(); // 关闭PdfStamper对象
reader.close(); // 关闭PdfReader对象
} catch (Exception e) {
e.printStackTrace();
}
return outputStream.toByteArray(); // 返回输出流的字节数组
}
/**
* 获取资源文件的临时文件
* 资源文件打jar包后,不能直接获取,需要通过流获取生成临时文件
*
* @param fileName 文件路径 temp/temp.ftl
* @return
*/
public static File getTempFile(String fileName) {
final File tempFile = new File(fileName);
InputStream fontTempStream = null;
try {
Resource resource = new ClassPathResource(fileName);
fontTempStream = resource.getInputStream();
System.out.println(fontTempStream);
FileUtils.copyInputStreamToFile(fontTempStream, tempFile);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fontTempStream != null) {
fontTempStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return tempFile;
}
/**
* 导出 PDF 文件
*
* @param response HTTP 响应
* @param fileName 导出的 PDF 文件名
* @param pdfBytes PDF 字节数组
*/
public static void exportPDF(HttpServletResponse response, String fileName, byte[] pdfBytes) {
response.setContentType("application/pdf");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
try {
response.getOutputStream().write(pdfBytes);
response.getOutputStream().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 下载文件
*
* @param response 相应
* @param data 数据
* @param fileName 文件名
*/
public static void generateFile(HttpServletResponse response, String fileName, byte[] data) {
response.setHeader("content-Type", "application/octet-stream");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
try {
response.getOutputStream().write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
response.getOutputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
步骤3、自定义HTML模版
注:需要前端配合自定义模版计算高度 否则会导致边框 分页失效
<!DOCTYPE html>
<html>
<head>
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"></link>
<style type="text/css">
body {
font-family: SimSun;
background: #ffffff;
margin: 0px;
padding: 0px;
}
html, body, div, p {
font-size: 14px;
margin: 0px;
padding: 0px;
}
@page {
size: A4 portrait; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
padding: 0;
margin: 0 0 10mm 0;
@bottom-center {
/*content: "第" counter(page) "页 共 " counter(pages) "页";*/
content: "第" counter(page) "页";
font-family: SimSun;
font-size: 4.236mm;
color: #000;
};
}
@media print {
html, body {
width: 210mm;
height: 287mm;
}
}
/***************************************************************************************/
.root {
background: #ffffff;
width: 196mm;
height: 273mm;
margin: 0mm;
padding: 7mm;
box-sizing: content-box;
overflow: scroll;
font-family: SimSun;
}
.clearfix {
/* 兼容ie */
*zoom: 1
}
/* 当前元素的前后,添加一个空白,且将当前元素转换成table类型 */
.clearfix:before, .clearfix:after {
content: " ";
display: table
}
.clearfix:after {
clear: both
}
.font-size-10 {
font-size: 3.53mm;
}
.font-size-12 {
font-size: 4.236mm;
}
.font-size-14 {
font-size: 4.924mm;
}
.font-size-16 {
font-size: 5.65mm;
}
.mar-top-10 {
margin-top: 3.53mm;
}
.mar-top-15 {
margin-top: 5.295mm;
}
.mar-top-25 {
margin-top: 8.825mm;
}
.mar-left-24 {
margin-left: 8.47mm;
}
.title {
font-weight: bold;
color: #2C7EFB;
text-align: center;
}
.sub {
text-align: center;
}
.sub .label {
/*font-size: 10px;*/
font-size: 3.53mm;
font-weight: 500;
color: #686868;
}
.sub .value {
/*font-size: 10px;*/
font-weight: 500;
color: #000000;
}
.page-break-before {
page-break-before: always;
}
.block-wrap {
page-break-inside: avoid;
page-break-after: auto;
}
.label-wrap {
font-weight: bold;
color: #333333;
}
.label-wrap .tag {
display: block;
float: left;
width: 1.059mm;
height: 4.236mm;
background: #025DF4;
margin-right: 1mm;
margin-top: 1.5mm;
}
/*******表格********/
table, table tr th, table tr td {
border-collapse: collapse;
border: 1px solid #000000;
vertical-align: middle !important;
white-space: normal;
word-break: break-all;
word-wrap: break-word
}
table tr th {
font-weight: 500 !important;
color: #333333;
}
table tr td {
color: #111111;
}
.table-wrap {
width: 100%;
text-align: center;
display: table;
table-layout: fixed;
}
.table-wrap .tr {
page-break-inside: avoid;
page-break-after: auto;
font-size: 3.53mm;
}
.table-wrap .tr th {
background: #F9F9FB;
padding: 2mm;
white-space: normal;
word-break: break-all;
word-wrap: break-word;
}
.table-wrap .tr td {
padding: 2mm;
white-space: normal;
word-break: break-all;
word-wrap: break-word;
}
.table-wrap .tr2 {
font-size: 3.53mm;
}
.table-wrap .tr2 th {
background: #F9F9FB;
width: 27.416mm;
padding-right: 3mm;
text-align: right;
}
.table-wrap .tr2 td {
padding: 2.5mm;
text-align: left;
}
.ckb-wrap .item {
display: inline;
margin-right: 6mm;
}
.ckb-wrap .item img {
width: 4.236mm;
height: 4.236mm;
margin-top: 2mm;
margin-right: 0.5mm;
}
.img-list-gz {
padding-top: 4mm;
padding-left: 4mm;
}
.img-list-gz img {
width: 40mm;
height: 40mm;
margin-right: 4mm;
margin-bottom: 4mm;
}
.img-wrap {
border: 1px solid #000000;
margin-top: 2mm;
padding-top: 4mm;
padding-left: 4mm;
}
.img-wrap img {
width: 42mm;
height: 48mm;
margin-right: 4mm;
margin-bottom: 4mm;
}
.img-list-zy {
padding-left: 4mm;
padding-top: 4mm;
}
.img-list-zy .item {
width: 58mm;
margin-right: 4mm;
margin-bottom: 4mm;
float: left;
}
.img-list-zy .item img {
width: 100%;
height: 58mm;
}
.img-list-zy .item .name {
width: 100%;
margin: 1mm 2mm;
font-size: 3.53mm;
text-align: center;
}
</style>
</head>
<body>
<div class="root">
<div class="title font-size-16">报告</div>
<div class="label-wrap font-size-14 mar-top-15">
<span class="tag"></span><span>基本信息</span>
</div>
<table class="table-wrap mar-top-10">
<tr class="tr2">
<th class="label">自定义</th>
<td> <#if workorderCode??>
${workorderCode?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if launchPersonName??>
${launchPersonName?html} <#if launchPersonMobile??>/${launchPersonMobile?html}</#if>
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if customerName??>
${customerName?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if stationName??>
${stationName?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if policyNo??>
${policyNo?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if insurerName??>
${insurerName?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if contactPerson??>
${contactPerson?html}/${contactPhone?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if appointmentTime??>
${appointmentTime?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if workorderLevelName??>
${workorderLevelName?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if launchTime??>
${launchTime?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if registerTime??>
${registerTime?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if assignTime??>
${assignTime?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if supplierName??>
${supplierName?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<span>
<#if servicePersonName??>
${servicePersonName?html}
</#if>
</span>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td>
<#if startTime??>
${startTime?html}
</#if>
</td>
<th class="label">自定义</th>
<td>
<#if finishTime??>
${finishTime?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td colspan="3">
<#if reportDescription??>
${reportDescription?html}
</#if>
</td>
</tr>
<tr class="tr2">
<th class="label">自定义</th>
<td colspan="3">
<#if stationAddress??>
${stationAddress?html}
</#if>
</td>
</tr>
</table>
<#if (attachmentList?size > 0)>
<div class="block-wrap">
<div class="label-wrap font-size-14 mar-top-15">
<span class="tag"></span><span>自定义</span>
</div>
<div class="img-wrap">
<#list attachmentList as img>
<img src="${img.url}"/>
</#list>
</div>
</div>
</#if>
<div class="block-wrap">
<div class="label-wrap font-size-14 mar-top-15">
<span class="tag"></span><span>自定义</span>
</div>
<table class="table-wrap mar-top-10">
<tr class="tr">
<td colspan="2">自定义</td>
<td colspan="6" style="text-align: left;">
<div class="ckb-wrap">
<div class="item">
<#if shootingResult??>
<#if shootingResult == '1' >
<img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_sel.png"/>
<#else>
<img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_nal.png"/>
</#if>
</#if>
<span>正常</span>
</div>
<div class="item">
<#if shootingResult??>
<#if shootingResult == '2' >
<img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_sel.png"/>
<#else>
<img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_nal.png"/>
</#if>
</#if>
<span>异常</span>
</div>
</div>
</td>
</tr>
<tr class="tr">
<td colspan="2">自定义</td>
<td colspan="6" style="text-align: left;">
<#if verifyDescription??>
${verifyDescription?html}
</#if>
</td>
</tr>
<tr class="tr">
<td colspan="2">自定义</td>
<td colspan="6" style="text-align: left;">
<#if shootingDescription??>
${shootingDescription?html}
</#if>
</td>
</tr>
<tr class="tr">
<td colspan="2">自定义</td>
<td colspan="6" style="text-align: left;">
<div class="img-list-gz">
<#if finishAttachmentList??>
<#list finishAttachmentList as finishImg>
<img src="${finishImg.realUrl}"/>
</#list>
</#if>
</div>
</td>
</tr>
</table>
</div>
<#if (userList?size > 0)>
<div class="block-wrap">
<div class="label-wrap font-size-14 mar-top-15">
<span class="tag"></span><span>自定义</span>
</div>
<table class="table-wrap mar-top-10">
<tr class="tr">
<th>名称</th>
<th>地址</th>
<th>手机号</th>
</tr>
<#list userList as user>
<tr class="tr">
<td>
<#if user.name??>
${user.name?html}
</#if>
</td>
<td>
<#if user.address??>
${user.address?html}
</#if>
</td>
<td>
<#if user.phone??>
${user.phone?html}
<#else>——
</#if>
</td>
</tr>
</#list>
</table>
</div>
</#if>
</div>
</body>
</html>
步骤4、Controller实现
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/basic")
public String test() throws IOException, DocumentException {
Map<String, Object> dataMap = new HashMap<>(16);
dataMap.put("workorderCode","23423423");
dataMap.put("launchPersonName","23423432423");
dataMap.put("customerName","234324324");
dataMap.put("stationName","234324324");
dataMap.put("policyNo","23423");
dataMap.put("insurerName","234324");
dataMap.put("contactPerson","111111111111111111111111111111111111111");
dataMap.put("contactPhone","12324343443");
dataMap.put("appointmentTime","32432432");
dataMap.put("workorderLevelName","234234");
dataMap.put("launchTime","23432432");
dataMap.put("registerTime","32423423");
dataMap.put("assignTime","234234234");
dataMap.put("supplierName","23432423");
dataMap.put("servicePersonName","23423423");
dataMap.put("startTime","23423423");
dataMap.put("finishTime","23423432");
dataMap.put("reportDescription","23423423");
dataMap.put("stationAddress","23423423");
JSONArray attachmentArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("url","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
attachmentArray.add(jsonObject);
dataMap.put("attachmentList",attachmentArray);
dataMap.put("shootingResult","1");
dataMap.put("verifyDescription","verifyDescription");
dataMap.put("shootingDescription","shootingDescription");
JSONArray finishAttachmentArray = new JSONArray();
JSONObject object = new JSONObject();
object.put("realUrl","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
finishAttachmentArray.add(object);
dataMap.put("finishAttachmentList",finishAttachmentArray);
List<User> userList = new ArrayList<>();
userList.add(new User(1L,"Tuzq","抚州","16607091111"));
userList.add(new User(2L,"Fuzp","进贤","16607091111"));
dataMap.put("userList",userList);
String htmlStr = FreeMarkUtils.freemarkerRender(dataMap, "01-basic.ftl");
byte[] pdfBytes = FreeMarkUtils.createHtml2Pdf(htmlStr, "simsun.ttc");
byte[] bytes = FreeMarkUtils.PDFAddWatermark(pdfBytes, "哈啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊");
PdfUtils.savePdfToFile(bytes,"D:\\FreeMarkUtils-水印文件.pdf");
byte[] bytes2 = FreeMarkUtils.PFAddWatermarkImage(pdfBytes, "src/main/resources/icon.png");
PdfUtils.savePdfToFile(bytes2,"D:\\FreeMarkUtils-水印图片.pdf");
return "";
}
@GetMapping("/exportPDF")
public String exportPDF(HttpServletResponse response) throws IOException, DocumentException {
Map<String, Object> dataMap = new HashMap<>(16);
dataMap.put("workorderCode","23423423");
dataMap.put("launchPersonName","23423432423");
dataMap.put("customerName","234324324");
dataMap.put("stationName","234324324");
dataMap.put("policyNo","23423");
dataMap.put("insurerName","234324");
dataMap.put("contactPerson","111111111111111111111111111111111111111");
dataMap.put("contactPhone","12324343443");
dataMap.put("appointmentTime","32432432");
dataMap.put("workorderLevelName","234234");
dataMap.put("launchTime","23432432");
dataMap.put("registerTime","32423423");
dataMap.put("assignTime","234234234");
dataMap.put("supplierName","23432423");
dataMap.put("servicePersonName","23423423");
dataMap.put("startTime","23423423");
dataMap.put("finishTime","23423432");
dataMap.put("reportDescription","23423423");
dataMap.put("stationAddress","23423423");
JSONArray attachmentArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("url","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
attachmentArray.add(jsonObject);
dataMap.put("attachmentList",attachmentArray);
dataMap.put("shootingResult","1");
dataMap.put("verifyDescription","verifyDescription");
dataMap.put("shootingDescription","shootingDescription");
JSONArray finishAttachmentArray = new JSONArray();
JSONObject object = new JSONObject();
object.put("realUrl","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
finishAttachmentArray.add(object);
dataMap.put("finishAttachmentList",finishAttachmentArray);
List<User> userList = new ArrayList<>();
userList.add(new User(1L,"Tuzq","抚州","16607091111"));
userList.add(new User(2L,"Fuzp","进贤","16607091111"));
dataMap.put("userList",userList);
String htmlStr = FreeMarkUtils.freemarkerRender(dataMap, "01-basic.ftl");
byte[] pdfBytes = FreeMarkUtils.createHtml2Pdf(htmlStr, "simsun.ttc");
FreeMarkUtils.exportPDF(response,"FreeMarkUtils.pdf",pdfBytes);
return "";
}
@GetMapping("/generateFile")
public String generateFile(HttpServletResponse response) throws IOException {
Map<String, Object> dataMap = new HashMap<>(16);
dataMap.put("workorderCode","23423423");
dataMap.put("launchPersonName","23423432423");
dataMap.put("customerName","234324324");
dataMap.put("stationName","234324324");
dataMap.put("policyNo","23423");
dataMap.put("insurerName","234324");
dataMap.put("contactPerson","111111111111111111111111111111111111111");
dataMap.put("contactPhone","12324343443");
dataMap.put("appointmentTime","32432432");
dataMap.put("workorderLevelName","234234");
dataMap.put("launchTime","23432432");
dataMap.put("registerTime","32423423");
dataMap.put("assignTime","234234234");
dataMap.put("supplierName","23432423");
dataMap.put("servicePersonName","23423423");
dataMap.put("startTime","23423423");
dataMap.put("finishTime","23423432");
dataMap.put("reportDescription","23423423");
dataMap.put("stationAddress","23423423");
JSONArray attachmentArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("url","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
attachmentArray.add(jsonObject);
dataMap.put("attachmentList",attachmentArray);
dataMap.put("shootingResult","1");
dataMap.put("verifyDescription","verifyDescription");
dataMap.put("shootingDescription","shootingDescription");
JSONArray finishAttachmentArray = new JSONArray();
JSONObject object = new JSONObject();
object.put("realUrl","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
finishAttachmentArray.add(object);
dataMap.put("finishAttachmentList",finishAttachmentArray);
List<User> userList = new ArrayList<>();
userList.add(new User(1L,"Tuzq","抚州","16607091111"));
userList.add(new User(2L,"Fuzp","进贤","16607091111"));
dataMap.put("userList",userList);
String htmlStr = FreeMarkUtils.freemarkerRender(dataMap, "01-basic.ftl");
byte[] pdfBytes = FreeMarkUtils.createHtml2Pdf(htmlStr, "simsun.ttc");
FreeMarkUtils.generateFile(response,"FreeMarkUtils.pdf",pdfBytes);
return "";
}
}
步骤5 Yml设置
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
suffix: .ftl #指定Freemarker模板文件的后缀名
文章最顶上有代码文件
最终实现效果