背景介绍:最近在做老旧项目的二次开发,所以用到了freemarker去生成PDF和Word,涉及到打印静态模板与动态模板。查看了这方面的资料发现大都不全,似是而非。废话不多说,上案例。
freemarker版本号:<freemarker.version>2.3.26-incubating</freemarker.version>
这个无所谓,我也没用最新的标签。
一、静态模板生成Word:
public void submitPrint(HttpServletRequest request, HttpServletResponse response ){
try {
String fileName = "***.doc";
// 设置response类型
String userAgent = request.getHeader("user-agent").toLowerCase();
if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
fileName = URLEncoder.encode(fileName, "UTF-8");
} else {
fileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");
}
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;" + " filename=" + fileName);
response.setContentType("application/octet-stream");
// 获取数据,设置数据
Map<String,Object> dataMap = new LinkedHashMap<>(2);
dataMap.put("st1Name","张三");
dataMap.put("st1Content","李四");
Writer out = response.getWriter();
//模板所在路径
String templateFolder = request.getSession().getServletContext().getRealPath("/template/reins/makeslip");
//判断使用哪个模板
createDoc(out, dataMap, "***模板名.ftl",templateFolder);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private void createDoc(Writer out, Map<String, Object> dataMap, String ftlName,String templateFolder) {
try {
configuration.setDirectoryForTemplateLoading(new File(templateFolder));
Template t = null;
t = configuration.getTemplate(ftlName,"UTF-8");
t.process(dataMap, out);
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
class Template<T> {
private String id;
private String value;
private String label;
private String parentId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getParentId() {
return parentId;
}
public void setParentId(String parentId) {
this.parentId = parentId;
}
}
class ToCreateDoc {
private Configuration configuration = null;
public ToCreateDoc() {
configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
}
public void createDoc(Writer out, Map<String, Object> dataMap, String ftlName,String templateFolder) {
try {
configuration.setDirectoryForTemplateLoading(new File(templateFolder));
Template t = null;
t = configuration.getTemplate(ftlName,"UTF-8");
t.process(dataMap, out);
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*模板:只粘出关键代码*/
<w:tr w:rsidR="00CF02E7" w:rsidRPr="00573DB1" w:rsidTr="003477DD">
<w:trPr>
<w:trHeight w:val="737"/>
</w:trPr>
<w:tc>
<w:tcPr>
<w:tcW w:w="2283" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00CF02E7" w:rsidRDefault="00CF02E7" w:rsidP="00CF02E7">
<w:pPr>
<w:widowControl/>
<w:jc w:val="left"/>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t>${st1Name!}</w:t>
</w:r>
<w:r>
<w:rPr>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t xml:space="preserve"> </w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="7371" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00CF02E7" w:rsidRDefault="00CF02E7" w:rsidP="00CF02E7">
<w:pPr>
<w:widowControl/>
<w:rPr>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t>${st1Content!}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
二、动态生成Word:
/*1、数据格式为:list<String>*/
List<String> pringDataOneList = new LinkedList<>(20);
for (int i = 0; i < 20; i++) {
pringDataOneList.add("张三"+(i+1));
}
Map<String,Object> resultMap = new HashMap<>(1);
resultMap.put("printlist",pringDataOneList);
因为模板数据规定为map,在list外面套了一层map。
模板如下:
<!--printlist:map的key -->
<#if printlist?exists>
<!--遍历list -->
<#list printlist as key>
<w:tr w:rsidR="00CF02E7" w:rsidRPr="00573DB1" w:rsidTr="003477DD">
<w:trPr>
<w:trHeight w:val="737"/>
</w:trPr>
<w:tc>
<w:tcPr>
<w:tcW w:w="2283" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00CF02E7" w:rsidRDefault="00CF02E7" w:rsidP="00CF02E7">
<w:pPr>
<w:widowControl/>
<w:jc w:val="left"/>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<!--打印数组下边并判断非空-->
<w:t>${(key_index)!}</w:t>
<!--打印数组内容并判断非空-->
<w:t>${(key)!}</w:t>
</w:r>
<w:r>
<w:rPr>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t xml:space="preserve"> </w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="7371" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00CF02E7" w:rsidRDefault="00CF02E7" w:rsidP="00CF02E7">
<w:pPr>
<w:widowControl/>
<w:rPr>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t>${(key_index)!}</w:t>
<w:t>${(key)!}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</#list>
</#if>
----------------------------------------------------------------------------------------------------------------------------
/*2、数据格式为:Map<String,List<Entity>>*/
// 获取数据,设置数据
List<Entity> printWordList = new LinkedList<>(20);
for (int i = 0; i < 20; i++) {
printWordList.add(new Entity("st"+(i+1)+"Name","st"+(i+1)+"Content"));
}
Map<String, Object> dataMap = new HashMap<>(1);
dataMap.put("printlist",printWordList);
class Entity{
private String name;
private String content;
public Entity (String name,String content){
this.name = name;
this.content = content;
}
@Override
public String toString() {
return "Entity{" +
"name='" + name + '\'' +
", content='" + content + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
/*其他代码与固定模板代码一致。*/
/*模板:*/
<!--先判断printlist的值存在,取值得到printWordList-->
<#if printlist?exists>
<!--遍历list-->
<#list printlist as mapa>
<w:tr w:rsidR="00CF02E7" w:rsidRPr="00573DB1" w:rsidTr="003477DD">
<w:trPr>
<w:trHeight w:val="737"/>
</w:trPr>
<w:tc>
<w:tcPr>
<w:tcW w:w="2283" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00CF02E7" w:rsidRDefault="00CF02E7" w:rsidP="00CF02E7">
<w:pPr>
<w:widowControl/>
<w:jc w:val="left"/>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<!--取list中的entity的name值-->
<w:t>${(mapa.name)!}</w:t>
</w:r>
<w:r>
<w:rPr>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<w:t xml:space="preserve"> </w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:tcPr>
<w:tcW w:w="7371" w:type="dxa"/>
</w:tcPr>
<w:p w:rsidR="00CF02E7" w:rsidRDefault="00CF02E7" w:rsidP="00CF02E7">
<w:pPr>
<w:widowControl/>
<w:rPr>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
<w:color w:val="000000"/>
<w:kern w:val="0"/>
<w:sz w:val="24"/>
</w:rPr>
<!--取list中的entity的content值-->
<w:t>${(mapa.content)!}</w:t>
</w:r>
</w:p>
</w:tc>
</w:tr>
</#list>
</#if>
三、静态模板生成PDF:
/*java代码:*/
public void printMarineNote(HttpServletResponse response){
String html = null;
Map<String,Object> map = new LinkedHashMap<>(4);
map.put("st1Name","张三");
map.put("st1Content","张三的java从入门到放弃");
map.put("st2Name","李四");
map.put("st2Content","李四的java从入门到放弃");
response.setCharacterEncoding("UTF-8");
html = getHtml("**模板名称",map);
//输出PDF文件名
response.setHeader("Content-Disposition","attachment;filename=" +
URLEncoder.encode("***.pdf","UTF-8"));
PdfUtils.createPdfForBsDebitNote(response, html);
}
private String getHtml(String fileName,Map<String,Object> map) throws Exception {
String freemarkerPath="templates\\modules\\freemarkermodle\\";
Resource resource = new ClassPathResource(freemarkerPath);
map.put("basePath", resource.getFile().getAbsoluteFile());
Template template = configuration.getTemplate(fileName);
StringWriter writer = new StringWriter();
template.process(map,writer);
writer.flush();
writer.close();
String html=writer.toString();
writer.close();
return html;
}
/*模板:*/
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>***</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body {
font-family: Microsoft YaHei;
font-size: 2px;
}
@page {
size: A4;
}
</style>
</head>
<body bgcolor="#FFFFFF" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<table style="width: 100%; border-collapse: collapse;">
<tr>
<td style="width: 100%;height:35px;font-size:20px;text-align:center;"><strong>***</strong></td>
</tr>
</table>
<table style="width: 100%; border-collapse: collapse; font-size: 12px;">
<tr>
<td style="width: 580px; height: 400px;padding-left: 20px">
<table style="font-size: 12px;padding-left: 20px">
<tr>
<td style="width: 20%;padding-left: 60px; float: left; height: auto;padding-top: 10px;padding- buttom:10px;">${st1Name?if_exists}</td>
<td style="width: 30%;float: left; height: auto;padding-top: 10px;padding-buttom:10px;">${st1Content?if_exists}</td>
<td style="width: 20%;padding-left: 60px; float: left; height: auto;padding-top: 10px;padding-buttom:10px;">${st2Name?if_exists}</td>
<td style="width: 30%;float: left; height: auto;padding-top: 10px;padding-buttom:10px;">${st2Content?if_exists}</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
四、动态生成PDF:其实动态模板和上面的Word一样。这里我用的其他方式,毕竟条条大路通罗马。
/**
*数据格式
* Map<String,Map<String,Object>> map = new HashMap<>();
*/
public void printMarineNote(HttpServletRequest request, HttpServletResponse response) throws Exception {
String html = null;
Map<String,Object> map = new HashMap<>();
Map<String,Object> map1 = new LinkedHashMap<>(20);
for (int i = 0; i < 20; i++) {
map1.put(i+"name",i+"content");
}
map.put("printMap",map1);
for (int i = 0; i < 2; i++) {
map.put(i+"dynamicMap",map1);
}
if(map.size()>1){
html = getDynamicHtml("***.ftl","***.ftl",map);
}else{
html = getDynamicHtml("***.ftl",null,map);
}
response.setHeader("Content-Disposition","attachment;filename=" +
URLEncoder.encode("***.pdf","UTF-8"));
createPdfForBsDebitNote(response, html);
}
public static void createPdfForBsDebitNote(HttpServletResponse response,String html) throws Exception{
Document doc = new Document(PageSize.A4,0,0,0,0);
OutputStream stream = response.getOutputStream();
PdfWriter pdfWriter = PdfWriter.getInstance(doc,stream);
pdfWriter.setPageEvent(new PdfPageEventHelper(){
@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
try{
cb.setFontAndSize(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED),6);
} catch (com.itextpdf.text.DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
cb.saveState();
cb.beginText();
cb.endText();
cb.restoreState();
}
});
doc.setMargins(10, 10, 10, 10);
doc.open();
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(html.getBytes());
XMLWorkerHelper.getInstance().parseXHtml(pdfWriter,doc,byteArrayInputStream);
doc.close();
stream.close();
}
private String getDynamicHtml(String fileName,String fileName1,Map<String,Object> map) throws Exception {
String freemarkerPath="templates\\modules\\freemarkermodle\\";
Resource resource = new ClassPathResource(freemarkerPath);
StringWriter writer = new StringWriter();
String html1;
String html2 = null;
Map<String,Object> map1 = (Map<String, Object>) map.get("printMap");
Template template = configuration.getTemplate(fileName);
template.process(map1,writer);
writer.flush();
writer.close();
html1=writer.toString();
writer.close();
if(StringUtils.isNotBlank(fileName1)){
int i = 0;
if(map.size()>1){
for(String key : map.keySet()){
StringWriter writer1 = new StringWriter();
if(!"printMap".equals(key)){
Map<String,Object> map2 = (Map<String, Object>) map.get(i+"dynamicMap");
Template template1 = configuration.getTemplate(fileName1);
template1.process(map2,writer1);
writer1.flush();
writer1.close();
String html3=writer1.toString();
writer1.close();
html2+=html3;
i++;
}
}
}
}
String html = html1 + html2;
return html;
}
/*模板同上*/
freemarker中文文档如下:http://freemarker.foofun.cn/ref_builtins_alphaidx.html
完整的Word模板如下: