#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 生成文章模板与界面占位图资源。 项目原先依赖 uniCloud / 七牛的带签名外链图片,签名过期后全部 403, 导致首页与文章详情顶部出现空白。这里改为生成本地静态资源, 产物落在 static/template/ 下,无任何外部依赖。 用法: python tools/gen-template-assets.py """ import os from PIL import Image, ImageDraw, ImageFont ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OUT_DIR = os.path.join(ROOT, "static", "template") # 军旅主题配色:(名称, 顶部色, 底部色, 文字色) THEMES = [ ("default", (193, 28, 31), (92, 2, 1), (255, 255, 255)), ("dark", (34, 40, 49), (17, 20, 24), (235, 235, 235)), ("gold", (176, 122, 40), (110, 71, 16), (255, 250, 235)), ("olive", (74, 94, 58), (40, 54, 32), (240, 245, 235)), ("steel", (86, 104, 122), (48, 61, 74), (240, 244, 248)), ("light", (250, 248, 245), (232, 226, 216), (58, 50, 44)), ] W, H = 750, 480 def pick_font(size): """挑选一个可用的中文字体,找不到就退回默认字体。""" candidates = [ r"C:\Windows\Fonts\msyh.ttc", r"C:\Windows\Fonts\msyhbd.ttc", r"C:\Windows\Fonts\simhei.ttf", r"C:\Windows\Fonts\simsun.ttc", "/System/Library/Fonts/PingFang.ttc", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", ] for path in candidates: if os.path.exists(path): try: return ImageFont.truetype(path, size) except Exception: continue return ImageFont.load_default() def vertical_gradient(size, top, bottom): """竖直线性渐变。""" w, h = size img = Image.new("RGB", size) draw = ImageDraw.Draw(img) for y in range(h): t = y / max(1, h - 1) color = tuple(int(top[i] + (bottom[i] - top[i]) * t) for i in range(3)) draw.line([(0, y), (w, y)], fill=color) return img def add_decor(img, accent): """右下角加一点几何装饰,避免整块纯色显得像加载失败。""" w, h = img.size overlay = Image.new("RGBA", img.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(overlay) for i in range(6): radius = 120 + i * 70 alpha = max(8, 42 - i * 6) draw.ellipse( [w - radius, h - radius, w + radius * 0.35, h + radius * 0.35], outline=(255, 255, 255, alpha), width=2, ) draw.rectangle([0, h - 6, w, h], fill=accent + (90,)) return Image.alpha_composite(img.convert("RGBA"), overlay).convert("RGB") def build(name, top, bottom, text_color): img = vertical_gradient((W, H), top, bottom) img = add_decor(img, top) draw = ImageDraw.Draw(img) title_font = pick_font(64) sub_font = pick_font(28) title = "军歌嘹亮" sub = "军旅故事 · 老兵记忆" tw = draw.textlength(title, font=title_font) draw.text(((W - tw) / 2, H * 0.36), title, font=title_font, fill=text_color) sw = draw.textlength(sub, font=sub_font) draw.text(((W - sw) / 2, H * 0.36 + 92), sub, font=sub_font, fill=text_color) # 分隔线 line_w = 120 draw.line( [((W - line_w) / 2, H * 0.36 + 66), ((W + line_w) / 2, H * 0.36 + 66)], fill=text_color, width=2, ) path = os.path.join(OUT_DIR, f"{name}.png") img.save(path, "PNG", optimize=True) return path def build_placeholder_cover(): """文章无封面时列表用的占位图。""" img = vertical_gradient((W, 420), (243, 241, 238), (226, 221, 214)) draw = ImageDraw.Draw(img) font = pick_font(34) text = "暂无封面" tw = draw.textlength(text, font=font) draw.text(((W - tw) / 2, 190), text, font=font, fill=(160, 152, 144)) path = os.path.join(OUT_DIR, "no-cover.png") img.save(path, "PNG", optimize=True) return path def main(): os.makedirs(OUT_DIR, exist_ok=True) made = [] for name, top, bottom, text_color in THEMES: made.append(build(name, top, bottom, text_color)) made.append(build_placeholder_cover()) print(f"输出目录: {OUT_DIR}") for p in made: size = os.path.getsize(p) print(f" {os.path.basename(p):<16} {size/1024:6.1f} KB") print(f"共 {len(made)} 个文件") if __name__ == "__main__": main()