diff --git a/.dsh/skills/font-regen/SKILL.md b/.dsh/skills/font-regen/SKILL.md index edc35e00..d5f99355 100644 --- a/.dsh/skills/font-regen/SKILL.md +++ b/.dsh/skills/font-regen/SKILL.md @@ -14,8 +14,10 @@ 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 头注) +# ⚠ extract 只认"当前源码字面量",会把清单里 KEEP_EXTRA 那批**刻意保留字**(历史落盘 +# 记录用字,逐条理由见 check_font_charset.py 的 KEEP_EXTRA 字典——不在本文列举,列举必 +# 然过期)从 txt 洗掉:跑 make_font 前必须把它们并回 ui_cn_chars.txt,否则 font-charset +# 门禁 A 段 FAIL 并逐字打印保留理由;事故路径实名登记见 docs/门禁说明/font-charset.md §6.1 # 3. 生成字体(需 fontTools,一律用 .venv;宋体 .ttc 的 face0 抽取与 ▶ 箭头补回均自动完成,幂等) .venv\Scripts\python.exe tools/font_gen/make_font.py # 变体:--bpp 2(宋体细笔画发虚时)/ --font C:/path/to/font.ttf(换字体) diff --git a/docs/门禁说明/font-charset.md b/docs/门禁说明/font-charset.md index 24748992..3d45ede4 100644 --- a/docs/门禁说明/font-charset.md +++ b/docs/门禁说明/font-charset.md @@ -15,7 +15,7 @@ | B | `ui_cn_chars.txt` vs `ui_font_cn_12.c` 的 cmap | txt 有而字体无 → **GAP FAIL**(转换器对缺字形/低码位会**静默丢字**) | A 通过 + B 通过 ⟹ 字体覆盖"源码在用"与"刻意保留"的每一个字。 -`KEEP_EXTRA` 的存在理由:防止将来 `extract_cn.py` 按"源码已不用"静默删掉**已落盘记录里还在用**的字。 +`KEEP_EXTRA` 的存在理由:防止将来 `extract_cn.py` 按"源码已不用"静默删掉**已落盘记录里还在用**的字(这条路径的实际形态与实测数字见 §6.1)。 - **单一规则源**:本脚本不自创第二套"从源码里找汉字"的规则,而是复用 `tools/font_gen/extract_cn.py` 的 `SCAN` 清单与 `collect()` 状态机(跳过注释与字符常量、只取双引号字符串)。**两处各写一套解析, @@ -55,6 +55,34 @@ A 通过 + B 通过 ⟹ 字体覆盖"源码在用"与"刻意保留"的每一个 2. 必需集来自"源码里出现的汉字",**运行期拼出来的字符串**(格式化进来的字、外部配置文件里的字、 记录内容回显)完全在视野外——那类白框本门照不到。 3. 只校验**码位覆盖**,不校验字形是否美观/是否为所需点阵尺寸。 + +### 6.1 「按 extract 输出重写字符集」= 洗字形(实名登记的事故路径) + +`tools/font_gen/extract_cn.py` 只认**当前源码里的字符串字面量**,照不出运行期数据里的字—— +典型是 2026-09 窗口固件写进 `/log/selftest.log`、`/log/run.log` 的定长 64 B 中文描述记录 +(跨固件持久,自检/运行记录页直接渲染 `desc`)。实测口径(2026-10-25):extract 扫出 +**144 字**,而 `ui_cn_chars.txt` 现有 **183 字**,差的 39 字全部由 `KEEP_EXTRA` 兜住: + +- **逐字登记 4 字**(异/常/片/芯):出处可指到 `9eb1bd63`(2026-09-14 中文自检描述) + 与 `6f51c62a`(2026-09-20 串口侧改 ASCII),脚本里逐字写了理由; +- **成组登记 35 字**(2026-10-25 道E task-15 并入):正是本门禁「未知冗余」WARN 的全部 + 条目(一位公分合外已年度延态总接显最月未求温状电示秒请跳载近道部配重长间闸障)。来源 + 能确认是那批落盘记录的用字,**但逐字出处已不可靠复原**(文案被整体改写,指不到某个 + 字来自哪条 commit)→ 选择整组保留并在脚本里点名成组,**而不是给每个字编一条看起来 + 可信的理由**。判据:字形不可再生(删了永久白框、无日志无报错,只在渲染历史记录时暴露), + 保留成本约 35 字 × 27 B ≈ 1 KB RO——收益与代价不对称(同本目录"收益不抵风险"暂缓)。 + +**事故路径**:有人跑 `extract_cn.py`,把输出当"权威清单"直接覆盖 `ui_cn_chars.txt` → +39 字被静默洗掉 → 下次 font-regen 生成的字体不再有这些字形 → 现场翻到 2026-09 那批历史 +记录时相关行渲染成空白方块,且没有任何地方报错。 + +**机械化拦截**:`KEEP_EXTRA` 按"必需字"参与 A 段判定——txt 缺任一保留字直接 GAP FAIL +并逐字打印保留理由(`--strict` 同样 FAIL)→ "洗字形"在**下一次提交**就响,不靠人记性。 +配套流程约束:font-regen 第 3 步(重生成字体)之前必须把 `KEEP_EXTRA` 并回 txt。 + +**删除前置条件**(满足后才可删条目,删后下次 font-regen 自然瘦身):确认设备上已无该窗口 +的落盘记录,或产品侧明确接受历史记录部分渲染空白。只删 txt/字体里的字而不删本清单 = +本门禁立刻 FAIL(刻意:清单是唯一真相源)。 4. 拉丁字母、数字、标点不在本门范围(另有字体与布局约定)。 5. `--strict` 不在钩子默认档位里(默认只拦 GAP):冗余字形长期靠人自觉清理。 6. 字体文件本身若被手工编辑(不跑生成器),A/B 两段仍可能同时"通过"而与实际 cmap 不符—— @@ -62,8 +90,8 @@ A 通过 + B 通过 ⟹ 字体覆盖"源码在用"与"刻意保留"的每一个 ## 7. 豁免与基线 -- `KEEP_EXTRA` 是**刻意保留清单**(存量 + 逐条理由),性质同 `registration-check` 的已知缺口表; - 新增要写理由,删掉等于放弃保护。 +- `KEEP_EXTRA` 是**刻意保留清单**(存量 + 理由;出处可考者逐字登记,出处不可考者成组登记, + 见 §6.1),性质同 `registration-check` 的已知缺口表;新增要写理由,删掉等于放弃保护。 - 没有"只减不增"的债务基线。 ## 8. 改这个门禁本身 @@ -76,3 +104,4 @@ A 通过 + B 通过 ⟹ 字体覆盖"源码在用"与"刻意保留"的每一个 | 日期 | 变更 | |---|---| | 2026-10-23 | 首篇逐项文档(两段链条判定口径 + 生成期校验覆盖不到的那一环) | +| 2026-10-25 | 新增 §6.1:实名登记「按 extract 输出重写字符集 = 洗字形」这条事故路径;把当时「未知冗余」WARN 的 35 字**成组**并入 `KEEP_EXTRA`(实测 extract 144 字 vs 清单 183 字,差 39 字 = 逐字 4 + 成组 35),并写清删除前置条件(道E task-15) | diff --git a/scripts/check_font_charset.py b/scripts/check_font_charset.py index fa1790f4..81667819 100644 --- a/scripts/check_font_charset.py +++ b/scripts/check_font_charset.py @@ -79,6 +79,25 @@ KEEP_EXTRA = { "片": "同 \"时钟芯片异常\"(9eb1bd63 写入,落盘持久)", } +# 2026-10-25 道E task-15 补充:**成组保留**的历史落盘记录用字(35 字)。 +# 发现方式:本钩子的「未知冗余」WARN 清单(txt 有、源码无、且不在 KEEP_EXTRA)实测正好 +# 35 字——全部是 2026-09 窗口固件写进 /log/selftest.log(与 /log/run.log)定长 64 B 记录 +# 里的中文描述用字。那批源码文案后来被改写(串口侧改 ASCII),**逐字出处已不可靠复原**: +# 能确认来源是落盘记录用字(不是美术字、也不是手工修 cmap),但指不到「这个字出自哪条 +# commit」。两难取其下——字形不可再生(删了就永久白框)而保留成本极低(35 字 ≈ 1 KB RO) +# → 整组保留并在此**点名成组**,而不是给每个字编一条看起来可信的理由。 +# 删除前置条件:确认设备上已无该窗口的落盘记录(或已接受历史记录部分渲染白框)。 +# ⚠ 本组真正的价值是把一条路径钉死:**「按 extract_cn 输出重写 ui_cn_chars.txt」= 洗字形** +# (extract 只认当前源码字面量,实测 144 字,而 txt 现在 183 字)——谁按它的输出覆盖 +# txt,就会把这 35 字连同上面 4 字一起洗掉,且只在渲染历史日志时才暴露、无日志无报错。 +# 成因与判据详述 → docs/门禁说明/font-charset.md §6.1 +_KEEP_HIST_SELFTEST = ("历史落盘记录用字(2026-09 窗口固件写入 /log/selftest.log 与" + " /log/run.log 的定长 64 B 中文描述;源码文案已改写、逐字出处不可靠" + " 复原,故整组保留——删任一即让对应历史行永久渲染空白且不可逆;" + " 成因与删除前置条件见 docs/门禁说明/font-charset.md §6.1)") +KEEP_EXTRA.update({c: _KEEP_HIST_SELFTEST for c in + "一位公分合外已年度延态总接显最月未求温状电示秒请跳载近道部配重长间闸障"}) + def load_tool(name): """按文件路径加载 tools/font_gen/.py(无包结构,importlib 直载)。