141 lines
4.0 KiB
Python
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 [""]
|