# -*- coding: utf-8 -*- """ 为没有产品图的系列生成统一风格占位缩略图 (web/series/<系列码>.webp)。 背景: 首页系列缩略图网格需要每个系列一张图。bom-viewer 项目已有 27 张真实产品图 (来自 onebot251231 网站后台), 其余 13 个系列 (KC/M/MS/MAL/KS 等基础气缸 + UFL/LAGP 老码系列) 暂无 → 生成占位图, 用户后续用真图覆盖同名文件即可。 风格对齐卡片: 浅灰底 (--gray-50 #f9fafb) + 青色系列码大字 (--primary-700 #0f766e) + "图片待补" 小字, 尺寸 500x450 (与 bom-viewer 产品图一致)。 用法: PYTHONUTF8=1 python gen-series-thumbs.py """ import os from PIL import Image, ImageDraw, ImageFont WEB_SERIES = r'D:/开发/OnebotCatalog/web/series' # 需要占位的系列 (bom-viewer 里没有对应的产品图) PLACEHOLDER = ['KC', 'KCB', 'KSB', 'KCS', 'KS', 'KSU', 'M', 'MS', 'MSC', 'MAL', 'MALC', 'UFL', 'LAGP'] W, H = 500, 450 BG = (249, 250, 251) # --gray-50 CODE_COLOR = (15, 118, 110) # --primary-700 SUB_COLOR = (156, 163, 175) # --gray-400 BOX = (229, 231, 235) # --gray-200 FONT_CANDIDATES = [ r'C:\Windows\Fonts\consolab.ttf', # Consolas Bold — 与卡片等宽字体一致 r'C:\Windows\Fonts\consola.ttf', r'C:\Windows\Fonts\msyhbd.ttc', ] CN_FONT_CANDIDATES = [ r'C:\Windows\Fonts\msyh.ttc', r'C:\Windows\Fonts\msyhl.ttc', ] def load_font(paths, size): for p in paths: if os.path.exists(p): try: return ImageFont.truetype(p, size) except Exception: continue return ImageFont.load_default() def make_one(code, out_path): im = Image.new('RGB', (W, H), BG) d = ImageDraw.Draw(im) # 虚线圆角外框 (暗示"这里是图位") d.rounded_rectangle([26, 26, W - 26, H - 26], radius=18, outline=BOX, width=2) # 系列码大字 (居中) f_code = load_font(FONT_CANDIDATES, 84) bbox = d.textbbox((0, 0), code, font=f_code) d.text(((W - (bbox[2] - bbox[0])) / 2 - bbox[0], H / 2 - 62 - (bbox[3] - bbox[1]) / 2), code, font=f_code, fill=CODE_COLOR) # 分隔短线 d.line([(W / 2 - 34, H / 2 + 8), (W / 2 + 34, H / 2 + 8)], fill=BOX, width=2) # "图片待补" (中英各一行, 居中) f_cn = load_font(CN_FONT_CANDIDATES, 20) f_en = load_font(FONT_CANDIDATES, 15) t1 = '图片待补' b1 = d.textbbox((0, 0), t1, font=f_cn) d.text(((W - (b1[2] - b1[0])) / 2 - b1[0], H / 2 + 30), t1, font=f_cn, fill=SUB_COLOR) t2 = 'IMAGE PENDING' b2 = d.textbbox((0, 0), t2, font=f_en) d.text(((W - (b2[2] - b2[0])) / 2 - b2[0], H / 2 + 62), t2, font=f_en, fill=SUB_COLOR) im.save(out_path, 'WEBP', quality=88, method=6) return os.path.getsize(out_path) def main(): os.makedirs(WEB_SERIES, exist_ok=True) total = 0 for code in PLACEHOLDER: p = os.path.join(WEB_SERIES, code + '.webp') sz = make_one(code, p) total += sz print(f' {code:6s} -> {sz/1024:5.1f} KB') print(f'\n生成 {len(PLACEHOLDER)} 张占位图, 共 {total/1024:.0f} KB') print('替换方式: 用真图覆盖 web/series/<系列码>.webp 即可 (无需改代码)') if __name__ == '__main__': main()