关于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 安装与配置

  1. 点上面的下载地址,点击下载并安装 wkhtmltopdf。
  2. PATH中添加配置你的wkhtmltopdf安装目录\bin
  3. 检查你的 wkhtmltopdf是否配置成功,打开终端,输入 wkhtmltopdf -V如果出现 wkhtmltopdf 工具的版本信息则说明你已经可以使用该工具了。

5. wkhtmltopdf 使用

  1. 命令行操作
  2. 通过代码操作

第一种操作方式网上有很多教程,在此不再赘述。我在这里讲一讲在代码中使用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 国际许可协议进行许可。

发布了9 篇原创文章 · 获赞 13 · 访问量 4341

猜你喜欢

转载自blog.csdn.net/qq_22926739/article/details/89962713