#!/usr/bin/env bash # 字体子集化脚本 —— 把 ~/Fonts 下的中文字体裁剪成 woff2 并写入 vendor/fonts/ # # 用途:离线部署,字体必须随仓库分发,禁止 CDN。 # 产物:vendor/fonts/-.woff2 + vendor/fonts/manifest.json # # 依赖:python3 + fonttools + brotli # 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 # # 用法: # vendor/fonts/tools/subset-fonts.sh # FONT_SRC=~/Fonts PYTHON=python3 vendor/fonts/tools/subset-fonts.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" OUT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" FONT_SRC="${FONT_SRC:-$HOME/Fonts}" PYTHON="${PYTHON:-python3}" FONTS_CONF="$SCRIPT_DIR/fonts.conf" # 子集字符集:ASCII + GB2312 一级/二级汉字 + 中文标点 + 数字/全角符号 CHARSET="$("$PYTHON" - <<'PY' import sys chars = set() # 1) ASCII 可打印字符 for c in range(0x20, 0x7F): chars.add(chr(c)) # 2) GB2312 全部合法双字节码位(一级 3755 + 二级 3008 汉字,及全角标点/希腊/俄文等) for hi in range(0xA1, 0xFF): for lo in range(0xA1, 0xFF): try: chars.add(bytes((hi, lo)).decode("gb2312")) except UnicodeDecodeError: pass # 3) 常用中文标点 / 数字符号补充 chars.update( " ·—…‘’“”※→←↑↓√×÷±≈≠≤≥∞°℃℉¥€£©®™№§¶†‡•‰′″〇" "「」『』【】〔〕〖〗《》〈〉" "0123456789" "!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~" ) sys.stdout.write("".join(sorted(chars))) PY )" CHAR_COUNT="$(printf '%s' "$CHARSET" | "$PYTHON" -c 'import sys; print(len(sys.stdin.read()))')" # 子集化参数: # --flavor=woff2 输出 woff2(需 brotli) # --layout-features 保留字距/连字等,丢弃 CJK 竖排等无用特性以压体积 # --name-IDs='*' 保留完整 name 表(含本地化族名) # --recommended-glyphs 保留 .notdef/.null 等推荐字形 # --drop-tables+=DSIG 丢弃无用的数字签名表 SUBSET_OPTS=( --flavor=woff2 --layout-features='kern,liga,clig,calt,ccmp,locl,mark,mkmk' --name-IDs='*' --name-languages='*' --recommended-glyphs --drop-tables+=DSIG ) mkdir -p "$OUT_DIR" echo "charset: ${CHAR_COUNT} 个字符(ASCII + GB2312 一级/二级 + 中文标点 + 数字符号)" while IFS='|' read -r id name family category license source_note reg_src bold_src; do case "$id" in ''|'#'*) continue ;; esac for pair in "regular:400:$reg_src" "bold:700:$bold_src"; do IFS=':' read -r weight_name weight_class src_rel <<<"$pair" src="$FONT_SRC/$src_rel" out="$OUT_DIR/$id-$weight_name.woff2" [ -f "$src" ] || { echo "缺少源字体: $src" >&2; exit 1; } printf ' %-26s %s -> %s\n' "$id-$weight_name" "$(basename "$src")" "$(basename "$out")" "$PYTHON" -m fontTools.subset "$src" "${SUBSET_OPTS[@]}" \ --text="$CHARSET" --output-file="$out" done done < "$FONTS_CONF" "$PYTHON" "$SCRIPT_DIR/normalize-names.py" "$PYTHON" "$SCRIPT_DIR/gen-manifest.py" echo "完成。产物目录:$OUT_DIR" ls -la "$OUT_DIR"/*.woff2