Merge pull request #751 from kdmukai/textarea_below_baseline_fix

[Bugfix] `TextArea`: Proper detection of chars rendering below baseline on final line of text
This commit is contained in:
Nick Klockenga
2025-05-25 10:45:23 -04:00
committed by GitHub
+14 -14
View File
@@ -475,9 +475,9 @@ class TextArea(BaseComponent):
# Multiply for the number of lines plus the spacer
total_text_height = self.text_height_above_baseline * len(self.text_lines) + self.line_spacing * (len(self.text_lines) - 1)
if not self.height_ignores_below_baseline and re.findall(f"[gjpqy]", self.text_lines[-1]["text"]):
# Last line has at least one char that dips below baseline
total_text_height += self.text_height_below_baseline
if not self.height_ignores_below_baseline:
# Account for the last line possibly rendering below baseline
total_text_height += self.text_lines[-1].get("px_below_baseline", 0)
self.text_offset_y = 0
if self.height is None:
@@ -582,7 +582,7 @@ class TextArea(BaseComponent):
# Crop off the top_padding and resize the result down to onscreen size
if self.supersampling_factor > 1:
resized = img.resize((image_width, total_text_height + 2*resample_padding), Image.LANCZOS)
resized = img.resize((image_width, total_text_height + 2*resample_padding), Image.Resampling.LANCZOS)
sharpened = resized.filter(ImageFilter.SHARPEN)
img = sharpened.crop((0, resample_padding, image_width, resample_padding + total_text_height))
@@ -1841,7 +1841,7 @@ def reflow_text_for_width(text: str,
# font.
font = Fonts.get_font(font_name=font_name, size=font_size)
# Measure from left baseline ("ls")
(left, top, full_text_width, bottom) = font.getbbox(text, anchor="ls")
(left, top, full_text_width, px_below_baseline) = font.getbbox(text, anchor="ls")
if not ImageFont.core.HAVE_RAQM:
# Fudge factor for imprecise width calcs w/out libraqm
@@ -1860,12 +1860,12 @@ def reflow_text_for_width(text: str,
# Stores each line of text and its rendering starting x-coord
text_lines = []
def _add_text_line(text, text_width):
text_lines.append({"text": text, "text_width": text_width})
def _add_text_line(text, text_width, px_below_baseline):
text_lines.append(dict(text=text, text_width=text_width, px_below_baseline=px_below_baseline))
if "\n" not in text and full_text_width < width:
# The whole text fits on one line
_add_text_line(text, full_text_width)
_add_text_line(text, full_text_width, px_below_baseline)
else:
# Have to calc how to break text into multiple lines
@@ -1877,7 +1877,7 @@ def reflow_text_for_width(text: str,
index = 1
# Measure rendered width from "left" anchor (anchor="l_")
(left, top, right, bottom) = font.getbbox(word_spacer.join(words[0:index]), anchor="ls")
(left, top, right, px_below_baseline) = font.getbbox(word_spacer.join(words[0:index]), anchor="ls")
line_width = right - left
if not ImageFont.core.HAVE_RAQM:
@@ -1891,7 +1891,7 @@ def reflow_text_for_width(text: str,
# It's just one long, unbreakable word. There's no good
# solution here. Just accept it as is and let it render off
# the edges.
return (index, line_width)
return (index, line_width, px_below_baseline)
else:
# There's still room to back down the min_index in the next
# round.
@@ -1899,7 +1899,7 @@ def reflow_text_for_width(text: str,
return _binary_len_search(min_index=min_index, max_index=index, word_spacer=word_spacer)
elif index == max_index:
# We have converged
return (index, line_width)
return (index, line_width, px_below_baseline)
else:
# Candidate line is possibly shorter than necessary.
return _binary_len_search(min_index=index, max_index=max_index, word_spacer=word_spacer)
@@ -1930,11 +1930,11 @@ def reflow_text_for_width(text: str,
if not words:
# It's a blank line
_add_text_line("", 0)
_add_text_line("", 0, 0)
else:
while words:
(index, tw) = _binary_len_search(0, len(words), word_spacer=word_spacer)
_add_text_line(word_spacer.join(words[0:index]), tw)
(index, tw, px_below_baseline) = _binary_len_search(0, len(words), word_spacer=word_spacer)
_add_text_line(word_spacer.join(words[0:index]), tw, px_below_baseline)
words = words[index:]
return text_lines