#!/usr/bin/env python3
"""HTML 盘面 → PNG 截图。
用法:
python chart_shot.py [-o 输出png路径]
截图分级回退:环境装有 playwright 则优先(file:// 全页截图,2 倍分辨率);
否则用 macOS Chrome 无头截图(固定窗口,高度按 HTML 内容估算避免截断)。
两者都不可用:退出码 2,stderr 提示直接打开 HTML。截图失败不影响排盘主流程。
"""
import argparse
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import _runtime_guard # noqa: F401 import 即检查:Python < 3.10 时中文报错退出(码 3)
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
def _shot_playwright(html: Path, out: Path) -> bool:
try:
from playwright.sync_api import sync_playwright
except ImportError:
return False
try:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(device_scale_factor=2)
page.goto(html.resolve().as_uri())
page.screenshot(path=str(out), full_page=True)
browser.close()
except Exception:
return False
return out.is_file() and out.stat().st_size > 0
def _content_height(html: Path) -> int:
"""按 HTML 结构估算整页高度(px):头卡/主表/页脚取固定高,条带按格数折行。"""
text = html.read_text(encoding="utf-8", errors="ignore")
height = (900 + 40 * text.count('class="warn"')
+ 150 * text.count('class="qrcard"'))
for strip in text.split('class="strip"')[1:]:
cells = strip.count('class="cell"')
height += 36 + 132 * -(-cells // 9)
return max(1300, min(height + 120, 4200))
def _shot_chrome(html: Path, out: Path) -> bool:
if not Path(CHROME).is_file():
return False
try:
r = subprocess.run(
[CHROME, "--headless=new", f"--screenshot={out.resolve()}",
f"--window-size=1100,{_content_height(html)}",
"--force-device-scale-factor=2", "--hide-scrollbars",
html.resolve().as_uri()],
capture_output=True, timeout=60)
except Exception:
return False
return r.returncode == 0 and out.is_file() and out.stat().st_size > 0
def shoot(html: Path, out: Path | None = None) -> Path | None:
"""对 HTML 盘面截图,成功返回 PNG 绝对路径,无可用工具或失败返回 None。"""
out = out or html.with_suffix(".png")
if _shot_playwright(html, out) or _shot_chrome(html, out):
return out.resolve()
return None
def main() -> int:
ap = argparse.ArgumentParser(
prog="chart_shot.py",
description="HTML 盘面 → PNG 截图(playwright 优先,Chrome 无头兜底)")
ap.add_argument("html", help="HTML 盘面文件路径")
ap.add_argument("-o", "--out", default=None,
help="输出 PNG 路径(默认与 HTML 同目录同名 .png)")
a = ap.parse_args()
html = Path(a.html)
if not html.is_file():
print(f"ERROR: 找不到 HTML 文件 {html}", file=sys.stderr)
return 2
png = shoot(html, Path(a.out) if a.out else None)
if png:
print(png)
return 0
print("无可用截图工具(未检测到 playwright 或 Chrome),"
"请直接打开 HTML 文件查看盘面。", file=sys.stderr)
return 2
if __name__ == "__main__":
raise SystemExit(main())