Unity开发微信小游戏-生僻字的处理

2026/03 09 19:03

Unity使用GB2312字体,在App中生僻字会Fallback到系统字体,但是在微信小游戏中直接无法显示

处理方案为,使用fontforge自动化导入其它替代字体中的字形到已有字体中进行补充

1、安装 fontforge

fontforge支持python脚本自动化运行;

2、添加替换脚本replace.py

import fontforge
import os
import tempfile

def get_glyph_by_unicode(font, unicode_code):
    try:
        if unicode_code in font:
            return font[unicode_code]
        glyph_name = f"uni{hex(unicode_code)[2:].upper()}"  
        if glyph_name in font:
            return font[glyph_name]
        glyph_name_u = f"u{hex(unicode_code)[2:]}"
        if glyph_name_u in font:
            return font[glyph_name_u]
        return None
    except:
        return None

def replace_single_glyph(source_font, target_font, unicode_code, glyph_alias=None):
    glyph_alias = glyph_alias or f"U+{hex(unicode_code)[2:].upper()}"
    source_glyph = get_glyph_by_unicode(source_font, unicode_code)
    if not source_glyph:
        print(f"警告:源字体中未找到字形 {glyph_alias}(Unicode:{unicode_code}),跳过")
        return False
    target_glyph = get_glyph_by_unicode(target_font, unicode_code)
    if target_glyph:
        print(f"提示:{glyph_alias} 存在..")
        return True
    target_font.createChar(int(unicode_code), f"uni{hex(unicode_code)[2:].upper()}")
    target_glyph = get_glyph_by_unicode(target_font, unicode_code)
    if not target_glyph:
        print(f"错误:无法创建字形 {glyph_alias},跳过")
        return False
    try:
        source_font.selection.select(int(unicode_code))
        source_font.copy()
        target_font.selection.select(int(unicode_code))
        target_font.paste()
        target_glyph.width = int(round(source_glyph.width))
        if hasattr(source_glyph, 'lsb'):
            target_glyph.lsb = int(round(source_glyph.lsb))
            target_glyph.rsb = int(round(source_glyph.rsb))
        elif hasattr(source_glyph, 'left_side_bearing'):
            target_glyph.left_side_bearing = int(round(source_glyph.left_side_bearing))
            target_glyph.right_side_bearing = int(round(source_glyph.right_side_bearing))
        
        print(f"成功替换字形 {glyph_alias}(Unicode:{unicode_code})")
        return True
    except Exception as e:
        print(f"错误:替换字形 {glyph_alias} 失败 - {str(e)}")
        return False

def replace_glyphs(source_font_path, target_font_path, output_font_path, glyph_unicode_map):
    temp_font = tempfile.mktemp(suffix=".sfd")
    try:
        source_font = fontforge.open(source_font_path)
        target_font = fontforge.open(target_font_path)
        source_em = int(round(source_font.em))
        target_font.em = source_em
        for glyph_alias, unicode_code in glyph_unicode_map.items():
            replace_single_glyph(source_font, target_font, unicode_code, glyph_alias)
        target_font.generate(output_font_path)
        print(f"\n字体替换完成!新字体路径:{output_font_path}")
    except Exception as e:
        print(f"\n执行出错:{str(e)}")
        import traceback
        traceback.print_exc()
    finally:
        if 'source_font' in locals():
            source_font.close()
        if 'target_font' in locals():
            target_font.close()
        if os.path.exists(temp_font):
            try:
                os.remove(temp_font)
            except:
                pass

if __name__ == "__main__":
    SOURCE_FONT = "gbk.ttf"
    TARGET_FONT = "gb2312.ttf"
    OUTPUT_FONT = "out.TTF"
    GLYPH_UNICODE_MAP = {
"睺": 30586,
"啰": 21872,
"妺": 22970,
"嶽": 23997,
"猨": 29480,
"臜": 33244,
"雘": 38616,
    }

    replace_glyphs(SOURCE_FONT, TARGET_FONT, OUTPUT_FONT, GLYPH_UNICODE_MAP)

3、书写一个工具全局扫描所有的生僻字

internal class Program
{
	static void Main(string[] args)
	{
		try
		{
			string filePath = "language.txt";
			if (!File.Exists(filePath))
			{
				Console.WriteLine($"文件 {filePath} 不存在!");
				return;
			}
			string[] lines = File.ReadAllLines(filePath);
			HashSet<char> allChars = new HashSet<char>();
			ExtractChineseCharacters(lines, allChars);
			SaveResults(allChars);
		}
		catch (Exception ex)
		{
			Console.WriteLine($"发生错误: {ex.Message}");
		}
	}
	static void ExtractChineseCharacters(string[] lines, HashSet<char> chars)
	{
		foreach (string line in lines)
		{
			foreach (char c in line)
			{
				if (c < 10000) // 小于10000的字符通常不是汉字,直接跳过
				{
					continue;
				}
				if (chars.Contains(c))
				{
					continue;
				}
				if (IsGB2312(c))
				{
					continue;
				}
				chars.Add(c);
			}
		}
	}

	private static Encoding gb2312 = Encoding.GetEncoding("gb2312");
	private static char[] charBuff = new char[1];
	public static bool IsGB2312(char c)
	{
		try
		{
			charBuff[0] = c;
			byte[] bytes = gb2312.GetBytes(charBuff);
			if (bytes.Length == 2)
			{
				if (bytes[0] >= 0xA1 && bytes[0] <= 0xFE && bytes[1] >= 0xA1 && bytes[1] <= 0xFE)
				{
					return true;
				}
			}
			return false;
		}
		catch
		{
			return false;
		}
	}
	static void SaveResults(HashSet<char> allChars)
	{
		using (StreamWriter writer = new StreamWriter("GBK.txt"))
		{
			foreach (char c in allChars)
			{
				writer.WriteLine($"\"{c}\": {(int)c},");
			}
		}
	}
}

将输出的生僻字贴到python脚本中,运行

fontforge.exe -script replace.py

即可获得补全的字体文件out.ttf