Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
@@ -75,16 +75,34 @@ def register_local_fonts():
|
|
75 |
return sorted(text_font_names), emoji_font_name
|
76 |
|
77 |
def apply_emoji_font(text: str, emoji_font_name: str) -> str:
|
78 |
-
"""
|
|
|
|
|
|
|
79 |
if not emoji_font_name:
|
80 |
return text
|
81 |
|
|
|
82 |
emoji_pattern = re.compile(f"([{re.escape(''.join(map(chr, range(0x1f600, 0x1f650))))}"
|
83 |
f"{re.escape(''.join(map(chr, range(0x1f300, 0x1f5ff))))}"
|
84 |
f"{re.escape(''.join(map(chr, range(0x1f900, 0x1f9ff))))}"
|
85 |
f"{re.escape(''.join(map(chr, range(0x2600, 0x26ff))))}"
|
86 |
f"{re.escape(''.join(map(chr, range(0x2700, 0x27bf))))}]+)")
|
87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
|
89 |
|
90 |
# --- PDF Generation & Handling ---
|
@@ -92,7 +110,6 @@ def apply_emoji_font(text: str, emoji_font_name: str) -> str:
|
|
92 |
def markdown_to_story(markdown_text: str, font_name: str, emoji_font: str):
|
93 |
"""Converts markdown to a ReportLab story, with enhanced styling and page breaks."""
|
94 |
styles = getSampleStyleSheet()
|
95 |
-
# Define styles for various markdown elements
|
96 |
style_normal = ParagraphStyle('BodyText', fontName=font_name, spaceAfter=6, leading=14, fontSize=10)
|
97 |
style_h1 = ParagraphStyle('h1', parent=styles['h1'], fontName=font_name, spaceBefore=12, fontSize=24, leading=28, textColor=colors.darkblue)
|
98 |
style_h2 = ParagraphStyle('h2', parent=styles['h2'], fontName=font_name, fontSize=18, leading=22, spaceBefore=10)
|
@@ -135,18 +152,21 @@ def markdown_to_story(markdown_text: str, font_name: str, emoji_font: str):
|
|
135 |
story.append(table); story.append(Spacer(1, 0.2 * inch))
|
136 |
table_data = []
|
137 |
|
138 |
-
|
139 |
-
formatted_line = re.sub(r'_(.*?)_', r'<i>\1</i>', re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>',
|
140 |
-
|
|
|
|
|
141 |
if line.startswith("# "):
|
142 |
if not first_heading: story.append(PageBreak())
|
143 |
-
|
144 |
-
|
145 |
-
elif line.startswith("
|
146 |
-
elif line.
|
147 |
-
elif
|
|
|
148 |
elif line.strip() == "": story.append(Spacer(1, 0.1 * inch))
|
149 |
-
else: story.append(Paragraph(
|
150 |
|
151 |
return story
|
152 |
|
@@ -222,8 +242,34 @@ def generate_pdfs_api(files, layouts, fonts, num_columns, page_w_mult, page_h_mu
|
|
222 |
|
223 |
# --- Gradio UI Definition ---
|
224 |
AVAILABLE_FONTS, EMOJI_FONT_NAME = register_local_fonts()
|
225 |
-
SAMPLE_MARKDOWN = "#
|
226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
227 |
|
228 |
with gr.Blocks(theme=gr.themes.Soft(), title="Advanced PDF Generator") as demo:
|
229 |
gr.Markdown("# 📄 Advanced PDF Layout Engine")
|
|
|
75 |
return sorted(text_font_names), emoji_font_name
|
76 |
|
77 |
def apply_emoji_font(text: str, emoji_font_name: str) -> str:
|
78 |
+
"""
|
79 |
+
Intelligently wraps emoji characters in a <font> tag, preserving existing HTML-like tags.
|
80 |
+
This prevents invalid nested tags like <b><font>...</font></b> which ReportLab handles poorly.
|
81 |
+
"""
|
82 |
if not emoji_font_name:
|
83 |
return text
|
84 |
|
85 |
+
# Regex to find emojis
|
86 |
emoji_pattern = re.compile(f"([{re.escape(''.join(map(chr, range(0x1f600, 0x1f650))))}"
|
87 |
f"{re.escape(''.join(map(chr, range(0x1f300, 0x1f5ff))))}"
|
88 |
f"{re.escape(''.join(map(chr, range(0x1f900, 0x1f9ff))))}"
|
89 |
f"{re.escape(''.join(map(chr, range(0x2600, 0x26ff))))}"
|
90 |
f"{re.escape(''.join(map(chr, range(0x2700, 0x27bf))))}]+)")
|
91 |
+
|
92 |
+
# Regex to split the string by existing tags (<b>, <i>)
|
93 |
+
tag_pattern = re.compile(r"(<[^>]+>)")
|
94 |
+
parts = tag_pattern.split(text)
|
95 |
+
|
96 |
+
result = []
|
97 |
+
for part in parts:
|
98 |
+
if tag_pattern.match(part):
|
99 |
+
# It's a tag, append it as is
|
100 |
+
result.append(part)
|
101 |
+
else:
|
102 |
+
# It's text, apply emoji font to any emojis within this segment
|
103 |
+
result.append(emoji_pattern.sub(fr'<font name="{emoji_font_name}">\1</font>', part))
|
104 |
+
|
105 |
+
return "".join(result)
|
106 |
|
107 |
|
108 |
# --- PDF Generation & Handling ---
|
|
|
110 |
def markdown_to_story(markdown_text: str, font_name: str, emoji_font: str):
|
111 |
"""Converts markdown to a ReportLab story, with enhanced styling and page breaks."""
|
112 |
styles = getSampleStyleSheet()
|
|
|
113 |
style_normal = ParagraphStyle('BodyText', fontName=font_name, spaceAfter=6, leading=14, fontSize=10)
|
114 |
style_h1 = ParagraphStyle('h1', parent=styles['h1'], fontName=font_name, spaceBefore=12, fontSize=24, leading=28, textColor=colors.darkblue)
|
115 |
style_h2 = ParagraphStyle('h2', parent=styles['h2'], fontName=font_name, fontSize=18, leading=22, spaceBefore=10)
|
|
|
152 |
story.append(table); story.append(Spacer(1, 0.2 * inch))
|
153 |
table_data = []
|
154 |
|
155 |
+
# Apply base formatting first
|
156 |
+
formatted_line = re.sub(r'_(.*?)_', r'<i>\1</i>', re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', line))
|
157 |
+
# Then apply emoji font to the already formatted line
|
158 |
+
line_with_emoji = apply_emoji_font(formatted_line, emoji_font)
|
159 |
+
|
160 |
if line.startswith("# "):
|
161 |
if not first_heading: story.append(PageBreak())
|
162 |
+
# Strip markdown characters from the original line before applying style
|
163 |
+
story.append(Paragraph(apply_emoji_font(re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', line[2:]), emoji_font), style_h1)); first_heading = False
|
164 |
+
elif line.startswith("## "): story.append(Paragraph(apply_emoji_font(re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', line[3:]), emoji_font), style_h2))
|
165 |
+
elif line.startswith("### "): story.append(Paragraph(apply_emoji_font(re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', line[4:]), emoji_font), style_h3))
|
166 |
+
elif line.strip().startswith(("- ", "* ")): story.append(Paragraph(line_with_emoji.strip()[2:], style_normal, bulletText='•'))
|
167 |
+
elif re.match(r'^\d+\.\s', line.strip()): story.append(Paragraph(line_with_emoji.strip(), style_normal))
|
168 |
elif line.strip() == "": story.append(Spacer(1, 0.1 * inch))
|
169 |
+
else: story.append(Paragraph(line_with_emoji, style_normal))
|
170 |
|
171 |
return story
|
172 |
|
|
|
242 |
|
243 |
# --- Gradio UI Definition ---
|
244 |
AVAILABLE_FONTS, EMOJI_FONT_NAME = register_local_fonts()
|
245 |
+
SAMPLE_MARKDOWN = """# Deities Guide: Mythology and Moral Lessons
|
246 |
+
|
247 |
+
1. 📜 **Introduction**
|
248 |
+
- **Purpose**: Explore deities, spirits, saints, and beings with their epic stories and morals!
|
249 |
+
- **Usage**: A guide for learning and storytelling across traditions. ️
|
250 |
+
- **Themes**: Justice ️, faith , hubris , redemption , cosmic order .
|
251 |
+
|
252 |
+
2. 🛠️ **Core Concepts of Divinity**
|
253 |
+
- **Powers**: Creation , omniscience ️️, shapeshifting across entities.
|
254 |
+
- **Life Cycle**: Mortality , immortality , transitions like saints and avatars .
|
255 |
+
- **Communication**: Omens ️, visions ️, miracles from gods and spirits.
|
256 |
+
|
257 |
+
# ⚔️ Arthurian Legends
|
258 |
+
- **Merlin, Morgan le Fay, Arthur**: Mentor , rival ️, son .
|
259 |
+
- **Relation**: Family tests loyalty .
|
260 |
+
- **Lesson**: Honor ️ vs. betrayal ️.
|
261 |
+
|
262 |
+
# 🏛️ Greek Mythology
|
263 |
+
- **Zeus, Hera, Athena**: Father , mother , daughter .
|
264 |
+
- **Relation**: Family rules with tension ️.
|
265 |
+
- **Lesson**: Hubris meets wisdom .
|
266 |
+
|
267 |
+
# 🕉️ Hindu Trimurti
|
268 |
+
- **Brahma, Vishnu, Shiva**: Creator , preserver ️, destroyer .
|
269 |
+
- **Relation**: Divine trio cycles existence .
|
270 |
+
- **Lesson**: Balance ️ sustains life .
|
271 |
+
"""
|
272 |
+
with open(CWD / "sample.md", "w", encoding="utf-8") as f: f.write(SAMPLE_MARKDOWN)
|
273 |
|
274 |
with gr.Blocks(theme=gr.themes.Soft(), title="Advanced PDF Generator") as demo:
|
275 |
gr.Markdown("# 📄 Advanced PDF Layout Engine")
|