import streamlit as st from PIL import Image, ImageEnhance from io import BytesIO import zipfile import os from fpdf import FPDF import hashlib st.set_page_config(page_title="Smart Doc Scanner", layout="wide") def resize_image(img, max_height=600): if img.height > max_height: ratio = max_height / img.height return img.resize((int(img.width * ratio), max_height), Image.Resampling.LANCZOS) return img def crop_sliders(img, key): w, h = img.size state = st.session_state for name, val in zip(["left", "top", "right", "bottom"], [0, 0, w, h]): state.setdefault(f"{key}_{name}", val) col1, col2 = st.columns(2) with col1: st.session_state[f"{key}_left"] = st.slider("Crop Left", 0, w - 10, st.session_state[f"{key}_left"], key=f"{key}_left_slider") st.session_state[f"{key}_right"] = st.slider("Crop Right", st.session_state[f"{key}_left"] + 10, w, st.session_state[f"{key}_right"], key=f"{key}_right_slider") with col2: st.session_state[f"{key}_top"] = st.slider("Crop Top", 0, h - 10, st.session_state[f"{key}_top"], key=f"{key}_top_slider") st.session_state[f"{key}_bottom"] = st.slider("Crop Bottom", st.session_state[f"{key}_top"] + 10, h, st.session_state[f"{key}_bottom"], key=f"{key}_bottom_slider") return ( st.session_state[f"{key}_left"], st.session_state[f"{key}_top"], st.session_state[f"{key}_right"], st.session_state[f"{key}_bottom"] ) st.title("๐Ÿ“„ Smart ID & Document Scanner") uploaded_files = st.file_uploader("๐Ÿ“ค Upload Images", type=["png", "jpg", "jpeg"], accept_multiple_files=True) if uploaded_files: mode = st.radio("Mode", ["Normal Mode", "Advanced Mode (Edit Each)"]) processed_images = [] file_names = [] for file in uploaded_files: file_bytes = file.getvalue() hash_key = hashlib.md5(file_bytes).hexdigest() key = f"img_{hash_key}" st.markdown(f"---\n### ๐Ÿ–ผ๏ธ {file.name}") original = Image.open(BytesIO(file_bytes)).convert("RGB") # Init session state if f"original_{key}" not in st.session_state: st.session_state[f"original_{key}"] = original st.session_state[f"enhance_{key}"] = False st.session_state[f"filename_{key}"] = file.name.rsplit(".", 1)[0] w, h = original.size st.session_state[f"{key}_left"] = 0 st.session_state[f"{key}_top"] = 0 st.session_state[f"{key}_right"] = w st.session_state[f"{key}_bottom"] = h st.session_state[f"angle_{key}"] = 0 # โœ… NORMAL MODE HANDLING if mode == "Normal Mode": st.image(resize_image(original), use_container_width=True) processed_images.append(original) file_names.append(st.session_state[f"filename_{key}"]) continue # ADVANCED MODE col1, col2 = st.columns([1, 1.5]) with col1: if st.button("๐Ÿ” Reset", key=f"reset_{key}"): st.session_state[f"angle_{key}"] = 0 st.session_state[f"enhance_{key}"] = False w, h = original.size st.session_state[f"{key}_left"] = 0 st.session_state[f"{key}_top"] = 0 st.session_state[f"{key}_right"] = w st.session_state[f"{key}_bottom"] = h angle = st.slider("๐ŸŒ€ Rotate", -180, 180, value=st.session_state[f"angle_{key}"], key=f"angle_{key}_slider") st.session_state[f"angle_{key}"] = angle st.checkbox("โœจ Enhance", key=f"enhance_{key}") left, top, right, bottom = crop_sliders(original, key) st.text_input("๐Ÿ“ Rename this file", value=st.session_state[f"filename_{key}"], key=f"filename_{key}_input") with col2: angle = st.session_state[f"angle_{key}"] enhance = st.session_state[f"enhance_{key}"] img = st.session_state[f"original_{key}"].crop((left, top, right, bottom)) img = resize_image(img) if enhance: img = ImageEnhance.Contrast(img).enhance(1.5) img = ImageEnhance.Sharpness(img).enhance(2.0) if angle != 0: img = img.rotate(angle, expand=True) st.image(img, caption=f"๐ŸŽฏ Live Preview ({angle}ยฐ)", use_container_width=True) if st.button("โœ… Save This Image", key=f"save_{key}"): st.session_state[f"final_{key}"] = img.copy() st.success("Saved for export!") final_img = st.session_state.get(f"final_{key}", original) filename = st.session_state.get(f"filename_{key}", file.name.rsplit(".", 1)[0]) processed_images.append(final_img) file_names.append(filename) # --- EXPORT SECTION --- st.markdown("### ๐Ÿงพ Export Options") # ZIP layout zip_col1, zip_col2 = st.columns([1, 2]) with zip_col1: if st.button("๐Ÿ“ฆ Generate ZIP"): zip_buffer = BytesIO() with zipfile.ZipFile(zip_buffer, "w") as zipf: for name, img in zip(file_names, processed_images): buffer = BytesIO() img.save(buffer, format="PNG") zipf.writestr(f"{name}.png", buffer.getvalue()) zip_buffer.seek(0) st.session_state["zip_file"] = zip_buffer with zip_col2: if "zip_file" in st.session_state: st.download_button("โฌ‡๏ธ Download ZIP", st.session_state["zip_file"], "images.zip", mime="application/zip", key="zip_dl") # PDF layout pdf_col1, pdf_col2 = st.columns([1, 2]) with pdf_col1: if st.button("๐Ÿ“„ Generate PDF"): pdf = FPDF() tmp_paths = [] for name, img in zip(file_names, processed_images): path = f"/tmp/{name}.jpg" img.save(path, "JPEG") pdf.add_page() pdf.image(path, x=10, y=10, w=190) tmp_paths.append(path) out_path = "/tmp/final.pdf" pdf.output(out_path) with open(out_path, "rb") as f: st.session_state["pdf_file"] = f.read() for p in tmp_paths + [out_path]: os.remove(p) with pdf_col2: if "pdf_file" in st.session_state: st.download_button("โฌ‡๏ธ Download PDF", st.session_state["pdf_file"], "scanned.pdf", mime="application/pdf", key="pdf_dl")