Spaces:
Build error
Build error
| """ | |
| Configuration utilities for loading and managing poster configurations. | |
| This module handles YAML configuration loading, type conversions, and | |
| configuration management for the poster generation pipeline. | |
| """ | |
| import os | |
| import yaml | |
| from typing import Dict, Any, Optional, Tuple | |
| def load_poster_yaml_config(poster_path: str) -> Dict[str, Any]: | |
| """ | |
| Load poster configuration from YAML files. | |
| Searches for poster.yaml in multiple locations: | |
| 1. Next to the poster PDF (per-poster config) | |
| 2. Project config directory | |
| 3. Root directory | |
| Later configs override earlier ones. | |
| Args: | |
| poster_path: Path to the poster PDF file | |
| Returns: | |
| Dictionary containing the merged configuration | |
| """ | |
| cfg = {} | |
| try_paths = [] | |
| # Prefer per-poster YAML next to the PDF | |
| poster_dir = os.path.dirname(poster_path) | |
| try_paths.append(os.path.join(poster_dir, 'poster.yaml')) | |
| # Project-level defaults | |
| try_paths.append(os.path.join('config', 'poster.yaml')) | |
| try_paths.append('poster.yaml') | |
| for p in try_paths: | |
| if os.path.exists(p): | |
| try: | |
| with open(p, 'r', encoding='utf-8') as f: | |
| loaded = yaml.safe_load(f) or {} | |
| if isinstance(loaded, dict): | |
| cfg.update(loaded) | |
| except Exception as e: | |
| print(f"Warning: failed to read YAML config {p}: {e}") | |
| return cfg | |
| def extract_font_sizes(yaml_cfg: Dict[str, Any]) -> Tuple[Optional[int], Optional[int], Optional[int], Optional[int]]: | |
| """ | |
| Extract font size configurations from YAML config. | |
| Supports both flat and nested structures: | |
| - Flat: main_text_font_size, section_title_font_size, etc. | |
| - Nested: text.main_font_size, title.font_size, etc. | |
| Args: | |
| yaml_cfg: YAML configuration dictionary | |
| Returns: | |
| Tuple of (bullet_fs, title_fs, poster_title_fs, poster_author_fs) | |
| """ | |
| bullet_fs = None | |
| title_fs = None | |
| poster_title_fs = None | |
| poster_author_fs = None | |
| if not isinstance(yaml_cfg, dict): | |
| return bullet_fs, title_fs, poster_title_fs, poster_author_fs | |
| # Main/bullet font size | |
| bullet_fs = yaml_cfg.get('main_text_font_size') | |
| text_cfg = yaml_cfg.get('text') if isinstance(yaml_cfg.get('text'), dict) else None | |
| if bullet_fs is None and text_cfg: | |
| bullet_fs = text_cfg.get('main_font_size') or text_cfg.get('main_text_font_size') | |
| # Section title font size | |
| title_fs = yaml_cfg.get('section_title_font_size') | |
| if title_fs is None and text_cfg: | |
| title_fs = text_cfg.get('section_title_font_size') or text_cfg.get('title_font_size') | |
| # Poster header (global) sizes | |
| poster_title_fs = yaml_cfg.get('poster_title_font_size') | |
| poster_author_fs = yaml_cfg.get('poster_author_font_size') | |
| title_cfg = yaml_cfg.get('title') if isinstance(yaml_cfg.get('title'), dict) else None | |
| authors_cfg = yaml_cfg.get('authors') if isinstance(yaml_cfg.get('authors'), dict) else None | |
| if poster_title_fs is None and title_cfg: | |
| poster_title_fs = title_cfg.get('font_size') or title_cfg.get('title_font_size') | |
| if poster_author_fs is None and authors_cfg: | |
| poster_author_fs = authors_cfg.get('font_size') or authors_cfg.get('author_font_size') | |
| return bullet_fs, title_fs, poster_title_fs, poster_author_fs | |
| def extract_colors(yaml_cfg: Dict[str, Any]) -> Tuple[Optional[Tuple], Optional[Tuple], Optional[Tuple], Optional[Tuple]]: | |
| """ | |
| Extract color configurations from YAML config. | |
| Supports both flat and nested structures: | |
| - Flat: title_text_color, main_text_color, etc. | |
| - Nested: colors.title_text, colors.main_text, etc. | |
| Args: | |
| yaml_cfg: YAML configuration dictionary | |
| Returns: | |
| Tuple of (title_text_color, title_fill_color, main_text_color, main_text_fill_color) | |
| """ | |
| title_text_color = None | |
| title_fill_color = None | |
| main_text_color = None | |
| main_text_fill_color = None | |
| if not isinstance(yaml_cfg, dict): | |
| return title_text_color, title_fill_color, main_text_color, main_text_fill_color | |
| # Load flat color configurations | |
| title_text_color = yaml_cfg.get('title_text_color') | |
| title_fill_color = yaml_cfg.get('title_fill_color') | |
| main_text_color = yaml_cfg.get('main_text_color') | |
| main_text_fill_color = yaml_cfg.get('main_text_fill_color') | |
| # Support nested color structure | |
| colors_cfg = yaml_cfg.get('colors') if isinstance(yaml_cfg.get('colors'), dict) else None | |
| if colors_cfg: | |
| if title_text_color is None: | |
| title_text_color = colors_cfg.get('title_text') | |
| if title_fill_color is None: | |
| title_fill_color = colors_cfg.get('title_fill') | |
| if main_text_color is None: | |
| main_text_color = colors_cfg.get('main_text') | |
| if main_text_fill_color is None: | |
| main_text_fill_color = colors_cfg.get('main_fill') | |
| return title_text_color, title_fill_color, main_text_color, main_text_fill_color | |
| def extract_vertical_alignment(yaml_cfg: Dict[str, Any]) -> Optional[str]: | |
| """ | |
| Extract vertical alignment configuration from YAML config. | |
| Args: | |
| yaml_cfg: YAML configuration dictionary | |
| Returns: | |
| Vertical alignment string ("top", "middle", or "bottom") or None | |
| """ | |
| if not isinstance(yaml_cfg, dict): | |
| return None | |
| return yaml_cfg.get('section_title_vertical_align') | |
| def extract_section_title_symbol(yaml_cfg: Dict[str, Any]) -> str: | |
| """ | |
| Extract section title symbol configuration from YAML config. | |
| Args: | |
| yaml_cfg: YAML configuration dictionary | |
| Returns: | |
| Section title symbol string (default: empty string) | |
| """ | |
| if not isinstance(yaml_cfg, dict): | |
| return '' | |
| return yaml_cfg.get('section_title_symbol', '') | |
| def to_int_or_none(value: Any) -> Optional[int]: | |
| """ | |
| Convert value to int or return None if conversion fails. | |
| Args: | |
| value: Value to convert | |
| Returns: | |
| Integer value or None | |
| """ | |
| try: | |
| return int(value) if value is not None else None | |
| except Exception: | |
| return None | |
| def to_color_tuple_or_none(value: Any) -> Optional[Tuple[int, int, int]]: | |
| """ | |
| Convert YAML color list [R, G, B] to tuple (R, G, B). | |
| Args: | |
| value: Color value (list, tuple, or None) | |
| Returns: | |
| RGB tuple or None | |
| """ | |
| if value is None: | |
| return None | |
| if isinstance(value, (list, tuple)) and len(value) == 3: | |
| try: | |
| return tuple(int(c) for c in value) | |
| except (ValueError, TypeError): | |
| return None | |
| return None | |
| def normalize_config_values( | |
| bullet_fs, title_fs, poster_title_fs, poster_author_fs, | |
| title_text_color, title_fill_color, main_text_color, main_text_fill_color | |
| ): | |
| """ | |
| Normalize configuration values to proper types. | |
| Args: | |
| Font sizes and colors (raw from YAML) | |
| Returns: | |
| Tuple of normalized values | |
| """ | |
| bullet_fs = to_int_or_none(bullet_fs) | |
| title_fs = to_int_or_none(title_fs) | |
| poster_title_fs = to_int_or_none(poster_title_fs) | |
| poster_author_fs = to_int_or_none(poster_author_fs) | |
| # Default: use bullet font size for title if not specified | |
| if title_fs is None: | |
| title_fs = bullet_fs | |
| title_text_color = to_color_tuple_or_none(title_text_color) | |
| title_fill_color = to_color_tuple_or_none(title_fill_color) | |
| main_text_color = to_color_tuple_or_none(main_text_color) | |
| main_text_fill_color = to_color_tuple_or_none(main_text_fill_color) | |
| return ( | |
| bullet_fs, title_fs, poster_title_fs, poster_author_fs, | |
| title_text_color, title_fill_color, main_text_color, main_text_fill_color | |
| ) | |