diff --git a/assets/fonts/dot_digital-7.ttf b/assets/fonts/dot_digital-7.ttf new file mode 100644 index 00000000..2757a91e Binary files /dev/null and b/assets/fonts/dot_digital-7.ttf differ diff --git a/src/of_the_day_manager.py b/src/of_the_day_manager.py index d1377319..78ab328a 100644 --- a/src/of_the_day_manager.py +++ b/src/of_the_day_manager.py @@ -8,7 +8,6 @@ from rgbmatrix import graphics import pytz from src.config_manager import ConfigManager import time -import freetype # Configure logger for this module logger = logging.getLogger(__name__) @@ -37,10 +36,10 @@ class OfTheDayManager: self.last_rotation_time = time.time() self.last_category_rotation_time = time.time() - # Load fonts using freetype + # Load fonts using PIL font_dir = os.path.join(os.path.dirname(__file__), '..', 'assets', 'fonts') - self.title_face = freetype.Face(os.path.join(font_dir, 'ic8x8u.bdf')) - self.body_face = freetype.Face(os.path.join(font_dir, 'cozette.bdf')) + self.title_font = ImageFont.truetype(os.path.join(font_dir, '5by7.regular.ttf'), 7) + self.body_font = ImageFont.truetype(os.path.join(font_dir, 'dot_digital-7.ttf'), 7) # Load categories and their data self.categories = self.of_the_day_config.get('categories', {}) @@ -165,25 +164,6 @@ class OfTheDayManager: logger.debug("OfTheDayManager update interval reached") self.last_update = current_time - def _draw_bdf_text(self, draw, face, text, x, y, color=(255,255,255)): - """Draw text using a BDF font loaded with freetype.""" - orig_x = x - # Calculate baseline offset for proper text alignment - baseline_offset = face.size.height - face.size.ascender - for char in text: - face.load_char(char) - bitmap = face.glyph.bitmap - # Position text using baseline alignment - for i in range(bitmap.rows): - for j in range(bitmap.width): - byte_index = i * bitmap.pitch + (j // 8) - if byte_index < len(bitmap.buffer): - byte = bitmap.buffer[byte_index] - if byte & (1 << (7 - (j % 8))): - draw.point((x + j, y + baseline_offset + i), fill=color) - x += face.glyph.advance.x >> 6 - return x - orig_x - def draw_item(self, category_name, item): try: title = item.get('title', 'No Title') @@ -192,60 +172,49 @@ class OfTheDayManager: draw = ImageDraw.Draw(self.display_manager.image) matrix_width = self.display_manager.matrix.width matrix_height = self.display_manager.matrix.height - title_face = self.title_face - body_face = self.body_face + title_font = self.title_font + body_font = self.body_font - # Get font heights and set consistent positioning - title_height = title_face.height - body_height = body_face.height + # Get font heights + title_height = title_font.getsize('A')[1] + body_height = body_font.getsize('A')[1] - # --- Draw Title (always at top, ic8x8u.bdf) --- + # --- Draw Title (always at top, 5by7.regular.ttf) --- title_y = 0 # Start at top - self._draw_bdf_text(draw, title_face, title, 1, title_y, color=self.title_color) + draw.text((1, title_y), title, font=title_font, fill=self.title_color) - # Calculate title width and actual height for proper spacing - title_width = 0 - max_title_height = 0 - baseline_offset = title_face.size.height - title_face.size.ascender - for c in title: - title_face.load_char(c) - title_width += title_face.glyph.advance.x - # Track the maximum height including baseline offset - bitmap = title_face.glyph.bitmap - actual_height = baseline_offset + bitmap.rows - if actual_height > max_title_height: - max_title_height = actual_height - title_width = title_width // 64 + # Calculate title width for underline + title_width = title_font.getsize(title)[0] - # Underline below title using actual title height - underline_y = max_title_height + 1 # Just below the actual title + # Underline below title + underline_y = title_height + 1 # Just below the title draw.line([(1, underline_y), (1 + title_width, underline_y)], fill=self.title_color, width=1) - # --- Draw Subtitle or Description (rotating, cozette.bdf) --- + # --- Draw Subtitle or Description (rotating, dot_digital-7.ttf) --- # Start subtitle/description below the title with proper spacing - y_start = max_title_height + 3 # Leave space between title and subtitle + y_start = title_height + 3 # Leave space between title and subtitle available_height = matrix_height - y_start available_width = matrix_width - 2 if self.rotation_state == 0 and subtitle: # Show subtitle - wrapped = self._wrap_text(subtitle, available_width, body_face, max_lines=3, line_height=body_height, max_height=available_height) + wrapped = self._wrap_text(subtitle, available_width, body_font, max_lines=3, line_height=body_height, max_height=available_height) for i, line in enumerate(wrapped): if line.strip(): # Only draw non-empty lines - self._draw_bdf_text(draw, body_face, line, 1, y_start + i * body_height, color=self.subtitle_color) + draw.text((1, y_start + i * body_height), line, font=body_font, fill=self.subtitle_color) elif self.rotation_state == 1 and description: # Show description - wrapped = self._wrap_text(description, available_width, body_face, max_lines=3, line_height=body_height, max_height=available_height) + wrapped = self._wrap_text(description, available_width, body_font, max_lines=3, line_height=body_height, max_height=available_height) for i, line in enumerate(wrapped): if line.strip(): # Only draw non-empty lines - self._draw_bdf_text(draw, body_face, line, 1, y_start + i * body_height, color=self.subtitle_color) + draw.text((1, y_start + i * body_height), line, font=body_font, fill=self.subtitle_color) # else: nothing to show return True except Exception as e: logger.error(f"Error drawing 'of the day' item: {e}", exc_info=True) return False - def _wrap_text(self, text, max_width, face, max_lines=3, line_height=8, max_height=24): + def _wrap_text(self, text, max_width, font, max_lines=3, line_height=8, max_height=24): if not text: return [""] lines = [] @@ -253,11 +222,7 @@ class OfTheDayManager: words = text.split() for word in words: test_line = ' '.join(current_line + [word]) if current_line else word - text_width = 0 - for c in test_line: - face.load_char(c) - text_width += face.glyph.advance.x - text_width = text_width // 64 + text_width = font.getsize(test_line)[0] if text_width <= max_width: current_line.append(word) else: @@ -267,11 +232,7 @@ class OfTheDayManager: else: truncated = word while len(truncated) > 0: - test_width = 0 - for c in (truncated + "..."): - face.load_char(c) - test_width += face.glyph.advance.x - test_width = test_width // 64 + test_width = font.getsize(truncated + "...")[0] if test_width <= max_width: lines.append(truncated + "...") break @@ -285,7 +246,7 @@ class OfTheDayManager: while len(lines) < max_lines: lines.append("") return lines[:max_lines] - + def display(self, force_clear=False): if not self.enabled: return