【日常】從批量合併 PDF 到 PyPDF2 的使用

序言

臨近卷鋪走人因此有不少手續要辦,提交文件遇到需要合併PDF文件的需求。恰好個人電腦還處於文件強制加密的狀態,編輯文檔保存會自動加密,出於某些原因不方便恢復到正常狀態的備份,因此給合併PDF帶來很多麻煩。

這時候會考慮是否有捷徑可以走,這時候筆者發現Python是有可以進行PDF文件編輯操作的開源包PyPDF2,簡單pip安裝即可👇

pip install pypdf2

下面提供了一個非常便捷的用於批量合併PDF文件的函數,只需要傳入需要合併的PDF文件所在目錄,以及合併後的文件導出路徑兩個變量(pdf_path, save_path)即可👇

# -*- coding: UTF-8 -*-
# Author: 囚生CY
# 合併pdf的工具函數

import os
from PyPDF2 import PdfFileReader,PdfFileWriter

# 合併同一目錄下的所有PDF文件
def merge_pdf(pdf_path,save_path):										 # pdf_path爲需要合併的所有pdf所在路徑, save_path爲合併後的文件導出路徑
	writer = PdfFileWriter()											 # 創建PDF文件書寫器對象
	total_pages = 0														 # 標記頁碼數
	pdf_filepaths = []													 # 用於存儲所有的pdf的路徑
	for root,dirname,filenames in os.walk(pdf_path):
		for filename in filenames:								
			try: suffix = filename[-4:]
			except: continue											 # 這個報錯說明文件名連4位都沒有, 直接跳過即可
			if suffix==".pdf":											 # 說明是pdf文件
				pdf_filepaths.append(os.path.join(pdf_path,filename))

	for path in pdf_filepaths:											 # 遍歷所有pdf我呢見
		reader = PdfFileReader(open(path,"rb"))							 # 讀取pdf文件
		pages = reader.getNumPages()									 # 獲取當前pdf文件的頁碼數
		total_pages += pages											 # 更新總頁碼數
		for i in range(pages): writer.addPage(reader.getPage(i))		 # 遍歷當前pdf文件的每一頁: 將每一頁的信息添加到pdf文件書寫器中
		print("已添加{}\t共{}頁,累計{}頁".format(path,pages,total_pages))
	writer.write(open(save_path,"wb"))
	print("PDF文件合併完成!")

if __name__ == "__main__":
	pdf_path = "raw"
	save_path = "merged_file.pdf"
	merge_pdf(pdf_path,save_path)

應該說合並的效率是非常高的,並且結果也是很滿意的,注意合併的順序是文件名的默認排序,如果需要自定義順序的話可以在PDF文件名前添加001, 002, ... , 010的前綴標註順序即可。

PyPDF2 使用

PyPDF2官方文檔

事實上PyPDF2是個很小的包,在site-packages中可以看到它只有一層目錄,而且主要的功能代碼只在pdf.py中,但是這並不影響PyPDF2仍是一個有很多重要功能的包。因此在索用了前人的勞動成果解決問題後,細看當中還是否有其他的可取用的知識也是不錯的選擇。

其實從序言中的代碼邏輯裏不難看出,PyPDF2可以任意挑選PDF頁面進行任意拼接的操作,因此可以很簡單的實現PDF頁面級別的增刪改操作。事實上PyPDF2中只有兩個功能類PdfFileWriter, PdfFileReader, 但是這兩個類中都封裝了很多功能,前者除了對PDF文件的簡單頁面編輯操作外,還可以對PDF文件進行一些改動,如加密、添加啓動時的將執行的操作等;後者則是用於挖掘PDF文件中的信息,比如使用LATEX生成的PDF中就可以很容易的去查出reference, contents等信息,並且相對應PdfFileWriter中的加密,這裏還給出瞭解密的功能。

PdfFileWriter類

writer對象可以爲最終導出的PDF文件增加各種信息,如Metadata, Link等,這裏主要介紹一下addJSencrypt方法👇

writer.addBlankPage(1080,1920)          # 添加一個空白頁面, 參數爲頁面的寬與高
writer.addMetadata({"version":"1.0.1"}) # 添加metadata後使用PdfFileReader對象可以讀取metadata
writer.addJS("alert('FBI Warning!');")  # 添加javascript可以在啓動時執行,但是這個似乎對PDF閱讀軟件有限制,筆者使用的Foxit似乎不支持這個效果
writer.addBookMark(title="Chapter 1",pagenum=1) # 指定頁碼添加書籤
writer.encrypt("123456","12345678")     # 加密文檔: 第一個參數是使用者的密碼, 以該密碼進入後權限受限制, 後者爲所有者密碼, 擁有文檔的所有權限
writer.removeImages()                   # 移除文檔中的所有圖片
writer.removeLinks()                    # 移除文檔中的所有鏈接
writer.removeText()                     # 移除文檔中的所有文字
  • addJS方法可以加入一段在導出的PDF文件啓動時立即執行的JS,雖然作爲JS小白並不能搞清楚脫離HTML頁面究竟應該如何寫JS代碼,但是簡單寫個alert應該是沒有太大問題的。但是這個似乎對PDF閱讀器有所限制,至少筆者使用的Foxit閱讀器是不能執行JS代碼的。標準的Adobe閱讀器應該是可行的。我有看到別人寫"this.print({bUI:true,bSilent:false,bShrinkToFit:true});",雖然並不太懂是什麼意思[Faceplam]
  • encrypt可以給導出的PDF文檔加密,啓動時需要輸入密碼才能進入,並且可以設定兩種不同權限的密碼——使用者密碼與所有者密碼,可以很容易的實現。

此外writer對象還可以最終移除導出文檔中所有圖片、鏈接、文字,以及最終可以修改頁面格式👇

 

PdfFileReader類

reader中有很多方法是和writer相對應的,writer設置了一些什麼,就會在reader中得到什麼👇

reader.decrypt("123456") # 解密進入文檔
reader.getNumPages()     # 總頁數
reader.getOutlines()     # 獲取提綱
reader.getPageLayout()   # 獲取頁面佈局情況
reader.getPageMode()     # 獲取頁面模式
reader.getXmpMetadata()  # 獲取元信息
reader.getBookMark()     # 獲取書籤

其他一些諸如getDocumentInfo()以及getFields()等PDF文件的屬性信息不多作介紹。

 

PageObject

其實裏面還有一個PageObject類,使用reader獲取到的每一頁就是一個PageObject類👇

page = reader.getPage(0) # 獲取當前pdf文件的第一頁

使用該類中的方法可以進行頁面尺寸的裁剪,擴張,以及進行頁面旋轉的操作,這樣使得在最終的合併結果中可以有形色各異的頁面,不過這已經屬於設計的範疇,筆者覺得沒有太大深究的必要👇

page = page.rotateClockwise(90)        # 順時針旋轉90°
page = page.rotateCounterClockwise(90) # 逆時針旋轉90°

page = page1.mergePage(page2)          # 合併兩個頁面, 比如當page2是水印圖片頁時可以起到對page1增加水印的效果

# 裁剪頁面大小爲當前的一半
page = page.mediaBox.upperRight = (
    page.mediaBox.getUpperRight_x() / 2,
    page.mediaBox.getUpperRight_y() / 2
)

值得注意的是PageObject類當中有一個page.extractText()方法可以用於挖掘頁面中的文字信息,不過這個方法並不總是能奏效,只有當PDF是非常工整的文字型的PDF時才能比較精確的獲取到頁面中的文字信息。

後記

其實很多時候不仔細過一遍可能只覺得PyPDF2只是可以編輯重組頁面,細細品後發現還是有很多有趣的功能。好在今晚不加班,抽空寫了這篇博客。

總之分享學習,共同進步!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章