kopia lustrzana https://github.com/peterhinch/micropython-font-to-py
165 wiersze
5.4 KiB
Python
165 wiersze
5.4 KiB
Python
![]() |
#! /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)
|