Java根据word 模板,生成自定义内容的word 文件

Java根据word 模板,生成自定义内容的word 文件

  • 背景
  • 1 使用技术
  • 2 实现方法
    • 依赖
  • 3 问题
  • 4

背景

主要是项目中需要定制化一个word,也就是有一部分是固定的,就是有一个底子,框架,里面的内容是需要填充的。然后填充的内容很多,包括文本框、图片、文本、前端传过来的富文本、表格的设计。
然后网上找了很多资料,没有一个比较详细的文档,就决定自己写一份。
  在我看来比较复杂的是

 1. 整个文本框的模版如何填充占位符2. 图片的填充如何控制长和宽(如何使用base64填充)3. 前端传过来的富文本,或者单纯的富文本,如何优化格式(比如表格信息的丢失)

在这里插入图片描述
上面是文本还有文本框,下面是表格和图片,在这里插入图片描述

在这里插入图片描述

1 使用技术

我这里最后采用的是EasyPoi 填充word模版。
实际使用的是JAVA poi-tl-ext 富文本转word。

使用PictureRenderData来控制生成图片的大小和base64。

2 实现方法

依赖

首先是依赖,在我看来核心的是

<dependency><groupId>io.github.draco1023</groupId><artifactId>poi-tl-ext</artifactId><version>0.4.15</version></dependency>
  • poi-tl-ext已经包含了poi,poi-tl等jar包,所以无需重复导入
  • poi-tl文档链接
  • poi-tl-ext github链接
    其他的网上还是蛮多的,这个主要就是渲染HTML的,HTML的代码,或者富文本都可以渲染的。

我最开始用的是EasyPoi的方法进行导入的,也就是网上比较常见的下面这样的方法进行填充word模版,如果你只比较简单的数据,那使用EasyPoi就够了。

XWPFDocument doc1 = WordExportUtil.exportWord07(templatePath, params).create;

但是我涉及到了富文本的转换,就是我要把富文本渲染成docx支持的格式,所以我换了种方法。

 doc = XWPFTemplate.compile(fileInputStream, configure).render(params).getXWPFDocument();

实际上还是获取模版,然后读取占位符,然后

核心代码如下:
// html渲染插件HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();// 第一个案例Configure configure = Configure.builder()// 注册html解析插件.bind("content", htmlRenderPolicy)// .bind("content2", htmlRenderPolicy).build();// 映射数据MapMap<String, Object> data = new HashMap<>();data.put("content", content2Html(你的HTML代码));// 读取模板文件,并渲染数据XWPFTemplate template = XWPFTemplate.compile(getResourceInputStream("/html2wordtemplate.docx"), configure).render(data);// 写入文件template.writeToFile("demo4.docx");template.close();

我这里是在静态资源里的模版,你也可以读取自己的其他目录下的文件,主要是只要是InputStream就都可以。

3 问题

其实到上面,最简单的已经结束了,后面主要是我遇到的设计到HTML富文本中,涉及一些跟表格有关的问题,最开始的时候,就比如最开的时候前端给我的富文本代码如下:

<p style="text-indent: 28pt;"><br></p>
<table style="width: auto;"><tbody><tr><td colSpan="1" rowSpan="2" width="226">活动名称</td><td colSpan="1" rowSpan="2" width="160">计划举办数</td><td colSpan="2" rowSpan="1" width="329">实际举办数</td><td colSpan="1" rowSpan="1" width="161">延期数</td></tr><tr><td colSpan="1" rowSpan="1" width="152">已成功举办数</td><td colSpan="1" rowSpan="1" width="177">筹备完成待举办</td><td colSpan="1" rowSpan="1" width="161">延期</td></tr><tr><td colSpan="1" rowSpan="1" width="226">适配开发</td><td colSpan="1" rowSpan="1" width="160">54</td><td colSpan="1" rowSpan="1" width="152">46</td><td colSpan="1" rowSpan="1" width="177">8</td><td colSpan="1" rowSpan="1" width="161">0</td></tr><tr><td colSpan="1" rowSpan="1" width="226">边缘缓存系统</td><td colSpan="1" rowSpan="1" width="160">24</td><td colSpan="1" rowSpan="1" width="152">11</td><td colSpan="1" rowSpan="1" width="177">13</td><td colSpan="1" rowSpan="1" width="161">0</td></tr>

他的表格宽度是内联在td标签中的,这个在HtmlRenderPolicy里面实际上渲染后,会出现两个问题。

  1. width属性会丢失,转换后的结果就是等分的。
  2. 前端传给我的没有边界线,没有表格的框线。

没有边框
在这里插入图片描述
表格等分
在这里插入图片描述
针对于这个等分的情况,解决办法就是:
把原始的html格式转变成css进行处理:
  其实我做了很多,一方面是

  1. 原始的html格式转变成css
  2. 为 <table>和 <td> 标签添加边框

到这其实已经结束了,但是我的需求涉及到HTML----->WORD----->HTML(发送邮件)。
3. 所以我多做了一步处理,就是给 <table>标签添加了一个"width: 100%;"样式
在这里插入图片描述

/*** 处理 HTML:转换 <td> 的宽度为 CSS 样式并为 <table> 和 <td> 标签添加边框* @param html 原始 HTML 字符串* @return 修改后的 HTML 字符串*/public static String convertTdWidthAndAddBorders(String html) {// 解析 HTMLDocument doc = Jsoup.parse(html);// 获取所有的 <tr> 标签Elements trs = doc.select("tr");// 遍历每个 <tr> 标签for (Element tr : trs) {Elements tds = tr.select("td");// 计算当前行所有 <td> 的宽度总和(只针对数值宽度)int totalWidth = 0;for (Element td : tds) {String widthValue = td.attr("width");// 累加数值格式的宽度if (!widthValue.isEmpty() && !widthValue.contains("%")) {totalWidth += Integer.parseInt(widthValue);}}// 如果该行有宽度总和,继续处理for (Element td : tds) {String widthValue = td.attr("width");String existingStyle = td.attr("style");  // 获取现有的 style 属性String borderStyle = "border: 1px solid #CCC;"; // 四周边框样式//String borderStyle = "border-right: 1px solid #CCC; border-bottom: 1px solid #CCC;"; // 边框样式// 处理百分比格式的宽度if (!widthValue.isEmpty() && widthValue.contains("%")) {// 如果已有 style,合并宽度和边框样式td.removeAttr("width");td.attr("style", mergeStyles(existingStyle, "width: " + widthValue + ";", borderStyle));}// 处理数值格式的宽度else if (!widthValue.isEmpty()) {int width = Integer.parseInt(widthValue);if (totalWidth > 0) {// 计算百分比double percentWidth = (double) width / totalWidth * 100;// 如果已有 style,合并宽度和边框样式td.removeAttr("width");td.attr("style", mergeStyles(existingStyle, String.format("width: %.2f%%;", percentWidth), borderStyle));}} else {// 直接添加边框样式,如果没有宽度td.attr("style", mergeStyles(existingStyle, "", borderStyle));}}}// 将 <table> 标签添加边框样式Elements tables = doc.select("table");for (Element table : tables) {String existingStyle = table.attr("style");  // 获取现有的 style 属性//String tableBorderStyle = "border-top: 1px solid #CCC; border-left: 1px solid #CCC;"; // 表格边框样式String tableBorderStyle = "border: 1px solid #CCC;";table.attr("style", mergeStyles(existingStyle, "width: 100%;", tableBorderStyle));}// 返回修改后的 HTMLreturn doc.toString();}/*** 合并多个 style 属性* @param existingStyle 原有的 style 属性* @param newStyle 新的 style 属性* @param additionalStyle 其他样式(如边框)* @return 合并后的 style 字符串*/private static String mergeStyles(String existingStyle, String newStyle, String additionalStyle) {StringBuilder mergedStyle = new StringBuilder();if (existingStyle != null && !existingStyle.trim().isEmpty()) {mergedStyle.append(existingStyle.trim());if (!existingStyle.trim().endsWith(";")) {mergedStyle.append("; ");}}if (!newStyle.isEmpty()) {mergedStyle.append(newStyle.trim());if (!newStyle.trim().endsWith(";")) {mergedStyle.append("; ");}}if (!additionalStyle.isEmpty()) {mergedStyle.append(additionalStyle.trim());if (!additionalStyle.trim().endsWith(";")) {mergedStyle.append("; ");}}return mergedStyle.toString().trim();}

转换的结果如下:

<tr><td colspan="1" rowspan="1" style="width: 34.76%;border: 1px solid #CCC;">节点下线流程</td><td colspan="1" rowspan="1" style="width: 16.95%;border: 1px solid #CCC;">3</td><td colspan="1" rowspan="1" style="width: 16.59%;border: 1px solid #CCC;">-3</td><td colspan="1" rowspan="1" style="width: 16.22%;border: 1px solid #CCC;">54.12%</td><td colspan="1" rowspan="1" style="width: 15.49%;border: 1px solid #CCC;">-0.17pp</td></tr><tr><td colspan="1" rowspan="1" style="width: 34.76%;border: 1px solid #CCC;">节点上线流程</td><td colspan="1" rowspan="1" style="width: 16.95%;border: 1px solid #CCC;">8</td><td colspan="1" rowspan="1" style="width: 16.59%;border: 1px solid #CCC;">-5</td><td colspan="1" rowspan="1" style="width: 16.22%;border: 1px solid #CCC;">36.91%</td><td colspan="1" rowspan="1" style="width: 15.49%;border: 1px solid #CCC;">+14.02pp</td></tr>

会把原始的内联转换成css的,然后就能成功转换并且成功显示标签了。
在这里插入图片描述

4

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

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

相关文章

Qt中的Base64编码

Qt中的Base64编码 Qt之Base64编解码 Base64编码是一种用于表示二进制数据的文本编码方式&#xff0c;通常用于在需要通过文本传输二进制数据的场景中&#xff0c;比如在电子邮件和URL中传递数据。它将二进制数据转换为由64个ASCII字符组成的字符串&#xff0c;便于在文本环境中…

宝塔部署前后端分离若依项目--CentOS7版

准备&#xff1a; CentOS7服务器一台 通过网盘分享的文件&#xff1a;CentOS 7 h 链接: https://pan.baidu.com/s/17DF8eRSSDuj9VeqselGa_Q 提取码: s7x4 大家有需要可以下载这个&#xff0c;密码61 若依前端编译后文件 通过网盘分享的文件&#xff1a;ruoyi-admin.jar 链…

基于SSM网络在线考试系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;在线考试管理&#xff0c;试题管理&#xff0c;考试管理&#xff0c;系统管理 前台账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;在线考试&#xff0c;公告信…

MySQL 实现简单的性能优化

一&#xff1a;硬件优化 更高的网络带宽&#xff1a;在处理大规模的远程请求时可以提高MySQL服务器的响应速度&#xff1b; 更大的内存空间&#xff1a;有助于缓存更多的数据库数据&#xff0c;减少磁盘I/O操作&#xff0c;提高整体性能&#xff1b; 换用企业级SSD&#xff1…

题解:P11215 【MX-J8-T3】水星湖

好久没写题解了&#xff0c;交一发吧。 题目传送门 思路讲解 我们用一个结构体 struct 存储每一个格子的状态&#xff0c;tp 表示格子的类型&#xff0c;t 表示如果该格子种过树&#xff0c;种树的最近时间&#xff0c;die 表示如果该格子有树&#xff0c;这棵树会不会永远都…

Offset Explorer 连接kafka使用SASL 进行身份验证详解

使用 Offset Explorer&#xff08;也称为 Kafka Tool&#xff09;3.0.1 连接到 Kafka 并通过 SASL 进行身份验证&#xff0c;可以按照以下步骤进行配置&#xff1a; 1. 确保 Kafka 配置支持 SASL 首先&#xff0c;确保你的 Kafka 集群已配置为支持 SASL。你需要在 server.pro…

笔记:weblogic升级及版本信息

官方文档 Patch Set Update (PSU) Release Listing for Oracle WebLogic Server (WLS) (Doc ID 1470197.1) 版本信息 12.2.1.412.2.1.312.2.1.212.2.1.112.2.1.012.1.3.012.1.2.012.1.1.010.3.610.3.510.3.4

【小白学机器学习17】 概率论的认识论和方法论

目录 1 分析概率问题的思路&#xff1a;三段论逻辑 2 学习概率时的三段论推导 1 分析概率问题的思路&#xff1a;三段论逻辑 现在很多辩证法&#xff0c;从黑格尔这继承&#xff0c;却变成了2段论&#xff0c;感觉缺乏一个桥梁&#xff0c;逻辑上思考问题的链条变难了基于一…

用python-pptx轻松统一调整演示文档配色方案

哈喽,大家好,我是木头左! 安装与准备:python-pptx入门 确保你的Python环境中已经安装了python-pptx库。如果没有,可以通过pip进行快速安装: pip install python-pptx此外,对于PPT文档的操作,了解一些基本的PowerPoint概念是有帮助的,比如幻灯片母版(Slide Master)…

基于微信小程序的购物系统【附源码、文档】

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

印章图片怎么抠出透明背景?这4个一键抠图工具建议收藏!

在处理印章图片时&#xff0c;背景色的存在往往成为影响使用效果的一大障碍&#xff0c;特别是在需要将印章与不同背景融合时&#xff0c;不透明的背景色会显得尤为突兀。为了应对这一挑战&#xff0c;市场上涌现了一系列高效的一键抠图工具&#xff0c;它们能够迅速将印章图片…

Scala的继承

Scala中的继承是指在原有的类的基础上定义一个新类&#xff0c;原有的类称为父类&#xff0c;新类成为子类 例&#xff1a; class Animal(){var leg4def run():Unit{println("animal run.....")} } //继承&#xff1a;不劳而获 class Dog extends Animal(){} object…

ORB-SLAM2 ---- Frame中在主函数中被调用的函数

文章目录 一、Frame::isInFrustum1. 函数讲解2. 源码 二、Frame::GetFeaturesInArea1. 函数讲解2. 函数源码 三、Frame::ComputeBoW1. 函数讲解2. 函数源码 四、Frame::UnprojectStereo1. 函数讲解2. 函数源码 五、总结 一、Frame::isInFrustum 1. 函数讲解 此函数判断地图点…

【Linux】进程池

目录 进程池 进程池的概念&#xff1a; 手搓进程池&#xff1a; 1、创建信道和子进程 2、通过channel控制子进程 3、回收管道和子进程 进程池 进程池的概念&#xff1a; 定义一个池子&#xff0c;在里面放上固定数量的进程&#xff0c;有需求来了&#xff0c;就拿一个池中…

05 go语言(golang) - 常量和条件语句

常量 在Go语言中&#xff0c;常量是使用 const 关键字定义的&#xff0c;并且一旦被赋值后&#xff0c;它们的值在程序运行期间不能改变。常量可以是字符、字符串、布尔或数值类型。 基本特性 不可修改&#xff1a;一旦一个常量被定义&#xff0c;它的值就不能被更新。编译时…

repo 命令大全详解(第十一篇 repo init)

repo forall 命令用于在指定的项目上执行给定的命令&#xff0c;非常适合批量操作。 参数分类及解释 基本参数 [<project>...]: 可选&#xff0c;指定要操作的项目。如果不指定&#xff0c;则对所有项目执行命令。 示例: repo forall my_project -c "git status&q…

YoloV10——专栏目录

摘要 &#x1f525;&#x1f680;本专栏教你如何嗨翻YoloV10&#xff01;&#x1f680;&#x1f525; &#x1f4a1;升级大招&#xff1a;汲取最新论文精华&#xff0c;给你一整套YoloV10升级秘籍&#xff01;包括但不限于&#xff1a;注意力加持、卷积大换血、Block革新、Ba…

微软运用欺骗性策略大规模打击网络钓鱼活动

微软正在利用欺骗性策略来打击网络钓鱼行为者&#xff0c;方法是通过访问 Azure 生成外形逼真的蜜罐租户&#xff0c;引诱网络犯罪分子进入以收集有关他们的情报。 利用收集到的数据&#xff0c;微软可以绘制恶意基础设施地图&#xff0c;深入了解复杂的网络钓鱼操作&#xff…

使用JMeter进行Spring Boot接口的压力测试

使用 Apache JMeter 对接口进行压力测试是一个相对简单的过程。以下是详细的步骤&#xff0c;包括安装、配置和执行测试计划。 1. 下载和安装 JMeter 下载 JMeter 从 JMeter 官方网站https://jmeter.apache.org/download_jmeter.cgi 下载最新版本的 JMeter。 解压缩 将下载的 …

记录一次部署 k8s 集群无法启动

情况是这样的&#xff0c;k8s 集群&#xff08;v1.31&#xff09;所需要的组件已安装完成&#xff0c;通过 kubeadm init 已经安装部署成功&#xff0c;但是等几分钟&#xff0c;集群组件就一直在重启&#xff0c;查看日志是连接 etcd 超时&#xff0c;所以查看了 etcd 的日志&…