利用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低版本的做,不然會出錯,下篇說具體的製作方法)