pdf2ppt-api / webui.py
Nanny7's picture
feat: restructure for Hugging Face Spaces deployment
f996a9b
Raw
History Blame Contribute Delete
12.3 kB
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)