From f77757137bd5789cde097ca844da713bc5bb5563 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 30 Jan 2021 16:31:05 -0600 Subject: [PATCH 01/12] add initial wrap_text_to_pixels function --- adafruit_display_text/__init__.py | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 58f8ce0..2cd0bc6 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2020 Tim C for Adafruit Industries +# SPDX-FileCopyrightText: 2020 Tim C, 2021 Jeff Epler for Adafruit Industries # # SPDX-License-Identifier: MIT @@ -7,6 +7,42 @@ """ +def wrap_text_to_pixels(text, max_width, font=None, indent0="", indent1=""): + if font is None: + def measure(s): + return len(s) + else: + if hasattr(font, 'load_glyphs'): + font.load_glyphs(text) + + def measure(s): + return sum(font.get_glyph(ord(c)).shift_x for c in s) + + lines = [] + partial = [indent0] + width = measure(indent0) + swidth = measure(' ') + firstword = True + for word in text.split(): + # TODO: split words that are larger than max_width + wwidth = measure(word) + print("{} - {}".format(word, wwidth)) + if firstword: + partial.append(word) + firstword = False + width += wwidth + elif width + swidth + wwidth < max_width: + partial.append(" ") + partial.append(word) + width += wwidth + swidth + else: + lines.append("".join(partial)) + partial = [indent1, word] + width = measure(indent1) + wwidth + swidth + if partial: + lines.append("".join(partial)) + return "\n".join(lines) + def wrap_text_to_lines(string, max_chars): """wrap_text_to_lines function A helper that will return a list of lines with word-break wrapping From 90e517bff5760fd3b0ad853a9fbfffda6946c879 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 31 Jan 2021 11:59:35 -0600 Subject: [PATCH 02/12] split and hyphenate words that are too long for single line --- adafruit_display_text/__init__.py | 50 ++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 2cd0bc6..103fead 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -27,18 +27,46 @@ def measure(s): # TODO: split words that are larger than max_width wwidth = measure(word) print("{} - {}".format(word, wwidth)) - if firstword: - partial.append(word) - firstword = False - width += wwidth - elif width + swidth + wwidth < max_width: - partial.append(" ") - partial.append(word) - width += wwidth + swidth + part_width = 0 + word_parts = [] + cur_part = "" + if wwidth > max_width: + if partial: + lines.append("".join(partial)) + partial = [] + for char in word: + #print(measure(cur_part) + measure(char) + measure("-")) + if measure(cur_part) + measure(char) + measure("-") > max_width: + word_parts.append(cur_part + "-") + cur_part = char + else: + cur_part += char + if cur_part: + word_parts.append(cur_part) + #print(word_parts) + for line in word_parts[:-1]: + lines.append(line) + partial.append(word_parts[-1]) + width = measure(word_parts[-1]) + if firstword: + firstword = False + print("cur_width after splitword: {}".format(width)) + else: - lines.append("".join(partial)) - partial = [indent1, word] - width = measure(indent1) + wwidth + swidth + + if firstword: + partial.append(word) + firstword = False + width += wwidth + elif width + swidth + wwidth < max_width: + partial.append(" ") + partial.append(word) + width += wwidth + swidth + else: + lines.append("".join(partial)) + partial = [indent1, word] + width = measure(indent1) + wwidth + print("cur_width: {}".format(width)) if partial: lines.append("".join(partial)) return "\n".join(lines) From bdceacfab97929d12f66caa8b69156ab08841f36 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 31 Jan 2021 13:07:02 -0600 Subject: [PATCH 03/12] pylint and pre-commit --- adafruit_display_text/__init__.py | 41 ++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 103fead..dfc0026 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -7,27 +7,43 @@ """ -def wrap_text_to_pixels(text, max_width, font=None, indent0="", indent1=""): +def wrap_text_to_pixels(string, max_width, font=None, indent0="", indent1=""): + """wrap_text_to_pixels function + A helper that will return a list of lines with word-break wrapping + + :param str string: The text to be wrapped. + :param int max_width: The maximum number of pixels on a line before wrapping. + :param Font font: The font to use for measuring the text. + :param str indent0: Additional character(s) to add to the first line. + :param str indent1: Additional character(s) to add to all other lines. + + + :return str lines: A string with newlines inserted appropriately to wrap + the input string at the given max_width in pixels + + """ + # pylint: disable=too-many-locals too-many-branches if font is None: - def measure(s): - return len(s) + + def measure(string): + return len(string) + else: - if hasattr(font, 'load_glyphs'): - font.load_glyphs(text) + if hasattr(font, "load_glyphs"): + font.load_glyphs(string) - def measure(s): - return sum(font.get_glyph(ord(c)).shift_x for c in s) + def measure(string): + return sum(font.get_glyph(ord(c)).shift_x for c in string) lines = [] partial = [indent0] width = measure(indent0) - swidth = measure(' ') + swidth = measure(" ") firstword = True - for word in text.split(): + for word in string.split(): # TODO: split words that are larger than max_width wwidth = measure(word) print("{} - {}".format(word, wwidth)) - part_width = 0 word_parts = [] cur_part = "" if wwidth > max_width: @@ -35,7 +51,7 @@ def measure(s): lines.append("".join(partial)) partial = [] for char in word: - #print(measure(cur_part) + measure(char) + measure("-")) + # print(measure(cur_part) + measure(char) + measure("-")) if measure(cur_part) + measure(char) + measure("-") > max_width: word_parts.append(cur_part + "-") cur_part = char @@ -43,7 +59,7 @@ def measure(s): cur_part += char if cur_part: word_parts.append(cur_part) - #print(word_parts) + # print(word_parts) for line in word_parts[:-1]: lines.append(line) partial.append(word_parts[-1]) @@ -71,6 +87,7 @@ def measure(s): lines.append("".join(partial)) return "\n".join(lines) + def wrap_text_to_lines(string, max_chars): """wrap_text_to_lines function A helper that will return a list of lines with word-break wrapping From f24d6ba7db677616caa6752c7cc4bc17e62f8ffd Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 31 Jan 2021 13:33:54 -0600 Subject: [PATCH 04/12] adding wrap_pixels example --- examples/display_text_wrap_pixels_test.py | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 examples/display_text_wrap_pixels_test.py diff --git a/examples/display_text_wrap_pixels_test.py b/examples/display_text_wrap_pixels_test.py new file mode 100644 index 0000000..aa9a4e6 --- /dev/null +++ b/examples/display_text_wrap_pixels_test.py @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: 2021 Tim C, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Test the wrap_text_to_pixels function. Try changing WRAP_WIDTH or text +and observe the results. The red bar represents the full size of +WRAP_WIDTH. +""" + +import board +import displayio +import terminalio +from adafruit_display_text import label, wrap_text_to_pixels + +WRAP_WIDTH = 140 +text = ( + "CircuitPython is a programming language designed to simplify experimenting " + "and learning to code on low-cost microcontroller boards. " +) + +# use built in display (PyPortal, PyGamer, PyBadge, CLUE, etc.) +# see guide for setting up external displays (TFT / OLED breakouts, RGB matrices, etc.) +# https://learn.adafruit.com/circuitpython-display-support-using-displayio/display-and-display-bus +display = board.DISPLAY + +# Make the display context +main_group = displayio.Group(max_size=10) +display.show(main_group) + +font = terminalio.FONT + +print(text) +print(display.width) + +text_area = label.Label( + font, text=wrap_text_to_pixels(text, WRAP_WIDTH, font), background_color=0x0000DD +) + +text_area.anchor_point = (0, 0) +text_area.anchored_position = (0, 0) + +main_group.append(text_area) + +# Create a bitmap with two colors +size_checker = displayio.Bitmap(WRAP_WIDTH, 10, 2) +# Create a two color palette +palette = displayio.Palette(2) +palette[0] = 0x0000DD +palette[1] = 0xDD0000 + +# Create a TileGrid using the Bitmap and Palette +tile_grid = displayio.TileGrid(size_checker, pixel_shader=palette) + +tile_grid.y = text_area.bounding_box[1] + text_area.bounding_box[3] + 10 + +size_checker.fill(1) + +main_group.append(tile_grid) + +while True: + pass From d99183a15cbcc83329f813f088229447dff8e3c5 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 31 Jan 2021 13:47:56 -0600 Subject: [PATCH 05/12] remove debuggin prints and completed todo comment --- adafruit_display_text/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index dfc0026..a8cb75b 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -41,9 +41,7 @@ def measure(string): swidth = measure(" ") firstword = True for word in string.split(): - # TODO: split words that are larger than max_width wwidth = measure(word) - print("{} - {}".format(word, wwidth)) word_parts = [] cur_part = "" if wwidth > max_width: @@ -51,7 +49,6 @@ def measure(string): lines.append("".join(partial)) partial = [] for char in word: - # print(measure(cur_part) + measure(char) + measure("-")) if measure(cur_part) + measure(char) + measure("-") > max_width: word_parts.append(cur_part + "-") cur_part = char @@ -59,17 +56,13 @@ def measure(string): cur_part += char if cur_part: word_parts.append(cur_part) - # print(word_parts) for line in word_parts[:-1]: lines.append(line) partial.append(word_parts[-1]) width = measure(word_parts[-1]) if firstword: firstword = False - print("cur_width after splitword: {}".format(width)) - else: - if firstword: partial.append(word) firstword = False @@ -82,7 +75,6 @@ def measure(string): lines.append("".join(partial)) partial = [indent1, word] width = measure(indent1) + wwidth - print("cur_width: {}".format(width)) if partial: lines.append("".join(partial)) return "\n".join(lines) From 23231814e42c968e3ff50c862194ab4d00cfac8a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 31 Jan 2021 13:51:27 -0600 Subject: [PATCH 06/12] change to return list instead of string --- adafruit_display_text/__init__.py | 6 +++--- examples/display_text_wrap_pixels_test.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index a8cb75b..22e4b08 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -18,8 +18,8 @@ def wrap_text_to_pixels(string, max_width, font=None, indent0="", indent1=""): :param str indent1: Additional character(s) to add to all other lines. - :return str lines: A string with newlines inserted appropriately to wrap - the input string at the given max_width in pixels + :return list lines: A list of the lines resulting from wrapping the + input text at max_width pixels size """ # pylint: disable=too-many-locals too-many-branches @@ -77,7 +77,7 @@ def measure(string): width = measure(indent1) + wwidth if partial: lines.append("".join(partial)) - return "\n".join(lines) + return lines def wrap_text_to_lines(string, max_chars): diff --git a/examples/display_text_wrap_pixels_test.py b/examples/display_text_wrap_pixels_test.py index aa9a4e6..1b0ff60 100644 --- a/examples/display_text_wrap_pixels_test.py +++ b/examples/display_text_wrap_pixels_test.py @@ -33,7 +33,9 @@ print(display.width) text_area = label.Label( - font, text=wrap_text_to_pixels(text, WRAP_WIDTH, font), background_color=0x0000DD + font, + text="\n".join(wrap_text_to_pixels(text, WRAP_WIDTH, font)), + background_color=0x0000DD, ) text_area.anchor_point = (0, 0) From 794451b70337b5233702a640603e768d141fc83d Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 13 Feb 2021 13:02:15 -0600 Subject: [PATCH 07/12] fix newlines getting removed by wrap_text_to_pixels --- adafruit_display_text/__init__.py | 79 ++++++++++++++++++------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 22e4b08..67ce1f1 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -40,41 +40,52 @@ def measure(string): width = measure(indent0) swidth = measure(" ") firstword = True - for word in string.split(): - wwidth = measure(word) - word_parts = [] - cur_part = "" - if wwidth > max_width: - if partial: - lines.append("".join(partial)) - partial = [] - for char in word: - if measure(cur_part) + measure(char) + measure("-") > max_width: - word_parts.append(cur_part + "-") - cur_part = char - else: - cur_part += char - if cur_part: - word_parts.append(cur_part) - for line in word_parts[:-1]: - lines.append(line) - partial.append(word_parts[-1]) - width = measure(word_parts[-1]) - if firstword: - firstword = False - else: - if firstword: - partial.append(word) - firstword = False - width += wwidth - elif width + swidth + wwidth < max_width: - partial.append(" ") - partial.append(word) - width += wwidth + swidth + for line_in_input in string.split("\n"): + + print("line: {}".format(line_in_input)) + for index, word in enumerate(line_in_input.split(" ")): + print("word: {}".format(word)) + wwidth = measure(word) + word_parts = [] + cur_part = "" + + if wwidth > max_width: + if partial: + lines.append("".join(partial)) + partial = [] + for char in word: + if measure(cur_part) + measure(char) + measure("-") > max_width: + word_parts.append(cur_part + "-") + cur_part = char + else: + cur_part += char + if cur_part: + word_parts.append(cur_part) + for line in word_parts[:-1]: + lines.append(line) + partial.append(word_parts[-1]) + width = measure(word_parts[-1]) + if firstword: + firstword = False else: - lines.append("".join(partial)) - partial = [indent1, word] - width = measure(indent1) + wwidth + if firstword: + partial.append(word) + firstword = False + width += wwidth + elif width + swidth + wwidth < max_width: + if index > 0: + partial.append(" ") + partial.append(word) + width += wwidth + swidth + else: + lines.append("".join(partial)) + partial = [indent1, word] + width = measure(indent1) + wwidth + + lines.append("".join(partial)) + partial = [indent1] + width = measure(indent1) + if partial: lines.append("".join(partial)) return lines From 8ac594d7098228e5364b4681474dec594b800601 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 13 Feb 2021 13:23:23 -0600 Subject: [PATCH 08/12] remove debugging prints --- adafruit_display_text/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 67ce1f1..08215cd 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -41,10 +41,7 @@ def measure(string): swidth = measure(" ") firstword = True for line_in_input in string.split("\n"): - - print("line: {}".format(line_in_input)) for index, word in enumerate(line_in_input.split(" ")): - print("word: {}".format(word)) wwidth = measure(word) word_parts = [] cur_part = "" From 6d6b6b797a3f4f0fae67a3a758dfb8fd31ca44d7 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 13 Feb 2021 16:22:51 -0600 Subject: [PATCH 09/12] long words start on current line instead of next line --- adafruit_display_text/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 08215cd..4059208 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -47,13 +47,11 @@ def measure(string): cur_part = "" if wwidth > max_width: - if partial: - lines.append("".join(partial)) - partial = [] for char in word: - if measure(cur_part) + measure(char) + measure("-") > max_width: - word_parts.append(cur_part + "-") + if measure("".join(partial)) + measure(cur_part) + measure(char) + measure("-") > max_width: + word_parts.append("".join(partial) + cur_part + "-") cur_part = char + partial = [indent1] else: cur_part += char if cur_part: From 8dcaefb490fbef49e96c6523977dc780ec3ce9c9 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 13 Feb 2021 16:29:05 -0600 Subject: [PATCH 10/12] black format --- adafruit_display_text/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 4059208..5c32568 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -48,7 +48,13 @@ def measure(string): if wwidth > max_width: for char in word: - if measure("".join(partial)) + measure(cur_part) + measure(char) + measure("-") > max_width: + if ( + measure("".join(partial)) + + measure(cur_part) + + measure(char) + + measure("-") + > max_width + ): word_parts.append("".join(partial) + cur_part + "-") cur_part = char partial = [indent1] From 392a4810384e2d0aa2af0ecbd65ad464d5a8536f Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sat, 13 Feb 2021 16:34:27 -0600 Subject: [PATCH 11/12] document leading and trailing whitespace removal --- adafruit_display_text/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index 5c32568..e3d4582 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -9,7 +9,10 @@ def wrap_text_to_pixels(string, max_width, font=None, indent0="", indent1=""): """wrap_text_to_pixels function - A helper that will return a list of lines with word-break wrapping + A helper that will return a list of lines with word-break wrapping. + Leading and trailing whitespace in your string will be removed. If + you wish to use leading whitespace see `indend0` and `indent1` + parameters. :param str string: The text to be wrapped. :param int max_width: The maximum number of pixels on a line before wrapping. From 264e5c8cc2d54dfdcd517f1d77f162b5117295d2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 14 Feb 2021 15:40:47 -0600 Subject: [PATCH 12/12] remove extra adding of indent1 --- adafruit_display_text/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py index e3d4582..34b6046 100644 --- a/adafruit_display_text/__init__.py +++ b/adafruit_display_text/__init__.py @@ -90,8 +90,6 @@ def measure(string): partial = [indent1] width = measure(indent1) - if partial: - lines.append("".join(partial)) return lines