diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py old mode 100644 new mode 100755 index 98f9322..8f57c75 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -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 @@ -144,7 +145,12 @@ 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, ) @@ -152,6 +158,7 @@ def __init__( 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( @@ -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( @@ -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, ) @@ -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)) @@ -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. @@ -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( @@ -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: @@ -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) @@ -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( @@ -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( @@ -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( @@ -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 diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 57832f2..bc9e987 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -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 @@ -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 @@ -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 @@ -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) @@ -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) @@ -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): @@ -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): @@ -394,14 +398,13 @@ 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) ), ) @@ -409,16 +412,13 @@ def anchored_position(self): 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 diff --git a/examples/display_text_background_color_padding.py b/examples/display_text_background_color_padding.py index 91b694b..8e5bda9 100755 --- a/examples/display_text_background_color_padding.py +++ b/examples/display_text_background_color_padding.py @@ -8,8 +8,8 @@ # from adafruit_st7789 import ST7789 from adafruit_ili9341 import ILI9341 -from adafruit_display_text import label from adafruit_bitmap_font import bitmap_font +from adafruit_display_text import label # Setup the SPI display @@ -55,7 +55,7 @@ # font=terminalio.FONT # this is the Builtin fixed dimension font -font = bitmap_font.load_font("fonts/BitstreamVeraSans-Roman-24.bdf") +font = bitmap_font.load_font("fonts/Helvetica-Bold-16.bdf") text = [] diff --git a/examples/display_text_textarea_boundingbox.py b/examples/display_text_textarea_boundingbox.py index 7978abb..fc0cb5e 100644 --- a/examples/display_text_textarea_boundingbox.py +++ b/examples/display_text_textarea_boundingbox.py @@ -1,8 +1,9 @@ import os import board import displayio -from adafruit_display_text.label import Label from adafruit_bitmap_font import bitmap_font +from adafruit_display_text.label import Label + # the current working directory (where this file is) cwd = ("/" + __file__).rsplit("/", 1)[0]