Python自动化办公:5分钟搞定PDF批量加水印(附透明水印代码)

你是否也曾被堆积如山的PDF文件搞得焦头烂额?行政部门的同事需要给上百份合同统一加盖公司水印,财务团队每个月都要为成堆的报表添加“机密”标识,市场部的小伙伴则要为产品手册打上品牌Logo。手动操作?打开每个PDF,插入图片,调整位置,保存……重复几十次,一个下午就没了,还容易出错。

这就是为什么我们需要自动化。今天,我们不谈复杂的编程理论,也不讲那些让人望而生畏的底层原理。我只想和你分享一个我用了好几年的“懒人”方案——用Python脚本,在5分钟内,给任意数量的PDF文件批量加上透明水印。你不需要是程序员,甚至不需要理解代码的每一行,只需要会复制粘贴、修改几个参数,就能立刻上手,把宝贵的时间还给更重要的工作。

1. 为什么选择Python处理PDF水印?

在深入代码之前,我们得先搞清楚,市面上有那么多PDF编辑器,为什么偏偏要选Python?答案很简单:极致的灵活性与批量处理能力

想象一下,你需要处理的场景:

  • 场景A:为500份员工手册添加“内部传阅”水印,水印文字需要倾斜30度,透明度为15%。
  • 场景B:为100张产品设计图PDF添加公司半透明Logo,Logo需要平铺在整个页面上。
  • 场景C:根据不同文件类型(如合同、报告、发票),自动应用不同的水印文字和样式。

如果用传统软件,场景A和B或许还能勉强应付,但场景C几乎不可能高效完成。而Python脚本,就像一个不知疲倦的智能助手,可以完美适配所有这些复杂、多变的需求。

更重要的是,Python处理PDF的生态非常成熟。我们主要会用到两个库:

库名称 核心用途 特点
PyPDF2 (或更新的 pypdf) PDF文件的读写、页面合并、内容提取 纯Python实现,轻量,专注于PDF结构操作
Pillow (PIL) 图像处理,如创建水印图片、调整透明度、旋转 功能强大的图像处理库,能生成高质量的水印图

这两个库的组合,为我们提供了从创建水印图像到将其“盖”到PDF每一页上的完整能力链。而且,整个过程是可编程、可批量化、可定制化的。

提示:在开始前,请确保你的电脑已经安装了Python(建议3.7及以上版本)。安装上述库非常简单,打开命令行(Windows的CMD或PowerShell,Mac/Linux的终端),输入以下两行命令即可:

pip install pypdf pillow

这里我们使用 pypdf,它是 PyPDF2 的一个积极维护的现代分支,API更友好。

2. 打造你的第一枚透明图片水印

水印的核心是一张透明的PNG图片。我们将用Pillow库来“画”出这张图。别担心,我们不需要从零开始设计,下面的代码模板已经为你准备好了一切。

2.1 核心代码解析:创建一个文本水印图片

让我们先看看如何把一段文字(比如“公司机密”)变成一张漂亮的、带透明度的水印图片。

from PIL import Image, ImageDraw, ImageFont, ImageEnhance

def create_watermark_image(text, font_size=80, opacity=0.2):
    """
    创建一张透明背景的文字水印图片。
    
    参数:
        text: 水印文字,如“内部资料”
        font_size: 字体大小,默认80
        opacity: 不透明度,0.0(全透明)到1.0(不透明),默认0.2
    """
    # 1. 加载字体。这里使用系统默认的字体,你也可以指定字体文件路径
    # 例如:font = ImageFont.truetype("simhei.ttf", font_size)
    try:
        font = ImageFont.truetype("arial.ttf", font_size)
    except IOError:
        font = ImageFont.load_default()  # 如果找不到指定字体,使用默认字体
    
    # 2. 计算文字所占的图片大小(bbox是包围盒)
    # 我们先用一个临时画布测量文字尺寸
    temp_image = Image.new('RGBA', (1, 1), (255, 255, 255, 0))
    temp_draw = ImageDraw.Draw(temp_image)
    bbox = temp_draw.textbbox((0, 0), text, font=font)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]
    
    # 3. 创建一张足够大的透明底图
    # 留一些边距,让文字看起来更舒服
    margin = 20
    image_size = (text_width + margin, text_height + margin)
    watermark_image = Image.new('RGBA', image_size, (255, 255, 255, 0))
    
    # 4. 在图片上绘制文字
    draw = ImageDraw.Draw(watermark_image)
    # 将文字画在图片中央
    text_position = ((image_size[0] - text_width) // 2 - bbox[0],
                     (image_size[1] - text_height) // 2 - bbox[1])
    draw.text(text_position, text, font=font, fill=(128, 128, 128, 255))  # 灰色,完全不透明
    
    # 5. 调整整张图片的透明度
    # 分离出Alpha通道(透明度通道),并降低其亮度
    alpha = watermark_image.split()[3]
    alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
    watermark_image.putalpha(alpha)
    
    return watermark_image

# 试试看:创建一个“草稿”水印图片并保存
watermark_img = create_watermark_image("草稿 - CONFIDENTIAL", opacity=0.15)
watermark_img.save("my_watermark.png")  # 保存为PNG,支持透明背景
print("水印图片已保存为 'my_watermark.png'")

运行这段代码,你会在当前文件夹得到一个 my_watermark.png 文件。用图片查看器打开它,你会看到灰色半透明的文字悬浮在透明背景上。这个PNG文件就是我们后续要批量添加到PDF上的“印章”。

2.2 进阶技巧:平铺与旋转水印

单一的水印放在页面中央可能不够用,特别是对于需要防伪或强调版权的文档,我们常常需要将水印平铺在整个页面上,并旋转一定角度,使其不影响正文阅读的同时又难以被简单去除。

下面的函数将基础水印图片进行复制、平铺和旋转,生成一张覆盖整个PDF页面大小的“水印底图”。

import math

def create_tiled_watermark(base_watermark_image, output_pdf_path, page_size=(595, 842), angle=30, spacing=150, dpi=100):
    """
    将基础水印图片平铺、旋转,生成一个完整的PDF水印页面。
    
    参数:
        base_watermark_image: 上一步创建的基础水印图片对象
        output_pdf_path: 生成的PDF水印文件路径,如 "full_page_watermark.pdf"
        page_size: PDF页面大小,默认A4 (595x842 points, 1 point = 1/72 inch)
        angle: 水印旋转角度,默认30度
        spacing: 水印之间的间隔(像素)
        dpi: 输出图片的DPI,影响清晰度
    """
    # 将页面尺寸从点(point)转换为像素(pixel)
    # 公式:像素 = 点 * (DPI / 72)
    page_width_px = int(page_size[0] * dpi / 72)
    page_height_px = int(page_size[1] * dpi / 72)
    
    # 创建一个和PDF页面一样大的透明图像
    full_page_image = Image.new('RGBA', (page_width_px, page_height_px), (255, 255, 255, 0))
    
    # 获取单个水印的尺寸
    wm_width, wm_height = base_watermark_image.size
    
    # 计算需要平铺的行数和列数
    # 考虑到旋转,我们需要在一个更大的画布上平铺,然后旋转,最后裁剪到页面大小
    # 这个画布的边长至少是页面对角线的长度
    canvas_size = int(math.sqrt(page_width_px**2 + page_height_px**2))
    tiling_canvas = Image.new('RGBA', (canvas_size, canvas_size), (255, 255, 255, 0))
    
    # 开始平铺
    y = 0
    row_offset = 0  # 用于实现错行平铺,效果更自然
    while y < canvas_size:
        x = -row_offset  # 错开起始位置
        while x < canvas_size:
            tiling_canvas.paste(base_watermark_image, (x, y), base_watermark_image)  # 第四个参数是蒙版,用于透明粘贴
            x += wm_width + spacing
        y += wm_height + spacing
        row_offset = (wm_width + spacing) // 2 if row_offset == 0 else 0  # 切换错行偏移量
    
    # 旋转平铺好的画布
    tiling_canvas = tiling_canvas.rotate(angle, expand=True, fillcolor=(255, 255, 255, 0))
    
    # 将旋转后的画布居中粘贴到最终页面图像上
    rotated_width, rotated_height = tiling_canvas.size
    paste_x = (page_width_px - rotated_width) // 2
    paste_y = (page_height_px - rotated_height) // 2
    full_page_image.paste(tiling_canvas, (paste_x, paste_y), tiling_canvas)
    
    # 将最终图像保存为PDF(单页)
    # 注意:Pillow保存PDF时,尺寸单位是像素,我们需要告诉它DPI以匹配原始页面尺寸
    full_page_image.save(output_pdf_path, "PDF", resolution=dpi, save_all=True)
    print(f"完整水印PDF已生成: {output_pdf_path}")

# 组合使用:创建平铺水印PDF
base_img = create_watermark_image("严禁复制", font_size=60, opacity=0.1)
create_tiled_watermark(base_img, "tiled_watermark.pdf", angle=45, spacing=200)

现在,你得到了一个名为 tiled_watermark.pdf 的文件。打开它,你会看到整个页面布满了倾斜的、半透明的“严禁复制”字样。这个PDF文件本身只有一页,但它的使命是作为“印章”被盖到其他PDF的每一页上。

3. 批量加水印:一键处理整个文件夹

有了水印PDF,最关键的一步来了:如何把它快速地“盖”到几十个、几百个目标PDF文件上?这就是 pypdf 库大显身手的时候。

3.1 单个PDF加水印函数

我们先从给一个PDF文件加水印开始,理解其核心操作。

from pypdf import PdfReader, PdfWriter

def add_watermark_to_pdf(input_pdf_path, output_pdf_path, watermark_pdf_path):
    """
    将一个水印PDF叠加到目标PDF的每一页。
    
    参数:
        input_pdf_path: 需要加水印的原始PDF路径
        output_pdf_path: 加水印后输出的PDF路径
        watermark_pdf_path: 水印PDF文件路径(由上一节生成)
    """
    # 读取水印PDF,获取其第一页作为“印章”
    watermark_reader = PdfReader(watermark_pdf_path)
    watermark_page = watermark_reader.pages[0]
    
    # 读取原始PDF
    pdf_reader = PdfReader(input_pdf_path)
    pdf_writer = PdfWriter()
    
    # 遍历原始PDF的每一页
    for page_num in range(len(pdf_reader.pages)):
        original_page = pdf_reader.pages[page_num]
        
        # 关键操作:将水印页面合并到原始页面上
        # `merge_page` 方法会将水印层叠加在原始内容之上
        original_page.merge_page(watermark_page)
        
        # 将合并后的页面添加到输出器中
        pdf_writer.add_page(original_page)
    
    # 将结果写入新的PDF文件
    with open(output_pdf_path, 'wb') as out_file:
        pdf_writer.write(out_file)
    
    print(f"成功处理: {input_pdf_path} -> {output_pdf_path}")

# 试用一下
add_watermark_to_pdf("原始合同.pdf", "带水印合同.pdf", "tiled_watermark.pdf")

这个过程非常直观:就像我们有两张透明的幻灯片,一张是原始文档,一张是水印图案,我们把它们叠在一起,就得到了最终效果。pypdfmerge_page 方法高效地完成了这个物理世界的操作。

3.2 自动化批量处理脚本

现在,我们将单个文件处理升级为批量处理。假设你有一个文件夹 待处理PDF,里面装满了需要加水印的文件。

import os
from pathlib import Path

def batch_add_watermark(source_folder, output_folder, watermark_pdf_path):
    """
    批量处理一个文件夹内所有的PDF文件。
    
    参数:
        source_folder: 存放原始PDF的文件夹路径
        output_folder: 存放处理后PDF的文件夹路径(会自动创建)
        watermark_pdf_path: 水印PDF文件路径
    """
    # 确保输出文件夹存在
    Path(output_folder).mkdir(parents=True, exist_ok=True)
    
    # 获取源文件夹中所有的PDF文件
    pdf_files = [f for f in os.listdir(source_folder) if f.lower().endswith('.pdf')]
    
    if not pdf_files:
        print(f"在文件夹 '{source_folder}' 中未找到PDF文件。")
        return
    
    print(f"找到 {len(pdf_files)} 个PDF文件,开始批量处理...")
    
    success_count = 0
    for pdf_file in pdf_files:
        try:
            input_path = os.path.join(source_folder, pdf_file)
            # 生成输出文件名,例如“原文件_水印.pdf”
            output_filename = f"{Path(pdf_file).stem}_watermarked.pdf"
            output_path = os.path.join(output_folder, output_filename)
            
            # 调用单个处理函数
            add_watermark_to_pdf(input_path, output_path, watermark_pdf_path)
            success_count += 1
            
        except Exception as e:
            print(f"处理文件 '{pdf_file}' 时出错: {e}")
    
    print(f"\n批量处理完成!成功处理 {success_count}/{len(pdf_files)} 个文件。")
    print(f"处理后的文件保存在: {os.path.abspath(output_folder)}")

# 使用示例:处理“待处理PDF”文件夹中的所有文件
batch_add_watermark(
    source_folder="./待处理PDF",
    output_folder="./已加水印PDF",
    watermark_pdf_path="tiled_watermark.pdf"
)

这个脚本会自动扫描文件夹,一个接一个地处理,并将结果保存到新文件夹。你只需要运行一次,就可以去喝杯咖啡,回来时所有工作都已完成。

4. 实战配置与高级定制指南

前面的代码已经构成了一个可用的流水线。但在真实办公场景中,需求往往更加具体。这一节,我们把这些代码模块组装成一个高度可配置的“工具箱”,并解决几个常见问题。

4.1 开箱即用的完整脚本模板

我将所有功能整合进一个脚本 pdf_watermark_tool.py。你只需要修改开头的配置部分,就能适应绝大多数场景。

#!/usr/bin/env python3
"""
PDF批量透明水印添加工具
开箱即用版 - 只需修改下方“用户配置区”
"""
import os
import math
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
from pypdf import PdfReader, PdfWriter

# ==================== 用户配置区 ====================
# 在这里修改参数,无需改动后面的代码

# 1. 水印内容与样式
WATERMARK_TEXT = "内部使用 - 请勿外传"  # 水印文字
FONT_SIZE = 70                           # 字体大小
FONT_COLOR = (100, 100, 100)            # 字体颜色 RGB值,灰色为(100,100,100)
OPACITY = 0.12                          # 不透明度,0.0透明 - 1.0不透明
WATERMARK_ANGLE = 30                    # 水印旋转角度(度)
WATERMARK_SPACING = 180                 # 平铺水印之间的间隔(像素)

# 2. 字体文件(可选)
# 如果使用自定义字体,请将字体文件(.ttf/.otf)放在同目录,并在此指定文件名
# 如果留空或字体文件不存在,将使用系统默认字体
CUSTOM_FONT_FILE = "simhei.ttf"  # 例如使用黑体

# 3. 文件与文件夹路径
SOURCE_FOLDER = "./待处理PDF"           # 放原始PDF的文件夹
OUTPUT_FOLDER = "./已处理PDF"           # 输出文件夹(会自动创建)
TEMP_WATERMARK_PDF = "./temp_watermark.pdf"  # 临时水印PDF文件(处理完后可删除)

# 4. 页面尺寸 (一般无需修改)
# 标准A4尺寸:595 x 842 points
PAGE_WIDTH = 595
PAGE_HEIGHT = 842
DPI = 100                               # 图像DPI,影响清晰度,100-150通常足够

# ==================== 核心函数区 ====================
# 以下代码无需修改,除非你有特殊定制需求

def create_watermark_image():
    """根据配置创建水印图片"""
    try:
        if CUSTOM_FONT_FILE and os.path.exists(CUSTOM_FONT_FILE):
            font = ImageFont.truetype(CUSTOM_FONT_FILE, FONT_SIZE)
        else:
            # 尝试几种常见字体,最后回退到默认字体
            for fallback_font in ["arial.ttf", "msyh.ttc"]:
                try:
                    font = ImageFont.truetype(fallback_font, FONT_SIZE)
                    break
                except IOError:
                    continue
            else:
                font = ImageFont.load_default()
                print("提示:使用系统默认字体,水印效果可能较粗。建议提供字体文件。")
    except Exception as e:
        print(f"字体加载失败,使用默认字体: {e}")
        font = ImageFont.load_default()
    
    # 测量文字尺寸
    temp_img = Image.new('RGBA', (1, 1))
    temp_draw = ImageDraw.Draw(temp_img)
    bbox = temp_draw.textbbox((0, 0), WATERMARK_TEXT, font=font)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]
    
    # 创建透明底图
    margin = 30
    img_size = (text_width + margin, text_height + margin)
    wm_img = Image.new('RGBA', img_size, (255, 255, 255, 0))
    draw = ImageDraw.Draw(wm_img)
    
    # 绘制文字(完全不透明)
    text_pos = ((img_size[0] - text_width)//2 - bbox[0],
                (img_size[1] - text_height)//2 - bbox[1])
    draw.text(text_pos, WATERMARK_TEXT, font=font, fill=(*FONT_COLOR, 255))
    
    # 整体调整透明度
    alpha = wm_img.split()[3]
    alpha = ImageEnhance.Brightness(alpha).enhance(OPACITY)
    wm_img.putalpha(alpha)
    
    return wm_img

def generate_full_page_watermark_pdf():
    """生成平铺的完整水印PDF页"""
    base_img = create_watermark_image()
    
    # 转换为像素尺寸
    page_w_px = int(PAGE_WIDTH * DPI / 72)
    page_h_px = int(PAGE_HEIGHT * DPI / 72)
    final_page = Image.new('RGBA', (page_w_px, page_h_px), (255, 255, 255, 0))
    
    # 平铺画布
    wm_w, wm_h = base_img.size
    canvas_size = int(math.sqrt(page_w_px**2 + page_h_px**2))
    canvas = Image.new('RGBA', (canvas_size, canvas_size), (255, 255, 255, 0))
    
    y = 0
    row_offset = 0
    while y < canvas_size:
        x = -row_offset
        while x < canvas_size:
            canvas.paste(base_img, (x, y), base_img)
            x += wm_w + WATERMARK_SPACING
        y += wm_h + WATERMARK_SPACING
        row_offset = (wm_w + WATERMARK_SPACING) // 2 if row_offset == 0 else 0
    
    # 旋转并居中
    canvas = canvas.rotate(WATERMARK_ANGLE, expand=True, fillcolor=(255, 255, 255, 0))
    rot_w, rot_h = canvas.size
    paste_x = (page_w_px - rot_w) // 2
    paste_y = (page_h_px - rot_h) // 2
    final_page.paste(canvas, (paste_x, paste_y), canvas)
    
    final_page.save(TEMP_WATERMARK_PDF, "PDF", resolution=DPI, save_all=True)
    print(f"水印模板已生成: {TEMP_WATERMARK_PDF}")

def process_single_pdf(input_path, output_path):
    """处理单个PDF文件"""
    watermark = PdfReader(TEMP_WATERMARK_PDF).pages[0]
    reader = PdfReader(input_path)
    writer = PdfWriter()
    
    for page in reader.pages:
        page.merge_page(watermark)
        writer.add_page(page)
    
    with open(output_path, 'wb') as f:
        writer.write(f)

def main():
    """主函数:批量处理"""
    print("="*50)
    print("PDF批量水印工具启动")
    print(f"水印文字: {WATERMARK_TEXT}")
    print(f"源文件夹: {SOURCE_FOLDER}")
    print("="*50)
    
    # 1. 检查源文件夹
    if not os.path.exists(SOURCE_FOLDER):
        print(f"错误:源文件夹 '{SOURCE_FOLDER}' 不存在。")
        return
    
    # 2. 生成水印PDF模板
    print("\n[步骤1/3] 生成水印模板...")
    generate_full_page_watermark_pdf()
    
    # 3. 准备输出文件夹
    Path(OUTPUT_FOLDER).mkdir(parents=True, exist_ok=True)
    
    # 4. 查找并处理PDF
    pdf_files = [f for f in os.listdir(SOURCE_FOLDER) if f.lower().endswith('.pdf')]
    if not pdf_files:
        print(f"在 '{SOURCE_FOLDER}' 中未找到PDF文件。")
        return
    
    print(f"\n[步骤2/3] 找到 {len(pdf_files)} 个PDF文件,开始处理...")
    success = 0
    for pdf_file in pdf_files:
        try:
            in_path = os.path.join(SOURCE_FOLDER, pdf_file)
            out_name = f"{Path(pdf_file).stem}_watermarked.pdf"
            out_path = os.path.join(OUTPUT_FOLDER, out_name)
            
            process_single_pdf(in_path, out_path)
            print(f"  ✓ {pdf_file}")
            success += 1
        except Exception as e:
            print(f"  ✗ {pdf_file} (失败: {e})")
    
    # 5. 清理与报告
    print(f"\n[步骤3/3] 处理完成!")
    print(f"成功: {success} 个,失败: {len(pdf_files)-success} 个")
    print(f"输出目录: {os.path.abspath(OUTPUT_FOLDER)}")
    
    # 可选:删除临时水印PDF文件
    if os.path.exists(TEMP_WATERMARK_PDF):
        os.remove(TEMP_WATERMARK_PDF)
        print(f"临时文件已清理。")

if __name__ == "__main__":
    main()

如何使用这个脚本?

  1. 将上面的代码完整复制,保存为一个新文件,例如 pdf_watermark_tool.py
  2. 在脚本开头的“用户配置区”,根据你的需求修改 WATERMARK_TEXTOPACITY 等参数。
  3. 在你的项目文件夹里创建一个名为 待处理PDF 的文件夹,把所有需要加水印的PDF文件放进去。
  4. 打开命令行,导航到脚本所在目录,运行:python pdf_watermark_tool.py
  5. 稍等片刻,所有处理好的文件就会出现在 已处理PDF 文件夹中,文件名后面会自动加上 _watermarked

4.2 常见问题与解决方案

即使有了完美的脚本,在实际操作中也可能遇到一些小麻烦。这里是我总结的几个常见坑点及其解决方法。

问题1:处理后的PDF文件变得特别大?

注意:pypdf 在合并页面时,可能会重复嵌入字体等资源,导致文件体积膨胀。解决方法是在写入前尝试压缩内容流(但注意,过度压缩可能损害某些PDF的特殊功能)。

# 在 process_single_pdf 函数的循环中,添加压缩选项
for page in reader.pages:
    page.merge_page(watermark)
    # 尝试压缩内容流,减小文件体积
    page.compress_content_streams()  # 就是这一行
    writer.add_page(page)

问题2:水印位置不对,或者只出现在第一页? 这通常是由于水印PDF的页面尺寸与目标PDF不匹配造成的。我们的脚本默认按A4尺寸生成水印。如果你的PDF是Letter尺寸或其他规格,需要调整 PAGE_WIDTHPAGE_HEIGHT

  • A4: 595 x 842 points
  • Letter: 612 x 792 points 你可以用Adobe Acrobat或在线工具查看PDF的属性,找到其页面尺寸。

问题3:水印太淡或太浓? 直接调整 OPACITY 参数。0.05-0.15通常适用于背景平铺水印,0.2-0.4适用于角落的单个标识水印。如果文字颜色太浅,可以修改 FONT_COLOR,使用更深的RGB值,如 (60, 60, 60)

问题4:我想用公司Logo图片做水印,而不是文字? 当然可以!你需要稍微修改 create_watermark_image 函数。思路是:用 PIL 打开Logo图片,调整其大小和透明度,然后返回这个图片对象。

def create_logo_watermark(logo_path, target_height=100, opacity=0.2):
    """从Logo图片创建水印"""
    logo = Image.open(logo_path).convert("RGBA")  # 确保是RGBA模式,支持透明
    
    # 按高度等比缩放Logo
    ratio = target_height / logo.height
    new_width = int(logo.width * ratio)
    logo = logo.resize((new_width, target_height), Image.Resampling.LANCZOS)
    
    # 调整透明度
    alpha = logo.split()[3]
    alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
    logo.putalpha(alpha)
    
    return logo

# 然后在配置区,将生成base_img的代码替换为:
# base_img = create_logo_watermark("company_logo.png", target_height=80, opacity=0.15)

4.3 效率优化:处理超多文件

如果你要处理成千上万个文件,基础的循环可能有点慢。一个简单的优化是使用Python的 concurrent.futures 模块进行多线程处理,充分利用多核CPU。

import concurrent.futures

def batch_process_parallel(source_folder, output_folder, watermark_pdf_path, max_workers=4):
    """使用线程池并行处理PDF文件"""
    Path(output_folder).mkdir(parents=True, exist_ok=True)
    pdf_files = [f for f in os.listdir(source_folder) if f.lower().endswith('.pdf')]
    
    def process_one_file(pdf_file):
        input_path = os.path.join(source_folder, pdf_file)
        output_path = os.path.join(output_folder, f"{Path(pdf_file).stem}_watermarked.pdf")
        add_watermark_to_pdf(input_path, output_path, watermark_pdf_path)
        return pdf_file
    
    print(f"开始并行处理 {len(pdf_files)} 个文件 (线程数: {max_workers})...")
    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(process_one_file, f): f for f in pdf_files}
        for future in concurrent.futures.as_completed(futures):
            pdf_file = futures[future]
            try:
                future.result()
                print(f"  完成: {pdf_file}")
            except Exception as e:
                print(f"  失败: {pdf_file} - {e}")

max_workers 设置为你的CPU核心数(通常4-8),处理速度会有显著提升。不过要注意,PDF处理是I/O密集型任务,线程数并非越多越好,过多的线程反而可能因磁盘读写争用而降低效率。

最后,别忘了这一切的起点:先在小批量文件上测试。调整好水印样式、位置、透明度,确认效果满意后,再放开手脚进行全量批处理。这个脚本已经成为我办公工具箱里最得力的助手之一,每次看到它几分钟就干完以前需要半天的手工活,都会觉得那点学习成本投入得太值了。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐