#!/usr/bin/env python3 """把子集字体内部的 name 表 / OS2 字重统一成 CSS 期望的规范值。 为什么需要:思源黑体的老版本 OTF 把每个字重写成独立族名 (`Source Han Sans CN Regular` / `Source Han Sans CN Bold`), MiSans 的 usWeightClass 是 330/630 这类非标准值。 @font-face 的 `font-family` 描述符虽然能屏蔽内部族名,但保持一致更省心, 也避免字体被单独安装/引用时族名对不上。 用法(由 subset-fonts.sh 调用): python3 vendor/fonts/tools/normalize-names.py """ from __future__ import annotations import sys from pathlib import Path from fontTools.ttLib import TTFont HERE = Path(__file__).resolve().parent OUT_DIR = HERE.parent CONF = HERE / "fonts.conf" WEIGHTS = (("regular", "Regular", 400), ("bold", "Bold", 700)) STALE_NAME_IDS = (1, 2, 3, 4, 6, 16, 17, 21, 22) PLATFORMS = ((3, 1, 0x409), (1, 0, 0)) # Windows / Mac,英文 def set_name(name, name_id: int, value: str) -> None: for platform_id, plat_enc_id, lang_id in PLATFORMS: name.setName(value, name_id, platform_id, plat_enc_id, lang_id) def normalize(path: Path, family: str, style: str, weight: int) -> None: font = TTFont(path) name = font["name"] for record in list(name.names): if record.nameID in STALE_NAME_IDS: name.removeNames(record.nameID, record.platformID, record.platEncID, record.langID) full_name = family if style == "Regular" else f"{family} {style}" set_name(name, 1, family) # family set_name(name, 2, style) # subfamily set_name(name, 3, f"{full_name}:subset") # unique id set_name(name, 4, full_name) # full name set_name(name, 6, full_name.replace(" ", "")) # postscript name set_name(name, 16, family) # typographic family set_name(name, 17, style) # typographic subfamily os2 = font["OS/2"] os2.usWeightClass = weight if weight >= 700: os2.fsSelection = (os2.fsSelection & ~0x40) | 0x20 # BOLD font["head"].macStyle |= 0x01 else: os2.fsSelection = (os2.fsSelection & ~0x20) | 0x40 # REGULAR font["head"].macStyle &= ~0x01 font.flavor = "woff2" font.save(path) font.close() def main() -> int: for line in CONF.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#"): continue fid, _, family = [p.strip() for p in line.split("|")][:3] for weight_name, style, weight_class in WEIGHTS: path = OUT_DIR / f"{fid}-{weight_name}.woff2" if not path.is_file(): print(f"缺少产物: {path}", file=sys.stderr) return 1 normalize(path, family, style, weight_class) print(f" {path.name:32s} -> {family} / {style} ({weight_class})") return 0 if __name__ == "__main__": raise SystemExit(main())