Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import tempfile | |
| import zipfile | |
| import shutil | |
| from pdf2image import convert_from_path | |
| from ppt_converter import create_pptx_from_images, reconstruct_pptx_from_zip | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="PDF/圖片轉可編輯PPTX - 零成本在線版", | |
| page_icon="📄", | |
| layout="centered" | |
| ) | |
| # Custom CSS for premium aesthetics (Dark Mode / Glassmorphism Feel) | |
| st.markdown(""" | |
| <style> | |
| /* Main Background & Fonts */ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap'); | |
| .stApp { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| } | |
| /* Title Style */ | |
| .title-container { | |
| text-align: center; | |
| padding: 1.5rem 0; | |
| margin-bottom: 2rem; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| /* Tab buttons and cards */ | |
| .css-1n76uvr { | |
| gap: 0rem; | |
| } | |
| div.stButton > button:first-child { | |
| background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); | |
| color: white; | |
| border: none; | |
| padding: 0.6rem 2rem; | |
| border-radius: 8px; | |
| font-weight: 600; | |
| letter-spacing: 0.5px; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 4px 12px rgba(124, 58, 237, 0.3); | |
| } | |
| div.stButton > button:first-child:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(124, 58, 237, 0.4); | |
| background: linear-gradient(135deg, #4338ca 0%, #6d28d9 100%); | |
| } | |
| .card { | |
| background: rgba(255, 255, 255, 0.05); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| /* Steps styling for Colab */ | |
| .step-number { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 28px; | |
| height: 28px; | |
| border-radius: 50%; | |
| background-color: #4f46e5; | |
| color: white; | |
| font-weight: bold; | |
| margin-right: 8px; | |
| } | |
| .colab-btn { | |
| display: inline-block; | |
| padding: 0.5rem 1.2rem; | |
| background-color: #f9ab00; | |
| color: #1e1e1e !important; | |
| border-radius: 6px; | |
| font-weight: bold; | |
| text-decoration: none; | |
| transition: background-color 0.3s; | |
| margin: 10px 0; | |
| box-shadow: 0 4px 10px rgba(249, 171, 0, 0.2); | |
| } | |
| .colab-btn:hover { | |
| background-color: #e39a00; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Title and introduction | |
| st.markdown("<div class='title-container'><h1>📄 PDF / 圖片 轉 可編輯 PPTX</h1></div>", unsafe_allow_html=True) | |
| st.markdown(""" | |
| <div style='text-align: center; margin-top: -20px; margin-bottom: 30px; color: #6b7280; font-size: 1.05rem;'> | |
| 將無文字圖層的 AI 扁平簡報(如 NotebookLM)完美轉回 **「圖文分離、背景修復、可編輯」** 的 PowerPoint 檔! | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Define Tabs | |
| tab1, tab2 = st.tabs(["⚡ 在線極速轉換 (CPU)", "🚀 雲端高精轉換 (Colab GPU 匯入)"]) | |
| # ----------------- | |
| # Tab 1: Local CPU Mode | |
| # ----------------- | |
| with tab1: | |
| st.markdown("### ⚡ 在線極速轉換") | |
| st.write("適合一般排版的 PDF 簡報。使用伺服器 CPU 進行 RapidOCR 辨識與 ONNX LaMa 去字修補,隱私安全,一鍵完成。") | |
| uploaded_file = st.file_uploader("選擇上傳您的 PDF 或圖片檔案", type=['pdf', 'png', 'jpg', 'jpeg', 'webp'], key="cpu_uploader") | |
| with st.expander("⚙️ 進階辨識設定"): | |
| lang_options = ['ch_tra', 'en', 'ja', 'ko', 'ch_sim'] | |
| selected_langs = st.multiselect( | |
| "選擇 OCR 辨識語言", | |
| options=lang_options, | |
| default=['ch_tra', 'en'], | |
| help="選擇檔案中包含的語言,可大幅提升辨識準確度。" | |
| ) | |
| high_accuracy = st.toggle( | |
| "開啟高精度影像增強 (CLAHE)", | |
| value=False, | |
| help="開啟後會對圖片進行對比度增強,提升模糊與微小字體的辨識率,但會稍微增加處理時間。" | |
| ) | |
| st.divider() | |
| if uploaded_file is not None: | |
| if st.button("🚀 開始線上轉換", key="cpu_convert_btn"): | |
| if not selected_langs: | |
| st.error("請至少選擇一種辨識語言!") | |
| else: | |
| progress_container = st.empty() | |
| progress_bar = st.progress(0) | |
| with st.spinner('處理中,正在進行 OCR 辨識與背景 AI 修復,請稍候...'): | |
| try: | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| input_path = os.path.join(temp_dir, uploaded_file.name) | |
| output_pptx_path = os.path.join(temp_dir, "output.pptx") | |
| # Save uploaded file | |
| with open(input_path, "wb") as f: | |
| f.write(uploaded_file.getbuffer()) | |
| ext = os.path.splitext(input_path)[1].lower() | |
| image_paths = [] | |
| if ext == ".pdf": | |
| progress_container.info("正在解析 PDF 並轉換為高清圖片...") | |
| images = convert_from_path(input_path) | |
| total_p = len(images) | |
| for idx, img in enumerate(images): | |
| temp_img_path = os.path.join(temp_dir, f"page_{idx}.png") | |
| img.save(temp_img_path, "PNG") | |
| image_paths.append(temp_img_path) | |
| progress_bar.progress(int((idx + 1) / total_p * 20)) # up to 20% | |
| elif ext in [".png", ".jpg", ".jpeg", ".webp"]: | |
| image_paths = [input_path] | |
| progress_bar.progress(20) | |
| # Callback for conversion progress | |
| def update_conversion_progress(current, total): | |
| percent = 20 + int((current / total) * 80) | |
| progress_bar.progress(min(percent, 100)) | |
| progress_container.info(f"正在執行 AI 去字與重組簡報:第 {current} / {total} 頁...") | |
| return True | |
| # Convert | |
| success = create_pptx_from_images( | |
| image_paths, | |
| output_pptx_path, | |
| selected_langs, | |
| high_accuracy=high_accuracy, | |
| progress_callback=update_conversion_progress | |
| ) | |
| if success: | |
| # Read results | |
| with open(output_pptx_path, "rb") as f: | |
| pptx_data = f.read() | |
| progress_container.success("🎉 轉換成功!請點擊下方按鈕下載 PPTX。") | |
| progress_bar.progress(100) | |
| st.download_button( | |
| label="⬇️ 下載可編輯 PPTX 檔案", | |
| data=pptx_data, | |
| file_name=f"{os.path.splitext(uploaded_file.name)[0]}_editable.pptx", | |
| mime="application/vnd.openxmlformats-officedocument.presentationml.presentation", | |
| key="cpu_download" | |
| ) | |
| else: | |
| progress_container.error("轉換取消或失敗。") | |
| except Exception as e: | |
| progress_container.error(f"轉換過程中發生錯誤: {str(e)}") | |
| st.exception(e) | |
| # ----------------- | |
| # Tab 2: Colab GPU Import Mode | |
| # ----------------- | |
| with tab2: | |
| st.markdown("### 🚀 雲端高精轉換 (Google Colab GPU 分流)") | |
| st.write("適用於多欄位、包含複雜圖表或超大檔案的簡報。此模式利用多模態大模型進行視覺定位,提供最極致的還原效果,且**完全免費**。") | |
| st.markdown(""" | |
| <div class="card"> | |
| <h4 style="margin-top:0;">💡 簡單三步驟完成高精度轉換:</h4> | |
| <div style="margin-bottom:10px;"> | |
| <span class="step-number">1</span> | |
| 點擊下方按鈕,在您自己的瀏覽器中打開 Google Colab 筆記本(會自動分配免費的 T4 GPU 算力): | |
| </div> | |
| <div style="text-align:center;"> | |
| <a href="https://colab.research.google.com/" target="_blank" class="colab-btn">💻 開啟 Google Colab 轉換腳本</a> | |
| </div> | |
| <div style="margin-bottom:10px; margin-top:10px;"> | |
| <span class="step-number">2</span> | |
| 在 Colab 中上傳您的 PDF 檔案,點擊「全部執行」,等待 1~3 分鐘後下載產生的 <code>Pack.zip</code> 壓縮包。 | |
| </div> | |
| <div> | |
| <span class="step-number">3</span> | |
| 將下載的 <code>Pack.zip</code> 拖入下方上傳區,即可在一秒內拼裝並下載您的完美可編輯 PPTX! | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| zip_file = st.file_uploader("請上傳 Colab 產出的 Pack.zip 壓縮包", type=['zip'], key="zip_uploader") | |
| if zip_file is not None: | |
| if st.button("🎨 一秒拼裝簡報", key="zip_assemble_btn"): | |
| with st.spinner('正在解壓並映射座標...'): | |
| try: | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| zip_path = os.path.join(temp_dir, zip_file.name) | |
| output_pptx_path = os.path.join(temp_dir, "reconstructed.pptx") | |
| # Save uploaded zip | |
| with open(zip_path, "wb") as f: | |
| f.write(zip_file.getbuffer()) | |
| # Reconstruct | |
| success = reconstruct_pptx_from_zip(zip_path, output_pptx_path) | |
| if success: | |
| with open(output_pptx_path, "rb") as f: | |
| pptx_data = f.read() | |
| st.success("🎉 拼裝完成!請點擊下方按鈕下載 PPTX。") | |
| st.download_button( | |
| label="⬇️ 下載已拼裝 PPTX 檔案", | |
| data=pptx_data, | |
| file_name=f"{os.path.splitext(zip_file.name)[0]}_assembled.pptx", | |
| mime="application/vnd.openxmlformats-officedocument.presentationml.presentation", | |
| key="zip_download" | |
| ) | |
| else: | |
| st.error("組裝失敗,請確認上傳的 ZIP 封包格式是否正確。") | |
| except Exception as e: | |
| st.error(f"組裝過程中發生錯誤: {str(e)}") | |
| st.exception(e) | |
| # Footer | |
| st.markdown(""" | |
| <div style='text-align: center; margin-top: 50px; font-size: 0.8rem; color: #9ca3af; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 15px;'> | |
| PDF/圖片轉可編輯PPTX系統 • 部署於 Hugging Face Spaces (CPU Basic) • 開源免運維版本 | |
| </div> | |
| """, unsafe_allow_html=True) | |