Aniket2012 commited on
Commit
1adb104
Β·
verified Β·
1 Parent(s): 7e0e601

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +434 -0
app.py ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import re
3
+ from pptx import Presentation
4
+ from pptx.util import Pt
5
+ from pptx.dml.color import RGBColor
6
+ from pptx.enum.shapes import PP_PLACEHOLDER
7
+ from pptx.enum.dml import MSO_FILL
8
+ from pptx.enum.text import PP_ALIGN
9
+ import os
10
+ import tempfile
11
+ import io
12
+
13
+ # --- 1. CONFIGURATION AND CONSTANTS ---
14
+
15
+ # List of common, web-safe fonts for the dropdown menu
16
+ COMMON_FONTS = [
17
+ 'Arial', 'Arial Black', 'Calibri', 'Calibri Light', 'Cambria', 'Candara',
18
+ 'Century Gothic', 'Consolas', 'Constantia', 'Corbel', 'Courier New',
19
+ 'Franklin Gothic Medium', 'Gabriola', 'Gadugi', 'Georgia', 'Gill Sans MT',
20
+ 'Impact', 'Lucida Console', 'Lucida Sans Unicode', 'Palatino Linotype',
21
+ 'Rockwell', 'Segoe UI', 'Sitka', 'Tahoma', 'Times New Roman',
22
+ 'Trebuchet MS', 'Verdana'
23
+ ]
24
+
25
+ # Default styles that will populate the UI
26
+ DEFAULT_STYLES = {
27
+ 'title': {'font_name': 'Calibri', 'font_size': 44, 'bold': True, 'color': '#000000'},
28
+ 'subtitle': {'font_name': 'Calibri', 'font_size': 24, 'bold': False, 'color': '#333333'},
29
+ 'body_title': {'font_name': 'Calibri', 'font_size': 36, 'bold': True, 'color': '#000000'},
30
+ 'body_level_0': {'font_name': 'Calibri', 'font_size': 24, 'bold': False, 'color': '#1E1E1E'},
31
+ 'body_level_1': {'font_name': 'Calibri', 'font_size': 20, 'bold': False, 'color': '#1E1E1E'},
32
+ 'body_level_2': {'font_name': 'Calibri', 'font_size': 18, 'bold': False, 'color': '#1E1E1E'},
33
+ 'hyperlink': {'font_name': 'Calibri', 'font_size': 16, 'underline': True, 'color': '#0563C1'}
34
+ }
35
+
36
+ # Mapping for bullet points to indentation levels
37
+ BULLET_MAP = {'β€’': 0, 'β—¦': 1, 'β–ͺ': 2}
38
+ HYPERLINK_RE = re.compile(r'^(.*?):\s*(https?://\S+)$')
39
+
40
+
41
+ # --- 2. DEFAULT CONTENT AND EXAMPLES ---
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
49
+ β€’ Problem: Creating presentations is time-consuming.
50
+ β€’ Solution: Automate slide generation from simple text outlines.
51
+ β€’ Technology:
52
+ β—¦ Python for backend logic.
53
+ β—¦ `python-pptx` for presentation manipulation.
54
+ β—¦ Gradio for the user interface.
55
+
56
+ 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.
64
+
65
+ Slide 4: How It Works
66
+ β€’ Step 1: Write your content using the 'Slide X:' format.
67
+ β€’ Step 2: Use the 'Customization' tab to tweak the design.
68
+ β€’ Step 3: Click 'Create PowerPoint' to generate and download your file.
69
+ β€’ More Info: https://github.com/gradio-app/gradio
70
+
71
+ 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 ---
117
+
118
+ def parse_color_to_rgb(color_string):
119
+ """Converts a color string (hex or rgb) to an RGBColor object."""
120
+ if isinstance(color_string, str):
121
+ if color_string.startswith('#'):
122
+ return RGBColor.from_string(color_string.lstrip('#'))
123
+ elif color_string.startswith('rgb'):
124
+ try:
125
+ r, g, b = map(int, re.findall(r'\d+', color_string))
126
+ return RGBColor(r, g, b)
127
+ except (ValueError, TypeError):
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
136
+ elif key == 'font_size':
137
+ font.size = Pt(value)
138
+ elif key == 'font_name':
139
+ font.name = value
140
+ else:
141
+ setattr(font, key, value)
142
+
143
+ def find_placeholder(slide, placeholder_enums):
144
+ """Finds a placeholder shape on a slide."""
145
+ for shape in slide.shapes:
146
+ if shape.is_placeholder and shape.placeholder_format.type in placeholder_enums:
147
+ return shape
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
+
155
+ title_val = "Title Not Found"
156
+ subtitle_vals = [line.lstrip('β€’ ').strip() for line in lines if line.strip()]
157
+ if subtitle_vals:
158
+ title_val = subtitle_vals.pop(0)
159
+
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):
235
+ """Main function to create the presentation file from text, a template, and styles."""
236
+ try:
237
+ prs = Presentation(template_path) if template_path else Presentation()
238
+ except Exception as e:
239
+ raise gr.Error(f"Could not load the presentation template. Please ensure it's a valid .pptx file. Error: {e}")
240
+
241
+ if template_path and len(prs.slides) > 0:
242
+ title_layout = prs.slides[0].slide_layout
243
+ content_layout = prs.slides[1].slide_layout if len(prs.slides) > 1 else prs.slides[0].slide_layout
244
+ else:
245
+ # Default layouts for blank presentation
246
+ title_layout = prs.slide_layouts[0] if len(prs.slide_layouts) > 0 else prs.slide_layouts[5]
247
+ content_layout = prs.slide_layouts[1] if len(prs.slide_layouts) > 1 else prs.slide_layouts[0]
248
+
249
+
250
+ slides_data = re.split(r'\nSlide \d+[a-zA-Z]?:', content, flags=re.IGNORECASE)
251
+ slides_data = [s.strip() for s in slides_data if s.strip()]
252
+ if not slides_data:
253
+ raise gr.Error("The input text does not contain any valid slides. Please use the format 'Slide X:'.")
254
+
255
+ for i in range(len(prs.slides) - 1, -1, -1):
256
+ rId = prs.slides._sldIdLst[i].rId
257
+ prs.part.drop_rel(rId)
258
+ del prs.slides._sldIdLst[i]
259
+
260
+ for i, slide_content in enumerate(slides_data):
261
+ lines = [line.strip() for line in slide_content.split('\n') if line.strip()]
262
+ if not lines: continue
263
+
264
+ slide_title_text = lines.pop(0)
265
+
266
+ if i == 0 and "title slide" in slide_title_text.lower():
267
+ slide = prs.slides.add_slide(title_layout)
268
+ populate_title_slide(slide, lines, style_config)
269
+ else:
270
+ slide = prs.slides.add_slide(content_layout)
271
+ populate_content_slide(slide, slide_title_text, lines, style_config)
272
+
273
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pptx") as tmp:
274
+ prs.save(tmp.name)
275
+ return tmp.name
276
+
277
+ def create_themed_template(theme):
278
+ """Creates a temporary .pptx file with a themed background."""
279
+ prs = Presentation()
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
+
290
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pptx") as tmp:
291
+ prs.save(tmp.name)
292
+ return tmp.name
293
+
294
+ def main_interface(*args):
295
+ """Collects all UI inputs and generates the presentation."""
296
+ (
297
+ content, template_choice, custom_template_file,
298
+ title_font, title_size, title_bold, title_color,
299
+ subtitle_font, subtitle_size, subtitle_bold, subtitle_color,
300
+ body_title_font, body_title_size, body_title_bold, body_title_color,
301
+ L0_font, L0_size, L0_bold, L0_color,
302
+ L1_font, L1_size, L1_bold, L1_color,
303
+ L2_font, L2_size, L2_bold, L2_color
304
+ ) = args
305
+
306
+ if not content:
307
+ raise gr.Error("Presentation content is empty. Please enter your text first.")
308
+
309
+ template_path = None
310
+ if custom_template_file is not None:
311
+ template_path = custom_template_file.name
312
+ elif template_choice != "Default White":
313
+ template_path = create_themed_template(template_choice)
314
+
315
+ hyperlink_style = DEFAULT_STYLES['hyperlink'].copy()
316
+ hyperlink_style['color'] = parse_color_to_rgb(hyperlink_style['color'])
317
+
318
+ style_config = {
319
+ 'title': {'font_name': title_font, 'font_size': title_size, 'bold': title_bold, 'color': parse_color_to_rgb(title_color)},
320
+ 'subtitle': {'font_name': subtitle_font, 'font_size': subtitle_size, 'bold': subtitle_bold, 'color': parse_color_to_rgb(subtitle_color)},
321
+ 'body_title': {'font_name': body_title_font, 'font_size': body_title_size, 'bold': body_title_bold, 'color': parse_color_to_rgb(body_title_color)},
322
+ 'body_level_0': {'font_name': L0_font, 'font_size': L0_size, 'bold': L0_bold, 'color': parse_color_to_rgb(L0_color)},
323
+ 'body_level_1': {'font_name': L1_font, 'font_size': L1_size, 'bold': L1_bold, 'color': parse_color_to_rgb(L1_color)},
324
+ 'body_level_2': {'font_name': L2_font, 'font_size': L2_size, 'bold': L2_bold, 'color': parse_color_to_rgb(L2_color)},
325
+ 'hyperlink': hyperlink_style
326
+ }
327
+
328
+ output_path = create_presentation_file(content, template_path, style_config)
329
+ return output_path
330
+
331
+ # --- 4. GRADIO UI ---
332
+ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important}") as app:
333
+ gr.Markdown("""
334
+ <div style="text-align: center; padding: 20px; background-image: linear-gradient(to right, #74ebd5, #acb6e5); color: white; border-radius: 12px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
335
+ <h1 style="font-size: 2.8em; margin: 0; font-weight: 700; text-shadow: 1px 1px 3px rgba(0,0,0,0.2);">✨ AI Presentation Architect</h1>
336
+ <p style="font-size: 1.2em; margin-top: 5px;">Craft stunning presentations from simple text. Customize everything.</p>
337
+ </div>
338
+ """)
339
+
340
+ with gr.Tabs():
341
+ with gr.TabItem("πŸ“ Content & Generation"):
342
+ with gr.Row(equal_height=True):
343
+ with gr.Column(scale=2):
344
+ gr.Markdown("### 1. Enter Presentation Content")
345
+ presentation_text_area = gr.Textbox(
346
+ label="Format: 'Slide 1: Title' followed by bullet points.",
347
+ lines=25,
348
+ value=DEFAULT_TEMPLATE.strip()
349
+ )
350
+ gr.Examples(
351
+ examples=[
352
+ [DEFAULT_TEMPLATE.strip()],
353
+ [BUSINESS_PITCH_EXAMPLE.strip()]
354
+ ],
355
+ inputs=presentation_text_area,
356
+ label="Example Outlines"
357
+ )
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>")
366
+ template_upload = gr.File(label="Upload a Custom .pptx Template", file_types=[".pptx"])
367
+ with gr.Accordion("πŸ’‘ Template Tips", open=False):
368
+ gr.Markdown("""
369
+ - An uploaded template will **override** the built-in choice.
370
+ - All existing slides in your template will be **removed** and replaced with the new content.
371
+ - The design (master slide) of your template will be preserved.
372
+ - For best results, use a template with standard 'Title' and 'Title and Content' layouts.
373
+ """)
374
+
375
+ gr.Markdown("### 3. Create & Download")
376
+ create_ppt_btn = gr.Button("πŸš€ Generate PowerPoint", variant="primary", scale=2)
377
+ output_file = gr.File(label="Download Your Presentation", interactive=False)
378
+
379
+ with gr.TabItem("🎨 Font & Style Customization"):
380
+ gr.Markdown("### Fine-tune the look and feel of your presentation text.")
381
+ with gr.Accordion("Title & Subtitle Styles", open=True):
382
+ with gr.Row():
383
+ title_font = gr.Dropdown(COMMON_FONTS, label="Title Font", value=DEFAULT_STYLES['title']['font_name'])
384
+ title_size = gr.Slider(10, 100, label="Title Size (pt)", value=DEFAULT_STYLES['title']['font_size'], step=1)
385
+ title_bold = gr.Checkbox(label="Bold", value=DEFAULT_STYLES['title']['bold'])
386
+ title_color = gr.ColorPicker(label="Color", value=DEFAULT_STYLES['title']['color'])
387
+ with gr.Row():
388
+ subtitle_font = gr.Dropdown(COMMON_FONTS, label="Subtitle Font", value=DEFAULT_STYLES['subtitle']['font_name'])
389
+ subtitle_size = gr.Slider(10, 60, label="Subtitle Size (pt)", value=DEFAULT_STYLES['subtitle']['font_size'], step=1)
390
+ subtitle_bold = gr.Checkbox(label="Bold", value=DEFAULT_STYLES['subtitle']['bold'])
391
+ subtitle_color = gr.ColorPicker(label="Color", value=DEFAULT_STYLES['subtitle']['color'])
392
+
393
+ with gr.Accordion("Content Body Styles", open=True):
394
+ with gr.Row():
395
+ body_title_font = gr.Dropdown(COMMON_FONTS, label="Slide Title Font", value=DEFAULT_STYLES['body_title']['font_name'])
396
+ body_title_size = gr.Slider(10, 80, label="Slide Title Size (pt)", value=DEFAULT_STYLES['body_title']['font_size'], step=1)
397
+ body_title_bold = gr.Checkbox(label="Bold", value=DEFAULT_STYLES['body_title']['bold'])
398
+ body_title_color = gr.ColorPicker(label="Color", value=DEFAULT_STYLES['body_title']['color'])
399
+ gr.HTML("<hr>")
400
+ with gr.Row():
401
+ L0_font = gr.Dropdown(COMMON_FONTS, label="Bullet Level 1 (β€’) Font", value=DEFAULT_STYLES['body_level_0']['font_name'])
402
+ L0_size = gr.Slider(8, 50, label="Size (pt)", value=DEFAULT_STYLES['body_level_0']['font_size'], step=1)
403
+ L0_bold = gr.Checkbox(label="Bold", value=DEFAULT_STYLES['body_level_0']['bold'])
404
+ L0_color = gr.ColorPicker(label="Color", value=DEFAULT_STYLES['body_level_0']['color'])
405
+ with gr.Row():
406
+ L1_font = gr.Dropdown(COMMON_FONTS, label="Bullet Level 2 (β—¦) Font", value=DEFAULT_STYLES['body_level_1']['font_name'])
407
+ L1_size = gr.Slider(8, 50, label="Size (pt)", value=DEFAULT_STYLES['body_level_1']['font_size'], step=1)
408
+ L1_bold = gr.Checkbox(label="Bold", value=DEFAULT_STYLES['body_level_1']['bold'])
409
+ L1_color = gr.ColorPicker(label="Color", value=DEFAULT_STYLES['body_level_1']['color'])
410
+ with gr.Row():
411
+ L2_font = gr.Dropdown(COMMON_FONTS, label="Bullet Level 3 (β–ͺ) Font", value=DEFAULT_STYLES['body_level_2']['font_name'])
412
+ L2_size = gr.Slider(8, 50, label="Size (pt)", value=DEFAULT_STYLES['body_level_2']['font_size'], step=1)
413
+ L2_bold = gr.Checkbox(label="Bold", value=DEFAULT_STYLES['body_level_2']['bold'])
414
+ L2_color = gr.ColorPicker(label="Color", value=DEFAULT_STYLES['body_level_2']['color'])
415
+
416
+ # List of all input components to be passed to the main function
417
+ all_inputs = [
418
+ presentation_text_area, template_radio, template_upload,
419
+ title_font, title_size, title_bold, title_color,
420
+ subtitle_font, subtitle_size, subtitle_bold, subtitle_color,
421
+ body_title_font, body_title_size, body_title_bold, body_title_color,
422
+ L0_font, L0_size, L0_bold, L0_color,
423
+ L1_font, L1_size, L1_bold, L1_color,
424
+ L2_font, L2_size, L2_bold, L2_color
425
+ ]
426
+
427
+ create_ppt_btn.click(
428
+ fn=main_interface,
429
+ inputs=all_inputs,
430
+ outputs=output_file
431
+ )
432
+
433
+ if __name__ == "__main__":
434
+ app.launch(debug=True, share=True)