Skip to content

Bug fixes related to positioning and bounding box size #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Aug 22, 2020
110 changes: 59 additions & 51 deletions adafruit_display_text/bitmap_label.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class Label(displayio.Group):
"""

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

Expand Down Expand Up @@ -144,14 +145,20 @@ def __init__(

# Calculate tight box to provide bounding box dimensions to match label for
# anchor_position calculations
(tight_box_x, tight_box_y, x_offset, tight_y_offset) = self._text_bounding_box(
(
tight_box_x,
tight_box_y,
tight_x_offset,
tight_y_offset,
) = self._text_bounding_box(
text, font, self._line_spacing, background_tight=True,
)

if background_tight:
box_x = tight_box_x
box_y = tight_box_y
y_offset = tight_y_offset
x_offset = tight_x_offset

else:
(box_x, box_y, x_offset, y_offset) = self._text_bounding_box(
Expand All @@ -176,16 +183,12 @@ def __init__(
text,
font,
self._line_spacing,
padding_left + x_offset,
padding_left - x_offset,
padding_top + y_offset,
)

label_position_yoffset = int( # To calibrate with label.py positioning
(
font.get_glyph(ord("M")).height
- text.count("\n") * font.get_bounding_box()[1] * self._line_spacing
)
/ 2
(font.get_glyph(ord("M")).height) / 2
)

self.tilegrid = displayio.TileGrid(
Expand All @@ -196,7 +199,7 @@ def __init__(
tile_width=box_x,
tile_height=box_y,
default_tile=0,
x=-padding_left,
x=-padding_left + x_offset,
y=label_position_yoffset - y_offset - padding_top,
)

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

def _text_bounding_box(
self, text, font, line_spacing, background_tight=False
): # **** change default background_tight=False
def _text_bounding_box(self, text, font, line_spacing, background_tight=False):

# This empirical approach checks several glyphs for maximum ascender and descender height
# (consistent with label.py)
glyphs = "M j'" # choose glyphs with highest ascender and lowest
# descender, will depend upon font used

try:
self._font.load_glyphs(text + glyphs)
except AttributeError:
# ignore if font does not have load_glyphs
pass

ascender_max = descender_max = 0
for char in glyphs:
this_glyph = font.get_glyph(ord(char))
Expand All @@ -246,19 +254,15 @@ def _text_bounding_box(

lines = 1

xposition = x_start = 0 # starting x position (left margin)
yposition = y_start = 0
xposition = (
x_start
) = yposition = y_start = 0 # starting x and y position (left margin)

left = right = x_start
left = None
right = x_start
top = bottom = y_start

y_offset_tight = int(
(
font.get_glyph(ord("M")).height
- text.count("\n") * self._line_spacing_ypixels(font, line_spacing)
)
/ 2
)
y_offset_tight = int((font.get_glyph(ord("M")).height) / 2)
# this needs to be reviewed (also in label.py), since it doesn't respond
# properly to the number of newlines.

Expand All @@ -283,37 +287,35 @@ def _text_bounding_box(
font, line_spacing
) # Add a newline
lines += 1
if xposition == x_start:
if left is None:
left = my_glyph.dx
else:
left = min(left, my_glyph.dx)
xright = xposition + my_glyph.width + my_glyph.dx
xposition += my_glyph.shift_x
right = max(right, xposition)

right = max(right, xposition, xright)

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

loose_height = (lines - 1) * self._line_spacing_ypixels(font, line_spacing) + (
ascender_max + descender_max
)

label_calibration_offset = int(
(
font.get_glyph(ord("M")).height
- text.count("\n") * self._line_spacing_ypixels(font, line_spacing)
)
/ 2
)

y_offset_tight = -top + label_calibration_offset
if left is None:
left = 0

final_box_width = right - left
if background_tight:
final_box_height = bottom - top
final_y_offset = y_offset_tight
final_y_offset = -top + y_offset_tight

else:
final_box_height = loose_height
final_box_height = (lines - 1) * self._line_spacing_ypixels(
font, line_spacing
) + (ascender_max + descender_max)
final_y_offset = ascender_max

return (final_box_width, final_box_height, 0, final_y_offset)
return (final_box_width, final_box_height, left, final_y_offset)

# pylint: disable=too-many-nested-blocks
def _place_text(
Expand Down Expand Up @@ -344,7 +346,8 @@ def _place_text(
x_start = xposition # starting x position (left margin)
y_start = yposition

left = right = x_start
left = None
right = x_start
top = bottom = y_start

for char in text:
Expand All @@ -362,8 +365,17 @@ def _place_text(
if my_glyph is None: # Error checking: no glyph found
print("Glyph not found: {}".format(repr(char)))
else:

right = max(right, xposition + my_glyph.shift_x)
if xposition == x_start:
if left is None:
left = my_glyph.dx
else:
left = min(left, my_glyph.dx)

right = max(
right,
xposition + my_glyph.shift_x,
xposition + my_glyph.width + my_glyph.dx,
)
if yposition == y_start: # first line, find the Ascender height
top = min(top, -my_glyph.height - my_glyph.dy)
bottom = max(bottom, yposition - my_glyph.dy)
Expand Down Expand Up @@ -420,7 +432,6 @@ def line_spacing(self):
bounding-box height. (E.g. 1.0 is the bounding-box height)"""
return self._line_spacing

# pylint: disable=no-self-use
@line_spacing.setter
def line_spacing(self, new_line_spacing):
raise RuntimeError(
Expand Down Expand Up @@ -462,7 +473,6 @@ def text(self):
"""Text to displayed."""
return self._text

# pylint: disable=no-self-use
@text.setter
def text(self, new_text):
raise RuntimeError(
Expand All @@ -474,7 +484,6 @@ def font(self):
"""Font to use for text display."""
return self.font

# pylint: disable=no-self-use
@font.setter
def font(self, new_font):
raise RuntimeError(
Expand Down Expand Up @@ -507,14 +516,13 @@ def anchored_position(self, new_position):

# Set anchored_position
if (self._anchor_point is not None) and (self._anchored_position is not None):
new_x = int(
self.x = int(
new_position[0]
- self._anchor_point[0] * (self._bounding_box[2] * self._scale)
- (self._bounding_box[0] * self._scale)
- round(self._anchor_point[0] * (self._bounding_box[2] * self._scale))
)
new_y = int(
self.y = int(
new_position[1]
- (self._anchor_point[1] * self._bounding_box[3] * self.scale)
+ round((self._bounding_box[3] * self.scale) / 2.0)
- (self._bounding_box[1] * self._scale)
- round(self._anchor_point[1] * self._bounding_box[3] * self._scale)
)
self.x = new_x
self.y = new_y
84 changes: 42 additions & 42 deletions adafruit_display_text/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,6 @@ def __init__(
self.x = x
self.y = y

self.palette = displayio.Palette(2)
self.palette[0] = 0
self.palette.make_transparent(0)
self.palette[1] = color

self.height = self._font.get_bounding_box()[1]
self._line_spacing = line_spacing
self._boundingbox = None
Expand All @@ -112,6 +107,12 @@ def __init__(
background_tight # sets padding status for text background box
)

# Create the two-color text palette
self.palette = displayio.Palette(2)
self.palette[0] = 0
self.palette.make_transparent(0)
self.color = color

self._background_color = background_color
self._background_palette = displayio.Palette(1)
self._added_background_tilegrid = False
Expand Down Expand Up @@ -185,14 +186,8 @@ def _update_background_color(self, new_color):
self._background_palette[0] = new_color
self._background_color = new_color

y_offset = int(
(
self._font.get_glyph(ord("M")).height
- self.text.count("\n") * self.height * self.line_spacing
)
/ 2
)
lines = self.text.count("\n") + 1
lines = self._text.rstrip("\n").count("\n") + 1
y_offset = int((self._font.get_glyph(ord("M")).height) / 2)

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

y_offset = int(
(
self._font.get_glyph(ord("M")).height
- new_text.count("\n") * self.height * self.line_spacing
)
/ 2
)
left = right = top = bottom = 0
lines = 1
y_offset = int((self._font.get_glyph(ord("M")).height) / 2)

right = top = bottom = 0
left = None

for character in new_text:
if character == "\n":
y += int(self.height * self._line_spacing)
x = 0
lines += 1
continue
glyph = self._font.get_glyph(ord(character))
if not glyph:
continue
right = max(right, x + glyph.shift_x)
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)
if y == 0: # first line, find the Ascender height
top = min(top, -glyph.height - glyph.dy + y_offset)
bottom = max(bottom, y - glyph.dy + y_offset)
Expand Down Expand Up @@ -298,10 +293,13 @@ def _update_text(
i += 1
# Remove the rest

if left is None:
left = 0

while len(self) > tilegrid_count: # i:
self.pop()
self._text = new_text
self._boundingbox = (left, top, left + right, bottom - top)
self._boundingbox = (left, top, right - left, bottom - top)

if self.background_color is not None:
self._update_background_color(self._background_color)
Expand Down Expand Up @@ -329,7 +327,13 @@ def color(self):

@color.setter
def color(self, new_color):
self.palette[1] = new_color
self._color = new_color
if new_color is not None:
self.palette[1] = new_color
self.palette.make_opaque(1)
else:
self.palette[1] = 0
self.palette.make_transparent(1)

@property
def background_color(self):
Expand All @@ -351,8 +355,8 @@ def text(self, new_text):
current_anchored_position = self.anchored_position
self._update_text(str(new_text))
self.anchored_position = current_anchored_position
except RuntimeError:
raise RuntimeError("Text length exceeds max_glyphs")
except RuntimeError as run_error:
raise RuntimeError("Text length exceeds max_glyphs") from run_error

@property
def font(self):
Expand Down Expand Up @@ -394,31 +398,27 @@ def anchored_position(self):
return (
int(
self.x
+ (self._boundingbox[0] * self._scale)
+ round(self._anchor_point[0] * self._boundingbox[2] * self._scale)
),
int(
round(
self.y
+ (self._anchor_point[1] * self._boundingbox[3] * self._scale)
- ((self._boundingbox[3] * self._scale) / 2.0)
)
self.y
+ (self._boundingbox[1] * self._scale)
+ round(self._anchor_point[1] * self._boundingbox[3] * self._scale)
),
)

@anchored_position.setter
def anchored_position(self, new_position):
if (self._anchor_point is None) or (new_position is None):
return # Note: anchor_point must be set before setting anchored_position
new_x = int(
self.x = int(
new_position[0]
- (self._boundingbox[0] * self._scale)
- round(self._anchor_point[0] * (self._boundingbox[2] * self._scale))
)
new_y = int(
round(
new_position[1]
- (self._anchor_point[1] * self._boundingbox[3] * self._scale)
+ ((self._boundingbox[3] * self._scale) / 2.0)
)
self.y = int(
new_position[1]
- (self._boundingbox[1] * self._scale)
- round(self._anchor_point[1] * self._boundingbox[3] * self._scale)
)
self.x = new_x
self.y = new_y
Loading