16:PDF 强力缝合------将几十个 PDF 合并为单个文档并添加页码
第三阶段:PDF 与图片处理(16-22)
场景引入
年终汇报需要将 15 份部门报告(PDF 格式)合并成一个总文件,并且添加统一的页码。手动操作需要打开每个 PDF,复制页面,粘贴到总文件------不仅慢,而且可能丢失格式。
本节教你用 Python 一行代码完成合并,还能自动添加页码。
技术原理
使用 PyPDF2 或 pypdf 库操作 PDF:
多个 PDF 文件
↓
PdfMerger 逐个追加
↓
添加页码(可选)
↓
输出合并后的单个 PDF
合并方式
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 简单拼接 | 按文件顺序追加 | 章节报告合并 |
| 指定顺序 | 按列表顺序合并 | 按部门/日期排序 |
| 插入页码 | 在每页底部添加页码 | 正式文档 |
| 添加书签 | 在每个文件开始处添加书签 | 方便跳转 |
环境准备
bash
pip install pypdf reportlab
pypdf:PDF 合并核心库(PyPDF2 的继任者)reportlab:用于添加页码水印
完整代码
python
import os
from pathlib import Path
from pypdf import PdfReader, PdfWriter, PdfMerger
# ==================== 方案 1:简单合并 ====================
def merge_pdfs_simple(pdf_files, output_file="合并结果.pdf"):
"""
将多个 PDF 文件按顺序合并为一个
参数:
pdf_files: PDF 文件路径列表
output_file: 输出文件名
"""
merger = PdfMerger()
for pdf_file in pdf_files:
if os.path.exists(pdf_file):
merger.append(pdf_file)
print(f"已添加: {os.path.basename(pdf_file)}")
else:
print(f"[跳过] 文件不存在: {pdf_file}")
merger.write(output_file)
merger.close()
print(f"\n合并完成: {output_file}")
# ==================== 方案 2:带书签的合并 ====================
def merge_pdfs_with_bookmarks(pdf_files, output_file="合并结果_带书签.pdf"):
"""
合并 PDF 并添加书签(每个文件一个书签)
"""
merger = PdfMerger()
for pdf_file in pdf_files:
if not os.path.exists(pdf_file):
continue
bookmark_name = Path(pdf_file).stem
merger.append(pdf_file)
# 获取此文件在合并文件中的起始页码
total_pages = sum(PdfReader(f).get_num_pages() for f in pdf_files[:pdf_files.index(pdf_file) + 1] if os.path.exists(f))
start_page = total_pages - PdfReader(pdf_file).get_num_pages()
# 添加书签
merger.add_outline_item(bookmark_name, start_page)
print(f"已添加书签: {bookmark_name} (第 {start_page + 1} 页)")
merger.write(output_file)
merger.close()
print(f"\n合并完成: {output_file}")
# ==================== 方案 3:批量合并文件夹中的所有 PDF ====================
def merge_folder_pdfs(folder_path, output_file="合并结果.pdf", sorted_by='name'):
"""
自动合并文件夹中的所有 PDF
参数:
folder_path: PDF 文件所在文件夹
output_file: 输出文件名
sorted_by: 排序方式 'name'(文件名) / 'date'(修改日期)
"""
folder = Path(folder_path)
pdf_files = sorted(
folder.glob('*.pdf'),
key=lambda f: f.stat().st_mtime if sorted_by == 'date' else f.name
)
if not pdf_files:
print(f"文件夹 {folder_path} 中没有 PDF 文件!")
return
print(f"找到 {len(pdf_files)} 个 PDF 文件:")
for f in pdf_files:
print(f" - {f.name}")
merge_pdfs_with_bookmarks(
[str(f) for f in pdf_files],
output_file
)
# ==================== 方案 4:添加页码 ====================
def add_page_numbers(input_pdf, output_file="带页码版本.pdf"):
"""
为 PDF 添加页码(每页底部居中)
"""
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
import io
reader = PdfReader(input_pdf)
writer = PdfWriter()
total_pages = len(reader.pages)
for i, page in enumerate(reader.pages):
# 创建页码水印
packet = io.BytesIO()
c = canvas.Canvas(packet, pagesize=A4)
width, height = A4
# 绘制页码
c.setFont("Helvetica", 9)
page_number = f"- {i + 1} -"
text_width = c.stringWidth(page_number, "Helvetica", 9)
c.drawString((width - text_width) / 2, 30, page_number)
c.save()
packet.seek(0)
# 叠加水印到原页面
watermark = PdfReader(packet)
page.merge_page(watermark.pages[0])
writer.add_page(page)
with open(output_file, 'wb') as f:
writer.write(f)
print(f"页码已添加: {output_file}")
# ==================== 使用示例 ====================
if __name__ == "__main__":
# 方案 1:手动指定文件列表
# pdf_files = [
# "第1章.pdf",
# "第2章.pdf",
# "第3章.pdf",
# ]
# merge_pdfs_simple(pdf_files, "完整手册.pdf")
# 方案 2:批量合并文件夹
merge_folder_pdfs(
r"D:\部门报告",
output_file="年度总报告.pdf",
sorted_by='name'
)
# 方案 3:添加页码
# add_page_numbers("年度总报告.pdf", "年度总报告_带页码.pdf")
常见问题
Q1:合并后中文显示为乱码?
reportlab 默认不支持中文。需要使用中文字体:
python
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont('SimSun', 'C:/Windows/Fonts/simsun.ttc'))
c.setFont('SimSun', 9)
Q2:合并后文件很大?
PDF 中可能包含大量内嵌字体和图片。可以尝试压缩:
bash
pip install pypdf
pypdf 在合并时不会重复嵌入相同字体,文件体积通常比预期小。
Q3:加密的 PDF 无法合并?
需要先解密:
python
reader = PdfReader("encrypted.pdf")
if reader.is_encrypted:
reader.decrypt("password")
总结
| 功能 | 核心类/函数 | 说明 |
|---|---|---|
| 合并 | PdfMerger() |
追加多个 PDF |
| 书签 | add_outline_item() |
添加跳转标记 |
| 页码 | reportlab + merge_page() |
底部叠加页码水印 |
| 排序 | sorted(glob(), key=...) |
按名称或日期排序 |
本节掌握了 PDF 批量合并与页码添加的技能。
下一节预告:17:PDF 暴力拆分------按页数或指定范围提取 PDF 核心页面!