POI利用word模板动态生成word报表以及动态生成word表格



在这里插入图片描述



核心依赖

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

动态表格

测试类

package com.uncle.demo.poi;


import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.lang.System.out;

/**
 * @program demo01
 * @description 测试工具类
 * @author: Sun Jinxin
 * @create: 2021/11/30 14:50
 */
public class Test {
    
    

    public static void main(String[] args){
    
    
        //需要进行文本替换的信息
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("name", "维维豆奶粉");

        //需要进行动态生成的信息
        Map<String,Object> mapList = new HashMap<String, Object>();

        //第一个动态生成的数据列表
        List<String[]> list01 = new ArrayList<String[]>();
        list01.add(new String[]{
    
    "1","维修服务","1","6100333","2020/8/4","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});
        list01.add(new String[]{
    
    "2","售后服务","2","6107986","2020/10/28","相关校正","设备运行正常","1.鼠标\n" + "2.头托垫\n"});
        list01.add(new String[]{
    
    "3","售后服务","2","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});
        list01.add(new String[]{
    
    "4","回退服务","3","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});
        list01.add(new String[]{
    
    "5","回退服务","3","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});
        list01.add(new String[]{
    
    "6","回退服务","3","6100922","2020/11/15","1、更换鼠标 \n" + "2、更换头托垫\n","设备运行正常","1.鼠标"});

        mapList.put("list01", list01);

        CustomXWPFDocument doc = WorderToNewWordUtils.changWord("C:\\Users\\sunjinxin\\Downloads\\project-office-temp-tool\\project-office-temp-tool\\demo01\\src\\main\\resources\\templates\\test1.docx",data,mapList);
        try (FileOutputStream fopts = new FileOutputStream("D:/呵呵.docx")) {
    
    

            doc.write(fopts);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

        out.println("成功!!!");
    }
}

工具类

package com.uncle.demo.poi;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

/**
 * @program demo01
 * @description 核心工具类
 * @author: Sun Jinxin
 * @create: 2021/11/30 14:50
 */
public class WorderToNewWordUtils {
    
    
    /**
     * 根据模板生成word文档
     *
     * @param inputUrl 模板路径
     * @param textMap  需要替换的文本内容
     * @param mapList  需要动态生成的内容
     * @return
     */
    public static CustomXWPFDocument changWord(String inputUrl, Map<String, Object> textMap, Map<String, Object> mapList) {
    
    
        CustomXWPFDocument document = null;
        try {
    
    
            //获取docx解析对象
            document = new CustomXWPFDocument(POIXMLDocument.openPackage(inputUrl));

            //解析替换文本段落对象
            WorderToNewWordUtils.changeText(document, textMap);

            //解析替换表格对象
            WorderToNewWordUtils.changeTable(document, textMap, mapList);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        return document;
    }

    /**
     * 替换段落文本
     *
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeText(CustomXWPFDocument document, Map<String, Object> textMap) {
    
    
        //获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (XWPFParagraph paragraph : paragraphs) {
    
    

            //判断此段落时候需要进行替换
            String text = paragraph.getText();
            System.out.println(text);
            if (checkText(text)) {
    
    
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
    
    
                    run.setFontSize(8);
                    //替换模板原来位置
                    Object ob = changeValue(run.toString(), textMap);
                    if (ob instanceof String) {
    
    
                        run.setText((String) ob, 0);
                    }
                }
            }
        }
    }

    /**
     * 替换表格对象方法
     *
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     * @param mapList  需要动态生成的内容
     */
    public static void changeTable(CustomXWPFDocument document, Map<String, Object> textMap, Map<String, Object> mapList) {
    
    
        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();

        //循环所有需要进行替换的文本,进行替换
        for (int i = 0; i < tables.size(); i++) {
    
    
            XWPFTable table = tables.get(i);
            if (checkText(table.getText())) {
    
    
                List<XWPFTableRow> rows = table.getRows();
                //遍历表格,并替换模板
                eachTable(document, rows, textMap);
            }
        }

        List<String[]> list01 = (List<String[]>) mapList.get("list01");
        //操作word中的表格
        for (int i = 0; i < tables.size(); i++) {
    
    
            //只处理行数大于等于2的表格,且不循环表头
            XWPFTable table = tables.get(i);
            //第二个表格使用daList,插入数据
            //TODO
            if (null != list01 && 0 < list01.size() && i == 0) {
    
    
                //TODO 处理第几个表格
                insertTable(table, null, list01, 2);

                int initFlag = 1;
                int mergeRowNum = initFlag;
                //获取第一条数据
                String s1 = list01.get(0)[1];
                for (int j = 0; j < list01.size(); j++) {
    
    

                    String s2 = list01.get(j)[1];
                    if (s1.equals(s2)) {
    
    
                        mergeRowNum++;
                        s1 = s2;
                    } else {
    
    
                        s1 = s2;
                        mergeCellVertically(table, 1, initFlag + 1, mergeRowNum);
                        mergeCellVertically(table, 2, initFlag + 1, mergeRowNum);
                        initFlag = mergeRowNum;
                        mergeRowNum = initFlag + 1;
                    }
                    if (j == list01.size() - 1) {
    
    
                        mergeCellVertically(table, 1, initFlag + 1, mergeRowNum);
                        mergeCellVertically(table, 2, initFlag + 1, mergeRowNum);
                    }


                }

            }
        }

    }


    /**
     * 遍历表格
     *
     * @param rows    表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(CustomXWPFDocument document, List<XWPFTableRow> rows, Map<String, Object> textMap) {
    
    
        for (XWPFTableRow row : rows) {
    
    
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
    
    
                //判断单元格是否需要替换
                if (checkText(cell.getText())) {
    
    
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
    
    
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
    
    

                            Object ob = changeValue(run.toString(), textMap);
                            if (ob instanceof String) {
    
    
                                run.setText((String) ob, 0);
                            } else if (ob instanceof Map) {
    
    

                                run.setText("", 0);
                                Map pic = (Map) ob;
                                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 = document.addPicture(byteInputStream, picType);
                                    document.createPicture(ind, width, height, paragraph);
                                } catch (Exception e) {
    
    
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 为表格插入数据,行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 第四个表格的插入数据
     * @param daList    第二个表格的插入数据
     * @param type      表格类型:1-第一个表格 2-第二个表格 3-第三个表格 4-第四个表格
     */
    public static void insertTable(XWPFTable table, List<String> tableList, List<String[]> daList, Integer type) {
    
    
        if (2 == type) {
    
    
            //创建行和创建需要的列
            for (int i = 0; i < daList.size(); i++) {
    
    
                //添加一个新行
                XWPFTableRow row = table.insertNewTableRow(i+2);

                for (int j = 0; j < daList.get(0).length; j++) {
    
    
                    row.createCell();//添加第一个列

                }

            }

            //创建行,根据需要插入的数据添加新行,不处理表头
            for (int i = 0; i < daList.size(); i++) {
    
    
                List<XWPFTableCell> cells = table.getRow(i + 2).getTableCells();

                for (int j = 0; j < cells.size(); j++) {
    
    
                    XWPFTableCell cell02 = cells.get(j);

                    cell02.setText(daList.get(i)[j]);
                }
            }
        } else if (4 == type) {
    
    
            //插入表头下面第一行的数据
            for (int i = 0; i < tableList.size(); i++) {
    
    
                XWPFTableRow row = table.createRow();
                List<XWPFTableCell> cells = row.getTableCells();
                cells.get(0).setText(tableList.get(i));
            }
        }
    }

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

    /**
     * 匹配传入信息集合与模板
     *
     * @param value   模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static Object changeValue(String value, Map<String, Object> textMap) {
    
    
        Set<Entry<String, Object>> textSets = textMap.entrySet();
        Object valu = "";
        for (Entry<String, Object> textSet : textSets) {
    
    
            //匹配模板与替换值 格式${key}
            String key = textSet.getKey();
            if (value.indexOf(key) != -1) {
    
    
                valu = textSet.getValue();
            }
        }
        return valu;
    }

    /**
     * 将输入流中的数据写入字节数组
     *
     * @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) {
    
    
                    System.out.println("关闭流失败");
                }
            }
        }
        return byteArray;
    }

    /**
     * 根据图片类型,取得对应的图片类型代码
     *
     * @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 table
     * @param col     需要合并的列
     * @param fromRow 开始行
     * @param toRow   结束行
     */
    public static void mergeCellVertically(XWPFTable table, int col, int fromRow, int toRow) {
    
    
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
    
    
            CTVMerge vmerge = CTVMerge.Factory.newInstance();
            if (rowIndex == fromRow) {
    
    
                vmerge.setVal(STMerge.RESTART);
            } else {
    
    
                vmerge.setVal(STMerge.CONTINUE);
            }
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            CTTcPr tcPr = cell.getCTTc().getTcPr();
            if (tcPr != null) {
    
    
                tcPr.setVMerge(vmerge);
            } else {
    
    
                tcPr = CTTcPr.Factory.newInstance();
                tcPr.setVMerge(vmerge);
                cell.getCTTc().setTcPr(tcPr);
            }
        }
    }

    //列合并  ,有点问题,用不了
    public static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
    
    
        for (int colIndex = fromCol; colIndex <= toCol; colIndex++) {
    
    
            CTHMerge hmerge = CTHMerge.Factory.newInstance();
            if (colIndex == fromCol) {
    
    
                hmerge.setVal(STMerge.RESTART);
            } else {
    
    
                hmerge.setVal(STMerge.CONTINUE);
            }
            XWPFTableCell cell = table.getRow(row).getCell(colIndex);
            CTTcPr tcPr = cell.getCTTc().getTcPr();
            if (tcPr != null) {
    
    
                tcPr.setHMerge(hmerge);
            } else {
    
    
                tcPr = CTTcPr.Factory.newInstance();
                tcPr.setHMerge(hmerge);
                cell.getCTTc().setTcPr(tcPr);
            }
        }
    }

    /**
     * 获取需要合并单元格的下标
     *
     * @return
     */
    public static List<Integer[]> startEnd(List<String[]> daList) {
    
    
        List<Integer[]> indexList = new ArrayList<Integer[]>();

        List<String> list = new ArrayList<String>();
        for (int i = 0; i < daList.size(); i++) {
    
    
            list.add(daList.get(i)[0]);
        }
        Map<Object, Integer> tm = new HashMap<Object, Integer>();
        for (int i = 0; i < daList.size(); i++) {
    
    
            if (!tm.containsKey(daList.get(i)[0])) {
    
    
                tm.put(daList.get(i)[0], 1);
            } else {
    
    
                int count = tm.get(daList.get(i)[0]) + 1;
                tm.put(daList.get(i)[0], count);
            }
        }
        for (Map.Entry<Object, Integer> entry : tm.entrySet()) {
    
    
            String key = entry.getKey().toString();
            String value = entry.getValue().toString();
            if (list.indexOf(key) != (-1)) {
    
    
                Integer[] index = new Integer[2];
                index[0] = list.indexOf(key);
                index[1] = list.lastIndexOf(key);
                indexList.add(index);
            }
        }
        return indexList;
    }
}
package com.uncle.demo.poi;


import java.io.IOException;
import java.io.InputStream;
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;


/**
 * @program demo01
 * @description 自定义 XWPFDocument,并重写 createPicture()方法
 * @author: Sun Jinxin
 * @create: 2021/11/30 14:50
 */
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();

        System.out.println(blipId+":"+inline);

        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("测试");
    }
}

动态数据

测试类

package com.hidata.tool;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Test {
    
    

    public static void main(String[] args) throws IOException {
    
    

        // 存储报表全部数据
        Map<String, Object> wordDataMap = new HashMap<String, Object>();
        // 存储报表中不循环的数据
        Map<String, Object> parametersMap = new HashMap<String, Object>();

        
        List<Map<String, Object>> table1 = new ArrayList<Map<String, Object>>();

        Map<String, Object> map1=new HashMap();
        map1.put("name", "张三");
        map1.put("age", "23");
        map1.put("email", "[email protected]");

        Map<String, Object> map2=new HashMap();
        map2.put("name", "李四");
        map2.put("age", "45");
        map2.put("email", "[email protected]");

        Map<String, Object> map3=new HashMap();
        map3.put("name", "Tom");
        map3.put("age", "34");
        map3.put("email", "[email protected]");

        table1.add(map1);
        table1.add(map2);
        table1.add(map3);




        List<Map<String, Object>> table2 = new ArrayList<Map<String, Object>>();
        Map<String, Object> map4=new HashMap();
        map4.put("name", "tom");
        map4.put("number", "sd1234");
        map4.put("address", "上海");

        Map<String, Object> map5=new HashMap();
        map5.put("name", "seven");
        map5.put("number", "sd15678");
        map5.put("address", "北京");

        Map<String, Object> map6=new HashMap();
        map6.put("name", "lisa");
        map6.put("number", "sd9078");
        map6.put("address", "广州");

        table2.add(map4);
        table2.add(map5);
        table2.add(map6);



        parametersMap.put("userName", "JUVENILESS");
        parametersMap.put("time", "2018-03-24");
        parametersMap.put("sum", "3");


        wordDataMap.put("table1", table1);
        wordDataMap.put("table2", table2);
        wordDataMap.put("parametersMap", parametersMap);
        //改成你本地文件所在目录
        File file = new File("C:\\Users\\sunjinxin\\Desktop\\time.docx");



        // 读取word模板
        FileInputStream fileInputStream = new FileInputStream(file);


        WordTemplate template = new WordTemplate(fileInputStream);

        // 替换数据
        template.replaceDocument(wordDataMap);


        //生成文件
        //改成你本地文件所在目录
        File outputFile=new File("C:\\Users\\sunjinxin\\Desktop\\输出.docx");
        FileOutputStream fos  = new FileOutputStream(outputFile);
        template.getDocument().write(fos);

    }

}

工具类

/**
 * @Title: WordTemplate2.java
 * @Package: com.highdata.templateTools
 * @Description: TODO
 * @author: Juveniless
 * @date: 2017年11月27日 下午3:23:13
 */
package com.hidata.tool;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.xwpf.usermodel.BodyElementType;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.PositionInParagraph;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

/**
 *
 * 对docx文件中的文本及表格中的内容进行替换 --模板仅支持对 {key} 标签的替换
 *
 * @ClassName: WordTemplate
 * @Description: TODO(!!!使用word2013 docx文件)
 * @author Juveniless
 * @date: 2017年11月27日 下午3:25:56
 * <br>(1)word模板注意页边距的问题,存在问题:比如页边距默认为3cm,画表格时,仍然可以通过
 * 拖拽,把表格边框拖动到看起来就像页边距只有1cm的样子,但是实际上此时页边距还是3cm,生成的
 * word报表的页边距还是会按照3cm来生成。解决办法,在word文件里,设置好页边距,如果需要表格
 * 两边页边距很窄,需要在word里设置页边距窄一点,而不是直接拖动表格边框来实现。
 *
 */

public class WordTemplate {
    
    

    private XWPFDocument document;

    public XWPFDocument getDocument() {
    
    
        return document;
    }

    public void setDocument(XWPFDocument document) {
    
    
        this.document = document;
    }

    /**
     * 初始化模板内容
     *
     * @author Juveniless
     * @date 2017年11月27日 下午3:59:22
     * @param inputStream
     *            模板的读取流(docx文件)
     * @throws IOException
     *
     */
    public WordTemplate(InputStream inputStream) throws IOException {
    
    
        document = new XWPFDocument(inputStream);
    }

    /**
     * 将处理后的内容写入到输出流中
     *
     * @param outputStream
     * @throws IOException
     */
    public void write(OutputStream outputStream) throws IOException {
    
    
        document.write(outputStream);
    }





    /**
     * 根据dataMap对word文件中的标签进行替换; <br><br>
     * !!!!***需要注意dataMap的数据格式***!!!! <br><br>
     * 对于需要替换的普通标签数据标签(不需要循环)-----必须在dataMap中存储一个key为parametersMap的map,
     * 来存储这些不需要循环生成的数据,比如:表头信息,日期,制表人等。 <br><br>
     * 对于需要循环生成的表格数据------key自定义,value为 --ArrayList&lt;Map&lt;String, String>>
     * @author Juveniless
     * @date 2017年11月27日 下午3:29:27
     * @param dataMap
     *
     */
    public void replaceDocument(Map<String, Object> dataMap) {
    
    

        if (!dataMap.containsKey("parametersMap")) {
    
    
            System.out.println("数据源错误--数据源(parametersMap)缺失");
            return;
        }
        @SuppressWarnings("unchecked")
        Map<String, Object> parametersMap = (Map<String, Object>) dataMap
                .get("parametersMap");

        List<IBodyElement> bodyElements = document.getBodyElements();// 所有对象(段落+表格)
        int templateBodySize = bodyElements.size();// 标记模板文件(段落+表格)总个数

        int curT = 0;// 当前操作表格对象的索引
        int curP = 0;// 当前操作段落对象的索引
        for (int a = 0; a < templateBodySize; a++) {
    
    
            IBodyElement body = bodyElements.get(a);
            if (BodyElementType.TABLE.equals(body.getElementType())) {
    
    // 处理表格
                XWPFTable table = body.getBody().getTableArray(curT);

                List<XWPFTable> tables = body.getBody().getTables();
                table = tables.get(curT);
                if (table != null) {
    
    

                    // 处理表格
                    List<XWPFTableCell> tableCells = table.getRows().get(0).getTableCells();
                    // 获取到模板表格第一行,用来判断表格类型
                    String tableText = table.getText();
                    // 表格中的所有文本

                    if (tableText.indexOf("##{foreach") > -1) {
    
    
                        // 查找到##{foreach标签,该表格需要处理循环
                        if (tableCells.size() != 2
                                || tableCells.get(0).getText().indexOf("##{foreach") < 0
                                || tableCells.get(0).getText().trim().length() == 0) {
    
    
                            System.out.println("文档中第"
                                            + (curT + 1)
                                            + "个表格模板错误,模板表格第一行需要设置2个单元格,"
                                            + "第一个单元格存储表格类型(##{foreachTable}## 或者 ##{foreachTableRow}##),第二个单元格定义数据源。");
                            return;
                        }

                        String tableType = tableCells.get(0).getText();
                        String dataSource = tableCells.get(1).getText();
                        System.out.println("读取到数据源:"+dataSource);
                        if (!dataMap.containsKey(dataSource)) {
    
    
                            System.out.println("文档中第" + (curT + 1) + "个表格模板数据源缺失");
                            return;
                        }
                        @SuppressWarnings("unchecked")
                        List<Map<String, Object>> tableDataList = (List<Map<String, Object>>) dataMap
                                .get(dataSource);
                        if ("##{foreachTable}##".equals(tableType)) {
    
    
                            // System.out.println("循环生成表格");
                            addTableInDocFooter(table, tableDataList, parametersMap, 1);

                        } else if ("##{foreachTableRow}##".equals(tableType)) {
    
    
                            // System.out.println("循环生成表格内部的行");
                            addTableInDocFooter(table, tableDataList, parametersMap, 2);
                        }

                    } else if (tableText.indexOf("{") > -1) {
    
    
                        // 没有查找到##{foreach标签,查找到了普通替换数据的{}标签,该表格只需要简单替换
                        addTableInDocFooter(table, null, parametersMap, 3);
                    } else {
    
    
                        // 没有查找到任何标签,该表格是一个静态表格,仅需要复制一个即可。
                        addTableInDocFooter(table, null, null, 0);
                    }
                    curT++;

                }
            } else if (BodyElementType.PARAGRAPH.equals(body.getElementType())) {
    
    // 处理段落
                // System.out.println("获取到段落");
                XWPFParagraph ph = body.getBody().getParagraphArray(curP);
                if (ph != null) {
    
    
                    // htmlText = htmlText+readParagraphX(ph);
                    addParagraphInDocFooter(ph, null, parametersMap, 0);

                    curP++;
                }
            }

        }
        // 处理完毕模板,删除文本中的模板内容
        for (int a = 0; a < templateBodySize; a++) {
    
    
            document.removeBodyElement(0);
        }

    }








    /**
     * 根据 模板表格 和 数据list 在word文档末尾生成表格
     * @author Juveniless
     * @date 2017年12月6日 上午10:12:05
     * @param templateTable 模板表格
     * @param list   循环数据集
     * @param parametersMap  不循环数据集
     * @param flag   (0为静态表格,1为表格整体循环,2为表格内部行循环,3为表格不循环仅简单替换标签即可)
     *
     */
    public void addTableInDocFooter(XWPFTable templateTable, List<Map<String, Object>> list,
                                    Map<String, Object> parametersMap, int flag) {
    
    

        if (flag == 1) {
    
    // 表格整体循环
            for (Map<String, Object> map : list) {
    
    
                List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行
                XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列
                for (int i = 1; i < templateTableRows.size(); i++) {
    
    
                    XWPFTableRow newCreateRow = newCreateTable.createRow();
                    CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行
                }
                newCreateTable.removeRow(0);// 移除多出来的第一行
                document.createParagraph();// 添加回车换行
                replaceTable(newCreateTable, map);//替换标签
            }

        } else if (flag == 2) {
    
    // 表格表格内部行循环
            XWPFTable newCreateTable = document.createTable();
            // 创建新表格,默认一行一列
            List<XWPFTableRow> TempTableRows = templateTable.getRows();
            // 获取模板表格所有行
            int tagRowsIndex = 0;// 标签行indexs
            for (int i = 0, size = TempTableRows.size(); i < size; i++) {
    
    
                String rowText = TempTableRows.get(i).getCell(0).getText();
                // 获取到表格行的第一个单元格
                if (rowText.indexOf("##{foreachRows}##") > -1) {
    
    
                    tagRowsIndex = i;
                    break;
                }
            }

            /* 复制模板行和标签行之前的行 */
            for (int i = 1; i < tagRowsIndex; i++) {
    
    
                XWPFTableRow newCreateRow = newCreateTable.createRow();
                CopyTableRow(newCreateRow, TempTableRows.get(i));
                // 复制行
                replaceTableRow(newCreateRow, parametersMap);
                // 处理不循环标签的替换
            }

            /* 循环生成模板行 */
            XWPFTableRow tempRow = TempTableRows.get(tagRowsIndex + 1);
            // 获取到模板行
            for (int i = 0; i < list.size(); i++) {
    
    
                XWPFTableRow newCreateRow = newCreateTable.createRow();
                CopyTableRow(newCreateRow, tempRow);
                // 复制模板行
                replaceTableRow(newCreateRow, list.get(i));
                // 处理标签替换
            }

            /* 复制模板行和标签行之后的行 */
            for (int i = tagRowsIndex + 2; i < TempTableRows.size(); i++) {
    
    
                XWPFTableRow newCreateRow = newCreateTable.createRow();
                CopyTableRow(newCreateRow, TempTableRows.get(i));
                // 复制行
                replaceTableRow(newCreateRow, parametersMap);
                // 处理不循环标签的替换
            }
            newCreateTable.removeRow(0);
            // 移除多出来的第一行
            document.createParagraph();
            // 添加回车换行

        } else if (flag == 3) {
    
    
            //表格不循环仅简单替换标签
            List<XWPFTableRow> templateTableRows = templateTable.getRows();
            // 获取模板表格所有行
            XWPFTable newCreateTable = document.createTable();
            // 创建新表格,默认一行一列
            for (int i = 0; i < templateTableRows.size(); i++) {
    
    
                XWPFTableRow newCreateRow = newCreateTable.createRow();
                CopyTableRow(newCreateRow, templateTableRows.get(i));
                // 复制模板行文本和样式到新行
            }
            newCreateTable.removeRow(0);
            // 移除多出来的第一行
            document.createParagraph();
            // 添加回车换行
            replaceTable(newCreateTable, parametersMap);

        } else if (flag == 0) {
    
    
            List<XWPFTableRow> templateTableRows = templateTable.getRows();
            // 获取模板表格所有行
            XWPFTable newCreateTable = document.createTable();
            // 创建新表格,默认一行一列
            for (int i = 0; i < templateTableRows.size(); i++) {
    
    
                XWPFTableRow newCreateRow = newCreateTable.createRow();
                CopyTableRow(newCreateRow, templateTableRows.get(i));
                // 复制模板行文本和样式到新行
            }
            newCreateTable.removeRow(0);
            // 移除多出来的第一行
            document.createParagraph();
            // 添加回车换行
        }

    }






    /**
     * 根据 模板段落 和 数据 在文档末尾生成段落
     *
     * @author Juveniless
     * @date 2017年11月27日 上午11:49:42
     * @param templateParagraph
     *            模板段落
     * @param list
     *            循环数据集
     * @param parametersMap
     *            不循环数据集
     * @param flag
     *            (0为不循环替换,1为循环替换)
     *
     */
    public void addParagraphInDocFooter(XWPFParagraph templateParagraph,
                                        List<Map<String, String>> list, Map<String, Object> parametersMap, int flag) {
    
    

        if (flag == 0) {
    
    
            XWPFParagraph createParagraph = document.createParagraph();
            // 设置段落样式
            createParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());
            // 移除原始内容
            for (int pos = 0; pos < createParagraph.getRuns().size(); pos++) {
    
    
                createParagraph.removeRun(pos);
            }
            // 添加Run标签
            for (XWPFRun s : templateParagraph.getRuns()) {
    
    
                XWPFRun targetrun = createParagraph.createRun();
                CopyRun(targetrun, s);
            }

            replaceParagraph(createParagraph, parametersMap);

        } else if (flag == 1) {
    
    
            // 暂无实现
        }

    }




    /**
     * 根据map替换段落元素内的{**}标签
     * @author Juveniless
     * @date 2017年12月4日 下午3:09:00
     * @param xWPFParagraph
     * @param parametersMap
     *
     */
    public void replaceParagraph(XWPFParagraph xWPFParagraph, Map<String, Object> parametersMap) {
    
    
        List<XWPFRun> runs = xWPFParagraph.getRuns();
        String xWPFParagraphText = xWPFParagraph.getText();
        String regEx = "\\{.+?\\}";
        Pattern pattern = Pattern.compile(regEx);
        Matcher matcher = pattern.matcher(xWPFParagraphText);//正则匹配字符串{****}

        if (matcher.find()) {
    
    
            // 查找到有标签才执行替换
            int beginRunIndex = xWPFParagraph.searchText("{", new PositionInParagraph()).getBeginRun();// 标签开始run位置
            int endRunIndex = xWPFParagraph.searchText("}", new PositionInParagraph()).getEndRun();// 结束标签
            StringBuffer key = new StringBuffer();

            if (beginRunIndex == endRunIndex) {
    
    
                // {**}在一个run标签内
                XWPFRun beginRun = runs.get(beginRunIndex);
                String beginRunText = beginRun.text();

                int beginIndex = beginRunText.indexOf("{");
                int endIndex = beginRunText.indexOf("}");
                int length = beginRunText.length();

                if (beginIndex == 0 && endIndex == length - 1) {
    
    
                    // 该run标签只有{**}
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    key.append(beginRunText.substring(1, endIndex));
                    insertNewRun.setText(getValueBykey(key.toString(),parametersMap));
                    xWPFParagraph.removeRun(beginRunIndex + 1);
                } else {
    
    
                    // 该run标签为**{**}** 或者 **{**} 或者{**}**,替换key后,还需要加上原始key前后的文本
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    key.append(beginRunText.substring(beginRunText.indexOf("{")+1, beginRunText.indexOf("}")));
                    String textString=beginRunText.substring(0, beginIndex) + getValueBykey(key.toString(),parametersMap)
                            + beginRunText.substring(endIndex + 1);
                    insertNewRun.setText(textString);
                    xWPFParagraph.removeRun(beginRunIndex + 1);
                }

            }else {
    
    
                // {**}被分成多个run

                //先处理起始run标签,取得第一个{key}值
                XWPFRun beginRun = runs.get(beginRunIndex);
                String beginRunText = beginRun.text();
                int beginIndex = beginRunText.indexOf("{");
                if (beginRunText.length()>1  ) {
    
    
                    key.append(beginRunText.substring(beginIndex+1));
                }
                ArrayList<Integer> removeRunList = new ArrayList();//需要移除的run
                //处理中间的run
                for (int i = beginRunIndex + 1; i < endRunIndex; i++) {
    
    
                    XWPFRun run = runs.get(i);
                    String runText = run.text();
                    key.append(runText);
                    removeRunList.add(i);
                }

                // 获取endRun中的key值
                XWPFRun endRun = runs.get(endRunIndex);
                String endRunText = endRun.text();
                int endIndex = endRunText.indexOf("}");
                //run中**}或者**}**
                if (endRunText.length()>1 && endIndex!=0) {
    
    
                    key.append(endRunText.substring(0,endIndex));
                }



                //*******************************************************************
                //取得key值后替换标签

                //先处理开始标签
                if (beginRunText.length()==2 ) {
    
    
                    // run标签内文本{
    
    
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    insertNewRun.setText(getValueBykey(key.toString(),parametersMap));
                    xWPFParagraph.removeRun(beginRunIndex + 1);
                    //移除原始的run
                }else {
    
    
                    // 该run标签为**{**或者 {** ,替换key后,还需要加上原始key前的文本
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex);
                    insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
                    // 设置文本
                    String textString=beginRunText.substring(0,beginRunText.indexOf("{"))+getValueBykey(key.toString(),parametersMap);
                    insertNewRun.setText(textString);
                    xWPFParagraph.removeRun(beginRunIndex + 1);
                    //移除原始的run
                }

                //处理结束标签
                if (endRunText.length()==1 ) {
    
    
                    // run标签内文本只有}
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex);
                    insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());
                    // 设置文本
                    insertNewRun.setText("");
                    xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run

                }else {
    
    
                    // 该run标签为**}**或者 }** 或者**},替换key后,还需要加上原始key后的文本
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex);
                    insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());
                    // 设置文本
                    String textString=endRunText.substring(endRunText.indexOf("}")+1);
                    insertNewRun.setText(textString);
                    xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run
                }

                //处理中间的run标签
                for (int i = 0; i < removeRunList.size(); i++) {
    
    
                    XWPFRun xWPFRun = runs.get(removeRunList.get(i));//原始run
                    XWPFRun insertNewRun = xWPFParagraph.insertNewRun(removeRunList.get(i));
                    insertNewRun.getCTR().setRPr(xWPFRun.getCTR().getRPr());
                    insertNewRun.setText("");
                    xWPFParagraph.removeRun(removeRunList.get(i) + 1);//移除原始的run
                }

            }// 处理${**}被分成多个run

            replaceParagraph( xWPFParagraph, parametersMap);

        }//if 有标签

    }








    /**
     * 复制表格行XWPFTableRow格式
     *
     * @param target
     *            待修改格式的XWPFTableRow
     * @param source
     *            模板XWPFTableRow
     */
    private void CopyTableRow(XWPFTableRow target, XWPFTableRow source) {
    
    

        int tempRowCellsize = source.getTableCells().size();// 模板行的列数
        for (int i = 0; i < tempRowCellsize - 1; i++) {
    
    
            target.addNewTableCell();// 为新添加的行添加与模板表格对应行行相同个数的单元格
        }
        // 复制样式
        target.getCtRow().setTrPr(source.getCtRow().getTrPr());
        // 复制单元格
        for (int i = 0; i < target.getTableCells().size(); i++) {
    
    
            copyTableCell(target.getCell(i), source.getCell(i));
        }
    }





    /**
     * 复制单元格XWPFTableCell格式
     *
     * @author Juveniless
     * @date 2017年11月27日 下午3:41:02
     * @param newTableCell
     *            新创建的的单元格
     * @param templateTableCell
     *            模板单元格
     *
     */
    private void copyTableCell(XWPFTableCell newTableCell, XWPFTableCell templateTableCell) {
    
    
        // 列属性
        newTableCell.getCTTc().setTcPr(templateTableCell.getCTTc().getTcPr());
        // 删除目标 targetCell 所有文本段落
        for (int pos = 0; pos < newTableCell.getParagraphs().size(); pos++) {
    
    
            newTableCell.removeParagraph(pos);
        }
        // 添加新文本段落
        for (XWPFParagraph sp : templateTableCell.getParagraphs()) {
    
    
            XWPFParagraph targetP = newTableCell.addParagraph();
            copyParagraph(targetP, sp);
        }
    }

    /**
     * 复制文本段落XWPFParagraph格式
     *
     * @author Juveniless
     * @date 2017年11月27日 下午3:43:08
     * @param newParagraph
     *            新创建的的段落
     * @param templateParagraph
     *            模板段落
     *
     */
    private void copyParagraph(XWPFParagraph newParagraph, XWPFParagraph templateParagraph) {
    
    
        // 设置段落样式
        newParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());
        // 添加Run标签
        for (int pos = 0; pos < newParagraph.getRuns().size(); pos++) {
    
    
            newParagraph.removeRun(pos);

        }
        for (XWPFRun s : templateParagraph.getRuns()) {
    
    
            XWPFRun targetrun = newParagraph.createRun();
            CopyRun(targetrun, s);
        }

    }

    /**
     * 复制文本节点run
     * @author Juveniless
     * @date 2017年11月27日 下午3:47:17
     * @param newRun
     *            新创建的的文本节点
     * @param templateRun
     *            模板文本节点
     *
     */
    private void CopyRun(XWPFRun newRun, XWPFRun templateRun) {
    
    
        newRun.getCTR().setRPr(templateRun.getCTR().getRPr());
        // 设置文本
        newRun.setText(templateRun.text());


    }




    /**
     * 根据参数parametersMap对表格的一行进行标签的替换
     *
     * @author Juveniless
     * @date 2017年11月23日 下午2:09:24
     * @param tableRow
     *            表格行
     * @param parametersMap
     *            参数map
     *
     */
    public void replaceTableRow(XWPFTableRow tableRow, Map<String, Object> parametersMap) {
    
    

        List<XWPFTableCell> tableCells = tableRow.getTableCells();
        for (XWPFTableCell xWPFTableCell : tableCells) {
    
    
            List<XWPFParagraph> paragraphs = xWPFTableCell.getParagraphs();
            for (XWPFParagraph xwpfParagraph : paragraphs) {
    
    

                replaceParagraph(xwpfParagraph, parametersMap);
            }
        }

    }

    /**
     * 根据map替换表格中的{key}标签
     * @author Juveniless
     * @date 2017年12月4日 下午2:47:36
     * @param xwpfTable
     * @param parametersMap
     *
     */
    public void replaceTable(XWPFTable xwpfTable,Map<String, Object> parametersMap){
    
    
        List<XWPFTableRow> rows = xwpfTable.getRows();
        for (XWPFTableRow xWPFTableRow : rows ) {
    
    
            List<XWPFTableCell> tableCells = xWPFTableRow.getTableCells();
            for (XWPFTableCell xWPFTableCell : tableCells ) {
    
    
                List<XWPFParagraph> paragraphs2 = xWPFTableCell.getParagraphs();
                for (XWPFParagraph xWPFParagraph : paragraphs2) {
    
    
                    replaceParagraph(xWPFParagraph, parametersMap);
                }
            }
        }

    }

    private String getValueBykey(String key, Map<String, Object> map) {
    
    
        String returnValue="";
        if (key != null) {
    
    
            try {
    
    
                returnValue=map.get(key)!=null ? map.get(key).toString() : "";
            } catch (Exception e) {
    
    
                // TODO: handle exception
                System.out.println("key:"+key+"***"+e);
                returnValue="";
            }

        }
        return returnValue;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_51945027/article/details/121632713