Architecte static.alpinux.org : assets binaires hors git
- Logo SVG (source texte) ajouté dans docs/assets/alpinux-logo.svg - .gitignore : exclut *.png, *.ico, docs/assets/images/ (binaires → static.alpinux.org) - overrides/partials/logo.html : logo depuis https://static.alpinux.org/logo/ - overrides/main.html : favicons depuis static.alpinux.org via {% block extrahead %} - mkdocs.yml : logo → SVG, ajout pymdownx.emoji (icônes Material) - home/index.html : page d'accueil alpinux.org (logo + favicon depuis static.alpinux.org, carte dynamic.alpinux.org) - scripts/build-assets.py : génère PNG/favicon depuis le SVG source - scripts/static.alpinux.org.vhost.conf : template vhost Apache Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2fa0fe0da9
commit
565165cc70
8 changed files with 566 additions and 2 deletions
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -1 +1,11 @@
|
|||
site/\n.obsidian/\n__pycache__/
|
||||
site/
|
||||
__pycache__/
|
||||
|
||||
# Assets binaires — générés par scripts/build-assets.py
|
||||
# Hébergés sur https://static.alpinux.org/logo/
|
||||
docs/assets/*.png
|
||||
docs/assets/*.ico
|
||||
docs/assets/images/
|
||||
|
||||
# Obsidian (local uniquement)
|
||||
.obsidian/
|
||||
|
|
|
|||
52
docs/assets/alpinux-logo.svg
Normal file
52
docs/assets/alpinux-logo.svg
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" font-family="'Segoe UI', system-ui, -apple-system, sans-serif">
|
||||
<!--
|
||||
Source SVG — Tux + Alpes + typographie Alpinux
|
||||
Générer le PNG depuis ce fichier avec : scripts/build-assets.py
|
||||
PNG et favicon sont hébergés sur https://static.alpinux.org/logo/
|
||||
-->
|
||||
|
||||
<!-- ── Montagnes (fond, coin sup. droit) ─────────────────── -->
|
||||
<polygon points="158,4 190,56 126,56" fill="#9dc87a" opacity=".8"/>
|
||||
<polygon points="158,4 149,26 167,26" fill="#fff" opacity=".9"/>
|
||||
<polygon points="146,14 184,64 108,64" fill="#c8e6a0" opacity=".65"/>
|
||||
<polygon points="146,14 137,34 155,34" fill="#fff" opacity=".85"/>
|
||||
|
||||
<!-- ── Tux ────────────────────────────────────────────────── -->
|
||||
<!-- corps -->
|
||||
<ellipse cx="90" cy="110" rx="42" ry="50" fill="#1c1c1c"/>
|
||||
<!-- tête -->
|
||||
<ellipse cx="90" cy="56" rx="32" ry="30" fill="#1c1c1c"/>
|
||||
<!-- ventre blanc -->
|
||||
<ellipse cx="90" cy="114" rx="27" ry="34" fill="#f0f0f0"/>
|
||||
<!-- face blanche -->
|
||||
<ellipse cx="90" cy="56" rx="20" ry="18" fill="#f0f0f0"/>
|
||||
|
||||
<!-- yeux -->
|
||||
<circle cx="80" cy="49" r="7.5" fill="#1c1c1c"/>
|
||||
<circle cx="100" cy="49" r="7.5" fill="#1c1c1c"/>
|
||||
<circle cx="80" cy="49" r="4.8" fill="#f0f0f0"/>
|
||||
<circle cx="100" cy="49" r="4.8" fill="#f0f0f0"/>
|
||||
<circle cx="81" cy="50" r="3.5" fill="#1c1c1c"/>
|
||||
<circle cx="101" cy="50" r="3.5" fill="#1c1c1c"/>
|
||||
<circle cx="82" cy="49" r="1.2" fill="#fff"/>
|
||||
<circle cx="102" cy="49" r="1.2" fill="#fff"/>
|
||||
|
||||
<!-- bec -->
|
||||
<path d="M82,71 Q90,83 98,71 Q90,79 82,71" fill="#e8820c"/>
|
||||
|
||||
<!-- ailes -->
|
||||
<path d="M48,94 Q30,110 34,146 Q50,156 58,125 Q50,110 50,94 Z" fill="#1c1c1c"/>
|
||||
<path d="M132,94 Q150,110 146,146 Q130,156 122,125 Q130,110 130,94 Z" fill="#1c1c1c"/>
|
||||
|
||||
<!-- pattes -->
|
||||
<path d="M70,157 Q55,166 59,173 Q73,169 82,161 Z" fill="#e8820c"/>
|
||||
<path d="M110,157 Q125,166 121,173 Q107,169 98,161 Z" fill="#e8820c"/>
|
||||
|
||||
<!-- ── Texte : A[l]p[inux] ─────────────────────────────────
|
||||
Typographie : A (400) l (700) p (400) inux (700)
|
||||
NB : text-anchor="middle" + tspan peut mal centrer selon le
|
||||
moteur SVG. Pour un rendu parfait → scripts/build-assets.py -->
|
||||
<text x="100" y="194" text-anchor="middle" font-size="32" fill="#0f4e8f" letter-spacing="-.5">
|
||||
<tspan font-weight="400">A</tspan><tspan font-weight="700">l</tspan><tspan font-weight="400">p</tspan><tspan font-weight="700">inux</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
297
home/index.html
Normal file
297
home/index.html
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Alpinux — Le LUG de Savoie. Groupe d'utilisateurs de Linux et logiciels libres à Chambéry.">
|
||||
<meta name="robots" content="index, follow">
|
||||
<title>Alpinux — Le LUG de Savoie</title>
|
||||
|
||||
<!-- Favicons -->
|
||||
<link rel="icon" type="image/x-icon" href="https://static.alpinux.org/logo/favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="https://static.alpinux.org/logo/favicon-32.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="https://static.alpinux.org/logo/favicon-96.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="https://static.alpinux.org/logo/favicon-192.png">
|
||||
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||||
background: #f3f6fb;
|
||||
color: #1a1a2e;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ── Header ────────────────────────────────────────────────── */
|
||||
header {
|
||||
background: #1a6bbf;
|
||||
color: #fff;
|
||||
padding: 3rem 1.5rem 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1.2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.logo-img {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-size: 2.8rem;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
letter-spacing: -.02em;
|
||||
}
|
||||
|
||||
.brand .light { font-weight: 300; }
|
||||
.brand .bold { font-weight: 800; }
|
||||
|
||||
.tagline {
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
letter-spacing: .18em;
|
||||
color: rgba(255,255,255,.8);
|
||||
margin-top: .3rem;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
max-width: 640px;
|
||||
margin: 1.8rem auto 0;
|
||||
font-size: 1.05rem;
|
||||
color: rgba(255,255,255,.88);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* ── Nav links ──────────────────────────────────────────────── */
|
||||
nav {
|
||||
background: #0f4e8f;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: .2rem;
|
||||
flex-wrap: wrap;
|
||||
padding: .4rem 1rem;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: rgba(255,255,255,.85);
|
||||
text-decoration: none;
|
||||
font-size: .88rem;
|
||||
padding: .45rem .9rem;
|
||||
border-radius: 4px;
|
||||
transition: background .15s;
|
||||
}
|
||||
|
||||
nav a:hover { background: rgba(255,255,255,.15); color: #fff; }
|
||||
|
||||
/* ── Main ───────────────────────────────────────────────────── */
|
||||
main {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 3rem 1.5rem;
|
||||
}
|
||||
|
||||
/* ── Events ─────────────────────────────────────────────────── */
|
||||
.section-title {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
color: #0f4e8f;
|
||||
margin-bottom: 1.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
}
|
||||
|
||||
.section-title::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 2px;
|
||||
background: #e8f1fb;
|
||||
}
|
||||
|
||||
.events {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.event-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 1.2rem 1.4rem;
|
||||
box-shadow: 0 2px 10px rgba(26,107,191,.1);
|
||||
border-left: 4px solid #1a6bbf;
|
||||
}
|
||||
|
||||
.event-date {
|
||||
font-size: .82rem;
|
||||
font-weight: 700;
|
||||
color: #1a6bbf;
|
||||
letter-spacing: .06em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
.event-title { font-size: 1rem; font-weight: 600; }
|
||||
.event-where { font-size: .88rem; color: #666; margin-top: .3rem; }
|
||||
|
||||
/* ── Services ───────────────────────────────────────────────── */
|
||||
.services {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 1.4rem;
|
||||
box-shadow: 0 2px 10px rgba(26,107,191,.08);
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: #1a1a2e;
|
||||
transition: transform .15s, box-shadow .15s;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.service-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 20px rgba(26,107,191,.18);
|
||||
color: #1a6bbf;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: .6rem;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: .3rem;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: .84rem;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ── Footer ─────────────────────────────────────────────────── */
|
||||
footer {
|
||||
background: #0f4e8f;
|
||||
color: rgba(255,255,255,.7);
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
font-size: .85rem;
|
||||
}
|
||||
|
||||
footer a { color: rgba(255,255,255,.85); text-decoration: none; }
|
||||
footer a:hover { color: #fff; }
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.brand { font-size: 2.2rem; }
|
||||
.logo-img { width: 70px; height: 70px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<div class="logo-wrap">
|
||||
<img src="https://static.alpinux.org/logo/alpinux-logo.png" alt="Logo Alpinux" class="logo-img">
|
||||
<div class="logo-text">
|
||||
<div class="brand">
|
||||
<span class="light">A</span><span class="bold">l</span><span class="light">p</span><span class="bold">inux</span>
|
||||
</div>
|
||||
<div class="tagline">le LUG de Savoie</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hero-text">
|
||||
Association loi 1901 dédiée à <strong>Linux et aux logiciels libres</strong> en Savoie.
|
||||
Réunions tous les 1<sup>er</sup> et 3<sup>e</sup> jeudis du mois à la <strong>Dynamo Chambéry</strong>.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
<a href="https://wiki.alpinux.org">Wiki</a>
|
||||
<a href="https://portail.alpinux.org">Portail membres</a>
|
||||
<a href="https://installparty.alpinux.org">Install Party</a>
|
||||
<a href="https://gitea.alpinux.org">Gitea</a>
|
||||
<a href="https://mamot.fr/@alpinux">Mastodon</a>
|
||||
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
|
||||
<h2 class="section-title">Prochains événements</h2>
|
||||
<div class="events">
|
||||
<div class="event-card">
|
||||
<div class="event-date">1<sup>er</sup> & 3<sup>e</sup> jeudis</div>
|
||||
<div class="event-title">Réunion mensuelle</div>
|
||||
<div class="event-where">📍 Dynamo Chambéry — 18h00</div>
|
||||
</div>
|
||||
<div class="event-card">
|
||||
<div class="event-date">Voir le calendrier</div>
|
||||
<div class="event-title">Install Party & ateliers</div>
|
||||
<div class="event-where">📅 <a href="https://installparty.alpinux.org" style="color:#1a6bbf;">installparty.alpinux.org</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="section-title">Nos services</h2>
|
||||
<div class="services">
|
||||
<a href="https://wiki.alpinux.org" class="service-card">
|
||||
<div class="service-icon">📖</div>
|
||||
<div class="service-name">Wiki</div>
|
||||
<div class="service-desc">Guides, tutoriels et présentations</div>
|
||||
</a>
|
||||
<a href="https://portail.alpinux.org" class="service-card">
|
||||
<div class="service-icon">🔐</div>
|
||||
<div class="service-name">Portail membres</div>
|
||||
<div class="service-desc">Espace adhérent, Nextcloud, Dolibarr</div>
|
||||
</a>
|
||||
<a href="https://installparty.alpinux.org" class="service-card">
|
||||
<div class="service-icon">🐧</div>
|
||||
<div class="service-name">Install Party</div>
|
||||
<div class="service-desc">Événements & inscriptions</div>
|
||||
</a>
|
||||
<a href="https://gitea.alpinux.org" class="service-card">
|
||||
<div class="service-icon">💻</div>
|
||||
<div class="service-name">Gitea</div>
|
||||
<div class="service-desc">Dépôts de code de l'association</div>
|
||||
</a>
|
||||
<a href="https://dynamic.alpinux.org" class="service-card">
|
||||
<div class="service-icon">🎮</div>
|
||||
<div class="service-name">Jeux & quiz</div>
|
||||
<div class="service-desc">Mini-jeux et questionnaires interactifs</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© Alpinux — Association loi 1901 · Chambéry, Savoie</p>
|
||||
<p style="margin-top:.4rem;">
|
||||
<a href="https://wiki.alpinux.org/alpinux/">À propos</a> ·
|
||||
<a href="https://wiki.alpinux.org/alpinux/faq/">FAQ</a> ·
|
||||
<a href="https://mamot.fr/@alpinux">Mastodon</a> ·
|
||||
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -23,7 +23,7 @@ theme:
|
|||
- search.suggest
|
||||
- content.code.copy
|
||||
- content.code.select
|
||||
logo: assets/alpinux-logo.png
|
||||
logo: assets/alpinux-logo.svg
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
|
|
@ -48,6 +48,9 @@ markdown_extensions:
|
|||
- toc:
|
||||
permalink: true
|
||||
- footnotes
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
|
||||
extra:
|
||||
social:
|
||||
|
|
|
|||
|
|
@ -5,3 +5,10 @@
|
|||
A<strong>l</strong>p<strong>inux</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrahead %}
|
||||
<link rel="icon" type="image/x-icon" href="https://static.alpinux.org/logo/favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="https://static.alpinux.org/logo/favicon-32.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="https://static.alpinux.org/logo/favicon-96.png">
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="https://static.alpinux.org/logo/favicon-192.png">
|
||||
{% endblock %}
|
||||
|
|
|
|||
3
overrides/partials/logo.html
Normal file
3
overrides/partials/logo.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<img src="https://static.alpinux.org/logo/alpinux-logo.png"
|
||||
alt="Alpinux"
|
||||
class="md-logo__image">
|
||||
155
scripts/build-assets.py
Normal file
155
scripts/build-assets.py
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Génère les assets binaires (logo PNG + favicons) depuis la source SVG.
|
||||
Dépendances : Pillow, chromium
|
||||
|
||||
Usage :
|
||||
python3 scripts/build-assets.py [--out /chemin/de/sortie]
|
||||
|
||||
Les fichiers générés sont ensuite uploadés sur static.alpinux.org/logo/
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
SVG = REPO / "docs/assets/alpinux-logo.svg"
|
||||
FONT_R = "/usr/share/fonts/truetype/msttcorefonts/arial.ttf"
|
||||
FONT_B = "/usr/share/fonts/truetype/msttcorefonts/arialbd.ttf"
|
||||
COLOR = (15, 78, 143) # #0f4e8f
|
||||
|
||||
|
||||
def render_shapes(svg_path: Path, width: int, height: int) -> Image.Image:
|
||||
"""Render SVG shapes via chromium headless (no text)."""
|
||||
html = f"""<!DOCTYPE html><html>
|
||||
<head><meta charset="utf-8">
|
||||
<style>*{{margin:0;padding:0}}html,body{{width:{width}px;height:{height}px;overflow:hidden;background:transparent}}</style>
|
||||
</head>
|
||||
<body><img src="file://{svg_path}" width="{width}" height="{height}"></body>
|
||||
</html>"""
|
||||
with tempfile.NamedTemporaryFile(suffix=".html", mode="w", delete=False) as f:
|
||||
f.write(html)
|
||||
tmp_html = f.name
|
||||
out_png = tmp_html.replace(".html", ".png")
|
||||
subprocess.run([
|
||||
"chromium", "--headless", "--disable-gpu", "--no-sandbox",
|
||||
f"--screenshot={out_png}", f"--window-size={width},{height}",
|
||||
f"file://{tmp_html}"
|
||||
], capture_output=True)
|
||||
img = Image.open(out_png).convert("RGBA")
|
||||
os.unlink(tmp_html)
|
||||
os.unlink(out_png)
|
||||
return img
|
||||
|
||||
|
||||
def add_text(canvas: Image.Image) -> Image.Image:
|
||||
"""Composite Alpinux text with correct weights onto the canvas."""
|
||||
draw = ImageDraw.Draw(canvas)
|
||||
size = 30
|
||||
f_reg = ImageFont.truetype(FONT_R, size)
|
||||
f_bld = ImageFont.truetype(FONT_B, size)
|
||||
parts = [("A", False), ("l", True), ("p", False), ("inux", True)]
|
||||
|
||||
widths = []
|
||||
for char, bold in parts:
|
||||
f = f_bld if bold else f_reg
|
||||
bb = f.getbbox(char)
|
||||
widths.append(bb[2] - bb[0])
|
||||
|
||||
total_w = sum(widths)
|
||||
x = (canvas.width - total_w) // 2
|
||||
y = 164 + (36 - size) // 2 + 1
|
||||
|
||||
for (char, bold), w in zip(parts, widths):
|
||||
f = f_bld if bold else f_reg
|
||||
bb = f.getbbox(char)
|
||||
draw.text((x - bb[0], y - bb[1]), char, font=f, fill=COLOR)
|
||||
x += w
|
||||
|
||||
return canvas
|
||||
|
||||
|
||||
def build_logo(out_dir: Path):
|
||||
"""Build 200×200 logo PNG."""
|
||||
shapes = render_shapes(SVG, 200, 164)
|
||||
canvas = Image.new("RGBA", (200, 200), (255, 255, 255, 255))
|
||||
canvas.paste(shapes, (0, 0))
|
||||
canvas = add_text(canvas)
|
||||
path = out_dir / "alpinux-logo.png"
|
||||
canvas.convert("RGB").save(path)
|
||||
print(f" {path} (200×200)")
|
||||
|
||||
# 512px high-res version
|
||||
shapes512 = render_shapes(SVG, 512, 421)
|
||||
canvas512 = Image.new("RGBA", (512, 512), (255, 255, 255, 255))
|
||||
canvas512.paste(shapes512, (0, 0))
|
||||
# Scale text proportionally
|
||||
draw = ImageDraw.Draw(canvas512)
|
||||
size = 77
|
||||
f_reg = ImageFont.truetype(FONT_R, size)
|
||||
f_bld = ImageFont.truetype(FONT_B, size)
|
||||
parts = [("A", False), ("l", True), ("p", False), ("inux", True)]
|
||||
widths = [f_bld.getbbox(c)[2]-f_bld.getbbox(c)[0] if b else f_reg.getbbox(c)[2]-f_reg.getbbox(c)[0] for c,b in parts]
|
||||
total_w = sum(widths)
|
||||
x = (512 - total_w) // 2
|
||||
y = 421 + (91 - size) // 2 + 2
|
||||
for (char, bold), w in zip(parts, widths):
|
||||
f = f_bld if bold else f_reg
|
||||
bb = f.getbbox(char)
|
||||
draw.text((x - bb[0], y - bb[1]), char, font=f, fill=COLOR)
|
||||
x += w
|
||||
path512 = out_dir / "alpinux-logo-512.png"
|
||||
canvas512.convert("RGB").save(path512)
|
||||
print(f" {path512} (512×512)")
|
||||
|
||||
|
||||
def build_favicons(out_dir: Path):
|
||||
"""Build favicon PNG set + .ico from the icon portion of the SVG."""
|
||||
icon_src = render_shapes(SVG, 200, 164).crop((0, 0, 164, 164))
|
||||
|
||||
sizes = {
|
||||
"favicon-16.png": 16,
|
||||
"favicon-32.png": 32,
|
||||
"favicon.png": 48,
|
||||
"favicon-96.png": 96,
|
||||
"favicon-192.png": 192,
|
||||
}
|
||||
imgs = {}
|
||||
for name, sz in sizes.items():
|
||||
img = icon_src.resize((sz, sz), Image.LANCZOS)
|
||||
bg = Image.new("RGBA", (sz, sz), (255, 255, 255, 255))
|
||||
bg.paste(img, (0, 0))
|
||||
p = out_dir / name
|
||||
bg.save(p)
|
||||
imgs[sz] = bg
|
||||
print(f" {p} ({sz}×{sz})")
|
||||
|
||||
ico = out_dir / "favicon.ico"
|
||||
imgs[16].save(ico, format="ICO", sizes=[(16,16),(32,32),(48,48)],
|
||||
append_images=[imgs[32], imgs[48]])
|
||||
print(f" {ico} (multi-size: 16+32+48)")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--out", default="/tmp/alpinux-static-assets",
|
||||
help="Répertoire de sortie (défaut: /tmp/alpinux-static-assets)")
|
||||
args = parser.parse_args()
|
||||
|
||||
out_dir = Path(args.out)
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print("Génération des assets Alpinux...")
|
||||
build_logo(out_dir)
|
||||
build_favicons(out_dir)
|
||||
print(f"\nFichiers dans : {out_dir}")
|
||||
print("À uploader sur : static.alpinux.org/logo/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
37
scripts/static.alpinux.org.vhost.conf
Normal file
37
scripts/static.alpinux.org.vhost.conf
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Apache vhost pour static.alpinux.org
|
||||
# À créer via ISPConfig : Sites > Ajouter un site web
|
||||
# Domaine : static.alpinux.org | DocumentRoot : /var/www/clients/clientX/webY/web
|
||||
# Activer SSL Let's Encrypt dans ISPConfig
|
||||
#
|
||||
# Ou, si créé manuellement, coller ce fichier dans /etc/apache2/sites-enabled/
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName static.alpinux.org
|
||||
Redirect permanent / https://static.alpinux.org/
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName static.alpinux.org
|
||||
DocumentRoot /var/www/clients/client1/web-static/web
|
||||
|
||||
# En-têtes CORS — permet au wiki et à la page d'accueil de charger les assets
|
||||
Header always set Access-Control-Allow-Origin "*"
|
||||
Header always set Cache-Control "public, max-age=31536000, immutable"
|
||||
|
||||
# Pas d'exécution PHP
|
||||
php_admin_flag engine Off
|
||||
|
||||
<Directory /var/www/clients/client1/web-static/web>
|
||||
Options -Indexes +FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
# Logs
|
||||
ErrorLog /var/log/apache2/static.alpinux.org-error.log
|
||||
CustomLog /var/log/apache2/static.alpinux.org-access.log combined
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/letsencrypt/live/static.alpinux.org/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/static.alpinux.org/privkey.pem
|
||||
</VirtualHost>
|
||||
Loading…
Reference in a new issue