如何在Python中为PDF文件加水印(实现代码示例)

本文带你了解如何使用 Python 中的 PyPDF4 和 reportlab 库在 PDF 文件中添加和删除水印,包括详细的Python为PDF文件加水印示例代码。
便携式文档格式 (PDF)标准化为 ISO 32000,是 Adob??e 于 1993 年开发的一种文件格式,用于以独立于应用程序软件、硬件和操作系统的方式呈现文档,包括文本格式和图像。
如何在Python中为PDF文件加水印?基于PostScript 语言,每个 PDF 文件都封装了一个固定版式平面文档的完整描述,包括文本、字体、矢量图形、光栅图像和其他显示所需的信息。
对 PDF 的熟悉导致其作为数字存档领域的解决方案被快速而广泛地采用。由于 PDF 比其他文件格式更通用,因此几乎可以从任何操作系统或设备轻松查看它们显示的信息。
Python如何为PDF文件加水印?在本教程中,你将学习如何在 Python 中使用PyPDF4和reportlab为 PDF 文件或包含 PDF 文件集合的文件夹添加水印。
首先,让我们安装必要的库:

$ pip install PyPDF4==1.27.0 reportlab==3.5.59

从代码开始,让我们导入库并定义一些我们需要的配置:
from PyPDF4 import PdfFileReader, PdfFileWriter from PyPDF4.pdf import ContentStream from PyPDF4.generic import TextStringObject, NameObject from PyPDF4.utils import b_ import os import argparse from io import BytesIO from typing import Tuple # Import the reportlab library from reportlab.pdfgen import canvas # The size of the page supposedly A4 from reportlab.lib.pagesizes import A4 # The color of the watermark from reportlab.lib import colorsPAGESIZE = A4 FONTNAME = 'Helvetica-Bold' FONTSIZE = 40 # using colors module # COLOR = colors.lightgrey # or simply RGB # COLOR = (190, 190, 190) COLOR = colors.red # The position attributes of the watermark X = 250 Y = 10 # The rotation angle in order to display the watermark diagonally if needed ROTATION_ANGLE = 45

接下来,定义我们的第一个效用函数:
def get_info(input_file: str): """ Extracting the file info """ # If PDF is encrypted the file metadata cannot be extracted with open(input_file, 'rb') as pdf_file: pdf_reader = PdfFileReader(pdf_file, strict=False) output = { "File": input_file, "Encrypted": ("True" if pdf_reader.isEncrypted else "False") } if not pdf_reader.isEncrypted: info = pdf_reader.getDocumentInfo() num_pages = pdf_reader.getNumPages() output[ "Author"] = info.author output[ "Creator"] = info.creator output[ "Producer"] = info.producer output[ "Subject"] = info.subject output[ "Title"] = info.title output[ "Number of pages"] = num_pages # To Display collected metadata print("## File Information ##################################################") print("\n".join("{}:{}".format(i, j) for i, j in output.items())) print("######################################################################") return True, output

Python如何为PDF文件加水印?该get_info()函数收集输入PDF文件的元数据,可以提取以下属性:作者、创建者、制作人、主题、标题和页数。
值得注意的是,你无法为加密的 PDF 文件提取这些属性。
def get_output_file(input_file: str, output_file: str): """ Check whether a temporary output file is needed or not """ input_path = os.path.dirname(input_file) input_filename = os.path.basename(input_file) # If output file is empty -> generate a temporary output file # If output file is equal to input_file -> generate a temporary output file if not output_file or input_file == output_file: tmp_file = os.path.join(input_path, 'tmp_' + input_filename) return True, tmp_file return False, output_file

当没有指定输出文件时,或者输入和输出文件的路径相等时,上述函数返回临时输出文件的路径。
def create_watermark(wm_text: str): """ Creates a watermark template. """ if wm_text: # Generate the output to a memory buffer output_buffer = BytesIO() # Default Page Size = A4 c = canvas.Canvas(output_buffer, pagesize=PAGESIZE) # you can also add image instead of text # c.drawImage("logo.png", X, Y, 160, 160) # Set the size and type of the font c.setFont(FONTNAME, FONTSIZE) # Set the color if isinstance(COLOR, tuple): color = (c/255 for c in COLOR) c.setFillColorRGB(*color) else: c.setFillColor(COLOR) # Rotate according to the configured parameter c.rotate(ROTATION_ANGLE) # Position according to the configured parameter c.drawString(X, Y, wm_text) c.save() return True, output_buffer return False, None

该函数执行以下操作:
  • 创建水印文件并将其存储在内存中。
  • 使用reportlab在我们创建的画布上应用之前定义的参数。
【如何在Python中为PDF文件加水印(实现代码示例)】如何在Python中为PDF文件加水印?请注意drawString(),你可以使用drawImage()绘制图像来代替使用该方法来编写文本,就像上面函数中的注释一样。
def save_watermark(wm_buffer, output_file): """ Saves the generated watermark template to disk """ with open(output_file, mode='wb') as f: f.write(wm_buffer.getbuffer()) f.close() return True

save_watermark()  将生成的水印模板保存到物理文件中,以防你需要对其进行可视化。
现在让我们编写负责为给定 PDF 文件添加水印的函数:
def watermark_pdf(input_file: str, wm_text: str, pages: Tuple = None): """ Adds watermark to a pdf file. """ result, wm_buffer = create_watermark(wm_text) if result: wm_reader = PdfFileReader(wm_buffer) pdf_reader = PdfFileReader(open(input_file, 'rb'), strict=False) pdf_writer = PdfFileWriter() try: for page in range(pdf_reader.getNumPages()): # If required to watermark specific pages not all the document pages if pages: if str(page) not in pages: continue page = pdf_reader.getPage(page) page.mergePage(wm_reader.getPage(0)) pdf_writer.addPage(page) except Exception as e: print("Exception = ", e) return False, None, None return True, pdf_reader, pdf_writer

Python为PDF文件加水印示例解释:此功能旨在将输入的 PDF 文件与生成的水印合并。它接受以下参数:
  • input_file:PDF 文件的水印路径。
  • wm_text:要设置为水印的文本。
  • pages:要加水印的页面。
它执行以下操作:
  • 创建水印并将其存储在内存缓冲区中。
  • 遍历输入文件的所有页面,并将每个选定页面与先前生成的水印合并。水印就像页面顶部的叠加层。
  • 将结果页面添加到pdf_writer对象。
def unwatermark_pdf(input_file: str, wm_text: str, pages: Tuple = None): """ Removes watermark from the pdf file. """ pdf_reader = PdfFileReader(open(input_file, 'rb'), strict=False) pdf_writer = PdfFileWriter() for page in range(pdf_reader.getNumPages()): # If required for specific pages if pages: if str(page) not in pages: continue page = pdf_reader.getPage(page) # Get the page content content_object = page[ "/Contents"].getObject() content = ContentStream(content_object, pdf_reader) # Loop through all the elements page elements for operands, operator in content.operations: # Checks the TJ operator and replaces the corresponding string operand (Watermark text) with '' if operator == b_("Tj"): text = operands[ 0] if isinstance(text, str) and text.startswith(wm_text): operands[ 0] = TextStringObject('') page.__setitem__(NameObject('/Contents'), content) pdf_writer.addPage(page) return True, pdf_reader, pdf_writer

此功能的目的是从 PDF 文件中去除水印文本。它接受以下参数:
  • input_file:PDF 文件的水印路径。
  • wm_text:要设置为水印的文本。
  • pages:要加水印的页面。
它执行以下操作:
  • 遍历输入文件的页面并获取每个页面的内容。
  • 使用抓取的内容,它找到运算符TJ并替换此运算符后面的字符串(水印文本)。
  • 将合并后的结果页面添加到pdf_writer对象。
def watermark_unwatermark_file(**kwargs): input_file = kwargs.get('input_file') wm_text = kwargs.get('wm_text') # watermark-> Watermark # unwatermark -> Unwatermark action = kwargs.get('action') # HDD -> Temporary files are saved on the Hard Disk Drive and then deleted # RAM -> Temporary files are saved in memory and then deleted. mode = kwargs.get('mode') pages = kwargs.get('pages') temporary, output_file = get_output_file( input_file, kwargs.get('output_file')) if action == "watermark": result, pdf_reader, pdf_writer = watermark_pdf( input_file=input_file, wm_text=wm_text, pages=pages) elif action == "unwatermark": result, pdf_reader, pdf_writer = unwatermark_pdf( input_file=input_file, wm_text=wm_text, pages=pages) # Completed successfully if result: # Generate to memory if mode == "RAM": output_buffer = BytesIO() pdf_writer.write(output_buffer) pdf_reader.stream.close() # No need to create a temporary file in RAM Mode if temporary: output_file = input_file with open(output_file, mode='wb') as f: f.write(output_buffer.getbuffer()) f.close() elif mode == "HDD": # Generate to a new file on the hard disk with open(output_file, 'wb') as pdf_output_file: pdf_writer.write(pdf_output_file) pdf_output_file.close() pdf_reader.stream.close() if temporary: if os.path.isfile(input_file): os.replace(output_file, input_file) output_file = input_file

上面的函数接受几个参数:
  • input_file:PDF 文件的水印路径。
  • wm_text:要设置为水印的文本。
  • action: 执行是否加水印或取消水印文件的操作。
  • mode: 临时文件的位置是内存还是硬盘。
  • pages:要加水印的页面。
watermark_unwatermark_file()函数调用先前定义的函数watermark_pdf()unwatermark_pdf()取决于所选的action.
Python如何为PDF文件加水印?基于选定的mode,如果输出文件与输入文件的路径相似或未指定输出文件,则将创建一个临时文件,以防选定的modeHDD(硬盘驱动器)。
接下来,让我们添加从包含多个 PDF 文件的文件夹中添加或删除水印的功能:
def watermark_unwatermark_folder(**kwargs): """ Watermarks all PDF Files within a specified path Unwatermarks all PDF Files within a specified path """ input_folder = kwargs.get('input_folder') wm_text = kwargs.get('wm_text') # Run in recursive mode recursive = kwargs.get('recursive') # watermark-> Watermark # unwatermark -> Unwatermark action = kwargs.get('action') # HDD -> Temporary files are saved on the Hard Disk Drive and then deleted # RAM -> Temporary files are saved in memory and then deleted. mode = kwargs.get('mode') pages = kwargs.get('pages') # Loop though the files within the input folder. for foldername, dirs, filenames in os.walk(input_folder): for filename in filenames: # Check if pdf file if not filename.endswith('.pdf'): continue # PDF File found inp_pdf_file = os.path.join(foldername, filename) print("Processing file:", inp_pdf_file) watermark_unwatermark_file(input_file=inp_pdf_file, output_file=None, wm_text=wm_text, action=action, mode=mode, pages=pages) if not recursive: break

如何在Python中为PDF文件加水印?该函数根据recursive参数的值是否递归地遍历指定文件夹的文件,并逐个处理这些文件。
接下来,让我们创建一个实用函数来检查路径是文件路径还是目录路径:
def is_valid_path(path): """ Validates the path inputted and checks whether it is a file path or a folder path """ if not path: raise ValueError(f"Invalid Path") if os.path.isfile(path): return path elif os.path.isdir(path): return path else: raise ValueError(f"Invalid Path {path}")

Python为PDF文件加水印示例 - 现在我们拥有了本教程所需的所有函数,让我们制作最后一个用于解析命令行参数的函数:
def parse_args(): """ Get user command line parameters """ parser = argparse.ArgumentParser(description="Available Options") parser.add_argument('-i', '--input_path', dest='input_path', type=is_valid_path, required=True, help="Enter the path of the file or the folder to process") parser.add_argument('-a', '--action', dest='action', choices=[ 'watermark', 'unwatermark'], type=str, default='watermark', help="Choose whether to watermark or to unwatermark") parser.add_argument('-m', '--mode', dest='mode', choices=[ 'RAM', 'HDD'], type=str, default='RAM', help="Choose whether to process on the hard disk drive or in memory") parser.add_argument('-w', '--watermark_text', dest='watermark_text', type=str, required=True, help="Enter a valid watermark text") parser.add_argument('-p', '--pages', dest='pages', type=tuple, help="Enter the pages to consider e.g.: [ 2,4]") path = parser.parse_known_args()[ 0].input_path if os.path.isfile(path): parser.add_argument('-o', '--output_file', dest='output_file', type=str, help="Enter a valid output file") if os.path.isdir(path): parser.add_argument('-r', '--recursive', dest='recursive', default=False, type=lambda x: ( str(x).lower() in [ 'true', '1', 'yes']), help="Process Recursively or Non-Recursively") # To Porse The Command Line Arguments args = vars(parser.parse_args()) # To Display The Command Line Arguments print("## Command Arguments #################################################") print("\n".join("{}:{}".format(i, j) for i, j in args.items())) print("######################################################################") return args

下面是定义的参数:
  • input_path: 一个必需参数,用于输入要处理的文件或文件夹的路径,该参数与is_valid_path()之前定义的函数相关联。
  • action:将要执行的动作,要么是watermarkunwatermarkPDF文件,默认是水印。
  • mode: 指定生成的临时文件的目的地,是内存还是硬盘。
  • watermark_text:要设置为水印的字符串。
  • pages:要加水印的页面(例如第一页是[0],第二页和第四页是[1, 3]等)。如果未指定,则为所有页面。
  • output_file: 输出文件的路径。
  • recursive: 是否递归处理文件夹。
现在我们拥有了一切,让我们编写主要代码以根据传递的参数执行:
if __name__ == '__main__': # Parsing command line arguments entered by user args = parse_args() # If File Path if os.path.isfile(args[ 'input_path']): # Extracting File Info get_info(input_file=args[ 'input_path']) # Encrypting or Decrypting a File watermark_unwatermark_file( input_file=args[ 'input_path'], wm_text=args[ 'watermark_text'], action=args[ 'action'], mode=args[ 'mode'], output_file=args[ 'output_file'], pages=args[ 'pages'] ) # If Folder Path elif os.path.isdir(args[ 'input_path']): # Encrypting or Decrypting a Folder watermark_unwatermark_folder( input_folder=args[ 'input_path'], wm_text=args[ 'watermark_text'], action=args[ 'action'], mode=args[ 'mode'], recursive=args[ 'recursive'], pages=args[ 'pages'] )

相关:  如何在 Python 中从 PDF 中提取图像。
现在让我们测试我们的程序,如果你打开一个终端窗口并输入:
$ python pdf_watermarker.py --help

它将显示定义的参数及其各自的描述:
usage: pdf_watermarker.py [ -h] -i INPUT_PATH [ -a {watermark,unwatermark}] [ -m {RAM,HDD}] -w WATERMARK_TEXT [ -p PAGES]Available Optionsoptional arguments: -h, --helpshow this help message and exit -i INPUT_PATH, --input_path INPUT_PATH Enter the path of the file or the folder to process -a {watermark,unwatermark}, --action {watermark,unwatermark} Choose whether to watermark or to unwatermark -m {RAM,HDD}, --mode {RAM,HDD} Choose whether to process on the hard disk drive or in memory -w WATERMARK_TEXT, --watermark_text WATERMARK_TEXT Enter a valid watermark text -p PAGES, --pages PAGES Enter the pages to consider e.g.: [ 2,4]

Python如何为PDF文件加水印?现在让我们以给我的简历加水印为例:
$ python pdf_watermarker.py -a watermark -i "CV Bassem Marji_Eng.pdf" -w "CONFIDENTIAL" -o CV_watermark.pdf

将产生以下输出:
## Command Arguments ################################################# input_path:CV Bassem Marji_Eng.pdf action:watermark mode:RAM watermark_text:CONFIDENTIAL pages:None output_file:CV_watermark.pdf ###################################################################### ## File Information ################################################## File:CV Bassem Marji_Eng.pdf Encrypted:False Author:TMS User Creator:Microsoft? Word 2013 Producer:Microsoft? Word 2013 Subject:None Title:None Number of pages:1 ######################################################################

Python为PDF文件加水印示例如下,这就是输出CV_watermark.pdf文件的样子:
如何在Python中为PDF文件加水印(实现代码示例)

文章图片
现在让我们删除添加的水印:
$ python pdf_watermarker.py -a unwatermark -i "CV_watermark.pdf" -w "CONFIDENTIAL" -o CV.pdf

这次水印将被删除并保存到一个新的 PDF 文件中CV.pdf
如何在Python中为PDF文件加水印?你还可以分别为模式和页面设置-m-p。你还可以为位于特定路径下的 PDF 文件列表添加水印:
$ python pdf_watermarker.py -i "C:\Scripts\Test" -a "watermark" -w "CONFIDENTIAL" -r False

或者从一堆 PDF 文件中去除水印:
$ python pdf_watermarker.py -i "C:\Scripts\Test" -a "unwatermark" -w "CONFIDENTIAL" -m HDD -p[ 0] -r False

结论Python如何为PDF文件加水印?在本教程中,你学习了如何使用 Python 中的 reportlab 和 PyPDF4 库在 PDF 文件中添加和删除水印。我希望这篇文章能帮助你理解这个很酷的功能。
以下是一些其他与 PDF 相关的教程:
  • 如何在 Python 中将 PDF 转换为 Docx
  • 如何在 Python 中合并 PDF 文件
  • 如何在 Python 中提取 PDF 元数据

    推荐阅读