使用字蛛font-spider压缩字体文件
解决方案
项目中遇到有特殊字体要求,且中文字体文件包比较大的时候,为了压缩字体文件一般有两种思路
一、让设计根据常用字将原文件筛选字体子集,只需要给出所需文字的字体包就好。
二、用font-spider对文字进行筛选子集生成新的字体文件-- 官网:http://font-spider.org/
一般的汉字字体都是所有的汉字和简体繁体等都包含,所以这两种方式都是所用字体能够确定,或者使用的都是常用汉字这种。
常用汉字介绍:https://github.com/kaienfr/Font/tree/master/learnfiles
以及:
txt
alphabet: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
numbers: '0123456789'
symbols: '!@#$%^&*()-=_+[]{}\\|;\':",.<>//?¥'
1
2
3
4
2
3
4
这些准备好之后,就开始在项目中使用font-spider进行字体压缩
方案优势
特性
轻巧:数 MB 的中文字体可被压成几十 KB 简单:完全基于 CSS,无需 js 与服务端支持兼容:自动转码,支持 IE 与标准化的浏览器自然:文本支持选中、搜索、翻译、朗读、缩放
原理
字蛛通过分析本地 CSS 与 HTML 文件获取 WebFont 中没有使用的字符,并将这些字符数据从字体中删除以实现压缩,并生成跨浏览器使用的格式。
构建 CSS 语法树,分析字体与选择器规则使用包含 WebFont 的 CSS 选择器索引站点的文本匹配字体的字符数据,剔除无用的字符编码成跨浏览器使用的字体格式
操作步骤
安装font-spider
bash
npm install font-spider -g
1
2
2
检查是否安装成功
bash
font-spider -V
1
2
2
txt
-h, --help 输出帮助信息
-V, --version 输出当前版本号
--info 仅提取 WebFont 信息显示,不压缩与转码
--ignore <pattern> 忽略的文件配置(可以是字体、CSS、HTML)
--map <remotePath,localPath> 映射 CSS 内部 HTTP 路径到本地
--debug 开启调试模式
--no-backup 关闭字体备份功能
--silent 不显示非关键错误
--revert 恢复被压缩的 WebFont
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
检索文字并进行压缩
特别说明: @font-face 中的 src 定义的 .ttf 文件必须存在,其余的格式将由工具自动生成。
- 创建 index.html,引入字体文件
Alibaba.css
,并且在index.html
中抄录所有文字。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="./Alibaba.css" rel="stylesheet" />
<title>Document</title>
</head>
<body>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 将这些文字设定
css:font-family: 'AlibabaPuHuiTi'
,在浏览器中预览index.html
确保字体设置以及生效。
css
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Thin.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Thin.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Thin.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Thin.ttf') format('truetype');
font-weight: 100;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Light.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Light.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Light.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Light.ttf') format('truetype');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Regular.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Regular.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Regular.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Medium.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Medium.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Medium.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_SemiBold.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_SemiBold.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_SemiBold.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_SemiBold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Bold.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Bold.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Bold.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_ExtraBold.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_ExtraBold.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_ExtraBold.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_ExtraBold.ttf') format('truetype');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Heavy.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Heavy.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Heavy.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Heavy.ttf') format('truetype');
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: 'AlibabaPuHuiTi';
src: url('./Alibaba/eot/AlibabaPuHuiTi_Black.eot') format('embedded-opentype'),
url('./Alibaba/woff2/AlibabaPuHuiTi_Black.woff2') format('woff2'),
url('./Alibaba/woff/AlibabaPuHuiTi_Black.woff') format('woff'),
url('./Alibaba/ttf/AlibabaPuHuiTi_Black.ttf') format('truetype');
font-weight: 900;
font-style: normal;
font-weight: bold;
}
html,body{
font-family: AlibabaPuHuiTi;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
- 在当前目录运行
font-spider .\font\index.html
这时候就会在相应的字体文件中生成压缩后的文件。且源文件(ttf格式)会放到新生成的.font-spider文件夹中,如下图
- 现在可以看见字体大小已经从原来8多M压缩到了200Kb左右。
原:
压缩后:
到现在字体文件压缩就算已经全部搞定,可以检测一下项目看是否生效。
注意事项
- font-spider 已经几年没有更新了,所以在使用中不可避免的会出现小问题,其中提示压缩的文件类型一般为ttf或者otf,可以在字体天下网站上下载字体进行测试:https://www.fonts.net.cn/
- 在压缩过程中,会提示Offset is outside the bounds of the DataView ,这种可以重启命令行,重新打开相应文件,删除其中生成的未完成文件,就能运行成功。
提取项目中的所有汉字
js
const fs = require("fs");
const path = require("path");
console.log(__dirname)
console.log('------------------start--------------------')
let filesList = []; //文件路径列表
let jsFilesList = []; //文件路径列表
function readFileList(dir, filesList = []) {
console.log(`------------------${dir}--------------------`)
const files = fs.readdirSync(dir);
files.forEach((item) => {
var fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
readFileList(path.join(dir, item), filesList); //递归读取文件
} else if (fullPath.endsWith(".vue")) {
filesList.push(fullPath);
} else if (fullPath.endsWith(".md")) {
filesList.push(fullPath);
} else if (/(.ts|.js)$/g.test(fullPath)) {
jsFilesList.push(fullPath);
}
});
return filesList;
}
readFileList(path.resolve(__dirname, "blog"), filesList);
readFileList(path.resolve(__dirname, "category"), filesList);
readFileList(path.resolve(__dirname, "gallery"), filesList);
fs.stat(path.resolve(__dirname, "font/local"), function (err, statObj) {
// 判断local文件是否存在,如果不存在则创建,如果创建则直接处理json文件
if (!statObj) {
fs.mkdir(path.resolve(__dirname, "font/local"), function (err) {
writeFile(filesList, "index");
writeFile(jsFilesList, "jsIndex");
});
} else {
writeFile(filesList, "index");
writeFile(jsFilesList, "jsIndex");
}
});
function writeFile(fileArr, fileName) {
const reg_1 =
/(?<!\/\/\s*.*|<!--\s.*)([\u2E80-\u9FFF]*\$?{{0,2}\w*\.*\w*}{0,2}[\u2E80-\u9FFF]+)*/g;
const obj = fileArr.reduce((pre, cur) => {
let fileSuffix = cur.match(/(.ts|.js|.vue|.md)$/g)?.[0];
let pathName = path.basename(cur, fileSuffix);
// 如果文件名是index,取父级文件名
if (pathName === "index") {
const pathArr = cur.split(path.sep);
pathName = pathArr[pathArr.length - 2];
}
pre[pathName] = {};
let pkg = fs.readFileSync(cur, "utf-8");
const strArr = pkg.match(reg_1);
if (strArr?.length) {
strArr.forEach((item, index) => {
if (item.includes("{")) {
let index = 0;
item = item.replace(/\$?{{0,2}\w*\.*\w*}{0,2}/g, function (val) {
if (val) {
index++;
return `{${index - 1}}`;
} else {
return "";
}
});
}
// 如果匹配的字符串的字数大于10,处理key 值
if (item.length) {
let str = item.length >= 10 ? `${item.substring(0, 7)}...` : item;
pre[pathName][str] = item;
}
});
}
return pre;
}, {});
// 创建json文件
fs.writeFile(
path.resolve(__dirname, `src/local/${fileName}.json`),
JSON.stringify(obj),
"utf8",
function (err) {
// console.log(err)
}
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
bash
node .\font_extractor.cjs
1
将生成的 json 内容复制到 index.html
中。