CapsulesVideoPro_v2 / src /graphics.py
omarbajouk's picture
Update src/graphics.py
0406053 verified
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