micropython-font-to-py/c_to_python_font.py

165 wiersze
5.4 KiB
Python
Executable File

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2022 Peter Hinch
import sys
from font_to_py import ByteWriter, var_write, write_func, STR02, STR02H
STR01 = """# Code generated by c_to_python_font.py.
# Char set: {}
# Cmd: {}
version = '0.10'
"""
class Glyph:
dstart = 0 # Start index of next glyph
def __init__(self):
self.width = 0
self.height = 0
def populate(self, fn, data, idx):
try:
with open(fn, "r") as f:
s = f.readline()
elements = s.split(" ")
if elements[1].endswith("width"):
self.width = int(elements[2])
data.extend((self.width).to_bytes(2, 'little'))
else:
return False
s = f.readline()
elements = s.split(" ")
if elements[1].endswith("height"):
self.height = int(elements[2])
else:
return False
s = f.readline()
if not s.startswith("static"):
return False
while s := f.readline():
if (lb := s.find("}")) != -1:
s = s[:lb] # Strip trailing };
p = s.strip().split(',')
if not p[-1]:
p = p[: -1]
z = [int(x, 16) for x in p]
data.extend(bytearray(z))
# index points to start of current data block
idx.extend((Glyph.dstart).to_bytes(2, 'little'))
Glyph.dstart += ((self.width - 1)//8 + 1) * self.height + 2
except OSError:
return False
return True
class Font:
def __init__(self):
self.glyphs = []
self.height = 0
self.max_width = 0
self.data = bytearray()
self.index = bytearray()
def __getitem__(self, glyph_index):
return self.glyphs[glyph_index]
def populate(self, filename="filenames.txt"):
try:
with open(filename, "r") as f:
while fn := f.readline().strip(): # Get current C file
if (lc := fn.find("#")) != -1:
if (fn := fn[:lc].strip()) == "":
continue
g = Glyph()
if g.populate(fn, self.data, self.index):
if ht := self.height:
if ht != g.height:
print(f"Fatal: file {fn} height is {g.height} while font height is {ht}")
return False
else:
self.height = g.height
self.glyphs.append(g)
self.max_width = max(self.max_width, g.width)
else:
print('Failed to read', fn)
return False
except OSError:
print("Failed to read", filename)
return False
return True
def output(self, stream):
minchar = ord("A")
nglyphs = len(self.glyphs) - 1 # Number less default glyph
maxchar = minchar + nglyphs - 1
st = ""
for z in range(nglyphs):
st = "".join((st, chr(minchar + z)))
cl = ' '.join(sys.argv)
stream.write(STR01.format(st, cl))
write_func(stream, 'height', self.height)
write_func(stream, 'baseline', self.height)
write_func(stream, 'max_width', self.max_width)
write_func(stream, 'hmap', True)
write_func(stream, 'reverse', True) # ????
write_func(stream, 'monospaced', False)
write_func(stream, 'min_ch', minchar)
write_func(stream, 'max_ch', maxchar)
bw_font = ByteWriter(stream, '_font')
bw_font.odata(self.data)
bw_font.eot()
bw_index = ByteWriter(stream, '_index')
bw_index.odata(self.index)
bw_index.eot()
stream.write(STR02.format(minchar, maxchar))
stream.write(STR02H.format(self.height))
def make_font(infile="filenames.txt", outfile="icofont.py"):
try:
with open(outfile, "w") as f:
font = Font()
if font.populate(infile):
font.output(f)
else:
return # Failed
except OSError:
print(f"Failed to open {outfile} for writing.")
print(f"{outfile} successfully written.")
def version_check():
n = sys.implementation.name
v = sys.implementation.version
if n == "cpython":
if v[0] == 3:
return v[1] >= 8
return v[0] > 3
return False # Requires CPython
usage = """Convert a set of C bitmaps to a Python font file. Usage:
c_to_python_font.py [infile [outfile]]
infile contains a list of C bitmap files, one per line.
outfile is the name of the output Python font.
Default args:
infile: filenames.txt
outfile: icofont.py
e.g.
$ ./c_to_python_font.py my_file_list.txt my_font.py
"""
if __name__ == "__main__":
a = sys.argv
if len(a) >= 2 and a[1] in ("--help", "-h", "help"):
print(usage)
elif not version_check():
print("This script requires Python 3.8 or above.")
else:
infile = a[1] if len(a) >= 2 else "filenames.txt"
outfile = a[2] if len(a) >= 3 else "icofont.py"
make_font(infile, outfile)