java生成PDF有多种方式,比如itextpdf、 Apache PDFBox、Flying Saucer (XHTMLRenderer)、 OpenPDF等。今天要介绍的是itextpdf,及在开发过程中处理的问题。
1. 引入POM
<!--PDF导出POM-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.9</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.9</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.0.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
2. 导出工具类
import cn.hutool.core.io.FileUtil;
import com.jxdj.framework.common.domain.exception.CustomException;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.file.Paths;
import java.util.Locale;
/**
* 生成pdf文件工具类
* @author *******
* @create 2025/7/09
*/
@Slf4j
public class PdfUtils {
public static String PDF_BASE_PATH = "com/file/export/";
public static String TEMPLATE_VERIFY_APPLY = "verifyApply.ftl"; // PDF模板名称
private final static String TEMPLATE_BASE_PATH = "template/";
private final static String FONT_BASE_PATH = "fonts/";
private final static String DEFAULT_FONT = "simkai.ttf"; // 字体资源文件
private final static String ENCODING = "UTF-8";
/**
* 生成pdf
* @param templateName 模板名称
* @param data 数据
* @param filePath 生成pdf文件路径
* @return 生成的文件数量
*/
public static void createPDF(String templateName, Object data, String filePath) throws Exception {
// 如果不存在,则创建目录
File file = new File(filePath);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream out = null;
boolean isSuccess = false;
try {
out = new FileOutputStream(file);
// 资源文件存放的根路径
String resourcePath = Paths.get(PdfUtils.class.getResource("/").toURI()).toString();
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
cfg.setDirectoryForTemplateLoading(Paths.get(resourcePath , TEMPLATE_BASE_PATH).toFile());
ITextRenderer renderer = new ITextRenderer();
// 设置 css中 的字体样式
renderer.getFontResolver().addFont(Paths.get(resourcePath, FONT_BASE_PATH , DEFAULT_FONT).toString(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
cfg.setEncoding(Locale.CHINA, ENCODING);
// 获取模板文件
Template template = cfg.getTemplate(templateName, ENCODING);
StringWriter writer = new StringWriter();
template.process(data, writer);
writer.flush();
String html = writer.toString();
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(out, false);
renderer.finishPDF();
out.flush();
isSuccess = true;
} catch (Exception e) {
throw e;
} finally {
if (out != null) {
try {
out.close();
if (!isSuccess) {
deleteFile(filePath);
}
} catch (Exception e) {
throw new CustomException("文件导出PDF异常,资源未正确释放", e);
}
}
}
}
private static void deleteFile(String path) {
try {
File file = new File(path);
if (file.exists()) {
boolean isDel = FileUtil.del(file);
if (!isDel) {
log.error("删除本地磁盘文件失败,请检查路径是否被占用或权限问题:{}", path);
}
}
} catch (Exception e) {
log.error("删除本地磁盘文件异常", e);
}
}
}
3. 资源文件的存放路径,与代码相匹配。(你放在其他位置也行,java代码的写法要相对应)
字体文件可以根据实际需求引入其他不同的字体。
模板文件根据实际进行修改
4. 模板文件(部分代码)
这实际就是一个html文件
<body>
<div class="mybody">
<div class="header"></div>
<div class="content_class">
<table border="1" width="100%" cellspacing="0" cellpadding="0"
style="table-layout: fixed; border-collapse: collapse; min-width:600px">
<thead>
</thead>
<tbody>
<tr>
<td style="text-align: center; width:100%" colspan="6">费用报销单</td>
</tr>
<tr>
<td style="text-align: center; width:70%; border-right: none" colspan="4"></td>
<td style="text-align: center; width:8%; border-left: none; border-right: none" >编号</td>
<td style="text-align: center; border-left: none; width: 22%">${applyNo!""}</td>
</tr>
<tr>
<td style="text-align: center; width:8%">公司</td>
<td style="width:20%" class="td_content_show">${companyName!""}</td>
<td style="text-align: center; width:8%">部门</td>
<td style="width:15%" class="td_content_show">${deptProject!""}</td>
<td style="text-align: center; width:8%">申请日期</td>
<td style="width:40%" class="td_content_show">${(createTime?string("yyyy-MM-dd HH:mm:ss"))!""}</td>
</tr>
<tr>
<td style="text-align: center">金额</td>
<td class="td_content_show" colspan="5">${amountUpper!""} 元( ¥${amount!""} ) </td>
</tr>
<tr>
<td style="text-align: center">申请人</td>
<td class="td_content_show" colspan="5">${creator!""}</td>
</tr>
<tr style="height:10px">
<td style="text-align: center" colspan="6"> </td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
5.PDF生成效果