Spaces:
Sleeping
Sleeping
| import io | |
| import os | |
| import re | |
| import glob | |
| import textwrap | |
| from datetime import datetime | |
| from pathlib import Path | |
| import streamlit as st | |
| import pandas as pd | |
| from PIL import Image | |
| from reportlab.pdfgen import canvas | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.lib.utils import ImageReader | |
| import mistune | |
| from gtts import gTTS | |
| # Page config | |
| st.set_page_config(page_title="PDF & Code Interpreter", layout="wide", page_icon="๐") | |
| def delete_asset(path): | |
| try: | |
| os.remove(path) | |
| except: | |
| pass | |
| st.rerun() | |
| # Tabs setup | |
| tab1, tab2 = st.tabs(["๐ PDF Composer", "๐งช Code Interpreter"]) | |
| with tab1: | |
| st.header("๐ PDF Composer & Voice Generator ๐") | |
| # Sidebar PDF text settings | |
| columns = st.sidebar.slider("Text columns", 1, 3, 1) | |
| font_family = st.sidebar.selectbox("Font", ["Helvetica","Times-Roman","Courier"]) | |
| font_size = st.sidebar.slider("Font size", 6, 24, 12) | |
| # Markdown input | |
| md_file = st.file_uploader("Upload Markdown (.md)", type=["md"]) | |
| if md_file: | |
| md_text = md_file.getvalue().decode("utf-8") | |
| stem = Path(md_file.name).stem | |
| else: | |
| md_text = st.text_area("Or enter markdown text directly", height=200) | |
| stem = datetime.now().strftime('%Y%m%d_%H%M%S') | |
| # Convert Markdown to plain text | |
| renderer = mistune.HTMLRenderer() | |
| markdown = mistune.create_markdown(renderer=renderer) | |
| html = markdown(md_text or "") | |
| plain_text = re.sub(r'<[^>]+>', '', html) | |
| # Voice settings | |
| languages = {"English (US)": "en", "English (UK)": "en-uk", "Spanish": "es"} | |
| voice_choice = st.selectbox("Voice Language", list(languages.keys())) | |
| voice_lang = languages[voice_choice] | |
| slow = st.checkbox("Slow Speech") | |
| if st.button("๐ Generate & Download Voice MP3 from Text"): | |
| if plain_text.strip(): | |
| voice_file = f"{stem}.mp3" | |
| tts = gTTS(text=plain_text, lang=voice_lang, slow=slow) | |
| tts.save(voice_file) | |
| st.audio(voice_file) | |
| with open(voice_file, 'rb') as mp3: | |
| st.download_button("๐ฅ Download MP3", data=mp3, file_name=voice_file, mime="audio/mpeg") | |
| else: | |
| st.warning("No text to generate voice from.") | |
| # Image uploads and ordering | |
| imgs = st.file_uploader("Upload Images for PDF", type=["png", "jpg", "jpeg"], accept_multiple_files=True) | |
| ordered_images = [] | |
| if imgs: | |
| df_imgs = pd.DataFrame([{"name": f.name, "order": i} for i, f in enumerate(imgs)]) | |
| edited = st.data_editor(df_imgs, use_container_width=True) | |
| for _, row in edited.sort_values("order").iterrows(): | |
| for f in imgs: | |
| if f.name == row['name']: | |
| ordered_images.append(f) | |
| break | |
| if st.button("๐๏ธ Generate PDF with Markdown & Images"): | |
| buf = io.BytesIO() | |
| c = canvas.Canvas(buf) | |
| # Render text with columns | |
| page_w, page_h = letter | |
| margin = 40 | |
| gutter = 20 | |
| col_w = (page_w - 2*margin - (columns-1)*gutter) / columns | |
| c.setFont(font_family, font_size) | |
| line_height = font_size * 1.2 | |
| col = 0 | |
| x = margin | |
| y = page_h - margin | |
| wrap_width = int(col_w / (font_size * 0.6)) | |
| for paragraph in plain_text.split("\n"): | |
| for line in textwrap.wrap(paragraph, wrap_width): | |
| if y < margin: | |
| col += 1 | |
| if col >= columns: | |
| c.showPage() | |
| c.setFont(font_family, font_size) | |
| col = 0 | |
| x = margin + col*(col_w+gutter) | |
| y = page_h - margin | |
| c.drawString(x, y, line) | |
| y -= line_height | |
| y -= line_height | |
| # Autosize pages to each image | |
| for img_f in ordered_images: | |
| try: | |
| img = Image.open(img_f) | |
| w, h = img.size | |
| c.showPage() | |
| c.setPageSize((w, h)) | |
| c.drawImage(ImageReader(img), 0, 0, w, h, preserveAspectRatio=True, mask='auto') | |
| except: | |
| continue | |
| c.save() | |
| buf.seek(0) | |
| pdf_name = f"{stem}.pdf" | |
| st.download_button("โฌ๏ธ Download PDF", data=buf, file_name=pdf_name, mime="application/pdf") | |
| st.markdown("---") | |
| st.subheader("๐ Available Assets") | |
| assets = sorted(glob.glob("*.*")) | |
| for a in assets: | |
| ext = a.split('.')[-1].lower() | |
| cols = st.columns([3, 1, 1]) | |
| cols[0].write(a) | |
| if ext == 'pdf': | |
| with open(a, 'rb') as fp: | |
| cols[1].download_button("๐ฅ", data=fp, file_name=a, mime="application/pdf") | |
| elif ext == 'mp3': | |
| cols[1].audio(a) | |
| with open(a, 'rb') as mp3: | |
| cols[1].download_button("๐ฅ", data=mp3, file_name=a, mime="audio/mpeg") | |
| cols[2].button("๐๏ธ", key=f"del_{a}", on_click=delete_asset, args=(a,)) | |
| with tab2: | |
| st.header("๐งช Python Code Executor & Demo") | |
| import io, sys | |
| from contextlib import redirect_stdout | |
| DEFAULT_CODE = '''import streamlit as st | |
| import random | |
| st.title("๐ Demo App") | |
| st.markdown("Random number and color demo") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| num = st.number_input("Number:", 1, 100, 10) | |
| mul = st.slider("Multiplier:", 1, 10, 2) | |
| if st.button("Calc"): | |
| st.write(num * mul) | |
| with col2: | |
| color = st.color_picker("Pick color","#ff0000") | |
| st.markdown(f'<div style="background:{color};padding:10px;">Color</div>', unsafe_allow_html=True) | |
| ''' # noqa | |
| def extract_python_code(md: str) -> list: | |
| return re.findall(r"```python\s*(.*?)```", md, re.DOTALL) | |
| def execute_code(code: str) -> tuple: | |
| buf = io.StringIO(); local_vars = {} | |
| try: | |
| with redirect_stdout(buf): exec(code, {}, local_vars) | |
| return buf.getvalue(), None | |
| except Exception as e: | |
| return None, str(e) | |
| up = st.file_uploader("Upload .py or .md", type=['py', 'md']) | |
| if 'code' not in st.session_state: | |
| st.session_state.code = DEFAULT_CODE | |
| if up: | |
| text = up.getvalue().decode() | |
| if up.type == 'text/markdown': | |
| codes = extract_python_code(text) | |
| st.session_state.code = codes[0] if codes else '' | |
| else: | |
| st.session_state.code = text | |
| st.code(st.session_state.code, language='python') | |
| else: | |
| st.session_state.code = st.text_area("๐ป Code Editor", value=st.session_state.code, height=300) | |
| c1, c2 = st.columns([1, 1]) | |
| if c1.button("โถ๏ธ Run Code"): | |
| out, err = execute_code(st.session_state.code) | |
| if err: | |
| st.error(err) | |
| elif out: | |
| st.code(out) | |
| else: | |
| st.success("Executed with no output.") | |
| if c2.button("๐๏ธ Clear Code"): | |
| st.session_state.code = '' | |
| st.rerun() | |