#!/usr/bin/env python3 """ 小程序 iconfont 生成工具(FontAwesome 子集化 + base64 输出) 用法: python3 scripts/iconfont-build.py <输出css路径> [--names name1,name2,...] 示例: python3 scripts/iconfont-build.py node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 src/styles/iconfont.css --names calendar,star,heart 功能: 1. 将 woff2 转为 ttf 2. 按 --names 指定的图标名子集化(保留最小字形,减小包体) 3. 生成 base64 内联 @font-face CSS(font-family: 'fa-icons') 4. 输出 unicode 映射 JSON(供 Icon 组件使用) 图标名 -> unicode 映射(FontAwesome v6 fa-solid): """ import sys import json import base64 import argparse from pathlib import Path from fontTools.ttLib import TTFont from fontTools import subset # FontAwesome v6 fa-solid 图标名 -> unicode 码点 ICON_NAMES = { 'arrow-up': 0xF062, 'arrow-down': 0xF063, 'arrow-left': 0xF060, 'arrow-right': 0xF061, 'check': 0xF00C, 'close': 0xF00D, 'right': 0xF054, 'checkbox-checked': 0xF14A, 'checkbox-unchecked': 0xF0C8, 'plus': 0xF067, 'minus': 0xF068, 'search': 0xF002, 'calendar': 0xF073, 'star': 0xF005, 'heart': 0xF004, 'settings': 0xF013, 'home': 0xF015, 'user': 0xF007, 'info': 0xF129, 'warning': 0xF071, 'refresh': 0xF021, 'download': 0xF019, 'upload': 0xF093, 'share': 0xF064, 'copy': 0xF0C5, 'delete': 0xF1F8, 'edit': 0xF044, 'filter': 0xF0B0, 'sort': 0xF0DC, 'export': 0xF14D, 'history': 0xF1DA, 'bookmark': 0xF02E, 'chevron-up': 0xF077, 'chevron-down': 0xF078, 'chevron-left': 0xF053, 'chevron-right': 0xF054, } def main(): parser = argparse.ArgumentParser(description='FontAwesome iconfont 子集化构建') parser.add_argument('woff2', help='fa-solid woff2 字体路径') parser.add_argument('output_css', help='输出 CSS 路径') parser.add_argument('--names', help='逗号分隔的图标名(默认全部)') parser.add_argument('--font-family', default='fa-icons', help='字体族名') args = parser.parse_args() # 解析图标名 if args.names: names = [n.strip() for n in args.names.split(',') if n.strip()] else: names = list(ICON_NAMES.keys()) # 收集 unicode unicodes = [] mapping = {} missing = [] for n in names: if n not in ICON_NAMES: print(f'⚠️ 未知图标名: {n}'); continue unicodes.append(ICON_NAMES[n]) mapping[n] = ICON_NAMES[n] # 加载并验证 cmap tmp_ttf = Path('/tmp/fa-icons-subset.ttf') font = TTFont(args.woff2) cmap = font.getBestCmap() for n, cp in mapping.items(): if cp not in cmap: missing.append(f'{n}(U+{cp:04X})') if missing: print(f'⚠️ 缺失字形: {", ".join(missing)}') print(f'图标数: {len(mapping)}') # 子集化 options = subset.Options() options.desubroutinize = True options.ignore_missing_glyphs = True options.drop_tables += ['GSUB', 'GPOS', 'GDEF', 'kern', 'cmap'] if False else [] sub = subset.Subsetter(options=options) sub.populate(unicodes=unicodes) sub.subset(font) font.save(str(tmp_ttf)) # base64 b64 = base64.b64encode(tmp_ttf.read_bytes()).decode() print(f'子集 ttf: {(tmp_ttf.stat().st_size/1024):.0f}KB | base64: {(len(b64)/1024):.0f}KB') # 生成 CSS css = f"""/* 自动生成: scripts/iconfont-build.py —— FontAwesome 子集化图标字体(仅含所需字形) */ @font-face {{ font-family: '{args.font_family}'; src: url(data:font/ttf;charset=utf-8;base64,{b64}) format('truetype'); font-weight: normal; font-style: normal; }} """ out = Path(args.output_css) out.parent.mkdir(parents=True, exist_ok=True) out.write_text(css) print(f'CSS 已生成: {out} ({len(css)/1024:.0f}KB)') # 输出映射 JSON(供 Icon 组件) json_path = Path(args.output_css).with_suffix('.json') json_path.write_text(json.dumps(mapping, ensure_ascii=False, indent=2)) print(f'映射已生成: {json_path}') if __name__ == '__main__': main()