Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -17,10 +17,9 @@ st.set_page_config(layout="wide", initial_sidebar_state="collapsed")
|
|
| 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 |
|
|
@@ -28,12 +27,11 @@ def create_pdf_tab(default_markdown):
|
|
| 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)
|
| 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)
|
| 35 |
|
| 36 |
-
# Markdown editor and buttons
|
| 37 |
if 'markdown_content' not in st.session_state:
|
| 38 |
st.session_state.markdown_content = default_markdown
|
| 39 |
|
|
@@ -44,16 +42,18 @@ def create_pdf_tab(default_markdown):
|
|
| 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
|
| 48 |
-
#
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
# Emoji font application
|
| 52 |
-
#
|
| 53 |
-
#
|
| 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
|
|
@@ -71,16 +71,13 @@ def create_pdf_tab(default_markdown):
|
|
| 71 |
)
|
| 72 |
def replace_emoji(match):
|
| 73 |
emoji = match.group(1)
|
| 74 |
-
# Limit to first character to avoid
|
| 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 |
-
#
|
| 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 = []
|
|
@@ -111,10 +108,6 @@ def create_pdf_tab(default_markdown):
|
|
| 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
|
|
@@ -129,6 +122,7 @@ def create_pdf_tab(default_markdown):
|
|
| 129 |
item_font_size = base_font_size
|
| 130 |
section_font_size = base_font_size * 1.1
|
| 131 |
|
|
|
|
| 132 |
section_style = ParagraphStyle(
|
| 133 |
'SectionStyle', parent=styles['Heading2'], fontName="Helvetica-Bold",
|
| 134 |
textColor=colors.darkblue, fontSize=section_font_size, leading=section_font_size * 1.2, spaceAfter=2
|
|
@@ -186,8 +180,7 @@ def create_pdf_tab(default_markdown):
|
|
| 186 |
buffer.seek(0)
|
| 187 |
return buffer.getvalue()
|
| 188 |
|
| 189 |
-
#
|
| 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")
|
|
@@ -245,7 +238,7 @@ default_markdown = """# Deities Guide: Mythology and Moral Lessons 🌟
|
|
| 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
|
| 249 |
- Morals: Obedience and devotion shape destiny.
|
| 250 |
|
| 251 |
6. 🎲 Storytelling and Games
|
|
|
|
| 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 |
font_files = glob.glob("*.ttf")
|
| 21 |
if not font_files:
|
| 22 |
+
st.error("No .ttf font files found in the current directory. Please add some, e.g., NotoColorEmoji-Regular.ttf or NotoEmoji-Regular.ttf.")
|
| 23 |
return
|
| 24 |
available_fonts = {os.path.splitext(os.path.basename(f))[0]: f for f in font_files}
|
| 25 |
|
|
|
|
| 27 |
with st.sidebar:
|
| 28 |
selected_font_name = st.selectbox("Select Font", options=list(available_fonts.keys()), index=0 if "NotoColorEmoji-Regular" in available_fonts else 0)
|
| 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)
|
| 31 |
plain_text_mode = st.checkbox("Render as Plain Text (Preserve Bold Only)", value=False)
|
| 32 |
auto_bold_numbers = st.checkbox("Auto-Bold Numbered Lines", value=False)
|
| 33 |
+
num_columns = st.selectbox("Number of Columns", options=[1, 2, 3, 4, 5, 6], index=3)
|
| 34 |
|
|
|
|
| 35 |
if 'markdown_content' not in st.session_state:
|
| 36 |
st.session_state.markdown_content = default_markdown
|
| 37 |
|
|
|
|
| 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 the selected font
|
| 46 |
+
# Noto Color Emoji is a color font, which ReportLab may not render correctly (monochrome only).
|
| 47 |
+
# If emojis don’t show, try a monochrome font like NotoEmoji-Regular.ttf as a fallback.
|
| 48 |
+
try:
|
| 49 |
+
pdfmetrics.registerFont(TTFont(selected_font_name, selected_font_path))
|
| 50 |
+
except Exception as e:
|
| 51 |
+
st.error(f"Failed to register font {selected_font_name}: {e}")
|
| 52 |
+
return
|
| 53 |
|
| 54 |
+
# Emoji font application
|
| 55 |
+
# We apply the selected font to emojis, falling back to plain text if the font doesn’t support them.
|
| 56 |
+
# ReportLab needs explicit font tagging for emojis; Noto Color Emoji may need special handling.
|
|
|
|
|
|
|
|
|
|
| 57 |
def apply_emoji_font(text, emoji_font):
|
| 58 |
emoji_pattern = re.compile(
|
| 59 |
r"([\U0001F300-\U0001F5FF" # Miscellaneous Symbols and Pictographs
|
|
|
|
| 71 |
)
|
| 72 |
def replace_emoji(match):
|
| 73 |
emoji = match.group(1)
|
| 74 |
+
if len(emoji) > 1: # Limit to first character to avoid multi-codepoint issues
|
|
|
|
|
|
|
| 75 |
emoji = emoji[0]
|
| 76 |
+
# Wrap emoji in font tag; if it doesn’t render, it’ll show as text
|
| 77 |
return f'<font face="{emoji_font}">{emoji}</font>'
|
| 78 |
return emoji_pattern.sub(replace_emoji, text)
|
| 79 |
|
| 80 |
+
# Markdown to PDF content
|
|
|
|
|
|
|
| 81 |
def markdown_to_pdf_content(markdown_text, plain_text_mode, auto_bold_numbers):
|
| 82 |
lines = markdown_text.strip().split('\n')
|
| 83 |
pdf_content = []
|
|
|
|
| 108 |
return pdf_content, total_lines
|
| 109 |
|
| 110 |
# Create PDF
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
def create_pdf(markdown_text, base_font_size, plain_text_mode, num_columns, auto_bold_numbers):
|
| 112 |
buffer = io.BytesIO()
|
| 113 |
page_width = A4[0] * 2
|
|
|
|
| 122 |
item_font_size = base_font_size
|
| 123 |
section_font_size = base_font_size * 1.1
|
| 124 |
|
| 125 |
+
# Use Helvetica as a fallback for text, emoji font for emojis
|
| 126 |
section_style = ParagraphStyle(
|
| 127 |
'SectionStyle', parent=styles['Heading2'], fontName="Helvetica-Bold",
|
| 128 |
textColor=colors.darkblue, fontSize=section_font_size, leading=section_font_size * 1.2, spaceAfter=2
|
|
|
|
| 180 |
buffer.seek(0)
|
| 181 |
return buffer.getvalue()
|
| 182 |
|
| 183 |
+
# PDF to image
|
|
|
|
| 184 |
def pdf_to_image(pdf_bytes):
|
| 185 |
try:
|
| 186 |
doc = fitz.open(stream=pdf_bytes, filetype="pdf")
|
|
|
|
| 238 |
|
| 239 |
5. 🌠 Ascension and Signs
|
| 240 |
- Paths: Birth, deeds, revelation, as with Jesus and Arjuna.
|
| 241 |
+
- Signs: Miracles and prophecies, like those in the Quran and Gita.
|
| 242 |
- Morals: Obedience and devotion shape destiny.
|
| 243 |
|
| 244 |
6. 🎲 Storytelling and Games
|