Spaces:
Sleeping
Sleeping
import streamlit as st | |
from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM | |
from pptx import Presentation | |
from pptx.util import Inches, Pt | |
from pptx.dml.color import RGBColor | |
import io, tempfile, os, re | |
st.set_page_config(page_title="PPTX Smart Enhancer", layout="wide") | |
# Load your hosted Hugging Face model | |
MODEL_ID = "Faisalkhany/llam1b_finetune" # ← change this | |
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, device_map="auto", torch_dtype="auto") | |
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer) | |
# Templates & fonts | |
TEMPLATES = { | |
"Office Default": { | |
"font":"Calibri", | |
"colors":{"primary":"#2B579A","secondary":"#5B9BD5","text":"#000000","background":"#FFFFFF"}, | |
"sizes":{"title":44,"heading":32,"body":18},"background":True | |
}, | |
"Modern Office": { | |
"font":"Segoe UI", | |
"colors":{"primary":"#404040","secondary":"#A5A5A5","text":"#FFFFFF","background":"#121212"}, | |
"sizes":{"title":36,"heading":24,"body":16},"background":True | |
}, | |
"Corporate": { | |
"font":"Arial", | |
"colors":{"primary":"#1976D2","secondary":"#BBDEFB","text":"#212121","background":"#FAFAFA"}, | |
"sizes":{"title":40,"heading":28,"body":18},"background":False | |
} | |
} | |
FONT_CHOICES = {f:f for f in ["Arial","Calibri","Times New Roman","Segoe UI","Verdana","Georgia"]} | |
# helpers | |
def extract_slide_text(slide): | |
return "\n".join(p.text for shp in slide.shapes if hasattr(shp,"text_frame") | |
for p in shp.text_frame.paragraphs) | |
def split_formatted_runs(text): | |
segs,cur,bold,italic=[],[],False,False | |
parts=re.split(r'(\*|_)',text) | |
for t in parts: | |
if t in ("*","_"): | |
if cur: | |
segs.append({"text":"".join(cur),"bold":bold,"italic":italic}) | |
cur=[] | |
if t=="*": bold=not bold | |
else: italic=not italic | |
else: cur.append(t) | |
if cur: segs.append({"text":"".join(cur),"bold":bold,"italic":italic}) | |
return segs | |
def apply_formatting(tf,content,ds): | |
tf.clear() | |
for line in [l for l in content.split("\n") if l.strip()]: | |
clean=re.sub(r'^#+\s*','',line).strip() | |
if re.match(r'^(What is|Understanding|Key|Conclusion)',clean,re.IGNORECASE): | |
p=tf.add_paragraph(); p.text=clean | |
p.font.size=Pt(ds["heading_size"]); p.font.bold=True; p.space_after=Pt(12) | |
continue | |
if re.match(r'^[\-\*\•] ',clean): | |
p=tf.add_paragraph(); p.level=0 | |
txt=clean[2:].strip() | |
for seg in split_formatted_runs(txt): | |
r=p.add_run(); r.text=seg["text"]; r.font.bold=seg["bold"]; r.font.italic=seg["italic"] | |
p.font.size=Pt(ds["body_size"]); continue | |
p=tf.add_paragraph() | |
for seg in split_formatted_runs(clean): | |
r=p.add_run(); r.text=seg["text"]; r.font.bold=seg["bold"]; r.font.italic=seg["italic"] | |
p.font.size=Pt(ds["body_size"]); p.space_after=Pt(6) | |
def apply_design(slide,ds): | |
cols=ds["colors"] | |
for shp in slide.shapes: | |
if hasattr(shp,"text_frame"): | |
for p in shp.text_frame.paragraphs: | |
for r in p.runs: | |
r.font.name=ds["font"] | |
r.font.color.rgb=RGBColor.from_string(cols["text"][1:]) | |
if shp==getattr(slide.shapes,"title",None): | |
r.font.color.rgb=RGBColor.from_string(cols["primary"][1:]) | |
r.font.size=Pt(ds["title_size"]); r.font.bold=True | |
if ds["set_background"]: | |
slide.background.fill.solid() | |
slide.background.fill.fore_color.rgb=RGBColor.from_string(cols["background"][1:]) | |
def enhance_slide(slide,prompt,ds): | |
orig=extract_slide_text(slide) | |
if not orig: return | |
out=pipe(f"Improve this slide:\n{orig}\n\nInstructions: {prompt}", | |
max_length=512, do_sample=False)[0]["generated_text"] | |
title_sp=getattr(slide.shapes,"title",None) | |
for shp in list(slide.shapes): | |
if shp is not title_sp: | |
shp._element.getparent().remove(shp._element) | |
left,top=Inches(1),Inches(1.5 if title_sp else 0.5) | |
tb=slide.shapes.add_textbox(left,top,Inches(8),Inches(5)); tf=tb.text_frame; tf.word_wrap=True | |
apply_formatting(tf,out,ds); apply_design(slide,ds) | |
def process_presentation(uploaded,prompt,ds): | |
with tempfile.NamedTemporaryFile(delete=False,suffix=".pptx") as tmp: | |
tmp.write(uploaded.read()); path=tmp.name | |
prs=Presentation(path) | |
for s in prs.slides: enhance_slide(s,prompt,ds) | |
os.unlink(path) | |
return prs | |
def main(): | |
st.title("PPTX Smart Enhancer") | |
uploaded=st.file_uploader("Upload PPTX",type=["pptx"]) | |
prompt =st.text_area("Enhancement Instructions",height=100) | |
with st.expander("🎨 Design Settings",True): | |
tpl=st.selectbox("Template",list(TEMPLATES.keys())) | |
if tpl=="Custom": | |
font=st.selectbox("Font",list(FONT_CHOICES)); ts=st.slider("Title size",12,60,32) | |
hs=st.slider("Heading size",12,48,24); bs=st.slider("Body size",8,36,18) | |
sb=st.checkbox("Apply background",True) | |
cols={k:st.color_picker(k.capitalize(),c) for k,c in TEMPLATES["Office Default"]["colors"].items()} | |
else: | |
t=TEMPLATES[tpl]; font,ts,hs,bs,sb=t["font"],*t["sizes"].values(),t["background"] | |
cols=t["colors"] | |
ds={"font":FONT_CHOICES[font],"colors":cols, | |
"title_size":ts,"heading_size":hs,"body_size":bs,"set_background":sb} | |
if st.button("Enhance")and uploaded and prompt: | |
out_prs=process_presentation(uploaded,prompt,ds) | |
buf=io.BytesIO(); out_prs.save(buf); buf.seek(0) | |
st.download_button("Download Enhanced PPTX",data=buf, | |
file_name="enhanced.pptx", | |
mime="application/vnd.openxmlformats-officedocument.presentationml.presentation") | |
if __name__=="__main__": | |
main() |