Rongbin Fan

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