awacke1 commited on
Commit
41bb485
Β·
verified Β·
1 Parent(s): 0331283

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -38
app.py CHANGED
@@ -1,6 +1,8 @@
1
  import io
2
  import re
3
  import streamlit as st
 
 
4
  from PIL import Image
5
  import fitz
6
  from reportlab.lib.pagesizes import A4
@@ -13,22 +15,22 @@ from reportlab.pdfbase.ttfonts import TTFont
13
  st.set_page_config(layout="wide", initial_sidebar_state="collapsed")
14
 
15
  def create_pdf_tab(default_markdown):
16
- # Font setup
17
- available_fonts = {
18
- "NotoEmoji Variable": "NotoEmoji-VariableFont_wght.ttf",
19
- "NotoEmoji Bold": "NotoEmoji-Bold.ttf",
20
- "NotoEmoji Light": "NotoEmoji-Light.ttf",
21
- "NotoEmoji Medium": "NotoEmoji-Medium.ttf",
22
- "NotoEmoji Regular": "NotoEmoji-Regular.ttf",
23
- "NotoEmoji SemiBold": "NotoEmoji-SemiBold.ttf"
24
- }
25
 
26
  # Sidebar configuration
27
  with st.sidebar:
28
- selected_font_name = st.selectbox("Select NotoEmoji Font", options=list(available_fonts.keys()))
29
  selected_font_path = available_fonts[selected_font_name]
30
  base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=9, step=1) # Default to 9
31
  plain_text_mode = st.checkbox("Render as Plain Text (Preserve Bold Only)", value=False)
 
32
  num_columns = st.selectbox("Number of Columns", options=[1, 2, 3, 4, 5, 6], index=3) # Default to 4
33
 
34
  # Markdown editor and buttons
@@ -42,35 +44,47 @@ def create_pdf_tab(default_markdown):
42
 
43
  st.download_button(label="Save Markdown", data=st.session_state.markdown_content, file_name="deities_guide.md", mime="text/markdown")
44
 
45
- # Register font
 
46
  pdfmetrics.registerFont(TTFont(selected_font_name, selected_font_path))
47
 
48
- # Emoji font application
 
 
 
 
 
49
  def apply_emoji_font(text, emoji_font):
50
  emoji_pattern = re.compile(
51
- r"([\U0001F300-\U0001F5FF"
52
- r"\U0001F600-\U0001F64F"
53
- r"\U0001F680-\U0001F6FF"
54
- r"\U0001F700-\U0001F77F"
55
- r"\U0001F780-\U0001F7FF"
56
- r"\U0001F800-\U0001F8FF"
57
- r"\U0001F900-\U0001F9FF"
58
- r"\U0001FA00-\U0001FA6F"
59
- r"\U0001FA70-\U0001FAFF"
60
- r"\u2600-\u26FF"
61
- r"\u2700-\u27BF]+)"
 
62
  )
63
  def replace_emoji(match):
64
  emoji = match.group(1)
 
 
65
  if len(emoji) > 1:
66
  emoji = emoji[0]
67
  return f'<font face="{emoji_font}">{emoji}</font>'
68
  return emoji_pattern.sub(replace_emoji, text)
69
 
70
- # Markdown to PDF content
71
- def markdown_to_pdf_content(markdown_text, plain_text_mode):
 
 
72
  lines = markdown_text.strip().split('\n')
73
  pdf_content = []
 
74
 
75
  if plain_text_mode:
76
  for line in lines:
@@ -88,6 +102,8 @@ def create_pdf_tab(default_markdown):
88
  if line.startswith('## ') or line.startswith('### '):
89
  text = line.replace('## ', '').replace('### ', '').strip()
90
  pdf_content.append(f"<b>{text}</b>")
 
 
91
  else:
92
  pdf_content.append(line.strip())
93
 
@@ -95,17 +111,20 @@ def create_pdf_tab(default_markdown):
95
  return pdf_content, total_lines
96
 
97
  # Create PDF
98
- def create_pdf(markdown_text, base_font_size, plain_text_mode, num_columns):
 
 
 
 
99
  buffer = io.BytesIO()
100
- # Double A4 page: A4 width * 2 (landscape)
101
  page_width = A4[0] * 2
102
  page_height = A4[1]
103
  doc = SimpleDocTemplate(buffer, pagesize=(page_width, page_height), leftMargin=36, rightMargin=36, topMargin=36, bottomMargin=36)
104
  styles = getSampleStyleSheet()
105
  story = []
106
  spacer_height = 10
107
- section_spacer_height = 15 # Extra spacing before numbered sections
108
- pdf_content, total_lines = markdown_to_pdf_content(markdown_text, plain_text_mode)
109
 
110
  item_font_size = base_font_size
111
  section_font_size = base_font_size * 1.1
@@ -125,12 +144,9 @@ def create_pdf_tab(default_markdown):
125
  current_line_count = 0
126
  current_column = 0
127
 
128
- # Regex to detect numbered sections (e.g., "1. ", "2. ")
129
  number_pattern = re.compile(r'^\d+\.\s')
130
-
131
  for i, item in enumerate(pdf_content):
132
- # Add extra spacing before numbered sections (but not the first one)
133
- if i > 0 and number_pattern.match(item):
134
  columns[current_column].append(Spacer(1, section_spacer_height))
135
 
136
  if current_line_count >= lines_per_column and current_column < num_columns - 1:
@@ -170,7 +186,8 @@ def create_pdf_tab(default_markdown):
170
  buffer.seek(0)
171
  return buffer.getvalue()
172
 
173
- # PDF to image
 
174
  def pdf_to_image(pdf_bytes):
175
  try:
176
  doc = fitz.open(stream=pdf_bytes, filetype="pdf")
@@ -185,9 +202,9 @@ def create_pdf_tab(default_markdown):
185
  st.error(f"Failed to render PDF preview: {e}")
186
  return None
187
 
188
- # Main logic (PDF generation and preview only)
189
  with st.spinner("Generating PDF..."):
190
- pdf_bytes = create_pdf(st.session_state.markdown_content, base_font_size, plain_text_mode, num_columns)
191
 
192
  with st.container():
193
  pdf_images = pdf_to_image(pdf_bytes)
@@ -197,7 +214,6 @@ def create_pdf_tab(default_markdown):
197
  else:
198
  st.info("Download the PDF to view it locally.")
199
 
200
- # "Download PDF" in sidebar
201
  with st.sidebar:
202
  st.download_button(label="Download PDF", data=pdf_bytes, file_name="deities_guide.pdf", mime="application/pdf")
203
 
@@ -229,7 +245,7 @@ default_markdown = """# Deities Guide: Mythology and Moral Lessons 🌟
229
 
230
  5. 🌠 Ascension and Signs
231
  - Paths: Birth, deeds, revelation, as with Jesus and Arjuna.
232
- - Signs: Miracles and prophecies, like those in the Quran and Gita.
233
  - Morals: Obedience and devotion shape destiny.
234
 
235
  6. 🎲 Storytelling and Games
 
1
  import io
2
  import re
3
  import streamlit as st
4
+ import glob
5
+ import os
6
  from PIL import Image
7
  import fitz
8
  from reportlab.lib.pagesizes import A4
 
15
  st.set_page_config(layout="wide", initial_sidebar_state="collapsed")
16
 
17
  def create_pdf_tab(default_markdown):
18
+ # Dynamically load all .ttf fonts from the current directory
19
+ # Fonts are sourced from: https://fonts.google.com/download/next-steps
20
+ # We use glob to find all .ttf files, making the font list dynamic instead of hardcoded.
21
+ font_files = glob.glob("*.ttf")
22
+ if not font_files:
23
+ st.error("No .ttf font files found in the current directory. Please add some, e.g., NotoColorEmoji-Regular.ttf.")
24
+ return
25
+ available_fonts = {os.path.splitext(os.path.basename(f))[0]: f for f in font_files}
 
26
 
27
  # Sidebar configuration
28
  with st.sidebar:
29
+ selected_font_name = st.selectbox("Select Font", options=list(available_fonts.keys()), index=0 if "NotoColorEmoji-Regular" in available_fonts else 0)
30
  selected_font_path = available_fonts[selected_font_name]
31
  base_font_size = st.slider("Font Size (points)", min_value=6, max_value=16, value=9, step=1) # Default to 9
32
  plain_text_mode = st.checkbox("Render as Plain Text (Preserve Bold Only)", value=False)
33
+ auto_bold_numbers = st.checkbox("Auto-Bold Numbered Lines", value=False)
34
  num_columns = st.selectbox("Number of Columns", options=[1, 2, 3, 4, 5, 6], index=3) # Default to 4
35
 
36
  # Markdown editor and buttons
 
44
 
45
  st.download_button(label="Save Markdown", data=st.session_state.markdown_content, file_name="deities_guide.md", mime="text/markdown")
46
 
47
+ # Register the selected font with ReportLab
48
+ # Note: Fonts must be TrueType (.ttf) for ReportLab compatibility.
49
  pdfmetrics.registerFont(TTFont(selected_font_name, selected_font_path))
50
 
51
+ # Emoji font application function
52
+ # This function handles Unicode emojis in the text, applying the selected font to them.
53
+ # We use a regex to match emoji ranges (Unicode blocks like Miscellaneous Symbols, Emoticons, etc.).
54
+ # To avoid multi-character emoji issues (e.g., base char + variation selector U+FE0F), we limit to the first character.
55
+ # Noto Color Emoji (NotoColorEmoji-Regular.ttf) is recommended as it supports a full range of modern emojis,
56
+ # including color rendering, though ReportLab renders in black-and-white by default.
57
  def apply_emoji_font(text, emoji_font):
58
  emoji_pattern = re.compile(
59
+ r"([\U0001F300-\U0001F5FF" # Miscellaneous Symbols and Pictographs
60
+ r"\U0001F600-\U0001F64F" # Emoticons
61
+ r"\U0001F680-\U0001F6FF" # Transport and Map Symbols
62
+ r"\U0001F700-\U0001F77F" # Alchemical Symbols
63
+ r"\U0001F780-\U0001F7FF" # Geometric Shapes Extended
64
+ r"\U0001F800-\U0001F8FF" # Supplemental Arrows-C
65
+ r"\U0001F900-\U0001F9FF" # Supplemental Symbols and Pictographs
66
+ r"\U0001FA00-\U0001FA6F" # Chess Symbols
67
+ r"\U0001FA70-\U0001FAFF" # Symbols and Pictographs Extended-A
68
+ r"\u2600-\u26FF" # Miscellaneous Symbols (e.g., ⚑ U+26A1)
69
+ r"\u2700-\u27BF]+" # Dingbats (e.g., ✝ U+271D)
70
+ r")"
71
  )
72
  def replace_emoji(match):
73
  emoji = match.group(1)
74
+ # Limit to first character to avoid rendering issues with multi-codepoint emojis
75
+ # Example: ✝️ (U+271D U+FE0F) becomes just ✝ (U+271D), dropping the variation selector
76
  if len(emoji) > 1:
77
  emoji = emoji[0]
78
  return f'<font face="{emoji_font}">{emoji}</font>'
79
  return emoji_pattern.sub(replace_emoji, text)
80
 
81
+ # Convert markdown to PDF content
82
+ # This function processes markdown lines, filtering out headers and applying bolding rules.
83
+ # If auto_bold_numbers is True, lines starting with "number. " (e.g., "1. ") are bolded.
84
+ def markdown_to_pdf_content(markdown_text, plain_text_mode, auto_bold_numbers):
85
  lines = markdown_text.strip().split('\n')
86
  pdf_content = []
87
+ number_pattern = re.compile(r'^\d+\.\s')
88
 
89
  if plain_text_mode:
90
  for line in lines:
 
102
  if line.startswith('## ') or line.startswith('### '):
103
  text = line.replace('## ', '').replace('### ', '').strip()
104
  pdf_content.append(f"<b>{text}</b>")
105
+ elif auto_bold_numbers and number_pattern.match(line):
106
+ pdf_content.append(f"<b>{line}</b>")
107
  else:
108
  pdf_content.append(line.strip())
109
 
 
111
  return pdf_content, total_lines
112
 
113
  # Create PDF
114
+ # This function builds a PDF using ReportLab, arranging content in columns.
115
+ # We use a double A4 landscape layout (A4 width * 2) for wide content.
116
+ # Spacers are added before numbered sections for visual separation.
117
+ # Paragraph styles define font sizes and bolding, with emojis rendered via the selected font.
118
+ def create_pdf(markdown_text, base_font_size, plain_text_mode, num_columns, auto_bold_numbers):
119
  buffer = io.BytesIO()
 
120
  page_width = A4[0] * 2
121
  page_height = A4[1]
122
  doc = SimpleDocTemplate(buffer, pagesize=(page_width, page_height), leftMargin=36, rightMargin=36, topMargin=36, bottomMargin=36)
123
  styles = getSampleStyleSheet()
124
  story = []
125
  spacer_height = 10
126
+ section_spacer_height = 15
127
+ pdf_content, total_lines = markdown_to_pdf_content(markdown_text, plain_text_mode, auto_bold_numbers)
128
 
129
  item_font_size = base_font_size
130
  section_font_size = base_font_size * 1.1
 
144
  current_line_count = 0
145
  current_column = 0
146
 
 
147
  number_pattern = re.compile(r'^\d+\.\s')
 
148
  for i, item in enumerate(pdf_content):
149
+ if i > 0 and number_pattern.match(item.replace('<b>', '').replace('</b>', '')):
 
150
  columns[current_column].append(Spacer(1, section_spacer_height))
151
 
152
  if current_line_count >= lines_per_column and current_column < num_columns - 1:
 
186
  buffer.seek(0)
187
  return buffer.getvalue()
188
 
189
+ # Convert PDF to image for preview
190
+ # Uses PyMuPDF (fitz) to render PDF pages as images for Streamlit display.
191
  def pdf_to_image(pdf_bytes):
192
  try:
193
  doc = fitz.open(stream=pdf_bytes, filetype="pdf")
 
202
  st.error(f"Failed to render PDF preview: {e}")
203
  return None
204
 
205
+ # Main logic
206
  with st.spinner("Generating PDF..."):
207
+ pdf_bytes = create_pdf(st.session_state.markdown_content, base_font_size, plain_text_mode, num_columns, auto_bold_numbers)
208
 
209
  with st.container():
210
  pdf_images = pdf_to_image(pdf_bytes)
 
214
  else:
215
  st.info("Download the PDF to view it locally.")
216
 
 
217
  with st.sidebar:
218
  st.download_button(label="Download PDF", data=pdf_bytes, file_name="deities_guide.pdf", mime="application/pdf")
219
 
 
245
 
246
  5. 🌠 Ascension and Signs
247
  - Paths: Birth, deeds, revelation, as with Jesus and Arjuna.
248
+ - Signs: Miracles and prophecies, like those in the Quran and Gita Kobe.
249
  - Morals: Obedience and devotion shape destiny.
250
 
251
  6. 🎲 Storytelling and Games