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");
}

參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM