import streamlit as st from pptx import Presentation from pptx.enum.shapes import MSO_SHAPE from pptx.enum.shapes import MSO_SHAPE_TYPE from pptx.dml.color import RGBColor from pptx.util import Inches, Pt from pptx.enum.chart import XL_CHART_TYPE from io import BytesIO import base64 import os import requests from PIL import Image import tempfile from visual_elements import ( add_shape_to_slide, add_image_to_slide, analyze_slide_for_visuals, apply_visuals_to_pptx_slide, download_image, get_random_placeholder_image, STANDARD_SHAPES, ICON_CATEGORIES ) def enhanced_create_ppt(slides_content, template_name): """Create an enhanced PowerPoint presentation with visual elements""" from utils import TEMPLATES # Get template info template = TEMPLATES.get(template_name, TEMPLATES["professional"]) # Check if it's a custom template if template_name == "custom" and "custom_template" in st.session_state: # Use the uploaded template prs = load_custom_template() else: # Create a basic presentation prs = Presentation() # Parse template colors for use in slides colors = template["colors"] primary_color = colors.get("primary", "#0F52BA") secondary_color = colors.get("secondary", "#E6E6E6") accent_color = colors.get("accent", "#D4AF37") text_color = colors.get("text", "#333333") # Title slide slide_layout = prs.slide_layouts[0] # Title Slide layout slide = prs.slides.add_slide(slide_layout) # Add title text title = slide.shapes.title if title: title.text = slides_content[0]["title"] # Find subtitle placeholder subtitle = None for shape in slide.placeholders: if shape.placeholder_format.type == 2: # subtitle subtitle = shape break if subtitle: subtitle.text = "Created with SlideGator.AI" # Add logo and branding to title slide logo_shape = slide.shapes.add_shape( MSO_SHAPE.ROUNDED_RECTANGLE, Inches(8.5), Inches(6.5), Inches(1.5), Inches(0.6) ) logo_shape.fill.solid() # Convert hex color to RGB if primary_color.startswith('#'): r = int(primary_color[1:3], 16) g = int(primary_color[3:5], 16) b = int(primary_color[5:7], 16) logo_shape.fill.fore_color.rgb = RGBColor(r, g, b) # Add text to logo shape text_frame = logo_shape.text_frame text_frame.text = "SlideGator.AI" text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255) text_frame.paragraphs[0].font.size = Pt(14) text_frame.paragraphs[0].font.bold = True # Process each content slide for i, slide_content in enumerate(slides_content[1:], 1): # Determine best layout based on content or explicit design layout_type = "standard" if "design" in slide_content and "layout" in slide_content["design"]: layout_type = slide_content["design"]["layout"].lower() # Select appropriate slide layout if "two column" in layout_type or "comparison" in layout_type: slide_layout = prs.slide_layouts[3] if len(prs.slide_layouts) > 3 else prs.slide_layouts[1] elif "title only" in layout_type or "quote" in layout_type: slide_layout = prs.slide_layouts[5] if len(prs.slide_layouts) > 5 else prs.slide_layouts[0] elif "picture" in layout_type: slide_layout = prs.slide_layouts[6] if len(prs.slide_layouts) > 6 else prs.slide_layouts[1] else: slide_layout = prs.slide_layouts[1] # Default: Title and Content # Create the slide slide = prs.slides.add_slide(slide_layout) # Add title title_shape = slide.shapes.title if title_shape: title_shape.text = slide_content["title"] # Apply template styling to title for paragraph in title_shape.text_frame.paragraphs: paragraph.font.size = Pt(28) # Apply accent color to title if specified if "design" in slide_content and "accent_color" in slide_content["design"]: custom_color = slide_content["design"]["accent_color"] if custom_color.startswith('#'): r = int(custom_color[1:3], 16) g = int(custom_color[3:5], 16) b = int(custom_color[5:7], 16) paragraph.font.color.rgb = RGBColor(r, g, b) else: # Use template accent color if accent_color.startswith('#'): r = int(accent_color[1:3], 16) g = int(accent_color[3:5], 16) b = int(accent_color[5:7], 16) paragraph.font.color.rgb = RGBColor(r, g, b) # Get content placeholders content_placeholders = [shape for shape in slide.placeholders if shape.placeholder_format.type != 1] # 1 is title # Add content based on layout and available placeholders if content_placeholders: content_placeholder = content_placeholders[0] # Format content based on what's available and layout type if "content" in slide_content: if isinstance(slide_content["content"], list): if "two column" in layout_type and len(content_placeholders) > 1: # Split content for two columns left_placeholder = content_placeholders[0] right_placeholder = content_placeholders[1] mid_point = len(slide_content["content"]) // 2 left_content = slide_content["content"][:mid_point] right_content = slide_content["content"][mid_point:] # Add content to left column tf = left_placeholder.text_frame tf.clear() for point in left_content: p = tf.add_paragraph() p.text = point p.level = 0 # Add content to right column tf = right_placeholder.text_frame tf.clear() for point in right_content: p = tf.add_paragraph() p.text = point p.level = 0 else: # Standard bullet points tf = content_placeholder.text_frame tf.clear() for point in slide_content["content"]: p = tf.add_paragraph() p.text = point p.level = 0 else: # Plain text content_placeholder.text = slide_content["content"] else: content_placeholder.text = "Content to be added" # Add visual elements if "visuals" in slide_content: visuals = slide_content["visuals"] # Add chart if specified if "chart" in visuals: chart_info = visuals["chart"] chart_type = chart_info.get("type", "column") # Map chart type to XL_CHART_TYPE enum chart_types = { "column": XL_CHART_TYPE.COLUMN_CLUSTERED, "bar": XL_CHART_TYPE.BAR_CLUSTERED, "line": XL_CHART_TYPE.LINE, "pie": XL_CHART_TYPE.PIE, "area": XL_CHART_TYPE.AREA, "scatter": XL_CHART_TYPE.SCATTER } xl_chart_type = chart_types.get(chart_type, XL_CHART_TYPE.COLUMN_CLUSTERED) # Position chart chart_left = Inches(1) chart_top = Inches(2.5) chart_width = Inches(8) chart_height = Inches(3) # Create chart on slide chart = slide.shapes.add_chart( xl_chart_type, chart_left, chart_top, chart_width, chart_height ).chart # Sample data (normally would come from actual data) chart.has_legend = True chart.has_title = True chart.chart_title.text_frame.text = f"{chart_type.capitalize()} Chart" # Set up chart data chart_data = chart.chart_data # Sample data if chart_type == "pie": chart_data.categories = ["Segment 1", "Segment 2", "Segment 3", "Segment 4"] chart_data.series[0].name = "Series 1" chart_data.series[0].values = [4.2, 2.8, 3.7, 5.4] else: chart_data.categories = ["Q1", "Q2", "Q3", "Q4"] chart_data.series[0].name = "2023" chart_data.series[0].values = [4.2, 2.8, 3.7, 5.4] # Add second series series2 = chart_data.series.add() series2.name = "2024" series2.values = [2.7, 3.2, 4.1, 5.0] # Add image if specified if "image" in visuals: image_info = visuals["image"] image_source = image_info.get("source", "") if image_source == "stock" and "url" in image_info: # Download stock image image_url = image_info["url"] try: image_data = download_image(image_url) if image_data: # Position image img_left = Inches(2) img_top = Inches(3) img_width = Inches(6) img_height = Inches(3.5) # Add image to slide slide.shapes.add_picture( image_data, img_left, img_top, img_width, img_height ) except Exception as e: st.error(f"Error adding stock image: {str(e)}") elif image_source == "upload" and "data" in image_info: # Use uploaded image try: # Convert base64 to image img_data = base64.b64decode(image_info["data"]) img_stream = BytesIO(img_data) # Position image img_left = Inches(2) img_top = Inches(3) img_width = Inches(6) img_height = Inches(3.5) # Add image to slide slide.shapes.add_picture( img_stream, img_left, img_top, img_width, img_height ) except Exception as e: st.error(f"Error adding uploaded image: {str(e)}") # Add AI-generated image if specified if "ai_image" in visuals: # In a real implementation, this would use the generated image # For now, use a placeholder try: placeholder_image = get_random_placeholder_image() # Position image img_left = Inches(2) img_top = Inches(3) img_width = Inches(6) img_height = Inches(3.5) # Add image to slide slide.shapes.add_picture( placeholder_image, img_left, img_top, img_width, img_height ) except Exception as e: st.error(f"Error adding AI image placeholder: {str(e)}") # Add icons if specified if "icons" in visuals: icons = visuals["icons"] for i, icon_name in enumerate(icons): # Skip if not a valid shape if icon_name not in STANDARD_SHAPES: continue # Position icons in a row icon_left = Inches(1 + (i * 1.5)) icon_top = Inches(5.5) icon_width = Inches(1) icon_height = Inches(1) # Add icon shape add_shape_to_slide( slide, icon_name, left=icon_left, top=icon_top, width=icon_width, height=icon_height, fill_color=accent_color ) # Apply intelligent design if no explicit visuals are defined if "visuals" not in slide_content: apply_visuals_to_pptx_slide(slide, slide_content, colors) # Add notes if available if "notes" in slide_content and slide_content["notes"]: notes_slide = slide.notes_slide notes_slide.notes_text_frame.text = slide_content["notes"] # Save to BytesIO output = BytesIO() prs.save(output) output.seek(0) return output def load_custom_template(): """Load a custom template from session state""" try: # Read the template from session state template_data = st.session_state.custom_template # Create a BytesIO object template_stream = BytesIO(template_data) # Load the presentation prs = Presentation(template_stream) return prs