-
Notifications
You must be signed in to change notification settings - Fork 1
/
ezFBfont.py
179 lines (164 loc) · 7.05 KB
/
ezFBfont.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# ezFBfont.py() : a simple string writer for small mono displays and user selected fonts.
# See MARQUEE.md for documentation
# Extensively re-worked from the 'writer' class by
# Peter Hinch:
# https://github.com/peterhinch/micropython-font-to-py
# - Released under the MIT License (MIT). See LICENSE.
# - Copyright (c) 2019-2021 Peter Hinch
import framebuf
# Basic string writing class
class ezFBfont():
def __init__(self, device,
font,
fg = 1,
bg = 0,
tkey = -1,
halign = 'left',
valign = 'top',
vgap = 0,
hgap = 0,
split = '\n',
cswap = False,
verbose = False):
self._device = device
self._font = font
self.name = self._font.__name__
# font and color; only monochrome HLSB fonts are supported
self._font_format = framebuf.MONO_HLSB
self._font_colors = 2
self._palette_format = framebuf.RGB565 # support up to 65536 colors when blitting
# byte order for 16bit colors
self._cswap = cswap
# inform
if verbose:
fstr = '{} : initialised: height: {}, {} width: {}, baseline: {}'
print(fstr.format(self.name, self._font.height(),
'fixed' if self._font.monospaced() else 'max',
self._font.max_width(), self._font.baseline()))
# apply init color and alignment as default
self.set_default(fg, bg, tkey, halign, valign, hgap, vgap, split, verbose)
def _check_halign(self, h):
if h not in ('left','center','right'):
raise ValueError('Unknown horizontal alignment: ' + h)
return h
def _check_valign(self, v):
if v not in ('top','center','baseline','bottom'):
raise ValueError('Unknown vertical alignment: ' + v)
return v
def _line_size(self, string):
x = 0
for char in string:
_, _, char_width = self._font.get_ch(char)
x += char_width + self.hgap if char_width > 0 else 0
x = x - self.hgap if x != 0 else x # remove any trailing hgap
return x, self._font.height()
def _swap_bytes(self, color):
# flip the left and right bytes in a 16 bit color word if required
return ((color & 255) << 8) + (color >> 8) if self._cswap else color
def _put_char(self, char, x, y, fg, bg, tkey):
# fetch the glyph
glyph, char_height, char_width = self._font.get_ch(char)
if glyph is None:
return None, None # Nothing to write
# buffers
palette_buf = bytearray(self._font_colors * 2)
buf = bytearray(glyph)
# assemble color map
palette = framebuf.FrameBuffer(palette_buf, self._font_colors, 1, self._palette_format)
palette.pixel(0, 0, self._swap_bytes(bg))
palette.pixel(self._font_colors -1, 0, self._swap_bytes(fg))
# fetch and blit the glyph
charbuf = framebuf.FrameBuffer(buf, char_width, char_height, self._font_format)
self._device.blit(charbuf, x, y, tkey, palette)
return char_width, char_height
def set_default(self, fg=None, bg=None, tkey=None,
halign=None, valign=None, hgap=None, vgap=None, split=None, verbose=None):
# Sets the default value for all supplied arguments
self.fg = self.fg if fg is None else fg
self.bg = self.bg if bg is None else bg
self.tkey = self.tkey if tkey is None else tkey
self.halign = self.halign if halign is None else self._check_halign(halign)
self.valign = self.valign if valign is None else self._check_valign(valign)
self.hgap = self.hgap if hgap is None else hgap
self.vgap = self.vgap if vgap is None else vgap
self.split = self.split if split is None else split
self._verbose = self._verbose if verbose is None else verbose
if self._verbose:
fstr = '{} = fg: {}, bg: {}, tkey: {}, halign: {}, valign: {}, hgap: {}, vgap: {}, split: {}'
print(fstr.format(self.name, self.fg, self.bg, self.tkey,
self.halign, self.valign, self.hgap, self.vgap, repr(split)))
def size(self, string):
if len(string) == 0:
return 0, 0
lines = string.split(self.split)
w = 0
for line in lines:
x, _ = self._line_size(line)
w = max(w, x) # record the widest line
h = (len(lines) * (self._font.height() + self.vgap)) - self.vgap
return w, h
def rect(self, string, x, y, halign=None, valign=None):
if len(string) == 0:
return x, y, 0, 0
# apply alignment overrides
halign = self.halign if halign is None else self._check_halign(halign)
valign = self.valign if valign is None else self._check_valign(valign)
# get the x,y size of the rendered string
wide, high = self.size(string)
# apply alignment
xmin = x
if halign == 'center':
xmin = int(x - (wide / 2))
elif halign == 'right':
xmin = x - wide
ymin = y
if valign == 'baseline':
ymin = y - self._font.baseline()
elif valign == 'center':
ymin = int(y - (high / 2))
elif valign == 'bottom':
ymin = y - high
# return the result
return xmin,ymin,wide,high
def write(self, string, x, y, fg=None, bg=None, tkey=None,
halign=None, valign=None):
if len(string) == 0:
return True
all_chars = True
# Argument overrides
fg = self.fg if fg is None else fg
bg = self.bg if bg is None else bg
tkey = self.tkey if tkey is None else tkey
halign = self.halign if halign is None else self._check_halign(halign)
valign = self.valign if valign is None else self._check_valign(valign)
# Break the string into lines
lines = string.split(self.split)
# vertical alignment
high = (len(lines) * (self._font.height() + self.vgap)) - self.vgap
ypos = y
if valign == 'baseline':
ypos = y - self._font.baseline() + 1
elif valign == 'center':
ypos = int(y - (high / 2))
elif valign == 'bottom':
ypos = y - high
for line in lines:
wide, high = self._line_size(line)
# horizontal alignment
if halign == 'left':
xpos = x
elif halign == 'right':
xpos = x - wide
else:
xpos = int(x - (wide / 2))
# write the line
for char in line:
cx, _ = self._put_char(char, xpos, ypos, fg, bg, tkey)
if cx is None:
if self._verbose:
print('{}: missing char: {} (0x{:02X})'.format(self.name, repr(char), ord(char)))
all_chars = False
else:
xpos += cx + self.hgap
ypos += high + self.vgap
return all_chars