Skip to content

Commit 9d28b6c

Browse files
authored
Merge pull request #86 from kmatch98/many_cleanups
Bug fixes related to positioning and bounding box size
2 parents 5ff8c19 + 59c9544 commit 9d28b6c

File tree

4 files changed

+105
-96
lines changed

4 files changed

+105
-96
lines changed

adafruit_display_text/bitmap_label.py

100644100755
+59-51
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class Label(displayio.Group):
8888
"""
8989

9090
# pylint: disable=unused-argument, too-many-instance-attributes, too-many-locals, too-many-arguments
91+
# pylint: disable=too-many-branches, no-self-use
9192
# Note: max_glyphs parameter is unnecessary, this is used for direct
9293
# compatibility with label.py
9394

@@ -144,14 +145,20 @@ def __init__(
144145

145146
# Calculate tight box to provide bounding box dimensions to match label for
146147
# anchor_position calculations
147-
(tight_box_x, tight_box_y, x_offset, tight_y_offset) = self._text_bounding_box(
148+
(
149+
tight_box_x,
150+
tight_box_y,
151+
tight_x_offset,
152+
tight_y_offset,
153+
) = self._text_bounding_box(
148154
text, font, self._line_spacing, background_tight=True,
149155
)
150156

151157
if background_tight:
152158
box_x = tight_box_x
153159
box_y = tight_box_y
154160
y_offset = tight_y_offset
161+
x_offset = tight_x_offset
155162

156163
else:
157164
(box_x, box_y, x_offset, y_offset) = self._text_bounding_box(
@@ -176,16 +183,12 @@ def __init__(
176183
text,
177184
font,
178185
self._line_spacing,
179-
padding_left + x_offset,
186+
padding_left - x_offset,
180187
padding_top + y_offset,
181188
)
182189

183190
label_position_yoffset = int( # To calibrate with label.py positioning
184-
(
185-
font.get_glyph(ord("M")).height
186-
- text.count("\n") * font.get_bounding_box()[1] * self._line_spacing
187-
)
188-
/ 2
191+
(font.get_glyph(ord("M")).height) / 2
189192
)
190193

191194
self.tilegrid = displayio.TileGrid(
@@ -196,7 +199,7 @@ def __init__(
196199
tile_width=box_x,
197200
tile_height=box_y,
198201
default_tile=0,
199-
x=-padding_left,
202+
x=-padding_left + x_offset,
200203
y=label_position_yoffset - y_offset - padding_top,
201204
)
202205

@@ -229,14 +232,19 @@ def _line_spacing_ypixels(font, line_spacing):
229232
return_value = int(line_spacing * font.get_bounding_box()[1])
230233
return return_value
231234

232-
def _text_bounding_box(
233-
self, text, font, line_spacing, background_tight=False
234-
): # **** change default background_tight=False
235+
def _text_bounding_box(self, text, font, line_spacing, background_tight=False):
235236

236237
# This empirical approach checks several glyphs for maximum ascender and descender height
237238
# (consistent with label.py)
238239
glyphs = "M j'" # choose glyphs with highest ascender and lowest
239240
# descender, will depend upon font used
241+
242+
try:
243+
self._font.load_glyphs(text + glyphs)
244+
except AttributeError:
245+
# ignore if font does not have load_glyphs
246+
pass
247+
240248
ascender_max = descender_max = 0
241249
for char in glyphs:
242250
this_glyph = font.get_glyph(ord(char))
@@ -246,19 +254,15 @@ def _text_bounding_box(
246254

247255
lines = 1
248256

249-
xposition = x_start = 0 # starting x position (left margin)
250-
yposition = y_start = 0
257+
xposition = (
258+
x_start
259+
) = yposition = y_start = 0 # starting x and y position (left margin)
251260

252-
left = right = x_start
261+
left = None
262+
right = x_start
253263
top = bottom = y_start
254264

255-
y_offset_tight = int(
256-
(
257-
font.get_glyph(ord("M")).height
258-
- text.count("\n") * self._line_spacing_ypixels(font, line_spacing)
259-
)
260-
/ 2
261-
)
265+
y_offset_tight = int((font.get_glyph(ord("M")).height) / 2)
262266
# this needs to be reviewed (also in label.py), since it doesn't respond
263267
# properly to the number of newlines.
264268

@@ -283,37 +287,35 @@ def _text_bounding_box(
283287
font, line_spacing
284288
) # Add a newline
285289
lines += 1
290+
if xposition == x_start:
291+
if left is None:
292+
left = my_glyph.dx
293+
else:
294+
left = min(left, my_glyph.dx)
295+
xright = xposition + my_glyph.width + my_glyph.dx
286296
xposition += my_glyph.shift_x
287-
right = max(right, xposition)
297+
298+
right = max(right, xposition, xright)
288299

289300
if yposition == y_start: # first line, find the Ascender height
290301
top = min(top, -my_glyph.height - my_glyph.dy + y_offset_tight)
291302
bottom = max(bottom, yposition - my_glyph.dy + y_offset_tight)
292303

293-
loose_height = (lines - 1) * self._line_spacing_ypixels(font, line_spacing) + (
294-
ascender_max + descender_max
295-
)
296-
297-
label_calibration_offset = int(
298-
(
299-
font.get_glyph(ord("M")).height
300-
- text.count("\n") * self._line_spacing_ypixels(font, line_spacing)
301-
)
302-
/ 2
303-
)
304-
305-
y_offset_tight = -top + label_calibration_offset
304+
if left is None:
305+
left = 0
306306

307307
final_box_width = right - left
308308
if background_tight:
309309
final_box_height = bottom - top
310-
final_y_offset = y_offset_tight
310+
final_y_offset = -top + y_offset_tight
311311

312312
else:
313-
final_box_height = loose_height
313+
final_box_height = (lines - 1) * self._line_spacing_ypixels(
314+
font, line_spacing
315+
) + (ascender_max + descender_max)
314316
final_y_offset = ascender_max
315317

316-
return (final_box_width, final_box_height, 0, final_y_offset)
318+
return (final_box_width, final_box_height, left, final_y_offset)
317319

318320
# pylint: disable=too-many-nested-blocks
319321
def _place_text(
@@ -344,7 +346,8 @@ def _place_text(
344346
x_start = xposition # starting x position (left margin)
345347
y_start = yposition
346348

347-
left = right = x_start
349+
left = None
350+
right = x_start
348351
top = bottom = y_start
349352

350353
for char in text:
@@ -362,8 +365,17 @@ def _place_text(
362365
if my_glyph is None: # Error checking: no glyph found
363366
print("Glyph not found: {}".format(repr(char)))
364367
else:
365-
366-
right = max(right, xposition + my_glyph.shift_x)
368+
if xposition == x_start:
369+
if left is None:
370+
left = my_glyph.dx
371+
else:
372+
left = min(left, my_glyph.dx)
373+
374+
right = max(
375+
right,
376+
xposition + my_glyph.shift_x,
377+
xposition + my_glyph.width + my_glyph.dx,
378+
)
367379
if yposition == y_start: # first line, find the Ascender height
368380
top = min(top, -my_glyph.height - my_glyph.dy)
369381
bottom = max(bottom, yposition - my_glyph.dy)
@@ -420,7 +432,6 @@ def line_spacing(self):
420432
bounding-box height. (E.g. 1.0 is the bounding-box height)"""
421433
return self._line_spacing
422434

423-
# pylint: disable=no-self-use
424435
@line_spacing.setter
425436
def line_spacing(self, new_line_spacing):
426437
raise RuntimeError(
@@ -462,7 +473,6 @@ def text(self):
462473
"""Text to displayed."""
463474
return self._text
464475

465-
# pylint: disable=no-self-use
466476
@text.setter
467477
def text(self, new_text):
468478
raise RuntimeError(
@@ -474,7 +484,6 @@ def font(self):
474484
"""Font to use for text display."""
475485
return self.font
476486

477-
# pylint: disable=no-self-use
478487
@font.setter
479488
def font(self, new_font):
480489
raise RuntimeError(
@@ -507,14 +516,13 @@ def anchored_position(self, new_position):
507516

508517
# Set anchored_position
509518
if (self._anchor_point is not None) and (self._anchored_position is not None):
510-
new_x = int(
519+
self.x = int(
511520
new_position[0]
512-
- self._anchor_point[0] * (self._bounding_box[2] * self._scale)
521+
- (self._bounding_box[0] * self._scale)
522+
- round(self._anchor_point[0] * (self._bounding_box[2] * self._scale))
513523
)
514-
new_y = int(
524+
self.y = int(
515525
new_position[1]
516-
- (self._anchor_point[1] * self._bounding_box[3] * self.scale)
517-
+ round((self._bounding_box[3] * self.scale) / 2.0)
526+
- (self._bounding_box[1] * self._scale)
527+
- round(self._anchor_point[1] * self._bounding_box[3] * self._scale)
518528
)
519-
self.x = new_x
520-
self.y = new_y

adafruit_display_text/label.py

+42-42
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,6 @@ def __init__(
9999
self.x = x
100100
self.y = y
101101

102-
self.palette = displayio.Palette(2)
103-
self.palette[0] = 0
104-
self.palette.make_transparent(0)
105-
self.palette[1] = color
106-
107102
self.height = self._font.get_bounding_box()[1]
108103
self._line_spacing = line_spacing
109104
self._boundingbox = None
@@ -112,6 +107,12 @@ def __init__(
112107
background_tight # sets padding status for text background box
113108
)
114109

110+
# Create the two-color text palette
111+
self.palette = displayio.Palette(2)
112+
self.palette[0] = 0
113+
self.palette.make_transparent(0)
114+
self.color = color
115+
115116
self._background_color = background_color
116117
self._background_palette = displayio.Palette(1)
117118
self._added_background_tilegrid = False
@@ -185,14 +186,8 @@ def _update_background_color(self, new_color):
185186
self._background_palette[0] = new_color
186187
self._background_color = new_color
187188

188-
y_offset = int(
189-
(
190-
self._font.get_glyph(ord("M")).height
191-
- self.text.count("\n") * self.height * self.line_spacing
192-
)
193-
/ 2
194-
)
195-
lines = self.text.count("\n") + 1
189+
lines = self._text.rstrip("\n").count("\n") + 1
190+
y_offset = int((self._font.get_glyph(ord("M")).height) / 2)
196191

197192
if not self._added_background_tilegrid: # no bitmap is in the self Group
198193
# add bitmap if text is present and bitmap sizes > 0 pixels
@@ -244,25 +239,25 @@ def _update_text(
244239
# ignore if font does not have load_glyphs
245240
pass
246241

247-
y_offset = int(
248-
(
249-
self._font.get_glyph(ord("M")).height
250-
- new_text.count("\n") * self.height * self.line_spacing
251-
)
252-
/ 2
253-
)
254-
left = right = top = bottom = 0
255-
lines = 1
242+
y_offset = int((self._font.get_glyph(ord("M")).height) / 2)
243+
244+
right = top = bottom = 0
245+
left = None
246+
256247
for character in new_text:
257248
if character == "\n":
258249
y += int(self.height * self._line_spacing)
259250
x = 0
260-
lines += 1
261251
continue
262252
glyph = self._font.get_glyph(ord(character))
263253
if not glyph:
264254
continue
265-
right = max(right, x + glyph.shift_x)
255+
right = max(right, x + glyph.shift_x, x + glyph.width + glyph.dx)
256+
if x == 0:
257+
if left is None:
258+
left = glyph.dx
259+
else:
260+
left = min(left, glyph.dx)
266261
if y == 0: # first line, find the Ascender height
267262
top = min(top, -glyph.height - glyph.dy + y_offset)
268263
bottom = max(bottom, y - glyph.dy + y_offset)
@@ -298,10 +293,13 @@ def _update_text(
298293
i += 1
299294
# Remove the rest
300295

296+
if left is None:
297+
left = 0
298+
301299
while len(self) > tilegrid_count: # i:
302300
self.pop()
303301
self._text = new_text
304-
self._boundingbox = (left, top, left + right, bottom - top)
302+
self._boundingbox = (left, top, right - left, bottom - top)
305303

306304
if self.background_color is not None:
307305
self._update_background_color(self._background_color)
@@ -329,7 +327,13 @@ def color(self):
329327

330328
@color.setter
331329
def color(self, new_color):
332-
self.palette[1] = new_color
330+
self._color = new_color
331+
if new_color is not None:
332+
self.palette[1] = new_color
333+
self.palette.make_opaque(1)
334+
else:
335+
self.palette[1] = 0
336+
self.palette.make_transparent(1)
333337

334338
@property
335339
def background_color(self):
@@ -351,8 +355,8 @@ def text(self, new_text):
351355
current_anchored_position = self.anchored_position
352356
self._update_text(str(new_text))
353357
self.anchored_position = current_anchored_position
354-
except RuntimeError:
355-
raise RuntimeError("Text length exceeds max_glyphs")
358+
except RuntimeError as run_error:
359+
raise RuntimeError("Text length exceeds max_glyphs") from run_error
356360

357361
@property
358362
def font(self):
@@ -394,31 +398,27 @@ def anchored_position(self):
394398
return (
395399
int(
396400
self.x
401+
+ (self._boundingbox[0] * self._scale)
397402
+ round(self._anchor_point[0] * self._boundingbox[2] * self._scale)
398403
),
399404
int(
400-
round(
401-
self.y
402-
+ (self._anchor_point[1] * self._boundingbox[3] * self._scale)
403-
- ((self._boundingbox[3] * self._scale) / 2.0)
404-
)
405+
self.y
406+
+ (self._boundingbox[1] * self._scale)
407+
+ round(self._anchor_point[1] * self._boundingbox[3] * self._scale)
405408
),
406409
)
407410

408411
@anchored_position.setter
409412
def anchored_position(self, new_position):
410413
if (self._anchor_point is None) or (new_position is None):
411414
return # Note: anchor_point must be set before setting anchored_position
412-
new_x = int(
415+
self.x = int(
413416
new_position[0]
417+
- (self._boundingbox[0] * self._scale)
414418
- round(self._anchor_point[0] * (self._boundingbox[2] * self._scale))
415419
)
416-
new_y = int(
417-
round(
418-
new_position[1]
419-
- (self._anchor_point[1] * self._boundingbox[3] * self._scale)
420-
+ ((self._boundingbox[3] * self._scale) / 2.0)
421-
)
420+
self.y = int(
421+
new_position[1]
422+
- (self._boundingbox[1] * self._scale)
423+
- round(self._anchor_point[1] * self._boundingbox[3] * self._scale)
422424
)
423-
self.x = new_x
424-
self.y = new_y

0 commit comments

Comments
 (0)