PDF 在分发、归档和正式输出场景里几乎离不开页眉页脚,比如公司名称、文档标题、章节名和页码。手工处理几页还能接受,但一旦文档页数多、生成频率高,最稳妥的办法还是交给代码批量完成。下面就按“先能用、再好维护”的思路,分别介绍用 Python 和 Spire.PDF 给 PDF 添加文本页眉页脚、插入图片、按页面设置不同内容,以及通过模板自动管理页码的方法。
为什么更适合用代码处理页眉页脚
把页眉页脚写进程序,最大的价值不只是省时间,而是让文档输出更稳定。
- 批量处理更直接:遍历所有页面后统一绘制,几十页、上百页的 PDF 也能一次完成。
- 动态内容更容易维护:页码、章节号、标题等内容可以跟着文档状态自动生成,不需要手工修改。
- 样式更统一:字体、颜色、线条、对齐方式都由代码控制,能减少人工操作带来的版式偏差。
环境准备与坐标规则
本文使用的库是 Spire.PDF,先安装依赖:
pip install Spire.PDF
Spire.PDF 提供了 PDF 的创建与编辑能力。对页眉页脚来说,核心思路其实很简单:在页面的 Canvas 上绘制文本、图片和线条。
这里有一个实现层面的关键点需要先记住:PDF 坐标系以页面左下角为原点,单位是点,1 点 = 1/72 英寸。后面无论是写文字、画分隔线,还是放 Logo,最终都要落到这个坐标体系里。
先从最常见的场景开始:逐页添加文本页眉和页脚
如果你的需求是“给现有 PDF 每一页都加上统一页眉和页码”,最直接的做法就是遍历全部页面,然后在每页画布上分别绘制顶部和底部内容。
from spire.pdf.common import *
from spire.pdf import *
doc = PdfDocument()
doc.LoadFromFile("Input.pdf")
margin = doc.PageSettings.Margins
# 定义好画笔、字体和对齐方式
pen = PdfPen(PdfBrushes.get_Black(), 0.75)
font = PdfTrueTypeFont("微软雅黑", 12.0, PdfFontStyle.Italic, True)
brush = PdfBrushes.get_Black()
format = PdfStringFormat(PdfTextAlignment.Right)
format.MeasureTrailingSpaces = True
space = font.Height * 0.75
for i in range(doc.Pages.Count):
page = doc.Pages.get_Item(i)
x = margin.Left
width = page.Canvas.ClientSize.Width - margin.Left - margin.Right
# 页眉:画一条分隔线,再写文本
y = margin.Top - space
page.Canvas.DrawLine(pen, x, y, x + width, y)
page.Canvas.DrawString("内部资料", font, brush, x + width, y - font.Height, format)
# 页脚:同样画一条分隔线,再写页码
y = page.Canvas.ClientSize.Height - margin.Bottom + space
page.Canvas.DrawLine(pen, x, y, x + width, y)
label = "{0} / {1}".format(i + 1, doc.Pages.Count)
page.Canvas.DrawString(label, font, brush, x + width, y + 1, format)
doc.Sa veToFile("HeaderAndFooter.pdf")
doc.Close()
这段代码里有几个容易忽略的点
PdfTrueTypeFont("微软雅黑", 12.0, PdfFontStyle.Italic, True)用来定义字体、字号和样式,其中PdfFontStyle.Italic表示斜体,最后一个布尔值表示是否嵌入字体。PdfStringFormat控制文本对齐方式;这里设置为右对齐,MeasureTrailingSpaces = True可以让尾随空格也参与测量,定位更稳定。- 页眉和页脚并不是随便写到页面边缘,而是结合边距和字体高度来计算位置。代码中的
font.Height * 0.75就是在给分隔线与文本之间留出间距。
这套写法的优点是足够直接,适合给已有 PDF 快速补齐统一的页眉页脚;缺点是如果后面还会不断新增页面,逐页绘制的维护成本会更高一些。
页眉里放 Logo 怎么做
很多企业文档不会只放文字,还会在页眉中加上公司 Logo。这个场景仍然是在画布上处理,只是把文本换成了图片对象。
headerImage = PdfImage.FromFile("Logo.png")
page.Canvas.DrawImage(headerImage, PointF(0.0, 0.0))
DrawImage() 的第二个参数表示图片左上角坐标。如果你还需要控制显示尺寸,可以继续传入宽度和高度,单位同样是点:
page.Canvas.DrawImage(headerImage, 50.0, 20.0,
headerImage.Width / 2.0, headerImage.Height / 2.0)
实际使用时,Logo 通常会和分隔线、标题文字一起组合成完整页眉,因此图片位置最好和页面边距、页眉高度一起统一规划,而不是单独凭经验摆放。
章节文档如何给不同页面设置不同页眉
如果文档包含多个章节,统一写同一个页眉往往不够用。比如首页写“第一章 概述”,下一节改成“第二章 实现”,这时可以直接按页面索引分别处理。
rect = RectangleF(PointF(0.0, 20.0), SizeF(doc.PageSettings.Size.Width, 50.0))
# 第 1 页:红色加粗、居中对齐
font1 = PdfTrueTypeFont("微软雅黑", 15.0, PdfFontStyle.Bold, True)
format1 = PdfStringFormat()
format1.Alignment = PdfTextAlignment.Center
doc.Pages[0].Canvas.DrawString("第一章 概述", font1, PdfBrushes.get_Red(), rect, format1)
# 第 2 页:黑色常规、左对齐
font2 = PdfTrueTypeFont("微软雅黑", 15.0, PdfFontStyle.Regular, True)
format2 = PdfStringFormat()
format2.Alignment = PdfTextAlignment.Left
doc.Pages[1].Canvas.DrawString("第二章 实现", font2, PdfBrushes.get_Black(), rect, format2)
这段做法的关键在于,页眉本身就是绘制在具体页面上的内容,所以完全可以通过 doc.Pages[索引] 精确控制每一页的文字、颜色和对齐方式。
如果你的 PDF 章节很多,通常不会手写每一页的逻辑,而是先整理出章节和页码范围的对应关系,再在循环中批量生成对应页眉。这样更适合长文档或定期更新的资料集。
需要自动页码时,更适合用页面模板
当需求从“给现有页面补内容”升级为“让整个文档统一套用页眉页脚,并且后续新增页面也自动生效”,页面模板 PdfPageTemplateElement 会更合适。
from spire.pdf.common import *
from spire.pdf import *
# 创建文档,设置页面尺寸和边距
doc = PdfDocument()
doc.PageSettings.Size = PdfPageSize.A4()
doc.PageSettings.Margins = PdfMargins(50.0, 50.0, 50.0, 50.0)
pageSize = doc.PageSettings.Size
# 创建页眉模板:绘制 Logo 和分隔线
headerSpace = PdfPageTemplateElement(pageSize.Width, 50.0)
headerSpace.Foreground = False
image = PdfImage.FromFile("Logo.png")
headerSpace.Graphics.DrawImage(image, 50.0, 20.0, image.Width / 2.0, image.Height / 2.0)
pen = PdfPen(PdfBrushes.get_LightGray(), 1.0)
headerSpace.Graphics.DrawLine(pen, 50.0, 45.0, pageSize.Width - 50.0, 45.0)
# 创建页脚模板:使用自动字段绘制“第 x 页,共 y 页”
footerSpace = PdfPageTemplateElement(pageSize.Width, 50.0)
footerSpace.Foreground = False
footerSpace.Graphics.DrawLine(pen, 50.0, 0.0, pageSize.Width - 50.0, 0.0)
number = PdfPageNumberField()
count = PdfPageCountField()
compositeField = PdfCompositeField(
PdfTrueTypeFont("微软雅黑", 10.0, PdfFontStyle.Regular, True),
PdfBrushes.get_Black(), "第 {0} 页,共 {1} 页", [number, count])
compositeField.StringFormat = PdfStringFormat(PdfTextAlignment.Left, PdfVerticalAlignment.Top)
compositeField.Bounds = RectangleF(PointF(50.0, 5.0), SizeF(200.0, 20.0))
widget = PdfGraphicsWidget(compositeField.Ptr)
widget.Draw(footerSpace.Graphics)
# 将模板应用到文档的顶部和底部
doc.Template.Top = headerSpace
doc.Template.Bottom = footerSpace
# 添加页面并保存
doc.Pages.Add()
doc.Sa veToFile("TemplateHeaderFooter.pdf", FileFormat.PDF)
doc.Close()
模板方案为什么更省事
- 页眉页脚与正文分离:模板统一挂在文档级别,后续新增页面时可以自动继承。
- 页码不用手算:
PdfPageNumberField和PdfPageCountField会在渲染时自动替换为当前页码和总页数。 - 格式控制更集中:通过
PdfCompositeField可以把多个自动字段组合进一段完整文本里,例如第 {0} 页,共 {1} 页。
另外,模板对象上的 Foreground = False 也值得留意。这个设置表示模板内容位于页面内容底层,能减少遮挡正文的风险,适合分隔线、页码和不需要压在正文之上的页眉元素。
实用细节:避免遮挡、控制透明度、保证字体一致
页眉页脚看起来只是“小元素”,但真正在批量处理 PDF 时,往往问题都出在细节上。
- 尽量避免覆盖原正文:直接在已有页面上继续绘制时,可能压住原文内容。更稳妥的办法是先通过
page.CreateTemplate()保存原页面,再在新页面中先绘制页眉页脚,之后用PdfGraphicsWidget把原内容绘制到底层。 - 需要更柔和的视觉效果时可设置透明度:调用
page.Canvas.SetTransparency(0.5)后,后续绘制内容会带半透明效果,适合做弱化处理。 - 正式文档建议嵌入字体:
PdfTrueTypeFont构造函数最后一个参数设为True,可以把字体嵌入文档,减少跨设备打开时出现乱码或字体替换的问题。
怎么选方案更合适
如果只是给现有 PDF 临时加统一页眉页脚,逐页遍历绘制最容易上手;如果需要插入企业 Logo,就在同样的画布逻辑里增加图片绘制;如果文档按章节组织,按页面索引或页码范围分别设置页眉会更灵活。
而一旦需求涉及自动页码、持续新增页面,或者想把页眉页脚当成文档级规则长期复用,页面模板通常是更省维护的方案。归根结底,区别就在于:你是在“逐页补内容”,还是在“给整份文档建立统一版式规则”。











