近期碰到一个稍微头疼的需求,将word模版中的参数替换为实际值,其中包括段落、列表(行数不够时自动递增)、页眉;本文以docx文档为例,其中代码有其他地方参考,如有冒犯,还请海涵;

模版:

JAVA替换word标签 java替换word模板_List

JAVA替换word标签 java替换word模板_java_02

 

实现效果:

JAVA替换word标签 java替换word模板_Source_03

JAVA替换word标签 java替换word模板_JAVA替换word标签_04

 

模版替换规则(可自定义):

规则:
   a、类型 LIST--列表下拉;DIC--字典转为多选框;{字段名}--此为主表普通字段名;
   b、格式 {#数据源名(或字典名)##字段名##类型#}
   c、列表下拉 例 {#GZ##XZGX_XZGXLX_NAME##LIST##false#}--false 表示不换行,true表示换行
   d、字典转为多选框 例 {#HR_ZPGL_FBZT##RCK_HHZYY##DIC##false#}

注意事项:1、本文采用poi 4.0.x,具体原因,查看API http://poi.apache.org/help/faq.html

JAVA替换word标签 java替换word模板_List_05

2、模版中的{#YESORNO##YSSQB_ZSSQTY_CODE##DIC##false#} 不要直接在word上书写,要从别的地方粘贴,这样不会出现解析在多个XWPFRun中的问题

以下为代码参考:

@Override
public InputStream expFile(DynaBean template, DynaBean dataBean, Map dataSource, JSONObject returnObj) {


    String fileKey="";
    if(StringUtil.isNotEmpty(template.getStr("OFFICETEM_FILE"))){
        fileKey=template.getStr("OFFICETEM_FILE").split("\\*")[1];
    }
    if(StringUtil.isEmpty(fileKey)){
        returnObj.put("errorMsg","未上传模版文件!");
        return null;
    }
    FileBO downloadFileBO=documentBusService.readFile(fileKey);
    if(downloadFileBO==null){
        returnObj.put("errorMsg","模版文件未找到!");
        return null;
    }
    //文件名
    String fileName = template.getStr("OFFICETEM_NAME")+ DateUtils.formatDate(new Date(),"yyyyMMdd")+".docx";
    returnObj.put("fileName",fileName);
    InputStream downloadFile=downloadFileBO.getFile();
    try {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        XWPFDocument doc = new XWPFDocument(downloadFile);
        //替换页眉中的变量
        this.replaceHeaderOverWrite(doc,dataBean,dataSource);
        //替换段落里面的变量
        this.replaceInParaOverWrite(doc,dataBean,dataSource);
        //替换表格里面的变量
        this.replaceInTableOverWrite(doc,dataBean,dataSource);
        doc.write(outputStream);
        doc.close();
        downloadFile.close();
        //输出转输入
        ByteArrayInputStream inputStream = IoUtil.toStream(outputStream.toByteArray());
        return inputStream;
    } catch (Exception e) {
        e.printStackTrace();
        returnObj.put("errorMsg","模版文件未找到!");
        
    }
}


/***
 *功能描述
 * 替换页眉中的内容
 * @author wenzhe.zhou
 * @date 2020/5/25
 * @param doc
 * @param dynaBean
 * @param dataSource
 * @return void
 */
private void replaceHeaderOverWrite(XWPFDocument doc,DynaBean dynaBean,Map dataSource) throws Exception {
    List<XWPFHeader> xwpfHeaderList = doc.getHeaderList();
    Iterator iterator = xwpfHeaderList.iterator();
    XWPFParagraph para;
    XWPFHeader xwpfHeader;
    while (iterator.hasNext()) {
        xwpfHeader = (XWPFHeader) iterator.next();
        List<XWPFParagraph> xwpfParagraphList = xwpfHeader.getParagraphs();
        Iterator iteratorPara = xwpfParagraphList.iterator();
        while (iteratorPara.hasNext()){
            para = (XWPFParagraph) iteratorPara.next();
            this.replaceInParaOverWrite(para,dynaBean,dataSource);
        }
    }
}

/***
 *功能描述
 * 替换段落里面的变量
 * @author wenzhe.zhou
 * @date 2020/5/9
 * @param doc  要替换的文档
 * @param dynaBean 数据源
 * @param dataSource
 * @return void
 */
private void replaceInParaOverWrite(XWPFDocument doc,DynaBean dynaBean,Map dataSource) throws Exception {
    Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
    XWPFParagraph para;
    while (iterator.hasNext()) {
        para = iterator.next();
        this.replaceInParaOverWrite(para,dynaBean,dataSource);
    }
}


/**
 *功能描述
 * 替换段落里面的变量
 * @author wenzhe.zhou
 * @date 2020/5/9
 * @param para
 * @param dynaBean
 * @param dataSource
 * @return void
 */
private void replaceInParaOverWrite(XWPFParagraph para,DynaBean dynaBean,Map dataSource) throws Exception {
    List<XWPFRun> runs;
    Boolean matcher;
    if (this.matcher(para.getParagraphText())) {
        runs = para.getRuns();
        for (int i=0; i<runs.size(); i++) {
            XWPFRun run = runs.get(i);
            int fontSize = run.getFontSize();
            String fontFamily = run.getFontFamily();
            String runText = run.toString().trim();
            Boolean isDic =false;
            matcher = this.matcher(runText);
            if (matcher|| ObjectUtil.isNotNull(dynaBean.get(runText))) {
                if ((this.matcher(runText))) {
                    //普通字段
                    if (!runText.contains("ONE")&&!runText.contains("LIST")&&!runText.contains("DIC")){
                        runText = runText.replaceAll("\\{","").replaceAll("}","").replaceAll("#","");
                        if (Objects.equals(runText.trim(),"")){
                            runText ="";
                        }else {
                            runText = getRangeData( dynaBean,runText.trim(),dataSource);
                        }
                    } else if (runText.contains("DIC")){
                        //字典
                        isDic = true;
                    }
                    else {
                        runText ="";
                    }
                }else {
                    runText = getRangeData( dynaBean,runText.trim(),dataSource);
                }
                //直接调用XWPFRun的setText()方法设置文本时,在底层会重新创建一个XWPFRun,把文本附加在当前文本后面,
                //所以我们不能直接设值,需要先删除当前run,然后再自己手动插入一个新的run。
                para.removeRun(i);
                XWPFRun newRun=para.insertNewRun(i);
                //字典
                if (isDic){
                    List<String> valueList = substringData(runText);
                    String dicCode =valueList.get(0);
                    String paramValue = valueList.get(1);
                    //是否换行
                    Boolean isBreak = false;
                    if (valueList.size()>3){
                        isBreak= ObjectUtil.isNotEmpty(valueList.get(3))?Boolean.valueOf(valueList.get(3)):false;
                    }
                    paramValue = getRangeData( dynaBean,paramValue.trim(),dataSource);
                    //设置字典
                    this.setCheckBox(dicCode,paramValue,newRun,isBreak);
                }else {
                    newRun.setText(runText);
                }
                //设置字体大小、格式
                if (ObjectUtil.isNotNull(fontFamily)){
                    newRun.setFontFamily(fontFamily);
                }
                if (fontSize>=0){
                    newRun.setFontSize(fontSize);
                }
            }
        }
    }
}


/***
 *功能描述
 * 替换表格里面的变量
 * @author wenzhe.zhou
 * @date 2020/5/9
 * @param doc
 * @param dynaBean
 * @param dataSource
 * @return void
 */
private void replaceInTableOverWrite(XWPFDocument doc,DynaBean dynaBean,Map dataSource) throws Exception {
    //模版table
    Iterator<XWPFTable> iterator = doc.getTablesIterator();
    XWPFTable table;
    List<XWPFTableRow> rows;
    List<XWPFTableCell> cells;
    List<XWPFParagraph> paras;
    //循环所有的文本进行添加定位
    while (iterator.hasNext()) {
        List<Map<String,Object>> mapList = new ArrayList<>(16);
        //要删除的row
        List<Integer> removeStringList = new ArrayList<>(16);
        int rowIndex =0;
        table = iterator.next();
        rows = table.getRows();
        Iterator iteratorRow = rows.iterator();
        if (CollectionUtil.isNotEmpty(dataSource)){
            while (iteratorRow.hasNext()){
                //是否添加
                Boolean isInsert = false;

                XWPFTableRow row = (XWPFTableRow) iteratorRow.next();
                cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    paras = cell.getParagraphs();
                    for (XWPFParagraph para : paras) {
                        List<XWPFRun> runs = para.getRuns();
                        for (int i=0; i<runs.size(); i++) {
                            XWPFRun run = runs.get(i);
                            String runText = run.toString().replaceAll("\\{","").replaceAll("}","");
                            //此表格为下拉框时
                            if (runText.contains("LIST")){
                                List<String> codeList=substringData(runText);
                                //数据源编码
                                String  funcCode = codeList.get(0);
                                //此为表格下拉框时
                                //数据源
                                List<Object> objects = (List<Object>) dataSource.get(funcCode);
                                if (CollectionUtil.isNotEmpty(objects)&&!isInsert){
                                    for (int j=0;j<objects.size();j++){
                                        Map<String,Object> mapData  = new HashMap<>(16);
                                        mapData.put("data",objects.get(j));
                                        mapData.put("table",table);
                                        mapData.put("row",row);
                                        mapData.put("rowIndex",rowIndex);
                                        mapList.add(mapData);
                                        rowIndex++;
                                    }
                                    isInsert = true;
                                    removeStringList.add(rowIndex);
                                }
                            }
                        }
                    }
                }
                rowIndex++;
            }
        }

        //添加row 并替换内容
        if (CollectionUtil.isNotEmpty(mapList)){
            for (Map map:mapList){
                Map<String,Object> data = (Map<String, Object>) map.get("data");
                XWPFTable xwpfTable = (XWPFTable) map.get("table");
                XWPFTableRow row = (XWPFTableRow) map.get("row");
                int rowNum = (int) map.get("rowIndex");
                copyRowOverWrite(xwpfTable,row,rowNum,data);
            }
        }
        //删除row--删除用来标记的row
        if (CollectionUtil.isNotEmpty(removeStringList)){
            int afterRemoveNumber = 0;
            for (Integer removeIndex:removeStringList){
                table.removeRow(removeIndex-afterRemoveNumber);
                afterRemoveNumber++;
            }
        }
        //循环所有的文本进行替换
        List<XWPFTableRow> newrows = table.getRows();
        //遍历表格,替换模版
        for (XWPFTableRow row : newrows) {
            cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                paras = cell.getParagraphs();
                for (XWPFParagraph para : paras) {
                    this.replaceInParaOverWrite(para,dynaBean,dataSource);
                }
            }
        }
    }
}



/*****
 *功能描述
 * 复制行
 * @author wenzhe.zhou
 * @date 2020/5/15
 * @param table
 * @param sourceRow
 * @param rowIndex
 * @param data
 * @return void
 */
public void copyRowOverWrite(XWPFTable table,XWPFTableRow sourceRow,int rowIndex,Map<String,Object> data) throws Exception {
    //在表格指定位置新增一行
    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) {
        Boolean isDic = false;
        //字段名
        String key ="";
        //数据源名或者字典名
        String dataSourceName = "";
        //字典是否换行
        Boolean isBreak =false;
        String runText = sourceCell.getText().replaceAll("\\{","").replaceAll("}","");
        if (runText.contains("DIC")){
            isDic = true;
        }
        List<String> valueList = substringData(runText);
        if (CollectionUtil.isNotEmpty(valueList)){
            dataSourceName = valueList.get(0);
            key = valueList.get(1);
            if (valueList.size()>3){
                isBreak = ObjectUtil.isNotEmpty(valueList.get(3))?Boolean.valueOf(valueList.get(3)):false;
            }
        }
        targetCell = targetRow.addNewTableCell();

        //列属性
        targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
        //段落属性
        if(sourceCell.getParagraphs()!=null&&sourceCell.getParagraphs().size()>0){
            targetCell.getParagraphs().get(0).getCTP().setPPr(sourceCell.getParagraphs().get(0).getCTP().getPPr());
            if(sourceCell.getParagraphs().get(0).getRuns()!=null&&sourceCell.getParagraphs().get(0).getRuns().size()>0){
                XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
                //字典
                if (isDic){
                    //数据源中的值
                    String paramValue = ObjectUtil.isNotEmpty(data.get(key))?data.get(key).toString():"";
                    //字典
                    this.setCheckBox(dataSourceName,paramValue,cellR,isBreak);
                    cellR.setBold(sourceCell.getParagraphs().get(0).getRuns().get(0).isBold());
                }else {
                    cellR.setText(ObjectUtil.isNotEmpty(data.get(key))?data.get(key).toString():"");
                    cellR.setBold(sourceCell.getParagraphs().get(0).getRuns().get(0).isBold());
                }
            }else{
                targetCell.setText(ObjectUtil.isNotEmpty(data.get(key))?data.get(key).toString():"");
            }
        }else{
            targetCell.setText(ObjectUtil.isNotEmpty(data.get(key))?data.get(key).toString():"");
        }
    }
}
/***
 *功能描述
 * @author wenzhe.zhou
 * @date 2020/5/22
 * @param dicCode 字典CODE
 * @param paramValue 此字典项对应得值
 * @param newRun 当前的 XWPFRun
 * @return void
 */
public void setCheckBox(String dicCode,String paramValue,XWPFRun newRun,Boolean isBreak) throws Exception {
    List<CTSym> ctSymList=newRun.getCTR().getSymList();
    List<Map<String,Object>> runTexts = getDicDataText(dicCode);
    for (Map<String,Object> map:runTexts){
        String str = (String) map.get("text");
        String val = (String) map.get("code");
        String charStr ="00A3";
        try{
            //判断选中
            if (paramValue.contains(val)){
                charStr ="0052";
            }
            CTSym ctSym=getCTSym("Wingdings 2",charStr);
            ctSymList.add(ctSym);
        }catch (Exception e){
            throw e;
        }

        newRun.setText(str+"  ");
        if (isBreak){
            newRun.addBreak();
        }
    }
}


/****
 *功能描述
 * 获取字典值
 * @author wenzhe.zhou
 * @date 2020/5/20
 * @param dicName
 * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
 */
private List<Map<String,Object>> getDicDataText(String dicName) {
    List<Map<String,Object>> resultList = new ArrayList<>(16);
    //获取此数据字典
    List<DictionaryItemVo> dicList=dictionaryManager.getDicList(dicName,new QueryInfo(),false);
    if (CollectionUtil.isNotEmpty(dicList)){
        for (DictionaryItemVo dictionaryItemVo:dicList){
            Map<String,Object> map = new HashMap<>(16);
            String text = dictionaryItemVo.getText();
            String code =dictionaryItemVo.getCode();
            map.put("text",text);
            map.put("code",code);
            resultList.add(map);
        }
    }
    return resultList;
}


/**
 *功能描述
 * 截取字符串
 * "#表##值##类型#"
 * @author wenzhe.zhou
 * @date 2020/5/20
 * @param text
 * @return java.lang.String
 */
public List<String> substringData(String text){
    String regex = "#([^#]+)#";
    List<String> paramList = new ArrayList<>(16);
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        paramList.add(matcher.group(1));
    }
    return paramList;
}

/***
 *功能描述
 *添加特殊字符的xml
 * @author wenzhe.zhou
 * @date 2020/5/20
 * @param wingType
 * @param charStr
 * @return org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSym
 */
public CTSym getCTSym(String wingType, String charStr) throws Exception {
    CTSym sym = CTSym.Factory
            .parse("<xml-fragment w:font=\""
                    + wingType
                    + "\" w:char=\""
                    + charStr
                    + "\" xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:wne=\"http://schemas.microsoft.com/office/word/2006/wordml\"> </xml-fragment>");
    return sym;
}



/***
 *功能描述
 * 验证格式
 * @author wenzhe.zhou
 * @date 2020/5/9
 * @param str
 * @return java.lang.Boolean
 */
private Boolean matcher(String str) {
    return str.contains("{")||str.contains("}");
}

/***
 *功能描述
 * 获取要填充的数据
 * @author wenzhe.zhou
 * @date 2020/5/9
 * @param dataBean
 * @param key
 * @param dataSource
 * @return java.lang.String
 */
private String getRangeData(DynaBean dataBean,String key,Map dataSource){
   //自己实现数据逻辑
    return “”;
}