1. 程式人生 > >利用freemarker轉pdf和

利用freemarker轉pdf和


java freemarker轉PDF和Word
置頂 2018年04月27日 17:55:34 qq_35925291 閱讀數:295 標籤: freemake製作PDF和word java 生成PDF和Word
最近專案要求做一個freemarker轉PDF和Word的功能,因為以前沒用過,遇到了很多坑所以在這裡記錄一下,方便自己以後用到,不說了直接上程式碼:

1.先說PDF的

(1)先做一個模板(用HTML頁面把你需要的模板檔案畫出來,記得用行內樣式,所有標籤必須閉合,畫完之後複製一份改名為.ftl格式的檔案)

正常的HTML檔案就不貼了(畫完之後裡面什麼都不用更改,再ftl檔案裡記得加上下面這兩個,不然生成不了)

body {
font-family: SimSun;
backgrou nd: none;
margin-left: 25px;
margin-right: auto;
}

然後把需要替換值得地方用freemarker標籤去替換;這是集合替換值得方法(key是別名)

    <#list tYxyUserMeal as key>
        <td>
            <div>${key.mealName}</div>
        </td>
    </#list> 

下面是物件替換方法(直接物件點屬性就好);

  <li>版本日期:${dataTime.date}</li>

還有一種if判斷的方法(我是判斷值等於一或者二顯示這一行 )

<#if (tyxyKeywordConf.encryption) == “1” || (tyxyKeywordConf.encryption) == “2”>
</#if>
(2)Freemarker轉化(tmppdfPath 是在你的電腦上找一個位置放置生成的HTML檔案,這個直接可以使用只需要改模板名稱就好)

這個再不同的操作模式下路徑//的轉換方法

package com.ums.dmp.base.common;

import java.io.File;
import javax.servlet.http.HttpServletRequest;

public class GetWebProjectRealPathTool {
/**
* 獲取專案在服務其中的真實路徑的工具類
* 這是在web專案中,獲取專案實際路徑的最佳方式,在windows和linux系統下均可正常使用
*/

//獲取專案的根路徑  
public static String classPath = GetWebProjectRealPathTool.class.getClassLoader().getResource("/").getPath();  
//對專案的根路徑進行解析,拿到專案路徑  
  
public static String getRootPath() {  
    String rootPath = "";  
    //windows下  
    if("\\".equals(File.separator)){  
        System.out.println("windows");  
    rootPath = classPath.substring(1,classPath.indexOf("/WEB-INF/classes"));  
    rootPath = rootPath.replace("/", "\\");  
    }  
    //linux下  
    if("/".equals(File.separator)){  
        System.out.println("linux");  
    rootPath = classPath.substring(0,classPath.indexOf("/WEB-INF/classes"));  
    rootPath = rootPath.replace("\\", "/");  
    }  
    return rootPath;  
    }  


public static String getBasePath(HttpServletRequest request){
	String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()+request.getContextPath()+"/";
	return basePath;
}

}
這個是轉換方法

package com.ums.dmp.base.service.impl;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.lowagie.text.pdf.BaseFont;
import com.ums.dmp.base.common.GetWebProjectRealPathTool;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**

  • html to pdf
  • @author yl

*/
public class H2p {
private Logger logger = LoggerFactory.getLogger(getClass());
private static H2p instance = new H2p();
private H2p() {
}

public static H2p getInstance() {
return instance;
}

private String tmppdfPath = GetWebProjectRealPathTool.getRootPath()
+ “/report/”;
/**
* 介面文件 html to pdf
*
* @param archiveInfoMap
* @return
* @throws Exception
*/
public File preExportPdfForSh(Map<String, Object> archiveInfoMap)
throws Exception {
// UUID uuid = UUID.randomUUID();
String outputFile = tmppdfPath + “介面文件”+ dateFormat()+".pdf"; // 生成的PDF名
// a.建立一個合適的Configration物件
Configuration configuration = new Configuration();
// configuration.setDirectoryForTemplateLoading(new
// File(basePath));//方式一:使用絕對路徑設定模版路徑
configuration.setDirectoryForTemplateLoading(new File(tmppdfPath+File.separatorChar+“ftl”));
// configuration.setClassForTemplateLoading(tmppdfPath, “ftl”);// 方式二:使用所在類的相對路徑設定模版路徑
configuration.setObjectWrapper(new DefaultObjectWrapper());
configuration.setDefaultEncoding(“UTF-8”); // 這個一定要設定,不然在生成的頁面中 會亂碼
// b.獲取或建立一個模版
Template template = configuration.getTemplate(“interfaceDoc.ftl”);
String outPutFileName = tmppdfPath + “interfaceDoc.html”; // 生成的html名
File outFile = new File(outPutFileName); // 生成檔案的路徑
Writer writer = null;
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(outFile), “UTF-8”));
// c.使用template將資料注入模版頁面生成要轉pdf的html頁面
template.process(archiveInfoMap, writer);
writer.flush();
// STEP2:使用itext將html頁面轉成PDF檔案
OutputStream os = new FileOutputStream(outputFile);
String url = new File(outPutFileName).toURI().toURL().toString();
// 生成ITextRenderer例項
ITextRenderer renderer = new ITextRenderer();
// 關聯html頁面
renderer.setDocument(url);
ITextFontResolver fontResolver = renderer.getFontResolver();
//字型
fontResolver.addFont("" + tmppdfPath + “Fonts/simsun.ttc”,
BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
renderer.layout();
renderer.createPDF(os);
writer.close();
os.close();
File emptyPDfFile = new File(tmppdfPath + “介面文件” + dateFormat()+".pdf");
if (!emptyPDfFile.exists()) {
emptyPDfFile.createNewFile();
}
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(emptyPDfFile));
// // 將pdf檔案先加水印然後輸出
// setWatermark(bos,outputFile);
// bos.close();

  return emptyPDfFile;

}

/**
* pdf 新增水印
* @param bos 輸出檔案(空檔案PDF)
* @param input (輸入檔案 freemark 生成的pdf檔案)
* @throws DocumentException
* @throws IOException
* @throws com.lowagie.text.DocumentException
*/
public void setWatermark(BufferedOutputStream bos, String input) throws DocumentException,
IOException,DocumentException {

    PdfReader reader = new PdfReader(input);  
    PdfStamper stamper = new PdfStamper(reader, bos);  
    int total = reader.getNumberOfPages() + 1;  
    PdfContentByte content;  
    PdfGState gs = new PdfGState();  
    for (int i = 1; i < total; i++) {  
        content = stamper.getOverContent(i);// 在內容上方加水印  
        //content = stamper.getUnderContent(i);//在內容下方加水印  
        gs.setFillOpacity(0.2f);  
        content.setGState(gs);  
        String imageAbsolutPath = GetWebProjectRealPathTool.getRootPath() + "/images/yxlogo1.jpg";
        Image image = Image.getInstance(imageAbsolutPath);  
        image.setAbsolutePosition(-50, 50); // set the first background image of the absolute   
        content.addImage(image);  
    }  
    stamper.close();  
} 

/**+
 * 日期方法
 * @return
 */
 public  String dateFormat(){
  return new SimpleDateFormat("yyyyMMdd").format(new Date());
}

}

(3)把所有資料以Map的形式封裝好(日期封裝只需要建一個日期的實體類DataTime)。

    我把這個PDF需要的所有資料放到一個Map裡面一起返回的

package com.ums.dmp.base.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import sun.misc.BASE64Encoder;

import com.ums.dmp.base.common.GetWebProjectRealPathTool;
import com.ums.dmp.base.entity.DataTime;
import com.ums.dmp.base.entity.LabelInformationTable;
import com.ums.dmp.base.entity.TYxyKeywordConf;
import com.ums.dmp.base.entity.TYxyMeal;
import com.ums.dmp.base.entity.TYxyUserMeal;
import com.ums.dmp.base.entity.TagInformationTable;
import com.ums.dmp.base.entity.TyxySeviceMessage;
import com.ums.dmp.base.entity.TyxySeviceMessageFenZhi;
import com.ums.dmp.base.manager.LabelInformationTableManager;
import com.ums.dmp.base.manager.TYxyMealManager;
import com.ums.dmp.base.manager.TYxyUserMealManager;
import com.ums.dmp.base.manager.TagInformationTableManager;
import com.ums.dmp.base.manager.TyxyKeywordConfManager;
import com.ums.dmp.base.manager.TyxySeviceMessageFenZhiManager;
import com.ums.dmp.base.manager.TyxySeviceMessagerManager;
import com.ums.dmp.base.service.TYxyUserMealService;

@Service
public class TYxyUserMealServiceImpl implements TYxyUserMealService {

@Autowired
private TYxyUserMealManager tYxyUserMealManager;

@Autowired
private TyxyKeywordConfManager tyxyKeywordConfManager;

@Autowired
private TYxyMealManager tYxyMealManager;

@Autowired
private LabelInformationTableManager LabelInformationTableManager;

@Autowired
private TagInformationTableManager TagInformationTableManager;

@Autowired
private TyxySeviceMessageFenZhiManager tyxySeviceMessageFenZhiManager;
@Autowired
private TyxySeviceMessagerManager tyxySeviceMessagerManager;

	
public Map<String, Object> getReportData(String userId, String mealId) {
	/**
	 * 根據使用者號和套餐號查詢套餐名稱
	 */
	Map<String, Object> dbMap = new HashMap<>();
	List<TYxyUserMeal> tYxyUserMeal = tYxyUserMealManager.getMealNameByMealId(userId, mealId);
	dbMap.put("tYxyUserMeal", tYxyUserMeal);
	dbMap.put("mealId", mealId);
	/**
	 * 日期封裝
	 */
	Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        String dayFormat=sdf.format(date);
        DataTime dataTime = new DataTime();
        dataTime.setDate(dayFormat);
        dbMap.put("dataTime", dataTime);
	
	/**
	 * 根據使用者號查詢
	 */
	TYxyKeywordConf tyxyKeywordConf = tyxyKeywordConfManager.queryByUserId(userId, mealId);
	dbMap.put("tyxyKeywordConf", tyxyKeywordConf);

	/**
	 * 到套餐表中根據服務編號(meal_id)讀取index_id欄位資訊
	 */

	List<String> indexIds = tYxyMealManager.selectmealId(mealId);
	
	/**
	 * 根據套餐表中index_id對應的flag欄位資訊: 0-到標籤資訊表(tag_information_table)讀取標籤名稱
	 * 1-到指標資訊表(label_information_table)讀取指標名稱
	 */
	List<Object> objs = new ArrayList<>();
	for (String indexId : indexIds) {
		TYxyMeal tYxyMealflag = tYxyMealManager.queryindexId(indexId,mealId);
		if (tYxyMealflag.getFlag().equals("0")) {
			// 到標籤資訊表(tag_information_table)讀取標籤名稱
			LabelInformationTable table = LabelInformationTableManager.queryByIndexNames(indexId);
			objs.add(table);
		} else if (tYxyMealflag.getFlag().equals("1")) {
			// 到指標資訊表(label_information_table)讀取指標名稱
			TagInformationTable table = TagInformationTableManager.selectIndexNames(indexId);
			objs.add(table);
		}

	}
	dbMap.put("objs", objs);
	 /**
	    * 根據comb查詢請求報文和響應報文
	    * @param comb
	    * @return
	    */
	TyxySeviceMessageFenZhi tyxySeviceMessager=tyxySeviceMessageFenZhiManager.selectbaowen(mealId);
	
	dbMap.put("tyxySeviceMessager", tyxySeviceMessager);
	
	return dbMap;
}	

(4)因為轉PDF是不能自動換行的所以我寫了一個方法直接呼叫就好(這個工具類直接使用就好)

package com.ums.dmp.base.common;
import java.awt.FontMetrics;
import javax.swing.JLabel;
import java.io.UnsupportedEncodingException;

/**

  • 按位元組擷取字串util
  • @author yl

/
public class NewlinetoolUtil {
/
*
* jkb編碼按位元組擷取字串拼接
方法
* @param str
* @param bytes
* @param charSetName
* @return
* @throws UnsupportedEncodingException
*/
public static String subStringOnlyByGBK(String str, int bytes){
String result = “”; // 每次迴圈擷取的字串的內容
String parameter=""; // 最後返回的結果字串
int i = 0; // 已經擷取字串的長度累計和
int j = 0; // 已經擷取字串的位元組長度累計和
int y = 0; // 每次迴圈擷取字串的的長度
int t = 0; // 已經擷取過的字串的長度和
byte[] a; // 減掉擷取過的剩下的字串的位元組陣列
if(str != null){
try {
int length = str.getBytes(“GBK”).length;// 獲取整個字串的位元組長度
while(true){
a = str.substring(i, str.length()).getBytes(“GBK”);
if(bytes<length-j){
result = new String(a, 0, bytes);
t = result.length();
y= y+t;
if(str.charAt(y-1) != result.charAt(t-1)){
//如果擷取最後一個字,產生亂碼情況,則替換為後一個字。
result = result.replace(result.charAt(t-1), str.charAt(y-1));
j +=1;
}
parameter+=result+"
";
j += bytes;
i += result.length();
}else{
break;
}
}

	        } catch (UnsupportedEncodingException e) {  
	            e.printStackTrace();  
	        }  
	        // 加上擷取之後後面剩餘字串
	        parameter+=str.substring(i,str.length()); 
	    	
	}
	        return parameter;  
   } 


 
	/**
	 * 按位元組擷取字串拼接<br/>通用方法
	 * @param str
	 * @param bytes
	 * @param charSetName
	 * @returnsubStringByBytes
	 */
	 public static String subStringByPX(String str, int bytes,String encode){ 
			String result = "";    // 每次迴圈擷取的字串的內容
			String parameter="";   // 最後返回的結果字串
			int i = 0;             // 擷取掉的字串的總長度
			int j = 0;             // 擷取掉的字串的位元組長總度
			String ss= "";         // 減掉擷取過的剩下的字串
			if(str != null){
			        try {  
			        	int length = str.getBytes(encode).length;// 獲取整個字串的位元組長度
			            while(true){ 
			            	ss = str.substring(i, str.length());
			            	if(bytes<length-j){
			            		//迴圈每次擷取的結果
			       		      	result = idgui(ss,bytes,encode);
			            		parameter+=result+"<br/>";
			            		//擷取掉的字串位元組數
			            		j += result.getBytes(encode).length;
			            		//擷取掉的字串長度
			            		i += result.length();
			            	}else{
			            		break;
			            	}
			              }  
			           
			        } catch (UnsupportedEncodingException e) {  
			            e.printStackTrace();  
			        }  
			        // 加上擷取之後後面剩餘字串
			        parameter+=str.substring(i,str.length()); 
			    	
	    	}
			        return parameter;  
		   } 

 
	 public static String idgui(String str,int bytes,String encode){
		    int changdu;
			try {
				changdu = str.getBytes(encode).length;
				if(changdu > bytes){
					str = str.substring(0, str.length() - 1);
					str = idgui(str,bytes,encode);
				}
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		    return str;
		  }
	 
	 /**
	  * 按畫素擷取字串加</br>
	  * @param str
	  * @return
	  */
	 public static String subStringByBytes(String str){ 
		 char[] charArray = str.substring(0, str.length()).toCharArray();
			String result = "";    // 每次迴圈擷取的字串的內容
			String parameter="";   // 最後返回的結果字串
			String sheng ="";     // 每次迴圈剩下的字串
			String p = "";
			int j = 0;
			JLabel label3 = new JLabel(str); 
			FontMetrics metrics3 = label3.getFontMetrics(label3.getFont()); 
			int textW3 = metrics3.stringWidth(label3.getText()); //字串的寬
			if(str != null && textW3 > 600){
					for(int i = 0; i<=charArray.length-1; i++){
						result+=charArray[i];
						JLabel label = new JLabel(result); 
						FontMetrics metrics = label.getFontMetrics(label.getFont()); 
						int textW = metrics.stringWidth(label.getText()); //字串的寬
						if(textW <= 600 && textW >= 587){
							parameter+=result+"<br/>";
							p+=result;
							result = "";
							j = p.length();
							sheng = str.substring(j, str.length());
							JLabel label2 = new JLabel(sheng); 
							FontMetrics metrics2 = label2.getFontMetrics(label2.getFont()); 
							int textW2 = metrics2.stringWidth(label2.getText()); //字串的寬
							if(textW2 <= 600){
								parameter+=sheng;
								break;
							}
						}
					}
					return parameter;  
	    	}
	    		return str;
	 } 		 		 

}
(5).使用的方法在實體類的get()方法呼叫就好

public String getRespMessage() {
	if (this.respMessage != null
			&& this.respMessage.indexOf("<br/>") == -1) {
		this.respMessage = NewlinetoolUtil.subStringByPX(this.respMessage, 90, "UTF-8");
	}
	return respMessage;
}

4.最後是下載的呼叫方法(Falg是我有兩個 我是判斷要下載word還是PDF)

package com.ums.dmp.base.controller;

import com.ums.dmp.base.service.TYxyUserMealService;
import com.ums.dmp.base.service.impl.H2p;
import com.ums.dmp.base.service.impl.MDoc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URLEncoder;
import java.util.Map;

/**

  • 獲取介面文件控制器
  • @author yl

*/
@RestController
@RequestMapping("/downloadFile")
public class KeywordConfXZController {

private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private TYxyUserMealService tYxyUserMealService;

/**
 * 生成文件
 * 
 * @param
 * @param req
 * @return
 * @throws IOException
 */
@RequestMapping("/download.do")
public ResponseEntity<byte[]> download(String userId,String mealId,String flag,HttpServletRequest req)
		throws IOException {
	Map<String, Object> baseInfoMap = null;				

    File file = null;
		try {
			if ("1".equals(flag)) {
				logger.info("開始進行PDF檔案的引數獲取");
				baseInfoMap = tYxyUserMealService.getReportData(userId, mealId);
				logger.info("開始進行PDF檔案的引數獲取成功");
				file = H2p.getInstance().preExportPdfForSh(baseInfoMap);
			} else if ("0".equals(flag)) {
				baseInfoMap = tYxyUserMealService.createWord(userId, mealId);
				file=MDoc.getInstance().createDoc(baseInfoMap);
			}
		} catch (Exception e) {
			// return 提示生成文件失敗
			logger.error("生成文件失敗");
		}
		InputStream in;
        ResponseEntity<byte[]> response=null ;
        try {
            in = new FileInputStream(file);
            byte[] b=new byte[in.available()];
            in.read(b);
            HttpHeaders headers = new HttpHeaders();
			String fileName = "";
			String userAgent = req.getHeader("user-agent").toLowerCase();
			if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
				// win10 ie edge 瀏覽器 和其他系統的ie
				fileName = URLEncoder.encode(file.getName(), "UTF-8");
			} else {
				fileName = new String(file.getName().getBytes("UTF-8"), "iso-8859-1");
			}
            
            headers.add("Content-Disposition", "attachment;filename="+fileName);
			headers.setContentType(MediaType.APPLICATION_PDF);
            
            HttpStatus statusCode=HttpStatus.OK;
            response = new ResponseEntity<byte[]>(b, headers, statusCode);  
            in.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return response;	
   }

}
5.word的方法和PDF得差不多(只是模板的製作方法不一樣,word的模板製作切記用office低版本的做,不然會出錯,下篇說具體的製作方法)