使用Python合并PDF文件并添加自定义目录及页脚

如何用Python合并PDF文件并添加自定义目录及页脚

在处理文档时,我们经常遇到需要合并多个PDF文件并添加目录及页脚的情况。本文将介绍如何使用Python,特别是PyPDF2reportlab库来实现这一功能。我们将通过一个实用的示例来演示整个过程,包括如何动态创建目录页和在每页底部添加页码。

步骤一:准备环境

首先,确保你的环境中安装了PyPDF2reportlab库。如果还没有安装,可以通过以下命令进行安装:

bash 复制代码
pip install PyPDF2 reportlab

步骤二:合并PDF文件

我们首先定义一个函数add_catalog_page(bookmarks)来创建一个包含目录的PDF页面。这个目录基于传入的书签列表动态生成,每个书签对应一个文档标题和其开始的页码。

接下来,使用create_footer_page(footer_text)函数为每个PDF页面添加自定义页脚。这个函数通过绘制一个足够大的白色矩形来覆盖原有的页码,然后在指定位置添加新的页码文本。

步骤三:处理特定文件

在合并多个PDF文件之前,我们可能需要调整它们的顺序。在本例中,我们将特定的文件(如"第一篇.pdf")移动到文件列表的开头,以确保它作为合并后PDF文档的第一个文件出现。

步骤四:合并并添加目录与页脚

遍历每个PDF文件,读取其页面,并将它们添加到一个列表中。同时,我们记录每个文件的标题和起始页码,用于生成目录页。

在所有页面都处理完毕后,我们首先将目录页添加到最终的PDF文档中,然后为每个页面添加页脚,并将它们一一加入到文档中。

步骤五:保存最终文档

完成所有页面的处理和添加后,我们将这些页面写入到一个新的PDF文件中,完成了合并、添加目录和页脚的整个过程。

结语

通过上述步骤,我们展示了如何使用Python处理PDF文件,包括合并多个PDF文件、动态创建目录页和在每页底部添加自定义页脚。这种方法不仅提高了文档处理的自动化程度,也为管理和阅读PDF文档提供了便利。希望这篇文章能帮助到需要进行PDF文档处理的读者。

完整代码

python 复制代码
import io
import os

from PyPDF2 import PdfReader
from PyPDF2 import PdfWriter
from reportlab.lib.pagesizes import letter
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.pdfgen import canvas


def create_footer_page(footer_text):
    packet = io.BytesIO()
    c = canvas.Canvas(packet, pagesize=letter)
    width, height = letter  # letter页面的宽度和高度
    font_name = "Helvetica"  # 使用的字体
    font_size = 12  # 字体大小
    cover_height = font_size + 4  # 覆盖区域的高度稍大于字体大小,以确保完全覆盖原有页码
    cover_y_position = 28  # 覆盖区域的Y位置,根据需要进行调整以确保覆盖原有页码

    # 计算文本宽度和起始X位置以居中文本
    text_width = c.stringWidth(footer_text, font_name, font_size)
    text_start_position = (width - text_width) / 2

    # 绘制一个足够大的白色矩形以覆盖原有页码
    c.setFillColorRGB(1, 1, 1)  # 设置填充颜色为白色
    c.rect(0, cover_y_position, width, cover_height, stroke=False, fill=True)

    # 在页脚区域居中添加文本,高度可以根据需要调整
    c.setFont(font_name, font_size)  # 设置字体和大小
    c.setFillColorRGB(0, 0, 0)  # 设置文本颜色为黑色
    c.drawString(text_start_position, 32, footer_text)  # 绘制居中的页脚文本

    c.save()
    packet.seek(0)
    return PdfReader(packet)


def add_catalog_page(bookmarks):
    packet = io.BytesIO()
    c = canvas.Canvas(packet, pagesize=letter)
    width, height = letter
    top_margin = 60  # 顶部留白增加
    bottom_margin = 60  # 底部留白增加
    y_position = height - top_margin  # 根据顶部留白调整初始y_position
    c.setFont("Helvetica-Bold", 16)  # 设置标题的字体和大小
    c.drawString(280, y_position, "Directory")  # 在顶部绘制标题"目录"
    y_position -= 30  # 更新y_position以为目录项留出空间

    c.setFont("Helvetica", 12)  # 设置目录项的字体和大小
    left_margin = 72
    right_margin = width - 72
    dot_space = 5  # 点间隔可调整
    different_title_spacing = 25  # 不同标题之间的间隔
    same_title_line_spacing = 15  # 同一个标题换行的间隔
    split_ratio = 0.9  # 定义分割点的位置比例

    for title, page_number in bookmarks:
        available_width = right_margin - left_margin - dot_space * 2  # 计算可用宽度
        title_width = stringWidth(title, "Helvetica", 12)
        page_number_str = str(page_number)
        page_number_width = stringWidth(page_number_str, "Helvetica", 12)

        # 判断标题是否需要分割
        if title_width > available_width * split_ratio:
            # 寻找分割点
            split_title = title
            while stringWidth(split_title + "-", "Helvetica", 12) > available_width * split_ratio:
                split_title = split_title[:-1]
            split_title += "-"
            c.drawString(left_margin, y_position, split_title)
            y_position -= same_title_line_spacing  # 分割后的第二行位置向下调整,间隔小一点
            title = title[len(split_title) - 1:]

        # 绘制标题
        c.drawString(left_margin, y_position, title)

        # 绘制页码
        c.drawRightString(right_margin, y_position, page_number_str)

        # 绘制点线
        dot_line_start = left_margin + stringWidth(title, "Helvetica", 12) + 10
        dot_line_end = right_margin - page_number_width - 10
        current_position = dot_line_start

        while current_position < dot_line_end:
            c.drawString(current_position, y_position, ".")
            current_position += dot_space

        y_position -= different_title_spacing  # 移到下一个标题,间隔大一点
        if y_position < bottom_margin:  # 根据底部留白调整翻页判断
            c.showPage()
            y_position = height - top_margin  # 重置y_position时也考虑顶部留白
            c.setFont("Helvetica", 12)  # 确保新页面开始时使用正确的字体和大小

    c.save()
    packet.seek(0)
    return PdfReader(packet)


# 读取PDF/路径下所有.pdf为后缀的文件
pdf_dir = '我的论文集/'
pdf_files = sorted([f for f in os.listdir(pdf_dir) if f.endswith('.pdf')])

# 找到特定文件并将其移动到列表的开头
specific_file = "第一篇.pdf"
if specific_file in pdf_files:
    pdf_files.insert(0, pdf_files.pop(pdf_files.index(specific_file)))
    #print(pdf_files)

# 存储所有处理后的页面,以便后续添加页脚
all_pages = []
bookmarks = []
total_pages = 0

# 首先处理每个PDF文件,但不立即添加页脚
for filename in pdf_files:
    reader = PdfReader(os.path.join(pdf_dir, filename))
    bookmarks.append((filename.replace('.pdf', ''), total_pages + 1))
    for page in reader.pages:
        all_pages.append(page)
        total_pages += 1

# 创建目录页并添加到最终PDF
writer = PdfWriter()
catalog_pdf = add_catalog_page(bookmarks)
for page in catalog_pdf.pages:
    writer.add_page(page)

# 为每页添加页脚,并将页面添加到最终的PDF中
current_page_number = 1  # 从目录页之后的第一页开始计数页码
catalog_pages_count = len(catalog_pdf.pages)  # 计算目录页数量

for page in all_pages:
    # 不再需要在页码中加上目录页的数量
    footer_pdf = create_footer_page(f"Page number: {current_page_number}")
    page.merge_page(footer_pdf.pages[0])
    writer.add_page(page)
    current_page_number += 1

# 保存最终的PDF
output_pdf_path = "MergePapers.pdf"
with open(output_pdf_path, "wb") as f_out:
    writer.write(f_out)
相关推荐
luoluoal2 分钟前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea7 分钟前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
小游鱼KF10 分钟前
Spring学习前置知识
java·学习·spring
扎克begod14 分钟前
JAVA并发编程系列(9)CyclicBarrier循环屏障原理分析
java·开发语言·python
青灯文案115 分钟前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
我就是程序猿25 分钟前
tomcat的配置
java·tomcat
阳光阿盖尔31 分钟前
EasyExcel的基本使用——Java导入Excel数据
java·开发语言·excel
二十雨辰33 分钟前
[苍穹外卖]-12Apache POI入门与实战
java·spring boot·mybatis
程序员皮皮林33 分钟前
开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)
java·pdf·开源·apache
蔚一34 分钟前
Java设计模式—面向对象设计原则(三) -----> 依赖倒转原则DIP(完整详解,附有代码+案例)
java·开发语言·设计模式·intellij-idea·依赖倒置原则