"""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 [""]