Files
open-school/SVG_CONTENT_ARCHITECTURE.md
2026-05-09 22:15:24 +02:00

7.3 KiB

Architecture des contenus SVG pédagogiques

Diagnostic

Le problème des traits de fraction n'est pas un simple défaut ponctuel de mise en page. Les retours du 2026-05-07 montrent le même motif sur plusieurs supports fractions :

  • traits obliques à proscrire pour les élèves de CM1 ;
  • barres horizontales qui recouvrent le numérateur ou le dénominateur ;
  • expressions de fractions qui se chevauchent dans les équations ;
  • corrections faites directement dans les SVG finaux.

L'architecture actuelle sait charger, servir, recadrer et relire des SVG. Elle ne sait pas garantir que les objets mathématiques dessinés à l'intérieur du SVG sont corrects.

État actuel

  • Les contenus sont stockés comme fichiers SVG finaux dans backend/contenus_pedagogiques.
  • Le backend expose les SVG tels quels via /program/assets/{asset_token}.
  • Le découpage en cards est fait par détection de grands groupes <g> dans backend/app/program_content.py.
  • Les retours de validation sont stockés séparément dans review_updates.
  • Les SVG fractions récents utilisent déjà une convention locale : math-expression et fraction-g.

Cette convention est utile, mais elle arrive trop tard : elle est écrite dans le SVG produit, sans source structurée, sans composant unique et sans validation automatique.

Décision recommandée

Garder SVG comme format de sortie, mais ne plus considérer le SVG final comme la source principale pour les objets mathématiques sensibles.

Pour les prochaines générations, il faut ajouter une couche intermédiaire :

  1. Une source structurée décrivant les cards et les objets pédagogiques.
  2. Une petite bibliothèque de primitives SVG contrôlées.
  3. Un validateur automatique lancé après génération.
  4. Une revue humaine uniquement sur les points que l'automatique ne peut pas juger.

Primitive prioritaire : fraction verticale

Une fraction ne doit plus être écrite à la main dans un SVG.

La primitive fraction(numerateur, denominateur, options) doit produire :

  • un groupe <g class="math-expression fraction-g" data-math="fraction"> ;
  • deux textes centrés ;
  • une barre horizontale ;
  • des espacements calculés depuis la taille de police ;
  • une largeur calculée depuis le plus long texte ;
  • aucun trait oblique pour les fractions CM1 ;
  • une boîte logique exportable, pour éviter les collisions dans les équations.

Règle de rendu CM1 :

  • fraction verticale obligatoire ;
  • barre horizontale visible et séparée des chiffres ;
  • pas de notation 3/4 dans les SVG destinés à l'élève, sauf dans les notes Markdown ou les contextes de card.

Validation automatique minimale

Le validateur doit refuser ou signaler :

  • texte SVG contenant une fraction oblique du type 3/4 ;
  • groupe fraction-g sans numérateur, barre et dénominateur ;
  • barre qui n'est pas située entre les deux textes ;
  • espacement vertical trop faible autour de la barre ;
  • barre trop courte par rapport aux chiffres ;
  • SVG sans viewBox ou dimensions incohérentes ;
  • card sans grand rectangle détectable, quand elle doit être découpée.

Le premier filet de sécurité est disponible dans backend/tools/validate_svg_quality.py.

Règles anti-chevauchement globales

Les retours sur les 8 supports fractions montrent que valider une fraction isolée ne suffit pas. Chaque expression mathématique doit réserver une boîte complète avant d'être placée dans la card.

Règles obligatoires :

  • toute fraction-g doit porter un data-box="x y width height" couvrant le numérateur, la barre, le dénominateur et la marge pédagogique ;
  • aucun texte, opérateur, figure, point, disque, bande ou autre fraction ne peut intersecter cette boîte ;
  • dans une expression, le curseur horizontal avance de la largeur réservée de la fraction plus un espacement minimal de 28 px pour du texte courant et 34 px pour les signes visibles ;
  • les signes +, -, =, <, > sont centrés sur l'axe de la barre de fraction, pas sur la ligne de base des chiffres ;
  • les signes de comparaison sont placés au milieu de l'espace entre les deux boîtes de fractions ;
  • les chiffres d'une fraction sont équilibrés optiquement autour du trait : le numérateur est placé plus bas que ne le suggère une symétrie de baseline SVG pure, afin que l'espace visible au-dessus et au-dessous du trait paraisse identique ;
  • une fraction posée près d'une figure utilise une zone mathématique dédiée au-dessus, en dessous ou à côté de la figure ; elle ne flotte jamais dans la zone de dessin ;
  • les lignes d'équations sont espacées d'au moins la hauteur réservée de la plus grande fraction de la ligne plus 18 px ;
  • une fraction inline dans une phrase garde un espace réservé avant et après, même si le texte semble court ;
  • les titres de card, légendes et encadrés À retenir sont des zones séparées : une fraction ne doit pas empiéter dessus.

En pratique, chaque card doit être pensée en zones :

  1. zone titre ;
  2. zone mathématique ;
  3. zone figure ;
  4. zone légende ou explication.

Une génération est rejetée si une boîte mathématique traverse une autre zone. C'est la règle qui évite les cas vus dans les captures : fractions collées aux mots, fractions sur les figures, signes décentrés, et lignes de fractions qui passent sous les schémas.

Règles de composition des expressions

Les expressions doivent être générées par un layout horizontal, jamais par coordonnées libres.

Le layout prend une liste de tokens :

  • fraction(n, d, size) ;
  • operator("+"), operator("="), operator("<") ;
  • text("est juste après 1").

Chaque token expose sa boîte. L'expression calcule ensuite les positions. Les opérateurs ne sont pas des textes décoratifs : ils ont une boîte propre et une position centrée entre deux tokens mathématiques.

Pour les équations sur plusieurs lignes, on compose chaque ligne séparément, puis on empile les lignes avec une hauteur constante. On ne réduit jamais l'interligne après coup pour "faire rentrer" une card ; si ça ne rentre pas, on change la disposition.

Architecture cible courte

À court terme :

  • continuer à servir les SVG existants ;
  • imposer fraction-g pour toutes les fractions verticales ;
  • lancer python backend/tools/validate_svg_quality.py backend/contenus_pedagogiques après chaque génération ;
  • corriger les fichiers signalés avant revue utilisateur.

À moyen terme :

  • générer les SVG depuis des descriptions structurées, par exemple *.content.json ou *.content.yaml ;
  • centraliser les primitives dans un module de génération ;
  • faire produire aux agents du contenu structuré plutôt que du SVG brut ;
  • rendre le SVG final reproductible à partir de la source.

À long terme :

  • ajouter une capture raster automatique en CI pour détecter les chevauchements visuels réels ;
  • comparer certains rendus à des snapshots de référence ;
  • étendre les primitives aux droites graduées, bandes partagées, tableaux de nombres, zones de réponse et schémas en barres.

Évaluation

L'architecture actuelle est acceptable pour afficher et relire des supports déjà produits. Elle n'est pas suffisante pour produire régulièrement des contenus mathématiques propres.

La bonne direction n'est pas de remplacer SVG, mais de déplacer l'intelligence avant le SVG final : composants mathématiques, contraintes géométriques, validation automatique, puis rendu.