66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
# -*- 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}')
|