首頁 > 軟體

Python實現為pdf新增水印功能

2022-04-02 19:00:47

建立需要的水印模板

wps建立

輸出pdf

水印pdf

實現步驟

安裝依賴

pip install PyPDF2

程式碼

import os
from PyPDF2 import PdfFileReader as pr
from PyPDF2 import PdfFileWriter as pw


def write_watermark(watermark_pdf_path: str, target_pdf_path: str):
    result_pdf = pw()
    pdf_file_name = os.path.basename(target_pdf_path)
    f_target = open(target_pdf_path, 'rb')
    f_watermark = open(watermark_pdf_path, 'rb')
    target_pdf = pr(f_target)
    watermark_page = pr(f_watermark).getPage(0)
    for page in range(target_pdf.getNumPages()):
        try: # 這一段try except是一個讓我超級麻煩的bug,讓我解決了一天。
            target_pdf.getPage(page).mergePage(watermark_page)
            result_pdf.addPage(target_pdf.getPage(page))
        except Exception as e:
            result_pdf.addPage(watermark_page)
    if not os.path.exists("output"):
        os.makedirs("output")
    result_pdf.write(open("output/已新增水印_" + pdf_file_name, 'wb'))
    f_target.close()
    f_watermark.close()


def folder_pdf_files(folder: str) -> list[str]:  # 一個資料夾裡面有多少pdf檔案
    file_list = []
    for a, b, c in os.walk(folder):
        if b == []:
            for filename in c:
                if filename[-3:].lower() == 'pdf':
                    file_path = os.path.join(a, filename)
                    file_list.append(file_path)
    print(folder, ": 有", len(file_list), "個pdf檔案")
    return file_list


def group_write_watermark(path_array: list[str], watermark_pdf_path: str):  # 一組pdf檔案新增水印
    for pdf_path in path_array:
        print(pdf_path, "新增水印中...")
        write_watermark(watermark_pdf_path, pdf_path)
    print("完成")


if __name__ == '__main__':
    watermark_pdf_path = "水印檔案.pdf"
    folder_pdf = "目錄"  # 需要新增水印的pdf的目錄
    pdf_list = folder_pdf_files(folder_pdf)
    group_write_watermark(pdf_list, watermark_pdf_path)

問題

UnicodeEncodeError: ‘latin-1’ codec can’t encode characters in position 8-9: ordinal not in range(256)

如果出現該錯誤,可以參考以下內容。

使用pypdf2出現編碼問題

報錯資訊

‘latin-1’ codec can’t encode characters in position 8-11: ordinal not in range(256)

通常這情況是出現了中文字元編碼問題

以下是使用pypdf2複製 pdf 時報錯資訊

//報錯資訊
<ipython-input-1-4f7e1b354328> in <module>()
     14      output.addPage(p)
     15 with open('D:\Program Files\2.pdf', 'wb') as f:
---> 16     output.write(f)

D:Program Files (x86)anacondalibsite-packagesPyPDF2pdf.py in write(self, stream)
    499                 md5_hash = md5(key).digest()
    500                 key = md5_hash[:min(16, len(self._encrypt_key) + 5)]
--> 501             obj.writeToStream(stream, key)
    502             stream.write(b_("nendobjn"))
    503 

D:Program Files (x86)anacondalibsite-packagesPyPDF2generic.py in writeToStream(self, stream, encryption_key)
    547             key.writeToStream(stream, encryption_key)
    548             stream.write(b_(" "))
--> 549             value.writeToStream(stream, encryption_key)
    550             stream.write(b_("n"))
    551         stream.write(b_(">>"))

D:Program Files (x86)anacondalibsite-packagesPyPDF2generic.py in writeToStream(self, stream, encryption_key)
    470 
    471     def writeToStream(self, stream, encryption_key):
--> 472         stream.write(b_(self))
    473 
    474     def readFromStream(stream, pdf):

D:Program Files (x86)anacondalibsite-packagesPyPDF2utils.py in b_(s)
    236             return s
    237         else:
--> 238             r = s.encode('latin-1')
    239             if len(s) < 2:
    240                 bc[s] = r

UnicodeEncodeError: 'latin-1' codec can't encode characters in position 8-11: ordinal not in range(256)

解決方法

1、修改pypdf2包中的generic.py檔案

由於我使用的是anaconda,路徑為anacondaLibsite-packagesPyPDF2generic.py

generic.py檔案第488行原文

try:
   return NameObject(name.decode('utf-8'))
   except (UnicodeEncodeError, UnicodeDecodeError) as e:
   # Name objects should represent irregular characters
   # with a '#' followed by the symbol's hex number
   if not pdf.strict:
      warnings.warn("Illegal character in Name Object", utils.PdfReadWarning)
      return NameObject(name)
   else:
      raise utils.PdfReadError("Illegal character in Name Object")

改成

try:
     return NameObject(name.decode('utf-8'))
 except (UnicodeEncodeError, UnicodeDecodeError) as e:
     try:
         return NameObject(name.decode('gbk'))
     except (UnicodeEncodeError, UnicodeDecodeError) as e:
         # Name objects should represent irregular characters
         # with a '#' followed by the symbol's hex number
         if not pdf.strict:
             warnings.warn("Illegal character in Name Object", utils.PdfReadWarning)
             return NameObject(name)
         else:
             raise utils.PdfReadError("Illegal character in Name Object")

2、修改pypdf2包中的utils.py檔案

utils.py238行原文

 r = s.encode('latin-1')
 if len(s) < 2:
   		bc[s] = r
 return r

修改為

try:
    r = s.encode('latin-1')
    if len(s) < 2:
        bc[s] = r
    return r
except Exception as e:
    print(s)
    r = s.encode('utf-8')
    if len(s) < 2:
        bc[s] = r
    return r

問題解決

感悟

此程式碼的創新點在於可以實現資料夾遍歷新增水印。

其實第一點也沒有什麼,我感覺我最有成功感的就是write_watermark函數裡那段try except語句處,這個一段程式碼解決pdf空白頁而傳送的錯誤。解決了一天,網路上都沒有解決方法。我摸著石頭過河。

有一個問題就是,這個程式碼對於圖片pdf的水印效果不好,因為圖片pdf的也頁面大小比普通的文字pdf大一些,難以控制水印的位置。我想的是在建立水印pdf的時候就把pdf的大小放大。

到此這篇關於Python實現為pdf新增水印功能的文章就介紹到這了,更多相關Python pdf水印內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com