From a3f0c60aef1cb4329f7c1aa036258b05a4e58408 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Sun, 25 Oct 2026 03:24:06 +0800 Subject: [PATCH] =?UTF-8?q?docs(UI=E8=AE=BE=E8=AE=A1):=20=E5=AD=97?= =?UTF-8?q?=E4=BD=93=E5=AD=97=E7=AC=A6=E9=9B=86=E8=AE=A1=E6=95=B0=E5=9B=9E?= =?UTF-8?q?=E5=A1=AB=20179=E5=AD=97/274=E5=AD=97=E5=BD=A2=E5=B9=B6?= =?UTF-8?q?=E7=99=BB=E8=AE=B0cmap=E9=A6=96=E5=91=BD=E4=B8=AD=E4=BA=8B?= =?UTF-8?q?=E6=95=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UI_中文字体生成与配置.md: §3 计数、§4 输出样例与补回规则(cmap 插入 位置/可达性守卫)、§7 体积口径;文末安全流程追加 2026-10-25 实测事故 - UI_界面制作流程.md: C3 计数 + 变更记录行(并校正 2026-10-15 瘦身后 119字/214字形未回填本表的历史计数) - UI_设计架构.md: 关键约束表默认字体计数 + 变更记录行 - simulator/README.md: 已知坑 #4 计数 - .dsh/skills/font-regen/SKILL.md: 步骤2 增 KEEP_EXTRA 并回警示;坑新增 "低于最小码位扩字移动稀疏 range 起点"条目;计数更新 --- .dsh/skills/font-regen/SKILL.md | 5 ++++- docs/UI设计/UI_中文字体生成与配置.md | 16 +++++++++------- docs/UI设计/UI_界面制作流程.md | 3 ++- docs/UI设计/UI_设计架构.md | 3 ++- simulator/README.md | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.dsh/skills/font-regen/SKILL.md b/.dsh/skills/font-regen/SKILL.md index 27585a51..edc35e00 100644 --- a/.dsh/skills/font-regen/SKILL.md +++ b/.dsh/skills/font-regen/SKILL.md @@ -14,6 +14,8 @@ whenToUse: UI 代码新增中文文案、用户报告屏幕缺字/空白块、 # 1. 代码落库(新界面字符串已写入 ui_menu.c / ui_content_page.c 等 UI 源码) # 2. 提取字符集(纯标准库,任意 python3 均可) python tools/font_gen/extract_cn.py # 扫描 UI 源码字符串 → 仓库根 ui_cn_chars.txt +# ⚠ extract 会把 KEEP_EXTRA 保留字(异/常/片/芯)洗掉:跑 make_font 前必须并回 +# ui_cn_chars.txt(否则 font-charset 门禁 A 段 FAIL,理由见 check_font_charset.py 头注) # 3. 生成字体(需 fontTools,一律用 .venv;宋体 .ttc 的 face0 抽取与 ▶ 箭头补回均自动完成,幂等) .venv\Scripts\python.exe tools/font_gen/make_font.py # 变体:--bpp 2(宋体细笔画发虚时)/ --font C:/path/to/font.ttf(换字体) @@ -30,4 +32,5 @@ python tools/font_gen/extract_cn.py # 扫描 UI 源码字符串 → - `requirements.txt` 必须**纯 ASCII**(Windows 下 pip 按 GBK 解码,中文注释 → UnicodeDecodeError) - 生僻字(宋体可能无字形)→ **写码前先手动验证字形**,避免"代码写完才发现是空白" - 生成成功后跑 `ui-verify` skill 全回归;黄金参考因字形变化而 FAIL 属预期 → `check_content_pages.py --update` 刷新 -- 字数变化时同步更新文档中的字符集计数(当前 97 汉字 + ASCII 0x20-0x7E,共 193 字形):`UI_中文字体生成与配置.md`、`UI_界面制作流程.md` C3、`UI_设计架构.md`、`simulator/README.md` 已知坑 #4 +- 字数变化时同步更新文档中的字符集计数(当前 179 汉字/符号 + ASCII 0x20-0x7E,共 274 字形):`UI_中文字体生成与配置.md`、`UI_界面制作流程.md` C3、`UI_设计架构.md`、`simulator/README.md` 已知坑 #4 +- **新增字低于既有最小码位**(如 Ⅰ U+2160)会下探稀疏 cmap 起点并吞掉单码位补回条目(实测 ▶ 箭头全变方框而旧版 coverage 全绿)——`make_font.py`(2026-10-25 起)按 LVGL 首命中语义插入/守卫/校验,扩字后模拟器像素回归**必跑** diff --git a/docs/UI设计/UI_中文字体生成与配置.md b/docs/UI设计/UI_中文字体生成与配置.md index d16d3467..3d6572e6 100644 --- a/docs/UI设计/UI_中文字体生成与配置.md +++ b/docs/UI设计/UI_中文字体生成与配置.md @@ -55,7 +55,7 @@ scanned 13 files; unique non-ASCII chars: 78 -> D:\Code\DTU Code\STM32F4-Base\ui_cn_chars.txt ``` -产物 `ui_cn_chars.txt` 为本工程实际显示的汉字清单(当前 122 字,含 ▶/,等符号),可留作字符集记录。 +产物 `ui_cn_chars.txt` 为本工程实际显示的汉字清单(当前 179 字,含 ▶/,/: 等符号;其中 60 字为 2026-10-25 按页面扩充规划显示串预置,源码未用前由 font-charset 门禁记 WARN 放行),可留作字符集记录。 --- @@ -85,16 +85,16 @@ npm i -g lv_font_conv # TTF/OTF -> LVGL 字体 离线转换器 ``` extracted SimSun regular (face0) -> C:\Users\...\Temp\simsun_regular.ttf RUN: lv_font_conv.cmd --font ... --size 12 --bpp 1 -r 0x20-0x7E --symbols ... --no-compress --format lvgl --lv-font-name ui_font_cn_12 -o ...\ui_font_cn_12.c -已合并箭头 U+25B6:位图+10 字节,glyph_id=206 -已合并 U+FF0C ',':1 字节位图,glyph_id=207 -OK 43614 bytes -> ...\Drivers\BSP\UI\ui_font_cn_12.c +已合并箭头 U+25B6:位图+10 字节,glyph_id=274 +U+FF0C ',' 已可经既有 cmap 解析,跳过合并 +OK 58192 bytes -> ...\Drivers\BSP\UI\ui_font_cn_12.c ``` > **▶ 箭头自动合并**:宋体(`simsun.ttc`)没有 U+25B6(▶)字形,`lv_font_conv` 会静默跳过 → 真机显示成方框。`make_font.py` 在生成后自动调用 `merge_arrow_glyph()`,把从 **Segoe UI Symbol**(`seguisym.ttf`,Windows 自带)预提取的箭头字形(位图 10 字节 + `glyph_dsc` + `cmap`)补回 `ui_font_cn_12.c`。该合并**幂等**(已存在则跳过),所以重生成字体后箭头不会丢。换用自带 ▶ 的字体(如黑体/Segoe)时可忽略此步。 -> **低码位符号自动合并**:`lv_font_conv` 的稀疏 cmap 从符号集最小码位起算,**低于该起点的符号被静默丢弃**(实测 U+3001/U+FF0C 等:U+FF0C 全角逗号 < 0x4E2D 范围起点时丢失,页脚"第 X 页,共 Y 页"的逗号长期渲染为空白)。`make_font.py` 的 `merge_extra_symbols()` 对每个此类符号单独生成迷你字体(真实宋体位图/度量),再并入主字体(位图 + `glyph_dsc` + 单码位 `cmap`),同样**幂等**。新增此类符号时把它加入脚本的 `EXTRA_SYMBOLS` 列表即可。 +> **低码位符号自动合并**:`lv_font_conv` 的稀疏 cmap 从符号集最小码位起算,**低于该起点的符号被静默丢弃**(实测 U+3001/U+FF0C 等:U+FF0C 全角逗号 < 0x4E2D 范围起点时丢失,页脚"第 X 页,共 Y 页"的逗号长期渲染为空白)。`make_font.py` 的 `merge_extra_symbols()` 对每个此类符号单独生成迷你字体(真实宋体位图/度量),再并入主字体(位图 + `glyph_dsc` + 单码位 `cmap`),同样**幂等**。新增此类符号时把它加入脚本的 `EXTRA_SYMBOLS` 列表即可。**两条 2026-10-25 规则**:合并前先用 `_cmap_lookup_gid()` 判可达(可达即跳过,不产生死条目);新 cmap 条目**插到第一个包含该码位的既有 range 之前**——LVGL `get_glyph_dsc_id()` 线性扫描、首个包含码位的 range 直接返回,追加在宽稀疏 range 之后的条目永远轮不到(见文末事故记录)。 -> **生成后覆盖率断言**:`make_font.py` 在合并后调用 `verify_coverage()`——解析生成字体的 cmap,校验 `ui_cn_chars.txt` 的**全部码位**都进了字体,缺一个即以非零退出码报错(列出 `U+XXXX`)。把 lv_font_conv 的静默丢字(低码位、源字体缺字形)从"上线后才发现空白"变成"生成期硬错误"。 +> **生成后覆盖率断言**:`make_font.py` 在合并后调用 `verify_coverage()`——解析生成字体的 cmap,校验 `ui_cn_chars.txt` 的**全部码位**都进了字体,缺一个即以非零退出码报错(列出 `U+XXXX`)。把 lv_font_conv 的静默丢字(低码位、源字体缺字形)从"上线后才发现空白"变成"生成期硬错误"。判据自 2026-10-25 起按 **LVGL 首命中语义**解析(进了某个 range ≠ 能渲染:旧判据下 cmap 条目顺序倒置可全绿而上屏全方框,见文末事故记录)。 参数说明: - `--bpp 1`:单色无抗锯齿;体积最小。**注意** lv_font_conv 在 1bpp 下斜线/细笔画偶发"丢像素",宋体尤甚。 @@ -152,7 +152,7 @@ OK 43614 bytes -> ...\Drivers\BSP\UI\ui_font_cn_12.c 1. **1bpp 细笔画发虚**:宋体在 1bpp 下可能丢像素。若上板/客户端中文偏淡,把第 4 节 `--bpp` 改为 `2` 重生成即可(子集小,体积可接受)。 2. **字符集变更**:以后新增中文菜单项,须重跑第 3、4 节(重新提取 `ui_cn_chars.txt` 并生成),否则新字显示为方框。 3. **编码**:UI 源文件须为 **UTF-8(带 BOM)**,LVGL 按 UTF-8 解码中文串;字体 `.c` 为纯 ASCII,无编码风险。当前 Keil 已加 Misc Controls 抑制项,无需额外 `--locale=english`。 -4. **字体体积**:12px + 1bpp + 122 字(含 ASCII/符号,共 217 字形)≈ 44.4KB(RO-data);改 bpp 2 约翻倍,仍在 F407 Flash 余量内。 +4. **字体体积**:12px + 1bpp + 179 字(含 ASCII/符号,共 274 字形)源文件 ≈ 56.8KB;ROM 增量以 `.map` 为准(2026-10-25 扩 60 字实测 `RO-data` +1,528 B、RAM 0);改 bpp 2 约翻倍,仍在 F407 Flash 余量内。 5. **换字体**:黑体用 `simhei.ttf`(单文件免抽取);其余字体同理替换 `--font` 路径后重生成。 --- @@ -184,3 +184,5 @@ OK 43614 bytes -> ...\Drivers\BSP\UI\ui_font_cn_12.c > **保留字形(`KEEP_EXTRA`)的存在理由**:`ui_cn_chars.txt` 里除了"源码在用",还可能有**刻意保留**的字——当前为 异 / 常 / 片 / 芯,因为它们曾在 2026-09-14 `9eb1bd63` 被写进中文自检描述落盘 `/log/selftest.log`(定长 64 B 记录跨固件升级持久),删字形会使历史故障记录渲染空白且不可逆。`scripts/check_font_charset.py` 把这类字**按必需处理**:既照"缺字",也照"保留字被静默剔除"(严格模式下 FAIL 并逐字打印保留理由)。 > **所以"只跑 `extract_cn` 重新生成字符集"会把保留字洗掉**——这是本流程最容易犯的错,钩子就是为了挡住它。`--strict` 的判据因此是"**未知**冗余 = 0",而不是"冗余 = 0"。 + +> **2026-10-25 实测事故(扩字 Ⅰ/Ⅱ/Ⅲ,页面扩充预置字批)**:新字 Ⅰ(U+2160) 低于既有最小码位,稀疏 cmap range 起点由 0x4E09 下探到 0x2160,把宋体缺字补回的 U+25B6 单码位条目**吞进了宽 range**——旧 `verify_coverage()` 判"进了 range 即覆盖"全绿,模拟器/真机箭头却全变方框(菜单截图 658 px 差异,A/B 同树定位)。根因:LVGL `get_glyph_dsc_id()` 线性扫描 cmaps[],**首个包含码位的 range 直接返回**,其 sparse 列表缺该码位也不再向后找。修复:`_insert_cmap_entry()` 把补回条目插到包含它的 range 之前 + `_cmap_lookup_gid()` 可达性守卫(可达即跳过合并且不再追加死条目)+ `verify_coverage()` 改按首命中语义。**结论:低于最小码位的扩字 = cmap 结构变更,结构对账(第 3 步)照不出解析路径倒置,模拟器像素回归必跑。** diff --git a/docs/UI设计/UI_界面制作流程.md b/docs/UI设计/UI_界面制作流程.md index 02aec070..6b608a25 100644 --- a/docs/UI设计/UI_界面制作流程.md +++ b/docs/UI设计/UI_界面制作流程.md @@ -48,7 +48,7 @@ flowchart TD |---|---|---|---| | C1 | 屏幕 | 160×160,1bpp 单色(`LV_COLOR_FORMAT_I1`),无物理屏(远端客户端显示) | `ui_port.h` | | C2 | 字体 | **唯一**:`ui_font_cn_12`(宋体 12px 1bpp)。汉字 12px/字,ASCII 6px/字,行高 13px。需要其他字号 = 大工程(新字体 + 双端 `lv_conf.h`),默认禁止 | `ui_font_cn_12.c` | -| C3 | 字符集 | 子集字体(当前 122 汉字/符号 + ASCII 0x20-0x7E,共 217 字形);**缺字显示空白**;新字必须重生成字体 | `tools/font_gen/` | +| C3 | 字符集 | 子集字体(当前 179 汉字/符号 + ASCII 0x20-0x7E,共 274 字形);**缺字显示空白**;新字必须重生成字体 | `tools/font_gen/` | | C4 | LVGL 内存池 | 16KB(`LV_MEM_SIZE`,2026-10-15 由 40KB 收缩;实测峰值 6,568 B = 池的 40%);任一时刻只放"当前导航路径 + 当前内容页" | `Lib/lv_conf.h` | | C5 | 文本 | 一律静态字符串(`lv_label_set_text_static`),禁止动态字符串/strdup | 架构文档 | | C6 | 编码 | `Drivers/BSP/UI/*`、`ui_task.c`、`Lib/lv_conf.h` 为 **UTF-8 带 BOM**;改动只用 Edit 工具,**禁止** PowerShell/cmd 整文件重写(U+FFFD 不可逆) | AGENTS.md 铁律 | @@ -412,3 +412,4 @@ P4 [ ] UI_设计架构.md 变更记录更新 | 2026-09-10 | 网口设置内容页落地后回填:C3 字符集 102→111 字符(208 字形,补网口设置/地址/掩码等,并修复 `lv_font_conv` 丢弃的低码位符号 U+FF0C 全角逗号 → 页脚逗号不再空白);新文案走 P4 字体重生成 + `check_content_pages.py` 新增网口设置 4 状态黄金参考(可编辑/只读/编辑光标反显/保存 OK);模拟器 `main` 调 `net_cfg_load` 使参考图显示工厂默认值 | | 2026-09-13 | 移除 HTML 样机阶段:五阶段 → **四阶段管线**(P0 需求录入+设计规格 / P1 规格→代码 / P2 模拟器 / P3 MCU / P4 归档);设计基准改为"规格布局参数表 + 参考图",P2 比对基准由"样机渲染图"改为"参考图 + 规格参数表"(根因分流:修代码 / 回 P0 修规格);用户视觉确认点由 HTML 浏览器移到 P2 模拟器窗口/出图;删除 `mockups/01_主界面.html`、`render_01.png`、`tools/ui_mock/render_mockup.py`(**均已删除**),保留 `NN_说明.md` 规格与原始参考图;`NN_说明.md` 升格为唯一设计基准(P1 照参数表翻译代码,P2 出图对照参数表 + 原图);`UI_自动化测试流程.md` 门禁编号同步改为 P2/P3 | | 2026-09-14 | 运行记录/自检记录页 + 待机屏落地后回填:C3 字符集 111→122 字(217 字形,补输/入/错/误/上/电/重/芯/片/异/常——**故障描述串在非 UI 源码中 extract_cn 扫不到,须手动补字**);新文案走 P4 字体重生成 + `check_content_pages.py` pwc 态参考更新(密码错误提示);池基线 4784→5440 / 往返 4864→5520(+656B = 待机模块常驻屏+容器,见两测试脚本头部注记) | +| 2026-10-25 | 字库扩充(页面扩充规划预置字):C3 字符集 122→179 字(217→274 字形;上次入表后 2026-10-15 字体瘦身实测 119 字/214 字形未回填本表,本次一并校正)。+60 字取自 `docs/UI设计/页面补齐规划.md`、`docs/UI设计/页面扩充详细方案.md` 的页面显示串(段/投/延/零/频/荷/闸/跳/低/过/Ⅰ/Ⅱ/Ⅲ 等 + 年/月/分/秒/分合/已请求/确认/取消/越界用字);源码未用期间 font-charset 记 [WARN] 属预期,批 2 页面接线后自然转"在用"。顺带修复 `make_font.py` 两处缺陷:补回字形 cmap 条目被宽稀疏 range 吞掉(Ⅰ 下探起点 0x2160 → ▶ 全变方框,A/B 同树实测)与 `verify_coverage()` 非首命中判定;菜单 8 状态 + 内容页 11 页黄金参考逐字节不变;ROM +1,528 B、RAM 0。详见 `docs/UI设计/UI_中文字体生成与配置.md` §4 与文末事故记录 | diff --git a/docs/UI设计/UI_设计架构.md b/docs/UI设计/UI_设计架构.md index 465a03df..ab8cdac7 100644 --- a/docs/UI设计/UI_设计架构.md +++ b/docs/UI设计/UI_设计架构.md @@ -38,7 +38,7 @@ MCU **无物理屏幕**:LVGL 做 160×160 **1bpp 单色离屏渲染**,帧缓 | LVGL 内存池 | 16 KB(`LV_MEM_SIZE`,2026-10-15 由 40 KB 收缩;实测峰值 6,568 B = 池的 40%) | `Lib/lv_conf.h` | | 刷新周期 | 10 ms(`ui_port_step(10)` + `osDelay(10)`) | `ui_task.c` | | 推流端口 | 7003(TCP,与 DTU-RemoteLCD 客户端对齐) | `ui_net_sink.h` | -| 默认字体 | `ui_font_cn_12`(12px 1bpp,122 汉字/符号 + ASCII 0x20–0x7E,共 217 字形) | `Lib/lv_conf.h` | +| 默认字体 | `ui_font_cn_12`(12px 1bpp,179 汉字/符号 + ASCII 0x20–0x7E,共 274 字形) | `Lib/lv_conf.h` | | uiTask 栈 | 16384 words(BelowNormal) | `Src/freertos.c` | ### 1.4 硬约束(改 UI 必须遵守) @@ -285,3 +285,4 @@ ENTER/→ 进子框: | 2026-09-09 | 用户密钥密码页落地(首个表单/操作页,§4.7 / `内容页架构.md` §4.1):框架描述符新增 title_y/hide_deco/hide_footer/key_intercept + 新 API `ui_content_page_invalidate`;新模块 `ui_page_password`(3 数字框反显光标 + arm API);菜单受保护叶子路由(arm→开密码页,确认一致后回调**先关页再续做进入动作**);字体 97→102 汉字(198 字形);双端注册;回归扩至 7 页 + 往返水位,五门禁全绿 | | 2026-09-10 | 网口设置内容页(§4.7 / `内容页架构.md`):新增 `Drivers/BSP/NETCFG`(Flash 持久化 + CRC-16,工厂默认 192.168.1.100/24)+ 页面插件 `ui_page_net_cfg`(5 行动态值 + 可编辑:↑↓调字节/←→移光标/ENTER 确认,保存行 ENTER → `net_cfg_save` 下次上电生效);框架新增钩子 `init_cb` + API `ui_content_page_selected_row`;`net_init` 增 `mac` 参数、netTask 等 `SYS_EVT_STORAGE_READY` → `net_cfg_load` → `net_init(ip,mask,gw,mac)` → 回写实际 MAC 影子;字体 102→111 字符(208 字形,修复低码位符号丢失);双端注册;回归扩至 11 页,五门禁全绿 | | 2026-09-14 | 运行记录/自检记录内容页 + 待机显示模块:新增 `App/log/sys_log`(定长 64B 文本记录引擎,MCU 落 NAND `/log/run.log`、`/log/selftest.log`,FTP 可直下)+ 页面插件 `ui_page_runlog` / `ui_page_selftest`(动态列表:count_cb + refresh,每条 2 行时间戳/描述)+ `ui_standby`(空闲超时自动切待机屏,仅确认键回菜单);密码页错误提示"密码输入错误"+清空重输;字体 111→122 字(217 字形);详见 `docs/子系统设计/运行与自检记录设计说明.md` | +| 2026-10-25 | 字库扩充(页面扩充规划预置字 +60,含罗马数字 Ⅰ/Ⅱ/Ⅲ):默认字体升至 179 字符/274 字形,像素基线不变,ROM +1,528 B、RAM 0;修复生成器 `make_font.py` 的 cmap 插入顺序缺陷——LVGL `get_glyph_dsc_id()` 线性扫描首个包含即返回,补回字形(▶)单码位条目被下探后的宽稀疏 range 吞掉即全渲染方框,现按首命中语义插位/守卫/校验(详见 `docs/UI设计/UI_中文字体生成与配置.md` 文末事故记录) | diff --git a/simulator/README.md b/simulator/README.md index 98d094bc..58174cc8 100644 --- a/simulator/README.md +++ b/simulator/README.md @@ -262,7 +262,7 @@ UI 改动的自动化门禁脚本(完整流程见 `docs/UI设计/UI_自动化 - `LV_USE_THEME_MONO = 1` 且 `sim_port_init` 中调用了 `lv_theme_mono_init` - 主循环调用了 `lv_refr_now(NULL)`(PARTIAL 模式保证首帧) - 没有重复设置 tick(SDL 驱动内部已 `lv_tick_set_cb`,**不要**再 `SDL_AddTimer` + `lv_tick_inc`) -4. **中文显示空白**:字体 `ui_font_cn_12` 是子集字体(当前 122 汉字/符号 + ASCII,共 217 字形),缺字渲染为空白。 +4. **中文显示空白**:字体 `ui_font_cn_12` 是子集字体(当前 179 汉字/符号 + ASCII,共 274 字形),缺字渲染为空白。 补字流程见 `docs/UI设计/UI_中文字体生成与配置.md`(`tools/font_gen/` 两步脚本)。 5. **改了 UI 代码但模拟器没变化**:确认改的是 `Drivers/BSP/UI/ui_menu.c`(共享原件)而非本目录副本(本目录无副本),然后重新 `cmake --build`。 6. **编码**:UI 源码为 UTF-8(带 BOM),MSVC 已加 `/utf-8`。**不要**用 PowerShell `Set-Content` 等重写含中文的文件(编码铁律,见根目录 `AGENTS.md`)。