热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

利用命令行将pdf转换为长图

利用命令行将pdf转换为长图业务场景项目中会时不时遇到展示pdf文件的需求,比如需要展示某些合同或者一些PPT报告之类的。我们在做《娱乐宝》、《票票专业版》项目期间都
利用命令行将pdf转换为长图

业务场景

项目中会时不时遇到展示pdf文件的需求,比如需要展示某些合同或者一些PPT报告之类的。我们在做《娱乐宝》、《票票专业版》项目期间都遇到了这样的需求。针对如何展现pdf文件的内容,一般无外乎以下几种方案:

  • 让客户端渲染pdf
  • H5页面通过开源的JS库对PDF文件进行渲染
  • 将pdf打印为图片,然后再利用H5页面对文件中的多张图片分步下载并渲染。

而这几种方案在实际操作过程中又分别有各自的问题,我们首先讨论第一种方案。让客户端渲染PDF有两种方式:1. 借助系统已有的功能,比如webbiew。2. 利用开源的pdf渲染库。iOS中webview自带了pdf渲染功能,同时支持缩放等操作,体验相当好,但安卓不支持,需要自己开发实现。而第三方PDF渲染库普遍比较大,一般都要好几兆,为了这样一个非核心功能引入这么大一个库,客户端同学是坚决不会答应的。第二种方案初看起来不错,至少省去了客户端兼容的成本。调研了下JS渲染PDF方面的实现,比较著名的是Mozzilapdf.js。但这个库也有一些问题:

  • 这个库的源文件体积不小,源文件282K,gzip压缩后110K。
  • 需用通过Ajax方式加载PDF文件,而正式项目中,我们一般需要把PDF文件上传到CDN。
  • 使用Canvas对PDF中的图片进行渲染,PDF中图片比较多的话将会生成大量的canvas
  • 渲染出的结果存在兼容性问题,不同字体设置会导致渲染结果差异很大。

前两个问题通过一些技术手段还能绕过去,但后两个几乎无解了,尤其是用canvas渲染图片。canvas在移动端太耗性能了,Canvas太多的话会造成浏览器渲染性能严重下降,iphone下甚至会导致APP崩溃。采用Canvas渲染图片的证据。

不同字体设置导致渲染结果不一致的问题:

此外在测试过程中还发现:那个库提供的Demo页面在UC下打开且打开的PDF文件比较大的情况下,多翻几页之后会出现页面加载不出来的情况,因此这个库在H5下的兼容问题堪忧。 因此采用JS库渲染PDF目前来看不太适合应用在H5项目中,在PC项目中还可以考虑下。

经过排除后目前只剩下将PDF转换为图片然后用H5来渲染这一方案了,至于此方案的具体实现,可以参照之前发布的一篇文章《一个简单H5活动页面模板的设计》。这个方案比较简单可靠,但面临一个很烦人的问题:需要将PDF的每一页转换为图片然后拼接为长图。如果这个过程需要人工来完成将是非常繁琐的,而如果文件比较大的话那简直是噩梦了,因此这个过程是必须由程序自动来完成的。

利用命令将PDF自动转换为长图

如果由程序完成将PDF转换为长图,必须要实现两个功能:

  • 将PDF的每一页转换为图片
  • 将转换后的多张图片合并为一张长图

还好这两个功能都有相应的软件支持,而且这两个软件的命令行支持都非常好,而且都支持brew进行安装。将PDF转换为图片最著名的库莫过于GhostScript,在项目中我们也选用了这个库。将PDF的每一页转换为图片可以通过下面的命令来实现

gs -sDEVICE=pngalpha \ # 输出格式为png-o "./tmp-pdf-page/$filename-%d.png" \ # 设置每一页对应图片的名称-r144 "$pdfname"; # 设置每英寸内的像素数

将多张图片拼合为一张有多种软件可以实现,比较有名的是ImageMagickGraphicsMagick。ImageMagick资历最老,文档最全,支持的特性最多,但运行起来比较缓慢。GraphicsMagick脱胎于ImageMagick 5.x,支持的特性比较少,命令格式几乎与ImageMagick通用,运行速度飞快,但文档非常少,而且有些特性不支持(本文后面程序中所使用的切功能:shave在测试时没有调试通过)。考虑到这个功能无论在本机还是服务端调用都不是很频繁,因此我们使用了ImageMagick。下面的代码可以实现将多张图片拼接为一张长图,为了输出的图片更加美观,图片之间添加了一定的白色空白。

convert ./tmp-pdf-page/$filename-*.png \-background white \-bordercolor white \ # 设置图片边框颜色-border 0x50 \ # 图片上下添加50像素边框 ,因此图片之间有100px的边框-append \ # 图片直接垂直拼接,如果水平拼接可用+append-shave 0x50 \ # 删除合并后图片的上下边框,GraphicsMagick不支持此操作-resize 1080 \ # 将拼接后的图片宽度调整为1080-quality 85 \ # 设置输出的JPG图片质量-sharpen 0x1.0 \ # 拼接后的图片字体有点发虚,在垂直方向做锐化处理
$filename-dest.jpg

有一点需要说明下,安装GhostScript后,ImageMagick内部可以直接调用GhostScript实现将PDF转换为长图,具体实现可以参考如下:

convert demo.pdf \-resize 620 \ # 设置每张图片尺寸-alpha remove \ -density 620 \ # 设置分辨率,按文档应该越高越好 -mattecolor '#cccccc' \ # 设置间隔颜色,作用与上面代码中的border相同-frame 10x5 \ # 设置图片间隔宽度-append \-quality 85 \-frame 0x5 \-sharpen 0x1.0 \demo.jpg

这段代码省去了第一步利用GhostScript将PDF转换为多张图片的步骤,但效果不是很理想,无论怎么设置分辨率(density)和JPG质量(quality),转换出来的图片都有点糊,因此实际项目中我们使用了分开处理的方案。

因为操作比较多,我们写了个bash脚本对这些逻辑做封装,使用方式为:bash convert.sh demo.pdf,脚本完整代码如下:


#!/bin/bash## 计算pdf文件名,参考资料:
# http://www.runoob.com/linux/linux-shell-variable.html
# https://stackoverflow.com/questions/965053/extract-filename-and-extension-in-bash/965072
# https://stackoverflow.com/questions/965053/extract-filename-and-extension-in-bash
# https://stackoverflow.com/questions/3362920/get-just-the-filename-from-a-path-in-a-bash-script
# pdfname=$1
filename="${pdfname%%.*}"## 创建临时文件夹存储每张pdf页面对应的图片
mkdir tmp-pdf-page## 将pdf转换为多张png
gs -sDEVICE=pngalpha -o "./tmp-pdf-page/$filename-%d.png" -r144 "$pdfname";## 将多张图片合并为一张,每张图片直接添加50像素间隔,最后
# 将图片尺寸设置1080宽度后裁掉第一张和最后一张的边框,并
# 进行锐化处理后输出为jpg。
# 参考资料:
# http://www.imagemagick.org/Usage/crop/#border
# http://www.imagemagick.org/Usage/crop/#frameconvert ./tmp-pdf-page/$filename-*.png \-background white \-bordercolor white \-border 0x50 \-append \-shave 0x50 \-resize 1080 \-quality 85 \-sharpen 0x1.0 \
$filename-dest.jpg## 删除单张pdf文件对应的图片
rm -rf ./tmp-pdf-page

转换后图片在线上的实际效果

是否可以应用到服务端?

答案是可以,这一方案依赖的两个软件:ImageMagick和GhostScript在Linux和Mac下均有提供,所以可以无缝移植的服务端。最早做这个方案的研究是在一年多以前,当时在做《娱乐宝》项目,每个项目上线都要上传合同,所以把生成图片并上传CDN的功能做到了小二后台中。当时是直接利用ImageMagick将PDF转换为长图的功能,没有使用先用GhostScript转换为多图然后再用ImageMagick拼接的方案。当时的效果不是很理想,文字总是比较糊。但当时一来没有找到理想的解决方案,二来支付宝对于图片的大小有要求,所以就将就着用了。后来项目中又遇到了这个需求,所以花了些时间整理和优化了下,所以有了本文提到的这个方案。

移植到服务端没有问题,但有几点需要注意下:

  • 服务端环境一般都没有安装ImageMagick,需要自己手动安装。而且Linux版本的ImageMagick处于安全考虑是不能直接完成pdf转图片的,需要对配置文件进行一些配置。具体配置很简单,基本看一眼就懂了。
  • Linux环境下中文字体普遍比较少,好像只有宋体,所以转换出来的效果没有Mac下好看。如果这种需求的频率比较低且对最终的转化效果由一些要求,建议还是在Mac下进行转换。
  • 本文的bash脚本方案会产生临时文件,不建议部署到服务端!

后记

目前这个方案还是不是特别理想,一个让人很不爽的地方是:因为每个pdf页面都需要生成一张图片,所以程序运行期间需要建立多个临时文件。我一向对临时文件深恶痛绝,因为临时文件不仅会凭空增加磁盘访问量,而且如果管理不好的话会造成垃圾文件越堆越多,而如果不巧这个程序运行在服务端那就有可能把磁盘都占满了。在写此文之前,我曾尝试了多个方法把这个临时文件干掉,但最终都不是很理想。

首先GhostScript提供将结果输出到标准IO的功能,但ImageMagick的append功能无法支持从标准IO读取多张图片文件,因此此方案行不通。GraphicsMagick也不支持从命令行读取多张方案,但gm与GhostScript协同调用的效果比ImageMagick的效果要好,转换后的效果与本文中用两部实现的效果相当,但需要自己手动计算PDF页数,而且因为不支持-shave参数,需要自己手动对最后转换后的图片进行必要的裁切。我们的使用场景主要是开发本机调用,开发时间所限,没有对GraphicsMagick方案进行进一步调研。如果是部署到服务端,建议使用GraphicsMagick,不仅效率高而且不会产生临时文件,GraphicsMagick直接将PDF转换为长图的代码:

gm convert -density 1080 \-mattecolor red \-frame 0x50 \-append \-shave 0x50 \ # 裁剪功能在GM下没有生效,不知是否是使用不当还是这种情况下不支持。-resize 1080 \ -quality 85 \test.pdf[1-4] \ # 这里需要手动制定要转换的页码范围test-tmp.jpg

参考资料

  • http://www.runoob.com/linux/linux-shell-variable.html
  • https://stackoverflow.com/questions/965053/extract-filename-and-extension-in-bash/965072
  • https://stackoverflow.com/questions/965053/extract-filename-and-extension-in-bash
  • https://stackoverflow.com/questions/3362920/get-just-the-filename-from-a-path-in-a-bash-script
  • https://stackoverflow.com/questions/653380/converting-a-pdf-to-png
  • http://www.imagemagick.org/discourse-server/viewtopic.php?t=15523
  • http://www.imagemagick.org/Usage/crop/#border
  • http://www.imagemagick.org/Usage/crop/#frame



推荐阅读
  • 1.man(相当于cmd--help)对不熟悉的命令想查询详细使用方法的帮助解释可以使用eg:manls就可以查看ls相关的用法注: ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • LaTeX使用XeLaTeX入门基础(一)
    主机平台:GentooLinux11.2内核版本:LinuxKernel3.2.1编译环境:XeTeX3.1415926-2.3-0. ... [详细]
  • 开发中,EXT封装的.NET控件,使用了ExtJsExtenderControl的开源控件,发现个问题,就是每次控件加载,都需要调EXT_ALL.JS文件,600K,导致页面加载很慢。想对这个问题进行 ... [详细]
  • 传送门上一篇:Day4-前端高频面试题之浏览器相关1、请介绍一下HTTP和HTTPS的区别?HTTPS是在HTTP的基础上加入了SSL协议 ... [详细]
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • Chrome浏览器非常强大,使用Chrome浏览器对页面性能进行检测,根据测试的结果进行优化。当然这个结果只是参考,在实际的项目中肯定有特殊情况存在,并不能为了满足某项测试结果而忽略特定情况的存在。1 ... [详细]
  • HTTP协议之总结展望篇
    文章目录HTTP2HTTP2内核HTTP3Nginx:高性能的Web服务器OpenResty:更灵活的Web服务器网络应用防火墙(WAF)CDN ... [详细]
  • Linux操作系统学习笔记4【文件管理与常用命令】
    文件目录:一:Linux文件基础知识1.Linux常用文件类别2.Linux目录结构概述3.Linux目录常见概念4.Linux系统目录及说明 ... [详细]
  • 本文目录一览:1、如何搭建php服务器2、如何 ... [详细]
  • 项目地址:https:github.comthe-xentropyxencrypt原文链接:http:caidaome.com?post246Xenc ... [详细]
author-avatar
U友48805799
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有