第一步:导入maven依赖
<!-- 导出为PDF依赖包 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.13.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.12</version>
</dependency>
第二步:制作thymeleaf静态模版文件并放置在resources目录下,可以自定义模版文件路径
如图所示
thymeleaf静态文件示例(附,在线实时制作预览html文件网址:https://www.jyshare.com/front-end/61/):
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<title>xxxx模版</title>
<style>
#mainTable {
border-collapse: collapse;
border-style: solid;
border-width: 0.5pt;
width: 100%;
}
#mainTable td{
text-align: center;
padding: 8px;
border-style: solid;
border-width: 0.5pt;
}
</style>
</head>
<body>
<table id="header" boder="0" cellpadding="0" cellspacing="0" width="100%" style="margin-bottom:10px;margin-top:10px;">
<tbody>
<tr>
<td id="img" rowspan="3"><img th:src="${xxxx}" style="border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;-ms-border-radius: 50%;-o-border-radius: 50%;width: 60%;height: 60%;"/></td>
<td id="mainTitle" colspan="5" th:text="' '+${xxxx}" style="font-size: 30px;font-style: '+';font-weight: bold;padding-left:30%;padding-bottom:10px;"></td>
</tr>
<tr>
<td></td>
<td id="xxxx" colspan="5" th:text="${xxxx}+哈哈" style="font-size: 30px;font-style: '+';font-weight: bold;padding-left:45%;padding-top:10px;"></td>
</tr>
<tr>
<td></td>
<td id="xxxx" colspan="5" style="text-align:right;padding-right:0px;">xxxx: <span th:text="${xxxx}"></span></td>
</tr>
</tbody>
</table>
<table id="mainTable">
<tbody>
<tr>
<td>xxxx</td>
<td th:text="${xxxx}"></td>
<td>xxxx</td>
<td th:text="${xxxx}"></td>
<td>xxxx</td>
<td th:text="${xxxx} + ${xxxx}"></td>
</tr>
<tr>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
</tr>
<tr>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
</tr>
<tr>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx} + '/' + ${xxxx}"></td>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
</tr>
<tr>
<td>xxxx</td>
<td colspan="5" style="text-align:left;" th:text="${xxxx}"></td>
</tr>
<tr>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
<td>xxxx</td>
<td colspan="2" th:text="${xxxx}"></td>
</tr>
<tr>
<td height="250">xxxx</td>
<td colspan="5" style="text-align:left;" th:text="${xxxxx}"></td>
</tr>
<tr>
<td height="250">xxxx</td>
<td colspan="5" style="text-align:left;" th:text="${xxxx}"></td>
</tr>
<tr>
<td>xxxx</td>
<td colspan="5" style="text-align:left;"><img th:src="${xxxx}" style="width: 50%;height: 30%;"/></td>
</tr>
</tbody>
</table>
<div style="margin-top:40px;">
<p>注:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
</p>
</div>
</body>
</html>
第三步:实现编写生成pdf与下载工具,注意区分项目格式是war包还是jar包,目前我这边是两套实现方案。
jar包的方案是否同样适用于war包,我这边没有尝试,有兴趣的可以自己尝试,然后在评论区分享一下。
1)在jar包环境下加载静态模版文件时的代码示例:
静态模版无需加载包含base64格式的图片内容时,可用以下代码处理:
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.TemplateMode;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* @author leon
* @date 2023/8/15
* @description pdf 模版下载工具
*
*/
@Component
@Slf4j
public class PdfTemplateDownload {
@Autowired
private ApplicationContext applicationContext;
/**
* 下载pdf模版
* @param response
* @param paramMap
* @param templateFileName
* @param outputFileName
*/
public void downloadPdfTemplate(HttpServletResponse response,
Map<String, Object> paramMap,
String templateFileName,
String outputFileName) {
try {
// 创建基于类路径资源的模板解析器
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext); // 你需要注入Spring的应用上下文
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
resolver.setCacheable(true); // 在开发阶段设置为false,生产环境可改为true
resolver.setPrefix("classpath:/file/");
resolver.setSuffix(".html");
// 创建模版引擎
TemplateEngine engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
// 填充变量参数
Context context = new Context();
paramMap.forEach((k,v) -> context.setVariable(k, v));
// 替换变量值
String output = engine.process(templateFileName, context);
// 设置导出文件名
String exportName = "attachment;filename="+outputFileName;
response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1"));
response.setContentType("application/mspdf");
response.setCharacterEncoding("utf-8");
// 新建Document对象
Document document = new Document(PageSize.A4);
// 新建PdfWriter对象
PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
// 打开文档
document.open();
// 读取html文件内容
InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(StandardCharsets.UTF_8));
// 使用XMLWorkerHelper将html内容转为pdf
XMLWorkerHelper xmlWorkerHelper = XMLWorkerHelper.getInstance();
xmlWorkerHelper.parseXHtml(pdfWriter, document, htmlInputStream, Charset.forName("UTF-8"), new AsianFontProvider());
// 关闭文档
document.close();
// 关闭输入流
htmlInputStream.close();
// 关闭文件输出流
IOUtils.closeQuietly(response.getOutputStream());
}
catch (IOException e) {
log.error("", e);
}
catch (DocumentException e) {
log.error("", e);
}
}
/**
* 用于中文显示的Provider
*/
class AsianFontProvider extends XMLWorkerFontProvider {
@Override
public Font getFont(final String fontname, String encoding, float size, final int style) {
try {
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
return new Font(bfChinese, size, style);
}
catch (Exception e) {
}
return super.getFont(fontname, encoding, size, style);
}
}
}
若静态模版中有base64格式的图片信息,可换用下边的方法实现
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.Pipeline;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFilesImpl;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.HTML;
import com.itextpdf.tool.xml.html.TagProcessorFactory;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.templatemode.TemplateMode;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* @author leon
* @date 2023/8/15
* @description pdf 模版下载工具
*
*/
@Component
@Slf4j
public class PdfTemplateDownload {
@Autowired
private ApplicationContext applicationContext;
/**
* 下载pdf模版,用于模版中需要展示base64格式的图片信息
* @param response
* @param paramMap
* @param templateFileName
* @param outputFileName
*/
public void downloadPdfTemplateForBase64Img(HttpServletResponse response,
Map<String, Object> paramMap,
String templateFileName,
String outputFileName) {
try {
// 创建基于类路径资源的模板解析器
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext); // 你需要注入Spring的应用上下文
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
resolver.setCacheable(true); // 在开发阶段设置为false,生产环境可改为true
resolver.setPrefix("classpath:/file/");
resolver.setSuffix(".html");
// 创建模版引擎
TemplateEngine engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
// 填充变量参数
Context context = new Context();
paramMap.forEach((k,v) -> context.setVariable(k, v));
// 替换变量值
String output = engine.process(templateFileName, context);
// 设置导出文件名
String exportName = "attachment;filename="+outputFileName;
response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1"));
response.setContentType("application/pdf");
response.setCharacterEncoding("utf-8");
// 新建Document对象
Document document = new Document(PageSize.A4);
// 新建PdfWriter对象
PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
// 打开文档
document.open();
// 自定义处理base64图片
final TagProcessorFactory htmlTagProcessorFactory = Tags.getHtmlTagProcessorFactory();
htmlTagProcessorFactory.removeProcessor(HTML.Tag.IMG);
htmlTagProcessorFactory.addProcessor(new ImageTagProcessor(),HTML.Tag.IMG);
final Charset charset = StandardCharsets.UTF_8;
final CssFilesImpl cssFiles = new CssFilesImpl();
cssFiles.add(XMLWorkerHelper.getInstance().getDefaultCSS());
final StyleAttrCSSResolver cssResolver = new StyleAttrCSSResolver(cssFiles);
final HtmlPipelineContext hpc = new HtmlPipelineContext(new CssAppliersImpl(new AsianFontProvider()));
hpc.setAcceptUnknown(true).autoBookmark(true).setTagFactory(htmlTagProcessorFactory);
final HtmlPipeline htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(document, pdfWriter));
final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
final XMLWorker worker = new XMLWorker(pipeline, true);
final XMLParser p = new XMLParser(true, worker, charset);
// 读取html文件内容
InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(charset));
p.parse(htmlInputStream, charset);
// 关闭文档
document.close();
// 关闭输入流
htmlInputStream.close();
// 关闭文件输出流
IOUtils.closeQuietly(response.getOutputStream());
}
catch (IOException e) {
log.error("", e);
}
catch (DocumentException e) {
log.error("", e);
}
}
/**
* 用于中文显示的Provider
*/
class AsianFontProvider extends XMLWorkerFontProvider {
@Override
public Font getFont(final String fontname, String encoding, float size, final int style) {
try {
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
return new Font(bfChinese, size, style);
}
catch (Exception e) {
}
return super.getFont(fontname, encoding, size, style);
}
}
}
2)在war包环境下加载静态模版文件时的代码示例:
包含了静态模版中需要加载与不需要加载base64格式的图片内容时的两个方法实现示例:
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.Pipeline;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFilesImpl;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.CssAppliersImpl;
import com.itextpdf.tool.xml.html.HTML;
import com.itextpdf.tool.xml.html.TagProcessorFactory;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.PdfWriterPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;
import com.wondersgroup.healthcloud.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.FileTemplateResolver;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
/**
* @author leon
* @date 2023/8/15
* @description pdf 模版下载工具
*
*/
@Component
@Slf4j
public class PdfTemplateDownload {
/**
* 下载pdf模版
* @param response
* @param paramMap
* @param templateFileName
* @param outputFileName
*/
public void downloadPdfTemplate(HttpServletResponse response,
Map<String, Object> paramMap,
String templateFileName,
String outputFileName) {
try {
URL url = PdfTemplateDownload.class.getClassLoader().getResource("file/"+templateFileName);
if (ObjectUtils.isEmpty(url)) {
throw new CommonException("下载模版文件缺失");
}
File htmlFile = new File(url.getPath());
// 创建html文件解析器
FileTemplateResolver resolver = new FileTemplateResolver();
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
// 创建模版引擎
TemplateEngine engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
// 填充变量参数
Context context = new Context();
paramMap.forEach((k,v) -> context.setVariable(k, v));
// 替换变量值
String output = engine.process(htmlFile.getAbsolutePath(), context);
// 设置导出文件名
String exportName = "attachment;filename="+outputFileName;
response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1"));
response.setContentType("application/pdf");
response.setCharacterEncoding("utf-8");
// 新建Document对象
Document document = new Document(PageSize.A4);
// 新建PdfWriter对象
PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
// 打开文档
document.open();
// 读取html文件内容
InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(StandardCharsets.UTF_8));
// 使用XMLWorkerHelper将html内容转为pdf
XMLWorkerHelper xmlWorkerHelper = XMLWorkerHelper.getInstance();
xmlWorkerHelper.parseXHtml(pdfWriter, document, htmlInputStream, Charset.forName("UTF-8"), new AsianFontProvider());
// 关闭文档
document.close();
// 关闭输入流
htmlInputStream.close();
// 关闭文件输出流
IOUtils.closeQuietly(response.getOutputStream());
}
catch (IOException e) {
log.error("", e);
}
catch (DocumentException e) {
log.error("", e);
}
}
/**
* 下载pdf模版,用于模版中需要展示base64格式的图片信息
* @param response
* @param paramMap
* @param templateFileName
* @param outputFileName
*/
public void downloadPdfTemplateForBase64Img(HttpServletResponse response,
Map<String, Object> paramMap,
String templateFileName,
String outputFileName) {
try {
URL url = PdfTemplateDownload.class.getClassLoader().getResource("file/"+templateFileName);
if (ObjectUtils.isEmpty(url)) {
throw new CommonException("下载模版文件缺失");
}
File htmlFile = new File(url.getPath());
// 创建html文件解析器
FileTemplateResolver resolver = new FileTemplateResolver();
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
// 创建模版引擎
TemplateEngine engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
// 填充变量参数
Context context = new Context();
paramMap.forEach((k,v) -> context.setVariable(k, v));
// 替换变量值
String output = engine.process(htmlFile.getAbsolutePath(), context);
// 设置导出文件名
String exportName = "attachment;filename="+outputFileName;
response.setHeader("Content-Disposition",new String(exportName.getBytes(StandardCharsets.UTF_8),"ISO8859-1"));
response.setContentType("application/pdf");
response.setCharacterEncoding("utf-8");
// 新建Document对象
Document document = new Document(PageSize.A4);
// 新建PdfWriter对象
PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
// 打开文档
document.open();
// 自定义处理base64图片
final TagProcessorFactory htmlTagProcessorFactory = Tags.getHtmlTagProcessorFactory();
htmlTagProcessorFactory.removeProcessor(HTML.Tag.IMG);
htmlTagProcessorFactory.addProcessor(new ImageTagProcessor(),HTML.Tag.IMG);
final Charset charset = StandardCharsets.UTF_8;
final CssFilesImpl cssFiles = new CssFilesImpl();
cssFiles.add(XMLWorkerHelper.getInstance().getDefaultCSS());
final StyleAttrCSSResolver cssResolver = new StyleAttrCSSResolver(cssFiles);
final HtmlPipelineContext hpc = new HtmlPipelineContext(new CssAppliersImpl(new AsianFontProvider()));
hpc.setAcceptUnknown(true).autoBookmark(true).setTagFactory(htmlTagProcessorFactory);
final HtmlPipeline htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(document, pdfWriter));
final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
final XMLWorker worker = new XMLWorker(pipeline, true);
final XMLParser p = new XMLParser(true, worker, charset);
// 读取html文件内容
InputStream htmlInputStream = new ByteArrayInputStream(output.getBytes(charset));
p.parse(htmlInputStream, charset);
// 关闭文档
document.close();
// 关闭输入流
htmlInputStream.close();
// 关闭文件输出流
IOUtils.closeQuietly(response.getOutputStream());
}
catch (IOException e) {
log.error("", e);
}
catch (DocumentException e) {
log.error("", e);
}
}
/**
* 用于中文显示的Provider
*/
class AsianFontProvider extends XMLWorkerFontProvider {
@Override
public Font getFont(final String fontname, String encoding, float size, final int style) {
try {
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
return new Font(bfChinese, size, style);
}
catch (Exception e) {
}
return super.getFont(fontname, encoding, size, style);
}
}
}