{ "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": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "字体: build/puhui-common.ttf, 大小: 20px, BPP: 2\n" ] }, { "data": { "image/png": "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", "text/plain": [ "" ] }, "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": [ "" ] }, "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": [ "" ] }, "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