import os, uuid, re, traceback from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageOps from .config import THEMES, FONT_REG, FONT_BOLD, W, H, MARGIN_X, SAFE_Y_TOP, TMP_DIR def wrap_text(text, font, max_width, draw): lines = [] for para in text.split("\n"): current = [] for word in para.split(" "): test = " ".join(current + [word]) try: w = draw.textlength(test, font=font) except AttributeError: bbox = draw.textbbox((0, 0), test, font=font) w = bbox[2] - bbox[0] if w <= max_width or not current: current.append(word) else: lines.append(" ".join(current)) current = [word] if current: lines.append(" ".join(current)) return lines def draw_text_shadow(draw, xy, text, font, fill=(255, 255, 255)): x, y = xy draw.text((x + 2, y + 2), text, font=font, fill=(0, 0, 0)) draw.text((x, y), text, font=font, fill=fill) def make_background(titre, sous_titre, texte_ecran, theme, logo_path, logo_pos, img_fond, fond_mode="plein écran"): try: print(f"[Fond] Création du fond - Thème: {theme}") if not titre: titre = "" if not theme or theme not in THEMES: theme = list(THEMES.keys())[0] print(f"[Fond] Thème invalide, utilisation par défaut: {theme}") c = THEMES[theme] primary = tuple(c["primary"]) secondary = tuple(c["secondary"]) bg = Image.new("RGB", (W, H), primary) draw = ImageDraw.Draw(bg) if img_fond and os.path.exists(img_fond): try: img = Image.open(img_fond).convert("RGB") if fond_mode == "plein écran": img = img.resize((W, H)) img = img.filter(ImageFilter.GaussianBlur(1)) overlay = Image.new("RGBA", (W, H), (*primary, 90)) bg = Image.alpha_composite(img.convert("RGBA"), overlay).convert("RGB") elif fond_mode == "moitié gauche": img = img.resize((W//2, H)) mask = Image.linear_gradient("L").resize((W//2, H)) color = Image.new("RGB", (W//2, H), primary) comp = Image.composite(img, color, ImageOps.invert(mask)) bg.paste(comp, (0, 0)) elif fond_mode == "moitié droite": img = img.resize((W//2, H)) mask = Image.linear_gradient("L").resize((W//2, H)) color = Image.new("RGB", (W//2, H), primary) comp = Image.composite(color, img, mask) bg.paste(comp, (W//2, 0)) elif fond_mode == "moitié bas": img = img.resize((W, H//2)) mask = Image.linear_gradient("L").rotate(90).resize((W, H//2)) color = Image.new("RGB", (W, H//2), primary) comp = Image.composite(color, img, mask) bg.paste(comp, (0, H//2)) draw = ImageDraw.Draw(bg) print(f"[Fond] Image de fond appliquée: {fond_mode}") except Exception as e: print(f"[Fond] Erreur image de fond: {e}") try: f_title = ImageFont.truetype(FONT_BOLD, 84) f_sub = ImageFont.truetype(FONT_REG, 44) f_text = ImageFont.truetype(FONT_REG, 40) f_small = ImageFont.truetype(FONT_REG, 30) except Exception as e: print(f"[Fond] Erreur polices, utilisation par défaut: {e}") f_title = ImageFont.load_default() f_sub = ImageFont.load_default() f_text = ImageFont.load_default() f_small = ImageFont.load_default() draw.rectangle([(0, 0), (W, 96)], fill=secondary) draw.rectangle([(0, H-96), (W, H)], fill=secondary) draw_text_shadow(draw, (MARGIN_X, 30), "CPAS BRUXELLES • Département d'Action Sociale", f_small) draw_text_shadow(draw, (W//2-280, H-72), "📞 02/543.61.11 • Respect, Inclusion, Solidarité, Innovation et Engagement", f_small) draw_text_shadow(draw, (MARGIN_X, SAFE_Y_TOP), titre, f_title) draw_text_shadow(draw, (MARGIN_X, SAFE_Y_TOP + 100), sous_titre, f_sub) y = SAFE_Y_TOP + 200 if texte_ecran: for line in texte_ecran.split("\n"): wrapped_lines = wrap_text("• " + line.strip("• "), f_text, W - MARGIN_X*2, draw) for l in wrapped_lines: draw_text_shadow(draw, (MARGIN_X, y), l, f_text) y += 55 if logo_path and os.path.exists(logo_path): try: logo = Image.open(logo_path).convert("RGBA") logo.thumbnail((260, 260)) lw, lh = logo.size if logo_pos == "haut-gauche": pos = (50, 50) elif logo_pos == "haut-droite": pos = (W - lw - 50, 50) else: pos = ((W - lw)//2, 50) bg.paste(logo, pos, logo) print(f"[Fond] Logo appliqué: {logo_pos}") except Exception as e: print(f"[Fond] Erreur logo: {e}") out_path = os.path.join(TMP_DIR, f"fond_{uuid.uuid4().hex[:6]}.png") bg.save(out_path) print(f"[Fond] ✅ Fond créé avec succès: {out_path}") return out_path except Exception as e: print(f"[Fond] ❌ ERREUR CRITIQUE: {e}") print(f"[Fond] Traceback: {traceback.format_exc()}") try: emergency_bg = Image.new("RGB", (W, H), (0, 82, 147)) out_path = os.path.join(TMP_DIR, f"emergency_fond_{uuid.uuid4().hex[:6]}.png") emergency_bg.save(out_path) print(f"[Fond] ✅ Fond d'urgence créé: {out_path}") return out_path except Exception as e2: print(f"[Fond] ❌ Même le fallback a échoué: {e2}") return None