""" Settings and rendering configuration management. This module handles font size, spacing, and other rendering settings. """ from __future__ import annotations from typing import Dict, Any, Optional from PIL import Image from pyWebLayout.layout.ereader_manager import EreaderLayoutManager from pyWebLayout.style.fonts import BundledFont class SettingsManager: """ Manages font size, spacing, font family, and rendering settings. Responsibilities: - Font scale adjustment - Font family selection (serif, sans-serif, monospace) - Line spacing control - Inter-block spacing control - Word spacing control - Settings persistence helpers """ def __init__(self): """Initialize the settings manager.""" self.font_scale = 1.0 self.font_scale_step = 0.1 # 10% change per step self.font_family: Optional[BundledFont] = None # None = use document default self.manager: Optional[EreaderLayoutManager] = None def set_manager(self, manager: EreaderLayoutManager): """ Set the layout manager to control. Args: manager: EreaderLayoutManager instance to manage settings for """ self.manager = manager self.font_scale = manager.font_scale self.font_family = manager.get_font_family() def set_font_size(self, scale: float) -> Optional[Image.Image]: """ Set the font size scale and re-render current page. Args: scale: Font scale factor (1.0 = normal, 2.0 = double size, 0.5 = half size) Returns: Rendered page with new font size, or None if no manager """ if not self.manager: return None try: self.font_scale = max(0.5, min(3.0, scale)) # Clamp between 0.5x and 3.0x page = self.manager.set_font_scale(self.font_scale) return page.render() if page else None except Exception as e: print(f"Error setting font size: {e}") return None def increase_font_size(self) -> Optional[Image.Image]: """ Increase font size by one step and re-render. Returns: Rendered page with increased font size """ new_scale = self.font_scale + self.font_scale_step return self.set_font_size(new_scale) def decrease_font_size(self) -> Optional[Image.Image]: """ Decrease font size by one step and re-render. Returns: Rendered page with decreased font size """ new_scale = self.font_scale - self.font_scale_step return self.set_font_size(new_scale) def get_font_size(self) -> float: """ Get the current font size scale. Returns: Current font scale factor """ return self.font_scale def set_font_family(self, font_family: Optional[BundledFont]) -> Optional[Image.Image]: """ Set the font family and re-render current page. Args: font_family: BundledFont enum value (SERIF, SANS, MONOSPACE) or None for document default Returns: Rendered page with new font family, or None if no manager """ if not self.manager: return None try: self.font_family = font_family page = self.manager.set_font_family(font_family) return page.render() if page else None except Exception as e: print(f"Error setting font family: {e}") return None def get_font_family(self) -> Optional[BundledFont]: """ Get the current font family. Returns: Current BundledFont or None if using document default """ return self.font_family def set_line_spacing(self, spacing: int) -> Optional[Image.Image]: """ Set line spacing using pyWebLayout's native support. Args: spacing: Line spacing in pixels Returns: Rendered page with new line spacing """ if not self.manager: return None try: # Calculate delta from current spacing current_spacing = self.manager.page_style.line_spacing target_spacing = max(0, spacing) delta = target_spacing - current_spacing # Use pyWebLayout's built-in methods to adjust spacing if delta > 0: self.manager.increase_line_spacing(abs(delta)) elif delta < 0: self.manager.decrease_line_spacing(abs(delta)) # Get re-rendered page page = self.manager.get_current_page() return page.render() if page else None except Exception as e: print(f"Error setting line spacing: {e}") return None def set_inter_block_spacing(self, spacing: int) -> Optional[Image.Image]: """ Set inter-block spacing using pyWebLayout's native support. Args: spacing: Inter-block spacing in pixels Returns: Rendered page with new inter-block spacing """ if not self.manager: return None try: # Calculate delta from current spacing current_spacing = self.manager.page_style.inter_block_spacing target_spacing = max(0, spacing) delta = target_spacing - current_spacing # Use pyWebLayout's built-in methods to adjust spacing if delta > 0: self.manager.increase_inter_block_spacing(abs(delta)) elif delta < 0: self.manager.decrease_inter_block_spacing(abs(delta)) # Get re-rendered page page = self.manager.get_current_page() return page.render() if page else None except Exception as e: print(f"Error setting inter-block spacing: {e}") return None def set_word_spacing(self, spacing: int) -> Optional[Image.Image]: """ Set word spacing using pyWebLayout's native support. Args: spacing: Word spacing in pixels Returns: Rendered page with new word spacing """ if not self.manager: return None try: # Calculate delta from current spacing current_spacing = self.manager.page_style.word_spacing target_spacing = max(0, spacing) delta = target_spacing - current_spacing # Use pyWebLayout's built-in methods to adjust spacing if delta > 0: self.manager.increase_word_spacing(abs(delta)) elif delta < 0: self.manager.decrease_word_spacing(abs(delta)) # Get re-rendered page page = self.manager.get_current_page() return page.render() if page else None except Exception as e: print(f"Error setting word spacing: {e}") return None def get_current_settings(self) -> Dict[str, Any]: """ Get current rendering settings. Returns: Dictionary with all current settings """ if not self.manager: return { 'font_scale': self.font_scale, 'font_family': self.font_family.name if self.font_family else None, 'line_spacing': 5, 'inter_block_spacing': 15, 'word_spacing': 0 } return { 'font_scale': self.font_scale, 'font_family': self.font_family.name if self.font_family else None, 'line_spacing': self.manager.page_style.line_spacing, 'inter_block_spacing': self.manager.page_style.inter_block_spacing, 'word_spacing': self.manager.page_style.word_spacing } def apply_settings(self, settings: Dict[str, Any]) -> bool: """ Apply rendering settings from a settings dictionary. This should be called after loading a book to restore user preferences. Args: settings: Dictionary with settings (font_scale, font_family, line_spacing, etc.) Returns: True if settings applied successfully, False otherwise """ if not self.manager: return False try: # Apply font family font_family_name = settings.get('font_family', None) if font_family_name: try: font_family = BundledFont[font_family_name] if font_family != self.font_family: self.set_font_family(font_family) except KeyError: print(f"Warning: Unknown font family '{font_family_name}', using default") elif font_family_name is None and self.font_family is not None: # Restore to document default self.set_font_family(None) # Apply font scale font_scale = settings.get('font_scale', 1.0) if font_scale != self.font_scale: self.set_font_size(font_scale) # Apply line spacing line_spacing = settings.get('line_spacing', 5) if line_spacing != self.manager.page_style.line_spacing: self.set_line_spacing(line_spacing) # Apply inter-block spacing inter_block_spacing = settings.get('inter_block_spacing', 15) if inter_block_spacing != self.manager.page_style.inter_block_spacing: self.set_inter_block_spacing(inter_block_spacing) # Apply word spacing word_spacing = settings.get('word_spacing', 0) if word_spacing != self.manager.page_style.word_spacing: self.set_word_spacing(word_spacing) return True except Exception as e: print(f"Error applying settings: {e}") return False