Files
Tracker/Code/Tools/Samples/generators/image_email.py
T
2026-02-21 21:55:42 +00:00

141 lines
4.0 KiB
Python

"""Generate PNG: Email screenshot from client (Outlook-style)."""
from pathlib import Path
try:
from PIL import Image, ImageDraw, ImageFont
HAS_PIL = True
except ImportError:
HAS_PIL = False
def _get_font(size, bold=False):
"""Get a font with CJK support, falling back to default if unavailable."""
# Prefer WenQuanYi Micro Hei for CJK support
cjk_names = [
"/usr/share/fonts/wenquanyi/wqy-microhei/wqy-microhei.ttc",
]
latin_names = [
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
if bold
else "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
if bold
else "/usr/share/fonts/TTF/DejaVuSans.ttf",
]
# Try CJK font first (it includes Latin glyphs too)
for name in cjk_names:
try:
return ImageFont.truetype(name, size, index=0)
except (OSError, IOError):
continue
# Fall back to Latin fonts
for name in latin_names:
try:
return ImageFont.truetype(name, size)
except (OSError, IOError):
continue
return ImageFont.load_default()
def generate(
output_path: Path,
from_email: str,
from_name: str,
to_email: str,
to_name: str,
subject: str,
body: str,
date: str,
):
"""Generate an Outlook-style email screenshot as PNG."""
if not HAS_PIL:
# Fallback: write a text file
output_path.with_suffix(".txt").write_text(
f"From: {from_name} <{from_email}>\n"
f"To: {to_name} <{to_email}>\n"
f"Date: {date}\n"
f"Subject: {subject}\n\n{body}"
)
return
# Image dimensions (simulate a screen capture)
width, height = 800, 600
bg_color = (255, 255, 255)
header_bg = (242, 242, 242)
accent_color = (0, 120, 212) # Outlook blue
text_color = (51, 51, 51)
label_color = (130, 130, 130)
img = Image.new("RGB", (width, height), bg_color)
draw = ImageDraw.Draw(img)
font_header = _get_font(11, bold=True)
font_label = _get_font(10)
font_body = _get_font(11)
font_subject = _get_font(13, bold=True)
# Top bar (Outlook-style)
draw.rectangle([0, 0, width, 45], fill=accent_color)
draw.text((15, 12), "Mail - Outlook", fill=(255, 255, 255), font=font_header)
# Email header area
y = 55
draw.rectangle([0, 45, width, 200], fill=header_bg)
# Subject
draw.text((20, y), subject, fill=text_color, font=font_subject)
y += 30
# From
draw.text((20, y), "From:", fill=label_color, font=font_label)
draw.text((80, y), f"{from_name} <{from_email}>", fill=text_color, font=font_body)
y += 22
# Sent
draw.text((20, y), "Sent:", fill=label_color, font=font_label)
draw.text((80, y), date, fill=text_color, font=font_body)
y += 22
# To
draw.text((20, y), "To:", fill=label_color, font=font_label)
draw.text((80, y), f"{to_name} <{to_email}>", fill=text_color, font=font_body)
y += 22
# Subject line in header
draw.text((20, y), "Subject:", fill=label_color, font=font_label)
draw.text((80, y), subject[:60], fill=text_color, font=font_body)
y += 30
# Divider
draw.line([(15, y), (width - 15, y)], fill=(220, 220, 220), width=1)
y += 15
# Body text
for para in body.split("\n\n"):
for line in _wrap_text(para.strip(), 90):
if y > height - 30:
break
draw.text((25, y), line, fill=text_color, font=font_body)
y += 18
y += 8
img.save(str(output_path), "PNG")
def _wrap_text(text, max_chars):
"""Simple word-wrap."""
words = text.split()
lines = []
current = ""
for word in words:
if len(current) + len(word) + 1 > max_chars:
lines.append(current)
current = word
else:
current = f"{current} {word}" if current else word
if current:
lines.append(current)
return lines or [""]