درس 4 — Composer en calques

Grille, typographie et calques

8 دقائق قراءة

هذا المحتوى غير مترجم بعد. تُعرض النسخة الأصلية.

Grille, typographie et calques

Le lundi, le service communication demande « le même visuel, mais pour LinkedIn ». Si la composition est une image aplatie, c'est une nouvelle commande. Si elle est un empilement de calques décrits par du code, c'est un nouveau gabarit de quarante lignes et les mêmes contrôles. Cette leçon construit ce modèle de scène et les règles de mise en page qui le remplissent : grille, échelle typographique, couleurs dont le contraste est calculé.

Le mécanisme : une scène, six calques, trois primitives

flowchart TB
  subgraph Scene["Scene (un format)"]
    direction TB
    L1["01-fond : image générée, recadrée"]
    L2["02-panneaux : cartes blanches semi-opaques"]
    L3["03-illustration : sujet détouré"]
    L4["04-graphique : barres, ligne de base"]
    L5["05-icones : pictogrammes détourés"]
    L6["06-texte : tout le texte, toujours au-dessus"]
  end
  Scene --> PNG["Pillow"]
  Scene --> SVG["SVG à calques"]
  Scene --> PDF["PDF vectoriel"]
files/src/infographie/scene.py
@dataclass
class Rect:
    x: float; y: float; w: float; h: float
    couleur: RGBA
    rayon: float = 0

@dataclass
class Texte:
    x: float; y: float; w: float
    h: float            # boîte réservée : le contrôle compare le texte à cette boîte
    lignes: list[str]   # déjà coupées, avec la police réelle
    taille: int; graisse: str; couleur: RGBA; role: str
    interligne: float = 1.2
    align: str = "left"

@dataclass
class ImageEl:
    x: float; y: float; w: float; h: float
    image: Image.Image
    nom: str

Trois primitives seulement, et un texte déjà coupé en lignes avec la police livrée. C'est ce qui rend les trois moteurs de rendu équivalents : aucun ne décide où passer à la ligne.

Deux règles d'ordre des calques :

  1. Le texte est le dernier calque. Le contrôle de contraste compose tout ce qui est dessous et mesure les pixels réels sous chaque ligne.
  2. Les panneaux sont entre le fond et le contenu. Une carte blanche à 91 % d'opacité (SURFACE = (255, 255, 255, 232)) garantit le contraste quel que soit le fond que le modèle a produit, tout en laissant deviner sa texture.

Pour gagner du temps, un développeur dessine les valeurs du graphique dans le calque 04-graphique, avec les barres, plutôt que dans 06-texte. Quelle conséquence vous inquiète le plus ?

Les six calques de l'affiche, exportés en PNG transparents (damier = transparence)

La grille

files/src/infographie/mise_en_page.py
m = round(min(fmt.w, fmt.h) * 0.06)       # marge : 6 % du petit côté
gout = round(fmt.base * 0.8)              # gouttière : liée à la taille du texte
x0, x1 = m, fmt.w - m
haut, bas = m + fmt.sure_haut, fmt.h - m - fmt.sure_bas

Marge proportionnelle au format, gouttière proportionnelle au texte : le rythme visuel reste le même de 627 à 1920 pixels de haut. La story ajoute des zones sûres de 250 px en haut et 340 px en bas, où l'interface d'Instagram ou de LinkedIn recouvre l'image ; ces valeurs sont un ordre de grandeur courant, à vérifier sur l'application cible.

L'échelle typographique

Une seule taille de base par format, le reste en découle par un rapport constant de 1,333 (la « quarte » des typographes) :

files/src/infographie/mise_en_page.py
RATIO = 1.333
def taille(fmt: Format, niveau: int) -> int:
    return round(fmt.base * RATIO**niveau)
NiveauRôleAffiche (base 30)Story (base 30)LinkedIn (base 19)
3titre71 px71 px45 px
2valeur de KPI53 px53 px34 px
1titre du graphique40 px40 px25 px
0texte courant30 px30 px19 px
−1source22 px22 px14 px, relevé au minimum de 15 px

Quatre niveaux visibles suffisent à une infographie. Un cinquième niveau brouille la hiérarchie : le lecteur ne sait plus ce qui compte.

Deux réglages de typographie française sont appliqués par le code et non par le rédacteur : l'espace insécable avant « % », « : », « ; » (couper() ne met jamais un deux-points en début de ligne) et l'interdiction du mot orphelin en dernière ligne d'un titre.

Les couleurs : jetons et contrastes mesurés

files/src/infographie/mise_en_page.py
ENCRE = (17, 24, 39, 255)
ENCRE_2 = (71, 85, 105, 255)
ACCENT = (15, 118, 110, 255)   # une seule couleur porteuse de sens
BARRE = (100, 116, 139, 255)

Contrastes calculés avec la formule WCAG (controles.contraste), sur blanc :

JetonUsageContrasteSeuil applicable
ENCREtitres, valeurs17,74:14,5:1 (texte)
ENCRE_2sous-titres, libellés7,58:14,5:1
ACCENTbarre mise en avant, icônes5,47:13:1 (élément graphique, critère 1.4.11)
BARREautres barres4,76:13:1
#777777 (à titre de comparaison)gris « élégant » courant4,48:1échoue de peu en AA

Le texte ne prend jamais la couleur d'une série. Il reste en encre ; la couleur porte l'identité des marques graphiques, pas des mots.

Le fond généré pour la story est plus sombre que prévu. Le sous-titre gris ENCRE_2, posé directement sur le fond (sans carte), passe à 3,9:1. Que faites-vous ?

Recadrer et contenir

files/src/infographie/mise_en_page.py
def couvrir(img, w, h):
    """Redimensionne pour couvrir w×h puis recadre au centre (équivalent CSS object-fit: cover)."""
def contenir(img, w, h):
    """Tient dans w×h sans recadrer (object-fit: contain) : pour un sujet détouré."""

Le fond est couvert (on accepte de perdre ses bords), le sujet est contenu (on ne coupe jamais l'entrepôt).

Exporter les calques

files/src/infographie/export.py
def exporter_calques(scene: Scene, dossier: Path) -> list[Path]:
    """Un PNG transparent par calque, pleine taille : se superposent tels quels dans n'importe quel outil."""
    for i, c in enumerate(scene.calques, start=1):
        p = dossier / f"{i:02d}-{c.nom}.png"
        rendre_calque(c, scene.format.w, scene.format.h).save(p, optimize=True)

Pleine taille et numérotés : un graphiste les glisse dans n'importe quel outil, ils retombent à la bonne place, dans le bon ordre, sans coordonnées à reporter.

Ce qui casse en production

SymptômeCauseParade
Le texte change de largeur chez un collègueautre police, ou autre version d'Interpolice livrée dans polices/, chargée par chemin
Titre sur 3 lignes en LinkedInmême taille de titre que l'affichetaille dérivée de la base du format, descente d'au plus deux demi-paliers (titre_ajuste)
Texte illisible sur une zone sombre du fondtexte posé directement sur l'image généréepanneaux semi-opaques, contrôle mesuré sur les pixels
Calques décalés dans l'outil du graphistecalques exportés recadrés sur leur contenuexport pleine taille

Règle de décision

  • Une scène = des calques ordonnés de primitives simples ; tout ce qui est plus riche (dégradé, ombre) est cuit dans une image.
  • Une base typographique par format, tout le reste par l'échelle.
  • Une couleur porteuse de sens par infographie ; les autres sont neutres.

Exercice

Ajoutez un format « bannière e-mail » de 1200×400 px. Quelles valeurs de Format choisissez-vous et quel gabarit (paysage ou vertical) va-t-il prendre ?

Voir la solution
"bandeau": Format("bandeau", 1200, 400, dpi=72, base=16, mots_max=50, taille_min=14, nb_kpis=2),

1200 > 400 × 1,3 : la propriété paysage est vraie, le gabarit à deux colonnes s'applique. Avec 400 px de haut, la marge vaut 24 px ; le graphique de six barres tiendra à peine : lancez make run et lisez le rapport. Si lisibilite bloque, la bonne décision est souvent de retirer le graphique de ce format (un titre et deux KPI) plutôt que de réduire la police sous 14 px.

Résultat obtenu

Run final du 2026-10-15 21:06, calques de l'affiche :

$ ls sorties/affiche/calques/
01-fond.png  02-panneaux.png  03-illustration.png  04-graphique.png  05-icones.png  06-texte.png

Je sais faire

  • Décrire une composition comme une scène de calques et de primitives
  • Dériver une échelle typographique d'une taille de base par format
  • Choisir des jetons de couleur et vérifier leur contraste par le calcul
  • Exporter des calques réutilisables par un graphiste
التالي