- M3 认证/权限/审计/限流 + 全部 REST 接口 - M4 管理面板(MIUIx + 成绩录入/积分榜下钻/大屏控制) - M5a 大屏页面(六页 + 明细覆盖层 + 缩放适配) - M6 WebSocket 实时同步中心 - M7 xlsx 导出 + 赛事包 zip + 备份 - 规则包 YAML 导入导出(差异预览) - 字体 vendor 子集化(4 套 x2 字重) - 集成:realtime/export/rules 接入 main+httpapi;backup 子命令 - 修复:中间件包装 ResponseWriter 丢失 Hijacker,导致 WS 握手 501
133 lines
4.6 KiB
Python
133 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""生成 vendor/fonts/manifest.json。
|
|
|
|
数据源:vendor/fonts/tools/fonts.conf(字体元数据)+ vendor/fonts/*.woff2(实际产物)。
|
|
每次跑完 subset-fonts.sh 会自动调用;也可单独运行:
|
|
python3 vendor/fonts/tools/gen-manifest.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from datetime import date
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
OUT_DIR = HERE.parent
|
|
ROOT = OUT_DIR.parent.parent # 仓库根,用于生成 vendor/fonts/... 形式的相对路径
|
|
CONF = HERE / "fonts.conf"
|
|
|
|
WEIGHTS = (("regular", 400), ("bold", 700))
|
|
SUBSET_DESC = "ASCII + GB2312 一级/二级汉字 + 中文标点 + 数字符号"
|
|
|
|
|
|
def charset_count() -> int:
|
|
"""复用 subset-fonts.sh 的字符集生成逻辑,保证清单与产物一致。"""
|
|
code = (
|
|
"chars=set()\n"
|
|
"for c in range(0x20,0x7F): chars.add(chr(c))\n"
|
|
"for hi in range(0xA1,0xFF):\n"
|
|
" for lo in range(0xA1,0xFF):\n"
|
|
" try: chars.add(bytes((hi,lo)).decode('gb2312'))\n"
|
|
" except UnicodeDecodeError: pass\n"
|
|
"chars.update(' ·—…‘’“”※→←↑↓√×÷±≈≠≤≥∞°℃℉¥€£©®™№§¶†‡•‰′″〇')\n"
|
|
"chars.update('「」『』【】〔〕〖〗《》〈〉')\n"
|
|
"chars.update('0123456789')\n"
|
|
"chars.update('!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~')\n"
|
|
"print(len(chars))\n"
|
|
)
|
|
return int(subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, check=True).stdout)
|
|
|
|
|
|
def human(n: int) -> str:
|
|
return f"{n / 1024 / 1024:.2f} MB" if n >= 1024 * 1024 else f"{n / 1024:.0f} KB"
|
|
|
|
|
|
def read_conf() -> list[dict[str, str]]:
|
|
rows = []
|
|
for line in CONF.read_text(encoding="utf-8").splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
fid, name, family, category, license_, source, reg, bold = (p.strip() for p in line.split("|"))
|
|
rows.append(
|
|
{
|
|
"id": fid,
|
|
"displayName": name,
|
|
"fontFamily": family,
|
|
"category": category,
|
|
"license": license_,
|
|
"source": source,
|
|
"regular": reg,
|
|
"bold": bold,
|
|
}
|
|
)
|
|
return rows
|
|
|
|
|
|
def main() -> int:
|
|
fonts = []
|
|
grand_total = 0
|
|
for row in read_conf():
|
|
weights = []
|
|
font_total = 0
|
|
for weight_name, weight_class in WEIGHTS:
|
|
path = OUT_DIR / f"{row['id']}-{weight_name}.woff2"
|
|
if not path.is_file():
|
|
print(f"缺少产物: {path},请先运行 subset-fonts.sh", file=sys.stderr)
|
|
return 1
|
|
data = path.read_bytes()
|
|
size = len(data)
|
|
font_total += size
|
|
weights.append(
|
|
{
|
|
"weight": weight_class,
|
|
"style": "normal",
|
|
"file": path.name,
|
|
"path": str(path.relative_to(ROOT)),
|
|
"bytes": size,
|
|
"size": human(size),
|
|
"sha256": hashlib.sha256(data).hexdigest(),
|
|
}
|
|
)
|
|
grand_total += font_total
|
|
fonts.append(
|
|
{
|
|
"id": row["id"],
|
|
"displayName": row["displayName"],
|
|
"fontFamily": row["fontFamily"],
|
|
"cssVar": f"--font-{row['id']}",
|
|
"category": row["category"],
|
|
"license": row["license"],
|
|
"source": row["source"],
|
|
"totalBytes": font_total,
|
|
"totalSize": human(font_total),
|
|
"weights": weights,
|
|
}
|
|
)
|
|
|
|
manifest = {
|
|
"version": 1,
|
|
"generatedAt": date.today().isoformat(),
|
|
"generator": "vendor/fonts/tools/subset-fonts.sh",
|
|
"subset": {
|
|
"charset": SUBSET_DESC,
|
|
"charCount": charset_count(),
|
|
"format": "woff2",
|
|
"layoutFeatures": "kern,liga,clig,calt,ccmp,locl,mark,mkmk",
|
|
},
|
|
"totalBytes": grand_total,
|
|
"totalSize": human(grand_total),
|
|
"fonts": fonts,
|
|
}
|
|
out = OUT_DIR / "manifest.json"
|
|
out.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
print(f"manifest.json: {len(fonts)} 套字体,{len(fonts) * 2} 个文件,合计 {human(grand_total)}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|