跳到主要内容

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进行修改:

1

`$ wkhtmltopdf --run-script "javascript:(function(){document.getElementsByClassName('dom_class_name')[0].style.display = 'none'}())" page input.html output.pdf`

更多详细参数可看[官网文档](https://wkhtmltopdf.org/usage/wkhtmltopdf.txt)

如果你使用Go语言,还有一个第三方包,是对wkhtmltopdf的使用封装:[go-wkhtmltopdf](https://github.com/SebastiaanKlippert/go-wkhtmltopdf)

**2\. 使用chromedp渲染pdf**

[chromedp](https://github.com/chromedp/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](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**

[unidoc](https://unidoc.io/)平台开发的[unipdf](https://unidoc.io/unipdf/)是一款用Go语言编写的PDF库,提供API和CLI使用模式,支持以下功能:



`$ 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](https://github.com/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工具以图片形式添加水印:

1

`$ 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](https://community.coherentpdf.com/)是一个开源免费的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:

1

`$ cpdf -merge input1.pdf input2.pdf -o output.pdf`

**2.使用unipdf合并pdf**

1

`$ unipdf merge output.pdf input1.pdf input2.pdf`

使用API合并pdf,参考unpdf github example

**3.使用pdfcpu合并pdf**

1

`$ pdfcpu merge output.pdf input1.pdf input2.pdf`

注意: pdfcpu只支持版本低于PDF V1.7的pdf文件

**四、拆分PDF**

* cpdf
* unipdf
* pdfcpu

**1.使用cpdf拆分pdf**

1

2

`## 逐页拆分成单个pdf`

`$ cpdf -split in.pdf 1 even -chunk 1 -o ./out%%%.pdf`

**2\. 使用unipdf拆分pdf**

1

2

`## 将第一页拆分出来`

`$ unipdf split input.pdf out.pdf 1-1`

使用api拆分pdf,参考[unipdf github examples](https://github.com/unidoc/unipdf-examples/blob/v3/pages/pdf_split.go)

**3.使用pdfcpu拆分pdf**

1

`$ pdfcpu split in.pdf .`

**五、PDF转图片**

* mupdf
* xpdf

**1\. 使用mupdf操作pdf转图片**

[MuPDF ](https://www.mupdf.com/index.html)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可用来转换图片

1

`$ pdfdraw -o out%d.png in.pdf`

注意: mupdf不支持mac OS

**2\. 使用xpdf操作pdf转图片**

[xpdf](https://www.xpdfreader.com/)是一个免费的PDF工具包,包括文字解析,图片转换,html转换等

下载该软件包后,可以得到一系列的工具:

> pdfdetach
> pdffonts
> pdfimages
> pdfinfo
> pdftohtml
> pdftopng
> pdftoppm
> pdftops
> pdftotext

从名称上看,大致能看出来每一个工具的用处

1

2

`## 使用pdftopng将pdf转换成png`

`$ pdftopng in.pdf out-prefix`

**六、PDF解密**

经常会遇到一种场景,读取pdf文件的时候发现会报错:文件被加密

但是在没有密码的情况下怎么解决呢?

* 使用qpdf解密

使用[qpdf](http://qpdf.sourceforge.net/)进行强制解密,有些情况是可以解密成功的,但是有些情况也不一定能解密成功

qpdf是一个支持命令行的pdf工具

1

`$ qpdf --decrypt in.pdf out.pdf`

使用pdfcpu解密

1

`$ pdfcpu decrypt encrypted.pdf output.pdf`

当有密码的情况下,可以使用密码解密:

使用unipdf解密pdf

1

`$ unipdf decrypt -p pass -o output.pdf input.pdf`

**七、PDF识别**

经常会遇到一些场景,比如识别一个文件是不是pdf文件,识别pdf中的文字,识别pdf中的图片等

**1.识别pdf中的文字**

这里使用xpdf将pdf中的文字解析出来,然后再使用一些字符串操作或者正则表达式进行业务分析

使用xpdf/pdftotext解析pdf中的文本

1

`$ pdftotext input.pdf output.txt`

使用unipdf解析pdf中的文本

1

`$ unipdf extract text input.pdf`

使用API解析pdf文本,参考[unipdf github examples](https://github.com/unidoc/unipdf-examples/blob/v3/text/pdf_extract_text.go)

使用坐标信息解析pdf数据

上面都是先解析出pdf的文本,再根据业务进行处理

还有一种方式是按照坐标位置解析pdf,这种方式更加灵活以及通用,利用的是[pdflib/tet](https://www.pdflib.com/products/tet)

1

2

`## 输入一组坐标,即可按照坐标解析pdf中的数据`

`$ tet --pageopt "includebox={{38 707.93 243.91 716.93}}" input.pdf`

坐标可以使用tet对pdf进行分析得到一个tetml文件,里面包含了坐标信息:

1

`$ 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文件,难道要逐个打开并另存为?这怎么能忍? 所以如果有一种批量修复的功能就好了

在网上找了很久,大概得到三种解决方案:

* 利用 Acrobat SDK,调用SDK中的[另存为功能](https://www.jb51.net/article/48171.htm),可以实现电脑打开另存为的效果
* 利用ghostscript进行pdf修复
* 利用[mupdf](https://www.mupdf.com/index.html)进行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介绍:**

[PDF-Writer](https://github.com/galkahana/PDF-Writer/wiki)
这是一个C++的开源库,支持创建pdf,合并pdf,图片水印文字操作等

对于gopher来讲,要使用这个库,需要封装一层CGO代码才可以

[rsc/pdf](https://github.com/rsc/pdf)
这是一个Go语言实现的pdf库,可以用于读取pdf信息,比如读取pdf内容/页数/字体等... 具体可以参考[文档](https://godoc.org/rsc.io/pdf)

介绍了这么多第三方库,简直就是五花八门,各显神通。有些功能在大多数库中都是有重复的,具体使用中会遇到什么问题,还是要看实际情况如何。

希望这些总结能够对读者有所帮助