awacke1 commited on
Commit
3c73f1c
·
verified ·
1 Parent(s): 06678db

Delete backup10.app.py

Browse files
Files changed (1) hide show
  1. backup10.app.py +0 -635
backup10.app.py DELETED
@@ -1,635 +0,0 @@
1
- import io
2
- import re
3
- import os
4
- import glob
5
- import asyncio
6
- import hashlib
7
- import unicodedata
8
- import streamlit as st
9
- from PIL import Image
10
- import fitz
11
- import edge_tts
12
- from reportlab.lib.pagesizes import A4
13
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
14
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
15
- from reportlab.lib import colors
16
- from reportlab.pdfbase import pdfmetrics
17
- from reportlab.pdfbase.ttfonts import TTFont
18
- from reportlab.pdfgen import canvas
19
- from datetime import datetime
20
- import pytz
21
- 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="collapsed")
26
-
27
- # Existing functions (unchanged)
28
- def get_timestamp_prefix():
29
- central = pytz.timezone("US/Central")
30
- now = datetime.now(central)
31
- return now.strftime("%a %m%d %I%M%p").upper()
32
-
33
- def clean_for_speech(text):
34
- text = text.replace("#", "")
35
- emoji_pattern = re.compile(
36
- r"[\U0001F300-\U0001F5FF"
37
- r"\U0001F600-\U0001F64F"
38
- r"\U0001F680-\U0001F6FF"
39
- r"\U0001F700-\U0001F77F"
40
- r"\U0001F780-\U0001F7FF"
41
- r"\U0001F800-\U0001F8FF"
42
- r"\U0001F900-\U0001F9FF"
43
- r"\U0001FA00-\U0001FA6F"
44
- r"\U0001FA70-\U0001FAFF"
45
- r"\u2600-\u26FF"
46
- r"\u2700-\u27BF]+", flags=re.UNICODE)
47
- text = emoji_pattern.sub('', text)
48
- return text
49
-
50
- def trim_emojis_except_numbered(markdown_text):
51
- emoji_pattern = re.compile(
52
- r"[\U0001F300-\U0001F5FF"
53
- r"\U0001F600-\U0001F64F"
54
- r"\U0001F680-\U0001F6FF"
55
- r"\U0001F700-\U0001F77F"
56
- r"\U0001F780-\U0001F7FF"
57
- r"\U0001F800-\U0001F8FF"
58
- r"\U0001F900-\U0001F9FF"
59
- r"\U0001FAD0-\U0001FAD9"
60
- r"\U0001FA00-\U0001FA6F"
61
- r"\U0001FA70-\U0001FAFF"
62
- r"\u2600-\u26FF"
63
- r"\u2700-\u27BF]+"
64
- )
65
- number_pattern = re.compile(r'^\d+\.\s')
66
- lines = markdown_text.strip().split('\n')
67
- processed_lines = []
68
-
69
- for line in lines:
70
- if number_pattern.match(line):
71
- processed_lines.append(line)
72
- else:
73
- processed_lines.append(emoji_pattern.sub('', line))
74
-
75
- return '\n'.join(processed_lines)
76
-
77
- async def generate_audio(text, voice, filename):
78
- communicate = edge_tts.Communicate(text, voice)
79
- await communicate.save(filename)
80
- return filename
81
-
82
- def detect_and_convert_links(text):
83
- url_pattern = re.compile(
84
- r'(https?://|www\.)[^\s\[\]()<>{}]+(\.[^\s\[\]()<>{}]+)+(/[^\s\[\]()<>{}]*)?',
85
- re.IGNORECASE
86
- )
87
- md_link_pattern = re.compile(r'\[(.*?)\]\((https?://[^\s\[\]()<>{}]+)\)')
88
- text = md_link_pattern.sub(r'<a href="\2">\1</a>', text)
89
- start_idx = 0
90
- result = []
91
- while start_idx < len(text):
92
- match = url_pattern.search(text, start_idx)
93
- if not match:
94
- result.append(text[start_idx:])
95
- break
96
- prev_text = text[start_idx:match.start()]
97
- tag_balance = prev_text.count('<a') - prev_text.count('</a')
98
- if tag_balance > 0:
99
- result.append(text[start_idx:match.end()])
100
- else:
101
- result.append(text[start_idx:match.start()])
102
- url = match.group(0)
103
- if url.startswith('www.'):
104
- url_with_prefix = 'http://' + url
105
- else:
106
- url_with_prefix = url
107
- result.append(f'<a href="{url_with_prefix}">{url}</a>')
108
- start_idx = match.end()
109
- return ''.join(result)
110
-
111
- def apply_emoji_font(text, emoji_font):
112
- link_pattern = re.compile(r'<a\s+href="([^"]+)">(.*?)</a>')
113
- links = []
114
- def save_link(match):
115
- link_idx = len(links)
116
- links.append((match.group(1), match.group(2)))
117
- return f"###LINK_{link_idx}###"
118
- text = link_pattern.sub(save_link, text)
119
- text = re.sub(r'<b>(.*?)</b>', lambda m: f'###BOLD_START###{m.group(1)}###BOLD_END###', text)
120
- emoji_pattern = re.compile(
121
- r"([\U0001F300-\U0001F5FF"
122
- r"\U0001F600-\U0001F64F"
123
- r"\U0001F680-\U0001F6FF"
124
- r"\U0001F700-\U0001F77F"
125
- r"\U0001F780-\U0001F7FF"
126
- r"\U0001F800-\U0001F8FF"
127
- r"\U0001F900-\U0001F9FF"
128
- r"\U0001FAD0-\U0001FAD9"
129
- r"\U0001FA00-\U0001FA6F"
130
- r"\U0001FA70-\U0001FAFF"
131
- r"\u2600-\u26FF"
132
- r"\u2700-\u27BF]+)"
133
- )
134
- def replace_emoji(match):
135
- emoji = match.group(1)
136
- emoji = unicodedata.normalize('NFC', emoji)
137
- return f'<font face="{emoji_font}">{emoji}</font>'
138
- segments = []
139
- last_pos = 0
140
- for match in emoji_pattern.finditer(text):
141
- start, end = match.span()
142
- if last_pos < start:
143
- segments.append(f'<font face="DejaVuSans">{text[last_pos:start]}</font>')
144
- segments.append(replace_emoji(match))
145
- last_pos = end
146
- if last_pos < len(text):
147
- segments.append(f'<font face="DejaVuSans">{text[last_pos:]}</font>')
148
- combined_text = ''.join(segments)
149
- combined_text = combined_text.replace('###BOLD_START###', '</font><b><font face="DejaVuSans">')
150
- combined_text = combined_text.replace('###BOLD_END###', '</font></b><font face="DejaVuSans">')
151
- for i, (url, label) in enumerate(links):
152
- placeholder = f"###LINK_{i}###"
153
- if placeholder in combined_text:
154
- parts = combined_text.split(placeholder)
155
- if len(parts) == 2:
156
- before, after = parts
157
- if before.rfind('<font') > before.rfind('</font>'):
158
- link_html = f'</font><a href="{url}">{label}</a><font face="DejaVuSans">'
159
- combined_text = before + link_html + after
160
- else:
161
- combined_text = before + f'<a href="{url}">{label}</a>' + after
162
- return combined_text
163
-
164
- def markdown_to_pdf_content(markdown_text, render_with_bold, auto_bold_numbers, add_space_before_numbered, headings_to_fonts):
165
- lines = markdown_text.strip().split('\n')
166
- pdf_content = []
167
- number_pattern = re.compile(r'^\d+\.\s')
168
- heading_pattern = re.compile(r'^(#{1,4})\s+(.+)$')
169
- first_numbered_seen = False
170
-
171
- for line in lines:
172
- line = line.strip()
173
- if not line:
174
- continue
175
-
176
- if headings_to_fonts and line.startswith('#'):
177
- heading_match = heading_pattern.match(line)
178
- if heading_match:
179
- level = len(heading_match.group(1))
180
- heading_text = heading_match.group(2).strip()
181
- formatted_heading = f"<h{level}>{heading_text}</h{level}>"
182
- pdf_content.append(formatted_heading)
183
- continue
184
-
185
- is_numbered_line = number_pattern.match(line) is not None
186
-
187
- if add_space_before_numbered and is_numbered_line:
188
- if first_numbered_seen and not line.startswith("1."):
189
- pdf_content.append("")
190
- if not first_numbered_seen:
191
- first_numbered_seen = True
192
-
193
- line = detect_and_convert_links(line)
194
-
195
- if render_with_bold or headings_to_fonts:
196
- line = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', line)
197
- if headings_to_fonts:
198
- line = re.sub(r'\*([^*]+?)\*', r'<b>\1</b>', line)
199
-
200
- if auto_bold_numbers and is_numbered_line:
201
- if not (line.startswith("<b>") and line.endswith("</b>")):
202
- if "<b>" in line and "</b>" in line:
203
- line = re.sub(r'</?b>', '', line)
204
- line = f"<b>{line}</b>"
205
- else:
206
- line = f"<b>{line}</b>"
207
- pdf_content.append(line)
208
- total_lines = len(pdf_content)
209
- return pdf_content, total_lines
210
-
211
- def create_pdf(markdown_text, base_font_size, render_with_bold, auto_bold_numbers, enlarge_numbered, num_columns, add_space_before_numbered, headings_to_fonts):
212
- buffer = io.BytesIO()
213
- page_width = A4[0] * 2
214
- page_height = A4[1]
215
- doc = SimpleDocTemplate(buffer, pagesize=(page_width, page_height), leftMargin=36, rightMargin=36, topMargin=36, bottomMargin=36)
216
- styles = getSampleStyleSheet()
217
- spacer_height = 10
218
- pdf_content, total_lines = markdown_to_pdf_content(markdown_text, render_with_bold, auto_bold_numbers, add_space_before_numbered, headings_to_fonts)
219
- try:
220
- available_font_files = glob.glob("*.ttf")
221
- if not available_font_files:
222
- st.error("No .ttf font files found.")
223
- return
224
- selected_font_path = next((f for f in available_font_files if "NotoEmoji-Bold" in f), None)
225
- if selected_font_path:
226
- pdfmetrics.registerFont(TTFont("NotoEmoji-Bold", selected_font_path))
227
- pdfmetrics.registerFont(TTFont("DejaVuSans", "DejaVuSans.ttf"))
228
- except Exception as e:
229
- st.error(f"Font registration error: {e}")
230
- return
231
- total_chars = sum(len(line) for line in pdf_content)
232
- hierarchy_weight = sum(1.5 if line.startswith("<b>") else 1 for line in pdf_content)
233
- content_density = total_lines * hierarchy_weight + total_chars / 50
234
- usable_height = page_height - 72 - spacer_height
235
- usable_width = page_width - 72
236
- avg_line_chars = total_chars / total_lines if total_lines > 0 else 50
237
- ideal_lines_per_col = 20
238
- suggested_columns = max(1, min(6, int(total_lines / ideal_lines_per_col) + 1))
239
- num_columns = num_columns if num_columns != 0 else suggested_columns
240
- col_width = usable_width / num_columns
241
- min_font_size = 6
242
- max_font_size = 16
243
- lines_per_col = total_lines / num_columns if num_columns > 0 else total_lines
244
- target_height_per_line = usable_height / lines_per_col if lines_per_col > 0 else usable_height
245
- estimated_font_size = int(target_height_per_line / 1.5)
246
- adjusted_font_size = max(min_font_size, min(max_font_size, estimated_font_size))
247
- if avg_line_chars > col_width / adjusted_font_size * 10:
248
- adjusted_font_size = int(col_width / (avg_line_chars / 10))
249
- adjusted_font_size = max(min_font_size, adjusted_font_size)
250
- item_style = ParagraphStyle(
251
- 'ItemStyle', parent=styles['Normal'], fontName="DejaVuSans",
252
- fontSize=adjusted_font_size, leading=adjusted_font_size * 1.15, spaceAfter=1,
253
- linkUnderline=True
254
- )
255
- numbered_bold_style = ParagraphStyle(
256
- 'NumberedBoldStyle', parent=styles['Normal'], fontName="NotoEmoji-Bold",
257
- fontSize=adjusted_font_size + 1 if enlarge_numbered else adjusted_font_size,
258
- leading=(adjusted_font_size + 1) * 1.15 if enlarge_numbered else adjusted_font_size * 1.15, spaceAfter=1,
259
- linkUnderline=True
260
- )
261
- section_style = ParagraphStyle(
262
- 'SectionStyle', parent=styles['Heading2'], fontName="DejaVuSans",
263
- textColor=colors.darkblue, fontSize=adjusted_font_size * 1.1, leading=adjusted_font_size * 1.32, spaceAfter=2,
264
- linkUnderline=True
265
- )
266
- columns = [[] for _ in range(num_columns)]
267
- lines_per_column = total_lines / num_columns if num_columns > 0 else total_lines
268
- current_line_count = 0
269
- current_column = 0
270
- number_pattern = re.compile(r'^\d+\.\s')
271
- for item in pdf_content:
272
- if current_line_count >= lines_per_column and current_column < num_columns - 1:
273
- current_column += 1
274
- current_line_count = 0
275
- columns[current_column].append(item)
276
- current_line_count += 1
277
- column_cells = [[] for _ in range(num_columns)]
278
- for col_idx, column in enumerate(columns):
279
- for item in column:
280
- if isinstance(item, str):
281
- heading_match = re.match(r'<h(\d)>(.*?)</h\1>', item) if headings_to_fonts else None
282
- if heading_match:
283
- level = int(heading_match.group(1))
284
- heading_text = heading_match.group(2)
285
- heading_style = ParagraphStyle(
286
- f'Heading{level}Style',
287
- parent=styles['Heading1'],
288
- fontName="DejaVuSans",
289
- textColor=colors.darkblue if level == 1 else (colors.black if level > 2 else colors.blue),
290
- fontSize=adjusted_font_size * (1.6 - (level-1)*0.15),
291
- leading=adjusted_font_size * (1.8 - (level-1)*0.15),
292
- spaceAfter=4 - (level-1),
293
- spaceBefore=6 - (level-1),
294
- linkUnderline=True
295
- )
296
- column_cells[col_idx].append(Paragraph(apply_emoji_font(heading_text, "NotoEmoji-Bold"), heading_style))
297
- elif item.startswith("<b>") and item.endswith("</b>"):
298
- content = item[3:-4].strip()
299
- if number_pattern.match(content):
300
- column_cells[col_idx].append(Paragraph(apply_emoji_font(content, "NotoEmoji-Bold"), numbered_bold_style))
301
- else:
302
- column_cells[col_idx].append(Paragraph(apply_emoji_font(content, "NotoEmoji-Bold"), section_style))
303
- else:
304
- column_cells[col_idx].append(Paragraph(apply_emoji_font(item, "DejaVuSans"), item_style))
305
- else:
306
- column_cells[col_idx].append(Paragraph(apply_emoji_font(str(item), "DejaVuSans"), item_style))
307
- max_cells = max(len(cells) for cells in column_cells) if column_cells else 0
308
- for cells in column_cells:
309
- cells.extend([Paragraph("", item_style)] * (max_cells - len(cells)))
310
- table_data = list(zip(*column_cells)) if column_cells else [[]]
311
- table = Table(table_data, colWidths=[col_width] * num_columns, hAlign='CENTER')
312
- table.setStyle(TableStyle([
313
- ('VALIGN', (0, 0), (-1, -1), 'TOP'),
314
- ('ALIGN', (0, 0), (-1, -1), 'LEFT'),
315
- ('BACKGROUND', (0, 0), (-1, -1), colors.white),
316
- ('GRID', (0, 0), (-1, -1), 0, colors.white),
317
- ('LINEAFTER', (0, 0), (num_columns-1, -1), 0.5, colors.grey),
318
- ('LEFTPADDING', (0, 0), (-1, -1), 2),
319
- ('RIGHTPADDING', (0, 0), (-1, -1), 2),
320
- ('TOPPADDING', (0, 0), (-1, -1), 1),
321
- ('BOTTOMPADDING', (0, 0), (-1, -1), 1),
322
- ]))
323
- story = [Spacer(1, spacer_height), table]
324
- doc.build(story)
325
- buffer.seek(0)
326
- return buffer.getvalue()
327
-
328
- def pdf_to_image(pdf_bytes):
329
- try:
330
- doc = fitz.open(stream=pdf_bytes, filetype="pdf")
331
- images = []
332
- for page in doc:
333
- pix = page.get_pixmap(matrix=fitz.Matrix(2.0, 2.0))
334
- img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
335
- images.append(img)
336
- doc.close()
337
- return images
338
- except Exception as e:
339
- st.error(f"Failed to render PDF preview: {e}")
340
- return None
341
-
342
- # PDF creation and linking functions
343
- WORDS_10 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]
344
- WORDS_20 = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
345
- "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]
346
-
347
- def create_crossfile_pdfs(source_pdf="TestSource.pdf", target_pdf="TestTarget.pdf"):
348
- """Create two PDFs with cross-file linking."""
349
- def create_base_pdf(filename):
350
- buffer = io.BytesIO()
351
- c = canvas.Canvas(buffer)
352
- c.setFont("Helvetica", 12)
353
- for i, word in enumerate(WORDS_10, 1):
354
- y = 800 - (i * 20)
355
- c.drawString(50, y, f"{i}. {word}")
356
- c.showPage()
357
- c.save()
358
- buffer.seek(0)
359
- with open(filename, "wb") as f:
360
- f.write(buffer.getvalue())
361
- buffer.close()
362
-
363
- def add_bookmark_to_seven(pdf_file):
364
- reader = PdfReader(pdf_file)
365
- writer = PdfWriter()
366
- for page in reader.pages:
367
- writer.add_page(page)
368
- page = writer.pages[0]
369
- y_position = 800 - (7 * 20)
370
- fit = Fit(fit_type="/XYZ", fit_args=[50, y_position, 0])
371
- writer.add_outline_item("Seven Bookmark", 0, fit=fit)
372
- with open(pdf_file, "wb") as f:
373
- writer.write(f)
374
-
375
- def modify_source_pdf(source, target):
376
- reader = PdfReader(source)
377
- writer = PdfWriter()
378
- for page in reader.pages:
379
- writer.add_page(page)
380
- buffer = io.BytesIO()
381
- c = canvas.Canvas(buffer)
382
- c.setFont("Helvetica", 8)
383
- seven_y = 800 - (7 * 20)
384
- c.drawString(90, seven_y - 5, "link")
385
- c.showPage()
386
- c.save()
387
- buffer.seek(0)
388
- text_pdf = PdfReader(buffer)
389
- page = writer.pages[0]
390
- page.merge_page(text_pdf.pages[0])
391
- link = Link(
392
- rect=(90, seven_y - 10, 150, seven_y + 10),
393
- url=f"file://{os.path.abspath(target)}#page=1"
394
- )
395
- writer.add_annotation(page_number=0, annotation=link)
396
- with open(source, "wb") as f:
397
- writer.write(f)
398
- buffer.close()
399
-
400
- def add_internal_link(pdf_file):
401
- reader = PdfReader(pdf_file)
402
- writer = PdfWriter()
403
- for page in reader.pages:
404
- writer.add_page(page)
405
- one_y = 800 - (1 * 20)
406
- ten_y = 800 - (10 * 20)
407
- link = Link(
408
- rect=(50, one_y - 10, 100, one_y + 10),
409
- target_page_index=0,
410
- fit=Fit(fit_type="/XYZ", fit_args=[50, ten_y, 0])
411
- )
412
- writer.add_annotation(page_number=0, annotation=link)
413
- with open(pdf_file, "wb") as f:
414
- writer.write(f)
415
-
416
- create_base_pdf(source_pdf)
417
- create_base_pdf(target_pdf)
418
- add_bookmark_to_seven(target_pdf)
419
- modify_source_pdf(source_pdf, target_pdf)
420
- add_internal_link(source_pdf)
421
- add_internal_link(target_pdf)
422
- return source_pdf, target_pdf
423
-
424
- def create_selflinking_pdf(pdf_file="SelfLinking.pdf"):
425
- """Create a PDF with a TOC on page 1 linking to a 1-20 list starting on page 2."""
426
- buffer = io.BytesIO()
427
- c = canvas.Canvas(buffer)
428
-
429
- # Page 1: Table of Contents
430
- c.setFont("Helvetica", 14)
431
- c.drawString(50, 800, "Table of Contents")
432
- c.setFont("Helvetica", 12)
433
- toc_y_positions = []
434
- for i, word in enumerate(WORDS_10, 1):
435
- y = 760 - (i * 20)
436
- c.drawString(50, y, f"{word}")
437
- toc_y_positions.append(y)
438
- c.showPage()
439
-
440
- # Page 2: Numbered list 1-20
441
- c.setFont("Helvetica", 12)
442
- list_y_positions = []
443
- for i, word in enumerate(WORDS_20, 1):
444
- y = 800 - (i * 20)
445
- c.drawString(50, y, f"{i}. {word}")
446
- list_y_positions.append(y)
447
- c.showPage()
448
-
449
- # Save the initial PDF
450
- c.save()
451
- buffer.seek(0)
452
- with open(pdf_file, "wb") as f:
453
- f.write(buffer.getvalue())
454
- buffer.close()
455
-
456
- # Add outlines and links
457
- reader = PdfReader(pdf_file)
458
- writer = PdfWriter()
459
- for page in reader.pages:
460
- writer.add_page(page)
461
-
462
- # Add outline entries
463
- toc_page = writer.pages[0]
464
- list_page = writer.pages[1]
465
- writer.add_outline_item("Table of Contents", 0, fit=Fit(fit_type="/Fit"))
466
- for i, word in enumerate(WORDS_10, 1):
467
- y = list_y_positions[i-1]
468
- writer.add_outline_item(word, 1, fit=Fit(fit_type="/XYZ", fit_args=[50, y, 0]))
469
-
470
- # Add TOC links from page 1 to page 2
471
- for i, word in enumerate(WORDS_10):
472
- toc_y = toc_y_positions[i]
473
- list_y = list_y_positions[i]
474
- link = Link(
475
- rect=(50, toc_y - 10, 150, toc_y + 10),
476
- target_page_index=1,
477
- fit=Fit(fit_type="/XYZ", fit_args=[50, list_y, 0])
478
- )
479
- writer.add_annotation(page_number=0, annotation=link)
480
-
481
- # Save the modified PDF
482
- with open(pdf_file, "wb") as f:
483
- writer.write(f)
484
-
485
- return pdf_file
486
-
487
- # Streamlit UI
488
- md_files = [f for f in glob.glob("*.md") if os.path.basename(f) != "README.md"]
489
- md_options = [os.path.splitext(os.path.basename(f))[0] for f in md_files]
490
-
491
- with st.sidebar:
492
- st.markdown("### PDF Options")
493
- if md_options:
494
- selected_md = st.selectbox("Select Markdown File", options=md_options, index=0)
495
- with open(f"{selected_md}.md", "r", encoding="utf-8") as f:
496
- st.session_state.markdown_content = f.read()
497
- else:
498
- st.warning("No markdown file found. Please add one to your folder.")
499
- selected_md = None
500
- st.session_state.markdown_content = ""
501
- available_font_files = {os.path.splitext(os.path.basename(f))[0]: f for f in glob.glob("*.ttf")}
502
- selected_font_name = st.selectbox("Select Emoji Font", options=list(available_font_files.keys()),
503
- index=list(available_font_files.keys()).index("NotoEmoji-Bold") if "NotoEmoji-Bold" in available_font_files else 0)
504
- base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=8, step=1)
505
- render_with_bold = st.checkbox("Render with Bold Formatting (remove ** markers)", value=True, key="render_with_bold")
506
- auto_bold_numbers = st.checkbox("Auto Bold Numbered Lines", value=True, key="auto_bold_numbers")
507
- enlarge_numbered = st.checkbox("Enlarge Font Size for Numbered Lines", value=True, key="enlarge_numbered")
508
- add_space_before_numbered = st.checkbox("Add Space Ahead of Numbered Lines", value=False, key="add_space_before_numbered")
509
-
510
- # Here we use a font that has more emojis
511
- headings_to_fonts = st.checkbox("Headings to Fonts", value=False, key="headings_to_fonts",
512
- help="Convert Markdown headings (# Heading) and emphasis (*word*) to appropriate font styles")
513
-
514
- auto_columns = st.checkbox("AutoColumns", value=False, key="auto_columns")
515
-
516
- if auto_columns and 'markdown_content' in st.session_state:
517
- current_markdown = st.session_state.markdown_content
518
- lines = current_markdown.strip().split('\n')
519
- longest_line_words = 0
520
- for line in lines:
521
- if line.strip():
522
- word_count = len(line.split())
523
- longest_line_words = max(longest_line_words, word_count)
524
- if longest_line_words > 25:
525
- recommended_columns = 1
526
- elif longest_line_words >= 18:
527
- recommended_columns = 2
528
- elif longest_line_words >= 11:
529
- recommended_columns = 3
530
- else:
531
- recommended_columns = "Auto"
532
- st.info(f"Longest line has {longest_line_words} words. Recommending {recommended_columns} columns.")
533
- else:
534
- recommended_columns = "Auto"
535
-
536
- column_options = ["Auto"] + list(range(1, 7))
537
- num_columns = st.selectbox("Number of Columns", options=column_options,
538
- index=0 if recommended_columns == "Auto" else column_options.index(recommended_columns))
539
- num_columns = 0 if num_columns == "Auto" else int(num_columns)
540
- st.info("Font size and columns adjust to fit one page.")
541
-
542
- edited_markdown = st.text_area("Input Markdown", value=st.session_state.markdown_content, height=300, key=f"markdown_{selected_md}_{selected_font_name}_{num_columns}")
543
-
544
- col1, col2 = st.columns(2)
545
- with col1:
546
- if st.button("🔄📄 Update PDF"):
547
- st.session_state.markdown_content = edited_markdown
548
- if selected_md:
549
- with open(f"{selected_md}.md", "w", encoding="utf-8") as f:
550
- f.write(edited_markdown)
551
- st.rerun()
552
-
553
- with col2:
554
- if st.button("✂️ Trim Emojis"):
555
- trimmed_content = trim_emojis_except_numbered(edited_markdown)
556
- st.session_state.markdown_content = trimmed_content
557
- if selected_md:
558
- with open(f"{selected_md}.md", "w", encoding="utf-8") as f:
559
- f.write(trimmed_content)
560
- st.rerun()
561
-
562
- prefix = get_timestamp_prefix()
563
- st.download_button(
564
- label="💾📝 Save Markdown",
565
- data=st.session_state.markdown_content,
566
- file_name=f"{prefix} {selected_md}.md" if selected_md else f"{prefix} default.md",
567
- mime="text/markdown"
568
- )
569
- st.markdown("### Text-to-Speech")
570
- VOICES = ["en-US-AriaNeural", "en-US-JennyNeural", "en-GB-SoniaNeural", "en-US-GuyNeural", "en-US-AnaNeural"]
571
- selected_voice = st.selectbox("Select Voice for TTS", options=VOICES, index=0)
572
- if st.button("Generate Audio"):
573
- cleaned_text = clean_for_speech(st.session_state.markdown_content)
574
- audio_filename = f"{prefix} {selected_md} {selected_voice}.mp3" if selected_md else f"{prefix} default {selected_voice}.mp3"
575
- audio_file = asyncio.run(generate_audio(cleaned_text, selected_voice, audio_filename))
576
- st.audio(audio_file)
577
- with open(audio_file, "rb") as f:
578
- audio_bytes = f.read()
579
- st.download_button(
580
- label="💾🔊 Save Audio",
581
- data=audio_bytes,
582
- file_name=audio_filename,
583
- mime="audio/mpeg"
584
- )
585
-
586
- if st.button("📑 Create CrossFile PDFs"):
587
- with st.spinner("Creating cross-file linked PDFs..."):
588
- source_pdf, target_pdf = create_crossfile_pdfs()
589
- st.success(f"Created {source_pdf} and {target_pdf}")
590
- for pdf_file in [source_pdf, target_pdf]:
591
- with open(pdf_file, "rb") as f:
592
- st.download_button(
593
- label=f"💾 Download {pdf_file}",
594
- data=f.read(),
595
- file_name=pdf_file,
596
- mime="application/pdf"
597
- )
598
-
599
- if st.button("🧪 Create SelfLinking PDF"):
600
- with st.spinner("Generating self-linking PDF with TOC..."):
601
- pdf_file = create_selflinking_pdf()
602
- st.success(f"Generated {pdf_file}")
603
- with open(pdf_file, "rb") as f:
604
- pdf_bytes = f.read()
605
- images = pdf_to_image(pdf_bytes)
606
- if images:
607
- st.subheader(f"Preview of {pdf_file}")
608
- for i, img in enumerate(images):
609
- st.image(img, caption=f"{pdf_file} Page {i+1}", use_container_width=True)
610
- with open(pdf_file, "rb") as f:
611
- st.download_button(
612
- label=f"💾 Download {pdf_file}",
613
- data=f.read(),
614
- file_name=pdf_file,
615
- mime="application/pdf"
616
- )
617
-
618
- with st.spinner("Generating PDF..."):
619
- pdf_bytes = create_pdf(st.session_state.markdown_content, base_font_size, render_with_bold, auto_bold_numbers, enlarge_numbered, num_columns, add_space_before_numbered, headings_to_fonts)
620
-
621
- with st.container():
622
- pdf_images = pdf_to_image(pdf_bytes)
623
- if pdf_images:
624
- for img in pdf_images:
625
- st.image(img, use_container_width=True)
626
- else:
627
- st.info("Download the PDF to view it locally.")
628
-
629
- with st.sidebar:
630
- st.download_button(
631
- label="💾📄 Save PDF",
632
- data=pdf_bytes,
633
- file_name=f"{prefix} {selected_md}.pdf" if selected_md else f"{prefix} output.pdf",
634
- mime="application/pdf"
635
- )