import os
import gradio as gr
import shutil
import tempfile
import uuid
from pathlib import Path
import json
import base64
from PIL import Image
import fitz # PyMuPDF for PDF handling
# Constants
TEMP_DIR = "temp"
UPLOAD_DIR = os.path.join(TEMP_DIR, "uploads")
OUTPUT_DIR = os.path.join(TEMP_DIR, "output")
THUMBS_DIR = os.path.join(OUTPUT_DIR, "thumbs")
HTML_DIR = os.path.join(OUTPUT_DIR, "html")
# Ensure directories exist
for dir_path in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_DIR, HTML_DIR]:
os.makedirs(dir_path, exist_ok=True)
def create_thumbnail(image_path, output_path, size=(300, 300)):
"""Create a thumbnail from an image."""
try:
with Image.open(image_path) as img:
img.thumbnail(size, Image.LANCZOS)
img.save(output_path)
return output_path
except Exception as e:
print(f"Error creating thumbnail: {e}")
return None
def process_pdf(pdf_path, session_id):
"""Extract pages from a PDF and save as images with thumbnails."""
pages_info = []
output_folder = os.path.join(OUTPUT_DIR, session_id)
thumbs_folder = os.path.join(THUMBS_DIR, session_id)
os.makedirs(output_folder, exist_ok=True)
os.makedirs(thumbs_folder, exist_ok=True)
try:
# Open the PDF
pdf_document = fitz.open(pdf_path)
# Process each page
for page_num, page in enumerate(pdf_document):
# Render page to an image with a higher resolution
pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
image_path = os.path.join(output_folder, f"page_{page_num + 1}.png")
pix.save(image_path)
# Create thumbnail
thumb_path = os.path.join(thumbs_folder, f"thumb_{page_num + 1}.png")
create_thumbnail(image_path, thumb_path)
# Add simple interactive content to first page
html_content = ""
if page_num == 0: # First page example
html_content = """
인터랙티브 플립북 예제
이 페이지는 인터랙티브 컨텐츠 기능을 보여줍니다.
"""
# Get relative web paths for the HTML file
rel_image_path = os.path.relpath(image_path, HTML_DIR).replace("\\", "/")
rel_thumb_path = os.path.relpath(thumb_path, HTML_DIR).replace("\\", "/")
# Add page info with interactive content
pages_info.append({
"src": rel_image_path,
"thumb": rel_thumb_path,
"title": f"페이지 {page_num + 1}",
"htmlContent": html_content if html_content else None
})
print(f"Processed PDF page {page_num+1}: {rel_image_path}")
return pages_info
except Exception as e:
print(f"Error processing PDF: {e}")
return []
def process_images(image_paths, session_id):
"""Process uploaded images and create thumbnails."""
pages_info = []
output_folder = os.path.join(OUTPUT_DIR, session_id)
thumbs_folder = os.path.join(THUMBS_DIR, session_id)
os.makedirs(output_folder, exist_ok=True)
os.makedirs(thumbs_folder, exist_ok=True)
for i, img_path in enumerate(image_paths):
try:
# Copy original image to output folder
dest_path = os.path.join(output_folder, f"image_{i + 1}.png")
shutil.copy(img_path, dest_path)
# Create thumbnail
thumb_path = os.path.join(thumbs_folder, f"thumb_{i + 1}.png")
create_thumbnail(img_path, thumb_path)
# Add interactive content as simple text overlays to avoid compatibility issues
html_content = ""
if i == 0: # First image example with HTML content
html_content = """
이미지 갤러리
갤러리의 첫 번째 이미지입니다.
"""
elif i == 1: # Second image
html_content = """
두 번째 이미지
페이지를 넘기거나 모서리를 드래그하여 이미지를 탐색할 수 있습니다.
"""
# Get relative web paths for the HTML file
rel_image_path = os.path.relpath(dest_path, HTML_DIR).replace("\\", "/")
rel_thumb_path = os.path.relpath(thumb_path, HTML_DIR).replace("\\", "/")
# Create a simpler page structure to avoid potential compatibility issues
page_info = {
"src": rel_image_path,
"thumb": rel_thumb_path,
"title": f"이미지 {i + 1}",
"htmlContent": html_content if html_content else None
}
pages_info.append(page_info)
print(f"Processed image {i+1}: {rel_image_path}")
except Exception as e:
print(f"Error processing image {img_path}: {e}")
return pages_info
def create_flipbook_from_pdf(pdf_file, view_mode="2d", skin="light"):
"""Create a flipbook from uploaded PDF."""
try:
session_id = str(uuid.uuid4())
pages_info = []
debug_info = ""
if pdf_file is not None:
# In Gradio, pdf_file is a file path string, not the actual content
pdf_path = pdf_file.name # Get the file path
debug_info += f"PDF path: {pdf_path}\n"
# Process PDF using the file path directly
pages_info = process_pdf(pdf_path, session_id)
debug_info += f"Number of pages processed: {len(pages_info)}\n"
else:
return """
PDF 파일을 업로드해주세요.
""", "No file uploaded"
if not pages_info:
return """
PDF 파일 처리 중 오류가 발생했습니다. 다시 시도해주세요.
""", "No pages processed"
# Generate HTML file and return iframe HTML
iframe_html = generate_flipbook_html(pages_info, session_id, view_mode, skin)
debug_info += f"HTML file generated with view mode: {view_mode}, skin: {skin}\n"
return iframe_html, debug_info
except Exception as e:
error_msg = f"Error creating flipbook from PDF: {e}"
print(error_msg)
return f"""
오류가 발생했습니다: {str(e)}
""", error_msg
def create_flipbook_from_images(images, view_mode="2d", skin="light"):
"""Create a flipbook from uploaded images."""
try:
session_id = str(uuid.uuid4())
pages_info = []
debug_info = ""
if images is not None and len(images) > 0:
# Process images using file paths
image_paths = [img.name for img in images]
debug_info += f"Image paths: {image_paths}\n"
pages_info = process_images(image_paths, session_id)
debug_info += f"Number of images processed: {len(pages_info)}\n"
else:
return """
최소 한 개 이상의 이미지를 업로드해주세요.
""", "No images uploaded"
if not pages_info:
return """
이미지 처리 중 오류가 발생했습니다. 다시 시도해주세요.
""", "No images processed"
# Generate HTML file and return iframe HTML
iframe_html = generate_flipbook_html(pages_info, session_id, view_mode, skin)
debug_info += f"HTML file generated with view mode: {view_mode}, skin: {skin}\n"
return iframe_html, debug_info
except Exception as e:
error_msg = f"Error creating flipbook from images: {e}"
print(error_msg)
return f"""
오류가 발생했습니다: {str(e)}
""", error_msg
def generate_flipbook_html(pages_info, session_id, view_mode, skin):
"""Generate a standalone HTML file for the flipbook and return iframe HTML."""
# Clean up pages_info to remove None values for JSON serialization
for page in pages_info:
if "htmlContent" in page and page["htmlContent"] is None:
del page["htmlContent"]
if "items" in page and page["items"] is None:
del page["items"]
# Convert pages_info to JSON for JavaScript
pages_json = json.dumps(pages_info)
# Create a unique filename for this session
html_filename = f"flipbook_{session_id}.html"
html_path = os.path.join(HTML_DIR, html_filename)
# Create the full HTML file content
html_content = f"""
3D 플립북
플립북 로딩 중...
"""
# Write the HTML file
with open(html_path, 'w', encoding='utf-8') as f:
f.write(html_content)
# Return iframe HTML to embed in Gradio
iframe_height = 700
iframe_html = f"""
"""
return iframe_html
# Define the Gradio interface
with gr.Blocks(title="3D Flipbook Viewer") as demo:
gr.Markdown("# 3D Flipbook Viewer")
gr.Markdown("""
## 3D 플립북 뷰어
PDF 파일이나 여러 이미지를 업로드하여 인터랙티브 3D 플립북을 만들 수 있습니다.
### 특징:
- 페이지 넘김 효과와 함께 인터랙티브한 기능 제공
- 첫 페이지에는 예시로 인터랙티브 요소가 포함됨
- 툴바를 사용하거나 페이지 모서리를 드래그하여 탐색
- 썸네일 보기로 빠른 탐색 가능
- 전체 화면으로 전환하여 더 나은 보기 경험
""")
with gr.Tabs():
with gr.TabItem("PDF 업로드"):
pdf_file = gr.File(label="PDF 파일 업로드", file_types=[".pdf"])
with gr.Accordion("고급 설정", open=False):
pdf_view_mode = gr.Radio(
choices=["webgl", "3d", "2d", "swipe"],
value="2d", # Changed default to 2d for better compatibility
label="뷰 모드",
info="WebGL: 최고 품질, 2D: 가장 안정적, 3D: 중간, Swipe: 모바일용"
)
pdf_skin = gr.Radio(
choices=["light", "dark", "gradient"],
value="light",
label="스킨",
info="light: 밝은 테마, dark: 어두운 테마, gradient: 그라데이션 테마"
)
pdf_create_btn = gr.Button("PDF에서 플립북 만들기", variant="primary", size="lg")
pdf_debug = gr.Textbox(label="디버그 정보", visible=False)
pdf_output = gr.HTML(label="플립북 결과물")
# Set up PDF event handler
pdf_create_btn.click(
fn=create_flipbook_from_pdf,
inputs=[pdf_file, pdf_view_mode, pdf_skin],
outputs=[pdf_output, pdf_debug]
)
with gr.TabItem("이미지 업로드"):
images = gr.File(label="이미지 파일 업로드", file_types=["image"], file_count="multiple")
with gr.Accordion("고급 설정", open=False):
img_view_mode = gr.Radio(
choices=["webgl", "3d", "2d", "swipe"],
value="2d", # Changed default to 2d for better compatibility
label="뷰 모드",
info="WebGL: 최고 품질, 2D: 가장 안정적, 3D: 중간, Swipe: 모바일용"
)
img_skin = gr.Radio(
choices=["light", "dark", "gradient"],
value="light",
label="스킨",
info="light: 밝은 테마, dark: 어두운 테마, gradient: 그라데이션 테마"
)
img_create_btn = gr.Button("이미지에서 플립북 만들기", variant="primary", size="lg")
img_debug = gr.Textbox(label="디버그 정보", visible=False)
img_output = gr.HTML(label="플립북 결과물")
# Set up image event handler
img_create_btn.click(
fn=create_flipbook_from_images,
inputs=[images, img_view_mode, img_skin],
outputs=[img_output, img_debug]
)
gr.Markdown("""
### 사용법:
1. 컨텐츠 유형에 따라 탭을 선택하세요 (PDF 또는 이미지)
2. 파일을 업로드하세요
3. 필요에 따라 고급 설정에서 뷰 모드와 스킨을 조정하세요
4. 플립북 만들기 버튼을 클릭하세요
5. 출력 영역에서 플립북과 상호작용하세요
### 참고:
- 처음 페이지에는 예시로 인터랙티브 요소와 링크가 포함되어 있습니다
- 최상의 결과를 위해 선명한 텍스트와 이미지가 있는 PDF를 사용하세요
- 지원되는 이미지 형식: JPG, PNG, GIF 등
- 플립북이 보이지 않는 경우, 2D 모드를 선택하고 다시 시도해보세요
""")
# Launch the app
if __name__ == "__main__":
demo.launch(share=True) # Set share=True to create a public link