feat: implement proper BDF font handling using freetype library

This commit is contained in:
ChuckBuilds
2025-04-23 14:53:44 -05:00
parent 646383c870
commit daea754e83

View File

@@ -6,6 +6,7 @@ import logging
import math import math
from .weather_icons import WeatherIcons from .weather_icons import WeatherIcons
import os import os
import freetype
# Get logger without configuring # Get logger without configuring
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -138,6 +139,30 @@ class DisplayManager:
except Exception as e: except Exception as e:
logger.error(f"Error clearing display: {e}") logger.error(f"Error clearing display: {e}")
def _draw_bdf_text(self, text, x, y, color=(255, 255, 255)):
"""Draw text using BDF font with proper bitmap handling."""
try:
face = freetype.Face(self.calendar_font_path)
for char in text:
face.load_char(char)
bitmap = face.glyph.bitmap
# Draw the character
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))):
self.draw.point((x + j, y + i), fill=color)
# Move to next character
x += face.glyph.advance.x >> 6
except Exception as e:
logger.error(f"Error drawing BDF text: {e}", exc_info=True)
def _load_fonts(self): def _load_fonts(self):
"""Load fonts with proper error handling.""" """Load fonts with proper error handling."""
try: try:
@@ -153,20 +178,19 @@ class DisplayManager:
try: try:
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
relative_font_path = os.path.join(script_dir, "../assets/fonts/tom-thumb.bdf") relative_font_path = os.path.join(script_dir, "../assets/fonts/tom-thumb.bdf")
font_path = os.path.abspath(relative_font_path) self.calendar_font_path = os.path.abspath(relative_font_path)
logger.info(f"Attempting to load tom-thumb font from: {font_path}") logger.info(f"Attempting to load tom-thumb font from: {self.calendar_font_path}")
if not os.path.exists(font_path): if not os.path.exists(self.calendar_font_path):
raise FileNotFoundError(f"Font file not found at {font_path}") raise FileNotFoundError(f"Font file not found at {self.calendar_font_path}")
self.calendar_font = ImageFont.load(font_path) # Load with freetype for proper BDF handling
logger.info(f"tom-thumb calendar font loaded successfully from {font_path}") face = freetype.Face(self.calendar_font_path)
logger.info(f"Calendar font type: {type(self.calendar_font)}") logger.info(f"tom-thumb calendar font loaded successfully from {self.calendar_font_path}")
logger.info(f"Calendar font size: {self.calendar_font.size}") logger.info(f"Calendar font size: {face.size.height >> 6} pixels")
# Verify it's a BDF font # Store the face for later use
if not isinstance(self.calendar_font, ImageFont.BdfFont): self.calendar_font = face
raise TypeError(f"Expected BDF font, got {type(self.calendar_font)}")
except Exception as font_err: except Exception as font_err:
logger.error(f"Failed to load tom-thumb font: {str(font_err)}", exc_info=True) logger.error(f"Failed to load tom-thumb font: {str(font_err)}", exc_info=True)
@@ -221,13 +245,20 @@ class DisplayManager:
# For TTF fonts, use textlength # For TTF fonts, use textlength
text_width = self.draw.textlength(text, font=current_font) text_width = self.draw.textlength(text, font=current_font)
else: else:
# For BDF fonts, use textbbox # For BDF fonts, use freetype to calculate width
bbox = self.draw.textbbox((0, 0), text, font=current_font) face = freetype.Face(self.calendar_font_path)
text_width = bbox[2] - bbox[0] width = 0
for char in text:
face.load_char(char)
width += face.glyph.advance.x >> 6
text_width = width
x = (self.matrix.width - text_width) // 2 x = (self.matrix.width - text_width) // 2
# Draw the text # Draw the text
self.draw.text((x, y), text, font=current_font, fill=color) if isinstance(current_font, freetype.Face):
self._draw_bdf_text(text, x, y, color)
else:
self.draw.text((x, y), text, font=current_font, fill=color)
except Exception as e: except Exception as e:
logger.error(f"Error drawing text: {e}", exc_info=True) logger.error(f"Error drawing text: {e}", exc_info=True)