# fontdot **Repository Path**: pangxiongfei/fontdot ## Basic Information - **Project Name**: fontdot - **Description**: 跨平台字模获取工具 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-24 - **Last Updated**: 2026-09-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # fontdot — 跨平台字模获取工具 一套**可在 Windows(VC / VS) 与 Linux 三平台使用**的字模获取工具。 核心改动:渲染后端由 Windows GDI 统一改为 **FreeType**,因此 不再依赖系统字体子系统,三个平台取模结果完全一致;并且字库二进制格式 保持与原工具兼容(`FONTV02` 等宽 / `PROPV02` 变宽),原有固件无需改动即可使用。 --- ## 目录结构 ``` fontdot/ ├── include/fontcore.h # 共享核心库头文件(渲染 + 取模 + 输出) ├── src/fontcore.c # FreeType 渲染 / 点阵打包 / 二进制&C头输出 ├── src/main.c # 命令行工具(fontdot) ├── win/FontDot/ # Windows MFC 界面 │ ├── DotFont.* # CDotFont:接口同原版,内部改用 fontcore │ ├── TmaxFont.* # 等宽/变宽字库生成(与原版一致) │ ├── DotView.* # 点阵预览控件 │ ├── PageGdi.* # GDI 系统字体预览页(仅桌面/Windows,不参与生成) │ ├── fontDlg.* / font.* # 对话框与应用程序框架(含 FreeType/GDI 选项卡) │ ├── FontLib.* / stdafx.* # 桩文件 / 预编译头 │ ├── font.rc / resource.h # 资源(含 IDD_PAGE_GDI 与 IDC_TAB_MAIN) │ └── res/fontdot.ico ├── external/freetype/ # vendored FreeType 2.13.3(不依赖系统库) │ └── freetype-2.13.3.tar.xz # 编译时由 CMake 解压到 output/build 下编译 ├── output/ # 构建产物目录(不入库) │ ├── build/ # 编译中间产物(含解压后的 FreeType 源码) │ └── target/ # 安装产物(fontdot / FontDot.exe) ├── CMakeLists.txt # 跨平台构建(Windows/Linux 通用) └── Makefile # Linux 便捷包装(cmake) ``` --- ## 字库格式(与原工具兼容) ### 等宽 `FONTV02` ``` font_head { // 18 字节: 8B magic + 4×u8 + 3×u16 (含 1 字节保留) char magic[8] = "FONTV02\0" // 含结尾 '\0', 共 8 字节 u8 YSize // 点阵高(行数) u8 XSize // 点阵宽(固定) u8 BytesPerLine // = (XSize*Bpp+7)/8 u8 Bpp // offset 11: 单色时为保留填充(0), 灰度为位深 1/2/4/8 u16 CharNum u16 FirstChar u16 LastChar } u16 index[CharNum] // 码点表(升序去重, 小端) u8 glyph[CharNum][YSize * BytesPerLine] // 1bit/像素, 每行 MSB 在前 ``` ### 变宽 `PROPV02` ``` font_head (同上, 18 字节) 再填充 2 字节至 4 字节对齐(共 20 字节) 才开始索引表 prop_font_idx[CharNum] { // 每元素 8 字节 u16 code u8 XSize // 该字实际墨迹宽 u8 BytesPerLine u32 offset // 该字点阵数据偏移(小端) } u8 glyph data... // 按 offset 排放, 每行 MSB 在前 ``` 点阵打包规则:第 `j` 行第 `i` 列像素对应字节 `buf[j*BytesPerLine + i/8]`, 位 `0x80 >> (i%8)`(高位在前)。与原 `CDotFont::Scan` 完全一致。 ### 灰度字库 `GRAYS02` / `GRAYP02`(新增) 与上面完全同构,仅 magic 与 `Bpp` 字段不同,用于输出 1/2/4/8bpp 灰度点阵 (FreeType 8 位灰阶反走样 → 按目标位深四舍五入量化): ``` font_head { // 头部结构同上, 但: char magic[8] // 等宽灰度="GRAYS02\0", 变宽灰度="GRAYP02\0" u8 YSize u8 XSize u8 BytesPerLine // = (XSize*Bpp+7)/8 u8 Bpp // offset 11: 每像素位数 1/2/4/8 (灰度取 2/4/8, 反走样保真) u16 CharNum u16 FirstChar u16 LastChar } ``` - 1bpp 单色:仍用 `FONTV02`/`PROPV02`,保持与旧固件 100% 兼容。 - 2/4/8bpp 灰度:用 `GRAYS02`/`GRAYP02`;像素值按字节对齐的行距打包(每行 `ceil(XSize*Bpp/8)` 字节),每字节内像素高位在前(如 4bpp:`byte = (p0<<4)|p1`, `p0` 为左像素)。 - 读取端 `font_reader` 自动按 magic 区分单色/灰度,并由 offset 11 的 `Bpp` 字段决定 取像素位深;`font_render` 提供 `font_get_gray()` / `font_blit_bpp()` 支持任意位深。 ### C 头文件格式(`-F c` 产物,编进固件) 结构与二进制同语义,但用 C 数组表达,索引顺序与二进制一致(升序去重): ``` #define _HEIGHT #define _WIDTH // 仅等宽(MONO)输出; 变宽(PROP)无此项 #define _BYTES_PER_LINE // 等宽: (XSize+7)/8; 变宽不使用 #define _CHAR_NUM #define _FIRST_CHAR 0x.... #define _LAST_CHAR 0x.... static const unsigned short _index[m]; // 码点表(升序去重) static const unsigned char _width[m]; // 仅变宽(PROP): 每字实际墨迹宽(像素) static const unsigned char _data[]; // 点阵数据(1bpp, 每行 MSB 在前) ``` > C 头文件目前只输出 **1bpp 单色**(沿用 `fc_cell_bits`,与 `FONTV02` 同语义)。 > 灰度(`--gray`)仅支持二进制格式 `GRAYS02`/`GRAYP02`,不生成 C 头。 - 等宽:每字固定 `cell_h * BYTES_PER_LINE` 字节(整单元,含水平居中留白)。 - 变宽:每字 `ceil(XSize / 8) * cell_h` 字节(仅实际墨迹列, `XSize` 由 `_width[i]` 给出),按 `index` 升序排放。 - C 头与二进制单色(`-F bin`,无 `--gray`)的字形字节逐字节一致,可任选一种 做固件集成。 --- ## 一、命令行工具(Linux / Windows 控制台) ### 构建(Linux) ```bash cd fontdot make # 内部调用 cmake,自动解压并编译 vendored FreeType # 中间产物: output/build/ 最终产物: output/target/fontdot make install # 装到 output/target ``` > 构建时会自动解压 `external/freetype/freetype-2.13.3.tar.xz`。解压按 > `cmake -E tar` → `unxz + tar` → `tar -J` 三级顺序回退,因此即使当前 CMake > 构建未内置 xz/liblzma 支持,也能用系统 `unxz`/`tar` 完成解压,不会报解压错误。 ### 构建(Windows 控制台,用 VS 开发者命令行) ```bat cmake -B output/build -S . cmake --build output/build --config Release cmake --install output/build ``` ### 用法 ```bash # 生成 ASCII 等宽字库(0x20~0x7E) fontdot -f DejaVuSans.ttf -s 16 -o ascii.bin -r 0x20-0x7E # 生成 C 头文件(可直接 #include 进固件) fontdot -f DejaVuSans.ttf -s 24 -o myfont.h -F c --name MYFONT -t "ABC012" # 变宽字库(中文字符) fontdot -f msyh.ttf -s 16 -o hanzi_prop.bin --prop -r 0x4E00-0x4E20 # 灰度字库: 4bpp 等宽(兼容 1/2/4/8bpp) fontdot -f msyh.ttf -s 16 -o hanzi_g4.bin --gray 4 -r 0x4E00-0x4E20 # 灰度变宽字库: 8bpp fontdot -f msyh.ttf -s 16 -o hanzi_g8p.bin --gray 8 --prop -r 0x4E00-0x4E20 # 从文本文件读取码点(UTF-8 或带 BOM 的 UTF-16LE) fontdot -f DejaVuSans.ttf -s 16 -o out.bin --file chars.txt # 一键回归验证: 生成 1/2/4/8bpp 灰度字库并渲染 "你好fontDot" ./test.sh ``` > 仓库根目录 `test.sh` 会依次生成 `output/font/ali_{1,2,4,8}bpp.bin` > (覆盖 `0x20-0x7E` + `你 U+4F60` + `好 U+597D`,字号 16),并对字符串 > 「你好fontDot」做灰度渲染验证,可用于快速回归确认灰度取模 / 解析链路。 ### 参数 | 参数 | 说明 | |------|------| | `-f, --font` | 字体文件路径(TTF/TTC/OTF),**必填** | | `-s, --size` | 字号:请求的 em 像素高,**必填**;默认单元 = size×size | | `-o, --output` | 输出文件路径,**必填** | | `-F, --format` | `bin`(默认) / `c` / `hex` | | `--mono/--prop` | 等宽(默认) / 变宽 | | `-b, --gray ` | 灰度字库,位深 1/2/4/8(如 4);默认关闭(单色)。`--gray` 仅对二进制格式生效 | | `-c, --cell WxH` | 点阵单元尺寸(默认等于 size x size) | | `--offset X,Y` | 字形在单元内偏移(默认 0,0) | | `--invert` | 反显(0/1 互换) | | `--name` | C 头文件符号前缀(默认 FONT) | | `-t, --text` | 直接指定字符串(UTF-8) | | `-r, --range` | 码点范围,如 `0x20-0x7E` 或 `0x4E00-0x4E20,0x41-0x5A` | | `--file` | 从文本文件读取码点(UTF-8 / UTF-16LE) | 未指定任何码点来源时,默认取 `0x20-0x7E`(ASCII 可打印字符)。 > **字形在单元内的摆放(默认行为)——墨迹自适应基线对齐**: > - `-s` 即请求的 em 像素高。写库前先以该 em 渲染**全部**码点,量出墨迹相对 > 基线的最大上伸/下伸跨距(`fc_fit_cell`,两遍取模的第一遍),把基线放在使 > 墨迹在单元内**垂直居中**的行;若墨迹总跨距超过单元高度,按比例缩小 em > 重测(最多 4 轮),保证**任何字形都不被裁剪**。 > (不能用 hhea 行高=size 的"行高语义":行高度量普遍虚高,如阿里普惠体 > 16px 的 hhea 行高达 22px,会把 em 压到 0.7 倍,1bpp 取模笔画碎裂、字形变形。) > - 每个字形再按 FreeType 的 `bitmap_left` / `bitmap_top`(相对基线的水平 > bearing / 上伸)摆放:数字与大写字母共享同一基线,小写降部(`g y p q`) > 自然伸到基线之下,中文几乎占满单元。 > - 实测(阿里普惠体 `-s 16`、单元 16×16):全字符集墨迹落在行 0-14 > (跨距 15/16,零裁剪),数字 0-9 底行一致(同基线),「你好」高 14 行, > 字形饱满不变形。圆弧字形(如 0/3/8)基线处可有 1px 反锯齿过冲,属正常排版。 > > 水平方向从 `bitmap_left`(左 bearing)起排,不做水平居中;变宽字库每字索引的 > `XSize` 字段为实际墨迹宽。 > > 可用 `--offset X,Y` 在基线摆放之上做整体微调。 --- ## 二、Windows MFC 界面 界面仅将“选择系统字体”改为“**选择字体文件**” (浏览按钮 `...`),其余(点阵尺寸、偏移、单字预览、生成等宽/变宽字库)均沿用。 #### 选项卡:FreeType 字体文件 / GDI 系统字体预览 主对话框顶部为 `SysTabControl32` 选项卡(`IDC_TAB_MAIN`),包含两个页面: - **第 0 页「FreeType 字体文件」**:原取模流程。选择字体文件,设定字号/点阵宽高/偏移, 单字预览用 FreeType 渲染(确定性、跨平台一致),可生成 `FONTV02`/`PROPV02` 字库。 - **第 1 页「GDI 系统字体预览」**(`IDD_PAGE_GDI` + `CPageGdi`):仅桌面用。 用 Windows GDI(`EnumFontFamiliesEx` 枚举已安装系统字体 → 选字号/粗体/斜体 → `CFont` + `TextOut` 渲染)做快速预览,复用 `CDotView` 点阵视图显示。 该页不参与字库生成——生成始终走 FreeType,以保证结果跨平台、可复现。 ### 用 CMake 生成 Visual Studio 工程(推荐,支持 VS2015 / 2017 / 2019 / 2022) ```bat rem VS2015 (32 位) cmake -B output/build -S . -DFONTDOT_BUILD_GUI=ON -G "Visual Studio 14 2015" rem VS2015 (64 位) cmake -B output/build -S . -DFONTDOT_BUILD_GUI=ON -G "Visual Studio 14 2015 Win64" rem 其他版本把生成器换成对应名称, 如 "Visual Studio 17 2022" cmake --build output/build --config Release cmake --install output/build # 产物: output/target/FontDot.exe ``` > 需安装对应 Visual Studio 并勾选 **“使用 C++ 的桌面开发” + “MFC 和 ATL”** 单个组件 > (MFC 界面依赖 MFC/ATL;不装则该 GUI 目标无法编译,但命令行 `fontdot` 仍可编译)。 > CMake 会自动解压并编译 vendored FreeType,无需系统 FreeType。 > 工程已同时启用 C/CXX 语言(核心库/CLI 为 C,MFC 界面为 C++),可正常生成 VS 解决方案。 ### 用旧版 VC6(.dsp/.dsw,可选) 已附带 `win/FontDot/FontDot.dsp` 与 `FontDot.dsw`。但 VC6 不包含 `stdint.h`、 对 C99 支持有限,使用旧版 VC 需先准备好两个静态库: 1. 用 FreeType 2.13.3 的 `builds/win32/...` 或自带 makefile 编译出 `freetype.lib` (关闭 zlib/bzip2/png/harfbuzz/brotli 等外部依赖)。 2. 将 `src/fontcore.c` 以 **C++ 方式(/TP)** 编译为 `fontcore.lib` (核心库已尽量采用 C89 保守写法,但仍建议用 C++ 编译以规避 VC6 的 C 缺陷)。 3. 在 VC6 中打开 `FontDot.dsw`,把上述两个 `.lib` 加入工程链接,并包含 `include/` 与 FreeType 的 `include/` 目录。 > 关于兼容性:界面/MFC 工程与 VS2015+ 完全兼容(通过 CMake 生成对应 VS 工程); > `.dsp/.dsw` 仅作为旧版 VC6 的备选入口,需按上面步骤自备 `freetype.lib` / `fontcore.lib`。 --- ## 三、验证 demo 与嵌入式移植参考(`demo/`) `demo/` 目录提供一套**不依赖 FreeType、不依赖 fontdot 可执行文件**的最小验证程序, 用于: 1. **肉眼验证取模结果**:把生成的字库读出来,渲染成 ASCII 点阵核对。 2. **嵌入式移植参考**:给出两种在 MCU 上集成字库的写法,且 **32 位 / 64 位 Linux 完全兼容** (全程使用 `stdint` 固定宽度类型、文件偏移用 `uint32_t`、无指针长度假设、无动态分配)。 详见 `demo/README.md`。要点: - `include/font_reader.h` / `src/font_reader.c`:逐字节小端解析 `FONTV02`/`PROPV02` (单色)与 `GRAYS02`/`GRAYP02`(灰度 1/2/4/8bpp),**零动态分配**,缓冲可直接 指向 Flash 映射地址。 - `include/font_render.h` / `src/font_render.c`: - `font_get_bit()` 取单像素(二进制 / 头文件字库通用,单色 1bpp); - `font_get_gray()` 取灰度像素(1/2/4/8bpp,与 `font_reader` 的 `bpp` 一致); - `font_blit_1bpp()` 把字绘制到 1bpp 帧缓冲(OLED / LED 点阵屏刷屏参考,纯算法、可上 MCU); - `font_blit_bpp()` 把任意位深字绘制到任意位深帧缓冲(源/目标 `bpp` 可不同,内部做位深缩放); - `font_render_unicode_gray()` / `font_render_ascii_gray()` 把灰度字库渲染成终端可看的 灰阶块 / ASCII 梯度,仅用于 PC 端验证(灰度字库 `GRAYS02`/`GRAYP02` 的肉眼核对)。 - `sample/`:`make_sample.py` 生成 8×8 数字示例字库(二进制 `sample_digits_8x8.bin` + 头文件 `digits_font.h`),也可直接替换为 fontdot 生成的真实字库。 - 交叉验证:单色路径下「头文件字库(DEMOFONT)」与「二进制字库(MONO)」渲染形状一致; 灰度路径下「二进制字库(GRAY)」与「帧缓冲 blit」两条灰阶块应**逐行完全一致** (demo 末尾会打印该提示);读取器对截断/损坏字库在 `font_bin_init` 阶段即拒绝, 不会越界。 ```bash cd demo make # 构建 demo_font(64 位) ./demo_font # 默认验证 0-9 ./demo_font "ABC" # 指定字库与字符串 # 32 位兼容验证(需 gcc-multilib): make CFLAGS="-O2 -Wall -Wextra -m32" ``` ---