从零开始构建PDF文档生成器(二)- 添加页眉页脚

在上一篇文章中,我们成功搭建了PDF文档生成器的基础框架,并生成了第一个能正确显示中文的PDF文档。如果你还没看过第一篇文章,建议先去了解一下基础框架的搭建过程。

在本篇文章中,我们将为PDF文档添加页眉和页脚,让你的文档看起来更专业。页眉页脚虽然看起来简单,但它们包含了不少有用的信息,比如文档标题、页码、制表人、打印时间等。

页面设置管理器完善

在添加页眉页脚之前,我们需要先完善页面设置管理器,这样可以更好地控制页面的布局和边距。

页面尺寸和边距管理

在PDF文档中,页面尺寸和边距是非常重要的概念。我们不能让内容贴着页面边缘,那样看起来太局促了。让我们来实现一个页面设置管理器:

python 复制代码
# src/utils/page_settings.py
"""
页面设置模块
负责管理页面尺寸、边距等设置
"""

from reportlab.lib.pagesizes import A4


class PageSettings:
    """页面设置管理器"""
    
    def __init__(self, config=None):
        """
        初始化页面设置
        
        Args:
            config: 配置对象
        """
        self.config = config or {}
        self.page_size = A4
        # 默认边距设置(单位:点,1英寸=72点)
        self.margin_top = 72      # 上边距1英寸
        self.margin_bottom = 72   # 下边距1英寸
        self.margin_left = 72     # 左边距1英寸
        self.margin_right = 72    # 右边距1英寸
        
        # 从配置中读取自定义边距
        self._load_margins_from_config()
    
    def _load_margins_from_config(self):
        """从配置文件中加载边距设置"""
        page_config = self.config.get('page', {})
        self.margin_top = page_config.get('margin_top', self.margin_top)
        self.margin_bottom = page_config.get('margin_bottom', self.margin_bottom)
        self.margin_left = page_config.get('margin_left', self.margin_left)
        self.margin_right = page_config.get('margin_right', self.margin_right)
    
    def get_content_area(self):
        """
        获取内容区域的尺寸和位置
        
        Returns:
            tuple: (x, y, width, height) 内容区域的左下角坐标和宽高
        """
        page_width, page_height = self.page_size
        x = self.margin_left
        y = self.margin_bottom
        width = page_width - self.margin_left - self.margin_right
        height = page_height - self.margin_top - self.margin_bottom
        return x, y, width, height
    
    def get_header_position(self):
        """
        获取页眉位置
        
        Returns:
            tuple: (x, y) 页眉的坐标位置
        """
        page_width, page_height = self.page_size
        x = self.margin_left
        y = page_height - self.margin_top + 10  # 页眉在上边距上方10点
        return x, y
    
    def get_footer_position(self):
        """
        获取页脚位置
        
        Returns:
            tuple: (x, y) 页脚的坐标位置
        """
        x = self.margin_left
        y = self.margin_bottom - 20  # 页脚在下边距下方20点
        return x, y

这个页面设置管理器可以帮助我们精确控制页面的各个区域。通过配置文件,我们可以灵活调整边距大小,而不需要修改代码。

内容区域计算

有了页面设置管理器,我们就可以计算出内容区域的具体位置和大小。这对于后续放置内容非常重要。

在PDF的坐标系统中,页面的左下角是坐标原点(0,0),x轴向右为正,y轴向上为正。内容区域的计算逻辑是:

  1. 起始位置(左下角坐标):

    • x坐标 = 左边距
    • y坐标 = 下边距
  2. 区域尺寸

    • 宽度 = 页面总宽度 - 左边距 - 右边距
    • 高度 = 页面总高度 - 上边距 - 下边距

简单来说,就是确定起始位置(内容区域的左下角),然后确定结束位置(内容区域的右上角),从而定义出内容区域的大小和位置。

页眉页脚实现

现在让我们来实现页眉和页脚的具体功能。

页眉内容和样式设计

页眉通常包含文档的标题或公司名称等信息。我们来看看如何实现:

python 复制代码
# src/utils/page_elements.py
"""
页面元素模块
负责处理页眉、页脚等页面元素
"""

from datetime import datetime
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas


class PageElements:
    """页面元素管理器"""
    
    def __init__(self, canvas_obj, page_settings, config=None):
        """
        初始化页面元素管理器
        
        Args:
            canvas_obj: Canvas对象
            page_settings: 页面设置对象
            config: 配置对象
        """
        self.canvas = canvas_obj
        self.page_settings = page_settings
        self.config = config or {}
        # 获取内容区域
        self.content_x, self.content_y, self.content_width, self.content_height = \
            self.page_settings.get_content_area()
    
    def draw_header(self):
        """绘制页眉"""
        # 从配置中获取页眉配置
        header_config = self.config.get('header',{})
        header_text = header_config.get('text', '')
        font_size = header_config.get('font_size', 12)
        
        # 设置字体和大小
        self.canvas.setFont('SimHei', font_size)

        # 获取页面位置
        x, y = self.page_settings.get_header_position()

        # 绘制居中页眉文本
        page_width, _ = self.page_settings.page_size
        
        # 计算文本宽度以实现居中
        text_width = self.canvas.stringWidth(header_text, 'SimHei', font_size)
        center_x = (self.content_x + (self.content_x + self.content_width)) / 2 - text_width / 2
        self.canvas.drawString(center_x, y, header_text)
        
        # 在页眉下方画一条线
        line_y = y - 5
        self.canvas.line(self.content_x, line_y, self.content_x + self.content_width, line_y)
    
    def draw_footer(self, page_number=1, total_pages=1):
        """
        绘制页脚
        
        Args:
            page_number: 当前页码
            total_pages: 总页数
        
        """
        # 从配置中获取页脚设置
        footer_config = self.config.get("footer", {})
        author = footer_config.get("author", "")
        font_size = footer_config.get("font_size", 8)

        # 设置字体和大小
        self.canvas.setFont('SimHei', font_size)

        # 获取页脚位置
        x, y = self.page_settings.get_footer_position()
        
        # 绘制页码
        page_info = f"第 {page_number} 页 / 共 {total_pages} 页"

        # 绘制页码信息
        self.canvas.drawString(x, y, page_info)

        # 绘制打印时间
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.canvas.drawString(x+150, y, f"打印时间:{current_time}")

        # 计算制表人的x坐标,使其靠右对齐
        page_width, _ = A4
        page_info_x = page_width - self.page_settings.margin_right - 100
        self.canvas.drawString(page_info_x, y, f"制表人:{author}")

        # 在页脚上方画一条线
        line_y = y + 15
        self.canvas.line(x, line_y, x + self.content_width, line_y)

页脚信息(页码、打印时间、制表人)

页脚通常包含页码、打印时间和制表人等信息。我们可以通过配置文件来设置这些信息,让文档更加个性化。

Canvas绘制技术详解

在ReportLab中,Canvas对象是我们绘制PDF内容的主要工具。它提供了丰富的绘图方法,比如:

  • drawString(x, y, text): 在指定位置绘制文本
  • line(x1, y1, x2, y2): 绘制直线
  • setFont(font_name, font_size): 设置字体和大小

这些方法的坐标系统是以页面左下角为原点(0,0),x轴向右,y轴向上。

配置文件扩展

为了让页眉页脚更加灵活,我们需要扩展配置文件,添加相关的配置项:

json 复制代码
{
  "output": {
    "filename": "我的第一个PDF文档",
    "add_timestamp": true
  },
  "page": {
    "margin_top": 80,
    "margin_bottom": 80,
    "margin_left": 72,
    "margin_right": 72
  },
  "header": {
    "text": "公司销售报表",
    "font_size": 12
  },
  "footer": {
    "author": "吴邪",
    "font_size": 10
  },
  "fonts": {
    "chinese_font": "黑体"
  }
}

修改主程序

现在我们需要修改主程序,让它支持页眉页脚的绘制:

python 复制代码
# src/core/main.py
"""
主程序入口
负责初始化和协调各模块工作
"""

import os
import sys
from datetime import datetime
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4

# 添加项目根目录到Python路径
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, project_root)

from src.config.config_manager import ConfigManager
from src.utils.font_handler import FontHandler
from src.utils.page_settings import PageSettings
from src.utils.page_elements import PageElements


def create_pdf_with_header_footer(config):
    """创建带页眉页脚的PDF文档"""
    # 获取输出配置
    output_config = config.get('output', {})
    filename = output_config.get('filename', 'document')
    
    # 如果需要添加时间戳
    if output_config.get('add_timestamp', False):
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{filename}_{timestamp}"
    
    # 确保输出目录存在
    output_dir = "output"
    os.makedirs(output_dir, exist_ok=True)
    
    # 构建完整输出路径
    output_path = os.path.join(output_dir, f"{filename}.pdf")
    
    # 创建PDF文档
    c = canvas.Canvas(output_path, pagesize=A4)
    width, height = A4
    
    # 初始化页面设置
    page_settings = PageSettings(config)
    
    # 注册并使用中文字体
    font_handler = FontHandler(config)
    chinese_font = font_handler.setup_chinese_font()
    
    # 初始化页面元素管理器
    page_elements = PageElements(c, page_settings, config)
    
    # 绘制页眉页脚
    page_elements.draw_header()
    page_elements.draw_footer(page_number=1, total_pages=1)
    
    # 保存PDF
    c.save()
    
    return output_path


def main():
    """主函数"""
    try:
        # 构建配置文件的完整路径
        project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        config_path = os.path.join(project_root, "configs", "document_config.json")     
        config_manager = ConfigManager(config_path)
        config = config_manager.get_config()
        
        print("配置文件加载成功!")
        print(f"配置内容: {config}")
        
        # 创建PDF文档
        output_file = create_pdf_with_header_footer(config)
        print(f"PDF文档已生成: {output_file}")
        
    except Exception as e:
        print(f"程序运行出错: {str(e)}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()

效果展示

总结与展望

通过这篇文章,我们完成了PDF文档生成器的页眉页脚功能,在下一篇文章中,我们将为PDF文档添加主副标题。

相关推荐
Source.Liu1 天前
【pdf-rs】color.rs 文件解析
pdf
ceffans1 天前
PDF文档中表格以及形状解析-后续处理(线段生成最小多边形)
c++·windows·算法·pdf
Source.Liu1 天前
【printpdf】color.rs 文件解析
rust·pdf
乘风!2 天前
前端Jquery,后端Java实现预览Word、Excel、PPT,pdf等文档
pdf·word·excel·jquery
我有一棵树2 天前
浏览器使用 <embed> 标签预览 PDF 的原理
pdf·embed
蜀中廖化2 天前
小技巧:ipynb转pdf
pdf·小工具·python to pdf
Eiceblue2 天前
使用 Python 向 PDF 添加附件与附件注释
linux·开发语言·vscode·python·pdf
蛋王派2 天前
本地部署DeepSeek-OCR:打造高效的PDF文字识别服务
人工智能·自然语言处理·pdf·ocr
Iloveskr2 天前
markdown转为pdf导出
java·pdf