如何在Pillow中定义网格区块并控制文本不超出网格块范围
Pillow宾果卡网格布局优化方案
核心优化点
- 提前定义网格基础参数,明确每个单元格的边界范围,彻底避免文本跨区块问题
- 新增文本自适应逻辑:动态计算文本尺寸,自动调整换行规则和字号,保证内容完全容纳在单元格内
- 用循环替换重复硬编码的坐标写法,大幅降低代码冗余度,后续调整布局只需要修改基础参数即可
完整实现代码
import random import textwrap from PIL import Image, ImageFont, ImageDraw # 此处保留你原有gclient相关的导入逻辑 def babbelbingo_file(): file = gclient.open_by_key("obfuscated") babbelbingo = file.get_worksheet(1) values = babbelbingo.get_all_values() list_values = [item for sublist in values for item in sublist] questions = random.sample(list_values, k=24) return questions def get_fit_text(text, font_path, max_width, max_height, initial_font_size=20): """ 自适应调整文本字号和换行,保证文本在指定宽高范围内显示 """ font_size = initial_font_size # 最小字号限制,避免文字过小无法识别 while font_size > 8: font = ImageFont.truetype(font_path, font_size) # 计算适配当前字号的换行宽度 avg_char_width = font.getlength("中") if "\u4e00" <= text <= "\u9fff" else font.getlength("x") wrap_width = int(max_width // avg_char_width) wrapped_text = "\n".join(textwrap.wrap(text, width=wrap_width)) # 计算换行后文本的总尺寸 _, _, text_w, text_h = ImageDraw.Draw(Image.new("RGB", (1,1))).multiline_textbbox( (0,0), wrapped_text, font=font ) if text_w <= max_width and text_h <= max_height: return wrapped_text, font font_size -= 1 # 降到最小字号仍放不下时返回最小字号适配结果 font = ImageFont.truetype(font_path, 8) wrapped_text = "\n".join(textwrap.wrap(text, width=int(max_width//font.getlength("x")))) return wrapped_text, font def make_bingocard(name, questions): # 网格基础参数,可根据你的底图实际尺寸调整 GRID_START_X = 140 GRID_START_Y = 275 CELL_WIDTH = 134 # 对应你原有代码里同列相邻格子的x坐标差 CELL_HEIGHT = 119 # 对应你原有代码里同行相邻格子的y坐标差 PADDING = 10 # 单元格内边距,避免文字贴边 FONT_PATH = 'jeevesbot/files/Overpass-regular.ttf' image = Image.open('jeevesbot/files/bingokaart.png') draw = ImageDraw.Draw(image) q_idx = 0 # 遍历5x5网格 for row in range(5): for col in range(5): # 跳过中间的免费格子(索引从0开始,第3行第3列对应row=2、col=2) if row == 2 and col == 2: continue # 计算当前格子的中心坐标、可用显示范围 cell_center_x = GRID_START_X + col * CELL_WIDTH cell_center_y = GRID_START_Y + row * CELL_HEIGHT available_w = CELL_WIDTH - 2 * PADDING available_h = CELL_HEIGHT - 2 * PADDING # 获取适配当前格子的文本和对应字体 current_q = questions[q_idx] fit_text, fit_font = get_fit_text(current_q, FONT_PATH, available_w, available_h) # 渲染文本 draw.text( (cell_center_x, cell_center_y), fit_text, fill=(0,0,0), font=fit_font, align='center', anchor='mm' ) q_idx += 1 image.save(f'jeevesbot/files/generated_bingocards/{name}.png') def bingo(name): questions = babbelbingo_file() make_bingocard(name, questions)
调整说明
如果后续更换宾果卡底图,只需要修改make_bingocard函数开头的网格基础参数,即可快速适配新模板,不需要逐个调整每个文本的坐标。
内容的提问来源于stack exchange,提问作者Peter van Arkel
相关产品推荐
相关产品推荐

