前言
- 
    
一开始准备使用
xhtmlrenderer实现html转pdf,好处是不用安装,mavn中直接引入依赖就可以使用。但使用过程中发现它只支持css2.1,很多样式显示不出来,而且对html中标签的要求很高,如果标签不闭合则不能使用。 - 
    
wkhtmltopdf对样式支持很好,基本能还原网页内容,缺点是需要在服务器中安装才能使用。 
安装wkhtmltopdf
- 本地环境为
win10 - 生产环境为
Ubuntu 5.4.0-6ubuntu1~16.04.4 
windows环境安装
- 
    
首先根据电脑系统下载安装包wkhtmltopdf官网
 - 
    
安装后在系统变量
Path中增加bin目录,我的是D:\Program Files\wkhtmltopdf\bin - 
    
使用它很简单,在
cmd中执行命令wkhtmltopdf https://www.baidu.com D://test.pdf,进度条走完后就会在D盘下生成test.pdf文件 
linux使用docker安装
- 在
DockerFile文件中增加代码: 
RUN apt-get update && \
    apt-get install -y xvfb && \
    apt-get install -y wkhtmltopdf
    
# 拷贝字体,否则中文显示不出来
COPY src/main/resources/fonts/msyh.ttf /usr/share/fonts/
COPY src/main/resources/fonts/minijiankai.ttf /usr/share/fonts/
- 一定要将字体拷贝到
/usr/share/fonts/中,否则生成的pdf不显示中文 
java代码
- 项目需求是前端提供
html字符串给后台接口,然后转换成pdf,所以需要先将html字符串生成html文件,代码如下: 
public void pdf(String html) {
    String uuid = UUID.randomUUID().toString();
    //生成的html文件名
    String htmlFileName = uuid + ".html";
    //转换后的pdf文件名
    String pdfFileName = uuid + ".pdf";
    //根据系统判断文件存放路径
    String os = System.getProperty("os.name");
    String filePath;
    if (os.toLowerCase().indexOf("win") > -1) {
        filePath = System.getProperty("java.io.tmpdir") + File.separator + "ReadHtml" + File.separator + htmlFileName;
    } else {
        filePath = File.separator + "data" + File.separator + "tmp" + File.separator + "ReadHtml" + File.separator + htmlFileName;
    }
    File htmlFile = null;
    File pdfFile = null;
    try {
        //生成html文件
        htmlFile = new File(filePath);
        if (!htmlFile.getParentFile().exists()) {
            htmlFile.getParentFile().mkdirs();
        }
        //将html字符串写入到html文件中
        FileWriter fileWriter = new FileWriter(htmlFile);
        fileWriter.write(html);
        fileWriter.close();
        //调用wkhtmltopdf将html转为pdf
        if (os.toLowerCase().indexOf("win") > -1) {
            String cmd = "cmd /c wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
            Process process = Runtime.getRuntime().exec(cmd);
            process.waitFor();
        } else {
            String cmd = "wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
            Process exec = Runtime.getRuntime().exec(cmd);
            exec.waitFor();
        }
        pdfFile = new File(htmlFile.getParent() + File.separator + pdfFileName);
        if (pdfFile.exists()) {
            System.out.println("成功");
        } else {
            System.out.println("失败");
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //删除临时文件
//          if (htmlFile != null && htmlFile.exists()) {
//              htmlFile.delete();
//          }
//          if (pdfFile != null && pdfFile.exists()) {
//              pdfFile.delete();
//          }
    }
}
- 以上代码在
windows中可以执行,但是在linux中不能执行 - 进入
docker容器,直接执行wkhtmltopdf命令发现报错如下: 
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-root'
qt.qpa.screen: QXcbConnection: Could not connect to display 
- 参考了这篇文章wkhtmltopdf依赖于X server的解决方案,发现是因为没有启动图形用户界面导致的,只需要在命令前增加
xvfb-run即可,修改后的代码如下: 
//在命令前增加 xvfb-run
String cmd = "xvfb-run wkhtmltopdf " + htmlFile.getPath() + " " + htmlFile.getParent() + File.separator + pdfFileName;
Process exec = Runtime.getRuntime().exec(cmd);
exec.waitFor();