Bootstrap

使用poi操作word

导入依赖

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.0</version>
        </dependency>
        <!-- hutool工具栏 -->
        <dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.7</version>
        </dependency>

新建一个word.docx

20210802205804809.png

依次插入柱状图 折线图 饼图 表格1 表格2 var和img

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNjA0ODkw,size_16,color_FFFFFF,t_70

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNjA0ODkw,size_16,color_FFFFFF,t_70

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNjA0ODkw,size_16,color_FFFFFF,t_70

将保存后的word模板放在resource目录下

 20210802210126728.png

如何获取word.docx 资源呢 

InputStream is = this.getClass().getClassLoader().getResourceAsStream("word/word.docx");
XWPFDocument doc = new XWPFDocument(is);
List<POIXMLDocumentPart> relations = doc.getRelations();
            relations.forEach((t) -> {
                System.out.println(t.toString());
            });

可以看到word里所有的信息

charts 是图表

以自上而下的顺序排序 chart1,chart2,chart3.....chart_n

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNjA0ODkw,size_16,color_FFFFFF,t_70

所以我只需将对应的图表更新数据即可

注意:柱状图折线图 展示的是数据,参数必须是数字

代码如下

import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * poi操作word中图表的工具类
 *
 * @author zhou
 */
@Component
public class PoiChartsTools {

    private static final BigDecimal bd2 = new BigDecimal("2");

    /**
     * 获取word模板中的所有图表元素,用map存放
     */
    public static Map<String, POIXMLDocumentPart> getPOIXMLDocumentPartMap(XWPFDocument doc) {
        // 获取word模板中的所有图表元素,用map存放
        // 为什么不用list保存:查看doc.getRelations()的源码可知,源码中使用了hashMap读取文档图表元素,
        // 对relations变量进行打印后发现,图表顺序和文档中的顺序不一致,也就是说relations的图表顺序不是文档中从上到下的顺序
        Map<String, POIXMLDocumentPart> chartsMap = new HashMap<>();
        //动态刷新图表
        List<POIXMLDocumentPart> relations = doc.getRelations();
        for (POIXMLDocumentPart poixmlDocumentPart : relations) {
            if (poixmlDocumentPart instanceof XWPFChart) {  // 如果是图表元素
                String str = poixmlDocumentPart.toString();
                String key = str.replaceAll("Name: ", "")
                        .replaceAll(" - Content Type: application/vnd\\.openxmlformats-officedocument\\.drawingml\\.chart\\+xml", "")
                        .trim();
                chartsMap.put(key, poixmlDocumentPart);
            }
        }
        return chartsMap;
    }

    /**
     * 获取word模板中的对应名称的图表元素
     * 返回null则查询不到
     */
    public static POIXMLDocumentPart getPOIXMLDocumentPart(XWPFDocument doc, String chartsName) {
        List<POIXMLDocumentPart> relations = doc.getRelations();
        for (POIXMLDocumentPart poixmlDocumentPart : relations) {
            // 如果是图表元素
            if (poixmlDocumentPart instanceof XWPFChart) {
                String str = poixmlDocumentPart.toString();
                String key = str.replaceAll("Name: ", "")
                        .replaceAll(" - Content Type: application/vnd\\.openxmlformats-officedocument\\.drawingml\\.chart\\+xml", "")
                        .trim();
                if (key.equals(chartsName)){
                    return poixmlDocumentPart;
                }
            }
        }
        return null;
    }

    /**
     * 调用替换柱状图数据
     */
    public static void replaceBarCharts(POIXMLDocumentPart poixmlDocumentPart,
                                        List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
        XWPFChart chart = (XWPFChart) poixmlDocumentPart;
        chart.getCTChart();

        //根据属性第一列名称切换数据类型
        CTChart ctChart = chart.getCTChart();
        CTPlotArea plotArea = ctChart.getPlotArea();

        CTBarChart barChart = plotArea.getBarChartArray(0);
        List<CTBarSer> BarSerList = barChart.getSerList();  // 获取柱状图单位

        //刷新内置excel数据
        refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
        //刷新页面显示数据
        refreshBarStrGraphContent(barChart, BarSerList, listItemsByType, fldNameArr, 1);

    }


    /**
     * 调用替换折线图数据
     */
    public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart,
                                         List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
        XWPFChart chart = (XWPFChart) poixmlDocumentPart;
        chart.getCTChart();

        //根据属性第一列名称切换数据类型
        CTChart ctChart = chart.getCTChart();
        CTPlotArea plotArea = ctChart.getPlotArea();

        CTLineChart lineChart = plotArea.getLineChartArray(0);
        List<CTLineSer> lineSerList = lineChart.getSerList();   // 获取折线图单位

        //刷新内置excel数据
        refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
        //刷新页面显示数据
        refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 1);

    }


    /**
     * 调用替换饼图数据
     */
    public static void replacePieCharts(POIXMLDocumentPart poixmlDocumentPart,
                                        List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
        XWPFChart chart = (XWPFChart) poixmlDocumentPart;
        chart.getCTChart();

        //根据属性第一列名称切换数据类型
        CTChart ctChart = chart.getCTChart();
        CTPlotArea plotArea = ctChart.getPlotArea();

        CTPieChart pieChart = plotArea.getPieChartArray(0);
        List<CTPieSer> pieSerList = pieChart.getSerList();  // 获取饼图单位

        //刷新内置excel数据
        refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
        //刷新页面显示数据
        refreshPieStrGraphContent(pieChart, pieSerList, listItemsByType, fldNameArr, 1);

    }


    /**
     * 调用替换柱状图、折线图组合数据
     */
    public static void replaceCombinationCharts(POIXMLDocumentPart poixmlDocumentPart,
                                                List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
        XWPFChart chart = (XWPFChart) poixmlDocumentPart;
        chart.getCTChart();

        //根据属性第一列名称切换数据类型
        CTChart ctChart = chart.getCTChart();
        CTPlotArea plotArea = ctChart.getPlotArea();

        CTBarChart barChart = plotArea.getBarChartArray(0);
        List<CTBarSer> barSerList = barChart.getSerList();  // 获取柱状图单位
        //刷新内置excel数据
        refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
        //刷新页面显示数据
        refreshBarStrGraphContent(barChart, barSerList, listItemsByType, fldNameArr, 1);


        CTBarChart barChart2 = plotArea.getBarChartArray(0);
        List<CTBarSer> barSerList2 = barChart2.getSerList();  // 获取柱状图单位
        //刷新内置excel数据
        refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
        //刷新页面显示数据
        refreshBarStrGraphContent(barChart2, barSerList2, listItemsByType, fldNameArr, 1);


        CTLineChart lineChart = plotArea.getLineChartArray(0);
        List<CTLineSer> lineSerList = lineChart.getSerList();   // 获取折线图单位
        //刷新内置excel数据
        refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
        //刷新页面显示数据
        refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 1);

    }


    /**
     * 刷新折线图数据方法
     *
     * @param typeChart
     * @param serList
     * @param dataList
     * @param fldNameArr
     * @param position
     * @return
     */
    public static boolean refreshLineStrGraphContent(Object typeChart,
                                                     List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {

        boolean result = true;
        //更新数据区域
        for (int i = 0; i < serList.size(); i++) {
            //CTSerTx tx=null;
            CTAxDataSource cat = null;
            CTNumDataSource val = null;
            CTLineSer ser = ((CTLineChart) typeChart).getSerArray(i);
            //tx= ser.getTx();
            // Category Axis Data
            cat = ser.getCat();
            // 获取图表的值
            val = ser.getVal();
            // strData.set
            CTStrData strData = cat.getStrRef().getStrCache();
            CTNumData numData = val.getNumRef().getNumCache();
            strData.setPtArray((CTStrVal[]) null); // unset old axis text
            numData.setPtArray((CTNumVal[]) null); // unset old values

            // set model
            long idx = 0;
            for (int j = 0; j < dataList.size(); j++) {
                //判断获取的值是否为空
                String value = "0";
                if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
                    value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
                }
                if (!"0".equals(value)) {
                    CTNumVal numVal = numData.addNewPt();//序列值
                    numVal.setIdx(idx);
                    numVal.setV(value);
                }
                CTStrVal sVal = strData.addNewPt();//序列名称
                sVal.setIdx(idx);
                sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
                idx++;
            }
            numData.getPtCount().setVal(idx);
            strData.getPtCount().setVal(idx);


            //赋值横坐标数据区域
            String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
                    .formatAsString("Sheet1", false);
            cat.getStrRef().setF(axisDataRange);

            //数据区域
            String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
                    .formatAsString("Sheet1", false);
            val.getNumRef().setF(numDataRange);

            // 设置系列生成方向


        }
        return result;
    }


    /**
     * 刷新柱状图数据方法
     *
     * @param typeChart
     * @param serList
     * @param dataList
     * @param fldNameArr
     * @param position
     * @return
     */
    public static boolean refreshBarStrGraphContent(Object typeChart,
                                                    List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {
        boolean result = true;
        //更新数据区域
        for (int i = 0; i < serList.size(); i++) {
            //CTSerTx tx=null;
            CTAxDataSource cat = null;
            CTNumDataSource val = null;
            CTBarSer ser = ((CTBarChart) typeChart).getSerArray(i);
            //tx= ser.getTx();
            // Category Axis Data
            cat = ser.getCat();
            // 获取图表的值
            val = ser.getVal();
            // strData.set
            CTStrData strData = cat.getStrRef().getStrCache();
            CTNumData numData = val.getNumRef().getNumCache();
            strData.setPtArray((CTStrVal[]) null); // unset old axis text
            numData.setPtArray((CTNumVal[]) null); // unset old values

            // set model
            long idx = 0;
            for (int j = 0; j < dataList.size(); j++) {
                //判断获取的值是否为空
                String value = "0";

                if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
                    value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
                }
                if (!"0".equals(value)) {
                    CTNumVal numVal = numData.addNewPt();//序列值
                    numVal.setIdx(idx);
                    numVal.setV(value);
                }
                CTStrVal sVal = strData.addNewPt();//序列名称
                sVal.setIdx(idx);
                sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
                idx++;
            }
            numData.getPtCount().setVal(idx);
            strData.getPtCount().setVal(idx);


            //赋值横坐标数据区域
            String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
                    .formatAsString("Sheet1", true);
            cat.getStrRef().setF(axisDataRange);

            //数据区域
            String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
                    .formatAsString("Sheet1", true);
            val.getNumRef().setF(numDataRange);

        }
        return result;
    }


    /**
     * 刷新饼图数据方法
     *
     * @param typeChart
     * @param serList
     * @param dataList
     * @param fldNameArr
     * @param position
     * @return
     */
    public static boolean refreshPieStrGraphContent(Object typeChart,
                                                    List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {

        boolean result = true;
        //更新数据区域
        for (int i = 0; i < serList.size(); i++) {
            //CTSerTx tx=null;
            CTAxDataSource cat = null;
            CTNumDataSource val = null;
            CTPieSer ser = ((CTPieChart) typeChart).getSerArray(i);

            //tx= ser.getTx();
            // Category Axis Data
            cat = ser.getCat();
            // 获取图表的值
            val = ser.getVal();
            // strData.set
            CTStrData strData = cat.getStrRef().getStrCache();
            CTNumData numData = val.getNumRef().getNumCache();
            strData.setPtArray((CTStrVal[]) null); // unset old axis text
            numData.setPtArray((CTNumVal[]) null); // unset old values

            // set model
            long idx = 0;
            for (int j = 0; j < dataList.size(); j++) {
                //判断获取的值是否为空
                String value = "0";
                if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
                    value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
                }
                if (!"0".equals(value)) {
                    CTNumVal numVal = numData.addNewPt();//序列值
                    numVal.setIdx(idx);
                    numVal.setV(value);
                }
                CTStrVal sVal = strData.addNewPt();//序列名称
                sVal.setIdx(idx);
                sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
                idx++;
            }
            numData.getPtCount().setVal(idx);
            strData.getPtCount().setVal(idx);


            //赋值横坐标数据区域
            String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
                    .formatAsString("Sheet1", true);
            cat.getStrRef().setF(axisDataRange);

            //数据区域
            String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
                    .formatAsString("Sheet1", true);
            val.getNumRef().setF(numDataRange);
        }
        return result;
    }


    /**
     * 刷新内置excel数据
     *
     * @param chart
     * @param dataList
     * @param fldNameArr
     * @param titleArr
     * @return
     */
    public static boolean refreshExcel(XWPFChart chart,
                                       List<Map<String, String>> dataList, List<String> fldNameArr, List<String> titleArr) {
        boolean result = true;
        Workbook wb = new XSSFWorkbook();
        Sheet sheet = wb.createSheet("Sheet1");
        //根据数据创建excel第一行标题行
        for (int i = 0; i < titleArr.size(); i++) {
            if (sheet.getRow(0) == null) {
                sheet.createRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
            } else {
                sheet.getRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
            }
        }

        //遍历数据行
        for (int i = 0; i < dataList.size(); i++) {
            Map<String, String> baseFormMap = dataList.get(i);//数据行
            //fldNameArr字段属性
            for (int j = 0; j < fldNameArr.size(); j++) {
                if (sheet.getRow(i + 1) == null) {
                    if (j == 0) {
                        try {
                            sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)) == null ? "" : baseFormMap.get(fldNameArr.get(j)));
                        } catch (Exception e) {
                            if (baseFormMap.get(fldNameArr.get(j)) == null) {
                                sheet.createRow(i + 1).createCell(j).setCellValue("");
                            } else {
                                sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)));
                            }
                        }
                    }
                } else {
                    BigDecimal b = new BigDecimal(baseFormMap.get(fldNameArr.get(j)));
                    double value = 0d;
                    if (b != null) {
                        value = b.doubleValue();
                    }
                    if (value == 0) {
                        sheet.getRow(i + 1).createCell(j);
                    } else {
                        sheet.getRow(i + 1).createCell(j).setCellValue(b.doubleValue());
                    }
                }
            }

        }
        // 更新嵌入的workbook

        List<POIXMLDocumentPart> pxdList = chart.getRelations();
        if (pxdList != null && pxdList.size() > 0) {
            for (int i = 0; i < pxdList.size(); i++) {
                if (pxdList.get(i).toString().contains("sheet")) {//判断为sheet再去进行更新表格数据
                    POIXMLDocumentPart xlsPart = pxdList.get(i);
                    OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();

                    try {
                        wb.write(xlsOut);
                        xlsOut.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        result = false;
                    } finally {
                        if (wb != null) {
                            try {
                                wb.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                                result = false;
                            }
                        }
                    }
                    break;
                }
            }
        }
        return result;
    }


    /**
     * 设置表格样式
     *
     * @param cell
     * @param fontName
     * @param fontSize
     * @param fontBlod
     * @param alignment
     * @param vertical
     * @param fontColor
     * @param bgColor
     * @param cellWidth
     * @param content
     */
    public static void setWordCellSelfStyle(XWPFTableCell cell, String fontName, String fontSize, int fontBlod,
                                            String alignment, String vertical, String fontColor,
                                            String bgColor, String cellWidth, String content) {

        //poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理
        BigInteger bFontSize = new BigInteger("24");
        if (fontSize != null && !fontSize.equals("")) {
            //poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理
            BigDecimal fontSizeBD = new BigDecimal(fontSize);
            fontSizeBD = bd2.multiply(fontSizeBD);
            fontSizeBD = fontSizeBD.setScale(0, BigDecimal.ROUND_HALF_UP);//这里取整
            bFontSize = new BigInteger(fontSizeBD.toString());// 字体大小
        }

        // 设置单元格宽度
        cell.setWidth(cellWidth);

        //=====获取单元格
        CTTc tc = cell.getCTTc();
        //====tcPr开始====》》》》
        CTTcPr tcPr = tc.getTcPr();//获取单元格里的<w:tcPr>
        if (tcPr == null) {//没有<w:tcPr>,创建
            tcPr = tc.addNewTcPr();
        }

        //  --vjc开始-->>
        CTVerticalJc vjc = tcPr.getVAlign();//获取<w:tcPr>  的<w:vAlign w:val="center"/>
        if (vjc == null) {//没有<w:w:vAlign/>,创建
            vjc = tcPr.addNewVAlign();
        }
        //设置单元格对齐方式
        vjc.setVal(vertical.equals("top") ? STVerticalJc.TOP : vertical.equals("bottom") ? STVerticalJc.BOTTOM : STVerticalJc.CENTER); //垂直对齐

        CTShd shd = tcPr.getShd();//获取<w:tcPr>里的<w:shd w:val="clear" w:color="auto" w:fill="C00000"/>
        if (shd == null) {//没有<w:shd>,创建
            shd = tcPr.addNewShd();
        }
        // 设置背景颜色
        shd.setFill(bgColor.substring(1));
        //《《《《====tcPr结束====

        //====p开始====》》》》
        CTP p = tc.getPList().get(0);//获取单元格里的<w:p w:rsidR="00C36068" w:rsidRPr="00B705A0" w:rsidRDefault="00C36068" w:rsidP="00C36068">

        //---ppr开始--->>>
        CTPPr ppr = p.getPPr();//获取<w:p>里的<w:pPr>
        if (ppr == null) {//没有<w:pPr>,创建
            ppr = p.addNewPPr();
        }
        //  --jc开始-->>
        CTJc jc = ppr.getJc();//获取<w:pPr>里的<w:jc w:val="left"/>
        if (jc == null) {//没有<w:jc/>,创建
            jc = ppr.addNewJc();
        }
        //设置单元格对齐方式
        jc.setVal(alignment.equals("left") ? STJc.LEFT : alignment.equals("right") ? STJc.RIGHT : STJc.CENTER); //水平对齐
        //  <<--jc结束--
        //  --pRpr开始-->>
        CTParaRPr pRpr = ppr.getRPr(); //获取<w:pPr>里的<w:rPr>
        if (pRpr == null) {//没有<w:rPr>,创建
            pRpr = ppr.addNewRPr();
        }
        CTFonts pfont = pRpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体"/>
        if (pfont == null) {//没有<w:rPr>,创建
            pfont = pRpr.addNewRFonts();
        }
        //设置字体
        pfont.setAscii(fontName);
        pfont.setEastAsia(fontName);
        pfont.setHAnsi(fontName);

        CTOnOff pb = pRpr.getB();//获取<w:rPr>里的<w:b/>
        if (pb == null) {//没有<w:b/>,创建
            pb = pRpr.addNewB();
        }
        //设置字体是否加粗
        pb.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);

        CTHpsMeasure psz = pRpr.getSz();//获取<w:rPr>里的<w:sz w:val="32"/>
        if (psz == null) {//没有<w:sz w:val="32"/>,创建
            psz = pRpr.addNewSz();
        }
        // 设置单元格字体大小
        psz.setVal(bFontSize);
        CTHpsMeasure pszCs = pRpr.getSzCs();//获取<w:rPr>里的<w:szCs w:val="32"/>
        if (pszCs == null) {//没有<w:szCs w:val="32"/>,创建
            pszCs = pRpr.addNewSzCs();
        }
        // 设置单元格字体大小
        pszCs.setVal(bFontSize);
        //  <<--pRpr结束--
        //<<<---ppr结束---

        //---r开始--->>>
        List<CTR> rlist = p.getRList(); //获取<w:p>里的<w:r w:rsidRPr="00B705A0">
        CTR r = null;
        if (rlist != null && rlist.size() > 0) {//获取第一个<w:r>
            r = rlist.get(0);
        } else {//没有<w:r>,创建
            r = p.addNewR();
        }
        //--rpr开始-->>
        CTRPr rpr = r.getRPr();//获取<w:r w:rsidRPr="00B705A0">里的<w:rPr>
        if (rpr == null) {//没有<w:rPr>,创建
            rpr = r.addNewRPr();
        }
        //->-
        CTFonts font = rpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体" w:hint="eastAsia"/>
        if (font == null) {//没有<w:rFonts>,创建
            font = rpr.addNewRFonts();
        }
        //设置字体
        font.setAscii(fontName);
        font.setEastAsia(fontName);
        font.setHAnsi(fontName);

        CTOnOff b = rpr.getB();//获取<w:rPr>里的<w:b/>
        if (b == null) {//没有<w:b/>,创建
            b = rpr.addNewB();
        }
        //设置字体是否加粗
        b.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);
        CTColor color = rpr.getColor();//获取<w:rPr>里的<w:color w:val="FFFFFF" w:themeColor="background1"/>
        if (color == null) {//没有<w:color>,创建
            color = rpr.addNewColor();
        }
        // 设置字体颜色
        if (content.contains("↓")) {
            color.setVal("43CD80");
        } else if (content.contains("↑")) {
            color.setVal("943634");
        } else {
            color.setVal(fontColor.substring(1));
        }
        CTHpsMeasure sz = rpr.getSz();
        if (sz == null) {
            sz = rpr.addNewSz();
        }
        sz.setVal(bFontSize);
        CTHpsMeasure szCs = rpr.getSzCs();
        if (szCs == null) {
            szCs = rpr.addNewSz();
        }
        szCs.setVal(bFontSize);
        //-<-
        //<<--rpr结束--
        List<CTText> tlist = r.getTList();
        CTText t = null;
        if (tlist != null && tlist.size() > 0) {//获取第一个<w:r>
            t = tlist.get(0);
        } else {//没有<w:r>,创建
            t = r.addNewT();
        }
        t.setStringValue(content);
        //<<<---r结束---
    }

}
import cn.hutool.core.util.IdUtil;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhou
 */
public class PoiWordUtil {

    /**
     * @param doc        docx解析对象
     * @param tableIndex 第几个表格
     */
    public static XWPFTable getTable(XWPFDocument doc, int tableIndex) {
        return doc.getTables().get(tableIndex);
    }

    /**
     * 为表格插入行数,此处不处理表头,所以从第二行开始
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     * @param index     在几行后开始插入数据 1为第一行
     */
    public static void insertTable(XWPFTable table, List<String[]> tableList, int index) {
        //创建与数据一致的行数
        for (int i = 0; i < tableList.size(); i++) {
            table.createRow();
        }
        int length = table.getRows().size() - index;
        for (int i = 0; i < length; i++) {
            XWPFTableRow newRow = table.getRow(i + index);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                cell.setText(tableList.get(i)[j]);
            }
        }
    }


    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    public static void replaceParams(XWPFDocument doc, Map<String, String> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph paragraph;
        while (iterator.hasNext()) {
            paragraph = iterator.next();
            replaceParam(paragraph, params);
        }
    }


    /**
     * 替换所有表格里面的变量
     *
     * @param doc 要替换的文档
     */
    public static void replaceAllTableParams(XWPFDocument doc, Map<String, String> testMap) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        while (iterator.hasNext()) {
            table = iterator.next();
            //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
            if (matcher(table.getText()).find()) {
                rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        paras = cell.getParagraphs();
                        for (XWPFParagraph para : paras) {
                            replaceParam(para, testMap);
                        }
                    }
                }
            }
        }
    }

    /**
     * 替换表格里面的变量
     */
    public static void replaceTableParams(XWPFTable table, Map<String, String> testMap) {
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
        if (matcher(table.getText()).find()) {
            rows = table.getRows();
            for (XWPFTableRow row : rows) {
                cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    paras = cell.getParagraphs();
                    for (XWPFParagraph para : paras) {
                        replaceParam(para, testMap);
                    }
                }
            }
        }
    }

    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    public static Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        return pattern.matcher(str);
    }

    /**
     * 替换段落里面的变量
     *
     * @param paragraph 要替换的段落
     * @param params    参数
     */
    public static void replaceParam(XWPFParagraph paragraph, Map<String, String> params) {
        List<XWPFRun> runs;
        Matcher matcher;
        StringBuilder runText = new StringBuilder();
        if (matcher(paragraph.getParagraphText()).find()) {
            runs = paragraph.getRuns();
            int j = runs.size();
            for (int i = 0; i < j; i++) {
                runText.append(runs.get(0).toString());
                //保留最后一个段落,在这段落中替换值,保留段落样式
                if (!((j - 1) == i)) {
                    paragraph.removeRun(0);
                }
            }
            matcher = matcher(runText.toString());
            if (matcher.find()) {
                while ((matcher = matcher(runText.toString())).find()) {
                    runText = new StringBuilder(matcher.replaceFirst(String.valueOf(params.get(matcher.group(1)))));
                }
                runs.get(0).setText(runText.toString(), 0);
            }
        }

    }

    /**
     * word单元格列合并 都以0开始计算
     *
     * @param table     表格
     * @param row       合并列所在行
     * @param startCell 开始列
     * @param endCell   结束列
     * @date 2020年4月8日 下午4:43:54
     */
    public static void mergeCellsHorizontal(XWPFTable table, int row, int startCell, int endCell) {
        for (int i = startCell; i <= endCell; i++) {
            XWPFTableCell cell = table.getRow(row).getCell(i);
            if (i == startCell) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * word单元格行合并
     *
     * @param table    表格
     * @param col      合并行所在列
     * @param startRow 开始行
     * @param endRow   结束行
     * @date 2020年4月8日 下午4:46:18
     */
    public static void mergeCellsVertically(XWPFTable table, int col, int startRow, int endRow) {
        for (int i = startRow; i <= endRow; i++) {
            XWPFTableCell cell = table.getRow(i).getCell(col);
            if (i == startRow) {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }


    /**
     * 替换图片
     * key 替换字段名称,与word中相同
     * value 图片地址
     */
    public static void doParagraphs(XWPFDocument doc, Map<String, String> imgMap) {
        List<XWPFParagraph> paragraphList = doc.getParagraphs();
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    String text = run.getText(0);
                    if (text != null) {
                        String imgkey = text.replaceAll("\\{\\{@", "").replaceAll("}}", "");
                        String imgPath = imgMap.get(imgkey);
                        if (imgPath != null) {
                            if (imgPath.startsWith("http")) {
                                imgPath = saveToFile(imgPath);
                            }
                            saveLocalImg(run, imgPath);
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取本地保存图片文件流
     *
     * @param imgPath
     * @return FileInputStream
     * @throws Exception
     */
    private static void saveLocalImg(XWPFRun run, String imgPath) {
        try (FileInputStream pictureData =  new FileInputStream(imgPath)) {
            run.setText("", 0);
            run.addPicture(pictureData,
                    Document.PICTURE_TYPE_PNG, "img.png", Units.toEMU(200), Units.toEMU(200));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取网络图片流
     *
     * @return
     */
    public static String saveToFile(String destUrl) {
        HttpURLConnection httpUrl = null;
        byte[] buf = new byte[1024];
        int size = 0;
        StringBuilder append = new StringBuilder()
                .append("D:/fjFile/")
                .append(IdUtil.simpleUUID())
                .append(".png");
        String fileUrl = append.toString();
        try {
            URL url = new URL(destUrl);
            httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.setConnectTimeout(5*1000);
            httpUrl.setReadTimeout(5*1000);
            httpUrl.connect();
            try (BufferedInputStream  bis = new BufferedInputStream(httpUrl.getInputStream());
                 FileOutputStream fos = new FileOutputStream(fileUrl)){
                while ((size = bis.read(buf)) != -1) {
                    fos.write(buf, 0, size);
                }
                fos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }  finally {
            try {
                if(httpUrl!=null){
                    httpUrl.disconnect();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return fileUrl;
    }


}

import cn.hutool.core.collection.CollUtil;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 操作word
 * @author zhou
 */
@RestController
public class PoiController {
    // http://localhost:8080/poi/barCharts
    @GetMapping("/poi/barCharts")
    public void barCharts(HttpServletResponse response) {
        try (InputStream is = this.getClass().getClassLoader()
                .getResourceAsStream("word/word.docx");
             ServletOutputStream out = response.getOutputStream()) {
            if (is == null) {
                throw new RuntimeException("未找到模板信息");
            }
            XWPFDocument doc = new XWPFDocument(is);
            this.doBarCharts(PoiChartsTools.getPOIXMLDocumentPart(doc,
                    "/word/charts/chart1.xml"));
            this.doLineChart(PoiChartsTools.getPOIXMLDocumentPart(doc,
                    "/word/charts/chart2.xml"));
            this.doPieCharts(PoiChartsTools.getPOIXMLDocumentPart(doc,
                    "/word/charts/chart3.xml"));
            XWPFTable table1 = PoiWordUtil.getTable(doc, 0);
            XWPFTable table2 = PoiWordUtil.getTable(doc, 1);
            this.insertTable(table1);
            this.replaceTable(table2);

            Map<String, String> map = new HashMap<>();
            map.put("var", "D:/fjFile/20210529111249.jpg");
            PoiWordUtil. replaceParams(doc,map);

            // 图片数据
            Map<String, String> imgMap = new HashMap<>();
            imgMap.put("img", "D:/fjFile/20210529111249.jpg");
            PoiWordUtil.doParagraphs(doc,imgMap);

            response.setContentType("application/octet-stream");
            String filename = "test.docx";
            response.setHeader("Content-disposition", "attachment;filename="
                    + URLEncoder.encode(filename, "UTF-8"));
            doc.write(out);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理图表数据-柱状图
     */
    private void doBarCharts(POIXMLDocumentPart chart1) {
        // 行 标题
        List<String> titleArr = CollUtil.newArrayList("类别", "系列1", "系列2", "系列3");
        // 字段名
        List<String> fldNameArr = CollUtil.newArrayList("item1", "item2", "item3", "item4");
        // 数据集合
        List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();
        for (int i = 1; i < 5; i++) {
            Map<String, String> base1 = new HashMap<>(4);
            base1.put("item1", "柱状图" + i);
            base1.put("item2", "100");
            base1.put("item3", "20");
            base1.put("item4", "3");
            listItemsByType.add(base1);
        }
        PoiChartsTools.replaceBarCharts(chart1, titleArr, fldNameArr, listItemsByType);
    }


    /**
     * 处理图表数据-折线图
     */
    private void doLineChart(POIXMLDocumentPart chart) {
        // 行 标题
        List<String> titleArr = CollUtil.newArrayList("类别", "系列1", "系列2", "系列3");
        // 字段名
        List<String> fldNameArr = CollUtil.newArrayList("item1", "item2", "item3", "item4");
        // 数据集合
        List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();
        for (int i = 1; i < 5; i++) {
            Map<String, String> base1 = new HashMap<>(4);
            base1.put("item1", "折线图" + i);
            base1.put("item2", "100");
            base1.put("item3", "20");
            base1.put("item4", "3");
            listItemsByType.add(base1);
        }
        PoiChartsTools.replaceLineCharts(chart, titleArr, fldNameArr, listItemsByType);
    }

    /**
     * 处理图表数据-饼图
     */
    private void doPieCharts(POIXMLDocumentPart chart) {
        // 行 标题
        List<String> titleArr = CollUtil.newArrayList("类别", "系列1", "系列2", "系列3");
        // 字段名
        List<String> fldNameArr = CollUtil.newArrayList("item1", "item2", "item3", "item4");
        // 数据集合
        List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();
        for (int i = 1; i < 5; i++) {
            Map<String, String> base1 = new HashMap<>(4);
            base1.put("item1", "饼图" + i);
            base1.put("item2", "100");
            base1.put("item3", "20");
            base1.put("item4", "3");
            listItemsByType.add(base1);
        }
        PoiChartsTools.replacePieCharts(chart, titleArr, fldNameArr, listItemsByType);
    }

    /**
     * 处理表格-新增行
     */
    private void insertTable(XWPFTable table) {
        List<List<String[]>> tableList = new ArrayList<>();
        List<String[]> table1 = new ArrayList<>();
        table1.add(new String[]{"1", "张三", "男", "22", "186xxxxxxxx"});
        table1.add(new String[]{"2", "李四", "女", "23", "187xxxxxxxx"});
        table1.add(new String[]{"3", "王五", "男", "24", "188xxxxxxxx"});
        table1.add(new String[]{"4", "赵六", "女", "25", "189xxxxxxxx"});
        tableList.add(table1);
        PoiWordUtil.insertTable(table, table1, 2);
        PoiWordUtil.mergeCellsHorizontal(table, 0, 0, 1);
        PoiWordUtil.mergeCellsHorizontal(table, 0, 3, 4);
    }

    /**
     * 处理表格-替换表格中信息
     */
    private void replaceTable(XWPFTable table) {
        Map<String, String> params = new HashMap<>();
        params.put("name", "XXXWord");
        params.put("start", "2021-04-21");
        params.put("end", "2021-04-28");
        PoiWordUtil.replaceTableParams(table, params);
    }


}

结果 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNjA0ODkw,size_16,color_FFFFFF,t_70

注意:推荐使用 poi-tl 框架操作 

springboot整合poi-tl_qq_250056868的博客-CSDN博客

java操作文档工具框架_qq_250056868的博客-CSDN博客

;