Chinese webfont subsetting & font splitting — 中文字体分包 / 子集化工具:按 unicode-range
把 CJK 字体切成 woff2 分片并生成 @font-face,让浏览器只下载页面真正用到的字。纯本地运行,
字体文件全程不出本机。
- 支持
.ttf.otf.woff2.ttc.otc,输出 woff2 / woff / ttf - 分片模式与兜底字表可按需组合,分片产物可逐片或一次性下载
- 内置「字体��览」「分片清单」「体积对比」「按需加载模拟」
一个完整的中文字体往往 5–20MB,但一个网页真正用到的字通常只有几百到几千个。 Zitrange 会:
- 根据你提供的「字符集」从字体里裁出需要的字形(子集化);
- 按频次把字符切成若干递增大小的分片(高频字进小片);
- 为每片生成带
unicode-range的@font-face,浏览器按需下载。
典型效果:原始 12MB 字体 → 实际首屏只下载几十 KB 的命中分片。
同赛道的工具还有 font-spider(字蛛)、fontmin、cn-font-split(中文网字计划)等:它们多在构建期 自动扫描源码取字;Zitrange 走的是「本地可视化 + 可调分片策略 + 产物落盘」的路子——排序可按 文本频次 / 通用字频 / 码位邻近 / 码块均分四种,配合兜底字表与 ASCII 首屏片自由组合, 且不做任何上传。
| 依赖 | 版本 | 用途 |
|---|---|---|
| Node.js | >= 20 | 前端(Vite)与本地 API(tsx) |
| Python 3 | 3.10+ | 子集化引擎(fontTools.subset) |
| fontTools / brotli | 见 requirements.txt |
由 npm run setup 自动装进 .venv |
# 1) 一键准备:创建 Python 虚拟环境并安装依赖 + 安装前端依赖
npm run setup
# 2) 同时启动前端(5173) 与 本地 API(5174)
npm run dev启动成功后:
- 前端界面:http://localhost:5173
- 本地 API(无需手动访问):
http://127.0.0.1:5174,已由 Vite 代理到/api与/output
若你已手动建好 Python 环境,也可分步执行:
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt npm install npm run dev
打开 http://localhost:5173,界面从左到右分四步。
- 拖拽或点击选择字体文件,支持:
.ttf.otf.woff2.ttc.otc - 文件只在本机磁盘落盘,不会上传到任何服务器
- 加载成功后,右侧顶部会出现「字体预览」面板(用你上传的字体渲染一段可编辑文字 + 字号滑块)
.ttc/.otc是字体集合,默认取首个字形面(fontNumber = 0)。
决定「切哪些字」:把网站的正文、导航、标题、按钮文案粘贴进来即可。 工具会按字去重并统计字数。
- 默认就是「拆分全量字体」:开关默认开启,工具会直接用字体 cmap 的全部码位出片, 绕过兜底字表上限,生僻字 / 扩展区 / 符号一律切出(此时下方文案输入框与兜底字表隐藏、不生效)。 想改为「只切网站实际用到的字」,关闭该开关即可,输入框会重新出现。
- 留空也可以:此时只用下方「兜底字表」来选字(见步骤 03)。
- 展开「我想预测单个页面的加载量」可粘贴某一个页面的文案,仅用于模拟首屏下载量, 不会改变产物。
| 配置项 | 说明 | 默认值 |
|---|---|---|
| 输出格式 | woff2(默认,体积小)/ woff(兼容老浏览器)/ ttf(非 Web 场景),可多选 | woff2 |
| 分片模式 | 见下 | 混合 |
| 单片字数 (baseSize) | 每片基准字数 | 200 |
| 递增系数 (growth) | >1 时低频片更大;1 = 等大小片 | 1.35 |
| 单片上限 (maxSize) | 单片的字数上限 | 800 |
| 兜底字表 | 站点未覆盖的字,按通用字频补全 | 常用字前 3500 |
三种分片模式:
- 混合:站点用字优先排前,字频表兜底补全生僻字。兼顾二者,适合大多数站点。
- 字频:完全按通用字频降序切分。适合内容不可预知的博客、CMS、UGC。
- 站点:只打包你粘贴到的字符。适合文案固定的落地页、活动页(此时兜底被强制关闭)。
点击左下角 「生成分片」 按钮,右侧即出现结果。
右侧会依次展示:
- 体积对比:原始字体大小 → 分片合计 →(若填了样本文本)模拟首屏实际下载量,三级对比。
- 实时校验:在「分片策略」面板里,参数一改即时提示合理性(如频率模式需选兜底字表、单片上限过大、字符集超出字体字形数将缺字回退等),仅提示不阻止。
- 分片清单:
- 每片显示序号
#、字数、覆盖的字符样本(鼠标悬停查看精确unicode-range)、文件大小; - 高亮行 = 样本文本会命中的片;
- 每片行内可下载该片的各格式产物文件(
woff2/woff/ttf),底部提供「下载全部」(详见第 8 节)。
- 每片显示序号
- 产物预览:可直接复制或下载的
@font-faceCSS(见第 6 节部署)。
生成的分片文件写在本地磁盘的:
output/<jobId>/
├── font-0.woff2
├── font-1.woff2
├── ...
└── font-N.woff2
当前界面没有「下载 zip」按钮,产物直接落在本项目的
output/目录,从那里取即可。
部署步骤:
- 复制「产物预览」面板里的整段 CSS;
- 把
output/<jobId>/整个文件夹上传到你的站点(与 CSS 中引用的路径一致); - 在你的页面
<head>引入这段 CSS。
CSS 示例(已自动生成,unicode-range 让浏览器按需下载):
@font-face {
font-family: 'YourFont';
src: url('/output/<jobId>/font-0.woff2') format('woff2');
font-display: swap;
unicode-range: U+4E00-4E7F, U+4E80-4EFF, ...;
}
/* ……每个分片一段…… */生产环境请把 CSS 里的
/output/<jobId>/前缀替换为你实际放置字体文件的 URL 路径 (例如https://cdn.example.com/fonts/yourfont/)。
打包成 zip(可选):
cd output/<jobId>
zip -r ../../yourfont-chunks.zip .工具按字符集切分,字符集 = 你粘贴的文案 + 兜底字表(自动与字体实际支持的码位取交集)+ ASCII/标点保底。 字体里没有的字会被自动跳过(不会报错,也不会产出对应字形)。
| 目标 | 推荐设置 |
|---|---|
| 拆出字体里的每一个字形(全部分片) | 步骤 02 的 「拆分全量字体」 开关(默认开启) |
| 覆盖常见中文全部(GB2312,6763 字) | 模式选 混合 或 字频;兜底字表选 GB2312 全集 |
| 覆盖常用 3500 / 7000 字 | 兜底字表选 常用字前 3500 / 7000 |
| 只切网站实际用字 | 模式选 站点,并把网站文案粘进步骤 02 |
| 覆盖字体里更多字形(GBK、生僻字、Unicode 扩展等) | 把完整字符清单粘贴进步骤 02 文案框;内置表最高到 GB2312 |
要点:
- 「站点」模式只切你粘贴的字、不补兜底,不适合「全部分片」场景。
- 注意内置字频表上限:当前内置字频表只有约 1766 字(近似数据,详见
src/core/assets/charfreq-zh.ts文件头说明),因此即便选「GB2312 全集(6763)」, 实际最多也只能补到约 1766 字。要真正拆出字体里的全部字形,请用 「拆分全量字体」 开关。 - 全量模式 = 字体 cmap 的全部码位,无需粘贴任何文案,是「拆成全部分片」最简单的方式。
- 全量模式 / GB2312 全集都会与字体 cmap 取交集,只会切字体里确实包含的字,避免产生空片。
- 逐片下载:每片行内可下载该片的各格式产物文件(
woff2/woff/ttf)。 - 下载全部:底部「下载全部」一次性打包所有分片文件与
@font-faceCSS 为.zip。
src/
web/ 前端界面(React + Vite)
api/ 本地 API 服务(上传 / 检视 / 处理 / 读盘)
adapters/ Node ↔ Python 引擎适配(fontEngine / pipeline)
engine/ Python 子集化引擎(fontTools.subset,两阶段提速)
core/ 纯函数:字符集、分片、模拟、校验(validate)
demo/ 示例字体(可拖入试用)
output/ 生成的分片产物落盘处
- 字体不出本机吗? 是。全部在本地
npm run dev进程内完成,文件只读写本机磁盘。 .ttc/.otc为什么只切出一部分字? 集合体默认取首个字形面,可在代码/接口传入fontNumber切换。- 为什么产物里没有某个字? 该字不在你提供的字符集(文案 + 兜底)与字体 cmap 的交集中; 扩大兜底字表或补充文案即可。
- Python 报错
No module named fontTools? 确认已执行npm run setup(或手动在.venv里pip install -r requirements.txt)。
| 命令 | 作用 |
|---|---|
npm run setup |
建 .venv + 装 Python 依赖 + npm install |
npm run dev |
启动前端(5173) 与 API(5174) |
npm run dev:web / npm run dev:api |
仅启动前端 / 仅启动 API |
npm run build |
类型检查 + 构建 |
npm run test |
运行单元测试 |
npm run typecheck |
仅类型检查 |
Zitrange subsets and splits CJK / Chinese webfonts into small woff2 chunks by unicode-range,
then generates ready-to-paste @font-face CSS, so the browser downloads only the glyphs a page
actually uses. Everything runs locally — your font files never leave the machine.
npm run setup # .venv + fontTools / brotli + npm install
npm run dev # web UI on :5173, local API on :5174- Load a font —
.ttf.otf.woff2.ttc.otc. - Pick the character set — paste your site copy, or keep split the whole font (font cmap) on.
- Choose a strategy preset — smallest size / balanced / fewest requests. Advanced knobs (ordering mode, fallback charset, ASCII first-slice) live in a collapsed section.
- Generate — compare sizes, inspect every chunk, copy the
@font-faceCSS, download chunks one by one or as a single.zip. Output is written tooutput/<jobId>/.
English: font subsetting · webfont subsetting · CJK font · Chinese webfont · font splitting · font chunking · unicode-range · woff2 · fontTools · pyftsubset · web performance · self-hosted
中文:中文字体分包 · 字体子集化 · 字体切割 · 中文 Web 字体优化 · webfont 压缩 · 按需加载字体 · 字体分片
同类:font-spider(字蛛)· fontmin · cn-font-split(中文网字计划)· pyftsubset
MIT © 2026 yuxing.wang
