kopia lustrzana https://github.com/peterhinch/micropython-font-to-py
Alternative .py writer
rodzic
ee9dbb2a1b
commit
db07a856bc
|
@ -25,51 +25,39 @@
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
# THE SOFTWARE.
|
# THE SOFTWARE.
|
||||||
|
|
||||||
import importlib.metadata
|
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import freetype
|
import freetype
|
||||||
|
|
||||||
from .byte_writer import ByteWriter
|
from .byte_writer import AltByteWriter
|
||||||
from .font import Font
|
from .font import Font
|
||||||
|
|
||||||
VERSION = importlib.metadata.version("micropython-font-to-py")
|
HEADER_TPL = """# Code generated by font_to_py.
|
||||||
|
|
||||||
|
|
||||||
# Define a global
|
|
||||||
def var_write(stream, name, value):
|
|
||||||
stream.write(f"{name} = {value}\n")
|
|
||||||
|
|
||||||
|
|
||||||
STR01 = """# Code generated by font_to_py.
|
|
||||||
# Font: {font:s}{charset:s}
|
# Font: {font:s}{charset:s}
|
||||||
# Cmd: {cmd:s}
|
# Cmd: {cmd:s}
|
||||||
version = '{version:s}'
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Code emitted for charsets spanning a small range of ordinal values
|
# Code emitted for charsets spanning a small range of ordinal values
|
||||||
STR02 = """_mvfont = memoryview(_font)
|
GET_CH_I_TPL = """def get_ch(self, ch):
|
||||||
_mvi = memoryview(_index)
|
def ifb(l):
|
||||||
ifb = lambda l : l[0] | (l[1] << 8)
|
return l[0] | (l[1] << 8)
|
||||||
|
|
||||||
def get_ch(ch):
|
|
||||||
oc = ord(ch)
|
oc = ord(ch)
|
||||||
ioff = 2 * (oc - {min:d} + 1) if oc >= {min:d} and oc <= {max:d} else 0
|
ioff = 2 * (oc - 32 + 1) if oc >= self.min_ch and oc <= self.max_ch else 0
|
||||||
doff = ifb(_mvi[ioff : ])
|
doff = ifb(self._index[ioff:])
|
||||||
width = ifb(_mvfont[doff : ])
|
width = ifb(self._font[doff:])"""
|
||||||
"""
|
|
||||||
|
|
||||||
# Code emiited for large charsets, assumed by build_arrays() to be sparse.
|
# Code emiited for large charsets, assumed by build_arrays() to be sparse.
|
||||||
# Binary search of sorted sparse index.
|
# Binary search of sorted sparse index.
|
||||||
# Offset into data array is saved after dividing by 8
|
# Offset into data array is saved after dividing by 8
|
||||||
STRSP = """_mvfont = memoryview(_font)
|
GET_CH_S_TPL = """def get_ch(self, ch):
|
||||||
_mvsp = memoryview(_sparse)
|
def ifb(l):
|
||||||
ifb = lambda l : l[0] | (l[1] << 8)
|
return l[0] | (l[1] << 8)
|
||||||
|
|
||||||
def bs(lst, val):
|
def bs(lst, val):
|
||||||
while True:
|
while True:
|
||||||
m = (len(lst) & ~ 7) >> 1
|
m = (len(lst) & ~ 7) >> 1
|
||||||
v = ifb(lst[m:])
|
v = ifb(lst[m:])
|
||||||
|
@ -79,39 +67,103 @@ def bs(lst, val):
|
||||||
return 0
|
return 0
|
||||||
lst = lst[m:] if v < val else lst[:m]
|
lst = lst[m:] if v < val else lst[:m]
|
||||||
|
|
||||||
def get_ch(ch):
|
doff = bs(self._sparse, ord(ch)) << 3
|
||||||
doff = bs(_mvsp, ord(ch)) << 3
|
width = ifb(self._font[doff : ])"""
|
||||||
width = ifb(_mvfont[doff : ])
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Code emitted for horizontally mapped fonts.
|
# Code emitted for horizontally mapped fonts.
|
||||||
STR02H = """
|
GET_CH_HMAP_TPL = """
|
||||||
next_offs = doff + 2 + ((width - 1)//8 + 1) * {height:d}
|
next_offs = doff + 2 + ((width - 1)//8 + 1) * self.height
|
||||||
return _mvfont[doff + 2:next_offs], {height:d}, width
|
return _mvfont[doff + 2:next_offs], self.height, width
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Code emitted for vertically mapped fonts.
|
# Code emitted for vertically mapped fonts.
|
||||||
STR02V = """
|
GET_CH_VMAP_TPL = """
|
||||||
next_offs = doff + 2 + (({height:d} - 1)//8 + 1) * width
|
next_offs = doff + 2 + ((self.height - 1)//8 + 1) * width
|
||||||
return _mvfont[doff + 2:next_offs], {height:d}, width
|
return _mvfont[doff + 2:next_offs], self.height, width
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Extra code emitted where -i is specified.
|
# Extra code emitted where -i is specified.
|
||||||
STR03 = '''
|
GLYPH_TPL = '''
|
||||||
def glyphs():
|
def glyphs(self):
|
||||||
for c in """{}""":
|
for c in """{keys:s}""":
|
||||||
yield c, get_ch(c)
|
yield c, self.get_ch(c)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
OBJECT_BEGIN_TPL = """font = type(
|
||||||
|
"",
|
||||||
|
(object,),
|
||||||
|
{
|
||||||
|
"""
|
||||||
|
|
||||||
def write_func(stream, name, arg):
|
OBJECT_END_TPL = """ },
|
||||||
stream.write(f"def {name}():\n return {arg}\n\n")
|
)()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def write_font( # noqa: PLR0913
|
def write_attribute(stream, name, value, indent=8):
|
||||||
|
stream.write(" " * indent + f'"{name}": {value},\n')
|
||||||
|
|
||||||
|
|
||||||
|
def write_data( # noqa: PLR0913
|
||||||
|
stream, fnt, font_path, hmap, reverse, iterate, charset
|
||||||
|
):
|
||||||
|
height = fnt.height # Actual height, not target height
|
||||||
|
minchar = min(fnt.crange)
|
||||||
|
maxchar = max(fnt.crange)
|
||||||
|
st = "" if charset == "" else f" Char set: {charset}"
|
||||||
|
cl = " ".join([str(Path(sys.argv[0]).stem), *sys.argv[1:]])
|
||||||
|
stream.write(HEADER_TPL.format(font=Path(font_path).stem, charset=st, cmd=cl))
|
||||||
|
|
||||||
|
data, index, sparse = fnt.build_arrays(hmap, reverse)
|
||||||
|
|
||||||
|
if sparse: # build_arrays() has returned a sparse index
|
||||||
|
click.echo("Sparse")
|
||||||
|
stream.write(GET_CH_S_TPL)
|
||||||
|
else:
|
||||||
|
click.echo("Normal")
|
||||||
|
stream.write(GET_CH_I_TPL)
|
||||||
|
|
||||||
|
if hmap:
|
||||||
|
stream.write(GET_CH_HMAP_TPL)
|
||||||
|
else:
|
||||||
|
stream.write(GET_CH_VMAP_TPL)
|
||||||
|
|
||||||
|
if iterate:
|
||||||
|
stream.write(GLYPH_TPL.format(keys="".join(sorted(fnt.keys()))))
|
||||||
|
|
||||||
|
stream.write(OBJECT_BEGIN_TPL)
|
||||||
|
write_attribute(stream, "height", height)
|
||||||
|
write_attribute(stream, "baseline", fnt._max_ascent)
|
||||||
|
write_attribute(stream, "max_width", fnt.max_width)
|
||||||
|
write_attribute(stream, "hmap", hmap)
|
||||||
|
write_attribute(stream, "reverse", reverse)
|
||||||
|
write_attribute(stream, "monospaced", fnt.monospaced)
|
||||||
|
write_attribute(stream, "min_ch", minchar)
|
||||||
|
write_attribute(stream, "max_ch", maxchar)
|
||||||
|
|
||||||
|
bw_font = AltByteWriter(stream, "_font")
|
||||||
|
bw_font.odata(data)
|
||||||
|
bw_font.eot()
|
||||||
|
|
||||||
|
if sparse: # build_arrays() has returned a sparse index
|
||||||
|
bw_sparse = AltByteWriter(stream, "_sparse")
|
||||||
|
bw_sparse.odata(sparse)
|
||||||
|
bw_sparse.eot()
|
||||||
|
else:
|
||||||
|
bw_index = AltByteWriter(stream, "_index")
|
||||||
|
bw_index.odata(index)
|
||||||
|
bw_index.eot()
|
||||||
|
|
||||||
|
write_attribute(stream, "get_ch", "get_ch")
|
||||||
|
if iterate:
|
||||||
|
write_attribute(stream, "glyphs", "glyphs")
|
||||||
|
stream.write(OBJECT_END_TPL)
|
||||||
|
|
||||||
|
|
||||||
|
def write_alt_font( # noqa: PLR0913
|
||||||
op_path,
|
op_path,
|
||||||
font_path,
|
font_path,
|
||||||
height,
|
height,
|
||||||
|
@ -139,73 +191,3 @@ def write_font( # noqa: PLR0913
|
||||||
click.echo(f"Can't open {op_path} for writing")
|
click.echo(f"Can't open {op_path} for writing")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def write_data( # noqa: PLR0913
|
|
||||||
stream, fnt, font_path, hmap, reverse, iterate, charset
|
|
||||||
):
|
|
||||||
height = fnt.height # Actual height, not target height
|
|
||||||
minchar = min(fnt.crange)
|
|
||||||
maxchar = max(fnt.crange)
|
|
||||||
st = "" if charset == "" else f" Char set: {charset}"
|
|
||||||
cl = " ".join([str(Path(sys.argv[0]).stem), *sys.argv[1:]])
|
|
||||||
stream.write(
|
|
||||||
STR01.format(font=Path(font_path).stem, charset=st, cmd=cl, version=VERSION)
|
|
||||||
)
|
|
||||||
write_func(stream, "height", height)
|
|
||||||
write_func(stream, "baseline", fnt._max_ascent)
|
|
||||||
write_func(stream, "max_width", fnt.max_width)
|
|
||||||
write_func(stream, "hmap", hmap)
|
|
||||||
write_func(stream, "reverse", reverse)
|
|
||||||
write_func(stream, "monospaced", fnt.monospaced)
|
|
||||||
write_func(stream, "min_ch", minchar)
|
|
||||||
write_func(stream, "max_ch", maxchar)
|
|
||||||
if iterate:
|
|
||||||
stream.write(STR03.format("".join(sorted(fnt.keys()))))
|
|
||||||
data, index, sparse = fnt.build_arrays(hmap, reverse)
|
|
||||||
bw_font = ByteWriter(stream, "_font")
|
|
||||||
bw_font.odata(data)
|
|
||||||
bw_font.eot()
|
|
||||||
if sparse: # build_arrays() has returned a sparse index
|
|
||||||
bw_sparse = ByteWriter(stream, "_sparse")
|
|
||||||
bw_sparse.odata(sparse)
|
|
||||||
bw_sparse.eot()
|
|
||||||
stream.write(STRSP)
|
|
||||||
click.echo("Sparse")
|
|
||||||
else:
|
|
||||||
bw_index = ByteWriter(stream, "_index")
|
|
||||||
bw_index.odata(index)
|
|
||||||
bw_index.eot()
|
|
||||||
stream.write(STR02.format(min=minchar, max=maxchar))
|
|
||||||
click.echo("Normal")
|
|
||||||
if hmap:
|
|
||||||
stream.write(STR02H.format(height=height))
|
|
||||||
else:
|
|
||||||
stream.write(STR02V.format(height=height))
|
|
||||||
|
|
||||||
|
|
||||||
# BINARY OUTPUT
|
|
||||||
# hmap reverse magic bytes
|
|
||||||
# 0 0 0x3f 0xe7
|
|
||||||
# 1 0 0x40 0xe7
|
|
||||||
# 0 1 0x41 0xe7
|
|
||||||
# 1 1 0x42 0xe7
|
|
||||||
def write_binary_font(op_path, font_path, height, hmap, reverse):
|
|
||||||
try:
|
|
||||||
fnt = Font(
|
|
||||||
font_path, height, 32, 126, True, None, ""
|
|
||||||
) # All chars have same width
|
|
||||||
except freetype.ft_errors.FT_Exception:
|
|
||||||
click.echo(f"Can't open {font_path}")
|
|
||||||
return False
|
|
||||||
sig = 1 if hmap else 0
|
|
||||||
if reverse:
|
|
||||||
sig += 2
|
|
||||||
try:
|
|
||||||
with open(op_path, "wb") as stream:
|
|
||||||
data = fnt.build_binary_array(hmap, reverse, sig)
|
|
||||||
stream.write(data)
|
|
||||||
except OSError:
|
|
||||||
click.echo(f"Can't open {op_path} for writing")
|
|
||||||
return False
|
|
||||||
return True
|
|
|
@ -63,3 +63,25 @@ class ByteWriter:
|
||||||
if self.bytecount:
|
if self.bytecount:
|
||||||
self._eot()
|
self._eot()
|
||||||
self.stream.write("\n")
|
self.stream.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
class AltByteWriter(ByteWriter):
|
||||||
|
def __init__(self, stream, varname, indent=8):
|
||||||
|
self.stream = stream
|
||||||
|
self.indent = indent
|
||||||
|
self.stream.write(" " * self.indent + f'"{varname}": memoryview(\n')
|
||||||
|
self.bytecount = 0 # For line breaks
|
||||||
|
|
||||||
|
def _eol(self):
|
||||||
|
self.stream.write('"\n')
|
||||||
|
|
||||||
|
def _eot(self):
|
||||||
|
self.stream.write('"\n')
|
||||||
|
self.stream.write(" " * self.indent + "),\n")
|
||||||
|
|
||||||
|
def _bol(self):
|
||||||
|
self.stream.write(" " * (self.indent + 4) + 'b"')
|
||||||
|
|
||||||
|
def eot(self): # User force EOL if one hasn't occurred
|
||||||
|
if self.bytecount:
|
||||||
|
self._eot()
|
||||||
|
|
|
@ -34,6 +34,7 @@ from pathlib import Path
|
||||||
import click
|
import click
|
||||||
import freetype
|
import freetype
|
||||||
|
|
||||||
|
from .alt_writer import write_alt_font
|
||||||
from .bin_writer import write_binary_font
|
from .bin_writer import write_binary_font
|
||||||
from .py_writer import write_font
|
from .py_writer import write_font
|
||||||
|
|
||||||
|
@ -72,6 +73,12 @@ CONTEXT_SETTINGS = dict(max_content_width=100)
|
||||||
is_flag=True,
|
is_flag=True,
|
||||||
help="Produce binary (random access) font file.",
|
help="Produce binary (random access) font file.",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"-a",
|
||||||
|
"--alt",
|
||||||
|
is_flag=True,
|
||||||
|
help="Use alt python writer (EXPERIMENTAL).",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-i",
|
"-i",
|
||||||
"--iterate",
|
"--iterate",
|
||||||
|
@ -124,6 +131,7 @@ def main( # noqa: C901, PLR0913, PLR0912
|
||||||
reverse,
|
reverse,
|
||||||
fixed,
|
fixed,
|
||||||
binary,
|
binary,
|
||||||
|
alt,
|
||||||
iterate,
|
iterate,
|
||||||
smallest,
|
smallest,
|
||||||
largest,
|
largest,
|
||||||
|
@ -205,7 +213,8 @@ def main( # noqa: C901, PLR0913, PLR0912
|
||||||
click.echo(f"Found font with size {height!s}")
|
click.echo(f"Found font with size {height!s}")
|
||||||
|
|
||||||
click.echo("Writing Python font file.")
|
click.echo("Writing Python font file.")
|
||||||
if not write_font(
|
if alt:
|
||||||
|
res = write_alt_font(
|
||||||
outfile,
|
outfile,
|
||||||
infile,
|
infile,
|
||||||
height,
|
height,
|
||||||
|
@ -218,7 +227,24 @@ def main( # noqa: C901, PLR0913, PLR0912
|
||||||
cset,
|
cset,
|
||||||
iterate,
|
iterate,
|
||||||
bitmapped,
|
bitmapped,
|
||||||
):
|
)
|
||||||
|
else:
|
||||||
|
res = write_font(
|
||||||
|
outfile,
|
||||||
|
infile,
|
||||||
|
height,
|
||||||
|
fixed,
|
||||||
|
xmap,
|
||||||
|
reverse,
|
||||||
|
smallest,
|
||||||
|
largest,
|
||||||
|
errchar,
|
||||||
|
cset,
|
||||||
|
iterate,
|
||||||
|
bitmapped,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not res:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
click.echo(f"{outfile} written successfully.")
|
click.echo(f"{outfile} written successfully.")
|
||||||
|
|
|
@ -37,12 +37,6 @@ from .font import Font
|
||||||
|
|
||||||
VERSION = importlib.metadata.version("micropython-font-to-py")
|
VERSION = importlib.metadata.version("micropython-font-to-py")
|
||||||
|
|
||||||
|
|
||||||
# Define a global
|
|
||||||
def var_write(stream, name, value):
|
|
||||||
stream.write(f"{name} = {value}\n")
|
|
||||||
|
|
||||||
|
|
||||||
STR01 = """# Code generated by font_to_py.
|
STR01 = """# Code generated by font_to_py.
|
||||||
# Font: {font:s}{charset:s}
|
# Font: {font:s}{charset:s}
|
||||||
# Cmd: {cmd:s}
|
# Cmd: {cmd:s}
|
||||||
|
@ -107,6 +101,11 @@ def glyphs():
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
# Define a global
|
||||||
|
def var_write(stream, name, value):
|
||||||
|
stream.write(f"{name} = {value}\n")
|
||||||
|
|
||||||
|
|
||||||
def write_func(stream, name, arg):
|
def write_func(stream, name, arg):
|
||||||
stream.write(f"def {name}():\n return {arg}\n\n")
|
stream.write(f"def {name}():\n return {arg}\n\n")
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue