VirtualOasis commited on
Commit
12f8f41
·
verified ·
1 Parent(s): efe0312

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -118
app.py CHANGED
@@ -1,6 +1,8 @@
1
  import gradio as gr
2
  import os
3
  import tempfile
 
 
4
  from PIL import Image, ImageDraw, ImageFont
5
 
6
  def draw_horizontal_lines(draw, width, height, spacing=60, color=(230, 230, 230)):
@@ -35,131 +37,201 @@ def text_to_images_mcp(text_content, style='lines', font_path=None):
35
  Returns:
36
  list: List of PIL Image objects for display in Gradio.
37
  """
38
- if not text_content.strip():
39
- return []
40
-
41
- # --- Configuration ---
42
- IMG_WIDTH = 1080
43
- IMG_HEIGHT = 1080
44
- BACKGROUND_COLOR = (255, 255, 255)
45
- TEXT_COLOR = (10, 10, 10)
46
- STYLE_COLOR = (225, 225, 225)
47
-
48
- PADDING_X = 80
49
- PADDING_Y = 80
50
- FONT_SIZE = 48
51
- LINE_SPACING = 20
52
-
53
- # --- Font Loading ---
54
- font = None
55
  try:
56
- if font_path and os.path.exists(font_path):
57
- font = ImageFont.truetype(font_path, FONT_SIZE)
58
- else:
59
- font_paths_to_try = [
60
- "Arial.ttf", "arial.ttf",
61
- "/System/Library/Fonts/Supplemental/Arial.ttf",
62
- "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
63
- ]
64
- for f_path in font_paths_to_try:
65
- try:
66
- font = ImageFont.truetype(f_path, FONT_SIZE)
67
- break
68
- except IOError:
69
- continue
70
- if not font:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  font = ImageFont.load_default()
72
- except Exception as e:
73
- font = ImageFont.load_default()
74
-
75
- # --- Text Wrapping Logic ---
76
- drawable_width = IMG_WIDTH - 2 * PADDING_X
77
- paragraphs = [p.strip() for p in text_content.strip().split('\n') if p.strip()]
78
-
79
- if not paragraphs:
80
- return []
81
-
82
- all_lines_and_breaks = []
83
- for i, paragraph in enumerate(paragraphs):
84
- words = paragraph.split()
85
- current_line = ""
86
- for word in words:
87
- if font.getlength(word) > drawable_width:
88
- temp_word = ""
89
- for char in word:
90
- if font.getlength(temp_word + char) > drawable_width:
91
- all_lines_and_breaks.append(temp_word)
92
- temp_word = char
 
 
 
 
 
 
 
 
93
  else:
94
- temp_word += char
95
- word = temp_word
96
- if font.getlength(current_line + " " + word) <= drawable_width:
97
- current_line += " " + word
98
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  all_lines_and_breaks.append(current_line.strip())
100
- current_line = word
101
- all_lines_and_breaks.append(current_line.strip())
102
- if i < len(paragraphs) - 1:
103
- all_lines_and_breaks.append(None)
104
-
105
- # --- Image Generation ---
106
- img_count = 0
107
- page_content = []
108
- y_text = PADDING_Y
109
-
110
- try:
111
- line_height = font.getbbox("A")[3] - font.getbbox("A")[1]
112
- except AttributeError:
113
- line_height = 12
114
-
115
- PARAGRAPH_SPACING = line_height
116
- generated_images = []
117
-
118
- def create_image_page(content, page_num):
119
- """Helper function to create and return a single image page."""
120
- img = Image.new('RGB', (IMG_WIDTH, IMG_HEIGHT), color=BACKGROUND_COLOR)
121
- draw = ImageDraw.Draw(img)
122
-
123
- # Draw the selected background style first
124
- if style == 'lines':
125
- line_style_spacing = line_height + LINE_SPACING
126
- draw_horizontal_lines(draw, IMG_WIDTH, IMG_HEIGHT, spacing=line_style_spacing, color=STYLE_COLOR)
127
- elif style == 'dots':
128
- draw_dot_grid(draw, IMG_WIDTH, IMG_HEIGHT, color=STYLE_COLOR)
129
- elif style == 'grid':
130
- draw_lattice_grid(draw, IMG_WIDTH, IMG_HEIGHT, color=STYLE_COLOR)
131
-
132
- # Draw the text on top of the background
133
- current_y = PADDING_Y
134
- for page_item in content:
135
- if page_item is not None:
136
- draw.text((PADDING_X, current_y), page_item, font=font, fill=TEXT_COLOR)
137
- current_y += line_height + LINE_SPACING
138
  else:
139
- current_y += PARAGRAPH_SPACING
 
 
140
 
141
- generated_images.append(img)
142
- return img
143
 
144
- for item in all_lines_and_breaks:
145
- is_break = item is None
146
- item_height = PARAGRAPH_SPACING if is_break else line_height
147
-
148
- if y_text + item_height > IMG_HEIGHT - PADDING_Y:
149
- img_count += 1
150
- create_image_page(page_content, img_count)
151
 
152
- page_content = [item]
153
- y_text = PADDING_Y + item_height + (0 if is_break else LINE_SPACING)
154
- else:
155
- page_content.append(item)
156
- y_text += item_height + (0 if is_break else LINE_SPACING)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
- if page_content:
159
- img_count += 1
160
- create_image_page(page_content, img_count)
161
 
162
- return generated_images
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  # Sample text for demonstration
165
  sample_text = """In the heart of a bustling city, there lived a clockmaker named Alistair. His shop, a quaint corner of tranquility amidst the urban chaos, was filled with the gentle ticking of countless timepieces. Each clock was a masterpiece, a testament to his dedication and skill. But Alistair held a secret. One of his clocks, an old grandfather clock in the corner, did not just tell time. It told stories.
@@ -176,7 +248,7 @@ demo = gr.Interface(
176
  lines=10,
177
  max_lines=20,
178
  label="Text Content",
179
- placeholder="Enter your long-form text here..."
180
  ),
181
  gr.Dropdown(
182
  choices=['plain', 'lines', 'dots', 'grid'],
@@ -193,7 +265,7 @@ demo = gr.Interface(
193
  ],
194
  outputs=[
195
  gr.Gallery(
196
- label="Generated Images",
197
  show_label=True,
198
  elem_id="gallery",
199
  columns=2,
@@ -204,7 +276,7 @@ demo = gr.Interface(
204
  )
205
  ],
206
  title="📖 Text to Images Converter",
207
- description="Transform long-form text into a series of attractive, readable images. Simply paste your text, choose a background style, and preview the generated images. You can download individual images by clicking on them.",
208
  examples=[
209
  [sample_text, 'lines', ''],
210
  [sample_text, 'dots', ''],
 
1
  import gradio as gr
2
  import os
3
  import tempfile
4
+ import traceback
5
+ import gc
6
  from PIL import Image, ImageDraw, ImageFont
7
 
8
  def draw_horizontal_lines(draw, width, height, spacing=60, color=(230, 230, 230)):
 
37
  Returns:
38
  list: List of PIL Image objects for display in Gradio.
39
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  try:
41
+ # Input validation
42
+ if not text_content or not text_content.strip():
43
+ return []
44
+
45
+ # Limit text length to prevent memory issues
46
+ if len(text_content) > 10000: # 10k character limit
47
+ text_content = text_content[:10000] + "\n\n[Text truncated due to length limit]"
48
+
49
+ # --- Configuration ---
50
+ IMG_WIDTH = 1080
51
+ IMG_HEIGHT = 1080
52
+ BACKGROUND_COLOR = (255, 255, 255)
53
+ TEXT_COLOR = (10, 10, 10)
54
+ STYLE_COLOR = (225, 225, 225)
55
+
56
+ PADDING_X = 80
57
+ PADDING_Y = 80
58
+ FONT_SIZE = 48
59
+ LINE_SPACING = 20
60
+
61
+ # Limit maximum number of pages to prevent memory issues
62
+ MAX_PAGES = 10
63
+
64
+ # --- Font Loading ---
65
+ font = None
66
+ try:
67
+ if font_path and os.path.exists(font_path):
68
+ font = ImageFont.truetype(font_path, FONT_SIZE)
69
+ else:
70
+ font_paths_to_try = [
71
+ "Arial.ttf", "arial.ttf",
72
+ "/System/Library/Fonts/Supplemental/Arial.ttf",
73
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
74
+ ]
75
+ for f_path in font_paths_to_try:
76
+ try:
77
+ font = ImageFont.truetype(f_path, FONT_SIZE)
78
+ break
79
+ except (IOError, OSError):
80
+ continue
81
+ if not font:
82
+ font = ImageFont.load_default()
83
+ except Exception as e:
84
+ print(f"Font loading error: {e}")
85
  font = ImageFont.load_default()
86
+
87
+ # --- Text Wrapping Logic ---
88
+ drawable_width = IMG_WIDTH - 2 * PADDING_X
89
+ paragraphs = [p.strip() for p in text_content.strip().split('\n') if p.strip()]
90
+
91
+ if not paragraphs:
92
+ return []
93
+
94
+ all_lines_and_breaks = []
95
+ for i, paragraph in enumerate(paragraphs):
96
+ words = paragraph.split()
97
+ current_line = ""
98
+ for word in words:
99
+ try:
100
+ # Handle very long words
101
+ if hasattr(font, 'getlength') and font.getlength(word) > drawable_width:
102
+ temp_word = ""
103
+ for char in word:
104
+ if font.getlength(temp_word + char) > drawable_width:
105
+ all_lines_and_breaks.append(temp_word)
106
+ temp_word = char
107
+ else:
108
+ temp_word += char
109
+ word = temp_word
110
+
111
+ # Check line length
112
+ test_line = current_line + " " + word if current_line else word
113
+ if hasattr(font, 'getlength'):
114
+ line_width = font.getlength(test_line)
115
  else:
116
+ line_width = len(test_line) * 12 # Rough estimate
117
+
118
+ if line_width <= drawable_width:
119
+ current_line = test_line
120
+ else:
121
+ if current_line:
122
+ all_lines_and_breaks.append(current_line.strip())
123
+ current_line = word
124
+ except Exception as e:
125
+ print(f"Text wrapping error: {e}")
126
+ # Fallback to simple character limit
127
+ if len(current_line + " " + word) <= 80: # Rough character limit
128
+ current_line += " " + word
129
+ else:
130
+ all_lines_and_breaks.append(current_line.strip())
131
+ current_line = word
132
+
133
+ if current_line:
134
  all_lines_and_breaks.append(current_line.strip())
135
+ if i < len(paragraphs) - 1:
136
+ all_lines_and_breaks.append(None) # Paragraph break
137
+
138
+ # --- Image Generation ---
139
+ img_count = 0
140
+ page_content = []
141
+ y_text = PADDING_Y
142
+
143
+ try:
144
+ if hasattr(font, 'getbbox'):
145
+ line_height = font.getbbox("A")[3] - font.getbbox("A")[1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  else:
147
+ line_height = FONT_SIZE
148
+ except Exception:
149
+ line_height = FONT_SIZE
150
 
151
+ PARAGRAPH_SPACING = line_height
152
+ generated_images = []
153
 
154
+ def create_image_page(content, page_num):
155
+ """Helper function to create and return a single image page."""
156
+ try:
157
+ img = Image.new('RGB', (IMG_WIDTH, IMG_HEIGHT), color=BACKGROUND_COLOR)
158
+ draw = ImageDraw.Draw(img)
 
 
159
 
160
+ # Draw the selected background style first
161
+ if style == 'lines':
162
+ line_style_spacing = line_height + LINE_SPACING
163
+ draw_horizontal_lines(draw, IMG_WIDTH, IMG_HEIGHT, spacing=line_style_spacing, color=STYLE_COLOR)
164
+ elif style == 'dots':
165
+ draw_dot_grid(draw, IMG_WIDTH, IMG_HEIGHT, color=STYLE_COLOR)
166
+ elif style == 'grid':
167
+ draw_lattice_grid(draw, IMG_WIDTH, IMG_HEIGHT, color=STYLE_COLOR)
168
+
169
+ # Draw the text on top of the background
170
+ current_y = PADDING_Y
171
+ for page_item in content:
172
+ if page_item is not None:
173
+ try:
174
+ draw.text((PADDING_X, current_y), page_item, font=font, fill=TEXT_COLOR)
175
+ current_y += line_height + LINE_SPACING
176
+ except Exception as e:
177
+ print(f"Text drawing error: {e}")
178
+ current_y += line_height + LINE_SPACING
179
+ else:
180
+ current_y += PARAGRAPH_SPACING
181
+
182
+ # Optimize image for web delivery
183
+ img = img.convert('RGB')
184
+ generated_images.append(img)
185
+ return img
186
+ except Exception as e:
187
+ print(f"Image creation error: {e}")
188
+ # Return a simple error image
189
+ error_img = Image.new('RGB', (IMG_WIDTH, IMG_HEIGHT), color=(255, 255, 255))
190
+ error_draw = ImageDraw.Draw(error_img)
191
+ try:
192
+ error_draw.text((PADDING_X, PADDING_Y), f"Error creating page {page_num}", font=font, fill=(255, 0, 0))
193
+ except:
194
+ pass
195
+ return error_img
196
+
197
+ # Process content with page limit
198
+ for item in all_lines_and_breaks:
199
+ if img_count >= MAX_PAGES:
200
+ break
201
+
202
+ is_break = item is None
203
+ item_height = PARAGRAPH_SPACING if is_break else line_height
204
 
205
+ if y_text + item_height > IMG_HEIGHT - PADDING_Y:
206
+ img_count += 1
207
+ create_image_page(page_content, img_count)
208
 
209
+ page_content = [item]
210
+ y_text = PADDING_Y + item_height + (0 if is_break else LINE_SPACING)
211
+ else:
212
+ page_content.append(item)
213
+ y_text += item_height + (0 if is_break else LINE_SPACING)
214
+
215
+ if page_content and img_count < MAX_PAGES:
216
+ img_count += 1
217
+ create_image_page(page_content, img_count)
218
+
219
+ # Memory cleanup
220
+ gc.collect()
221
+
222
+ return generated_images[:MAX_PAGES] # Ensure we don't exceed limit
223
+
224
+ except Exception as e:
225
+ print(f"Critical error in text_to_images_mcp: {e}")
226
+ print(traceback.format_exc())
227
+ # Return a simple error image
228
+ try:
229
+ error_img = Image.new('RGB', (1080, 1080), color=(255, 255, 255))
230
+ error_draw = ImageDraw.Draw(error_img)
231
+ error_draw.text((80, 80), f"Error processing text: {str(e)[:100]}", fill=(255, 0, 0))
232
+ return [error_img]
233
+ except:
234
+ return []
235
 
236
  # Sample text for demonstration
237
  sample_text = """In the heart of a bustling city, there lived a clockmaker named Alistair. His shop, a quaint corner of tranquility amidst the urban chaos, was filled with the gentle ticking of countless timepieces. Each clock was a masterpiece, a testament to his dedication and skill. But Alistair held a secret. One of his clocks, an old grandfather clock in the corner, did not just tell time. It told stories.
 
248
  lines=10,
249
  max_lines=20,
250
  label="Text Content",
251
+ placeholder="Enter your long-form text here (max 10,000 characters)..."
252
  ),
253
  gr.Dropdown(
254
  choices=['plain', 'lines', 'dots', 'grid'],
 
265
  ],
266
  outputs=[
267
  gr.Gallery(
268
+ label="Generated Images (Max 10 pages)",
269
  show_label=True,
270
  elem_id="gallery",
271
  columns=2,
 
276
  )
277
  ],
278
  title="📖 Text to Images Converter",
279
+ description="Transform long-form text into a series of attractive, readable images. Simply paste your text, choose a background style, and preview the generated images. You can download individual images by clicking on them. Limited to 10,000 characters and 10 pages for optimal performance.",
280
  examples=[
281
  [sample_text, 'lines', ''],
282
  [sample_text, 'dots', ''],