Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| from pathlib import Path | |
| import base64 | |
| import datetime | |
| import markdown2 | |
| from weasyprint import HTML, CSS | |
| # --- Configuration & Setup --- | |
| # Define the layouts based on the specification. | |
| # The 'size' key uses CSS-compatible dimensions. | |
| LAYOUTS = { | |
| "A4 Portrait": {"size": "210mm 297mm", "icon": "π"}, | |
| "A4 Landscape": {"size": "297mm 210mm", "icon": "π"}, | |
| "Letter Portrait": {"size": "8.5in 11in", "icon": "π"}, | |
| "Letter Landscape": {"size": "11in 8.5in", "icon": "π"}, | |
| "Wide 16:9": {"aspect_ratio": "16/9", "icon": "πΊ"}, | |
| "Vertical 9:16": {"aspect_ratio": "9/16", "icon": "π±"}, | |
| "Square 1:1": {"aspect_ratio": "1/1", "icon": "πΌοΈ"}, | |
| } | |
| # Directory to save the generated PDFs | |
| OUTPUT_DIR = Path("generated_pdfs") | |
| OUTPUT_DIR.mkdir(exist_ok=True) | |
| # --- Helper Functions --- | |
| def get_file_download_link(file_path: Path) -> str: | |
| """Generates a base64-encoded download link for a file.""" | |
| with open(file_path, "rb") as f: | |
| data = base64.b64encode(f.read()).decode() | |
| return f'<a href="data:application/octet-stream;base64,{data}" download="{file_path.name}">Download</a>' | |
| def display_file_explorer(): | |
| """Renders a simple file explorer in the Streamlit app.""" | |
| st.header("π File Explorer") | |
| # Display Source Markdown files | |
| st.subheader("Source Markdown Files (.md)") | |
| md_files = list(Path(".").glob("*.md")) | |
| if not md_files: | |
| st.info("No Markdown files found in the current directory. Create a `.md` file to begin.") | |
| else: | |
| for md_file in md_files: | |
| col1, col2 = st.columns([0.8, 0.2]) | |
| with col1: | |
| st.write(f"π `{md_file.name}`") | |
| with col2: | |
| st.markdown(get_file_download_link(md_file), unsafe_allow_html=True) | |
| # Display Generated PDF files | |
| st.subheader("Generated PDF Files") | |
| pdf_files = sorted(list(OUTPUT_DIR.glob("*.pdf")), reverse=True) | |
| if not pdf_files: | |
| st.info("No PDFs generated yet. Click the button above.") | |
| else: | |
| for pdf_file in pdf_files: | |
| col1, col2 = st.columns([0.8, 0.2]) | |
| with col1: | |
| st.write(f"π `{pdf_file.name}`") | |
| with col2: | |
| st.markdown(get_file_download_link(pdf_file), unsafe_allow_html=True) | |
| def generate_pdf_from_markdown(md_path: Path): | |
| """ | |
| Reads a markdown file and generates PDFs for all defined layouts. | |
| """ | |
| try: | |
| md_content = md_path.read_text(encoding="utf-8") | |
| html_content = markdown2.markdown(md_content, extras=["tables", "fenced-code-blocks", "cuddled-lists"]) | |
| # Basic styling for the PDF | |
| base_css = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap'); | |
| body { font-family: 'Inter', sans-serif; line-height: 1.6; } | |
| h1, h2, h3 { font-weight: 700; } | |
| code { | |
| background-color: #f0f0f0; | |
| padding: 2px 4px; | |
| border-radius: 3px; | |
| font-family: monospace; | |
| } | |
| pre { background-color: #f0f0f0; padding: 1em; border-radius: 5px; overflow: auto; } | |
| table { border-collapse: collapse; width: 100%; } | |
| th, td { border: 1px solid #ddd; padding: 8px; } | |
| th { background-color: #f2f2f2; } | |
| """ | |
| date_str = datetime.datetime.now().strftime("%Y-%m-%d") | |
| for name, properties in LAYOUTS.items(): | |
| st.write(f" - Generating `{name}` format...") | |
| page_css = f"@page {{ size: {properties.get('size', 'A4')}; margin: 2cm; }}" | |
| if 'aspect_ratio' in properties: | |
| # For aspect ratio, we fix width and calculate height. This is an approximation. | |
| # A more robust solution might require more complex CSS. | |
| page_css = f"@page {{ size: 210mm calc(210mm * {properties['aspect_ratio']}); margin: 1cm; }}" | |
| final_css = CSS(string=base_css + page_css) | |
| output_filename = f"{md_path.stem}_{name.replace(' ', '-')}_{date_str}.pdf" | |
| output_path = OUTPUT_DIR / output_filename | |
| HTML(string=html_content).write_pdf(output_path, stylesheets=[final_css]) | |
| except Exception as e: | |
| st.error(f"Failed to process {md_path.name}: {e}") | |
| # --- Streamlit App UI --- | |
| st.set_page_config(layout="wide", page_title="PDF Generator") | |
| st.title("π Markdown to PDF Generator") | |
| st.markdown("This tool finds all `.md` files in this directory, converts them to PDF in various layouts, and provides download links.") | |
| # Create a sample markdown file if none exists | |
| if not list(Path(".").glob("*.md")): | |
| with open("sample.md", "w", encoding="utf-8") as f: | |
| f.write("# Sample Document\n\n") | |
| f.write("This is a sample markdown file created for you. You can edit this file or add your own `.md` files to this directory.\n\n") | |
| f.write("- Item 1\n- Item 2\n\n") | |
| f.write("`code snippet`\n\n") | |
| f.write("Click the button below to start the PDF generation process.") | |
| st.rerun() | |
| if st.button("π Generate PDFs from all Markdown Files", type="primary"): | |
| markdown_files = list(Path(".").glob("*.md")) | |
| if not markdown_files: | |
| st.warning("No `.md` files found. Please add a markdown file to the directory.") | |
| else: | |
| with st.spinner("Generating PDFs... This may take a moment."): | |
| progress_bar = st.progress(0) | |
| total_steps = len(markdown_files) | |
| for i, md_file in enumerate(markdown_files): | |
| st.info(f"Processing: **{md_file.name}**") | |
| generate_pdf_from_markdown(md_file) | |
| progress_bar.progress((i + 1) / total_steps) | |
| st.success("β PDF generation complete!") | |
| # Use st.rerun() to immediately refresh the file explorer | |
| st.rerun() | |
| # Display the file explorer section | |
| display_file_explorer() | |