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);

    }

}
 

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

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

相关文章

银行数据仓库体系实践(7)--数据模型设计及流程

数据仓库作为全行或全公司的数据中心和总线&#xff0c;汇集了全行各系统以及外部数据&#xff0c;通过良好的系统架构可以保证系统稳定性和处理高效性&#xff0c;那如何保障系统数据的完备性、规范性和统一性呢&#xff1f;这里就需要有良好的数据分区和数据模型&#xff0c;…

【Vue实用功能】Vue实现文档在线预览功能,在线预览PDF、Word等office文件

1、Office Web(微软的开发接口) 优点 没有 Office也可以直接查看Office 文件适用于移动端、PC无需下载文件就可以在浏览器中查看 <iframe src"文档地址" frameborder"0" /> const docUrl 外网可预览的地址 const url encodeURIComponent(docUrl…

C#用DateAndTime.DateDiff方法和TimeSpan分别计算时间间隔

目录 一、计算时间间隔的方法 1.用DateAndTime.DateDiff方法计算时间间隔 2.使用TimeSpan获取日期时间间隔 二、实例 1.示例一&#xff1a;用DateAndTime.DateDiff方法计算时间间隔 2.示例二&#xff1a;使用TimeSpan获取日期时间间隔 一、计算时间间隔的方法 1.用Date…

element el-date-picker type=“datetimerange“

刚写完结果需求变更了。封装的时间组件重新做。结合eacharts 。 直接上代码了 日期选择组件封装 <template><section class"warning-container"><header class"query-head"><el-form :inline"true" class"query-form…

运费业务:

新增或修改&#xff1a; 第一步&#xff1a;查询是否有存在的模板&#xff0c;没有的话直接新增 第二步&#xff1a;模板不为空(存在经济区互寄情况)&#xff0c;判断经济区中的关联城市是否已经添加&#xff08;一条SQL数据中经济区中的关联城市可以选择多个,但其他经济区中也…

JavaSE基础学习

一、编程入门 二、Java语言概述 三、Java基本语法 四、程序流程控制 五、数组 六、面向对象(上) 数组工具类的封装: 七、面向对象(中) 八、面向对象(下) 九、异常处理 十、多线程 十一、常用类 十二、枚举类与注解 十三、集合 十四、泛型 十五、IO流 十六、网络编程 十七、反射…

【VSAN数据恢复】VSAN数据重构迁移失败的数据恢复案例

VSAN简介&#xff1a; VSAN存储是一个对象存储&#xff0c;以文件系统呈现给在vSphere主机上。这个对象存储服务会从VSAN集群中的每台主机上加载卷&#xff0c;将卷展现为单一的、在所有节点上都可见的分布式共享数据存储。 对于虚拟机来说&#xff0c;只有一个数据存储&#x…

JS 中的 async 与 await

课程地址 有 4 个返回 Promise 对象的函数 ABCD&#xff0c;现在想让这 4 个 Promise 顺序执行&#xff1a; const isA true; const isB true; const isC true; const isD true;function A() {return new Promise((resolve, reject) > {console.log("running A&q…

Docker容器基本管理

目录 一、概述 &#xff08;一&#xff09;为什么要用到容器 &#xff08;二&#xff09;docker概念 1.镜像 2.容器 3.仓库 &#xff08;三&#xff09;Docker与虚拟机的区别 &#xff08;四&#xff09;Linux namespace的六大类型 二、安装docker容器引擎 &#xff…

区块链技术在教育领域的应用:Web3教育变革

随着Web3时代的来临&#xff0c;区块链技术在各个领域都展现出了巨大的潜力&#xff0c;而在教育领域&#xff0c;区块链的应用正引领着一场教育变革。本文将深入探讨区块链技术在教育领域的创新应用&#xff0c;以及这一应用如何推动Web3时代的教育变革。 1. 学历和成绩的去中…

成本更低、更可控,云原生可观测新计费模式正式上线

云布道师 在上云开始使用云产品过程中&#xff0c;企业一定遇见过两件“讨厌”事&#xff1a; 难以理解的复杂计费逻辑&#xff0c;时常冒出“这也能收费”的感叹&#xff1b; 某个配置参数调节之后&#xff0c;云产品使用成本不可预估的暴涨。 可观测作为企业 IT 运维必须品…

Pandas数据可视化:折线图、条形图、直方图、箱形图、核密度图、面截图、饼图、散点图、六边形图

plot()方法 plot()方法可以绘制图形,常见参数如下: (1)x:用来设置所绘制图形的x轴数据。 (2)y:用来设置所绘制图形的y轴数据。 (3)kind:用来设置所绘制图形的样式, 默认为:line(折线图)、 bar(垂直条形图)、 barh(横向柱状图,即横向条形图)、 hist(直方…

第13次修改了可删除可持久保存的前端html备忘录:删除按钮靠右,做了一个背景主题:现代深色

第13次修改了可删除可持久保存的前端html备忘录&#xff1a;删除按钮靠右&#xff0c;做了一个背景主题&#xff1a;现代深色 备忘录代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"vi…

Go语言指针变量

1. 指针变量 区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 Go语言中的指针3个概念&#xff1a;指针地址、指针类型和指针取值。 1.1. Go语言中的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想要修改某个变量的时候&a…

Python工具:openpyxl

文档&#xff1a;https://openpyxl.readthedocs.io/en/stable/tutorial.html# 源代码&#xff1a;https://foss.heptapod.net/openpyxl/openpyxl 1.工作簿对象和工作表对象的使用 openpyxl 中有一个 Workbook 类&#xff0c;可以使用 Workbook() 创建工作簿对象&#xff08;该…

【数据结构】72变的双端队列

双端队列 前言一、双端队列1.1 双端队列的定义1.2 输入受限的双端队列1.3 输出受限的双端队列1.5 输入输出都受限的双端队列1.6 小结 二、双端队列的使用2.1 双端队列的出队序列——暴力求解2.1.1 栈的出栈序列2.1.2 输入受限的双端队列2.1.3 输出受限的双端队列2.1.4 输入输出…

2859. 计算 K 置位下标对应元素的和(01-25每日一题)

链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 思路 : 直接模拟 代码 class Solution { public:int get(int n){int cnt 0 ;while(n){cnt ;n n & (n-1) ;}return cnt ;}int sumIndicesWithKSetBits(vector<int>& nums, …

three.js中Meshline库的使用

three.js中Meshline的使用 库的地址为什么要使用MeshLine,three.js内置的线不好用吗?MeshLine入门MeshLine的深入思考样条曲线一个问题 库的地址 https://github.com/spite/THREE.MeshLine?tabreadme-ov-file 为什么要使用MeshLine,three.js内置的线不好用吗? 确实不好用,…

金融帝国实验室(Capitalism Lab)V10版本推出新招高管待命选项

金融帝国实验室&#xff08;Capitalism Lab&#xff09;V10版本推出新招高管待命选项 ————————————— 【全新V10版本开发播报】 即将发布的V10版本&#xff0c;在“分配管理器”菜单上将引入一个名为“暂时待命”的新功能。启用此选项后&#xff0c;分配给公司的高…

256:vue+openlayers利用高德逆地理编码,点击地图,弹出某点坐标和地址信息

第256个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中利用高德逆地理编码,点击地图,弹出某点坐标和地址信息。这里要仔细阅读高德地图的逆编码API,同时要注意的是,这种转换在中国很好用,到了欧美国家就不好使了。 直接复制下面的 vue+openlayers源代码…