2026-08-28 11:24:06 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""Generate MiSans bitmap font C arrays for ILI9341 LCD driver."""
|
|
|
|
|
|
|
|
|
|
|
|
import glob
|
|
|
|
|
|
import math
|
|
|
|
|
|
import os
|
|
|
|
|
|
import re
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
|
|
|
|
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
|
HEADER_IN = os.path.join(ROOT, "device", "LCD_ILI9341", "Drv_ILI9341_Lcd_ChineseFont.h")
|
|
|
|
|
|
HEADER_OUT = HEADER_IN
|
|
|
|
|
|
ENG_HEADER_IN = os.path.join(ROOT, "device", "LCD_ILI9341", "Drv_ILI9341_Lcd_EnglishFont.h")
|
|
|
|
|
|
ENG_HEADER_OUT = ENG_HEADER_IN
|
|
|
|
|
|
|
|
|
|
|
|
MISANS_DIR = os.path.join(os.path.dirname(ROOT), "..", "Doc", "UI", "MiSans")
|
|
|
|
|
|
MISANS_DIR = os.path.normpath(MISANS_DIR)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_ttf(name_part):
|
|
|
|
|
|
for path in glob.glob(os.path.join(MISANS_DIR, "**", "*.ttf"), recursive=True):
|
|
|
|
|
|
if name_part.lower() in os.path.basename(path).lower():
|
|
|
|
|
|
return path
|
|
|
|
|
|
raise FileNotFoundError(f"TTF matching {name_part} not found under {MISANS_DIR}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_chs_tables(text):
|
|
|
|
|
|
tables = {}
|
|
|
|
|
|
for m in re.finditer(r"const char (CHS\d+Table)\[\]\[2\] = \{(.*?)\};", text, re.S):
|
|
|
|
|
|
name = m.group(1)
|
|
|
|
|
|
body = m.group(2)
|
|
|
|
|
|
entries = []
|
|
|
|
|
|
comments = []
|
|
|
|
|
|
for line in body.splitlines():
|
|
|
|
|
|
cm = re.search(r"\{(0x[0-9A-Fa-f]{2})\s*,\s*(0x[0-9A-Fa-f]{2})\}", line)
|
|
|
|
|
|
if cm:
|
|
|
|
|
|
entries.append((int(cm.group(1), 16), int(cm.group(2), 16)))
|
|
|
|
|
|
cmt = re.search(r"//\s*(.+)", line)
|
|
|
|
|
|
comments.append(cmt.group(1).strip() if cmt else "")
|
|
|
|
|
|
tables[name] = list(zip(entries, comments))
|
|
|
|
|
|
return tables
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def gbk_char(h, l):
|
|
|
|
|
|
return bytes([h, l]).decode("gbk", errors="replace")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-03 17:59:58 +08:00
|
|
|
|
def _dilate(pixels, w, h, times=1):
|
|
|
|
|
|
cur = pixels[:]
|
|
|
|
|
|
for _ in range(times):
|
|
|
|
|
|
nxt = cur[:]
|
|
|
|
|
|
for y in range(h):
|
|
|
|
|
|
for x in range(w):
|
|
|
|
|
|
if cur[y * w + x]:
|
|
|
|
|
|
continue
|
|
|
|
|
|
hit = False
|
|
|
|
|
|
for dy in (-1, 0, 1):
|
|
|
|
|
|
for dx in (-1, 0, 1):
|
|
|
|
|
|
nx, ny = x + dx, y + dy
|
|
|
|
|
|
if 0 <= nx < w and 0 <= ny < h and cur[ny * w + nx]:
|
|
|
|
|
|
nxt[y * w + x] = 1
|
|
|
|
|
|
hit = True
|
|
|
|
|
|
break
|
|
|
|
|
|
if hit:
|
|
|
|
|
|
break
|
|
|
|
|
|
cur = nxt
|
|
|
|
|
|
return cur
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_glyph(ch, size, font, square=True, threshold=90, pack_ink=False, thicken=0):
|
|
|
|
|
|
"""Rasterize into size×size (CJK) or (size/2)×size (ASCII).
|
|
|
|
|
|
|
|
|
|
|
|
pack_ink=True (recommended for size<=13): crop FreeType ink bbox, fit into
|
|
|
|
|
|
cell, lower threshold — keeps strokes at tiny LCD sizes where MiSans Medium
|
|
|
|
|
|
otherwise collapses to sparse fragments.
|
|
|
|
|
|
"""
|
2026-08-28 11:24:06 +08:00
|
|
|
|
if square:
|
|
|
|
|
|
w = h = size
|
|
|
|
|
|
else:
|
2026-09-03 17:59:58 +08:00
|
|
|
|
w = max(3, size // 2)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
h = size
|
|
|
|
|
|
|
2026-09-03 17:59:58 +08:00
|
|
|
|
if not pack_ink:
|
|
|
|
|
|
scale = 4
|
|
|
|
|
|
big = Image.new("L", (w * scale, h * scale), 0)
|
|
|
|
|
|
draw = ImageDraw.Draw(big)
|
|
|
|
|
|
bbox = draw.textbbox((0, 0), ch, font=font)
|
|
|
|
|
|
tw = max(1, bbox[2] - bbox[0])
|
|
|
|
|
|
th = max(1, bbox[3] - bbox[1])
|
|
|
|
|
|
pad = scale
|
|
|
|
|
|
inner_w = w * scale - 2 * pad
|
|
|
|
|
|
inner_h = h * scale - 2 * pad
|
|
|
|
|
|
x = pad + (inner_w - tw) // 2 - bbox[0]
|
|
|
|
|
|
y = pad + (inner_h - th) // 2 - bbox[1]
|
|
|
|
|
|
draw.text((x, y), ch, fill=255, font=font)
|
|
|
|
|
|
img = big.resize((w, h), Image.Resampling.LANCZOS)
|
|
|
|
|
|
pixels = [1 if img.getpixel((col, row)) > threshold else 0
|
|
|
|
|
|
for row in range(h) for col in range(w)]
|
|
|
|
|
|
if thicken:
|
|
|
|
|
|
pixels = _dilate(pixels, w, h, times=thicken)
|
|
|
|
|
|
return pixels, w, h
|
|
|
|
|
|
|
|
|
|
|
|
# --- small-size path: oversample → threshold → crop ink → fit cell ---
|
|
|
|
|
|
try:
|
|
|
|
|
|
ttf_path = font.path
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
ttf_path = None
|
|
|
|
|
|
scale = 8
|
|
|
|
|
|
pt = max(size + 2, int(size * 1.35)) * scale
|
|
|
|
|
|
font_big = ImageFont.truetype(ttf_path, pt) if ttf_path else font
|
|
|
|
|
|
canvas = Image.new("L", (w * scale * 2, h * scale * 2), 0)
|
|
|
|
|
|
draw = ImageDraw.Draw(canvas)
|
|
|
|
|
|
bbox = draw.textbbox((0, 0), ch, font=font_big)
|
|
|
|
|
|
margin = scale
|
|
|
|
|
|
draw.text((margin - bbox[0], margin - bbox[1]), ch, fill=255, font=font_big)
|
|
|
|
|
|
ink = canvas.point(lambda p: 255 if p > threshold else 0)
|
|
|
|
|
|
ib = ink.getbbox()
|
|
|
|
|
|
if ib is None:
|
|
|
|
|
|
return [0] * (w * h), w, h
|
|
|
|
|
|
cropped = ink.crop(ib)
|
|
|
|
|
|
cw, chh = cropped.size
|
|
|
|
|
|
pad = 1 if size >= 11 else 0
|
|
|
|
|
|
tw = max(1, w - 2 * pad)
|
|
|
|
|
|
th = max(1, h - 2 * pad)
|
|
|
|
|
|
fit = min(tw / cw, th / chh)
|
|
|
|
|
|
nw = max(1, int(round(cw * fit)))
|
|
|
|
|
|
nh = max(1, int(round(chh * fit)))
|
|
|
|
|
|
resized = cropped.resize((nw, nh), Image.Resampling.LANCZOS)
|
|
|
|
|
|
out = Image.new("L", (w, h), 0)
|
|
|
|
|
|
out.paste(resized, ((w - nw) // 2, (h - nh) // 2))
|
|
|
|
|
|
pixels = [1 if out.getpixel((col, row)) > 128 else 0
|
|
|
|
|
|
for row in range(h) for col in range(w)]
|
|
|
|
|
|
if thicken:
|
|
|
|
|
|
pixels = _dilate(pixels, w, h, times=thicken)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
return pixels, w, h
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cn_to_bytes_msb(pixels, width, height):
|
|
|
|
|
|
row_bytes = (width + 7) // 8
|
|
|
|
|
|
out = []
|
|
|
|
|
|
for row in range(height):
|
|
|
|
|
|
for bi in range(row_bytes):
|
|
|
|
|
|
val = 0
|
|
|
|
|
|
for bit in range(8):
|
|
|
|
|
|
col = bi * 8 + bit
|
|
|
|
|
|
if col < width and pixels[row * width + col]:
|
|
|
|
|
|
val |= (0x80 >> bit)
|
|
|
|
|
|
elif col >= width:
|
|
|
|
|
|
pass
|
|
|
|
|
|
out.append(val)
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ascii_to_bytes_lsb(pixels, width, height):
|
|
|
|
|
|
row_bytes = (width + 7) // 8
|
|
|
|
|
|
out = []
|
|
|
|
|
|
for row in range(height):
|
|
|
|
|
|
for bi in range(row_bytes):
|
|
|
|
|
|
val = 0
|
|
|
|
|
|
for bit in range(8):
|
|
|
|
|
|
col = bi * 8 + bit
|
|
|
|
|
|
if col < width and pixels[row * width + col]:
|
|
|
|
|
|
val |= (0x01 << bit)
|
|
|
|
|
|
out.append(val)
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fmt_bytes(data):
|
|
|
|
|
|
parts = []
|
|
|
|
|
|
for i, b in enumerate(data):
|
|
|
|
|
|
parts.append(f"0x{b:02X}")
|
|
|
|
|
|
lines = []
|
|
|
|
|
|
for i in range(0, len(parts), 16):
|
|
|
|
|
|
lines.append(",".join(parts[i:i + 16]))
|
|
|
|
|
|
return "{" + ",".join(lines) + "}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_chinese_array(table_name, entries, size, font):
|
2026-09-03 17:59:58 +08:00
|
|
|
|
# Small CJK: pack ink + lower threshold (Semibold preferred by caller)
|
|
|
|
|
|
pack = size <= 13
|
|
|
|
|
|
thr = 40 if pack else 90
|
2026-08-28 11:24:06 +08:00
|
|
|
|
lines = [f"const unsigned char Chinese_font_{size}[][{((size + 7)//8)*size}] = {{"]
|
|
|
|
|
|
for idx, ((h, l), comment) in enumerate(entries):
|
|
|
|
|
|
ch = gbk_char(h, l)
|
2026-09-03 17:59:58 +08:00
|
|
|
|
pixels, w, hgt = render_glyph(
|
|
|
|
|
|
ch, size, font, square=True, threshold=thr, pack_ink=pack, thicken=0)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
data = cn_to_bytes_msb(pixels, w, hgt)
|
|
|
|
|
|
cmt = comment or ch
|
|
|
|
|
|
lines.append(f"{fmt_bytes(data)},/*\"{cmt}\",{idx}*/")
|
|
|
|
|
|
lines.append("};")
|
|
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_ascii_array(size, font, array_name=None):
|
|
|
|
|
|
sizex = size // 2
|
|
|
|
|
|
row_bytes = (sizex + 7) // 8
|
|
|
|
|
|
total = row_bytes * size
|
|
|
|
|
|
suffix = f"{size}{sizex}"
|
|
|
|
|
|
name = array_name or f"ascii_{suffix}"
|
2026-09-03 17:59:58 +08:00
|
|
|
|
# Tiny ASCII (KEY/BPM/digits): Semibold + ink-pack; no dilate (avoids blob)
|
|
|
|
|
|
pack = size <= 13
|
|
|
|
|
|
thr = 40 if pack else 90
|
2026-08-28 11:24:06 +08:00
|
|
|
|
lines = [f"const unsigned char {name}[][{total}]={{"]
|
|
|
|
|
|
|
|
|
|
|
|
for num in range(95):
|
|
|
|
|
|
ch = chr(num + 32)
|
2026-09-03 17:59:58 +08:00
|
|
|
|
pixels, w, hgt = render_glyph(
|
|
|
|
|
|
ch, size, font, square=False, threshold=thr, pack_ink=pack, thicken=0)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
data = ascii_to_bytes_lsb(pixels, w, hgt)
|
|
|
|
|
|
lines.append(f"{fmt_bytes(data)},/*\"{ch}\",{num}*/")
|
|
|
|
|
|
lines.append("};")
|
|
|
|
|
|
return name, "\n".join(lines)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def patch_header_chinese(text, tables, fonts_by_size):
|
|
|
|
|
|
# Replace each Chinese_font_XX array
|
|
|
|
|
|
for size, font in fonts_by_size.items():
|
|
|
|
|
|
table_key = {
|
|
|
|
|
|
8: "CHS8Table",
|
|
|
|
|
|
11: "CHS11Table",
|
|
|
|
|
|
12: "CHS12Table",
|
|
|
|
|
|
13: "CHS13Table",
|
|
|
|
|
|
15: "CHS15Table",
|
|
|
|
|
|
16: "CHS16Table",
|
|
|
|
|
|
28: "CHS24Table", # reuse 24 table chars for 28 if no CHS28
|
|
|
|
|
|
}.get(size)
|
|
|
|
|
|
|
|
|
|
|
|
if size == 28:
|
|
|
|
|
|
table_key = "CHS24Table"
|
|
|
|
|
|
if table_key not in tables:
|
|
|
|
|
|
print(f"Skip size {size}: no {table_key}")
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
entries = tables[table_key]
|
|
|
|
|
|
new_array = generate_chinese_array(table_key, entries, size, font)
|
|
|
|
|
|
|
|
|
|
|
|
pat = rf"const unsigned char Chinese_font_{size}\[\]\[\d+\] = \{{.*?\}};"
|
|
|
|
|
|
if re.search(pat, text, re.S):
|
|
|
|
|
|
text = re.sub(pat, new_array, text, count=1, flags=re.S)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# insert before Chinese_font for next size or at end of tables section
|
|
|
|
|
|
insert_after = f"const char {table_key}"
|
|
|
|
|
|
# add new table for CHS8/CHS15 if missing
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_chs8_table(text, tables):
|
|
|
|
|
|
if "CHS8Table" in tables:
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
if "CHS12Table" not in tables:
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
# Insert CHS8Table copy of CHS12Table before CHS11Table
|
|
|
|
|
|
entries = tables["CHS12Table"]
|
|
|
|
|
|
block = "const char CHS8Table[][2] = {\n"
|
|
|
|
|
|
for (h, l), cmt in entries:
|
|
|
|
|
|
block += f" {{0x{h:02X},0x{l:02X}}}, // {cmt}\n"
|
|
|
|
|
|
block += "};\n\n"
|
|
|
|
|
|
text = text.replace("const char CHS11Table", block + "const char CHS11Table", 1)
|
|
|
|
|
|
tables["CHS8Table"] = entries
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-09-03 11:58:08 +08:00
|
|
|
|
def ensure_chs9_table(text, tables):
|
|
|
|
|
|
if "CHS9Table" in tables:
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
src = "CHS11Table" if "CHS11Table" in tables else ("CHS12Table" if "CHS12Table" in tables else None)
|
|
|
|
|
|
if not src:
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
entries = tables[src]
|
|
|
|
|
|
block = "const char CHS9Table[][2] = {\n"
|
|
|
|
|
|
for (h, l), cmt in entries:
|
|
|
|
|
|
block += f" {{0x{h:02X},0x{l:02X}}}, // {cmt}\n"
|
|
|
|
|
|
block += "};\n\n"
|
|
|
|
|
|
text = text.replace("const char CHS11Table", block + "const char CHS11Table", 1)
|
|
|
|
|
|
tables["CHS9Table"] = entries
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-08-28 11:24:06 +08:00
|
|
|
|
def ensure_chs15_table(text, tables):
|
|
|
|
|
|
if "CHS15Table" in tables:
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
if "CHS13Table" not in tables:
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
entries = tables["CHS13Table"]
|
|
|
|
|
|
block = "const char CHS15Table[][2] = {\n"
|
|
|
|
|
|
for (h, l), cmt in entries:
|
|
|
|
|
|
block += f" {{0x{h:02X},0x{l:02X}}}, // {cmt}\n"
|
|
|
|
|
|
block += "};\n\n"
|
|
|
|
|
|
text = text.replace("const char CHS16Table", block + "const char CHS16Table", 1)
|
|
|
|
|
|
tables["CHS15Table"] = entries
|
|
|
|
|
|
return text, tables
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
medium = find_ttf("MiSans-Medium")
|
|
|
|
|
|
semibold = find_ttf("MiSans-Semibold")
|
|
|
|
|
|
print("Medium:", medium)
|
|
|
|
|
|
print("Semibold:", semibold)
|
|
|
|
|
|
|
2026-09-03 11:58:08 +08:00
|
|
|
|
only = None
|
|
|
|
|
|
for a in sys.argv[1:]:
|
|
|
|
|
|
if a.startswith("--only="):
|
|
|
|
|
|
only = {int(x) for x in a.split("=", 1)[1].split(",") if x.strip()}
|
|
|
|
|
|
|
2026-08-28 11:24:06 +08:00
|
|
|
|
with open(HEADER_IN, "r", encoding="utf-8", errors="replace") as f:
|
|
|
|
|
|
text = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
tables = parse_chs_tables(text)
|
|
|
|
|
|
print("Tables:", {k: len(v) for k, v in tables.items()})
|
|
|
|
|
|
|
|
|
|
|
|
text, tables = ensure_chs8_table(text, tables)
|
2026-09-03 11:58:08 +08:00
|
|
|
|
text, tables = ensure_chs9_table(text, tables)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
text, tables = ensure_chs15_table(text, tables)
|
|
|
|
|
|
|
|
|
|
|
|
# Regenerate all Chinese font arrays
|
2026-09-03 11:58:08 +08:00
|
|
|
|
cn_sizes = [8, 9, 11, 12, 13, 15, 16, 24, 28]
|
|
|
|
|
|
if only:
|
|
|
|
|
|
cn_sizes = [s for s in cn_sizes if s in only]
|
2026-08-28 11:24:06 +08:00
|
|
|
|
for size in cn_sizes:
|
|
|
|
|
|
if size in (24, 28):
|
|
|
|
|
|
table_key = "CHS24Table"
|
|
|
|
|
|
font = ImageFont.truetype(semibold if size == 28 else medium, size - 2)
|
|
|
|
|
|
else:
|
|
|
|
|
|
table_key = {
|
2026-09-03 11:58:08 +08:00
|
|
|
|
8: "CHS8Table", 9: "CHS9Table", 11: "CHS11Table", 12: "CHS12Table",
|
2026-08-28 11:24:06 +08:00
|
|
|
|
13: "CHS13Table", 15: "CHS15Table", 16: "CHS16Table",
|
|
|
|
|
|
}[size]
|
2026-09-03 17:59:58 +08:00
|
|
|
|
# ≤13: Semibold + pack_ink inside generate_*; larger: Medium oversample
|
|
|
|
|
|
ttf = semibold if size <= 13 else medium
|
|
|
|
|
|
pt = max(8, size + 2) if size <= 13 else max(8, size + 2) * 4
|
|
|
|
|
|
font = ImageFont.truetype(ttf, pt)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
|
|
|
|
|
|
if table_key not in tables:
|
|
|
|
|
|
continue
|
|
|
|
|
|
entries = tables[table_key]
|
|
|
|
|
|
new_array = generate_chinese_array(table_key, entries, size, font)
|
|
|
|
|
|
pat = rf"const unsigned char Chinese_font_{size}\[\]\[\d+\] =[\s\S]*?\}};"
|
|
|
|
|
|
if re.search(pat, text, re.S):
|
|
|
|
|
|
text = re.sub(pat, new_array, text, count=1, flags=re.S)
|
|
|
|
|
|
print(f"Updated Chinese_font_{size} ({len(entries)} glyphs)")
|
|
|
|
|
|
else:
|
|
|
|
|
|
# Insert new array before Chinese_font for next existing or after CHS tables
|
|
|
|
|
|
anchor = "const unsigned char Chinese_font_11"
|
|
|
|
|
|
if size == 8:
|
|
|
|
|
|
anchor = "const unsigned char Chinese_font_11"
|
2026-09-03 11:58:08 +08:00
|
|
|
|
elif size == 9:
|
|
|
|
|
|
anchor = "const unsigned char Chinese_font_11"
|
2026-08-28 11:24:06 +08:00
|
|
|
|
elif size == 15:
|
|
|
|
|
|
anchor = "const unsigned char Chinese_font_16"
|
|
|
|
|
|
elif size == 28:
|
|
|
|
|
|
anchor = None
|
|
|
|
|
|
if anchor and anchor in text:
|
|
|
|
|
|
text = text.replace(anchor, new_array + "\n\n" + anchor, 1)
|
|
|
|
|
|
print(f"Inserted Chinese_font_{size}")
|
|
|
|
|
|
elif size == 28:
|
|
|
|
|
|
text = text.rstrip() + "\n\n" + new_array + "\n"
|
|
|
|
|
|
print(f"Appended Chinese_font_{size}")
|
|
|
|
|
|
|
|
|
|
|
|
with open(HEADER_OUT, "w", encoding="utf-8", newline="\n") as f:
|
|
|
|
|
|
f.write(text)
|
|
|
|
|
|
|
2026-09-03 09:19:29 +08:00
|
|
|
|
# English / ASCII:默认跳过,避免破坏已校准的数字字模;需要时传 --ascii
|
2026-09-03 11:58:08 +08:00
|
|
|
|
# --only= 仅过滤要生成的字号,仍须配合 --ascii 才会写英文头文件
|
2026-09-03 09:19:29 +08:00
|
|
|
|
if "--ascii" not in sys.argv:
|
|
|
|
|
|
print("Skip English fonts (pass --ascii to regenerate).")
|
|
|
|
|
|
print("Done.")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-08-28 11:24:06 +08:00
|
|
|
|
with open(ENG_HEADER_IN, "r", encoding="utf-8", errors="replace") as f:
|
|
|
|
|
|
eng = f.read()
|
|
|
|
|
|
|
2026-09-03 17:59:58 +08:00
|
|
|
|
# ≤13 ASCII: Semibold (thicker strokes survive 4×8 / 5×11 cells)
|
2026-08-28 11:24:06 +08:00
|
|
|
|
ascii_targets = [
|
2026-09-03 17:59:58 +08:00
|
|
|
|
(8, semibold, None), (9, semibold, None), (11, semibold, None), (12, semibold, None),
|
|
|
|
|
|
(13, semibold, "ascii_1306"), (15, medium, None), (16, medium, "ascii_1608"),
|
2026-09-03 11:58:08 +08:00
|
|
|
|
(24, medium, "ascii_2412"), (28, semibold, None),
|
2026-08-28 11:24:06 +08:00
|
|
|
|
]
|
2026-09-03 11:58:08 +08:00
|
|
|
|
if only:
|
|
|
|
|
|
ascii_targets = [t for t in ascii_targets if t[0] in only]
|
2026-08-28 11:24:06 +08:00
|
|
|
|
for size, ttf, legacy_name in ascii_targets:
|
2026-09-03 17:59:58 +08:00
|
|
|
|
font = ImageFont.truetype(ttf, max(8, size + 1) if size <= 13 else max(6, size - 1))
|
2026-08-28 11:24:06 +08:00
|
|
|
|
name, arr = generate_ascii_array(size, font, legacy_name)
|
|
|
|
|
|
pat = rf"const unsigned char {name}\[\]\[\d+\][\s\S]*?\}};"
|
|
|
|
|
|
if re.search(pat, eng, re.S):
|
|
|
|
|
|
eng = re.sub(pat, arr, eng, count=1, flags=re.S)
|
|
|
|
|
|
print(f"Updated {name}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
auto_name, _ = generate_ascii_array(size, font)
|
|
|
|
|
|
pat2 = rf"const unsigned char {auto_name}\[\]\[\d+\][\s\S]*?\}};"
|
|
|
|
|
|
if auto_name != name and re.search(pat2, eng, re.S):
|
|
|
|
|
|
eng = re.sub(pat2, arr, eng, count=1, flags=re.S)
|
|
|
|
|
|
print(f"Updated {auto_name} -> {name}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
eng = eng.rstrip() + "\n\n" + arr + "\n"
|
|
|
|
|
|
print(f"Appended {name}")
|
|
|
|
|
|
|
|
|
|
|
|
with open(ENG_HEADER_OUT, "w", encoding="utf-8", newline="\n") as f:
|
|
|
|
|
|
f.write(eng)
|
|
|
|
|
|
|
|
|
|
|
|
print("Done.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|