zoukankan      html  css  js  c++  java
  • Java根据模板生成word

    Java 根据模板生成Word

    书接上文,要做的功能其实是把条形码word文档下载,在生成条形码之后,就是写入word。(条形码生成见此文章

    本文没有采用原始的poi,而是使用了poi-tl,一个poi的封装,可以更好的根据模板生成word文档。

    首先是maven依赖:

    <!-- 截止2021-10-1,最新版是1.10.0,本文同时也使用了最新版的api -->
    <dependency>
        <groupId>com.deepoove</groupId>
        <artifactId>poi-tl</artifactId>
        <version>1.10.0</version>
    </dependency>
    

    引入时注意poi的版本是否冲突(这就是另一个坑了,详见我的下一篇博文(还没写)

    这个是官方文档(注意版本),写的其实已经很详细了,本文只是根据文档写出一个例子。

    本例子主要参考了官方文档中5.3、6.2、8.7、8.8、9.2、9.3等说明。
    先说需求:把生成的条形码放到word中方便打印,每行两个,条形码数量不定。

    首先,由于条形码数量不定,因此模板应该是动态生成的,因此

    首先创建一个基础word模板,如图所示:

    初始word模板

    我们要做的是:初始模板word -> 渲染文字 -> 新模板word -> 渲染图片 -> 带图片的word

    首先生成模板并进行测试:

    package fun.psgame.test.util;
    
    import com.deepoove.poi.XWPFTemplate;
    import com.deepoove.poi.config.Configure;
    import com.deepoove.poi.data.DocumentRenderData;
    import com.deepoove.poi.data.Documents;
    import com.deepoove.poi.data.Paragraphs;
    import com.deepoove.poi.policy.DocumentRenderPolicy;
    import org.springframework.core.io.ClassPathResource;
    
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class WordUtils {
        /**
         * 图片的tag名
         */
        private static final String PICTURE_TAG_NAME = "pic";
    
        /**
         * 初始多个图片的tag名
         */
        private static final String PICTURES_TAG_NAME = "pics";
    
        /**
         * resources下初始模板文件路径
         */
        private static final String TEMPLATE_PATH = "wordTemplate/studentBarCodeTemplate.docx";
    
        /**
         * 生成word
         *
         * @param imageList 要放入word的图片
         * @return pio-tl的word对象
         */
        public static XWPFTemplate generateWord(List<BufferedImage> imageList) {
            ClassPathResource resource = new ClassPathResource(TEMPLATE_PATH);
            InputStream inputStream;
            try {
                inputStream = resource.getInputStream();
            } catch (IOException e) {
                throw new RuntimeException("模板读取失败", e);
            }
    
            // 用初始模板生成新的模板
            Map<String, Object> firstRenderData = new HashMap<>();
            // ① 官方提供的生成方式
            Configure config = Configure.builder().bind(PICTURES_TAG_NAME, new DocumentRenderPolicy()).build();
            // 读取模板
            XWPFTemplate template = XWPFTemplate.compile(inputStream, config);
            // 创建新模板内容
            Documents.DocumentBuilder docBuilder = Documents.of();
            for (int i = 0; i < imageList.size(); i++) {
                docBuilder.addParagraph(Paragraphs.of(getPictureTemplate(i)).create());
            }
            DocumentRenderData documentRenderData = docBuilder.create();
            firstRenderData.put(PICTURES_TAG_NAME, documentRenderData);
    
            template.render(firstRenderData);
    
            return template;
        }
    
        /**
         * 获取图片占位符
         * @return
         */
        private static String getPictureTemplate(int index) {
            // 图片的占位符格式:{{@var}}
            return "{{@" + PICTURE_TAG_NAME + index + "}}";
        }
    }
    
    

    然后可以测试一下生成的模板:

        @Test
        public void generateWordTest() throws IOException {
            List<BufferedImage> imageList = new ArrayList<>();
            int imageCount = 10;
    
            // 准备要放入word的图片
            // 这里使用了之前写的条形码生成工具,实际随便放什么图片都可以
            for (int i = 0; i < imageCount; i++) {
                String stuCode = String.format("%09d", i);
                imageList.add(BarCodeUtils.getBarCodeWithWords(stuCode,
                        "学号:" + stuCode,
                        "三年二班",
                        "王宝强"));
            }
    
            XWPFTemplate xwpfTemplate = WordUtils.generateWord(imageList);
    
            xwpfTemplate.writeToFile("D:/Temp/学生条形码.docx");
        }
    

    可以看一下生成的模板:

    生成的模板

    由于我们想要的是一行放2个图片,使用Documents.DocumentBuilder.addParagraph()方法会出现不想要的换行符

    因此采用自定义插件的方式生成新模板

    把模板的创建方式修改为如下:

    // ② 使用自定义插件的方式生成新模板
    Configure config = Configure.builder().bind(PICTURES_TAG_NAME, (eleTemplate, data, template) -> {
        XWPFRun run = ((RunTemplate) eleTemplate).getRun();
        StringBuilder tempSb = new StringBuilder();
    
        for (int i = 0; i < imageList.size(); i++) {
            // 每两个加一个换行符
            if (i != 0 && (i % 2) == 0) {
                tempSb.append(System.lineSeparator());
            }
    
            tempSb.append(getPictureTemplate(i));
        }
    
    
        run.setText(tempSb.toString(), 0);
    }).build();
    // 读取模板
    XWPFTemplate template = XWPFTemplate.compile(inputStream, config);
    

    这次看一下结果:

    自定义生成的模板

    可以看到换行符没有生效,先不用管,把图片渲染上去再说:

    // 读取模板
    XWPFTemplate template = XWPFTemplate.compile(inputStream, config);
    
    template.render(firstRenderData);
    
    // 创建一个空白文档
    XWPFTemplate xwpfTemplate = XWPFTemplate.create(Documents.of().create());
    // 把渲染好的模板内容加载进去
    xwpfTemplate.reload(template.getXWPFDocument());
    Map<String, Object> secondData = new HashMap<>();
    
    for (int i = 0; i < imageList.size(); i++) {
        secondData.put(PICTURE_TAG_NAME + i,
                Pictures.ofBufferedImage(imageList.get(i), PictureType.JPEG)
                        .size(250, 180)
                        .create());
    }
    
    xwpfTemplate.render(secondData);
    
    return xwpfTemplate;
    

    在模板创建好之后渲染图片:

    看一下最终效果:

    最终word文档效果

    这样生成好之后就可以让浏览器直接下载了。

    最后给一下完整代码:

    WordUtils
    package fun.psgame.test.util;
    
    import com.deepoove.poi.XWPFTemplate;
    import com.deepoove.poi.config.Configure;
    import com.deepoove.poi.data.*;
    import com.deepoove.poi.policy.DocumentRenderPolicy;
    import com.deepoove.poi.policy.RenderPolicy;
    import com.deepoove.poi.template.ElementTemplate;
    import com.deepoove.poi.template.run.RunTemplate;
    import org.apache.poi.xwpf.usermodel.XWPFRun;
    import org.springframework.core.io.ClassPathResource;
    
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class WordUtils {
        /**
         * 图片的tag名
         */
        private static final String PICTURE_TAG_NAME = "pic";
    
        /**
         * 初始多个图片的tag名
         */
        private static final String PICTURES_TAG_NAME = "pics";
    
        /**
         * resources下初始模板文件路径
         */
        private static final String TEMPLATE_PATH = "wordTemplate/studentBarCodeTemplate.docx";
    
        /**
         * 生成word
         *
         * @param imageList 要放入word的图片
         * @return pio-tl的word对象
         */
        public static XWPFTemplate generateWord(List<BufferedImage> imageList) {
            ClassPathResource resource = new ClassPathResource(TEMPLATE_PATH);
            InputStream inputStream;
            try {
                inputStream = resource.getInputStream();
            } catch (IOException e) {
                throw new RuntimeException("模板读取失败", e);
            }
    
            // 用初始模板生成新的模板
            Map<String, Object> firstRenderData = new HashMap<>();
    //        // ① 官方提供的生成方式
    //        Configure config = Configure.builder().bind(PICTURES_TAG_NAME, new DocumentRenderPolicy()).build();
    //        // 读取模板
    //        XWPFTemplate template = XWPFTemplate.compile(inputStream, config);
    //        // 创建新模板内容
    //        Documents.DocumentBuilder docBuilder = Documents.of();
    //        for (int i = 0; i < imageList.size(); i++) {
    //            docBuilder.addParagraph(Paragraphs.of(getPictureTemplate(i)).create());
    //        }
    //        DocumentRenderData documentRenderData = docBuilder.create();
    //        firstRenderData.put(PICTURES_TAG_NAME, documentRenderData);
            // ② 使用自定义插件的方式生成新模板
            Configure config = Configure.builder().bind(PICTURES_TAG_NAME, (eleTemplate, data, template) -> {
                XWPFRun run = ((RunTemplate) eleTemplate).getRun();
                StringBuilder sb = new StringBuilder();
    
                for (int i = 0; i < imageList.size(); i++) {
                    // 每两个加一个换行符
                    // 不加换行符会导致最后一行出现奇怪的布局
                    if (i != 0 && (i % 2) == 0) {
                        sb.append(System.lineSeparator());
                    }
    
                    sb.append(getPictureTemplate(i));
                }
    
                run.setText(sb.toString(), 0);
            }).build();
            // 读取模板
            XWPFTemplate template = XWPFTemplate.compile(inputStream, config);
    
            template.render(firstRenderData);
    
            // 创建一个空白文档
            XWPFTemplate xwpfTemplate = XWPFTemplate.create(Documents.of().create());
            // 把渲染好的模板内容加载进去
            xwpfTemplate.reload(template.getXWPFDocument());
            Map<String, Object> secondData = new HashMap<>();
    
            for (int i = 0; i < imageList.size(); i++) {
                secondData.put(PICTURE_TAG_NAME + i,
                        Pictures.ofBufferedImage(imageList.get(i), PictureType.JPEG)
                                .size(250, 180)
                                .create());
            }
    
            xwpfTemplate.render(secondData);
    
            return xwpfTemplate;
        }
    
        /**
         * 获取图片占位符
         * @return
         */
        private static String getPictureTemplate(int index) {
            // 图片的占位符格式:{{@var}}
            return "{{@" + PICTURE_TAG_NAME + index + "}}";
        }
    }
    
    

    测试用例:

    测试方法
    @Test
    public void generateWordTest() throws IOException {
        List<BufferedImage> imageList = new ArrayList<>();
        int imageCount = 10;
    
        // 准备要放入word的图片
        for (int i = 0; i < imageCount; i++) {
            String stuCode = String.format("%09d", i + 1);
            // 这里的image使用的是之前写好的工具类,实际随便什么图片都可以
            imageList.add(BarCodeUtils.getBarCodeWithWords(stuCode,
                    "学号:" + stuCode,
                    "三年二班",
                    "王宝强" + i));
        }
    
        XWPFTemplate xwpfTemplate = WordUtils.generateWord(imageList);
    
        xwpfTemplate.writeToFile("D:/Temp/学生条形码.docx");
    }
    

    参考:

  • 相关阅读:
    进程的由来
    进程管理逻辑图
    OS的特征

    4.6 路由相关
    设计测试用例时应该考虑哪些方面,即不同的测试用例针对那些方面进行测试?
    如何测试一个纸杯?
    软件产品质量特性是什么?
    一个京东登录的安全漏洞
    文章、书阅读
  • 原文地址:https://www.cnblogs.com/lixin-link/p/15362533.html
Copyright © 2011-2022 走看看