Skip to content

Instantly share code, notes, and snippets.

@pleabargain
Created July 8, 2025 09:01
Show Gist options
  • Save pleabargain/fc2014ff73b7afa3e306f4b1a0013cc9 to your computer and use it in GitHub Desktop.
Save pleabargain/fc2014ff73b7afa3e306f4b1a0013cc9 to your computer and use it in GitHub Desktop.
python random font to image
from PIL import Image, ImageDraw, ImageFont
import os
import random
import argparse
import sys
from datetime import datetime
def get_system_fonts():
"""Finds all usable TrueType font paths on the system."""
font_paths = []
if os.name == 'nt': # Windows
font_dir = "C:\\Windows\\Fonts"
for item in os.listdir(font_dir):
if item.lower().endswith('.ttf'):
font_paths.append(os.path.join(font_dir, item))
elif os.name == 'posix': # Linux, macOS
font_dirs = [
"/usr/share/fonts/truetype/",
"/System/Library/Fonts/Supplemental/", # macOS
"/Library/Fonts/", # macOS
]
for font_dir in font_dirs:
if os.path.exists(font_dir):
for root, _, files in os.walk(font_dir):
for file in files:
if file.lower().endswith('.ttf'):
font_paths.append(os.path.join(root, file))
return font_paths
def wrap_text(text, font, max_width):
"""Wraps text to fit a specific pixel width."""
lines = []
words = text.split(' ')
current_line = ""
for word in words:
if font.getlength(current_line + " " + word) <= max_width:
current_line += " " + word
else:
lines.append(current_line.strip())
current_line = word
lines.append(current_line.strip())
return [line for line in lines if line]
def draw_random_text(draw, text, fonts, image_size, text_color):
"""Draws text with a random font for each character."""
if not fonts:
print("Warning: No system fonts found. Cannot use random font feature.")
# Fallback to a simple drawing method
font = ImageFont.load_default()
draw.text((10, 10), text, font=font, fill=text_color)
return
padding = image_size[0] * 0.1
max_width = image_size[0] - (padding * 2)
max_height = image_size[1] - (padding * 2)
# --- Simplified text layout for random fonts ---
font_size = 100 # Start with a reasonable size
# Simple line breaking
words = text.split()
lines = []
current_line = ""
for word in words:
# A proper width check is complex here, so we do a simpler split
if len(current_line) + len(word) < 30: # Character count based wrapping
current_line += word + " "
else:
lines.append(current_line.strip())
current_line = word + " "
lines.append(current_line.strip())
# --- Drawing Logic ---
y = (image_size[1] - (len(lines) * font_size)) / 2 # Center vertically
for line in lines:
# Estimate line width for centering
line_width = 0
for char in line:
try:
font = ImageFont.truetype(random.choice(fonts), font_size)
line_width += font.getlength(char)
except Exception:
line_width += font_size * 0.6 # Estimate for failed fonts
x = (image_size[0] - line_width) / 2
for char in line:
try:
font = ImageFont.truetype(random.choice(fonts), font_size)
draw.text((x, y), char, font=font, fill=text_color)
x += font.getlength(char)
except Exception as e:
# If a font fails, skip the character and move on
print(f"Skipping character due to font error: {e}")
x += font_size * 0.6 # Move cursor by an estimated width
y += font_size * 1.2 # Move to the next line
def generate_image_with_text(text, output_filename="output_image.png", image_size=(800, 800), text_color=(0, 0, 0), background_color=(255, 255, 255), randomize_fonts=False):
"""
Generates an image with text. Can use a single font or randomize fonts per character.
"""
img = Image.new("RGB", image_size, color=background_color)
draw = ImageDraw.Draw(img)
if randomize_fonts:
all_fonts = get_system_fonts()
draw_random_text(draw, text, all_fonts, image_size, text_color)
else:
# --- Original single-font logic ---
font_paths = get_system_fonts()
font_path = font_paths[0] if font_paths else None
if not font_path:
print("Warning: Could not find a scalable system font. Falling back to default PIL font.")
font = ImageFont.load_default()
padding = image_size[0] * 0.1 # 10% padding
max_width = image_size[0] - (padding * 2)
max_height = image_size[1] - (padding * 2)
font_size = 350
font = None
wrapped_lines = []
while font_size > 10:
if font_path:
font = ImageFont.truetype(font_path, font_size)
else:
font = ImageFont.load_default()
wrapped_lines = wrap_text(text, font, max_width)
wrapped_text = "\n".join(wrapped_lines)
text_bbox = draw.multiline_textbbox((0, 0), wrapped_text, font=font, align="center")
text_height = text_bbox[3] - text_bbox[1]
if text_height <= max_height:
break
font_size -= 5
if font_size <= 10:
print("Warning: Text might be too long to fit properly.")
final_bbox = draw.multiline_textbbox((0, 0), "\n".join(wrapped_lines), font=font, align="center")
text_block_width = final_bbox[2] - final_bbox[0]
text_block_height = final_bbox[3] - final_bbox[1]
x = (image_size[0] - text_block_width) / 2 - final_bbox[0]
y = (image_size[1] - text_block_height) / 2 - final_bbox[1]
draw.multiline_text((x, y), "\n".join(wrapped_lines), font=font, fill=text_color, align="center")
img.save(output_filename)
print(f"Image saved as {output_filename}")
if __name__ == "__main__":
# Simplified argument handling
if len(sys.argv) < 2:
user_text = input("Enter text for the image: ")
if not user_text:
user_text = "Hello, world!"
print("No text provided. Using default 'Hello, world!'")
randomize_fonts = "--random-fonts" in sys.argv
else:
user_text = sys.argv[1]
randomize_fonts = "--random-fonts" in sys.argv[2:]
base_filename = user_text[:20].replace(" ", "_")
safe_filename = "".join(c for c in base_filename if c.isalnum() or c in ('_', '-')).rstrip()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_filename = f"{safe_filename}_{timestamp}.png"
generate_image_with_text(user_text, output_filename=output_filename, randomize_fonts=randomize_fonts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment