在不少业务里,PDF 不只是'拿来看的文档',它也常被当成数据采集和流转的载体。比起'打印—填写—扫描'这套老办法,交互式 PDF 表单更适合在线收集信息,后续再由程序接着处理。
像问卷、客户登记、入职申请这类场景,只要表单结构设计得还算规整,自动创建和自动填充都能做得比较顺手。这里用 Spire.PDF for Python 走一遍流程:先创建可填写的表单域,再把字段值自动写进去。
安装依赖
这里用到的依赖是:
- Spire.PDF for Python
- plum-dispatch v1.7.4(底层方法分发支持)
安装命令很直接:
pip install Spire.PDF
小型项目也可以直接用免费版:pip install Spire.PDF.Free
如果在 Windows 上安装卡住了,先看官方安装说明,通常问题都出在环境和字体这两类地方。
创建可填写表单域
先把几个基本概念捋清楚:
PdfForm:整个交互式表单PdfField:单个字段- 各类字段对象:不同类型的表单控件
Spire.PDF 支持的字段类型不少,常见的基本就是下面这些:
| 类名 | 说明 | 常用属性 |
|---|---|---|
| PdfTextBoxField | 文本框,用于录入姓名、地址等 | Bounds, Font, Text |
| PdfCheckBoxField | 复选框,用于多选场景 | Checked (True/False) |
| PdfRadioButtonListField | 单选按钮组,互斥选项 | Items, SelectedIndex |
| PdfComboBoxField | 组合框(下拉列表) | Items, Font |
| PdfButtonField | 提交/重置按钮 | Actions.MouseDown |
多数业务表单其实就是这些控件的组合,没必要把问题想得太复杂。
表单创建流程
表单创建的步骤并不绕,核心就是先建文档,再把字段一点点放进去。字段少的时候这样看起来很朴素,但真到后面要改版,结构越清楚,维护越省事。
基本流程如下:
- 创建
PdfDocument - 添加页面
- 创建字段对象
- 设置字段位置(Bounds)
- 添加到
doc.Form.Fields - 保存文档
下面这个示例做了一份员工入职登记表,包含文本输入、材料清单复选、性别单选,以及部门和职位级别的下拉选择。
from spire.pdf.common import *
from spire.pdf import *
# 初始化文档
doc = PdfDocument()
page = doc.Pages.Add()
# 定义布局基准坐标
baseX = 120.0
baseY = 40.0
brush_title = PdfSolidBrush(PdfRGBColor(Color.get_DarkBlue()))
brush_text = PdfSolidBrush(PdfRGBColor(Color.get_Black()))
# 设置字体
font = PdfTrueTypeFont("宋体", 11.0, PdfFontStyle.Regular, True)
# 1. 文本框:姓名
page.Canvas.DrawString("员工姓名:", font, brush_title, PointF(10.0, baseY))
txt_name = PdfTextBoxField(page, "EmpName")
txt_name.Bounds = RectangleF(baseX, baseY, 160.0, 16.0)
txt_name.Font = font
doc.Form.Fields.Add(txt_name)
# 2. 复选框:入职材料清单
baseY += 35.0
page.Canvas.DrawString("提交材料:", font, brush_title, PointF(10.0, baseY))
# 选项一:身份证
cb_id = PdfCheckBoxField(page, "Doc_ID")
cb_id.Bounds = RectangleF(baseX, baseY, 16.0, 16.0)
doc.Form.Fields.Add(cb_id)
page.Canvas.DrawString("身份证复印件", font, brush_text, PointF(baseX + 22.0, baseY))
# 选项二:学历学位证书
cb_edu = PdfCheckBoxField(page, "Doc_Edu")
cb_edu.Bounds = RectangleF(baseX + 100.0, baseY, 16.0, 16.0)
doc.Form.Fields.Add(cb_edu)
page.Canvas.DrawString("学历学位证书", font, brush_text, PointF(baseX + 122.0, baseY))
# 3. 单选按钮:性别
baseY += 35.0
page.Canvas.DrawString("性别:", font, brush_title, PointF(10.0, baseY))
rb_gender = PdfRadioButtonListField(page, "Gender")
item_m = PdfRadioButtonListItem("男")
item_m.Bounds = RectangleF(baseX, baseY, 16.0, 16.0)
page.Canvas.DrawString("男", font, brush_text, PointF(baseX + 22.0, baseY))
item_f = PdfRadioButtonListItem("女")
item_f.Bounds = RectangleF(baseX + 60.0, baseY, 16.0, 16.0)
page.Canvas.DrawString("女", font, brush_text, PointF(baseX + 82.0, baseY))
rb_gender.Items.Add(item_m)
rb_gender.Items.Add(item_f)
doc.Form.Fields.Add(rb_gender)
# 4. 组合框:所属部门
baseY += 35.0
page.Canvas.DrawString("入职部门:", font, brush_title, PointF(10.0, baseY))
cmb_dept = PdfComboBoxField(page, "Dept")
cmb_dept.Bounds = RectangleF(baseX, baseY, 160.0, 16.0)
cmb_dept.Items.Add(PdfListFieldItem("研发部", "RD"))
cmb_dept.Items.Add(PdfListFieldItem("市场部", "MKT"))
cmb_dept.Items.Add(PdfListFieldItem("行政部", "ADMIN"))
cmb_dept.Font = font
doc.Form.Fields.Add(cmb_dept)
# 5. 组合框:职位级别
baseY += 35.0
page.Canvas.DrawString("职位级别:", font, brush_title, PointF(10.0, baseY))
cmb_level = PdfComboBoxField(page, "JobLevel")
cmb_level.Bounds = RectangleF(baseX, baseY, 160.0, 16.0)
cmb_level.Items.Add(PdfListFieldItem("初级 (P1-P2)", "Junior"))
cmb_level.Items.Add(PdfListFieldItem("中级 (P3-P5)", "Senior"))
cmb_level.Items.Add(PdfListFieldItem("高级 (P6+)", "Expert"))
cmb_level.Font = font
doc.Form.Fields.Add(cmb_level)
# 6. 保存模板
doc.SaveToFile("/output/入职表.pdf")
doc.Close()
表单数据自动填充
模板建好之后,下一步就是把数据写进去。手工填当然也行,但真要批量处理,还是程序直接赋值更省心,字段名一对上就能跑。
填充时的思路很简单:
- 加载 PDF
- 获取表单对象
- 获取字段集合
- 按字段类型和字段名赋值
下面这个例子沿用上面的入职表,分别对文本框、复选框、单选按钮和组合框做填充,最后生成一份已填写完成的 PDF。
from spire.pdf.common import *
from spire.pdf import *
# 1. 加载文档
doc = PdfDocument()
doc.LoadFromFile("/output/入职表.pdf")
# 2. 获取表单字段集合
form = doc.Form
try:
formWidget = PdfFormWidget(form)
fields = formWidget.FieldsWidget
except:
fields = form.FieldsWidget
# 3. 遍历并填充字段
if fields is not None and fields.Count > 0:
for i in range(fields.Count):
field = fields.get_Item(i)
name = field.Name
# 文本框
if isinstance(field, PdfTextBoxFieldWidget):
if name == "EmpName":
field.Text = "张岚"
# 复选框
if isinstance(field, PdfCheckBoxWidgetFieldWidget):
if name == "Doc_ID" or name == "Doc_Edu":
field.Checked = True
# 单选按钮
if isinstance(field, PdfRadioButtonListFieldWidget):
if name == "Gender":
field.SelectedIndex = 1
# 组合框
if isinstance(field, PdfComboBoxFieldWidget):
if name == "Dept":
field.SelectedIndex = [0]
if name == "JobLevel":
field.SelectedIndex = [2]
# 4. 保存文档
doc.SaveToFile("/output/员工入职表 1-1.pdf")
doc.Close()
print("填充任务已顺利完成!")
常见问题
中文显示问题
如果填充后中文变成空白或者乱码,先别怀疑代码,通常是字体没处理好。
这里还是建议老老实实用:
PdfTrueTypeFont("宋体", 12.0, PdfFontStyle.Regular, True)
同时确认系统里确实有这个字体。没有字体,后面怎么画都不稳。
SelectedIndex 从 0 开始
列表框和组合框用的是 0-based 索引。显示出来的文本和内部值不是一回事,尤其是你要和数据库字段对接的时候,这一点很容易踩空。
小结
用 Spire.PDF for Python 做交互式 PDF 表单,思路其实很朴素:先把字段建出来,再按字段名把数据填进去。它适合结构比较固定、需要批量处理的场景,像入职表、登记表、调查表这类都很顺手。Python 代码也比较直接,维护起来不会太费劲。

