Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/ilo/cog_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 and value < 10000)

def load_file(file_path, file_name) -> List[str] | Dict:
path = Path(file_path).parent / file_name
Expand Down
48 changes: 47 additions & 1 deletion src/ilo/cogs/sitelen/cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -115,6 +136,8 @@ async def slash_sp(
hide,
# proxy,
convert,
linewrap,
linewidth
)

@locale.command("sitelenpona")
Expand All @@ -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,
Expand All @@ -151,6 +176,8 @@ async def slash_sitelenpona(
hide,
# proxy,
convert,
linewrap,
linewidth
)

@locale.command("ss")
Expand All @@ -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,
Expand All @@ -182,6 +212,9 @@ async def slash_ss(
spoiler,
hide,
# proxy,
False,
linewrap,
linewidth
)

@locale.command("sitelensitelen")
Expand All @@ -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,
Expand All @@ -214,6 +250,8 @@ async def slash_sitelensitelen(
hide,
# proxy,
False,
linewrap,
linewidth
)

async def make_sp_reply(
Expand All @@ -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)

Expand All @@ -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)
Expand All @@ -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(
Expand All @@ -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)
Expand All @@ -289,6 +333,8 @@ async def sp(
bgstyle,
spoiler,
convert,
linewrap,
linewidth
)

kwargs["file"] = file
Expand Down
22 changes: 21 additions & 1 deletion src/ilo/cogs/sitelen/locale.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand All @@ -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.",
Expand All @@ -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.",
Expand All @@ -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 ***{}***.",
Expand Down Expand Up @@ -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"
}
2 changes: 2 additions & 0 deletions src/ilo/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 35 additions & 2 deletions src/ilo/sitelen.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
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")

BLACK: ColorAlpha = (0x36, 0x39, 0x3F, 0xFF)
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:
Expand Down Expand Up @@ -70,9 +71,33 @@ 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:
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"
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
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)
Expand All @@ -81,6 +106,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),
Expand All @@ -90,6 +119,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),
Expand Down