关于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 国际许可协议进行许可。