思路如下:
1、制作模板(建议写静态HTML,样式会好看些):按照导出的格式弄一个word文件,然后通过转换工具转成HTML,但是一般转换的会有一些问题,需要删除一些样式字体等
推荐一个在线转换(百度随便搜一下):在线WORD转HTMLhttps://cn.office-converter.com/Word-to-HTML-Converter
在样式中添加字体样式(重要),这是不乱码的关键一步
<style type="text/css">
body {
font-family: "simsun";
}
</style>
2、准备字体:simsun.tcc,在windows目录也有拷一个就行:C:\Windows\Fonts
3、在项目中导入相关的依赖,主要是Freemarker、itextpdf
<properties>
<flying-saucer-pdf.version>9.1.16</flying-saucer-pdf.version>
<itextpdf.version>5.5.13</itextpdf.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>${flying-saucer-pdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>${itextpdf.version}</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>${itextpdf.version}</version>
</dependency>>
此处为核心:maven打包会压缩字体,需要排除一下
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttc</nonFilteredFileExtension>
<nonFilteredFileExtension>html</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-filtering</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
</plugin>
下面贴一下核心代码:
渲染模板、渲染PDF
package com.yx.exportpdfdemo.utils;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Template;
import org.springframework.core.io.ClassPathResource;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
/**
* @Author: yaoxin
* @Description: pdf 导出工具类
* @createDate: 2022/3/26 10:20
**/
public class PDFUtil {
/**
* 字体缓存目录
*/
private static String FONT_CACHE_DIR = null;
/**
* freemarker渲染html
*
* @param data 数据
* @return
*/
public static String freeMarkerRender(Map<String, Object> data, Template template) {
Writer out = new StringWriter();
try {
template.setOutputEncoding("UTF-8");
// 合并数据模型与模板
//将合并后的数据和模板写入到流中,这里使用的字符流
template.process(data, out);
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return null;
}
/**
* 创建pdf文件并渲染pdf
*
* @param content pdf内容
* @param tempDir pdf生成目录
* @param font 字体resource路径
* @throws IOException
* @throws DocumentException
*/
public static void createPdf(String content, String tempDir, String font) throws IOException, DocumentException {
Document document = new Document(PageSize.A4, 36.0F, 36.0F, 60.0F, 36.0F);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(tempDir));
document.open();
XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
/**
* 此处从resource目录下获取字体并拷贝到临时目录,并缓存目录
* 因为打成jar包后无法获取直接获取文件,最好是拷贝到服务器写绝对目录
*/
if (StrUtil.isBlankOrUndefined(FONT_CACHE_DIR)) {
File fontTemp = new File(System.getProperty("java.io.tmpdir") + File.separator + "simsun.ttc");
if (!fontTemp.exists()) {
fontTemp.createNewFile();
}
ClassPathResource simResource = new ClassPathResource(font);
FileUtil.writeFromStream(simResource.getInputStream(), fontTemp);
FONT_CACHE_DIR = fontTemp.getPath();
}
fontImp.register(FONT_CACHE_DIR);
XMLWorkerHelper.getInstance().parseXHtml(writer, document,
new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"), fontImp);
document.close();
writer.close();
}
}
Controller:
package com.yx.exportpdfdemo.controller;
import cn.hutool.json.JSONUtil;
import com.yx.exportpdfdemo.config.PDFExportConfig;
import com.yx.exportpdfdemo.utils.PDFUtil;
import freemarker.template.Template;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: yaoxin
* @Description:
* @createDate: 2022/3/28 17:23
**/
@RestController
@AllArgsConstructor
public class UserController {
private final FreeMarkerConfigurer freeMarkerConfigurer;
private final PDFExportConfig pdfExportConfig;
/**
* 导出文件名
*/
private static String EXPORT_NAME = "export";
/**
* 导出后缀
*/
private static String EXPORT_SUFFIX = "pdf";
/**
* 测试导出pdf
*
* @param response
*/
@SneakyThrows
@GetMapping(value = "/export")
public void domesticViolenceSee(HttpServletResponse response) {
//添加测试数据
Map<String, Object> data = new HashMap();
data.put("username", "法外狂徒张三");
data.put("age", "24");
//根据配置类拿到模板名称,再通过FreeMarkerConfigurer获取Template对象
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(pdfExportConfig.getUserFtl());
String content = PDFUtil.freeMarkerRender(data, template);
File exportFile = File.createTempFile(EXPORT_NAME, EXPORT_SUFFIX);
PDFUtil.createPdf(content, exportFile.getPath(), pdfExportConfig.getFont());
FileInputStream fin;
try {
fin = new FileInputStream(exportFile);
OutputStream output = response.getOutputStream();
byte[] buf = new byte[1024];
int r;
response.setContentType("application/pdf;charset=GB2312");
while ((r = fin.read(buf, 0, buf.length)) != -1) {
output.write(buf, 0, r);
}
fin.close();
output.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
exportFile.delete();
}
}
}
配置类:
package com.yx.exportpdfdemo.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* 配置类
*/
@Configuration
@Data
public class PDFExportConfig {
/**
* 字体目录
*/
@Value("${font}")
private String font;
/**
* 线索报告——查看 模板名称
*/
@Value("${user-ftl}")
private String userFtl;
}
HTML模板:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<style type="text/css">
body {
font-family: "simsun";
}
table {
border-collapse: collapse;
border-spacing: 0;
empxy-cells: show
}
td, th {
vertical-align: top;
font-size: 12px;
}
h1, h2, h3, h4, h5, h6 {
clear: both;
}
ol, ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
margin: 0;
padding: 0;
}
/* "li span.odfLiEnd" - IE 7 issue*/
li span. {
clear: both;
line-height: 0;
width: 0;
height: 0;
margin: 0;
padding: 0;
}
span.footnodeNumber {
padding-right: 1em;
}
span.annotation_style_by_filter {
font-size: 95%;
background-color: #fff000;
margin: 0;
border: 0;
padding: 0;
}
span.heading_numbering {
margin-right: 0.8rem;
}
* {
margin: 0;
}
.P1 {
font-size: 10.5px;
margin-bottom: 0in;
margin-top: 0in;
text-align: justify ! important;
writing-mode: horizontal-tb;
direction: ltr;
}
.Standard {
font-size: 10.5px;
writing-mode: horizontal-tb;
direction: ltr;
margin-top: 0in;
margin-bottom: 0in;
text-align: justify ! important;
}
.Table1 {
width: 5.7611in;
margin-left: 0.0035in;
margin-top: 0in;
margin-bottom: 0in;
margin-right: auto;
writing-mode: horizontal-tb;
direction: ltr;
}
.Table1_A1 {
padding-left: 0.075in;
padding-right: 0.075in;
padding-top: 0in;
padding-bottom: 0in;
border-width: 0.0176cm;
border-style: solid;
border-color: #000000;
writing-mode: horizontal-tb;
direction: ltr;
}
</style>
</head>
<body dir="ltr" style="max-width:8.2681in;margin-top:1in; margin-bottom:1in; margin-left:1.25in; margin-right:1.25in; ">
<table border="0" cellspacing="0" cellpadding="0" class="Table1">
<colgroup>
<col width="320"/>
<col width="320"/>
</colgroup>
<p>测试导出PDF</p>
<tr class="Table11">
<td style="text-align:left;width:2.8806in; " class="Table1_A1"><p class="P1">姓名</p></td>
<td style="text-align:left;width:2.8799in; " class="Table1_A1"><p class="P1">${username}</p></td>
</tr>
<tr class="Table11">
<td style="text-align:left;width:2.8806in; " class="Table1_A1"><p class="P1">年龄</p></td>
<td style="text-align:left;width:2.8799in; " class="Table1_A1"><p class="P1">${age}</p></td>
</tr>
</table>
<p class="Standard"></p></body>
</html>
YML:
spring:
application:
name: demo
freemarker:
enabled: true
content-type: text/html
suffix: .html #后缀名
charset: UTF-8 #编码格式
template-loader-path: classpath:/templates/ #此为freemarker默认配置
server:
port: 8090
#字体
font: simsun.ttc
user-ftl: user-ftl.html
此为导出结果: