Go处理PDF
工作中经常会遇到一些pdf文件处理的问题,一千种pdf有一千种处理方式,每次都是绞尽脑汁和这些pdf战斗到底。
本人又是一个gopher,所以这篇文章会以一个goper的视角,列举一下我所经历过的每一种pdf处理场景,比如:
pdf渲染
pdf校验
pdf加水印
pdf获取页数
pdf合并
pdf拆分
修复受损pdf
pdf转png
识别pdf中的字体
pdf解密
...
本文大多是场景问题的罗列,可以根据标题摘取自己有兴趣的部分查看
很多pdf的问题我也不是特别专业,如果问题或者疑问欢迎与我交流
一、HTML页面渲染PDF
根据html页面渲染pdf,我使用过以下两种方案:
- wkhtmltopdf
- chromedp
1. 使用wkhtmltopdf渲染pdf
wkhtmltopdf是一个命令行工具,用于将HTML页面渲染为PDF,基于Qt WebKit渲染引擎实现
使用方式比较简单:
## 将一个静态html页面打印成pdf
$ wkhtmltopdf input.html output.pdf
## 将一个网页打印成pdf
$ wkhtmltopdf https://www.google.com output.pdf
wkhtmltopdf的参数很丰富,比如:
支持发送 http post请求,适合将自定义开发的网页渲染成pdf文件:
$ wkhtmltopdf --help
...
--post <name> <value> Add an additional post field (repeatable)
...
支持javascript脚本,在渲染pdf前对html进行修改:
$ wkhtmltopdf --run-script "javascript:(function(){document.getElementsByClassName('dom_class_name')[0].style.display = 'none'}())" page input.html output.pdf
更多详细参数可看官网文档
如果你使用Go语言,还有一个第三方包,是对wkhtmltopdf的使用封装: go-wkhtmltopdf
2. 使用chromedp渲染pdf
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加水印
我了解到的支持pdf加水印的工具有:
- unidoc/unipdf
- pdfcpu
1.unidoc/unipdf
$ 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使用
2.pdfcpu
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)
}
三、PDF合并
- cpdf
- unipdfc
- pdfcpu
1.使用cpdf合并pdf
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
2.使用unipdf合并pdf
$ unipdf merge output.pdf input1.pdf input2.pdf
使用API合并pdf,参考unpdf github example
3.使用pdfcpu合并pdf
$ pdfcpu merge output.pdf input1.pdf input2.pdf
注意: pdfcpu只支持版本低于PDF V1.7的pdf文件
四、拆分PDF
- cpdf
- unipdf
- pdfcpu
1.使用cpdf拆分pdf
## 逐页拆分成单个pdf
$ cpdf -split in.pdf 1 even -chunk 1 -o ./out%%%.pdf
2. 使用unipdf拆分pdf
## 将第一页拆分出来
$ unipdf split input.pdf out.pdf 1-1
使用api拆分pdf,参考unipdf github examples
3.使用pdfcpu拆分pdf
$ pdfcpu split in.pdf .
五、PDF转图片
- mupdf
- xpdf
1. 使用mupdf操作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
注意: mupdf不支持mac OS
2. 使用xpdf操作pdf转图片
xpdf是一个免费的PDF工具包,包括文字解析,图片转换,html转换等
下载该软件包后,可以得到一系列的工具:
pdfdetach
pdffonts
pdfimages
pdfinfo
pdftohtml
pdftopng
pdftoppm
pdftops
pdftotext
从名称上看,大致能看出来每一个工具的用处
## 使用pdftopng将pdf转换成png
$ pdftopng in.pdf out-prefix
六、PDF解密
经常会遇到一种场景,读取pdf文件的时候发现会报错:文件被加密
但是在没有密码的情况下怎么解决呢?
- 使用qpdf解密
使用qpdf进行强制解密,有些情况是可以解密成功的,但是有些情况也不一定能解密成功
qpdf是一个支持命令行的pdf工具
$ qpdf --decrypt in.pdf out.pdf
- 使用pdfcpu解密
$ pdfcpu decrypt encrypted.pdf output.pdf
当有密码的情况下,可以使用密码解密:
- 使用unipdf解密pdf
$ unipdf decrypt -p pass -o output.pdf input.pdf
七、PDF识别
经常会遇到一些场景,比如识别一个文件是不是pdf文件,识别pdf中的文字,识别pdf中的图片等
1.识别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的文本,再根据业务进行处理
还有一种方式是按照坐标位置解析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文件
有一些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文本字体保持一致,免不得要去分析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
其他Libiray介绍:
这是一个C++的开源库,支持创建pdf,合并pdf,图片水印文字操作等
对于gopher来讲,要使用这个库,需要封装一层CGO代码才可以
这是一个Go语言实现的pdf库,可以用于读取pdf信息,比如读取pdf内容/页数/字体等... 具体可以参考文档
介绍了这么多第三方库,简直就是五花八门,各显神通。有些功能在大多数库中都是有重复的,具体使用中会遇到什么问题,还是要看实际情况如何。
希望这些总结能够对读者有所帮助
参考:
Go处理PDF的更多相关文章
- C#给PDF文档添加文本和图片页眉
页眉常用于显示文档的附加信息,我们可以在页眉中插入文本或者图形,例如,页码.日期.公司徽标.文档标题.文件名或作者名等等.那么我们如何以编程的方式添加页眉呢?今天,这篇文章向大家分享如何使用了免费组件 ...
- 在线浏览PDF之PDF.JS (附demo)
平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html#skill 下载地址:http://mozilla.gith ...
- .NET平台开源项目速览(16)C#写PDF文件类库PDF File Writer介绍
1年前,我在文章:这些.NET开源项目你知道吗?.NET平台开源文档与报表处理组件集合(三)中(第9个项目),给大家推荐了一个开源免费的PDF读写组件 PDFSharp,PDFSharp我2年前就看过 ...
- C#如何在PDF文件添加图片印章
文档中添加印章可以起一定的作用,比如,防止文件随意被使用,或者确保文档内容的安全性和权威性.C#添加图片印章其实也有很多实现方法,这里我使用的是免费的第三方软件Free Spire.PDF,向大家阐述 ...
- C#中如何创建PDF网格并插入图片
这篇文章我将向大家演示如何以编程的方式在PDF文档中创建一个网格,并将图片插入特定的网格中. 网上有一些类似的解决方法,在这里我选择了一个免费版的PDF组件.安装控件后,创建新项目,添加安装目录下的d ...
- 在Asp.Net中操作PDF – iTextSharp - 使用表格
使用Asp.Net生成PDF最常用的元素应该是表格,表格可以帮助比如订单或者发票类型的文档更加格式化和美观.本篇文章并不会深入探讨表格,仅仅是提供一个使用iTextSharp生成表格的方法介绍 使用i ...
- 开源免费且稳定实用的.NET PDF打印组件itextSharp(.NET组件介绍之八)
在这个.NET组件的介绍系列中,受到了很多园友的支持,一些园友(如:数据之巅. [秦时明月]等等这些大神 )也给我提出了对应的建议,我正在努力去改正,有不足之处还望大家多多包涵.在传播一些简单的知识的 ...
- ASP.Net MVC——使用 ITextSharp 完美解决HTML转PDF(中文也可以)
前言: 最近在做老师交代的一个在线写实验报告的小项目中,有这么个需求:把学生提交的实验报告(HTML形式)直接转成PDF,方便下载和打印. 以前都是直接用rdlc报表实现的,可这次牵扯到图片,并且更为 ...
- 在禅道中实现WORD等OFFICE文档转换为PDF进行在线浏览
条件: 安装好禅道的服务器 能直接浏览PDF的浏览器(或通过 安装插件实现 ) 文档转换服务程序(建议部署在另一台服务器上) 实现 原理: 修改禅道的文件预览功能(OFFICE文档其使用的是下 ...
- 一年之计在于春,2015开篇:PDF.NET SOD Ver 5.1完全开源
前言: 自从我2014年下半年到现在的某电商公司工作后,工作太忙,一直没有写过一篇博客,甚至连14年股票市场的牛市都错过了,现在马上要过年了,而今天又是立春节气,如果再不动手,那么明年这个无春的年,也 ...
随机推荐
- 2018-7-9-dotnet-设计规范-·-数组定义
title author date CreateTime categories dotnet 设计规范 · 数组定义 lindexi 2018-07-09 14:26:48 +0800 2018-2- ...
- 【datagrid】动态加载列 2016-01-03 16:32 2013人阅读 评论(19) 收藏
之前我们的项目在前台显示只需要把数据从数据库读出来进行显示就可以,datagrid的表头字段都是写死的,把数据往表里一扔,就基本没什么事儿了,结果客户前几天要求,其中一个字段不能是死的,应该是有多少项 ...
- qt 在ui界面添加控件后在cpp文件中无法调用?
问题:qt 在ui界面添加控件后在cpp文件中无法调用? 解决方法:在build选项中选择“重新build项目”,再次在cpp中调用添加的控件发现可以调用了. 还有一种情况导致添加控件后无法调用,就是 ...
- Mybatis Generator配置文件完整配置详解
完整的Mybatis Generator(简称MBG)的最完整配置文件,带详解,再也不用去看EN的User Guide了 可以搭配着mybatis generator的中文文档看:http://mbg ...
- C运行时库函数和API函数的区别和联系
C运行时库函数 C运行时库函数是指C语言本身支持的一些基本函数,通常是汇编直接实现的. API函数 API函数是操作系统为方便用户设计应用程序而提供的实现特定功能的函数,API函数也是C语言的函 ...
- CRT (C run-time library)
一 产生 运行时库是程序在运行时所需要的库文件,通常以LIB或DLL形式提供. C运行时库就是C run-time library,诞生于20世纪70年代,是C而非C++语言世界的概念,C程序运行时需 ...
- sublime 插件安装packagecontrol
https://packagecontrol.io/installation 第一步: Installation Simple The simplest method of installation ...
- Android Studio(十一):代码混淆及打包apk
Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...
- hdu 5723 Abandoned country(2016多校第一场) (最小生成树+期望)
Abandoned country Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- ccf-201703-4-地铁修建
二分+并查集 发现了一个很奇怪的问题,二分的上界设置错了,结果是运行错误...只有95分.后来改了上界就100了,百思不得其解 #include<cstdio> #include<c ...