当前位置: 首页 > news >正文

java 解析word模板(2024-01-25)

本文主要功能是解析word模板

       这是一个word解析类,因为我做的系统用到了而且没有可用的帮助类,只能自己写。之前的实现方式是freemarker 模板解析。但是这次要求用poi不在使用freemarker。实现功能比较少,主要是满足开发需求即可,没有实现其它功能。实现功能如下:

1、word内文本内容解析

2、word内表格内容解析

3、word内图片内容解析

4、word脚注内容解析

     功能实现的比较匆忙没有好好设计,如果可以将图标,图片,脚注等设置为实体类,便于配置管理。

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.List;
import java.util.Properties;

import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFFootnote;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPicture;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
import org.springframework.util.PropertyPlaceholderHelper;

import com.alibaba.cloud.commons.lang.StringUtils;

/**
 * 通过word模板生成新的word工具类
 **
 */
public class WordUtil {
    public static final PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");

    /**
     * 根据模板生成新word文档 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
     * 
     * @param textMap 需要替换的信息集合
     * @return 成功返回true,失败返回false
     */
    public static void changWord(InputStream inputStream, Properties properties, int height, int width) {

        // InputStream in = null;
        try {
            // 获取docx解析对象
            XWPFDocument document = new XWPFDocument(inputStream);
            // 解析替换文本段落对象
            WordUtil.changeText(document, properties);
            // 解析替换表格对象
            WordUtil.changeTable(document, properties);
            // 替换文本中的图片
            WordUtil.changePicture(document, properties, height, width);
            // 脚注/尾注解析 footnote
            WordUtil.changeFootNote(document, properties);

            File file = new File("I://实体文件.docx");
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();
            document.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 尾注解析
     * 
     * @param document
     * @param properties
     */
    public static void changeFootNote(XWPFDocument document, Properties properties) {
        List<XWPFFootnote> footNoteList = document.getFootnotes();
        for (XWPFFootnote footnote : footNoteList) {
            List<XWPFParagraph> paragraphs = footnote.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                String text = paragraph.getText();
                if (checkText(text)) {
                    List<XWPFRun> runs = paragraph.getRuns();
                    String key = keyParam(runs);
                    for (XWPFRun run : runs) {
                        run.setText("", 0);
                    }
                    // 替换模板原来位置
                    String value = changeValue(key, properties);
                    // 字符串中有可能是图片转换的字符串
                    if (StringUtils.isNotEmpty(value)) {
                        runs.get(0).setText(value, 0);
                    }

                }
            }
        }
    }

    /***
     * 指定替换模板中的图片
     * 
     * @param document
     * @param filePath
     * @param height
     * @param width
     */
    public static void changePicture(XWPFDocument document, Properties properties, Integer height, Integer width) {

        // 获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            // 判断此段落时候需要进行替换
            String text = paragraph.getText();
            if (checkText(text)) {
                List<XWPFRun> runs = paragraph.getRuns();
                String key = keyParam(runs);
                for (XWPFRun run : runs) {
                    // 字符串中有可能是图片转换的字符串
                    String value = changeValue(key, properties);
                    if (value.startsWith("data:image")) {
                        byte[] imageBytes = Base64.getDecoder().decode(value.split(",")[1]); // 获取Base64编码后的图像数据部分

                        try(ByteArrayInputStream in = new ByteArrayInputStream(imageBytes); ){// 创建ByteArrayInputStream对象
                        // 添加图片
                        XWPFPicture xwpfPicture = run.addPicture(in, XWPFDocument.PICTURE_TYPE_JPEG, "图片1.jpg",
                                Units.toEMU(width), Units.toEMU(height));
                        // 为图片添加边框
                        xwpfPicture.getCTPicture().getSpPr().addNewLn().addNewSolidFill().addNewSchemeClr()
                                .setVal(STSchemeColorVal.Enum.forString("tx1"));
                        }catch(Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    public static String keyParam(List<XWPFRun> runs) {
        if (runs.isEmpty()) {
            return "";
        }
        StringBuffer st = new StringBuffer();
        // 转换为一个字符串 [${E_002, 1, }${E_002, 2, }${E_002, 3, }]
        for (XWPFRun run : runs) {
            st.append(run.text());
        }
        return st.toString().replace(",", "");
    }

    /**
     * 替换段落文本
     * 
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeText(XWPFDocument document, Properties properties) {
        // 获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (XWPFParagraph paragraph : paragraphs) {
            // 判断此段落时候需要进行替换
            String text = paragraph.getText();
            if (checkText(text)) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    // 替换模板原来位置
                    String value = changeValue(run.toString(), properties);
                    // 字符串中有可能是图片转换的字符串
                    if (StringUtils.isNotEmpty(value) && !value.startsWith("data:image")) {
                        run.setText(value, 0);
                    }
                }
            }
        }

    }

    /**
     * 替换表格对象方法
     * 
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeTable(XWPFDocument document, Properties properties) {
        // 获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        for (int i = 0; i < tables.size(); i++) {
            // 只处理行数大于等于2的表格,且不循环表头
            XWPFTable table = tables.get(i);
            if (table.getRows().size() > 1) {
                // 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
                if (checkText(table.getText())) {
                    List<XWPFTableRow> rows = table.getRows();
                    // 遍历表格,并替换模板
                    eachTable(rows, properties);
                }
            }
        }
    }

    /**
     * 遍历表格
     * 
     * @param rows    表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(List<XWPFTableRow> rows, Properties properties) {
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                // 判断单元格是否需要替换
                if (checkText(cell.getText())) {
                    // 基本一个单元格,都是size=1,如果预防意外,可以增加判断,或者添加循环
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    // System.out.println(String.format("text:%s,paragraphs:%d",cell.getText(),
                    // paragraphs.size()));
                    // for (XWPFParagraph paragraph : paragraphs) {
                    // List<XWPFRun> runs = paragraph.getRuns();
                    // 替换模板原来位置
                    XWPRunValue(paragraphs.get(0).getRuns(), properties);
                    // }
                }
            }
        }
    }

    /**
     * 这个方法是一次处理一个单元格,一个单元格内,被解析成了 XWPFRun, 只给第一个 XWPFRun赋值即可,其它都赋值""
     * 
     * @param runs
     * @param textMap
     */
    public static void XWPRunValue(List<XWPFRun> runs, Properties properties) {
        if (runs.size() == 1) {
            runs.get(0).setText(changeValue(runs.get(0).toString(), properties), 0);
            return;
        }
        StringBuffer st = new StringBuffer();
        // 转换为一个字符串 [${E_002, 1, }${E_002, 2, }${E_002, 3, }]
        for (XWPFRun run : runs) {
            //
            st.append(run.text());
            run.setText("", 0);
        }
        String value = st.toString().replace(",", "");
        value = changeValue(value, properties);
        // 一次性替换全部的值
        runs.get(0).setText(value, 0);
    }

    /**
     * 判断文本中时候包含$
     * 
     * @param text 文本
     * @return 包含返回true,不包含返回false
     */
    public static boolean checkText(String text) {
        return (text.indexOf("$") != -1);
    }

    /**
     * 匹配传入信息集合与模板
     * 
     * @param value   模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static String changeValue(String value, Properties properties) {
        if (!checkText(value)) {
            return value;
        }
        return helper.replacePlaceholders(value, properties);
    }


    public static void main(String[] args) throws Exception {
        // 从FTP读取文件模板
        InputStream is = new FileInputStream(new File("I://模板文件.docx"));

        // 填充文本和表格需要替换的数据
        Properties properties = new Properties();
        properties.put("E_0001", "2000年01月01日");
        properties.put("E_0002", "第一行");
        properties.put("E_0003", "脚注解析异常");
        // 图片字符串
        properties.put("P01", "data:image/jpg;base64,"+"图片转换的字符串") ;
        WordUtil.changWord(is, properties, 140, 400);

    }

}
 

相关文章:

java 解析word模板(2024-01-25)

本文主要功能是解析word模板 这是一个word解析类&#xff0c;因为我做的系统用到了而且没有可用的帮助类&#xff0c;只能自己写。之前的实现方式是freemarker 模板解析。但是这次要求用poi不在使用freemarker。实现功能比较少&#xff0c;主要是满足开发需求即可&#xff0c;没…...

flutter-相关个人记录

1、flutter 安卓打包打包报错 flutter build apk -v --no-tree-shake-icons 2、获取华为指纹证书命令 keytool -list -v -keystore ***.jks 3、IOS项目中私有方法查找隐藏文件中 1、cd 项目目录地址 2、grep -r xerbla. "xerbla"为需要查找的关键字 3…...

互斥锁/读写锁(Linux)

一、互斥锁 临界资源概念&#xff1a; 不能同时访问的资源&#xff0c;比如写文件&#xff0c;只能由一个线程写&#xff0c;同时写会写乱。 比如外设打印机&#xff0c;打印的时候只能由一个程序使用。 外设基本上都是不能共享的资源。 生活中比如卫生间&#xff0c;同一…...

Jackson序列化Bean额外属性附加--@JsonAnyGetter、@JsonUnwrapped用户

1. 场景 有一项工作&#xff0c;需要将数据从一个服务S中读取出来&#xff08;得到的是一个JSON&#xff09;&#xff0c;将数据解析转换以后构造成一个数组的类型A的对象&#xff0c;写入到一个服务T中。 A.class Data public class A {String f0 ;String f1 ; }在发现需要…...

排序算法——冒泡排序算法详解

冒泡排序算法详解 1.引言2.算法概览2.1输入处理2.2核心算法步骤2.3数据结构2.4复杂度分析 3.算法优化4.边界条件和异常处理5.实验和测试6.应用和扩展7.代码示例8.总结 1.引言 冒泡排序是一种简单而直观的比较排序算法&#xff0c;它通过多次遍历数组&#xff0c;比较相邻元素并…...

宋仕强论道之华强北的缺货潮(十六)

始于2019年缺货潮让华强北又生产一大批亿万富翁&#xff0c;缺货的原因主要是&#xff1a;首先&#xff0c;疫情封控导致大量白领在家远程办公&#xff0c;需要购买电脑、打印机等办公设备&#xff0c;同时孩子们也要在家上网课&#xff0c;进一步增加对电子智能终端产品的需求…...

登录注册页面

前提&#xff1a;基于element-ui环境 模态登录组件 分析Login.vue <template><div class"login"><span click"handleClose">X</span></div> </template><script> export default {name: "Login",m…...

视频美颜SDK详解:动态贴纸技术的前沿探索

当下&#xff0c;美颜SDK的动态贴纸技术作为视频美颜的独特亮点&#xff0c;吸引了越来越多开发者和用户的关注。 一、技术详解 动态贴纸技术是视频美颜SDK中的一项创新性功能&#xff0c;它通过在实时视频中添加各种动态效果&#xff0c;为用户提供更加生动有趣的拍摄体验。…...

vue3 实现上传图片裁剪

在线的例子以及代码&#xff0c;请点击访问链接...

flink1.18 广播流 The Broadcast State Pattern 官方案例scala版本

对应官网 https://nightlies.apache.org/flink/flink-docs-master/docs/dev/datastream/fault-tolerance/broadcast_state/ 测试数据 * 广播流 官方案例 scala版本* 广播状态* https://nightlies.apache.org/flink/flink-docs-master/docs/dev/datastream/fault-tolerance…...

vueRouter中scrollBehavior实现滚动固定位置

使用前端路由&#xff0c;当切换到新路由时&#xff0c;想要页面滚到顶部&#xff0c;或者是保持原先的滚动位置&#xff0c;就像重新加载页面那样。 vue-router 能做到&#xff0c;而且更好&#xff0c;它让你可以自定义路由切换时页面如何滚动。 注意: 这个功能只在 HTML5 h…...

解决WinForms跨线程操作控件的问题

解决WinForms跨线程操作控件的问题 介绍 在构建Windows窗体应用程序时&#xff0c;我们通常会遇到需要从非UI线程更新UI元素的场景。由于WinForms控件并不是线程安全的&#xff0c;直接这样做会抛出一个异常&#xff1a;“控件’control name’是从其他线程创建的&#xff0c;…...

从零开始:Git 上传与使用指南

Git 是一种非常强大的版本控制系统&#xff0c;它可以帮助您在多人协作开发项目中更好地管理代码版本&#xff0c;并确保每个团队成员都能及时地获取最新的代码更改。在使用 Git 进行版本控制之前&#xff0c;您需要先进行一些设置&#xff0c;以确保您的代码能够顺利地与远程仓…...

Docker compose部署Golang服务

Docker Compose 部署 在使用docker部署时&#xff0c;除了使用--link的方式来关联容器之外&#xff0c;还可以使用 docker compose 运行多个容器。 本文以项目&#xff1a;https://github.com/johncxf/go-api 为例。 定义 Dockerfile 我这里用于区分默认 Dockerfile 文件&a…...

Day36 435无重叠区间 763划分字母区间

435 无重叠区间 给定一个区间的集合&#xff0c;找到需要移除区间的最小数量&#xff0c;使剩余区间互不重叠。 注意: 可以认为区间的终点总是大于它的起点。 区间 [1,2] 和 [2,3] 的边界相互“接触”&#xff0c;但没有相互重叠。 本题与上一题类似&#xff1a; 如果按照左…...

【Servlet】如何编写第一个Servlet程序

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Servlet】 本专栏旨在分享学习Servlet的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; Servlet是Java编写的服务器端…...

读懂比特币—bitcoin代码分析(五)

今天的代码分析主要是 bitcoin/src/init.cpp 文件中的三个函数&#xff1a;AppInitSanityChecks、AppInitLockDataDirectory、AppInitInterfaces&#xff0c;下面我们来说明这三个函数是用来干什么的&#xff0c;并逐行解读函数代码&#xff0c;先贴出源代码如下&#xff1a; …...

uniapp使用uQRCode插件生成二维码的简单使用

最近在找移动端绘制二维码的问题 &#xff0c;直接上代码 下载 weapp-qrcode.js(可以通过npm install weapp-qrcode --save 下载,之后把它父子到untils目录下&#xff09; npm install weapp-qrcode --save在组件页面使用 <canvas id"couponQrcode" canvas-id&qu…...

【寒假每日一题·2024】AcWing 4965. 三国游戏(补)

文章目录 一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、时间复杂度3、代码详解 一、题目 1、原题链接 4965. 三国游戏 2、题目描述 二、解题报告 1、思路分析 思路参考y总&#xff1a;y总讲解视频 &#xff08;1&#xff09;题目中的获胜情况分为三种&#xff…...

docker 安装mongodb 数据库

1.拉取mongodb镜像 docker pull mongo2.创建文件夹 mkdir -p /home/mongo/conf/ mkdir -p /home/mongo/data/ mkdir -p /home/mongo/logs/3.新增mongod.conf文件 cd /home/mongo/conf && vi mongod.confmongod.conf文件内容&#xff1a; # 数据库文件存储位置 dbpa…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...