根据模板生成word文件

来源:互联网 发布:黄金白银行情软件 编辑:程序博客网 时间:2024/05/22 15:08

最近一个项目,需要根据word模板,写入相应的数据,生成新的文件。运用最基础的poi写word文件。本次项目遇到了多种形式的word内容,
第一:基础的paragraph内容;
第二:基于表格的paragraph内容(固定表格);
第三:list表格(动态表格);
如下图:
这里写图片描述
前两个的核心思想都是运用正则查找到需要写入数据的地方(用${XXX}的方式标识),然后调用XWPFRun的setText()方法设置文本,第三个重点在于手动添加行。下面附上各个部分的代码:

以流的形式读取模板文件

path = request.getSession().getServletContext()                .getRealPath(".../XXX.docx");FileInputStream is = null;is = new FileInputStream(new File(path));XWPFDocument doc = new XWPFDocument(is);

获取需要写入模板的数据
需要写入的数据有两种形式的,一种是map,一种是list,map很简单,key对应模板中${}里面的内容即可,list我是采用List

   // 获取paragraph的数据   Map<String, Object> paraData = getParaData();   // 获取table的数据   List<String[]> tblData = getTblData();

替换段落里面的变量

this.replaceInPara(doc, paraData);
private void replaceInPara(XWPFDocument doc, Map<String, Object> paramData) {        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();        XWPFParagraph para;        while (iterator.hasNext()) {            para = iterator.next();            this.replaceInPara(para, paramData);        }    }private void replaceInPara(XWPFParagraph para, Map<String, Object> paramData) {    List<XWPFRun> runs;    Matcher matcher;    Map<String, Object> mapAttr = new HashMap<String, Object>();    if (this.matcher(para.getParagraphText()).find()) {        runs = para.getRuns();        for (int i = 0; i < runs.size(); i++) {            XWPFRun run = runs.get(i);            String runText = run.toString();            matcher = this.matcher(runText);            if (matcher.find()) {                while ((matcher = this.matcher(runText)).find()) {                    mapAttr = getWordXWPFRunStyle(run);                    runText = matcher.replaceFirst(String.valueOf(paramData                            .get(matcher.group(1))));                }                // 直接调用XWPFRun的setText()方法设置文本时,在底层会重新创建一个XWPFRun,把文本附加在当前文本后面,                // 所以我们不能直接设值,需要先删除当前run,然后再自己手动插入一个新的run。                para.removeRun(i);                XWPFRun runNew = para.insertNewRun(i);                setWordXWPFRunStyle(runNew, mapAttr, runText);            }        }    }}private Map<String, Object> getWordXWPFRunStyle(XWPFRun runOld) {    Map<String, Object> mapAttr = new HashMap<String, Object>();    mapAttr.put("Color", runOld.getColor());    if (-1 == runOld.getFontSize()) {        mapAttr.put("FontSize", 10);    } else {        mapAttr.put("FontSize", runOld.getFontSize());    }    mapAttr.put("Subscript", runOld.getSubscript());    mapAttr.put("Underline", runOld.getUnderline());    mapAttr.put("FontFamily", runOld.getFontFamily());    mapAttr.put("Bold", runOld.isBold());    mapAttr.put("Italic", runOld.isItalic());    return mapAttr;}private XWPFRun setWordXWPFRunStyle(XWPFRun runNew,        Map<String, Object> mapAttr, String text) {    runNew.setColor((String) mapAttr.get("Color"));    if ("-1".equals(mapAttr.get("FontSize").toString())) {        // 五号字只能处理为10磅,无法设置为10.5磅        runNew.setFontSize(10);    } else {        runNew.setFontSize((Integer) mapAttr.get("FontSize"));    }    runNew.setBold((boolean) mapAttr.get("Bold"));    runNew.setItalic((boolean) mapAttr.get("Italic"));    runNew.setUnderline((UnderlinePatterns) mapAttr.get("Underline"));    runNew.setText(text);    runNew.setSubscript((VerticalAlign) mapAttr.get("Subscript"));    runNew.setFontFamily((String) mapAttr.get("FontFamily"));    return runNew;}private Matcher matcher(String str) {    Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}",            Pattern.CASE_INSENSITIVE);    Matcher matcher = pattern.matcher(str);    return matcher;}

此处的难点在于需要保留模板当中的字体格式,用了两个方法做了这件事情mapAttr = getWordXWPFRunStyle(run);先获取,放到map中,然后再通过setWordXWPFRunStyle(runNew, mapAttr, runText)set进去,但是如果字号设置是五号字,而不是pt的话就会出现异常值-1,需要处理,但是又无法设置10.5磅(五号字),所以这边是有小问题的,不知道是否还有更好的办法。

往表格里面写入数据

this.replaceInTable(doc, tblData, paraData);
private void replaceInTable(XWPFDocument doc, List<String[]> listData, Map paraData) {        Iterator<XWPFTable> iterator = doc.getTablesIterator();        XWPFTable table;        List<XWPFTableRow> rows;        List<XWPFTableCell> cells;        List<XWPFParagraph> paras;        while (iterator.hasNext()) {            table = iterator.next();            rows = table.getRows();            if (this.matcher(table.getText()).find()) {                // 模板固定表格赋值(根据${}匹配赋值)                for (XWPFTableRow row : rows) {                    cells = row.getTableCells();                    for (XWPFTableCell cell : cells) {                        paras = cell.getParagraphs();                        for (XWPFParagraph para : paras) {                            this.replaceInPara(para, paraData);                        }                    }                }            } else {                // 模板LIST表格赋值(需手动增加行)                this.replaceInTbl(table, listData);            }        }    }private void replaceInTbl(XWPFTable table, List<String[]> tableList) {        // 创建行,根据需要插入的数据添加新行,不处理表头        for (int i = 1; i < tableList.size(); i++) {            XWPFTableRow targetRow = table.insertNewTableRow(i + 1);            targetRow.getCtRow().setTrPr(table.getRow(i).getCtRow().getTrPr());            List<XWPFTableCell> cellList = table.getRow(i).getTableCells();            XWPFTableCell targetCell = null;            for (XWPFTableCell sourceCell : cellList) {                targetCell = targetRow.addNewTableCell();                targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());                targetCell.getParagraphs().get(0).getCTP().setPPr(                                sourceCell.getParagraphs().get(0).getCTP().getPPr());            }        }        // 遍历表格插入数据        List<XWPFTableRow> rows = table.getRows();        for (int i = 1; i < rows.size(); i++) {            XWPFTableRow newRow = table.getRow(i);            List<XWPFTableCell> cells = newRow.getTableCells();            for (int j = 0; j < cells.size(); j++) {                XWPFTableCell cell = cells.get(j);                cell.setText(tableList.get(i - 1)[j]);            }        }        //如果合计行需要合并单元格的话        mergeCellsHorizontal(table, tableList.size(), 0, 2);    }    public void mergeCellsHorizontal(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);            }        }    }

这边就需要根据模板当中是否有配置${}来进行区分了,是固定表格还是需要手动增加行数的,如果是固定表格的话,只需要和paragraph一样赋值就行了,关键就是获取一个个单元格。而非固定表格,在增加行的时候遇到挺多坑的,有用过table.createRow(),但是创建之后是没有边框的;有用过table.addNewRowBetween(),但是最新的poi jar包根本没有实现这个方法QAQ,所以根本不起作用;有用过table.addRow(),空行是创建成功了,但是写数据的时候全部写进同一个row里面了。。最后查到了上面的方法,创建newRow,再创建newCell,然后复制模板行的每一个单元格的格式,最后根据需求是否合并单元格,针对表格当中居左居中还是居右没有做处理

最后输出流,关闭输入输出流

OutputStream os = new FileOutputStream("D:\\XXX.docx");doc.write(os);this.close(os);this.close(is);private void close(InputStream is) {    if (is != null) {       try {          is.close();       } catch (IOException e) {          e.printStackTrace();       }    }  } private void close(OutputStream os) {    if (os != null) {       try {          os.close();       } catch (IOException e) {          e.printStackTrace();       }    }  }  

———————————————– 完 ——————————————–

原创粉丝点击