惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
Security Latest
Security Latest
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
Cybersecurity and Infrastructure Security Agency CISA
Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
大猫的无限游戏
大猫的无限游戏
C
CXSECURITY Database RSS Feed - CXSecurity.com
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
雷峰网
雷峰网
C
Cisco Blogs
V
Vulnerabilities – Threatpost
S
Security Archives - TechRepublic
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
J
Java Code Geeks
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Know Your Adversary
Know Your Adversary
博客园 - 叶小钗
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Privacy International News Feed
P
Palo Alto Networks Blog
博客园_首页
V
V2EX
WordPress大学
WordPress大学
Schneier on Security
Schneier on Security
月光博客
月光博客
博客园 - 司徒正美
Google DeepMind News
Google DeepMind News
TaoSecurity Blog
TaoSecurity Blog
博客园 - 聂微东
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
The Cloudflare Blog
罗磊的独立博客
美团技术团队
N
News | PayPal Newsroom
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Last Week in AI
Last Week in AI
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
S
SegmentFault 最新的问题
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog

优世界

OpenWrt 路由器改纯 AP 模式记录(Cudy TR3000 + 中兴 F50) Ubuntu 24.04 安装 Zabbix 8.0 全记录 我的静态博客动态化方案 Claude Code 启动脚本 我的5G路由器方案:中兴F50+cudy tr3000 256MB 还是弃用了使用多年的全拼输入法 临走前一份襄阳牛肉面 EdgeOne Pages 部署 Twikoo 评论 一个朋友圈风格 Hugo 主题 搓了一个仿朋友圈的Hugo主题 一个人烧烤被拒单了 周末闲暇时间翻修了一下博客 我把Vercel换成了EdgeOne Pages 博客友链实时健康监测方案 Artalk评论区接入AI摘要的尝试 给Hugo博客添加瀑布流相册功能 Hugo静态博客如何实现搜索功能 Artalk评论系统实现段落评论功能 为什么我觉得网页昼夜切换那么重要 博客六周年:从折腾到回归平淡 如何hugo静态实现友联朋友圈功能 脂溢性皮炎的烦恼 还是放弃了iPhone16e 米环勿扰同步问题 除草日记 Hugo使用GitHub Action自动刷新多吉云CDN缓存 迁移博客至hugo 添加ikun摆件 parsec远程软件报6023错误 win11右键菜单改回win10方法 主板电流声 主机配置单 一次点亮 访客体验优化 博客除草 老爷机升级 完善主题 家乡随拍 襄阳唐城 进厂日记 苏州一日游 四月随笔 评论置顶 Time Taker 近来二三事 感谢哥哥给的网站 又又又双叒叕换主题啦 生活篇:疫情放开前后的这些日子 .cc域名后缀续费即将涨价 Apple Watch Series7 三个月的使用体验 2022年·襄阳第一场雪,谨以此片记录 记一次莫名其妙ddos攻击,致谢天御云高防cdn 毕业篇:不出意外,这是我最后几个月的大学生活啦 毕业纪念篇:图书馆 生活篇:我的三年封校生活 没错,我回来了,湖北管局一天不到通过备案,强的! 因需要更换备案主体,临时关闭博客通知 记录篇:将笔记本联想小新pro13网卡由螃蟹网卡换成AX210网卡 主力机由荣耀20切换到iPhone13使用半年的感受 生活篇:温馨提示假期余额不足,浅浅总结一下这个暑假 typecho实现QQ头像用户评论加密,注意:pigeon,twitter用户有彩蛋哦! 生活篇:离校,暑假,租房,面试,致那些日子的琐碎事 生活篇:我跳绳的那些日子,谈谈自己的变化 去除typecho1.2.0正式版的后台提示更新bug 利用fontspider压缩博客字体大小,达到加快访问速度 iphone快捷指令发布动态说说,支持大部分typecho主题 寒假二三事 2022,除夕过后的那些事 2022,致我的春节回忆录 祝大家元旦快乐,给自己的博客加一个对联和灯笼 更换掉jsdelivr,改用腾讯云静态网站托管,网站速度比之前提升了不少 这次的落日比较有特色,西边摇摇欲坠的咸鸭蛋 盘点一下建站以来所注册的域名,我居然注册了一堆学费米 可惜不能一直做小孩子,总要长大,不知道实习生活会是什么样子 这组照片的主题,咱就叫它光吧 双十一已经变味了,不知道从什么时候反感双十一了 第一次尝试ai画简笔画,本是给自己设计logo误打误撞画起了简笔画 不用改变图片原地址,实现图片自动转webp格式,速看,一会删 让typecho支持webp格式的图片,告别阿里云oss和腾讯云cos被恶意刷流量的风险 运动会闲暇这几天,简单给大家分享一下日常吧 我又双叒叕换主题,我发现我好像一直在折腾 记录人生第一次洗牙,不得不说感觉真的特别好 我为什么要写博客?这位博主给了我答案 我想,这是一个我人生中最特殊的中秋国庆节 理工的晚霞,拿起相机记录青春的样子 别让抖音支配了你的美好大学生活,尝试做一些自己感兴趣的事 一岁一礼,一寸欢喜,生日快乐,致我的二十岁 2021暑假总结,记录一下这个充实的暑假 Twitter主题设置仿mac UI 语法高亮代码方法 Twitter主题加入加载耗时,访问总量功能 一把过,科二结束! 再次投资科目二,科目二花了400,希望19号一把过吧 宝塔面板设置Typecho伪静态去掉index.php教程 开学倒计时,再见,老家 或许不是没有年味了,只是快乐已经不属于我们这一辈人了 五福开奖,你好,2021 新春快乐!牛年大吉! 祝母校越办越好 军训太痛苦了,希望早点结束 高考加油,相信自己
用 Python 给博客字体瘦了一下身
2026-06-04 · via 优世界

前言

之前18兄弟推荐我换到astro框架。

jb18.cm https://jb18.cm

这个astro框架我之前了解过,按需加载,0 JavaScript,还支持SSG、SSR和中间件,既可以静态也可以动态。

docs.astro.js.cn https://docs.astro.js.cn/en/getting-started

目前这个主题我已经使用了五年了,里面堆砌着大大小小的屎山,要是迁移的话算是一次不小的工程。后面考虑了一下,还是继续待在hugo。

既然不准备换到astro,那么就借鉴一下astro的优点,取长补短。

周末花了一点时间给博客做了一波JS按需加载优化,效果还不错——首页JS从800KB直接砍到了350KB。

打开Chrome DevTools的Network测试了一下,最影响加载速度的还是字体文件。从开始使用这个主题到现在一直使用的这个字体,看习惯了,也没必要因为字体文件大就换。看了一下字体文件大小大概有1.2MB,反正比我的JS还大。

说起来,之前也写过一篇用fontspider压缩字体的文章,不过那个工具年久失修,对静态博客支持不太好。最近正好在学Python,发现了一个更好用的工具——fonttools

这篇就跟各位朋友来聊聊怎么用fonttools,给字体瘦瘦身。

为什么字体文件这么大?

先给大家科普一下,为什么中文字体动不动就几MB。

中文字和英文字不一样。英文字母就26个大小写,加上数字和符号,撑死几百个字符。但中文呢?《通用规范汉字表》收录了8105个汉字,如果算上生僻字,得有好几万。

而字体文件里面,是把所有字符的字形都打包进去的。也就是说,不管你用没用到“龘”这个字,它都在你的字体文件里躺着,白白占空间。

但实际上,一个博客能用到多少字呢?我统计了一下我的博客,也就2500个字符左右。

1.2MB的字体,实际只需要700多KB,剩下的都是浪费。

什么是fonttools?

fonttools是一个Python库,专门用来处理字体文件。它能做很多事情,比如读取字体信息、转换格式、提取字符等等。

我们要用到的功能是字体子集化(Font Subsetting)——简单来说,就是只保留我们用到的字符,把其他的都扔掉。

安装环境

首先,你需要安装Python。这个就不多说了,去官网下载安装就行:

www.python.org https://www.python.org/downloads/

安装的时候记得勾选 “Add Python to PATH”,这样就能在命令行里直接用了。

装好之后,打开cmd,输入:

能看到版本号就说明装好了。

然后安装fonttools和brotli(用于压缩):

pip install fonttools brotli

如果提示权限不够,加上 --break-system-packages

pip install fonttools brotli --break-system-packages

编写优化脚本

我写了一个Python脚本,可以自动扫描博客的所有内容,提取用到的字符,然后生成精简版字体。

如果你是Hugo框架,可以直接套用我的脚本;如果是其他框架,可以把这篇文章或者关键词喂给AI,也是同理。

首先在项目根目录创建一个文件:scripts/subset-font-safe.py

#!/usr/bin/env python3
"""
字体子集化脚本
从HTML和CSS文件中提取实际使用的字符,生成优化的子集字体
"""

import os
import re
from fontTools.ttLib import TTFont
from fontTools.subset import Subsetter, Options

def extract_chars_from_files(directories):
    """从文件中提取使用的字符"""
    chars = set()

    for directory in directories:
        if not os.path.exists(directory):
            print(f"⚠️  目录不存在,跳过: {directory}")
            continue

        print(f"🔍 扫描目录: {directory}")

        for root, dirs, files in os.walk(directory):
            for file in files:
                if file.endswith(('.html', '.md', '.css')):
                    filepath = os.path.join(root, file)
                    try:
                        with open(filepath, 'r', encoding='utf-8') as f:
                            content = f.read()
                            # 提取中文字符
                            chinese_chars = re.findall(r'[一-鿿]', content)
                            chars.update(chinese_chars)
                            # 提取英文和数字
                            ascii_chars = re.findall(r'[a-zA-Z0-9]', content)
                            chars.update(ascii_chars)
                            # 提取常用标点
                            en_punct = set('!@#$%^&*()_+-=[]{}|;:,.<>?/\\`~"\'-')
                            chars.update(en_punct)
                    except Exception as e:
                        print(f"⚠️  读取失败 {filepath}: {e}")

    return chars

def main():
    print("🔤 字体子集化工具")
    print("=" * 50)

    # 配置路径
    font_dir = "themes/Ying/static/font"
    input_font = os.path.join(font_dir, "zql-v2.woff2")
    chars_file = os.path.join(font_dir, "used_chars.txt")

    # 检查输入文件
    if not os.path.exists(input_font):
        print(f"❌ 字体文件不存在: {input_font}")
        return

    # 读取现有的字符列表(如果有)
    existing_chars = set()
    if os.path.exists(chars_file):
        with open(chars_file, 'r', encoding='utf-8') as f:
            existing_chars = set(f.read())
        print(f"📖 读取现有字符: {len(existing_chars)} 个")

    # 扫描目录
    scan_dirs = ["content", "layouts"]
    if os.path.exists("public"):
        scan_dirs.append("public")

    # 提取字符
    new_chars = extract_chars_from_files(scan_dirs)
    print(f"📝 从文件提取了 {len(new_chars)} 个字符")

    # 合并字符(保留手动添加的字符)
    all_chars = existing_chars | new_chars
    print(f"📊 合并后: {len(all_chars)} 个字符")

    # 保存字符列表
    with open(chars_file, 'w', encoding='utf-8') as f:
        f.write(''.join(sorted(all_chars)))

    # 生成woff2子集
    output_woff2 = os.path.join(font_dir, "zql-v2-subset.woff2")
    print(f"\n🎯 生成 woff2 子集字体...")
    
    font = TTFont(input_font)
    options = Options()
    options.flavor = 'woff2'
    
    subsetter = Subsetter(options=options)
    subsetter.populate(text=''.join(all_chars))
    subsetter.subset(font)
    font.save(output_woff2)

    # 计算优化效果
    original_size = os.path.getsize(input_font)
    subset_size = os.path.getsize(output_woff2)
    reduction = original_size - subset_size
    percentage = (reduction / original_size) * 100

    print(f"\n✅ 优化完成!")
    print(f"📊 原始大小: {original_size / 1024:.1f} KB")
    print(f"📊 子集大小: {subset_size / 1024:.1f} KB")
    print(f"📊 减少: {reduction / 1024:.1f} KB ({percentage:.1f}%)")

if __name__ == "__main__":
    main()

脚本主要干了什么?

其实这个脚本的逻辑很简单,主要分三步:

  1. 扫描:遍历 contentlayoutspublic 目录,找出所有用到的字符。
  2. 提取:把中文、英文、数字、标点符号都提取出来。
  3. 生成:用fonttools生成只包含这些字符的字体。

这里有个细节:脚本会读取 used_chars.txt 里手动添加的字符。这样如果你有一些特殊字符(比如我博客里的古风官职表),可以手动加进去,不会被覆盖。

运行效果

首先,确保你已经构建过Hugo:

hugo --destination=public

然后运行脚本:

python scripts/subset-font-safe.py

我这边的运行结果:

🔤 字体子集化工具
==================================================
📖 读取现有字符: 2489 个
🔍 扫描目录: content
🔍 扫描目录: layouts
🔍 扫描目录: public
📝 从文件提取了 2492 个字符
📊 合并后: 2500 个字符

🎯 生成 woff2 子集字体...

✅ 优化完成!
📊 原始大小: 1226.6 KB
📊 子集大小: 740.7 KB
📊 减少: 485.9 KB (39.6%)

1.2MB的字体,优化后只剩740KB,减少了40%!

更新CSS

字体文件生成了,还需要把你的字体CSS文件替换一下路径。打开 themes/Ying/assets/css/main.css,找到字体声明:

@font-face {
    font-family: 'zql';
    src: url('../font/zql-v2.woff2') format('woff2'),
        url('../font/zql-v2.woff') format('woff');
    font-display: swap;
    unicode-range: U+0000-007F, U+4E00-9FFF, U+2000-206F, U+3000-303F;
}

改成:

@font-face {
    font-family: 'zql';
    src: url('../font/zql-v2-subset.woff2') format('woff2'),
        url('../font/zql-v2-subset.woff') format('woff');
    font-display: swap;
    unicode-range: U+0000-007F, U+4E00-9FFF, U+2000-206F, U+3000-303F;
}

这样就OK了,还是很简单的。如果你觉得手动执行py脚本比较麻烦,其实编写GitHub Action自动处理字体也是一个不错的选择。