根据模板生成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(); } } }
———————————————– 完 ——————————————–
- 根据模板生成word文件
- .net根据模板生成Word文件
- java根据模板生成word文件
- 根据模板生成word
- 使用 poi 根据 word 模板生成 word 文件
- 使用 poi 根据 word 模板生成 word 文件
- 根据模板生成word文件并导出保存到本地
- 根据模板生成word文件并导出保存到本地
- 根据Word模板生成动态网页
- jacob根据word模板生成文
- freemarker根据模板生成word文档,换行
- freemarker根据模板生成word步骤优化
- java根据ftl模板生成word文档
- 根据模板生成word文档下载
- Java根据xml模板生成word
- freemarker模板生成word文件
- asp 使用word模板生成word文件
- 根据word模板生成word表格报表文档(C#)
- 搭建 WordPress 个人博客(Ubuntu)
- 事件冒泡处理
- Hadoop安全模式理解
- Nginx && PHP 搭建配置管理
- VS2013调用动态链接库
- 根据模板生成word文件
- mysql left join的使用
- Fixing ImportError: cannot import name ‘urlencode’ in Python3
- git深入了解
- linux下查看已安装的软件与卸载
- 线性判别分析(LDA)实现二分类的思路
- cs231n学习笔记-CNN-目标检测、定位、分割
- logstash安装
- 文件或目录权限chmod 、更改所有者和所属组chow、umask、隐藏权限lsattr/chattr