本人又是一个gopher,因此这篇文章会以一个goper的视角,列举一下我所经历过的每一种pdf处理场景,好比:javascript
pdf渲染
pdf校验
pdf加水印
pdf获取页数
pdf合并
pdf拆分
修复受损pdf
pdf转png
识别pdf中的字体
pdf解密
...
复制代码
本文大可能是场景问题的罗列,能够根据标题摘取本身有兴趣的部分查看html
不少pdf的问题我也不是特别专业,若是问题或者疑问欢迎与我交流java
根据html页面渲染pdf,我使用过如下两种方案:node
wkhtmltopdf是一个命令行工具,用于将HTML页面渲染为PDF,基于Qt WebKit渲染引擎实现linux
使用方式比较简单:git
## 将一个静态html页面打印成pdf $ wkhtmltopdf input.html output.pdf ## 将一个网页打印成pdf $ wkhtmltopdf https://www.google.com output.pdf 复制代码
wkhtmltopdf的参数很丰富,好比:github
支持发送 http post请求,适合将自定义开发的网页渲染成pdf文件:正则表达式
$ wkhtmltopdf --help
...
--post <name> <value> Add an additional post field (repeatable)
...
复制代码
支持javascript脚本,在渲染pdf前对html进行修改:chrome
$ wkhtmltopdf --run-script "javascript:(function(){document.getElementsByClassName('dom_class_name')[0].style.display = 'none'}())" page input.html output.pdf 复制代码
更多详细参数可看官网文档shell
若是你使用Go语言,还有一个第三方包,是对wkhtmltopdf的使用封装: go-wkhtmltopdf
chromedp是一种在Go语言中以更快,更简单的方式来驱动支持Chrome DevTools协议的浏览器的软件包,而无需外部依赖((例如Selenium或PhantomJS).
使用方式:
package main import ( "context" "io/ioutil" "github.com/chromedp/cdproto/page" "github.com/chromedp/chromedp" "errors" ) func main(){ err := ChromedpPrintPdf("https://www.google.com", "/path/to/file.pdf") if err != nil { fmt.Println(err) return } } func ChromedpPrintPdf(url string, to string) error { ctx, cancel := chromedp.NewContext(context.Background()) defer cancel() var buf []byte err := chromedp.Run(ctx, chromedp.Tasks{ chromedp.Navigate(url), chromedp.WaitReady("body"), chromedp.ActionFunc(func(ctx context.Context) error { var err error buf, _, err = page.PrintToPDF(). Do(ctx) return err }), }) if err != nil { return fmt.Errorf("chromedp Run failed,err:%+v", err) } if err := ioutil.WriteFile(to, buf, 0644); err != nil { return fmt.Errorf("write to file failed,err:%+v", err) } return nil } 复制代码
我了解到的支持pdf加水印的工具备:
$ unipdf -h ... Available Commands: decrypt Decrypt PDF files encrypt Encrypt PDF files explode Explodes the input file into separate single page PDF files extract Extract PDF resources form PDF form operations grayscale Convert PDF to grayscale help Help about any command info Output PDF information merge Merge PDF files optimize Optimize PDF files passwd Change PDF passwords rotate Rotate PDF file pages search Search text in PDF files split Split PDF files version Output version information and exit watermark Add watermark to PDF files ... 复制代码
CLI模式添加水印
$ unipdf watermark in.pdf watermark.png -o out.pdf Watermark successfully applied to in.pdf Output file saved to out.pdf 复制代码
使用API添加水印,能够直接参考unipdf github example
注意:unidoc的产品须要付费购买license使用
pdfcpu 是一个用Go语言编写的PDF处理库,提供API和CLI模式使用
支持如下功能:
$ pdfcpu help ... The commands are: attachments list, add, remove, extract embedded file attachments changeopw change owner password changeupw change user password decrypt remove password protection encrypt set password protection extract extract images, fonts, content, pages, metadata fonts install, list supported fonts grid rearrange pages or images for enhanced browsing experience import import/convert images to PDF info print file info merge concatenate 2 or more PDFs nup rearrange pages or images for reduced number of pages optimize optimize PDF by getting rid of redundant page resources pages insert, remove selected pages paper print list of supported paper sizes permissions list, set user access permissions rotate rotate pages split split multi-page PDF into several PDFs according to split span stamp add, remove, update text, image or PDF stamps for selected pages trim create trimmed version of selected pages validate validate PDF against PDF 32000-1:2008 (PDF 1.7) version print version watermark add, remove, update text, image or PDF watermarks for selected pages ... 复制代码
使用CLI工具以图片形式添加水印:
$ pdfcpu watermark add -mode image 'voucher_watermark.png' 's:1 abs, rot:0' in.pdf out.pdf 复制代码
调用api添加水印
package main import ( "github.com/pdfcpu/pdfcpu/pkg/api" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" ) func main() { onTop := false wm, _ := pdfcpu.ParseImageWatermarkDetails("watermark.png", "s:1 abs, rot:0", onTop) api.AddWatermarksFile("in.pdf", "out.pdf", nil, wm, nil) } 复制代码
cpdf是一个开源免费的PDF命令行工具库,有丰富的功能,好比:
- Merge PDF files together, or split them apart
- Encrypt and decrypt
- Scale, crop and rotate pages
- Read and set document info and metadata
- Copy, add or remove bookmarks
- Stamp logos, text, dates, page numbers
- Add or remove attachments
- Losslessly compress PDF files
合并pdf:
$ cpdf -merge input1.pdf input2.pdf -o output.pdf 复制代码
$ unipdf merge output.pdf input1.pdf input2.pdf 复制代码
使用API合并pdf,参考unpdf github example
$ pdfcpu merge output.pdf input1.pdf input2.pdf 复制代码
注意: pdfcpu只支持版本低于PDF V1.7的pdf文件
## 逐页拆分红单个pdf $ cpdf -split in.pdf 1 even -chunk 1 -o ./out%%%.pdf 复制代码
## 将第一页拆分出来 $ unipdf split input.pdf out.pdf 1-1 复制代码
使用api拆分pdf,参考unipdf github examples
$ pdfcpu split in.pdf . 复制代码
MuPDF is a lightweight PDF, XPS, and E-book viewer.
MuPDF consists of a software library, command line tools, and viewers for various platforms.
下载mupdf后获得一些工具,好比:
mupdf
pdfdraw
pdfinfo
pdfclean
pdfextract
pdfshow
xpsdraw
复制代码
其中pdfdraw
可用来转换图片
$ pdfdraw -o out%d.png in.pdf 复制代码
xpdf是一个免费的PDF工具包,包括文字解析,图片转换,html转换等
下载该软件包后,能够获得一系列的工具:
pdfdetach
pdffonts
pdfimages
pdfinfo
pdftohtml
pdftopng
pdftoppm
pdftops
pdftotext
复制代码
从名称上看,大体能看出来每个工具的用处
## 使用pdftopng将pdf转换成png $ pdftopng in.pdf out-prefix 复制代码
常常会遇到一种场景,读取pdf文件的时候发现会报错:文件被加密
可是在没有密码的状况下怎么解决呢?
使用qpdf进行强制解密,有些状况是能够解密成功的,可是有些状况也不必定能解密成功
qpdf是一个支持命令行的pdf工具
$ qpdf --decrypt in.pdf out.pdf 复制代码
$ pdfcpu decrypt encrypted.pdf output.pdf 复制代码
当有密码的状况下,可使用密码解密:
$ unipdf decrypt -p pass -o output.pdf input.pdf 复制代码
常常会遇到一些场景,好比识别一个文件是否是pdf文件,识别pdf中的文字,识别pdf中的图片等
这里使用xpdf将pdf中的文字解析出来,而后再使用一些字符串操做或者正则表达式进行业务分析
xpdf/pdftotext
解析pdf中的文本$ pdftotext input.pdf output.txt 复制代码
unipdf
解析pdf中的文本$ unipdf extract text input.pdf 复制代码
使用API解析pdf文本,参考unipdf github examples
上面都是先解析出pdf的文本,再根据业务进行处理
还有一种方式是按照坐标位置解析pdf,这种方式更加灵活以及通用,利用的是pdflib/tet
## 输入一组坐标,便可按照坐标解析pdf中的数据 $ tet --pageopt "includebox={{38 707.93 243.91 716.93}}" input.pdf 复制代码
坐标可使用tet对pdf进行分析获得一个tetml文件,里面包含了坐标信息:
$ tet --tetml input.pdf 复制代码
固然也能够用一些其余的方式获取pdf中数据的坐标信息,好比nodejs等
注意:
pdflib/tet
是收费软件,可是根据官方文档说明,tet提供基础功能,处理不超过10页或者小于1M的pdf文件是不须要购买license的
pdflib/tet
提供了命令行工具以及多种语言的sdk支持,好比C/C++/Java/.NET/Perl/PHP/Python/Ruby/Swift
但目前还不支持Go语言,因此对于gopher而言目前只有两种选择:CLI OR CGO
有一些pdf文件在电脑上打开时,显示正常,可是用代码检测倒是不正常的,好比在Go中尝试用一个第三方库去解析一个(受损的)pdf:
import ( "fmt" "github.com/rsc.io/pdf" ) func main() { filePath := "path/to/your/broken.pdf" _, err := pdf.Open(filePath) if err != nil { fmt.Println("open pdf failed,err:", err.Error()) return } } 复制代码
运行后会获得这样一个结果:
open pdf failed,err: malformed PDF: cross-reference table not found: {5 0 obj}<</Contents 6 0 R /Group <</CS /DeviceRGB /S /Transparency /Type /Group>> /MediaBox [0 0 595.27600098 841.89001465] /Parent 3 0 R /Type /Page>>
复制代码
电脑打开正常,程序却读取错误!
这时候若是尝试在电脑上打开pdf,而后另存为一个新的pdf文件,再用代码去检测,会发现居然修复了!
太好了,问题解决!
等等,若是我有1000张pdf文件,难道要逐个打开并另存为?这怎么能忍? 因此若是有一种批量修复的功能就行了
在网上找了好久,大概获得三种解决方案:
这里我只验证了第三种方式是可行的,这里我使用mupdf-0.9-linux-amd64
这个版本进行验证
下载软件包后,获得其中一个可执行文件:pdfclean
$ pdfclean broken.pdf repaired.pdf + pdf/pdf_xref.c:160: pdf_read_trailer(): cannot recognize xref format: '%' | pdf/pdf_xref.c:481: pdf_load_xref(): cannot read trailer \ pdf/pdf_xref.c:537: pdf_open_xref_with_stream(): trying to repair 复制代码
从输出结果来看,mupdf尝试了修复处理
获得新的pdf文件以后,再用前面的Go代码尝试打开,就正常了
剩下的就是写一个bash脚本,批量修复,目标达成!
有时候要使多个pdf文本字体保持一致,免不得要去分析pdf中都使用了哪些字体,这时候可使用
xpdf/pdffonts
进行字体分析
$ pdffonts input.pdf name type encoding emb sub uni object ID ------------------------------------ ----------------- ---------------- --- --- --- --------- NimbusSanL-Regu CID TrueType Identity-H yes no yes 10 0 NimbusSanL-Bold CID TrueType Identity-H yes no yes 20 0 复制代码
这是一个C++的开源库,支持建立pdf,合并pdf,图片水印文字操做等
对于gopher来说,要使用这个库,须要封装一层CGO代码才能够
这是一个Go语言实现的pdf库,能够用于读取pdf信息,好比读取pdf内容/页数/字体等... 具体能够参考文档
介绍了这么多第三方库,简直就是五花八门,各显神通。有些功能在大多数库中都是有重复的,具体使用中会遇到什么问题,仍是要看实际状况如何。
但愿这些总结可以对读者有所帮助
参考: