不安装第三方库能否使用Python填充PDF表单字段
原生Python无第三方库填充PDF表单方案
完全可以实现,全程仅依赖Python标准库,不需要安装PyPDF2等任何第三方包,适配你当前的AcroForm表单场景。
实现原理
日常办公场景下可Tab切换填充的PDF表单都是标准AcroForm结构,表单域信息存储在PDF根目录(Catalog)的AcroForm字段列表中,填充表单本质就是修改对应字段对象下的/V(字段值)条目,再重新打包为合法PDF文件即可,不需要依赖外部库做复杂渲染。
适配当前场景的实现逻辑
- 你已经拿到的表单域alt文本,对应PDF字段对象下的
/T(字段名)条目,可以直接做精确匹配 - 未命名字段按Tab跳转顺序(也就是字段在AcroForm/Fields数组中的排列顺序)按序号定位即可
- PDF的压缩流直接用标准库
zlib解压处理,不需要额外解析工具
可直接运行的实现代码
import re import zlib def fill_pdf_form(input_pdf_path, field_value_map, output_pdf_path, unnamed_field_values=None): """ 无第三方依赖填充PDF AcroForm表单 :param field_value_map: 命名字段映射,key为你获取到的alt文本/字段名,value为待填充内容 :param unnamed_field_values: 未命名字段映射,key为字段在Tab跳转顺序中的序号(从0开始计数),value为待填充内容 """ if unnamed_field_values is None: unnamed_field_values = {} # 读取原PDF二进制内容 with open(input_pdf_path, 'rb') as f: pdf_raw = f.read() # 解压PDF内的压缩流,避免字段结构藏在压缩块里匹配不到 stream_re = re.compile(rb'stream\r?\n(.*?)\r?\nendstream', re.DOTALL) def decompress_stream(match): stream_data = match.group(1) try: decompressed = zlib.decompress(stream_data) return b'stream\n' + zlib.compress(decompressed) + b'\nendstream' except Exception: return match.group(0) pdf_raw = stream_re.sub(decompress_stream, pdf_raw) # 遍历替换表单域值 field_re = re.compile(rb'<<(.*?)/T\s*\((.*?)\)(.*?)>>', re.DOTALL) unnamed_idx = 0 def replace_field(match): nonlocal unnamed_idx field_prefix = match.group(1) field_name_raw = match.group(2) field_suffix = match.group(3) target_value = None # 优先匹配命名字段 field_name = field_name_raw.decode('latin-1', errors='ignore') if field_name in field_value_map: target_value = field_value_map[field_name] # 其次按Tab顺序匹配未命名字段 elif unnamed_idx in unnamed_field_values: target_value = unnamed_field_values[unnamed_idx] unnamed_idx += 1 # 无匹配值直接返回原内容 if target_value is None: return match.group(0) # 清除字段原有值 field_suffix = re.sub(rb'/V\s*<[^>]*>', b'', field_suffix) field_suffix = re.sub(rb'/V\s*\([^)]*\)', b'', field_suffix) # 转义PDF字符串特殊字符 escaped_val = str(target_value).replace('\\', '\\\\').replace('(', '\\(').replace(')', '\\)') # 写入新值 field_suffix += f' /V ({escaped_val})'.encode('latin-1', errors='replace') return b'<<' + field_prefix + b'/T (' + field_name_raw + b')' + field_suffix + b'>>' pdf_raw = field_re.sub(replace_field, pdf_raw) # 输出填充后的PDF with open(output_pdf_path, 'wb') as f: f.write(pdf_raw) # 调用示例 if __name__ == "__main__": # 命名字段填充映射 named_fields = { "姓名": "张三", "员工编号": "A1024", "申请日期": "2024-06-15" } # 未命名字段填充映射,key为Tab顺序序号(从0开始数) unnamed_fields = { 3: "申请办公设备更换,原因是原有设备使用年限超过5年" } fill_pdf_form("原始表单.pdf", named_fields, "填充完成.pdf", unnamed_fields)
使用注意事项
- 代码仅支持标准AcroForm表单(即可Tab切换的常规表单),不支持老旧的XFA动态表单,日常办公场景99%的可填写PDF都是AcroForm格式
- 如果填充中文出现乱码,是原表单域未配置中文字体导致的,和代码逻辑无关,打开原PDF手动在对应字段输入一个中文保存后再用脚本填充即可解决
- 未命名字段的序号如果拿不准,可以先传空映射跑一次脚本,手动按Tab顺序数字段位置确认序号即可
- 填充后的PDF打开如果弹出“是否保存更改”提示,直接确认保存即可,填充的内容不会丢失
内容的提问来源于stack exchange,提问作者Spartacus
相关产品推荐
相关产品推荐

