Spaces:
Sleeping
Sleeping
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"
|