dseditor commited on
Commit
03b6904
·
verified ·
1 Parent(s): b15a147

Upload 3 files

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +632 -0
  3. cht.ttf +3 -0
  4. requirements.txt +5 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ cht.ttf filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,632 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # gemini_ppt_generator.py
2
+ import os
3
+ import json
4
+ import requests
5
+ import tempfile
6
+ from io import BytesIO
7
+ from PIL import Image
8
+ import gradio as gr
9
+ import google.generativeai as genai
10
+ from pptx import Presentation
11
+ from pptx.util import Inches, Pt
12
+ from pptx.enum.text import PP_ALIGN
13
+ from pptx.dml.color import RGBColor
14
+
15
+ class GeminiPPTGenerator:
16
+ def __init__(self):
17
+ self.pexels_headers = {}
18
+ self.gemini_model = None
19
+
20
+ # 版型配置
21
+ self.themes = {
22
+ "商務專業": {
23
+ "bg_color": RGBColor(255, 255, 255),
24
+ "title_color": RGBColor(31, 73, 125),
25
+ "text_color": RGBColor(68, 68, 68),
26
+ "accent_color": RGBColor(79, 129, 189),
27
+ "layout": "image_right"
28
+ },
29
+ "科技創新": {
30
+ "bg_color": RGBColor(240, 240, 240),
31
+ "title_color": RGBColor(0, 102, 204),
32
+ "text_color": RGBColor(51, 51, 51),
33
+ "accent_color": RGBColor(255, 102, 0),
34
+ "layout": "image_background"
35
+ },
36
+ "創意設計": {
37
+ "bg_color": RGBColor(255, 250, 250),
38
+ "title_color": RGBColor(220, 20, 60),
39
+ "text_color": RGBColor(70, 70, 70),
40
+ "accent_color": RGBColor(255, 140, 0),
41
+ "layout": "image_left"
42
+ },
43
+ "教育學術": {
44
+ "bg_color": RGBColor(255, 255, 255),
45
+ "title_color": RGBColor(102, 51, 153),
46
+ "text_color": RGBColor(85, 85, 85),
47
+ "accent_color": RGBColor(153, 204, 0),
48
+ "layout": "image_top"
49
+ }
50
+ }
51
+
52
+ # 圖片風格
53
+ self.image_styles = {
54
+ "professional": "business professional corporate clean",
55
+ "creative": "creative artistic colorful vibrant",
56
+ "minimalist": "minimal clean simple white space",
57
+ "modern": "modern contemporary sleek design",
58
+ "natural": "natural outdoor organic environment",
59
+ "technology": "technology digital modern tech innovation"
60
+ }
61
+
62
+ def setup_apis(self, gemini_api_key, pexels_api_key):
63
+ """設定 API 金鑰"""
64
+ try:
65
+ # 設定 Gemini API
66
+ if gemini_api_key:
67
+ genai.configure(api_key=gemini_api_key)
68
+ self.gemini_model = genai.GenerativeModel('gemini-2.5-flash-preview-05-20')
69
+
70
+ # 設定 Pexels API
71
+ if pexels_api_key:
72
+ self.pexels_headers = {
73
+ "Authorization": pexels_api_key
74
+ }
75
+
76
+ return True, "✅ API 設定成功"
77
+ except Exception as e:
78
+ return False, f"❌ API 設定失敗:{str(e)}"
79
+
80
+ def generate_content_with_gemini(self, topic, slide_count=5):
81
+ """使用 Gemini 生成簡報內容"""
82
+
83
+ prompt = f"""
84
+ 請為主題「{topic}」製作一個 {slide_count} 頁的簡報大綱,以 JSON 格式回傳。
85
+
86
+ 格式要求:
87
+ {{
88
+ "title": "簡報主標題",
89
+ "subtitle": "簡報副標題",
90
+ "slides": [
91
+ {{
92
+ "title": "投影片標題",
93
+ "content": [
94
+ "重點1",
95
+ "重點2",
96
+ "重點3"
97
+ ],
98
+ "image_keywords": "英文關鍵字,用於搜尋相關圖片"
99
+ }}
100
+ ]
101
+ }}
102
+
103
+ 要求:
104
+ 1. 內容要專業且有邏輯性
105
+ 2. 每頁 3-4 個重點
106
+ 3. image_keywords 要用英文,描述該投影片適合的圖片內容
107
+ 4. 關鍵字要具體明確,例如 "business meeting", "technology innovation", "data analysis"
108
+ 5. 使用繁體中文(除了 image_keywords)
109
+ 6. 第一頁是概述介紹,最後一頁是結論總結
110
+ 7. 請直接回傳 JSON,不要包含其他文字說明
111
+ """
112
+
113
+ try:
114
+ if self.gemini_model:
115
+ response = self.gemini_model.generate_content(prompt)
116
+ content = response.text
117
+
118
+ # 清理回應內容,提取 JSON
119
+ content = content.strip()
120
+ if content.startswith('```json'):
121
+ content = content[7:]
122
+ if content.endswith('```'):
123
+ content = content[:-3]
124
+
125
+ # 尋找 JSON 開始和結束位置
126
+ start = content.find('{')
127
+ end = content.rfind('}') + 1
128
+
129
+ if start != -1 and end > start:
130
+ json_str = content[start:end]
131
+ return json.loads(json_str)
132
+ else:
133
+ raise ValueError("無法在回應中找到有效的 JSON")
134
+
135
+ else:
136
+ return self.get_default_structure_with_images(topic)
137
+
138
+ except Exception as e:
139
+ print(f"Gemini API 錯誤: {e}")
140
+ return self.get_default_structure_with_images(topic)
141
+
142
+ def get_default_structure_with_images(self, topic):
143
+ """預設簡報結構(含圖片關鍵字)"""
144
+ return {
145
+ "title": f"{topic} 簡報",
146
+ "subtitle": "由 AI 自動生成",
147
+ "slides": [
148
+ {
149
+ "title": "簡介與背景",
150
+ "content": [
151
+ "主題背景介紹",
152
+ "研究目的與範圍",
153
+ "簡報架構說明"
154
+ ],
155
+ "image_keywords": "presentation introduction business"
156
+ },
157
+ {
158
+ "title": "主要內容分析",
159
+ "content": [
160
+ "核心概念說明",
161
+ "重要特點分析",
162
+ "相關案例討論"
163
+ ],
164
+ "image_keywords": "analysis data research content"
165
+ },
166
+ {
167
+ "title": "深入探討",
168
+ "content": [
169
+ "優勢與機會識別",
170
+ "挑戰與問題分析",
171
+ "影響因素評估"
172
+ ],
173
+ "image_keywords": "strategy planning discussion"
174
+ },
175
+ {
176
+ "title": "解決方案與建議",
177
+ "content": [
178
+ "策略建議提出",
179
+ "實施方法規劃",
180
+ "預期效果評估"
181
+ ],
182
+ "image_keywords": "solution implementation strategy"
183
+ },
184
+ {
185
+ "title": "結論與展望",
186
+ "content": [
187
+ "重點總結回顧",
188
+ "未來發展趨勢",
189
+ "行動建議提出"
190
+ ],
191
+ "image_keywords": "conclusion future success"
192
+ }
193
+ ]
194
+ }
195
+
196
+ def search_pexels_with_style(self, keywords, image_style="professional", per_page=10):
197
+ """根據風格搜尋 Pexels 圖片"""
198
+ if not self.pexels_headers:
199
+ return None
200
+
201
+ # 組合關鍵字
202
+ style_modifier = self.image_styles.get(image_style, "")
203
+ enhanced_keywords = f"{keywords} {style_modifier}"
204
+
205
+ url = "https://api.pexels.com/v1/search"
206
+ params = {
207
+ "query": enhanced_keywords,
208
+ "per_page": per_page,
209
+ "orientation": "landscape",
210
+ "size": "medium"
211
+ }
212
+
213
+ try:
214
+ response = requests.get(url, headers=self.pexels_headers, params=params)
215
+ if response.status_code == 200:
216
+ data = response.json()
217
+ return data["photos"] if data["photos"] else None
218
+ return None
219
+ except Exception as e:
220
+ print(f"Pexels API 錯誤: {e}")
221
+ return None
222
+
223
+ def select_best_image(self, photos, slide_title=""):
224
+ """從多張圖片中選擇最適合的"""
225
+ if not photos:
226
+ return None
227
+
228
+ # 選擇解析度較高的圖片
229
+ best_photo = photos[0]
230
+ for photo in photos[:3]:
231
+ if photo["width"] * photo["height"] > best_photo["width"] * best_photo["height"]:
232
+ best_photo = photo
233
+
234
+ return best_photo["src"]["medium"]
235
+
236
+ def download_image(self, image_url):
237
+ """下載圖片並返回檔案路徑"""
238
+ if not image_url:
239
+ return None
240
+
241
+ try:
242
+ response = requests.get(image_url)
243
+ if response.status_code == 200:
244
+ temp_dir = tempfile.mkdtemp()
245
+ image_path = os.path.join(temp_dir, "slide_image.jpg")
246
+
247
+ # 處理圖片
248
+ image = Image.open(BytesIO(response.content))
249
+
250
+ # 調整圖片大小
251
+ max_size = (800, 600)
252
+ image.thumbnail(max_size, Image.Resampling.LANCZOS)
253
+
254
+ # 轉換並儲存
255
+ if image.mode in ("RGBA", "P"):
256
+ image = image.convert("RGB")
257
+ image.save(image_path, "JPEG", quality=85)
258
+
259
+ return image_path
260
+ return None
261
+ except Exception as e:
262
+ print(f"圖片下載錯誤: {e}")
263
+ return None
264
+
265
+ def add_image_to_slide(self, slide, image_path, layout_type="image_right"):
266
+ """將圖片添加到投影片"""
267
+ if not image_path or not os.path.exists(image_path):
268
+ return
269
+
270
+ try:
271
+ if layout_type == "image_right":
272
+ left, top, width, height = Inches(5.5), Inches(1.5), Inches(4), Inches(3)
273
+ elif layout_type == "image_left":
274
+ left, top, width, height = Inches(0.5), Inches(1.5), Inches(4), Inches(3)
275
+ elif layout_type == "image_top":
276
+ left, top, width, height = Inches(1), Inches(1), Inches(8), Inches(2.5)
277
+ elif layout_type == "image_background":
278
+ left, top, width, height = Inches(0), Inches(0), Inches(10), Inches(7.5)
279
+
280
+ picture = slide.shapes.add_picture(image_path, left, top, width, height)
281
+
282
+ # 背景圖片移到後層
283
+ if layout_type == "image_background":
284
+ picture.element.getparent().remove(picture.element)
285
+ slide.shapes._spTree.insert(2, picture.element)
286
+
287
+ except Exception as e:
288
+ print(f"添加圖片錯誤: {e}")
289
+
290
+ def adjust_content_layout(self, slide, layout_type):
291
+ """根據圖片位置調整文字內容位置"""
292
+ try:
293
+ content_shape = slide.placeholders[1]
294
+
295
+ if layout_type == "image_right":
296
+ content_shape.left = Inches(0.5)
297
+ content_shape.width = Inches(4.5)
298
+ elif layout_type == "image_left":
299
+ content_shape.left = Inches(5)
300
+ content_shape.width = Inches(4.5)
301
+ elif layout_type == "image_top":
302
+ content_shape.top = Inches(4)
303
+ content_shape.height = Inches(3)
304
+
305
+ except Exception as e:
306
+ print(f"調整版面錯誤: {e}")
307
+
308
+ def format_title(self, shape, theme, font_size):
309
+ """格式化標題"""
310
+ paragraph = shape.text_frame.paragraphs[0]
311
+ paragraph.font.name = "Microsoft JhengHei"
312
+ paragraph.font.size = Pt(font_size)
313
+ paragraph.font.color.rgb = theme["title_color"]
314
+ paragraph.alignment = PP_ALIGN.CENTER
315
+
316
+ def format_content(self, paragraph, theme, font_size):
317
+ """格式化內容"""
318
+ paragraph.font.name = "Microsoft JhengHei"
319
+ paragraph.font.size = Pt(font_size)
320
+ paragraph.font.color.rgb = theme["text_color"]
321
+
322
+ def create_presentation_with_images(self, topic, theme_name="商務專業",
323
+ slide_count=5, image_style="professional"):
324
+ """建立包含圖片的簡報"""
325
+
326
+ # 生成內容結構
327
+ structure = self.generate_content_with_gemini(topic, slide_count)
328
+ theme = self.themes[theme_name]
329
+ layout_type = theme["layout"]
330
+
331
+ # 建立簡報
332
+ prs = Presentation()
333
+
334
+ # 建立標題頁
335
+ title_slide = prs.slides.add_slide(prs.slide_layouts[0])
336
+ title_shape = title_slide.shapes.title
337
+ subtitle_shape = title_slide.placeholders[1]
338
+
339
+ title_shape.text = structure["title"]
340
+ subtitle_shape.text = structure["subtitle"]
341
+
342
+ # 格式化標題頁
343
+ self.format_title(title_shape, theme, 44)
344
+ self.format_title(subtitle_shape, theme, 24)
345
+
346
+ # 為標題頁添加主題相關圖片
347
+ main_keywords = f"{topic} presentation cover"
348
+ title_photos = self.search_pexels_with_style(main_keywords, image_style)
349
+ if title_photos:
350
+ title_image_url = self.select_best_image(title_photos, structure["title"])
351
+ if title_image_url:
352
+ title_image_path = self.download_image(title_image_url)
353
+ if title_image_path:
354
+ self.add_image_to_slide(title_slide, title_image_path, "image_background")
355
+
356
+ # 建立內容頁
357
+ for i, slide_data in enumerate(structure["slides"]):
358
+ slide = prs.slides.add_slide(prs.slide_layouts[1])
359
+
360
+ # 設定標題
361
+ title_shape = slide.shapes.title
362
+ title_shape.text = slide_data["title"]
363
+ self.format_title(title_shape, theme, 32)
364
+
365
+ # 根據版面類型調整內容位置
366
+ self.adjust_content_layout(slide, layout_type)
367
+
368
+ # 設定內容
369
+ content_shape = slide.placeholders[1]
370
+ text_frame = content_shape.text_frame
371
+ text_frame.clear()
372
+
373
+ for j, point in enumerate(slide_data["content"]):
374
+ if j == 0:
375
+ p = text_frame.paragraphs[0]
376
+ else:
377
+ p = text_frame.add_paragraph()
378
+
379
+ p.text = point
380
+ p.level = 0
381
+ self.format_content(p, theme, 18)
382
+
383
+ # 搜尋並添加風格化圖片
384
+ keywords = slide_data.get("image_keywords", f"{topic} slide {i+1}")
385
+ photos = self.search_pexels_with_style(keywords, image_style)
386
+
387
+ if photos:
388
+ image_url = self.select_best_image(photos, slide_data["title"])
389
+ if image_url:
390
+ image_path = self.download_image(image_url)
391
+ if image_path:
392
+ self.add_image_to_slide(slide, image_path, layout_type)
393
+
394
+ # 建立感謝頁
395
+ self.add_thank_you_slide(prs, theme, image_style)
396
+
397
+ return prs, structure
398
+
399
+ def add_thank_you_slide(self, prs, theme, image_style):
400
+ """添加感謝頁"""
401
+ thank_slide = prs.slides.add_slide(prs.slide_layouts[5])
402
+
403
+ # 添加感謝圖片
404
+ thank_keywords = "thank you appreciation success celebration"
405
+ thank_photos = self.search_pexels_with_style(thank_keywords, image_style)
406
+ if thank_photos:
407
+ thank_image_url = self.select_best_image(thank_photos)
408
+ if thank_image_url:
409
+ thank_image_path = self.download_image(thank_image_url)
410
+ if thank_image_path:
411
+ self.add_image_to_slide(thank_slide, thank_image_path, "image_background")
412
+
413
+ # 添加感謝文字
414
+ left, top, width, height = Inches(1), Inches(3), Inches(8), Inches(2)
415
+ textbox = thank_slide.shapes.add_textbox(left, top, width, height)
416
+ text_frame = textbox.text_frame
417
+ text_frame.text = "謝謝聆聽\nThank You"
418
+
419
+ for paragraph in text_frame.paragraphs:
420
+ paragraph.font.name = "Microsoft JhengHei"
421
+ paragraph.font.size = Pt(48)
422
+ paragraph.font.color.rgb = theme["title_color"]
423
+ paragraph.alignment = PP_ALIGN.CENTER
424
+
425
+ def save_presentation(self, prs, filename):
426
+ """儲存簡報"""
427
+ temp_dir = tempfile.mkdtemp()
428
+ filepath = os.path.join(temp_dir, filename)
429
+ prs.save(filepath)
430
+ return filepath
431
+
432
+ def generate_preview_text(self, structure):
433
+ """生成簡報預覽文字"""
434
+ preview = f"📊 {structure['title']}\n"
435
+ preview += f" {structure['subtitle']}\n\n"
436
+
437
+ for i, slide in enumerate(structure['slides'], 1):
438
+ preview += f"{i}. {slide['title']}\n"
439
+ for point in slide['content'][:2]:
440
+ preview += f" • {point}\n"
441
+ if len(slide['content']) > 2:
442
+ preview += f" • ...(共 {len(slide['content'])} 個重點)\n"
443
+ preview += "\n"
444
+
445
+ return preview
446
+
447
+ def generate_ppt_with_gemini(gemini_api_key, pexels_api_key, topic, theme, slide_count, image_style):
448
+ """生成簡報的主要函數"""
449
+
450
+ # 檢查輸入
451
+ if not gemini_api_key.strip():
452
+ return None, "", "❌ 請輸入 Gemini API 金鑰"
453
+
454
+ if not pexels_api_key.strip():
455
+ return None, "", "❌ 請輸入 Pexels API 金鑰"
456
+
457
+ if not topic.strip():
458
+ return None, "", "❌ 請輸入簡報主題"
459
+
460
+ generator = GeminiPPTGenerator()
461
+
462
+ try:
463
+ # 設定 API
464
+ success, message = generator.setup_apis(gemini_api_key, pexels_api_key)
465
+ if not success:
466
+ return None, "", message
467
+
468
+ # 生成簡報
469
+ prs, structure = generator.create_presentation_with_images(
470
+ topic, theme, slide_count, image_style
471
+ )
472
+
473
+ # 生成預覽
474
+ preview = generator.generate_preview_text(structure)
475
+
476
+ # 儲存檔案
477
+ filename = f"{topic.replace(' ', '_')}_{image_style}_簡報.pptx"
478
+ filepath = generator.save_presentation(prs, filename)
479
+
480
+ success_msg = f"✅ 成功生成《{topic}》{image_style}風格簡報!({slide_count} 頁,含圖片)"
481
+
482
+ return filepath, preview, success_msg
483
+
484
+ except Exception as e:
485
+ return None, "", f"❌ 生成失敗:{str(e)}"
486
+
487
+ # Gradio 介面
488
+ def create_gemini_interface():
489
+ """建立 Gradio 介面"""
490
+ with gr.Blocks(title="Gemini AI 圖文簡報生成器", theme=gr.themes.Soft()) as iface:
491
+ gr.Markdown("# 🤖 Gemini AI 智能圖文簡報生成器")
492
+ gr.Markdown("**使用 Google Gemini 2.0 + Pexels 圖庫**,智能生成專業圖文簡報")
493
+
494
+ # API 設定區域
495
+ with gr.Group():
496
+ gr.Markdown("### 🔑 API 設定")
497
+ with gr.Row():
498
+ gemini_api_input = gr.Textbox(
499
+ label="🤖 Gemini API Key",
500
+ placeholder="請輸入你的 Gemini API 金鑰",
501
+ type="password",
502
+ info="免費額度,前往 https://ai.google.dev/ 獲取"
503
+ )
504
+ pexels_api_input = gr.Textbox(
505
+ label="📸 Pexels API Key",
506
+ placeholder="請輸入你的 Pexels API 金鑰",
507
+ type="password",
508
+ info="免費 200次/月,前往 https://www.pexels.com/api/ 獲取"
509
+ )
510
+
511
+ # 主要設定區域
512
+ with gr.Row():
513
+ with gr.Column(scale=2):
514
+ topic_input = gr.Textbox(
515
+ label="📝 簡報主題",
516
+ placeholder="請輸入具體的簡報主題...",
517
+ value="人工智慧在現代教育中的應用與挑戰"
518
+ )
519
+
520
+ with gr.Row():
521
+ theme_dropdown = gr.Dropdown(
522
+ choices=["商務專業", "科技創新", "創意設計", "教育學術"],
523
+ value="商務專業",
524
+ label="🎨 版型風格"
525
+ )
526
+
527
+ image_style_dropdown = gr.Dropdown(
528
+ choices=["professional", "creative", "minimalist", "modern", "natural", "technology"],
529
+ value="professional",
530
+ label="🖼️ 圖片風格"
531
+ )
532
+
533
+ slide_count = gr.Slider(
534
+ minimum=3,
535
+ maximum=10,
536
+ value=6,
537
+ step=1,
538
+ label="📄 投影片數量"
539
+ )
540
+
541
+ generate_btn = gr.Button("🚀 生成專業簡報", variant="primary", size="lg")
542
+
543
+ with gr.Column(scale=1):
544
+ status_output = gr.Textbox(label="📊 生成狀態", interactive=False)
545
+ file_output = gr.File(label="📁 下載簡報")
546
+
547
+ # 預覽區域
548
+ with gr.Group():
549
+ gr.Markdown("### 📋 簡報預覽")
550
+ preview_output = gr.Textbox(
551
+ label="內容大綱",
552
+ placeholder="生成後將顯示簡報大綱...",
553
+ lines=8,
554
+ interactive=False
555
+ )
556
+
557
+ # 說明區域
558
+ with gr.Accordion("📖 使用說明與功能特色", open=False):
559
+ gr.Markdown("""
560
+ ### 🌟 核心特色
561
+
562
+ #### 🤖 Google Gemini 2.0 Flash
563
+ - **最新模型**:使用 Gemini 2.0 Flash Preview 版本
564
+ - **免費額度**:Google 提供慷慨的免費使用額度
565
+ - **中文優化**:對繁體中文有優秀的理解和生成能力
566
+ - **結構化輸出**:精確生成 JSON 格式的簡報結構
567
+
568
+ #### 📸 Pexels 圖片整合
569
+ - **百萬圖庫**:Pexels 提供高品質免費圖片
570
+ - **智能匹配**:AI 為每頁生成最適合的搜尋關鍵字
571
+ - **風格選擇**:6 種圖片風格滿足不同需求
572
+ - **自動配圖**:每張投影片自動配上相關圖片
573
+
574
+ #### 🎨 專業版面設計
575
+ - **4 種版型**:商務、科技、創意、學術風格
576
+ - **智能排版**:根據版型自動調整圖文位置
577
+ - **色彩搭配**:專業的色彩主題設計
578
+ - **中文字型**:完美支援繁體中文顯示
579
+
580
+ ### 📋 使用步驟
581
+ 1. **獲取 API 金鑰**:
582
+ - Gemini API:前往 [Google AI Studio](https://ai.google.dev/) 免費申請
583
+ - Pexels API:前往 [Pexels API](https://www.pexels.com/api/) 免費申請(200次/月)
584
+
585
+ 2. **輸入 API 金鑰**:在上方輸入框中填入你的 API 金鑰
586
+
587
+ 3. **設定簡報參數**:
588
+ - 輸入具體明確的簡報主題
589
+ - 選擇適合的版型和圖片風格
590
+ - 設定所需的投影片數量
591
+
592
+ 4. **生成簡報**:點擊生成按鈕,系統將自動完成所有工作
593
+
594
+ 5. **下載使用**:獲得完整的 .pptx 檔案,可直接在 PowerPoint 中使用
595
+
596
+ ### 💡 專業建議
597
+ - **主題要具體**:「AI在醫療診斷的應用」比「人工智慧」效果更好
598
+ - **選對風格**:商務場合用「professional」,創意展示用「creative」
599
+ - **適當頁數**:建議 5-8 頁,內容豐富但不冗長
600
+ - **測試 API**:第一次使用建議先測試 API 連接是否正常
601
+
602
+ ### 🔧 技術特點
603
+ - **純 Python 實現**:不需要安裝 Microsoft Office
604
+ - **即時生成**:通常 30-60 秒完成整個簡報
605
+ - **高品質輸出**:生成的 .pptx 檔案完全相容 PowerPoint
606
+ - **跨平台支援**:Windows、macOS、Linux 都能正常使用
607
+ """)
608
+
609
+ # 事件綁定
610
+ generate_btn.click(
611
+ fn=generate_ppt_with_gemini,
612
+ inputs=[
613
+ gemini_api_input,
614
+ pexels_api_input,
615
+ topic_input,
616
+ theme_dropdown,
617
+ slide_count,
618
+ image_style_dropdown
619
+ ],
620
+ outputs=[file_output, preview_output, status_output]
621
+ )
622
+
623
+ return iface
624
+
625
+ if __name__ == "__main__":
626
+ # 啟動應用
627
+ iface = create_gemini_interface()
628
+ iface.launch(
629
+ server_name="0.0.0.0",
630
+ server_port=7860,
631
+ share=True
632
+ )
cht.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9d5bf4932d31fe94c18cd8cfddc98bc1b14ce10f4e354c682179db290a99c825
3
+ size 4911464
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ python-pptx>=0.6.21
3
+ Pillow>=9.0.0
4
+ requests>=2.28.0
5
+ google-generativeai>=0.3.0