Java導出帶有單選款(radio)和複選框(checkbox)選中效果的word doc文檔-Freemarker實現方式

今天客戶提出要求,要求把表數據導出成爲word文檔格式。導成word格式的這種需求很尋常,但是看到word模板裏面的這種單選框和多選框(如下圖),要求實現選中效果,我就有點懵B了。到處百度,google,查詢了下,發現好多都是要使用office開發工具來實現,我覺得太麻煩,駕馭不住。

 

最後想到有沒有單選框或者複選框這樣的特殊字符。於是在搜狗輸入法裏面查詢特殊,發現還真有特殊字符,但是字符效果不太符合我要求,單選框選中效果太醜,複選框選中效果是方框裏面打叉形式,瞬間就被我PASS掉了。皇天不負有心人,終於讓我在word的特殊字符中找到想要的效果( Word > 插入 > 符號 > 其他符號(M)..), 也就是字體 Wingdings有我們需要的特殊字符

 

這裏先展示下最終導出的顯示效果(如下圖),可以看到複選框和單選框被選中的效果都已經有了

這種方式唯一的弱點就是: 無法導出的doc文檔沒有真正的點擊效果,也就是你打開word文檔,點擊複選框,複選框不會被置爲選中效果,只是使用特殊字符,實現展示上的選中和非選中效果而已,但是對我的需求來說已經夠用了。

下面就來看下較爲完整的代碼實現:

 

一: 首先是創建freemark的word導出模板, word模板如下圖所示:

 

其中${XXXX} 形式的語法爲freemarker的語法,不多介紹了。這裏有個問題就是,${survey_date} 這樣的語法內容在第二步導出的word xml文檔中,容易被打斷。有的建議是用類似文本編輯器編寫好後一次性黏貼到word裏面,不要在word裏面一個個字符去輸入,但是我發現即使都是複製黏貼進去的也會出現被打斷的情況。不過沒關係,後面會說明如何校正回來。

二: 將文檔保存爲word 2003 XML文檔格式

網絡上有些文章是說導出爲Word 2003 XML文檔格式,可以支持2003版本的word,具體因爲我電腦沒有2003 word,,沒有嘗試過,所以我也不確定是不是能兼容2003版本word。

三: 修改XML文檔內容

這時候你如果使用尋常的編輯器打開第二個步驟生成的xml文件,發現是一堆沒有格式化過的XML文檔,非常難處理。有網絡大神說使用【firstobject XML Editor】來打開,但是我安裝後,打開xml文件,直接卡死。於是我用另一個編輯器【Visual Studio Code】,直接右鍵點擊下 “格式化文件” (如果你沒有這個功能,可能要安裝下插件,在擴展裏面,搜索下 xml format 就會發現有好幾個XML format插件。)

通過查找  "${" 發現很多freemark的語法內容被打亂了,如下:

調整後效果如下:

 

然後我們需要將單選框和複選框的判斷條件加上,通過查看XML文檔,找到複選框/單選框所在位置:

紅色部分指定使用Wingdings字體中的特殊字符,通過事先插入特殊字符(單選/複選),我找到分別代表單選框和複選框特殊字符的wordXML內容:

<w:sym w:font="Wingdings" w:char="F0A8"/>   複選框沒選中
<w:sym w:font="Wingdings" w:char="F0FE"/>   複選框選中

<w:sym w:font="Wingdings" w:char="F0A1"/>   單選框沒選中
<w:sym w:font="Wingdings" w:char="F0A4"/>   單選框選中

 

現在要做的僅僅是使用freemark判斷條件,來判斷是要展示覆選框選中效果還是非選中效果:

 

使用Freemark判斷條件,來判斷單選框選中效果:

 然後剩下的無非是重複性勞動,一個個修改過去,反正我是眼睛都瞪累了才全部改完的。

最後記得把生成的XML文件,修改後綴名爲 XXXX.ftl 放到你自己Freemarker的模板加載路徑中去。

 

四: Spring加入Freemarker配置並導出doc文件

spring,application文件直接加入相關Freemarker配置,並加入freemarker相關jar包,這裏簡要展示下配置信息:

<bean id="freeMarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
		<property name="templateLoaderPaths" value="/template/ftl" />
		<property name="freemarkerSettings">
			<props>
				<prop key="defaultEncoding">UTF-8</prop>
				<prop key="url_escaping_charset">utf-8</prop>
				<prop key="locale">zh_CN</prop>
				<!-- FreeMarker默認每隔N秒檢查模板是否被更新,如果已經更新了,就會重新加載並分析模板。 
				但經常檢查模板是否更新可能比較耗時。如果你的應用運行在生產模式下,而且你預期模板不會經常更新,   
            	則可以將更新的延遲時間延長至一個小時或者更久。 可以通過爲freemarkerSettings屬性設置template_update_delay達到這一目的 -->  
				<prop key="template_update_delay">3600</prop>
				<prop key="tag_syntax">auto_detect</prop>
				<prop key="whitespace_stripping">true</prop>
				<prop key="classic_compatible">true</prop>
				<prop key="number_format">0.######</prop>
				<prop key="boolean_format">true,false</prop>
				<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
				<prop key="date_format">yyyy-MM-dd</prop>
				<prop key="time_format">HH:mm:ss</prop>
			</props>
		</property>	
		
	</bean> 

簡單展示下Java代碼調用Freemarker生成doc:

private String generalDoc(String storePath, String unid, String templateName) throws Exception {
		Template template = null;
		Writer out = null;
		String docName = null;	//文件名
		String docPath = ResourceUtils.assemblyPath(storePath, docName);;	//完整文件路徑
		try {
			// 輸出文件
			out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docPath), "UTF-8"));
			Map<String, Object> dataModel = getTemplateDataModel(unid);		//組織數據
			
			template = freeMarkerConfigurer.getConfiguration().getTemplate(templateName);
			//freemark居然這麼差,需要手動指定版本?
			template.process(dataModel, out);
			//template.process(dataModel, out, new BeansWrapperBuilder(Configuration.VERSION_2_3_24).build());
		} catch (Exception e) {
			throw e;
		} finally {
			// 關閉流
			IOUtils.closeQuietly(out);
		}
		return docPath;
	}

 這裏需要注意的可能就是,如果是需要使用JavaBean形式反射機制進行屬性訪問的話,可能需要使用new BeansWrapperBuilder(Configuration.VERSION_2_3_24).build()  方式構建BeansWrapper。

private Map<String, Object> getTemplateDataModel(String unid) {
		Map<String, Object> row = getGermplasmForMap(unid);	//根據id獲取數據,返回數據爲map形式
		if(row == null) {	//數據不存在
			throw new RuntimeException("數據不存在");
		}
		
		row.put("no", "1號表");
		row.put("dept_province", "福建省");
		row.put("survey_place", "延平區大大鄉小小村");
		
		ArrayList<String> treeCategoryList = new ArrayList<String>();
		treeCategoryList.add("國家Ⅰ級重點保護植物");
		treeCategoryList.add("省外引進樹種");
		
		row.put("tree_category_list", treeCategoryList);
		
		return row;
	}

 

附錄:word2007-xml存儲標籤屬性說明文件

地址: http://www.doc88.com/p-402265130456.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章