File size: 23,733 Bytes
6a0a931
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96815db
6a0a931
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
import os
import requests
import streamlit as st
from io import BytesIO
from PIL import Image
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
import random
import json
import base64

# Define standard icons and shapes that come with python-pptx
STANDARD_SHAPES = {
    "rectangle": MSO_SHAPE.RECTANGLE,
    "rounded_rectangle": MSO_SHAPE.ROUNDED_RECTANGLE,
    "oval": MSO_SHAPE.OVAL,
    "triangle": MSO_SHAPE.ISOSCELES_TRIANGLE,
    "right_triangle": MSO_SHAPE.RIGHT_TRIANGLE,
    "diamond": MSO_SHAPE.DIAMOND,
    "pentagon": MSO_SHAPE.PENTAGON,
    "hexagon": MSO_SHAPE.HEXAGON,
    "heptagon": MSO_SHAPE.HEPTAGON,
    "octagon": MSO_SHAPE.OCTAGON,
    "star": MSO_SHAPE.STAR_5_POINT,
    "arrow": MSO_SHAPE.RIGHT_ARROW,
    "up_arrow": MSO_SHAPE.UP_ARROW,
    "down_arrow": MSO_SHAPE.DOWN_ARROW,
    "left_arrow": MSO_SHAPE.LEFT_ARROW,
    "curved_arrow": MSO_SHAPE.CURVED_RIGHT_ARROW,
    "curved_up_arrow": MSO_SHAPE.CURVED_UP_ARROW,
    "curved_down_arrow": MSO_SHAPE.CURVED_DOWN_ARROW,
    "curved_left_arrow": MSO_SHAPE.CURVED_LEFT_ARROW,
    "smiley_face": MSO_SHAPE.SMILEY_FACE,
    "heart": MSO_SHAPE.HEART,
    "lightning_bolt": MSO_SHAPE.LIGHTNING_BOLT,
    "sun": MSO_SHAPE.SUN,
    "moon": MSO_SHAPE.MOON,
    "cloud": MSO_SHAPE.CLOUD,
    "arc": MSO_SHAPE.ARC,
    "bracket": MSO_SHAPE.LEFT_BRACKET,
    "brace": MSO_SHAPE.LEFT_BRACE,
    "can": MSO_SHAPE.CAN,
    "cube": MSO_SHAPE.CUBE,
    "gear": MSO_SHAPE.GEAR_6,
    "donut": MSO_SHAPE.DONUT,
    "chart": MSO_SHAPE.PIE,
    "plus": MSO_SHAPE.MATH_PLUS,
    "minus": MSO_SHAPE.MATH_MINUS,
    "multiply": MSO_SHAPE.MATH_MULTIPLY,
    "divide": MSO_SHAPE.MATH_DIVIDE,
    "equal": MSO_SHAPE.MATH_EQUAL,
    "not_equal": MSO_SHAPE.MATH_NOT_EQUAL
}

# Stock icon categories with predefined colors
ICON_CATEGORIES = {
    "business": ["chart", "gear", "cube", "can", "plus", "minus", "equal"],
    "arrows": ["arrow", "up_arrow", "down_arrow", "left_arrow", "curved_arrow"],
    "basic": ["rectangle", "oval", "triangle", "diamond", "star"],
    "nature": ["sun", "moon", "cloud", "lightning_bolt"],
    "emotion": ["smiley_face", "heart"],
    "math": ["plus", "minus", "multiply", "divide", "equal", "not_equal"]
}

# Local image library path
IMAGE_LIBRARY_PATH = "images"
os.makedirs(IMAGE_LIBRARY_PATH, exist_ok=True)

# Pexels API for stock images (need to get a free API key)
PEXELS_API_KEY = os.getenv("PEXELS_API_KEY", "")

def search_stock_images(query, per_page=10):
    """Search for stock images using Pexels API"""
    if not PEXELS_API_KEY:
        st.warning("Pexels API key not set. Using placeholder images.")
        return [{"url": f"https://via.placeholder.com/800x600?text=Placeholder+{i}", 
                 "thumbnail": f"https://via.placeholder.com/100x100?text=Thumbnail+{i}"} 
                for i in range(1, 6)]
    
    try:
        headers = {"Authorization": PEXELS_API_KEY}
        response = requests.get(
            f"https://api.pexels.com/v1/search?query={query}&per_page={per_page}",
            headers=headers
        )
        
        if response.status_code == 200:
            data = response.json()
            return [{"url": photo["src"]["large"], 
                     "thumbnail": photo["src"]["tiny"]} 
                    for photo in data.get("photos", [])]
        else:
            st.error(f"Error fetching stock images: {response.status_code}")
            return []
    except Exception as e:
        st.error(f"Error in stock image search: {str(e)}")
        return []

def download_image(url, filename=None):
    """Download image from URL and save to local library"""
    try:
        response = requests.get(url)
        if response.status_code == 200:
            image_data = BytesIO(response.content)
            
            if filename:
                filepath = os.path.join(IMAGE_LIBRARY_PATH, filename)
                with open(filepath, 'wb') as f:
                    f.write(response.content)
                return filepath
            else:
                return image_data
        else:
            st.error(f"Error downloading image: {response.status_code}")
            return None
    except Exception as e:
        st.error(f"Error downloading image: {str(e)}")
        return None

def get_icon_suggestions(slide_content):
    """Get icon suggestions based on slide content"""
    content_text = ""
    if isinstance(slide_content.get('content', []), list):
        content_text = " ".join(slide_content.get('content', []))
    else:
        content_text = str(slide_content.get('content', ''))
    
    keywords = {
        "growth": ["up_arrow", "chart"],
        "decline": ["down_arrow"],
        "innovation": ["gear", "lightning_bolt"],
        "success": ["star", "smiley_face"],
        "challenge": ["triangle"],
        "balance": ["equal"],
        "addition": ["plus"],
        "subtraction": ["minus"],
        "division": ["divide"],
        "partnership": ["heart"],
        "business": ["cube", "can"],
        "nature": ["sun", "cloud"],
        "direction": ["arrow", "curved_arrow"]
    }
    
    suggested_icons = []
    for keyword, icons in keywords.items():
        if keyword.lower() in content_text.lower():
            suggested_icons.extend(icons)
    
    # If no specific matches, return some general business icons
    if not suggested_icons:
        suggested_icons = random.sample(ICON_CATEGORIES["business"], min(3, len(ICON_CATEGORIES["business"])))
    
    # Deduplicate
    return list(set(suggested_icons))

def get_shape_by_name(shape_name):
    """Get shape enum from name"""
    return STANDARD_SHAPES.get(shape_name.lower(), MSO_SHAPE.RECTANGLE)

def add_shape_to_slide(slide, shape_name, left=Inches(1), top=Inches(1), width=Inches(1), height=Inches(1), fill_color=None):
    """Add a shape to a slide"""
    shape_type = get_shape_by_name(shape_name)
    shape = slide.shapes.add_shape(shape_type, left, top, width, height)
    
    if fill_color:
        # Convert hex color to RGB
        if fill_color.startswith('#'):
            r = int(fill_color[1:3], 16)
            g = int(fill_color[3:5], 16)
            b = int(fill_color[5:7], 16)
            shape.fill.solid()
            shape.fill.fore_color.rgb = RGBColor(r, g, b)
    
    return shape

def add_image_to_slide(slide, image_stream, left=Inches(1), top=Inches(1), width=Inches(4), height=Inches(3)):
    """Add an image to a slide"""
    try:
        picture = slide.shapes.add_picture(image_stream, left, top, width, height)
        return picture
    except Exception as e:
        st.error(f"Error adding image to slide: {str(e)}")
        return None

def create_chart_based_on_content(slide, chart_type, data, left=Inches(1), top=Inches(1), width=Inches(6), height=Inches(4)):
    """Create a chart based on the slide content"""
    try:
        # Basic chart types supported by python-pptx
        chart_types = {
            "bar": 1,  # MSO_CHART_TYPE.BAR_CLUSTERED
            "column": 0,  # MSO_CHART_TYPE.COLUMN_CLUSTERED
            "line": 4,  # MSO_CHART_TYPE.LINE
            "pie": 5,  # MSO_CHART_TYPE.PIE
            "scatter": 15,  # MSO_CHART_TYPE.SCATTER
            "area": 8,  # MSO_CHART_TYPE.AREA
        }
        
        chart_type_id = chart_types.get(chart_type.lower(), 0)  # Default to column chart
        
        # Create sample data if not provided
        if not data:
            data = {
                "categories": ["Category 1", "Category 2", "Category 3", "Category 4"],
                "series": [
                    {
                        "name": "Series 1",
                        "values": [4.3, 2.5, 3.5, 4.5]
                    },
                    {
                        "name": "Series 2",
                        "values": [2.4, 4.4, 1.8, 2.8]
                    }
                ]
            }
        
        # Add chart to slide
        chart = slide.shapes.add_chart(
            chart_type_id, 
            left, top, width, height
        ).chart
        
        # Set chart data
        chart_data = chart.chart_data
        
        # Set categories
        chart_data.categories = data.get("categories", ["Category 1", "Category 2", "Category 3"])
        
        # Add series
        for i, series in enumerate(data.get("series", [])):
            if i == 0:
                # First series already exists
                chart_data.series[0].name = series.get("name", f"Series {i+1}")
                chart_data.series[0].values = series.get("values", [1, 2, 3])
            else:
                # Add additional series
                new_series = chart_data.series.add()
                new_series.name = series.get("name", f"Series {i+1}")
                new_series.values = series.get("values", [1, 2, 3])
        
        return chart
    except Exception as e:
        st.error(f"Error creating chart: {str(e)}")
        return None

def analyze_slide_for_visuals(slide_content):
    """Analyze slide content to suggest appropriate visuals"""
    content_text = ""
    if isinstance(slide_content.get('content', []), list):
        content_text = " ".join(slide_content.get('content', []))
    else:
        content_text = str(slide_content.get('content', ''))
    
    title_text = slide_content.get('title', '')
    purpose = slide_content.get('purpose', '')
    
    # Analyze for data visualization needs
    data_viz_keywords = [
        'compare', 'comparison', 'versus', 'vs', 
        'trend', 'growth', 'decline', 'increase', 'decrease',
        'percentage', 'proportion', 'distribution',
        'data', 'statistics', 'numbers', 'metrics', 'kpi'
    ]
    
    # Chart type suggestions based on content
    chart_suggestions = {
        'comparison': 'bar',
        'trend': 'line',
        'growth': 'line',
        'decline': 'line',
        'percentage': 'pie',
        'proportion': 'pie',
        'distribution': 'column',
        'correlation': 'scatter',
    }
    
    need_chart = False
    suggested_chart_type = 'column'
    
    for keyword in data_viz_keywords:
        if keyword.lower() in content_text.lower() or keyword.lower() in title_text.lower():
            need_chart = True
            # Find most appropriate chart type
            for chart_keyword, chart_type in chart_suggestions.items():
                if chart_keyword.lower() in content_text.lower() or chart_keyword.lower() in title_text.lower():
                    suggested_chart_type = chart_type
                    break
    
    # Analyze for image needs
    image_keywords = [
        'show', 'display', 'picture', 'image', 'photo', 'visual',
        'illustration', 'diagram', 'screenshot', 'graphic'
    ]
    
    need_image = False
    image_search_query = ""
    
    for keyword in image_keywords:
        if keyword.lower() in content_text.lower() or keyword.lower() in purpose.lower():
            need_image = True
            # Try to extract the subject of the image
            text_parts = content_text.split()
            keyword_index = -1
            
            for i, part in enumerate(text_parts):
                if keyword.lower() in part.lower():
                    keyword_index = i
                    break
            
            if keyword_index != -1 and keyword_index < len(text_parts) - 1:
                # Take the next few words as the image subject
                image_search_query = " ".join(text_parts[keyword_index+1:keyword_index+4])
            else:
                # Use the title as a fallback
                image_search_query = title_text
    
    # If no specific image query was found, use title
    if not image_search_query:
        image_search_query = title_text
    
    # Analyze for icon needs
    icon_suggestions = get_icon_suggestions(slide_content)
    
    # Analyze layout needs
    layout_keywords = {
        'compare': 'Two Column',
        'versus': 'Two Column',
        'vs': 'Two Column',
        'quote': 'Quote',
        'testimonial': 'Quote',
        'picture': 'Picture with Caption',
        'image': 'Picture with Caption',
        'photo': 'Picture with Caption',
        'overview': 'Title Only',
        'list': 'Standard'
    }
    
    suggested_layout = 'Standard'
    for keyword, layout in layout_keywords.items():
        if (keyword.lower() in content_text.lower() or 
            keyword.lower() in title_text.lower() or 
            keyword.lower() in purpose.lower()):
            suggested_layout = layout
            break
    
    return {
        'need_chart': need_chart,
        'chart_type': suggested_chart_type,
        'need_image': need_image,
        'image_query': image_search_query,
        'icon_suggestions': icon_suggestions,
        'suggested_layout': suggested_layout
    }

def get_random_placeholder_image():
    """Generate a random placeholder image"""
    width = random.randint(800, 1200)
    height = random.randint(600, 800)
    image_url = f"https://via.placeholder.com/{width}x{height}?text=SlideGator+Placeholder"
    return download_image(image_url)

def get_default_icon_set():
    """Return a set of default icons for common topics"""
    return {
        "business": ["cube", "gear", "chart"],
        "growth": ["up_arrow", "chart"],
        "teamwork": ["heart", "smiley_face"],
        "innovation": ["lightning_bolt", "star"],
        "nature": ["sun", "cloud"],
        "time": ["clock", "hourglass"]
    }

def generate_html_preview_with_visuals(slide, template_name):
    """Generate HTML preview that includes visual representations of charts, icons, images"""
    from utils import TEMPLATES
    
    # Get template info
    template = TEMPLATES.get(template_name, TEMPLATES["professional"])
    colors = template["colors"]
    fonts = template["fonts"]
    
    # Prepare content
    title = slide.get('title', 'Untitled Slide')
    
    if isinstance(slide.get('content', []), list):
        content = slide.get('content', [])
    else:
        content_text = slide.get('content', '')
        content = content_text.split('\n') if content_text else []
    
    # Determine layout based on slide design or content
    layout_type = "standard"
    if "design" in slide and "layout" in slide["design"]:
        layout_type = slide["design"]["layout"].lower()
    
    # Check for visual elements
    visual_elements = slide.get('visual_elements', [])
    if isinstance(visual_elements, str):
        visual_elements = [visual_elements]
    
    # Analyze what visuals would be appropriate
    visual_analysis = analyze_slide_for_visuals(slide)
    
    # Prepare visual HTML
    visual_html = ""
    
    # Handle charts if needed
    if visual_analysis['need_chart'] or any('chart' in str(ve).lower() for ve in visual_elements):
        chart_type = visual_analysis['chart_type']
        chart_html = f"""
        <div class="chart-container" style="height: 150px; margin-bottom: 15px; border: 1px dashed {colors['accent']}; text-align: center; line-height: 150px;">
            <div style="display: inline-block; vertical-align: middle;">
                <div style="font-weight: bold;">{chart_type.capitalize()} Chart</div>
                <div style="font-size: 12px; color: {colors['accent']};">Based on slide content analysis</div>
            </div>
        </div>
        """
        visual_html += chart_html
    
    # Handle images if needed
    if visual_analysis['need_image'] or any('image' in str(ve).lower() for ve in visual_elements):
        image_query = visual_analysis['image_query']
        image_html = f"""
        <div class="image-container" style="height: 150px; margin-bottom: 15px; border: 1px dashed {colors['accent']}; text-align: center; line-height: 150px;">
            <div style="display: inline-block; vertical-align: middle;">
                <div style="font-weight: bold;">Image: "{image_query}"</div>
                <div style="font-size: 12px; color: {colors['accent']};">Click 'Add Image' to select</div>
            </div>
        </div>
        """
        visual_html += image_html
    
    # Handle icons if needed
    if visual_analysis['icon_suggestions']:
        icons = visual_analysis['icon_suggestions']
        icon_html = f"""
        <div class="icon-container" style="margin-bottom: 15px; text-align: center;">
            <div style="display: inline-block; margin: 0 5px; font-size: 24px;">{"βš™οΈ"}</div>
            <div style="display: inline-block; margin: 0 5px; font-size: 24px;">{"πŸ“ˆ"}</div>
            <div style="display: inline-block; margin: 0 5px; font-size: 24px;">{"πŸ”"}</div>
        </div>
        """
        visual_html += icon_html
    
    # Generate HTML based on layout type
    if "two column" in layout_type or "comparison" in layout_type:
        # Two-column layout
        mid_point = len(content) // 2
        left_content = content[:mid_point]
        right_content = content[mid_point:]
        
        content_html = f"""
        <div class="row" style="display: flex; flex-direction: row;">
            <div class="col" style="flex: 1; padding-right: 10px;">
                <ul>
                    {"".join(f'<li style="margin-bottom: 8px;">{item}</li>' for item in left_content)}
                </ul>
            </div>
            <div class="col" style="flex: 1; padding-left: 10px;">
                <ul>
                    {"".join(f'<li style="margin-bottom: 8px;">{item}</li>' for item in right_content)}
                </ul>
            </div>
        </div>
        """
    elif "quote" in layout_type:
        # Quote layout
        if content:
            quote_text = content[0] if isinstance(content, list) and len(content) > 0 else "Quote text"
            author = content[1] if isinstance(content, list) and len(content) > 1 else ""
            
            content_html = f"""
            <div class="quote-container" style="text-align: center; padding: 20px;">
                <blockquote style="font-size: 24px; font-style: italic; color: {colors['accent']};">
                    "{quote_text}"
                </blockquote>
                {f'<div style="text-align: right; font-size: 18px;">β€” {author}</div>' if author else ''}
            </div>
            """
        else:
            content_html = '<div class="quote-container" style="text-align: center;"><p>No quote content</p></div>'
    elif "picture" in layout_type:
        # Picture with caption layout
        caption = content[0] if content else "Image caption"
        
        content_html = f"""
        <div class="picture-container" style="text-align: center;">
            <div class="image-placeholder" style="height: 150px; border: 1px dashed {colors['accent']}; margin-bottom: 10px; line-height: 150px;">
                <span>Image Area</span>
            </div>
            <div class="caption" style="font-style: italic; color: {colors['text']};">{caption}</div>
        </div>
        """
    else:
        # Standard layout with bullet points
        content_html = f"""
        <ul>
            {"".join(f'<li style="margin-bottom: 8px;">{item}</li>' for item in content)}
        </ul>
        """
    
    # Create the full HTML preview
    html = f"""
    <div class="slide-preview" style="background-color: {colors['secondary']}; border: 1px solid #dee2e6; border-radius: 4px; padding: 20px; height: 300px; overflow: auto; font-family: {fonts['body']};">
        <div class="slide-title" style="color: {colors['text']}; font-family: {fonts['title']}; font-size: 24px; margin-bottom: 20px; border-bottom: 2px solid {colors['primary']}; padding-bottom: 10px;">
            {title}
        </div>
        <div class="slide-visuals">
            {visual_html}
        </div>
        <div class="slide-content" style="color: {colors['text']}; font-family: {fonts['body']}; font-size: 16px;">
            {content_html}
        </div>
    </div>
    """
    
    return html

def apply_visuals_to_pptx_slide(slide, slide_content, template_colors):
    """Apply the suggested visuals to an actual PowerPoint slide"""
    # Get content areas
    content_placeholders = [shape for shape in slide.placeholders 
                           if shape.placeholder_format.type != 1]  # 1 is title
    
    # No content placeholders, can't continue
    if not content_placeholders:
        return slide
    
    # Analyze visual needs
    visual_analysis = analyze_slide_for_visuals(slide_content)
    visual_elements = slide_content.get('visual_elements', [])
    
    # Handle charts
    if visual_analysis['need_chart'] or any('chart' in str(ve).lower() for ve in visual_elements):
        # Getting a reference to the content placeholder
        placeholder = content_placeholders[0]
        
        # Extract placeholder dimensions
        left = placeholder.left
        top = placeholder.top
        width = placeholder.width
        height = placeholder.height / 2  # Use half the height for the chart
        
        # Sample chart data
        data = {
            "categories": ["Q1", "Q2", "Q3", "Q4"],
            "series": [
                {
                    "name": "Series 1",
                    "values": [4.3, 2.5, 3.5, 4.5]
                },
                {
                    "name": "Series 2", 
                    "values": [2.4, 4.4, 1.8, 2.8]
                }
            ]
        }
        
        create_chart_based_on_content(slide, visual_analysis['chart_type'], data, left, top, width, height)
        
        # Adjust content placeholder position to make room for chart
        placeholder.top = top + height + Inches(0.5)
        placeholder.height = height
    
    # Handle icons if requested
    if visual_analysis['icon_suggestions']:
        icons = visual_analysis['icon_suggestions'][:3]  # Limit to 3 icons
        
        # Get last shape for reference
        if len(slide.shapes) > 0:
            last_shape = slide.shapes[-1]
            left = Inches(0.5)
            top = last_shape.top + last_shape.height + Inches(0.2)
        else:
            left = Inches(0.5)
            top = Inches(3)
        
        # Add icons
        for i, icon_name in enumerate(icons):
            if icon_name in STANDARD_SHAPES:
                icon_left = left + (i * Inches(1.2))
                add_shape_to_slide(
                    slide, 
                    icon_name, 
                    left=icon_left, 
                    top=top, 
                    width=Inches(0.8), 
                    height=Inches(0.8),
                    fill_color=template_colors.get('accent', '#0000FF')
                )
    
    return slide

def get_ai_generated_image_placeholder():
    """Create a base64 encoded placeholder for AI-generated images"""
    # Simple placeholder SVG
    svg = '''
    <svg width="400" height="300" xmlns="http://www.w3.org/2000/svg">
        <rect width="100%" height="100%" fill="#f0f0f0"/>
        <text x="50%" y="50%" font-family="Arial" font-size="20" fill="#666" text-anchor="middle">
            AI Generated Image
        </text>
    </svg>
    '''
    encoded = base64.b64encode(svg.encode('utf-8')).decode('utf-8')
    return f"data:image/svg+xml;base64,{encoded}"

def generate_image_prompt(slide_content):
    """Generate a prompt for AI image generation based on slide content"""
    title = slide_content.get('title', '')
    
    if isinstance(slide_content.get('content', []), list):
        content_text = " ".join(slide_content.get('content', []))
    else:
        content_text = str(slide_content.get('content', ''))
    
    prompt = f"Create an image for a slide titled '{title}' with content about {content_text[:100]}..."
    return prompt