Spaces:
Running
Running
| 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 | |