根据模板生成word文件

最近一个项目,需要根据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();  
     }  
  }  
}  

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

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spire.Doc for Java是一款功能强大的Java Word组件,可以帮助Java开发者快速实现Word文档的创建、读取和编辑等操作。其中,根据模板生成Word文档是Spire.Doc for Java的一项重要功能,下面简单介绍一下它的使用方法。 1. 创建模板文档 首先,我们需要创建一个Word模板文档,其中包含需要替换的占位符和样式等信息。可以使用Microsoft Word等工具创建模板文档,并将其保存为doc或docx格式。 2. 加载模板文档 在Java代码中,使用Spire.Doc for Java的Document类加载模板文档,代码如下: ```java import com.spire.doc.*; public class LoadTemplate { public static void main(String[] args) { // 创建Document对象 Document doc = new Document(); // 加载模板文档 doc.loadFromFile("template.docx"); } } ``` 其中,loadFromFile方法的参数为模板文档的路径。 3. 替换占位符 接下来,我们需要根据实际需求替换模板文档中的占位符。Spire.Doc for Java提供了replace方法来实现占位符的替换,代码如下: ```java import com.spire.doc.*; public class ReplacePlaceholder { public static void main(String[] args) { // 创建Document对象 Document doc = new Document(); // 加载模板文档 doc.loadFromFile("template.docx"); // 替换占位符 doc.replace("{{name}}", "张三", true, true); doc.replace("{{age}}", "25", true, true); // 保存文档 doc.saveToFile("output.docx", FileFormat.Docx_2013); } } ``` 其中,replace方法的第一个参数为需要替换的占位符,第二个参数为替换后的文本内容,第三个参数表示是否区分大小写,第四个参数表示是否替换整个单词。 4. 设置样式 如果需要设置生成Word文档的样式,可以使用Spire.Doc for Java提供的各种样式类来实现,例如ParagraphStyle、CharacterFormat等。具体使用方法可以参考Spire.Doc for Java的官方文档。 5. 保存文档 最后,调用Document类的saveToFile方法将生成Word文档保存到指定路径,代码如下: ```java doc.saveToFile("output.docx", FileFormat.Docx_2013); ``` 其中,第一个参数为保存的文件路径,第二个参数为保存的文档格式,可以选择doc、docx、pdf等多种格式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值