關於WkHtmltopdf插件將網頁導出成PDF的使用
一、Wkhtmltopdf介紹
首先,先放一下Wkhtmltopdf工具的官網和下載連接:
下面正式開始介紹wkhtmltopdf:
1. wkhtmltopdf 簡介
工具全名叫 “wkhtmltopdf” ; 是一個使用 Qt WebKit 引擎做渲染的,能夠把html 文檔轉換成 pdf 文檔 或 圖片(image) 的命令行工具。支持多個平臺,可在win,linux,os x等系統下運行。
2. wkhtmltopdf 優點
- 生成PDF時會自動根據你在HTML頁面中
H系(<h1> <h2> 等等)
標籤生成樹形目錄結構。 - 小巧方便,轉換速度快。
- 跨平臺。
PS:目前只在Linux和Windows下用過
3. wkhtmltopdf 缺點
在實際使用中發現 wkhtmltopdf 工具並不是非常完美的:
- 在導出pdf時,會洗掉 部分css樣式。
- 在導出圖表時,非矢量圖失真的厲害。
4. wkhtmltopdf 安裝與配置
- 點上面的下載地址,點擊下載並安裝 wkhtmltopdf。
- 在
PATH
中添加配置你的wkhtmltopdf安裝目錄\bin
。 - 檢查你的 wkhtmltopdf是否配置成功,打開終端,輸入
wkhtmltopdf -V
如果出現 wkhtmltopdf 工具的版本信息則說明你已經可以使用該工具了。
5. wkhtmltopdf 使用
- 命令行操作
- 通過代碼操作
第一種操作方式網上有很多教程,在此不再贅述。我在這裏講一講在代碼中使用wkhtmltopdf。
二、代碼中操作 wkhtmltopdf
1. 介紹一下wkhtmltopdf 中的幾種對象:
- 文檔對象
“文檔對象”是指PDF文檔中的文檔對象,共有三種類型的“文檔對象”,他們分別是“頁面對象”,“封面對象”和“目錄對象”。 - 頁面對象
“頁面對象”是指以頁面的形式在PDF文檔中呈現的對象,這個是相對於“封面對象”和“目錄對象”來講的。此類對象會成爲PDF文檔中內容。 - 封面對象
“封面對象”是指以封面的形式在PDF文檔中呈現的對象。這類對象會成爲PDF文檔中的封面。 - 目錄對象
“目錄對象”是以目錄的形式在PDF文檔中呈現的對象,又叫“TOC對象”。這類對象會成爲PDF文檔中的目錄。
這一部分我大體羅列這四部分,至於其他的術語和具體的命令請移駕到JSON_NULL的簡書查看。
2. wkhtmltopdf 的應用場景
開發過B/S端金融項目的小夥伴們應該對報表不陌生,那麼wkhtmltopdf的一個應用場景就是導出報表。對我來說也是最主要的應用場景。
3. wkhtmltopdf 的實際操作(Java)
1. 新建一個Java類htmltopdf
public class CrHtmlToPdf {
/**
* 將HTML文件內容輸出爲PDF文件
* 若不使用自定義頁眉頁腳請使用兩參的htmlToPdf();
* @param htmlFileName HTML文件名
* @param headerHtmlName 自定義頁眉/頁腳文件路徑
* @param coverHtml 封面
* @param pdfFileName PDF文件名
*/
public boolean htmlToPdf(String htmlFileName,String headerHtmlName,String coverHtml,String pdfFileName) {
this.pdfName = pdfFileName;
boolean result = true;
try {
//調用和執行wkhtmltopdf命令
StringBuilder cmd = new StringBuilder();
//如果wkhtmltopdf沒有加入環境變量,可以寫成wkhtmltopdf所在的絕對目錄
cmd.append("wkhtmltopdf");
/*cmd.append(" ");
//編碼
cmd.append("--encoding utf8");
cmd.append(" ");
//指定一個要分辨率(這在 X11 系統中並沒有什麼卵用)
cmd.append("-d 800");
cmd.append(" ");
//設置頁面寬度
cmd.append("--page-width 12.7cm");
cmd.append(" ");
//設置紙張大小
cmd.append("--page-size A4");
cmd.append(" ");
//靜態模式,不在標準輸出中打印任何信息
cmd.append("--quiet");
cmd.append(" ");
//不使用智能收縮策略,防止變形
*//*cmd.append("--disable-smart-shrinking");
cmd.append(" ");*//*
//不執行Javascript
cmd.append("--disable-javascript");
cmd.append(" ");*/
cmd.append(" ");
//編碼
cmd.append("--encoding utf8");
cmd.append(" ");
//指定一個要分辨率(這在 X11 系統中並沒有什麼卵用)
cmd.append("-d 2000");
cmd.append(" ");
//靜態模式,不在標準輸出中打印任何信息
cmd.append("--quiet");
cmd.append(" ");
//不使用智能收縮策略,防止變形
/*cmd.append("--disable-smart-shrinking");
cmd.append(" ");*/
//執行Javascript
cmd.append("--enable-javascript");
cmd.append(" ");
//生成頁眉
cmd.append("--margin-top 25");
cmd.append(" ");
cmd.append(" --header-html file:///"+headerHtmlName+" ");
cmd.append("--header-spacing 10 ");
cmd.append(" ");
//生成頁腳
cmd.append(" --footer-left \"敬請閱讀文末的法律說明\" --footer-center \"[page]/[toPage]\" --footer-font-size 8 --footer-line");
cmd.append(" ");
cmd.append("--outline cover file:///"+coverHtml);
cmd.append(" ");
cmd.append(" toc --toc-header-text \"目錄\"");
cmd.append(" ");
//html文件目錄
cmd.append(htmlWorkRoot + htmlFileName);
cmd.append(" ");
//pdf文件目錄
cmd.append(pdfWorkRoot + pdfFileName);
cmd.append(" ");
//執行命令Thread
//System.err.println("------Start wkhtmltopdf");
Process process = Runtime.getRuntime().exec(cmd.toString());
//System.err.println(cmd.toString());
//new Thread(new HtmlToPdfThread(process.getInputStream())).start();
process.waitFor();
} catch (Exception e) {
result = false;
throw new RuntimeException(e);
}
return result;
}
}
2. 要導出頁面的獲取
這裏只簡單講一下思路,具體代碼就不拋出來了。
1. 將要導出的HTML頁面轉換成html字符串
2. 將html字符串寫入到本地文件夾中,生成新的臨時html文件。
3. 頁眉的自定義
這裏要實現的頁眉樣式爲左上角是logo(關於logo圖片的處理稍後再講解),右上角文字隨報告文件名變化而變化。
這裏我是這麼解決的
1. 創建css字符串,html字符串。
2. 拼接需要設定的css樣式。
3. 拼接完整的html頁。
4. 將html字符串寫入文件中。
創建html字符串,拼接css和html
/**
* 生成頁眉html的字符串,準備生成html文件
* @param logoPath Logo存放的路徑
* @param templateName 當前查詢的報告名
* @return 返回生成的html字符串
*/
private String buildHeaderHtmlStr(String logoPath,String templateName){
StringBuffer buffer = new StringBuffer();
//創建css字符串
StringBuffer cssBuffer = new StringBuffer();
//拼接css字符串
//創建css標籤
cssBuffer.append("<style type=\"text/css\">\n");
cssBuffer.append(".box{\n" +
" width: 1100px;\n" +
" height: 55px;\n" +
" position: relative;\n" +
" }\n");
cssBuffer.append(".logo{\n" +
" width: 180px;\n" +
" height: 55px;\n" +
" left: 20px;\n" +
" position: absolute;\n" +
" }\n");
cssBuffer.append(".text{\n" +
" width: 836px;\n" +
" height: 50px;\n" +
" position: absolute;\n" +
" left: 235px;\n" +
" top: 10px;\n" +
" background-color: #fff;\n" +
" }\n");
cssBuffer.append(".text_title{\n" +
" width: 826px;\n" +
" height: 50px;\n" +
" border-bottom: 2px solid #c00000;\n" +
" text-align: right;\n" +
" left: 235px;\n" +
" line-height: 73px;\n" +
" padding: 0px;\n" +
" margin: 0px;\n" +
" color: #808080;\n" +
" font-size: 14px;\n" +
" }\n");
cssBuffer.append("</style>\n");
//聲明文檔標籤
buffer.append("<!DOCTYPE html>\n");
//組裝<head></head>內容
buffer.append("<html lang=\"en\">\n" +
"<head>\n<meta charset=\"UTF-8\">\n" +
" <title>自定義頁眉</title>\n"+cssBuffer.toString()+"\n</head>");
buffer.append("<body>\n");
//組裝logo div
buffer.append("<div class=\"box\">\n" +
" <div class=\"logo\">\n" +
" <img src=\""+logoPath+"\">\n" +
" </div>\n");
//組裝頁眉右側文字
buffer.append("<div class=\"text\">\n" +
" <p class=\"text_title\">"+templateName+"</p>\n" +
" </div>\n");
buffer.append("</div>\n</body>\n</html>");
// System.err.println("Html字符串:"+buffer.toString());
return buffer.toString();
}
將html字符串寫入到文件中
/**
* 生成頁眉html文件,並返回其所在的路徑
* @param templateName 當前查詢的報告名
* @return 返回生成的headerHtml文件路徑
*/
public String getHeaderPath(String templateName){
String htmlFileName = "headerHtml" + System.currentTimeMillis()+".html";
String htmlFileString = buildHeaderHtmlStr(getThematicImageString(LOGO_PATH), templateName);
String savePath = HTML_PATH+"/"+htmlFileName;
writingFile(savePath,htmlFileString);
// System.err.println(savePath);
return savePath;
}
4. 封面的自定義
一般來說封面應該由兩部分組成,一個是封面背景(圖片),還有一個就是封面上的說明文字 所以封面處理與頁眉處理類似,那麼這個地方就直接放代碼不在贅述了。
先處理html字符串
/**
* 封面頁面HTML拼接
* @param imagePath 背景圖片路徑
* @return
*/
public String bulidCoverHtmlStr(String imagePath){
StringBuffer css = new StringBuffer();
css.append("<style type=\"text/css\">\n");
css.append(".toggleDiv{ position: relative;height:100%;margin-top: 2px;width:1000px;margin:0 auto;page-break-inside:avoid;}");
css.append(".static_tables>p {font-size:20px !important;}");
css.append(".mod_title{text-align: left;padding:30px 0 0 0}");
css.append(".mod_title>hr{ height:2px;border:none;border-top: 2px solid #fff;margin: 0px}");
css.append(".mod_title>h1{display: inline-block;font-size: 26px;margin-bottom: 0px;margin-top: 10px;position: relative;top: -14px;padding: 0 20px;background: #FFF; font-weight: 700;color: #000;}");
css.append(".mod_title>h2{display: inline-block;font-size: 26px;margin-bottom: 0px;margin-top: 10px;position: relative;top: -14px;padding: 0 20px;background: #FFF; font-weight: 700;color: #000;}");
css.append(".null_data{text-align: center}");
css.append(".half{padding:0px 40px 0px 30px}");
css.append(".half>div{width:50%;display: inline-block;height:300px}");
css.append(".half>.static_tables{position: absolute;padding:20px 0px}");
css.append(".half>.static_tables>table{position: absolute;top: 12%;margin: auto;left: 0;right: 0;width: 94%;padding: 0;table-layout:fixed}");
css.append(".echartDiv{padding: 10px 0;color: #333;/*font-family: FangSong;*/font-size:50px !important;text-align:center;width:100%;height:300px;line-height: 15;}");
css.append(".toggleDiv>.description{ line-height: 25px;font-family:STKaiti;font-size: 18px !important;margin-left: 40px;display: block;line-height:30px;padding-right:2%;}");
css.append(".static_tables {padding: 20px 40px 20px 30px;}");
css.append(".static_tables>p {font-size:20px !important;}");
css.append(".static_tables>table{border-spacing:0px;width:100%}");
css.append(".static_tables>table>thead>tr{background-color: #464d6c}");
css.append(".static_tables>table>thead>tr>th{font-weight:bold; font-size: 16px !important; color: #fff !important;}");
css.append(".static_tables>table th,.static_tables>table td{border-bottom:1px solid #d4d4d4;border-left:1px solid #d4d4d4;text-align:center;/*font-family: FangSong;*/font-size: 16px !important;height:40px;color: #333;word-wrap:break-word;word-break:break-all;line-height:25px; padding: 5px 10px;}");
css.append(".static_tables>table th:last-child,.static_tables>table td:last-child{border-right:1px solid #d4d4d4;}");
css.append(".static_tables>table>tbody>tr:nth-child(2n){ background-color: #fff}");
css.append(".static_tables>table tr:first-child td{border-top: 1px solid #d4d4d4}");
css.append(".titleDiv { position: relative;top:14px;display: block;width: 100%;text-align: center}");
css.append(".toggleDiv>.title{/*font-family:FangSong;*/font-size:16px;color:#000}");
css.append(".bz_box{width: 100%; height: 100%;border: 1px solid #e0e0e1;margin: 0 auto;}");
css.append(".bz_box_title {border-bottom: 1px solid #e0e0e1;background-color: #ebecf3;padding-left: 20px;height: 35px;line-height: 35px;font-weight: bold;}");
css.append(".bz_box_con {height: 100%;overflow: auto;padding-left:20px;padding-right: 10px;;line-height: 30px;}");
css.append(".bz_box_con p{ padding: 0px;margin: 0px;}");
css.append(".bz_box_con p span{font-weight: bold;}");
css.append("</style>\n");
StringBuilder htmlStr = new StringBuilder();
htmlStr.append("<!DOCTYPE html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\"></meta>");
htmlStr.append("<style type=\"text/css\">html,body{height:100%;} span{display: block;font-size: 36px;padding: 12px;} .test{left:3%;top: 73%;bottom: 25%;right: 3%;position: relative;width: 1330px;} .cover2{left:3%; right: 5%;top: 78%;bottom: 22%;width: 1330px;position: relative;background-color: white; font-size:26px;} span>i{font-style: normal;padding: 0 5px;}");
htmlStr.append("</style>");
htmlStr.append(css.toString());
htmlStr.append("</head><body><div style=\"width:1400px;height:1850px;\"><div style=\"background-size:100% 100%;width:100%;height:100%;background:url("+imagePath+") no-repeat\">");
htmlStr.append("<div class=\"test\" style=\"background-color:red\"></div>");
htmlStr.append("</body></html>");
return htmlStr.toString();
}
在生成html文件
public String getCoverPath() {
String htmlFileName = "coverHtml" + System.currentTimeMillis() + ".html";
String htmlFileString = bulidCoverHtmlStr(getThematicImageString(COVER_BGM_PATH));
String savePath = HTML_PATH + "/" + htmlFileName;
writingFile(savePath,htmlFileString);
// System.err.println(savePath);
return savePath;
}
細心的小夥伴們看到這裏會發現,在這兩個拼接的方法裏都用到了一個getThematicImageString(String arg)
的方法,但是這個方法我去沒有給出代碼,難道說這是一個Java本身就定義好的方法嗎?當然不是,這個方法就是在圖片處理時要用的方法。
5. 獲取資源圖片
在 Java web項目中,在編寫項目的時候就會把所有的靜態資源,例如image,.css文件,.js文件,.html文件等等的放在webapp文件夾中。那麼這樣的資源在編譯後會被放到/WEB-INF/classes/
文件夾下,所以在這裏我們要對圖片進行處理,獲取圖片。
/**
* 獲取圖片
* @param path 圖片路徑
* @return
*/
private String getThematicImageString(String path){
InputStream inputStream = null;
byte[] data = null;
String classPath = this.getClass().getClassLoader().getResource("/").getPath();
String fileNameT = path;
String rootPathT;
if("\\".equals(File.separator)){
//Windows
rootPathT = classPath.substring(1,classPath.indexOf("/WEB-INF/classes"));
fileNameT = (rootPathT+File.separator+fileNameT).replace("/", "\\");
}else if("/".equals(File.separator)){
//linux
rootPathT = classPath.substring(0,classPath.indexOf("/WEB-INF/classes"));
fileNameT = (rootPathT+File.separator+fileNameT).replace("\\", "/");
}
try {
inputStream = new FileInputStream(fileNameT);
data = new byte[inputStream.available()];
inputStream.read(data);
} catch (IOException e) {
UnitedLogger.error("加載圖片"+e.getMessage(), e);
}finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException io){
UnitedLogger.error("關閉流失敗"+io.getMessage(), io);
}
}
}
BASE64Encoder encoder = new BASE64Encoder();
return "data:image/png;base64,"+encoder.encode(data).replaceAll("\r\n","");
}
這裏使用了base64 對圖片進行編碼。[小Q一下,這裏用base64進行編碼處理的原因]
6.寫文件
當然在整個過程中還缺少一個方法,就是寫文件的方法,由於該方法在同一個類中多次被用到,所以就把他抽出來,作爲一個公共方法。
寫文件
private void writingFile(String path,String fileText){
FileOutputStream fos;
BufferedWriter bw;
try {
fos = new FileOutputStream(path);
bw = new BufferedWriter(new OutputStreamWriter(fos,"UTF-8"));
bw.write(fileText);
bw.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
7. 代碼整合
最後,我們將這些方法放到同一個類中,形成一個DiyUtil類:
/**
*
* 〈自定義工具類〉
*
* @author fritain
* @create 2019/4/26
* @version v0.0.1
*/
public class DiyUtil {
/**
* LOGO_PATH logo地址
*/
private static final String LOGO_PATH = PropertyReader.getValue("conf.properties", "con.report.logoUrl");
/**
* COVER_BGM_PATH 封面圖片存放位置
*/
private static final String COVER_BGM_PATH = PropertyReader.getValue("conf.properties", "con.report.coverBgpUrl");
/**
* HTML_PATH 用於臨時存放生成的HTML
*/
private static final String HTML_PATH = PropertyReader.getValue("conf.properties", "con.report.htmlUrl");
/**
* 生成頁眉html文件,並返回其所在的路徑
* @param templateName 當前查詢的報告名
* @return 返回生成的headerHtml文件路徑
*/
public String getHeaderPath(String templateName){
String htmlFileName = "headerHtml" + System.currentTimeMillis()+".html";
String htmlFileString = buildHeaderHtmlStr(getThematicImageString(LOGO_PATH), templateName);
String savePath = HTML_PATH+"/"+htmlFileName;
writingFile(savePath,htmlFileString);
// System.err.println(savePath);
return savePath;
}
public String getCoverPath() {
String htmlFileName = "coverHtml" + System.currentTimeMillis() + ".html";
String htmlFileString = bulidCoverHtmlStr(getThematicImageString(COVER_BGM_PATH));
String savePath = HTML_PATH + "/" + htmlFileName;
writingFile(savePath,htmlFileString);
// System.err.println(savePath);
return savePath;
}
/**
* 生成頁眉html的字符串,準備生成html文件
* @param logoPath Logo存放的路徑
* @param templateName 當前查詢的報告名
* @return 返回生成的html字符串
*/
private String buildHeaderHtmlStr(String logoPath,String templateName){
StringBuffer buffer = new StringBuffer();
//創建css字符串
StringBuffer cssBuffer = new StringBuffer();
//拼接css字符串
//創建css標籤
cssBuffer.append("<style type=\"text/css\">\n");
cssBuffer.append(".box{\n" +
" width: 1100px;\n" +
" height: 55px;\n" +
" position: relative;\n" +
" }\n");
cssBuffer.append(".logo{\n" +
" width: 180px;\n" +
" height: 55px;\n" +
" left: 20px;\n" +
" position: absolute;\n" +
" }\n");
cssBuffer.append(".text{\n" +
" width: 836px;\n" +
" height: 50px;\n" +
" position: absolute;\n" +
" left: 235px;\n" +
" top: 10px;\n" +
" background-color: #fff;\n" +
" }\n");
cssBuffer.append(".text_title{\n" +
" width: 826px;\n" +
" height: 50px;\n" +
" border-bottom: 2px solid #c00000;\n" +
" text-align: right;\n" +
" left: 235px;\n" +
" line-height: 73px;\n" +
" padding: 0px;\n" +
" margin: 0px;\n" +
" color: #808080;\n" +
" font-size: 14px;\n" +
" }\n");
cssBuffer.append("</style>\n");
//聲明文檔標籤
buffer.append("<!DOCTYPE html>\n");
//組裝<head></head>內容
buffer.append("<html lang=\"en\">\n" +
"<head>\n<meta charset=\"UTF-8\">\n" +
" <title>自定義頁眉</title>\n"+cssBuffer.toString()+"\n</head>");
buffer.append("<body>\n");
//組裝logo div
buffer.append("<div class=\"box\">\n" +
" <div class=\"logo\">\n" +
" <img src=\""+logoPath+"\">\n" +
" </div>\n");
//組裝頁眉右側文字
buffer.append("<div class=\"text\">\n" +
" <p class=\"text_title\">"+templateName+"</p>\n" +
" </div>\n");
buffer.append("</div>\n</body>\n</html>");
// System.err.println("Html字符串:"+buffer.toString());
return buffer.toString();
}
/**
* 封面頁面HTML拼接
* @param imagePath 背景圖片路徑
* @return
*/
public String bulidCoverHtmlStr(String imagePath){
StringBuffer css = new StringBuffer();
css.append("<style type=\"text/css\">\n");
css.append(".toggleDiv{ position: relative;height:100%;margin-top: 2px;width:1000px;margin:0 auto;page-break-inside:avoid;}");
css.append(".static_tables>p {font-size:20px !important;}");
css.append(".mod_title{text-align: left;padding:30px 0 0 0}");
css.append(".mod_title>hr{ height:2px;border:none;border-top: 2px solid #fff;margin: 0px}");
css.append(".mod_title>h1{display: inline-block;font-size: 26px;margin-bottom: 0px;margin-top: 10px;position: relative;top: -14px;padding: 0 20px;background: #FFF; font-weight: 700;color: #000;}");
css.append(".mod_title>h2{display: inline-block;font-size: 26px;margin-bottom: 0px;margin-top: 10px;position: relative;top: -14px;padding: 0 20px;background: #FFF; font-weight: 700;color: #000;}");
css.append(".null_data{text-align: center}");
css.append(".half{padding:0px 40px 0px 30px}");
css.append(".half>div{width:50%;display: inline-block;height:300px}");
css.append(".half>.static_tables{position: absolute;padding:20px 0px}");
css.append(".half>.static_tables>table{position: absolute;top: 12%;margin: auto;left: 0;right: 0;width: 94%;padding: 0;table-layout:fixed}");
css.append(".echartDiv{padding: 10px 0;color: #333;/*font-family: FangSong;*/font-size:50px !important;text-align:center;width:100%;height:300px;line-height: 15;}");
css.append(".toggleDiv>.description{ line-height: 25px;font-family:STKaiti;font-size: 18px !important;margin-left: 40px;display: block;line-height:30px;padding-right:2%;}");
css.append(".static_tables {padding: 20px 40px 20px 30px;}");
css.append(".static_tables>p {font-size:20px !important;}");
css.append(".static_tables>table{border-spacing:0px;width:100%}");
css.append(".static_tables>table>thead>tr{background-color: #464d6c}");
css.append(".static_tables>table>thead>tr>th{font-weight:bold; font-size: 16px !important; color: #fff !important;}");
css.append(".static_tables>table th,.static_tables>table td{border-bottom:1px solid #d4d4d4;border-left:1px solid #d4d4d4;text-align:center;/*font-family: FangSong;*/font-size: 16px !important;height:40px;color: #333;word-wrap:break-word;word-break:break-all;line-height:25px; padding: 5px 10px;}");
css.append(".static_tables>table th:last-child,.static_tables>table td:last-child{border-right:1px solid #d4d4d4;}");
css.append(".static_tables>table>tbody>tr:nth-child(2n){ background-color: #fff}");
css.append(".static_tables>table tr:first-child td{border-top: 1px solid #d4d4d4}");
css.append(".titleDiv { position: relative;top:14px;display: block;width: 100%;text-align: center}");
css.append(".toggleDiv>.title{/*font-family:FangSong;*/font-size:16px;color:#000}");
css.append(".bz_box{width: 100%; height: 100%;border: 1px solid #e0e0e1;margin: 0 auto;}");
css.append(".bz_box_title {border-bottom: 1px solid #e0e0e1;background-color: #ebecf3;padding-left: 20px;height: 35px;line-height: 35px;font-weight: bold;}");
css.append(".bz_box_con {height: 100%;overflow: auto;padding-left:20px;padding-right: 10px;;line-height: 30px;}");
css.append(".bz_box_con p{ padding: 0px;margin: 0px;}");
css.append(".bz_box_con p span{font-weight: bold;}");
css.append("</style>\n");
StringBuilder htmlStr = new StringBuilder();
htmlStr.append("<!DOCTYPE html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\"></meta>");
htmlStr.append("<style type=\"text/css\">html,body{height:100%;} span{display: block;font-size: 36px;padding: 12px;} .test{left:3%;top: 73%;bottom: 25%;right: 3%;position: relative;width: 1330px;} .cover2{left:3%; right: 5%;top: 78%;bottom: 22%;width: 1330px;position: relative;background-color: white; font-size:26px;} span>i{font-style: normal;padding: 0 5px;}");
htmlStr.append("</style>");
htmlStr.append(css.toString());
htmlStr.append("</head><body><div style=\"width:1400px;height:1850px;\"><div style=\"background-size:100% 100%;width:100%;height:100%;background:url("+imagePath+") no-repeat\">");
htmlStr.append("<div class=\"test\" style=\"background-color:red\"></div>");
htmlStr.append("</body></html>");
return htmlStr.toString();
}
/**
* 獲取圖片
* @param path 圖片路徑
* @return
*/
private String getThematicImageString(String path){
InputStream inputStream = null;
byte[] data = null;
String classPath = this.getClass().getClassLoader().getResource("/").getPath();
String fileNameT = path;
String rootPathT;
if("\\".equals(File.separator)){
//Windows
rootPathT = classPath.substring(1,classPath.indexOf("/WEB-INF/classes"));
fileNameT = (rootPathT+File.separator+fileNameT).replace("/", "\\");
}else if("/".equals(File.separator)){
//linux
rootPathT = classPath.substring(0,classPath.indexOf("/WEB-INF/classes"));
fileNameT = (rootPathT+File.separator+fileNameT).replace("\\", "/");
}
try {
inputStream = new FileInputStream(fileNameT);
data = new byte[inputStream.available()];
inputStream.read(data);
} catch (IOException e) {
UnitedLogger.error("加載圖片"+e.getMessage(), e);
}finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException io){
UnitedLogger.error("關閉流失敗"+io.getMessage(), io);
}
}
}
BASE64Encoder encoder = new BASE64Encoder();
return "data:image/png;base64,"+encoder.encode(data).replaceAll("\r\n","");
}
private void writingFile(String path,String fileText){
FileOutputStream fos;
BufferedWriter bw;
try {
fos = new FileOutputStream(path);
bw = new BufferedWriter(new OutputStreamWriter(fos,"UTF-8"));
bw.write(fileText);
bw.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
conf.properties配置文件的內容
#wkhtmltopdf html+pdf temp save dir
con.report.htmlUrl=c:/temp
con.report.pdfPath=c:/temp
con.report.logoUrl=static/module/img/zxlogo.png
con.report.coverBgpUrl=static/module/img/backThematic.jpg
三、寫在後面
由於項目的保密要求,所以很抱歉不能和大家分享怎麼去讓他們相互調用,生成一份好看的報告。等到項目脫敏以後,我會把具體的調用還有目錄的動態處理一起拿出來和大家分享。
本作品採用知識共享署名-相同方式共享 4.0 國際許可協議進行許可。