Files
pangolin/tools/fonts/make-cjk-subset.sh
T
wangjia 6fc032bac8 feat(client): 字体改本地打包(拉丁全量 + Noto Sans SC GB2312 子集)
生产 PangolinFonts.useBundled=true,离线可用,不再运行时拉 google_fonts;
中文经 fontFamilyFallback=[Noto Sans SC] 兜底,子集外字符再兜系统 CJK 字体。
- 拉丁:Sora/Manrope/JetBrains Mono 静态权重(复用 test/fonts)。
- 中文:Noto Sans SC 变量字体 instance 到 Regular + subset 到 GB2312 6763 字
  (单权重,粗体引擎合成),client/fonts 合计约 3.4MB。
- tools/fonts/make-cjk-subset.sh 可复现生成(GB2312 字表无需联网)。
- 新增 test/unit/fonts_test.dart 锁定接线;golden 因 notdef 占位字形微调已更新。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 07:48:21 +08:00

68 lines
2.4 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 生成 Noto Sans SC 子集,用于 client 离线打包(全平台)。
#
# 策略:GB2312 一级+二级常用字(6763 字,经 Python 编解码确定性生成,无需联网)
# 仓库 client/lib 实际用到的 CJK 字符 → 子集字表;
# 源用 Noto Sans CJK SC 变量字体,先 instance 到 Regular(wght=400)再 subset
# (变量 CFF2 的字重轴体积很大;单 Regular 静态 + 引擎合成粗体,显著缩小)。
# 产物:client/fonts/NotoSansSC-Regular-subset.otf (~3MB,6763 字)。
#
# 依赖:python3 + fonttools(pip install fonttools brotli)。
# 用法:bash tools/fonts/make-cjk-subset.sh
set -euo pipefail
cd "$(dirname "$0")/../.." # 仓库根
SRC_DIR="tools/fonts/_src"
CHARS="tools/fonts/common-hanzi.txt"
OUT="client/fonts/NotoSansSC-Regular-subset.otf"
mkdir -p "$SRC_DIR"
# fonttools:优先用隔离 venv(避免污染系统 python),否则用 python3 -m。
PY="python3"
SUB="python3 -m fontTools.subset"
if [ -x "/tmp/fonttools-venv/bin/python" ]; then
PY="/tmp/fonttools-venv/bin/python"
SUB="/tmp/fonttools-venv/bin/pyftsubset"
fi
# 1) 字表:GB2312 6763 常用字 ∪ 仓库实际用字。
"$PY" - "$CHARS" <<'PYGEN'
import sys, glob
chars=set()
for hi in range(0xB0,0xF8):
for lo in range(0xA1,0xFF):
try:
ch=bytes([hi,lo]).decode('gb2312')
if '一'<=ch<='鿿': chars.add(ch)
except UnicodeDecodeError:
pass
# 仓库实际用到的 CJK(确保 UI/l10n 文案一定在子集内)
for path in glob.glob('client/lib/**/*.dart', recursive=True):
try:
for ch in open(path,encoding='utf-8').read():
if '一'<=ch<='鿿': chars.add(ch)
except (UnicodeDecodeError,OSError):
pass
open(sys.argv[1],'w',encoding='utf-8').write(''.join(sorted(chars)))
print('charset hanzi:',len(chars))
PYGEN
# 2) 源字体(变量 OTF);若已存在则跳过。失效时换 google/fonts 或 notofonts 最新路径。
SRC_TTF="$SRC_DIR/NotoSansSC.ttf"
if [ ! -f "$SRC_TTF" ]; then
curl -fsSL "https://github.com/notofonts/noto-cjk/raw/main/Sans/Variable/OTF/NotoSansCJKsc-VF.otf" -o "$SRC_TTF"
fi
# 3) instance 到 Regular(wght=400)。
STATIC="$SRC_DIR/NotoSC-Regular.otf"
"$PY" -m fontTools.varLib.instancer "$SRC_TTF" wght=400 -o "$STATIC" --quiet
# 4) subset(不用 --retain-gids,否则保留全部原 gid 致体积暴涨)。
"$SUB" "$STATIC" \
--text-file="$CHARS" \
--output-file="$OUT" \
--layout-features='*' \
--name-IDs='*'
ls -la "$OUT"