Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
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() |