openfree commited on
Commit
d248d0e
·
verified ·
1 Parent(s): a5266aa

Update content_utils.py

Browse files
Files changed (1) hide show
  1. content_utils.py +313 -262
content_utils.py CHANGED
@@ -1510,280 +1510,331 @@ def create_pptx_file(results: List[Dict], topic: str, template_name: str,
1510
  theme_name: str = "Minimal Light", design_themes: Dict = None) -> str:
1511
  """Create PPTX file with speaker notes"""
1512
  print(f"[PPTX] Creating file... Theme: {theme_name}")
 
1513
 
1514
- # Create presentation (16:9 ratio)
1515
- prs = Presentation()
1516
- prs.slide_width = Inches(16)
1517
- prs.slide_height = Inches(9)
1518
-
1519
- # Get selected theme
1520
- theme = design_themes.get(theme_name, design_themes["Minimal Light"]) if design_themes else {}
1521
-
1522
- # Add slides
1523
- for i, result in enumerate(results):
1524
- if not result.get("success", False):
1525
- continue
1526
-
1527
- slide_data = result.get("slide_data", {})
1528
 
1529
- # Use blank layout
1530
- blank_layout = prs.slide_layouts[6]
1531
- slide = prs.slides.add_slide(blank_layout)
 
 
 
 
 
 
 
 
 
1532
 
1533
- # Title slide
1534
- if slide_data.get('title') in ['Cover', '표지']:
1535
- # Add background image
1536
- if slide_data.get('image'):
1537
- try:
1538
- img_buffer = BytesIO()
1539
- slide_data['image'].save(img_buffer, format='PNG')
1540
- img_buffer.seek(0)
1541
-
1542
- # Full screen background image
1543
- pic = slide.shapes.add_picture(
1544
- img_buffer,
1545
- 0, 0,
1546
- width=prs.slide_width,
1547
- height=prs.slide_height
1548
- )
1549
- # Send to back
1550
- slide.shapes._spTree.remove(pic._element)
1551
- slide.shapes._spTree.insert(2, pic._element)
1552
- except Exception as e:
1553
- print(f"[PPTX] Failed to add title image: {str(e)}")
1554
-
1555
- # Title background box (semi-transparent)
1556
- title_bg = slide.shapes.add_shape(
1557
- MSO_SHAPE.ROUNDED_RECTANGLE,
1558
- Inches(2), Inches(2.8),
1559
- Inches(12), Inches(3.2)
1560
- )
1561
- title_bg.fill.solid()
1562
- title_bg.fill.fore_color.rgb = RGBColor(255, 255, 255)
1563
- title_bg.fill.transparency = 0.8
1564
- title_bg.line.fill.background()
1565
-
1566
- # Shadow effect
1567
- shadow = title_bg.shadow
1568
- shadow.visible = True
1569
- shadow.distance = Pt(6)
1570
- shadow.size = 100
1571
- shadow.blur_radius = Pt(12)
1572
- shadow.transparency = 0.8
1573
- shadow.angle = 45
1574
-
1575
- # Title text
1576
- title_box = slide.shapes.add_textbox(
1577
- Inches(2), Inches(3.2),
1578
- Inches(12), Inches(1.5)
1579
- )
1580
- title_frame = title_box.text_frame
1581
- title_frame.text = topic
1582
- title_para = title_frame.paragraphs[0]
1583
- title_para.font.size = Pt(48)
1584
- title_para.font.bold = True
1585
- title_para.font.color.rgb = RGBColor(0, 0, 0)
1586
- title_para.alignment = PP_ALIGN.CENTER
1587
-
1588
- # Subtitle
1589
- subtitle_box = slide.shapes.add_textbox(
1590
- Inches(2), Inches(4.3),
1591
- Inches(12), Inches(1.0)
1592
- )
1593
- subtitle_frame = subtitle_box.text_frame
1594
- subtitle_frame.text = slide_data.get('subtitle', f'{template_name} - AI Presentation')
1595
- subtitle_para = subtitle_frame.paragraphs[0]
1596
- subtitle_para.font.size = Pt(28)
1597
- subtitle_para.font.color.rgb = RGBColor(33, 37, 41)
1598
- subtitle_para.alignment = PP_ALIGN.CENTER
1599
-
1600
- # Thank You slide
1601
- elif slide_data.get('title') == 'Thank You':
1602
- # Add background image
1603
- if slide_data.get('image'):
1604
- try:
1605
- img_buffer = BytesIO()
1606
- slide_data['image'].save(img_buffer, format='PNG')
1607
- img_buffer.seek(0)
1608
-
1609
- # Full screen background image
1610
- pic = slide.shapes.add_picture(
1611
- img_buffer,
1612
- 0, 0,
1613
- width=prs.slide_width,
1614
- height=prs.slide_height
1615
- )
1616
- # Send to back
1617
- slide.shapes._spTree.remove(pic._element)
1618
- slide.shapes._spTree.insert(2, pic._element)
1619
- except Exception as e:
1620
- print(f"[PPTX] Failed to add Thank You image: {str(e)}")
1621
-
1622
- # Thank You background box
1623
- thanks_bg = slide.shapes.add_shape(
1624
- MSO_SHAPE.ROUNDED_RECTANGLE,
1625
- Inches(2), Inches(3.5),
1626
- Inches(12), Inches(2.5)
1627
- )
1628
- thanks_bg.fill.solid()
1629
- thanks_bg.fill.fore_color.rgb = RGBColor(255, 255, 255)
1630
- thanks_bg.fill.transparency = 0.8
1631
- thanks_bg.line.fill.background()
1632
-
1633
- # Shadow effect
1634
- shadow = thanks_bg.shadow
1635
- shadow.visible = True
1636
- shadow.distance = Pt(6)
1637
- shadow.size = 100
1638
- shadow.blur_radius = Pt(12)
1639
- shadow.transparency = 0.8
1640
- shadow.angle = 45
1641
-
1642
- # Thank You text (conclusion phrase)
1643
- thanks_box = slide.shapes.add_textbox(
1644
- Inches(2), Inches(4),
1645
- Inches(12), Inches(1.5)
1646
- )
1647
- thanks_frame = thanks_box.text_frame
1648
- thanks_frame.text = slide_data.get('subtitle', 'Thank You')
1649
- thanks_para = thanks_frame.paragraphs[0]
1650
- thanks_para.font.size = Pt(42)
1651
- thanks_para.font.bold = True
1652
- thanks_para.font.color.rgb = RGBColor(0, 0, 0)
1653
- thanks_para.alignment = PP_ALIGN.CENTER
1654
-
1655
- # Regular slides
1656
  else:
1657
- # Background color
1658
- background = slide.background
1659
- fill = background.fill
1660
- fill.solid()
1661
- fill.fore_color.rgb = theme.get("background", RGBColor(250, 250, 252))
 
 
 
 
 
 
 
1662
 
1663
- # Slide title background box
1664
- title_box_bg = slide.shapes.add_shape(
1665
- MSO_SHAPE.ROUNDED_RECTANGLE,
1666
- Inches(0.3), Inches(0.2),
1667
- Inches(15.4), Inches(1.0)
1668
- )
1669
- title_box_bg.fill.solid()
1670
- title_box_bg.fill.fore_color.rgb = theme.get("box_fill", RGBColor(255, 255, 255))
1671
- title_box_bg.fill.transparency = 1 - theme.get("box_opacity", 0.95)
1672
 
1673
- # Shadow effect
1674
- if theme.get("shadow", True):
1675
- shadow = title_box_bg.shadow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1676
  shadow.visible = True
1677
- shadow.distance = Pt(4)
1678
  shadow.size = 100
1679
- shadow.blur_radius = Pt(8)
1680
- shadow.transparency = 0.75
1681
  shadow.angle = 45
1682
-
1683
- title_box_bg.line.fill.background()
1684
-
1685
- # Slide title
1686
- title_box = slide.shapes.add_textbox(
1687
- Inches(0.5), Inches(0.3),
1688
- Inches(15), Inches(0.8)
1689
- )
1690
- title_frame = title_box.text_frame
1691
- title_frame.text = f"{slide_data.get('title', '')}"
1692
- title_para = title_frame.paragraphs[0]
1693
- title_para.font.size = Pt(28)
1694
- title_para.font.bold = True
1695
- title_para.font.color.rgb = theme.get("title_color", RGBColor(33, 37, 41))
1696
-
1697
- # Left text area background box
1698
- text_box_bg = slide.shapes.add_shape(
1699
- MSO_SHAPE.ROUNDED_RECTANGLE,
1700
- Inches(0.3), Inches(1.4),
1701
- Inches(7.8), Inches(6.8)
1702
- )
1703
- text_box_bg.fill.solid()
1704
- text_box_bg.fill.fore_color.rgb = theme.get("box_fill", RGBColor(255, 255, 255))
1705
- text_box_bg.fill.transparency = 1 - theme.get("box_opacity", 0.95)
1706
-
1707
- if theme.get("shadow", True):
1708
- shadow = text_box_bg.shadow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1709
  shadow.visible = True
1710
- shadow.distance = Pt(5)
1711
  shadow.size = 100
1712
- shadow.blur_radius = Pt(10)
1713
- shadow.transparency = 0.7
1714
  shadow.angle = 45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1715
 
1716
- text_box_bg.line.fill.background()
1717
-
1718
- # Left text area
1719
- text_box = slide.shapes.add_textbox(
1720
- Inches(0.8), Inches(1.8),
1721
- Inches(7.0), Inches(6.0)
1722
- )
1723
- text_frame = text_box.text_frame
1724
- text_frame.word_wrap = True
1725
-
1726
- # Subtitle
1727
- subtitle_para = text_frame.paragraphs[0]
1728
- subtitle_para.text = slide_data.get('subtitle', '')
1729
- subtitle_para.font.size = Pt(20)
1730
- subtitle_para.font.bold = True
1731
- subtitle_para.font.color.rgb = theme.get("subtitle_color", RGBColor(52, 58, 64))
1732
- subtitle_para.space_after = Pt(20)
1733
-
1734
- # Bullet points
1735
- bullet_points = slide_data.get('bullet_points', [])
1736
- for point in bullet_points:
1737
- p = text_frame.add_paragraph()
1738
- # Remove and add text only (keep emojis)
1739
- clean_text = point.replace('•', '').strip()
1740
- p.text = clean_text
1741
- p.font.size = Pt(16)
1742
- p.font.color.rgb = theme.get("text_color", RGBColor(73, 80, 87))
1743
- p.level = 0
1744
- p.space_after = Pt(12)
1745
- p.line_spacing = 1.5
1746
- p.left_indent = Pt(0)
1747
 
1748
- # Right image
1749
- if slide_data.get('image'):
1750
- try:
1751
- img_buffer = BytesIO()
1752
- slide_data['image'].save(img_buffer, format='PNG')
1753
- img_buffer.seek(0)
1754
-
1755
- pic = slide.shapes.add_picture(
1756
- img_buffer,
1757
- Inches(8.5), Inches(1.6),
1758
- width=Inches(6.8), height=Inches(6.4)
1759
- )
1760
-
1761
- pic.line.fill.background()
1762
-
1763
- except Exception as e:
1764
- print(f"[PPTX] Failed to add image: {str(e)}")
1765
 
1766
- # Page number
1767
- page_num = slide.shapes.add_textbox(
1768
- Inches(15), Inches(8.5),
1769
- Inches(1), Inches(0.5)
1770
- )
1771
- page_frame = page_num.text_frame
1772
- page_frame.text = str(i + 1)
1773
- page_para = page_frame.paragraphs[0]
1774
- page_para.font.size = Pt(12)
1775
- page_para.font.color.rgb = theme.get("text_color", RGBColor(73, 80, 87))
1776
- page_para.alignment = PP_ALIGN.RIGHT
1777
-
1778
- # Add speaker notes
1779
- notes_slide = slide.notes_slide
1780
- notes_slide.notes_text_frame.text = slide_data.get('speaker_notes', '')
1781
-
1782
- # Save file
1783
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1784
- filename = f"presentation_{timestamp}.pptx"
1785
- filepath = os.path.join("/tmp", filename)
1786
- prs.save(filepath)
1787
-
1788
- print(f"[PPTX] File created: {filename}")
1789
- return filepath
 
1510
  theme_name: str = "Minimal Light", design_themes: Dict = None) -> str:
1511
  """Create PPTX file with speaker notes"""
1512
  print(f"[PPTX] Creating file... Theme: {theme_name}")
1513
+ print(f"[PPTX] Processing {len(results)} slides")
1514
 
1515
+ try:
1516
+ # Create presentation (16:9 ratio)
1517
+ prs = Presentation()
1518
+ prs.slide_width = Inches(16)
1519
+ prs.slide_height = Inches(9)
 
 
 
 
 
 
 
 
 
1520
 
1521
+ # Get selected theme with proper defaults
1522
+ default_theme = {
1523
+ "background": RGBColor(250, 250, 252),
1524
+ "title_color": RGBColor(33, 37, 41),
1525
+ "subtitle_color": RGBColor(52, 58, 64),
1526
+ "text_color": RGBColor(73, 80, 87),
1527
+ "accent_color": RGBColor(0, 123, 255),
1528
+ "box_fill": RGBColor(255, 255, 255),
1529
+ "box_opacity": 0.95,
1530
+ "shadow": True,
1531
+ "gradient": False
1532
+ }
1533
 
1534
+ if design_themes and theme_name in design_themes:
1535
+ theme = design_themes[theme_name]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1536
  else:
1537
+ theme = default_theme
1538
+ print(f"[PPTX] Using default theme as {theme_name} not found")
1539
+
1540
+ # Add slides
1541
+ slide_count = 0
1542
+ for i, result in enumerate(results):
1543
+ if not result.get("success", False):
1544
+ print(f"[PPTX] Skipping slide {i+1} - not successful")
1545
+ continue
1546
+
1547
+ slide_data = result.get("slide_data", {})
1548
+ print(f"[PPTX] Adding slide {i+1}: {slide_data.get('title', 'Unknown')}")
1549
 
1550
+ # Use blank layout
1551
+ blank_layout = prs.slide_layouts[6]
1552
+ slide = prs.slides.add_slide(blank_layout)
1553
+ slide_count += 1
 
 
 
 
 
1554
 
1555
+ # Title slide
1556
+ if slide_data.get('title') in ['Cover', '표지']:
1557
+ # Add background image
1558
+ if slide_data.get('image'):
1559
+ try:
1560
+ img_buffer = BytesIO()
1561
+ slide_data['image'].save(img_buffer, format='PNG')
1562
+ img_buffer.seek(0)
1563
+
1564
+ # Full screen background image
1565
+ pic = slide.shapes.add_picture(
1566
+ img_buffer,
1567
+ 0, 0,
1568
+ width=prs.slide_width,
1569
+ height=prs.slide_height
1570
+ )
1571
+ # Send to back
1572
+ slide.shapes._spTree.remove(pic._element)
1573
+ slide.shapes._spTree.insert(2, pic._element)
1574
+ print(f"[PPTX] Added title background image")
1575
+ except Exception as e:
1576
+ print(f"[PPTX] Failed to add title image: {str(e)}")
1577
+
1578
+ # Title background box (semi-transparent)
1579
+ title_bg = slide.shapes.add_shape(
1580
+ MSO_SHAPE.ROUNDED_RECTANGLE,
1581
+ Inches(2), Inches(2.8),
1582
+ Inches(12), Inches(3.2)
1583
+ )
1584
+ title_bg.fill.solid()
1585
+ title_bg.fill.fore_color.rgb = RGBColor(255, 255, 255)
1586
+ title_bg.fill.transparency = 0.8
1587
+ title_bg.line.fill.background()
1588
+
1589
+ # Shadow effect
1590
+ shadow = title_bg.shadow
1591
  shadow.visible = True
1592
+ shadow.distance = Pt(6)
1593
  shadow.size = 100
1594
+ shadow.blur_radius = Pt(12)
1595
+ shadow.transparency = 0.8
1596
  shadow.angle = 45
1597
+
1598
+ # Title text
1599
+ title_box = slide.shapes.add_textbox(
1600
+ Inches(2), Inches(3.2),
1601
+ Inches(12), Inches(1.5)
1602
+ )
1603
+ title_frame = title_box.text_frame
1604
+ title_frame.text = topic
1605
+ title_para = title_frame.paragraphs[0]
1606
+ title_para.font.size = Pt(48)
1607
+ title_para.font.bold = True
1608
+ title_para.font.color.rgb = RGBColor(0, 0, 0)
1609
+ title_para.alignment = PP_ALIGN.CENTER
1610
+
1611
+ # Subtitle
1612
+ subtitle_box = slide.shapes.add_textbox(
1613
+ Inches(2), Inches(4.3),
1614
+ Inches(12), Inches(1.0)
1615
+ )
1616
+ subtitle_frame = subtitle_box.text_frame
1617
+ subtitle_frame.text = slide_data.get('subtitle', f'{template_name} - AI Presentation')
1618
+ subtitle_para = subtitle_frame.paragraphs[0]
1619
+ subtitle_para.font.size = Pt(28)
1620
+ subtitle_para.font.color.rgb = RGBColor(33, 37, 41)
1621
+ subtitle_para.alignment = PP_ALIGN.CENTER
1622
+
1623
+ # Thank You slide
1624
+ elif slide_data.get('title') == 'Thank You':
1625
+ # Add background image
1626
+ if slide_data.get('image'):
1627
+ try:
1628
+ img_buffer = BytesIO()
1629
+ slide_data['image'].save(img_buffer, format='PNG')
1630
+ img_buffer.seek(0)
1631
+
1632
+ # Full screen background image
1633
+ pic = slide.shapes.add_picture(
1634
+ img_buffer,
1635
+ 0, 0,
1636
+ width=prs.slide_width,
1637
+ height=prs.slide_height
1638
+ )
1639
+ # Send to back
1640
+ slide.shapes._spTree.remove(pic._element)
1641
+ slide.shapes._spTree.insert(2, pic._element)
1642
+ except Exception as e:
1643
+ print(f"[PPTX] Failed to add Thank You image: {str(e)}")
1644
+
1645
+ # Thank You background box
1646
+ thanks_bg = slide.shapes.add_shape(
1647
+ MSO_SHAPE.ROUNDED_RECTANGLE,
1648
+ Inches(2), Inches(3.5),
1649
+ Inches(12), Inches(2.5)
1650
+ )
1651
+ thanks_bg.fill.solid()
1652
+ thanks_bg.fill.fore_color.rgb = RGBColor(255, 255, 255)
1653
+ thanks_bg.fill.transparency = 0.8
1654
+ thanks_bg.line.fill.background()
1655
+
1656
+ # Shadow effect
1657
+ shadow = thanks_bg.shadow
1658
  shadow.visible = True
1659
+ shadow.distance = Pt(6)
1660
  shadow.size = 100
1661
+ shadow.blur_radius = Pt(12)
1662
+ shadow.transparency = 0.8
1663
  shadow.angle = 45
1664
+
1665
+ # Thank You text (conclusion phrase)
1666
+ thanks_box = slide.shapes.add_textbox(
1667
+ Inches(2), Inches(4),
1668
+ Inches(12), Inches(1.5)
1669
+ )
1670
+ thanks_frame = thanks_box.text_frame
1671
+ thanks_frame.text = slide_data.get('subtitle', 'Thank You')
1672
+ thanks_para = thanks_frame.paragraphs[0]
1673
+ thanks_para.font.size = Pt(42)
1674
+ thanks_para.font.bold = True
1675
+ thanks_para.font.color.rgb = RGBColor(0, 0, 0)
1676
+ thanks_para.alignment = PP_ALIGN.CENTER
1677
+
1678
+ # Regular slides
1679
+ else:
1680
+ # Background color
1681
+ background = slide.background
1682
+ fill = background.fill
1683
+ fill.solid()
1684
+ fill.fore_color.rgb = theme.get("background", RGBColor(250, 250, 252))
1685
+
1686
+ # Slide title background box
1687
+ title_box_bg = slide.shapes.add_shape(
1688
+ MSO_SHAPE.ROUNDED_RECTANGLE,
1689
+ Inches(0.3), Inches(0.2),
1690
+ Inches(15.4), Inches(1.0)
1691
+ )
1692
+ title_box_bg.fill.solid()
1693
+ title_box_bg.fill.fore_color.rgb = theme.get("box_fill", RGBColor(255, 255, 255))
1694
+ title_box_bg.fill.transparency = 1 - theme.get("box_opacity", 0.95)
1695
+
1696
+ # Shadow effect
1697
+ if theme.get("shadow", True):
1698
+ shadow = title_box_bg.shadow
1699
+ shadow.visible = True
1700
+ shadow.distance = Pt(4)
1701
+ shadow.size = 100
1702
+ shadow.blur_radius = Pt(8)
1703
+ shadow.transparency = 0.75
1704
+ shadow.angle = 45
1705
+
1706
+ title_box_bg.line.fill.background()
1707
+
1708
+ # Slide title
1709
+ title_box = slide.shapes.add_textbox(
1710
+ Inches(0.5), Inches(0.3),
1711
+ Inches(15), Inches(0.8)
1712
+ )
1713
+ title_frame = title_box.text_frame
1714
+ title_frame.text = f"{slide_data.get('title', '')}"
1715
+ title_para = title_frame.paragraphs[0]
1716
+ title_para.font.size = Pt(28)
1717
+ title_para.font.bold = True
1718
+ title_para.font.color.rgb = theme.get("title_color", RGBColor(33, 37, 41))
1719
+
1720
+ # Left text area background box
1721
+ text_box_bg = slide.shapes.add_shape(
1722
+ MSO_SHAPE.ROUNDED_RECTANGLE,
1723
+ Inches(0.3), Inches(1.4),
1724
+ Inches(7.8), Inches(6.8)
1725
+ )
1726
+ text_box_bg.fill.solid()
1727
+ text_box_bg.fill.fore_color.rgb = theme.get("box_fill", RGBColor(255, 255, 255))
1728
+ text_box_bg.fill.transparency = 1 - theme.get("box_opacity", 0.95)
1729
+
1730
+ if theme.get("shadow", True):
1731
+ shadow = text_box_bg.shadow
1732
+ shadow.visible = True
1733
+ shadow.distance = Pt(5)
1734
+ shadow.size = 100
1735
+ shadow.blur_radius = Pt(10)
1736
+ shadow.transparency = 0.7
1737
+ shadow.angle = 45
1738
+
1739
+ text_box_bg.line.fill.background()
1740
+
1741
+ # Left text area
1742
+ text_box = slide.shapes.add_textbox(
1743
+ Inches(0.8), Inches(1.8),
1744
+ Inches(7.0), Inches(6.0)
1745
+ )
1746
+ text_frame = text_box.text_frame
1747
+ text_frame.word_wrap = True
1748
+
1749
+ # Subtitle
1750
+ subtitle_para = text_frame.paragraphs[0]
1751
+ subtitle_para.text = slide_data.get('subtitle', '')
1752
+ subtitle_para.font.size = Pt(20)
1753
+ subtitle_para.font.bold = True
1754
+ subtitle_para.font.color.rgb = theme.get("subtitle_color", RGBColor(52, 58, 64))
1755
+ subtitle_para.space_after = Pt(20)
1756
+
1757
+ # Bullet points
1758
+ bullet_points = slide_data.get('bullet_points', [])
1759
+ for point in bullet_points:
1760
+ p = text_frame.add_paragraph()
1761
+ # Remove • and add text only (keep emojis)
1762
+ clean_text = point.replace('•', '').strip()
1763
+ p.text = clean_text
1764
+ p.font.size = Pt(16)
1765
+ p.font.color.rgb = theme.get("text_color", RGBColor(73, 80, 87))
1766
+ p.level = 0
1767
+ p.space_after = Pt(12)
1768
+ p.line_spacing = 1.5
1769
+ p.left_indent = Pt(0)
1770
+
1771
+ # Right image
1772
+ if slide_data.get('image'):
1773
+ try:
1774
+ img_buffer = BytesIO()
1775
+ slide_data['image'].save(img_buffer, format='PNG')
1776
+ img_buffer.seek(0)
1777
+
1778
+ pic = slide.shapes.add_picture(
1779
+ img_buffer,
1780
+ Inches(8.5), Inches(1.6),
1781
+ width=Inches(6.8), height=Inches(6.4)
1782
+ )
1783
+
1784
+ pic.line.fill.background()
1785
+
1786
+ except Exception as e:
1787
+ print(f"[PPTX] Failed to add image: {str(e)}")
1788
+
1789
+ # Page number
1790
+ page_num = slide.shapes.add_textbox(
1791
+ Inches(15), Inches(8.5),
1792
+ Inches(1), Inches(0.5)
1793
+ )
1794
+ page_frame = page_num.text_frame
1795
+ page_frame.text = str(i + 1)
1796
+ page_para = page_frame.paragraphs[0]
1797
+ page_para.font.size = Pt(12)
1798
+ page_para.font.color.rgb = theme.get("text_color", RGBColor(73, 80, 87))
1799
+ page_para.alignment = PP_ALIGN.RIGHT
1800
 
1801
+ # Add speaker notes
1802
+ notes_slide = slide.notes_slide
1803
+ notes_slide.notes_text_frame.text = slide_data.get('speaker_notes', '')
1804
+ print(f"[PPTX] Added speaker notes for slide {i+1}")
1805
+
1806
+ print(f"[PPTX] Total slides added: {slide_count}")
1807
+
1808
+ # Save file using tempfile for better compatibility
1809
+ import tempfile
1810
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1811
+ filename = f"presentation_{timestamp}.pptx"
1812
+
1813
+ # Create a temporary file
1814
+ with tempfile.NamedTemporaryFile(mode='wb', suffix='.pptx', delete=False) as tmp_file:
1815
+ prs.save(tmp_file)
1816
+ temp_path = tmp_file.name
1817
+ print(f"[PPTX] File saved to temporary path: {temp_path}")
1818
+
1819
+ # Verify file exists and has content
1820
+ if os.path.exists(temp_path):
1821
+ file_size = os.path.getsize(temp_path)
1822
+ print(f"[PPTX] File created successfully: {filename}")
1823
+ print(f"[PPTX] File size: {file_size} bytes")
1824
+ print(f"[PPTX] File path: {temp_path}")
 
 
 
 
 
 
 
1825
 
1826
+ if file_size > 0:
1827
+ return temp_path
1828
+ else:
1829
+ print(f"[PPTX] ERROR: File created but has 0 bytes")
1830
+ os.remove(temp_path)
1831
+ return None
1832
+ else:
1833
+ print(f"[PPTX] ERROR: File was not created at {temp_path}")
1834
+ return None
 
 
 
 
 
 
 
 
1835
 
1836
+ except Exception as e:
1837
+ print(f"[PPTX] CRITICAL ERROR during file creation: {str(e)}")
1838
+ import traceback
1839
+ traceback.print_exc()
1840
+ return None