itext7 +thymeleaf实现html转pdf, 模板动态填值,解决linux环境兼容问题
springboot项目实现html转pdf功能,踩过一些坑,记录遇到的问题。 附java代码,html代码,字体库下载地址,可直接运行main方法,生成pdf。
前言: springboot项目实现html转pdf功能,踩过一些坑,记录遇到的问题。 附java代码,html代码,字体库下载地址,可直接运行main方法,生成pdf。
1.导入需要使用的包
<!--itext7 html转pdf用到的包--> <dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency>
2.转行工具类
package com.example.demo.controller;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.layout.font.FontProvider;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.io.*;
/**
* @Author: yh
* @Date: 2023/6/5 12:05
* @Description:
*/
public class PdfItext {
/**
* @param templatePath 模板全路径
* @param path 生成pdf文件夹
* @param fileName 生成pdf文件名
* @param context 填充参数
*/
public static void htmlToPdf(String templatePath, String path, String fileName, Context context) {
String linuxPath = PdfItext.getFileAbsolutePath(templatePath, "/mnt/pdf/templates/template1.html", true);
ConverterProperties converterProperties = new ConverterProperties();
FontProvider dfp = new DefaultFontProvider();
// dfp.addSystemFonts(); //添加中文字体库 方式a -window环境有自带字体 linux环境需要安装中文字体,可参考 https://www.zhihu.com/question/423159370/answer/2706867741
//添加中文字体库 方式b 使用项目导入的字体库 (已验证linux环境不安装中文字体,能正常显示中文)
dfp.addFont(getFileAbsolutePath("/templates/simhei.ttf", "/mnt/pdf/templates/simhei.ttf", false));
converterProperties.setFontProvider(dfp);
//将本地的模板转换成字符串
String htmlStr = toHtmlString(new File(linuxPath));
//模板参数替换
String replaceHtmlStr = new TemplateEngine().process(htmlStr, context);
File destDir = new File(path);
if (!destDir.exists()) {
destDir.mkdirs();
}
try (OutputStream out = new FileOutputStream(new File(path + fileName))) {
HtmlConverter.convertToPdf(replaceHtmlStr, out, converterProperties);
} catch (Exception e) {
e.printStackTrace();
System.out.println("pdf转换异常3{}"+ e);
}
}
public static void main(String[] args) throws Exception {
//模板内容填充参数
Context context = new Context();
context.setVariable("name2", "晨曦话java");
context.setVariable("name1", "晨曦话三国");
PdfItext.htmlToPdf("/templates/template.html", "/mnt/pdf/", "temp.pdf", context);
}
/**
* 读取本地html文件里的html代码
* @param file File file=new File("文件的绝对路径")
* @return
*/
public static String toHtmlString(File file) {
// 获取HTML文件流
StringBuffer htmlSb = new StringBuffer();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(file), "utf-8"));
while (br.ready()) {
htmlSb.append(br.readLine());
}
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// HTML文件字符串
String htmlStr = htmlSb.toString();
// 返回经过清洁的html文本
return htmlStr;
}
/**
* 解决linux下运行jar,无法加载jar包下文件(window环境能正常加载)
*
* @param templatePath 模板路径 传相对路径 比如 /templates/template.htm
* @param createPath 生成路径 比如 /mnt/pdf/templates/template.html
* @param isConvered 是否覆盖模板,默认false. 如模板变化,需覆盖生成
* @return 返回的路径就是放在linux服务器上的文件路径
*/
public static String getFileAbsolutePath(String templatePath, String createPath, boolean isConvered) {
try {
// 创建临时文件,获取jar里面的配置文件
File file = new File(createPath);
if (file.exists() && !isConvered) {
return file.getAbsolutePath();
}
InputStream inputStream = null;
try {
ClassPathResource resource = new ClassPathResource(templatePath);
inputStream = resource.getInputStream();
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
OutputStream outStream = new FileOutputStream(file);
outStream.write(buffer);
return file.getAbsolutePath();
} finally {
IOUtils.closeQuietly(inputStream);
}
} catch (Exception e) {
System.out.println("FileUtil getFilePath Fail cause by:"+ e);
}
return null;
}
}
3.原模板放在项目资源目录下:resources/templates linux环境不能加载jar包下模板文件/templates/simhei.ttf,/templates/template1.html,解决方法,将模板文件生成在服务器磁盘指定位置,再读取,笔者生成在\mnt\pdf\templates目录下,如果html模板发生变化,需要覆盖生成,或者先删掉原来生成的,再生成。
4.中文字体不显示问题,方式a. dfp.addSystemFonts()可加载中文字体库,window环境有自带的字体库; linux环境需要安装中文字体库 ,可参考 https://www.zhihu.com/question/423159370/answer/2706867741
方式b dfp.addFont("/templates/simhei.ttf") 使用项目导入的字体库(已验证linux环境不安装中文字体库也能正常线上中文)
simhei.ttf字体网上下载资源比较多,比如,http://xiazaiziti.com/210356.html
5.生成pdf文件位置:D:\mnt\pdf\temp.pdf,效果:
6.感谢ui和前端大佬画的html文件,效果炸裂。由于需要显示的内容比较多, pdf有默认边距,会换页。以下代码调整边距,使其在一页纸显示@page{ margin:10pt 25pt 0pt 25pt;}
html代码:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="google" content="notranslate" />
<meta http-equiv="expires" content="0">
<link rel="icon" href="<%= BASE_URL %>topLogo.png">
<title>智能管理平台</title>
</head>
<style>
@page{ margin:10pt 25pt 0pt 25pt;}
body{
margin-top: 0px;
}
.body {
width: 100%;
}
.body h1 {
text-align: center;
font-size: 25px;
margin-top: 0px;
margin-bottom: 10px;
}
.body h2 {
font-size: 16px;
margin: 5px 0px;
}
.body .info {
width: 88%;
}
.body .info span {
display: inline-block;
font-size: 14px;
line-height: 24px;
margin-right: 50px;
}
.body .info span label {
display: inline-block;
text-align: right;
}
.table th {
font-weight: 400;
}
.table td {
text-align: center;
}
.tableJe tr td:first-child {
text-align: left;
padding-left: 20px;
}
.tableLc tr td:first-child {
text-align: left;
padding-left: 20px;
}
.tableJe tr td:last-child {
text-align: right;
padding-right: 20px;
}
</style>
<body>
<div class="body">
<h1>表</h1>
<div style="position: relative;">
<h2 th:text='${name2}'></h2>
<h2 th:text='${name1}' style="font-weight: 400; position: absolute; width: 100%; text-align: right;top: 0px;"></h2>
</div>
<h2>明细:</h2>
<table class="table" style="width: 100%; border-collapse: collapse;font-size: 16px; line-height: 24px;" border="1">
<tr style="background-color: #E9F5FF;">
<th>金额(元)</th>
<th>额(元)</th>
<th>总金额(元)</th>
</tr>
<tr>
<td th:text='${name2}'></td>
<td ></td>
<td ></td>
</tr>
</table>
<table style="margin-top:13px; width: 100%; border-collapse: collapse;">
<tr>
<td style="width: 60%;padding-right: 20px;">
<table class="table tableJe" style="width: 100%; border-collapse: collapse;font-size: 16px;line-height: 24px;"
border="1">
<tr style="background-color: #E9F5FF;">
<th>额</th>
<th style="width: 30%;">张</th>
<th>金额(元)</th>
</tr>
<tr>
<td>1</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>2</td>
<td >0</td>
<td >0.00</td>
</tr>
<tr>
<td>3</td>
<td x></td>
<td ></td>
</tr>
<tr>
<td>4</td>
<td ></td>
<td></td>
</tr>
<tr>
<td>(5)</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>6</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>7</td>
<td></td>
<td ></td>
</tr>
<tr>
<td>8</td>
<td></td>
<td ></td>
</tr>
<tr>
<td>9</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>10</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>11</td>
<td ></td>
<td ></td>
</tr>
<tr style="background-color: #FEE4E4;">
<td>合</td>
<td ></td>
<td ></td>
</tr>
</table>
</td>
<td style="vertical-align: top;">
<table class="table tableJe"
style="width: 100%; border-collapse: collapse;font-size: 16px; line-height: 24px;" border="1">
<tr style="background-color: #E9F5FF;">
<th>AA</th>
<th>金额</th>
</tr>
<tr>
<td>a</td>
<td></td>
</tr>
<tr>
<td>a</td>
<td ></td>
</tr>
<tr>
<td>a</td>
<td ></td>
</tr>
<tr>
<td>a</td>
<td ></td>
</tr>
<tr>
<td>a</td>
<td ></td>
</tr>
<tr>
<td>a</td>
<td ></td>
</tr>
<tr style="background-color: #FEE4E4;">
<td>合计</td>
<td ></td>
</tr>
</table>
</td>
</tr>
</table>
<div style="margin-top: 13px;">
<table class="table tableJe" style="width: 100%; border-collapse: collapse;font-size: 16px;line-height: 24px;"
border="1">
<tr style="background-color: #E9F5FF;">
<th>ZZ</th>
<th>ZZ</th>
</tr>
<tr>
<td>ZZ</td>
<td></td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr>
<td>v</td>
<td >0.00</td>
</tr>
<tr>
<td>ZZ</td>
<td >0.00</td>
</tr>
<tr style="background-color: #FEE4E4;">
<td>ZZ</td>
<td >0.00</td>
</tr>
</table>
</div>
<h2>ZZ:</h2>
<table class="table tableLc" style="width: 100%; border-collapse: collapse;font-size: 16px;line-height: 24px;"
border="1">
<tr style="background-color: #FFF8EE;">
<th>ZZ</th>
<th>ZZ</th>
<th>ZZ</th>
</tr>
<tr>
<td>ZZ</td>
<td >2023-06-01 23:59:59</td>
<td ></td>
</tr>
<tr>
<td>ZZ</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>ZZ</td>
<td ></td>
<td >ww</td>
</tr>
<tr>
<td>ZZ</td>
<td ></td>
<td ></td>
</tr>
<tr>
<td>ZZ</td>
<td ></td>
<td >ww</td>
</tr>
</table>
</div>
</body>
</html>
更多推荐
所有评论(0)