如何在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
相关产品推荐
相关产品推荐

