You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Pillow如何获取带换行文本尺寸实现框内居中

Pillow 矩形内多行/单行文本自动缩放居中实现

问题表现

  • 参考常规Pillow文本居中方案实现时,不带换行符的单行文本可以正常匹配矩形尺寸、居中渲染
  • 文本中加入\n换行符后,出现字号适配错误、文本居中偏移的问题
  • 问题根因:Pillow 7.1.2版本的FreeTypeFont.getsize()方法不会识别换行符,会把带换行的多行文本识别为单行计算宽高,返回的尺寸和实际渲染结果完全不符
  • 适用场景:制作游戏卡牌等需要同时兼容单行、多行文本自动适配居中的需求

问题复现

正常运行的单行文本代码

title_text = "Hello World"

img = Image.new(size=(400, 300), mode='RGB')
draw = ImageDraw.Draw(img)
font_path = "/content/Charlie don't surf.ttf"

# 绘制200x100的白色矩形,中心点坐标为(200,150)
draw.rectangle((200-100, 150-50, 200+100, 150+50), fill='white')
draw.line(((0, 150), (400, 150)), 'gray')
draw.line(((200, 0), (200, 300)), 'gray')

# 遍历查找能让文本适配200x100矩形的最大字号
selected_size = 1
for size in range(1, 150):
    title_font = ImageFont.FreeTypeFont(font_path, size=size)
    w, h = title_font.getsize(title_text)
    
    if w > 200 or h > 100:
        break
        
    selected_size = size

# 文本居中绘制
title_font = ImageFont.FreeTypeFont(font_path, size=selected_size)
draw.text((200-(w//2), 150-h//2), title_text, fill='red', font=title_font)

display(img)

运行效果:
文本居中和尺寸适配效果符合预期

异常的多行文本代码

仅把title_text修改为带换行的"Hello\nWorld",其余逻辑不变:

title_text = "Hello\nWorld"

img = Image.new(size=(400, 300), mode='RGB')
draw = ImageDraw.Draw(img)
font_path = "/content/Charlie don't surf.ttf"

# 绘制200x100的白色矩形,中心点坐标为(200,150)
draw.rectangle((200-100, 150-50, 200+100, 150+50), fill='white')
draw.line(((0, 150), (400, 150)), 'gray')
draw.line(((200, 0), (200, 300)), 'gray')

# 遍历查找能让文本适配200x100矩形的最大字号
selected_size = 1
for size in range(1, 150):
    title_font = ImageFont.FreeTypeFont(font_path, size=size)
    w, h = title_font.getsize(title_text)
    
    if w > 200 or h > 100:
        break
        
    selected_size = size

# 文本居中绘制
title_font = ImageFont.FreeTypeFont(font_path, size=selected_size)
draw.text((200-(w//2), 150-h//2), title_text, fill='red', font=title_font)

display(img)

运行效果:
换行文本运行效果异常

修复方案

核心逻辑是自行拆分多行文本计算总尺寸,不要直接用getsize()处理带换行符的整段文本,步骤如下:

  • 用split('\n')把文本拆分为单行列表
  • 逐行计算单行宽高:总宽度取所有行的宽度最大值,总高度为所有行的高度累加值(如果需要加行间距,可以额外加上(行数-1)*行间距数值)
  • 字号适配阶段用上述方法计算得到的多行总宽高和矩形尺寸对比,筛选最大可用字号
  • 绘制阶段逐行渲染:先计算整个文本块的左上角起始坐标,每绘制完一行就向下偏移一个行高的距离,每行单独计算水平居中位置

修复后可兼容单行/多行的完整代码

def get_multiline_text_size(font, text, line_spacing=0):
    """计算带换行符的文本实际渲染宽高"""
    lines = text.split('\n')
    max_width = 0
    total_height = 0
    line_heights = []
    for line in lines:
        w, h = font.getsize(line)
        max_width = max(max_width, w)
        line_heights.append(h)
        total_height += h
    total_height += line_spacing * (len(lines) - 1)
    return max_width, total_height, line_heights

# 测试文本,可替换为单行/任意行数的带换行文本
title_text = "Hello\nWorld"
rect_w, rect_h = 200, 100
rect_center_x, rect_center_y = 200, 150

img = Image.new(size=(400, 300), mode='RGB')
draw = ImageDraw.Draw(img)
font_path = "/content/Charlie don't surf.ttf"

# 绘制参考矩形和辅助线
draw.rectangle((rect_center_x - rect_w//2, rect_center_y - rect_h//2, 
                rect_center_x + rect_w//2, rect_center_y + rect_h//2), fill='white')
draw.line(((0, rect_center_y), (400, rect_center_y)), 'gray')
draw.line(((rect_center_x, 0), (rect_center_x, 300)), 'gray')

# 查找适配矩形的最大字号
selected_size = 1
line_spacing = 4 # 行间距,可根据需求调整
for size in range(1, 150):
    title_font = ImageFont.FreeTypeFont(font_path, size=size)
    w, h, _ = get_multiline_text_size(title_font, title_text, line_spacing)
    if w > rect_w or h > rect_h:
        break
    selected_size = size

# 逐行居中绘制文本
title_font = ImageFont.FreeTypeFont(font_path, size=selected_size)
total_w, total_h, line_heights = get_multiline_text_size(title_font, title_text, line_spacing)
# 文本块左上角起始坐标
start_x = rect_center_x - total_w//2
start_y = rect_center_y - total_h//2

current_y = start_y
lines = title_text.split('\n')
for idx, line in enumerate(lines):
    line_w, _ = title_font.getsize(line)
    # 每行单独水平居中
    draw.text((start_x + (total_w - line_w)//2, current_y), line, fill='red', font=title_font)
    current_y += line_heights[idx] + line_spacing

display(img)

上述代码不管传入的是无换行的单行文本,还是带任意数量换行的多行文本,都可以自动计算适配的最大字号,在矩形内完美居中。


内容的提问来源于stack exchange,提问作者user3347814

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 02:15:41