Python批量为PDF添加个性化水印:从Excel到最终输出的完整实现
本文详解如何利用Python将Excel中的经销商名单批量转换为带有个人专属水印的PDF文件。通过Reportlab生成带透明度的旋转文字水印PDF,再使用Pikepdf将水印图层叠加至目标价目表,实现防泄露追踪。文章提供完整代码逻辑与参数配置说明,适合初学者学习模块化编程思路。
在商业场景中,向不同客户分发机密文件时,常需添加差异化水印以防范泄露风险。即使文件内容一致,每个客户接收的PDF均包含其专属标识。一旦资料外泄,即可通过水印溯源至具体责任人。
近日,一位从事市场工作的朋友咨询如何为PDF批量添加水印,具体需求场景如下所示:

该需求涉及一个包含10000名经销商名称的Excel文件。目标是将价目表PDF发送给所有经销商,且确保每位经销商收到的PDF文件中,水印均为其自身的名称。
若采用手动操作,此任务将极其耗时且易出错。然而,借助Python脚本处理,整个过程仅需不到30行代码即可轻松完成。
环境准备
实现该功能需安装两个Python库:reportlab与pikepdf。可通过Pip工具进行安装,命令如下:
python3 -m pip install reportlab pikepdf
此外,需准备.ttf或.ttc格式的中文字体文件。用户可直接从网络下载,亦可调用系统内置字体。此处以macOS系统默认宋体为例进行说明。
macOS系统的字体文件存储于/System/Library/Fonts目录下。宋体对应的.ttc文件路径为/System/Library/Fonts/Supplemental/Songti.ttc。对于系统预装字体,仅需知晓其文件名Songti.ttc即可。若使用网络下载的第三方字体,则需指定绝对路径或相对于项目代码的相对路径。
获取经销商名单
鉴于咨询者未使用pandas库,此处演示使用Python原生方法读取经销商名单。假设Excel数据格式如下所示:

首先,将该Excel文件导出为csv格式:

随后,使用Python读取该csv文件以提取经销商名称列表:
import csv
with open('经销商信息.csv') as f:
reader = csv.DictReader(f)
name_list = [x['经销商名字'] for x in reader]
print(name_list)
代码运行结果如下图所示:

生成水印PDF
通常无法直接将文本作为水印嵌入PDF,需先将文字生成图片或独立的水印PDF文件,再将其作为图层覆盖至目标PDF之上。
因此,需为每位经销商生成包含其名称的水印PDF文件,该文件仅包含水印文字,效果如下:

实现此功能的代码文件名为create_watermark.py,具体代码如下:
import csv
from pathlib import Path
from reportlab.lib import units
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
with open('经销商信息.csv') as f:
reader = csv.DictReader(f)
name_list = [x['经销商名字'] for x in reader]
pdfmetrics.registerFont(TTFont('Songti', 'Songti.ttc')) # 加载中文字体
water_mark_folder = Path('water_pdf') # 用一个文件夹存放所有的水印PDF
water_mark_folder.mkdir(exist_ok=True)
for name in name_list:
path = str(water_mark_folder / Path(f'{name}.pdf'))
c = canvas.Canvas(path, pagesize=(200 * units.mm, 200 * units.mm)) # 生成画布,长宽都是200毫米
c.translate(0.1 * 200 * units.mm, 0.1 * 200 * units.mm)
c.rotate(45) # 把水印文字旋转45°
c.setFont('Songti', 35) # 字体大小
c.setStrokeColorRGB(0, 0, 0) # 设置字体颜色
c.setFillColorRGB(0, 0, 0) # 设置填充颜色
c.setFillAlpha(0.3) # 设置透明度,越小越透明
c.drawString(0, 0, f'{name}专用价目表,严禁泄露!')
c.save()
代码注释已详细说明各部分作用。运行后,项目根目录将生成water_pdf文件夹,其中包含所有生成的水印PDF文件。
合并水印与目标PDF
最后一步是将每位经销商的水印PDF与原始目标PDF进行合并。此过程通过水印PDF作为图层覆盖在目标PDF上完成。
利用pikepdf库可简化此操作。编写combine.py文件,代码如下:
import glob
from pathlib import Path
from pikepdf import Pdf, Page, Rectangle
water_pdf_list = glob.glob('water_pdf/*.pdf')
result = Path('result')
result.mkdir(exist_ok=True)
col = 2 # 每页多少列水印
row = 3 # 每页多少行水印
for path in water_pdf_list:
target = Pdf.open('./PythonisinstanceGolang.pdf') # 必须每次重新打开PDF,因为添加水印是inplace的操作
file = Path(path)
name = file.stem
water_mark_pdf = Pdf.open(path)
water_mark = water_mark_pdf.pages[0]
for page in target.pages:
for x in range(col): # 每一行显示多少列水印
for y in range(row): # 每一页显示多少行PDF
page.add_overlay(water_mark,
Rectangle(page.trimbox[2] * x / col,
page.trimbox[3] * y / row,
page.trimbox[2] * (x + 1) / col,
page.trimbox[3] * (y + 1) / row))
result_name = Path('result', f'{name}_添加水印.pdf')
target.save(str(result_name))
运行脚本后,项目根目录将生成result文件夹,内含已添加水印的PDF文件,效果如下:

此处需对代码中的关键部分进行解释,带行号的代码截图如下:

代码第21至22行包含两个for循环,旨在为单页添加多个水印。请注意图中圈出的关键位置:

每页包含6个水印,排列为3行2列。其中3行由变量row控制,2列由变量col控制。用户可根据需求调整这两个数值,甚至实现水印位置随机变换,以增强防去除能力。
其中,page.trimbox[2]代表PDF页面的宽度,page.trimbox[3]代表PDF页面的高度。
总结
本文将任务拆解为三个主要部分:
- 将Excel转换为CSV格式,便于Python读取
- 使用Python读取CSV并生成水印PDF
- 将水印PDF与目标PDF文件合并
上述三部分代码可整合至单个.py文件中,但考虑到提问者非专业程序员且Python基础较弱,合并代码可能导致错误排查困难。
在计算机领域,通过模块化拆分问题或增加中间层是解决复杂问题的通用策略。本文采用的方法即为此思路的体现:对初学者而言,每一步骤相对独立且即时可见效果。第二步依赖第一步结果,第三步依赖第二步结果,清晰的输入输出关系显著降低了复杂度。若出现错误,也更容易定位问题所在。
至此,关于Python批量为PDF添加水印的详细教程已结束。如需了解更多Python PDF处理相关内容,请查阅本站往期文章或继续浏览后续内容。感谢大家的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









