序言
臨近卷鋪走人因此有不少手續要辦,提交文件遇到需要合併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是個很小的包,在site-packages中可以看到它只有一層目錄,而且主要的功能代碼只在pdf.py中,但是這並不影響PyPDF2仍是一個有很多重要功能的包。因此在索用了前人的勞動成果解決問題後,細看當中還是否有其他的可取用的知識也是不錯的選擇。
其實從序言中的代碼邏輯裏不難看出,PyPDF2可以任意挑選PDF頁面進行任意拼接的操作,因此可以很簡單的實現PDF頁面級別的增刪改操作。事實上PyPDF2中只有兩個功能類PdfFileWriter, PdfFileReader, 但是這兩個類中都封裝了很多功能,前者除了對PDF文件的簡單頁面編輯操作外,還可以對PDF文件進行一些改動,如加密、添加啓動時的將執行的操作等;後者則是用於挖掘PDF文件中的信息,比如使用LATEX生成的PDF中就可以很容易的去查出reference, contents等信息,並且相對應PdfFileWriter中的加密,這裏還給出瞭解密的功能。
PdfFileWriter類
writer對象可以爲最終導出的PDF文件增加各種信息,如Metadata, Link等,這裏主要介紹一下addJS與encrypt方法👇
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只是可以編輯重組頁面,細細品後發現還是有很多有趣的功能。好在今晚不加班,抽空寫了這篇博客。
總之分享學習,共同進步!