All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
Merges full history from art-dag/mono.git into the monorepo under the artdag/ directory. Contains: core (DAG engine), l1 (Celery rendering server), l2 (ActivityPub registry), common (shared templates/middleware), client (CLI), test (e2e). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> git-subtree-dir: artdag git-subtree-mainline:1a179de547git-subtree-split:4c2e716558
184 lines
6.6 KiB
Python
184 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Explore PIL text options and test if we can match them.
|
|
"""
|
|
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
def load_font(font_name=None, font_size=32):
|
|
"""Load a font."""
|
|
candidates = [
|
|
font_name,
|
|
'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
|
|
'/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
|
|
'/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf',
|
|
]
|
|
for path in candidates:
|
|
if path is None:
|
|
continue
|
|
try:
|
|
return ImageFont.truetype(path, font_size)
|
|
except (IOError, OSError):
|
|
continue
|
|
return ImageFont.load_default()
|
|
|
|
|
|
def test_pil_options():
|
|
"""Test various PIL text options."""
|
|
|
|
# Create a test frame
|
|
frame_size = (600, 400)
|
|
|
|
font = load_font(None, 36)
|
|
font_bold = load_font('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 36)
|
|
font_italic = load_font('/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf', 36)
|
|
|
|
tests = []
|
|
|
|
# Test 1: Basic text
|
|
def basic_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Basic Text", fill=(255, 255, 255, 255), font=font)
|
|
return img, "basic"
|
|
tests.append(basic_text)
|
|
|
|
# Test 2: Stroke/outline
|
|
def stroke_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Stroke Text", fill=(255, 255, 255, 255), font=font,
|
|
stroke_width=2, stroke_fill=(255, 0, 0, 255))
|
|
return img, "stroke"
|
|
tests.append(stroke_text)
|
|
|
|
# Test 3: Bold font
|
|
def bold_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Bold Text", fill=(255, 255, 255, 255), font=font_bold)
|
|
return img, "bold"
|
|
tests.append(bold_text)
|
|
|
|
# Test 4: Italic font
|
|
def italic_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Italic Text", fill=(255, 255, 255, 255), font=font_italic)
|
|
return img, "italic"
|
|
tests.append(italic_text)
|
|
|
|
# Test 5: Different anchors
|
|
def anchor_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
# Draw crosshairs at anchor points
|
|
for x in [100, 300, 500]:
|
|
draw.line([(x-10, 50), (x+10, 50)], fill=(100, 100, 100, 255))
|
|
draw.line([(x, 40), (x, 60)], fill=(100, 100, 100, 255))
|
|
|
|
draw.text((100, 50), "Left", fill=(255, 255, 255, 255), font=font, anchor="lm")
|
|
draw.text((300, 50), "Center", fill=(255, 255, 255, 255), font=font, anchor="mm")
|
|
draw.text((500, 50), "Right", fill=(255, 255, 255, 255), font=font, anchor="rm")
|
|
return img, "anchor"
|
|
tests.append(anchor_text)
|
|
|
|
# Test 6: Multiline text
|
|
def multiline_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.multiline_text((20, 20), "Line One\nLine Two\nLine Three",
|
|
fill=(255, 255, 255, 255), font=font, spacing=10)
|
|
return img, "multiline"
|
|
tests.append(multiline_text)
|
|
|
|
# Test 7: Semi-transparent text
|
|
def alpha_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Alpha 100%", fill=(255, 255, 255, 255), font=font)
|
|
draw.text((20, 60), "Alpha 50%", fill=(255, 255, 255, 128), font=font)
|
|
draw.text((20, 100), "Alpha 25%", fill=(255, 255, 255, 64), font=font)
|
|
return img, "alpha"
|
|
tests.append(alpha_text)
|
|
|
|
# Test 8: Colored text
|
|
def colored_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Red", fill=(255, 0, 0, 255), font=font)
|
|
draw.text((20, 60), "Green", fill=(0, 255, 0, 255), font=font)
|
|
draw.text((20, 100), "Blue", fill=(0, 0, 255, 255), font=font)
|
|
draw.text((20, 140), "Yellow", fill=(255, 255, 0, 255), font=font)
|
|
return img, "colored"
|
|
tests.append(colored_text)
|
|
|
|
# Test 9: Large stroke
|
|
def large_stroke():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
draw.text((20, 20), "Big Stroke", fill=(255, 255, 255, 255), font=font,
|
|
stroke_width=5, stroke_fill=(0, 0, 0, 255))
|
|
return img, "large_stroke"
|
|
tests.append(large_stroke)
|
|
|
|
# Test 10: Emoji (if supported)
|
|
def emoji_text():
|
|
img = Image.new('RGBA', frame_size, (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
try:
|
|
# Try to find an emoji font
|
|
emoji_font = None
|
|
emoji_paths = [
|
|
'/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf',
|
|
'/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf',
|
|
]
|
|
for p in emoji_paths:
|
|
try:
|
|
emoji_font = ImageFont.truetype(p, 36)
|
|
break
|
|
except:
|
|
pass
|
|
if emoji_font:
|
|
draw.text((20, 20), "Hello 🎵 World 🎸", fill=(255, 255, 255, 255), font=emoji_font)
|
|
else:
|
|
draw.text((20, 20), "No emoji font found", fill=(255, 255, 255, 255), font=font)
|
|
except Exception as e:
|
|
draw.text((20, 20), f"Emoji error: {e}", fill=(255, 255, 255, 255), font=font)
|
|
return img, "emoji"
|
|
tests.append(emoji_text)
|
|
|
|
# Run all tests
|
|
print("PIL Text Options Test")
|
|
print("=" * 60)
|
|
|
|
for test_fn in tests:
|
|
img, name = test_fn()
|
|
fname = f"/tmp/pil_test_{name}.png"
|
|
img.save(fname)
|
|
print(f"Saved: {fname}")
|
|
|
|
print("\nCheck /tmp/pil_test_*.png for results")
|
|
|
|
# Print available parameters
|
|
print("\n" + "=" * 60)
|
|
print("PIL draw.text() parameters:")
|
|
print(" - xy: position tuple")
|
|
print(" - text: string to draw")
|
|
print(" - fill: color (R,G,B) or (R,G,B,A)")
|
|
print(" - font: ImageFont object")
|
|
print(" - anchor: 2-char code (la=left-ascender, mm=middle-middle, etc.)")
|
|
print(" - spacing: line spacing for multiline")
|
|
print(" - align: 'left', 'center', 'right' for multiline")
|
|
print(" - direction: 'rtl', 'ltr', 'ttb' (requires libraqm)")
|
|
print(" - features: OpenType features list")
|
|
print(" - language: language code for shaping")
|
|
print(" - stroke_width: outline width in pixels")
|
|
print(" - stroke_fill: outline color")
|
|
print(" - embedded_color: use embedded color glyphs (emoji)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
test_pil_options()
|