PHP利用ImageMagick实现PDF、PPT转图片

最近应业务需要,需要实现在线浏览 PPT、PDF 的功能。搜了一阵,看似好用的微软 Office Web Viewer 却有着速度极慢、限制10M大小等麻烦,前端直接使用 pdf.js 也遇到了跨域之类的问题,索性一不做二不休,把 PPT 和 PDF 都转换成图片,再分页传回给前端。

去网上搜了一圈,看到了 imagick 这个扩展库,看了操作好像也是十分地简单,没想到真的动手做起来遇到了不少的麻烦。

首先是网上的版本,即用 PHP 的扩展库 imagick 来实现。
第一步是要安装,我用的是宝塔面板,在 PHP 的安装扩展中找到了 imagemagick,一键安装,并没有再遇到什么问题,直接就能使用了。后来发现不少人手动安装时遇到了许多问题,好像是因为 ImageMagick 需要 Ghostscript 的支持,后面我也装了一遍,确实挺麻烦的,这里记录一下我的安装过程:
我的系统是 CentOS7 的,在这个网址下载——https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs923/ghostscript-9.23.tar.gz
上传到服务器进行解压,然后安装;或者直接通过命令行下载安装(比较慢),输入gs –version 查看是否成功安装。

//编译安装
# wget -c https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs923/ghostscript-9.23.tar.gz
# tar xzvf ghostscript-9.23.tar.gz
# cd ghostscript-9.23
# ./configure && make && make install
//直接yum安装
yum -y install ghostscrip

之后是安装 ImageMagick 和 imagick,这里因为我是宝塔直接装的,没有手动安装过,不知道还有没有坑。另外,装完 imagick 之后还需要在 php.ini 中开启相应的扩展。

//编译安装ImageMagick
# tar xf ImageMagick-6.8.9-9.tar.gz
# cd ImageMagick-6.8.9-9
# ./configure --prefix=/usr/local/imagemagick
# make && make install
//直接yum安装
# yum install ImageMagick

//编译安装imagick
# tar -xf imagick-3.2.0RC1.tgz
# cd imagick-3.2.0RC1
# /usr/local/php/bin/phpize
# ./configure --with-php-config=/usr/local/php/bin/php-config --with-imagick=/usr/local/imagemagick
# make && make install

环境真是开发过程中最大的敌人,仅仅只是想把 pdf 转成图片这么个小操作,就得安装三个东西,真是麻烦的很。光顾着安装了,还不知道这三个分别是啥呢。这三个当中最核心的是 ImageMagick,它是第三方的图片处理软件,功能强大,可以理解为命令行版本的PS。然后是 imagick,是php的一个扩展模块,可以调用 ImageMagick 提供的API来进行图片操作。最后是 Ghostscript,是一款可以操作 pdf 的软件,在这里 ImageMagick 无法直接实现 pdf 文档到图片的转换,需要借助于 Ghostscript 软件包。

总算差不多了,终于能够进行开发了。本以为应该很容易就能实现,没想到踩坑之路才刚刚开始。首先先贴一段根据网上的代码修改的代码。

public function pdfToPng($pdf,$path,$page=-1) {
    if (!extension_loaded('imagick') || !file_exists($pdf) || !is_readable($pdf)) {
        return ['ok' => FALSE, 'msg' => '服务器错误', 'code' => 2003, 'data' => []];
    }
    $im = new \Imagick();
    //设置分辨率
    $im->setResolution(150, 150);
    //设置压缩质量,1-100,100为最高
    $im->setCompressionQuality(100);
    //是否进行分页操作
    if ($page == -1) {
        $im->readImage($pdf);
    } else {
        $im->readImage($pdf . "[" . $page . "]");
    }

    foreach ($im as $Key => $Var) {
        //设置图片格式
        $Var->setImageFormat('png');
        $filename = $path . md5($Key . time()) . '.png';
        if ($Var->writeImage($filename) == true) {
            $Return[] = $filename;
        }
    }
    return $Return;
}

核心代码其实很简单,先实例化 Imagick 类,然后进行一些相应的设置,最后再通过 writeImage 导出为图片。接下来看看效果(第一张为图片,第二章为原PDF)

一开始我眼瞎,以为这个库只能做到读取 PDF 中的图片,文字并不能读取到。在漫长的百度过程中又把 png 格式改成了 jpg,中间又出现了些奇怪的黑色区域。后来无意中按出 F12 加上仔细观察才发现,原来文字并没有显示出来的原因是背景为透明的。

找到问题之后,目标变得明确了——把背景搞白!在又一阵搜索之后,找到了一个废弃的方法 flattenImages,但是很可惜,它已经被官方弃用了,并且没有明确地说用哪个函数代替它。即使这样,还是不能气馁,去搜了搜 它,大概了解到它的功能——合并图层!没错,把我们之前的结果和一个空白图层合并,不就能达到把背景变成白色的效果了吗!按照这种思路,继续搜索,终于找到了!这里贴一下核心代码,只需要修改之前代码中的 foreach 循环中的内容就够了。

$blankPage = new \Imagick();
//一张白纸,作为背景
$blankPage->newPseudoImage($item->getImageWidth(), $item->getImageHeight(), "canvas:white");
//设置合并的位置
$blankPage->compositeImage($item, \Imagick::COMPOSITE_ATOP, 0, 0);
//合并!
$blankPage = $blankPage->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN);
$filename = str_replace('.pdf', '-' . $key . '.jpg', $pdfStr);
if ($blankPage->writeImage($filename) == true) {
    $result[] = $filename;
}

果不其然,白色的背景来了!但是就在我欢呼雀跃,想着终于把问题解决了的时候,又发现了一点不对劲,这图片失真也太严重了吧!稍微放大一点都模糊得不行了。本以为这应该不是什么问题,只需要改一改之前提过的分辨率和压缩质量,结果发现,不论怎么改图片的分辨率都没有半点变化。

虽说至此,问题已经勉勉强强地解决了,但是这个分辨率是个硬伤,肯定不能放着不管。又在一顿疯狂的搜索之后,我仍然没能解决,终于,我败给了时间,放弃了这条路。但于此同时,一条光明大道逐渐在我眼前明亮起来。之前也提到过,imagick 只是把 ImageMagick 的命令封装了一下给 PHP 使用而已,那我索性直接绕过 imagick,直接执行 ImageMagick 命令不就好了?一顿操作之后,果然,成了,终于成了。

$command = 'convert -density 150 -background white -alpha remove '.$pdfStr.' '.str_replace('.pdf', '.jpg', $pdfStr);
exec($command);

稍微解释一下吧,-density 150 指定输出的分辨率,越大越清晰,但是文件也会相应地变大。
-background white -alpha remove 可以一次命令转换多页 PDF 成多个图片并保持白色背景。
后面两个参数分别是需要被转换的 pdf 文件地址和转成的图片名,比如像我这样写,能够把两页的 pdf 文件1.pdf 转成1-0.jpg 和 1-1.jpg 。

接下来是 PPT 转图片。这里要利用 Linux 下的另一个工具:unoconv。unoconv,全称为 Universal Office Converter ,是一个命令行工具,可在 LibreOffice/OpenOffice 支持的任意文件格式之间进行转换。

首先是安装:我的系统是 CentOS7 的,如果是别的系统不适用的话,还得再去百度一下。

1、安装libreoffice:
yum install -y libreoffice.x86_64
2、下载或者克隆unoconv:
wget https://github.com/unoconv/unoconv/archive/master.zip
3、解压并安装:
unzip master.zip
cd unoconv-master/
make install

就这样简简单单的三步,unoconv 就安装完了。unoconv 的使用也非常的简单,举一个最简单的例子:

unoconv -f new.pdf old.ppt 

虽然转换十分的简单,只需要一个指令,但是这里还有一个隐藏的小坑——Linux 下和 Windows 下的字体文件不同,很多 Windows 有但是 Linux 并没有,这就会导致转换之后出现文字乱码、排版出错等问题,解决方案就是,把 Windows 下的字体拷一份到 Linux 下。

1、找到Windows下的这个目录:C:\Windows\Fonts,把里面的文件拷到Linux下的/usr/share/fonts/ 下,最好新建一个目录,再放在里面,方便管理。
2、进入目录,执行指令,比如说我新建的目录叫win
cd /usr/share/fonts/win
mkfontscale
mkfontdir            //这两条命令是生成字体的索引信息
fc-cache -fv        //更新字体缓存

安装好字体之后,再执行一次转换指令,应该不会有什么大问题了。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注