三特性: 字体分层(gen_font.py --charset + meow_font_reload_tier + font.load_full + 尺寸标记) + 全屏机制/状态栏收合(meow_ui_sys/input) + 通知#6 TF转储(CSV+10MB+开机清板载+内存表)

This commit is contained in:
MeowOS Dev committed 2026-08-30 11:46:29 +08:00
1 parent 0b4d1d3215
commit a2673d5922
18 files changed
+819 -80

No files matched your search

+93 -6
View File
@@ -44,8 +44,11 @@ idx 二进制布局(little-endian, version=2):
+15 u8 dy
"""
import argparse
import hashlib
import json
import os
import struct
import unicodedata
import zlib
from PIL import Image, ImageDraw, ImageFont
@@ -96,32 +99,80 @@ def pack_plane_depth(plane_l8, width, height, depth):
# --------------------------------------------------------------------------
# 字符集
# 字符集(字体档位:common / gbk / full)
# 口径见 docs/MEOWOS_SPEC_REF.md §5.2「字体档位/大字库」与 §5.7.8:
# common ≈ 4k 码位(ASCII + GB2312 常用 + 标点 + --extra) = 现状;
# gbk ≈ 20k+(再并入 CJK 统一表意 U+4E00-9FFF + CJK 符号/全角,含常用简繁/生僻);
# full ≈ 全 Unicode 可渲染区间(gbk + CJK 扩展 A/B-F + 拉丁/希腊/西里尔 + 通用标点,可选)。
# 每档在产物 font.json 写入 charset + glyph_count(见 write_font_json)。
# --------------------------------------------------------------------------
def build_charset(extra=""):
CHARSET_TIERS = ("common", "gbk", "full")
CHARSET_ALIAS = {"0": "common", "common": "common", "1": "gbk", "gbk": "gbk",
"2": "full", "full": "full"}
def build_charset(tier="common", extra=""):
if tier not in CHARSET_TIERS:
tier = "common"
cps = []
def add(cp):
if cp not in cps:
if 0 <= cp <= 0x10FFFF and cp not in cps:
cps.append(cp)
# ASCII 可打印字符
for c in range(0x20, 0x7F):
add(c)
# GB2312 一/二级常用简体(B0-D7 + D8 常用二级首段)
for hi in range(0xB0, 0xD8):
for lo in range(0xA1, 0xFF):
try:
add(ord(bytes([hi, lo]).decode("gb2312")))
except UnicodeDecodeError:
pass
for hi in range(0xA1, 0xA4):
# GB2312 符号/全角(A1-A4)
for hi in range(0xA1, 0xA5):
for lo in range(0xA1, 0xFF):
try:
add(ord(bytes([hi, lo]).decode("gb2312")))
except UnicodeDecodeError:
pass
if tier in ("gbk", "full"):
# CJK 统一表意(含常用简繁/生僻):U+4E00-9FFF ≈ 2万+
for cp in range(0x4E00, 0xA000):
add(cp)
# CJK 符号与标点 + 全角形式(补齐 common 未含的)
for cp in range(0x3000, 0x3040):
add(cp)
for cp in range(0xFF00, 0xFFF0):
add(cp)
if tier == "full":
# 拉丁补充 / 希腊 / 西里尔 等常用非 CJK(Latin-1 起已覆盖 AI 常见重音)
for cp in range(0xA0, 0x0530):
add(cp)
# CJK 扩展 A + 兼容表意
for cp in range(0x3400, 0x4DC0):
add(cp)
for cp in range(0xF900, 0xFB00):
add(cp)
# 通用标点 / 货币等(— … “ ” 等已在 gbk;此处补全)
for cp in range(0x2000, 0x2070):
add(cp)
for cp in range(0x20A0, 0x20D0):
add(cp)
# CJK 扩展 B-F(补充平面)
for cp in range(0x20000, 0x2EBF0):
add(cp)
for cp in range(0x30000, 0x31350):
add(cp)
for ch in extra:
add(ord(ch))
return sorted(cps)
def charset_glyph_count(cps):
"""glyph_count = 字符集码位数(信息用,落 font.json)。"""
return len(cps)
# --------------------------------------------------------------------------
# 逐字形渲染
# --------------------------------------------------------------------------
@@ -227,6 +278,33 @@ def write_idx(path, bit_depth, faces, glyph_tabs):
return len(header) + len(body)
# --------------------------------------------------------------------------
# font.json(元数据 + 完整性校验;schema 见 docs/MEOWOS_SPEC_REF.md §5.2)
# --------------------------------------------------------------------------
def write_font_json(out_dir, name, family, tier, glyph_count, sizes, bitdepth,
png_path, idx_path):
def sha256(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for blk in iter(lambda: f.read(1 << 16), b""):
h.update(blk)
return h.hexdigest()
meta = {
"family": family or name, # 同目录名(信息用;扫描端以目录名为 family)
"name": name, # 下拉显示名(缺则回退目录名)
"glyph_count": glyph_count, # 信息用
"charset": tier, # common|gbk|full(档位;驱动按需加载)
"size_list": sizes, # 原生面字号
"bit_depth": bitdepth, # 4|8
"sha256_png": sha256(png_path), # font.png 校验(扫描端仅 WARN)
"sha256_idx": sha256(idx_path), # font.idx 校验(扫描端仅 WARN)
}
jpath = os.path.join(out_dir, "font.json")
with open(jpath, "w", encoding="utf-8") as f:
json.dump(meta, f, ensure_ascii=False, indent=2)
return jpath
# --------------------------------------------------------------------------
# 主流程
# --------------------------------------------------------------------------
@@ -236,14 +314,18 @@ def main():
ap.add_argument("--sizes", default="28,20")
ap.add_argument("--out", required=True)
ap.add_argument("--name", default="font")
ap.add_argument("--family", default="", help="font.json family(缺省 = --name;信息用)")
ap.add_argument("--charset", default="common", choices=CHARSET_TIERS,
help="字体档位:common≈4k / gbk≈20k+ / full(可选,默认不推)")
ap.add_argument("--bitdepth", type=int, default=4, choices=(4, 8))
ap.add_argument("--extra", default="")
args = ap.parse_args()
os.makedirs(args.out, exist_ok=True)
sizes = [int(s) for s in args.sizes.split(",") if s.strip()]
cps = build_charset(args.extra)
print(f"字符集: {len(cps)} 码位")
tier = CHARSET_ALIAS.get(args.charset, "common")
cps = build_charset(tier, args.extra)
print(f"字符集: {len(cps)} 码位 (charset={tier})")
all_planes = [] # (aw, ah, plane_l8 bytes, tab)
max_w = 0
@@ -311,10 +393,15 @@ def main():
idx_path = os.path.join(args.out, f"{args.name}.idx")
idx_size = write_idx(idx_path, args.bitdepth, face_meta, [p[3] for p in all_planes])
# ---- font.json(写入 charset + glyph_count + sha256,供扫描端标注档位/大小标记) ----
jpath = write_font_json(args.out, args.name, args.family, tier, charset_glyph_count(cps),
sizes, args.bitdepth, png_path, idx_path)
png_size = os.path.getsize(png_path)
total = png_size + idx_size
print(f"\nfont.png {png_size} B")
print(f"font.idx {idx_size} B")
print(f"font.json {os.path.getsize(jpath)} B (charset={tier}, glyph_count={len(cps)})")
print(f"合计 {total} B = {total/1024:.1f} KiB = {total/1048576:.2f} MiB (预算 2MiB)")