import gradio as gr from pathlib import Path import datetime import re import os import shutil import io import base64 from collections import defaultdict from PIL import Image import json # Document Generation Libs from docx import Document import openpyxl from pypdf import PdfWriter from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, BaseDocTemplate, Frame, PageTemplate from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.pagesizes import letter, A4, landscape from reportlab.lib.units import inch from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont # Media Libs import fitz # PyMuPDF # --- Configuration & Setup --- CWD = Path.cwd() OUTPUT_DIR = CWD / "generated_outputs" PREVIEW_DIR = CWD / "previews" UPLOAD_DIR = CWD / "uploads" FONT_DIR = CWD # Create necessary directories OUTPUT_DIR.mkdir(exist_ok=True) PREVIEW_DIR.mkdir(exist_ok=True) UPLOAD_DIR.mkdir(exist_ok=True) LAYOUTS = { "A4 Portrait": {"size": A4}, "A4 Landscape": {"size": landscape(A4)}, "Letter Portrait": {"size": letter}, "Letter Landscape": {"size": landscape(letter)}, } # --- ✍️ Document Generation Engines --- def create_pdf(md_content, font_name, emoji_font, pagesize, num_columns): """📄 Builds a beautiful PDF from a Markdown story using ReportLab.""" pdf_buffer = io.BytesIO() story = markdown_to_story(md_content, font_name, emoji_font) if num_columns > 1: doc = BaseDocTemplate(pdf_buffer, pagesize=pagesize, leftMargin=0.5 * inch, rightMargin=0.5 * inch) frame_width = (doc.width / num_columns) - (num_columns - 1) * 0.1 * inch frames = [Frame(doc.leftMargin + i * (frame_width + 0.2 * inch), doc.bottomMargin, frame_width, doc.height) for i in range(num_columns)] doc.addPageTemplates([PageTemplate(id='MultiCol', frames=frames)]) else: doc = SimpleDocTemplate(pdf_buffer, pagesize=pagesize) doc.build(story) pdf_buffer.seek(0) return pdf_buffer def create_docx(md_content): """📝 Crafts a DOCX document, translating Markdown to Word elements.""" document = Document() for line in md_content.split('\n'): if line.startswith('# '): document.add_heading(line[2:], level=1) elif line.startswith('## '): document.add_heading(line[3:], level=2) elif line.strip().startswith(('- ', '* ')): document.add_paragraph(line.strip()[2:], style='List Bullet') else: p = document.add_paragraph() parts = re.split(r'(\*\*.*?\*\*)', line) for part in parts: if part.startswith('**') and part.endswith('**'): p.add_run(part[2:-2]).bold = True else: p.add_run(part) return document def create_xlsx(md_content): """📊 Organizes a Markdown outline into columns in an XLSX file.""" workbook = openpyxl.Workbook(); sheet = workbook.active sections = re.split(r'\n# ', '\n' + md_content) if sections and sections[0] == '': sections.pop(0) column_data = [] for section in sections: lines = section.split('\n'); header = lines[0] content = [l.strip() for l in lines[1:] if l.strip()] column_data.append({'header': header, 'content': content}) for c_idx, col in enumerate(column_data, 1): sheet.cell(row=1, column=c_idx, value=col['header']) for r_idx, line_content in enumerate(col['content'], 2): sheet.cell(row=r_idx, column=c_idx, value=line_content) return workbook def markdown_to_story(markdown_text: str, font_name: str, emoji_font: str): """📜 Translates Markdown text into a sequence of ReportLab flowables for PDF rendering.""" styles = getSampleStyleSheet() bold_font = f"{font_name}-Bold" if font_name != "Helvetica" else "Helvetica-Bold" style_normal = ParagraphStyle('BodyText', fontName=font_name, spaceAfter=6, fontSize=10, leading=14) style_h1 = ParagraphStyle('h1', fontName=bold_font, spaceBefore=12, fontSize=24, textColor=colors.HexColor("#1E3A8A")) style_h2 = ParagraphStyle('h2', fontName=bold_font, spaceBefore=10, fontSize=18, textColor=colors.HexColor("#374151")) style_h3 = ParagraphStyle('h3', fontName=bold_font, spaceBefore=8, fontSize=14, textColor=colors.HexColor("#4B5563")) story, first_heading = [], True for line in markdown_text.split('\n'): stripped_line = line.strip() if not stripped_line: story.append(Spacer(1, 0.1 * inch)); continue content, style, extra_args = stripped_line, style_normal, {} if stripped_line.startswith("# "): if not first_heading: story.append(PageBreak()) content, style, first_heading = stripped_line.lstrip('# '), style_h1, False elif stripped_line.startswith("## "): content, style = stripped_line.lstrip('## '), style_h2 elif stripped_line.startswith("### "): content, style = stripped_line.lstrip('### '), style_h3 elif stripped_line.startswith(("- ", "* ")): content, extra_args = stripped_line[2:], {'bulletText': '•'} formatted_content = re.sub(r'_(.*?)_', r'\1', re.sub(r'\*\*(.*?)\*\*', r'\1', content)) final_content = apply_emoji_font(formatted_content, emoji_font) story.append(Paragraph(final_content, style, **extra_args)) return story # --- 🔮 Virtual AI Omni-Model Functions --- def process_text_input(prompt): """💬 Simulates an AI response to a text prompt.""" return f"# Virtual AI Response\n\n**Your Prompt:**\n> {prompt}\n\n**Generated Content:**\n- This is a simulated response for your text input.\n- Here's an emoji: 😊" def process_image_input(image_path, prompt): """🖼️ Simulates an AI description of an image.""" return f"# Virtual AI Image Analysis: {Path(image_path).name}\n\n**Your Prompt:**\n> {prompt}\n\n**Generated Content:**\n1. Simulated analysis of the uploaded image.\n2. File type appears to be `{Path(image_path).suffix}`." def process_audio_input(audio_path, prompt): """🎤 Simulates AI transcription and summarization of an audio file.""" return f"# Virtual AI Audio Summary: {Path(audio_path).name}\n\n**Your Prompt:**\n> {prompt}\n\n**Simulated Transcription:**\n> \"This is a test of the emergency broadcast system.\"\n\n**Generated Summary:**\nThe audio is a test broadcast." def process_pdf_input(pdf_path, prompt, progress): """📄 Simulates AI-powered OCR of a PDF document.""" progress(0.5, desc="Simulating PDF page processing...") ocr_text = f"# Virtual AI OCR of: {Path(pdf_path).name}\n\n**Your Prompt:**\n> {prompt}\n\n**Extracted Content (Simulated):**\n- **Page 1:** Simulated text from the first page.\n- **Page 2:** Simulated text from the second page." progress(1.0, desc="PDF OCR Simulation Complete!") return ocr_text # --- 🛠️ Helpers & Main API --- def register_local_fonts(): """✒️ Scans for local .ttf fonts and registers them for PDF creation.""" text_font_names, emoji_font_name = [], None font_files = list(FONT_DIR.glob("*.ttf")) for font_path in font_files: try: font_name = font_path.stem pdfmetrics.registerFont(TTFont(font_name, str(font_path))) pdfmetrics.registerFont(TTFont(f"{font_name}-Bold", str(font_path))) pdfmetrics.registerFontFamily(font_name, normal=font_name, bold=f"{font_name}-Bold") if "notocoloremoji-regular" in font_name.lower(): emoji_font_name = font_name else: text_font_names.append(font_name) except: pass if not text_font_names: text_font_names.append('Helvetica') return sorted(text_font_names), emoji_font_name def apply_emoji_font(text: str, emoji_font_name: str) -> str: """😊 Finds emojis and wraps them in special font tags for the PDF.""" if not emoji_font_name: return text emoji_pattern = re.compile(f"([{re.escape(''.join(map(chr, range(0x1f600, 0x1f650))))}" f"{re.escape(''.join(map(chr, range(0x1f300, 0x1f5ff))))}]+)") return emoji_pattern.sub(fr'\1', text) def create_pdf_preview(pdf_path: Path): """🏞️ Generates a PNG thumbnail for the first page of a PDF.""" preview_path = PREVIEW_DIR / f"{pdf_path.stem}.png" try: doc = fitz.open(pdf_path); page = doc.load_page(0); pix = page.get_pixmap(dpi=96) pix.save(str(preview_path)); doc.close() return preview_path except: return None def build_file_explorer_html(generated_files, pdf_files_for_gallery): """🗂️ Constructs the HTML/JS for the file explorer and PDF gallery.""" file_explorer_html = "" file_icons = {".pdf": "📄", ".docx": "📝", ".xlsx": "📊"} action_buttons = """
{gallery_items[0]['filename']}
""" html = f"""