poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录

文章目录

poi-tl 入门示例

可参照poi-tl 官方api文档:poi-tl
引入maven 后

							<dependency>
							  <groupId>com.deepoove</groupId>
							  <artifactId>poi-tl</artifactId>
							  <version>1.11.0</version>
							</dependency>

1.准备word模板

《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

2.代码

  public class PoiTest { 
   public static void main(String[] args) throws IOException { 
        //要写入模板的数据
        Map<String,Object> exampleData = new HashMap<>();
        exampleData.put("username","admin");
        exampleData.put("password","123456");
        FileInputStream inputStream = new FileInputStream("D:/test_word/example1.docx");
        XWPFTemplate template = XWPFTemplate.compile(inputStream)
                .render(exampleData);
        //文件输出流
        FileOutputStream out = new FileOutputStream("D:/test_word/example1.docx");
        template.write(out);
        out.flush();
        out.close();
        template.close();
    }
}

自定义列表序号

1. poi-tl 支持的序号列表,直接使用文档种说明即可

《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

2. 要是没有,可以利用NumberingFormat类去生成指定的样式;例如生成 a) b)的序号

《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

3. 同样按照官方api指定{ {*var}}后

4. 具体代码如下

public class test { 


    public static void main(String[] args) throws IOException { 
        //要写入模板的数据
        Map<String, Object> datas1 = new HashMap<String, Object>();
        //列表集合
        List<String> varList = Arrays.asList("序号1",
                "序号2");

        //自定义序号的样式为 a) b) c)
        NumberingFormat.Builder builder = NumberingFormat.
                builder("%{0})") //%{0}) 可以指定自己需要的样式
                .withNumFmt(NumFormat.LOWER_LETTER);  //小写字母
        NumberingFormat numberingFormat = builder.build(0);
        Numberings.NumberingBuilder of = Numberings.of(numberingFormat);//a) b) c)
        //列表 数据赋值
        varList.forEach(s -> of.addItem(s));
        NumberingRenderData numberingRenderData = of.create();
        datas1.put("var", numberingRenderData);
        XWPFTemplate.compile("D:/test_word/test.docx")
                .render(datas1).writeToFile("D:/test_word/test1.docx");
    }
}

自定义表格宽度和表格合并

poi-tl导出的word表格默认宽度是自适应的,若是想要指定列的宽度增加,可以采用如下方式:
最终实现的word表格

《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

1. 在word模板文件中,生成表格位置添加标签:{ {#check_table}}

2. 具体整体代码如下

 public class test { 


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

        Map<String, Object> datas = new HashMap<String, Object>();
        // create table
        //word的 表格
        List tableList = new ArrayList<>();
        tableList.add("数据1");
        tableList.add("数据2");
        tableList.add("数据3");
        tableList.add("数据4");
        tableList.add("数据5");
        tableList.add("数据6");
        //验证类型集合
        List<String> typeList = Arrays.asList("MC0",
                "MC1",
                "MC2",
                "MC3",
                "MC4",
                "MC5",
                "MC6",
                "MC7",
                "MC8",
                "MC9");
        //表头是两行
        //第一行数据是固定内容 "序号", "章节号", "项目", "内容", "验证方法", "备注"
        List<String> headerCellListTemp1 = Arrays.asList("序号", "章节号", "项目", "内容", "验证方法");
        List<String> headerCellList1 = new ArrayList<>(headerCellListTemp1);
        //验证方法,可能是多列数据 第一列是验证方法,后面是赋空字符串,用于合并单元格;
        //数据类型:验证方法 "" "" ""
        //验证类型长度
        int typeListSize = typeList.size();
        for (int k = 0; k < typeListSize - 1; k++) { 
            headerCellList1.add(" "); //赋空字符串
        }
        headerCellList1.add("备注");

        String[] headerCell1 = headerCellList1.toArray(new String[headerCellList1.size()]);

        RowRenderData header1 = Rows.of(headerCell1).center().create();


        //headerCell2 :验证方法对应列,是动态内容
        List<String> headerCellListTemp2 = Arrays.asList("序号", "章节号", "项目", "内容");
        List<String> headerCellList2 = new ArrayList<>(headerCellListTemp2);
        // 验证方法对应列
        headerCellList2.addAll(typeList);
        //备注对应的列 赋空值
        headerCellList2.add(" ");

        String[] headerCell2 = headerCellList2.toArray(new String[headerCellList2.size()]);
        RowRenderData header2 = Rows.of(headerCell2).center().create();
        //表格 列的数量
        int cellLength = headerCell1.length;

        //无数据只有表头数据 tableList 无值
        if (CollectionUtil.isEmpty(tableList)) { 
            RowRenderData[] RowRenderDataHeader = new RowRenderData[2];
            RowRenderDataHeader[0] = header1;
            RowRenderDataHeader[1] = header2;

            TableRenderData check_table = setTableRenderDataAndColWidth(RowRenderDataHeader, cellLength);


            // table 只有表头数据
            datas.put("check_table", check_table);
        } else { 
            int length = 2 + tableList.size();
            //行数据 数组
            RowRenderData[] RowRenderData = new RowRenderData[length];

            RowRenderData[0] = header1;
            RowRenderData[1] = header2;

            for (int i = 0; i < tableList.size(); i++) { 

                String index = Integer.toString(i + 1);
                List<String> tempList = Arrays.asList("A", "B", "C",
                        "D", "E", "F", "G", "H", "I", "G");

                String[] cellString = new String[cellLength];
                cellString[0] = index; //序号
                cellString[1] = "5.1.1";//标题序号
                cellString[2] = "项目"; // 项目
                cellString[3] = "说明"; //内容 : 描述
                //验证方法对应数据赋值
                if (headerCell2.length > 4) { 
                    for (int j = 0; j < tempList.size(); j++) { 
                        cellString[j + 4] = tempList.get(j);
                    }
                }

                //备注
                cellString[cellLength - 1] = "备注";
                RowRenderData rowRenderData = Rows.of(cellString).
                        center().create();
                //行数据赋值
                RowRenderData[2 + i] = rowRenderData;

            }
            TableRenderData check_table = setTableRenderDataAndColWidth(RowRenderData, cellLength);
            // table
            datas.put("check_table", check_table);


        }
        XWPFTemplate.compile("D:/test_word/test.docx")
                .render(datas).writeToFile("D:/test_word/test1.docx");
    }

    /** * 表格赋值, * 设置列宽和合并单元格 * * @param rowRenderDataArray * @param cellLength * @return */
    private static TableRenderData setTableRenderDataAndColWidth(RowRenderData[] rowRenderDataArray, Integer cellLength) { 
        //table赋值set方法需要list
        List<RowRenderData> RowRenderDataList = Arrays.asList(rowRenderDataArray);
        //设置列宽:
        double[] colWidthsCm = new double[cellLength];
        for (int i = 0; i < cellLength; i++) { 
            // "章节号", "项目", "内容" 设置为 2
            if (i == 1 || i == 2 || i == 3) { 
                colWidthsCm[i] = 2D;
            } else { 
                colWidthsCm[i] = 1D;
            }
        }
        //18.450000762939453D A4纸张
        TableRenderData check_table = Tables.ofWidth(18.450000762939453D, colWidthsCm).center().create();
        check_table.setRows(RowRenderDataList);
        //合并单元格
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = mergeCell(cellLength);
        check_table.setMergeRule(mergeCellRuleBuilder.build());

        return check_table;
    }
    /** * 表格 合并单元格 * * @param cellLength * @return */
    private static MergeCellRule.MergeCellRuleBuilder mergeCell(Integer cellLength) { 
        /** * 设置表格合并规则 从0开始 * 1.起始行 MergeCellRule.Grid.of(i, j) i: 行 j: 列 * 2.结束行 MergeCellRule.Grid.of(i, j) i: 行 j: 列 */
        //合并单元格
        //合并到【备注】前一列
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = MergeCellRule.builder();
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 0), MergeCellRule.Grid.of(1, 0));//序号合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 1), MergeCellRule.Grid.of(1, 1));//章节号合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 2), MergeCellRule.Grid.of(1, 2));//项目合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 3), MergeCellRule.Grid.of(1, 3));//内容合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 4), MergeCellRule.Grid.of(0, cellLength - 2));//验证类型合并 第一行
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, cellLength - 1), MergeCellRule.Grid.of(1, cellLength - 1));//备注合并
        return mergeCellRuleBuilder;
    }

}

3. 设置表格宽度 setTableRenderDataAndColWidth

  /** * 表格赋值, * 设置列宽和合并单元格 * * @param rowRenderDataArray * @param cellLength * @return */
    private static TableRenderData setTableRenderDataAndColWidth(RowRenderData[] rowRenderDataArray, Integer cellLength) { 
        //table赋值set方法需要list
        List<RowRenderData> RowRenderDataList = Arrays.asList(rowRenderDataArray);
        //设置列宽:
        double[] colWidthsCm = new double[cellLength];
        for (int i = 0; i < cellLength; i++) { 
            // "章节号", "项目", "内容" 设置为 2 ;这几列宽度大
            if (i == 1 || i == 2 || i == 3) { 
                colWidthsCm[i] = 2D;
            } else { 
                colWidthsCm[i] = 1D;
            }
        }
        //18.450000762939453D A4纸张
        TableRenderData check_table = Tables.ofWidth(18.450000762939453D, colWidthsCm).center().create();
        check_table.setRows(RowRenderDataList);
        //合并单元格
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = mergeCell(cellLength);
        check_table.setMergeRule(mergeCellRuleBuilder.build());

        return check_table;
    }

4. 表格合并方法 mergeCell

 /** * 表格 合并单元格 * * @param cellLength * @return */
    private static MergeCellRule.MergeCellRuleBuilder mergeCell(Integer cellLength) { 
        /** * 设置表格合并规则 从0开始 * 1.起始行 MergeCellRule.Grid.of(i, j) i: 行 j: 列 * 2.结束行 MergeCellRule.Grid.of(i, j) i: 行 j: 列 */
        //合并单元格
        //合并到【备注】前一列
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = MergeCellRule.builder();
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 0), MergeCellRule.Grid.of(1, 0));//序号合并 上下行合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 1), MergeCellRule.Grid.of(1, 1));//章节号合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 2), MergeCellRule.Grid.of(1, 2));//项目合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 3), MergeCellRule.Grid.of(1, 3));//内容合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, 4), MergeCellRule.Grid.of(0, cellLength - 2));//验证类型合并 第一行
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(0, cellLength - 1), MergeCellRule.Grid.of(1, cellLength - 1));//备注合并
        return mergeCellRuleBuilder;
    }

自定义标题

标题可以采用原生poi生成,这里实现利用的是poi-tl的Markdown插件中的标题;
最终生成的样式如下
《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

1. word模板内容如下

{ {?comment}}
Heading1简介
Heading2简介
Heading3简介
Heading4简介
Heading5简介
{ {/comment}}
{ {md}}

黑色内容是word模板上的内容
《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》 ## 2. md文件内容如下
《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

3. 需要生成的标题按照md文件格式拼接为字符串即可

代码如下:

public class test { 


    public static void main(String[] args) throws IOException { 
        //要写入模板的数据
        Map<String, Object> datas = new HashMap<String, Object>();
        //标题的数据 需要按照md文件,标题的格式拼接数据
        MarkdownRenderData code = new MarkdownRenderData();
// byte[] bytes = Files.readAllBytes(Paths.get("src/test/resources/markdown/basic1.md"));
// String mkdn = new String(bytes);
        String mkdn = "### Heading 31\n" +
                "#### Heading 41\n" +
                "### Heading 32\n" +
                "#### Heading 42";
        code.setMarkdown(mkdn);

        MarkdownStyle style = MarkdownStyle.newStyle();
        style.setShowHeaderNumber(true); //设置标题序号
        code.setStyle(style);

        //标题的数据
        datas.put("md", code);
        Configure config = Configure.builder().bind("md", new MarkdownRenderPolicy()).build();
        XWPFTemplate.compile("src/test/resources/template_finnish/test_title.docx", config)
                .render(datas)
                .writeToFile("src/test/resources/out/out_markdown2.docx");
    }

生成标题其他注意点

1. 自己指定生成标题序号开始位置:重写DocumentVisitor.class 类

下面的内容是修改后的内容:
直接标题赋值时指定值:《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》
或者是修改原生的getHeaderNumber() 方法

   private String getHeaderNumber(int level) { 
        if (level == 1) return "";
        String str = StringUtils.join(Arrays.
                copyOfRange(headerNumberArray, 2, headerNumberArray.length), '.');
        String substring = str.substring(0, (level - 1) * 2 >= str.length() ? str.length() : (level - 1) * 2);
        //目前是 5.2.1.
        //去除标题序号的. 保持和文档上一致 类似这种5.2.1
        if (StringUtils.isNotEmpty(substring)) { 
            substring = substring.substring(0, substring.length() - 1);
        }

        return substring + " ";
    }

2. 自动生成标题的样式,也可以按照自己想要的方式设置

方式是修改DocumentVisitor 类中的visit()方法
代码如下:展示的代码都是自己修改后的

   @Override
    public void visit(Heading heading) { 
        int level = heading.getLevel();
        resetHeaderNumberArray(level);

        ParagraphBuilder paraOf = Paragraphs.of().styleId(String.valueOf(level)).left().allowWordBreak();
        if (style.isShowHeaderNumber()) { 
            paraOf.addText(Texts.of(getHeaderNumber(level)).style(getStyle(level)).create());
        }
        DocumentRenderData renderData = parseNode(heading);
        if (!renderData.getContents().isEmpty()) { 
            ParagraphRenderData headerParagraph = (ParagraphRenderData) renderData.getContents().get(0);
           // paraOf.addParagraph(headerParagraph);
           // paraOf.addText(Texts.of("").bookmark(evalText(headerParagraph)).create());
            paraOf.addText(Texts.of(evalText(headerParagraph)).style(getStyle(level)).create());
        }
        of.addParagraph(paraOf.create());
    }

/** * 自定义标题的样式; * 不使用该方法,标题样式都是标签{ {md}}的样式 * * @param level * @return */

    private Style getStyle(int level) { 
        Style style = null;
        switch (level) { 
            case 2: //标题1
                style = new Style("宋体", 22D); //二号字体
                break;
            case 3: //标题2
                style = new Style("宋体", 16D);//三号字体
                break;
            case 4: //标题3
                style = new Style("宋体", 14D);//四号字体
                break;
            //其他都是4号字体
            default:
                style = new Style("宋体", 14D);//二号字体
                break;
        }
        return style;
    }

上述代码中,getStyle方法用于指定自己想要修改的样式

更新目录 (wps 不支持,只支持office)

网上搜到的方式,直接采用该方法,可以生成并且更新目录

转载链接如下:
转载生成poi目录

poi-tl的方式

word模板添加标签 { {TOC}}

《poi-tl导出word;自定义列表序号和表格宽度,表格合并,自定义标题,更新目录》

代码如下

public class AutoMenuWordPoi { 


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


        //要写入模板的数据
        Map<String, Object> datas = new HashMap<String, Object>();
        datas.put("TOC", "TOC");
        Configure config = Configure.builder().bind("TOC", new TOCRenderPolicy()).build();


        XWPFTemplate.compile("src/test/resources/out/out_markdownToc_poi.docx", config)
                .render(datas)
                .writeToFile("src/test/resources/out/out_menu1.docx");



    }


}

也可以word已经生成目录,后面只是修改了添加标题,去更新目录

采用如下代码也可以:

public class AutoMenuWord2 { 
    private static final String HEADING1 = "1";

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


        XWPFDocument doc = new XWPFDocument(new FileInputStream("src/test/resources/out/out_markdown2.docx"));
        doc.enforceUpdateFields(); // 更新目录




        doc.write(new FileOutputStream("src/test/resources/out/out_markdown3.docx"));
        doc.close();


    }

}

    原文作者:qunwuhui
    原文地址: https://blog.csdn.net/qunwuhui/article/details/121974873
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞