CoolFace
Apppublic

CursiveCurse/Markup-To-Handwritten-PDF

sourceHugging Facemitupdated 1y agoView on Hugging Face
1likes
app.py152 linesDownload Raw Back to root
1# app.py (for Gradio)2import gradio as gr3import note_generator  # Your refactored script4import io5import os6import time # To create unique filenames if needed7import traceback # For detailed error logging8 9OUTPUT_DIR = "outputs" # Optional: Directory to save generated PDFs temporarily10os.makedirs(OUTPUT_DIR, exist_ok=True)11 12# --- Load Markup Guide ---13try:14    with open("MARKUP_GUIDE.md", "r", encoding="utf-8") as f:15        markup_guide_content = f.read()16except FileNotFoundError:17    markup_guide_content = "Error: MARKUP_GUIDE.md not found."18except Exception as e:19    markup_guide_content = f"Error reading markup guide: {e}"20 21# --- Core PDF Generation Function for Gradio ---22def generate_notes_pdf(markup_text_paste, markup_file_upload_path): # Renamed input var for clarity23    """24    Takes markup text (either pasted or from file path), generates PDF,25    and returns the path to the generated PDF file.26    """27    markup_text = ""28    input_source_name = ""29 30    # markup_file_upload_path will be the temporary file path provided by Gradio31    if markup_file_upload_path is not None:32        try:33            # Read directly from the provided path34            with open(markup_file_upload_path, "r", encoding="utf-8") as f:35                markup_text = f.read()36            input_source_name = os.path.basename(markup_file_upload_path) # Get filename part37            print(f"Using uploaded file: {input_source_name} (path: {markup_file_upload_path})")38        except Exception as e:39            gr.Warning(f"Error reading uploaded file: {e}")40            return None, f"Error reading file: {e}" # Return None for file, and error message41    elif markup_text_paste and markup_text_paste.strip():42        markup_text = markup_text_paste43        input_source_name = "Pasted Text"44        print("Using pasted text.")45    else:46        gr.Warning("Please provide markup text via paste or upload a file.")47        return None, "No input provided." # Return None for file, and status message48 49    if not markup_text:50        return None, "Markup text is empty."51 52    print(f"Generating PDF from '{input_source_name}'...")53    try:54        # 1. Parse the markup55        parsed_elements = note_generator.parse_markup(markup_text)56 57        if not parsed_elements:58            gr.Warning("Parsing resulted in no content elements. Cannot generate PDF.")59            return None, "Parsing resulted in empty content."60        else:61            # 2. Generate PDF bytes IN MEMORY62            pdf_bytes = note_generator.generate_pdf_bytes(parsed_elements)63 64            if not pdf_bytes:65                 gr.Error("PDF generation failed or produced empty output.")66                 return None, "PDF generation failed."67            else:68                # 3. Save bytes to a temporary file for Gradio output69                output_filename = os.path.join(OUTPUT_DIR, f"generated_notes_{int(time.time())}.pdf")70                # Or just use a fixed name:71                # output_filename = os.path.join(OUTPUT_DIR, "generated_notes.pdf")72 73                with open(output_filename, "wb") as f_out:74                    f_out.write(pdf_bytes)75 76                print(f"Successfully generated PDF: {output_filename}")77                # Return the path to the file for Gradio's File output component78                # and a success message for the status Textbox79                return output_filename, f"๐ŸŽ‰ PDF generated successfully from '{input_source_name}'!"80 81    except Exception as e:82        gr.Error(f"An error occurred during PDF generation: {e}")83        print(f"Error details: {e}") # Log detailed error84        traceback.print_exc() # Print traceback to console/logs85        # Return None for file, and a user-friendly error message86        return None, f"An error occurred: {str(e)[:100]}..." # Truncate long errors for UI87 88# --- Gradio Interface Definition ---89with gr.Blocks(theme=gr.themes.Soft(), title="Handwritten Notes PDF Generator") as demo:90    gr.Markdown("# Handwritten Notes PDF Generator")91    gr.Markdown("Paste your formatted text or upload a `.txt` file to generate notes based on the markup guide below.")92 93    with gr.Row():94        with gr.Column(scale=2):95            with gr.Tabs():96                with gr.TabItem("Paste Text"):97                    input_paste = gr.Textbox(lines=15, label="Paste Formatted Markup Here", placeholder="[MAIN_TITLE]My Notes[/MAIN_TITLE]\n[SUB_TITLE]Topic 1[/SUB_TITLE]\n[POINT:->]First point...\n[BOX]\nContent inside a box.\n[/BOX]")98                with gr.TabItem("Upload File"):99                    # *** THE FIX IS HERE ***100                    input_file = gr.File(label="Upload .txt Markup File", type="filepath", file_types=[".txt"])101 102            generate_button = gr.Button("Generate Notes PDF", variant="primary")103            status_message = gr.Textbox(label="Status", interactive=False) # To show success/error messages104 105        with gr.Column(scale=1):106             output_pdf = gr.File(label="Download Generated PDF", interactive=False)107 108    with gr.Accordion("Markup Guide", open=False):109        gr.Markdown(markup_guide_content)110 111    # --- Event Handling ---112    # Define what happens when the button is clicked113    generate_button.click(114        fn=generate_notes_pdf,115        inputs=[input_paste, input_file], # input_file now provides a path string116        outputs=[output_pdf, status_message] # Map outputs to components117    )118 119    # Clear other input when one is used (optional, but good UX)120    # Minor adjustments needed for updates in older Gradio versions if gr.update is needed121    # But simple clearing might work without explicit gr.update122    def clear_paste_if_file(file_path):123        if file_path is not None:124            # For older Gradio, returning the component type with a new value works125            return gr.Textbox(value="")126        return gr.Textbox() # No change127 128    def clear_file_if_paste(text_content):129        if text_content and text_content.strip():130            # Return the component type with value=None to clear131            return gr.File(value=None)132        return gr.File() # No change133 134    # Connect the change events135    input_file.change(fn=clear_paste_if_file, inputs=input_file, outputs=input_paste)136    input_paste.change(fn=clear_file_if_paste, inputs=input_paste, outputs=input_file)137 138 139# --- Launch the Gradio App ---140if __name__ == "__main__":141    # Ensure fonts are available before launching142    if not os.path.exists(note_generator.FONT_MAIN_PATH) or not os.path.exists(note_generator.FONT_SUB_PATH):143         print("\n" + "="*30)144         print("ERROR: Font files not found!")145         print(f"Make sure '{note_generator.FONT_MAIN_PATH}' and '{note_generator.FONT_SUB_PATH}' are in the same directory as app.py.")146         print("="*30 + "\n")147    else:148         print("Font files found. Ready to launch.")149 150    # Share=True creates a public link151    # Set debug=True for more detailed logs152    demo.launch(debug=True) # Share=True removed for safety unless needed