iText通过FreeMarker模板生成PDF解决方案
首先定义一个HTML模板,通过后台数据填充,生成PDF文件。
目录
一、所需依赖
<!-- pdf所需依赖模块begin-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.19</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf-itext5 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-itext5</artifactId>
<version>9.1.5</version>
</dependency>
<!-- pdf所需依赖模块end-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.16</version>
</dependency>
二、生成工具类
package com.ruoyi.print_system.util;
import cn.hutool.core.io.resource.ResourceUtil;
import com.itextpdf.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.util.Map;
public class JavaToPdfUtil {
private static Logger logger = LoggerFactory.getLogger(JavaToPdfUtil.class);
//模板文件
private static final String HTML = "template/pdf-template.html";
//仿宋字体
private static final String FONT_TTF = "fonts/FangSong.ttf";
private static Configuration freemarkerCfg = null;
static {
freemarkerCfg = new Configuration();
//freemarker的模板目录
try {
freemarkerCfg.setDirectoryForTemplateLoading(new File(ResourceUtil.getResource("").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建PDF文件
*
* @param data 数据加载
* @param filename 文件名称
* @return
*/
public static boolean createPdfFile(Map<String, Object> data, String filename) {
try {
String content = JavaToPdfUtil.freeMarkerRender(data, HTML);
JavaToPdfUtil.createPdf(content, filename);
logger.info("PDF生成成功");
} catch (Exception e) {
logger.error("PDF生成失败:{}", e.getMessage());
return false;
}
return true;
}
/**
* freemarker渲染html
*/
public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
Writer out = new StringWriter();
try {
// 获取模板,并设置编码方式
Template template = freemarkerCfg.getTemplate(htmlTmp);
template.setEncoding("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;
}
/**
* html生成PDF文件
*
* @param content 渲染后的html字符串
* @param dest PDF生成路径地址
* @throws Exception
*/
public static void createPdf(String content, String dest) throws Exception {
ITextRenderer render = new ITextRenderer();
ITextFontResolver fontResolver = render.getFontResolver();
// 解决itext生成中文不显示问题
String FONT_TTF_PATH = ResourceUtil.getResource("").getPath().replace("target/classes/", "").concat("src/main/resources/") + FONT_TTF;
fontResolver.addFont(FONT_TTF_PATH, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 解析html生成pdf
render.setDocumentFromString(content);
//解决图片相对路径的问题
render.getSharedContext().setBaseURL(null);
render.layout();
render.createPDF(new FileOutputStream(dest));
}
}
三、准备模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
<!-- A4纸张横向显示 -->
@page {
size: 297mm 210mm;
}
body {
/*中文字体*/
font-family: FangSong;
}
#main {
font-size: 20px;
width: 843px;
height: 596px;
margin-left: 20px;
}
#photo {
width: 95px;
height: 130px;
position: absolute;
left: 532px;
top: 100px;
}
#left {
width: 330px;
height: 270px;
position: relative;
left: 0px;
top: 160px;
text-align: left;
padding: 5px;
}
#left>p {
text-indent: 2em;
}
#left p:nth-child(1) {
line-height: 30px;
}
#left p:nth-child(2) {
line-height: 20px;
font-size: 16px;
/*英文字体*/
font-family: "PmingLiu";
}
#left p:nth-child(3) {
position: relative;
left: 100px;
top: 25px;
}
#right {
width: 300px;
height: 320px;
float: right;
position: relative;
right: 150px;
bottom: 120px;
}
#right>p {
text-align: center;
}
#right p:nth-child(1) {
position: relative;
top: 18px;
left: 0;
}
#right p:nth-child(2) {
position: relative;
top: 24px;
left: 0;
}
#right p:nth-child(3) {
position: relative;
top: 28px;
left: 0;
}
#right p:nth-child(4) {
position: relative;
top: 30px
}
#right p:nth-child(5) {
position: relative;
top: 36px;
left: 15px;
}
#right p:nth-child(6) {
position: relative;
top: 42px;
left: 15px;
}
#right p:nth-child(7) {
position: relative;
top: 47px;
left: 15px;
}
a {
text-decoration: underline;
color: black;
}
</style>
</head>
<body>
<div id="main">
<div id="left">
<p>
本证书由<a>${evaluationOrganization!''}</a>颁发,表明持证人通过本机构组织的职业技能等级认证,具备该职业
${occupationName!''}相应技能等级水平。
</p>
<p>
This is to certify that the bearer has demonstrated
corresponding competency in this occupation
(${occupationEnName!''}) for successful compaction of the
occupational skill Level assessment organized by
<a>${evaluationOrganizationEn!''}</a>
</p>
<p>
${evaluationOrganization}<br />发证日期:${certificateDate!''}
</p>
</div>
<img src="${imgUrl}" id="photo" />
<div id="right">
<p>${name!''}</p>
<p>${certificatesType!''}</p>
<p>${certificatesCode!''}</p>
<p>${occupationName!''}</p>
<p>${worktypeName!''}</p>
<p>${skillLevel!''}</p>
<p>${certificateNo!''}</p>
</div>
</div>
</body>
</html>
四、 字体和模板放置的位置
工程使用的是springboot,所以将资源文件放置在src/main/resources下。
五、生成PDF文件预览
作者:YangRoc
出处:https://www.cnblogs.com/YangRoc/p/17186434.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
如果本篇文章有帮助到你,你可以请作者喝杯咖啡表示鼓励 ☕️
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!