"""ImageMagick slide renderer for presentation videos.""" import logging import os import subprocess import textwrap logger = logging.getLogger(__name__) # Slide dimensions WIDTH = 1920 HEIGHT = 1080 HEADER_HEIGHT = 120 FOOTER_HEIGHT = 60 FONT = "DejaVu-Sans" FONT_BOLD = "DejaVu-Sans-Bold" def render_slide( heading: str, text: str, bullet_points: list[str], slide_number: int, total_slides: int, module_code: str, output_path: str, ) -> None: """Render a single slide as PNG using ImageMagick.""" cmd = [ "convert", "-size", f"{WIDTH}x{HEIGHT}", "xc:white", # Blue header bar "-fill", "#1e3a5f", "-draw", f"rectangle 0,0 {WIDTH},{HEADER_HEIGHT}", # Header text "-fill", "white", "-font", FONT_BOLD, "-pointsize", "42", "-gravity", "NorthWest", "-annotate", f"+60+{(HEADER_HEIGHT - 42) // 2}", heading[:80], ] y_pos = HEADER_HEIGHT + 40 # Main text if text: wrapped = textwrap.fill(text, width=80) for line in wrapped.split("\n")[:6]: cmd.extend([ "-fill", "#333333", "-font", FONT, "-pointsize", "28", "-gravity", "NorthWest", "-annotate", f"+60+{y_pos}", line, ]) y_pos += 38 y_pos += 20 # Bullet points for bp in bullet_points[:8]: wrapped_bp = textwrap.fill(bp, width=75) first_line = True for line in wrapped_bp.split("\n"): prefix = " • " if first_line else " " cmd.extend([ "-fill", "#444444", "-font", FONT, "-pointsize", "26", "-gravity", "NorthWest", "-annotate", f"+60+{y_pos}", f"{prefix}{line}", ]) y_pos += 34 first_line = False y_pos += 8 # Footer bar cmd.extend([ "-fill", "#f0f0f0", "-draw", f"rectangle 0,{HEIGHT - FOOTER_HEIGHT} {WIDTH},{HEIGHT}", "-fill", "#888888", "-font", FONT, "-pointsize", "20", "-gravity", "SouthWest", "-annotate", f"+60+{(FOOTER_HEIGHT - 20) // 2}", f"{module_code}", "-gravity", "SouthEast", "-annotate", f"+60+{(FOOTER_HEIGHT - 20) // 2}", f"Folie {slide_number}/{total_slides}", ]) cmd.append(output_path) result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if result.returncode != 0: raise RuntimeError(f"ImageMagick failed: {result.stderr}") def render_title_slide( title: str, subtitle: str, output_path: str, ) -> None: """Render a title slide.""" cmd = [ "convert", "-size", f"{WIDTH}x{HEIGHT}", "xc:white", # Full blue background "-fill", "#1e3a5f", "-draw", f"rectangle 0,0 {WIDTH},{HEIGHT}", # Title "-fill", "white", "-font", FONT_BOLD, "-pointsize", "56", "-gravity", "Center", "-annotate", "+0-60", title[:60], # Subtitle "-fill", "#b0c4de", "-font", FONT, "-pointsize", "32", "-gravity", "Center", "-annotate", "+0+40", subtitle[:80], # Footer "-fill", "#6688aa", "-pointsize", "22", "-gravity", "South", "-annotate", "+0+30", "BreakPilot Compliance Training", output_path, ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if result.returncode != 0: raise RuntimeError(f"ImageMagick title slide failed: {result.stderr}")