当前位置: 首页 > news >正文

实现前端.ttf字体包的压缩

前言

  • 平常字体包都有1M+的大小,所以网络请求耗时会比较长,所以对字体包的压缩也是前端优化的一个点。
  • 但是前端如果想要特点字符打包成字体包,网上查阅资料后,都是把前端代码里面的字符获取,但是对于动态的内容,是没有进行处理的。

思路

获取到前端所有的字符,以及数据库所有的字符,对这些字符打包成字体包
每次上线前检查字符是否与之前的一致,不一致则重新打包

  • 最开始的思路是通过nodejsfontmin依赖包去实现,但是该包对有的字体包没有作用(如:PingFangSC-Regular.ttf字体),而有的也有作用(如:Alimama_ShuHeiTi.ttf字体)。
    • 在使用fontmin的时候也要注意,如果使用require引入,版本需要控制在v1内
  • 后面选取了python技术栈fontTools
    • 没有对单个字体包考虑使用了什么字符,这样有点麻烦,所以在一样的字符集上处理。

环境

  • 如果代码运行有问题,可以考虑是不是版本不匹配
  • 版本
Python 3.9.6
fontTools 4.56.0
mysql.connector 9.2.0
  • 依赖安装
pip install fontTools
pip install mysql-connector-python

python代码

import os
import re
import fontTools
from fontTools import subset
print("fontTools ", fontTools.__version__)
import mysql.connector
print("mysql.connector ", mysql.connector.__version__)

# 获取数据库字符集
def obtain_mysql_characters(config) -> set:
    # 建立数据库连接
    cnx = mysql.connector.connect(**config)
    cursor = cnx.cursor()
    # 获取所有表名
    cursor.execute("SHOW TABLES")
    tables = cursor.fetchall()
    # 初始化字符集合
    all_characters = set()
    # 遍历每个表获取字符数据
    for table in tables:
        table_name = table[0]
        cursor.execute(f"SELECT * FROM {table_name}")
        rows = cursor.fetchall()
        for row in rows:
            for column in row:
                if isinstance(column, str):
                    for char in column:
                        all_characters.add(char)
    # 关闭数据库连接
    cursor.close()
    cnx.close()
    print("数据库字符数量 ", len(all_characters))
    return all_characters


# 获取前端项目字符集合
def obtain_font_characters(font_path_dir) -> set:
    # 初始化字符集合
    all_characters = set()
    # 遍历前端项目文件
    for root, dirs, files in os.walk(font_path_dir):
        for file in files:
            # 处理Vue文件,使用者可根据实际情况修改
            if file.endswith(('.vue')):  
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                        # 过滤注释
                        content = re.sub(r'//.*?\n|/\*.*?\*/', '', content, flags=re.DOTALL)
                        for char in content:
                            all_characters.add(char)
                except Exception as e:
                    print(f'读取文件 {file_path} 时出错: {e}')
    print("前端项目字符数量 ", len(all_characters))
    return all_characters

# 对单个字体包进行子字符集提取
def obtain_font_characters_single(font_path, text, store_dir) -> set:
    # 通过font_path获取字体名称
    font_name = os.path.basename(font_path)
    # 嵌入代码中,实现生成字体子集
    options = subset.Options()
    # 加载字体
    font = subset.load_font(font_path, options)
    subsetter = subset.Subsetter(options=options)
    subsetter.populate(text=str(text))
    subsetter.subset(font)
    # 保存字体子集,存放在store_dir下,字体名称为font_name
    subset.save_font(font, os.path.join(store_dir, font_name), options)

# 获取目录下,所有的ttf字体文件
def obtain_font_files(font_path_dir) -> list:
    font_files = []
    for root, dirs, files in os.walk(font_path_dir):
        for file in files:
            if file.endswith(('.ttf')):
                font_files.append(os.path.join(root, file))
    return font_files


# 主函数
if __name__ == "__main__":
    # 数据库配置
    mysqlConfig = {
        'user': 'user',
        'password': 'password',
        'host': 'localhost',
        'database': 'database',
        'port': '3306'
    }
    # 根据实际情况修改为前端项目的根目录,使用正斜杠或者双斜杠避免转义问题 e.g. E:\\xx\\xx\\src
    vue_project_dir = 'D:\\xxx\\src'
    # 字体文件目录
    font_dir = 'D:\\xxx\\src\\assets\\fonts-original'
    # 字体文件存放目录
    store_dir = 'D:\\xxx\\src\\assets\\fonts'


    # 获取数据库字符集
    mysql_characters = obtain_mysql_characters(mysqlConfig)
    # 获取前端项目字符集合
    font_characters = obtain_font_characters(vue_project_dir)
    # 计算并集
    intersection = mysql_characters | font_characters
    # 输出结果
    print("交集字符数量 ", len(intersection))
    # 字体文件列表
    font_files = obtain_font_files(font_dir)
    # 输出字体文件列表
    print("字体文件列表 ", font_files)
    # 对每个字体文件进行子字符集提取
    for font_file in font_files:
        obtain_font_characters_single(font_file, intersection, store_dir)

效果

  • 目录下面7个字体包
    font

出现的问题

meta NOT subset; don’t know how to subset; dropped

  • AI的解释(字体包亲测使用没有异常):在字体子集化过程中,meta表无法被处理,因此被丢弃。这通常不会影响字体的核心功能,但可能会导致某些元数据丢失。建议检查pyftsubset工具的版本和字体文件的完整性。

参考资料

  • github仓库fonttools
  • fonttools官方文档

相关文章:

  • 日程提醒类软件选择用哪个?理由是什么?
  • error LNK2019: 无法解析的外部符号 main,函数 “int __cdecl invoke_main(void)“
  • 信创系统极速文件查找:locate 命令详解
  • 【初学者】请介绍一下指针分析(Pointer Analysis)?
  • 三.Go的第一个程序hello.go
  • Python pyqt+flask做一个简单实用的自动排班系统
  • 天梯赛 L2-010 排座位
  • MATLAB例程:TOA测距定位,三维任意(>3)个锚节点,对一个未知点定位、带EKF的轨迹解算(附代码下载链接)
  • Python核心:Django配置swagger的详细步骤和代码举例
  • 如何实现一个call函数?
  • TCP 协议
  • Java EE(12)——网络编程——UDP/TCP回显服务器
  • 2024年国赛高教杯数学建模E题交通流量管控解题全过程文档及程序
  • C语言编译链接详解
  • Matplotlib 柱形图
  • 卡码网25题——掌握ACM输入输出方式(15 至 18)
  • 【大模型面试知识】基础问题分析总结
  • AnyTouch:跨多个视觉触觉传感器学习统一的静态动态表征
  • @Resource注解的两种注入方式
  • Prometheus使用
  • 上海启动万兆光网试点建设,助力“模速空间”跑出发展加速度
  • 绿城房地产集团:近半年累计花费20.6亿元购买旗下债券
  • “救护车”转运病人半路加价,从宝鸡到西安往返都要多收钱
  • 上海将发布新一版不予行政处罚清单、首份减轻行政处罚清单
  • 美联储主席:美联储工作方式不会受特朗普影响,从未寻求与总统会面
  • 上海市政府党组会议传达学习习近平总书记重要讲话精神,部署抓好学习贯彻落实