Skip to content
6 changes: 5 additions & 1 deletion adafruit_display_text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@ class LabelBase(Group):
This is helpful when two or more labels need to be aligned to the same baseline
:param: (int,str) tab_replacement: tuple with tab character replace information. When
(4, " ") will indicate a tab replacement of 4 spaces, defaults to 4 spaces by
tab character"""
tab character
:param: str label_direction: string defining the label text orientation. There are 5
configurations possibles ``LTR``:Left-To-Right ``RTL``:Right-To-Left
``TTB``:TTB Top-To-Bottom ``UPR``:Upwards ``DWR``:Downwards. It defaults to ``LTR`` """

# pylint: disable=unused-argument, too-many-instance-attributes, too-many-locals, too-many-arguments
def __init__(
Expand All @@ -208,6 +211,7 @@ def __init__(
scale: int = 1,
base_alignment: bool = False,
tab_replacement: Tuple[int, str] = (4, " "),
label_direction: str = "LTR",
**kwargs,
) -> None:
super().__init__(max_size=1, x=x, y=y, scale=1)
Expand Down
196 changes: 164 additions & 32 deletions adafruit_display_text/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ class Label(LabelBase):
This is helpful when two or more labels need to be aligned to the same baseline
:param: (int,str) tab_replacement: tuple with tab character replace information. When
(4, " ") will indicate a tab replacement of 4 spaces, defaults to 4 spaces by
tab character"""
tab character
:param: str label_direction: string defining the label text orientation. There are 5
configurations possibles ``LTR``:Left-To-Right ``RTL``:Right-To-Left
``TTB``:TTB Top-To-Bottom ``UPR``:Upwards ``DWR``:Downwards. It defaults to ``STR``"""

# pylint: disable=too-many-instance-attributes, too-many-locals
# This has a lot of getters/setters, maybe it needs cleanup.
Expand Down Expand Up @@ -122,6 +125,7 @@ def __init__(self, font, **kwargs) -> None:
self._padding_left = kwargs.get("padding_left", 0)
self._padding_right = kwargs.get("padding_right", 0)
self.base_alignment = kwargs.get("base_alignment", False)
self.label_type = kwargs.get("label_direction", "LTR")
Comment thread
jposada202020 marked this conversation as resolved.
Outdated
Comment thread
jposada202020 marked this conversation as resolved.
Outdated

if text is not None:
self._update_text(str(text))
Expand All @@ -136,7 +140,6 @@ def _create_background_box(self, lines: int, y_offset: int) -> None:
:param y_offset: int y pixel bottom coordinate for the background_box"""

left = self._bounding_box[0]

if self._background_tight: # draw a tight bounding box
box_width = self._bounding_box[2]
box_height = self._bounding_box[3]
Expand All @@ -146,14 +149,33 @@ def _create_background_box(self, lines: int, y_offset: int) -> None:
else: # draw a "loose" bounding box to include any ascenders/descenders.
ascent, descent = self._get_ascent_descent()

box_width = self._bounding_box[2] + self._padding_left + self._padding_right
x_box_offset = -self._padding_left
box_height = (
(ascent + descent)
+ int((lines - 1) * self.height * self._line_spacing)
+ self._padding_top
+ self._padding_bottom
)
if (
self.label_type == "UPR"
or self.label_type == "DWR"
or self.label_type == "TTB"
Comment thread
FoamyGuy marked this conversation as resolved.
Outdated
):
box_height = (
self._bounding_box[3] + self._padding_top + self._padding_bottom
)
x_box_offset = -self._padding_bottom
box_width = (
(ascent + descent)
+ int((lines - 1) * self.width * self._line_spacing)
+ self._padding_left
+ self._padding_right
)
else:
box_width = (
self._bounding_box[2] + self._padding_left + self._padding_right
)
x_box_offset = -self._padding_left
box_height = (
(ascent + descent)
+ int((lines - 1) * self.height * self._line_spacing)
+ self._padding_top
+ self._padding_bottom
)

if self.base_alignment:
y_box_offset = -ascent - self._padding_top
else:
Expand All @@ -162,12 +184,25 @@ def _create_background_box(self, lines: int, y_offset: int) -> None:
box_width = max(0, box_width) # remove any negative values
box_height = max(0, box_height) # remove any negative values

if self.label_type == "UPR":
movx = left + x_box_offset
movy = -box_height - x_box_offset
elif self.label_type == "DWR":
movx = left + x_box_offset
movy = x_box_offset
elif self.label_type == "TTB":
movx = left + x_box_offset
movy = x_box_offset
else:
movx = left + x_box_offset
movy = y_box_offset

background_bitmap = displayio.Bitmap(box_width, box_height, 1)
tile_grid = displayio.TileGrid(
background_bitmap,
pixel_shader=self._background_palette,
x=left + x_box_offset,
y=y_box_offset,
x=movx,
y=movy,
)

return tile_grid
Expand Down Expand Up @@ -239,12 +274,19 @@ def _update_text(
i = 0
tilegrid_count = i
if self.base_alignment:
self._y_offset = 0
y_offset = 0
else:
self._y_offset = self._get_ascent() // 2

right = top = bottom = 0
left = None
y_offset = self._get_ascent() // 2

if self.label_type == "RTL":
left = top = bottom = 0
right = None
elif self.label_type == "LTR":
right = top = bottom = 0
left = None
else:
top = right = left = 0
bottom = 0

for character in new_text:
if character == "\n":
Expand All @@ -254,17 +296,74 @@ def _update_text(
glyph = self._font.get_glyph(ord(character))
if not glyph:
continue
right = max(right, x + glyph.shift_x, x + glyph.width + glyph.dx)
if x == 0:
if left is None:
left = glyph.dx

if self.label_type == "LTR" or self.label_type == "RTL":
bottom = max(bottom, y - glyph.dy + y_offset)
if y == 0: # first line, find the Ascender height
top = min(top, -glyph.height - glyph.dy + y_offset)
position_y = y - glyph.height - glyph.dy + y_offset

if self.label_type == "LTR":
right = max(right, x + glyph.shift_x, x + glyph.width + glyph.dx)
if x == 0:
if left is None:
left = glyph.dx
else:
left = min(left, glyph.dx)
position_x = x + glyph.dx
else:
left = min(left, glyph.dx)
if y == 0: # first line, find the Ascender height
top = min(top, -glyph.height - glyph.dy + self._y_offset)
bottom = max(bottom, y - glyph.dy + self._y_offset)
position_y = y - glyph.height - glyph.dy + self._y_offset
position_x = x + glyph.dx
left = max(
left, abs(x) + glyph.shift_x, abs(x) + glyph.width + glyph.dx
)
if x == 0:
if right is None:
right = glyph.dx
else:
right = max(right, glyph.dx)
position_x = x - glyph.width

if self.label_type == "TTB":
if x == 0:
if left is None:
left = glyph.dx
else:
left = min(left, glyph.dx)
if y == 0:
top = min(top, -glyph.dy)

bottom = max(bottom, y + glyph.height, y + glyph.height + glyph.dy)
right = max(
right, x + glyph.width + glyph.dx, x + glyph.shift_x + glyph.dx
)
position_y = y + glyph.dy
position_x = x - glyph.width // 2 + y_offset

if self.label_type == "UPR":
if x == 0:
if bottom is None:
bottom = -glyph.dx

if y == 0: # first line, find the Ascender height
bottom = min(bottom, -glyph.dy)
left = min(left, x - glyph.height + y_offset)
top = min(top, y - glyph.width - glyph.dx, y - glyph.shift_x)
right = max(right, x + glyph.height, x + glyph.height - glyph.dy)
position_y = y - glyph.width - glyph.dx
position_x = x - glyph.height - glyph.dy + y_offset

if self.label_type == "DWR":
if y == 0:
if top is None:
top = -glyph.dx
top = min(top, -glyph.dx)
if x == 0:
left = min(left, -glyph.dy)
left = min(left, x, x - glyph.dy - y_offset)
bottom = max(bottom, y + glyph.width + glyph.dx, y + glyph.shift_x)
right = max(right, x + glyph.height)
position_y = y + glyph.dx
position_x = x + glyph.dy - y_offset

if glyph.width > 0 and glyph.height > 0:
try:
# pylint: disable=unexpected-keyword-arg
Expand All @@ -286,23 +385,56 @@ def _update_text(
x=position_x,
y=position_y,
)

if self.label_type == "UPR":
face.transpose_xy = True
face.flip_x = True
if self.label_type == "DWR":
face.transpose_xy = True
face.flip_y = True

if tilegrid_count < len(self.local_group):
self.local_group[tilegrid_count] = face
else:
self.local_group.append(face)
tilegrid_count += 1
x += glyph.shift_x
if self.label_type == "RTL":
x = x - glyph.shift_x
if self.label_type == "TTB":
if glyph.height < 2:
y = y + glyph.shift_x
else:
y = y + glyph.height + 1
if self.label_type == "UPR":
y = y - glyph.shift_x
if self.label_type == "DWR":
y = y + glyph.shift_x
if self.label_type == "LTR":
x = x + glyph.shift_x
i += 1
# Remove the rest

if left is None:
if self.label_type == "LTR" and left is None:
left = 0
if self.label_type == "RTL" and right is None:
right = 0
if self.label_type == "TTB" and top is None:
top = 0

while len(self.local_group) > tilegrid_count: # i:
self.local_group.pop()
self._text = new_text
self._bounding_box = (left, top, right - left, bottom - top)
# pylint: disable=invalid-unary-operand-type
if self.label_type == "RTL":
self._bounding_box = (-left, top, left - right, bottom - top)
if self.label_type == "TTB":
self._bounding_box = (left, top, right - left, bottom - top)
if self.label_type == "UPR":
self._bounding_box = (left, top, right, bottom - top)
if self.label_type == "DWR":
self._bounding_box = (left, top, right, bottom - top)
if self.label_type == "LTR":
self._bounding_box = (left, top, right - left, bottom - top)

self._text = new_text
if self.background_color is not None:
self._update_background_color(self._background_color)

Expand Down