Bootstrap

使用poi根据模版生成word文档,支持插入图片,复制表格,插入、循环插入数据,继承模板大部分格式及样式(优化版)

一、制作word模版,${xxxx}是一会要替换的内容,最下面的表格是要插入数据,根据是否以$开头来判断是需要替换还是插入数据,

  • 注意如果是需要循环插入数据,制作的表格模版需要一行全部输入井号#,格式样式可以对#设置
  • 表格中${header}和${hearder2}是放入需要替换的图片
  • 替换数据的格式样式同$的一样
  • 如果需要根据数据再确定需要在哪替换图片,可以使用带ImgAgain的方法,原理是数据先替换为*{}占位符,再替换一遍
  • 一些设置参数,看getWord方法参数
  • 支持:段落插表格(有两个插入表格的方法)
  • 支持公式:看六,得具体分析
  • 不支持特殊格式:如上下标、双行合一等
  • 复制表格时及循环插入不支持特殊样式:如高亮、艺术字体、快速样式(复制表格,循环时无法复制高亮样式)等
  • 复制表格时,若字体原来为默认五号,则复制是变为10号字体,应为10.5,但只支持传整数 :(
  • 表格中特殊符号:#、$、{、}、,使用时需注意,在循环插入数据的单元格内 [ ] 中括号也是特殊符号
  • 循环插数据的表格列数支持多或少;支持调换列的顺序
  • 循环插数据的表格行中每个单元格都要有#
  • 在循环单元格内允许有多个段落,单插入数据的段落需是单独段落,且#号开头
  • 循环插数据的以#开头,后面跟 [ ] ,里面写数据顺序的序号(注意第一列是以零0开头),若按默认循序则不用加 [ ] ,若在数据长度内的单元格又不想填数据则中括号没填-1及 [-1]
  • 循环插数据的数据格式样式同#,所以模板可以自定义#格式样式

循环数据行举例:

结果:

下面是测试模板

二、添加poi所需要的jar包文件,我用的maven对jar包进行管理

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>3.15</version>
</dependency>
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-scratchpad</artifactId>
	<version>3.15</version>
</dependency>

三、由于poi自身bug,会出现图片无法显示问题,这里需要自定义一个类继承XWPFDocument类,接下来使用的都是我们自己创建的这个类来操作word对象,这个

  类对XWPFDocument进行了继承,所以不用担心会有什么问题

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import java.io.IOException;
import java.io.InputStream;

/******************************************* 
 *
 * @Package com.cccuu.project.myUtils
 * @Author duan
 * @Date 2018/3/29 17:55
 * @Version V1.0
 *******************************************/
public class CustomXWPFDocument extends XWPFDocument {

        public CustomXWPFDocument(InputStream in) throws IOException {
                super(in);
        }

        public CustomXWPFDocument() {
                super();
        }

        public CustomXWPFDocument(OPCPackage pkg) throws IOException {
                super(pkg);
        }

        /**
         * @param id
         * @param width 宽
         * @param height 高
         * @param paragraph  段落
         */
        public void createPicture(int id, int width, int height,XWPFParagraph paragraph) {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        String blipId = getAllPictures().get(id).getPackageRelationship().getId();
        CTInline inline = paragraph.createRun().getCTR().addNewDrawing().addNewInline();
        String picXml = ""
        +"<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
        +"   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
        +"      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
        +"         <pic:nvPicPr>" + "            <pic:cNvPr id=\""
        + id
        +"\" name=\"Generated\"/>"
        +"            <pic:cNvPicPr/>"
        +"         </pic:nvPicPr>"
        +"         <pic:blipFill>"
        +"            <a:blip r:embed=\""
        + blipId
        +"\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
        +"            <a:stretch>"
        +"               <a:fillRect/>"
        +"            </a:stretch>"
        +"         </pic:blipFill>"
        +"         <pic:spPr>"
        +"            <a:xfrm>"
        +"               <a:off x=\"0\" y=\"0\"/>"
        +"               <a:ext cx=\""
        + width
        +"\" cy=\""
        + height
        +"\"/>"
        +"            </a:xfrm>"
        +"            <a:prstGeom prst=\"rect\">"
        +"               <a:avLst/>"
        +"            </a:prstGeom>"
        +"         </pic:spPr>"
        +"      </pic:pic>"
        +"   </a:graphicData>" + "</a:graphic>";

        inline.addNewGraphic().addNewGraphicData();
        XmlToken xmlToken = null;
        try{
        xmlToken = XmlToken.Factory.parse(picXml);
        }catch(XmlException xe) {
        xe.printStackTrace();
        }
        inline.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("图片"+ id);
        docPr.setDescr("测试");
        }
}

四、接下来就是导出word的工具类了

import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <b> 通过word模板生成新的word工具类
 * </b><br><br><i>Description</i> :
 * <br><br>Date: 2019/12/9 ${time}    <br>Author : dxl
 */
public class WordGrtUtil {
    public static void main(String[] args) throws Exception {
//        wordGrtUtil.copyTbaleByLoop("C:\\Users\\00\\Desktop\\baogao\\testmodel.docx",0,4,"C:\\Users\\00\\Desktop\\baogao\\testmodel-copy.docx");
//        WordGrtUtil wordGrtUtil=new WordGrtUtil();
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("${position}", "*{aaa}");
        params.put("${name}", "222段然涛222");
        params.put("${sex}", "男");
        params.put("${national}", "汉族");
        params.put("${birthday}", "生日");
        params.put("${address}", "许昌");
        params.put("${height}", "165cm");
        params.put("${biYeDate}", "1994-02-03");
        params.put("${landscape}", "团员");
        params.put("${zhuanYe}", "社会工作");
        params.put("${xueLi}", "本科");
        params.put("${school}", "江西科技师范大学");
        params.put("${phone}", "177");
        params.put("${eMail}", "157");

        try{
            Map<String,Object> header = new HashMap<String, Object>();
            header.put("width", 50);
            header.put("height", 50);
            header.put("type", "jpg");
            header.put("content", inputStream2ByteArray(new FileInputStream("C:\\Users\\00\\Desktop\\baogao\\tupian\\垂直度.jpg"), true));
            params.put("${tihuan}",header);
            params.put("*{aaa}",header);
//            Map<String,Object> header2 = new HashMap<String, Object>();
//            header2.put("width", 100);
//            header2.put("height", 150);
//            header2.put("type", "jpg");
//            header2.put("content", WordUtils.inputStream2ByteArray(new FileInputStream("C:/Users/Administrator/Desktop/jar包/22.jpg"), true));
//            params.put("${header2}",header2);
            List<String[]> testList = new ArrayList<String[]>();
            testList.add(new String[]{"1","1AA","1BB","1CC"});
            testList.add(new String[]{"2","2AA","2BB","2CC"});
            testList.add(new String[]{"3","3AA","3BB","3CC"});
            testList.add(new String[]{"4","4AA","4BB","4CC"});
            String modelPath="C:\\Users\\00\\Desktop\\baogao\\testmodel3.docx";  //模板文件位置
            String stroePath="C:\\Users\\00\\Desktop\\baogao\\aaaa.docx";  //模板文件位置
            String fileName= new String("测试文档.docx".getBytes("UTF-8"),"iso-8859-1");    //生成word文件的文件名
            List<List<Integer>> mergeRowLists = new ArrayList<>();
            WordGrtUtil.getWordStore(modelPath,params,testList,stroePath,false,0,2,false,null);
//            wordGrtUtil.getWord(path,params,testList,fileName);


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

    }
    /**
     * 根据模板生成word 并存储
     * @param srcPath     模板的路径
     * @param params   需要替换的参数
     * @param tableList   需要循环插入的参数
     * @param storePath 生成的word文件存储路径,包含文件名全称
     * @param isCopyTable 是否需要循环复制表格,
     * @param copyTableIndex 复制第几个表格,若copyTable为false,后两个参数随便填
     * @param copyTableNum  复制几个表格
     * @param isMergeRow  是否自动合并行
     * @param mergeRowLists    List<Integer>应包含三个,第一个为要合并的列,第二个合并的起始行,第三个合并的结束行
     */
    public static void getWordStore(String srcPath, Map<String, Object> params, List<String[]> tableList, String storePath,boolean isCopyTable,int copyTableIndex,int copyTableNum,boolean isMergeRow,List<List<Integer>> mergeRowLists) throws Exception {
        File file = new File(srcPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(isCopyTable){
            String temPath = file.getParentFile()+"/tem"+System.currentTimeMillis()+file.getName();
            copyTbaleByLoop(srcPath,copyTableIndex,copyTableNum,temPath);
            File fileTem = new File(temPath);
            InputStream is = new FileInputStream(fileTem);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInPara(doc, params);    //替换文本里面的变量
            OutputStream os =  new FileOutputStream(storePath);
            doc.write(os);
            close(os);
            close(is);
            File fileDel = new File(temPath);
            if(fileDel.exists()){
                fileDel.delete();
            }
        }else {
            InputStream is = new FileInputStream(file);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInPara(doc, params);    //替换文本里面的变量
            OutputStream os =  new FileOutputStream(storePath);
            doc.write(os);
            close(os);
            close(is);
        }
    }
    /**
     * 根据模板生成word 不存储直接下载
     * @param srcPath     模板的路径
     * @param params   需要替换的参数
     * @param tableList   需要插入的参数
     * @param fileName 生成word文件的文件名
     * @param response
     * @param isCopyTable 是否需要循环复制表格,
     * @param copyTableIndex 复制第几个表格,若copyTable为false,后两个参数随便填
     * @param copyTableNum  复制几个表格
     * @param isMergeRow  是否自动合并行
     * @param mergeRowLists    List<Integer>应包含三个,第一个为要合并的列,第二个合并的起始行,第三个合并的结束行
     */
    public static void getWordDown(String srcPath, Map<String, Object> params, List<String[]> tableList, String fileName, HttpServletResponse response,boolean isCopyTable,int copyTableIndex,int copyTableNum,boolean isMergeRow,List<List<Integer>> mergeRowLists) throws Exception {
        fileName= new String(fileName.getBytes("UTF-8"),"iso-8859-1");
        File file = new File(srcPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(isCopyTable){
            String temPath = file.getParentFile()+"/tem"+System.currentTimeMillis()+file.getName();
            copyTbaleByLoop(srcPath,copyTableIndex,copyTableNum,temPath);
            File fileTem = new File(temPath);
            InputStream is = new FileInputStream(fileTem);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInPara(doc, params);    //替换文本里面的变量
            OutputStream os = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(os);
            close(os);
            close(is);
            File fileDel = new File(temPath);
            if(fileDel.exists()){
                fileDel.delete();
            }
        }else {
            InputStream is = new FileInputStream(file);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInPara(doc, params);    //替换文本里面的变量
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            OutputStream os = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(os);
            close(os);
            close(is);
        }
    }
    /**
     * 根据模板生成word 存储并下载
     * @param srcPath     模板的路径
     * @param params   需要替换的参数
     * @param tableList   需要插入的参数
     * @param fileName 生成word文件的文件名
     * @param response
     * @param isCopyTable 是否需要循环复制表格,
     * @param copyTableIndex 复制第几个表格,若copyTable为false,后两个参数随便填
     * @param copyTableNum  复制几个表格
     * @param isMergeRow  是否自动合并行
     * @param mergeRowLists    List<Integer>应包含三个,第一个为要合并的列,第二个合并的起始行,第三个合并的结束行
     */
    public static void getWordStoreAndDown(String srcPath, Map<String, Object> params, List<String[]> tableList, String storePath, String fileName, HttpServletResponse response,boolean isCopyTable,int copyTableIndex,int copyTableNum,boolean isMergeRow,List<List<Integer>> mergeRowLists) throws Exception {
        fileName= new String(fileName.getBytes("UTF-8"),"iso-8859-1");
        File file = new File(srcPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(isCopyTable){
            String temPath = file.getParentFile()+"/tem"+System.currentTimeMillis()+file.getName();
            copyTbaleByLoop(srcPath,copyTableIndex,copyTableNum,temPath);
            File fileTem = new File(temPath);
            InputStream is = new FileInputStream(fileTem);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInPara(doc, params);    //替换文本里面的变量
            OutputStream oss =  new FileOutputStream(temPath);
            OutputStream osd = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(oss);
            doc.write(osd);
            close(oss);
            close(osd);
            close(is);
            File fileDel = new File(temPath);
            if(fileDel.exists()){
                fileDel.delete();
            }
        }else {
            InputStream is = new FileInputStream(file);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInPara(doc, params);    //替换文本里面的变量
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            OutputStream oss =  new FileOutputStream(storePath);
            OutputStream osd = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(oss);
            doc.write(osd);
            close(oss);
            close(osd);
            close(is);
        }
    }

    /**
     * 根据模板生成word 并存储
     * 因为有的地方替换图片不确定,先替换成其他占位符,在替换掉
     * @param srcPath     模板的路径
     * @param params   需要替换的参数
     * @param tableList   需要循环插入的参数
     * @param storePath 生成的word文件存储路径,包含文件名全称
     * @param isCopyTable 是否需要循环复制表格,
     * @param copyTableIndex 复制第几个表格,若copyTable为false,后两个参数随便填
     * @param copyTableNum  复制几个表格
     *  @param isMergeRow  是否自动合并行
     * @param mergeRowLists    List<Integer>应包含三个,第一个为要合并的列,第二个合并的起始行,第三个合并的结束行
     */
    public static void getWordStoreImgAgain(String srcPath, Map<String, Object> params, List<String[]> tableList, String storePath,boolean isCopyTable,int copyTableIndex,int copyTableNum,boolean isMergeRow,List<List<Integer>> mergeRowLists) throws Exception {
        File file = new File(srcPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(isCopyTable){
            String temPath = file.getParentFile()+"/tem"+System.currentTimeMillis()+file.getName();
            copyTbaleByLoop(srcPath,copyTableIndex,copyTableNum,temPath);
            File fileTem = new File(temPath);
            InputStream is = new FileInputStream(fileTem);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInParaImgAgain(doc, params);    //替换文本里面的变量
            OutputStream os =  new FileOutputStream(storePath);
            doc.write(os);
            close(os);
            close(is);
            File fileDel = new File(temPath);
            if(fileDel.exists()){
                fileDel.delete();
            }
        }else {
            InputStream is = new FileInputStream(file);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInParaImgAgain(doc, params);    //替换文本里面的变量
            OutputStream os =  new FileOutputStream(storePath);
            doc.write(os);
            close(os);
            close(is);
        }
    }
    /**
     * 根据模板生成word 不存储直接下载
     * 因为有的地方替换图片不确定,先替换成其他占位符,在替换掉
     * @param srcPath     模板的路径
     * @param params   需要替换的参数
     * @param tableList   需要插入的参数
     * @param fileName 生成word文件的文件名
     * @param response
     * @param isCopyTable 是否需要循环复制表格,
     * @param copyTableIndex 复制第几个表格,若copyTable为false,后两个参数随便填
     * @param copyTableNum  复制几个表格
     *  @param isMergeRow  是否自动合并行
     * @param mergeRowLists    List<Integer>应包含三个,第一个为要合并的列,第二个合并的起始行,第三个合并的结束行
     */
    public static void getWordDownImgAgain(String srcPath, Map<String, Object> params, List<String[]> tableList, String fileName, HttpServletResponse response,boolean isCopyTable,int copyTableIndex,int copyTableNum,boolean isMergeRow,List<List<Integer>> mergeRowLists) throws Exception {
        fileName= new String(fileName.getBytes("UTF-8"),"iso-8859-1");
        File file = new File(srcPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(isCopyTable){
            String temPath = file.getParentFile()+"/tem"+System.currentTimeMillis()+file.getName();
            copyTbaleByLoop(srcPath,copyTableIndex,copyTableNum,temPath);
            File fileTem = new File(temPath);
            InputStream is = new FileInputStream(fileTem);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInParaImgAgain(doc, params);    //替换文本里面的变量
            OutputStream os = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(os);
            close(os);
            close(is);
            File fileDel = new File(temPath);
            if(fileDel.exists()){
                fileDel.delete();
            }
        }else {
            InputStream is = new FileInputStream(file);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInParaImgAgain(doc, params);    //替换文本里面的变量
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            OutputStream os = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(os);
            close(os);
            close(is);
        }
    }
    /**
     * 根据模板生成word 存储并下载
     * 因为有的地方替换图片不确定,先替换成其他占位符,在替换掉
     * @param srcPath     模板的路径
     * @param params   需要替换的参数
     * @param tableList   需要插入的参数
     * @param fileName 生成word文件的文件名
     * @param response
     * @param isCopyTable 是否需要循环复制表格,
     * @param copyTableIndex 复制第几个表格,若copyTable为false,后两个参数随便填
     * @param copyTableNum  复制几个表格
     * @param isMergeRow  是否自动合并行
     * @param mergeRowLists    List<Integer>应包含三个,第一个为要合并的列,第二个合并的起始行,第三个合并的结束行
     */
    public static void getWordStoreAndDownImgAgain(String srcPath, Map<String, Object> params, List<String[]> tableList, String storePath, String fileName, HttpServletResponse response,boolean isCopyTable,int copyTableIndex,int copyTableNum,boolean isMergeRow,List<List<Integer>> mergeRowLists) throws Exception {
        fileName= new String(fileName.getBytes("UTF-8"),"iso-8859-1");
        File file = new File(srcPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        if(isCopyTable){
            String temPath = file.getParentFile()+"/tem"+System.currentTimeMillis()+file.getName();
            copyTbaleByLoop(srcPath,copyTableIndex,copyTableNum,temPath);
            File fileTem = new File(temPath);
            InputStream is = new FileInputStream(fileTem);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            replaceInParaImgAgain(doc, params);    //替换文本里面的变量
            OutputStream oss =  new FileOutputStream(temPath);
            OutputStream osd = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(oss);
            doc.write(osd);
            close(oss);
            close(osd);
            close(is);
            File fileDel = new File(temPath);
            if(fileDel.exists()){
                fileDel.delete();
            }
        }else {
            InputStream is = new FileInputStream(file);
            CustomXWPFDocument doc = new CustomXWPFDocument(is);
            replaceInParaImgAgain(doc, params);    //替换文本里面的变量
            replaceInTable(doc, params,tableList,isMergeRow,mergeRowLists); //替换表格里面的变量
            OutputStream oss =  new FileOutputStream(storePath);
            OutputStream osd = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);
            doc.write(oss);
            doc.write(osd);
            close(oss);
            close(osd);
            close(is);
        }
    }

    /**
    * <b> 循环复制Word中的表格
    * </b><br><br><i>Description</i> :
    * @param srcPath 模板路径, srcTableIndex 模板中复制目标表格的下标, copyNum 复制个数, storePath 存储路径(包含文件名全称)
    * @return void
    * <br><br>Date: 2019/12/9 13:21     <br>Author : dxl
    */
    public static void copyTbaleByLoop(String srcPath, int srcTableIndex,int copyNum,String storePath) throws Exception {
        File file = new File(srcPath);
        InputStream is = new FileInputStream(file);
        CustomXWPFDocument doc = new CustomXWPFDocument(is);
        XWPFTable sourceTable = doc.getTableArray(srcTableIndex);
        for(int j = 0; j < copyNum; j++){
            XWPFTable tableOne = doc.createTable();
            for(int i = 0; i < sourceTable.getRows().size(); i++){
                copy(tableOne,sourceTable.getRow(i),i);
            }
            doc.createParagraph();
            tableOne.removeRow(tableOne.getRows().size()-1);
        }
        OutputStream os =  new FileOutputStream(storePath);

        doc.write(os);
        close(os);
        close(is);

    }

    /**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) throws IOException {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
//        List<XWPFParagraph> paragraphs = doc.getParagraphs();
//        for(int i = paragraphs.size()-1; i >= 0; i--){
//            replaceInPara(paragraphs.get(i), params, doc);
//            insertTableInParaFromAtherDoc("C:\\Users\\00\\Desktop\\baogao\\testmodel4.docx",0,doc,paragraphs.get(i),false);
//        }
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            replaceInPara(para, params, doc);
        }
    }
    /**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInParaImgAgain(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            replaceInPara(para, params, doc);
            replaceImgAgain(para, params, doc);
        }
    }

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private static void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs;
        Matcher matcher;
        if (matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            int start = -1;
            int end = -1;
            String str = "";
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString();
                if ((runText.length() > 0 && runText.contains("${"))
                        || (runText.length() > 0 && '$' == runText.charAt(runText.length() - 1) && i+1 <runs.size() && '$' == runs.get(i+1).toString().charAt(0))) {
                    start = i;
                }
                if (runText.length() > 0 && runText.contains("}")) {
                    if (start != -1) {
                        end = i;
                        break;
                    }
                }
            }
            for(int i = start; i <= end; i++){
                str = str + runs.get(i).toString();
            }
            //去掉标识符开始之后到结束的run,保留第一个run是为了使用run的样式
            for (int i = start+1; i <= end; i++) {
                para.removeRun(i);
                i--;
                end--;
            }
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                String key = entry.getKey();
                if (str.indexOf(key) != -1) {
                    Object value = entry.getValue();
                    if (value instanceof String) {
                        str = str.replace(key, value.toString());
                        para.getRuns().get(start).setText(str,0);//替换保留的第一个run中的内容,若没有0就变为插入了
                        break;
                    } else if (value instanceof Map) {
                        str = str.replace(key, "");
                        Map pic = (Map) value;
                        int width = Integer.parseInt(pic.get("width").toString());
                        int height = Integer.parseInt(pic.get("height").toString());
                        int picType = getPictureType(pic.get("type").toString());
                        byte[] byteArray = (byte[]) pic.get("content");
                        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                        try {
                            //int ind = doc.addPicture(byteInputStream,picType);
                            //doc.createPicture(ind, width , height,para);
                            doc.addPictureData(byteInputStream, picType);
                            if(start == 0){
                                para.getRuns().get(0).setText("",0);
                                doc.createPicture(doc.getAllPictures().size() - 1, width, height, para,start);
                            }else {
                                para.removeRun(start);
                                doc.createPicture(doc.getAllPictures().size() - 1, width, height, para,start-1);
                            }
                            break;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        if (matcher(para.getParagraphText()).find()) {
            replaceInPara(para, params, doc);
        }
    }

    /**
     * 为表格插入数据,行数不够添加新行
     * 说明:当表格中出现一行中每一个单元格都为'#'时,认为需要循环插入
     * @param tableList 插入数据集合
     */
    private static void insertTable(XWPFTable table, List<String[]> tableList,boolean isMergeRow,List<List<Integer>> mergeRowLists) {
        int headRowNum = 0;
        int tailRowNum = 0;
        List<XWPFTableRow> rows = table.getRows();
        List<Integer> cellOrder = new ArrayList<>();
        for(int i = 0; i < rows.size(); i++){
            String flagHead = "";
            String flagFor = "";
            for(int j = 0; j < rows.get(i).getTableCells().size(); j++){
                String cellText = rows.get(i).getTableCells().get(j).getText().replace(" ","");
                if(cellText.length() > 0){
                    if(cellText.contains("#")){
                        flagHead = flagHead + "#";
                    }
                    if(cellText.contains("#[") && cellText.contains("]") && cellText.indexOf("]") > cellText.indexOf("[")+1
                            && cellOrder.size() < rows.get(i).getTableCells().size()){
                        cellOrder.add(Integer.valueOf(cellText.substring(cellText.indexOf("[")+1,cellText.indexOf("]"))));
                    }else if(cellText.contains("#")  && cellOrder.size() < rows.get(i).getTableCells().size()){
                        cellOrder.add(null);
                    }
                }
                //删除循环单元格内多余段落,不想单元格内有默认数据放开注释
//                List<XWPFParagraph> paragraphs = rows.get(i).getTableCells().get(j).getParagraphs();
//                if(paragraphs.size() > 1){
//                    for(int k = paragraphs.size()-1; k >= 0; k--){
//                        if(!paragraphs.get(k).getText().contains("#")){
//                            rows.get(i).getTableCells().get(j).removeParagraph(k);
//                        }
//                    }
//                }
                //删除循环单元格内要替换段落后的多余run
                if(rows.get(i).getTableCells().get(j).getParagraphs().size() > 0){
                    for(XWPFParagraph paragraph : rows.get(i).getTableCells().get(j).getParagraphs()){
                        if(paragraph.getText().indexOf("#") == 0){
                            for(int k = paragraph.getRuns().size()-1; k > 0; k--){
                                paragraph.removeRun(k);
                            }
                        }
                    }
                }
                flagFor = flagFor + "#";
            }
            if(flagFor.equals(flagHead)){
                headRowNum = i;
                break;
            }
        }
        if(headRowNum > 0){
            tailRowNum = rows.size()- headRowNum - 1;
            //创建行,根据需要插入的数据添加新行,不处理表头
            for (int i = 0; i < tableList.size(); i++) {
                copy(table,table.getRow(headRowNum),headRowNum+1+i);
            }
            //遍历表格插入数据

            int length = table.getRows().size();
            for (int i = headRowNum; i < length - tailRowNum - 1; i++) {
                XWPFTableRow newRow = table.getRow(i);
                List<XWPFTableCell> cells = newRow.getTableCells();
                for (int j = 0; j < cells.size(); j++) {
                    XWPFTableCell cell = cells.get(j);
                    String s ="";
                    if(j < tableList.get(i - headRowNum).length){
                        if(cellOrder.get(j) != null && !cellOrder.get(j).equals(-1)){
                            s = tableList.get(i - headRowNum)[cellOrder.get(j)];
                        }else if(cellOrder.get(j) != null &&cellOrder.get(j).equals(-1)){
                            s = "";
                        }else {
                            s = tableList.get(i - headRowNum)[j];
                        }
                    }else if(cellOrder.get(j) != null && !cellOrder.get(j).equals(-1)){
                        s = tableList.get(i - headRowNum)[cellOrder.get(j)];
                    }
                    for(XWPFParagraph paragraph : cell.getParagraphs()){
                        if(paragraph.getText().contains("#")){
                            paragraph.getRuns().get(0).setText(s,0);
                        }
                    }
                }
            }
            table.removeRow(table.getRows().size()-1-tailRowNum);
            if(isMergeRow && mergeRowLists.size() > 0){
                for(List<Integer> listInts : mergeRowLists){
                    mergeCellsRow(table,listInts.get(0),listInts.get(1),listInts.get(2));
                }
            }
//            mergeCellsCol(table,1,2,3);

        }
    }

    /**
     * 替换表格里面的变量
     * @param doc    要替换的文档
     * @param params 参数
     */
    private static void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList,boolean isMergeRow,List<List<Integer>> mergeRowLists) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        while (iterator.hasNext()) {
            table = iterator.next();
            if (table.getRows().size() > 1) {
                insertTable(table,tableList,isMergeRow,mergeRowLists);
                //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
//                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) {
                                replaceInPara(para, params, doc);
                            }
                        }
                    }
            }
        }
    }

    /**
    * <b> 再次替换图片
    * </b><br><br><i>Description</i> : 因为有的地方替换图片不确定,先替换成其他占位符,在替换掉
    * @param para 要替换的段落
    * @param params 参数
    * @param doc 文档
    * @return void
    * <br><br>Date: 2019/12/9 15:54     <br>Author : dxl
    */
    private static void replaceImgAgain(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs;
        if (matcherStar(para.getParagraphText()).find()) {
            runs = para.getRuns();
            int start = -1;
            int end = -1;
            String str = "";
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString();
                if ((runText.length() > 0 && runText.contains("*{"))
                        || (runText.length() > 0 && '*' == runText.charAt(runText.length() - 1) && i+1 <runs.size() && '$' == runs.get(i+1).toString().charAt(0))) {
                    start = i;
                }
                if (runText.length() > 0 && runText.contains("}")) {
                    if (start != -1) {
                        end = i;
                        break;
                    }
                }
            }
            for(int i = start; i <= end; i++){
                str = str + runs.get(i).toString();
            }
            //去掉标识符开始之后到结束的run,保留第一个run是为了使用run的样式
            for (int i = start+1; i <= end; i++) {
                para.removeRun(i);
                i--;
                end--;
            }
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                String key = entry.getKey();
                if (str.indexOf(key) != -1) {
                    Object value = entry.getValue();
                    if (value instanceof String) {
                        str = str.replace(key, value.toString());
                        para.getRuns().get(start).setText(str,0);//替换保留的第一个run中的内容,若没有0就变为插入了
                        break;
                    } else if (value instanceof Map) {
                        str = str.replace(key, "");
                        Map pic = (Map) value;
                        int width = Integer.parseInt(pic.get("width").toString());
                        int height = Integer.parseInt(pic.get("height").toString());
                        int picType = getPictureType(pic.get("type").toString());
                        byte[] byteArray = (byte[]) pic.get("content");
                        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                        try {
                            //int ind = doc.addPicture(byteInputStream,picType);
                            //doc.createPicture(ind, width , height,para);
                            doc.addPictureData(byteInputStream, picType);
                            if(start == 0){
                                para.getRuns().get(0).setText("",0);
                                doc.createPicture(doc.getAllPictures().size() - 1, width, height, para,start);
                            }else {
                                para.removeRun(start);
                                doc.createPicture(doc.getAllPictures().size() - 1, width, height, para,start-1);
                            }
                            break;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        if (matcher(para.getParagraphText()).find()) {
            replaceInPara(para, params, doc);
        }
    }
    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private static Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }
    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private static Matcher matcherStar(String str) {
        Pattern pattern = Pattern.compile("\\*\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 根据图片类型,取得对应的图片类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }

    /**
     * 将输入流中的数据写入字节数组
     *
     * @param in
     * @return
     */
    public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
        byte[] byteArray = null;
        try {
            int total = in.available();
            byteArray = new byte[total];
            in.read(byteArray);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isClose) {
                try {
                    in.close();
                } catch (Exception e2) {
                    e2.getStackTrace();
                }
            }
        }
        return byteArray;
    }


    /**
     * 关闭输入流
     *
     * @param is
     */
    private static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭输出流
     *
     * @param os
     */
    private static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
    * <b> 复制表格
    * </b><br><br><i>Description</i> :
    * @param table, sourceRow, rowIndex
    * @return void
    * <br><br>Date: 2019/12/9 13:23     <br>Author : dxl
    */
    public static void copy(XWPFTable table,XWPFTableRow sourceRow,int rowIndex){
        //在表格指定位置新增一行
        XWPFTableRow targetRow = table.insertNewTableRow(rowIndex);
        //复制行属性
        targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
        List<XWPFTableCell> cellList = sourceRow.getTableCells();
        if (null == cellList) {
            return;
        }
        //复制列及其属性和内容
        XWPFTableCell targetCell = null;
        for (XWPFTableCell sourceCell : cellList) {
            targetCell = targetRow.addNewTableCell();
            //处理一个单元格内有多个段落
            List<XWPFParagraph> paragraphs = sourceCell.getParagraphs();
            if(paragraphs.size() > 1){
                for(int i = 1; i < paragraphs.size(); i++){
                    targetCell.addParagraph();
                }
            }
            //列属性
            targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
            //段落属性
            if(sourceCell.getParagraphs()!=null&&sourceCell.getParagraphs().size()>0){
                for(int i = 0; i < sourceCell.getParagraphs().size();i++){
                    targetCell.getParagraphs().get(i).getCTP().setPPr(sourceCell.getParagraphs().get(i).getCTP().getPPr());
                    if(sourceCell.getParagraphs().get(i).getRuns()!=null&&sourceCell.getParagraphs().get(i).getRuns().size()>0){
                        for(int j = 0; j < sourceCell.getParagraphs().get(i).getRuns().size(); j++){
                            XWPFRun cellR = targetCell.getParagraphs().get(i).createRun();
                            cellR.setText(sourceCell.getParagraphs().get(i).getRuns().get(j).getText(0));
                            cellR.setBold(sourceCell.getParagraphs().get(i).getRuns().get(j).isBold());
                            cellR.setColor(sourceCell.getParagraphs().get(i).getRuns().get(j).getColor());
                            if(sourceCell.getParagraphs().get(i).getRuns().get(j).getFontSize() == -1){
                                cellR.setFontSize(10);
                            }else {
                                cellR.setFontSize(sourceCell.getParagraphs().get(i).getRuns().get(j).getFontSize());
                            }
                            cellR.setCapitalized(sourceCell.getParagraphs().get(i).getRuns().get(j).isCapitalized());
                            cellR.setDoubleStrikethrough(sourceCell.getParagraphs().get(i).getRuns().get(j).isDoubleStrikeThrough());
                            cellR.setEmbossed(sourceCell.getParagraphs().get(i).getRuns().get(j).isEmbossed());
                            cellR.setFontFamily(sourceCell.getParagraphs().get(i).getRuns().get(j).getFontFamily());
                            cellR.setImprinted(sourceCell.getParagraphs().get(i).getRuns().get(j).isImprinted());
                            cellR.setItalic(sourceCell.getParagraphs().get(i).getRuns().get(j).isItalic());
                            cellR.setKerning(sourceCell.getParagraphs().get(i).getRuns().get(j).getKerning());
                            cellR.setShadow(sourceCell.getParagraphs().get(i).getRuns().get(j).isShadowed());
                            cellR.setStrikeThrough(sourceCell.getParagraphs().get(i).getRuns().get(j).isStrikeThrough());
                            cellR.setSmallCaps(sourceCell.getParagraphs().get(i).getRuns().get(j).isSmallCaps());
                            cellR.setSubscript(sourceCell.getParagraphs().get(i).getRuns().get(j).getSubscript());
                            cellR.setTextPosition(sourceCell.getParagraphs().get(i).getRuns().get(j).getTextPosition());
                            cellR.setUnderline(sourceCell.getParagraphs().get(i).getRuns().get(j).getUnderline());
                        }
                    }else{
                        targetCell.setText(sourceCell.getText());
                    }
                }

            }else{
                targetCell.setText(sourceCell.getText());
            }
        }
    }
    /**
    * <b> 在段落中插入表格
    * </b><br><br><i>Description</i> : 表格单元格中建段落再插入表格,原段落会挤到下一个段落,用isDelNullPara控制删除空原段落,
     * isDelNullPara为true时要注意循环迭代,最好倒序循环para,插入表格后还有对para操作时也要注意,可能isDelNullPara要设为false
    * @param sourceTable 要插入的源表格
     * @param targetDoc 要插入的文档
     * @param  para 要插入表格的段落
     * @param  isDelNullPara 是否删除para为空是多出来的空段落
    * @return void
    * <br><br>Date: 2019/12/12 11:39     <br>Author : dxl
    */
    public static void insertTableInPara(XWPFTable sourceTable,CustomXWPFDocument targetDoc,XWPFParagraph para,boolean isDelNullPara){
        XmlCursor cursor = para.getCTP().newCursor();
        XWPFTable tableOne = targetDoc.insertNewTbl(cursor);
        for(int i = 0; i < sourceTable.getRows().size(); i++){
            copy(tableOne,sourceTable.getRow(i),i+1);
        }
        if(isDelNullPara){
            if(para.getText() != null && para.getText().length() > 0){
            }else {
                targetDoc.removeBodyElement(targetDoc.getPosOfParagraph(para));
            }
        }
        tableOne.removeRow(0);
    }
    /**
     * <b> 在段落中插入表格--源表格来源于其他Word
     * </b><br><br><i>Description</i> : 表格单元格中建段落再插入表格,原段落会挤到下一个段落,用isDelNullPara控制删除空原段落,
     * isDelNullPara为true时要注意循环迭代,最好倒序循环para,插入表格后还有对para操作时也要注意,可能isDelNullPara要设为false
     * @param srcPath 源表格所在源Word的路径
     * @param tableIndex 源表格所在源Word中是第几个,即下标
     * @param targetDoc 要插入的文档
     * @param  para 要插入表格的段落
     * @param  isDelNullPara 是否删除para为空是多出来的空段落
     * @return void
     * <br><br>Date: 2019/12/12 11:39     <br>Author : dxl
     */
    public static void insertTableInParaFromAtherDoc(String srcPath,int tableIndex,CustomXWPFDocument targetDoc,XWPFParagraph para,boolean isDelNullPara) throws IOException {
        XmlCursor cursor = para.getCTP().newCursor();
        File file = new File(srcPath);
        InputStream is = new FileInputStream(file);
        CustomXWPFDocument srcDoc = new CustomXWPFDocument(is);
        XWPFTable sourceTable = srcDoc.getTableArray(tableIndex);
        XWPFTable tableOne = targetDoc.insertNewTbl(cursor);
        for(int i = 0; i < sourceTable.getRows().size(); i++){
            copy(tableOne,sourceTable.getRow(i),i+1);
            tableOne.removeRow(0);
        }
        if(isDelNullPara){
            if(para.getText() != null && para.getText().length() > 0){
            }else {
                targetDoc.removeBodyElement(targetDoc.getPosOfParagraph(para));
            }
        }
        close(is);
    }

    /**
     * @Description: 跨列合并
     */
    public static void mergeCellsCol(XWPFTable table, int row, int fromCell, int toCell) {
        for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if ( cellIndex == fromCell ) {
                // 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);
            }
        }
    }
    /**
     * @Description: 跨行合并
     */
    public static void mergeCellsRow(XWPFTable table, int col, int fromRow, int toRow) {
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            if ( rowIndex == fromRow ) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

}

五、最后生成的word文档

六、Word中公式解析(好似得具体公式具体分析)

6.1需要在上面依赖基础上添加依赖

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>ooxml-schemas</artifactId>
	<version>1.4</version>
</dependency>

6.2以最简单的公式为例:

在WordGrtUtil中的代码测试的

/**
     * 替换段落里面的变量
     * @param doc 要替换的文档
     * @param params 参数
     */
    private static void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            System.out.println(".........000.......:   "+para.getCTP().getOMathList());
            if(para.getCTP().getOMathList().size() > 0){
                System.out.println("..........111......:   "+para.getCTP().getOMathList().get(0).getSSubSupList().get(0).getE().getRList().get(0).getT2List().get(0).getStringValue());
                System.out.println("........222........:   "+para.getCTP().getOMathList().get(0).getSSubSupList().get(0).getSub().getRList().get(0).getT2List().get(0).getStringValue());
                System.out.println("........333........:   "+para.getCTP().getOMathList().get(0).getSSubSupList().get(0).getSup().getRList().get(0).getT2List().get(0).getStringValue());
                para.getCTP().getOMathList().get(0).getSSubSupList().get(0).getSup().getRList().get(0).getT2List().get(0).setStringValue("www");
            }
            replaceInPara(para, params, doc);
        }
    }

6.3打印为:

.........000.......:   []
.........000.......:   [<m:sSubSup xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
  <m:sSubSupPr>
    <m:ctrlPr>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
    </m:ctrlPr>
  </m:sSubSupPr>
  <m:e>
    <m:r>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
      <m:t>a</m:t>
    </m:r>
  </m:e>
  <m:sub>
    <m:r>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
      <m:t>n</m:t>
    </m:r>
  </m:sub>
  <m:sup>
    <m:r>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
      <m:t>m</m:t>
    </m:r>
  </m:sup>
</m:sSubSup>, <m:sSubSup xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape">
  <m:sSubSupPr>
    <m:ctrlPr>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
    </m:ctrlPr>
  </m:sSubSupPr>
  <m:e>
    <m:r>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
      <m:t>b</m:t>
    </m:r>
  </m:e>
  <m:sub>
    <m:r>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
      <m:t>y</m:t>
    </m:r>
  </m:sub>
  <m:sup>
    <m:r>
      <w:rPr>
        <w:rFonts w:ascii="Cambria Math" w:hAnsi="Cambria Math"/>
      </w:rPr>
      <m:t>x</m:t>
    </m:r>
  </m:sup>
</m:sSubSup>]
..........111......:   a
........222........:   n
........333........:   m
.........000.......:   []

6.4分析:

  1. 第二个000行打印为有公式时的集合(两个),第一和第三000为空 [ ];
  2. 公式是以<m:sSubSup 开头的,所以 para.getCTP().getOMathList() 之后要拿到 SSubSup 集合,对应为如 para.getCTP().getOMathList().get(0).getSSubSupList() ;
  3. 可以看到公式中的 “a” 在<m:sSubSup 标签下三层标签<m:e>  <m:r> <m:t> 中,获得每一层都有对应的方法,如getE().getRList().get(0).getT2List() ,其中有的方法返回是集合有的不是;
  4. 拿到<m:t> 后就可以拿到具体的公式中的值了(a),如getE().getRList().get(0).getT2List().get(0).getStringValue() ;
  5.   111输出打印的就是..........111......:   a
  6. 同理 上标 “m” 在<m:sup>   <m:r> <m:t> 标签中,getSup().getRList().get(0).getT2List().get(0).getStringValue() 拿到 “ m”值;
  7. 同理拿到 “n”的值;
  8. 注意:每个标签都有对应的获取方法,通常应为标签中冒号后字符的对应如<m:sup> 对应 getSup(),  <m:r> 对应getRList();
  9. 改变值时使用getStringValue() 对应的 setStringValue() 即可,如:para.getCTP().getOMathList().get(0).getSSubSupList().get(0).getSup().getRList().get(0).getT2List().get(0).setStringValue("www");
  10. 新建公式是,应该是按照这个标签层层新增,方法以 add 开头,每个标签都有对应的方法,对应关系应该同 8;
     

    6.5修改结果

     

    七、段落(单元格)插入表格

    7.1原word

7.2另外word制定表格

7.3插入表格

在方法replaceInPara中遍历段落时使用insertTableInParaFromAtherDoc方法插入7.2中的表格;具体解释看方法注释

结果如下:

 

;