From 6a31b5509e413962571dd73b6d4340b2431398ab Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:57:32 +1200 Subject: [PATCH 1/8] Update sitelen.py --- src/ilo/sitelen.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/ilo/sitelen.py b/src/ilo/sitelen.py index 52e392b..877a798 100644 --- a/src/ilo/sitelen.py +++ b/src/ilo/sitelen.py @@ -1,9 +1,11 @@ import io import logging +import re from PIL import Image, ImageDraw, ImageFont from ilo.cog_utils import BgStyle, Color, ColorAlpha +from ilo.ucsur import chars LOG = logging.getLogger("ilo") @@ -11,7 +13,6 @@ WHITE: ColorAlpha = (0xF0, 0xF0, 0xF0, 0xFF) TRANSPARENT: ColorAlpha = (0, 0, 0, 0) - def subpixel_luminance(num: int) -> float: srgb = num / 255 if srgb <= 0.04045: @@ -70,9 +71,29 @@ def passes_aa(color: Color, bg_color: Color, font_size: int) -> bool: # return BLACK, BLACK # return WHITE, WHITE +def wrap_text(text:str, font: ImageFont.FreeTypeFont, line_width: int): + lines = text.splitlines() + wrapped_text = "" + for line in lines: + if font.getlength(line) < line_width: + wrapped_text += line+"\n" + continue + + split_line = re.sub(rf"({"|".join(chars)})", r"\g<0> ", line) # insert space after every uscur character + split_line = split_line.split(" ") + current_line_segment = "" + for word in split_line: + newline = current_line_segment + word + if font.getlength(newline) < line_width: + current_line_segment += word + else: + wrapped_text += current_line_segment + "\n" + current_line_segment = word + wrapped_text += current_line_segment + "\n" + return(wrapped_text) # by jan Tepo -def display(text: str, font_path: str, font_size: int, color: Color, bgstyle: BgStyle): +def display(text: str, font_path: str, font_size: int, color: Color, bgstyle: BgStyle, linewrap:bool, linewidth:int): STROKE_WIDTH = round((font_size / 133) * 5) LINE_SPACING = round((font_size / 2.5)) PAD = round(font_size / 25) @@ -81,6 +102,10 @@ def display(text: str, font_path: str, font_size: int, color: Color, bgstyle: Bg bg_color = stroke_color if bgstyle == "background" else TRANSPARENT font = ImageFont.truetype(font_path, font_size) + + if linewrap: + text = wrap_text(text, font, linewidth) + d = ImageDraw.Draw(Image.new("RGBA", (0, 0), (0, 0, 0, 0))) x, y, w, h = d.multiline_textbbox( (0, 0), @@ -90,6 +115,10 @@ def display(text: str, font_path: str, font_size: int, color: Color, bgstyle: Bg stroke_width=STROKE_WIDTH, font_size=font_size, ) + + if linewrap: + w = max(w, linewidth) # mobile sometimes expands images to the screen's width, so this helps keep the characters displayi ng at a constant size + image = Image.new( mode="RGBA", size=(w + PAD, h + PAD + 25), From ab046a7721c1e126854c9946fc64bf29d37504c2 Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:58:10 +1200 Subject: [PATCH 2/8] Update data.py --- src/ilo/data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ilo/data.py b/src/ilo/data.py index 68f5e0c..af55de8 100644 --- a/src/ilo/data.py +++ b/src/ilo/data.py @@ -103,6 +103,8 @@ class UsageCategory(Enum): DEFAULT_LANGUAGE = "en" DEFAULT_USAGE_CATEGORY = "common" DEFAULT_PROXY = False +DEFAULT_LINE_WRAP = False +DEFAULT_LINE_WIDTH = 1000 # TODO: # - rework all functions here to use the api From 55250765715e2d6a347999cc2d859491af413616 Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:02:08 +1200 Subject: [PATCH 3/8] Update cog.py --- src/ilo/cogs/sitelen/cog.py | 48 ++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/ilo/cogs/sitelen/cog.py b/src/ilo/cogs/sitelen/cog.py index fd0f742..95613a6 100644 --- a/src/ilo/cogs/sitelen/cog.py +++ b/src/ilo/cogs/sitelen/cog.py @@ -78,6 +78,22 @@ def __init__(self, bot: Bot): validation=lambda x: isinstance(x, bool), ) ) + preferences.register( + Template( + locale=self.locale, + name="linewrap", + default=data.DEFAULT_LINE_WRAP, + validation=lambda x: isinstance(x, bool), + ) + ) + preferences.register( + Template( + locale=self.locale, + name="linewidth", + default=data.DEFAULT_LINE_WIDTH, + validation=utils.is_valid_line_width, + ) + ) locale = utils.Locale(__file__) @@ -91,6 +107,9 @@ def __init__(self, bot: Bot): @locale.option("sp-hide") # @locale.option("sp-proxy") @locale.option("sp-convert") + @locale.option("sp-linewrap") + @locale.option("sp-linewidth") + async def slash_sp( self, ctx: ApplicationContext, @@ -103,6 +122,8 @@ async def slash_sp( hide: bool = False, # proxy: bool = False, convert: bool = False, + linewrap: bool = False, + linewidth: int = 0 ): await self.sp( ctx, @@ -115,6 +136,8 @@ async def slash_sp( hide, # proxy, convert, + linewrap, + linewidth ) @locale.command("sitelenpona") @@ -139,6 +162,8 @@ async def slash_sitelenpona( hide: bool = False, # proxy: bool = False, convert: bool = False, + linewrap: bool = False, + linewidth: int = 0 ): await self.sp( ctx, @@ -151,6 +176,8 @@ async def slash_sitelenpona( hide, # proxy, convert, + linewrap, + linewidth ) @locale.command("ss") @@ -165,12 +192,15 @@ async def slash_ss( self, ctx: ApplicationContext, text: str, + font: str = "", fontsize: int = 0, color: str = "", bgstyle: str = "", spoiler: bool = False, hide: bool = False, # proxy: bool = False, + linewrap: bool = False, + linewidth: int = 0 ): await self.sp( ctx, @@ -182,6 +212,9 @@ async def slash_ss( spoiler, hide, # proxy, + False, + linewrap, + linewidth ) @locale.command("sitelensitelen") @@ -196,12 +229,15 @@ async def slash_sitelensitelen( self, ctx: ApplicationContext, text: str, + font: str = "", fontsize: int = 0, color: str = "", bgstyle: str = "", spoiler: bool = False, hide: bool = False, # proxy: bool = False, + linewrap: bool = False, + linewidth: int = 0 ): await self.sp( ctx, @@ -214,6 +250,8 @@ async def slash_sitelensitelen( hide, # proxy, False, + linewrap, + linewidth ) async def make_sp_reply( @@ -226,6 +264,8 @@ async def make_sp_reply( bgstyle: str = "", spoiler: bool = False, convert: bool = False, + linewrap: bool = False, + linewidth: int = 0 ) -> tuple[File, list[str]]: user_id = str(ctx.author.id) @@ -236,6 +276,8 @@ async def make_sp_reply( fontsize = await utils.handle_pref_error(ctx, user_id, "fontsize", fontsize) color = await utils.handle_pref_error(ctx, user_id, "color", color) bgstyle = await utils.handle_pref_error(ctx, user_id, "bgstyle", bgstyle) + linewrap = await utils.handle_pref_error(ctx, user_id, "linewrap", linewrap) + linewidth = await utils.handle_pref_error(ctx, user_id, "linewidth", linewidth) # TODO: get channel name or user display name? # we can't stop them from getting smooshed in the render process... text, refs = rm_refs(text) @@ -249,7 +291,7 @@ async def make_sp_reply( text = unescape_newline(text) # desktop alt text has no newlines image = io.BytesIO( - sitelen.display(text, font, fontsize, utils.rgb_tuple(color), bgstyle) + sitelen.display(text, font, fontsize, utils.rgb_tuple(color), bgstyle, linewrap, linewidth) ) filename = text_to_filename(text) + ".png" file = File( @@ -274,6 +316,8 @@ async def sp( spoiler: bool = False, hide: bool = False, convert: bool = False, + linewrap: bool = False, + linewidth: int = 0 ): user_id = str(ctx.author.id) proxy = await utils.handle_pref_error(ctx, user_id, "proxy", False) @@ -289,6 +333,8 @@ async def sp( bgstyle, spoiler, convert, + linewrap, + linewidth ) kwargs["file"] = file From 07ba7ed75156f2bae5bcd2e3f9ee12e10ef1dd7f Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:03:25 +1200 Subject: [PATCH 4/8] Update locale.json --- src/ilo/cogs/sitelen/locale.json | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ilo/cogs/sitelen/locale.json b/src/ilo/cogs/sitelen/locale.json index faff2a9..31ccf8a 100644 --- a/src/ilo/cogs/sitelen/locale.json +++ b/src/ilo/cogs/sitelen/locale.json @@ -9,6 +9,8 @@ "sp-bgstyle": "Select background or outline for your background style.", "sp-convert": "Whether or not to convert text from and to ucsur.", "sp-proxy": "Whether to send an image via the bot or proxying you.", + "sp-linewrap": "Whether to wrap text when image becomes wider than specified line width", + "sp-linewidth": "The width in pixels after which a line gets wrapped if line wrap is enabled", "sitelenpona": "Get sitelen pona of a toki pona phrase", "sitelenpona-text": "The phrase you want to convert to sitelen pona.", @@ -20,6 +22,8 @@ "sitelenpona-bgstyle": "Select background or outline for your background style.", "sitelenpona-convert": "Whether or not to convert text from and to ucsur.", "sitelenpona-proxy": "Whether to send an image via the bot or proxying you.", + "sitelenpona-linewrap": "Whether to wrap text when image becomes wider than specified line width", + "sitelenpona-linewidth": "The width in pixels after which a line gets wrapped if line wrap is enabled", "ss": "Get the sitelen sitelen of a toki pona phrase", "ss-text": "The phrase you want to convert to sitelen sitelen.", @@ -29,6 +33,8 @@ "ss-hide": "Whether to show the image to only you.", "ss-bgstyle": "Select background or outline for your background style.", "ss-proxy": "Whether to send an image via the bot or proxying you.", + "ss-linewrap": "Whether to wrap text when image becomes wider than specified line width", + "ss-linewidth": "The width in pixels after which a line gets wrapped if line wrap is enabled", "sitelensitelen": "Get the sitelen sitelen of a toki pona phrase", "sitelensitelen-text": "The phrase you want to convert to sitelen sitelen.", @@ -38,6 +44,8 @@ "sitelensitelen-hide": "Whether to show the image to only you.", "sitelensitelen-bgstyle": "Select background or outline for your background style.", "sitelensitelen-proxy": "Whether to send an image via the bot or proxying you.", + "sitelensitelen-linewrap": "Whether to wrap text when image becomes wider than specified line width", + "sitelensitelen-linewidth": "The width in pixels after which a line gets wrapped if line wrap is enabled", "prefs-fontsize": "Set the font size that sitelen pona will be displayed as", "prefs-fontsize-fallback": "The fontsize ***{}*** is not in the range 14 to 500. Using the default ***{}***.", @@ -67,5 +75,17 @@ "prefs-proxy-fallback": "The proxy setting ***{}*** is invalid. Using the default ***{}***.", "prefs-proxy-pref-invalid": "The proxy setting ***{}*** is in your preferences but not valid. Using the default ***{}***.", "prefs-proxy-choice-invalid": "The proxy setting ***{}*** is invalid. Did not update preferences.", - "prefs-proxy-option": "Enable or disable proxying." + "prefs-proxy-option": "Enable or disable proxying.", + + "prefs-linewrap": "Set whether to wrap text when image becomes wider than specified line width", + "prefs-linewrap-fallback": "The line wrap setting ***{}*** is invalid. Using the default ***{}***.", + "prefs-linewrap-pref-invalid": "The line wrap setting ***{}*** is in your preferences but not valid. Using the default ***{}***.", + "prefs-linewrap-choice-invalid": "The line wrap setting ***{}*** is invalid. Did not update preferences.", + "prefs-linewrap-option": "Enable or disable line wrapping.", + + "prefs-linewidth": "Set the width in pixels after which a line gets wrapped if line wrap is enabled", + "prefs-linewidth-fallback": "The line width setting ***{}*** is invalid. Using the default ***{}***.", + "prefs-linewidth-pref-invalid": "The line width setting ***{}*** is in your preferences but not valid. Using the default ***{}***.", + "prefs-linewidth-choice-invalid": "The line width setting ***{}*** is invalid. Did not update preferences.", + "prefs-linewidth-option": "Set the width in pixels after which a line gets wrapped if line wrap is enabled" } From 60b8118263fc9d94e47fe63df98f28b2783f4e7e Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:13:58 +1200 Subject: [PATCH 5/8] Update cog_utils.py --- src/ilo/cog_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ilo/cog_utils.py b/src/ilo/cog_utils.py index a29836c..df9ab0a 100644 --- a/src/ilo/cog_utils.py +++ b/src/ilo/cog_utils.py @@ -52,10 +52,11 @@ def is_valid_color(value: str) -> bool: return True return False - def is_valid_bgstyle(style: BgStyle) -> bool: return style in VALID_STYLES +def is_valid_line_width(value: int) -> bool: + return(value >= 1) def load_file(file_path, file_name) -> List[str] | Dict: path = Path(file_path).parent / file_name From ee2bf0f5186cb43641a5879d97c1d47f438635c2 Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:23:00 +1200 Subject: [PATCH 6/8] Update sitelen.py --- src/ilo/sitelen.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ilo/sitelen.py b/src/ilo/sitelen.py index 877a798..60749dd 100644 --- a/src/ilo/sitelen.py +++ b/src/ilo/sitelen.py @@ -90,6 +90,8 @@ def wrap_text(text:str, font: ImageFont.FreeTypeFont, line_width: int): wrapped_text += current_line_segment + "\n" current_line_segment = word wrapped_text += current_line_segment + "\n" + if wrapped_text[-1] == "\n": + wrapped_text = wrapped_text[:-1] return(wrapped_text) # by jan Tepo From 9ad8f867bc90cc874ae2cb235d01dd88f5409e06 Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:33:30 +1200 Subject: [PATCH 7/8] Update cog_utils.py --- src/ilo/cog_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ilo/cog_utils.py b/src/ilo/cog_utils.py index df9ab0a..6adcb7f 100644 --- a/src/ilo/cog_utils.py +++ b/src/ilo/cog_utils.py @@ -56,7 +56,7 @@ def is_valid_bgstyle(style: BgStyle) -> bool: return style in VALID_STYLES def is_valid_line_width(value: int) -> bool: - return(value >= 1) + return(value > 1 and value < 10000) def load_file(file_path, file_name) -> List[str] | Dict: path = Path(file_path).parent / file_name From c8337ce6b33e02c72ded8db859ca4886ab4d4155 Mon Sep 17 00:00:00 2001 From: greybeetle213 <65577606+greybeetle213@users.noreply.github.com> Date: Thu, 9 Apr 2026 22:22:41 +1200 Subject: [PATCH 8/8] o weka e pakala ni: sitelen Lasina la sitelen " " ali li weka --- src/ilo/sitelen.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ilo/sitelen.py b/src/ilo/sitelen.py index 60749dd..7c785d6 100644 --- a/src/ilo/sitelen.py +++ b/src/ilo/sitelen.py @@ -85,6 +85,8 @@ def wrap_text(text:str, font: ImageFont.FreeTypeFont, line_width: int): for word in split_line: newline = current_line_segment + word if font.getlength(newline) < line_width: + if current_line_segment != "" and word not in chars: # add space not first word on line and if not uscur char + current_line_segment += " " current_line_segment += word else: wrapped_text += current_line_segment + "\n"