# -*- coding: utf-8 -*- """ 生成 contact 公共邮箱头像:品牌深底 + 青→红渐变 @ 符号 视觉规范与官网一致(assets/css/style.css 的 --bg/--cyan/--brand) 用法: uv run --with pillow python3 tools/gen_contact_avatar.py """ from PIL import Image, ImageDraw, ImageFilter, ImageFont SIZE = 512 BG = (7, 11, 22) # 官网 --bg-1 CYAN = (76, 195, 255) # 官网 --cyan RED = (255, 61, 90) # 官网 --brand OUT = 'assets/brand/contact-avatar.png' # ---------- 背景:深底 + 左上青辉光 / 右下红辉光 ---------- bg = Image.new('RGB', (SIZE, SIZE), BG) def glow_blob(box, blur): blob = Image.new('L', (SIZE, SIZE), 0) ImageDraw.Draw(blob).ellipse(box, fill=80) return blob.filter(ImageFilter.GaussianBlur(blur)) bg.paste(Image.new('RGB', (SIZE, SIZE), CYAN), (0, 0), glow_blob((-220, -220, 240, 240), 100)) bg.paste(Image.new('RGB', (SIZE, SIZE), RED), (0, 0), glow_blob((SIZE - 240, SIZE - 240, SIZE + 220, SIZE + 220), 100)) # ---------- 对角渐变图(青 → 红),用作 @ 的填充色 ---------- c1, c2 = CYAN, RED span = 2 * (SIZE - 1) grad = Image.new('RGB', (SIZE, SIZE)) grad.putdata([ ( round(c1[0] + (c2[0] - c1[0]) * (x + y) / span), round(c1[1] + (c2[1] - c1[1]) * (x + y) / span), round(c1[2] + (c2[2] - c1[2]) * (x + y) / span), ) for y in range(SIZE) for x in range(SIZE) ]) # ---------- 渲染 @ 文字 mask ---------- # macOS 西文字体按优先级找,@ 字形各字体差异不大 FONT_CANDIDATES = [ '/System/Library/Fonts/SFNS.ttf', '/System/Library/Fonts/SFNSText.ttf', '/System/Library/Fonts/Helvetica.ttc', ] font_path = next((p for p in FONT_CANDIDATES if __import__('os').path.exists(p)), None) if not font_path: raise SystemExit('找不到可用系统字体: ' + ', '.join(FONT_CANDIDATES)) font = ImageFont.truetype(font_path, 400) mask = Image.new('L', (SIZE, SIZE), 0) ImageDraw.Draw(mask).text((SIZE // 2, SIZE // 2), '@', font=font, fill=255, anchor='mm') # 自检:@ 必须真的渲染出来了,否则静默输出空底图 ink = sum(1 for v in mask.getdata() if v > 0) ratio = ink / (SIZE * SIZE) if not 0.05 < ratio < 0.45: raise SystemExit(f'@ 渲染异常,墨迹占比 {ratio:.1%}(预期 5%~45%),检查字体 {font_path}') bg.paste(grad, (0, 0), mask) import os os.makedirs(os.path.dirname(OUT), exist_ok=True) bg.save(OUT, 'PNG', optimize=True) print(f'完成: {OUT} {SIZE}x{SIZE} @{ratio:.0%} 字体={font_path}')