前段时间想做一款像素风的游戏,中文像素字体就是一个绕不过去的问题。这里记录下我制作12像素字体的过程。
使用的软件是 fontforge ,https://fontforge.org/en-US/
自己一个字一个字的做是不可能自己做的,所以再网上找到了两个开源的字体库
1.M+ BITMAP FONTS https://mplus-fonts.osdn.jp/mplus-bitmap-fonts/download/index.html
这是来自日本的完全免费字体,看着还蛮好看,有一部分的中文部分。
2. HZK-12 https://github.com/aguegu/BitmapFont
覆盖GB2312的中文12像素字体
制作思路
灵感来自 ipix中文像素字体的制作 我在原作的基础上进行了一些修改。原作是将字模导出为png 然后Potrace 转化成 svg 图片, 最后再把SVG图片导入FontForge中就制作。
我是直接调用了命令行的 fontforge, 使用python脚本读取字模信息,然后调用 fontforge 的矩形绘制函数,把每个像素点绘制成一个 100x100 的正方形。省去了一些中间步骤,所以效率提升了很多,制作过程只用了几分钟就完成了。当然摸索这个方法的时间也用了三个晚上,好在以后重新制作的时间缩短到了几分钟。这里记录下详细的制作步骤,以及分享下源码。
1. 安装fontforge 并添加支持库 bdflib bdf支持库是为了解析 M+ BITMAP FONTS ,hzk12不需要额外插件。
fontforge 安装步骤省略,fontforge 会默认安装上python运行环境到软件目录下面 。
下载 bdflib 的 whl 文件解压到 FontForgeBuilds\lib\python3.8\site-packages 目录下就安装好了。
2. 利用Hzk12 生成fontforge工程。
新建一个文件夹使用 vscode 打开, 新建 python 脚本命名为hzk_to_sfd.py,安装python插件,设置 python 目录为fontforge 下的python环境 如图所示。
源码部分:
import fontforge import binascii KEYS = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01] sdf_path = "HZK12.sfd" hzk_path = "C:/Users/lo/Downloads/HZK/HZK12" gb2312_path = "C:/Users/lo/Downloads/HZK/gb2312.txt" def Draw(font, ch, rect_list): gb2312 = ch.encode('gb2312') hex_str = binascii.b2a_hex(gb2312) result = str(hex_str, encoding='utf-8') area = eval('0x' + result[:2]) - 0x80 index = eval('0x' + result[2:]) - 0x80 ioj = (area << 8) + index # print(ch, gb2312, hex_str, result, ioj) glyph = font.createMappedChar(ioj) pen = glyph.glyphPen() y = 11 max_x = 0 for row in rect_list: y = y - 1 x = -1 for i in row: x = x + 1 if i: pen.moveTo((100 * x , 100 * y )) pen.lineTo((100 * x , 100 * y + 100)) pen.lineTo((100 * x + 100 , 100 * y + 100)) pen.lineTo((100 * x + 100 , 100 * y )) pen.closePath() max_x = max(max_x,x) # print("#") # else: # print(" ") # print("\n") pen = None glyph.removeOverlap() glyph.width = max_x*100 + 200 def draw_glyph(ch): rect_list = [] * 12 for i in range(12): rect_list.append([] * 16) # 获取中文的gb2312编码,一个汉字是由2个字节编码组成 gb2312 = ch.encode('gb2312') # 将二进制编码数据转化为十六进制数据 hex_str = binascii.b2a_hex(gb2312) # 将数据按unicode转化为字符串 result = str(hex_str, encoding='utf-8') # 前两位对应汉字的第一个字节:区码,每一区记录94个字符 area = eval('0x' + result[:2]) - 0xA0 # 后两位对应汉字的第二个字节:位码,是汉字在其区的位置 index = eval('0x' + result[2:]) - 0xA0 # 汉字在HZK16中的绝对偏移位置,最后乘24是因为字库中的每个汉字字模都需要24字节 offset = (94 * (area-1) + (index-1)) * 24 font_rect = None # 读取HZK16汉字库文件 with open(hzk_path, "rb") as f: # 找到目标汉字的偏移位置 f.seek(offset) # 从该字模数据中读取24字节数据 font_rect = f.read(24) # font_rect的长度是24,此处相当于for k in range(16) for k in range(len(font_rect) // 2): # 每行数据 row_list = rect_list[k] for j in range(2): for i in range(8): asc = font_rect[k * 2 + j] # 此处&为Python中的按位与运算符 flag = asc & KEYS[i] # 数据规则获取字模中数据添加到16行每行中16个位置处每个位置 row_list.append(flag) return rect_list def OpenGBK(): f = open(gb2312_path, 'r', encoding='UTF-8') line = f.readline() for index, ch in enumerate(line): if ch == "\n": continue print(index, end=' ') print(ch) f.close() def Start(): font = fontforge.font() # Open a font font.encoding = "gb2312" font.ascent = 1200 f = open(gb2312_path, 'r', encoding='UTF-8') line = f.readline() while line: for index, ch in enumerate(line): if ch == "\n": continue print(ch) rect = draw_glyph(ch) Draw(font, ch, rect) line = f.readline() font.save(sdf_path) f.close() Start()
这里依赖两个文件 HZK12 和 gb2312.txt ,可以在 源码中下载,在Vscode中运行脚本,或者在终端输入 ,
"c:/Program Files (x86)/FontForgeBuilds/bin/ffpython.exe" c:/Users/lo/Downloads/HZK/hzk_to_sfd.py #当然这里的ffpython.exe文件和python文件的路径需要修改。
就能生成一个hzk12.sfd 的文件,双击使用fontforge打开。 看看效果。
还行 文字没有错位(hzk12没有前面的英文字模,所以看效果需要向下多翻一点)下面就来添加英文字模进来
3. 利用 M+ BITMAP FONTS 生成fontforge工程。
我这里选择的是 mplus_jf12r.bdf 文件作为字模,也可以使用其他的。
与第二步相同新建一个名为 bdf_to_sdf.py 的文件,这里依赖了第一步中安装的 bdflib 插件。
from bdflib import reader from bdflib import writer import fontforge def OpenBDF(path): with open(path, "rb") as handle: return reader.read_bdf(handle) def Start(): bdf_font = OpenBDF("C:/Users/lo/Downloads/HZK/mplus_jf12r.bdf") sdf_font = fontforge.font() sdf_font.encoding = "jis208" sdf_font.ascent = 1200 for bdf_glyph in bdf_font.glyphs: # bdf_glyph = bdf_font[12321] x = -1 y = 10 print(bdf_glyph.codepoint) glyph = sdf_font.createMappedChar(bdf_glyph.codepoint) pen = glyph.glyphPen() for ch in bdf_glyph.__str__(): if ch == '\n': y = y - 1 x = -1 print("") if ch == '#' : pen.moveTo((100 * x , 100 * y )) pen.lineTo((100 * x , 100 * y + 100)) pen.lineTo((100 * x + 100 , 100 * y + 100)) pen.lineTo((100 * x + 100 , 100 * y )) pen.closePath() print("#",end=" ") else: print(" ",end=" ") x = x + 1 pen = None glyph.removeOverlap() # break # sdf_font.autoWidth() sdf_font.save("mplus_jf12r.sfd") Start()
这里使用的文件同 mplus_jf12r.bdf 样放在了源码中。和第二步一样的运行方式,运行后得到一个 mplus_jf12r.sfd 的 fontforge 工程。
也没有错位,太棒了。 第4步就是把这两个字体合并一下了。
4. 合并字体
hzk12 和 mplus bitmap中有很多重合的字,所以有两种合并方式,一个是重合的部分使用hzk12的字模,一个是使用mplus的。我选择保留mplus 的重合部分。
由于两者编码格式不同所以先统一下编码格式,一个是gb2312 一个是 jis208,所以我这全都设置成 Unicode Full 的格式。(fontforge 下的菜单 Encoding -> reencode -> ISO 10646-1 ( Unicode, Full ))。 然后在打开Mplus-jf12r的fontforge的窗口中 选择菜单Element -> Merge Fonts 然后选择 hzk12.sfd 文件,然后会弹出一个对话框询问 是否用导入的 graph 替换本工程的同位置graph,这里选择 NO,就是使用mplus 的重合部分。
选择英文和符号部分 调整下Graph 的宽度 ,推荐 菜单 Metrics -> Auto Width。
推荐按下 Ctrl + S 保存。
下一步生成ttf字体文件。生成之前可以先编辑下字体信息, 菜单 Element -> font info ,这里不介绍了。
5. 生成ttf 文件
菜单 File -> Generate Fonts
设置下TrueType 就可以点击 Generate 了。这里会有对话框提示 :有重合的点或者面, 我还没有找到比较好的办法消除,但是并不影响使用,所以就没有再多处理。依然点击Generate 生成。
好了现在打开字体看下效果。
还行吧 就是符号的部分不是很好看,也可以重新找点好看的字模用上面的方法替换下,我觉得也能用就没换了,有需要可以自己动手了,接着看下在Unity 中的效果怎么样吧。
效果不错 没有错位。
结束。
源码以及TTF文件: https://github.com/Luckeee/mplus_hzk_12
参考:
你好,目前这个字库中符号还缺少如下两个:
破折号——
有个点·
如果这两个可以修复就比较完美了。
感谢付出。
@Justus:已添加 😬
造福社会 帮大忙了
牛的,牛的,牛的,大神就是牛的,你字体很好,但下一秒我的copy忍术发动,就变成..哈哈