FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.16</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>
<!-- https://mvnrepository.com/artifact/com.itextpdf/kernel -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>kernel</artifactId>
<version>7.1.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/html2pdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>3.0.4</version>
</dependency>
由于要解文档中应用字体不支持中文的情况,需要自定义加入字体文件,但是maven编译时会破坏改文件导致不可用,所以需要加入以下配置,在编译时会忽略该文件,没有字体文件可以在windows,C盘目录下的fonts文件夹下找到需要的字体,没有的需另寻下载
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttc</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
封装一个工具类用来完成FreeMark的动生成以及获取生成文件
configuration.setDirectoryForTemplateLoading();这个属性是设置ftl模板加载目录的,不是文件的绝对路径,主义这里需要获取的是文件的父级目录
private volatile static Configuration configuration;
static {
if (configuration == null) {
synchronized (PdfUtil.class) {
if (configuration == null) {
configuration = new Configuration(Configuration.VERSION_2_3_28);
}
}
}
}
/**
* 获取resources下文件的绝对路径
*
* @param fileName : 文件名称
* @return 绝对路径
*/
private static String getAbsolutePath(String fileName) {
URL url = Thread.currentThread().getContextClassLoader().getResource(fileName);
if (!ObjectUtils.isEmpty(url)) {
return url.getPath();
}
throw new RuntimeException("未获取到文件路径");
}
/**
* 获取资源父路径
* @param path
* @return
*/
public static String getParentPath(String path) {
return new File(path).getParent();
}
/**
* freeMark配置
*
* @param data:动态数据
* @param filePath:html文件相对路径
* @param fileName:文件名称
* @return
*/
public static String freemarkerRender(Object data, String filePath, String fileName) {
configuration.setDefaultEncoding("UTF-8");
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
try(Writer out = new StringWriter()) {
String absolutePath = getAbsolutePath(filePath);
configuration.setDirectoryForTemplateLoading(new File(getParentPath(absolutePath)));
configuration.setLogTemplateExceptions(false);
configuration.setWrapUncheckedExceptions(true);
Template template = configuration.getTemplate(fileName);
template.process(data, out);
out.flush();
return out.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成Pdf文件并返回PDF字节数组
* @param htmlTmpStr:html字符流
* @param fontFilePath:字体文件相对路径
* @return
*/
public static byte[] getPdfStream(String htmlTmpStr, String fontFilePath) {
byte[] result = null;
try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(htmlTmpStr);
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont(getAbsolutePath(fontFilePath), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
renderer.layout();
renderer.createPDF(outputStream);
result = outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
Controller层代码,这里是生成pdf以后直接返回了字节数,这么写并不是最优写法,只是为了测试方便无需保存到本地
/**
* 生成Pdf
*
* @return :文件名称
*/
@GetMapping("/generatePdf")
public HttpEntity<? extends Serializable> generatePdf() {
HttpHeaders httpHeaders = new HttpHeaders();
Achievement achievement = new Achievement();
List<Student> list = new ArrayList<Student>();
list.add(new Student("张三", "80", "70", "80"));
list.add(new Student("李四", "88", "90", "77"));
list.add(new Student("王五", "99", "88", "50"));
achievement.setClassName("三年级二班");
achievement.setStudents(list);
String outStr = PdfUtil.freemarkerRender(achievement, "templates/achievement.ftl", "achievement.ftl");
byte[] stream = PdfUtil.getPdfStream(outStr, "fonts/simsun.ttc");
if (stream != null && stream.length > 0) {
String fileName = "成绩单.pdf";
httpHeaders.setContentDispositionFormData("attachment", fileName);
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<byte[]>(stream, httpHeaders, HttpStatus.OK);
}
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
httpHeaders, HttpStatus.NOT_FOUND);
}
使用postman测试生成后的文件下载