import os from PIL import Image, ImageDraw, ImageFont, ImageFilter font_song = "/System/Library/Fonts/Supplemental/Songti.ttc" font_hei = "/System/Library/Fonts/STHeiti Medium.ttc" font_hei_bold = "/System/Library/Fonts/Supplemental/Arial Unicode.ttf" # 清新渐变配色方案 items = [ ('menu_city_center', '市级', '运营', (68, 165, 255), (28, 118, 240), False), ('menu_city_partner', '市级', '合伙', (255, 115, 96), (235, 60, 68), False), ('menu_province_center', '省级', '服务', (58, 202, 148), (22, 160, 108), False), ('menu_province_partner', '省级', '合伙', (172, 118, 238), (128, 72, 210), False), ('menu_region_branch', '大区', '分公司', (252, 180, 68), (225, 132, 30), True), ] def generate_icons(size=320, font_family="song"): f_main_path = font_song if font_family == "song" else font_hei f_sub_path = font_hei font_main = ImageFont.truetype(f_main_path, 96) font_sub = ImageFont.truetype(f_sub_path, 48) font_sub_3 = ImageFont.truetype(f_sub_path, 42) out_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../public/uploads/decorate')) os.makedirs(out_dir, exist_ok=True) generated_images = [] for base_name, t1, t2, c1, c2, is_3 in items: # 画布尺寸 img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) # 1. 垂直柔和渐变底色 grad = Image.new('RGBA', (size, size)) for y in range(size): r = y / float(size) cr = int(c1[0] * (1 - r) + c2[0] * r) cg = int(c1[1] * (1 - r) + c2[1] * r) cb = int(c1[2] * (1 - r) + c2[2] * r) for x in range(size): grad.putpixel((x, y), (cr, cg, cb, 255)) # 2. 圆形遮罩(留 12px 边距防抗锯齿边缘裁切) pad = 12 mask = Image.new('L', (size, size), 0) m_draw = ImageDraw.Draw(mask) m_draw.ellipse([pad, pad, size - pad, size - pad], fill=255) img.paste(grad, (0, 0), mask) d = ImageDraw.Draw(img) # 3. 外层高光细环 + 内层细光环 d.ellipse([pad, pad, size - pad, size - pad], outline=(255, 255, 255, 230), width=6) d.ellipse([pad + 16, pad + 16, size - pad - 16, size - pad - 16], outline=(255, 255, 255, 110), width=2) # 4. 顶部晨光弧光 d.arc([pad + 20, pad + 20, size - pad - 20, size - pad - 20], start=210, end=330, fill=(255, 255, 255, 150), width=4) # 5. 标题 1 居中绘制 (大字 96px) bbox1 = d.textbbox((0, 0), t1, font=font_main) w1 = bbox1[2] - bbox1[0] h1 = bbox1[3] - bbox1[1] y1 = 66 d.text(((size - w1) / 2.0, y1), t1, font=font_main, fill=(255, 255, 255, 255)) # 6. 标题 2 居中绘制 (大字 48px / 42px) cur_sub_font = font_sub_3 if is_3 else font_sub bbox2 = d.textbbox((0, 0), t2, font=cur_sub_font) w2 = bbox2[2] - bbox2[0] y2 = 192 if is_3 else 188 d.text(((size - w2) / 2.0, y2), t2, font=cur_sub_font, fill=(255, 255, 255, 245)) # 保存原文件名及 v2 版本文件名 p_base = os.path.join(out_dir, f"{base_name}.png") p_v2 = os.path.join(out_dir, f"{base_name}_v2.png") img.save(p_base) img.save(p_v2) generated_images.append((base_name, img)) print(f"Generated {p_base} and {p_v2} (size: {size}x{size})") # 生成一条 5 个横排在一起的整体预览图 (5 * 320 = 1600 宽) strip_w = size * len(items) + 80 strip_h = size + 60 strip = Image.new('RGBA', (strip_w, strip_h), (246, 248, 250, 255)) s_draw = ImageDraw.Draw(strip) for idx, (name, icon) in enumerate(generated_images): pos_x = 40 + idx * size pos_y = 30 strip.paste(icon, (pos_x, pos_y), icon) strip_path = os.path.join(out_dir, "fresh_hd_strip_preview.png") strip.save(strip_path) print(f"Preview strip saved to {strip_path}") if __name__ == '__main__': generate_icons(320, "song")