Merge pull request #345 from aceinnolab/hotfix/#326
fix an issue where the text would not be vertically centered
This commit is contained in:
commit
90948d2d29
@ -8,17 +8,17 @@ import logging
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from typing import Tuple
|
||||
|
||||
import arrow
|
||||
import PIL
|
||||
import requests
|
||||
import tzlocal
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
|
||||
logs = logging.getLogger(__name__)
|
||||
logs.setLevel(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(level=logging.INFO)
|
||||
|
||||
# Get the path to the Inkycal folder
|
||||
top_level = "/".join(os.path.dirname(os.path.abspath(os.path.dirname(__file__))).split("/")[:-1])
|
||||
@ -39,7 +39,7 @@ for path, dirs, files in os.walk(fonts_location):
|
||||
if _.endswith(".ttf"):
|
||||
name = _.split(".ttf")[0]
|
||||
fonts[name] = os.path.join(path, _)
|
||||
logs.debug(f"Found fonts: {json.dumps(fonts, indent=4, sort_keys=True)}")
|
||||
logger.debug(f"Found fonts: {json.dumps(fonts, indent=4, sort_keys=True)}")
|
||||
available_fonts = [key for key, values in fonts.items()]
|
||||
|
||||
|
||||
@ -77,16 +77,16 @@ def get_system_tz() -> str:
|
||||
|
||||
>>> import arrow
|
||||
>>> print(arrow.now()) # returns non-timezone-aware time
|
||||
>>> print(arrow.now(tz=get_system_tz()) # prints timezone aware time.
|
||||
>>> print(arrow.now(tz=get_system_tz())) # prints timezone aware time.
|
||||
"""
|
||||
try:
|
||||
local_tz = tzlocal.get_localzone().key
|
||||
logs.debug(f"Local system timezone is {local_tz}.")
|
||||
logger.debug(f"Local system timezone is {local_tz}.")
|
||||
except:
|
||||
logs.error("System timezone could not be parsed!")
|
||||
logs.error("Please set timezone manually!. Falling back to UTC...")
|
||||
logger.error("System timezone could not be parsed!")
|
||||
logger.error("Please set timezone manually!. Falling back to UTC...")
|
||||
local_tz = "UTC"
|
||||
logs.debug(f"The time is {arrow.now(tz=local_tz).format('YYYY-MM-DD HH:mm:ss ZZ')}.")
|
||||
logger.debug(f"The time is {arrow.now(tz=local_tz).format('YYYY-MM-DD HH:mm:ss ZZ')}.")
|
||||
return local_tz
|
||||
|
||||
|
||||
@ -115,7 +115,7 @@ def auto_fontsize(font, max_height):
|
||||
return font
|
||||
|
||||
|
||||
def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
def write(image: Image, xy: Tuple[int, int], box_size: Tuple[int, int], text: str, font=None, **kwargs):
|
||||
"""Writes text on an image.
|
||||
|
||||
Writes given text at given position on the specified image.
|
||||
@ -165,7 +165,7 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
|
||||
while text_width < int(box_width * fill_width) and text_height < int(box_height * fill_height):
|
||||
size += 1
|
||||
@ -173,23 +173,23 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
|
||||
# Truncate text if text is too long, so it can fit inside the box
|
||||
if (text_width, text_height) > (box_width, box_height):
|
||||
logs.debug(("truncating {}".format(text)))
|
||||
logger.debug(("truncating {}".format(text)))
|
||||
while (text_width, text_height) > (box_width, box_height):
|
||||
text = text[0:-1]
|
||||
text_bbox = font.getbbox(text)
|
||||
text_width = text_bbox[2] - text_bbox[0]
|
||||
text_bbox_height = font.getbbox("hg")
|
||||
text_height = text_bbox_height[3] - text_bbox_height[1]
|
||||
logs.debug(text)
|
||||
text_height = abs(text_bbox_height[3]) # - abs(text_bbox_height[1])
|
||||
logger.debug(text)
|
||||
|
||||
# Align text to desired position
|
||||
if alignment == "center" or None:
|
||||
@ -199,10 +199,13 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
elif alignment == "right":
|
||||
x = int(box_width - text_width)
|
||||
|
||||
# Vertical centering
|
||||
y = int((box_height / 2) - (text_height / 2))
|
||||
|
||||
# Draw the text in the text-box
|
||||
draw = ImageDraw.Draw(image)
|
||||
space = Image.new('RGBA', (box_width, box_height))
|
||||
ImageDraw.Draw(space).text((x, 0), text, fill=colour, font=font)
|
||||
ImageDraw.Draw(space).text((x, y), text, fill=colour, font=font)
|
||||
|
||||
# Uncomment following two lines, comment out above two lines to show
|
||||
# red text-box with white text (debugging purposes)
|
||||
@ -217,7 +220,7 @@ def write(image, xy, box_size, text, font=None, **kwargs):
|
||||
image.paste(space, xy, space)
|
||||
|
||||
|
||||
def text_wrap(text, font=None, max_width=None):
|
||||
def text_wrap(text: str, font=None, max_width=None):
|
||||
"""Splits a very long text into smaller parts
|
||||
|
||||
Splits a long text to smaller lines which can fit in a line with max_width.
|
||||
@ -253,7 +256,7 @@ def text_wrap(text, font=None, max_width=None):
|
||||
return lines
|
||||
|
||||
|
||||
def internet_available():
|
||||
def internet_available() -> bool:
|
||||
"""checks if the internet is available.
|
||||
|
||||
Attempts to connect to google.com with a timeout of 5 seconds to check
|
||||
@ -278,15 +281,16 @@ def internet_available():
|
||||
return False
|
||||
|
||||
|
||||
def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
def draw_border(image: Image, xy: Tuple[int, int], size: Tuple[int, int], radius: int = 5, thickness: int = 1,
|
||||
shrinkage: Tuple[int, int] = (0.1, 0.1)) -> None:
|
||||
"""Draws a border at given coordinates.
|
||||
|
||||
Args:
|
||||
- image: The image on which the border should be drawn (usually im_black or
|
||||
im_colour.
|
||||
im_colour).
|
||||
|
||||
- xy: Tuple representing the top-left corner of the border e.g. (32, 100)
|
||||
where 32 is the x co-ordinate and 100 is the y-coordinate.
|
||||
where 32 is the x-coordinate and 100 is the y-coordinate.
|
||||
|
||||
- size: Size of the border as a tuple -> (width, height).
|
||||
|
||||
@ -324,6 +328,7 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
c5, c6 = ((x + width) - diameter, (y + height) - diameter), (x + width, y + height)
|
||||
c7, c8 = (x, (y + height) - diameter), (x + diameter, y + height)
|
||||
|
||||
|
||||
# Draw lines and arcs, creating a square with round corners
|
||||
draw = ImageDraw.Draw(image)
|
||||
draw.line((p1, p2), fill=colour, width=thickness)
|
||||
@ -338,7 +343,7 @@ def draw_border(image, xy, size, radius=5, thickness=1, shrinkage=(0.1, 0.1)):
|
||||
draw.arc((c7, c8), 90, 180, fill=colour, width=thickness)
|
||||
|
||||
|
||||
def draw_border_2(im: PIL.Image, xy: tuple, size: tuple, radius: int):
|
||||
def draw_border_2(im: Image, xy: Tuple[int, int], size: Tuple[int, int], radius: int):
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
x, y = xy
|
||||
|
@ -6,16 +6,16 @@ Copyright by aceinnolab
|
||||
# pylint: disable=logging-fstring-interpolation
|
||||
|
||||
import calendar as cal
|
||||
import arrow
|
||||
from inkycal.modules.template import inkycal_module
|
||||
|
||||
from inkycal.custom import *
|
||||
from inkycal.modules.template import inkycal_module
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Calendar(inkycal_module):
|
||||
"""Calendar class
|
||||
Create monthly calendar and show events from given icalendars
|
||||
Create monthly calendar and show events from given iCalendars
|
||||
"""
|
||||
|
||||
name = "Calendar - Show monthly calendar with events from iCalendars"
|
||||
@ -61,7 +61,7 @@ class Calendar(inkycal_module):
|
||||
self._days_with_events = None
|
||||
|
||||
# optional parameters
|
||||
self.weekstart = config['week_starts_on']
|
||||
self.week_start = config['week_starts_on']
|
||||
self.show_events = config['show_events']
|
||||
self.date_format = config["date_format"]
|
||||
self.time_format = config['time_format']
|
||||
@ -109,7 +109,7 @@ class Calendar(inkycal_module):
|
||||
# Allocate space for month-names, weekdays etc.
|
||||
month_name_height = int(im_height * 0.10)
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
weekdays_height = int((text_bbox_height[3] - text_bbox_height[1])* 1.25)
|
||||
weekdays_height = int((abs(text_bbox_height[3]) + abs(text_bbox_height[1])) * 1.25)
|
||||
logger.debug(f"month_name_height: {month_name_height}")
|
||||
logger.debug(f"weekdays_height: {weekdays_height}")
|
||||
|
||||
@ -156,13 +156,13 @@ class Calendar(inkycal_module):
|
||||
|
||||
now = arrow.now(tz=self.timezone)
|
||||
|
||||
# Set weekstart of calendar to specified weekstart
|
||||
if self.weekstart == "Monday":
|
||||
# Set week-start of calendar to specified week-start
|
||||
if self.week_start == "Monday":
|
||||
cal.setfirstweekday(cal.MONDAY)
|
||||
weekstart = now.shift(days=-now.weekday())
|
||||
week_start = now.shift(days=-now.weekday())
|
||||
else:
|
||||
cal.setfirstweekday(cal.SUNDAY)
|
||||
weekstart = now.shift(days=-now.isoweekday())
|
||||
week_start = now.shift(days=-now.isoweekday())
|
||||
|
||||
# Write the name of current month
|
||||
write(
|
||||
@ -174,9 +174,9 @@ class Calendar(inkycal_module):
|
||||
autofit=True,
|
||||
)
|
||||
|
||||
# Set up weeknames in local language and add to main section
|
||||
# Set up week-names in local language and add to main section
|
||||
weekday_names = [
|
||||
weekstart.shift(days=+_).format('ddd', locale=self.language)
|
||||
week_start.shift(days=+_).format('ddd', locale=self.language)
|
||||
for _ in range(7)
|
||||
]
|
||||
logger.debug(f'weekday names: {weekday_names}')
|
||||
@ -192,7 +192,7 @@ class Calendar(inkycal_module):
|
||||
fill_height=0.9,
|
||||
)
|
||||
|
||||
# Create a calendar template and flatten (remove nestings)
|
||||
# Create a calendar template and flatten (remove nesting)
|
||||
calendar_flat = self.flatten(cal.monthcalendar(now.year, now.month))
|
||||
# logger.debug(f" calendar_flat: {calendar_flat}")
|
||||
|
||||
@ -281,7 +281,7 @@ class Calendar(inkycal_module):
|
||||
month_start = arrow.get(now.floor('month'))
|
||||
month_end = arrow.get(now.ceil('month'))
|
||||
|
||||
# fetch events from given icalendars
|
||||
# fetch events from given iCalendars
|
||||
self.ical = iCalendar()
|
||||
parser = self.ical
|
||||
|
||||
@ -300,8 +300,6 @@ class Calendar(inkycal_module):
|
||||
|
||||
# Handle multi-day events by adding all days between start and end
|
||||
for event in month_events:
|
||||
start_date = event['begin'].date()
|
||||
end_date = event['end'].date()
|
||||
|
||||
# Convert start and end dates to arrow objects with timezone
|
||||
start = arrow.get(event['begin'].date(), tzinfo=self.timezone)
|
||||
@ -325,8 +323,6 @@ class Calendar(inkycal_module):
|
||||
grid[days],
|
||||
(icon_width, icon_height),
|
||||
radius=6,
|
||||
thickness=1,
|
||||
shrinkage=(0.4, 0.2),
|
||||
)
|
||||
|
||||
# Filter upcoming events until 4 weeks in the future
|
||||
@ -345,12 +341,12 @@ class Calendar(inkycal_module):
|
||||
|
||||
date_width = int(max((
|
||||
self.font.getlength(events['begin'].format(self.date_format, locale=lang))
|
||||
for events in upcoming_events))* 1.1
|
||||
for events in upcoming_events)) * 1.1
|
||||
)
|
||||
|
||||
time_width = int(max((
|
||||
self.font.getlength(events['begin'].format(self.time_format, locale=lang))
|
||||
for events in upcoming_events))* 1.1
|
||||
for events in upcoming_events)) * 1.1
|
||||
)
|
||||
|
||||
text_bbox_height = self.font.getbbox("hg")
|
||||
@ -369,7 +365,8 @@ class Calendar(inkycal_module):
|
||||
event_duration = (event['end'] - event['begin']).days
|
||||
if event_duration > 1:
|
||||
# Format the duration using Arrow's localization
|
||||
days_translation = arrow.get().shift(days=event_duration).humanize(only_distance=True, locale=lang)
|
||||
days_translation = arrow.get().shift(days=event_duration).humanize(only_distance=True,
|
||||
locale=lang)
|
||||
the_name = f"{event['title']} ({days_translation})"
|
||||
else:
|
||||
the_name = event['title']
|
||||
|
@ -1,12 +1,22 @@
|
||||
"""
|
||||
Test the functions in the functions module.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from PIL import Image, ImageFont
|
||||
from inkycal.custom import write, fonts
|
||||
|
||||
from inkycal.custom import write, fonts, get_system_tz
|
||||
|
||||
|
||||
def test_write():
|
||||
class TestIcalendar(unittest.TestCase):
|
||||
|
||||
def test_write(self):
|
||||
im = Image.new("RGB", (500, 200), "white")
|
||||
font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'], size = 40)
|
||||
write(im, (125,75), (250, 50), "Hello World", font)
|
||||
font = ImageFont.truetype(fonts['NotoSans-SemiCondensed'], size=40)
|
||||
write(im, (125, 75), (250, 50), "Hello World", font)
|
||||
# im.show()
|
||||
|
||||
def test_get_system_tz(self):
|
||||
tz = get_system_tz()
|
||||
assert isinstance(tz, str)
|
||||
|
||||
|
@ -20,7 +20,7 @@ tests = [
|
||||
{
|
||||
"name": "Calendar",
|
||||
"config": {
|
||||
"size": [500, 500],
|
||||
"size": [500, 600],
|
||||
"week_starts_on": "Monday",
|
||||
"show_events": True,
|
||||
"ical_urls": sample_url,
|
||||
|
Loading…
Reference in New Issue
Block a user