

Excel & PDF 水印工具:功能、原理与打包指南
本文详细介绍一个基于 PyQt5、Pillow、ReportLab、PyPDF 与 OpenPyXL 的桌面水印工具的实现细节,从界面交互到预览算法、从 PDF/Excel 处理到打包分发的全流程。项目现已将所有核心逻辑合并到单文件 main.py,便于按需打包成单个可执行文件。
Github下载链接
一、功能概览
- 支持两类水印:
文字水印、图片水印 - 预览所见即所得:参数变更(文本/颜色/透明度/角度/排列/数量/缩放)实时更新
- 文本水印排列:
居中、平铺、对角平铺、左上角、右下角 - 文本水印数量控制:
1–100,支持智能栅格分布 - 批量处理文件:拖拽或添加
PDF / XLSX / XLS - 安全写出:PDF 采用临时文件写入替换,避免 “不能打开文件写入” 等问题
二、界面与交互
- 主窗口类:
MainWindow(d:\测试\daily-code\101-Excel&PDF添加水印\main.py:441) - 文件拖拽列表:
FileDropListWidget支持拖拽 URL 并过滤后缀(main.py:14) - 预览区:
QScrollArea + QLabel,白底模拟纸张 - 设置区:
- 水印类型切换(文字/图片)
- 文本内容、字体大小、颜色、数量、排列
- 图片路径、缩放比例
- 通用:透明度(默认
80%)、角度(默认0°)
- 处理区:进度条、状态提示、开始处理按钮
三、预览实现(所见即所得)
- 入口方法:
update_preview(main.py:655) - 预览逻辑:
- 构造
500×300的 RGBA 白底图作为“纸张” - 使用
Pillow.ImageDraw画浅灰横线模拟文档内容 - 根据水印类型生成
watermark_img:- 文字:调用
create_text_image(main.py:190)生成单位图 - 图片:打开、调透明度、旋转、缩放得到单位图
- 文字:调用
- 排列/数量:
tile/diag_tile:单位图按步长平铺,计数达到count即止- 其他:
count==1居中;否则按cols×rows栅格均匀分布
- 转换为
QPixmap:优先ImageQt,失败时走BGRA回退路径
- 构造
预览与导出保持一致的视觉策略(角度、透明度、排列、数量),确保一致性。
四、核心算法与实现
4.1 文本水印图像(Pillow)
- 函数:
create_text_image(main.py:190) - 要点:
- 字体优先尝试
SimHei与msyh,回退Arial/默认 - 使用
textbbox测量文本尺寸,创建略大于文本的透明画布 - 按中心绘制,应用
alpha,再旋转angle得到单位图
- 字体优先尝试
4.2 PDF 文字水印(ReportLab + PyPDF)
- 单页水印 PDF 生成:
_create_text_watermark_page(main.py:32)- 设置
FillAlpha/StrokeAlpha控制透明度 - 文本宽度通过
pdfmetrics.stringWidth,失败回退估算 arrangement:center:count==1居中绘制;否则按栅格分布top_left/bottom_right:相对页边定位tile/diag_tile:按步长平铺,达到数量停止
- 设置
- 合并到目标 PDF:
add_text_watermark_to_pdf(main.py:146)- 对每页:计算页宽高,生成一页水印 PDF,再
merge_page - 安全写出:临时文件写入后
os.replace原文件(避免写入失败)
- 对每页:计算页宽高,生成一页水印 PDF,再
4.3 PDF 图片水印
- 函数:
create_watermark_pdf_from_image(main.py:109)- 使用
Pillow预处理透明度、旋转、缩放后,嵌入ReportLabPDF - 居中绘制,支持自定义
x/y(默认居中)
- 使用
- 合并函数:
add_watermark_to_pdf(main.py:172)统一按页merge_page
4.4 Excel 水印(OpenPyXL)
- 函数:
add_watermark_to_excel(main.py:221) - 文本水印:
tile/diag_tile:预先生成大画布平铺单位图,插入到 Excel- 其他:
count==1直接单位图;否则按栅格组合成一张图再插入
- 图片水印:读入后按透明度、角度、缩放处理再插入
- 位置锚点:
top_left → A1、bottom_right → M30、tile/diag_tile → A1、默认居中近似C5
五、排列与数量:一致的可视策略
- 预览与导出均遵循相同策略,保证视觉一致:
- 栅格分布:
cols = floor(sqrt(count)),rows = ceil(count/cols) - 平铺步长:根据单位图尺寸动态计算,避免过密/过疏
count到达即停止,避免“数量设置 2 预览出现 3”这类问题
- 栅格分布:
相关实现参考:update_preview(main.py:655)、_create_text_watermark_page(main.py:32)、add_watermark_to_excel(main.py:221)。
六、兼容性与错误修复
- 中文字体支持:
- 预注册
SimHei,失败回退Arial/Helvetica(main.py顶部字体注册) Pillow侧优先simhei.ttf / msyh.ttf / arial.ttf
- 预注册
- 预览 BGRA 回退:
- 若
ImageQt转换失败,使用BGRA原始字节构造QImage(update_preview)
- 若
- PDF 写出安全:
- 临时文件写入后
os.replace原文件,防止 “can't open file for write” 错误(main.py:146)
- 临时文件写入后
- 拖拽修复:
- 支持
dragEnterEvent/dragMoveEvent/dropEvent,过滤后缀与去重(main.py:20/26/32)
- 支持
七、依赖与打包
- 依赖:
PyQt5,Pillow,reportlab,pypdf,openpyxl - 安装:
pip install PyQt5 Pillow reportlab pypdf openpyxl
- 单文件打包示例(无控制台窗口):
pyinstaller -F -w main.py
- 常见打包注意:
- 中文字体文件
simhei.ttf/msyh.ttf如需强依赖,可随程序放置同目录或指定搜索路径 - 打包时 PyQt5 资源体积较大,建议使用
-w以减少控制台闪现
- 中文字体文件
八、使用指南(简明)
- 启动程序,拖拽或添加
PDF/Excel文件到左侧列表 - 选择水印类型(文字/图片)并设置参数:
- 文字:内容、字体大小、颜色、数量与排列
- 图片:选择路径与缩放比例
- 通用:透明度(默认 80%)、角度(默认 0°)
- 查看预览区,确认效果
- 点击“开始处理”,输出文件位于程序目录下
output/中
九、常见问题(FAQ)
- 预览不显示或报错?
- 可能为
ImageQt转换异常,程序已内置 BGRA 回退;检查图片路径与权限
- 可能为
- 中文文字方块或乱码?
- 系统缺少中文字体时会回退
Arial/默认;可手动放置simhei.ttf/msyh.ttf同目录以提升显示效果
- 系统缺少中文字体时会回退
- PDF 水印后出现错误码
1/2?- 已移除过时的合并方式,并采用临时文件写入替换,避免写入失败
- Excel 水印看起来位置不对?
- 锚点是近似位置(如
C5),不同工作表布局可能有偏差,可根据需要调整锚点策略
- 锚点是近似位置(如
十、后续改进方向
- 可选自定义字体文件上传与缓存
- Excel 多锚点分布与分页适配
- PDF 平铺时根据页面尺寸自适应更精确的步长
- 预览区增加缩放与适应窗口模式切换
项目核心代码均在单文件 main.py 中,关键函数位置:
- 文本水印图像:
d:\测试\daily-code\101-Excel&PDF添加水印\main.py:190 - PDF 文字水印页生成:
d:\测试\daily-code\101-Excel&PDF添加水印\main.py:32 - PDF 图片水印生成:
d:\测试\daily-code\101-Excel&PDF添加水印\main.py:109 - 合并 PDF:
d:\测试\daily-code\101-Excel&PDF添加水印\main.py:146/172 - Excel 水印:
d:\测试\daily-code\101-Excel&PDF添加水印\main.py:221 - 预览:
d:\测试\daily-code\101-Excel&PDF添加水印\main.py:655
如需我直接执行打包并提供可执行文件输出路径,可告知是否需要控制台、图标与目标目录,我将自动完成并回传打包结果。