教你如何使用 PHP 优雅的生成 PDF 和 Image

导读:本人初次写博客,有不足之处,请多多指教 。

起因

因为项目中有需求,要动态的生成一些图片,图片的格式要求也比较严格就像是这样的

file

表格中的数据如 单价 数量都是动态的数据, 刚开始准备用 GD库 来操作,后来想想不太现实所以去找了找有没有比较成熟的 php 类库。有不少的类库,可以实现直接生成 PDF 而没有可以直接生成 Image 的扩展包。

后来发现了一个 开源的软件 wkhtmltox 该软件支持读取本地和网络端的网页,直接在本地生成 pdfimagewkhtmltox官方地址

  • 该软件有 Window 版本和 Linux 版本(具体支持详情可以查看其官网)
  • 下载之后需要将可执行文件的路径添加到环境变量中

具体的使用大概是这样的

    $ D:/wktopdf/wkhtmltopdf/bin/wkhtmltoimage --format "jpg" "./1-夏天/夏天.html" "./1-夏天/夏天.jpg"
    $ wkhtmltopdf --format "pdf"  "./1-夏天/夏天.html" "./1-夏天/夏天.pdf"

支持中文路径和中文文件名, 但是用户可不会这种操作,所以需要一些封装(将执行流程抽象化),有一个哥们已经为我们做了,所以不需要我们去造轮子了


PHP实现方式一

KnpLabs/snappy一个基于 wkhtmltoxphp 扩展包 扩展包GitHup地址

使用方式

$ composer require knplabs/knp-snappy
<?php
require __DIR__ . '/vendor/autoload.php';

use Knp\Snappy\Image;

$snappy = new Image('D:/wktopdf/wkhtmltopdf/bin/wkhtmltoimage');                           // 如果你是window环境, 需要将该路径添加到环境变量中
$snappy = new Image(__DIR__ . '/vendor/h4cc/wkhtmltoimage-amd64/bin/wkhtmltoimage-amd64'); // 如果你是linux环境

$str = <<<'EOD'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<style>
    @font-face {
        font-family: myFirstFont;
        src: url('C:/Users/Administrator/Desktop/snopy/Microsoft Yahei.ttf');
    }

    body {
        font-family: myFirstFont;
    }

</style>
<body>

</body>
</html>
EOD;

$path = __DIR__ .'/我的/';

if (!is_dir($path)) {
    mkdir($path, 0755, true);
}

// 实际生产环境中为了不报错,需要进行异常处理
try {
    // 第一个参数是需要转换的 html
    // 第二个参数是文件保存路径
    $result = $snappy->generateFromHtml($str, $path . '夏天.jpg');
} catch( Exception $e) {
    var_dump($e->getMessage());
}

你也可以从 compoer 中安装需要的 wkhtmltox 依赖,
file

由于 wkhtmltox 是基于 QtWebKit rendering engine, 所以理论上是可以,生成比较复杂的 PDF ,只要你的 Css 写的够好。同时也支持网络协议,可以从网页中生成 PDFImage
如果要引入一些外部的 css 字体 图片 请使用 绝对路径, 生成的 PDF 的大小格式或者分页的使用方式请移步 GitHub地址

重点该扩展包会使用 proc_open 函数 去执行生成的 shell 脚本,所以对生产环境的安全有一定的威胁,最后被砍掉了。


安全的实现方式

由于安全原因,自己做的东西被砍了以后,也尝试了别的方式去实现这个功能,但是都不太如意,因为需要生成的 Image 比较复杂,要么是 css 支持的不是很好,要么就是字体和图片无法引入的错乱。最后想到了在服务端 window的批处理文件 .bat 文件来实现。后来也实现了,可是使用的复杂程度就上去了。
最后在 php 的官方文档中发现了这个

file

查看了一下也是基于 wkhtmltox 的一个 php 的扩展包,于是就动手去实现了一下。

依赖项

  • wkhtmltox 软件 liunx 版本(目前个人只是编译了linux的扩展,如果有兴趣的可以尝试去编译 window版本的)

具体步骤(linux环境)

  1. 首先需要到官网去下载软件 软件下载地址
  2. 扩展需要编译的源码 源码下载地址
  3. 请注意 wkhtmltox 区分操作系统是32位还是64位的
  4. 个人服务器环境是centos7, 使用的 wkhtmltox 的版本是 0.124 0.124版软件包下载地址

开始编译需要的php扩展

git clone https://github.com/krakjoe/wkhtmltox
cd wkhtmltox
phpize
// 这里因为每个人安装环境不一样 /path/to/wkhtmltox/installation 请将这个地址替为wkhtmltox真实的安装路径
 /// PATH 替换为真实的php配置路径
./configure --with-wkhtmltox=/path/to/wkhtmltox/installation --with-php-config = PATH
make
make install
  1. 修改配置文件 ,将生成的 wkhtmltox.so 添加到 php.ini 的配置中,重启php
  2. 安装成功效果

file

  1. 然后就可以 开始愉快的使用这个强大的工具了

HELLO PDF

use wkhtmltox\PDF\Converter as PDFConverter;
use wkhtmltox\PDF\Object as PDFObject;

$converter = new PDFConverter([
    "out" => "test.pdf"
]);

$converter->add(new PDFObject(
    file_get_contents("http://www.baidu.com")));

$converter->convert();

HELLO IMAGE

use wkhtmltox\Image\Converter as ImageConverter;
// 这里第一个参数可以传入 ` html ` 的字符串
$converter = new ImageConverter(null, [
    "fmt" => "png",
    "in" => "http://www.baidu.com",
    "out" => "test.png"
]);

$converter = new ImageConverter($str, [
    "fmt" => "jpg",
    "out" => "test.jpg"
]);
$converter->convert();

这个安全的实现方式,不会使用到一些会威胁到服务器安全的函数

本文章首发在 Laravel China 社区