Aniket2012 commited on
Commit
c84c977
·
verified ·
1 Parent(s): ad63ee3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -78
app.py CHANGED
@@ -42,7 +42,7 @@ HYPERLINK_RE = re.compile(r'^(.*?):\s*(https?://\S+)$')
42
 
43
  DEFAULT_TEMPLATE = """
44
  Slide 1: Title Slide
45
- Python-Powered Presentation Architect
46
  • A Gradio & Python-pptx Project
47
 
48
  Slide 2: Introduction
@@ -57,7 +57,7 @@ Slide 3: Key Features
57
  • Text-to-Slide Conversion: Automatically creates slides from a formatted script.
58
  • Full Customization:
59
  ◦ Control font styles, sizes, and colors for every element.
60
- ◦ Use built-in themes or upload your own `.pptx` template.
61
  • Intelligent Layouts:
62
  ▪ Differentiates between title slides and content slides.
63
  ▪ Supports multi-level bullet points.
@@ -72,45 +72,31 @@ Slide 5: Q&A
72
  • Questions & Discussion
73
  """
74
 
75
- BUSINESS_PITCH_EXAMPLE = """
76
  Slide 1: Title Slide
77
- InnovateX: AI-Powered Logistics
78
- • Revolutionizing Supply Chain Management
79
-
80
- Slide 2: The Problem
81
- Global supply chains are inefficient and costly.
82
- Estimated $1.8 Trillion lost annually due to inefficiencies.
83
- • Key Pain Points:
84
- Lack of real-time visibility.
85
- High fuel and labor costs.
86
- Manual, error-prone documentation.
87
-
88
- Slide 3: Our Solution
89
- A unified platform leveraging AI and IoT.
90
- Features:
91
- Predictive route optimization.
92
- Real-time cargo tracking & monitoring.
93
- ◦ Automated customs documentation.
94
- Blockchain for secure transactions.
95
-
96
- Slide 4: Market Opportunity
97
- Global Logistics Market Size: $12 Trillion
98
- • Targetable Market (TAM): $500 Billion
99
- Our goal is to capture 2% of the TAM within 5 years.
100
-
101
- Slide 5: Business Model
102
- • Subscription-based SaaS model.
103
- • Tiers:
104
- ◦ Basic: $99/month/vehicle
105
- ◦ Pro: $199/month/vehicle
106
- ◦ Enterprise: Custom pricing
107
-
108
- Slide 6: Meet the Team
109
- • Jane Doe, CEO: Ex-Google, Logistics expert.
110
- • John Smith, CTO: PhD in AI from MIT.
111
- • Contact Us: [email protected]
112
-
113
- Slide 7: Q&A
114
  """
115
 
116
  # --- 3. PRESENTATION GENERATION LOGIC ---
@@ -128,8 +114,9 @@ def parse_color_to_rgb(color_string):
128
  return RGBColor(0, 0, 0)
129
  return RGBColor(0, 0, 0)
130
 
131
- def apply_font_style(font, style_config):
132
- """Applies a dictionary of style attributes to a font object."""
 
133
  for key, value in style_config.items():
134
  if key == 'color':
135
  font.color.rgb = value
@@ -148,7 +135,7 @@ def find_placeholder(slide, placeholder_enums):
148
  return None
149
 
150
  def populate_title_slide(slide, lines, style_config):
151
- """Populates a title slide by setting the paragraph's default font style."""
152
  title_ph = find_placeholder(slide, [PP_PLACEHOLDER.TITLE, PP_PLACEHOLDER.CENTER_TITLE])
153
  subtitle_ph = find_placeholder(slide, [PP_PLACEHOLDER.SUBTITLE])
154
 
@@ -160,75 +147,87 @@ def populate_title_slide(slide, lines, style_config):
160
  if title_ph and title_ph.has_text_frame:
161
  tf = title_ph.text_frame
162
  tf.clear()
163
- p = tf.paragraphs[0]
164
- # --- FIX: Style first, then add text ---
165
- apply_font_style(p.font, style_config['title'])
166
- p.text = title_val
167
  p.alignment = PP_ALIGN.CENTER
 
 
 
 
 
 
168
 
169
 
170
  if subtitle_ph and subtitle_ph.has_text_frame:
171
  tf = subtitle_ph.text_frame
172
  tf.clear()
173
- p = tf.paragraphs[0]
174
- # --- FIX: Style first, then add text ---
175
- apply_font_style(p.font, style_config['subtitle'])
176
- p.text = '\n'.join(subtitle_vals)
177
  p.alignment = PP_ALIGN.CENTER
 
 
 
 
 
 
 
 
 
 
178
 
179
 
180
  def populate_content_slide(slide, title, lines, style_config):
181
- """Populates a content slide by setting each paragraph's default font style."""
182
  title_ph = find_placeholder(slide, [PP_PLACEHOLDER.TITLE])
183
  body_ph = find_placeholder(slide, [PP_PLACEHOLDER.BODY, PP_PLACEHOLDER.OBJECT])
184
 
185
  if title_ph and title_ph.has_text_frame:
186
  tf = title_ph.text_frame
187
  tf.clear()
188
- p = tf.paragraphs[0]
189
- # --- FIX: Style first, then add text ---
190
- apply_font_style(p.font, style_config['body_title'])
191
- p.text = title
 
 
192
 
193
 
194
  if body_ph and body_ph.has_text_frame:
195
  tf = body_ph.text_frame
196
- tf.clear()
197
-
198
- # Remove the single paragraph left by clear() before adding new ones.
199
- if lines and len(tf.paragraphs) > 0:
200
- p_element = tf.paragraphs[0]._p
201
- p_element.getparent().remove(p_element)
202
 
203
  for line in lines:
204
  clean_line = line.strip()
205
  if not clean_line: continue
206
 
207
- p = tf.add_paragraph()
208
 
209
  hyperlink_match = HYPERLINK_RE.match(clean_line.lstrip('•◦▪ '))
210
  if hyperlink_match:
211
  link_text, url = hyperlink_match.groups()
212
- # Hyperlinks must be runs, so they are a special case
213
  run = p.add_run()
214
- apply_font_style(run.font, style_config['hyperlink'])
215
  run.text = f"{link_text}: {url}"
216
  run.hyperlink.address = url
 
217
  continue
218
 
219
  if clean_line.startswith(('•', '◦', '▪')):
220
  level = BULLET_MAP.get(clean_line[0], 0)
221
  text = clean_line[1:].lstrip()
222
- style_key = f'body_level_{level}'
223
- # --- FIX: Style first, then add text and set level ---
224
- apply_font_style(p.font, style_config.get(style_key, style_config['body_level_0']))
225
- p.text = text
226
  p.level = level
 
 
 
 
 
 
227
  else:
228
- # --- FIX: Style first, then add text and set level ---
229
- apply_font_style(p.font, style_config['body_level_0'])
230
- p.text = clean_line
231
  p.level = 0
 
 
 
 
 
 
 
232
 
233
 
234
  def create_presentation_file(content, template_path, style_config):
@@ -280,10 +279,10 @@ def create_themed_template(theme):
280
  slide_master = prs.slide_masters[0]
281
  fill = slide_master.background.fill
282
 
283
- if theme == "Modern Dark":
284
  fill.solid()
285
  fill.fore_color.rgb = RGBColor(0x1E, 0x1E, 0x1E)
286
- elif theme == "Professional Blue":
287
  fill.solid()
288
  fill.fore_color.rgb = RGBColor(0xE7, 0xF1, 0xFF)
289
 
@@ -350,7 +349,7 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}")
350
  gr.Examples(
351
  examples=[
352
  [DEFAULT_TEMPLATE.strip()],
353
- [BUSINESS_PITCH_EXAMPLE.strip()]
354
  ],
355
  inputs=presentation_text_area,
356
  label="Example Outlines"
@@ -358,8 +357,8 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}")
358
  with gr.Column(scale=1):
359
  gr.Markdown("### 2. Choose a Template")
360
  template_radio = gr.Radio(
361
- ["Default White", "Modern Dark", "Professional Blue"],
362
- label="Built-in Themed Templates",
363
  value="Default White"
364
  )
365
  gr.Markdown("<p style='text-align: center; margin: 10px 0;'>OR</p>")
@@ -431,4 +430,4 @@ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}")
431
  )
432
 
433
  if __name__ == "__main__":
434
- app.launch(debug=True, share=True)
 
42
 
43
  DEFAULT_TEMPLATE = """
44
  Slide 1: Title Slide
45
+ AI-Powered Presentation Generator
46
  • A Gradio & Python-pptx Project
47
 
48
  Slide 2: Introduction
 
57
  • Text-to-Slide Conversion: Automatically creates slides from a formatted script.
58
  • Full Customization:
59
  ◦ Control font styles, sizes, and colors for every element.
60
+ ◦ Use built-in themes (like Dark Mode) or upload your own `.pptx` template.
61
  • Intelligent Layouts:
62
  ▪ Differentiates between title slides and content slides.
63
  ▪ Supports multi-level bullet points.
 
72
  • Questions & Discussion
73
  """
74
 
75
+ MARKETING_PLAN_EXAMPLE = """
76
  Slide 1: Title Slide
77
+ Project Phoenix: Q3 Marketing Campaign
78
+
79
+ Slide 2: Campaign Goals
80
+ Increase brand awareness by 20%.
81
+ Generate 500 new qualified leads.
82
+ Boost social media engagement by 30%.
83
+
84
+ Slide 3: Target Audience
85
+ Tech startups in the AI sector.
86
+ Mid-size e-commerce businesses.
87
+ • Digital marketing agencies.
88
+
89
+ Slide 4: Key Channels
90
+ LinkedIn sponsored content & tech-focused blog partnerships.
91
+ Targeted email campaigns & virtual webinar series.
92
+ Link to our blog: https://gradio.app/blog
93
+
94
+ Slide 5: Budget Overview
95
+ • Content Creation: $5,000
96
+ Paid Advertising: $10,000
97
+ Total: $15,000
98
+
99
+ Slide 6: Next Steps & Q&A
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  """
101
 
102
  # --- 3. PRESENTATION GENERATION LOGIC ---
 
114
  return RGBColor(0, 0, 0)
115
  return RGBColor(0, 0, 0)
116
 
117
+ def apply_font_style(run, style_config):
118
+ """Applies a dictionary of style attributes to a text run."""
119
+ font = run.font
120
  for key, value in style_config.items():
121
  if key == 'color':
122
  font.color.rgb = value
 
135
  return None
136
 
137
  def populate_title_slide(slide, lines, style_config):
138
+ """Populates a title slide with content and styles using a robust run-based approach."""
139
  title_ph = find_placeholder(slide, [PP_PLACEHOLDER.TITLE, PP_PLACEHOLDER.CENTER_TITLE])
140
  subtitle_ph = find_placeholder(slide, [PP_PLACEHOLDER.SUBTITLE])
141
 
 
147
  if title_ph and title_ph.has_text_frame:
148
  tf = title_ph.text_frame
149
  tf.clear()
150
+ p = tf.add_paragraph() # Create a fresh paragraph
 
 
 
151
  p.alignment = PP_ALIGN.CENTER
152
+ run = p.add_run()
153
+ run.text = title_val
154
+ apply_font_style(run, style_config['title'])
155
+ # Remove the empty paragraph that might be left by tf.clear()
156
+ if len(tf.paragraphs) > 1:
157
+ tf._element.remove(tf.paragraphs[0]._p)
158
 
159
 
160
  if subtitle_ph and subtitle_ph.has_text_frame:
161
  tf = subtitle_ph.text_frame
162
  tf.clear()
163
+ p = tf.add_paragraph()
 
 
 
164
  p.alignment = PP_ALIGN.CENTER
165
+
166
+ for i, line_text in enumerate(subtitle_vals):
167
+ if i > 0:
168
+ p.add_run().text = '\n'
169
+
170
+ run = p.add_run()
171
+ run.text = line_text
172
+ apply_font_style(run, style_config['subtitle'])
173
+ if len(tf.paragraphs) > 1:
174
+ tf._element.remove(tf.paragraphs[0]._p)
175
 
176
 
177
  def populate_content_slide(slide, title, lines, style_config):
178
+ """Populates a content slide with a title and bullet points using a robust run-based approach."""
179
  title_ph = find_placeholder(slide, [PP_PLACEHOLDER.TITLE])
180
  body_ph = find_placeholder(slide, [PP_PLACEHOLDER.BODY, PP_PLACEHOLDER.OBJECT])
181
 
182
  if title_ph and title_ph.has_text_frame:
183
  tf = title_ph.text_frame
184
  tf.clear()
185
+ p = tf.add_paragraph()
186
+ run = p.add_run()
187
+ run.text = title
188
+ apply_font_style(run, style_config['body_title'])
189
+ if len(tf.paragraphs) > 1:
190
+ tf._element.remove(tf.paragraphs[0]._p)
191
 
192
 
193
  if body_ph and body_ph.has_text_frame:
194
  tf = body_ph.text_frame
195
+ tf.clear()
 
 
 
 
 
196
 
197
  for line in lines:
198
  clean_line = line.strip()
199
  if not clean_line: continue
200
 
201
+ p = tf.add_paragraph() # Always create a new paragraph for each line
202
 
203
  hyperlink_match = HYPERLINK_RE.match(clean_line.lstrip('•◦▪ '))
204
  if hyperlink_match:
205
  link_text, url = hyperlink_match.groups()
 
206
  run = p.add_run()
 
207
  run.text = f"{link_text}: {url}"
208
  run.hyperlink.address = url
209
+ apply_font_style(run, style_config['hyperlink'])
210
  continue
211
 
212
  if clean_line.startswith(('•', '◦', '▪')):
213
  level = BULLET_MAP.get(clean_line[0], 0)
214
  text = clean_line[1:].lstrip()
 
 
 
 
215
  p.level = level
216
+ run = p.add_run()
217
+ # --- FIX: Include the bullet character in the run's text ---
218
+ # This ensures the custom style (including color) applies to the bullet itself.
219
+ run.text = f"{clean_line[0]} {text}"
220
+ style_key = f'body_level_{level}'
221
+ apply_font_style(run, style_config.get(style_key, style_config['body_level_0']))
222
  else:
 
 
 
223
  p.level = 0
224
+ run = p.add_run()
225
+ run.text = clean_line
226
+ apply_font_style(run, style_config['body_level_0'])
227
+
228
+ # After loop, remove the initial empty paragraph if it exists
229
+ if len(tf.paragraphs) > 0 and not tf.paragraphs[0].text.strip():
230
+ tf._element.remove(tf.paragraphs[0]._p)
231
 
232
 
233
  def create_presentation_file(content, template_path, style_config):
 
279
  slide_master = prs.slide_masters[0]
280
  fill = slide_master.background.fill
281
 
282
+ if theme == "Dark":
283
  fill.solid()
284
  fill.fore_color.rgb = RGBColor(0x1E, 0x1E, 0x1E)
285
+ elif theme == "Blue":
286
  fill.solid()
287
  fill.fore_color.rgb = RGBColor(0xE7, 0xF1, 0xFF)
288
 
 
349
  gr.Examples(
350
  examples=[
351
  [DEFAULT_TEMPLATE.strip()],
352
+ [MARKETING_PLAN_EXAMPLE.strip()]
353
  ],
354
  inputs=presentation_text_area,
355
  label="Example Outlines"
 
357
  with gr.Column(scale=1):
358
  gr.Markdown("### 2. Choose a Template")
359
  template_radio = gr.Radio(
360
+ ["Default White", "Dark", "Blue"],
361
+ label="Built-in Blank Templates",
362
  value="Default White"
363
  )
364
  gr.Markdown("<p style='text-align: center; margin: 10px 0;'>OR</p>")
 
430
  )
431
 
432
  if __name__ == "__main__":
433
+ app.launch(debug=True, share=True)