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() | |