前言
作为学生,想必大家都有过被海量手写实验报告、课程作业支配的痛苦。要花费大量时间一笔一划地抄写到纸质文档中再进行扫描成pdf提交,不仅耗时耗力,手写的字迹还可能参差不齐,影响作业美观度。
为了偷懒,博主开发了一款基于python的pdf手写模拟器,它能够模拟真实手写笔迹,将电脑上的文字批量填充到 pdf 指定区域,支持中文/英文分开配置、手写扰动效果自定义、配置模版复用等功能,生成的效果高度贴近真实手写,帮你彻底告别手动抄写的烦恼。首先来个界面以及效果预览:

流程思路总览
这款 pdf 手写模拟器的整体运作流程可以分为「用户操作流程」和「技术实现流程」两层,清晰易懂:
1. 用户操作流程(简单易上手)
打开目标 pdf:选择需要填写的实验报告、作业等 pdf 文件;


框选填写区域:在 pdf 预览界面拖动鼠标,框选出需要插入文字的区域(红色矩形标记);

配置手写参数:分别在中文、英文/数字选项卡中调整字体、大小、颜色、扰动效果等参数;

输入并预览文字:在右侧文本框输入需要填写的内容,开启「实时预览」查看手写效果;


不满意可实时调整参数

确认并保存:效果满意后确认应用笔迹,支持撤销、擦除错误内容,也可保存当前配置为模版,最后导出填写完成的 pdf。

2. 技术实现流程(分层协作)
- ui 交互层:基于 tkinter 搭建可视化界面,提供按钮、选项卡、文本框等交互组件,接收用户操作;
- 参数配置层:管理中文/英文手写参数,支持配置保存(json)与加载,实现模版复用;
- pdf 操作层:基于 pymupdf 实现 pdf 的读取、页面渲染、文字插入、擦除、保存等核心操作;
- 手写渲染层:核心逻辑层,实现文字排版、语言区分、手写扰动效果生成,最终将模拟手写文字插入 pdf。
┌─────────────────────┐
│ ui交互层(tkinter) │ # 可视化交互,接收操作
└─────────────┬───────┘
▼
┌─────────────────────┐
│ 参数配置层(json) │ # 参数管理,模版复用
└─────────────┬───────┘
▼
┌─────────────────────┐
│ pdf操作层(pymupdf) │ # pdf核心操作(读写/保存等)
└─────────────┬───────┘
▼
┌─────────────────────┐
│ 手写渲染层(核心) │ # 手写效果生成,文字插入
└─────────────────────┘
涉及的具体技术
这款工具基于 python 生态的常用库开发,技术栈轻量化且实用性强,核心涉及以下技术:
- tkinter:python 内置 gui 库,无需额外安装,负责搭建整个应用的可视化界面,实现按钮点击、参数调整、文本输入、pdf 预览等交互功能;
- pymupdf(fitz):核心 pdf 处理库,提供高效的 pdf 读取、写入、页面渲染、文字插入、红act标注(擦除功能)等接口,是实现 pdf 编辑的核心依赖;
- pillow(pil):图像处理库,将 pymupdf 渲染出的 pdf 页面像素数据,转换成 tkinter 画布可显示的图像格式,实现 pdf 页面的可视化预览;
- json:轻量级数据格式,用于保存用户配置的手写参数模版(字体、大小、扰动值等),方便后续直接复用,无需重复调整;
- python 随机数与数学计算:实现手写效果的「不规则扰动」,模拟真实手写的位置偏移、大小波动、行倾斜等特性;
- ttf 字体支持:读取自定义 truetype 字体文件,支持切换不同手写风格字体,适配不同用户的手写习惯。
最终效果演示

代码原理简单讲解(手写字体生成 + 扰动效果)
我们重点讲解核心功能 —— 手写字体生成与扰动效果的实现,忽略 ui 搭建等辅助代码,聚焦核心逻辑:
手写字体生成(文字插入 pdf 核心)
这部分的核心是将文字按规则排版后,插入到 pdf 指定区域,关键步骤如下:
1.文字与区域预处理
首先获取文本框中的输入文字,按换行符 \n 分割成多行,同时将用户框选的预览区域坐标(带缩放比例)转换为 pdf 实际坐标(除以缩放系数 self.zoom),得到真实的填写范围 (x1, y1, x2, y2)。
# 读取输入文字并分割行
text = self.text_editor.get("1.0", tk.end).strip("\n")
lines = text.split('\n')
# 转换为 pdf 实际坐标(去除预览缩放影响)
x1, y1 = min(self.start_x, self.end_x)/self.zoom, min(self.start_y, self.end_y)/self.zoom
x2, y2 = max(self.start_x, self.end_x)/self.zoom, max(self.start_y, self.end_y)/self.zoom
2.语言区分与参数匹配
通过 is_chinese 函数判断单个字符是中文还是英文/数字,分别匹配对应的配置参数(中文/英文独立的字体、大小、字距等),确保不同语言的手写效果适配合理。
# 判断是否为中文(含中文标点)
def is_chinese(self, char):
if '\u4e00' <= char <= '\u9fff': return true
if char in "。,、?!:;“”‘'()《》【】": return true
return false
# 遍历字符时匹配对应语言参数
lang = 'zh' if self.is_chinese(char) else 'en'
p = self.params[lang] # 获取对应语言的配置参数
3.pdf 文字插入核心 api
使用 pymupdf 的 page.insert_text 方法实现文字插入,这是手写字体生成的关键,核心参数说明如下:
fitz.point(final_x, final_y):文字的实际插入坐标(经排版和扰动调整后);char:要插入的单个字符(逐字符插入实现精细排版);fontsize:字符的实际大小(带大小抖动);fontfile:ttf 手写字体文件路径,决定字体风格;color:文字颜色(默认纯黑,支持用户自定义);morph:变换矩阵,实现字符旋转效果(叠加行倾斜与字符旋转抖动)。
4.行与字符排版
按行遍历文字,逐字符计算插入坐标,处理空格(单独预留间距),当字符横坐标超出框选区域右侧时自动换行,同时根据「行距参数」调整下一行的纵坐标,确保文字排版规整且不超出框选范围。
扰动效果实现(模拟真实手写不规则性)
这是工具的灵魂所在,通过「行级扰动」和「字符级扰动」两层效果,彻底摆脱打印体的规整感,贴近真实手写,对应代码中的配置参数:
行级扰动(整行不规则性):针对每一行文字,添加整体的偏移和倾斜,模拟手写时「行不直、略有偏移」的特点,对应三个参数:
# 行级扰动参数计算 line_dx = random.uniform(-p_line['line_jitter_x'].get(), p_line['line_jitter_x'].get()) line_dy = random.uniform(-p_line['line_jitter_y'].get(), p_line['line_jitter_y'].get()) line_angle_deg = random.uniform(-p_line['line_tilt'].get(), p_line['line_tilt'].get()) line_angle_rad = math.radians(line_angle_deg)
- 行左右平移(
line_jitter_x):随机生成-line_jitter_x ~ line_jitter_x范围内的偏移量line_dx,使每行文字左右轻微晃动; - 行上下平移(
line_jitter_y):随机生成-line_jitter_y ~ line_jitter_y范围内的偏移量line_dy,使行与行之间的间距略有差异; - 行整行倾斜(
line_tilt):随机生成倾斜角度line_angle_deg,转换为弧度后,通过tilt_y_offset = (curr_x - line_start_x) * math.tan(line_angle_rad)计算每个字符的y轴偏移,实现整行轻微倾斜。
字符级扰动(单个字符不规则性):针对每个字符,添加位置、大小、旋转的细微差异,模拟手写时「每个字大小不一、略有晃动」的特点,对应三个参数:
# 字符级扰动参数计算 char_jitter_x = random.uniform(-p['jitter_pos'].get(), p['jitter_pos'].get()) char_jitter_y = random.uniform(-p['jitter_pos'].get(), p['jitter_pos'].get()) char_jitter_rot = random.uniform(-p['jitter_rot'].get(), p['jitter_rot'].get()) char_jitter_fs = f_size + random.uniform(-p['jitter_size'].get(), p['jitter_size'].get())
- 字位置抖动(
jitter_pos):随机生成(-jitter_pos ~ jitter_pos)的x、y偏移量,使字符脱离严格的水平对齐; - 字大小抖动(
jitter_size):在基础字体大小上,随机增减(-jitter_size ~ jitter_size)的数值,生成char_jitter_fs,使字符大小略有差异; - 字旋转抖动(
jitter_rot):随机生成(-jitter_rot ~ jitter_rot)的旋转角度,叠加行倾斜角度后,通过morph参数实现字符轻微旋转。
随机数控制(预览稳定 + 实际随机):为了保证实时预览时效果稳定可调整,设置随机数种子 random.seed(42) 固定扰动效果;实际应用笔迹时,不设置种子 random.seed(),生成随机的扰动效果,让每次生成的手写风格略有差异,更贴近真实场景。
if is_preview: random.seed(42) # 预览模式:固定随机数,效果稳定 else: random.seed() # 实际生成:随机种子,效果更真实
总结
这款 pdf 手写模拟器完美解决了手写作业、实验报告的痛点,兼具「实用性」和「灵活性」:
- 实用性拉满:无需手动抄写,一键生成手写风格 pdf,节省大量时间和精力,生成效果高度贴近真实手写;
- 自定义性强:中文/英文独立配置,支持字体、颜色、大小、字距行距调整,多层扰动参数可精细调控手写风格;
- 操作便捷:可视化界面友好,支持实时预览、撤销、擦除、模版复用,零基础用户也能快速上手;
- 技术轻量化:基于 python 常用库开发,无需复杂环境配置,代码可灵活修改扩展。
当然,这款工具还有优化空间,比如支持连笔效果、笔迹粗细变化、批量处理多个 pdf 等。如果大家有类似需求,可以基于这份代码进行二次开发,适配自己的使用场景。
以上就是基于python打造一个pdf手写模拟器的详细内容,更多关于python pdf手写模拟器的资料请关注代码网其它相关文章!
发表评论