File size: 5,975 Bytes
a96dec4 ddd44f7 a96dec4 ddd44f7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
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()
|