使用freemarker引擎动态生成word文件
使用freemarker引擎动态生成word文件
- 0 前言
- 1 工具与环境配置
- 1.1 sublime
- 1.2 依赖包
- 1.3 word模版ftl文件生成步骤
- 2 Word Xml基本语法介绍
- 3 动态数据填充
- 单元测试
- ftl模板
- 4 动态表格生成
- 单元测试
- ftl模板
- 5 动态目录与动态标题结构
- 单元测试
- ftl模板
- 6 插入图片
- 单元测试
- ftl模板
0 前言
本文使用freemarker模板引擎动态建立word文件,可以实现数据填充,文档结构和目录的变化,表格生成等功能,使用WPS2019,jdk版本1.8.0_141,maven版本3.1.1,freemarker版本2.3.23,idea版本Community Edition 2018.1.1。
1 工具与环境配置
1.1 sublime
安装sublime,选择Preferences–>Package Control–>Install Package–>安装HTML/CSS/JS Prettify。
1.2 依赖包
以maven工程为例,新建项目,在pom.xml文件中引入freemarker、junit、lombok依赖
<dependency><groupId>freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.23</version>
</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.8</version>
</dependency>
1.3 word模版ftl文件生成步骤
freemarker使用专门的模板文件ftl(FreeMarker Template Language)格式,为了快速建立word模板文件,可以采用反编译的方式。
- 新建一个doc格式的word文档;
- 另存为xml格式文件,注意并非word xml文件;
- 修改后缀名为ftl;
- 使用sublime打开和编辑,在空白处右键,HTML/CSS/JS Prettify–>Prettify Code可以格式化代码。
2 Word Xml基本语法介绍
ftl文件采用word xml格式的语法。以这种格式存储的文件可以通过其他软件的编译创建word文档,不需要用专门的文稿软件打开。一个简单ftl文件如下所示
<?xml version="1.0"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"><w:body><w:p><w:r><w:t>tyler</w:t></w:r></w:p></w:body>
</w:wordDocument>
文档内容包含在<w:body>
标签之间。<w:p>
、<w:r>
、<w:t>
是三个基本标签。
<w:p>
标签表示一个段落,段落属性用<w:pPr>
来指定,一个段落中可以包含很多“执行段”<w:r>
。在执行段中,包含文本、制表符、回车符等特殊符号。<w:rPr>
可以指定该执行段的属性。<w:t>
之间包含真实的文本。
参考资料officeopenxml
3 动态数据填充
很多时候,人们会制定一些模板,如会议通知等,仅在会议时间、地点处留出空白,这样下次使用的时候只需要填写时间、地点就可以快速生成一个会议通知。因此我们首先实现文档内数据内容的动态填充。新建maven工程,工程目录如下
建立word工具类WordUtil,按照路径读取已有的ftl模板
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.*;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;@Getter
@Setter
public class WordUtil {private Configuration configure;public WordUtil() {configure = new Configuration(Configuration.VERSION_2_3_23);configure.setDefaultEncoding("utf-8");}public boolean createWord(Map<String, Object> dataMap, String inputFilePath, String outFilePath) {boolean flag = false;try {TemplateLoader templateLoader;templateLoader=new FileTemplateLoader(new File("./"));configure.setTemplateLoader(templateLoader);Template template=configure.getTemplate(inputFilePath,"UTF-8");Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outFilePath)), "utf-8"), 10240);template.process(dataMap, out);out.close();flag = true;} catch (Exception e) {e.printStackTrace();}return flag;}
}
建立Person模型
import lombok.Getter;
import lombok.Setter;@Setter
@Getter
public class Person {String name;String age;String gender;public Person(String name, String age, String gender) {this.name = name;this.age = age;this.gender = gender;}
}
单元测试
建立单元测试,调用WordUtil的createWord方法。其入参为存储待填充的数据的map、ftl的存储路径、生成文档的路径。文件名以时间戳命名,避免每次生成文件覆盖。
import com.tyler.freemarker.model.Person;
import com.tyler.freemarker.WordUtil;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.*;public class WordGenerateTest {@Testpublic void variableTest() {SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");Date date = new Date(System.currentTimeMillis());String outputFileName = formatter.format(date);String inputFilePath = "./src/main/resources/template.ftl";String outFilePath = "./src/main/resources/"+outputFileName+".doc";WordUtil wu = new WordUtil();Map<String, Object> map = new HashMap<>();Person person = new Person("Tyler", "18", "male");map.put("person", person);wu.createWord(map, inputFilePath, outFilePath);}
}
ftl模板
ftl模板中留出了Person的属性的位置,可以通过读取map的数据填充进去。在freemarker中,${variable}
可以获取变量variable
的值,person
是传进来Person类实例的一个key值,通过key值访问到这个实例。和Java一样,类的属性可以通过.
操作符获取。这样就建立了Java类与ftl中数据的映射,实现了对同一个模板的复用,给定不同的数据可以生成很多相同格式的word文档。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core" xmlns:aml="http://schemas.microsoft.com/aml/2001/core" xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no" xml:space="preserve"><w:body><w:p><w:r><w:t>姓名:${person.name}</w:t></w:r></w:p><w:p><w:r><w:t>年龄:${person.age}</w:t></w:r></w:p><w:p><w:r><w:t>性别:${person.gender}</w:t></w:r></w:p></w:body>
</w:wordDocument>
生成结果展示
4 动态表格生成
表格是数据展示的重要方式。在可以填充数据的基础上,可以将这些数据以表格的形式展示。比如用一个表格统计公司员工的基本信息,姓名、年龄、性别。只需要在map中存储一个List<Person>
,再设计一个与之对应的表格并留出空白就好。
但是另一个公司也想用这套模板,但是两个公司的员工数量不同,因此需要修改表格的行数。接下来实现表格的动态生成,行数与List
的尺寸对应。这就需要用到循环语句。
freemarker可以设定计数循环,如
<#list 1..5 as i><w:t>${i}</w:t>
</#list>
也可以直接循环List
的内容
<#list personList as pl><w:t>${pl.name}</w:t>
</#list>
此处选择第二种,直接循环personList
的元素。建立单元测试,生成一个List<Person>
作为数据传输给ftl模板。
单元测试
import com.tyler.freemarker.model.Person;
import com.tyler.freemarker.WordUtil;
import org.junit.Test;
import java.text.SimpleDateFormat;
import java.util.*;public class WordGenerateTest {@Testpublic void tableTest() {SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");Date date = new Date(System.currentTimeMillis());String outputFileName = formatter.format(date);String inputFilePath = "./src/main/resources/template.ftl";String outFilePath = "./src/main/resources/"+outputFileName+".doc";WordUtil wu = new WordUtil();Map<String, Object> map = new HashMap<>();List<Person> personList = new ArrayList<>();personList.add(new Person("Tyler", "18", "male"));personList.add(new Person("Tom", "20", "male"));personList.add(new Person("Marry", "19", "female"));map.put("personList", personList);wu.createWord(map, inputFilePath, outFilePath);}
}
ftl模板
freemarker中,表格内容在<w:tbl> </w:tbl>
中间,此处的ftl模板包含了很多表格的属性设置,在应用过程中,可以按照想要的表格样式先建立一个带有空表格的word文档,再反编译成ftl模板,避免手动设置属性。这里只实现了动态的行数,列数同理。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core" xmlns:aml="http://schemas.microsoft.com/aml/2001/core" xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no" xml:space="preserve"><w:body><w:tbl><w:tblPr><w:tblStyle w:val="a63" /><w:tblW w:w="9243" w:type="dxa" /><w:tblInd w:w="0" w:type="dxa" /><w:tblBorders><w:top w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:left w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:bottom w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:right w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:insideH w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:insideV w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /></w:tblBorders><w:tblLayout w:type="Fixed" /><w:tblCellMar><w:top w:w="0" w:type="dxa" /><w:left w:w="108" w:type="dxa" /><w:bottom w:w="0" w:type="dxa" /><w:right w:w="108" w:type="dxa" /></w:tblCellMar></w:tblPr><w:tblGrid><w:gridCol w:w="3499" /><w:gridCol w:w="5744" /></w:tblGrid><!-->以下开始循环内容,list的每个元素为一行,总共三行<--><#list personList as pl><w:tr><w:tblPrEx><w:tblBorders><w:top w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:left w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:bottom w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:right w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:insideH w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /><w:insideV w:val="single" w:sz="4" wx:bdrwidth="10" w:space="0" w:color="auto" /></w:tblBorders><w:tblCellMar><w:top w:w="0" w:type="dxa" /><w:left w:w="108" w:type="dxa" /><w:bottom w:w="0" w:type="dxa" /><w:right w:w="108" w:type="dxa" /></w:tblCellMar></w:tblPrEx><w:trPr><w:trHeight w:val="361" w:h-rule="atLeast" /></w:trPr><w:tc><w:tcPr><w:tcW w:w="3499" w:type="dxa" /><w:shd w:val="clear" w:color="auto" w:fill="auto" /><w:noWrap w:val="0" /><w:vAlign w:val="center" /></w:tcPr><w:p><w:pPr><w:spacing w:before-lines="50" w:after-lines="50" w:line="240" w:line-rule="auto" /><w:jc w:val="left" /><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="default" /><w:color w:val="auto" /><w:sz-cs w:val="21" /></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:cs="宋体" w:hint="fareast" /><w:color w:val="auto" /><w:kern w:val="0" /><w:sz-cs w:val="21" /></w:rPr><w:t>${pl.name}</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:w="5744" w:type="dxa" /><w:shd w:val="clear" w:color="auto" w:fill="auto" /><w:noWrap w:val="0" /><w:vAlign w:val="center" /></w:tcPr><w:p><w:pPr><w:spacing w:before-lines="50" w:after-lines="50" w:line="240" w:line-rule="auto" /><w:jc w:val="left" /><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="default" /><w:sz-cs w:val="21" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="fareast" /><w:sz-cs w:val="21" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr><w:t>${pl.age}</w:t></w:r></w:p></w:tc><w:tc><w:tcPr><w:tcW w:w="5744" w:type="dxa" /><w:shd w:val="clear" w:color="auto" w:fill="auto" /><w:noWrap w:val="0" /><w:vAlign w:val="center" /></w:tcPr><w:p><w:pPr><w:spacing w:before-lines="50" w:after-lines="50" w:line="240" w:line-rule="auto" /><w:jc w:val="left" /><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="default" /><w:sz-cs w:val="21" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="fareast" /><w:sz-cs w:val="21" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr><w:t>${pl.gender}</w:t></w:r></w:p></w:tc></w:tr></#list></w:tbl></w:body>
</w:wordDocument>
生成结果展示
5 动态目录与动态标题结构
使用循环结构,我们还可以建立动态的文档框架。一个文档的框架由一级标题、二级标题等确定。如果各级标题的数量和内容也由List
控制,那么文档就可以实现动态结构。同理也可以实现动态的目录。
首先建立一个Company类做为一级标题,每个Company包含一个长度不等的List<Person>
。
import lombok.Getter;
import lombok.Setter;
import java.util.List;@Getter
@Setter
public class Company {String name;List<Person> personList;public Company(String name, List<Person> personList) {this.name = name;this.personList = personList;}
}
单元测试
建立单元测试
import com.tyler.freemarker.FigureUtil;
import com.tyler.freemarker.WordUtil;
import com.tyler.freemarker.model.Company;
import com.tyler.freemarker.model.Person;
import org.junit.Test;import java.text.SimpleDateFormat;
import java.util.*;public class WordGenerateTest {@Testpublic void titleTest() {Random r = new Random();SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");Date date = new Date(System.currentTimeMillis());String outputFileName = formatter.format(date);String inputFilePath = "./src/main/resources/template.ftl";String outFilePath = "./src/main/resources/"+outputFileName+".doc";WordUtil wu = new WordUtil();Map<String, Object> map = new HashMap<>();List<Company> companyList = new ArrayList<>();for (int i = 0; i < r.nextInt(5)+2; i++) {List<Person> personList = new ArrayList<>();for (int j = 0; j <r.nextInt(4)+2 ; j++) {Person person = new Person("name"+r.nextInt(15), ""+r.nextInt(50), "male");personList.add(person);}Company company = new Company("company"+r.nextInt(13), personList);companyList.add(company);}map.put("companyList", companyList);wu.createWord(map, inputFilePath, outFilePath);}}
ftl模板
ftl模版文件,此处使用了嵌套的双层循环,外层循环一级标题,内层循环二级标题。目录与标题之间需要建立超链接,点击目录可以跳转正文标题。目录处<w:instrText> HYPERLINK \l ${cl.name} </w:instrText>
是建立超链接,<w:instrText> PAGEREF ${cl.name} </w:instrText>
是将超链接与页码关联。这两个地方的索引 ${cl.name}
必须和标题的<aml:annotation aml:id="0" w:type="Word.Bookmark.Start" w:name="${cl.name}" />
一致,才能建立超链接。因此为了避免索引冲突,这里选取名字作为索引值,也可以自行设定其他不会冲突的字符串,保证唯一性就可以。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"xmlns:v="urn:schemas-microsoft-com:vml"xmlns:w10="urn:schemas-microsoft-com:office:word"xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core"xmlns:aml="http://schemas.microsoft.com/aml/2001/core"xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint"xmlns:o="urn:schemas-microsoft-com:office:office"xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no"xml:space="preserve"><w:body><wx:sect><w:p><w:pPr><w:spacing w:before="0" w:before-lines="0" w:after="0" w:after-lines="0" w:line="240" w:line-rule="auto" /><w:ind w:left="0" w:left-chars="0" w:right="0" w:right-chars="0" w:first-line="0" w:first-line-chars="0" /><w:jc w:val="center" /></w:pPr><w:r><w:rPr><w:rFonts w:ascii="宋体" w:h-ansi="宋体" w:fareast="宋体" w:hint="default" /><w:sz w:val="21" /></w:rPr><w:t>目录</w:t></w:r></w:p><#list companyList as cl><w:p><w:pPr><w:pStyle w:val="a4" /><w:tabs><w:tab w:val="right" w:leader="dot" w:pos="8306" /></w:tabs></w:pPr><w:r><w:fldChar w:fldCharType="begin" /></w:r><w:r><w:instrText>TOC \o "1-3" \h \u </w:instrText></w:r><w:r><w:fldChar w:fldCharType="separate" /></w:r><w:r><w:fldChar w:fldCharType="begin" /></w:r><w:r><w:instrText> HYPERLINK \l ${cl.name} </w:instrText></w:r><w:r><w:fldChar w:fldCharType="separate" /></w:r><w:r><w:rPr><w:rFonts w:hint="fareast" /><w:lang w:fareast="ZH-CN" /></w:rPr><w:t>${cl.name}</w:t></w:r><w:r><w:tab /></w:r><w:r><w:fldChar w:fldCharType="begin" /></w:r><w:r><w:instrText> PAGEREF ${cl.name} </w:instrText></w:r><w:r><w:fldChar w:fldCharType="separate" /></w:r><w:r><w:t>1</w:t></w:r><w:r><w:fldChar w:fldCharType="end" /></w:r><w:r><w:fldChar w:fldCharType="end" /></w:r></w:p><#list cl.getPersonList() as pl><w:p><w:pPr><w:pStyle w:val="a5" /><w:tabs><w:tab w:val="right" w:leader="dot" w:pos="8306" /></w:tabs></w:pPr><w:r><w:fldChar w:fldCharType="begin" /></w:r><w:r><w:instrText> HYPERLINK \l ${pl.name} </w:instrText></w:r><w:r><w:fldChar w:fldCharType="separate" /></w:r><w:r><w:rPr><w:rFonts w:hint="fareast" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr><w:t>${pl.name}</w:t></w:r><w:r><w:tab /></w:r><w:r><w:fldChar w:fldCharType="begin" /></w:r><w:r><w:instrText> PAGEREF ${pl.name} </w:instrText></w:r><w:r><w:fldChar w:fldCharType="separate" /></w:r><w:r><w:t>1</w:t></w:r><w:r><w:fldChar w:fldCharType="end" /></w:r><w:r><w:fldChar w:fldCharType="end" /></w:r></w:p></#list></#list><w:p><w:r><w:fldChar w:fldCharType="end" /></w:r></w:p><#list companyList as cl><w:p><w:pPr><w:pStyle w:val="2" /><w:rPr><w:rFonts w:hint="fareast" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr></w:pPr><aml:annotation aml:id="0" w:type="Word.Bookmark.Start" w:name="${cl.name}" /><w:r><w:rPr><w:rFonts w:hint="fareast" /><w:lang w:fareast="ZH-CN" /><w:sz w:val="50" /></w:rPr><w:t>${cl.name}</w:t></w:r><aml:annotation aml:id="0" w:type="Word.Bookmark.End" /></w:p><#list cl.getPersonList() as pl><w:p><w:pPr><w:pStyle w:val="3" /><w:rPr><w:rFonts w:hint="fareast" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr></w:pPr><aml:annotation aml:id="1" w:type="Word.Bookmark.Start" w:name="${pl.name}" /><w:r><w:rPr><w:rFonts w:hint="fareast" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /><w:sz w:val="30" /></w:rPr><w:t>${pl.name}</w:t></w:r><aml:annotation aml:id="1" w:type="Word.Bookmark.End" /></w:p></#list></#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="720" /><w:docGrid w:type="lines" w:line-pitch="312" /></w:sectPr></wx:sect></w:body>
</w:wordDocument>
生成结果展示
注:上述生成的文件中,暂时无法做到更新页码,只能手动打开word文件,右键–>更新域–>更新整个目录。
目录更新的实现思路可以参考:代码操作Word时,目录自动更新的两种方法
6 插入图片
插入word的图片要转码成base64码,才能写入ftl文件中,因此先建立转码处理的接口。生成的base64码以字符串形式存储。
import java.util.Base64;
import java.util.Base64.Encoder;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;public class FigureUtil {public static String getImageStr(String imgFilePath) {InputStream in = null;byte[] data = null;try {in = new FileInputStream(imgFilePath);int avai = in.available();data = new byte[avai];in.read(data);in.close();} catch (Exception e) {e.printStackTrace();}finally {if(in !=null){try{in.close();} catch(IOException e) {e.printStackTrace();}}}Encoder encoder = Base64.getEncoder();;return encoder.encodeToString(data);}
}
单元测试
建立单元测试
import com.tyler.freemarker.FigureUtil;
import com.tyler.freemarker.WordUtil;
import org.junit.Test;import java.text.SimpleDateFormat;
import java.util.*;public class WordGenerateTest {@Testpublic void figureTest() {String imgFilePath = "./src/main/resources/1.jpg";SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");Date date = new Date(System.currentTimeMillis());String outputFileName = formatter.format(date);String inputFilePath = "./src/main/resources/template.ftl";String outFilePath = "./src/main/resources/"+outputFileName+".doc";WordUtil wu = new WordUtil();Map<String, Object> map = new HashMap<>();String img = FigureUtil.getImageStr(imgFilePath);map.put("img", img);wu.createWord(map, inputFilePath, outFilePath);}}
ftl模板
ftl模版文件,base64码非常长,这里使用img来代替base64码字符串
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"xmlns:v="urn:schemas-microsoft-com:vml"xmlns:w10="urn:schemas-microsoft-com:office:word"xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core"xmlns:aml="http://schemas.microsoft.com/aml/2001/core"xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint"xmlns:o="urn:schemas-microsoft-com:office:office"xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"w:macrosPresent="no" w:embeddedObjPresent="no"w:ocxPresent="no" xml:space="preserve"><w:body><wx:sect><w:p><w:pPr><w:rPr><w:rFonts w:hint="default" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:hint="default" /><w:lang w:val="EN-US" w:fareast="ZH-CN" /></w:rPr><w:pict><w:binData w:name="wordml://1.png">${img}</w:binData><v:shape id="_x0000_s1026" o:spt="75" alt="1" type="#_x0000_t75"style="height:140.9pt;width:211.35pt;" filled="f"o:preferrelative="t" stroked="f" coordsize="21600,21600"><v:path /><v:fill on="f" focussize="0,0" /><v:stroke on="f" /><v:imagedata src="wordml://1.png" o:title="1" /><o:lock v:ext="edit" aspectratio="t" /><w10:wrap type="none" /><w10:anchorlock /></v:shape></w:pict></w:r></w:p><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="720" /><w:docGrid w:type="lines" w:line-pitch="312" /></w:sectPr></wx:sect></w:body>
</w:wordDocument>
生成结果展示
建议预先生成模版的时候随便插入一个图片占位,并设置好格式和位置。
使用freemarker引擎动态生成word文件相关推荐
- Java中利用freemarker模板动态生成word含表格
最近公司有导出word的需求,由于word的样式有的很复杂所以记录一下Java中利用freemarker模板动态生成word含表格,以防以后忘记. 1.word表格的模板 删掉无用的数据留下基础的样式 ...
- PageOffice动态生成Word文件并转换为PDF
说明:PageOffice是客户端插件,做不到纯后台调用把word转为pdf.但是pageoffice的FileMaker对象可以实现不在客户端打开文件直接转换文件为pdf并保存到服务器端,看起来跟服 ...
- Springboot中使用freemarker动态生成word文档
文章目录 freemarker模板动态生成word文档 前言 准备 简单模板准备 <一> `word 2003` 新建`.doc` 模板 <二> 另存为`.xml` 文件,格式 ...
- Java动态生成word文档(图文并茂)
很多情况下,软件开发者需要从数据库读取数据,然后将数据动态填充到手工预先准备好的Word模板文档里,这对于大批量生成拥有相同格式排版的正式文件非常有用,这个功能应用PageOffice的基本动态填充功 ...
- Java技术:SpringBoot集成FreeMarker生成word文件
今天给大家分享SpringBoot集成FreeMarker模板引擎生成word文件的用法,感兴趣的可以学一下,完整源码地址在文章末尾处,欢迎互相沟通交流! 一.什么是F ...
- Freemarker - 根据模板动态生成word文档
文章目录 Freemarker 根据模板动态生成word文档 Freemarker 介绍: Freemarker 使用: freemarker加载模板目录的方法 参考资料 Freemarker 根据模 ...
- Java项目中利用Freemarker模板引擎导出--生成Word文档
应邀写的一篇文章:Java项目中利用Freemarker模板引擎导出--生成Word文档 资源下载:https://download.csdn.net/download/weixin_41367523 ...
- 火影推荐程序连载32-我是如何使用freemarker生成Word文件的?
背景 一天,产品经理递给我了一份word报告,我定睛一看 这个文档有大大小小的标题层级,还有排版好的段落.各种一目了然的饼图.走势图,当然还少不了颜色循环交替的报表.精致程度不亚于小明同学的学习报告. ...
- freemarker动态生成word、pdf文档(写入图片)
需求:给出固定的word模板通过后台查询出的数据动态生成word文档 结果展示:列表中的数据就是动态生成的. 一.创建.ftl模板文件,(我这里就不详细展开了,直接粘贴ftl模板的代码) <!D ...
- Freemarker生成word文件,打开后页眉和页脚图片不显示
Freemarker生成word文件,打开后页眉和页脚图片不显示 在我们开发过程中通常会把模板文件Ctrl+Alt+L格式化,方便处理.这样就造成word的标签格式产生多余的换行或者空白,但是word ...
最新文章
- 他25岁当上985博导,今年秋季学生已招满
- UWP开发随笔——使用SQLite数据库
- 首次发现!AI脑回路演化向人类更进一步,OpenAI最新研究引热议,有学者评价:太吓人了...
- Node.js 使用webpack-dev-server工具运行项目实现自动打包编译的功能
- 2016去哪儿编程题:字符替换
- oracle analyze 全部表,Oracle analyze table
- linux硬盘怎么分配合适,500G的硬盘,怎么分区比较合理?
- 20165306 Exp9 Web安全基础
- android 无法播放mp3文件夹,Android Assets文件夹中的Mp3音频无法通过签名...
- solidworks二次开发 学习日记--1 开发方式
- excel中去重计数_在Excel中计数
- OpenBmc开发13:添加传感器(fan)
- 训练3.21(C:Summer Dichotomy)
- pdf如何转换成ppt
- 小程序微信支付功能开发
- OrientDB入门
- Qt编写可视化大屏电子看板系统8-调整间距
- linux基础——sed
- 《这是全网最硬核redis总结,谁赞成,谁反对?》六万字大合集
- 谭宏川老师(Samuel Tan)专业化销售技术提升专家
热门文章
- AutoCAD转MapGIS快速方法
- 自己动手实现简易光线追踪算法
- app支付宝验证登录
- 论文阅读:Seg4Reg+: Consistency Learning Between Spine Segmentation and Cobb Angle Regression
- Intellij IDEA与Eclipse对比
- 制作FlappyBird时出现的一些问题
- 切换无线网卡失败服务器提示,电脑无线网卡切换为AP模式时提示ICS启动失败的解决方法...
- ArcGIS实验教程——实验二十:ArcGIS数字高程模型DEM建立
- arcgis 空间交集 计算_基于ArcGIS的GIS缓冲区与叠加分析的综合应用——以大型商场选址为例...
- java判断英文符号_Java判断中英文符号、标点的实现