# app.py """ Main Streamlit application file for the PDF to Single Image Converter. Coordinates the UI, file handling, and calls the processing logic. """ import streamlit as st import tempfile import os import time from typing import Optional # Import functions from our modules from pdf_processor import pdf_to_single_image from ui_components import ( render_sidebar, display_file_details, display_results, render_initial_info, display_installation_info ) # --- Page Configuration --- st.set_page_config( page_title="PDF to Single Image Converter", page_icon="📄", layout="centered", # Can be "wide" or "centered" initial_sidebar_state="expanded" # Keep sidebar open initially ) # --- Main Application --- st.title("📄 PDF to Single Image Converter") st.markdown("Upload a multi-page PDF and convert it into a single, tall image file (PNG or JPG).") # --- Sidebar --- dpi_setting, format_setting = render_sidebar() # --- File Upload --- # Use a key for the file uploader to potentially reset it later if needed uploaded_file: Optional[st.runtime.uploaded_file_manager.UploadedFile] = st.file_uploader( "Choose a PDF file", type="pdf", key="pdf_uploader" ) # --- Processing Logic --- if uploaded_file is not None: # Display details of the uploaded file display_file_details(uploaded_file) # Use a temporary file for robust handling by PyMuPDF temp_pdf_path: Optional[str] = None try: # Create a temporary file to store the uploaded PDF content with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file: tmp_file.write(uploaded_file.getvalue()) temp_pdf_path = tmp_file.name # Store the path # Add a button to trigger the conversion if st.button(f"🚀 Convert to {format_setting}", key="convert_button"): if temp_pdf_path: # Ensure temp path is valid try: # Show a spinner during processing with st.spinner(f"Converting PDF to {format_setting} at {dpi_setting} DPI... Please wait."): start_time = time.time() # Call the core conversion function from pdf_processor img_buffer = pdf_to_single_image( pdf_path=temp_pdf_path, output_format=format_setting, dpi=dpi_setting ) processing_time = time.time() - start_time # Prepare output filename base_filename = os.path.splitext(uploaded_file.name)[0] output_filename = f"{base_filename}_converted.{format_setting.lower()}" # Display the results (download button, preview) if img_buffer.getbuffer().nbytes > 0: # Check if buffer has content display_results( img_buffer=img_buffer, output_filename=output_filename, output_format=format_setting, processing_time=processing_time ) else: st.error("Conversion resulted in an empty image. Please check the PDF file.") except Exception as e: st.error(f"❌ An error occurred during conversion:") st.exception(e) # Displays the full traceback for debugging finally: # --- Cleanup --- # Ensure the temporary file is deleted after processing or if an error occurs if temp_pdf_path and os.path.exists(temp_pdf_path): try: os.unlink(temp_pdf_path) # st.write(f"Temporary file {temp_pdf_path} deleted.") # Optional debug message except OSError as e: st.warning(f"Could not delete temporary file {temp_pdf_path}: {e}") else: # Show initial instructions if no file is uploaded render_initial_info() # --- Footer / Installation Info --- display_installation_info()