Files
xiaozhi-esp32/managed_components/78__xiaozhi-fonts/generate_fonts.ipynb
2025-09-05 13:25:11 +08:00

834 lines
59 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "markdown",
"id": "3036ad45",
"metadata": {},
"source": [
"# 生成字体步骤\n",
"\n",
"- 获取字符列表\n",
"- 从字体中提取子集,合并成一个完整的字体文件\n",
"- 生成不同 size 与 bpp 的点阵字体"
]
},
{
"cell_type": "markdown",
"id": "93205a30",
"metadata": {},
"source": [
"## 获取字符列表\n",
"\n",
"这里使用 DeepSeek 的词表来提取常用字符"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "45820776",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: modelscope in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (1.28.1)\n",
"Requirement already satisfied: transformers in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (4.54.1)\n",
"Requirement already satisfied: requests>=2.25 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from modelscope) (2.32.3)\n",
"Requirement already satisfied: setuptools in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from modelscope) (75.8.0)\n",
"Requirement already satisfied: tqdm>=4.64.0 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from modelscope) (4.67.1)\n",
"Requirement already satisfied: urllib3>=1.26 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from modelscope) (2.4.0)\n",
"Requirement already satisfied: filelock in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (3.18.0)\n",
"Requirement already satisfied: huggingface-hub<1.0,>=0.34.0 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (0.34.3)\n",
"Requirement already satisfied: numpy>=1.17 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (2.2.6)\n",
"Requirement already satisfied: packaging>=20.0 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (25.0)\n",
"Requirement already satisfied: pyyaml>=5.1 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (6.0.2)\n",
"Requirement already satisfied: regex!=2019.12.17 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (2025.7.34)\n",
"Requirement already satisfied: tokenizers<0.22,>=0.21 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (0.21.4)\n",
"Requirement already satisfied: safetensors>=0.4.3 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from transformers) (0.5.3)\n",
"Requirement already satisfied: fsspec>=2023.5.0 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from huggingface-hub<1.0,>=0.34.0->transformers) (2025.7.0)\n",
"Requirement already satisfied: typing-extensions>=3.7.4.3 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from huggingface-hub<1.0,>=0.34.0->transformers) (4.14.1)\n",
"Requirement already satisfied: hf-xet<2.0.0,>=1.1.3 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from huggingface-hub<1.0,>=0.34.0->transformers) (1.1.5)\n",
"Requirement already satisfied: charset-normalizer<4,>=2 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from requests>=2.25->modelscope) (3.4.2)\n",
"Requirement already satisfied: idna<4,>=2.5 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from requests>=2.25->modelscope) (3.10)\n",
"Requirement already satisfied: certifi>=2017.4.17 in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (from requests>=2.25->modelscope) (2025.8.3)\n"
]
}
],
"source": [
"# 安装 modelscope\n",
"!pip install modelscope transformers"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "c3acd714",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n",
"None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Downloading Model from https://www.modelscope.cn to directory: /home/terrence/.cache/modelscope/hub/models/deepseek-ai/DeepSeek-R1-0528\n"
]
}
],
"source": [
"from tqdm import tqdm\n",
"from modelscope import AutoTokenizer\n",
"\n",
"model_name = \"deepseek-ai/DeepSeek-R1-0528\"\n",
"\n",
"# load the tokenizer and the model\n",
"tokenizer = AutoTokenizer.from_pretrained(model_name)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "e4ab8eb6",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 128815/128815 [00:00<00:00, 363801.76it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Total unique chars: 7404\n",
"Wrote 7404 unique characters to unique_chars.txt (sorted by token ID)\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"\n",
"# 创建临时目录\n",
"if not os.path.exists(\"build\"):\n",
" os.makedirs(\"build\")\n",
"\n",
"unique_chars = {} # 存储字符和对应的token ID\n",
"\n",
"for id in tqdm(range(len(tokenizer.get_vocab()))):\n",
" token = tokenizer.decode(id)\n",
" if token.startswith(\"<\"):\n",
" continue\n",
" # 对于每个字符记录最小的token ID\n",
" for char in token:\n",
" if char not in unique_chars:\n",
" unique_chars[char] = id\n",
"\n",
"# count total unique chars\n",
"print(f\"Total unique chars: {len(unique_chars)}\")\n",
"\n",
"sorted_chars = sorted(unique_chars.items(), key=lambda x: x[1])\n",
"# write to file - 按照token ID排序\n",
"with open(\"./build/chars.txt\", \"w\") as f:\n",
" # 按照token ID排序输出\n",
" for char, token_id in sorted_chars:\n",
" f.write(char + \"\\n\")\n",
"\n",
"print(f\"Wrote {len(unique_chars)} unique characters to unique_chars.txt (sorted by token ID)\")"
]
},
{
"cell_type": "markdown",
"id": "82281aaa",
"metadata": {},
"source": [
"## 从字体中提取子集\n",
"\n",
"字体文件可以从 https://www.alibabafonts.com/ 或 https://fonts.google.com/noto 下载到 fonts 目录下"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "031a7a93",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: fonttools in /home/terrence/miniconda3/envs/dev/lib/python3.12/site-packages (4.59.0)\n"
]
}
],
"source": [
"!pip install fonttools"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "ac8117b0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Building subsetter\n",
"basic_unicodes: Added 767 chars from assets\n",
"common_unicodes: Added chars: 7418\n",
"Subsetting 7 fonts\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 7/7 [00:01<00:00, 4.55it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Merging 7 fonts\n",
"Merged font saved to build/puhui-basic.ttf\n",
"Subsetting 7 fonts\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 7/7 [00:06<00:00, 1.05it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Merging 7 fonts\n",
"Merged font saved to build/puhui-common.ttf\n"
]
}
],
"source": [
"import os\n",
"import shutil\n",
"import json\n",
"from fontTools.subset import Subsetter, Options, load_font, save_font\n",
"from fontTools.merge import Merger\n",
"from fontTools.ttLib.scaleUpem import scale_upem\n",
"\n",
"print(f\"Building subsetter\")\n",
"basic_unicodes = set(range(0x20, 0x7f)) | set(range(0xA1, 0x100))\n",
"for parent, dirs, files in os.walk(\"../../main/assets/locales\"):\n",
" for filename in files:\n",
" if filename == 'language.json':\n",
" with open(os.path.join(parent, filename), 'r') as f:\n",
" strings = json.load(f)[\"strings\"]\n",
" # Add all values to unicodes\n",
" for value in strings.values():\n",
" for char in value:\n",
" basic_unicodes.add(ord(char))\n",
"print(f\"basic_unicodes: Added {len(basic_unicodes)} chars from assets\")\n",
"\n",
"common_unicodes = basic_unicodes.copy()\n",
"for char, token_id in sorted_chars:\n",
" common_unicodes.add(ord(char))\n",
"print(f\"common_unicodes: Added chars: {len(common_unicodes)}\")\n",
"\n",
"font_list = []\n",
"\n",
"def build_ttf(font_type, font_style, font_name, unicodes):\n",
" global font_list\n",
" # font_list = [\n",
" # f\"fonts/Noto_Sans/static/NotoSans-{font_style}.ttf\",\n",
" # f\"fonts/Noto_Emoji/static/NotoEmoji-{font_style}.ttf\",\n",
" # f\"fonts/Noto_Sans_JP/static/NotoSansJP-{font_style}.ttf\",\n",
" # f\"fonts/Noto_Sans_KR/static/NotoSansKR-{font_style}.ttf\",\n",
" # f\"fonts/Noto_Sans_SC/static/NotoSansSC-{font_style}.ttf\",\n",
" # f\"fonts/Noto_Sans_TC/static/NotoSansTC-{font_style}.ttf\",\n",
" # f\"fonts/Noto_Sans_Thai/static/NotoSansThai-{font_style}.ttf\"\n",
" # ]\n",
" font_list = [\n",
" f\"fonts/AlibabaSans/AlibabaSans-{font_style}/AlibabaSans-{font_style}.ttf\",\n",
" f\"fonts/AlibabaPuHuiTi-3/AlibabaPuHuiTi-3-55-{font_style}/AlibabaPuHuiTi-3-55-{font_style}.ttf\",\n",
" f\"fonts/AlibabaSansJP/AlibabaSansJP-{font_style}/AlibabaSansJP-{font_style}.ttf\",\n",
" f\"fonts/AlibabaSansKR/AlibabaSansKR-{font_style}/AlibabaSansKR-{font_style}.ttf\",\n",
" f\"fonts/AlibabaSansTC/AlibabaSansTC-55/AlibabaSansTC-55.ttf\",\n",
" f\"fonts/AlibabaSansThai/AlibabaSansThai-Rg/AlibabaSansThai-Rg.ttf\",\n",
" f\"fonts/AlibabaSansViet/AlibabaSansViet-Rg/AlibabaSansViet-Rg.ttf\",\n",
" ]\n",
"\n",
" if not os.path.exists(f\"build/subsets\"):\n",
" os.makedirs(f\"build/subsets\")\n",
"\n",
" print(f\"Subsetting {len(font_list)} fonts\")\n",
" subset_fonts = []\n",
" subsetter = Subsetter(Options())\n",
" subsetter.populate(unicodes=list(unicodes))\n",
" # 遍历 font_list提取每个字体中的字符\n",
" for font_path in tqdm(font_list):\n",
" if not os.path.exists(font_path):\n",
" print(f\"Font {font_path} not found\")\n",
" continue\n",
" font = load_font(font_path, Options())\n",
" font_file = font_path.split('/')[-1].split('.')[0]\n",
" subsetter.subset(font)\n",
" scale_upem(font, 1000)\n",
" save_path = f\"build/subsets/{font_file}.ttf\"\n",
" font.save(save_path)\n",
" subset_fonts.append(save_path)\n",
"\n",
" # 合并所有字体,保存到 build/puhui-{len(unicodes)}-{style}.ttf\n",
" print(f\"Merging {len(subset_fonts)} fonts\")\n",
" output_path = f\"build/{font_type}-{font_name}.ttf\"\n",
" cmd = f\"fonttools merge {\" \".join(subset_fonts)} --output-file={output_path} --drop-tables=vhea,vmtx\"\n",
" os.system(cmd)\n",
" print(f\"Merged font saved to {output_path}\")\n",
"\n",
"\n",
"build_ttf(\"puhui\", \"Regular\", \"basic\", basic_unicodes)\n",
"build_ttf(\"puhui\", \"Regular\", \"common\", common_unicodes)"
]
},
{
"cell_type": "markdown",
"id": "5f1cb872",
"metadata": {},
"source": [
"## 生成不同 size 与 bpp 的点阵字体"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "49c783db",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"字体: build/puhui-common.ttf, 大小: 20px, BPP: 1\n"
]
},
{
"data": {
"image/png": "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",
"text/plain": [
"<PIL.Image.Image image mode=RGBA size=679x30>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"字体: build/puhui-common.ttf, 大小: 20px, BPP: 2\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAtUAAAAeCAYAAADjA+I1AAATxklEQVR4Ae2dUXYcuXJEpXdmN96Df58XYM1i7A+PP+zFjLwAv197DfZ2xn0pXTqUTKBQXU2KIwHnlABkRkZGJtDNJqnRfPzjNj7ssTuwO7A7sDuwO7A7sDuwO7A7sDtwdwd+uTtyB+4O7A7sDuwO7A7sDuwO7A7sDjyyA7/97Zjtt78eYx6JWNF0y/eXR+bcXLsDuwO7A7sDuwO7A7sDuwO7Az9jB/aH6p/x1HfNuwO7A7sDuwO7A7sDuwO7Aw/twP5Q/dB2brLdgd2B3YHdgd2B3YHdgd2Bn7ED+0P1z3jqu+bdgd2B3YHdgd2B3YHdgd2Bh3Zgf6h+aDs32e7A7sDuwO7A7sDuwO7A7sCVDvz6+d8+fPzXf/jA/J4GmnzU9vl//uvJxrw/VL+n03olLR8/fvyG+ddff/1Qbd8ADjafP3++FH9A/13cV3vSiX5knx7J1Wndtj9PB7gLf7bB+w2vsUeOR/UBnuTK9SP1bq7dgd2BtQ7w4fTz//73hz/+5T+f5rWo10fxYZrx+z/+09PDGhtaHfuf1LMTP+jcfSHji8anT58eXnGXqyb5/fffq2m6v/oFbrXO1+iJuenL2bqnTfkTOfkwRR+6+me+91YiZ0gdnumKPu/umZgVXrV0PR3Fo4W4o/8tAWcC74pm+KzRvMSd0WVcneWe6T3TB3V2dcHDMNcKL3w+aofbR9uef6wOcObcD18j7h9179+qW2ffe8EzfI28lc4uDx+4GZ8+vPE/qfdVDB+oP/3d33/R8HX2wzbG5w/VXo6vcU9vDt2bIzgGs2svmLFcOn3aZpfuLF5O54zvDh0tYLrR4ROXsV0NXraMqesuR2oWX/uonbniOy2Jd41+sI48l1zrd84Ybc6zuJlvxil3nUfnVnGjfe19py9tuR5x1jqONMI5wsBV+ToN2pyrtsqh33tT+3D0pmrc7E6a4z3N6M5e5Fqd9LCz66+zvTgTAwd5eGrvK/89+3u03JNnFOP7HjrUYr34XqPmkZYVu9qu6oKH++CoteNnzF43lSO5iNvj/XbA+17vwPc8N9+fatdmd7BiZ3trvframeWoPj60frr99NcPq87i+GD7vcav//HvHz7wDMbTh+o8lHyT6N4cbfCA79ksDwbf0Do+AxJvzAxvnNzuu9k3upqjw2qrb3zEdvGdTQ5mc6eNuhhyWgO97V4IiZeHmKP+mDs1ajOnfHXuXkDGyJF3Ad2jNxb1j/w1d+47HfrRgYYZRqxzatbmbH3uR3PmW42xZ5UTe+WbaRz5kqPm+Jn2q+fBa2L1Pnp2+Tq6t6dw5Rkmp3lmZymG/K5zPlNXrUGeaq97cTUXe3zUx5y1VY669z3iyF5zVvxr7q2NHKMzEkMPZhg4sj/E8Ry9pxO3x/ftwOr7xluozNdNvU++z4zu4Rl993JwpxmpbTXv75/++RnKT6f96fCzcXFxRUNNwV9HORy3ZvG/Kf/jVjTLb8bILogYMLdLpmk4gzniy2DxR9xyqiU5XM98YuoML8+V0XHM6prhR+cz09jVDX7W0y7GHqivm0ecYuV45Gwvr3Ie9eQe/nvrHtU0OpeRXc0jP/rwjYZxo3MdxVX7LM/MV3nO7tE9e1b57MMqPnGjOyCn/jonB+vqH+3hHZ2XMckt1j6pi9mHuDrEVbt7YsDk6GzpN9/RrGZijzgrf1eLPOkb8R7VbT40wpFa9c1m+c/GzTi378ftwNF90c9cx+iOV9zK3vsOpw85zV/vc4evmNuH1z8OnxDXcc40GEqcOlP7Cz0ErGi6YfgOYvgGYBIF1HkWW7Hs4SNmdYBvi/tKYH62ua789+Ql5uro9M90dr7Opq6ZDwz5s44OX/sLHtxs1Lrq3ljsPK810H6Vf6QR+1EfRnXNOGu/K8eoprN2ebszx3dUn3GdXrVYJ3OHO8pzpKHLc++Z2I8zs/nvzXlU36oWdPjYc/ZnRtXi+co3mrvajR3llyv92qzjSP8KDs5OX+Z1bX73xPlU34i34uTq5hFHh9VGzcSNemPf1ZGzHDmrQd7Ej3IQ3+FHfU7OXI/wq/yzWq2r1pr5XXc65M74XI/8XV7izMWcQ57057rT1vV+dFZyZc66HmGwd/mN73SM8NbJ7GPeLkaMs9hv6lz5AKvY2wyXNVVe9t3IGo2RB65v9ECwoumG+WX2o/FbgqdfSYFh/ZZDXaOc+HluhY8gl+xXef3VS+2bPe3EdTV3ti622syvfdQvcNSKLmPuqb3Wqe7bVVTCpZlfc5HjHm2jxGp8JKc97Oq2x/Z7pOs92/11I7V45vSRuh95PvDV82HP81a/Ivcsr9wPe3TlTCsH+2pb4c+YribPcfV+gk/O1DCy21P8HSbPPflGmtQMdsSZPGpmPjvgX4lbwZzNDV7ePDv7OeIjhidj2BNHPWmXwz4yM+SYve6SRzw50g5Xnq/85mB2qIG9fGkTV+fMZ1zVAYZaql0u4lbHDJt61ZK2muPMe6t54ZsN/GJnuOpTp/zq7+5A9jx5wHa5O7zn0X3dTM7RuuMEO9KAj/NndDmv6nn6hH8jfjFuQp98zN24NXzqz5gjrsTKyzwa9CL9xnR4sPUB39Ulj3ozrsN3+bARN8LLmf7Mm5xi0+ZajcmDT3vGws9Tx0xnxbKXW1/dH9n1n53RWfWb29rqfJSj4zRm5hNTZ/If9RP/DGNN4lbnqsW9mtw7w4tvNIxDj0NtXZx4sc6zPCPfLM/MZ85HzOZhvmdcjR/lpGdnNZ3RMjqTqgccTx3eg6pxhdfYvF9qJ77jVIdz1cM+OZJbbI1l3+Hk6XxyMVe+9I3WchM7Gh1vZzP+Xp/xOVMzfN0ZdP3ocltjh89cdW1czZ048nW8nQ7iRvZZri6HNvuTmnI94wWnv6uh4xbPPBvGVpy6Z7HVN+KqOPejHuvP+QV25afCSTBYv+AN3KwH1hrw5Z9Uv9m/U+13BText1peDr4z8OG7G3C3i/ASeLOAY4z8T874A1w+cJMDTd13UoTiU4N5ZvhI9/wd0KjW20E9weHLmslTY+p+JQ+8jIy1/oy/Z1375T5roSY1WJ+z9ntyj2LQ0D0jPHb0MDzbGo+v2tiPBnz6ay+s3ZxwHPXB83L2LN07ax/pmtnRm9pybS0Zr43cdahDTPWf2cvx2nlmmjwf6kKP+1lM57Mvne+sTQ3JqTZ89m3Em3EjzKpdLu4MuXlYowGf/lU+cOrPc4fH90v9yakfjLj0Jy/YjqPiR3vi5fC1Yu3Wj52RNVQ+NBjvTLy1VHzuwZwZI7z21X6IX83d4c01680q/yqu00GsGtQkn/tRnDhmsXKl7+x6xqUWMZn7KI+xR7gV/yO5VvK9BcaafP0yXx3P/6TeVaJZvEJnly99XB4e3nDqG+UKV9Vi47S7hx++zOHFRY844sB0eDlzhiNj08caHkatGS3EpZ01eJ7UZB+eiMofcnQYbOjLmg2vObTnXGtz39WrL+M7XPrvWWdfVuLtv1h0dr3CX+2zXFmbtWNjnXt50dGdA/7kYs+Q48vu///Efu/o8sDVcVZb7nN9r5bVOHta8Wjg4YyuDGshT96V2XnVfHJU+5U9nNZe7yW85pzd0Sv5M5Yc5PPBhzafxJ5ZEz8a1jfyj+zEwYtmzpDe3XtHjIMjaye3tc9qSFzqleuKtuQ7WqPx3n4ecVvLUR+OeK76ZzrUBsY1+ep+poGzytgZ9shH3hy5z3Vi3sva10Lq8XWSttdcn9WQPWXN42e89N2r+elXITfCF+PWmPZXPgJvF2rqByeG+cwwd427Fdn+msc8Z3J0MfDzdENNnU9bx6mPeeYf+cyrNnvgPvlz3fERA58j9x1enHPNmfFimNWctitr8qAvxz051H8U2+XL3LO13MyO2qf0iWE2Nm332I0fnelRfcalTns3mxOPhlmekU9+a6iz2qr9KK7iR3t5mK3Hc3FfY6t9phHfPSN1wVFzwikm+UdaEuO6i9d3ZYb3qO4ZptM1w6u1xtmL1FIxR7xymMP56I6IG83ypjaxI+6qXTwzvu6O4BvxaZe3zpWv+nNPnhz60rayVlPNnbFyd3Picm2/tR3lgduzqbF1L6fzCnenPW1Z/xGfedWVsfiyFrHOcmfuXCeXWPKw9km8vLNZ/DPmxF//uEeDMamX3OjAZt+e9bBY0cR/qHgjGY6rn9j97uEm8PRPBohhpAbXzPkTpCwA+z35kuPKGm1q73isofMRh79yYP9y3t9GWeu31vHO3CN9t8v01NeaPxnBcK6Ze8SXce9hbf3U8BaaZzlmPnqlVvvm3rna3deZPDWmYlb3cnV3cZVjBWeeFWzFHPW14us+e5V1HvHymji6V3IfcVVN7OF3zPKgmdemeoyZzeqaYd7K12mx9rN9ow8M+uVgDR95eM5yyvMaM9rQ3PWgs61oOFOjfaEntS/wjDR0eLiopb6GRhwrtRxhVnXIk/0mVm21dvE5g817lb571ubPfq3woGNF7wqGfGfvgD2rvfB9aKWGq5irGuyN7xeexb26fpGgOxzFmvRMEgXS7Hviu1zwjLhS6whTOY1JO7GdHczIbjwXklEvmP5Hzmo5kwt9R73BD2704sbvCwYNR3yPrPkql9qv8hzFezZHuJnfu1QxI3vFuafmM3fEuPc8P6K/o/rs76hn3Z035jVfC9Y8el3WesRjR1fuxfoe7T5nahr1oOLgXtWVsXVNPvKqq+pe0dNx1nMxT7XX2Pe09/zOajau1tLZtY36rL9ysa+67DEx1dfFP8pWc63o4M5xf1e1vsXr/agf1jk7EziO/DWP+NU7IN7X7Nn3AeLtZ9Wyur+qgTzek7P6O41/sXkKqyAPr9pnexsM9z3xcKunxsPZPeJWc4745ekO2phR7fiNH2Fm9iN+Y8Gh70wuuenPbKxyioNX7hnvka/r91HMe/XTD/tzpLH2jjhe2PWRr9rdk4fXXeWr+yM9M78aXvusZnmsR0zVi19M9R3tjYN7xF85zLeKJ948lWu2z3Oe4fQd6fEM5XU2Ho3dfdJ/dvY9exaHZnSoHQ2sicV+diRXjT16H0w8OuiXj+fn3ln7CJ+c3dp466+Ykb3i6l7etGtb5aTGM6PjN9dZrjN5K7bTkRjvgTj3ick1OJ4jXMasrO/pjTFqr3nss7jqP7uXr4vz9TnDdHHg6eUjNN6rgf6po9N42nYT8vz3R27BT+tbgU9/t4T9bIi7NeUZRkzygKnPM/i2ILb65TjKnzxqSZvrs/yZ31htWav8zLP8iSNeLrmNxc66G2IztsOlTV5sI27staYRVm55nTsO6zRmNoutOjKm02Rc4s6s4RyNLt8Im3Z7kjbWXY9m9hq/UmunGVuXu8NmTuuoZ6LdePZpSw7r6/If+ZKTNTrSVvOwt0fkS33YjU8ObDmMS1uuqz+5EsdaLaMc6sPPA1c+lW913+XtbGqvvOrCLoZ5NMSM/K9pT61X8tRzrbz6751r/+TH7pPcWYtn18XYe31gc2h3xp8xFa+PGZ8P8T41Ju3i5cFXR/pY+3RYY+HFX3PrZz6ro4udacgcHc66kjfXKzXIAb99SVvyubbuUUynlVjjak/Nx4zPRzxzxoiH0xjWDGO+7Po/5UueJ+TK31/+SpmxqxqogdzGZk3Qav+a4su0oumGeb71JrERkB4NE6cg42dz4mte41bypz61pM21nDmnBnE5y2fMkR5xyTFbV37iZ5rkP9KROc1hj9PHemQ3ruIzJnWoLfEzjsSxHmHR52MO98zGMae9rms+9/Zc7tXZ+Dqnnuoz16q94qgJjtnoclgT8Tmwo3c0rKXGgVeL3M5H2OpPnuob5Zlptha1q2s0i3cGN+PveGf4EV/HkxpnnGrt5uSt/qole09cxuZZ6Kt87o1z/5Zzrene3Pbe+Efxyldne2ZeZ+x1jLDG5Fzj8WHLsxafZ5w5Z1hia5x8da5ajnKs4Gvu5Kz53c94jQcDfsYPVs4OJ4ecdSZmJYc4czl3Oc1hbrHOs5jMU3Hpq1zsK14Mcw7taavr1P4N78oH2CAz16qGrPGbvF851RUplv9DxY8E3YTs8QN2gF9p8KuNesTYfCi7+ketkO924V78+gs+7PIyr3Lza+aO079GNNKzYu94jYP/9oJyuzzDmYNa6Q1jlM9aMp8xK/3PHMlBTnvOeoUL3B4vO0Af69lWVPYa7Aw/uteV8+zee2McmnJ0d6DTQlzl4m7Naso8rIlf6RvYo36BOTO6ms7Ei608dS/ue8z2tzvT1NNp7mwZs9dfOrDa492vN+zAb387TvbbX48xj0SsaLrlm/7rH4/Us7neVwf8Qlw/oM1U+sWzi/ELcX6R7nCVf6bj6AtJ5bpnr+57YonxDZn17AMJeag1+2MM89HwA0nHYexKv8Xu+WUHVu6C5/Ay+qXlte6vr5nMqK5RDZ0WsJ09eVfXnaYaC+ZR+Sr3lf171HSlnh273gHuJM/odbPOtJG7A186sD9U/8A3YfQh694vpitffFYw2XLe0EY6E/ca67NaOw1o54PyUQ1H/o672h7BUTn3/s/XgUfc20dV/T3v5Gv14bV4H9XzzXO9A36YZmZ8z3t8vZrN8J46sD9Uv6fT+Am1/AhvZj9CDT/h1dsl7w68+w6svrfsbwTOHaUfqvkB02qPz2XY6J+1A/tD9c968rvu3YHdgd2B3YEftgP7g/b4aPcH6XFvtudaB/Z/qHitfzt6d2B3YHdgd2B3YHdgd2B3YHfgw192D3YHdgd2B3YHdgd2B3YHdgd2B3YHrnXg/wC03Wbz1AjTPwAAAABJRU5ErkJggg==",
"text/plain": [
"<PIL.Image.Image image mode=RGBA size=725x30>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"字体: build/puhui-common.ttf, 大小: 20px, BPP: 3\n"
]
},
{
"data": {
"image/png": "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",
"text/plain": [
"<PIL.Image.Image image mode=RGBA size=725x30>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"字体: build/puhui-common.ttf, 大小: 20px, BPP: 4\n"
]
},
{
"data": {
"image/png": "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",
"text/plain": [
"<PIL.Image.Image image mode=RGBA size=725x30>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import os\n",
"from PIL import Image\n",
"import json\n",
"from IPython.display import display\n",
"\n",
"lv_font_conv = \"node ./tmp/lv_font_conv/lv_font_conv.js\"\n",
"flags = \"--force-fast-kern-format --no-compress --no-prefilter \"\n",
"\n",
"def preview_font(font_path, size, bpp):\n",
" symbols_str = \"0123456789你好正在充电Helloこんにちは안녕하세요ЗдравствуйтеOláสวัสดี\"\n",
" output = \"./build/preview\"\n",
" if os.path.exists(output):\n",
" shutil.rmtree(output)\n",
" os.makedirs(output)\n",
" cmd = f\"{lv_font_conv} {flags} --font {font_path} --format dump --bpp {bpp} -o {output} --size {size} --symbols {symbols_str} \"\n",
" os.system(cmd)\n",
" \n",
" # 读取字体信息\n",
" font_info_path = os.path.join(output, \"font_info.json\")\n",
" if not os.path.exists(font_info_path):\n",
" print(\"未找到font_info.json文件\")\n",
" return\n",
" \n",
" with open(font_info_path, 'r', encoding='utf-8') as f:\n",
" font_info = json.load(f)\n",
" \n",
" # 获取字体高度信息\n",
" font_height = font_info.get('size', size)\n",
" ascent = font_info.get('ascent', font_height)\n",
" descent = font_info.get('descent', 0)\n",
" \n",
" # 收集所有PNG文件并按照字符顺序排序\n",
" png_files = []\n",
" char_positions = {} # 存储每个字符在原始字符串中的位置\n",
" \n",
" for i, char in enumerate(symbols_str):\n",
" unicode_hex = f\"{ord(char):x}.png\"\n",
" png_path = os.path.join(output, unicode_hex)\n",
" if os.path.exists(png_path):\n",
" png_files.append((char, png_path, i))\n",
" char_positions[char] = i\n",
" \n",
" # 按照原始字符串顺序排序\n",
" png_files.sort(key=lambda x: x[2])\n",
" \n",
" if not png_files:\n",
" print(\"未找到任何PNG文件\")\n",
" return\n",
" \n",
" # 加载所有图片并计算总尺寸\n",
" images = []\n",
" total_width = 0\n",
" max_height = 0\n",
" \n",
" for char, png_path, _ in png_files:\n",
" try:\n",
" img = Image.open(png_path).convert('RGBA')\n",
" images.append((char, img))\n",
" total_width += img.width + 2 # 添加2像素间距\n",
" max_height = max(max_height, img.height)\n",
" except Exception as e:\n",
" print(f\"无法加载图片 {png_path}: {e}\")\n",
" continue\n",
" \n",
" if not images:\n",
" print(\"没有成功加载任何图片\")\n",
" return\n",
" \n",
" # 移除最后一个字符的间距\n",
" total_width -= 2\n",
" \n",
" # 创建预览图画布,使用字体高度作为画布高度\n",
" canvas_height = max(max_height, font_height + 10) # 添加一些padding\n",
" canvas = Image.new('RGBA', (total_width, canvas_height), (255, 255, 255, 255))\n",
" \n",
" # 拼接图片\n",
" x_offset = 0\n",
" for char, img in images:\n",
" # 计算垂直居中位置\n",
" y_offset = (canvas_height - img.height) // 2\n",
" canvas.paste(img, (x_offset, y_offset), img)\n",
" x_offset += img.width + 2\n",
" \n",
" # 打印字符信息\n",
" print(f\"字体: {font_path}, 大小: {size}px, BPP: {bpp}\")\n",
" \n",
" # 在 notebook 中直接显示图片\n",
" display(canvas)\n",
" \n",
" return canvas\n",
"\n",
"# 调用函数并显示图片\n",
"font_size = 20\n",
"for bpp in [1, 2, 3, 4]:\n",
" preview_image = preview_font(\"build/puhui-common.ttf\", font_size, bpp)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "44c7121f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generating src/font_puhui_basic_14_1.c...\n",
"Generating src/font_puhui_basic_16_4.c...\n",
"Generating src/font_puhui_basic_20_4.c...\n",
"Generating src/font_puhui_basic_30_4.c...\n",
"Generating build/font_puhui_common_14_1.bin...\n",
"Forced faster kerning format (via classes). Size increase is 700 bytes.\n",
"Generating build/font_puhui_common_16_4.bin...\n",
"Forced faster kerning format (via classes). Size increase is 700 bytes.\n",
"Generating build/font_puhui_common_20_4.bin...\n",
"Forced faster kerning format (via classes). Size increase is 700 bytes.\n",
"Generating build/font_puhui_common_30_4.bin...\n",
"Forced faster kerning format (via classes). Size increase is 700 bytes.\n"
]
}
],
"source": [
"\n",
"flags = \"--force-fast-kern-format --no-compress --no-prefilter \"\n",
"\n",
"def generate_font_source(font_path, name, size, bpp):\n",
" output = f\"src/font_{name}_{size}_{bpp}.c\"\n",
" print(f\"Generating {output}...\")\n",
" cmd = f\"{lv_font_conv} {flags} --font {font_path} --format lvgl --lv-include lvgl.h --bpp {bpp} -o {output} --size {size} -r 0x0-0xfffff\"\n",
" os.system(cmd)\n",
"\n",
"def generate_font_bin(font_path, name, size, bpp):\n",
" output = f\"build/font_{name}_{size}_{bpp}.bin\"\n",
" print(f\"Generating {output}...\")\n",
" cmd = f\"{lv_font_conv} {flags} --font {font_path} --format cbin --bpp {bpp} -o {output} --size {size} -r 0x0-0xfffff\"\n",
" os.system(cmd)\n",
"\n",
"generate_font_source(\"build/puhui-basic.ttf\", \"puhui_basic\", 14, 1)\n",
"generate_font_source(\"build/puhui-basic.ttf\", \"puhui_basic\", 16, 4)\n",
"generate_font_source(\"build/puhui-basic.ttf\", \"puhui_basic\", 20, 4)\n",
"generate_font_source(\"build/puhui-basic.ttf\", \"puhui_basic\", 30, 4)\n",
"generate_font_bin(\"build/puhui-common.ttf\", \"puhui_common\", 14, 1)\n",
"generate_font_bin(\"build/puhui-common.ttf\", \"puhui_common\", 16, 4)\n",
"generate_font_bin(\"build/puhui-common.ttf\", \"puhui_common\", 20, 4)\n",
"generate_font_bin(\"build/puhui-common.ttf\", \"puhui_common\", 30, 4)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "275869d3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generating src/font_puhui_common_20_4.c...\n",
"Forced faster kerning format (via classes). Size increase is 700 bytes.\n"
]
}
],
"source": [
"generate_font_source(\"build/puhui-common.ttf\", \"puhui_common\", 20, 4)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "58590bf1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generating build/font_puhui_common_20_4.bin...\n",
"Forced faster kerning format (via classes). Size increase is 700 bytes.\n"
]
}
],
"source": [
"\n",
"generate_font_bin(\"build/puhui-common.ttf\", \"puhui_common\", 20, 4)"
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "fb65161e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"=== 开始并行生成字体bin文件 ===\n",
"开始并行生成 28 个字体文件,使用 8 个进程...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"生成字体文件: 0%| | 0/28 [00:00<?, ?it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generating build/full/AlibabaSans-Regular_30_4.bin...Generating build/full/AlibabaPuHuiTi-3-55-Regular_30_4.bin...Generating build/full/AlibabaPuHuiTi-3-55-Regular_14_1.bin...Generating build/full/AlibabaSans-Regular_16_4.bin...Generating build/full/AlibabaPuHuiTi-3-55-Regular_16_4.bin...Generating build/full/AlibabaPuHuiTi-3-55-Regular_20_4.bin...Generating build/full/AlibabaSans-Regular_14_1.bin...Generating build/full/AlibabaSans-Regular_20_4.bin...\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"生成字体文件: 4%|▎ | 1/28 [00:01<00:29, 1.09s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generating build/full/AlibabaSansJP-Regular_14_1.bin...\n",
"Generating build/full/AlibabaSansJP-Regular_16_4.bin...\n",
"Generating build/full/AlibabaSansJP-Regular_20_4.bin...\n",
"Generating build/full/AlibabaSansJP-Regular_30_4.bin...\n",
"Generating build/full/AlibabaSansKR-Regular_14_1.bin...\n",
"Generating build/full/AlibabaSansKR-Regular_16_4.bin...\n",
"Generating build/full/AlibabaSansKR-Regular_20_4.bin...\n",
"Generating build/full/AlibabaSansKR-Regular_30_4.bin...\n",
"Generating build/full/AlibabaSansTC-55_14_1.bin...\n",
"Generating build/full/AlibabaSansTC-55_16_4.bin...\n",
"Generating build/full/AlibabaSansTC-55_20_4.bin...\n",
"Generating build/full/AlibabaSansTC-55_30_4.bin...\n",
"Generating build/full/AlibabaSansThai-Rg_14_1.bin...\n",
"Forced faster kerning format (via classes). Size increase is 9024 bytes.\n",
"Generating build/full/AlibabaSansThai-Rg_16_4.bin...\n",
"Generating build/full/AlibabaSansThai-Rg_20_4.bin...\n",
"Generating build/full/AlibabaSansThai-Rg_30_4.bin...\n",
"Generating build/full/AlibabaSansViet-Rg_14_1.bin...\n",
"Generating build/full/AlibabaSansViet-Rg_16_4.bin...\n",
"Generating build/full/AlibabaSansViet-Rg_20_4.bin...\n",
"Generating build/full/AlibabaSansViet-Rg_30_4.bin...\n",
"Forced faster kerning format (via classes). Size increase is 9024 bytes.\n",
"Forced faster kerning format (via classes). Size increase is 9024 bytes.\n",
"Forced faster kerning format (via classes). Size increase is 9024 bytes.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"生成字体文件: 18%|█▊ | 5/28 [03:36<17:36, 45.94s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Forced faster kerning format (via classes). Size increase is 12716 bytes.\n",
"Forced faster kerning format (via classes). Size increase is 12716 bytes.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"生成字体文件: 21%|██▏ | 6/28 [03:37<13:01, 35.52s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Forced faster kerning format (via classes). Size increase is 12716 bytes.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"生成字体文件: 100%|██████████| 28/28 [03:53<00:00, 8.34s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Forced faster kerning format (via classes). Size increase is 12716 bytes.\n",
"完成生成 28 个字体文件\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
},
{
"data": {
"text/plain": [
"['build/full/AlibabaSans-Regular_14_1.bin',\n",
" 'build/full/AlibabaSans-Regular_16_4.bin',\n",
" 'build/full/AlibabaSans-Regular_20_4.bin',\n",
" 'build/full/AlibabaSans-Regular_30_4.bin',\n",
" 'build/full/AlibabaPuHuiTi-3-55-Regular_14_1.bin',\n",
" 'build/full/AlibabaPuHuiTi-3-55-Regular_16_4.bin',\n",
" 'build/full/AlibabaPuHuiTi-3-55-Regular_20_4.bin',\n",
" 'build/full/AlibabaPuHuiTi-3-55-Regular_30_4.bin',\n",
" 'build/full/AlibabaSansJP-Regular_14_1.bin',\n",
" 'build/full/AlibabaSansJP-Regular_16_4.bin',\n",
" 'build/full/AlibabaSansJP-Regular_20_4.bin',\n",
" 'build/full/AlibabaSansJP-Regular_30_4.bin',\n",
" 'build/full/AlibabaSansKR-Regular_14_1.bin',\n",
" 'build/full/AlibabaSansKR-Regular_16_4.bin',\n",
" 'build/full/AlibabaSansKR-Regular_20_4.bin',\n",
" 'build/full/AlibabaSansKR-Regular_30_4.bin',\n",
" 'build/full/AlibabaSansTC-55_14_1.bin',\n",
" 'build/full/AlibabaSansTC-55_16_4.bin',\n",
" 'build/full/AlibabaSansTC-55_20_4.bin',\n",
" 'build/full/AlibabaSansTC-55_30_4.bin',\n",
" 'build/full/AlibabaSansThai-Rg_14_1.bin',\n",
" 'build/full/AlibabaSansThai-Rg_16_4.bin',\n",
" 'build/full/AlibabaSansThai-Rg_20_4.bin',\n",
" 'build/full/AlibabaSansThai-Rg_30_4.bin',\n",
" 'build/full/AlibabaSansViet-Rg_14_1.bin',\n",
" 'build/full/AlibabaSansViet-Rg_16_4.bin',\n",
" 'build/full/AlibabaSansViet-Rg_20_4.bin',\n",
" 'build/full/AlibabaSansViet-Rg_30_4.bin']"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import multiprocessing as mp\n",
"from functools import partial\n",
"import os\n",
"import shutil\n",
"\n",
"build_dir = \"build/full\"\n",
"if os.path.exists(build_dir):\n",
" shutil.rmtree(build_dir)\n",
"os.makedirs(build_dir)\n",
"\n",
"def generate_font_full_bin(args):\n",
" \"\"\"单个字体生成任务\"\"\"\n",
" font_path, name, size, bpp = args\n",
" output = f\"{build_dir}/{name}_{size}_{bpp}.bin\"\n",
" print(f\"Generating {output}...\")\n",
" cmd = f\"lv_font_conv {flags} --font {font_path} --format bin --bpp {bpp} -o {output} --size {size} -r 0x0-0xfffff\"\n",
" os.system(cmd)\n",
" return output\n",
"\n",
"# 创建任务列表\n",
"def create_font_tasks(font_list, task_type=\"bin\"):\n",
" \"\"\"创建字体生成任务列表\"\"\"\n",
" tasks = []\n",
" sizes_bpps = [(14, 1), (16, 4), (20, 4), (30, 4)]\n",
" \n",
" for font_path in font_list:\n",
" font_name = font_path.split('/')[-1].split('.')[0]\n",
" for size, bpp in sizes_bpps:\n",
" tasks.append((font_path, font_name, size, bpp))\n",
" \n",
" return tasks\n",
"\n",
"\n",
"# 多进程生成字体文件\n",
"def generate_fonts_parallel(font_list, task_type=\"bin\", max_workers=None):\n",
" \"\"\"使用进程池并行生成字体文件\"\"\"\n",
" if max_workers is None:\n",
" max_workers = min(mp.cpu_count(), len(font_list) * 4) # 每个字体4个任务\n",
" \n",
" tasks = create_font_tasks(font_list, task_type)\n",
" \n",
" print(f\"开始并行生成 {len(tasks)} 个字体文件,使用 {max_workers} 个进程...\")\n",
" \n",
" with mp.Pool(processes=max_workers) as pool:\n",
" if task_type == \"bin\":\n",
" results = list(tqdm(\n",
" pool.imap(generate_font_full_bin, tasks),\n",
" total=len(tasks),\n",
" desc=\"生成字体文件\"\n",
" ))\n",
" else:\n",
" results = list(tqdm(\n",
" pool.imap(generate_font_source_mp, tasks),\n",
" total=len(tasks),\n",
" desc=\"生成字体源码\"\n",
" ))\n",
" \n",
" print(f\"完成生成 {len(results)} 个字体文件\")\n",
" return results\n",
"\n",
"# 并行生成bin文件\n",
"print(\"=== 开始并行生成字体bin文件 ===\")\n",
"generate_fonts_parallel(font_list, task_type=\"bin\", max_workers=8)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "dev",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}