Golang處理PDF

工做中常常會遇到一些pdf文件處理的問題,一千種pdf有一千種處理方式,每次都是絞盡腦汁和這些pdf戰鬥到底。

本人又是一個gopher,因此這篇文章會以一個goper的視角,列舉一下我所經歷過的每一種pdf處理場景,好比:javascript

pdf渲染
pdf校驗
pdf加水印
pdf獲取頁數
pdf合併
pdf拆分
修復受損pdf
pdf轉png
識別pdf中的字體
pdf解密
...
複製代碼

本文大可能是場景問題的羅列,能夠根據標題摘取本身有興趣的部分查看html

不少pdf的問題我也不是特別專業,若是問題或者疑問歡迎與我交流java


1、HTML頁面渲染PDF

根據html頁面渲染pdf,我使用過如下兩種方案:node

  • wkhtmltopdf
  • chromedp

1. 使用wkhtmltopdf渲染pdf

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

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
}
複製代碼

2、PDF加水印

我瞭解到的支持pdf加水印的工具備:

  • unidoc/unipdf
  • pdfcpu

1.unidoc/unipdf

unidoc平臺開發的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 是一個用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)
}
複製代碼

3、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文件

4、拆分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 .
複製代碼

5、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
複製代碼

6、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
複製代碼

7、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

8、修復受損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中的另存爲功能,能夠實現電腦打開另存爲的效果
  • 利用ghostscript進行pdf修復
  • 利用mupdf進行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腳本,批量修復,目標達成!

9、識別一個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內容/頁數/字體等... 具體能夠參考文檔


介紹了這麼多第三方庫,簡直就是五花八門,各顯神通。有些功能在大多數庫中都是有重複的,具體使用中會遇到什麼問題,仍是要看實際狀況如何。

但願這些總結可以對讀者有所幫助


參考:

相關文章
相關標籤/搜索