java使用freemarker模板生成html,再生成pdf

1.freemarker模板生成html

  1. 添加Maven依赖

在pom.xml文件中添加以下依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  1. 创建Freemarker模板

新建一个HTML文件,例如table.ftl,然后在其中编写HTML模板,包括表格的头部、内容和尾部等部分。具体可以参考下面这个例子:

<table border="1"><thead><tr><th>ID</th><th>Name</th><th>Age</th></tr></thead><tbody><#list users as user><tr><td>${user.id}</td><td>${user.name}</td><td>${user.age}</td></tr></#list></tbody>
</table>

上面的模板中使用了Freemarker的指令语法来实现动态生成表格内容,包括使用<#list>标签来遍历用户列表,并使用${}语法来输出用户信息。

  1. 创建Controller方法

在Controller中编写一个方法来获取用户列表,然后渲染上面的模板并返回HTML内容。示例代码如下:

@Controller
public class UserController {@GetMapping("/users")public String userList(Model model) {List<User> users = new ArrayList<>();users.add(new User(1, "Tom", 18));users.add(new User(2, "Jerry", 20));users.add(new User(3, "John", 22));model.addAttribute("users", users);return "table";}
}

上面的方法使用@GetMapping注解来处理请求,然后创建一个用户列表,并将其添加到模型中。最后返回table字符串,代表要使用的HTML模板文件。

  1. 运行项目

运行Spring Boot应用程序,然后使用浏览器访问http://localhost:8080/users,即可看到动态生成的HTML表格。

注意:上面的例子仅供参考,实际应用中需要根据自己的需求进行修改扩展。

2.利用iText将生成的HTML转换为PDF文件

理解了freemarker生成html的步骤以后,就可以利用iText把html生成pdf文件了。

  1. 编写转换代码
    添加Maven依赖
    在pom.xml文件中添加以下依赖:
<dependency>
<groupId>com.itextpdf.tool</groupId><artifactId>xmlworker</artifactId><version>5.5.1</version>
</dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version>
</dependency><!-- 支持css样式渲染 -->
<dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf-itext5</artifactId><version>9.0.9</version>
</dependency>
  1. 编写转换代码
    在SpringBoot应用程序中创建一个Service或者Controller类,然后编写HTML转PDF的代码。

freeMarker转换为html的方法:

public class HtmlGenerator {public static String generate(String template, Map<String, Object> variables) throws IOException, TemplateException, IOException {Configuration config = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);// 指定FreeMarker模板文件的位置config.setClassForTemplateLoading(HtmlGenerator.class, "/filePath");//读取模板文件地址config.setDefaultEncoding("UTF-8");//获取模板文件Template tp = config.getTemplate(template);StringWriter stringWriter = new StringWriter();BufferedWriter writer = new BufferedWriter(stringWriter);tp.setEncoding("UTF-8");//把map数据写入tp.process(variables, writer);String htmlStr = stringWriter.toString();writer.flush();writer.close();return htmlStr;}
}

/filePath为项目中的ftl文件相对路径。
html生成pdf的方法:

public class PdfDocumentGenerator {private static final Logger logger = LoggerFactory.getLogger(PdfDocumentGenerator.class);/*** Output a pdf to the specified outputstream* * @param htmlStr*            the htmlstr* @param out*            the specified outputstream* @throws Exception*/public static void generate(String htmlStr, OutputStream out)throws Exception {DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // Compliantdf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // compliantDocumentBuilder builder = df.newDocumentBuilder();org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(htmlStr.getBytes()));ITextRenderer renderer = new ITextRenderer();renderer.setDocument(doc, null);renderer.layout();renderer.createPDF(out);out.close();}public static void generatePlus(String htmlStr, OutputStream out) throws IOException, DocumentException {final String charsetName = "UTF-8";Document document = new Document(PageSize.A4, 30, 30, 30, 30);document.setMargins(30, 30, 30, 30);PdfWriter writer = PdfWriter.getInstance(document, out);document.open();// html内容解析HtmlPipelineContext htmlContext = new HtmlPipelineContext(new CssAppliersImpl(new XMLWorkerFontProvider() {@Overridepublic Font getFont(String fontname, String encoding,float size, final int style) {if (fontname == null) {fontname = getChineseFont();      }return super.getFont(fontname, encoding, size,style);}})) {@Overridepublic HtmlPipelineContext clone()throws CloneNotSupportedException {HtmlPipelineContext context = super.clone();try {ImageProvider imageProvider = this.getImageProvider();context.setImageProvider(imageProvider);} catch (NoImageProviderException e) {}return context;}};// 图片解析htmlContext.setImageProvider(new AbstractImageProvider() {String rootPath = PdfDocumentGenerator.class.getResource("/").getPath();@Overridepublic String getImageRootPath() {return rootPath;}@Overridepublic Image retrieve(String src) {if (StringUtils.isEmpty(src)) {return null;}try {Image image = Image.getInstance(new File(rootPath, src).toURI().toString());// 图片显示位置image.setAbsolutePosition(400, 400);		if (image != null) {store(src, image);return image;}} catch (Throwable e) {e.printStackTrace();}return super.retrieve(src);}});htmlContext.setAcceptUnknown(true).autoBookmark(true).setTagFactory(Tags.getHtmlTagProcessorFactory());// css解析CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);cssResolver.setFileRetrieve(new FileRetrieve() {@Overridepublic void processFromStream(InputStream in,ReadingProcessor processor) throws IOException {try (InputStreamReader reader = new InputStreamReader(in, charsetName)) {int i = -1;while (-1 != (i = reader.read())) {processor.process(i);}} catch (Throwable e) {}}// 解析href@Overridepublic void processFromHref(String href, ReadingProcessor processor) throws IOException {InputStream is = PdfDocumentGenerator.class.getResourceAsStream("/" + href);try (InputStreamReader reader = new InputStreamReader(is,charsetName)) {int i = -1;while (-1 != (i = reader.read())) {processor.process(i);}} catch (Throwable e) {e.printStackTrace();}}});HtmlPipeline htmlPipeline = new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer));Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);XMLWorker worker = null;worker = new XMLWorker(pipeline, true);XMLParser parser = new XMLParser(true, worker, Charset.forName(charsetName));try (InputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes())) {parser.parse(inputStream, Charset.forName(charsetName));}document.close();}/*** 获取中文字体位置 * @return*/public static String getChineseFont() {String chineseFont = null;chineseFont = Object.class.getResource("/").getPath() + "font/simsun.ttc";if(!new File(chineseFont).exists()){throw new RuntimeException("字体文件不存在!"+chineseFont);}return chineseFont;}
}

运行测试方法:

public class Pdfdest {public static void main(String[] args) throws Exception {String outputFile = "d:/test1.pdf";Map<String, Object> map = new HashMap<>();map.put("XXX", "测试");//生成工具,下面有代码String htmlStr = HtmlGenerator.generate("test.ftl", map);//生成工具,下面有代码OutputStream out = new FileOutputStream(outputFile);PdfDocumentGenerator.generatePlus(htmlStr,out);}
}

准备一个test.ftl放到resource/filePath下,当然字体最好也放到resource/font下,运行时需要使用。
test.ftl的代码如下:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></meta><style>@page {@top-center { content: element(header) }}@page {@bottom-center { content: element(footer) }}.apply {margin: 0 auto;padding: 0 30px;}.title {margin-top: 40px ;text-align: center;font-weight: bold;//字体需要和后台对应上font-family: SimSun;font-weight: bold;font-size: 20px;color: #333333;letter-spacing: 0;}.table {border-collapse: collapse;width: 100%;margin-top: 30px;font-family: SimSun;font-size: 14px;color: #111111;letter-spacing: 0.54px;}.label {background-color: #E6E6E6;width: 20%;}.normaltd {padding: 10px 0;}.maxtd {height: 250px;}.value {width: 30%;padding-left: 10px;}.apply {margin: 0 auto;padding: 0 30px;}.title {margin-top: 40px ;text-align: center;font-weight: bold;//字体需要和后台对应上font-family: SimSun;font-weight: bold;font-size: 20px;color: #333333;letter-spacing: 0;}.table {width: 100%;margin-top: 30px;font-family: SimSun;font-size: 14px;color: #111111;letter-spacing: 0.54px;}.label {background-color: #E6E6E6;width: 20%;}.normaltd {padding: 10px 0;}.maxtd {height: 250px;}.value {width: 30%;padding-left: 10px;}tr {page-break-inside: avoid;page-break-after: auto;}</style>
</head><body style="font-family: SimSun">
<div class="apply"><p class="title">申请单</p><table border="1" cellspacing="0" class="table"><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">XXX</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr ><td valign="middle" colspan="1" class="label maxtd" align="center">XXX</td><td valign="middle" colspan="3" class="maxtd value">${XXX}</td></tr><tr><td colspan="1" class="label normaltd" align="center">XXX</td><td colspan="3" class="normaltd value">${XXX}</td></tr><tr><td colspan="1" class="label normaltd" align="center">XXX</td><td colspan="3" class="normaltd value">${XXX}</td></tr><tr><td colspan="1" class="label normaltd" align="center">XXX</td><td colspan="3" class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">XXX</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr ><td valign="middle" colspan="1" class="label maxtd" align="center">XXX</td><td valign="middle" colspan="3" class="maxtd value">${XXX}</td></tr><tr><td colspan="1" class="label normaltd" align="center">XXX</td><td colspan="3" class="normaltd value">${XXX}</td></tr><tr><td colspan="1" class="label normaltd" align="center">XXX</td><td colspan="3" class="normaltd value">${XXX}</td></tr><tr><td colspan="1" class="label normaltd" align="center">XXX</td><td colspan="3" class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">XXX</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr><tr><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td><td class="label normaltd" align="center">XXX</td><td class="normaltd value">${XXX}</td></tr></table>
</div>
</body>
</html>

执行就可以看到生成的pdf文件了,文件路径在d:/test1.pdf。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/179692.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络和Linux网络_6(应用层)HTTPS协议(加密解密+中间人攻击+证书)

目录 1. HTTPS协议介绍 1.1 加密解密和秘钥的概念 1. 2 为什么要加密 2. 对称加密和非对称加密 2.1 只使用对称加密 2.2 只使用非对称加密 2.3 双方都使用非对称加密 2.4 使用非对称加密对称加密 2.5 中间人攻击MITM 3. 证书的概念和HTTPS的通信方式 3.1 CA认证机构…

Redis 事件轮询

1 Redis 为什么快 数据存在内存中, 直接操作内存中的数据单线程处理业务请求避免了多线的上下文切换, 锁竞争等弊端使用 IO 多路复用支撑更高的网络请求使用事件驱动模型, 通过事件通知模式, 减少不必要的等待… 这些都是 Redis 快的原因。 但是这些到了代码层面是如何实现的呢…

负载均衡集群 LVS

集群 含义&#xff1a; Cluster、集群、群集多台主机组成一个整体&#xff0c;对外只提供一个访问接口 集群的分类 负载均衡集群&#xff08;LBC&#xff09; 提高系统的响应能力、尽可能处理更多的访问请求、以减少延迟为目标&#xff0c;获得高并发、高负载(LB)的整体性能…

【Linux】系统初始化配置

CentOS 7 的虚拟机安装后必须要做的几个操作&#xff0c;记录以下&#xff0c;网络配置修改、yum源安装、基础工具安装&#xff1a; 1、先修改权限&#xff0c;新建普通用户&#xff0c;并授权普通用户apps 的sudo权限&#xff1b; useradd apps password apps visudo apps A…

基于SSM的论文管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

集合框架(二)LinkedList的常见使用

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍LinkedList的常见使用以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题可以在评论区留言 目…

【小爱学大数据】一个方法调试了一个上午

为了解决一个棘手的问题&#xff0c;我花了整个上午来调试一个“setBulkFlushInterval”的方法。这个方法的主要作用是控制批量数据刷新的时间间隔&#xff0c;以确保数据的及时性和稳定性。但在面对一个复杂的业务场景时&#xff0c;它却表现得不如预期。 我首先分析了代码逻辑…

Vue2问题:如何全局使用less和sass变量?

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约2400字&#xff0c;整篇阅读大约需要4分钟。 本文主要内容分三部分&#xff0c;如果您只需要解决问题&#xff0c;请阅读第一、二部分即可。如果您有更多时间&#xff…

大一学编程怎么学?刚接触编程怎么学习,有没有中文编程开发语言工具?

大一学编程怎么学&#xff1f;刚接触编程怎么学习&#xff0c;有没有中文编程开发语言工具&#xff1f; 1、大一刚开始学编程&#xff0c;面对复杂的代码学习非常吃力&#xff0c;很难入门。建议刚接触编程可以先学习中文编程&#xff0c;了解其中的编程逻辑&#xff0c;学编程…

Flink流批一体计算(22):Flink SQL之单流kafka写入mysql

1. 准备工作 什么是Kafka源表 Kafka是分布式、高吞吐、可扩展的消息队列服务&#xff0c;广泛用于日志收集、监控数据聚合、流式数据处理、在线和离线分析等大数据领域。 docker部署zookeeper docker pull wurstmeister/zookeeperdocker run -d --restartalways \ --log-dr…

LeetCode [中等]3. 无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 1. 滑动窗口&#xff08;Sliding Window&#xff09;&#xff1a; 滑动窗口是一种用于处理数组或列表的子数组或子序列的问题…

Vue实现纯前端导入excel数据

准备工作 - 下载 xlsx npm install xlsx下面直接上代码&#x1f447; <template><div><input type"file" accept".xlsx, .xls" change"handleClick"></div> </template><script langts setup> import * a…

Vue基础入门(四):Vue3快速开发模板

快速开发Vue的解决方案 ​ Vue 的开发需要的 node 环境&#xff0c;其实上在开发的过程中会遇到一些你想不到的问题&#xff0c;比如 npm工具的版本和 node 环境不匹配&#xff08;你把其他项目导入到自己的环境&#xff09; ​ vue-element-admin&#xff08;是一个官方提供…

git仓库如何撤销提交,恢复提交,重置版本命令

撤销提交&#xff1a; 要撤销最近一次提交&#xff08;未推送到远程仓库&#xff09;&#xff0c;可以使用以下命令&#xff1a; git reset HEAD^该命令将会把最后一次提交的修改从当前主分支中移除&#xff0c;并将这些修改的状态保留在本地工作目录中。 如果想要取消所有的…

HuggingFace学习笔记--利用API实现简单的NLP任务

目录 1--中文分类 1-1--使用预训练模型推理 1-2--基于预训练模型实现下游任务 2--中文填空 3--中文句子关系推断 1--中文分类 1-1--使用预训练模型推理 代码实例&#xff1a; import torch from datasets import load_dataset from transformers import BertTokenizer,…

leetCode 216.组合总和 III + 回溯算法 + 剪枝 + 图解 + 笔记

找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回 示例 1: 输入: k 3, n 7 输出: [[1,2,4]] 解释…

kotlin中sealed语句的使用

sealed 密封类是 Kotlin 中的一种特殊类别&#xff0c;它的主要作用是限制类的继承结构。密封类用于表示受限的类继承结构&#xff0c;即一个值只能有有限几种类型&#xff0c;而不能有任意类型。密封类通常用于表示一种有限集合的类型。 下面是密封类的主要特性和作用&#x…

pinia从入门到使用

pinia: 比vuex更适合vue3的状态管理工具&#xff0c;只保留了vuex 原有的 state, getters&#xff0c;actions 作用等同于 data computed methods&#xff0c;可以有多个 state 1.安装创建导入 安装&#xff1a;npm install pinia 或 yarn add pinia 创建stores/index.js inde…

Springboot2+WebSocket

一、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency> 二、添加配置 新增配置文件 config/WebSocketConfig.java import org.springframewo…

Jmeter接口测试和性能测试

目前最新版本发展到5.0版本&#xff0c;需要Java7以上版本环境&#xff0c;下载解压目录后&#xff0c;进入\apache-jmeter-5.0\bin\&#xff0c;双击ApacheJMeter.jar文件启动JMemter。 1、创建测试任务 添加线程组&#xff0c;右击测试计划&#xff0c;在快捷菜单单击添加-…