# vendor/fonts —— 本地字体(离线部署,禁止 CDN) 现场部署无外网,所有中文字体必须随仓库分发、从本目录本地加载。本目录只放**子集化后的 woff2**, 原始大字体(每套 8–12 MB)不入库。 ## 字体清单 | id | 显示名 | font-family | 字重 | 单套合计 | | --- | --- | --- | --- | --- | | `noto-sans` | Noto Sans SC | `Noto Sans SC` | 400 / 700 | 2.72 MB | | `noto-serif` | Noto Serif SC | `Noto Serif SC` | 400 / 700 | 3.71 MB | | `misans` | MiSans | `MiSans` | 400 / 700 | 1.76 MB | | `source-han-sans` | 思源黑体 Source Han Sans CN | `Source Han Sans CN` | 400 / 700 | 2.72 MB | 合计 8 个 woff2、10.91 MB。逐文件体积与 sha256 见 [`manifest.json`](./manifest.json), 每个文件均 ≤ 4 MB / 套的目标(最大为 Noto Serif SC 的 3.71 MB)。 ## 来源与授权 | 字体 | 来源 | 授权 | | --- | --- | --- | | Noto Sans SC | Google Noto(`~/Fonts/noto-sans/sc`) | SIL Open Font License 1.1 | | Noto Serif SC | Google Noto(`~/Fonts/noto-serif/sc/SubsetOTF/SC`) | SIL Open Font License 1.1 | | MiSans | 小米(`~/Fonts/MiSans/ttf`) | MiSans 字体知识产权许可协议 | | 思源黑体 Source Han Sans CN | Adobe(`~/Fonts/思源黑体`) | SIL Open Font License 1.1 | > **授权说明**:本项目为**内部赛事**使用,不对外公开发布,字体授权不构成障碍 > (见 `devdocs/06-大屏端设计.md` 9.1、`devdocs/07-管理面板设计.md` 9)。 > 若将来对外分发,需逐字体复核许可:Noto / 思源黑体为 OFL,可自由分发; > **MiSans 为厂商许可,公开分发前必须另行确认**。OFL 字体保留其原始版权声明, > 本目录未附 LICENSE 副本,需要时从 `~/Fonts/<字体目录>/LICENSE` 取回。 ## 子集化命令 依赖 `fonttools` + `brotli`(生成 woff2 必需): ```bash pip install fonttools brotli # 系统 Python 受 PEP 668 保护时,用带系统包的虚拟环境: python3 -m venv --system-site-packages ~/.tmp/fontenv ~/.tmp/fontenv/bin/pip install brotli PYTHON=~/.tmp/fontenv/bin/python vendor/fonts/tools/subset-fonts.sh ``` 脚本会:生成字符集 → 逐字体跑 `fontTools.subset` → 重新生成 `manifest.json`。 **子集字符集**:ASCII 可打印字符 + GB2312 全部合法码位(一级 3755 + 二级 3008 汉字, 含全角标点、希腊/俄文、全角数字)+ 常用中文标点与数字符号,共 **7553 字符**。 **子集化参数**: ```bash python -m fontTools.subset <源字体> \ --flavor=woff2 \ --layout-features='kern,liga,clig,calt,ccmp,locl,mark,mkmk' \ --name-IDs='*' --name-languages='*' \ --recommended-glyphs --drop-tables+=DSIG \ --text="$(字符集)" --output-file=-.woff2 ``` 去掉 CJK 竖排(`vert`/`vrt2`)等用不到的 OpenType 特性以压体积;保留 `kern`/`liga` 等排版特性。 ## 如何使用 `web/src/shared/styles/fonts.css` 为每套字体写好 `@font-face`(`font-display: swap`) 与 `--font-` 变量。在全局样式入口加一行即可生效: ```css @import './fonts.css'; ``` CSS 里的 `url()` 是相对 `web/src/shared/styles/` 的 `../../../../vendor/fonts/...`。 Vite 构建时会把这些 woff2 作为资源打进产物(输出到 `server/internal/webui/assets/`), 运行时不再依赖仓库外的路径。 ## 如何新增字体 1. 把源字体(`.otf`/`.ttf`)放到 `~/Fonts/<目录>/`(不要直接复制进仓库)。 2. 在 `tools/fonts.conf` 追加一行:`id|显示名|font-family|分类|授权|来源说明|Regular 源文件|Bold 源文件`。 3. 重跑 `PYTHON=... vendor/fonts/tools/subset-fonts.sh`,它会生成 `-regular.woff2`、 `-bold.woff2` 并更新 `manifest.json`。 4. 在 `web/src/shared/styles/fonts.css` 补对应 `@font-face` 和 `--font-` 变量。 5. 检查单套合计 ≤ 4 MB;超了就精简字符集或换更轻的字重。 ## 已知限制 - MiSans 缺 `・`(U+30FB,日文片假名中点,GB2312 内含),其余 7552 个字符齐全; 中文场景无影响。其余三套字符集全覆盖。 - 只做 Regular + Bold 两档;如需 Medium/Light,按上节流程加字重(需扩展 `fonts.conf` 的字段格式)。 - 字体文件已子集化,**不要**用原始 `~/Fonts` 大字体覆盖本目录文件。