使用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模板文件,可以采用反编译的方式。

  1. 新建一个doc格式的word文档;
  2. 另存为xml格式文件,注意并非word xml文件;
  3. 修改后缀名为ftl;
  4. 使用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 &quot;1-3&quot; \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文件相关推荐

  1. Java中利用freemarker模板动态生成word含表格

    最近公司有导出word的需求,由于word的样式有的很复杂所以记录一下Java中利用freemarker模板动态生成word含表格,以防以后忘记. 1.word表格的模板 删掉无用的数据留下基础的样式 ...

  2. PageOffice动态生成Word文件并转换为PDF

    说明:PageOffice是客户端插件,做不到纯后台调用把word转为pdf.但是pageoffice的FileMaker对象可以实现不在客户端打开文件直接转换文件为pdf并保存到服务器端,看起来跟服 ...

  3. Springboot中使用freemarker动态生成word文档

    文章目录 freemarker模板动态生成word文档 前言 准备 简单模板准备 <一> `word 2003` 新建`.doc` 模板 <二> 另存为`.xml` 文件,格式 ...

  4. Java动态生成word文档(图文并茂)

    很多情况下,软件开发者需要从数据库读取数据,然后将数据动态填充到手工预先准备好的Word模板文档里,这对于大批量生成拥有相同格式排版的正式文件非常有用,这个功能应用PageOffice的基本动态填充功 ...

  5. Java技术:SpringBoot集成FreeMarker生成word文件

                    今天给大家分享SpringBoot集成FreeMarker模板引擎生成word文件的用法,感兴趣的可以学一下,完整源码地址在文章末尾处,欢迎互相沟通交流! 一.什么是F ...

  6. Freemarker - 根据模板动态生成word文档

    文章目录 Freemarker 根据模板动态生成word文档 Freemarker 介绍: Freemarker 使用: freemarker加载模板目录的方法 参考资料 Freemarker 根据模 ...

  7. Java项目中利用Freemarker模板引擎导出--生成Word文档

    应邀写的一篇文章:Java项目中利用Freemarker模板引擎导出--生成Word文档 资源下载:https://download.csdn.net/download/weixin_41367523 ...

  8. 火影推荐程序连载32-我是如何使用freemarker生成Word文件的?

    背景 一天,产品经理递给我了一份word报告,我定睛一看 这个文档有大大小小的标题层级,还有排版好的段落.各种一目了然的饼图.走势图,当然还少不了颜色循环交替的报表.精致程度不亚于小明同学的学习报告. ...

  9. freemarker动态生成word、pdf文档(写入图片)

    需求:给出固定的word模板通过后台查询出的数据动态生成word文档 结果展示:列表中的数据就是动态生成的. 一.创建.ftl模板文件,(我这里就不详细展开了,直接粘贴ftl模板的代码) <!D ...

  10. Freemarker生成word文件,打开后页眉和页脚图片不显示

    Freemarker生成word文件,打开后页眉和页脚图片不显示 在我们开发过程中通常会把模板文件Ctrl+Alt+L格式化,方便处理.这样就造成word的标签格式产生多余的换行或者空白,但是word ...

最新文章

  1. 他25岁当上985博导,今年秋季学生已招满
  2. UWP开发随笔——使用SQLite数据库
  3. 首次发现!AI脑回路演化向人类更进一步,OpenAI最新研究引热议,有学者评价:太吓人了...
  4. Node.js 使用webpack-dev-server工具运行项目实现自动打包编译的功能
  5. 2016去哪儿编程题:字符替换
  6. oracle analyze 全部表,Oracle analyze table
  7. linux硬盘怎么分配合适,500G的硬盘,怎么分区比较合理?
  8. 20165306 Exp9 Web安全基础
  9. android 无法播放mp3文件夹,Android Assets文件夹中的Mp3音频无法通过签名...
  10. solidworks二次开发 学习日记--1 开发方式
  11. excel中去重计数_在Excel中计数
  12. OpenBmc开发13:添加传感器(fan)
  13. 训练3.21(C:Summer Dichotomy)
  14. pdf如何转换成ppt
  15. 小程序微信支付功能开发
  16. OrientDB入门
  17. Qt编写可视化大屏电子看板系统8-调整间距
  18. linux基础——sed
  19. 《这是全网最硬核redis总结,谁赞成,谁反对?》六万字大合集
  20. 谭宏川老师(Samuel Tan)专业化销售技术提升专家

热门文章

  1. AutoCAD转MapGIS快速方法
  2. 自己动手实现简易光线追踪算法
  3. app支付宝验证登录
  4. 论文阅读:Seg4Reg+: Consistency Learning Between Spine Segmentation and Cobb Angle Regression
  5. Intellij IDEA与Eclipse对比
  6. 制作FlappyBird时出现的一些问题
  7. 切换无线网卡失败服务器提示,电脑无线网卡切换为AP模式时提示ICS启动失败的解决方法...
  8. ArcGIS实验教程——实验二十:ArcGIS数字高程模型DEM建立
  9. arcgis 空间交集 计算_基于ArcGIS的GIS缓冲区与叠加分析的综合应用——以大型商场选址为例...
  10. java判断英文符号_Java判断中英文符号、标点的实现