Spaces:
Paused
Paused
| import os | |
| import gradio as gr | |
| import shutil | |
| import tempfile | |
| import uuid | |
| from pathlib import Path | |
| import json | |
| 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") | |
| # Ensure directories exist | |
| for dir_path in [TEMP_DIR, UPLOAD_DIR, OUTPUT_DIR, THUMBS_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 interactive content to first page as an example | |
| html_content = "" | |
| items = [] | |
| if page_num == 0: # First page example | |
| html_content = """ | |
| <div style="position: absolute; top: 50px; left: 50px; background-color: rgba(255,255,255,0.7); padding: 10px; border-radius: 5px;"> | |
| <h2 style="color: #333;">Interactive Flipbook Example</h2> | |
| <p style="color: #666;">This page demonstrates interactive content capabilities.</p> | |
| <a href="#" data-page="2" style="color: blue; text-decoration: underline;">Go to page 2</a> | |
| </div> | |
| """ | |
| elif page_num == 1: # Second page example with items | |
| items = [ | |
| { | |
| "type": "link", | |
| "x": 50, | |
| "y": 50, | |
| "width": 200, | |
| "height": 50, | |
| "page": 1, | |
| "title": "Back to first page" | |
| } | |
| ] | |
| # Add page info with interactive content | |
| pages_info.append({ | |
| "src": os.path.join("output", session_id, f"page_{page_num + 1}.png"), | |
| "thumb": os.path.join("thumbs", session_id, f"thumb_{page_num + 1}.png"), | |
| "title": f"Page {page_num + 1}", | |
| "htmlContent": html_content if html_content else None, | |
| "items": items if items else None | |
| }) | |
| 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 to specific pages as examples | |
| html_content = "" | |
| items = [] | |
| if i == 0: # First image example with HTML content | |
| html_content = """ | |
| <div style="position: absolute; top: 50px; left: 50px; background-color: rgba(255,255,255,0.7); padding: 10px; border-radius: 5px;"> | |
| <h2 style="color: #333;">Image Gallery</h2> | |
| <p style="color: #666;">This is the first image in your gallery.</p> | |
| <a href="#" data-page="2" style="color: blue; text-decoration: underline;">Next Image</a> | |
| </div> | |
| """ | |
| elif i == 1: # Second image with video example (if available) | |
| items = [ | |
| { | |
| "type": "link", | |
| "x": 50, | |
| "y": 50, | |
| "width": 200, | |
| "height": 50, | |
| "page": 1, | |
| "title": "Previous image" | |
| } | |
| ] | |
| # Example of adding a YouTube video if it's the second image | |
| if len(image_paths) > 1: | |
| html_content = """ | |
| <iframe class="flipbook-page-item" | |
| src="https://www.youtube.com/embed/dQw4w9WgXcQ" | |
| style="top:200px;left:50px;width:300px;height:200px;" | |
| frameborder="0" | |
| allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" | |
| allowfullscreen=""> | |
| </iframe> | |
| """ | |
| # Add page info with interactive content | |
| pages_info.append({ | |
| "src": os.path.join("output", session_id, f"image_{i + 1}.png"), | |
| "thumb": os.path.join("thumbs", session_id, f"thumb_{i + 1}.png"), | |
| "title": f"Image {i + 1}", | |
| "htmlContent": html_content if html_content else None, | |
| "items": items if items else None | |
| }) | |
| except Exception as e: | |
| print(f"Error processing image {img_path}: {e}") | |
| return pages_info | |
| def create_flipbook_from_pdf(pdf_file, view_mode="webgl", skin="light"): | |
| """Create a flipbook from uploaded PDF.""" | |
| try: | |
| session_id = str(uuid.uuid4()) | |
| pages_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 | |
| # Process PDF using the file path directly | |
| pages_info = process_pdf(pdf_path, session_id) | |
| else: | |
| return """<div style="color: red; padding: 20px;">Please upload a PDF file.</div>""" | |
| if not pages_info: | |
| return """<div style="color: red; padding: 20px;">Failed to process the uploaded PDF. Please try again.</div>""" | |
| # Create and return HTML for the flipbook | |
| flipbook_html = generate_flipbook_html(pages_info, session_id, view_mode, skin) | |
| return flipbook_html | |
| except Exception as e: | |
| print(f"Error creating flipbook from PDF: {e}") | |
| return f"""<div style="color: red; padding: 20px;">An error occurred: {str(e)}</div>""" | |
| def create_flipbook_from_images(images, view_mode="webgl", skin="light"): | |
| """Create a flipbook from uploaded images.""" | |
| try: | |
| session_id = str(uuid.uuid4()) | |
| pages_info = [] | |
| if images is not None and len(images) > 0: | |
| # Process images using file paths | |
| image_paths = [img.name for img in images] | |
| pages_info = process_images(image_paths, session_id) | |
| else: | |
| return """<div style="color: red; padding: 20px;">Please upload at least one image.</div>""" | |
| if not pages_info: | |
| return """<div style="color: red; padding: 20px;">Failed to process the uploaded images. Please try again.</div>""" | |
| # Create and return HTML for the flipbook | |
| flipbook_html = generate_flipbook_html(pages_info, session_id, view_mode, skin) | |
| return flipbook_html | |
| except Exception as e: | |
| print(f"Error creating flipbook from images: {e}") | |
| return f"""<div style="color: red; padding: 20px;">An error occurred: {str(e)}</div>""" | |
| def generate_flipbook_html(pages_info, session_id, view_mode, skin): | |
| """Generate HTML for the flipbook.""" | |
| # 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 custom ID for this flipbook | |
| flipbook_id = f"flipbook_{session_id}" | |
| # HTML template with embedded CSS and JavaScript | |
| html = f""" | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>3D Flipbook</title> | |
| <style> | |
| #flipbook-container {{ | |
| width: 100%; | |
| height: 600px; | |
| margin: 0 auto; | |
| position: relative; | |
| }} | |
| body, html {{ | |
| margin: 0; | |
| padding: 0; | |
| height: 100%; | |
| overflow: hidden; | |
| }} | |
| </style> | |
| <link rel="stylesheet" type="text/css" href="flipbook.css"> | |
| </head> | |
| <body> | |
| <div id="{flipbook_id}" class="flipbook-container"></div> | |
| <script src="flipbook.js"></script> | |
| <script src="flipbook.webgl.js"></script> | |
| <script src="flipbook.swipe.js"></script> | |
| <script src="flipbook.scroll.js"></script> | |
| <script src="flipbook.book3.js"></script> | |
| <script> | |
| // Initialize flipbook when page loads | |
| document.addEventListener('DOMContentLoaded', function() {{ | |
| const options = {{ | |
| pages: {pages_json}, | |
| viewMode: '{view_mode}', | |
| skin: '{skin}', | |
| responsiveView: true, | |
| singlePageMode: false, | |
| singlePageModeIfMobile: true, | |
| pageFlipDuration: 1, | |
| sound: true, | |
| backgroundMusic: false, | |
| thumbnailsOnStart: true, | |
| btnThumbs: {{ enabled: true }}, | |
| btnPrint: {{ enabled: true }}, | |
| btnDownloadPages: {{ enabled: true }}, | |
| btnDownloadPdf: {{ enabled: true }}, | |
| btnShare: {{ enabled: true }}, | |
| btnSound: {{ enabled: true }}, | |
| btnExpand: {{ enabled: true }}, | |
| rightToLeft: false, | |
| autoplayOnStart: false, | |
| autoplayInterval: 3000 | |
| }}; | |
| const container = document.getElementById('{flipbook_id}'); | |
| new FlipBook(container, options); | |
| }}); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| return html | |
| with gr.Blocks(title="3D Flipbook Viewer") as demo: | |
| gr.Markdown("# 3D Flipbook Viewer") | |
| gr.Markdown(""" | |
| ## Create interactive 3D flipbooks from PDFs or images | |
| Upload a PDF file or multiple images to generate an interactive flipbook with page-turning effects. | |
| ### Interactive Features: | |
| - The created flipbook includes interactive elements on the first pages | |
| - Navigate using the toolbar or by dragging page corners | |
| - Use the thumbnails view for quick navigation | |
| - Toggle fullscreen for better viewing experience | |
| """) | |
| with gr.Tabs(): | |
| with gr.TabItem("PDF Upload"): | |
| pdf_file = gr.File(label="Upload PDF", file_types=[".pdf"]) | |
| with gr.Accordion("Advanced Settings", open=False): | |
| pdf_view_mode = gr.Dropdown( | |
| choices=["webgl", "3d", "2d", "swipe"], | |
| value="webgl", | |
| label="View Mode" | |
| ) | |
| pdf_skin = gr.Dropdown( | |
| choices=["light", "dark", "gradient"], | |
| value="light", | |
| label="Skin" | |
| ) | |
| pdf_create_btn = gr.Button("Create Flipbook from PDF", variant="primary") | |
| pdf_output = gr.HTML(label="Flipbook Output") | |
| # 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 | |
| ) | |
| with gr.TabItem("Image Upload"): | |
| images = gr.File(label="Upload Images", file_types=["image"], file_count="multiple") | |
| with gr.Accordion("Advanced Settings", open=False): | |
| img_view_mode = gr.Dropdown( | |
| choices=["webgl", "3d", "2d", "swipe"], | |
| value="webgl", | |
| label="View Mode" | |
| ) | |
| img_skin = gr.Dropdown( | |
| choices=["light", "dark", "gradient"], | |
| value="light", | |
| label="Skin" | |
| ) | |
| img_create_btn = gr.Button("Create Flipbook from Images", variant="primary") | |
| img_output = gr.HTML(label="Flipbook Output") | |
| # Set up image event handler | |
| img_create_btn.click( | |
| fn=create_flipbook_from_images, | |
| inputs=[images, img_view_mode, img_skin], | |
| outputs=img_output | |
| ) | |
| gr.Markdown(""" | |
| ### Usage Instructions: | |
| 1. Select the tab for your content type (PDF or images) | |
| 2. Upload your file(s) | |
| 3. Adjust view mode and skin in Advanced Settings (optional) | |
| 4. Click the Create Flipbook button | |
| 5. Interact with your flipbook in the output area | |
| ### Notes: | |
| - The first pages contain interactive elements and links as examples | |
| - For best results, use PDFs with clear text and images | |
| - Supported image formats: JPG, PNG, GIF, etc. | |
| """) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| demo.launch() |