動態文字 Glyph Push 擴充
本文件定義 glyph_push 協議擴充的版本 1。伺服器可以透過該擴充向裝置下發本地文字
字型檔中缺失的點陣圖 glyph。能力宣告和 JSON 訊息處理位於公共協議層,因此 WebSocket 與
MQTT/UDP 使用完全相同的擴充格式。
該擴充只補充文字渲染能力,不改變訊息中的文字、TTS 音訊或 STT 語義。
1. 能力宣告
裝置在客戶端 hello 訊息中宣告能力:
{
"type": "hello",
"version": 1,
"features": {
"mcp": true,
"glyph_push": true
},
"text_font": {
"bundle": "noto-v1",
"charset": "common",
"size": 20,
"bpp": 4
}
}features.glyph_push 表示裝置支援該擴充。欄位缺失或為 false 時,伺服器必須視為裝置不支援。
每次推送 payload 中的 v 欄位負責表達擴充版本。
text_font 描述裝置實際安裝的文字字型檔:
| 欄位 | 型別 | 含義 |
|---|---|---|
bundle | string | 顯式的字型 bundle 標識。glyph 度量、渲染方式、字符集或線格式變化時需要更換。 |
charset | string | 已安裝字符集。版本 1 裝置報告 basic 或 common。 |
size | number | 韌體使用的文字字型畫素規格。 |
bpp | number | 字型點陣圖的每畫素位數,目前為 1 或 4。 |
basic 是連結進韌體的字型檔。標準小智 assets 從分割槽載入 common 字型檔後會報告 common。
伺服器必須使用每個連線在 hello 中報告的實際值,不能根據板型推測。
OTA assets 可以把文字字型替換為不同字號、bpp、字符集或字型家族的 CBIN 字型。韌體仍會
載入任何結構有效的 CBIN 字型;如果 assets 同時提供完整的 text_font_meta,裝置會宣告當前
實際使用的執行時字型參數,並據此校驗 glyph push。舊版或自定義 assets 缺少相容的 glyph
metadata 時,自定義字型、表情、顏色和背景仍然正常使用,但裝置會宣告 glyph_push: false
並省略 text_font,從而只禁止不相容的 fallback glyph,不限制主題定製能力。
2. 伺服器下發格式
伺服器可以在以下訊息中附加 glyph_push 物件:
"type": "tts"、"state": "sentence_start"的 TTS 訊息;"type": "stt"的 STT 訊息。
示例:
{
"type": "tts",
"state": "sentence_start",
"text": "𠮷野家",
"glyph_push": {
"v": 1,
"bundle": "noto-v1",
"size": 20,
"bpp": 4,
"glyphs": [
{
"codepoint": 134071,
"adv_w": 320,
"box_w": 20,
"box_h": 20,
"ofs_x": 0,
"ofs_y": 0,
"bitmap": "<base64 編碼的點陣圖>"
}
]
}
}payload 頭必須與裝置能力完全匹配:
| 欄位 | 要求 |
|---|---|
v | 必須為 1。 |
bundle | 必須等於 text_font.bundle。 |
size | 必須等於 text_font.size。 |
bpp | 必須等於 text_font.bpp。 |
glyphs | 本次增量推送的 glyph 陣列,最多 64 項。 |
每個 item 使用 LVGL 原生點陣圖字型度量:
| 欄位 | 含義 |
|---|---|
codepoint | 1 到 0x10FFFF 的 Unicode code point。 |
adv_w | 帶 4 位小數的 LVGL 定點水平 advance,16 個單位等於 1 畫素。 |
box_w、box_h | 點陣圖寬高,單項範圍為 0 到 64 畫素。 |
ofs_x、ofs_y | 相對於文字基線和游標位置的有符號 16 位偏移。 |
bitmap | 未壓縮 LVGL plain 點陣圖的 Base64 編碼。 |
解碼後的點陣圖長度必須嚴格等於:
ceil(box_w * box_h * bpp / 8)點陣圖必須與對應 Noto full bundle CBIN 字型使用相同的 plain、零 stride 佈局。伺服器應直接 提取相同 profile 的 CBIN 點陣圖和度量,不應臨時使用其他字型重新光柵化。
單條 payload 的解碼點陣圖總長度不得超過 64 KiB。任意頭欄位、glyph 或點陣圖無效時,裝置會 拒絕整批 glyph,但仍使用本地字型顯示訊息文字;有 PSRAM 的裝置還可能使用此前快取的 fallback glyph。
3. 伺服器選擇流程
對於宣告 glyph_push: true 的每個連線,伺服器應:
- 根據
text_font.bundle找到對應 full 字型 bundle。 - 根據
text_font.size和text_font.bpp選擇 CBIN profile。 - 將訊息文字解碼為 Unicode code point。
- 去掉控制字元、重複字元及
text_font.charset已包含的字元。 - 從 full bundle 提取剩餘 glyph。
- 執行單訊息限制,並將一個
glyph_push物件附加到文字訊息。 - 沒有可用的缺失 glyph 時省略
glyph_push。
裝置先查詢本地文字字型,再查詢動態 fallback。因此 glyph push 只補充缺字,不會覆蓋
basic 或 common 中已有的 glyph。
同一伺服器程序中的所有裝置連線可以共享 full 字型 bundle 服務。每個連線只需根據
(bundle, charset, size, bpp) 能力組合判斷缺字並選擇 profile。
4. 裝置快取行為
同一訊息中的全部 glyph 會先加入快取,隨後只執行一次 fallback 字型 rebuild,不會每加入 一個 glyph 就 rebuild 一次。
裝置有已初始化的 PSRAM 時:
- bitmap、cmap、descriptor 和快取條目存放在 PSRAM;
- glyph 跨訊息保留;
- 快取上限為 256 個 glyph 和 64 KiB 解碼點陣圖;
- 超限時淘汰最早插入或更新的條目。
裝置沒有 PSRAM 時:
- 資料使用內部 RAM;
- 只保留當前訊息的 glyph 批次;
- 下一條文字訊息會替換或清空上一批 glyph。
該差異不改變協議。伺服器無需知道裝置是否有 PSRAM,可以為每條訊息傳送它所需的 glyph。
5. 相容與版本管理
以下任一條件成立時,伺服器不得傳送 glyph:
features.glyph_push缺失、為 false,或伺服器不支援該擴充;- 伺服器沒有裝置宣告的 bundle;
- 找不到匹配 size 和 bpp 的 full 字型 profile;
- glyph 資料不滿足版本 1 的校驗要求。
相容降級是自動的:沒有 glyph_push,或 glyph payload 被拒絕時,裝置仍會正常處理訊息。
字型生成器的度量、點陣圖佈局、源字型、字符集或渲染方式變化時,應釋出新的顯式 bundle 標識。即使 size 和 bpp 相同,也不能用一個 bundle 的標識下發另一個 bundle 的 glyph。
6. 安全要求
glyph payload 是不可信網路輸入。實現必須先完整校驗 payload,再修改正在使用的字型;限制 item 數量和解碼總長度;驗證 Base64 解碼長度;拒絕非法 code point 或度量。伺服器也應限制 單訊息處理量,避免重複傳送裝置宣告字符集已經包含的 glyph。