awacke1 commited on
Commit
ddd44f7
Β·
verified Β·
1 Parent(s): c9c67a5

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +147 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,149 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ from pathlib import Path
3
+ import base64
4
+ import datetime
5
+ import markdown2
6
+ from weasyprint import HTML, CSS
7
 
8
+ # --- Configuration & Setup ---
9
+
10
+ # Define the layouts based on the specification.
11
+ # The 'size' key uses CSS-compatible dimensions.
12
+ LAYOUTS = {
13
+ "A4 Portrait": {"size": "210mm 297mm", "icon": "πŸ“„"},
14
+ "A4 Landscape": {"size": "297mm 210mm", "icon": "πŸ“„"},
15
+ "Letter Portrait": {"size": "8.5in 11in", "icon": "πŸ“„"},
16
+ "Letter Landscape": {"size": "11in 8.5in", "icon": "πŸ“„"},
17
+ "Wide 16:9": {"aspect_ratio": "16/9", "icon": "πŸ“Ί"},
18
+ "Vertical 9:16": {"aspect_ratio": "9/16", "icon": "πŸ“±"},
19
+ "Square 1:1": {"aspect_ratio": "1/1", "icon": "πŸ–ΌοΈ"},
20
+ }
21
+
22
+ # Directory to save the generated PDFs
23
+ OUTPUT_DIR = Path("generated_pdfs")
24
+ OUTPUT_DIR.mkdir(exist_ok=True)
25
+
26
+ # --- Helper Functions ---
27
+
28
+ def get_file_download_link(file_path: Path) -> str:
29
+ """Generates a base64-encoded download link for a file."""
30
+ with open(file_path, "rb") as f:
31
+ data = base64.b64encode(f.read()).decode()
32
+ return f'<a href="data:application/octet-stream;base64,{data}" download="{file_path.name}">Download</a>'
33
+
34
+ def display_file_explorer():
35
+ """Renders a simple file explorer in the Streamlit app."""
36
+ st.header("πŸ“‚ File Explorer")
37
+
38
+ # Display Source Markdown files
39
+ st.subheader("Source Markdown Files (.md)")
40
+ md_files = list(Path(".").glob("*.md"))
41
+ if not md_files:
42
+ st.info("No Markdown files found in the current directory. Create a `.md` file to begin.")
43
+ else:
44
+ for md_file in md_files:
45
+ col1, col2 = st.columns([0.8, 0.2])
46
+ with col1:
47
+ st.write(f"πŸ“ `{md_file.name}`")
48
+ with col2:
49
+ st.markdown(get_file_download_link(md_file), unsafe_allow_html=True)
50
+
51
+ # Display Generated PDF files
52
+ st.subheader("Generated PDF Files")
53
+ pdf_files = sorted(list(OUTPUT_DIR.glob("*.pdf")), reverse=True)
54
+ if not pdf_files:
55
+ st.info("No PDFs generated yet. Click the button above.")
56
+ else:
57
+ for pdf_file in pdf_files:
58
+ col1, col2 = st.columns([0.8, 0.2])
59
+ with col1:
60
+ st.write(f"πŸ“„ `{pdf_file.name}`")
61
+ with col2:
62
+ st.markdown(get_file_download_link(pdf_file), unsafe_allow_html=True)
63
+
64
+ def generate_pdf_from_markdown(md_path: Path):
65
+ """
66
+ Reads a markdown file and generates PDFs for all defined layouts.
67
+ """
68
+ try:
69
+ md_content = md_path.read_text(encoding="utf-8")
70
+ html_content = markdown2.markdown(md_content, extras=["tables", "fenced-code-blocks", "cuddled-lists"])
71
+
72
+ # Basic styling for the PDF
73
+ base_css = """
74
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');
75
+ body { font-family: 'Inter', sans-serif; line-height: 1.6; }
76
+ h1, h2, h3 { font-weight: 700; }
77
+ code {
78
+ background-color: #f0f0f0;
79
+ padding: 2px 4px;
80
+ border-radius: 3px;
81
+ font-family: monospace;
82
+ }
83
+ pre { background-color: #f0f0f0; padding: 1em; border-radius: 5px; overflow: auto; }
84
+ table { border-collapse: collapse; width: 100%; }
85
+ th, td { border: 1px solid #ddd; padding: 8px; }
86
+ th { background-color: #f2f2f2; }
87
+ """
88
+
89
+ date_str = datetime.datetime.now().strftime("%Y-%m-%d")
90
+
91
+ for name, properties in LAYOUTS.items():
92
+ st.write(f" - Generating `{name}` format...")
93
+
94
+ page_css = f"@page {{ size: {properties.get('size', 'A4')}; margin: 2cm; }}"
95
+ if 'aspect_ratio' in properties:
96
+ # For aspect ratio, we fix width and calculate height. This is an approximation.
97
+ # A more robust solution might require more complex CSS.
98
+ page_css = f"@page {{ size: 210mm calc(210mm * {properties['aspect_ratio']}); margin: 1cm; }}"
99
+
100
+ final_css = CSS(string=base_css + page_css)
101
+
102
+ output_filename = f"{md_path.stem}_{name.replace(' ', '-')}_{date_str}.pdf"
103
+ output_path = OUTPUT_DIR / output_filename
104
+
105
+ HTML(string=html_content).write_pdf(output_path, stylesheets=[final_css])
106
+
107
+ except Exception as e:
108
+ st.error(f"Failed to process {md_path.name}: {e}")
109
+
110
+
111
+ # --- Streamlit App UI ---
112
+
113
+ st.set_page_config(layout="wide", page_title="PDF Generator")
114
+
115
+ st.title("πŸ“„ Markdown to PDF Generator")
116
+ st.markdown("This tool finds all `.md` files in this directory, converts them to PDF in various layouts, and provides download links.")
117
+
118
+ # Create a sample markdown file if none exists
119
+ if not list(Path(".").glob("*.md")):
120
+ with open("sample.md", "w", encoding="utf-8") as f:
121
+ f.write("# Sample Document\n\n")
122
+ 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")
123
+ f.write("- Item 1\n- Item 2\n\n")
124
+ f.write("`code snippet`\n\n")
125
+ f.write("Click the button below to start the PDF generation process.")
126
+ st.rerun()
127
+
128
+
129
+ if st.button("πŸš€ Generate PDFs from all Markdown Files", type="primary"):
130
+ markdown_files = list(Path(".").glob("*.md"))
131
+
132
+ if not markdown_files:
133
+ st.warning("No `.md` files found. Please add a markdown file to the directory.")
134
+ else:
135
+ with st.spinner("Generating PDFs... This may take a moment."):
136
+ progress_bar = st.progress(0)
137
+ total_steps = len(markdown_files)
138
+
139
+ for i, md_file in enumerate(markdown_files):
140
+ st.info(f"Processing: **{md_file.name}**")
141
+ generate_pdf_from_markdown(md_file)
142
+ progress_bar.progress((i + 1) / total_steps)
143
+
144
+ st.success("βœ… PDF generation complete!")
145
+ # Use st.rerun() to immediately refresh the file explorer
146
+ st.rerun()
147
+
148
+ # Display the file explorer section
149
+ display_file_explorer()