使用自定义字体子集优化加载时间 (转译)

  • 更新于 8 7月 2023

问题

自定义字体导致火狐浏览器中的文本闪烁。有关 gif 和更多详细信息,请参阅此问题

解决方案

为了解决这个问题,tabi 为标题加载了一个字形的子集。由于这会(略微)增加初始加载时间,因此尽量减小这个子集的大小是一个好主意。

默认情况下,有英语和西班牙语字符(以及几个符号)的字符集文件。当 Zola 页面/站点设置为该语言时,会加载这些文件。

对于进一步优化,您可以创建一个只包含头部使用的字符的自定义字体子集。

要求

安装以下工具:

运行 pip install fonttools brotli 来安装上述两个工具.

脚本

下面的脚本接受一个 config.toml 文件和一个字体文件作为输入,提取必要的字符,创建一个字体的子集,并生成一个包含基本 64 编码子集的 CSS 文件。

#!/usr/bin/env bash

usage() {
    echo "Usage: $0 [--config | -c CONFIG_FILE] [--font | -f FONT_FILE] [--output | -o OUTPUT_PATH]"
    echo
    echo "Options:"
    echo "  --config, -c   Path to the config.toml file."
    echo "  --font, -f     Path to the font file."
    echo "  --output, -o   Output path for the generated subset.css file (default: current directory)"
    echo "  --help, -h     Show this help message and exit"
}

# Default output is current directory.
output_path="."

# Parse command line options
while [ "$#" -gt 0 ]; do
    case "$1" in
        --config|-c)
            config_file="$2"
            shift 2
            ;;
        --font|-f)
            font_file="$2"
            shift 2
            ;;
        --output|-o)
            output_path="$2"
            shift 2
            ;;
        --help|-h)
            usage
            exit 0
            ;;
        *)
            echo "Unknown option: $1"
            usage
            exit 1
            ;;
    esac
done

# Check if -c and -f options are provided
if [ -z "$config_file" ]; then
    echo "Error: --config|-c option is required."
    usage
    exit 1
fi

if [ -z "$font_file" ]; then
    echo "Error: --font|-f option is required."
    usage
    exit 1
fi

# Check if config and font files exist.
if [ ! -f "$config_file" ]; then
    echo "Error: Config file '$config_file' not found."
    exit 1
fi

if [ ! -f "$font_file" ]; then
    echo "Error: Font file '$font_file' not found."
    exit 1
fi

# Extract the title and menu names from the config file.
title=$(awk -F' = ' '/^title/{print $2}' "$config_file" | tr -d '"')
menu_names=$(awk -F' = ' '/^menu/{f=1;next} /socials/{f=0} f && /name/{print $2}' "$config_file" | cut -d',' -f1 | tr -d '"' )
language_names=$(awk -F' = ' '/^language_name\./{print $2}' "$config_file" | tr -d '"' )

# If the site is multilingual, get the menu translations.
if [ -n "$language_names" ]; then
    for menu_name in $menu_names; do
        # Find the line with the menu name inside a [languages.*.translations] section and get the translated menus.
        menu_translation=$(awk -F' = ' "/\\[languages.*\\.translations\\]/{f=1;next} /^\\[/ {f=0} f && /$menu_name =/{print \$2}" "$config_file" | tr -d '"' )
        # Add the found menu value to the translations string
        menu_names+="$menu_translation"
    done
fi

# Combine the extracted strings.
combined="$title$menu_names$language_names"

# Get unique characters.
unique_chars=$(echo "$combined" | grep -o . | sort -u | tr -d '\n')

# Create a temporary file for subset.woff2.
temp_subset=$(mktemp)

# Create the subset.
pyftsubset "$font_file" \
    --text="$unique_chars" \
    --layout-features="*" --flavor="woff2" --output-file="$temp_subset" --with-zopfli

# Remove trailing slash from output path, if present.
output_path=${output_path%/}

# Base64 encode the temporary subset.woff2 file and create the CSS file.
base64_encoded_font=$(base64 -i "$temp_subset")
echo "@font-face{font-family:\"Inter Subset\";src:url(data:application/font-woff2;base64,$base64_encoded_font);}" > "$output_path/custom_subset.css"

# Remove the temporary subset.woff2 file.
rm "$temp_subset"

使用方法

将脚本保存在类似 ~/bin/subset_font 的地方。使用 chmod +x ~/bin/subset_font 使其可执行。

现在您可以使用所需的 --config--font 选项运行它:

~/bin/subset_font --config path/to/config.toml --font path/to/font.woff2

By default, this generates a custom_subset.css file in the current directory. Use -o or --output to specify a different path:

默认情况下,此操作会在当前目录生成一个 custom_subset.css 文件。使用 -o--output 指定不同的路径:

~/bin/subset_font -c path/to/config.toml -f path/to/font.woff2 -o path/to/output

你应该将这个 custom_subset.css 文件放置在 static/ 目录中。

使用预提交钩子自动化

您可能更改网站的标题或菜单选项,使得自定义子集不再有用。

要自动化创建此文件的过程,您可以将脚本集成到 Git 预提交钩子中,该钩子检查 config.toml 文件的变化,运行脚本,并将生成的 CSS 文件存储在您的站点 static/ 目录中。

  1. 在 Git 项目中创建一个 .git/hooks/pre-commit 文件(如果尚不存在)。

  2. 使用 chmod +x .git/hooks/pre-commit使其可执行。

  3. 在文件中添加以下代码:

# Check if config.toml has been modified.
if git diff --cached --name-only | grep -q "config.toml"; then
    echo "config.toml modified. Running subset_font…"

    # Call the subset_font script.
    ~/bin/subset_font -c config.toml -f static/fonts/Inter4.woff2 -o static/

    # Add the generated subset.css file to the commit.
    git add static/custom_subset.css
fi

确保修改脚本以匹配您存储 subset_font 脚本的路径。配置和字体路径应该与 Tabi 的默认设置一起正常工作。

现在,每次您对您的 Git 项目进行提交时,预提交钩子将会检查 config.toml 文件中的修改并自动运行 subset_font 脚本以更新 custom_subset.css 文件

顺便说一下,如果您对自动更新您的 Zola 文章的日期或压缩 PNG 文件感兴趣,请查看此文章

如果你想一次使用所有脚本(压缩 png 文件、更新日期和创建字体子集),可以将它们的代码合并到一个 .git/hooks/pre-commit 文件中。