YashMK89 commited on
Commit
fa1fb11
Β·
verified Β·
1 Parent(s): 483b1fe

update formate

Browse files
Files changed (1) hide show
  1. pages/1_πŸ“Š_Text_to_PPT.py +123 -57
pages/1_πŸ“Š_Text_to_PPT.py CHANGED
@@ -1025,8 +1025,8 @@ import tempfile
1025
  import os
1026
  from PIL import Image
1027
  import time
1028
- import json
1029
- from hashlib import sha256
1030
 
1031
  # Load the API key securely from environment variable
1032
  api_key = os.getenv("GOOGLE_API_KEY")
@@ -1043,7 +1043,8 @@ DEFAULT_THEMES = {
1043
  "text_color": RGBColor(200, 200, 200),
1044
  "accent": RGBColor(0, 112, 192),
1045
  "title_font": "Calibri",
1046
- "text_font": "Calibri"
 
1047
  },
1048
  "Modern Green": {
1049
  "background": RGBColor(22, 82, 66),
@@ -1051,7 +1052,8 @@ DEFAULT_THEMES = {
1051
  "text_color": RGBColor(220, 220, 220),
1052
  "accent": RGBColor(76, 175, 80),
1053
  "title_font": "Arial",
1054
- "text_font": "Arial"
 
1055
  },
1056
  "Light Corporate": {
1057
  "background": RGBColor(255, 255, 255),
@@ -1059,7 +1061,8 @@ DEFAULT_THEMES = {
1059
  "text_color": RGBColor(33, 33, 33),
1060
  "accent": RGBColor(25, 118, 210),
1061
  "title_font": "Segoe UI",
1062
- "text_font": "Segoe UI"
 
1063
  },
1064
  "Dark Tech": {
1065
  "background": RGBColor(33, 33, 33),
@@ -1067,7 +1070,8 @@ DEFAULT_THEMES = {
1067
  "text_color": RGBColor(200, 200, 200),
1068
  "accent": RGBColor(0, 150, 255),
1069
  "title_font": "Consolas",
1070
- "text_font": "Consolas"
 
1071
  }
1072
  }
1073
 
@@ -1097,6 +1101,7 @@ def extract_theme_from_pptx(uploaded_file):
1097
  "accent": RGBColor(79, 129, 189), # Default blue
1098
  "title_font": "Calibri",
1099
  "text_font": "Calibri",
 
1100
  "template_path": tmp_file_path # Store the template path for later use
1101
  }
1102
 
@@ -1324,20 +1329,54 @@ def add_title_separator(slide, title_shape, accent_color):
1324
  fill.fore_color.rgb = accent_color
1325
  line.line.fill.background()
1326
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1327
  def create_question_slide(prs, question, question_num, theme):
1328
- """Create a slide for a single question with proper theme support"""
1329
  # Try to use Title + Content layout, fallback to blank if not available
1330
  try:
1331
  slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
1332
  except IndexError:
1333
  slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
1334
 
1335
- # Remove unused placeholders
1336
- for shape in slide.shapes:
1337
- if shape.has_text_frame and not shape.text.strip():
1338
- sp = shape._element
1339
- sp.getparent().remove(sp)
1340
-
1341
  # Apply background if using custom theme
1342
  if "template_path" not in theme:
1343
  background = slide.background
@@ -1345,6 +1384,12 @@ def create_question_slide(prs, question, question_num, theme):
1345
  fill.solid()
1346
  fill.fore_color.rgb = theme["background"]
1347
 
 
 
 
 
 
 
1348
  # Set title
1349
  title = slide.shapes.title if hasattr(slide.shapes, 'title') else None
1350
  if title:
@@ -1369,18 +1414,25 @@ def create_question_slide(prs, question, question_num, theme):
1369
  tf = content_box.text_frame
1370
  tf.word_wrap = True
1371
 
1372
- # Add question text
1373
- p = tf.add_paragraph()
1374
- p.text = question['text']
1375
- p.font.bold = True
1376
- p.space_after = Pt(24)
1377
 
1378
- # Apply text formatting
1379
- if "template_path" not in theme:
1380
- p.font.color.rgb = theme["text_color"]
1381
- p.font.size = Pt(24)
1382
- if "text_font" in theme:
1383
- p.font.name = theme["text_font"]
 
 
 
 
 
 
 
 
 
1384
 
1385
  # Add options with proper spacing
1386
  for j, opt in enumerate(question['options']):
@@ -1393,7 +1445,7 @@ def create_question_slide(prs, question, question_num, theme):
1393
  # Apply text formatting
1394
  if "template_path" not in theme:
1395
  p.font.color.rgb = theme["text_color"]
1396
- p.font.size = Pt(20)
1397
  if "text_font" in theme:
1398
  p.font.name = theme["text_font"]
1399
 
@@ -1407,7 +1459,7 @@ def create_question_slide(prs, question, question_num, theme):
1407
  return slide
1408
 
1409
  def create_answer_key_slide(prs, questions, theme):
1410
- """Create answer key slide with proper theme support"""
1411
  # Try to use Title + Content layout, fallback to blank if not available
1412
  try:
1413
  slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
@@ -1565,9 +1617,9 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None, p
1565
 
1566
  # Create left column
1567
  left = Inches(0.5)
1568
- top = Inches(1.5)
1569
  width = prs.slide_width / 2 - Inches(1)
1570
- height = prs.slide_height - Inches(2.5) # Leave space at bottom
1571
  left_box = slide.shapes.add_textbox(left, top, width, height)
1572
  left_tf = left_box.text_frame
1573
 
@@ -1578,39 +1630,51 @@ def create_detailed_pptx(slides_data, questions, theme, branding_options=None, p
1578
 
1579
  for content_part, tf in [(left_content, left_tf), (right_content, right_tf)]:
1580
  for point in content_part:
1581
- p = tf.add_paragraph()
1582
- point_text = point.replace('- ', '').strip()
1583
- p.text = point_text
1584
- p.level = 0
1585
- p.alignment = PP_ALIGN.JUSTIFY
1586
- p.space_after = Pt(12)
1587
- if "template_path" not in theme: # Only apply custom formatting if no template
1588
- p.font.color.rgb = theme["text_color"]
1589
- p.font.size = Pt(18)
1590
- if "text_font" in theme:
1591
- p.font.name = theme["text_font"]
 
 
 
 
 
 
1592
 
1593
  elif layout != 'title-only': # Content slides
1594
  # Create content area
1595
  left = Inches(1)
1596
- top = Inches(1.5)
1597
  width = prs.slide_width - Inches(2)
1598
- height = prs.slide_height - Inches(2.5) # Leave space at bottom
1599
  content_box = slide.shapes.add_textbox(left, top, width, height)
1600
  tf = content_box.text_frame
1601
 
1602
  for point in slide_info.get('content', []):
1603
- p = tf.add_paragraph()
1604
- point_text = point.replace('- ', '').strip()
1605
- p.text = point_text
1606
- p.level = 0
1607
- p.alignment = PP_ALIGN.JUSTIFY
1608
- p.space_after = Pt(12)
1609
- if "template_path" not in theme: # Only apply custom formatting if no template
1610
- p.font.color.rgb = theme["text_color"]
1611
- p.font.size = Pt(18)
1612
- if "text_font" in theme:
1613
- p.font.name = theme["text_font"]
 
 
 
 
 
 
1614
 
1615
  # Add notes if available
1616
  if slide_info.get('notes') and hasattr(slide, 'notes_slide'):
@@ -1666,7 +1730,7 @@ def regenerate_slide(slide_index, topic, slide_count, theme, ppt_version):
1666
  Please regenerate only slide number {slide_index+1} with the same structure as the others.
1667
  The slide should have:
1668
  - A clear title in [Title:] format
1669
- - 3-5 detailed bullet points in [Content:] format
1670
  - Optional speaker notes in [Notes:] format
1671
  - Layout suggestion in [Layout:] format
1672
 
@@ -1674,11 +1738,11 @@ def regenerate_slide(slide_index, topic, slide_count, theme, ppt_version):
1674
  [Title:] Slide Title
1675
  [Layout:] layout-type
1676
  [Content:]
1677
- - Point 1
1678
- - Point 2
1679
  [Notes:] Optional notes
1680
 
1681
- Make sure the content is relevant to the overall topic and flows with the presentation.
1682
  """
1683
 
1684
  response = model.generate_content(prompt)
@@ -1762,6 +1826,7 @@ def main():
1762
  accent_color = st.color_picker("Accent Color", "#0070C0")
1763
  title_font = st.text_input("Title Font", "Calibri")
1764
  text_font = st.text_input("Text Font", "Calibri")
 
1765
 
1766
  if st.form_submit_button("Save Custom Theme"):
1767
  if new_theme_name:
@@ -1771,7 +1836,8 @@ def main():
1771
  "text_color": hex_to_rgb(text_color),
1772
  "accent": hex_to_rgb(accent_color),
1773
  "title_font": title_font,
1774
- "text_font": text_font
 
1775
  }
1776
  st.success(f"Theme '{new_theme_name}' saved successfully!")
1777
  else:
 
1025
  import os
1026
  from PIL import Image
1027
  import time
1028
+ import textwrap
1029
+ import math
1030
 
1031
  # Load the API key securely from environment variable
1032
  api_key = os.getenv("GOOGLE_API_KEY")
 
1043
  "text_color": RGBColor(200, 200, 200),
1044
  "accent": RGBColor(0, 112, 192),
1045
  "title_font": "Calibri",
1046
+ "text_font": "Calibri",
1047
+ "min_font_size": 14
1048
  },
1049
  "Modern Green": {
1050
  "background": RGBColor(22, 82, 66),
 
1052
  "text_color": RGBColor(220, 220, 220),
1053
  "accent": RGBColor(76, 175, 80),
1054
  "title_font": "Arial",
1055
+ "text_font": "Arial",
1056
+ "min_font_size": 14
1057
  },
1058
  "Light Corporate": {
1059
  "background": RGBColor(255, 255, 255),
 
1061
  "text_color": RGBColor(33, 33, 33),
1062
  "accent": RGBColor(25, 118, 210),
1063
  "title_font": "Segoe UI",
1064
+ "text_font": "Segoe UI",
1065
+ "min_font_size": 14
1066
  },
1067
  "Dark Tech": {
1068
  "background": RGBColor(33, 33, 33),
 
1070
  "text_color": RGBColor(200, 200, 200),
1071
  "accent": RGBColor(0, 150, 255),
1072
  "title_font": "Consolas",
1073
+ "text_font": "Consolas",
1074
+ "min_font_size": 14
1075
  }
1076
  }
1077
 
 
1101
  "accent": RGBColor(79, 129, 189), # Default blue
1102
  "title_font": "Calibri",
1103
  "text_font": "Calibri",
1104
+ "min_font_size": 14,
1105
  "template_path": tmp_file_path # Store the template path for later use
1106
  }
1107
 
 
1329
  fill.fore_color.rgb = accent_color
1330
  line.line.fill.background()
1331
 
1332
+ def smart_text_wrap(text, max_width=100):
1333
+ """Wrap text intelligently at sentence boundaries when possible"""
1334
+ if len(text) <= max_width:
1335
+ return [text]
1336
+
1337
+ # Try to split at sentence boundaries
1338
+ sentences = re.split(r'(?<=[.!?]) +', text)
1339
+ lines = []
1340
+ current_line = ""
1341
+
1342
+ for sentence in sentences:
1343
+ if len(current_line) + len(sentence) + 1 <= max_width:
1344
+ current_line += (sentence + " ") if current_line else sentence
1345
+ else:
1346
+ if current_line:
1347
+ lines.append(current_line.strip())
1348
+ current_line = sentence
1349
+
1350
+ if current_line:
1351
+ lines.append(current_line.strip())
1352
+
1353
+ # If we didn't split into sentences, do regular word wrap
1354
+ if len(lines) == 1 and len(text) > max_width:
1355
+ return textwrap.wrap(text, max_width)
1356
+
1357
+ return lines
1358
+
1359
+ def calculate_optimal_font_size(text, max_width, max_height, initial_size=18):
1360
+ """Calculate optimal font size to fit text in specified dimensions"""
1361
+ # Estimate based on character count
1362
+ char_count = len(text)
1363
+ if char_count < 100:
1364
+ return initial_size
1365
+ elif char_count < 200:
1366
+ return max(initial_size - 2, 14)
1367
+ elif char_count < 300:
1368
+ return max(initial_size - 4, 12)
1369
+ else:
1370
+ return max(initial_size - 6, 10)
1371
+
1372
  def create_question_slide(prs, question, question_num, theme):
1373
+ """Create a slide for a single question with proper text wrapping"""
1374
  # Try to use Title + Content layout, fallback to blank if not available
1375
  try:
1376
  slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
1377
  except IndexError:
1378
  slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank layout
1379
 
 
 
 
 
 
 
1380
  # Apply background if using custom theme
1381
  if "template_path" not in theme:
1382
  background = slide.background
 
1384
  fill.solid()
1385
  fill.fore_color.rgb = theme["background"]
1386
 
1387
+ # Remove unused placeholders
1388
+ for shape in slide.shapes:
1389
+ if shape.has_text_frame and not shape.text.strip():
1390
+ sp = shape._element
1391
+ sp.getparent().remove(sp)
1392
+
1393
  # Set title
1394
  title = slide.shapes.title if hasattr(slide.shapes, 'title') else None
1395
  if title:
 
1414
  tf = content_box.text_frame
1415
  tf.word_wrap = True
1416
 
1417
+ # Add question text with smart wrapping
1418
+ question_text = question['text']
1419
+ wrapped_question = smart_text_wrap(question_text, 100)
 
 
1420
 
1421
+ # Calculate optimal font size
1422
+ font_size = calculate_optimal_font_size(question_text, width, height, 24)
1423
+
1424
+ for line in wrapped_question:
1425
+ p = tf.add_paragraph()
1426
+ p.text = line
1427
+ p.font.bold = True
1428
+ p.space_after = Pt(12)
1429
+
1430
+ # Apply text formatting
1431
+ if "template_path" not in theme:
1432
+ p.font.color.rgb = theme["text_color"]
1433
+ p.font.size = Pt(font_size)
1434
+ if "text_font" in theme:
1435
+ p.font.name = theme["text_font"]
1436
 
1437
  # Add options with proper spacing
1438
  for j, opt in enumerate(question['options']):
 
1445
  # Apply text formatting
1446
  if "template_path" not in theme:
1447
  p.font.color.rgb = theme["text_color"]
1448
+ p.font.size = Pt(font_size - 4) # Slightly smaller for options
1449
  if "text_font" in theme:
1450
  p.font.name = theme["text_font"]
1451
 
 
1459
  return slide
1460
 
1461
  def create_answer_key_slide(prs, questions, theme):
1462
+ """Create answer key slide with proper text wrapping"""
1463
  # Try to use Title + Content layout, fallback to blank if not available
1464
  try:
1465
  slide = prs.slides.add_slide(prs.slide_layouts[1]) # Title + Content layout
 
1617
 
1618
  # Create left column
1619
  left = Inches(0.5)
1620
+ top = Inches(1.8) # Increased top margin
1621
  width = prs.slide_width / 2 - Inches(1)
1622
+ height = prs.slide_height - top - Inches(1) # Leave space at bottom
1623
  left_box = slide.shapes.add_textbox(left, top, width, height)
1624
  left_tf = left_box.text_frame
1625
 
 
1630
 
1631
  for content_part, tf in [(left_content, left_tf), (right_content, right_tf)]:
1632
  for point in content_part:
1633
+ # Wrap text to prevent overflow
1634
+ wrapped_text = smart_text_wrap(point, 100)
1635
+
1636
+ # Calculate optimal font size
1637
+ font_size = calculate_optimal_font_size(point, width, height, 18)
1638
+
1639
+ for line in wrapped_text:
1640
+ p = tf.add_paragraph()
1641
+ p.text = line
1642
+ p.level = 0
1643
+ p.alignment = PP_ALIGN.JUSTIFY
1644
+ p.space_after = Pt(8) # Reduced space between lines
1645
+ if "template_path" not in theme: # Only apply custom formatting if no template
1646
+ p.font.color.rgb = theme["text_color"]
1647
+ p.font.size = Pt(font_size)
1648
+ if "text_font" in theme:
1649
+ p.font.name = theme["text_font"]
1650
 
1651
  elif layout != 'title-only': # Content slides
1652
  # Create content area
1653
  left = Inches(1)
1654
+ top = Inches(1.8) # Increased top margin
1655
  width = prs.slide_width - Inches(2)
1656
+ height = prs.slide_height - top - Inches(1) # Leave space at bottom
1657
  content_box = slide.shapes.add_textbox(left, top, width, height)
1658
  tf = content_box.text_frame
1659
 
1660
  for point in slide_info.get('content', []):
1661
+ # Wrap text to prevent overflow
1662
+ wrapped_text = smart_text_wrap(point, 100)
1663
+
1664
+ # Calculate optimal font size
1665
+ font_size = calculate_optimal_font_size(point, width, height, 18)
1666
+
1667
+ for line in wrapped_text:
1668
+ p = tf.add_paragraph()
1669
+ p.text = line
1670
+ p.level = 0
1671
+ p.alignment = PP_ALIGN.JUSTIFY
1672
+ p.space_after = Pt(8) # Reduced space between lines
1673
+ if "template_path" not in theme: # Only apply custom formatting if no template
1674
+ p.font.color.rgb = theme["text_color"]
1675
+ p.font.size = Pt(font_size)
1676
+ if "text_font" in theme:
1677
+ p.font.name = theme["text_font"]
1678
 
1679
  # Add notes if available
1680
  if slide_info.get('notes') and hasattr(slide, 'notes_slide'):
 
1730
  Please regenerate only slide number {slide_index+1} with the same structure as the others.
1731
  The slide should have:
1732
  - A clear title in [Title:] format
1733
+ - 3-5 detailed bullet points in [Content:] format (each point should be concise, 1-2 lines max)
1734
  - Optional speaker notes in [Notes:] format
1735
  - Layout suggestion in [Layout:] format
1736
 
 
1738
  [Title:] Slide Title
1739
  [Layout:] layout-type
1740
  [Content:]
1741
+ - Point 1: Concise point
1742
+ - Point 2: Another concise point
1743
  [Notes:] Optional notes
1744
 
1745
+ Make sure the content is concise and fits on a single slide. Avoid long paragraphs.
1746
  """
1747
 
1748
  response = model.generate_content(prompt)
 
1826
  accent_color = st.color_picker("Accent Color", "#0070C0")
1827
  title_font = st.text_input("Title Font", "Calibri")
1828
  text_font = st.text_input("Text Font", "Calibri")
1829
+ min_font_size = st.slider("Minimum Font Size", 10, 18, 14)
1830
 
1831
  if st.form_submit_button("Save Custom Theme"):
1832
  if new_theme_name:
 
1836
  "text_color": hex_to_rgb(text_color),
1837
  "accent": hex_to_rgb(accent_color),
1838
  "title_font": title_font,
1839
+ "text_font": text_font,
1840
+ "min_font_size": min_font_size
1841
  }
1842
  st.success(f"Theme '{new_theme_name}' saved successfully!")
1843
  else: