1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
""" 生成微雪风格的 CH_CN 字库 C 文件。 默认:微软雅黑 24 号,32x41 点阵,横向取模,MSB first,自动居中。
用法示例: python gen_font.py --text "中华人民共和国" python gen_font.py --text "你好微雪" --size 24 --w 32 --h 41 --out Font24CN.c """
import argparse import os import sys from PIL import Image, ImageDraw, ImageFont
DEFAULT_TTF = r"C:\Windows\Fonts\msyh.ttc" DEFAULT_SIZE = 24 DEFAULT_W = 32 DEFAULT_H = 41 DEFAULT_OUT = "Font24CN.c" DEFAULT_STRUCT = "Font24CN" DEFAULT_TABLE = "Font24CN_Table" DEFAULT_ASCII_W = 24 DEFAULT_COMMENT = "微软雅黑"
def render_glyph(ch, font, w, h, x_off=None, y_off=None, threshold=128): """ 渲染字符到 w x h 二值位图。 默认:在大画布上画字,按包围盒裁剪后居中贴到目标画布。 x_off/y_off 不为 None 时用固定偏移(相对目标画布左上角)。 返回 list[list[int]](0/1),行优先,每行 w 个像素。 """ pad = 16 tmp = Image.new("L", (w + pad * 2, h + pad * 2), 0) d = ImageDraw.Draw(tmp) d.text((pad, pad), ch, font=font, fill=255) bbox = tmp.getbbox()
if bbox is None: return [[0] * w for _ in range(h)]
glyph = tmp.crop(bbox) gw, gh = glyph.size
img = Image.new("L", (w, h), 0) if x_off is None and y_off is None: dx = (w - gw) // 2 dy = (h - gh) // 2 else: dx = x_off or 0 dy = y_off or 0 img.paste(glyph, (dx, dy))
pixels = img.load() rows = [] for y in range(h): row = [] for x in range(w): row.append(1 if pixels[x, y] >= threshold else 0) rows.append(row) return rows
def rows_to_bytes(rows, w, h): """ 横向取模:每行按 8 位一组打包,高位在前(MSB first),不足补 0。 """ data = [] for y in range(h): row = rows[y] for byte_start in range(0, w, 8): byte_val = 0 for bit in range(8): col = byte_start + bit if col < w and row[col]: byte_val |= (0x80 >> bit) data.append(byte_val) return data
def format_bytes(data, per_line=16): lines = [] for i in range(0, len(data), per_line): chunk = data[i:i + per_line] lines.append(",".join(f"0x{b:02X}" for b in chunk) + ",") return "\n".join(lines)
def c_escape(s): return s.replace("\\", "\\\\").replace('"', '\\"')
def build_c_content(chars, font, w, h, x_off, y_off, table_name, struct_name, ascii_w, comment, font_size): entries = [] for ch in chars: rows = render_glyph(ch, font, w, h, x_off, y_off) data = rows_to_bytes(rows, w, h) entries.append((ch, data))
lines = [] lines.append("/*") lines.append(" Auto-generated by gen_font.py") lines.append(f" Font: msyh size {font_size}") lines.append(f" Glyph: {w}x{h}, horizontal scan, MSB first, auto-centered") lines.append("*/") lines.append("") lines.append('#include "fonts.h"') lines.append("") lines.append(f"const CH_CN {table_name}[] = ") lines.append("{")
for ch, data in entries: lines.append(f"/*-- 文字: {ch} --*/") lines.append(f"/*-- {comment}{font_size}; 此字体下对应的点阵为:宽x高={w}x{h} --*/") lines.append("{") lines.append(f'"{c_escape(ch)}",') lines.append(format_bytes(data)) lines.append("},") lines.append("")
lines.append("};") lines.append("") lines.append(f"cFONT {struct_name} = {{") lines.append(f" {table_name},") lines.append(f" sizeof({table_name})/sizeof(CH_CN), /*size of table*/") lines.append(f" {ascii_w}, /* ASCII Width */") lines.append(f" {w}, /* Width */") lines.append(f" {h}, /* Height */") lines.append("};") lines.append("") return "\n".join(lines)
def generate_c(text, ttf_path, font_size, w, h, out_path, x_off=None, y_off=None, struct_name=DEFAULT_STRUCT, table_name=DEFAULT_TABLE, ascii_w=DEFAULT_ASCII_W, comment=DEFAULT_COMMENT): font = ImageFont.truetype(ttf_path, font_size)
seen, chars = set(), [] for ch in text: if ch not in seen: seen.add(ch) chars.append(ch)
content = build_c_content( chars, font, w, h, x_off, y_off, table_name, struct_name, ascii_w, comment, font_size)
with open(out_path, "w", encoding="utf-8", newline="\n") as f: f.write(content) enc = "UTF-8"
print(f"已写出 {out_path}({enc} 编码),共 {len(chars)} 个字符") for ch in chars: rows = render_glyph(ch, font, w, h, x_off, y_off) dots = sum(sum(r) for r in rows) if dots == 0: print(f" [警告] 字符 '{ch}' 渲染为空!") else: print(f" '{ch}': {dots} 个非零点")
def main(): parser = argparse.ArgumentParser( description="生成微雪风格 CH_CN 字库 C 文件(默认微软雅黑 24,自动居中)") parser.add_argument("--text", required=True, help='要生成的字符,如 "中华人民共和国"') parser.add_argument("--ttf", default=DEFAULT_TTF, help=f"字体文件路径(默认 {DEFAULT_TTF})") parser.add_argument("--size", type=int, default=DEFAULT_SIZE, help=f"字号(默认 {DEFAULT_SIZE})") parser.add_argument("--w", type=int, default=DEFAULT_W, help=f"点阵宽(默认 {DEFAULT_W})") parser.add_argument("--h", type=int, default=DEFAULT_H, help=f"点阵高(默认 {DEFAULT_H})") parser.add_argument("--out", default=DEFAULT_OUT, help=f"输出 C 文件路径(默认 {DEFAULT_OUT})") parser.add_argument("--x-off", type=int, default=None, help="固定 X 偏移(不设则自动居中)") parser.add_argument("--y-off", type=int, default=None, help="固定 Y 偏移(不设则自动居中)") parser.add_argument("--ascii-w", type=int, default=DEFAULT_ASCII_W, help=f"结构体 ASCII Width 字段(默认 {DEFAULT_ASCII_W})") parser.add_argument("--struct", default=DEFAULT_STRUCT, help=f"cFONT 变量名(默认 {DEFAULT_STRUCT})") parser.add_argument("--table", default=DEFAULT_TABLE, help=f"字模数组名(默认 {DEFAULT_TABLE})") parser.add_argument("--comment", default=DEFAULT_COMMENT, help=f"注释里的字体名(默认 {DEFAULT_COMMENT})") args = parser.parse_args()
if not os.path.exists(args.ttf): print(f"错误:找不到字体文件 {args.ttf}") sys.exit(1)
generate_c( text=args.text, ttf_path=args.ttf, font_size=args.size, w=args.w, h=args.h, out_path=args.out, x_off=args.x_off, y_off=args.y_off, struct_name=args.struct, table_name=args.table, ascii_w=args.ascii_w, comment=args.comment, )
if __name__ == "__main__": main()
|