1.介绍
POI-TL 是一个基于 Apache POI 的 Java 库,专注于在 Microsoft Word 文档(.docx 格式)中进行模板填充和动态内容生成。它的全称是 "POI Template Language",旨在简化文档生成过程,特别是在需要根据动态数据生成复杂文档的场景中。POI-TL 通过提供一种简单而强大的模板引擎,使开发者能够轻松地将数据嵌入到 Word 文档中,从而自动化生成报告、合同、发票等文档。
2.原理
POI-TL 的核心原理是将 Word 文档视为模板,并在其中定义占位符。占位符通常以 {{key}}
的形式出现,代表需要动态替换的内容。开发者通过 Java 代码提供一个数据模型,其中包含占位符对应的实际值。POI-TL 使用这些数据来替换模板中的占位符,从而生成最终的文档。
POI-TL 基于 Apache POI 构建,利用 POI 提供的对 Word 文档的低级别操作能力,实现了对文档的高效处理。它支持复杂的文档结构,包括表格、图片、段落等,使得生成的文档不仅内容准确,还能保持良好的格式和样式。
3.代码工程
目标
利用word模版生成简历
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Java-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>poi-tl</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
</dependency>
<!-- Log4j dependencies for logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
模版
在代码仓库的测试包下面
bash
src/test/resources/resume.docx
代码
ExperienceData.java
typescript
package com.et;
import com.deepoove.poi.data.NumberingRenderData;
public class ExperienceData {
private String company;
private String department;
private String time;
private String position;
private NumberingRenderData responsibility;
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public NumberingRenderData getResponsibility() {
return responsibility;
}
public void setResponsibility(NumberingRenderData responsibility) {
this.responsibility = responsibility;
}
}
ResumeDataV2.java
kotlin
package com.et;
import java.util.List;
import com.deepoove.poi.data.NumberingRenderData;
import com.deepoove.poi.data.PictureRenderData;
public class ResumeDataV2 {
private PictureRenderData portrait;
private String name;
private String job;
private String sex;
private String phone;
private String birthday;
private String province;
private String email;
private String address;
private String english;
private String University;
private String rank;
private String education;
private String profession;
private NumberingRenderData stack;
private String hobbies;
private List<ExperienceData> experiences;
public void setPortrait(PictureRenderData portrait) {
this.portrait = portrait;
}
public PictureRenderData getPortrait() {
return this.portrait;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setJob(String job) {
this.job = job;
}
public String getJob() {
return this.job;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getSex() {
return this.sex;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPhone() {
return this.phone;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getBirthday() {
return this.birthday;
}
public void setProvince(String province) {
this.province = province;
}
public String getProvince() {
return this.province;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return this.email;
}
public void setAddress(String address) {
this.address = address;
}
public String getAddress() {
return this.address;
}
public void setEnglish(String english) {
this.english = english;
}
public String getEnglish() {
return this.english;
}
public void setUniversity(String University) {
this.University = University;
}
public String getUniversity() {
return this.University;
}
public void setRank(String rank) {
this.rank = rank;
}
public String getRank() {
return this.rank;
}
public void setEducation(String education) {
this.education = education;
}
public String getEducation() {
return this.education;
}
public void setProfession(String profession) {
this.profession = profession;
}
public String getProfession() {
return this.profession;
}
public void setStack(NumberingRenderData stack) {
this.stack = stack;
}
public NumberingRenderData getStack() {
return this.stack;
}
public void setHobbies(String hobbies) {
this.hobbies = hobbies;
}
public String getHobbies() {
return this.hobbies;
}
public List<ExperienceData> getExperiences() {
return experiences;
}
public void setExperiences(List<ExperienceData> experiences) {
this.experiences = experiences;
}
}
ResumeIterableExample.java
ini
package com.et;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.Numberings;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.data.TextRenderData;
import com.deepoove.poi.data.style.Style;
@DisplayName("Foreach resume example")
public class ResumeIterableExample {
ResumeDataV2 datas = new ResumeDataV2();
@BeforeEach
public void init() {
datas.setPortrait(Pictures.ofLocal("src/test/resources/sayi.png").size(100, 100).create());
datas.setName("卅一");
datas.setJob("BUG工程师");
datas.setPhone("18080809090");
datas.setSex("男");
datas.setProvince("杭州");
datas.setBirthday("2000.08.19");
datas.setEmail("adasai90@gmail.com");
datas.setAddress("浙江省杭州市西湖区古荡1号");
datas.setEnglish("CET6 620");
datas.setUniversity("美国斯坦福大学");
datas.setProfession("生命工程");
datas.setEducation("学士");
datas.setRank("班级排名 1/36");
datas.setHobbies("音乐、画画、乒乓球、旅游、读书\nhttps://github.com/Sayi");
// 技术栈部分
TextRenderData textRenderData = new TextRenderData("SpringBoot、SprigCloud、Mybatis");
Style style = Style.builder().buildFontSize(10).buildColor("7F7F7F").buildFontFamily("微软雅黑").build();
textRenderData.setStyle(style);
datas.setStack(Numberings.of(textRenderData, textRenderData, textRenderData).create());
/*
* {{?experiences}} {{company}} {{department}} {{time}} {{position}}
* {{*responsibility}} {{/experiences}}
*/
List<ExperienceData> experiences = new ArrayList<ExperienceData>();
ExperienceData data0 = new ExperienceData();
data0.setCompany("香港微软");
data0.setDepartment("Office 事业部");
data0.setTime("2001.07-2020.06");
data0.setPosition("BUG工程师");
textRenderData = new TextRenderData("负责生产BUG,然后修复BUG,同时有效实施招聘行为");
textRenderData.setStyle(style);
data0.setResponsibility(Numberings.of(textRenderData, textRenderData).create());
ExperienceData data1 = new ExperienceData();
data1.setCompany("自由职业");
data1.setDepartment("OpenSource 项目组");
data1.setTime("2015.07-2020.06");
data1.setPosition("研发工程师");
textRenderData = new TextRenderData("开源项目的迭代和维护工作");
textRenderData.setStyle(style);
TextRenderData textRenderData1 = new TextRenderData("持续集成、Swagger文档等工具调研");
textRenderData1.setStyle(style);
data1.setResponsibility(Numberings.of(textRenderData, textRenderData1, textRenderData).create());
experiences.add(data0);
experiences.add(data1);
experiences.add(data0);
experiences.add(data1);
datas.setExperiences(experiences);
}
@Test
public void testResumeExample() throws Exception {
XWPFTemplate template = XWPFTemplate.compile("src/test/resources/resume.docx").render(datas);
FileOutputStream out = new FileOutputStream("target/out_example_resume_iterable.docx");
template.write(out);
out.flush();
out.close();
template.close();
}
}
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- github.com/Harries/Jav...(POI-TL )
4.测试
运行测试代码,打开生成的文件(target/out_example_resume_iterable.docx)如下图展示