背景
有时候我们做推广、宣传之类的需要动态生成一些比较炫丽的图片,或者生成一个PDF文件(如:订单、捡货单等)。这些需求对界面及排版格式等要求比较高,如果采取代码直接绘图的方式可以实现起来成本比较高,且后期改动不方便,因此考虑使用HTML页面转换的方式实现。
实现方案
安装 wkhtmltox
wkhtmltox 包括两部分 wkhtmltopdf
和 wkhtmltoimage
分别用于生成PDF文档和图片,安装比较简单,如下:
# 进入用户安装目录
cd /usr/local
# 下载
sudo wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
# 解压
sudo tar -xvf wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
# 进入bin目录建立软链接
cd /usr/local/bin
ln -s /usr/local/wkhtmltox/bin/wkhtmltoimage
ln -s /usr/local/wkhtmltox/binwkhtmltopdf
使用 wkhtmltox
wkhtmltoimage --help
查看使用说明,更详细的说明可以看这里,比如基于网页生成一张图片,只需要在命令行执行如下操作
sudo wkhtmltoimage --width 1146 /tmp/demo.html /tmp/demo.png
说明:
width
- 指定图片生成的宽度,长度可以不指定,会自动处理/tmp/demo.html
- 网页绝对路径/tmp/demo.png
- 生成图片的绝对路径
注意:
- 使用时如果不能运行该命令可能是缺少依赖
libxrender1
,需要自行安装 - 切记需要把
exec
命令允许在服务器执行中 - 如果服务器没有中文字体时,用到中文时可能会出现乱码的情况
- 如果想提高图片的质量,可以将页面尺寸等比例放大,然后再生成
PHP代码示例
下面以生成日签图如示例,讲解在项目中的使用。
/**
* 生成日签
*
* @param string $title 标题
* @param string $dailyDate 日期
* @param string $content 内容
* @param string $coverUrl 封面图
* @return string
*/
protected function generateDaily($title, $dailyDate, $content, $coverUrl)
{
$filePath = base_path('util/temp/daily-generate.html');
if (! is_file($filePath)) {
throw new Exception\RuntimeException('日签图模板不存在,请联系管理员');
}
// 获取日签图模版
$dailyTemp = file_get_contents($filePath);
// 替换模版数据
$tempData = [
'var_title' => $title,
'var_dailyDate' => $dailyDate,
'var_content' => $content,
'var_coverUrl' => $coverUrl,
];
$html = str_replace(array_keys($tempData), array_values($tempData), $dailyTemp);
// 生成临时文件
$tmpFile = self::TMP_DIR . '/' . str_random() . '.html';
file_put_contents($tmpFile, $html);
// 生成日签图
$dailyFilename = date('Ymd', strtotime($dailyDate)) . time() . '.jpg';
$daily = storage_path('app/public/daily/' . $dailyFilename);
exec("/usr/local/bin/wkhtmltoimage --width 1146 {$tmpFile} {$daily}", $output, $return);
if ($return !== 0 || ! is_array($output)) {
throw new Exception\InvalidArgumentException('日签图片生成失败');
}
// 判断图片是有效
if (! is_file($daily) || filesize($daily) <= 0) {
throw new Exception\InvalidArgumentException('日签图片未生成或者无效');
}
// 删除临时文件
unlink($tmpFile);
return asset("/storage/daily/{$dailyFilename}");
}
效果图如下
拓展知识
安全性的考虑
上面的方式是利于PHPexec
来执行服务器端脚本生成的,如果觉得不安全的话,还可以采用编译PHP扩展的方式,具体实现参考PHP扩展文档 wkhtmltox
中文字体支持
# 进入系统字体目录并自定义文件夹
cd /usr/share/fonts
sudo mkdir -p chinese/truetype
cd chinese/truetype
# 将下载好的字体上传到该目录(如:微软雅黑 msyh.ttf)
sudo rz msyh.ttf
# 扫描字体目录并生成字体信息的缓存
sudo fc-cache -fv
# 查看安装是否成功
sudo fc-list
目前同类功能的项目
目前能达到此效果的同类项目有很多,有大型也有小型的,整理如下,感兴趣的话可以探索一下:
TCPDF
- 可以生成PDF和图片PhantomJs
- 多用于爬虫,也可以用于生成网页截图、自动化测试等CasperJs
- 与phantomjs
类似,是在它基于上做了进一步的封装,使用起来更加顺手Puppeteer
- 谷歌主刀的开源爬虫项目,功能更大强大,以至于当此项目出来后PhantomJs
项目作者宣布不再维护