Spaces:
Sleeping
Sleeping
Update backup11.app.py
Browse files- backup11.app.py +81 -57
backup11.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:
|
|
@@ -218,7 +213,7 @@ def create_pdf(markdown_text, base_font_size, render_with_bold, auto_bold_number
|
|
| 218 |
usable_width = page_width - 72
|
| 219 |
avg_line_chars = total_chars / total_lines if total_lines > 0 else 50
|
| 220 |
ideal_lines_per_col = 20
|
| 221 |
-
suggested_columns = max(
|
| 222 |
num_columns = num_columns if num_columns != 0 else suggested_columns
|
| 223 |
col_width = usable_width / num_columns
|
| 224 |
min_font_size = 6
|
|
@@ -230,6 +225,12 @@ def create_pdf(markdown_text, base_font_size, render_with_bold, auto_bold_number
|
|
| 230 |
if avg_line_chars > col_width / adjusted_font_size * 10:
|
| 231 |
adjusted_font_size = int(col_width / (avg_line_chars / 10))
|
| 232 |
adjusted_font_size = max(min_font_size, adjusted_font_size)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
item_style = ParagraphStyle(
|
| 234 |
'ItemStyle', parent=styles['Normal'], fontName="DejaVuSans",
|
| 235 |
fontSize=adjusted_font_size, leading=adjusted_font_size * 1.15, spaceAfter=1,
|
|
@@ -237,8 +238,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(
|
|
@@ -323,9 +323,9 @@ def pdf_to_image(pdf_bytes):
|
|
| 323 |
return None
|
| 324 |
|
| 325 |
# PDF creation and linking functions
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]
|
| 329 |
|
| 330 |
def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pdf"):
|
| 331 |
"""Create two PDFs with cross-file linking."""
|
|
@@ -333,7 +333,7 @@ def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pd
|
|
| 333 |
buffer = io.BytesIO()
|
| 334 |
c = canvas.Canvas(buffer)
|
| 335 |
c.setFont("Helvetica", 12)
|
| 336 |
-
for i, word in enumerate(
|
| 337 |
y = 800 - (i * 20)
|
| 338 |
c.drawString(50, y, f"{i}. {word}")
|
| 339 |
c.showPage()
|
|
@@ -414,7 +414,7 @@ def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
|
| 414 |
c.drawString(50, 800, "Table of Contents")
|
| 415 |
c.setFont("Helvetica", 12)
|
| 416 |
toc_y_positions = []
|
| 417 |
-
for i, word in enumerate(
|
| 418 |
y = 760 - (i * 20)
|
| 419 |
c.drawString(50, y, f"{word}")
|
| 420 |
toc_y_positions.append(y)
|
|
@@ -423,7 +423,7 @@ def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
|
| 423 |
# Page 2: Numbered list 1-20
|
| 424 |
c.setFont("Helvetica", 12)
|
| 425 |
list_y_positions = []
|
| 426 |
-
for i, word in enumerate(
|
| 427 |
y = 800 - (i * 20)
|
| 428 |
c.drawString(50, y, f"{i}. {word}")
|
| 429 |
list_y_positions.append(y)
|
|
@@ -446,12 +446,12 @@ def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
|
|
| 446 |
toc_page = writer.pages[0]
|
| 447 |
list_page = writer.pages[1]
|
| 448 |
writer.add_outline_item("Table of Contents", 0, fit=Fit(fit_type="/Fit"))
|
| 449 |
-
for i, word in enumerate(
|
| 450 |
y = list_y_positions[i-1]
|
| 451 |
writer.add_outline_item(word, 1, fit=Fit(fit_type="/XYZ", fit_args=[50, y, 0]))
|
| 452 |
|
| 453 |
# Add TOC links from page 1 to page 2
|
| 454 |
-
for i, word in enumerate(
|
| 455 |
toc_y = toc_y_positions[i]
|
| 456 |
list_y = list_y_positions[i]
|
| 457 |
link = Link(
|
|
@@ -472,7 +472,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,51 +481,74 @@ 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
current_markdown = st.session_state.markdown_content
|
| 500 |
lines = current_markdown.strip().split('\n')
|
| 501 |
-
|
| 502 |
for line in lines:
|
| 503 |
if line.strip():
|
| 504 |
word_count = len(line.split())
|
| 505 |
longest_line_words = max(longest_line_words, word_count)
|
| 506 |
-
if
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
|
|
|
| 512 |
else:
|
| 513 |
-
recommended_columns =
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 517 |
|
| 518 |
-
column_options = [
|
| 519 |
-
num_columns = st.selectbox(
|
| 520 |
-
|
| 521 |
-
|
|
|
|
|
|
|
| 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 +566,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 +583,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,16 +625,16 @@ 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,
|
| 610 |
-
doc_title=selected_md if selected_md else "Untitled"
|
|
|
|
|
|
|
| 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 +644,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, longest_line_words, total_lines):
|
| 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:
|
|
|
|
| 213 |
usable_width = page_width - 72
|
| 214 |
avg_line_chars = total_chars / total_lines if total_lines > 0 else 50
|
| 215 |
ideal_lines_per_col = 20
|
| 216 |
+
suggested_columns = max(2, min(4, int(total_lines / ideal_lines_per_col) + 1))
|
| 217 |
num_columns = num_columns if num_columns != 0 else suggested_columns
|
| 218 |
col_width = usable_width / num_columns
|
| 219 |
min_font_size = 6
|
|
|
|
| 225 |
if avg_line_chars > col_width / adjusted_font_size * 10:
|
| 226 |
adjusted_font_size = int(col_width / (avg_line_chars / 10))
|
| 227 |
adjusted_font_size = max(min_font_size, adjusted_font_size)
|
| 228 |
+
|
| 229 |
+
# Adjust font size to fit one page based on longest line
|
| 230 |
+
if longest_line_words > 17:
|
| 231 |
+
font_scale = 17 / longest_line_words # Scale down from reference (17 words)
|
| 232 |
+
adjusted_font_size = max(min_font_size, int(adjusted_font_size * font_scale))
|
| 233 |
+
|
| 234 |
item_style = ParagraphStyle(
|
| 235 |
'ItemStyle', parent=styles['Normal'], fontName="DejaVuSans",
|
| 236 |
fontSize=adjusted_font_size, leading=adjusted_font_size * 1.15, spaceAfter=1,
|
|
|
|
| 238 |
)
|
| 239 |
numbered_bold_style = ParagraphStyle(
|
| 240 |
'NumberedBoldStyle', parent=styles['Normal'], fontName="NotoEmoji-Bold",
|
| 241 |
+
fontSize=adjusted_font_size, leading=adjusted_font_size * 1.15, spaceAfter=1,
|
|
|
|
| 242 |
linkUnderline=True
|
| 243 |
)
|
| 244 |
section_style = ParagraphStyle(
|
|
|
|
| 323 |
return None
|
| 324 |
|
| 325 |
# PDF creation and linking functions
|
| 326 |
+
WORDS_12 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"]
|
| 327 |
+
WORDS_24 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
|
| 328 |
+
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty", "twenty one", "twenty two", "twenty three", "twenty four"]
|
| 329 |
|
| 330 |
def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pdf"):
|
| 331 |
"""Create two PDFs with cross-file linking."""
|
|
|
|
| 333 |
buffer = io.BytesIO()
|
| 334 |
c = canvas.Canvas(buffer)
|
| 335 |
c.setFont("Helvetica", 12)
|
| 336 |
+
for i, word in enumerate(WORDS_12, 1):
|
| 337 |
y = 800 - (i * 20)
|
| 338 |
c.drawString(50, y, f"{i}. {word}")
|
| 339 |
c.showPage()
|
|
|
|
| 414 |
c.drawString(50, 800, "Table of Contents")
|
| 415 |
c.setFont("Helvetica", 12)
|
| 416 |
toc_y_positions = []
|
| 417 |
+
for i, word in enumerate(WORDS_12, 1):
|
| 418 |
y = 760 - (i * 20)
|
| 419 |
c.drawString(50, y, f"{word}")
|
| 420 |
toc_y_positions.append(y)
|
|
|
|
| 423 |
# Page 2: Numbered list 1-20
|
| 424 |
c.setFont("Helvetica", 12)
|
| 425 |
list_y_positions = []
|
| 426 |
+
for i, word in enumerate(WORDS_24, 1):
|
| 427 |
y = 800 - (i * 20)
|
| 428 |
c.drawString(50, y, f"{i}. {word}")
|
| 429 |
list_y_positions.append(y)
|
|
|
|
| 446 |
toc_page = writer.pages[0]
|
| 447 |
list_page = writer.pages[1]
|
| 448 |
writer.add_outline_item("Table of Contents", 0, fit=Fit(fit_type="/Fit"))
|
| 449 |
+
for i, word in enumerate(WORDS_12, 1):
|
| 450 |
y = list_y_positions[i-1]
|
| 451 |
writer.add_outline_item(word, 1, fit=Fit(fit_type="/XYZ", fit_args=[50, y, 0]))
|
| 452 |
|
| 453 |
# Add TOC links from page 1 to page 2
|
| 454 |
+
for i, word in enumerate(WORDS_12):
|
| 455 |
toc_y = toc_y_positions[i]
|
| 456 |
list_y = list_y_positions[i]
|
| 457 |
link = Link(
|
|
|
|
| 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 |
st.warning("No markdown file found. Please add one to your folder.")
|
| 482 |
selected_md = None
|
| 483 |
st.session_state.markdown_content = ""
|
| 484 |
+
|
| 485 |
available_font_files = {os.path.splitext(os.path.basename(f))[0]: f for f in glob.glob("*.ttf")}
|
| 486 |
+
selected_font_name = st.selectbox(
|
| 487 |
+
"Select Emoji Font",
|
| 488 |
+
options=list(available_font_files.keys()),
|
| 489 |
+
index=list(available_font_files.keys()).index("NotoEmoji-Bold") if "NotoEmoji-Bold" in available_font_files else 0
|
| 490 |
+
)
|
| 491 |
base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=8, step=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
|
| 493 |
+
add_space_before_numbered = st.checkbox("Add Space Ahead of Numbered Lines", value=False)
|
| 494 |
+
headings_to_fonts = st.checkbox(
|
| 495 |
+
"Headings to Fonts",
|
| 496 |
+
value=False,
|
| 497 |
+
help="Convert Markdown headings (# Heading) to styled fonts"
|
| 498 |
+
)
|
| 499 |
+
auto_columns = st.checkbox("AutoColumns", value=True)
|
| 500 |
|
| 501 |
+
# Calculate document stats
|
| 502 |
+
longest_line_words = 0
|
| 503 |
+
total_lines = 0
|
| 504 |
+
adjusted_font_size_display = base_font_size
|
| 505 |
+
if 'markdown_content' in st.session_state:
|
| 506 |
current_markdown = st.session_state.markdown_content
|
| 507 |
lines = current_markdown.strip().split('\n')
|
| 508 |
+
total_lines = len([line for line in lines if line.strip()])
|
| 509 |
for line in lines:
|
| 510 |
if line.strip():
|
| 511 |
word_count = len(line.split())
|
| 512 |
longest_line_words = max(longest_line_words, word_count)
|
| 513 |
+
if auto_columns:
|
| 514 |
+
if longest_line_words > 38:
|
| 515 |
+
recommended_columns = 2
|
| 516 |
+
elif longest_line_words < 18 and total_lines < 20:
|
| 517 |
+
recommended_columns = 4
|
| 518 |
+
else:
|
| 519 |
+
recommended_columns = 3
|
| 520 |
else:
|
| 521 |
+
recommended_columns = 3
|
| 522 |
+
# Adjust font size for one-page fit
|
| 523 |
+
if longest_line_words > 17:
|
| 524 |
+
font_scale = 17 / longest_line_words
|
| 525 |
+
adjusted_font_size_display = max(6, int(base_font_size * font_scale))
|
| 526 |
+
st.markdown("**Document Stats**")
|
| 527 |
+
st.write(f"- Longest Line: {longest_line_words} words")
|
| 528 |
+
st.write(f"- Total Lines: {total_lines}")
|
| 529 |
+
st.write(f"- Recommended Columns: {recommended_columns}")
|
| 530 |
+
st.write(f"- Adjusted Font Size: {adjusted_font_size_display} points")
|
| 531 |
|
| 532 |
+
column_options = [2, 3, 4]
|
| 533 |
+
num_columns = st.selectbox(
|
| 534 |
+
"Number of Columns",
|
| 535 |
+
options=column_options,
|
| 536 |
+
index=column_options.index(recommended_columns)
|
| 537 |
+
)
|
| 538 |
st.info("Font size and columns adjust to fit one page.")
|
| 539 |
|
| 540 |
+
st.markdown("### βοΈ Edit Markdown")
|
| 541 |
+
edited_markdown = st.text_area(
|
| 542 |
+
"Input Markdown",
|
| 543 |
+
value=st.session_state.markdown_content,
|
| 544 |
+
height=200,
|
| 545 |
+
key=f"markdown_{selected_md}_{selected_font_name}_{num_columns}"
|
| 546 |
+
)
|
| 547 |
|
| 548 |
+
st.markdown("### πΎ Actions")
|
| 549 |
col1, col2 = st.columns(2)
|
| 550 |
with col1:
|
| 551 |
+
if st.button("π Update PDF"):
|
| 552 |
st.session_state.markdown_content = edited_markdown
|
| 553 |
if selected_md:
|
| 554 |
with open(f"{selected_md}.md", "w", encoding="utf-8") as f:
|
|
|
|
| 566 |
|
| 567 |
prefix = get_timestamp_prefix()
|
| 568 |
st.download_button(
|
| 569 |
+
label="πΎ Save Markdown",
|
| 570 |
data=st.session_state.markdown_content,
|
| 571 |
file_name=f"{prefix} {selected_md}.md" if selected_md else f"{prefix} default.md",
|
| 572 |
mime="text/markdown"
|
| 573 |
)
|
| 574 |
+
|
| 575 |
+
st.markdown("### π Text-to-Speech")
|
| 576 |
VOICES = ["en-US-AriaNeural", "en-US-JennyNeural", "en-GB-SoniaNeural", "en-US-GuyNeural", "en-US-AnaNeural"]
|
| 577 |
selected_voice = st.selectbox("Select Voice for TTS", options=VOICES, index=0)
|
| 578 |
if st.button("Generate Audio"):
|
|
|
|
| 583 |
with open(audio_file, "rb") as f:
|
| 584 |
audio_bytes = f.read()
|
| 585 |
st.download_button(
|
| 586 |
+
label="πΎ Save Audio",
|
| 587 |
data=audio_bytes,
|
| 588 |
file_name=audio_filename,
|
| 589 |
mime="audio/mpeg"
|
|
|
|
| 625 |
pdf_bytes = create_pdf(
|
| 626 |
st.session_state.markdown_content,
|
| 627 |
base_font_size,
|
|
|
|
|
|
|
|
|
|
| 628 |
num_columns,
|
| 629 |
add_space_before_numbered,
|
| 630 |
headings_to_fonts,
|
| 631 |
+
doc_title=selected_md if selected_md else "Untitled",
|
| 632 |
+
longest_line_words=longest_line_words,
|
| 633 |
+
total_lines=total_lines
|
| 634 |
)
|
| 635 |
|
| 636 |
with st.container():
|
| 637 |
+
st.markdown("### π PDF Preview")
|
| 638 |
pdf_images = pdf_to_image(pdf_bytes)
|
| 639 |
if pdf_images:
|
| 640 |
for img in pdf_images:
|
|
|
|
| 644 |
|
| 645 |
with st.sidebar:
|
| 646 |
st.download_button(
|
| 647 |
+
label="πΎ Save PDF",
|
| 648 |
data=pdf_bytes,
|
| 649 |
file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
|
| 650 |
mime="application/pdf"
|