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

如何在Pillow中处理文本溢出:指定像素位置的文本放置

处理Pillow中文本溢出的方案

当你在Pillow中需要在指定像素区域内放置文本时,可通过以下几种方式解决溢出问题:

1. 自动换行适配区域宽度

先确定文本的可用显示区域(起始坐标+区域宽度/结束坐标),将长文本拆分成多行后逐行绘制。核心是用ImageDraw.textbbox()计算文本的宽度,判断是否需要拆分内容。

实现示例

针对你的代码中的Address字段,假设其显示区域的结束x坐标为2000(需根据实际模板调整),可用宽度为2000 - 1652 = 348像素。先添加换行处理函数:

def wrap_text(draw, text, font, max_width):
    lines = []
    current_line = ""
    for word in text.split():
        test_line = current_line + word + " "
        # 获取文本边界框计算宽度
        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.strip())
            current_line = word + " "
    if current_line:
        lines.append(current_line.strip())
    return lines

再修改generate_card函数调用该逻辑:

def generate_card(data):
    template = Image.open("template.png")
    draw = ImageDraw.Draw(template)

    # 原有文本绘制逻辑
    draw.text((1455, 537), str(data['id']), font=ID_Font, fill='black')
    draw.text((375, 534), str(data['id']), font=ID_Font, fill='black')
    draw.text((328, 255), data['name'], font=font2, fill='black')
    draw.text((250, 255), data['hname'], font=H_Font, fill='black')
    draw.text((580, 307), data['dob'], font=font3, fill='black')

    # 处理Address自动换行
    address_x = 1652
    address_y = 241
    max_address_width = 2000 - address_x  # 替换为实际区域结束x坐标
    wrapped_address = wrap_text(draw, data['Address'], font3, max_address_width)
    # 逐行绘制,行间距设为字体高度的1.2倍(可调整)
    line_height = draw.textbbox((0,0), "A", font=font3)[3] - draw.textbbox((0,0), "A", font=font3)[1]
    for i, line in enumerate(wrapped_address):
        draw.text((address_x, address_y + i * line_height * 1.2), line, font=font3, fill='black')

    return template

2. 动态缩小字体适配区域

如果希望文本保持单行,可逐步缩小字体大小,直到文本宽度不超过区域限制。

实现示例

针对name字段,假设其显示区域结束x为600,可用宽度为600 - 328 = 272像素:

def get_fitted_font(text, initial_font, max_width, draw):
    font = initial_font
    while True:
        bbox = draw.textbbox((0,0), text, font=font)
        text_width = bbox[2] - bbox[0]
        # 设置最小字号下限,避免字体过小
        if text_width <= max_width or font.size <= 10:
            break
        # 每次缩小2号字号
        font = ImageFont.truetype(font.path, font.size - 2)
    return font

在generate_card中应用该逻辑:

def generate_card(data):
    template = Image.open("template.png")
    draw = ImageDraw.Draw(template)

    # 处理name字段的字体适配
    name_x = 328
    name_y = 255
    max_name_width = 600 - name_x  # 替换为实际区域结束x坐标
    fitted_name_font = get_fitted_font(data['name'], font2, max_name_width, draw)
    draw.text((name_x, name_y), data['name'], font=fitted_name_font, fill='black')

    # 其他原有绘制逻辑
    draw.text((1455, 537), str(data['id']), font=ID_Font, fill='black')
    draw.text((375, 534), str(data['id']), font=ID_Font, fill='black')
    draw.text((250, 255), data['hname'], font=H_Font, fill='black')
    draw.text((1652, 241), data['Address'], font=font3, fill='black')
    draw.text((580, 307), data['dob'], font=font3, fill='black')

    return template

3. 截断文本并添加省略号

如果不需要换行或缩小字体,可截断过长文本并在末尾添加...,确保总宽度不超过区域限制。

实现示例

针对Address字段:

def truncate_text(text, font, max_width, draw):
    truncated = text
    while True:
        bbox = draw.textbbox((0,0), truncated + "...", font=font)
        text_width = bbox[2] - bbox[0]
        if text_width <= max_width or len(truncated) <= 1:
            break
        truncated = truncated[:-1]
    return truncated + "..." if len(truncated) < len(text) else truncated

在generate_card中应用该逻辑:

def generate_card(data):
    template = Image.open("template.png")
    draw = ImageDraw.Draw(template)

    # 处理Address文本截断
    address_x = 1652
    address_y = 241
    max_address_width = 2000 - address_x  # 替换为实际区域结束x坐标
    truncated_address = truncate_text(data['Address'], font3, max_address_width, draw)
    draw.text((address_x, address_y), truncated_address, font=font3, fill='black')

    # 其他原有绘制逻辑
    draw.text((1455, 537), str(data['id']), font=ID_Font, fill='black')
    draw.text((375, 534), str(data['id']), font=ID_Font, fill='black')
    draw.text((328, 255), data['name'], font=font2, fill='black')
    draw.text((250, 255), data['hname'], font=H_Font, fill='black')
    draw.text((580, 307), data['dob'], font=font3, fill='black')

    return template

注意:你需要根据模板的实际区域尺寸,调整每个字段的max_width(即区域结束坐标减去起始x坐标)。另外,ImageDraw.textbbox()是Pillow 9.1.0及以上版本的方法,若版本较低可使用已弃用的draw.textsize()替代。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:48:21