300 lines
9.8 KiB
Python
300 lines
9.8 KiB
Python
"""
|
|
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
|