Github Actions + fonttools 拉取字符并创建中文字体子集
帮助解决中文字体体积过大的问题
创建子集部分参考自谢益辉老师的做法。 如果你需要的中文字体在字图 CDN 有覆盖, 用这个也能一定程度上提升性能。
本文将通过解释写好的 workflow 文件来展开, 完整代码见最下方。
on:
workflow_dispatch:
workflow_run:
workflows: ["deploy"]
types:
- completed
告诉 Github Actions 什么时候自动运行, workflow_dispatch:使得这个 workflow 能在对应页面手动点击运行, workflow_run:这里我定义了使其在名字为 deploy的 workflow 完成后运行。
steps:
- name: Checkout repository
uses: actions/checkout@v3
checkout 把仓库的代码从 Github 克隆到 workflow 构建的运行环境中。
steps:
- name: Install fonttools
run: sudo apt-get update && sudo apt-get install -y fonttools
安装 fonttools。
steps:
- name: Extract used characters from content
run: |
mkdir -p font
find . -name '*.md' -o -name '*.html' | xargs cat \
| grep -oP '[\x{3000}-\x{30FF}\x{31F0}-\x{31FF}\x{4E00}-\x{9FFF}\x{AC00}-\x{D7AF}\x{FF00}-\x{FFEF}\x{FE10}-\x{FE1F}]' \
| sort | uniq > used-chars.txt
从所在项目的.md 和.html 提取出出现过的中日韩字符 (CJK):在工作环境创建/font 文件夹, 查找当前目录及子目录下所有以.md 或.html 结尾的文件, 用 cat 拼接成一个文件, 用 grep 来匹配字符范围 (Unicode 区间可自行重新设定), sort 排序, uniq 去重, > used-chars.txt 保存到一个文本文件。
steps:
- name: Download fonts
run: |
mkdir -p font/raw
cd font/raw
declare -A urls
urls["SourceHanSansSC-Regular"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/SimplifiedChinese/SourceHanSansSC-Regular.otf"
...
for name in "${!urls[@]}"; do
echo "Downloading ${name}..."
wget -nv --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 -t 3 "${urls[$name]}" -O "${name}.otf"
done
echo "Copying local fonts STKaiti and DFKai-SB..."
cp ../../resources/STKaiti.otf .
cp ../../resources/DFKai-SB.otf .
创建/font/raw 目录来保存在线下载的中文字体文件 (.otf 格式)。 定义了一个关联数组 urls 来储存字体名和链接。 用 for 遍历, 用 wget 下载每个字体 (--retry-connrefused 访问被拒绝的话自动重试, --t 3 最多重试 3 次) 并保存到/font/raw 中。
如果手动准备了字体 (这里存储在仓库的/resources 下), 用 cp 也复制到/font/raw。
- name: Subset fonts
run: |
mkdir -p font/woff2
for font in font/raw/*.otf; do
name=$(basename "$font" .otf)
pyftsubset "$font" \
--text-file=used-chars.txt \
--flavor=woff2 \
--no-hinting \
--layout-features='*' \
--output-file="font/woff2/${name}-subset.woff2"
done
创建/font/woff2 来存放生成的子集字体。
for 循环遍历.otf 字体, 使用 pyftsubset 生成子集字体, 只保留 used-chars.txt 列出的字符, 输出格式为 woff2, --no-hinting 不保留字体 hinting 信息, --layout-features="*"保留 OpenType 的功能, --output-file 指定输出文件路径。
- name: Move subset fonts to static/font
run: |
mkdir -p static/font
mv font/woff2/*.woff2 static/font/
移动到仓库的/static/font 目录下。 (如果你不需要在这个仓库存一份, 可以省去这一步)
- name: Commit and push subset fonts to Github Pages
env:
TOKEN: ${{ secrets.PERSONAL_TOKEN }}
run: |
git clone --depth 1 https://x-access-token:${TOKEN}@github.com/rongbinf/rongbinf.github.io.git out
cp -f static/font/*.woff2 out/font/
cp -f used-chars.txt out/font/used-chars.txt
cd out
git config user.name github-actions
git config user.email github-actions@github.com
git add font/*.woff2
git add font/used-chars.txt
git commit -m "chore: update subset Source Han Sans fonts" || echo "No changes to commit"
git push
推送到 Github Pages 仓库, 使用环境变量 TOKEN 认证 Git 操作 (可参考之前写的自动部署 Hugo 教程), 克隆下来到/out 后把子集字体放到/out/font 下, 然后配置并 push 变更。
完整 yml 如下:
name: Subset Source Han Sans
on:
workflow_dispatch:
workflow_run:
workflows: ["deploy"]
types:
- completed
jobs:
subset-fonts:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install fonttools
run: sudo apt-get update && sudo apt-get install -y fonttools
- name: Extract used characters from content
run: |
mkdir -p font
find . -name '*.md' -o -name '*.html' | xargs cat \
| grep -oP '[\x{3000}-\x{30FF}\x{31F0}-\x{31FF}\x{4E00}-\x{9FFF}\x{AC00}-\x{D7AF}\x{FF00}-\x{FFEF}\x{FE10}-\x{FE1F}]' \
| sort | uniq > used-chars.txt
- name: Download fonts
run: |
mkdir -p font/raw
cd font/raw
declare -A urls
urls["SourceHanSansSC-Regular"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/SimplifiedChinese/SourceHanSansSC-Regular.otf"
urls["SourceHanSansTC-Regular"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/TraditionalChinese/SourceHanSansTC-Regular.otf"
urls["SourceHanSans-Regular"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/Japanese/SourceHanSans-Regular.otf"
urls["SourceHanSansK-Regular"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/Korean/SourceHanSansK-Regular.otf"
urls["SourceHanSansSC-Bold"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/SimplifiedChinese/SourceHanSansSC-Bold.otf"
urls["SourceHanSansTC-Bold"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/TraditionalChinese/SourceHanSansTC-Bold.otf"
urls["SourceHanSans-Bold"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/Japanese/SourceHanSans-Bold.otf"
urls["SourceHanSansK-Bold"]="https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/Korean/SourceHanSansK-Bold.otf"
for name in "${!urls[@]}"; do
echo "Downloading ${name}..."
wget -nv --retry-connrefused --waitretry=1 --read-timeout=20 --timeout=15 -t 3 "${urls[$name]}" -O "${name}.otf"
done
echo "Copying local fonts STKaiti and DFKai-SB..."
cp ../../resources/STKaiti.otf .
cp ../../resources/DFKai-SB.otf .
- name: Subset fonts
run: |
mkdir -p font/woff2
for font in font/raw/*.otf; do
name=$(basename "$font" .otf)
pyftsubset "$font" \
--text-file=used-chars.txt \
--flavor=woff2 \
--no-hinting \
--layout-features='*' \
--output-file="font/woff2/${name}-subset.woff2"
done
- name: Move subset fonts to static/font
run: |
mkdir -p static/font
mv font/woff2/*.woff2 static/font/
- name: Commit and push subset fonts to Github Pages
env:
TOKEN: ${{ secrets.PERSONAL_TOKEN }}
run: |
git clone --depth 1 https://x-access-token:${TOKEN}@github.com/rongbinf/rongbinf.github.io.git out
cp -f static/font/*.woff2 out/font/
cp -f used-chars.txt out/font/used-chars.txt
cd out
git config user.name github-actions
git config user.email github-actions@github.com
git add font/*.woff2
git add font/used-chars.txt
git commit -m "chore: update subset Source Han Sans fonts" || echo "No changes to commit"
git push