Infographics_Generator_1 / src /template_manager.py
3Stark123's picture
Create src/template_manager.py
a7b9475 verified
"""
Template management for different infographic styles
"""
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from config import Config, TEMPLATE_COLORS
import logging
logger = logging.getLogger(__name__)
@dataclass
class TemplateStyle:
"""Template style configuration"""
name: str
colors: Dict[str, str]
fonts: Dict[str, Tuple[str, int, str]]
spacing: Dict[str, int]
layout_rules: Dict[str, any]
visual_style: Dict[str, any]
class TemplateManager:
"""Manage and apply infographic templates"""
def __init__(self):
"""Initialize template manager with predefined styles"""
self.templates = self._initialize_templates()
logger.info(f"Template manager initialized with {len(self.templates)} templates")
def get_template(self, template_name: str, custom_color: Optional[str] = None) -> TemplateStyle:
"""
Get template configuration
Args:
template_name: Name of the template
custom_color: Optional custom primary color
Returns:
TemplateStyle configuration
"""
template = self.templates.get(template_name, self.templates['Modern'])
# Apply custom color if provided
if custom_color:
template = self._apply_custom_color(template, custom_color)
return template
def apply_template_to_content(self, content: Dict, template_name: str,
custom_color: Optional[str] = None,
layout_type: str = "Vertical") -> Dict:
"""
Apply template styling to content
Args:
content: Analyzed content from TextProcessor and GeminiClient
template_name: Selected template name
custom_color: Optional custom color
layout_type: Layout arrangement
Returns:
Styled content ready for image generation
"""
template = self.get_template(template_name, custom_color)
styled_content = {
'template': template,
'layout_type': layout_type,
'title': self._style_title(content, template),
'sections': self._style_sections(content, template, layout_type),
'visual_elements': self._style_visual_elements(content, template),
'design_specs': self._create_design_specs(template, layout_type),
'color_scheme': template.colors,
'typography': template.fonts
}
return styled_content
def _initialize_templates(self) -> Dict[str, TemplateStyle]:
"""Initialize predefined templates"""
templates = {}
# Modern Template
templates['Modern'] = TemplateStyle(
name='Modern',
colors=TEMPLATE_COLORS['Modern'],
fonts={
'title': ('Arial', 32, 'bold'),
'heading': ('Arial', 24, 'bold'),
'body': ('Arial', 16, 'normal'),
'caption': ('Arial', 14, 'normal')
},
spacing={
'title_margin': 40,
'section_margin': 30,
'line_spacing': 8,
'padding': 60
},
layout_rules={
'max_sections': 6,
'title_position': 'top',
'alignment': 'left',
'use_icons': True,
'use_dividers': True
},
visual_style={
'border_radius': 12,
'shadow': True,
'gradient': False,
'icon_style': 'filled',
'emphasis': 'color'
}
)
# Corporate Template
templates['Corporate'] = TemplateStyle(
name='Corporate',
colors=TEMPLATE_COLORS['Corporate'],
fonts={
'title': ('Arial', 30, 'bold'),
'heading': ('Arial', 22, 'bold'),
'body': ('Arial', 15, 'normal'),
'caption': ('Arial', 13, 'normal')
},
spacing={
'title_margin': 35,
'section_margin': 25,
'line_spacing': 6,
'padding': 50
},
layout_rules={
'max_sections': 8,
'title_position': 'top',
'alignment': 'left',
'use_icons': False,
'use_dividers': True
},
visual_style={
'border_radius': 4,
'shadow': False,
'gradient': False,
'icon_style': 'outline',
'emphasis': 'typography'
}
)
# Creative Template (continued)
templates['Creative'] = TemplateStyle(
name='Creative',
colors=TEMPLATE_COLORS['Creative'],
fonts={
'title': ('Arial', 36, 'bold'),
'heading': ('Arial', 26, 'bold'),
'body': ('Arial', 17, 'normal'),
'caption': ('Arial', 15, 'italic')
},
spacing={
'title_margin': 50,
'section_margin': 35,
'line_spacing': 10,
'padding': 70
},
layout_rules={
'max_sections': 5,
'title_position': 'center',
'alignment': 'center',
'use_icons': True,
'use_dividers': False
},
visual_style={
'border_radius': 20,
'shadow': True,
'gradient': True,
'icon_style': 'colorful',
'emphasis': 'visual'
}
)
# Minimalist Template
templates['Minimalist'] = TemplateStyle(
name='Minimalist',
colors=TEMPLATE_COLORS['Minimalist'],
fonts={
'title': ('Arial', 28, 'normal'),
'heading': ('Arial', 20, 'bold'),
'body': ('Arial', 14, 'normal'),
'caption': ('Arial', 12, 'normal')
},
spacing={
'title_margin': 60,
'section_margin': 40,
'line_spacing': 12,
'padding': 80
},
layout_rules={
'max_sections': 4,
'title_position': 'top',
'alignment': 'left',
'use_icons': False,
'use_dividers': True
},
visual_style={
'border_radius': 0,
'shadow': False,
'gradient': False,
'icon_style': 'minimal',
'emphasis': 'whitespace'
}
)
# Academic Template
templates['Academic'] = TemplateStyle(
name='Academic',
colors=TEMPLATE_COLORS['Academic'],
fonts={
'title': ('Arial', 28, 'bold'),
'heading': ('Arial', 20, 'bold'),
'body': ('Arial', 14, 'normal'),
'caption': ('Arial', 12, 'italic')
},
spacing={
'title_margin': 30,
'section_margin': 20,
'line_spacing': 6,
'padding': 40
},
layout_rules={
'max_sections': 10,
'title_position': 'top',
'alignment': 'justify',
'use_icons': False,
'use_dividers': True
},
visual_style={
'border_radius': 0,
'shadow': False,
'gradient': False,
'icon_style': 'academic',
'emphasis': 'content'
}
)
return templates
def _apply_custom_color(self, template: TemplateStyle, custom_color: str) -> TemplateStyle:
"""Apply custom primary color to template"""
new_colors = template.colors.copy()
new_colors['primary'] = custom_color
# Create a new template with custom color
return TemplateStyle(
name=template.name,
colors=new_colors,
fonts=template.fonts,
spacing=template.spacing,
layout_rules=template.layout_rules,
visual_style=template.visual_style
)
def _style_title(self, content: Dict, template: TemplateStyle) -> Dict:
"""Style the title section"""
# Try to get AI-generated title first
title_text = ""
if 'ai_insights' in content and hasattr(content['ai_insights'], 'get'):
title_text = content['ai_insights'].get('title', '')
# Fallback to first line or generated title
if not title_text:
first_line = content.get('original_text', '').split('\n')[0].strip()
if len(first_line.split()) <= 10:
title_text = first_line
else:
title_text = ' '.join(content.get('original_text', '').split()[:8])
return {
'text': title_text,
'font': template.fonts['title'],
'color': template.colors['primary'],
'alignment': template.layout_rules['title_position'],
'margin': template.spacing['title_margin']
}
def _style_sections(self, content: Dict, template: TemplateStyle, layout_type: str) -> List[Dict]:
"""Style content sections"""
sections = content.get('sections', [])
max_sections = template.layout_rules['max_sections']
# Limit sections based on template
sections = sections[:max_sections]
styled_sections = []
for i, section in enumerate(sections):
styled_section = {
'id': section.get('id', i + 1),
'content': section.get('content', ''),
'condensed_text': section.get('condensed_text', section.get('content', '')[:100]),
'type': section.get('type', 'body'),
'priority': section.get('priority', 5),
'visual_treatment': section.get('visual_treatment', 'bullet'),
'color_suggestion': section.get('color_suggestion', 'primary'),
'styling': self._get_section_styling(section, template, i)
}
styled_sections.append(styled_section)
return styled_sections
def _get_section_styling(self, section: Dict, template: TemplateStyle, index: int) -> Dict:
"""Get styling for individual section"""
section_type = section.get('type', 'body')
if section_type == 'header':
font = template.fonts['heading']
color = template.colors['primary']
elif section_type == 'key_point':
font = template.fonts['body']
color = template.colors['accent']
elif section_type == 'data':
font = template.fonts['heading']
color = template.colors['secondary']
else:
font = template.fonts['body']
color = template.colors['text']
return {
'font': font,
'color': color,
'background_color': self._get_alternating_background(index, template),
'margin': template.spacing['section_margin'],
'padding': 20,
'border_radius': template.visual_style['border_radius'],
'alignment': template.layout_rules['alignment']
}
def _get_alternating_background(self, index: int, template: TemplateStyle) -> str:
"""Get alternating background colors"""
if template.name == 'Minimalist':
return 'transparent'
elif index % 2 == 0:
return template.colors['background']
else:
return self._lighten_color(template.colors['background'], 0.05)
def _lighten_color(self, hex_color: str, factor: float) -> str:
"""Lighten a hex color"""
# Simple color lightening (fallback)
return hex_color # For now, return original color
def _style_visual_elements(self, content: Dict, template: TemplateStyle) -> List[Dict]:
"""Style visual elements like icons and charts"""
visual_elements = content.get('visual_elements', [])
styled_elements = []
for element in visual_elements:
styled_element = {
'type': element.get('type', 'icon'),
'description': element.get('description', ''),
'placement': element.get('placement', 'body'),
'importance': element.get('importance', 5),
'styling': {
'color': template.colors['accent'],
'size': self._get_element_size(element.get('importance', 5)),
'style': template.visual_style.get('icon_style', 'filled')
}
}
styled_elements.append(styled_element)
return styled_elements
def _get_element_size(self, importance: int) -> int:
"""Get element size based on importance"""
size_map = {
1: 16, 2: 20, 3: 24, 4: 28, 5: 32,
6: 36, 7: 40, 8: 44, 9: 48, 10: 52
}
return size_map.get(importance, 32)
def _create_design_specs(self, template: TemplateStyle, layout_type: str) -> Dict:
"""Create comprehensive design specifications"""
return {
'canvas_size': self._get_canvas_size(layout_type),
'margins': {
'top': template.spacing['padding'],
'bottom': template.spacing['padding'],
'left': template.spacing['padding'],
'right': template.spacing['padding']
},
'grid': self._get_grid_specs(layout_type),
'typography': {
'line_height': template.spacing['line_spacing'],
'paragraph_spacing': template.spacing['section_margin']
},
'colors': template.colors,
'visual_effects': {
'shadows': template.visual_style['shadow'],
'gradients': template.visual_style['gradient'],
'border_radius': template.visual_style['border_radius']
},
'layout_rules': template.layout_rules
}
def _get_canvas_size(self, layout_type: str) -> Tuple[int, int]:
"""Get canvas size based on layout"""
sizes = {
'Vertical': (Config.DEFAULT_WIDTH, Config.DEFAULT_HEIGHT),
'Horizontal': (Config.DEFAULT_HEIGHT, Config.DEFAULT_WIDTH),
'Grid': (Config.DEFAULT_WIDTH, Config.DEFAULT_WIDTH),
'Flow': (Config.DEFAULT_WIDTH, int(Config.DEFAULT_HEIGHT * 0.75))
}
return sizes.get(layout_type, (Config.DEFAULT_WIDTH, Config.DEFAULT_HEIGHT))
def _get_grid_specs(self, layout_type: str) -> Dict:
"""Get grid specifications for layout"""
grid_specs = {
'Vertical': {'columns': 1, 'rows': 'auto', 'gap': 30},
'Horizontal': {'columns': 2, 'rows': 'auto', 'gap': 40},
'Grid': {'columns': 2, 'rows': 3, 'gap': 25},
'Flow': {'columns': 'auto', 'rows': 'auto', 'gap': 20}
}
return grid_specs.get(layout_type, grid_specs['Vertical'])
def get_available_templates(self) -> List[str]:
"""Get list of available template names"""
return list(self.templates.keys())
def get_template_preview_data(self, template_name: str) -> Dict:
"""Get template preview information"""
template = self.templates.get(template_name)
if not template:
return {}
return {
'name': template.name,
'colors': template.colors,
'description': self._get_template_description(template.name),
'best_for': self._get_template_use_cases(template.name)
}
def _get_template_description(self, template_name: str) -> str:
"""Get template description"""
descriptions = {
'Modern': 'Clean, contemporary design with vibrant colors and modern typography',
'Corporate': 'Professional business style with conservative colors and clean lines',
'Creative': 'Artistic and expressive with bold colors and creative elements',
'Minimalist': 'Simple, elegant design focusing on whitespace and essential elements',
'Academic': 'Scholarly presentation style optimized for educational content'
}
return descriptions.get(template_name, 'Professional infographic template')
def _get_template_use_cases(self, template_name: str) -> List[str]:
"""Get template use cases"""
use_cases = {
'Modern': ['Social Media', 'Marketing', 'Tech Content', 'Startups'],
'Corporate': ['Business Reports', 'Annual Reports', 'Corporate Communications', 'Finance'],
'Creative': ['Creative Industries', 'Art Projects', 'Entertainment', 'Personal Branding'],
'Minimalist': ['Luxury Brands', 'Architecture', 'Design Portfolios', 'Premium Products'],
'Academic': ['Research Papers', 'Educational Content', 'Scientific Reports', 'Publications']
}
return use_cases.get(template_name, ['General Purpose'])