ChevalierJoseph commited on
Commit
a87c076
·
verified ·
1 Parent(s): dd58079

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -29
app.py CHANGED
@@ -1,18 +1,19 @@
1
- import re
2
- import os
3
- import tempfile
4
- import glob
5
- from fontTools.pens.svgPathPen import SVGPathPen
6
- from fontTools.svgLib.path import parsePath
7
- from defcon import Font, Glyph
8
- from fontmake.font_project import FontProject
9
  import spaces
10
  from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
11
  import gradio as gr
12
  import torch
13
  from threading import Thread
14
- import zipfile
15
  import io
 
 
 
 
 
 
 
 
 
16
 
17
  # ------------------------
18
  # MODELE
@@ -76,59 +77,73 @@ def create_zip(svg_files):
76
  # ------------------------
77
  # UFO / FONTMAKE
78
  # ------------------------
79
- def parse_svg_path(path_data, glyph):
80
- """ Parse the SVG path and draw it into the glyph using the correct pen methods."""
81
  pen = glyph.getPen()
82
- path = parsePath(path_data)
83
-
84
- for segment in path:
85
- if segment.length() > 0:
86
- if segment.command == "M":
87
- pen.moveTo((segment.endPoint.real, segment.endPoint.imag))
88
- elif segment.command == "L":
89
- pen.lineTo((segment.endPoint.real, segment.endPoint.imag))
90
- elif segment.command == "C":
 
 
91
  pen.curveTo(
92
- ((segment.ctrl1.real, segment.ctrl1.imag),
93
- (segment.ctrl2.real, segment.ctrl2.imag),
94
- (segment.endPoint.real, segment.endPoint.imag))
95
  )
96
- elif segment.command == "Z":
97
  pen.closePath()
 
 
 
 
 
 
 
 
 
 
98
 
99
  def build_ufo_from_glyphs(glyphs):
100
  font = Font()
101
  font.info.familyName = "TipTopType"
102
  font.info.styleName = "Regular"
 
103
  for letter, path_data in glyphs:
104
  glyph = Glyph()
105
  glyph.name = letter
106
  glyph.unicode = ord(letter)
107
- try:
108
- parse_svg_path(path_data.strip(), glyph)
109
- glyph.width = 600 # largeur du glyphe
110
- except Exception as e:
111
- print(f"Erreur injection SVG pour {letter}: {e}")
112
  font.insertGlyph(glyph)
 
113
  return font
114
 
115
  def save_otf_font(glyphs, font_name="TipTopType-Regular.otf"):
116
  if not glyphs:
117
  return None
 
118
  with tempfile.TemporaryDirectory() as tmpdir:
119
  ufo_path = os.path.join(tmpdir, "font.ufo")
120
  font = build_ufo_from_glyphs(glyphs)
121
  font.save(ufo_path)
 
122
  output_dir = os.path.join(tmpdir, "out")
123
  os.makedirs(output_dir, exist_ok=True)
 
124
  project = FontProject()
125
  project.run_from_ufos([ufo_path], output=["otf"], output_dir=output_dir)
 
126
  otf_files = glob.glob(os.path.join(output_dir, "**/*.otf"), recursive=True)
127
  if not otf_files:
128
  raise FileNotFoundError("Aucun fichier OTF généré par fontmake.")
 
129
  generated_path = otf_files[0]
130
  final_path = os.path.join(tempfile.gettempdir(), font_name)
131
  os.replace(generated_path, final_path)
 
132
  return final_path
133
 
134
  # ------------------------
@@ -140,15 +155,19 @@ def respond(message: str, system_message: str, max_tokens: int, temperature: flo
140
  if torch.cuda.is_available():
141
  model = model.to('cuda')
142
  model_device = next(model.parameters()).device
 
143
  messages = [{"role": "system", "content": system_message}]
144
  messages.append({"role": "user", "content": message})
 
145
  inputs = tokenizer.apply_chat_template(
146
  messages,
147
  tokenize=True,
148
  add_generation_prompt=True,
149
  return_tensors="pt",
150
  ).to(model_device)
 
151
  streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
 
152
  generation_kwargs = {
153
  "input_ids": inputs,
154
  "streamer": streamer,
@@ -162,8 +181,10 @@ def respond(message: str, system_message: str, max_tokens: int, temperature: flo
162
  generation_kwargs["do_sample"] = False
163
  generation_kwargs.pop("temperature", None)
164
  generation_kwargs.pop("top_p", None)
 
165
  thread = Thread(target=model.generate, kwargs=generation_kwargs)
166
  thread.start()
 
167
  partial_response = ""
168
  for new_text in streamer:
169
  partial_response += new_text
@@ -179,6 +200,7 @@ def create_demo():
179
  gr.Markdown("# TypTopType")
180
  glyphs_state = gr.State([])
181
  message_history = gr.State([])
 
182
  with gr.Row():
183
  with gr.Column(scale=1):
184
  msg = gr.Textbox(label="input box, type here")
@@ -192,15 +214,19 @@ def create_demo():
192
  cols = gr.Slider(minimum=1, maximum=10, value=5, step=1, visible=False)
193
  width = gr.Slider(minimum=50, maximum=200, value=100, step=10, visible=False)
194
  height = gr.Slider(minimum=50, maximum=200, value=100, step=10, visible=False)
 
195
  download_btn = gr.Button("Download svg file")
196
  download_otf_btn = gr.Button("Download OTF font")
 
197
  with gr.Column(scale=3):
198
  gr.Markdown("## preview")
199
  svg_preview = gr.HTML(label="SVG Preview")
200
  download_output = gr.File(label="Download ZIP")
201
  download_otf_output = gr.File(label="Download OTF")
 
202
  def user(user_message, history):
203
  return "", history + [[user_message, None]]
 
204
  def bot(history, system_message, max_tokens, temperature, top_p, cols, width, height):
205
  message = history[-1][0]
206
  response_generator = respond(message, system_message, max_tokens, temperature, top_p)
@@ -211,31 +237,38 @@ def create_demo():
211
  glyphs_list = glyphs
212
  svg_html = generate_glyphs_html(glyphs_list, cols=cols, width=width, height=height) if glyphs_list else "No glyphs found."
213
  yield svg_html, glyphs_list
 
214
  def download_svg(glyphs, width, height):
215
  if not glyphs:
216
  return None
217
  svg_files = generate_svg_files(glyphs, width=width, height=height)
218
  zip_path = create_zip(svg_files)
219
  return zip_path
 
220
  def download_otf(glyphs):
221
  if not glyphs:
222
  return None
223
  return save_otf_font(glyphs)
 
224
  msg.submit(user, [msg, message_history], [msg, message_history], queue=False).then(
225
  bot, [message_history, system_message, max_tokens, temperature, top_p, cols, width, height], [svg_preview, glyphs_state]
226
  )
 
227
  download_btn.click(
228
  download_svg,
229
  inputs=[glyphs_state, width, height],
230
  outputs=download_output
231
  )
 
232
  download_otf_btn.click(
233
  download_otf,
234
  inputs=[glyphs_state],
235
  outputs=download_otf_output
236
  )
 
237
  return demo
238
 
239
  demo = create_demo()
 
240
  if __name__ == "__main__":
241
  demo.launch()
 
 
 
 
 
 
 
 
 
1
  import spaces
2
  from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
3
  import gradio as gr
4
  import torch
5
  from threading import Thread
6
+ import re
7
  import io
8
+ import zipfile
9
+ import tempfile
10
+ import os
11
+ import glob
12
+
13
+ # Fontmake / UFO
14
+ from fontmake.font_project import FontProject
15
+ from defcon import Font, Glyph
16
+ from svgpathtools import parse_path, Line, CubicBezier, QuadraticBezier
17
 
18
  # ------------------------
19
  # MODELE
 
77
  # ------------------------
78
  # UFO / FONTMAKE
79
  # ------------------------
80
+ def add_svg_to_glyph(glyph: Glyph, path_d: str, width=600):
 
81
  pen = glyph.getPen()
82
+ try:
83
+ svg_path = parse_path(path_d)
84
+ for segment in svg_path:
85
+ start = segment.start
86
+ end = segment.end
87
+ if isinstance(segment, Line):
88
+ pen.moveTo((start.real, start.imag))
89
+ pen.lineTo((end.real, end.imag))
90
+ pen.closePath()
91
+ elif isinstance(segment, CubicBezier):
92
+ pen.moveTo((start.real, start.imag))
93
  pen.curveTo(
94
+ (segment.control1.real, segment.control1.imag),
95
+ (segment.control2.real, segment.control2.imag),
96
+ (end.real, end.imag)
97
  )
 
98
  pen.closePath()
99
+ elif isinstance(segment, QuadraticBezier):
100
+ pen.moveTo((start.real, start.imag))
101
+ pen.qCurveTo(
102
+ (segment.control.real, segment.control.imag),
103
+ (end.real, end.imag)
104
+ )
105
+ pen.closePath()
106
+ except Exception as e:
107
+ print(f"Erreur parsing SVG: {e}")
108
+ glyph.width = width
109
 
110
  def build_ufo_from_glyphs(glyphs):
111
  font = Font()
112
  font.info.familyName = "TipTopType"
113
  font.info.styleName = "Regular"
114
+
115
  for letter, path_data in glyphs:
116
  glyph = Glyph()
117
  glyph.name = letter
118
  glyph.unicode = ord(letter)
119
+ add_svg_to_glyph(glyph, path_data)
 
 
 
 
120
  font.insertGlyph(glyph)
121
+
122
  return font
123
 
124
  def save_otf_font(glyphs, font_name="TipTopType-Regular.otf"):
125
  if not glyphs:
126
  return None
127
+
128
  with tempfile.TemporaryDirectory() as tmpdir:
129
  ufo_path = os.path.join(tmpdir, "font.ufo")
130
  font = build_ufo_from_glyphs(glyphs)
131
  font.save(ufo_path)
132
+
133
  output_dir = os.path.join(tmpdir, "out")
134
  os.makedirs(output_dir, exist_ok=True)
135
+
136
  project = FontProject()
137
  project.run_from_ufos([ufo_path], output=["otf"], output_dir=output_dir)
138
+
139
  otf_files = glob.glob(os.path.join(output_dir, "**/*.otf"), recursive=True)
140
  if not otf_files:
141
  raise FileNotFoundError("Aucun fichier OTF généré par fontmake.")
142
+
143
  generated_path = otf_files[0]
144
  final_path = os.path.join(tempfile.gettempdir(), font_name)
145
  os.replace(generated_path, final_path)
146
+
147
  return final_path
148
 
149
  # ------------------------
 
155
  if torch.cuda.is_available():
156
  model = model.to('cuda')
157
  model_device = next(model.parameters()).device
158
+
159
  messages = [{"role": "system", "content": system_message}]
160
  messages.append({"role": "user", "content": message})
161
+
162
  inputs = tokenizer.apply_chat_template(
163
  messages,
164
  tokenize=True,
165
  add_generation_prompt=True,
166
  return_tensors="pt",
167
  ).to(model_device)
168
+
169
  streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
170
+
171
  generation_kwargs = {
172
  "input_ids": inputs,
173
  "streamer": streamer,
 
181
  generation_kwargs["do_sample"] = False
182
  generation_kwargs.pop("temperature", None)
183
  generation_kwargs.pop("top_p", None)
184
+
185
  thread = Thread(target=model.generate, kwargs=generation_kwargs)
186
  thread.start()
187
+
188
  partial_response = ""
189
  for new_text in streamer:
190
  partial_response += new_text
 
200
  gr.Markdown("# TypTopType")
201
  glyphs_state = gr.State([])
202
  message_history = gr.State([])
203
+
204
  with gr.Row():
205
  with gr.Column(scale=1):
206
  msg = gr.Textbox(label="input box, type here")
 
214
  cols = gr.Slider(minimum=1, maximum=10, value=5, step=1, visible=False)
215
  width = gr.Slider(minimum=50, maximum=200, value=100, step=10, visible=False)
216
  height = gr.Slider(minimum=50, maximum=200, value=100, step=10, visible=False)
217
+
218
  download_btn = gr.Button("Download svg file")
219
  download_otf_btn = gr.Button("Download OTF font")
220
+
221
  with gr.Column(scale=3):
222
  gr.Markdown("## preview")
223
  svg_preview = gr.HTML(label="SVG Preview")
224
  download_output = gr.File(label="Download ZIP")
225
  download_otf_output = gr.File(label="Download OTF")
226
+
227
  def user(user_message, history):
228
  return "", history + [[user_message, None]]
229
+
230
  def bot(history, system_message, max_tokens, temperature, top_p, cols, width, height):
231
  message = history[-1][0]
232
  response_generator = respond(message, system_message, max_tokens, temperature, top_p)
 
237
  glyphs_list = glyphs
238
  svg_html = generate_glyphs_html(glyphs_list, cols=cols, width=width, height=height) if glyphs_list else "No glyphs found."
239
  yield svg_html, glyphs_list
240
+
241
  def download_svg(glyphs, width, height):
242
  if not glyphs:
243
  return None
244
  svg_files = generate_svg_files(glyphs, width=width, height=height)
245
  zip_path = create_zip(svg_files)
246
  return zip_path
247
+
248
  def download_otf(glyphs):
249
  if not glyphs:
250
  return None
251
  return save_otf_font(glyphs)
252
+
253
  msg.submit(user, [msg, message_history], [msg, message_history], queue=False).then(
254
  bot, [message_history, system_message, max_tokens, temperature, top_p, cols, width, height], [svg_preview, glyphs_state]
255
  )
256
+
257
  download_btn.click(
258
  download_svg,
259
  inputs=[glyphs_state, width, height],
260
  outputs=download_output
261
  )
262
+
263
  download_otf_btn.click(
264
  download_otf,
265
  inputs=[glyphs_state],
266
  outputs=download_otf_output
267
  )
268
+
269
  return demo
270
 
271
  demo = create_demo()
272
+
273
  if __name__ == "__main__":
274
  demo.launch()