下面是一个基于Python的PDF手写模拟器,它可以将普通文本转换为看起来像手写的PDF文档。这个工具使用了多个库来模拟不同的手写风格、纸张背景和墨水效果。
功能特点
- 支持多种手写字体模拟
- 可调整”书写”速度和笔迹变化
- 添加纸张纹理背景
- 模拟墨水渗透和污渍效果
- 支持PDF导出
![图片[1]_基于Python打造一个PDF手写模拟器_知途无界](https://zhituwujie.com/wp-content/uploads/2026/01/d2b5ca33bd20260105101958-1024x691.png)
安装依赖
首先安装所需的库:
pip install reportlab pillow numpy matplotlib
完整代码实现
import random
import math
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.lib.colors import Color, black, gray
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import io
class HandwritingSimulator:
def __init__(self):
self.page_width, self.page_height = A4
self.margin = 2 * cm
# 手写风格参数
self.base_font_size = 14
self.line_spacing = 22
self.char_spacing = 1.2
# 模拟手写的不规则性
self.wobble_intensity = 0.5 # 笔迹抖动强度
self.ink_bleed = 0.1 # 墨水渗透程度
self.paper_texture = True # 是否添加纸张纹理
def create_paper_background(self, width, height):
"""创建纸张纹理背景"""
if not self.paper_texture:
return None
# 创建浅黄色纸张底色
img = Image.new('RGB', (int(width), int(height)), (255, 253, 245))
draw = ImageDraw.Draw(img)
# 添加细微的纸张纹理
for _ in range(int(width * height / 100)):
x = random.randint(0, int(width)-1)
y = random.randint(0, int(height)-1)
color_variation = random.randint(-10, 10)
r = max(240, min(255, 245 + color_variation))
g = max(240, min(255, 243 + color_variation))
b = max(230, min(255, 235 + color_variation))
draw.point((x, y), fill=(r, g, b))
# 添加一些随机的微小污点
for _ in range(int(width * height / 5000)):
x = random.randint(0, int(width)-1)
y = random.randint(0, int(height)-1)
radius = random.uniform(0.1, 0.5)
color = random.choice([(240, 240, 235), (245, 245, 240), (250, 248, 245)])
draw.ellipse([x-radius, y-radius, x+radius, y+radius], fill=color)
return img
def get_handwriting_style(self, text_style="casual"):
"""返回不同手写风格的参数"""
styles = {
"casual": { # 随意手写
"font_size": self.base_font_size,
"wobble": self.wobble_intensity * 1.2,
"slant": random.uniform(-0.1, 0.1),
"ink_color": (0, 0, 20), # 深灰色墨水
"line_variation": 0.3
},
"neat": { # 工整手写
"font_size": self.base_font_size * 0.9,
"wobble": self.wobble_intensity * 0.5,
"slant": random.uniform(-0.05, 0.05),
"ink_color": (0, 0, 40), # 较深的灰色
"line_variation": 0.1
},
"fast": { # 快速书写
"font_size": self.base_font_size * 1.1,
"wobble": self.wobble_intensity * 1.5,
"slant": random.uniform(0.1, 0.3),
"ink_color": (30, 30, 50), # 较浅的灰色
"line_variation": 0.5
}
}
return styles.get(text_style, styles["casual"])
def add_char_variations(self, char, style):
"""为字符添加手写变化"""
# 随机轻微缩放
scale = random.uniform(0.95, 1.05)
# 随机旋转
rotation = random.uniform(-2, 2)
# 随机位置偏移(模拟手写抖动)
offset_x = random.uniform(-style["wobble"], style["wobble"])
offset_y = random.uniform(-style["wobble"]/2, style["wobble"]/2)
return char, scale, rotation, offset_x, offset_y
def create_handwritten_text_image(self, text, style_name="casual", width=None):
"""将文本渲染为手写风格的图像"""
if width is None:
width = int(self.page_width - 2 * self.margin)
style = self.get_handwriting_style(style_name)
font_size = style["font_size"]
# 估算所需高度
lines = []
current_line = ""
words = text.split()
for word in words:
test_line = current_line + " " + word if current_line else word
# 这里简化估算,实际应用中可能需要更精确的文本宽度计算
if len(test_line) * font_size * 0.6 < width:
current_line = test_line
else:
lines.append(current_line)
current_line = word
if current_line:
lines.append(current_line)
estimated_height = len(lines) * self.line_spacing + 50
# 创建图像
img_height = max(estimated_height, 100)
background = self.create_paper_background(width, img_height)
if background:
img = background
else:
img = Image.new('RGB', (width, img_height), (255, 253, 245))
draw = ImageDraw.Draw(img)
# 绘制文本行
y_position = 20
for line in lines:
x_position = 10
chars_drawn = 0
for char in line:
if chars_drawn > 0 and char != ' ':
# 添加字符间距
x_position += font_size * self.char_spacing * 0.5
# 获取字符变化参数
styled_char, scale, rotation, offset_x, offset_y = self.add_char_variations(char, style)
# 计算实际位置
char_x = x_position + offset_x
char_y = y_position + offset_y
# 绘制字符(这里使用简单矩形模拟,实际应用可使用手写字体)
char_width = font_size * scale
char_height = font_size * scale
# 绘制字符轮廓(模拟手写笔画)
ink_color = style["ink_color"]
# 添加墨水渗透效果
if random.random() < self.ink_bleed:
bleed_radius = random.uniform(0.5, 1.5)
draw.ellipse([
char_x - bleed_radius,
char_y - bleed_radius,
char_x + char_width + bleed_radius,
char_y + char_height + bleed_radius
], fill=tuple(max(0, c-10) for c in ink_color))
# 绘制主字符
draw.rectangle([
char_x, char_y,
char_x + char_width, char_y + char_height
], fill=ink_color)
# 添加笔画变化(模拟手写不连贯)
if random.random() < 0.1:
# 随机省略部分笔画
skip_part = random.choice(["top", "bottom", "left", "right"])
if skip_part == "top":
draw.rectangle([char_x, char_y, char_x + char_width, char_y + char_height/3], fill=(255, 253, 245))
elif skip_part == "bottom":
draw.rectangle([char_x, char_y + 2*char_height/3, char_x + char_width, char_y + char_height], fill=(255, 253, 245))
x_position += char_width + font_size * self.char_spacing * 0.3
chars_drawn += 1
# 移动到下一行
y_position += self.line_spacing + random.uniform(-style["line_variation"], style["line_variation"])
return img
def text_to_pdf_handwritten(self, text, output_filename, style="casual", page_orientation="portrait"):
"""将文本转换为手写风格PDF"""
c = canvas.Canvas(output_filename, pagesize=A4)
# 设置页面方向
if page_orientation == "landscape":
c.setPageSize((A4[1], A4[0]))
self.page_width, self.page_height = A4[1], A4[0]
else:
self.page_width, self.page_height = A4
# 分割文本为多页
margin = self.margin
content_width = self.page_width - 2 * margin
content_height = self.page_height - 2 * margin
# 简单分页逻辑(实际应用中需要更复杂的分页)
words = text.split()
current_page_text = ""
pages = []
for word in words:
test_text = current_page_text + " " + word if current_page_text else word
# 估算文本高度(简化版)
estimated_lines = len(test_text) * self.base_font_size / content_width
estimated_height = estimated_lines * self.line_spacing
if estimated_height < content_height:
current_page_text = test_text
else:
pages.append(current_page_text)
current_page_text = word
if current_page_text:
pages.append(current_page_text)
# 为每一页生成手写内容
for i, page_text in enumerate(pages):
if i > 0:
c.showPage() # 新建页面
# 创建手写文本图像
img = self.create_handwritten_text_image(page_text, style, int(content_width))
# 将图像添加到PDF
img_bytes = io.BytesIO()
img.save(img_bytes, format='PNG')
img_bytes.seek(0)
# 计算图像在页面上的位置和大小
img_width, img_height = img.size
aspect_ratio = img_width / img_height
# 适应页面宽度
display_width = content_width
display_height = display_width / aspect_ratio
# 如果高度超出页面,则按比例缩小
if display_height > content_height:
display_height = content_height
display_width = display_height * aspect_ratio
# 居中显示
x_position = margin + (content_width - display_width) / 2
y_position = self.page_height - margin - display_height
c.drawImage(img_bytes, x_position, y_position, width=display_width, height=display_height)
c.save()
print(f"PDF已生成: {output_filename}")
# 使用示例
if __name__ == "__main__":
simulator = HandwritingSimulator()
sample_text = """
尊敬的先生/女士:
您好!这是一份测试文档,用于演示Python PDF手写模拟器的功能。
该模拟器可以将普通文本转换为看起来像手写的PDF文档,具有以下特点:
1. 支持多种手写字体模拟(随意、工整、快速等风格)
2. 可调整书写速度和笔迹变化
3. 添加纸张纹理背景
4. 模拟墨水渗透和污渍效果
5. 支持PDF导出
这项技术可以应用于多种场景,如生成个性化信件、制作仿古文档、
创建教学材料等。通过调整参数,可以模拟不同年龄、性别和心情下的手写风格。
感谢您的使用!
此致
敬礼
Python PDF手写模拟器开发团队
2023年12月
"""
# 生成不同风格的手写PDF
simulator.text_to_pdf_handwritten(sample_text, "handwritten_casual.pdf", style="casual")
simulator.text_to_pdf_handwritten(sample_text, "handwritten_neat.pdf", style="neat")
simulator.text_to_pdf_handwritten(sample_text, "handwritten_fast.pdf", style="fast")
print("所有示例PDF已生成完毕!")
进阶改进建议
- 使用真实手写字体:
- 下载手写风格的TrueType字体(.ttf)
- 使用
ImageFont.truetype()加载字体 - 替换矩形字符为真实文字渲染
- 增加更多手写特征:
- 模拟连笔字
- 添加墨水晕染效果
- 实现字母倾斜和大小变化
- 性能优化:
- 对于长文档,实现流式处理避免内存溢出
- 使用缓存机制减少重复计算
- 用户界面:
- 添加GUI界面方便非程序员使用
- 提供实时预览功能
这个手写模拟器提供了基础框架,您可以根据需要进一步扩展和完善功能。
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END

























暂无评论内容