ArrcttacsrjksX commited on
Commit
0e78616
·
verified ·
1 Parent(s): 98ddded

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +159 -480
app.py CHANGED
@@ -1,512 +1,191 @@
1
  import gradio as gr
2
  import matplotlib.pyplot as plt
3
  from io import BytesIO
4
- from PIL import Image, ImageDraw, ImageFont, ImageFilter
5
  import textwrap
6
  import os
7
  import matplotlib
8
  import math
9
  import zipfile
10
- import tempfile
11
- import shutil
12
- import numpy as np
13
- from typing import Dict, List, Tuple, Optional
14
- import re
15
 
16
- # Constants
17
- FONTS_DIR = os.path.join(tempfile.gettempdir(), 'text_to_image_fonts')
18
- SUPPORTED_FONT_TYPES = ('.ttf', '.otf', '.shx')
19
- DEFAULT_MARGIN = 10
 
 
20
 
21
- class FontManager:
22
- def __init__(self):
23
- self.fonts_dir = FONTS_DIR
24
- os.makedirs(self.fonts_dir, exist_ok=True)
25
- self.font_cache: Dict[str, Dict] = {}
26
- self.reload_fonts()
27
-
28
- def reload_fonts(self):
29
- """Reload all available fonts"""
30
- self.font_cache.clear()
31
- # System fonts
32
- for font in matplotlib.font_manager.findSystemFonts(fontpaths=None, fontext='ttf'):
33
- font_name = os.path.basename(font)
34
- self.font_cache[font_name] = {
35
- "name": font_name,
36
- "path": font,
37
- "type": "system"
38
- }
39
-
40
- # Custom fonts
41
- if os.path.exists(self.fonts_dir):
42
- for font in os.listdir(self.fonts_dir):
43
- if font.lower().endswith(SUPPORTED_FONT_TYPES):
44
- font_path = os.path.join(self.fonts_dir, font)
45
- self.font_cache[font] = {
46
- "name": font,
47
- "path": font_path,
48
- "type": f"custom_{os.path.splitext(font)[1][1:]}"
49
- }
50
-
51
- def get_font_list(self) -> List[Dict]:
52
- """Get sorted list of all available fonts"""
53
- return sorted(self.font_cache.values(), key=lambda x: (x["type"], x["name"]))
54
 
55
- def extract_fonts_from_zip(self, zip_path: str) -> List[str]:
56
- """Extract fonts from ZIP file"""
57
- extracted_files = []
58
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
59
- for file in zip_ref.namelist():
60
- if file.lower().endswith(SUPPORTED_FONT_TYPES):
61
- zip_ref.extract(file, self.fonts_dir)
62
- extracted_files.append(os.path.join(self.fonts_dir, file))
63
- self.reload_fonts()
64
- return extracted_files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- def get_font(self, font_name: str, size: int) -> ImageFont.FreeTypeFont:
67
- """Get font instance by name and size"""
68
- font_info = self.font_cache.get(font_name)
69
- if not font_info:
70
- return ImageFont.load_default()
71
-
72
- try:
73
- if font_info["type"] == "custom_shx":
74
- # Implement SHX font loading here
75
- # For now, return default font
76
- return ImageFont.load_default()
77
- else:
78
- return ImageFont.truetype(font_info["path"], size)
79
- except Exception as e:
80
- print(f"Error loading font {font_name}: {e}")
81
- return ImageFont.load_default()
82
 
83
- class TextEffects:
84
- @staticmethod
85
- def apply_shadow(draw: ImageDraw.ImageDraw, text: str, x: int, y: int,
86
- font: ImageFont.FreeTypeFont, shadow_color: Tuple[int, int, int, int],
87
- offset: int = 2):
88
- """Apply shadow effect to text"""
89
- draw.text((x + offset, y + offset), text, fill=shadow_color, font=font)
90
 
91
- @staticmethod
92
- def apply_outline(draw: ImageDraw.ImageDraw, text: str, x: int, y: int,
93
- font: ImageFont.FreeTypeFont, outline_color: Tuple[int, int, int, int],
94
- width: int = 1):
95
- """Apply outline effect to text"""
96
- for off_x, off_y in [(i, j) for i in range(-width, width+1)
97
- for j in range(-width, width+1)
98
- if (i, j) != (0, 0)]:
99
- draw.text((x + off_x, y + off_y), text, fill=outline_color, font=font)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- @staticmethod
102
- def apply_gradient(img: Image.Image, start_color: Tuple[int, int, int],
103
- end_color: Tuple[int, int, int], direction: str = 'vertical'):
104
- """Apply gradient effect to image"""
105
- width, height = img.size
106
- gradient = Image.new('RGBA', img.size, (0, 0, 0, 0))
107
- draw = ImageDraw.Draw(gradient)
108
-
109
- for i in range(height if direction == 'vertical' else width):
110
- progress = i / (height if direction == 'vertical' else width)
111
- color = tuple(int(start + (end - start) * progress)
112
- for start, end in zip(start_color, end_color))
113
- if direction == 'vertical':
114
- draw.line([(0, i), (width, i)], fill=color)
115
- else:
116
- draw.line([(i, 0), (i, height)], fill=color)
117
-
118
- return Image.alpha_composite(img, gradient)
119
 
120
- class TextRenderer:
121
- def __init__(self):
122
- self.font_manager = FontManager()
123
 
124
- def parse_color(self, color: str) -> Tuple[int, int, int]:
125
- """Parse color string to RGB tuple"""
126
- if isinstance(color, str):
127
- if color.startswith('rgba'):
128
- color = color.replace('rgba', '').strip('()').split(',')
129
- return tuple(int(float(c.strip())) for c in color[:3])
130
- elif color.startswith('#'):
131
- color = color.lstrip('#')
132
- return tuple(int(color[i:i+2], 16) for i in (0, 2, 4))
133
- return color
134
 
135
- def calculate_text_dimensions(self, text: str, font: ImageFont.FreeTypeFont,
136
- max_width: int) -> Tuple[List[str], int, int]:
137
- """Calculate text dimensions and wrap lines"""
138
- lines = []
139
- for line in text.split('\n'):
140
- wrapped = textwrap.wrap(line, width=int(max_width / font.size * 1.8))
141
- lines.extend(wrapped if wrapped else ['']) # Preserve empty lines
142
-
143
- bbox = font.getbbox('Ay')
144
- line_height = bbox[3] - bbox[1]
145
- total_height = line_height * len(lines)
146
-
147
- return lines, line_height, total_height
148
 
149
- def create_text_segment(self, lines: List[str], start_idx: int, max_lines: int,
150
- width: int, height: int, bg_color: Tuple[int, int, int, int],
151
- text_color: Tuple[int, int, int, int], font_info: Dict,
152
- font_size: int, align: str, margin: int,
153
- effects: List[str]) -> Tuple[Image.Image, int]:
154
- """Create a segment of text with applied effects"""
155
- img = Image.new("RGBA", (width, height), color=bg_color)
156
- draw = ImageDraw.Draw(img)
157
-
158
- font = self.font_manager.get_font(font_info["name"], font_size)
159
- bbox = font.getbbox('Ay')
160
- line_height = bbox[3] - bbox[1]
161
-
162
- y = margin
163
- end_idx = min(start_idx + max_lines, len(lines))
164
- segment_lines = lines[start_idx:end_idx]
165
-
166
- for line in segment_lines:
167
- bbox = font.getbbox(line)
168
- line_width = bbox[2] - bbox[0]
169
-
170
- if align == 'Left':
171
- x = margin
172
- elif align == 'Center':
173
- x = (width - line_width) // 2
174
- else: # Right alignment
175
- x = width - line_width - margin
176
-
177
- # Apply effects
178
- if 'shadow' in effects:
179
- TextEffects.apply_shadow(draw, line, x, y, font,
180
- (0, 0, 0, 128))
181
-
182
- if 'outline' in effects:
183
- TextEffects.apply_outline(draw, line, x, y, font,
184
- (0, 0, 0, 255))
185
-
186
- if 'gradient' in effects:
187
- # Save current position for gradient application
188
- gradient_pos = (x, y, x + line_width, y + line_height)
189
-
190
- draw.text((x, y), line, fill=text_color, font=font)
191
-
192
- if 'gradient' in effects:
193
- # Apply gradient to the text area
194
- gradient_overlay = Image.new('RGBA', (line_width, line_height))
195
- TextEffects.apply_gradient(gradient_overlay,
196
- (255, 0, 0), (0, 0, 255))
197
- img.paste(gradient_overlay, gradient_pos, gradient_overlay)
198
-
199
- y += line_height
200
-
201
- if 'blur' in effects:
202
- img = img.filter(ImageFilter.GaussianBlur(radius=1))
203
-
204
- return img, end_idx
205
 
206
- def render_text_image(self, text: str, font_size: int, width: int, height: int,
207
- bg_color: Tuple[int, int, int, int],
208
- text_color: Tuple[int, int, int, int],
209
- font_name: str, align: str, effects: List[str],
210
- margin: int = DEFAULT_MARGIN) -> Image.Image:
211
- """Render text to image with all effects"""
212
- font_info = next((f for f in self.font_manager.get_font_list()
213
- if f["name"] == font_name), None)
214
- if not font_info:
215
- font_info = {"name": "default", "path": None, "type": "system"}
216
-
217
- font = self.font_manager.get_font(font_info["name"], font_size)
218
- max_width = width - 2 * margin
219
- lines, line_height, total_height = self.calculate_text_dimensions(
220
- text, font, max_width)
221
-
222
- max_lines_per_segment = (height - 2 * margin) // line_height
223
- num_segments = math.ceil(len(lines) / max_lines_per_segment)
224
-
225
- segments = []
226
- current_line = 0
227
-
228
- for i in range(num_segments):
229
- segment_img, current_line = self.create_text_segment(
230
- lines, current_line, max_lines_per_segment,
231
- width, height, bg_color, text_color, font_info,
232
- font_size, align, margin, effects
233
- )
234
- segments.append(segment_img)
235
-
236
- total_height = len(segments) * height
237
- final_image = Image.new("RGBA", (width, total_height), color=bg_color)
238
-
239
- for i, segment in enumerate(segments):
240
- final_image.paste(segment, (0, i * height), segment)
241
-
242
- return final_image
243
 
244
- def render_math_image(self, text: str, font_size: int, width: int, height: int,
245
- bg_color: Tuple[int, int, int, int],
246
- text_color: Tuple[int, int, int, int]) -> Image.Image:
247
- """Render mathematical equations"""
248
- fig, ax = plt.subplots(figsize=(width/100, height/100))
249
- ax.set_facecolor(bg_color)
250
- fig.patch.set_facecolor(bg_color)
251
- ax.axis('off')
252
-
253
- if not (text.startswith('$') and text.endswith('$')):
254
- text = f'${text}$'
255
-
256
- ax.text(0.5, 0.5, text, fontsize=font_size, ha='center', va='center',
257
- color=text_color)
258
-
259
- buf = BytesIO()
260
- plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0,
261
- transparent=True)
262
- plt.close(fig)
263
-
264
- buf.seek(0)
265
- return Image.open(buf)
266
 
267
- class TextToImageApp:
268
- def __init__(self):
269
- self.renderer = TextRenderer()
270
- self.setup_interface()
271
 
272
- def handle_font_upload(self, file):
273
- """Handle font file upload"""
274
- if file is not None:
275
- self.renderer.font_manager.extract_fonts_from_zip(file.name)
276
- return gr.Dropdown.update(
277
- choices=[f["name"] for f in self.renderer.font_manager.get_font_list()]
278
- )
279
- return gr.Dropdown.update()
280
 
281
- def convert_text(self, input_text: str, font_size: int, width: int, height: int,
282
- bg_color: str, text_color: str, mode: str, font_name: str,
283
- align: str, image_format: str, effects: List[str],
284
- opacity: float) -> Image.Image:
285
- """Convert text to image with all options"""
286
- bg_color = (*self.renderer.parse_color(bg_color), int(opacity * 255))
287
- text_color = (*self.renderer.parse_color(text_color), 255)
288
-
289
- if mode == "Plain Text":
290
- img = self.renderer.render_text_image(
291
- input_text, font_size, width, height,
292
- bg_color, text_color, font_name, align, effects
293
- )
294
- else: # LaTeX Math
295
- img = self.renderer.render_math_image(
296
- input_text, font_size, width, height,
297
- bg_color, text_color
298
- )
299
-
300
- # Convert to selected format
301
- if image_format == "JPEG":
302
- # Convert to RGB for JPEG
303
- bg = Image.new('RGB', img.size, (255, 255, 255))
304
- bg.paste(img, mask=img.split()[3])
305
- img = bg
306
-
307
- return img
308
 
309
- def setup_interface(self):
310
- """Setup Gradio interface"""
311
- with gr.Blocks() as self.demo:
312
- gr.Markdown("# 🖼️ Advanced Text to Image Converter")
313
-
314
- with gr.Row():
315
- font_upload = gr.File(
316
- label="Upload Fonts ZIP",
317
- type="file",
318
- file_types=[".zip"]
319
- )
320
-
321
- with gr.Row():
322
- input_text = gr.Textbox(
323
- label="Enter Text",
324
- placeholder="Type or paste text here...",
325
- lines=5
326
- )
327
-
328
- with gr.Row():
329
- font_size = gr.Slider(10, 100, value=30, label="Font Size")
330
- font_name = gr.Dropdown(
331
- choices=[f["name"] for f in self.renderer.font_manager.get_font_list()],
332
- label="Font"
333
- )
334
- align = gr.Radio(
335
- ["Left", "Center", "Right"],
336
- label="Text Alignment",
337
- value="Center"
338
- )
339
-
340
- with gr.Row():
341
- effects = gr.CheckboxGroup(
342
- ["shadow", "outline", "gradient", "blur"],
343
- label="Text Effects"
344
- )
345
- opacity = gr.Slider(0, 1, value=1, label="Background Opacity")
346
- with gr.Row():
347
- width = gr.Slider(200, 2000, value=800, label="Image Width")
348
- height = gr.Slider(200, 2000, value=600, label="Base Height")
349
-
350
- with gr.Row():
351
- bg_color = gr.ColorPicker(label="Background Color", value="#FFFFFF")
352
- text_color = gr.ColorPicker(label="Text Color", value="#000000")
353
-
354
- with gr.Row():
355
- mode = gr.Radio(
356
- ["Plain Text", "LaTeX Math"],
357
- label="Rendering Mode",
358
- value="Plain Text"
359
- )
360
- image_format = gr.Radio(
361
- ["PNG", "JPEG"],
362
- label="Image Format",
363
- value="PNG"
364
- )
365
-
366
- output_image = gr.Image(label="Generated Image")
367
-
368
- # Connect components
369
- font_upload.upload(
370
- self.handle_font_upload,
371
- inputs=[font_upload],
372
- outputs=[font_name]
373
- )
374
-
375
- convert_button = gr.Button("Convert Text to Image")
376
- convert_button.click(
377
- self.convert_text,
378
- inputs=[
379
- input_text, font_size, width, height,
380
- bg_color, text_color, mode, font_name,
381
- align, image_format, effects, opacity
382
- ],
383
- outputs=output_image
384
- )
385
 
386
- def launch(self):
387
- """Launch the Gradio interface"""
388
- self.demo.launch()
389
 
 
 
 
390
 
391
- class SHXFontRenderer:
392
- """SHX Font Renderer for AutoCAD SHX fonts"""
393
-
394
- def __init__(self, shx_file_path: str):
395
- self.shx_file_path = shx_file_path
396
- self.font_data = self.load_shx_file()
397
-
398
- def load_shx_file(self) -> Dict:
399
- """Load and parse SHX font file"""
400
- font_data = {}
401
- try:
402
- with open(self.shx_file_path, 'rb') as f:
403
- # Read SHX header
404
- header = f.read(6)
405
- if header[0:2] != b'\x1a\x00':
406
- raise ValueError("Invalid SHX file format")
407
-
408
- # Parse font records
409
- while True:
410
- record = self.read_record(f)
411
- if not record:
412
- break
413
- if 'char_code' in record:
414
- font_data[record['char_code']] = record['vectors']
415
-
416
- except Exception as e:
417
- print(f"Error loading SHX font: {e}")
418
-
419
- return font_data
420
-
421
- def read_record(self, file) -> Optional[Dict]:
422
- """Read a single record from SHX file"""
423
- try:
424
- record_type = int.from_bytes(file.read(1), byteorder='little')
425
- if record_type == 0: # EOF
426
- return None
427
-
428
- if record_type == 0x0A: # Character definition
429
- char_code = int.from_bytes(file.read(2), byteorder='little')
430
- vectors = self.read_vectors(file)
431
- return {'char_code': char_code, 'vectors': vectors}
432
-
433
- return {}
434
-
435
- except Exception:
436
- return None
437
-
438
- def read_vectors(self, file) -> List[Dict]:
439
- """Read vector data for a character"""
440
- vectors = []
441
- while True:
442
- try:
443
- vector_type = int.from_bytes(file.read(1), byteorder='little')
444
- if vector_type == 0: # End of character
445
- break
446
-
447
- x = int.from_bytes(file.read(1), byteorder='little', signed=True)
448
- y = int.from_bytes(file.read(1), byteorder='little', signed=True)
449
-
450
- vectors.append({
451
- 'type': vector_type,
452
- 'x': x,
453
- 'y': y
454
- })
455
-
456
- except Exception:
457
- break
458
-
459
- return vectors
460
-
461
- def render_text(self, text: str, size: int, color: Tuple[int, int, int, int]) -> Image.Image:
462
- """Render text using SHX font"""
463
- # Calculate image size based on text and font size
464
- scale = size / 16.0 # Standard SHX size is 16 units
465
- img_width = int(len(text) * size * 0.75) # Approximate width
466
- img_height = int(size * 1.5)
467
-
468
- # Create image
469
- img = Image.new('RGBA', (img_width, img_height), (0, 0, 0, 0))
470
- draw = ImageDraw.Draw(img)
471
-
472
- x_pos = 10
473
- y_pos = img_height // 2
474
-
475
- for char in text:
476
- char_code = ord(char)
477
- if char_code in self.font_data:
478
- vectors = self.font_data[char_code]
479
- last_pos = None
480
-
481
- for vector in vectors:
482
- if vector['type'] == 1: # Draw
483
- if last_pos:
484
- draw.line(
485
- [
486
- (x_pos + last_pos[0] * scale,
487
- y_pos - last_pos[1] * scale),
488
- (x_pos + vector['x'] * scale,
489
- y_pos - vector['y'] * scale)
490
- ],
491
- fill=color,
492
- width=max(1, int(scale / 4))
493
- )
494
- last_pos = (vector['x'], vector['y'])
495
- else: # Move
496
- last_pos = (vector['x'], vector['y'])
497
-
498
- x_pos += size * 0.75 # Move to next character
499
- else:
500
- x_pos += size * 0.5 # Space for unknown characters
501
-
502
- return img
503
 
 
504
 
505
- def main():
506
- """Main function to run the application"""
507
- app = TextToImageApp()
508
- app.launch()
509
 
 
 
 
 
 
 
 
 
510
 
511
- if __name__ == "__main__":
512
- main()
 
1
  import gradio as gr
2
  import matplotlib.pyplot as plt
3
  from io import BytesIO
4
+ from PIL import Image, ImageDraw, ImageFont
5
  import textwrap
6
  import os
7
  import matplotlib
8
  import math
9
  import zipfile
 
 
 
 
 
10
 
11
+ def get_system_fonts():
12
+ fonts = []
13
+ for font in matplotlib.font_manager.findSystemFonts(fontpaths=None, fontext='ttf'):
14
+ font_name = os.path.basename(font)
15
+ fonts.append(font_name)
16
+ return sorted(set(fonts))
17
 
18
+ def extract_and_load_fonts(zip_path, extract_to="extracted_fonts"):
19
+ if not os.path.exists(extract_to):
20
+ os.makedirs(extract_to)
21
+
22
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
23
+ zip_ref.extractall(extract_to)
24
+
25
+ fonts = []
26
+ for root, dirs, files in os.walk(extract_to):
27
+ for file in files:
28
+ if file.endswith(".ttf") or file.endswith(".shx"):
29
+ fonts.append(os.path.join(root, file))
30
+ return fonts
31
+
32
+ def parse_color(color):
33
+ if isinstance(color, str) and color.startswith('rgba'):
34
+ color = color.replace('rgba', '').strip('()').split(',')
35
+ return tuple(int(float(c.strip())) for c in color[:3])
36
+ return color
37
+
38
+ def calculate_text_dimensions(text, font, max_width, margin):
39
+ lines = []
40
+ for line in text.split('\n'):
41
+ lines.extend(textwrap.wrap(line, width=int(max_width / font.size * 1.8)))
42
+
43
+ bbox = font.getbbox('Ay')
44
+ line_height = bbox[3] - bbox[1]
45
+ total_height = line_height * len(lines)
46
+
47
+ return lines, line_height, total_height
 
 
 
48
 
49
+ def create_text_segment(lines, start_idx, max_lines, width, height, bg_color, text_color, font, align, margin):
50
+ img = Image.new("RGB", (width, height), color=bg_color)
51
+ draw = ImageDraw.Draw(img)
52
+
53
+ bbox = font.getbbox('Ay')
54
+ line_height = bbox[3] - bbox[1]
55
+
56
+ y = margin
57
+ end_idx = min(start_idx + max_lines, len(lines))
58
+ segment_lines = lines[start_idx:end_idx]
59
+
60
+ for line in segment_lines:
61
+ bbox = font.getbbox(line)
62
+ line_width = bbox[2] - bbox[0]
63
+
64
+ if align == 'Left':
65
+ x = margin
66
+ elif align == 'Center':
67
+ x = (width - line_width) // 2
68
+ else: # Right alignment
69
+ x = width - line_width - margin
70
+
71
+ draw.text((x, y), line, fill=text_color, font=font)
72
+ y += line_height
73
+
74
+ return img, end_idx
75
 
76
+ def render_plain_text_image(text, font_size, width, height, bg_color, text_color, font_path, align):
77
+ bg_color = parse_color(bg_color)
78
+ text_color = parse_color(text_color)
79
+ margin = 10
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ try:
82
+ font = ImageFont.truetype(font_path, font_size)
83
+ except Exception:
84
+ font = ImageFont.load_default()
 
 
 
85
 
86
+ # Calculate dimensions
87
+ max_width = width - 2 * margin
88
+ lines, line_height, total_text_height = calculate_text_dimensions(text, font, max_width, margin)
89
+
90
+ # Calculate how many lines can fit in one segment
91
+ max_lines_per_segment = (height - 2 * margin) // line_height
92
+
93
+ # Calculate number of segments needed
94
+ num_segments = math.ceil(len(lines) / max_lines_per_segment)
95
+
96
+ # Create segments
97
+ segments = []
98
+ current_line = 0
99
+
100
+ for i in range(num_segments):
101
+ segment_img, current_line = create_text_segment(
102
+ lines, current_line, max_lines_per_segment,
103
+ width, height, bg_color, text_color, font, align, margin
104
+ )
105
+ segments.append(segment_img)
106
+
107
+ # Combine all segments vertically
108
+ total_height = len(segments) * height
109
+ final_image = Image.new("RGB", (width, total_height), color=bg_color)
110
+
111
+ for i, segment in enumerate(segments):
112
+ final_image.paste(segment, (0, i * height))
113
+
114
+ return final_image
115
 
116
+ def render_math_image(text, font_size, width, height, bg_color, text_color):
117
+ bg_color = parse_color(bg_color)
118
+ text_color = parse_color(text_color)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
+ fig, ax = plt.subplots(figsize=(width / 100, height / 100), facecolor=bg_color)
121
+ ax.set_facecolor(bg_color)
122
+ ax.axis('off')
123
 
124
+ if not (text.startswith(r"$") and text.endswith(r"$")):
125
+ text = rf"${text}$"
 
 
 
 
 
 
 
 
126
 
127
+ ax.text(0.5, 0.5, text, fontsize=font_size, ha='center', va='center', color=text_color)
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
+ buf = BytesIO()
130
+ plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0)
131
+ plt.close(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ buf.seek(0)
134
+ img = Image.open(buf)
135
+ return img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
+ def text_to_image(input_text, font_size, width, height, bg_color, text_color, mode, font_path, align, image_format):
138
+ if mode == "Plain Text":
139
+ img = render_plain_text_image(input_text, font_size, width, height, bg_color, text_color, font_path, align)
140
+ elif mode == "LaTeX Math":
141
+ img = render_math_image(input_text, font_size, width, height, bg_color, text_color)
142
+ else:
143
+ return "Invalid mode selected!"
144
+
145
+ return img
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
+ # Extract fonts from Fontsshx.zip
148
+ extracted_fonts = extract_and_load_fonts("Fontsshx.zip")
149
+ system_fonts = get_system_fonts()
150
+ all_fonts = extracted_fonts + system_fonts
151
 
152
+ with gr.Blocks() as demo:
153
+ gr.Markdown("# 🖼️ Text to Image Converter")
 
 
 
 
 
 
154
 
155
+ with gr.Row():
156
+ input_text = gr.Textbox(label="Enter Text", placeholder="Type or paste text here...", lines=5)
157
+ file_input = gr.File(label="Upload a Text File", type="filepath")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
+ with gr.Row():
160
+ font_size = gr.Slider(10, 100, value=30, label="Font Size")
161
+ font_name = gr.Dropdown(choices=all_fonts, value=all_fonts[0], label="Font")
162
+ align = gr.Radio(["Left", "Center", "Right"], label="Text Alignment", value="Center")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ with gr.Row():
165
+ width = gr.Slider(200, 2000, value=800, label="Image Width")
166
+ height = gr.Slider(200, 2000, value=600, label="Base Height")
167
 
168
+ with gr.Row():
169
+ bg_color = gr.ColorPicker(label="Background Color", value="#FFFFFF")
170
+ text_color = gr.ColorPicker(label="Text Color", value="#000000")
171
 
172
+ with gr.Row():
173
+ mode = gr.Radio(["Plain Text", "LaTeX Math"], label="Rendering Mode", value="Plain Text")
174
+ image_format = gr.Radio(["PNG", "JPEG"], label="Image Format", value="PNG")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
+ output_image = gr.Image(label="Generated Image")
177
 
178
+ with gr.Row():
179
+ convert_button = gr.Button("Convert Text to Image")
180
+ file_convert_button = gr.Button("Convert File to Image")
 
181
 
182
+ convert_button.click(
183
+ text_to_image,
184
+ inputs=[
185
+ input_text, font_size, width, height, bg_color, text_color,
186
+ mode, font_name, align, image_format
187
+ ],
188
+ outputs=output_image
189
+ )
190
 
191
+ demo.launch()