Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- app.py +47 -0
- generate_svg.py +137 -0
- requirements.txt +2 -0
app.py
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from generate_svg import main
|
3 |
+
|
4 |
+
theme = gr.themes.Soft(
|
5 |
+
primary_hue="purple",
|
6 |
+
secondary_hue="cyan",
|
7 |
+
neutral_hue="slate",
|
8 |
+
font=[
|
9 |
+
gr.themes.GoogleFont("Syne"),
|
10 |
+
gr.themes.GoogleFont("Poppins"),
|
11 |
+
gr.themes.GoogleFont("Poppins"),
|
12 |
+
gr.themes.GoogleFont("Poppins")
|
13 |
+
],
|
14 |
+
)
|
15 |
+
|
16 |
+
with gr.Blocks(theme=theme, title="MarkDown SVG Generator", fill_height=True) as app:
|
17 |
+
gr.HTML(
|
18 |
+
value ="""
|
19 |
+
<h1 style="text-align: center;">MarkDown SVG Generator<p style="text-align: center;">Designed and Developed by <a href="https://raannakasturi.eu.org" target="_blank" rel="nofollow noreferrer external">Nayan Kasturi</a></p> </h1>
|
20 |
+
<p style="text-align: center;">MarkDown SVG Generator for ReXplore</p>
|
21 |
+
""")
|
22 |
+
with gr.Row():
|
23 |
+
with gr.Column():
|
24 |
+
markdown = gr.Textbox(label="Markdown Text", placeholder="Enter the markdown text", lines=7)
|
25 |
+
get_file = gr.Button(value="SVG File", variant="primary")
|
26 |
+
svg_viewer = gr.Textbox(label="SVG Viewer", placeholder="SVG Viewer", lines=7, interactive=False, show_copy_button=True)
|
27 |
+
get_file.click(
|
28 |
+
main,
|
29 |
+
inputs=[markdown],
|
30 |
+
outputs=[svg_viewer],
|
31 |
+
concurrency_limit=25,
|
32 |
+
scroll_to_output=True,
|
33 |
+
show_api=True,
|
34 |
+
api_name="markdown_svg_generator",
|
35 |
+
show_progress="full",
|
36 |
+
)
|
37 |
+
|
38 |
+
import subprocess
|
39 |
+
try:
|
40 |
+
subprocess.run(["apt", "update"], check=True)
|
41 |
+
subprocess.run(["apt" "install" "graphviz"], check=True)
|
42 |
+
app.queue(default_concurrency_limit=25).launch(show_api=True, show_error=True, debug=True)
|
43 |
+
except subprocess.CalledProcessError:
|
44 |
+
print("Graphviz is not installed. Please install it using `apt install graphviz` and try again.")
|
45 |
+
except Exception as e:
|
46 |
+
print(f"An error occurred: {e}")
|
47 |
+
raise e
|
generate_svg.py
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from graphviz import Digraph
|
3 |
+
# from cairosvg import svg2pdf
|
4 |
+
import re
|
5 |
+
import random
|
6 |
+
|
7 |
+
def parse_markdown_to_dict(md_text):
|
8 |
+
lines = md_text.strip().splitlines()
|
9 |
+
mindmap = {}
|
10 |
+
stack = []
|
11 |
+
for line in lines:
|
12 |
+
heading_match = re.match(r'^(#{1,6})\s+(.*)', line)
|
13 |
+
bullet_match = re.match(r'^\s*-\s+(.*)', line)
|
14 |
+
if heading_match:
|
15 |
+
level = len(heading_match.group(1))
|
16 |
+
title = heading_match.group(2).strip()
|
17 |
+
node = {'title': title, 'children': []}
|
18 |
+
while len(stack) >= level:
|
19 |
+
stack.pop()
|
20 |
+
if stack:
|
21 |
+
stack[-1]['children'].append(node)
|
22 |
+
else:
|
23 |
+
mindmap = node
|
24 |
+
stack.append(node)
|
25 |
+
elif bullet_match and stack:
|
26 |
+
stack[-1]['children'].append({'title': bullet_match.group(1), 'children': []})
|
27 |
+
return mindmap
|
28 |
+
|
29 |
+
generated_colors = set()
|
30 |
+
|
31 |
+
def generate_random_color():
|
32 |
+
"""Generate a random color that hasn't been generated before."""
|
33 |
+
while True:
|
34 |
+
# Generate a random color in hex format
|
35 |
+
color = "#{:02x}{:02x}{:02x}".format(random.randint(128, 255), random.randint(128, 255), random.randint(128, 255))
|
36 |
+
# If the color is not in the set, it's unique
|
37 |
+
if color not in generated_colors:
|
38 |
+
generated_colors.add(color) # Add the color to the set of generated colors
|
39 |
+
return color # Return the unique color
|
40 |
+
else:
|
41 |
+
continue # Try again
|
42 |
+
|
43 |
+
def brighten_color(color, factor=0.15):
|
44 |
+
"""Brighten the color by a certain factor (default 10%)"""
|
45 |
+
# Remove the '#' symbol
|
46 |
+
color = color.lstrip('#')
|
47 |
+
|
48 |
+
# Convert hex to RGB
|
49 |
+
r, g, b = [int(color[i:i+2], 16) for i in (0, 2, 4)]
|
50 |
+
|
51 |
+
# Increase each component by the factor, but clamp to 255
|
52 |
+
r = min(255, int(r * (1 + factor)))
|
53 |
+
g = min(255, int(g * (1 + factor)))
|
54 |
+
b = min(255, int(b * (1 + factor)))
|
55 |
+
|
56 |
+
# Convert back to hex
|
57 |
+
return "#{:02x}{:02x}{:02x}".format(r, g, b)
|
58 |
+
|
59 |
+
def add_nodes_to_graph(graph, node, parent_id=None, font_size=9, parent_color=None):
|
60 |
+
node_id = str(id(node))
|
61 |
+
title = node['title']
|
62 |
+
if parent_color is None:
|
63 |
+
node_color = "#ADD8E6" # Light Blue for the main heading
|
64 |
+
border_color = "#000000" # Dark Blue border for the main heading
|
65 |
+
parent_color = "#ADD8E6"
|
66 |
+
elif parent_color == "#ADD8E6":
|
67 |
+
node_color = generate_random_color()
|
68 |
+
border_color = "#808080"
|
69 |
+
parent_color = node_color
|
70 |
+
else:
|
71 |
+
# Child node and its descendants with the same random color
|
72 |
+
node_color = brighten_color(parent_color, factor=0.15)
|
73 |
+
border_color = "#808080"
|
74 |
+
# Check for markdown links
|
75 |
+
url_match = re.search(r'\[(.*?)\]\((.*?)\)', title)
|
76 |
+
if url_match:
|
77 |
+
prefix_text = title[:url_match.start()].strip()
|
78 |
+
display_text = url_match.group(1)
|
79 |
+
url = url_match.group(2)
|
80 |
+
|
81 |
+
label = f'{prefix_text} {display_text}'
|
82 |
+
graph.node(node_id, label=label, shape="box", style="rounded,filled", color=border_color, fontcolor="black", fillcolor=node_color, href=url, tooltip=title, fontsize=str(font_size))
|
83 |
+
else:
|
84 |
+
graph.node(node_id, title, shape="box", style="rounded,filled", color=border_color, fontcolor="black", fillcolor=node_color, tooltip=title, fontsize=str(font_size))
|
85 |
+
|
86 |
+
if parent_id:
|
87 |
+
graph.edge(parent_id, node_id)
|
88 |
+
|
89 |
+
# Recurse to children, passing down color for the child and its descendants
|
90 |
+
for child in node.get('children', []):
|
91 |
+
# Assign a random color to each child node (no inheritance from parent)
|
92 |
+
add_nodes_to_graph(graph, child, node_id, font_size=max(8, font_size - 1), parent_color=parent_color)
|
93 |
+
|
94 |
+
# def generate_mindmap_pdf(svg_file):
|
95 |
+
# pdf_file = svg_file.replace(".svg", ".pdf")
|
96 |
+
# svg2pdf(file_obj=open(svg_file, "rb"), write_to=pdf_file)
|
97 |
+
# return pdf_file
|
98 |
+
|
99 |
+
def generate_mindmap_svg(md_text):
|
100 |
+
mindmap_dict = parse_markdown_to_dict(md_text)
|
101 |
+
root_title = mindmap_dict.get('title', 'Mindmap')
|
102 |
+
sanitized_title = re.sub(r'[^a-zA-Z0-9_\-]', '', root_title.replace(" ", ""))
|
103 |
+
output_filename = f"{sanitized_title}_mindmap.svg"
|
104 |
+
graph = Digraph(format='svg')
|
105 |
+
graph.attr(rankdir='LR', size='10,10!', pad="0.5", margin="0.2", ratio="auto")
|
106 |
+
graph.attr('node', fontname="Arial", fontsize="9")
|
107 |
+
add_nodes_to_graph(graph, mindmap_dict)
|
108 |
+
svg_content = graph.pipe(format='svg').decode('utf-8')
|
109 |
+
svg_content = svg_content.replace("%3", root_title)
|
110 |
+
# Save the modified SVG content to a file
|
111 |
+
with open(output_filename, 'w') as f:
|
112 |
+
f.write(svg_content)
|
113 |
+
return output_filename
|
114 |
+
|
115 |
+
def generate_mindmap(md_text):
|
116 |
+
mindmap_svg = generate_mindmap_svg(md_text)
|
117 |
+
# mindmap_pdf = generate_mindmap_pdf(mindmap_svg)
|
118 |
+
return mindmap_svg
|
119 |
+
|
120 |
+
def upload_svg(mindmap_svg):
|
121 |
+
from huggingface_hub import HfApi
|
122 |
+
api = HfApi()
|
123 |
+
api.upload_file(
|
124 |
+
path_or_fileobj=mindmap_svg,
|
125 |
+
path_in_repo=f"SVG/{mindmap_svg}",
|
126 |
+
repo_id="raannakasturi/ReXploreData",
|
127 |
+
repo_type="dataset",
|
128 |
+
)
|
129 |
+
if os.path.exists(mindmap_svg):
|
130 |
+
os.remove(mindmap_svg)
|
131 |
+
return f"https://huggingface.co/datasets/raannakasturi/ReXploreData/raw/main/SVG/{mindmap_svg}"
|
132 |
+
|
133 |
+
def main(markdown_text):
|
134 |
+
mindmap_svg = generate_mindmap_svg(markdown_text.replace("**", ""))
|
135 |
+
url = upload_svg(mindmap_svg)
|
136 |
+
print(f"Uploaded SVG to {url}")
|
137 |
+
return url
|
requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
graphviz
|
2 |
+
cairosvg
|