在上一篇文章中,我们成功搭建了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轴向上为正。内容区域的计算逻辑是:
-
起始位置(左下角坐标):
- x坐标 = 左边距
- y坐标 = 下边距
-
区域尺寸:
- 宽度 = 页面总宽度 - 左边距 - 右边距
- 高度 = 页面总高度 - 上边距 - 下边距
简单来说,就是确定起始位置(内容区域的左下角),然后确定结束位置(内容区域的右上角),从而定义出内容区域的大小和位置。
页眉页脚实现
现在让我们来实现页眉和页脚的具体功能。
页眉内容和样式设计
页眉通常包含文档的标题或公司名称等信息。我们来看看如何实现:
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文档添加主副标题。