Slide_gator / enhanced_pptx.py
cryogenic22's picture
Create enhanced_pptx.py
b954154 verified
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