freemarker根據靜態模板和動態模板生成PDF與Word

錦衣夜行燚發表於2020-11-26

背景介紹:最近在做老舊專案的二次開發,所以用到了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模板如下:

相關文章