Description of Freemarker
Freemarker is a template engine, a generic tool based on template generate static files. It is a development package, or class library, for Java programmers. It is not for end users, but for programmers Provides an app that can be embedded in their developed products.
Freemarker generate static page, you first need to use the template page you defined, which can be the most common html, or a nested freemarker value expression, tags or custom tags, etc. Then read the template page in the background, parse the tags in it to complete the corresponding operation, and then use key-value pairs to pass parameters to replace the value expression in the template, After that, generate a new HTML page based on the configured path to achieve the static purpose of the visit.
Create Freemarker word Template
Create a word document, enter the content, replace it with ${}, and save it as word xml document type
Import the xml file into the project, and modify the Freemarker tag according to the specific business logic
eg:
<#list users as user> <w:p> <w:pPr> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> </w:pPr> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> <w:t>Name: ${user.name}</w:t> </w:r> </w:p> <w:p> <w:pPr> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> </w:pPr> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> <w:t>gender: ${user.gender}</w:t> </w:r> </w:p> <w:p> <w:pPr> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> </w:pPr> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> <w:t>age: ${user.age}</w:t> </w:r> <w:bookmarkStart w:id="0" w:name="_GoBack"/> <w:bookmarkEnd w:id="0"/> </w:p> <#if user_has_next> <w:p> <w:pPr> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> </w:pPr> <w:bookmarkStart w:id="0" w:name="_GoBack"/> <w:bookmarkEnd w:id="0"/> <w:r> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="Song Dynasty" w:hAnsi="Song Dynasty" w:eastAsia="Song Dynasty" w:cs="Song Dynasty"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> </w:rPr> <w:br w:type="page"/> </w:r> </w:p> </#if> </#list> <w:sectPr> <w:pgSz w:w="11906" w:h="16838"/> <w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/> <w:cols w:space="425" w:num="1"/> <w:docGrid w:type="lines" w:linePitch="312" w:charSpace="0"/> </w:sectPr>
guide package
<!-- freemarker,generate doc --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.4.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.docx4j/docx4j --> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j</artifactId> <version>6.0.1</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j-export-fo</artifactId> <version>8.1.6</version> </dependency> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j-core</artifactId> <version>8.1.6</version> </dependency> <dependency> <groupId>org.docx4j</groupId> <artifactId>docx4j-JAXB-ReferenceImpl</artifactId> <version>8.1.6</version> </dependency>
accomplish
Directory Structure:
Code:
package com.zl.test.service.impl; import com.zl.test.service.TestService; import com.zl.test.util.DocUtils; import com.zl.test.vo.UserVO; import freemarker.template.Configuration; import freemarker.template.Template; import lombok.extern.slf4j.Slf4j; import org.docx4j.Docx4J; import org.docx4j.convert.out.FOSettings; import org.docx4j.fonts.IdentityPlusMapper; import org.docx4j.fonts.Mapper; import org.docx4j.fonts.PhysicalFonts; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.InputStreamSource; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Service @Slf4j public class TestServiceImpl implements TestService { /** * Generate docx documentation */ @Override public void downloadDocx(HttpServletResponse response) throws Exception { Configuration configuration = new Configuration(Configuration.VERSION_2_3_30); configuration.setDefaultEncoding("utf-8"); configuration.setClassForTemplateLoading(DocUtils.class, "/"); Template template = configuration.getTemplate("templates/test.xml"); StringWriter sw = new StringWriter(); Writer writer = new BufferedWriter(sw, 1024); template.process(getData(), writer); InputStreamSource streamSource = new ByteArrayResource(sw.toString().getBytes(StandardCharsets.UTF_8)); InputStream inputStream = streamSource.getInputStream(); String fileName = "test documentation.docx"; response.setCharacterEncoding("utf-8"); fileName = URLEncoder.encode(fileName, "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); OutputStream out = response.getOutputStream(); byte[] bytes = new byte[1024]; while ((inputStream.read(bytes)) != -1) { out.write(bytes);// data input } out.flush(); out.close(); inputStream.close(); } /** * Generate pdf document */ @Override public void downloadPdf(HttpServletResponse response) throws Exception { Configuration configuration = new Configuration(Configuration.VERSION_2_3_30); configuration.setDefaultEncoding("utf-8"); configuration.setClassForTemplateLoading(DocUtils.class, "/"); Template template = configuration.getTemplate("templates/test.xml"); StringWriter sw = new StringWriter(); Writer writer = new BufferedWriter(sw, 1024); template.process(getData(), writer); InputStreamSource streamSource = new ByteArrayResource(sw.toString().getBytes(StandardCharsets.UTF_8)); InputStream inputStream = streamSource.getInputStream(); WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(inputStream); Mapper fontMapper = new IdentityPlusMapper(); fontMapper.put("Song Dynasty", PhysicalFonts.get("SimSun")); fontMapper.put("Song Dynasty (Chinese text)", PhysicalFonts.get("SimSun")); mlPackage.setFontMapper(fontMapper); String fileName = "test documentation.pdf"; response.setCharacterEncoding("utf-8"); fileName = URLEncoder.encode(fileName, "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName); OutputStream out = response.getOutputStream(); // docx to pdf FOSettings foSettings = Docx4J.createFOSettings(); foSettings.setWmlPackage(mlPackage); Docx4J.toFO(foSettings, out, Docx4J.FLAG_EXPORT_PREFER_XSL); out.flush(); out.close(); } private Map<String, Object> getData() { List<UserVO> list = new ArrayList<>(0); for (int i = 0; i < 3; i++) { UserVO user = new UserVO(); user.setName("zhangsan" + i); user.setAge(20 + i); user.setGender("male"); list.add(user); } Map<String, Object> dataMap = new HashMap<>(0); dataMap.put("users", list); return dataMap; } }
Note when using the list: the users in the template pass in the map object, the user must be an entity object, not a map object, see the getData() method for details
generate results
Freemarker part syntax
The specific grammar you need to use can be Baidu
list
<#list nameList as name> ${name} </#list>
Equivalent to
for (String name : nameList) { System.out.println(name); }
if
<#if (name=="zhangsan")> my name is ${name} </#if>
Equivalent to
if("zhangsan".equals(name)){ System.out.println("my name is zhangsan"); }
include
This import tag is very useful, especially for page reuse.
<#include "default.html"/>
if...elseif...else
<#if (name=="zhangsan")> my name is zhangsan <#elseif (name=="lisi")> <#--note that there is no return but at the end --> my name is lisi <#else> my name is wangwu </#if>
Equivalent to
if("zhangsan".equals(name)){ System.out.println("my name is zhangsan"); }else if("lisi".equals(name)){ System.out.println("my name is lisi"); }else{ my name is wangwu }
map
<#list usersMap?keys as key> key------${key}:value------${usersMap[key]!("null")} </#list>
fremarker does not support null, you can use ! to replace the empty value.
You can also give a default value, value-----${usersMap[key]?default("null")}
You can also judge whether it is null before output, <#if usersMap[key]??></#if>
list handles the first and last elements
show index _index get subscript
<#list nameList as name> index: ${name_index},value: ${name} </#list>
Variable name + _index, you can indicate the current loop to the number of items
For the first element processing, use _index=0
<#if name_index=0>...</#if>
To determine whether it is the last item, use _has_next
eg: Sometimes it is necessary to remove the comma from the last item of the loop
<#list nameList as name> ${name}<#if name_has_next>,</#if> </#list>
Output results: zhangsan, lisi, wangwu
break to exit the loop
<#list nameList as name> ${name} <#if name=="lisi"><#break></#break> </#list>