ArrcttacsrjksX commited on
Commit
4b57180
·
verified ·
1 Parent(s): 30fb75c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -123
app.py CHANGED
@@ -6,35 +6,70 @@ import textwrap
6
  import os
7
  import matplotlib
8
  import math
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
- def get_system_fonts():
 
11
  fonts = []
 
12
  for font in matplotlib.font_manager.findSystemFonts(fontpaths=None, fontext='ttf'):
13
  font_name = os.path.basename(font)
14
- fonts.append(font_name)
15
- return sorted(set(fonts))
16
-
17
- def parse_color(color):
18
- if isinstance(color, str) and color.startswith('rgba'):
19
- color = color.replace('rgba', '').strip('()').split(',')
20
- return tuple(int(float(c.strip())) for c in color[:3])
21
- return color
22
-
23
- def calculate_text_dimensions(text, font, max_width, margin):
24
- lines = []
25
- for line in text.split('\n'):
26
- lines.extend(textwrap.wrap(line, width=int(max_width / font.size * 1.8)))
27
 
28
- bbox = font.getbbox('Ay')
29
- line_height = bbox[3] - bbox[1]
30
- total_height = line_height * len(lines)
 
 
 
 
31
 
32
- return lines, line_height, total_height
33
-
34
- def create_text_segment(lines, start_idx, max_lines, width, height, bg_color, text_color, font, align, margin):
35
- img = Image.new("RGB", (width, height), color=bg_color)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  draw = ImageDraw.Draw(img)
37
 
 
38
  bbox = font.getbbox('Ay')
39
  line_height = bbox[3] - bbox[1]
40
 
@@ -52,139 +87,100 @@ def create_text_segment(lines, start_idx, max_lines, width, height, bg_color, te
52
  x = (width - line_width) // 2
53
  else: # Right alignment
54
  x = width - line_width - margin
55
-
 
 
 
 
 
 
 
 
 
 
 
 
56
  draw.text((x, y), line, fill=text_color, font=font)
57
  y += line_height
58
 
59
  return img, end_idx
60
 
61
- def render_plain_text_image(text, font_size, width, height, bg_color, text_color, font_name, align):
62
- bg_color = parse_color(bg_color)
63
- text_color = parse_color(text_color)
64
- margin = 10
65
-
66
- try:
67
- font_path = matplotlib.font_manager.findfont(font_name)
68
- font = ImageFont.truetype(font_path, font_size)
69
- except Exception:
70
- font = ImageFont.load_default()
71
-
72
- # Calculate dimensions
73
- max_width = width - 2 * margin
74
- lines, line_height, total_text_height = calculate_text_dimensions(text, font, max_width, margin)
75
-
76
- # Calculate how many lines can fit in one segment
77
- max_lines_per_segment = (height - 2 * margin) // line_height
78
 
79
- # Calculate number of segments needed
80
- num_segments = math.ceil(len(lines) / max_lines_per_segment)
 
 
 
81
 
82
- # Create segments
83
- segments = []
84
- current_line = 0
85
-
86
- for i in range(num_segments):
87
- segment_img, current_line = create_text_segment(
88
- lines, current_line, max_lines_per_segment,
89
- width, height, bg_color, text_color, font, align, margin
90
- )
91
- segments.append(segment_img)
92
-
93
- # Combine all segments vertically
94
- total_height = len(segments) * height
95
- final_image = Image.new("RGB", (width, total_height), color=bg_color)
96
-
97
- for i, segment in enumerate(segments):
98
- final_image.paste(segment, (0, i * height))
99
-
100
- return final_image
101
-
102
- def render_math_image(text, font_size, width, height, bg_color, text_color):
103
- bg_color = parse_color(bg_color)
104
- text_color = parse_color(text_color)
105
-
106
- fig, ax = plt.subplots(figsize=(width / 100, height / 100), facecolor=bg_color)
107
- ax.set_facecolor(bg_color)
108
- ax.axis('off')
109
-
110
- if not (text.startswith(r"$") and text.endswith(r"$")):
111
- text = rf"${text}$"
112
-
113
- ax.text(0.5, 0.5, text, fontsize=font_size, ha='center', va='center', color=text_color)
114
-
115
- buf = BytesIO()
116
- plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0)
117
- plt.close(fig)
118
-
119
- buf.seek(0)
120
- img = Image.open(buf)
121
- return img
122
-
123
- def text_to_image(input_text, font_size, width, height, bg_color, text_color, mode, font_name, align, image_format):
124
  if mode == "Plain Text":
125
- img = render_plain_text_image(input_text, font_size, width, height, bg_color, text_color, font_name, align)
 
 
126
  elif mode == "LaTeX Math":
127
- img = render_math_image(input_text, font_size, width, height, bg_color, text_color)
 
128
  else:
129
  return "Invalid mode selected!"
130
 
131
  return img
132
 
133
- def handle_file_upload(file, font_size, width, height, bg_color, text_color, mode, font_name, align, image_format):
134
- if file is not None:
135
- file_path = file[0]
136
- with open(file_path, "r", encoding="utf-8") as f:
137
- text = f.read()
138
- return text_to_image(text, font_size, width, height, bg_color, text_color, mode, font_name, align, image_format)
139
- return "No file uploaded!"
140
-
141
- font_list = get_system_fonts()
142
- default_font = "DejaVuSans.ttf" if "DejaVuSans.ttf" in font_list else font_list[0]
143
-
144
  with gr.Blocks() as demo:
145
- gr.Markdown("# 🖼️ Text to Image Converter")
146
-
147
  with gr.Row():
148
- input_text = gr.Textbox(label="Enter Text", placeholder="Type or paste text here...", lines=5)
 
 
 
 
 
149
  file_input = gr.File(label="Upload a Text File", type="filepath")
150
-
151
  with gr.Row():
152
  font_size = gr.Slider(10, 100, value=30, label="Font Size")
153
- font_name = gr.Dropdown(choices=font_list, value=default_font, label="Font")
154
- align = gr.Radio(["Left", "Center", "Right"], label="Text Alignment", value="Center")
155
-
 
 
 
 
 
 
 
156
  with gr.Row():
157
  width = gr.Slider(200, 2000, value=800, label="Image Width")
158
  height = gr.Slider(200, 2000, value=600, label="Base Height")
159
-
160
  with gr.Row():
161
  bg_color = gr.ColorPicker(label="Background Color", value="#FFFFFF")
162
  text_color = gr.ColorPicker(label="Text Color", value="#000000")
163
-
164
  with gr.Row():
165
- mode = gr.Radio(["Plain Text", "LaTeX Math"], label="Rendering Mode", value="Plain Text")
166
- image_format = gr.Radio(["PNG", "JPEG"], label="Image Format", value="PNG")
167
-
 
 
168
  output_image = gr.Image(label="Generated Image")
169
-
170
- with gr.Row():
171
- convert_button = gr.Button("Convert Text to Image")
172
- file_convert_button = gr.Button("Convert File to Image")
173
-
 
 
174
  convert_button.click(
175
  text_to_image,
176
  inputs=[
177
  input_text, font_size, width, height, bg_color, text_color,
178
- mode, font_name, align, image_format
179
- ],
180
- outputs=output_image
181
- )
182
-
183
- file_convert_button.click(
184
- handle_file_upload,
185
- inputs=[
186
- file_input, font_size, width, height, bg_color, text_color,
187
- mode, font_name, align, image_format
188
  ],
189
  outputs=output_image
190
  )
 
6
  import os
7
  import matplotlib
8
  import math
9
+ import zipfile
10
+ import tempfile
11
+ import shutil
12
+
13
+ # Create a fonts directory in temp
14
+ FONTS_DIR = os.path.join(tempfile.gettempdir(), 'text_to_image_fonts')
15
+ os.makedirs(FONTS_DIR, exist_ok=True)
16
+
17
+ def extract_fonts_from_zip(zip_path):
18
+ """Extract TTF and SHX fonts from zip file"""
19
+ supported_extensions = ('.ttf', '.shx')
20
+ font_files = []
21
+
22
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
23
+ # Extract all font files
24
+ for file in zip_ref.namelist():
25
+ if file.lower().endswith(supported_extensions):
26
+ zip_ref.extract(file, FONTS_DIR)
27
+ font_files.append(os.path.join(FONTS_DIR, file))
28
+
29
+ return font_files
30
 
31
+ def get_all_fonts():
32
+ """Get system fonts and custom fonts"""
33
  fonts = []
34
+ # System fonts
35
  for font in matplotlib.font_manager.findSystemFonts(fontpaths=None, fontext='ttf'):
36
  font_name = os.path.basename(font)
37
+ fonts.append({"name": font_name, "path": font, "type": "system"})
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ # Custom fonts from FONTS_DIR
40
+ if os.path.exists(FONTS_DIR):
41
+ for font in os.listdir(FONTS_DIR):
42
+ if font.lower().endswith(('.ttf', '.shx')):
43
+ font_path = os.path.join(FONTS_DIR, font)
44
+ fonts.append({"name": font, "path": font_path,
45
+ "type": "custom_" + os.path.splitext(font)[1][1:]})
46
 
47
+ return sorted(fonts, key=lambda x: (x["type"], x["name"]))
48
+
49
+ def load_font(font_info, size):
50
+ """Load font based on type"""
51
+ if font_info["type"] == "custom_shx":
52
+ # Here you would implement SHX font loading
53
+ # For now, fallback to default
54
+ return ImageFont.load_default()
55
+ else:
56
+ try:
57
+ return ImageFont.truetype(font_info["path"], size)
58
+ except Exception:
59
+ return ImageFont.load_default()
60
+
61
+ def handle_font_upload(zip_file):
62
+ """Handle font zip file upload"""
63
+ if zip_file is not None:
64
+ extracted_fonts = extract_fonts_from_zip(zip_file.name)
65
+ return gr.Dropdown.update(choices=[f["name"] for f in get_all_fonts()])
66
+ return gr.Dropdown.update()
67
+
68
+ def create_text_segment(lines, start_idx, max_lines, width, height, bg_color, text_color, font_info, font_size, align, margin, text_effects):
69
+ img = Image.new("RGBA", (width, height), color=bg_color)
70
  draw = ImageDraw.Draw(img)
71
 
72
+ font = load_font(font_info, font_size)
73
  bbox = font.getbbox('Ay')
74
  line_height = bbox[3] - bbox[1]
75
 
 
87
  x = (width - line_width) // 2
88
  else: # Right alignment
89
  x = width - line_width - margin
90
+
91
+ # Apply text effects
92
+ if 'shadow' in text_effects:
93
+ shadow_offset = 2
94
+ draw.text((x + shadow_offset, y + shadow_offset), line,
95
+ fill=(0, 0, 0, 128), font=font)
96
+
97
+ if 'outline' in text_effects:
98
+ outline_color = (0, 0, 0, 255)
99
+ for off_x, off_y in [(1,0), (-1,0), (0,1), (0,-1)]:
100
+ draw.text((x + off_x, y + off_y), line,
101
+ fill=outline_color, font=font)
102
+
103
  draw.text((x, y), line, fill=text_color, font=font)
104
  y += line_height
105
 
106
  return img, end_idx
107
 
108
+ def text_to_image(input_text, font_size, width, height, bg_color, text_color,
109
+ mode, font_name, align, image_format, text_effects, opacity):
110
+ # Convert colors to RGBA
111
+ bg_color = (*parse_color(bg_color), int(opacity * 255))
112
+ text_color = (*parse_color(text_color), 255)
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ # Get font info
115
+ font_list = get_all_fonts()
116
+ font_info = next((f for f in font_list if f["name"] == font_name), None)
117
+ if not font_info:
118
+ font_info = {"name": "default", "path": None, "type": "system"}
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  if mode == "Plain Text":
121
+ img = render_plain_text_image(input_text, font_size, width, height,
122
+ bg_color, text_color, font_info, align,
123
+ text_effects, opacity)
124
  elif mode == "LaTeX Math":
125
+ img = render_math_image(input_text, font_size, width, height,
126
+ bg_color, text_color)
127
  else:
128
  return "Invalid mode selected!"
129
 
130
  return img
131
 
132
+ # Update the Gradio interface
 
 
 
 
 
 
 
 
 
 
133
  with gr.Blocks() as demo:
134
+ gr.Markdown("# 🖼️ Enhanced Text to Image Converter")
135
+
136
  with gr.Row():
137
+ font_upload = gr.File(label="Upload Fonts ZIP", type="file",
138
+ file_types=[".zip"])
139
+
140
+ with gr.Row():
141
+ input_text = gr.Textbox(label="Enter Text",
142
+ placeholder="Type or paste text here...", lines=5)
143
  file_input = gr.File(label="Upload a Text File", type="filepath")
144
+
145
  with gr.Row():
146
  font_size = gr.Slider(10, 100, value=30, label="Font Size")
147
+ font_name = gr.Dropdown(choices=[f["name"] for f in get_all_fonts()],
148
+ label="Font")
149
+ align = gr.Radio(["Left", "Center", "Right"],
150
+ label="Text Alignment", value="Center")
151
+
152
+ with gr.Row():
153
+ text_effects = gr.CheckboxGroup(
154
+ ["shadow", "outline"], label="Text Effects")
155
+ opacity = gr.Slider(0, 1, value=1, label="Background Opacity")
156
+
157
  with gr.Row():
158
  width = gr.Slider(200, 2000, value=800, label="Image Width")
159
  height = gr.Slider(200, 2000, value=600, label="Base Height")
160
+
161
  with gr.Row():
162
  bg_color = gr.ColorPicker(label="Background Color", value="#FFFFFF")
163
  text_color = gr.ColorPicker(label="Text Color", value="#000000")
164
+
165
  with gr.Row():
166
+ mode = gr.Radio(["Plain Text", "LaTeX Math"],
167
+ label="Rendering Mode", value="Plain Text")
168
+ image_format = gr.Radio(["PNG", "JPEG"],
169
+ label="Image Format", value="PNG")
170
+
171
  output_image = gr.Image(label="Generated Image")
172
+
173
+ # Connect components
174
+ font_upload.upload(handle_font_upload,
175
+ inputs=[font_upload],
176
+ outputs=[font_name])
177
+
178
+ convert_button = gr.Button("Convert Text to Image")
179
  convert_button.click(
180
  text_to_image,
181
  inputs=[
182
  input_text, font_size, width, height, bg_color, text_color,
183
+ mode, font_name, align, image_format, text_effects, opacity
 
 
 
 
 
 
 
 
 
184
  ],
185
  outputs=output_image
186
  )