# -*- coding: utf-8 -*- """ render_mockup.py — 把 mockup HTML 的 1:1 视图渲染成 160×160 PNG(无头 Chrome/Edge) 用法: python tools/ui_mock/render_mockup.py [--pad 24] [--size 160] 原理: mockup 的 body 有 padding(默认 24px),1:1 视图是第一个 flex 子项,位于 (pad, pad), 尺寸 160×160。用无头浏览器截整页,再裁出 (pad, pad, pad+160, pad+160) 即得纯页面位图。 浏览器抗锯齿会在边缘产生灰阶;对比前由 compare_mockup.py 统一二值化(阈值 128)。 """ import argparse import os import subprocess import sys from pathlib import Path from PIL import Image import numpy as np BROWSER_CANDIDATES = [ r"C:\Program Files\Google\Chrome\Application\chrome.exe", r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", r"C:\Program Files\Microsoft\Edge\Application\msedge.exe", r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe", ] def find_browser(): for c in BROWSER_CANDIDATES: if os.path.exists(c): return c sys.exit("未找到 Chrome/Edge(用于无头渲染)") def render(html_path, out_png, pad=24, size=160): browser = find_browser() html_abs = Path(html_path).resolve() if not html_abs.exists(): sys.exit("找不到 HTML: %s" % html_abs) out_abs = Path(out_png).resolve() out_abs.parent.mkdir(parents=True, exist_ok=True) win = pad + size + pad + 12 # 留一点余量 cmd = [ browser, "--headless", "--disable-gpu", "--hide-scrollbars", "--force-device-scale-factor=1", "--window-size=%d,%d" % (win, win), "--screenshot=%s" % out_abs, html_abs.as_uri(), ] r = subprocess.run(cmd, capture_output=True, text=True, timeout=90) if not out_abs.exists(): sys.exit("截图失败:\n%s\n%s" % (r.stdout, r.stderr)) img = Image.open(out_abs).convert("L") box = (pad, pad, pad + size, pad + size) page = img.crop(box) arr = np.array(page) dark = float((arr < 128).mean()) page.save(out_abs) print("OK %s -> %s (%dx%d, 黑像素 %.1f%%)" % (html_abs, out_abs, size, size, dark * 100)) return out_abs if __name__ == "__main__": ap = argparse.ArgumentParser() ap.add_argument("html") ap.add_argument("out") ap.add_argument("--pad", type=int, default=24, help="mockup body padding(1:1 视图左上偏移)") ap.add_argument("--size", type=int, default=160) a = ap.parse_args() render(a.html, a.out, pad=a.pad, size=a.size)