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