Spaces:
Sleeping
Sleeping
| """Generate the social-share card (Open Graph / Twitter image), 1200x630. | |
| Run once; the PNG is committed to static/ and copied to the site root at build | |
| time. The container does not need Pillow at runtime. Re-run only if the wording | |
| or brand colours change: python generate_og.py | |
| """ | |
| from __future__ import annotations | |
| import os | |
| from PIL import Image, ImageDraw, ImageFont | |
| from generate_icons import make_icon | |
| STATIC_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static") | |
| W, H, SS = 1200, 630, 2 # final size + supersample factor for crisp text | |
| INK = (33, 37, 41) | |
| GREY = (90, 99, 110) | |
| GREEN = (26, 152, 80) | |
| # Price legend gradient (matches the map): cheap green -> pricey red. | |
| LEGEND = [(26, 152, 80), (145, 207, 96), (254, 224, 139), (252, 141, 89), (215, 48, 39)] | |
| # Font candidates per weight: Arial on macOS (local), DejaVu in the container. | |
| FONTS = { | |
| "bold": ["/System/Library/Fonts/Supplemental/Arial Bold.ttf", | |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", | |
| "/Library/Fonts/Arial Bold.ttf"], | |
| "regular": ["/System/Library/Fonts/Supplemental/Arial.ttf", | |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", | |
| "/Library/Fonts/Arial.ttf"], | |
| } | |
| def _font(weight: str, size: int) -> ImageFont.FreeTypeFont: | |
| for path in FONTS[weight]: | |
| if os.path.exists(path): | |
| return ImageFont.truetype(path, size) | |
| return ImageFont.load_default() | |
| def _lerp(a, b, t): | |
| return tuple(round(a[i] + (b[i] - a[i]) * t) for i in range(3)) | |
| def _gradient_bar(draw: ImageDraw.ImageDraw, x0, y0, x1, y1) -> None: | |
| """Horizontal multi-stop gradient bar (the price scale).""" | |
| span = x1 - x0 | |
| seg = span / (len(LEGEND) - 1) | |
| for x in range(x0, x1): | |
| f = (x - x0) / seg | |
| i = min(int(f), len(LEGEND) - 2) | |
| draw.line([(x, y0), (x, y1)], fill=_lerp(LEGEND[i], LEGEND[i + 1], f - i)) | |
| def build() -> Image.Image: | |
| w, h = W * SS, H * SS | |
| img = Image.new("RGB", (w, h), (255, 255, 255)) | |
| d = ImageDraw.Draw(img) | |
| # Left: the brand icon tile. | |
| icon = make_icon(360 * SS, rounded=True) | |
| img.paste(icon, (70 * SS, (h - icon.height) // 2), icon) | |
| tx = 470 * SS # text column | |
| d.text((tx, 160 * SS), "Prix des carburants", font=_font("bold", 60 * SS), fill=INK) | |
| d.text((tx, 240 * SS), "en France", font=_font("bold", 60 * SS), fill=GREEN) | |
| d.text((tx, 350 * SS), "Carte en temps réel — gazole, SP95,", | |
| font=_font("regular", 34 * SS), fill=GREY) | |
| d.text((tx, 396 * SS), "SP98, E85, GPL & bornes de recharge", | |
| font=_font("regular", 34 * SS), fill=GREY) | |
| _gradient_bar(d, tx, 470 * SS, (W - 80) * SS, 492 * SS) | |
| d.text((tx, 504 * SS), "moins cher", font=_font("regular", 26 * SS), fill=GREY) | |
| pricey = "plus cher" | |
| pf = _font("regular", 26 * SS) | |
| d.text(((W - 80) * SS - d.textlength(pricey, font=pf), 504 * SS), pricey, font=pf, fill=GREY) | |
| d.text((tx, 556 * SS), "samdnx-prix-carburant.hf.space", | |
| font=_font("bold", 26 * SS), fill=GREEN) | |
| return img.resize((W, H), Image.LANCZOS) | |
| def save(path: str) -> None: | |
| """Render the card and write it to path (used at build time).""" | |
| build().save(path, optimize=True) | |
| def main() -> None: | |
| os.makedirs(STATIC_DIR, exist_ok=True) | |
| out = os.path.join(STATIC_DIR, "og-card.png") | |
| save(out) | |
| print("wrote", out, f"({os.path.getsize(out) // 1024} KB)") | |
| if __name__ == "__main__": | |
| main() | |