Update app.py
Browse files
app.py
CHANGED
@@ -22,7 +22,7 @@ from pypdf import PdfReader, PdfWriter
|
|
22 |
from pypdf.annotations import Link
|
23 |
from pypdf.generic import Fit
|
24 |
|
25 |
-
st.set_page_config(layout="wide", initial_sidebar_state="
|
26 |
|
27 |
# Existing functions (unchanged)
|
28 |
def get_timestamp_prefix():
|
@@ -139,7 +139,7 @@ def apply_emoji_font(text, emoji_font):
|
|
139 |
|
140 |
return ''.join(result)
|
141 |
|
142 |
-
def markdown_to_pdf_content(markdown_text,
|
143 |
lines = markdown_text.strip().split('\n')
|
144 |
pdf_content = []
|
145 |
number_pattern = re.compile(r'^\d+(\.\d+)*\.\s')
|
@@ -174,16 +174,11 @@ def markdown_to_pdf_content(markdown_text, render_with_bold, auto_bold_numbers,
|
|
174 |
line = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', line)
|
175 |
line = re.sub(r'\*([^*]+?)\*', r'<b>\1</b>', line)
|
176 |
|
177 |
-
if auto_bold_numbers and is_numbered_line:
|
178 |
-
if not (line.startswith("<b>") and line.endswith("</b>")):
|
179 |
-
if "<b>" in line and "</b>" in line:
|
180 |
-
line = re.sub(r'</?b>', '', line)
|
181 |
-
line = f"<b>{line}</b>"
|
182 |
pdf_content.append(line)
|
183 |
total_lines = len(pdf_content)
|
184 |
return pdf_content, total_lines
|
185 |
|
186 |
-
def create_pdf(markdown_text, base_font_size,
|
187 |
buffer = io.BytesIO()
|
188 |
page_width = A4[0] * 2
|
189 |
page_height = A4[1]
|
@@ -198,7 +193,7 @@ def create_pdf(markdown_text, base_font_size, render_with_bold, auto_bold_number
|
|
198 |
)
|
199 |
styles = getSampleStyleSheet()
|
200 |
spacer_height = 10
|
201 |
-
pdf_content, total_lines = markdown_to_pdf_content(markdown_text,
|
202 |
try:
|
203 |
available_font_files = glob.glob("*.ttf")
|
204 |
if not available_font_files:
|
@@ -237,8 +232,7 @@ def create_pdf(markdown_text, base_font_size, render_with_bold, auto_bold_number
|
|
237 |
)
|
238 |
numbered_bold_style = ParagraphStyle(
|
239 |
'NumberedBoldStyle', parent=styles['Normal'], fontName="NotoEmoji-Bold",
|
240 |
-
fontSize=adjusted_font_size
|
241 |
-
leading=(adjusted_font_size + 1) * 1.15 if enlarge_numbered else adjusted_font_size * 1.15, spaceAfter=1,
|
242 |
linkUnderline=True
|
243 |
)
|
244 |
section_style = ParagraphStyle(
|
@@ -472,7 +466,7 @@ md_files = [f for f in glob.glob("*.md") if os.path.basename(f) != "README.md"]
|
|
472 |
md_options = [os.path.splitext(os.path.basename(f))[0] for f in md_files]
|
473 |
|
474 |
with st.sidebar:
|
475 |
-
st.markdown("### PDF Options")
|
476 |
if md_options:
|
477 |
selected_md = st.selectbox("Select Markdown File", options=md_options, index=0)
|
478 |
with open(f"{selected_md}.md", "r", encoding="utf-8") as f:
|
@@ -481,19 +475,22 @@ with st.sidebar:
|
|
481 |
st.warning("No markdown file found. Please add one to your folder.")
|
482 |
selected_md = None
|
483 |
st.session_state.markdown_content = ""
|
|
|
484 |
available_font_files = {os.path.splitext(os.path.basename(f))[0]: f for f in glob.glob("*.ttf")}
|
485 |
-
selected_font_name = st.selectbox(
|
486 |
-
|
|
|
|
|
|
|
487 |
base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=8, step=1)
|
488 |
-
render_with_bold = st.checkbox("Render with Bold Formatting (remove ** markers)", value=True, key="render_with_bold")
|
489 |
-
auto_bold_numbers = st.checkbox("Auto Bold Numbered Lines", value=True, key="auto_bold_numbers")
|
490 |
-
enlarge_numbered = st.checkbox("Enlarge Font Size for Numbered Lines", value=True, key="enlarge_numbered")
|
491 |
-
add_space_before_numbered = st.checkbox("Add Space Ahead of Numbered Lines", value=False, key="add_space_before_numbered")
|
492 |
-
|
493 |
-
headings_to_fonts = st.checkbox("Headings to Fonts", value=False, key="headings_to_fonts",
|
494 |
-
help="Convert Markdown headings (# Heading) and emphasis (*word*) to appropriate font styles")
|
495 |
|
496 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
497 |
|
498 |
if auto_columns and 'markdown_content' in st.session_state:
|
499 |
current_markdown = st.session_state.markdown_content
|
@@ -503,29 +500,39 @@ with st.sidebar:
|
|
503 |
if line.strip():
|
504 |
word_count = len(line.split())
|
505 |
longest_line_words = max(longest_line_words, word_count)
|
506 |
-
if longest_line_words >
|
507 |
-
recommended_columns = 1
|
508 |
-
elif longest_line_words >= 18:
|
509 |
recommended_columns = 2
|
510 |
-
elif longest_line_words >=
|
511 |
recommended_columns = 3
|
|
|
|
|
512 |
else:
|
513 |
-
recommended_columns =
|
514 |
st.info(f"Longest line has {longest_line_words} words. Recommending {recommended_columns} columns.")
|
515 |
else:
|
516 |
-
recommended_columns =
|
517 |
|
518 |
column_options = ["Auto"] + list(range(1, 7))
|
519 |
-
num_columns = st.selectbox(
|
520 |
-
|
|
|
|
|
|
|
521 |
num_columns = 0 if num_columns == "Auto" else int(num_columns)
|
522 |
st.info("Font size and columns adjust to fit one page.")
|
523 |
|
524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
|
|
|
526 |
col1, col2 = st.columns(2)
|
527 |
with col1:
|
528 |
-
if st.button("
|
529 |
st.session_state.markdown_content = edited_markdown
|
530 |
if selected_md:
|
531 |
with open(f"{selected_md}.md", "w", encoding="utf-8") as f:
|
@@ -543,12 +550,13 @@ with st.sidebar:
|
|
543 |
|
544 |
prefix = get_timestamp_prefix()
|
545 |
st.download_button(
|
546 |
-
label="
|
547 |
data=st.session_state.markdown_content,
|
548 |
file_name=f"{prefix} {selected_md}.md" if selected_md else f"{prefix} default.md",
|
549 |
mime="text/markdown"
|
550 |
)
|
551 |
-
|
|
|
552 |
VOICES = ["en-US-AriaNeural", "en-US-JennyNeural", "en-GB-SoniaNeural", "en-US-GuyNeural", "en-US-AnaNeural"]
|
553 |
selected_voice = st.selectbox("Select Voice for TTS", options=VOICES, index=0)
|
554 |
if st.button("Generate Audio"):
|
@@ -559,7 +567,7 @@ with st.sidebar:
|
|
559 |
with open(audio_file, "rb") as f:
|
560 |
audio_bytes = f.read()
|
561 |
st.download_button(
|
562 |
-
label="
|
563 |
data=audio_bytes,
|
564 |
file_name=audio_filename,
|
565 |
mime="audio/mpeg"
|
@@ -601,9 +609,6 @@ with st.spinner("Generating PDF..."):
|
|
601 |
pdf_bytes = create_pdf(
|
602 |
st.session_state.markdown_content,
|
603 |
base_font_size,
|
604 |
-
render_with_bold,
|
605 |
-
auto_bold_numbers,
|
606 |
-
enlarge_numbered,
|
607 |
num_columns,
|
608 |
add_space_before_numbered,
|
609 |
headings_to_fonts,
|
@@ -611,6 +616,7 @@ with st.spinner("Generating PDF..."):
|
|
611 |
)
|
612 |
|
613 |
with st.container():
|
|
|
614 |
pdf_images = pdf_to_image(pdf_bytes)
|
615 |
if pdf_images:
|
616 |
for img in pdf_images:
|
@@ -620,7 +626,7 @@ with st.container():
|
|
620 |
|
621 |
with st.sidebar:
|
622 |
st.download_button(
|
623 |
-
label="
|
624 |
data=pdf_bytes,
|
625 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
626 |
mime="application/pdf"
|
|
|
22 |
from pypdf.annotations import Link
|
23 |
from pypdf.generic import Fit
|
24 |
|
25 |
+
st.set_page_config(layout="wide", initial_sidebar_state="expanded")
|
26 |
|
27 |
# Existing functions (unchanged)
|
28 |
def get_timestamp_prefix():
|
|
|
139 |
|
140 |
return ''.join(result)
|
141 |
|
142 |
+
def markdown_to_pdf_content(markdown_text, add_space_before_numbered, headings_to_fonts):
|
143 |
lines = markdown_text.strip().split('\n')
|
144 |
pdf_content = []
|
145 |
number_pattern = re.compile(r'^\d+(\.\d+)*\.\s')
|
|
|
174 |
line = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', line)
|
175 |
line = re.sub(r'\*([^*]+?)\*', r'<b>\1</b>', line)
|
176 |
|
|
|
|
|
|
|
|
|
|
|
177 |
pdf_content.append(line)
|
178 |
total_lines = len(pdf_content)
|
179 |
return pdf_content, total_lines
|
180 |
|
181 |
+
def create_pdf(markdown_text, base_font_size, num_columns, add_space_before_numbered, headings_to_fonts, doc_title):
|
182 |
buffer = io.BytesIO()
|
183 |
page_width = A4[0] * 2
|
184 |
page_height = A4[1]
|
|
|
193 |
)
|
194 |
styles = getSampleStyleSheet()
|
195 |
spacer_height = 10
|
196 |
+
pdf_content, total_lines = markdown_to_pdf_content(markdown_text, add_space_before_numbered, headings_to_fonts)
|
197 |
try:
|
198 |
available_font_files = glob.glob("*.ttf")
|
199 |
if not available_font_files:
|
|
|
232 |
)
|
233 |
numbered_bold_style = ParagraphStyle(
|
234 |
'NumberedBoldStyle', parent=styles['Normal'], fontName="NotoEmoji-Bold",
|
235 |
+
fontSize=adjusted_font_size, leading=adjusted_font_size * 1.15, spaceAfter=1,
|
|
|
236 |
linkUnderline=True
|
237 |
)
|
238 |
section_style = ParagraphStyle(
|
|
|
466 |
md_options = [os.path.splitext(os.path.basename(f))[0] for f in md_files]
|
467 |
|
468 |
with st.sidebar:
|
469 |
+
st.markdown("### π PDF Options")
|
470 |
if md_options:
|
471 |
selected_md = st.selectbox("Select Markdown File", options=md_options, index=0)
|
472 |
with open(f"{selected_md}.md", "r", encoding="utf-8") as f:
|
|
|
475 |
st.warning("No markdown file found. Please add one to your folder.")
|
476 |
selected_md = None
|
477 |
st.session_state.markdown_content = ""
|
478 |
+
|
479 |
available_font_files = {os.path.splitext(os.path.basename(f))[0]: f for f in glob.glob("*.ttf")}
|
480 |
+
selected_font_name = st.selectbox(
|
481 |
+
"Select Emoji Font",
|
482 |
+
options=list(available_font_files.keys()),
|
483 |
+
index=list(available_font_files.keys()).index("NotoEmoji-Bold") if "NotoEmoji-Bold" in available_font_files else 0
|
484 |
+
)
|
485 |
base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=8, step=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
486 |
|
487 |
+
add_space_before_numbered = st.checkbox("Add Space Ahead of Numbered Lines", value=False)
|
488 |
+
headings_to_fonts = st.checkbox(
|
489 |
+
"Headings to Fonts",
|
490 |
+
value=False,
|
491 |
+
help="Convert Markdown headings (# Heading) to styled fonts"
|
492 |
+
)
|
493 |
+
auto_columns = st.checkbox("AutoColumns", value=True)
|
494 |
|
495 |
if auto_columns and 'markdown_content' in st.session_state:
|
496 |
current_markdown = st.session_state.markdown_content
|
|
|
500 |
if line.strip():
|
501 |
word_count = len(line.split())
|
502 |
longest_line_words = max(longest_line_words, word_count)
|
503 |
+
if longest_line_words > 38:
|
|
|
|
|
504 |
recommended_columns = 2
|
505 |
+
elif longest_line_words >= 25:
|
506 |
recommended_columns = 3
|
507 |
+
elif longest_line_words >= 18:
|
508 |
+
recommended_columns = 4
|
509 |
else:
|
510 |
+
recommended_columns = 5
|
511 |
st.info(f"Longest line has {longest_line_words} words. Recommending {recommended_columns} columns.")
|
512 |
else:
|
513 |
+
recommended_columns = 3
|
514 |
|
515 |
column_options = ["Auto"] + list(range(1, 7))
|
516 |
+
num_columns = st.selectbox(
|
517 |
+
"Number of Columns",
|
518 |
+
options=column_options,
|
519 |
+
index=0 if recommended_columns == "Auto" else column_options.index(recommended_columns)
|
520 |
+
)
|
521 |
num_columns = 0 if num_columns == "Auto" else int(num_columns)
|
522 |
st.info("Font size and columns adjust to fit one page.")
|
523 |
|
524 |
+
st.markdown("### βοΈ Edit Markdown")
|
525 |
+
edited_markdown = st.text_area(
|
526 |
+
"Input Markdown",
|
527 |
+
value=st.session_state.markdown_content,
|
528 |
+
height=200,
|
529 |
+
key=f"markdown_{selected_md}_{selected_font_name}_{num_columns}"
|
530 |
+
)
|
531 |
|
532 |
+
st.markdown("### πΎ Actions")
|
533 |
col1, col2 = st.columns(2)
|
534 |
with col1:
|
535 |
+
if st.button("π Update PDF"):
|
536 |
st.session_state.markdown_content = edited_markdown
|
537 |
if selected_md:
|
538 |
with open(f"{selected_md}.md", "w", encoding="utf-8") as f:
|
|
|
550 |
|
551 |
prefix = get_timestamp_prefix()
|
552 |
st.download_button(
|
553 |
+
label="πΎ Save Markdown",
|
554 |
data=st.session_state.markdown_content,
|
555 |
file_name=f"{prefix} {selected_md}.md" if selected_md else f"{prefix} default.md",
|
556 |
mime="text/markdown"
|
557 |
)
|
558 |
+
|
559 |
+
st.markdown("### π Text-to-Speech")
|
560 |
VOICES = ["en-US-AriaNeural", "en-US-JennyNeural", "en-GB-SoniaNeural", "en-US-GuyNeural", "en-US-AnaNeural"]
|
561 |
selected_voice = st.selectbox("Select Voice for TTS", options=VOICES, index=0)
|
562 |
if st.button("Generate Audio"):
|
|
|
567 |
with open(audio_file, "rb") as f:
|
568 |
audio_bytes = f.read()
|
569 |
st.download_button(
|
570 |
+
label="πΎ Save Audio",
|
571 |
data=audio_bytes,
|
572 |
file_name=audio_filename,
|
573 |
mime="audio/mpeg"
|
|
|
609 |
pdf_bytes = create_pdf(
|
610 |
st.session_state.markdown_content,
|
611 |
base_font_size,
|
|
|
|
|
|
|
612 |
num_columns,
|
613 |
add_space_before_numbered,
|
614 |
headings_to_fonts,
|
|
|
616 |
)
|
617 |
|
618 |
with st.container():
|
619 |
+
st.markdown("### π PDF Preview")
|
620 |
pdf_images = pdf_to_image(pdf_bytes)
|
621 |
if pdf_images:
|
622 |
for img in pdf_images:
|
|
|
626 |
|
627 |
with st.sidebar:
|
628 |
st.download_button(
|
629 |
+
label="πΎ Save PDF",
|
630 |
data=pdf_bytes,
|
631 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
632 |
mime="application/pdf"
|