【java】poi-tl 1.9.1 word模板插入文本及动态复杂表格


前言

  • word生成是较为常见的功能,通常解决方案有freemarker、poi-tl等。
  • 一般常规的word模板替换可采用第三方封装好的工具实现。
  • 本文涉及动态复杂表格的插入,最好还是采用poi-tl。
  • poi-tl版本差异性较大,如果被版本束缚,较难找到对应版本的copy代码,这时候可以参考其他版本再对照自己版本的jar包文件来对应实现。
    poi 与 poi-tl 版本对应关系 http://deepoove.com/poi-tl

一、需求

根据模板生成对应的word文件。

  1. 包含文本替换
  2. 动态表格,涉及动态合并单元格
  3. word中包含分割线的图片
    前期难点在于动态表格的绘制和锚点替换,后期难点在于poi-tl版本约束(由于某些原因,必须用1.9.1版本)。

二、方案

  1. poi 从头到尾画(即时这个word并不大,code硬编码起来也较为复杂,主要是比较难受,还要考虑各种样式,图片插入等)
  2. 拼html写入word(同上的硬编码较为麻烦,但样式结构可以word另存html后直接用,代码和word打开后的模式不友好,默认为web模式)
  3. 死磕poi-tl

三、实现

1.生成word 关键代码

//构建模板替换的map对象
Map<String, Object> dataMap = wordMaps(findDTO);
//获取模板信息等
String fileName = UUIDUtil.uuid() + ".docx";
String midPath = "uploadFile" + File.separator + "duty" + File.separator;
String filePath = fileUploadRoot + midPath;
String templatePath = this.getFullPathName("evaluate.docx");
//主要执行代码 指定替换内容形式为${xxxx},指定${table}标签的渲染规则为DetailTablePolicy
Configure builder = Configure.newBuilder().buildGramer("${", "}").bind("table", new DetailTablePolicy()).build();
XWPFTemplate template = XWPFTemplate.compile(templatePath, builder).render(dataMap);
try {
    template.writeToFile(filePath + fileName);
    template.close();
} catch (IOException e) {
    e.printStackTrace();
}

2.自定义规则类

package com.gsafety.gemp.evaluate.manage.rule.impl;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.MergeCellRule;
import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.data.TableRenderData;
import com.deepoove.poi.data.Tables;
import com.deepoove.poi.data.style.TableStyle;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.policy.TableRenderPolicy;
import com.deepoove.poi.policy.TextRenderPolicy;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import lombok.SneakyThrows;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Mr.wanter
 * @time 2022-9-7 0007
 * @description
 */
public class DetailTablePolicy implements RenderPolicy {

    @SneakyThrows
    @Override
    public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
        NiceXWPFDocument doc = template.getXWPFDocument();
        RunTemplate runTemplate = (RunTemplate) eleTemplate;
        XWPFRun run = runTemplate.getRun();
        if (null == data) {
            return;
        }
        List<RowRenderData> rows = (List<RowRenderData>) data;
        TableStyle.BorderStyle borderStyle = new TableStyle.BorderStyle();
        borderStyle.setColor("A6A6A6");
        borderStyle.setSize(4);
        borderStyle.setType(XWPFTable.XWPFBorderType.SINGLE);
        TableRenderData tableRenderData = Tables.ofA4MediumWidth()
                .border(borderStyle).center()
                .create();
        rows.forEach(r -> {
            tableRenderData.addRow(r);
        });

        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder = MergeCellRule.builder();
        /**
         * 设置表格合并规则
         * 1.起始行 MergeCellRule.Grid.of(i, j) i: 行 j: 列
         * 2.结束行 MergeCellRule.Grid.of(i, j) i: 行 j: 列
         */
        //评估对象合并
        mergeCellRuleBuilder.map(MergeCellRule.Grid.of(1, 0), MergeCellRule.Grid.of(rows.size() - 1, 0));
        List<String> subTargetContent = new ArrayList<>();
        rows.forEach(r -> {
            subTargetContent.add(r.getCells().get(1).getParagraphs().get(0).getContents().get(0).toString());
        });
        Set<String> set = subTargetContent.stream().collect(Collectors.toSet());
        set.forEach(s -> {
            //只有重复的合并,获取到需要合并项的列的行起始索引后进行合并
            if (subTargetContent.stream().filter(st -> s.equals(st)).count() > 1) {
                mergeCellRuleBuilder.map(MergeCellRule.Grid.of(subTargetContent.indexOf(s), 1), MergeCellRule.Grid.of(subTargetContent.lastIndexOf(s), 1));//评估项合并
                mergeCellRuleBuilder.map(MergeCellRule.Grid.of(subTargetContent.indexOf(s), 4), MergeCellRule.Grid.of(subTargetContent.lastIndexOf(s), 4));//权重合并
            }
        });

        /**
         * MergeCellRule支持多合并规则,会以Map的形式存入可以看一下源码
         * !!! 一定要设置完规则后再调用 MergeCellRule的build方法进行构建
         */
        tableRenderData.setMergeRule(mergeCellRuleBuilder.build());
        TableRenderPolicy.Helper.renderTable(run, tableRenderData);
        //todo 如果不加入下面代码生成的word会保留模板中的${table}
        TextRenderPolicy.Helper.renderTextRun(run, "");
    }
}

3.效果展示
在这里插入图片描述
在这里插入图片描述

总结

poi版本差异较大,很难拿来即用。
参考:https://blog.csdn.net/weixin_45051216/article/details/112471339解决了:

  1. table样式
  2. RowRenderData的构建方式。
  3. 合并单元格

表格渲染失败(不知道是不是版本问题),最终还是要仔细看jar包中都有哪些类和方法,根据文件名一点一点研究出来。渲染table的关键:TableRenderPolicy.Helper.renderTable(run, tableRenderData)

在这里插入图片描述

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
确保您已经在项目的pom.xml文件中添加了poi-tl库的依赖: ```xml <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.0</version> </dependency> ``` 然后,您可以按照以下步骤使用poi-tl库来合并表格并下载Word文档: 1. 创建一个Word模板文件,其中包含您要合并的表格。您可以使用Microsoft Word或其他编辑器创建模板文件,并确保在模板文件中使用`${}`标记来标记需要替换的文本。 2. 创建一个Java类来处理导出请求。例如,创建一个名为WordExportController的类。 ```java import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; import com.deepoove.poi.data.TableRenderData; import com.deepoove.poi.data.TextRenderData; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; @Controller public class WordExportController { @GetMapping("/export") public ResponseEntity<InputStreamResource> exportWord() throws IOException { // 加载Word模板文件 XWPFTemplate template = XWPFTemplate.compile("templates/template.docx") .render(getTemplateData()); // 将生成的Word文档转换为字节数组 ByteArrayOutputStream out = new ByteArrayOutputStream(); template.write(out); byte[] documentBytes = out.toByteArray(); // 设置下载响应的头信息 HttpHeaders headers = new HttpHeaders(); headers.setContentDispositionFormData("attachment", "merged_table.docx"); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 创建一个包含Word文档字节数组的InputStreamResource InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(documentBytes)); // 返回响应实体 return ResponseEntity.ok() .headers(headers) .body(resource); } private Map<String, Object> getTemplateData() { Map<String, Object> data = new HashMap<>(); // 创建表格数据 TableRenderData tableData = new TableRenderData(); tableData.setHeader(new TextRenderData("FFFFFF", "Header 1")); tableData.setRow(new TextRenderData("FFFFFF", "Cell 1"), new TextRenderData("FFFFFF", "Cell 2")); // 将表格数据存储到模板数据中 data.put("tableData", tableData); return data; } } ``` 3. 在resources目录下创建一个名为`template.docx`的Word模板文件,并按照您的需求设置表格样式和内容。在模板文件中,您可以使用`${tableData}`来标记需要替换的表格数据。 4. 启动您的Spring Boot应用程序,并访问导出请求的URL(例如:http://localhost:8080/export)。将会自动下载名为`merged_table.docx`的Word文档,其中包含合并表格的内容。 请确保按照您的需求修改代码,并根据模板文件的位置进行相应的调整。 希望对您有所帮助!如果您有任何其他问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr-Wanter

感谢大佬

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值