import os import time import freetype from PIL import Image, ImageDraw, ImageFont import logging from typing import Dict, Any from src.display_manager import DisplayManager # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class FontTestManager: """Manager for testing fonts with easy BDF/TTF switching.""" def __init__(self, config: Dict[str, Any], display_manager: DisplayManager): self.display_manager = display_manager self.config = config self.logger = logging.getLogger('FontTest') # FONT CONFIGURATION - EASY SWITCHING # Set to 'bdf' or 'ttf' to switch font types self.font_type = 'bdf' # Change this to 'ttf' to use TTF font # Font configurations self.font_configs = { 'bdf': { 'path': "assets/fonts/ic8x8u.bdf", 'display_name': "5x7 BDF", 'description': "5x7 BDF font" }, 'ttf': { 'path': "assets/fonts/5by7.regular.ttf", 'display_name': "5x7 Regular TTF", 'description': "5x7 regular TTF font" } } # Get current font configuration self.current_config = self.font_configs[self.font_type] self.font_path = self.current_config['path'] # Verify font exists if not os.path.exists(self.font_path): self.logger.error(f"Font file not found: {self.font_path}") raise FileNotFoundError(f"Font file not found: {self.font_path}") # Load the font based on type if self.font_type == 'bdf': self._load_bdf_font() else: self._load_ttf_font() self.logger.info(f"Initialized FontTestManager with {self.current_config['description']}") def _load_bdf_font(self): """Load BDF font using freetype.""" try: self.face = freetype.Face(self.font_path) self.logger.info(f"Successfully loaded BDF font from {self.font_path}") except Exception as e: self.logger.error(f"Failed to load BDF font: {e}") raise def _load_ttf_font(self): """Load TTF font using PIL.""" try: self.font = ImageFont.truetype(self.font_path, 8) # Size 8 for 5x7 font self.logger.info(f"Successfully loaded TTF font from {self.font_path}") except Exception as e: self.logger.error(f"Failed to load TTF font: {e}") raise def update(self): """No update needed for static display.""" pass def display(self, force_clear: bool = False): """Display the font with sample text.""" try: # Clear the display self.display_manager.clear() # Get display dimensions width = self.display_manager.matrix.width height = self.display_manager.matrix.height # Draw font name at the top self.display_manager.draw_text(self.current_config['display_name'], y=2, color=(255, 255, 255)) # Draw sample text draw = ImageDraw.Draw(self.display_manager.image) sample_text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # Calculate starting position x = 10 # Start 10 pixels from the left y = 10 # Start 10 pixels from the top # Draw text based on font type if self.font_type == 'bdf': self._draw_bdf_text(draw, sample_text, x, y) else: self._draw_ttf_text(draw, sample_text, x, y) # Update the display once self.display_manager.update_display() # Log that display is complete self.logger.info("Font test display complete.") except Exception as e: self.logger.error(f"Error displaying font test: {e}", exc_info=True) def _draw_bdf_text(self, draw, text, x, y): """Draw text using BDF font.""" for char in text: # Load the glyph self.face.load_char(char) bitmap = self.face.glyph.bitmap # Draw the glyph for i in range(bitmap.rows): for j in range(bitmap.width): try: # Get the byte containing the pixel byte_index = i * bitmap.pitch + (j // 8) if byte_index < len(bitmap.buffer): byte = bitmap.buffer[byte_index] # Check if the specific bit is set if byte & (1 << (7 - (j % 8))): draw.point((x + j, y + i), fill=(255, 255, 255)) except IndexError: self.logger.warning(f"Index out of range for char '{char}' at position ({i}, {j})") continue # Move to next character position x += self.face.glyph.advance.x >> 6 def _draw_ttf_text(self, draw, text, x, y): """Draw text using TTF font.""" draw.text((x, y), text, font=self.font, fill=(255, 255, 255))