Python Pillow库加载本地字体文件绘制文字
在 Python 图形生成与批量图像处理中,使用 Pillow 库在画布上绘制文字是一项高频需求。Pillow 默认内置的位图字体仅支持 ASCII 字符、尺寸固定且边缘锯齿严重,无法满足中文显示与排版设计的要求。解决这一问题的标准做法是通过 PIL.ImageFont.truetype() 方法载入本地的矢量字体文件(如 .ttf、.otf 或 .ttc),再结合 PIL.ImageDraw.Draw() 对象进行文本坐标测算与渲染输出。本文将按实际开发步骤完整阐述从文件定位、字体加载、坐标测算、多行换行到常见中文乱码排查的全过程。
一、环境准备与核心 API 简介
在 Python 脚本中引入 Pillow 库需要导入三个核心子模块:Image、ImageDraw 与 ImageFont。在安装了 pillow 包后,代码中统一使用 PIL 作为命名空间:
from PIL import Image, ImageDraw, ImageFont
绘制文字的核心是 ImageFont.truetype(font, size, index=0, encoding='', layout_engine=None) 函数。其参数含义如下:
- font:本地字体文件的存储路径,支持字符串或
pathlib.Path对象。可以是 TrueType 字体(.ttf)、OpenType 字体(.otf)或 TrueType 字体集合(.ttc)。 - size:字体的点数或像素大小,通常为正整数(如 24、36、48)。
- index:针对
.ttc这类包含多个字型分支的集合文件,通过index选择具体子字体,默认值为 0。
二、加载本地字体的基础实现示例
下面给出一段完整的、可直接执行的代码。示例中创建了一张背景为米白色的图片,通过加载本地的 .ttf 文件写入中文字符,并保存为本地图片:
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
# 1. 确定本地字体文件路径(推荐使用 pathlib 避免路径拼接问题)
# 请将 'SourceHanSansCN-Regular.otf' 替换为您机器上的真实中文字体路径
FONT_PATH = Path("./fonts/SourceHanSansCN-Regular.otf")
if not FONT_PATH.exists():
raise FileNotFoundError(f"未找到指定的字体文件: {FONT_PATH.resolve()}")
# 2. 创建 RGB 画布(宽 800,高 400,背景米白色)
canvas_width, canvas_height = 800, 400
image = Image.new("RGB", (canvas_width, canvas_height), color=(248, 247, 243))
draw = ImageDraw.Draw(image)
# 3. 加载本地字体,字号设置为 32 像素
custom_font = ImageFont.truetype(str(FONT_PATH), size=32)
# 4. 在指定坐标 (x, y) 处绘制文字
text_content = "你好,Pillow!这是本地字体渲染效果。"
text_position = (60, 160)
draw.text(text_position, text_content, font=custom_font, fill=(48, 54, 60))
# 5. 保存结果文件
image.save("rendered_text_output.png")
print("图像已成功生成并保存至 rendered_text_output.png")
在上述代码中,draw.text() 接收文本起始坐标、文字内容、字体实例以及字体颜色 fill。颜色值可以使用 RGB 三元组(如 (48, 54, 60))或十六进制字符串(如 "#30363C")。
三、默认位图字体与本地矢量字体对比
很多开发者在刚开始使用 Pillow 时会调用 ImageFont.load_default()。理解默认字体与本地 truetype 字体的本质差异有助于明确为何必须引入本地文件:
四、文本尺寸度量与精准定位(Bounding Box)
在设计图绘制、水印居中或卡片标签排版时,预先计算文字所占矩形区域是不可避免的步骤。需要特别注意的是:在 Pillow 10.0.0 及以上版本中,旧式的 draw.textsize() 与 font.getsize() 已被彻底废弃移除。当前必须统一使用 draw.textbbox() 或 font.getbbox()。
实现文字完全居中渲染
利用 textbbox 获取的四元组尺寸,可以通过数学计算将文字精确居中放置在画面的指定区域内:
def draw_centered_text(image, text, font, fill_color=(48, 54, 60)):
draw = ImageDraw.Draw(image)
img_w, img_h = image.size
# 测算文本相对于原点 (0, 0) 的边界框
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# 计算居中坐标(需减去 bbox 的起始偏移量 bbox[0] 与 bbox[1])
x = (img_w - text_width) / 2 - bbox[0]
y = (img_h - text_height) / 2 - bbox[1]
draw.text((x, y), text, font=font, fill=fill_color)
return image
五、多行文本排版与自动折行控制
Pillow 提供了 draw.multiline_text() 方法,支持包含换行符 \n 的字符串排版,并允许通过 spacing 参数指定行间距,通过 align 参数设定对齐方式("left"、"center"、"right")。但需要注意,Pillow 本身不会根据图片宽度自动切分长句进行自动换行,必须由上层逻辑预先切分。
下面是一个通用的中文单字符贪心折行函数实现:
def wrap_text_by_width(text, font, max_width, draw):
"""
根据给定的最大像素宽度,将文本主动切分为带换行符的多行文本
"""
lines = []
current_line = ""
for char in text:
test_line = current_line + char
bbox = draw.textbbox((0, 0), test_line, font=font)
line_width = bbox[2] - bbox[0]
if line_width <= max_width:
current_line = test_line
else:
lines.append(current_line)
current_line = char
if current_line:
lines.append(current_line)
return "\n".join(lines)
# 使用示例
sample_text = "Python中的Pillow库是一个非常强大的图像处理工具,通过加载本地字体文件可以轻松实现各种风格的中文排版与海报生成。"
wrapped_text = wrap_text_by_width(sample_text, custom_font, max_width=400, draw=draw)
# 绘制多行文本,设定行距为 10 像素
draw.multiline_text((50, 50), wrapped_text, font=custom_font, fill=(48, 54, 60), spacing=10, align="left")
六、本地字体加载的高频问题与避坑指南
避坑清单:
- 问题 1:
OSError: cannot open resource
原因通常是路径不正确。在脚本中写相对路径时,其基准是终端执行时的当前工作目录(CWD),而非当前 Python 脚本所在的目录。推荐使用Path(__file__).resolve().parent / "fonts" / "font.ttf"进行显式绝对路径定位。 - 问题 2:中文文字变成方块(豆腐块)或空白
原因是加载的字体文件本身不包含对应汉字的 Glyph(字形)。例如系统自带的arial.ttf或times.ttf是西文字体,无法渲染汉字。开发时必须指定具备中文字形集的字体(如思源黑体、微软雅黑msyh.ttc、苹方PingFang.ttc等)。 - 问题 3:TTC(TrueType Collection)集合字体的索引选择
.ttc文件内封装了多个子字型。若直接加载可能默认命中英文或简易字体。可通过传入index=0或index=1切换主副字型。 - 问题 4:RGBA 图像透明通道绘制
在透明背景画布(Image.new("RGBA", (w, h), (0, 0, 0, 0)))上绘制文字时,fill参数必须包含第 4 位 Alpha 值,如(48, 54, 60, 255),否则默认为全透明导致看不到文字。
七、总结
利用 Pillow 加载本地字体绘制文字是 Python 处理中文动态海报、生成数据长图或添加个性化水印的核心机制。开发者只需把握三点:一是明确区分字体文件格式并采用稳定的绝对路径载入;二是全面使用现代化 Pillow 的 textbbox 接口取代废弃方法,以获取真实字符尺寸并完成居中对齐;三是在工程层面对字体对象实施复用以避免频繁解析的开销。遵循上述规范即可构建出稳定、高品质的图像文本渲染逻辑。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













