ARTICLE · 1189426
Python批量给PDF加水印,再也不用一个个手动加了!
工作中经常遇到需要给PDF加水印的情况:合同要标“机密”,简历要加姓名,教程要注版权……如果只有一两个文件,手动加还能忍;但要是几十上百个,那真是点到手抽筋。
今天就教大家用Python写一个批量PDF加水印脚本,一次运行,全部搞定,效率直接起飞!
一、准备工作
这次我们用两个库:
reportlab:生成水印PDF(支持中文、旋转、透明度)
pypdf:合并水印和原PDF(PyPDF2的升级版,维护更活跃)
安装命令:
pip install reportlab pypdf如果之前装过PyPDF2,建议换成pypdf,接口基本兼容,但性能更好。
二、核心思路
PDF加水印的本质是:做一个只有水印的透明页面,然后把它“盖”到原PDF的每一页上。
步骤拆解:
读取原PDF,获取每一页的宽高
用reportlab生成一个同样大小的水印页(文字可旋转、半透明)
把水印页和原页面合并
保存为新PDF
三、完整代码
import osimport iofrom reportlab.pdfgen import canvasfrom reportlab.pdfbase import pdfmetricsfrom reportlab.pdfbase.ttfonts import TTFontfrom pypdf import PdfReader, PdfWriter# ============ 配置区 ============INPUT_DIR = "./pdfs" # 原PDF文件夹OUTPUT_DIR = "./output" # 输出文件夹WATERMARK_TEXT = "机密文件 请勿外传" # 水印文字FONT_PATH = "C:/Windows/Fonts/simhei.ttf" # 中文字体路径FONT_SIZE = 40 # 字号OPACITY = 0.15 # 透明度(0-1,越小越淡)ANGLE = 45 # 旋转角度# ================================# 注册中文字体pdfmetrics.registerFont(TTFont("SimHei", FONT_PATH))def create_watermark_page(width, height, text):"""生成水印PDF页面,返回BytesIO"""packet = io.BytesIO()can = canvas.Canvas(packet, pagesize=(width, height))can.setFont("SimHei", FONT_SIZE)can.setFillAlpha(OPACITY)can.setFillColorRGB(0.5, 0.5, 0.5) # 灰色# 平移到页面中心并旋转can.translate(width / 2, height / 2)can.rotate(ANGLE)# 文字居中text_width = pdfmetrics.stringWidth(text, "SimHei", FONT_SIZE)can.drawString(-text_width / 2, 0, text)can.save()packet.seek(0)return packetdef add_watermark(input_path, output_path, text):"""给单个PDF添加水印"""reader = PdfReader(input_path)writer = PdfWriter()for page in reader.pages:width = float(page.mediabox.width)height = float(page.mediabox.height)# 生成水印页watermark_packet = create_watermark_page(width, height, text)watermark_page = PdfReader(watermark_packet).pages[0]# 合并page.merge_page(watermark_page)writer.add_page(page)with open(output_path, "wb") as f:writer.write(f)def batch_process():"""批量处理文件夹下所有PDF"""if not os.path.exists(OUTPUT_DIR):os.makedirs(OUTPUT_DIR)pdf_files = [f for f in os.listdir(INPUT_DIR) if f.lower().endswith(".pdf")]if not pdf_files:print(f"❌ 在 {INPUT_DIR} 中没有找到PDF文件")returnprint(f"📄 共找到 {len(pdf_files)} 个PDF文件\n")for i, filename in enumerate(pdf_files, 1):input_path = os.path.join(INPUT_DIR, filename)output_path = os.path.join(OUTPUT_DIR, filename)try:add_watermark(input_path, output_path, WATERMARK_TEXT)print(f"✅ [{i}/{len(pdf_files)}] {filename} 处理成功")except Exception as e:print(f"❌ [{i}/{len(pdf_files)}] {filename} 处理失败:{e}")print(f"\n🎉 全部完成!输出目录:{os.path.abspath(OUTPUT_DIR)}")if __name__ == "__main__":batch_process()
四、效果展示
把需要处理的PDF丢进 ./pdfs 文件夹,运行脚本:
📄 共找到 5 个PDF文件✅ [1/5] 报告A.pdf 处理成功✅ [2/5] 报告B.pdf 处理成功✅ [3/5] 合同C.pdf 处理成功✅ [4/5] 简历D.pdf 处理成功✅ [5/5] 说明E.pdf 处理成功🎉 全部完成!输出目录:/Users/xxx/output
打开输出文件,每页都会带上倾斜45度的灰色半透明水印,既不影响阅读,又能起到标识作用。
五、进阶玩法
1. 平铺水印(铺满整页)
把 create_watermark_page 改成循环绘制:
def create_watermark_page(width, height, text):packet = io.BytesIO()can = canvas.Canvas(packet, pagesize=(width, height))can.setFont("SimHei", FONT_SIZE)can.setFillAlpha(OPACITY)can.setFillColorRGB(0.5, 0.5, 0.5)step_x, step_y = 250, 180 # 间距for x in range(0, int(width) + step_x, step_x):for y in range(0, int(height) + step_y, step_y):can.saveState()can.translate(x, y)can.rotate(ANGLE)can.drawString(0, 0, text)can.restoreState()can.save()packet.seek(0)return packet
2. 添加日期水印
from datetime import datetimetext = f"机密文件 {datetime.now().strftime('%Y-%m-%d')}"
3. 图片水印
用 drawImage 代替 drawString,即可插入Logo图片。
六、常见问题
Q1:中文显示成方框?
字体路径不对。Windows 用 C:/Windows/Fonts/simhei.ttf,Mac 用 /System/Library/Fonts/PingFang.ttc,Linux 可下载 simhei.ttf 放项目目录。
Q2:水印太深/太浅?
调整 OPACITY,建议 0.1~0.3。
Q3:加密PDF能处理吗?
先解密:reader.decrypt("密码")。
Q4:文件很大处理慢?
pypdf 比 PyPDF2 快,但超大文件建议用 pikepdf。
七、总结
今天我们用不到80行代码,实现了批量PDF加水印。核心就是生成水印页 + 合并页面,思路简单,效果拔群。
同样的方法还能扩展:
批量加页码
批量合并/拆分PDF
批量提取文字
PDF转图片
把重复的事交给代码,把时间留给更重要的事。















































































