TextToImage / app.py
ArrcttacsrjksX's picture
Update app.py
30fb75c verified
import gradio as gr
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
import textwrap
import os
import matplotlib
import math
def get_system_fonts():
fonts = []
for font in matplotlib.font_manager.findSystemFonts(fontpaths=None, fontext='ttf'):
font_name = os.path.basename(font)
fonts.append(font_name)
return sorted(set(fonts))
def parse_color(color):
if isinstance(color, str) and color.startswith('rgba'):
color = color.replace('rgba', '').strip('()').split(',')
return tuple(int(float(c.strip())) for c in color[:3])
return color
def calculate_text_dimensions(text, font, max_width, margin):
lines = []
for line in text.split('\n'):
lines.extend(textwrap.wrap(line, width=int(max_width / font.size * 1.8)))
bbox = font.getbbox('Ay')
line_height = bbox[3] - bbox[1]
total_height = line_height * len(lines)
return lines, line_height, total_height
def create_text_segment(lines, start_idx, max_lines, width, height, bg_color, text_color, font, align, margin):
img = Image.new("RGB", (width, height), color=bg_color)
draw = ImageDraw.Draw(img)
bbox = font.getbbox('Ay')
line_height = bbox[3] - bbox[1]
y = margin
end_idx = min(start_idx + max_lines, len(lines))
segment_lines = lines[start_idx:end_idx]
for line in segment_lines:
bbox = font.getbbox(line)
line_width = bbox[2] - bbox[0]
if align == 'Left':
x = margin
elif align == 'Center':
x = (width - line_width) // 2
else: # Right alignment
x = width - line_width - margin
draw.text((x, y), line, fill=text_color, font=font)
y += line_height
return img, end_idx
def render_plain_text_image(text, font_size, width, height, bg_color, text_color, font_name, align):
bg_color = parse_color(bg_color)
text_color = parse_color(text_color)
margin = 10
try:
font_path = matplotlib.font_manager.findfont(font_name)
font = ImageFont.truetype(font_path, font_size)
except Exception:
font = ImageFont.load_default()
# Calculate dimensions
max_width = width - 2 * margin
lines, line_height, total_text_height = calculate_text_dimensions(text, font, max_width, margin)
# Calculate how many lines can fit in one segment
max_lines_per_segment = (height - 2 * margin) // line_height
# Calculate number of segments needed
num_segments = math.ceil(len(lines) / max_lines_per_segment)
# Create segments
segments = []
current_line = 0
for i in range(num_segments):
segment_img, current_line = create_text_segment(
lines, current_line, max_lines_per_segment,
width, height, bg_color, text_color, font, align, margin
)
segments.append(segment_img)
# Combine all segments vertically
total_height = len(segments) * height
final_image = Image.new("RGB", (width, total_height), color=bg_color)
for i, segment in enumerate(segments):
final_image.paste(segment, (0, i * height))
return final_image
def render_math_image(text, font_size, width, height, bg_color, text_color):
bg_color = parse_color(bg_color)
text_color = parse_color(text_color)
fig, ax = plt.subplots(figsize=(width / 100, height / 100), facecolor=bg_color)
ax.set_facecolor(bg_color)
ax.axis('off')
if not (text.startswith(r"$") and text.endswith(r"$")):
text = rf"${text}$"
ax.text(0.5, 0.5, text, fontsize=font_size, ha='center', va='center', color=text_color)
buf = BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0)
plt.close(fig)
buf.seek(0)
img = Image.open(buf)
return img
def text_to_image(input_text, font_size, width, height, bg_color, text_color, mode, font_name, align, image_format):
if mode == "Plain Text":
img = render_plain_text_image(input_text, font_size, width, height, bg_color, text_color, font_name, align)
elif mode == "LaTeX Math":
img = render_math_image(input_text, font_size, width, height, bg_color, text_color)
else:
return "Invalid mode selected!"
return img
def handle_file_upload(file, font_size, width, height, bg_color, text_color, mode, font_name, align, image_format):
if file is not None:
file_path = file[0]
with open(file_path, "r", encoding="utf-8") as f:
text = f.read()
return text_to_image(text, font_size, width, height, bg_color, text_color, mode, font_name, align, image_format)
return "No file uploaded!"
font_list = get_system_fonts()
default_font = "DejaVuSans.ttf" if "DejaVuSans.ttf" in font_list else font_list[0]
with gr.Blocks() as demo:
gr.Markdown("# 🖼️ Text to Image Converter")
with gr.Row():
input_text = gr.Textbox(label="Enter Text", placeholder="Type or paste text here...", lines=5)
file_input = gr.File(label="Upload a Text File", type="filepath")
with gr.Row():
font_size = gr.Slider(10, 100, value=30, label="Font Size")
font_name = gr.Dropdown(choices=font_list, value=default_font, label="Font")
align = gr.Radio(["Left", "Center", "Right"], label="Text Alignment", value="Center")
with gr.Row():
width = gr.Slider(200, 2000, value=800, label="Image Width")
height = gr.Slider(200, 2000, value=600, label="Base Height")
with gr.Row():
bg_color = gr.ColorPicker(label="Background Color", value="#FFFFFF")
text_color = gr.ColorPicker(label="Text Color", value="#000000")
with gr.Row():
mode = gr.Radio(["Plain Text", "LaTeX Math"], label="Rendering Mode", value="Plain Text")
image_format = gr.Radio(["PNG", "JPEG"], label="Image Format", value="PNG")
output_image = gr.Image(label="Generated Image")
with gr.Row():
convert_button = gr.Button("Convert Text to Image")
file_convert_button = gr.Button("Convert File to Image")
convert_button.click(
text_to_image,
inputs=[
input_text, font_size, width, height, bg_color, text_color,
mode, font_name, align, image_format
],
outputs=output_image
)
file_convert_button.click(
handle_file_upload,
inputs=[
file_input, font_size, width, height, bg_color, text_color,
mode, font_name, align, image_format
],
outputs=output_image
)
demo.launch()