精华内容
下载资源
问答
  • poi-tl相关的请参考:http://deepoove.com/poi-tl/ ...另外这种方法不会保存富文本的一些样式,...想要具体的Demo请看:https://gitee.com/xuwangcheng/poi-tl-html-to-word 有疑问请加QQ群:468324085 作者:白卡pala
  • poi-tl-1.8.2.jar

    2020-12-23 16:42:12
    为了更方便大家下载poi-tl-1.8.2.jar的Jar包
  • poi-tl-plus Enhancement to POI-TL (). Support defining Table templates directly in Microsoft Word (Docx) file. POI-TL的 MiniTableRenderData 可以支持简单的表格,但是表格样式和内容的样式无法在 Word 中...
  • poi-tl-1.7.2.jar

    2020-04-23 00:25:32
    java导出word,使用最新版poi-tl-1.7.2jar。
  • poi-tl-1.3.1

    2018-10-17 15:53:35
    poi-tlpoi template language)是基于Apache POI的Word模板引擎。纯Java组件,跨平台,代码短小精悍,通过插件机制使其具有高度扩展性。完整的中文文档参见 http://deepoove.com/poi-tl
  • POI-TL合并多个Word文档

    2021-06-27 23:13:49
    POI-TL合并多个Word文档
  • 参考https://gitee.com/xuwangcheng/poi-tl-html-to-word,利用poi-tl将前端富文本生成的HTML内容转换为可插入word的内容。利用poi-tl将前端富文本生成的HTML内容转换为可插入word的内容。
  • poi-tlpoi-template-language) Word模板引擎,基于Apache POI-Microsoft文档的Java API。 什么是poi-tl FreeMarker,Velocity基于文本模板和数据生成新HTML页面,配置文件等,poi-tl是Word模板引擎,基于...
  • * poi-tl引擎实现world模板内容替换(支持docx) * 依赖poi-tl,commons-lang3, poi-ooxml-schemas, poi-ooxml, poi,slf4j
  • poi-tl-1.2.0.jar

    2018-08-02 11:54:49
    poi-tl-1.2.0.jar
  • poi-tl实现根据模板生成合同和html转doc,doc转docx的Demo,可以直接运行,jdk1.8, poi-tl 是新的1.6版本的,
  • poi-tl解析Word文档

    2016-12-21 16:08:10
    poi-tl解析Word文档,包含表格类型的也可以
  • poi-tl模板文件

    2021-02-08 16:21:54
    poi-tl模板文件
  • 生成数据字典 使用更新mysql数据字典
  • word源码java-poi-tl:点

    2021-06-05 22:57:00
    Poi-tl(Poi-template-language) :memo: Word 模板引擎,基于Apache Poi,目标是在文档的任何地方做任何事情(Do Anything Anywhere)。 下表对一些处理Word的解决方案作了一些比较: 方案 跨平台 样式处理 易用性 Poi-...
  • POIPOI-TL操作Word

    2021-09-15 08:33:41
    POI操作Word 操作依赖包org.apache.poi,包括poipoi-ooxml。 创建空白Word文件 void poiCreateNewWord() { String path = "C:\\Users\\wisdom21111\\Desktop\\"; XWPFDocument document = new XWPFDocument();...

    POI操作Word

    操作依赖包org.apache.poi,包括poi和poi-ooxml。
    

    创建空白Word文件

        void poiCreateNewWord() {
            String path = "C:\\Users\\wisdom21111\\Desktop\\";
            XWPFDocument document = new XWPFDocument();
            try {
                FileOutputStream out = new FileOutputStream(path + "newFile.docx");
                document.write(out);
                out.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    复制Word文件

        void poiCopyFile() throws IOException {
            XWPFDocument xwpfDocument1 = new XWPFDocument(new FileInputStream("C:\\Users\\wisdom21111\\Desktop\\oldFile.docx"));
            XWPFDocument xwpfDocument2 = xwpfDocument1;
            FileOutputStream out = new FileOutputStream("C:\\Users\\wisdom21111\\Desktop\\newFile.docx");
            xwpfDocument2.write(out);
            out.close();
        }
    

    合并Word文档

        @Test
        void poiMergeMultiFile(File[] files,String outfile) throws IOException, XmlException {
            XWPFDocument xwpfDocument = new XWPFDocument(new FileInputStream(files[0]));
            CTBody ctBody = xwpfDocument.getDocument().getBody();
            String srcstr = ctBody.xmlText(); //<xml-fragment ...>...</xml-frament> 整体信息
            String prefix = srcstr.substring(0,srcstr.indexOf(">")+1); //<xml-frament ...> 头
            String suffix = srcstr.substring(srcstr.lastIndexOf("<")); //</xml-frament> 尾
            String mainbody = srcstr.substring(srcstr.indexOf(">")+1,srcstr.lastIndexOf("<")); //...主体内容
            List<String> list = new ArrayList<>();
            //使用循环遍历获得后续文件内容
            for(int i=1;i<files.length;i++){
                XWPFDocument xwpfDocument1 = new XWPFDocument(new FileInputStream(files[i]));
                CTBody ctBody1 = xwpfDocument1.getDocument().getBody();
                String appendContent = ctBody1.xmlText().substring(srcstr.indexOf(">")+1,srcstr.lastIndexOf("<")); //后续文件的主体内容
                list.add(appendContent);
            }
            StringBuilder sb = new StringBuilder();
            sb.append(prefix); //xml头
            sb.append(mainbody); //第一个文件主体内容
            for(String str:list){
                sb.append("    <w:p>\n" +
                        "        <w:pPr>\n" +
                        "            <w:pageBreakBefore w:val=\"true\"/>\n" +
                        "        </w:pPr>\n" +
                        "    </w:p>"); // 分页符
                sb.append(str); //后续文件主体
            }
            sb.append(suffix); //xml文件尾
            CTBody ctResult = CTBody.Factory.parse(sb.toString());
            ctBody.set(ctResult); //重新设置第一个文件内容
    
            FileOutputStream outputStream = new FileOutputStream(outfile);
            xwpfDocument.write(outputStream);
            outputStream.flush();
            xwpfDocument.close();
            outputStream.close();
        }
    
        @Test
        void poiMergeMultiFileTest() throws IOException, XmlException {
            File one = new File("C:\\Users\\wisdom21111\\Desktop\\File.docx");
            File two = new File("C:\\Users\\wisdom21111\\Desktop\\File.docx");
            File three = new File("C:\\Users\\wisdom21111\\Desktop\\File.docx");
            String out = "C:\\Users\\wisdom21111\\Desktop\\new.docx";
            File[] files = new File[]{one,two,three};
            poiMergeMultiFile(files,out);
        }
    

    合并Word文档简洁方法,使用POI-TL

        //依赖包 com.deepoove.poi
        void nicePoiMergeFile() throws Exception {
        NiceXWPFDocument niceXWPFDocument1 = new NiceXWPFDocument(ne FileInputStream("C:\\Users\\wisdom21111\\Desktop\\output.docx"));
        NiceXWPFDocument niceXWPFDocument2 = new NiceXWPFDocument(ne FileInputStream("C:\\Users\\wisdom21111\\Desktop\\output.docx"));
        niceXWPFDocument1.createParagraph().setPageBreak(true);
        NiceXWPFDocument newDoc = niceXWPFDocument1.merge(niceXWPFDocument2);
        FileOutputStream out = new FileOutputStream("C:\\Users\\wisdom21111\\Desktop\\new_doc.docx");
        newDoc.write(out);
        newDoc.close();
        out.close();
        }
    

    使用POI-TL根据模板生成Word文件

        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("str","我是字符串");
        XWPFTemplate template = XWPFTemplate.compile("C:\\Users\\wisdom21111\\Desktop\\template.docx").render(hashMap);
        template.writeAndClose(new FileOutputStream("C:\\Users\\wisdom21111\\Desktop\\output.docx"));
    
    文本标签 {{str}}
    图片标签 {{@image}}
    表格标签 {{#table}}
    
        hashMap.put("str","Hello,World");
        hasnMap.put("image","C:\\Users\\wisdom21111\\Desktop\\xxx.jpeg");
        //创建表格第一种方式
        hashMap.put("table",Tables.of(new String[][]{
            new String[][]{"00","01"},
            new String[][]{"10","11"}
        }).border(BorderStyle.DEFAULT).create());
        //创建表格第二种方式
        RowRenderData row0 = Rows.of("姓名","性别").textColor("FFFFFF").bgColor("4472C4").center().create();
        RowRenderData row1 = Rows.create("张三","男");
        hashMap.put("table",Tables.create(row0,row1));
    
    引用标签
        替换图片,编辑替换文字-替换文字 {{img}}
    
        put("img",Pictures.ofLocal("C:\\Users\\wisdom21111\\Desktop\\newPic.jpg").create());
    

    POI-TL表格行循环

        void poitlRowTest() throws IOException {
            Student student1 = new Student("张三","男","一班","001");
            Student student2 = new Student("李四","男","一班","002");
            Student student3 = new Student("王五","男","一班","003");
            List<Student> list = asList(student1,student2,student3);
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            Configure config = Configure.builder().bind("list",policy).build();
    
            HashMap<String,Object> hashMap = new HashMap<>();
            hashMap.put("list",list);
            XWPFTemplate template = XWPFTemplate.compile("C:\\Users\\wisdom21111\\Desktop\\template.docx",config).render(hashMap);
            template.writeAndClose(new FileOutputStream("C:\\Users\\wisdom21111\\Desktop\\hello.docx"));
        }
    
    模板template.docx

    在这里插入图片描述


    POI-TL创建表格

    void poitlCreateTable() throws IOException {
        //表格行,注意每行的单元格数量相同
        RowRenderData row0 =Rows.of("功率因数","允许误差","电流","实际相对误差",null,null).center().crate();
        RowRenderData row1 =Rows.of(null,null,null,"{{bar1}}","{{bar2}}","{{bar3}}").center().create();
        RowRenderData row2 =Rows.of("{{name}}","{{f1}}","{{a1}}","{{r11}}","{{r12}}","{{r13}}").center.create();
        RowRenderData row3 =Rows.of(null,null,"{{a2}}","{{r21}}","{{r22}}","{{r23}}").center().create(;
        RowRenderData row4 =Rows.of(null,null,"{{a3}}","{{r31}}","{{r32}}","{{r33}}").center().create(;
        RowRenderData row5 =Rows.of(null,null,"{{a4}}","{{r41}}","{{r42}}","{{r43}}").center().create(;
        RowRenderData row6 =Rows.of(null,null,"{{a5}}","{{r51}}","{{r52}}","{{r53}}").center().create(;
        RowRenderData row7 =Rows.of(null,"{{f2}}","{{a6}}","{{r61}}","{{r62}}","{{r63}}").center().crete();
        //单元格合并规格
        Map<MergeCellRule.Grid, MergeCellRule.Grid> map = new HashMap<>();
        map.put(MergeCellRule.Grid.of(0,0), MergeCellRule.Grid.of(1,0));
        map.put(MergeCellRule.Grid.of(0,3), MergeCellRule.Grid.of(0,5));
        map.put(MergeCellRule.Grid.of(0,1), MergeCellRule.Grid.of(1,1));
        map.put(MergeCellRule.Grid.of(0,2), MergeCellRule.Grid.of(1,2));
        map.put(MergeCellRule.Grid.of(2,0), MergeCellRule.Grid.of(7,0));
        map.put(MergeCellRule.Grid.of(2,1), MergeCellRule.Grid.of(6,1));
        MergeCellRule.MergeCellRuleBuilder mergeCellRuleBuilder =MergeCellRule.builder();
        for(Map.Entry<MergeCellRule.Grid, MergeCellRule.Grid> e:map.entrySet()){
            mergeCellRuleBuilder.map(e.getKey(),e.getValue());
        }
        //将表格添加到模板中
        Map<String,Object> hashMap = new HashMap<>();
        hashMap.put("table",Tables.of(row0,row1,row2,row3,row4,row5,row6,row7).mereRule(mergeCellRuleBuilder.build()).create());
        XWPFTemplate template =XWPFTemplate.compile("C:\\Users\\wisdom21111\\Desktop\\template.docx").rener(hashMap);
        template.writeAndClose(newFileOutputStream("C:\\Users\\wisdom21111\\Desktop\\hello.docx"));
    }
    
    创建效果:
    

    在这里插入图片描述


    POI动态表格

    创建表格数据类
    public class DetailData {
        private List<RowRenderData> goods;
        private List<RowRenderData> labors;
        //...省略get/set方法
    }
    
    创建自定义渲染类
        public class DetailTablePolicy extends DynamicTableRenderPolicy {
        //表格渲染策略
        int goodStartRow = 2;
        int laborStartRow = 5;
        @Override
        public void render(XWPFTable xwpfTable, Object o) throws Exception {
            if(null==o){
                return;
            }
            DetailData detailData = (DetailData)o;
            List<RowRenderData> labors = detailData.getLabors();
            if(null!=labors){
                xwpfTable.removeRow(laborStartRow);
                //循环插入
                for(int i=0;i<labors.size();i++){
                    XWPFTableRow insertRow = xwpfTable.insertNewTableRow(laborStartRow);
                    for(int j=0;j<7;j++) insertRow.createCell();
                    TableTools.mergeCellsHorizonal(xwpfTable,laborStartRow,0,3);
                    TableRenderPolicy.Helper.renderRow(xwpfTable.getRow(laborStartRow),labors.get(i));
                }
            }
            List<RowRenderData> goods = detailData.getGoods();
            if (null != goods) {
                xwpfTable.removeRow(goodStartRow);
                for (int i = 0; i < goods.size(); i++) {
                    XWPFTableRow insertNewTableRow = xwpfTable.insertNewTableRow(goodStartRow);
                    for (int j = 0; j < 7; j++) insertNewTableRow.createCell();
                    TableRenderPolicy.Helper.renderRow(xwpfTable.getRow(goodStartRow), goods.get(i));
                }
            }
        }
    }
    
    填充数据执行渲染
    void dynamicRenderTable() throws IOException {
        //动态表格 从底部向上逐个添加
        //自上向下添加位置不易确定
    
        DetailData detailData = new DetailData();
        RowRenderData good =Rows.of("4","墙纸","书房+卧室","1500","/","400","1600").center().create();
        List<RowRenderData> goodList = Arrays.asList(good,good,good);
        RowRenderData labor = Rows.of("油漆工","2","200","400").center().create();
        List<RowRenderData> laborList = Arrays.asList(labor,labor,labor,labor);
        detailData.setGoods(goodList);
        detailData.setLabors(laborList);
    
        Map<String,Object> map = new HashMap<>();
        map.put("detail_table",detailData);
        Configure config = Configure.builder().bind("detail_table",newDetailTablePolicy()).build();
        XWPFTemplate template =XWPFTemplate.compile("C:\\Users\\wisdom21111\\Desktop\\template.docx",confg).render(map);
        template.writeToFile("C:\\Users\\wisdom21111\\Desktop\\hello2.docx");
    }
    
    模板预览:

    在这里插入图片描述

    生成预览:

    在这里插入图片描述

    展开全文
  • poi-tlpoi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档。 在文档的任何地方做任何事情(Do Anything Anywhere)是poi-tl的星辰大海。 瞅瞅这话说的,忍不住体验一下 主要技术栈:...

    开头语

    要能重新选择,我一定不做马云  ————马云
    

    需求分析

    之前一直在做导出excel表的功能,导出word也实现过但是并没有什么亮点,最近接触到了poi-tl。
    

    来看下官网的介绍:

    poi-tl(poi template language)是Word模板引擎,使用Word模板和数据创建很棒的Word文档。
    在文档的任何地方做任何事情(Do Anything Anywhere)是poi-tl的星辰大海。
    

    瞅瞅这话说的,忍不住体验一下

    主要技术栈:poi-tl官网路径

    本文的示例代码按照官方提供的api,结合自己的需求,完成开发工作。
    

    资源下载:完整项目资源下载

    所用jar包

    我个人是很喜欢先从jar包开始说起,因为我觉得先搭建好一个环境再进行开发工作是很有效率的一件事,
    所以这里先上jar包,方便阅读。
    官网提供了很多个版本,pot-tl的版本是要配合apache.poi的版本去使用的,我这里使用的版本是
    
    poi-tl  	1.10.0
    apache.poi  4.1.2
    
    原因有两个:1、1.10版支持el表达式,能够使用三目运算符等 2、我的apache.poi版本本身就高
    
    	<!--apachepoi 4.1.2 start-->
    	<dependency>
    	     <groupId>org.apache.poi</groupId>
    	     <artifactId>poi</artifactId>
    	     <version>${apachepoi.version}</version>
    	 </dependency>
    	 <dependency>
    	     <groupId>org.apache.poi</groupId>
    	     <artifactId>poi-ooxml</artifactId>
    	     <version>${apachepoi.version}</version>
    	 </dependency>
    	 <dependency>
    	     <groupId>org.apache.poi</groupId>
    	     <artifactId>poi-ooxml-schemas</artifactId>
    	     <version>${apachepoi.version}</version>
    	 </dependency>
    	 <!--apachepoi 4.1.2 end-->
    	 <!--poi-tl 模板引擎 start-->
    	 <dependency>
    	     <groupId>com.deepoove</groupId>
    	     <artifactId>poi-tl</artifactId>
    	     <version>1.10.0</version>
    	 </dependency>
    	 <dependency>
    	     <groupId>org.springframework</groupId>
    	     <artifactId>spring-expression</artifactId>
    	     <version>4.3.6.RELEASE</version>
    	 </dependency>
    	 <!--poi-tl 模板引擎 end-->
    

    {apachepoi.version}=4.1.2

    项目代码

    主方法 :WordController

    package com.langlang.wps.controller;
    
    import com.langlang.wps.utils.WpsUtil;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author :Jim
     * @date :Created in 2021/8/10 17:29
     * @description:导出word控制类
     * @modified By:
     * @version: 1.0.0$
     */
    @Slf4j
    public class WordController {
        
        /**
         * @author Jim
         * @description:生成word文档
         * @date 2021/8/12 10:48
         * @param [args]
         * @return void
         */
        public static void main(String[] args) {
            //模拟数据
            Map<String, Object> map = new HashMap<>();
            map.put("startTime","2014年");
            List<Map<String, Object>> list = new ArrayList<Map<String, Object>>() {
                {
                    add(new HashMap<String, Object>() {{this.put("value","是我人生中最美的点缀,因你的陪伴,我不再孤单,愿与你漫观云卷云舒,静看花开花落");}});
                    add(new HashMap<String, Object>() {{this.put("value","是我生命中最美的际遇,因你的爱,我不再忧伤,愿与你共赴天涯,缠绵缱绻");}});
                    add(new HashMap<String, Object>() {{this.put("value","愿我年华里最美的眷恋,因你的关切,我不再彷徨,愿与你琴瑟和鸣,共谱一曲爱的梵音");}});
                }
            };
            map.put("list", list);
            WpsUtil.exportWord(map);
        }
    
    }
    
    

    工具类 :WpsUtil

    package com.langlang.wps.utils;
    
    import cn.hutool.core.io.resource.ResourceUtil;
    import com.deepoove.poi.XWPFTemplate;
    import com.deepoove.poi.config.Configure;
    import com.deepoove.poi.config.ConfigureBuilder;
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.util.Map;
    
    /**
     * word工具类
     */
    @Slf4j
    public class WpsUtil{
    
        /**
         * @author Jim
         * @description:导出word
         * @date 2021/8/12 15:09
         * @param [data]
         * @return void
         */
        public static void exportWord(Map<String,Object> data) {
            //文件路径
            String filePath = "";
            try {
                //设置导出路径  创建一个word目录
                String path = "D:/word/";
                File file = new File(path);
                //如果不存在则创建
                if (!file.exists()) {
                    file.mkdirs();
                }
                //获取文件名称
                String name = getFilename();
                //获取模板
                XWPFTemplate template = getWordTemplate(data);
                //输出到指定目录下
                filePath = path+name;
                FileOutputStream ostream = new FileOutputStream(filePath);
                template.write(ostream);
                //关闭输出流
                ostream.flush();
                ostream.close();
                template.close();
            }catch(IOException e){
                //异常处理
                log.error("文件导出错误", e);
            }
        }
    
        /**
         * @author Jim
         * @description:获取导出word的文件名称
         * @date 2021/8/12 15:09
         * @param []
         * @return java.lang.String
         */
        private static String getFilename() {
            //获取100以内的随机数
            double random = Math.random();
            return "与你相遇"+random*100+".docx";
        }
    
        /**
         * @author Jim
         * @description:获取word模板添加数据后的word文档
         * @date 2021/8/12 15:10
         * @param [data]
         * @return com.deepoove.poi.XWPFTemplate
         */
        private static XWPFTemplate getWordTemplate (Map<String,Object> data) {
            //获取模板的输入流
            InputStream stream = ResourceUtil.getStream("classpath:templates/exportWord.docx");
            ConfigureBuilder builder = Configure.builder();
            builder.useSpringEL();
            XWPFTemplate template = XWPFTemplate.compile(stream,builder.build()).render(data);
            return template;
        }
    }
    
    

    对应的模板

    							与你相遇
    {{startTime}}我与你相遇,如果命运可以让我再选择一次,我依然无怨无悔与你相遇。
    {{?list}}遇见你,{{value}}{{_is_last ? '。' : ';'}}{{/list}}
    

    word模板文档

    实现的结果

    文档路径
    生成结果

    遇到的问题

    1、poi-tl版本的选择
    
    2、如何遍历list和三目运算符使用
    

    解决的方法

    1、因为我apache.poi的版本是4.1.0 ,我不可能为了这个功能去降低我的版本,所以只能去找和它配套的
    poi-tl版本,另外,按照poi-tl官网api所说,在遍历list时_is_last 这种判断是否是最后一个元素的属性
    只有在1.10.0版本才有,正好它对应的apache.poi版本是4.1.2,升级一个小版本还是没什么问题的
    
    2、遍历list以{{?list}}开头{{/ist}}结尾 中间直接写{{value}}属性名 
    

    结束语

    你真正喜欢想要的,没有一样,是可以轻易得到的。这就是努力的理由。
    向往美好生活

    展开全文
  • 前言 poi-tlpoi template language)是基于Apache POI的Word模板引擎,纯Java组件,跨平台,代码短小精悍,通过插件机制使其具有高度扩展性。

    前言

    poi-tl(poi template language)是基于Apache POI的Word模板引擎,纯Java组件,跨平台,代码短小精悍,通过插件机制使其具有高度扩展性。

    1.Maven

    		<!-- excel工具 -->
    		<dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi</artifactId>
    			<version>3.16</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi-ooxml</artifactId>
    			<version>3.16</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi-ooxml-schemas</artifactId>
    			<version>3.16</version>
    		</dependency>
    
    		<!-- poi-tl导出word -->
    		<dependency>
    			<groupId>com.deepoove</groupId>
    			<artifactId>poi-tl</artifactId>
    			<version>1.5.0</version>
    		</dependency>

    2.application.properties

    # 模板路径
    data.template.path=D:/workspace/gitlab101/sc-bianguan/target/classes/template/

    3.快速开始

    新建Word文档list.docx。

    3.1.文本模板{{var}}

    TextRenderData 或 String 数据模型。

        @ApiOperation(value = "文本模板")
        @PostMapping("/downloadWord1")
        public Result downloadWord1(HttpServletRequest request, HttpServletResponse response) throws IOException {
            Map<String,Object> tempMap = new HashMap<>();
            tempMap.put("name", "这是一个文本测试");
            tempMap.put("age", 134);
            //获取文件的相对路径  可在控制台打印查看输出结果
            File file = new File(path + "list.docx");
            /*1.根据模板生成文档*/
            XWPFTemplate template = XWPFTemplate.compile(file).render(tempMap);
            response.setContentType("application/octet-stream");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            String fileName = "测试";
            URLEncoder.encode(fileName,"UTF-8");
            response.setHeader("Content-Disposition", "attachment;fileName="+ fileName +".docx");
            response.flushBuffer();
            template.write(response.getOutputStream());
            return success();
        }

    3.2.图片模板{{@var}}

    PictureRenderData 数据模型。

        @ApiOperation(value = "图片模板")
        @PostMapping("/downloadWord2")
        public Result downloadWord2(HttpServletRequest request, HttpServletResponse response) throws IOException {
            Map<String,Object> tempMap = new HashMap<>();
            // 本地图片
            tempMap.put("local", new PictureRenderData(80, 100, "F:/图片.jpg"));
    
            //获取文件的相对路径  可在控制台打印查看输出结果
            File file = new File(path + "list.docx");
            /*1.根据模板生成文档*/
            XWPFTemplate template = XWPFTemplate.compile(file).render(tempMap);
            response.setContentType("application/octet-stream");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            String fileName = "测试";
            URLEncoder.encode(fileName,"UTF-8");
            response.setHeader("Content-Disposition", "attachment;fileName="+ fileName +".docx");
            response.flushBuffer();
            template.write(response.getOutputStream());
            return success();
        }

    3.3.表格模板{{#var}}

    poi-tl默认实现了N行N列的样式(如下图),同时提供了当数据为空时,展示一行空数据的文案(如下图中的No Data Descs),数据模型是 MiniTableRenderData 。

        @ApiOperation(value = "表格模板")
        @PostMapping("/downloadWord3")
        public Result downloadWord3(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
            RowRenderData header = RowRenderData.build(new TextRenderData("FFFFFF", "姓名"), new TextRenderData("FFFFFF", "学历"));
    
            RowRenderData row0 = RowRenderData.build("张三", "研究生");
            RowRenderData row1 = RowRenderData.build("李四", "博士");
            RowRenderData row2 = RowRenderData.build("王五", "博士后");
    
            Map<String,Object> tempMap = new HashMap<>();
    
            tempMap.put("table", new MiniTableRenderData(header, Arrays.asList(row0, row1, row2)));
    
            //获取文件的相对路径  可在控制台打印查看输出结果
            File file = new File(path + "list.docx");
            /*1.根据模板生成文档*/
            XWPFTemplate template = XWPFTemplate.compile(file).render(tempMap);
            response.setContentType("application/octet-stream");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            String fileName = "测试";
            URLEncoder.encode(fileName,"UTF-8");
            response.setHeader("Content-Disposition", "attachment;fileName="+ fileName +".docx");
            response.flushBuffer();
            template.write(response.getOutputStream());
            return success();

    3.4.列表模板{{*var}}

    NumbericRenderData 数据模型。

        @ApiOperation(value = "列表模板")
        @PostMapping("/downloadWord4")
        public Result downloadWord4(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
            List<TextRenderData> tempDataList = new ArrayList<>();
    
            Map<String,Object> tempMap = new HashMap<>();
    
            TextRenderData data0 = new TextRenderData("这是一个列表测试1");
            TextRenderData data2 = new TextRenderData("这是一个列表测试2");
            tempDataList.add(data0);
            tempDataList.add(data2);
    
            // 列表样式支持罗马字符、有序无序等
            // FMT_DECIMAL //1. 2. 3.
            // FMT_DECIMAL_PARENTHESES //1) 2) 3)
            // FMT_BULLET //● ● ●
            // FMT_LOWER_LETTER //a. b. c.
            // FMT_LOWER_ROMAN //i ⅱ ⅲ
            // FMT_UPPER_LETTER //A. B. C.
            Pair<Enum, String> numFmt = NumbericRenderData.FMT_LOWER_ROMAN;
    
            // 列表数据模型
            NumbericRenderData numbericRenderData = new NumbericRenderData(numFmt,tempDataList);
            tempMap.put("list", numbericRenderData);
    
            //获取文件的相对路径  可在控制台打印查看输出结果
            File file = new File(path + "list.docx");
            /*1.根据模板生成文档*/
            XWPFTemplate template = XWPFTemplate.compile(file).render(tempMap);
            response.setContentType("application/octet-stream");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            String fileName = "测试";
            URLEncoder.encode(fileName,"UTF-8");
            response.setHeader("Content-Disposition", "attachment;fileName="+ fileName +".docx");
            response.flushBuffer();
            template.write(response.getOutputStream());
            return success();
        }

    3.5.其他

    还支持很多高级模板功能,详细见官方文档Github源码

    注意:

    • poi-tl v1.5.x是构建在Apache poi3.16+和JDK1.6+上的版本。
    • 高版本的建议使用Apache poi4.0.0+和JDK1.8+,poi-tl 最新文档
    • 一定请注意版本问题,不然出现各种BUG会很麻烦。
    展开全文
  • POI-TL使用及工具类

    千次阅读 2020-04-15 16:16:15
    -- poi-tl --> <!-- http://deepoove.com/poi-tl/ --> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <ver...

    maven依赖

    <!-- poi-tl -->
    		<!-- http://deepoove.com/poi-tl/ -->
    		<dependency>
    			<groupId>com.deepoove</groupId>
    			<artifactId>poi-tl</artifactId>
    			<version>1.7.1</version>
    		</dependency>
    		  <!-- excel工具 -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
            </dependency>
    

    工具类所需目录结构:
    在这里插入图片描述
    核心工具类:

    package com.ruoyi.common.utils.poi;
    
    import com.deepoove.poi.XWPFTemplate;
    import com.deepoove.poi.config.Configure;
    import com.deepoove.poi.data.*;
    import com.deepoove.poi.data.style.Style;
    import com.deepoove.poi.data.style.TableStyle;
    import com.ruoyi.common.utils.poi.custom.CustomListRenderPolicyWP;
    import com.ruoyi.common.utils.poi.custom.CustomTableRenderPolicyWP;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
    
    import java.io.FileOutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * description: PoiTlUtils <br>
     * date: 2020/2/10 14:26 <br>
     * author: <br>
     * version: 1.0 <br>
     */
    public class PoiTlUtils {
    
        public static void main(String[] args){
            Map<String,Object> map = new HashMap<>();
            ArrayList<String[]> strings = new ArrayList<>();
            String[] heard = new String[]{"t1","t2"};
            String[] row1 = new String[]{"r1","r2"};
            String[] row2 = new String[]{"s1","s2"};
            strings.add(row1);
            strings.add(row2);
            MiniTableRenderData miniTableRenderData = list2Table(heard, strings);
            map.put("test",miniTableRenderData);
            try {
                CustomTableRenderPolicy customTableRenderPolicy = new CustomTableRenderPolicy();
                ITableToolsMCImpl tableToolsMC = new ITableToolsMCImpl();
                customTableRenderPolicy.setMergeCells(true);
                customTableRenderPolicy.setiTableToolsMC(tableToolsMC);
                createDocByTemplateName("test.docx","D:\\ruoyi\\resultPath\\test.docx",map,customTableRenderPolicy);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    
        }
    
        /** 功能描述: 根据指定的模板和数据生成docx文件到指定的路径
         * @param templateName
         * @param docPath
         * @param data
        * @return: void
        * @Author: 
        * @Date: 2020/2/20 14:31
        */
        public static void createDocByTemplateName(String templateName,
                                               String docPath,
                                               Object data) throws Exception{
            String docTemplatePath = PoiTlUtils.class.getClassLoader()
                    .getResource("docTemplates/" + templateName).getPath(); // 模板文件的路径
            createDocByTemplate(docTemplatePath,docPath,data,null);
        }
    
        public static void createDocByTemplateName(String templateName,String docPath,
                                               Object data,CustomTableRenderPolicy customTableRenderPolicy) throws Exception{
            String docTemplatePath = PoiTlUtils.class.getClassLoader()
                    .getResource("docTemplates/" + templateName).getPath(); // 模板文件的路径
            createDocByTemplate(docTemplatePath,docPath,data,customTableRenderPolicy);
        }
    
        public static void createDocByTemplate(String docTemplatePath,
                                               String docPath,
                                               Object data,CustomTableRenderPolicy customTableRenderPolicy) throws Exception{
            if(customTableRenderPolicy == null){
                customTableRenderPolicy = new CustomTableRenderPolicy();
                customTableRenderPolicy.setMergeCells(false);
            }
    
            // 自定义表格控件覆盖原有控件
            Configure config = Configure.newBuilder()
                    .addPlugin('#', customTableRenderPolicy)
                    .addPlugin('+', new CustomTableRenderPolicyWP()) // zidingyibaioghhe
                    .addPlugin('>', new CustomListRenderPolicyWP())  // 自定义列表
                    .build();
    
            // 生成报告doc文件
            XWPFTemplate template = XWPFTemplate
                    .compile(docTemplatePath,config)
                    .render(data);
            FileOutputStream out = new FileOutputStream(docPath);
            template.write(out);
            out.flush();
            out.close();
            template.close();
        }
    
    
        public static MiniTableRenderData list2Table(
                String[] headerArr,
                List<String[]> datas){
            // 通用样式
            TableStyle tableStyle = new TableStyle();
            tableStyle.setAlign(STJc.Enum.forInt(2));
    
            RowRenderData header = null;
            if(headerArr != null){
                List<TextRenderData> headerDatas = new ArrayList();
                for (String headerText: headerArr){
                    headerDatas.add(new TextRenderData(headerText));
                }
                 header = new RowRenderData(headerDatas,"eeeeee"); // 表头
            }
    
    
            List<RowRenderData> rows = new ArrayList<>();
            for (String[] dataRow: datas){
                List<TextRenderData> rowData = new ArrayList();
                for (String cellData: dataRow){
                    rowData.add(new TextRenderData( cellData)); // 每行
                }
                RowRenderData row = new RowRenderData(rowData,null); // 行数据
                rows.add(row);
            }
    
    
            MiniTableRenderData tableRenderData = new MiniTableRenderData(header,rows,MiniTableRenderData.WIDTH_A4_MEDIUM_FULL);
    
            return tableRenderData;
        }
    
    
        public static MiniTableRenderData list2Table4Pcr2(
                String[] headerArr,
                List<String[]> datas){
            // 通用样式
            TableStyle tableStyle = new TableStyle();
            tableStyle.setAlign(STJc.Enum.forInt(2));
    
                    RowRenderData header = null;
            if(headerArr != null){
                List<TextRenderData> headerDatas = new ArrayList();
                for (String headerText: headerArr){
                    headerDatas.add(new TextRenderData(headerText));
                }
                 header = new RowRenderData(headerDatas,"d3dce9"); // 表头
            }
    
            String backgroundColor = "ffffff";
            List<RowRenderData> rows = new ArrayList<>();
            for (int i = 0; i < datas.size(); i++) {
                String[] dataRow = datas.get(i);
                List<TextRenderData> rowData = new ArrayList();
                for (String cellData: dataRow){
                    rowData.add(new TextRenderData( cellData)); // 每行
                }
                if (i == datas.size() - 1) backgroundColor = "d3dce9";
                RowRenderData row = new RowRenderData(rowData,backgroundColor); // 行数据
                rows.add(row);
            }
            MiniTableRenderData tableRenderData = new MiniTableRenderData(header,rows,MiniTableRenderData.WIDTH_A4_FULL);
            return tableRenderData;
        }
    
    
        /** 功能描述: map 列表转表格
         * @param headerArr
         * @param cellKeyArr
         * @param mapLists
        * @return: com.deepoove.poi.data.MiniTableRenderData
        * @Author: 
        * @Date: 2020/2/16 16:21
        */
        public static MiniTableRenderData listMap2Table(
                String[] headerArr, String[] cellKeyArr,
                List<Map<String,String>>... mapLists){
    
            List<Map<String,String>> mapList = new ArrayList<>();
            for (List<Map<String,String>> list: mapLists){
                mapList.addAll(list);
            }
    
            // 表头单元格样式
            Style style = new Style();
            style.setBold(true); // 加粗
    
            // 表头样式
            TableStyle headerStyle = new TableStyle();
            headerStyle.setAlign(STJc.Enum.forInt(2)); // 文字居中
            headerStyle.setBackgroundColor("eeeeee");
    
    
            // 内容样式
            TableStyle contentStyle = new TableStyle();
            contentStyle.setAlign(STJc.Enum.forInt(2)); // 文字居中
    
    
            CellRenderData cellRenderData = new CellRenderData();
            List<TextRenderData> headerDatas = new ArrayList();
            for (String headerText: headerArr){
                TextRenderData textRenderData = new TextRenderData(headerText,style);
                headerDatas.add(new TextRenderData(headerText,style));
            }
            RowRenderData header = new RowRenderData(headerDatas,"eeeeee"); // 表头
            header.setRowStyle(headerStyle);
    
    
            List<RowRenderData> rows = new ArrayList<>();
            for (Map<String,String> map: mapList){
                List<TextRenderData> rowData = new ArrayList();
                for (String cellKey: cellKeyArr){
                    rowData.add(new TextRenderData(map.get(cellKey))); // 每行
                }
                RowRenderData row = new RowRenderData(rowData,null); // 行数据
                row.setRowStyle(contentStyle);
                rows.add(row);
            }
    
    
            MiniTableRenderData tableRenderData = new MiniTableRenderData(header,rows,MiniTableRenderData.WIDTH_A4_MEDIUM_FULL);
            tableRenderData.setNoDatadesc("无");
    
            return tableRenderData;
        }
    
    
    
        /* 功能描述: String 列表转带序号的表格
         * @param headerArr 必须是偶数个 [序号 , 名字 ,  序号 , 名字]
         * @param strList
        * @return: com.deepoove.poi.data.MiniTableRenderData
        * @Author: 
        * @Date: 2020/2/15 17:51
        *
        * 例子:  序号  名字   序号  名字
        *         1   张三   3    李四
        *         2   王五   4    赵柳
        */
        public static MiniTableRenderData listStr2TableWithNum(
                String[] headerArr,
                List<String> strList){
    
            Style style = new Style();
            style.setBold(true); // 加粗
    
            // 表头
            List<TextRenderData> headerDatas = new ArrayList();
            for (String headerText: headerArr){
                headerDatas.add(new TextRenderData(headerText,style));
            }
            RowRenderData header = new RowRenderData(headerDatas,"eeeeee"); // 表头
    
    
            // 内容
            // 算出来除去序号多少列
            int leiCount = headerArr.length / 2;
            if (strList.size() < leiCount){
                leiCount = strList.size();
            }
    
            // 算出来应该占多少行
            int hangCount = strList.size()%leiCount==0?strList.size() / leiCount:strList.size() / leiCount +1;
    
            List<RowRenderData> rows = new ArrayList<>();
    
            for (int hangNum = 1; hangNum <= hangCount; hangNum++) {
                int num = hangNum;
                List<TextRenderData> rowData = new ArrayList();
                for (int lieNum = 1; lieNum <= leiCount; lieNum++) {
                    if (strList.size() >= num){
                        rowData.add(new TextRenderData(String.valueOf(num))); // 每行
                        rowData.add(new TextRenderData(strList.get(num - 1))); // 每行
                    }else {
                        rowData.add(new TextRenderData("")); // 每行
                        rowData.add(new TextRenderData("")); // 每行
                    }
    
                    num += hangCount;
                }
                RowRenderData row = new RowRenderData(rowData, null); // 行数据
                rows.add(row);
            }
    
    
            MiniTableRenderData tableRenderData =
                    new MiniTableRenderData(header,rows,MiniTableRenderData.WIDTH_A4_MEDIUM_FULL); // 列宽
    
            return tableRenderData;
        }
    
    
    
    
        /* 功能描述: 文本列表  转 doc列表  带字体颜色
         * @param textList
         * @param color  "FFFFFF"
         * @param fontSize  9 - 小五
        * @return: com.deepoove.poi.data.NumbericRenderData
        * @Author: 
        * @Date: 2020/2/13 20:40
        */
        public static NumbericRenderData list2DocList(List<String> textList,Style style){
            List<TextRenderData> list = new ArrayList<TextRenderData>();
            for (String text: textList){
                TextRenderData data = new TextRenderData(text);
                if (style != null){
                    data.setStyle(style);
                }
                list.add(data);
            }
            return new NumbericRenderData(NumbericRenderData.FMT_DECIMAL,list);  // NumbericRenderData.FMT_DECIMAL 数字列表
        }
    
        public static NumbericRenderData list2DocList(List<String> textList,
                                                      String color,
                                                      int fontSize){
            Style style = new Style();
            style.setColor(color);
            style.setFontSize(fontSize);
            return list2DocList(textList,style);
        }
    
        public static NumbericRenderData list2DocList(List<String> textList){
            return list2DocList(textList,null);
        }
    
    
    
        public static Style createStyle(String fontFamily, int fontSize, String color){
            Style style = new Style();
            if (fontSize != 0) style.setFontSize(fontSize);
            if (fontFamily != null) style.setFontFamily(fontFamily);
            style.setColor(color);
            return style;
        }
    
    }
    
    

    自定义poi-tl模板插件
    CustomTableRenderPolicy:

    package com.ruoyi.common.utils.poi;
    
    import com.deepoove.poi.data.CellRenderData;
    import com.deepoove.poi.data.MiniTableRenderData;
    import com.deepoove.poi.data.RowRenderData;
    import com.deepoove.poi.policy.AbstractRenderPolicy;
    import com.deepoove.poi.render.RenderContext;
    import com.deepoove.poi.util.TableTools;
    import com.deepoove.poi.xwpf.BodyContainer;
    import com.deepoove.poi.xwpf.BodyContainerFactory;
    import com.deepoove.poi.xwpf.NiceXWPFDocument;
    import com.ruoyi.common.utils.StringUtils;
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
    
    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.List;
    
    /** 自定义word表格   -- 自定义poi-tl模板插件
     * description: CustomTableRenderPolicy <br>
     * date: 2020/2/28 13:47 <br>
     * author: <br>
     * version: 1.0 <br>
     */
    public class CustomTableRenderPolicy extends AbstractRenderPolicy<MiniTableRenderData> {
    
        private boolean isMergeCells;
    
        private ITableToolsMC iTableToolsMC;
    
        public ITableToolsMC getiTableToolsMC() {
            return iTableToolsMC;
        }
    
        public void setiTableToolsMC(ITableToolsMC iTableToolsMC) {
            this.iTableToolsMC = iTableToolsMC;
        }
    
        public boolean isMergeCells() {
            return isMergeCells;
        }
    
        public void setMergeCells(boolean mergeCells) {
            isMergeCells = mergeCells;
        }
    
        @Override
        protected void afterRender(RenderContext<MiniTableRenderData> context) {
            // 清空模板标签
            clearPlaceholder(context, true);
        }
    
        @Override
        public void doRender(RenderContext<MiniTableRenderData> context)
                throws Exception {
    
            NiceXWPFDocument doc = context.getXWPFDocument();
            XWPFRun run = context.getRun();
            Object data = context.getData();
            MiniTableRenderData miniTableRenderData = (MiniTableRenderData) data;
    
    
            // 当前位置的容器
            BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
    
    
            List<CellRenderData> headerCellList = miniTableRenderData.getHeader().getCells();
            List<RowRenderData> rowsDataList = miniTableRenderData.getRows();
    
            // 定义行列
            int colCount = headerCellList.size(),
                    rowCount = rowsDataList.size() == 0?rowsDataList.size() + 2:rowsDataList.size() + 1;
            // 定义列宽
    //        String[] colWidthArr = new String[]{"1960","7670","5820","1640","1700"};
    
    
            // 创建表格
            // 当前位置插入表格
            XWPFTable table = bodyContainer.insertNewTable(run, rowCount, colCount);
    
    //        setTableWidth(table,"18790");
    
    
            // 设置表头
            XWPFTableRow headerRow = table.getRow(0);
            setRowHeight(headerRow,"400");
            for (int colNum = 0;colNum < colCount; colNum++){
                XWPFTableCell cell = headerRow.getCell(colNum);
    //            cell = cellCenterY(cell);
                setCellWidthAndXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER,null);
                if (miniTableRenderData.getHeader().getRowStyle() != null){
                    cell.setColor(miniTableRenderData.getHeader().getRowStyle().getBackgroundColor());
                }
                cell.setText(headerCellList.get(colNum).getCellText().getText());
            }
    
            // 向表格添加数据
            for (int rowNum = 1;rowNum < rowCount;rowNum++){
                XWPFTableRow row = table.getRow(rowNum);
                if (rowsDataList.size() > 0){
                    List<CellRenderData> cellDataList = rowsDataList.get(rowNum -1).getCells();
                    for (int colNum = 0;colNum < colCount; colNum++){
                        XWPFTableCell cell = row.getCell(colNum);
                        setCellWidthAndXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER,null);
                        cell.setText(cellDataList.get(colNum).getCellText().getText());
                        if (rowsDataList.get(rowNum -1).getRowStyle() != null){
                            cell.setColor(rowsDataList.get(rowNum -1).getRowStyle().getBackgroundColor());
                        }
                    }
                }else {
                    XWPFTableCell cell = row.getCell(0);
                    setCellWidthAndXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER,null);
                    cell.setText("无数据");
                    TableTools.mergeCellsHorizonal(table, rowNum, 0, colCount - 1); // 水平合并
                }
            }
    
            // 定义表格宽度、边框和样式
            TableTools.widthTable(table, MiniTableRenderData.WIDTH_A4_MEDIUM_FULL, colCount);
            TableTools.borderTable(table, 4);
    
            // TODO 调用XWPFTable API操作表格:data对象可以包含任意你想要的数据,包括图片文本等
            // TODO 调用MiniTableRenderPolicy.Helper.renderRow方法快速方便的渲染一行数据
            // TODO 调用TableTools类方法操作表格,比如合并单元格
            // ......
    //        TableTools.mergeCellsHorizonal(table, 0, 0, 7); // 水平合并
            // 合并第0列的第2行到第4行的单元格
    //        TableTools.mergeCellsVertically(table, 0, 2, 4);
            if(isMergeCells){
                iTableToolsMC.mergeCells(table);
            }
    
        }
    
        private void setTableWidth(XWPFTable table, String width) {
            // 定义表格宽度
            CTTblPr tblPr = table.getCTTbl().getTblPr();
            tblPr.getTblW().setType(STTblWidth.DXA);
            tblPr.getTblW().setW(new BigInteger(width));
        }
    
    
        // 设置列宽和水平垂直居中
        public void setCellWidthAndXYAlign(XWPFTableCell cell, STVerticalJc.Enum typeEnum, STJc.Enum vAlign, String width) {
            CTTc cttc = cell.getCTTc();
            CTTcPr cellPr = cttc.addNewTcPr();
            cellPr.addNewVAlign().setVal(typeEnum);
            cttc.getPList().get(0).addNewPPr().addNewJc().setVal(vAlign);
            if(!StringUtils.isEmpty(width)){
                CTTblWidth tblWidth = cellPr.isSetTcW() ? cellPr.getTcW() : cellPr.addNewTcW();
                tblWidth.setType(STTblWidth.DXA);
                tblWidth.setW(new BigInteger(width));
            }
        }
    
        // 设置表格样式
        public void setCellStyle(XWPFTableCell cell,boolean bold) {
            XWPFParagraph p = cell.addParagraph();
            XWPFRun headRun = p.createRun();
            headRun.setBold(bold);// 是否粗体
        }
    
    
    
        private void setRowHeight(XWPFTableRow row, String height){
                CTTrPr trPr = row.getCtRow().addNewTrPr();
                CTHeight ht = trPr.addNewTrHeight();
                ht.setVal(new BigInteger(height));
        }
    
    
        private List<String[]> getList (){
            List<String[]> list = new ArrayList<>();
            String[] arr1 = new String[]{"1.1","1.2","1.3","1.4","1.5"};
            String[] arr2 = new String[]{"2.1","2.2","2.3","2.4","2.5"};
            String[] arr3 = new String[]{"3.1","3.2","3.3","3.4","3.5"};
            String[] arr4 = new String[]{"4.1","4.2","4.3","4.4","4.5"};
            String[] arr5 = new String[]{"5.1","5.2","5.3","5.4","5.5"};
            list.add(arr1);
            list.add(arr2);
            list.add(arr3);
            list.add(arr4);
            list.add(arr5);
    
            return list;
        }
    }
    
    

    CustomListRenderPolicyWP:

    package com.ruoyi.common.utils.poi.custom;
    
    import com.deepoove.poi.XWPFTemplate;
    import com.deepoove.poi.data.RowRenderData;
    import com.deepoove.poi.data.TextRenderData;
    import com.deepoove.poi.data.style.Style;
    import com.deepoove.poi.policy.AbstractRenderPolicy;
    import com.deepoove.poi.policy.DynamicTableRenderPolicy;
    import com.deepoove.poi.policy.MiniTableRenderPolicy;
    import com.deepoove.poi.policy.RenderPolicy;
    import com.deepoove.poi.render.RenderContext;
    import com.deepoove.poi.template.ElementTemplate;
    import com.deepoove.poi.template.run.RunTemplate;
    import com.deepoove.poi.util.TableTools;
    import com.deepoove.poi.xwpf.BodyContainer;
    import com.deepoove.poi.xwpf.BodyContainerFactory;
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
    
    import java.math.BigInteger;
    import java.util.List;
    
    /** 自定义word表格   -- 自定义poi-tl模板插件
     * description: CustomTableRenderPolicy <br>
     * date: 2020/2/28 13:47 <br>
     * author: <br>
     * version: 1.0 <br>
     */
    public class CustomListRenderPolicyWP extends AbstractRenderPolicy<MyListDataWP> {
    
        @Override
        protected void afterRender(RenderContext<MyListDataWP> context) {
            // 清空模板标签
            clearPlaceholder(context, true);
        }
    
        @Override
        public void doRender(RenderContext<MyListDataWP> context) throws Exception {
            XWPFRun run = context.getRun();
            BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
            XWPFParagraph paragraph = bodyContainer.insertNewParagraph(run);
            MyListDataWP data = context.getData();
    
            paragraphList(paragraph,bodyContainer,data);//
        }
    
    
    
        /** 功能描述: 在当前位置插入多个段落组成的列表
         * @param paragraph
         * @param bodyContainer
         * @param dataWP
        * @return: void
        * @Author: 
        * @Date: 2020/3/25 14:02
        */
        private void paragraphList(XWPFParagraph paragraph,
                                   BodyContainer bodyContainer,
                                   MyListDataWP dataWP){
            List<TextRenderData> textList = dataWP.getTextList();
    
            int firstLineIndent = dataWP.getFirstLineIndent() * 200;
    
            paragraph.setFirstLineIndent(firstLineIndent); // 首行缩进  具体怎么换算的不清楚
            XWPFRun pRun = paragraph.createRun();
    //		p.setAlignment(ParagraphAlignment.CENTER); // 居中
    //		p.setSpacingBefore(1000); // 段前
    
            for (int i = 0; i < textList.size() - 1; i++) {
                TextRenderData text = textList.get(i);
                XWPFParagraph p = bodyContainer.insertNewParagraph(pRun);
                paragraph.setFirstLineIndent(firstLineIndent); // 首行缩进  具体怎么换算的不清楚
    
                XWPFRun r = p.createRun();
                r.setText(text.getText());
                setRunStyle(r,text.getStyle());
            }
    
            TextRenderData text = textList.get(textList.size() - 1);
            pRun.setText(text.getText());
            setRunStyle(pRun,text.getStyle());
        }
    
    
        /* 功能描述: 为 run设置style
         * @param run
         * @param style
        * @return: void
        * @Author: 
        * @Date: 2020/3/25 14:03
        */
        private void setRunStyle(XWPFRun run,Style style){
            if (style != null){
                run.setColor(style.getColor());
                run.setFontFamily(style.getFontFamily());
                if (style.getFontSize() != 0){
                    run.setFontSize(style.getFontSize());
                }
            }
        }
    }
    
    

    CustomTableRenderPolicyWP:

    package com.ruoyi.common.utils.poi.custom;
    
    import com.deepoove.poi.data.RowRenderData;
    import com.deepoove.poi.policy.AbstractRenderPolicy;
    import com.deepoove.poi.policy.DynamicTableRenderPolicy;
    import com.deepoove.poi.policy.MiniTableRenderPolicy;
    import com.deepoove.poi.render.RenderContext;
    import com.deepoove.poi.util.TableTools;
    import com.deepoove.poi.xwpf.NiceXWPFDocument;
    import com.ruoyi.common.utils.StringUtils;
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
    
    import java.math.BigInteger;
    import java.util.List;
    
    /** 自定义word表格   -- 自定义poi-tl模板插件
     * description: CustomTableRenderPolicy <br>
     * date: 2020/2/28 13:47 <br>
     * author: <br>
     * version: 1.0 <br>
     */
    public class CustomTableRenderPolicyWP extends DynamicTableRenderPolicy {
    
        @Override
        public void render(XWPFTable table, Object data) {
    
            if (null == data) return;
            MiniTableDataWP tableDataWP = (MiniTableDataWP) data;
    
            int dataStartRowNum = tableDataWP.getDataStartRowNum(); // 数据从第几行开始填充
            int colCount = tableDataWP.getColCount(); // 总列数
    
            List<RowRenderData> rowDataList = tableDataWP.getRowDataList();
            if (null != rowDataList && rowDataList.size() > 0) {
                table.removeRow(dataStartRowNum);
                // 循环插入行
                for (int i = 0; i < rowDataList.size(); i++) {
                    XWPFTableRow insertNewTableRow = table.insertNewTableRow(dataStartRowNum);
                    for (int j = 0; j < colCount; j++) { // 循环初始化每列的单元格
                        setCellXYAlign(insertNewTableRow.createCell(),STVerticalJc.CENTER,STJc.CENTER); // 内容水平垂直剧中
                    }
                    // 合并单元格
    //                TableTools.mergeCellsHorizonal(table, dataStartRowNum, 0, 3);
                    MiniTableRenderPolicy.Helper.renderRow(table, dataStartRowNum, rowDataList.get(i));
                    dataStartRowNum ++;
                }
    
    
                // 垂直合并
                // TableTools.mergeCellsVertically(table,0,4,5); // 合并  第0列 的 4~5行
                if (tableDataWP.getMergeCellsVerticallyDataList() != null
                        && tableDataWP.getMergeCellsVerticallyDataList().size() > 0){
                    for (MergeCellsVerticallydata mergeCellsVerticallydata: tableDataWP.getMergeCellsVerticallyDataList()){
                        TableTools.mergeCellsVertically(table,
                                mergeCellsVerticallydata.getCellNum(),
                                mergeCellsVerticallydata.getStartRowNum(),
                                mergeCellsVerticallydata.getEndRowNum()); // 垂直合并
                    }
                }
            }else {
                table.removeRow(dataStartRowNum);
                XWPFTableRow insertNewTableRow = table.insertNewTableRow(dataStartRowNum);
                for (int j = 0; j < colCount; j++) { // 循环初始化每列的单元格
                    XWPFTableCell cell = insertNewTableRow.createCell();
                    cell.setText("/");
                    setCellXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER); // 内容水平垂直剧中
                }
            }
        }
    
    
    
        // 设置单元格 水平垂直居中
        public void setCellXYAlign(XWPFTableCell cell, STVerticalJc.Enum typeEnum, STJc.Enum vAlign) {
            CTTc cttc = cell.getCTTc();
            CTTcPr cellPr = cttc.addNewTcPr();
            cellPr.addNewVAlign().setVal(typeEnum);
            cttc.getPList().get(0).addNewPPr().addNewJc().setVal(vAlign);
        }
    }
    
    

    自定义样式相关:
    MergeCellsVerticallydata:

    package com.ruoyi.common.utils.poi.custom;
    
    import lombok.Data;
    
    /** 垂直合并
     * description: mergeCellsVerticallydata <br>
     * date: 2020/3/11 10:50 <br>
     * author: <br>
     * version: 1.0 <br>
     */
    @Data
    public class MergeCellsVerticallydata {
    
        private int cellNum; // 第几列
    
        private int startRowNum; // 开始行
    
        private int endRowNum; // 结束行
    
        public MergeCellsVerticallydata(int cellNum,int startRowNum,int endRowNum){
            this.cellNum = cellNum;
            this.startRowNum = startRowNum;
            this.endRowNum = endRowNum;
        }
    
    }
    
    

    MiniTableDataWP:

    package com.ruoyi.common.utils.poi.custom;
    
    import com.deepoove.poi.data.RowRenderData;
    import lombok.Data;
    
    import java.util.List;
    
    /** 针对自定义poi-tl插件封装的word表格实体
     * description: CustomTableWP <br>
     * date: 2020/3/10 15:15 <br>
     * author:  <br>
     * version: 1.0 <br>
     */
    @Data
    public class MiniTableDataWP {
        private int colCount; // 总列数
        private int dataStartRowNum; // 从第几行开始填充数据
    
        List<RowRenderData> rowDataList; // 数据
    
        List<MergeCellsVerticallydata> mergeCellsVerticallyDataList; // 需要合并行的数据
    
        public MiniTableDataWP(int dataStartRowNum, int colCount, List<RowRenderData> rowDataList){
            this.colCount = colCount;
            this.dataStartRowNum = dataStartRowNum;
            this.rowDataList = rowDataList;
        }
    }
    
    

    MyListDataWP:

    package com.ruoyi.common.utils.poi.custom;
    
    import com.deepoove.poi.data.RowRenderData;
    import com.deepoove.poi.data.TextRenderData;
    import lombok.Data;
    
    import java.util.List;
    
    /* 功能描述: 针对自定义poi-tl插件封装的word列表实体
     * @param null
    * @return:
    * @Author: 
    * @Date: 2020/3/25 13:05
    */
    @Data
    public class MyListDataWP {
        List<TextRenderData> textList;
    
        int firstLineIndent; // 首行缩进
    
    
        public MyListDataWP(List<TextRenderData> textList,int firstLineIndent){
            this.textList = textList;
            this.firstLineIndent = firstLineIndent;
        }
    }
    
    

    使用例子1:

    //这是要输出到word中的数据集map
     Map<String,Object> templateMap = Maps.newHashMap();
                    templateMap.put("age","1");
    //这是生成word的模版路径
    String templateFileName = "pcr/PCR_REPORT_TEMPLATE_8.docx";
     ArrayList<String[]> strings = new ArrayList<>();//这是word中表格行数据集
                        List<GeneraResultsItem> generaResultsItemList = genera.getGeneraResults().getGeneraResultsItemList();//业务代码,查询出要绑定的数据源
                        if(generaResultsItemList != null && !generaResultsItemList.isEmpty()){
                            int len = generaResultsItemList.size() -1;
                            for (int i = 0; i < len; i++) {
                                GeneraResultsItem generaResultsItem = generaResultsItemList.get(i);
                                GeneraResultsItem generaResultsItem1 = generaResultsItemList.get(i+1);
                                GeneraResultsItem generaResultsItem2 = generaResultsItemList.get(i+2);
                                String[] row = new String[]{generaResultsItem.getItemName()+"("+generaResultsItem.getGeneraItemResults()+")"
                                        ,generaResultsItem1.getItemName()+"("+generaResultsItem1.getGeneraItemResults()+")"
                                        ,generaResultsItem2.getItemName()+"("+generaResultsItem2.getGeneraItemResults()+")"};//这是一行数据
                                strings.add(row);//放到数组中
    
                                i += 2;
                            }
                        }
                        List<RowRenderData> rows = PoiTlUtils.list2Table(new String[]{"", "", ""}, strings).getRows();//获取所有的表格数据,第一个参数为表格表头数组
    					//创建自定义插件表格数据源对象
                        MiniTableDataWP miniTableDataWP = new MiniTableDataWP(0,3,rows);
    					//存放到map中
                        templateMap.put("tables",miniTableDataWP);
    

    例子1模版中使用方法:
    普通字段绑定:
    在这里插入图片描述
    表格部分绑定:
    在这里插入图片描述
    例子2:

    templateFileName = "pcr/PCR_REPORT_TEMPLATE_6.docx";//模版路径
                        templateMap.put("age","1");
                        //表格的表头
                        String[] heard = new String[]{"项目","数据(CT)","结果","项目","数据(CT)","结果"};
                        //表的行数据源
                        ArrayList<String[]> strings = new ArrayList<>();
                        List<GeneraResultsItem> generaResultsItemList = genera.getGeneraResults().getGeneraResultsItemList();
                        if(generaResultsItemList != null && !generaResultsItemList.isEmpty()){
                            int size = generaResultsItemList.size();
                            int len = size /2 ;
                            for (int i = 0; i < len; i++) {
                                GeneraResultsItem generaResultsItem = generaResultsItemList.get(i);
                                GeneraResultsItem generaResultsItem1 = generaResultsItemList.get(i+len);
                                String[] row = new String[]{generaResultsItem.getItemName(),"No Ct",generaResultsItem.getGeneraItemResults()
                                        ,generaResultsItem1.getItemName(),"No Ct",generaResultsItem1.getGeneraItemResults()};
                                strings.add(row);
                            }
    
                        }
                        templateMap.put("tables",PoiTlUtils.list2Table(heard,strings));
    

    例子2模版中使用方法:
    普通字段绑定:
    在这里插入图片描述
    表格绑定:
    在这里插入图片描述

    20200702补充基础类:

    
    import com.deepoove.poi.data.CellRenderData;
    import com.deepoove.poi.data.MiniTableRenderData;
    import com.deepoove.poi.data.RowRenderData;
    import com.deepoove.poi.policy.AbstractRenderPolicy;
    import com.deepoove.poi.render.RenderContext;
    import com.deepoove.poi.util.TableTools;
    import com.deepoove.poi.xwpf.BodyContainer;
    import com.deepoove.poi.xwpf.BodyContainerFactory;
    import com.deepoove.poi.xwpf.NiceXWPFDocument;
    import com.ruoyi.common.utils.StringUtils;
    import org.apache.poi.xwpf.usermodel.*;
    import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
    
    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.List;
    
    /** 自定义word表格   -- 自定义poi-tl模板插件
     * description: CustomTableRenderPolicy <br>
     * version: 1.0 <br>
     */
    public class CustomTableRenderPolicy extends AbstractRenderPolicy<MiniTableRenderData> {
    
        private boolean isMergeCells;
    
        private ITableToolsMC iTableToolsMC;
    
        public ITableToolsMC getiTableToolsMC() {
            return iTableToolsMC;
        }
    
        public void setiTableToolsMC(ITableToolsMC iTableToolsMC) {
            this.iTableToolsMC = iTableToolsMC;
        }
    
        public boolean isMergeCells() {
            return isMergeCells;
        }
    
        public void setMergeCells(boolean mergeCells) {
            isMergeCells = mergeCells;
        }
    
        @Override
        protected void afterRender(RenderContext<MiniTableRenderData> context) {
            // 清空模板标签
            clearPlaceholder(context, true);
        }
    
        @Override
        public void doRender(RenderContext<MiniTableRenderData> context)
                throws Exception {
    
            NiceXWPFDocument doc = context.getXWPFDocument();
            XWPFRun run = context.getRun();
            Object data = context.getData();
            MiniTableRenderData miniTableRenderData = (MiniTableRenderData) data;
    
    
            // 当前位置的容器
            BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
    
    
            List<CellRenderData> headerCellList = miniTableRenderData.getHeader().getCells();
            List<RowRenderData> rowsDataList = miniTableRenderData.getRows();
    
            // 定义行列
            int colCount = headerCellList.size(),
                    rowCount = rowsDataList.size() == 0?rowsDataList.size() + 2:rowsDataList.size() + 1;
            // 定义列宽
    //        String[] colWidthArr = new String[]{"1960","7670","5820","1640","1700"};
    
    
            // 创建表格
            // 当前位置插入表格
            XWPFTable table = bodyContainer.insertNewTable(run, rowCount, colCount);
    
    //        setTableWidth(table,"18790");
    
    
            // 设置表头
            XWPFTableRow headerRow = table.getRow(0);
            setRowHeight(headerRow,"400");
            for (int colNum = 0;colNum < colCount; colNum++){
                XWPFTableCell cell = headerRow.getCell(colNum);
    //            cell = cellCenterY(cell);
                setCellWidthAndXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER,null);
                if (miniTableRenderData.getHeader().getRowStyle() != null){
                    cell.setColor(miniTableRenderData.getHeader().getRowStyle().getBackgroundColor());
                }
                cell.setText(headerCellList.get(colNum).getCellText().getText());
            }
    
            // 向表格添加数据
            for (int rowNum = 1;rowNum < rowCount;rowNum++){
                XWPFTableRow row = table.getRow(rowNum);
                if (rowsDataList.size() > 0){
                    List<CellRenderData> cellDataList = rowsDataList.get(rowNum -1).getCells();
                    for (int colNum = 0;colNum < colCount; colNum++){
                        XWPFTableCell cell = row.getCell(colNum);
                        setCellWidthAndXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER,null);
                        cell.setText(cellDataList.get(colNum).getCellText().getText());
                        if (rowsDataList.get(rowNum -1).getRowStyle() != null){
                            cell.setColor(rowsDataList.get(rowNum -1).getRowStyle().getBackgroundColor());
                        }
                    }
                }else {
                    XWPFTableCell cell = row.getCell(0);
                    setCellWidthAndXYAlign(cell,STVerticalJc.CENTER,STJc.CENTER,null);
                    cell.setText("无数据");
                    TableTools.mergeCellsHorizonal(table, rowNum, 0, colCount - 1); // 水平合并
                }
            }
    
            // 定义表格宽度、边框和样式
            TableTools.widthTable(table, MiniTableRenderData.WIDTH_A4_MEDIUM_FULL, colCount);
            TableTools.borderTable(table, 4);
    
            // TODO 调用XWPFTable API操作表格:data对象可以包含任意你想要的数据,包括图片文本等
            // TODO 调用MiniTableRenderPolicy.Helper.renderRow方法快速方便的渲染一行数据
            // TODO 调用TableTools类方法操作表格,比如合并单元格
            // ......
    //        TableTools.mergeCellsHorizonal(table, 0, 0, 7); // 水平合并
            // 合并第0列的第2行到第4行的单元格
    //        TableTools.mergeCellsVertically(table, 0, 2, 4);
            if(isMergeCells){
                iTableToolsMC.mergeCells(table);
            }
    
        }
    
        private void setTableWidth(XWPFTable table, String width) {
            // 定义表格宽度
            CTTblPr tblPr = table.getCTTbl().getTblPr();
            tblPr.getTblW().setType(STTblWidth.DXA);
            tblPr.getTblW().setW(new BigInteger(width));
        }
    
    
        // 设置列宽和水平垂直居中
        public void setCellWidthAndXYAlign(XWPFTableCell cell, STVerticalJc.Enum typeEnum, STJc.Enum vAlign, String width) {
            CTTc cttc = cell.getCTTc();
            CTTcPr cellPr = cttc.addNewTcPr();
            cellPr.addNewVAlign().setVal(typeEnum);
            cttc.getPList().get(0).addNewPPr().addNewJc().setVal(vAlign);
            if(!StringUtils.isEmpty(width)){
                CTTblWidth tblWidth = cellPr.isSetTcW() ? cellPr.getTcW() : cellPr.addNewTcW();
                tblWidth.setType(STTblWidth.DXA);
                tblWidth.setW(new BigInteger(width));
            }
        }
    
        // 设置表格样式
        public void setCellStyle(XWPFTableCell cell,boolean bold) {
            XWPFParagraph p = cell.addParagraph();
            XWPFRun headRun = p.createRun();
            headRun.setBold(bold);// 是否粗体
        }
    
    
    
        private void setRowHeight(XWPFTableRow row, String height){
                CTTrPr trPr = row.getCtRow().addNewTrPr();
                CTHeight ht = trPr.addNewTrHeight();
                ht.setVal(new BigInteger(height));
        }
    
    
        private List<String[]> getList (){
            List<String[]> list = new ArrayList<>();
            String[] arr1 = new String[]{"1.1","1.2","1.3","1.4","1.5"};
            String[] arr2 = new String[]{"2.1","2.2","2.3","2.4","2.5"};
            String[] arr3 = new String[]{"3.1","3.2","3.3","3.4","3.5"};
            String[] arr4 = new String[]{"4.1","4.2","4.3","4.4","4.5"};
            String[] arr5 = new String[]{"5.1","5.2","5.3","5.4","5.5"};
            list.add(arr1);
            list.add(arr2);
            list.add(arr3);
            list.add(arr4);
            list.add(arr5);
    
            return list;
        }
    }
    
    public interface ITableToolsMC {
        void mergeCells(XWPFTable table);
    }
    
    import com.deepoove.poi.util.TableTools;
    import org.apache.poi.xwpf.usermodel.XWPFTable;
    
    
    
    public class ITableToolsMCImpl implements ITableToolsMC {
        @Override
        public void mergeCells( XWPFTable table) {
            // 合并第0列的第0行到第1行的单元格
            TableTools.mergeCellsVertically(table, 0, 0, 1);
        }
    }
    

    如有不足请留言补充

    展开全文
  • poi-tl的使用

    万次阅读 2020-09-17 16:03:31
    1.入门 1.添加依赖 <dependency> <...poi-tl</artifactId> <version>1.7.3</version> </dependency> 2.定义模板 3.代码 import com.deepoove.poi.XWPFTempla
  • 每个实例最后有源码地址:https://github.com/Sayi/poi-tl/tree/v1.9.0 本人配置: jdk:1.8 依赖包: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi<...
  • 学习目标 今天,和大家分享一款Word模板引擎,它可以基于Word模板和数据生成新的文档。...poi-tlpoi template language)是Word模板引擎,基于Word模板和数据生成新的文档。 官方文档:http://deepoove.com/p
  • 学习分享:POI-TL 导出Word复杂表格合并分享

    千次阅读 多人点赞 2021-01-11 15:39:35
    学习分享:POI-TL 导出Word合并表格 文章目录学习分享:POI-TL 导出Word合并表格关于POI-TL 导出Word的一些使用一、前期准备?二、代码演示1.数据结构2.代码示例2.1 写入表头表体的数据2.2 设置合并规则2.3 输出结果...
  • Poi-tl

    2021-05-24 15:41:18
    Poi-tl 使用模板导出wordpoi-tl 官网maven模板制作实体类的创建逻辑代码的编写注意事项 poi-tl 官网 http://deepoove.com/poi-tl/ maven 1.最新版 <dependency> <groupId>com.deepoove</groupId>...
  • 文章目录poi-tl初识相关配置项目目录poi-tl maven依赖doc文件核心代码测试以及结果 poi-tl初识 poi-tlpoi template language)是Word模板引擎,基于Word模板和数据生成新的文档。 具体参考地址 相关配置 项目目录 ...
  • poi-tl 根据 模板 生成 word 文档1.依赖2.模板样式3. java代码4. 效果 1.依赖 <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>...
  • 生成word文档 吃水不忘挑水人 参考链接
  • java通过poi-tl生成表格以及源码分析依赖模板如何动态生成表格参考文档及分析代码 最近导出的word文件要求是越来越多了,而且对样式也做了很多要求,今天参考文档学习了一下普通表格构建表格、动态构建word表格的...
  • POI-tl 官网0. 介绍 - 《poi-tl 基于Apache POI的Word模板引擎》 - 书栈网 · BookStack 如果只是单纯的生成wrod 可以使用poi-tl的最新版本 其内置的区块对主要针对遍历操作, </image><@image><?...
  • poi-tl导出word实现图片环绕方式为浮于在文字上方办法poi-tl简介因为poi-tl暂时不支持设置图片环绕方式,但是可以自定义图片渲染插件 poi-tl简介 poi-tlpoi template language)是Word模板引擎,基于Microsoft ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,478
精华内容 591
关键字:

poi-tl