bakyt92 commited on
Commit
6d002da
·
1 Parent(s): 3676989

Added font selector

Browse files
Files changed (1) hide show
  1. app.py +106 -86
app.py CHANGED
@@ -80,13 +80,14 @@ def process_uploaded_image(image):
80
  return image, f"Image uploaded with warning: {e}", image
81
 
82
  # Text Overlay Functions
83
- def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_outline):
84
  """Overlay 2- or 3-line text on the image in a preset layout."""
85
  print("=== DEBUG: add_text_to_image called ===")
86
  print(f"Input image: {type(img)}")
87
  print(f"Pattern: {pattern}")
88
  print(f"Lines: [{line1!r}, {line2!r}, {line3!r}]")
89
  print(f"Font size: {font_size}")
 
90
  print(f"Color: {color!r}")
91
  print(f"Add outline: {add_outline}")
92
 
@@ -102,42 +103,18 @@ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_o
102
  draw = ImageDraw.Draw(image)
103
  print("ImageDraw created successfully")
104
 
105
- # Font loading (same as before)
106
- font = None
107
- font_paths = [
108
- "DejaVuSans-Bold.ttf",
109
- "arial.ttf",
110
- "/System/Library/Fonts/Arial.ttf",
111
- "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
112
- "/usr/share/fonts/TTF/arial.ttf",
113
- ]
114
-
115
- print("Attempting to load fonts...")
116
- for i, font_path in enumerate(font_paths):
117
- try:
118
- font = ImageFont.truetype(font_path, font_size)
119
- print(f"SUCCESS: Loaded font {font_path}")
120
- break
121
- except (OSError, IOError) as e:
122
- print(f"FAILED: Font {font_path} - {e}")
123
- continue
124
-
125
  if font is None:
126
- try:
127
- font = ImageFont.load_default()
128
- print("Using default font as fallback")
129
- except Exception as e:
130
- print(f"ERROR: Even default font failed: {e}")
131
- return None, f"Font loading error: {e}"
132
 
133
  w, h = image.size
134
  print(f"Image dimensions: {w}x{h}")
135
 
136
- # ROBUST pattern matching - handle both simple names and descriptive text
137
  positions = []
138
  detected_pattern = "unknown"
139
 
140
- # Normalize pattern string for matching
141
  pattern_lower = pattern.lower()
142
 
143
  if ("2-lines-top" in pattern_lower) or ("2 lines - top" in pattern_lower):
@@ -166,45 +143,23 @@ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_o
166
  print(f"Detected pattern: {detected_pattern}")
167
  print(f"Text positions calculated: {positions}")
168
 
169
- # FIXED: Parse RGBA string from Gradio ColorPicker
170
  original_color = color
171
- final_color = (255, 255, 255) # Default white
172
 
173
  if isinstance(color, str):
174
  if color.startswith('rgba(') and color.endswith(')'):
175
- # Parse RGBA string: 'rgba(253.2203125, 3.331846217105286, 3.331846217105286, 1)'
176
  print(f"Parsing RGBA string: {color}")
177
  try:
178
- # Extract numbers from the string
179
- rgba_part = color[5:-1] # Remove 'rgba(' and ')'
180
  values = [float(x.strip()) for x in rgba_part.split(',')]
181
  if len(values) >= 3:
182
- # Convert to integers and take only RGB (ignore alpha)
183
  final_color = (int(round(values[0])), int(round(values[1])), int(round(values[2])))
184
  print(f"Successfully parsed RGBA {color} to RGB: {final_color}")
185
- else:
186
- print(f"Invalid RGBA format {color}, using white")
187
- final_color = (255, 255, 255)
188
  except (ValueError, IndexError) as e:
189
  print(f"Error parsing RGBA {color}: {e}, using white")
190
  final_color = (255, 255, 255)
191
- elif color.startswith('rgb(') and color.endswith(')'):
192
- # Parse RGB string: 'rgb(253, 3, 3)'
193
- print(f"Parsing RGB string: {color}")
194
- try:
195
- rgb_part = color[4:-1] # Remove 'rgb(' and ')'
196
- values = [float(x.strip()) for x in rgb_part.split(',')]
197
- if len(values) >= 3:
198
- final_color = (int(round(values[0])), int(round(values[1])), int(round(values[2])))
199
- print(f"Successfully parsed RGB {color} to RGB: {final_color}")
200
- else:
201
- print(f"Invalid RGB format {color}, using white")
202
- final_color = (255, 255, 255)
203
- except (ValueError, IndexError) as e:
204
- print(f"Error parsing RGB {color}: {e}, using white")
205
- final_color = (255, 255, 255)
206
  elif color.startswith('#'):
207
- # Parse hex color
208
  print(f"Converting hex color {color}")
209
  hex_color = color.lstrip('#')
210
  if len(hex_color) == 6:
@@ -214,47 +169,24 @@ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_o
214
  except ValueError as e:
215
  print(f"Invalid hex color {color}: {e}, using white")
216
  final_color = (255, 255, 255)
217
- elif len(hex_color) == 3:
218
- try:
219
- final_color = tuple(int(hex_color[i]*2, 16) for i in range(3))
220
- print(f"Successfully converted short hex {color} to RGB: {final_color}")
221
- except ValueError as e:
222
- print(f"Invalid short hex color {color}: {e}, using white")
223
- final_color = (255, 255, 255)
224
- else:
225
- print(f"Invalid hex color length {color}, using white")
226
- final_color = (255, 255, 255)
227
- else:
228
- # Try to use as named color
229
- print(f"Attempting to use named color: {color}")
230
- final_color = color # Let PIL try to handle it
231
- elif isinstance(color, (tuple, list)) and len(color) >= 3:
232
- final_color = tuple(int(round(x)) for x in color[:3])
233
- print(f"Using tuple/list color: {final_color}")
234
- else:
235
- print(f"Unrecognized color format: {color}, using white")
236
- final_color = (255, 255, 255)
237
 
238
  print(f"Final color for drawing: {final_color}")
239
 
240
- # Draw text - PROPERLY handle all lines
241
  lines = [line1, line2, line3]
242
  print(f"All input lines: {lines}")
243
 
244
- # Only use as many lines as we have positions, but process non-empty ones
245
  active_lines = []
246
  for i in range(len(positions)):
247
  if i < len(lines) and lines[i] and lines[i].strip():
248
  active_lines.append((positions[i], lines[i].strip()))
249
 
250
  print(f"Active lines to draw: {len(active_lines)} out of {len(positions)} positions")
251
- for i, (pos, text) in enumerate(active_lines):
252
- print(f" Line {i+1}: '{text}' at {pos}")
253
 
254
  text_drawn = False
255
 
256
  for i, ((x, y), txt) in enumerate(active_lines):
257
- print(f"Drawing line {i+1}: '{txt}' at position ({x}, {y})")
258
 
259
  # Optional outline/stroke
260
  if add_outline:
@@ -275,13 +207,11 @@ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_o
275
  draw.text((x - text_w//2 + dx, y - text_h//2 + dy), txt, fill=(0, 0, 0), font=font)
276
  except Exception:
277
  draw.text((x + dx, y + dy), txt, fill=(0, 0, 0), font=font)
278
- else:
279
- print("Skipping outline (disabled)")
280
 
281
  # Draw main text
282
  try:
283
  draw.text((x, y), txt, fill=final_color, font=font, anchor="mm")
284
- print(f"SUCCESS: Main text drawn with color {final_color} at ({x}, {y})")
285
  except TypeError:
286
  try:
287
  bbox = draw.textbbox((0, 0), txt, font=font)
@@ -290,10 +220,10 @@ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_o
290
  fallback_x = x - text_w//2
291
  fallback_y = y - text_h//2
292
  draw.text((fallback_x, fallback_y), txt, fill=final_color, font=font)
293
- print(f"SUCCESS: Fallback text drawn with color {final_color} at ({fallback_x}, {fallback_y})")
294
  except Exception:
295
  draw.text((x, y), txt, fill=final_color, font=font)
296
- print(f"SUCCESS: Basic text drawn with color {final_color} at ({x}, {y})")
297
 
298
  text_drawn = True
299
  print(f"Completed drawing line {i+1}")
@@ -303,7 +233,7 @@ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_o
303
  return img, "No text to add (all lines were empty)"
304
 
305
  print("=== Text drawing completed successfully ===")
306
- return image, f"✅ {len(active_lines)} lines added! Pattern: {detected_pattern}"
307
 
308
  except Exception as e:
309
  print(f"CRITICAL ERROR in add_text_to_image: {e}")
@@ -423,6 +353,81 @@ def update_pattern_template(selected_pattern):
423
  # Return default template
424
  return create_pattern_template("2-lines-top")
425
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  # Create the Gradio Interface
427
  with gr.Blocks(title="AI Image Generator & Text Overlay") as demo:
428
  gr.Markdown("# 🎨 AI Image Generator & Text Overlay")
@@ -565,6 +570,21 @@ with gr.Blocks(title="AI Image Generator & Text Overlay") as demo:
565
  label="Text Color",
566
  value="#FFFFFF"
567
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
 
569
  # Add outline option
570
  add_outline = gr.Checkbox(
@@ -591,7 +611,7 @@ with gr.Blocks(title="AI Image Generator & Text Overlay") as demo:
591
  # Add text to image
592
  add_text_btn.click(
593
  add_text_to_image,
594
- inputs=[current_image, pattern, line1_inp, line2_inp, line3_inp, font_size, color, add_outline],
595
  outputs=[text_image, status_message]
596
  )
597
 
 
80
  return image, f"Image uploaded with warning: {e}", image
81
 
82
  # Text Overlay Functions
83
+ def add_text_to_image(img, pattern, line1, line2, line3, font_size, color, add_outline, font_name):
84
  """Overlay 2- or 3-line text on the image in a preset layout."""
85
  print("=== DEBUG: add_text_to_image called ===")
86
  print(f"Input image: {type(img)}")
87
  print(f"Pattern: {pattern}")
88
  print(f"Lines: [{line1!r}, {line2!r}, {line3!r}]")
89
  print(f"Font size: {font_size}")
90
+ print(f"Font name: {font_name}")
91
  print(f"Color: {color!r}")
92
  print(f"Add outline: {add_outline}")
93
 
 
103
  draw = ImageDraw.Draw(image)
104
  print("ImageDraw created successfully")
105
 
106
+ # UPDATED: Use selected font instead of hardcoded paths
107
+ font = get_font_path(font_name, font_size)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  if font is None:
109
+ return None, f"Could not load font: {font_name}"
 
 
 
 
 
110
 
111
  w, h = image.size
112
  print(f"Image dimensions: {w}x{h}")
113
 
114
+ # Pattern matching (same as before)
115
  positions = []
116
  detected_pattern = "unknown"
117
 
 
118
  pattern_lower = pattern.lower()
119
 
120
  if ("2-lines-top" in pattern_lower) or ("2 lines - top" in pattern_lower):
 
143
  print(f"Detected pattern: {detected_pattern}")
144
  print(f"Text positions calculated: {positions}")
145
 
146
+ # Color parsing (same as before)
147
  original_color = color
148
+ final_color = (255, 255, 255)
149
 
150
  if isinstance(color, str):
151
  if color.startswith('rgba(') and color.endswith(')'):
 
152
  print(f"Parsing RGBA string: {color}")
153
  try:
154
+ rgba_part = color[5:-1]
 
155
  values = [float(x.strip()) for x in rgba_part.split(',')]
156
  if len(values) >= 3:
 
157
  final_color = (int(round(values[0])), int(round(values[1])), int(round(values[2])))
158
  print(f"Successfully parsed RGBA {color} to RGB: {final_color}")
 
 
 
159
  except (ValueError, IndexError) as e:
160
  print(f"Error parsing RGBA {color}: {e}, using white")
161
  final_color = (255, 255, 255)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  elif color.startswith('#'):
 
163
  print(f"Converting hex color {color}")
164
  hex_color = color.lstrip('#')
165
  if len(hex_color) == 6:
 
169
  except ValueError as e:
170
  print(f"Invalid hex color {color}: {e}, using white")
171
  final_color = (255, 255, 255)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  print(f"Final color for drawing: {final_color}")
174
 
175
+ # Draw text (same logic as before)
176
  lines = [line1, line2, line3]
177
  print(f"All input lines: {lines}")
178
 
 
179
  active_lines = []
180
  for i in range(len(positions)):
181
  if i < len(lines) and lines[i] and lines[i].strip():
182
  active_lines.append((positions[i], lines[i].strip()))
183
 
184
  print(f"Active lines to draw: {len(active_lines)} out of {len(positions)} positions")
 
 
185
 
186
  text_drawn = False
187
 
188
  for i, ((x, y), txt) in enumerate(active_lines):
189
+ print(f"Drawing line {i+1}: '{txt}' at position ({x}, {y}) with font {font_name}")
190
 
191
  # Optional outline/stroke
192
  if add_outline:
 
207
  draw.text((x - text_w//2 + dx, y - text_h//2 + dy), txt, fill=(0, 0, 0), font=font)
208
  except Exception:
209
  draw.text((x + dx, y + dy), txt, fill=(0, 0, 0), font=font)
 
 
210
 
211
  # Draw main text
212
  try:
213
  draw.text((x, y), txt, fill=final_color, font=font, anchor="mm")
214
+ print(f"SUCCESS: Main text drawn with {font_name} at ({x}, {y})")
215
  except TypeError:
216
  try:
217
  bbox = draw.textbbox((0, 0), txt, font=font)
 
220
  fallback_x = x - text_w//2
221
  fallback_y = y - text_h//2
222
  draw.text((fallback_x, fallback_y), txt, fill=final_color, font=font)
223
+ print(f"SUCCESS: Fallback text drawn with {font_name} at ({fallback_x}, {fallback_y})")
224
  except Exception:
225
  draw.text((x, y), txt, fill=final_color, font=font)
226
+ print(f"SUCCESS: Basic text drawn with {font_name} at ({x}, {y})")
227
 
228
  text_drawn = True
229
  print(f"Completed drawing line {i+1}")
 
233
  return img, "No text to add (all lines were empty)"
234
 
235
  print("=== Text drawing completed successfully ===")
236
+ return image, f"✅ {len(active_lines)} lines added! Font: {font_name}, Pattern: {detected_pattern}"
237
 
238
  except Exception as e:
239
  print(f"CRITICAL ERROR in add_text_to_image: {e}")
 
353
  # Return default template
354
  return create_pattern_template("2-lines-top")
355
 
356
+ # Add this function to get available fonts with fallbacks
357
+ def get_font_path(font_name, font_size):
358
+ """Get font path for the selected font with fallbacks"""
359
+ font_mappings = {
360
+ "Impact": [
361
+ "impact.ttf",
362
+ "Impact.ttf",
363
+ "/System/Library/Fonts/Impact.ttf",
364
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf"
365
+ ],
366
+ "Arial Bold": [
367
+ "arial-bold.ttf",
368
+ "arialbd.ttf",
369
+ "Arial-Bold.ttf",
370
+ "/System/Library/Fonts/Arial Bold.ttf",
371
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
372
+ ],
373
+ "Times Bold": [
374
+ "times-bold.ttf",
375
+ "timesbd.ttf",
376
+ "Times-Bold.ttf",
377
+ "/System/Library/Fonts/Times Bold.ttf",
378
+ "/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf"
379
+ ],
380
+ "Comic Sans": [
381
+ "comic.ttf",
382
+ "COMIC.TTF",
383
+ "Comic Sans MS.ttf",
384
+ "/System/Library/Fonts/Comic Sans MS.ttf",
385
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
386
+ ],
387
+ "Bebas Neue": [
388
+ "bebas-neue.ttf",
389
+ "BebasNeue-Regular.ttf",
390
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" # fallback
391
+ ]
392
+ }
393
+
394
+ # Get font paths for the selected font
395
+ font_paths = font_mappings.get(font_name, [])
396
+
397
+ # Try each path
398
+ for font_path in font_paths:
399
+ try:
400
+ font = ImageFont.truetype(font_path, font_size)
401
+ print(f"SUCCESS: Loaded {font_name} from {font_path}")
402
+ return font
403
+ except (OSError, IOError):
404
+ continue
405
+
406
+ # If no specific font found, try default system fonts
407
+ default_paths = [
408
+ "DejaVuSans-Bold.ttf",
409
+ "arial.ttf",
410
+ "/System/Library/Fonts/Arial.ttf",
411
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
412
+ ]
413
+
414
+ for font_path in default_paths:
415
+ try:
416
+ font = ImageFont.truetype(font_path, font_size)
417
+ print(f"FALLBACK: Loaded default font from {font_path} for {font_name}")
418
+ return font
419
+ except (OSError, IOError):
420
+ continue
421
+
422
+ # Final fallback
423
+ try:
424
+ font = ImageFont.load_default()
425
+ print(f"FINAL FALLBACK: Using system default for {font_name}")
426
+ return font
427
+ except Exception:
428
+ print(f"ERROR: Could not load any font for {font_name}")
429
+ return None
430
+
431
  # Create the Gradio Interface
432
  with gr.Blocks(title="AI Image Generator & Text Overlay") as demo:
433
  gr.Markdown("# 🎨 AI Image Generator & Text Overlay")
 
570
  label="Text Color",
571
  value="#FFFFFF"
572
  )
573
+
574
+ # NEW: Font selector
575
+ with gr.Row():
576
+ font_selector = gr.Dropdown(
577
+ choices=[
578
+ "Impact", # Bold, condensed - great for memes
579
+ "Arial Bold", # Clean, readable
580
+ "Times Bold", # Classic, serif
581
+ "Comic Sans", # Casual, friendly
582
+ "Bebas Neue" # Modern, tall
583
+ ],
584
+ value="Impact",
585
+ label="Font Style",
586
+ info="Choose font for text overlay"
587
+ )
588
 
589
  # Add outline option
590
  add_outline = gr.Checkbox(
 
611
  # Add text to image
612
  add_text_btn.click(
613
  add_text_to_image,
614
+ inputs=[current_image, pattern, line1_inp, line2_inp, line3_inp, font_size, color, add_outline, font_selector],
615
  outputs=[text_image, status_message]
616
  )
617