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'
Color
', 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()