Génération automatique de numérotation, table des matières et références

Voici un ensemble de nouveaux raccourcis typographiques pour gérer les titres de sections de façon plus évoluée. On obtient ainsi des titres à plusieurs niveaux, automatiquement numérotés et recensés avec une table des matières.

Nouveaux Raccourcis

Avec Tex (par exemple), on peut définir un certain nombre de titre de section et sous section. Ils seront ensuite numérotés automatiquement et si c’est nécessaire, une table des matières sera construite.

Par contre, dans SPIP, on ne peut définir qu’un niveau de section avec le raccourcis : {{{ma section}}}. Cette contrib utilise les points d’entrée dans inc_texte.php3 pour ajouter trois nouveaux raccourcis :

  1. {{{* ma section <référence>}}} {{{** ma sous section}}} etc...
  2. #TABLEMATIERES
  3. <référence>

Le premier jeux de raccourcis permet de faire une hiérarchisation des sections dans l’article. On pourra ainsi tapper :

{{{* Section de haut niveau }}}

du texte plein ...

{{{** Section de 2e niveau }}}

du texte plein ...

{{{*** Section de 3e niveau }}}

du texte plein ...

{{{*** autre Section de 3e niveau }}}

du texte plein ...

{{{* une autre Section de haut niveau }}}

du texte plein ...

et l’on obtiendra :


1 Section de haut niveau

du texte plein ...

1.1 Section de 2e niveau

du texte plein ...

1.1.1 Section de 3e niveau

du texte plein ...

1.1.2 autre Section de 3e niveau

du texte plein ...

2 une autre Section de haut niveau

du texte plein ...


La balise #TABLEMATIERES placée dans un article sera, quant à elle remplacée, par une liste des sections, avec des liens vers l’emplacement des sections dans la page [1]. Par exemple :


  • 1 Section de haut niveau
    • 1.1 Section de 2e niveau
      • 1.1.1 Section de 3e niveau
      • 1.1.2 autre Section de 3e niveau
  • 2 une autre Section de haut niveau

La dernière balise permet de référencer un numéro de section. Comme en Tex, on peut associer un raccourci à chaque section, grâce à : {{{** ma section <raccourci>}}}. On peut ensuite mettre dans le texte une référence qui sera plus tard remplacée par le numéro de la section et un lien dans le document.

on peut trouver un exemple ici.

Limitations

Deux sections ne peuvent pas avoir exactement le même titre (en comptant les espaces). On peut contourner cette limitation en mettant des espaces de plus à la fin de chaque titre.

On ne peut pas utiliser de < dans les titres de section. De même, pas de raccourcis docXXX, embXXX ou imgXXX.

Si vous voulez m’aider à réécrire cette expression régulière :

(($debut_intertitre(\\*+)([^<]*)(<([^>]*)>)?$fin_intertitre))

ça pourrait aider.

Code

Ce code s’insère dans mes_options.php3.

/*
 *   +----------------------------------+
 *    Nom :   Table des matieres                                               
 *   +----------------------------------+
 *    Date : aout 2004
 *    Auteur :  Mortimer Porte mortimer.pa@free.fr                                     
 *   +-------------------------------------+
 *    Fonctions de ce filtre :
 *     affiche une table des matières et génère automatiquement la numérotation des titres.
 *   +-------------------------------------+ 
 *  
 * Pour toute suggestion, remarque, proposition d'ajout
 * reportez-vous au forum de l'article :
 * http://www.spip-contrib.net/article.php3?id_article=627
*/
function table_des_matieres($texte) {
  global $debut_intertitre, $fin_intertitre;

   // définition de la balise pour les titres des sections %num% sera remplacé 
  // par la profondeur de la section
  $css_debut_intertitre = "\n<h3 class=\"spiphead%num%\">";
  $css_fin_intertitre = "</h3>\n";

  // on cherche les noms de section commençant par des *  
  $count = preg_match_all("(($debut_intertitre(\\*+)(.*?)(<(.*?)>)?$fin_intertitre))",$texte,$matches);
  $table = '';
  //initialisation du compteur
  $cnt[0] = 0;
  //initialisation du code de la table des matières
  $table = "\n<ins><div id=\"tablematiere\">\n";
  $lastlevel = 1;
  $cite[''] = '';

  //pour chaque titre trouvé
  for ($j=0; $j< $count; $j++) {
	$level = $matches[2][$j];
	$titre = $matches[3][$j];
	$ref = $matches[5][$j];
	if(strlen($level) == 1) {
           //on est au niveau de base
          //on réinitialise les compteurs
	  for ($i=1; $i < count($cnt); $i++) {
		$cnt[$i] = 0;
	  } 
          //on génère le titre et le numéros
	  $numeros = ++$cnt[0];
	  $titre = $numeros.' '.$titre;
	} else {
           //on est à un niveau plus profond
          // on construit le numéros
	  $numeros = $cnt[0].'.';
	  for ($i=1; $i < strlen($level)-1; $i++) {
		$numeros .= $cnt[$i].".";
	  }
	  $numeros = $numeros.(++$cnt[$i]);
          //on génère le titre
	  $titre = $numeros.' '.$titre;
	}

        //gestion de la liste dans la table
	if($lastlevel < strlen($level)) {
          //on ouvre une sous liste
	  $table .= "<ul>\n";
	}
	if($lastlevel > strlen($level)) {
          //on doit fermer les derniers niveaux
	  for ($i=0; $i < ($lastlevel - strlen($level)); $i++) {
             if($i+1==$lastlevel) {
               $table .= "\n</div></ins>";	// derniere fermeture
             } else {
               $table .= "</li>\n</ul>"; 
             }	
	  }
	}
	if($lastlevel >= strlen($level)) {
          //on doit fermer l'item précédent
	  if($cnt[0] > 1 || strlen($level) > 1) {
		$table .= "</li>\n";
	  }
	} 
       //on se rappelle du raccourcis
	$cite[$ref] = $numeros;
	$table .= "<li><a href=\"#$numeros\">$titre</a>";

       //on mémorise le niveau de ce titre
	$lastlevel = strlen($level);

        //on génère la balise avec le bon style pour le niveau
	$mdebut_intertitre = str_replace('%num%',$lastlevel,$css_debut_intertitre);
	$mfin_intertitre = str_replace('%num%',$lastlevel,$css_fin_intertitre);
        //on remplace le titre dans le texte
	$texte = str_replace($matches[0][$j],"$mdebut_intertitre<a name='$numeros'></a>$titre$mfin_intertitre",$texte);
  }
  //on fini la table
  for ($i=0; $i < $lastlevel; $i++) {
	$table .= "</li>\n</ul>";		
  }
  //on remplace les raccourcis par les numéros des sections.
  foreach ($cite as $ref => $num) {
	$texte = str_replace("<$ref>","<a href=\"#$num\">$num</a>",$texte);	
  }
  //on place la table des matières dans le texte
  $texte = str_replace('#TABLEMATIERES',$table,$texte);
  return $texte;
}

Il faut ensuite l’appeler depuis apres_propre, par exemple :

function apres_propre($texte) {
  $new_texte = table_des_matieres($texte);
  return $new_texte;
}

Style

le style de la table des matières ainsi que des différents niveaux sont gérés par du css. Il faut donc ajouter à vos fichier css, les classes spiphead1, spiphead2, ... (autant que de niveaux utilisés) ainsi que l’id #tablematiere

Notes

[1en utilisant des ancres html

Dernière modification de cette page le 21 janvier 2007

Discussion

14 discussions

  • Désolé pour le flood... petit bug chez moi.

    Répondre à ce message

  • Ca marche plutot pas mal, si ce n’est quelques petits bugs.

    1er :
    Probleme avec l’ordre des balises ul et li qui se chevauchent et qui font donc un code non standard et parfois maltraitent un peu le design de la page web.

    Faudrait modifier un peu le code mais je n’ai pas le temps pour l’instant...

    2e :
    C’est pas vraiment un bug mais il y a une balise ins dont je ne vois pas l’utilité ?

    3e :
    Pas non plus un bug dû à ce code mais il y a toujours un problème dû au code qui s’inclus dans un paragraphe généré par spip (p class=« spip »)

    4e :
    Intertitres de niveaux différents, c’est bien mais du point de vue de l’accessibilité ça reste tous des titres de niveau h3 alors que ça devrait etre h3 puis h4 puis h5 puis h6.

    Voilà, quelques conseils pour améliorer ce code :)

    Répondre à ce message

  • Ca marche plutot pas mal, si ce n’est quelques petits bugs.

    1er :
    Probleme avec l’ordre des balises ul et li qui se chevauchent et qui font donc un code non standard et parfois maltraitent un peu le design de la page web.

    Faudrait modifier un peu le code mais je n’ai pas le temps pour l’instant...

    2e :
    C’est pas vraiment un bug mais il y a une balise ins dont je ne vois pas l’utilité ?

    3e :
    Pas non plus un bug dû à ce code mais il y a toujours un problème dû au code qui s’inclus dans un paragraphe généré par spip (p class=« spip »)

    4e :
    Intertitres de niveaux différents, c’est bien mais du point de vue de l’accessibilité ça reste tous des titres de niveau h3 alors que ça devrait etre h3 puis h4 puis h5 puis h6.

    Voilà, quelques conseils pour améliorer ce code :)

    Répondre à ce message

  • Ca marche plutot pas mal, si ce n’est quelques petits bugs.

    1er :
    Probleme avec l’ordre des balises

      et

      qui se chevauchent et qui font donc un code non standard et parfois maltraitent un peu le design de la page web.

      Faudrait modifier un peu le code mais je n’ai pas le temps pour l’instant...

      2e :
      C’est pas vraiment un bug mais il y a une balise dont je ne vois pas l’utilité ?

      3e :
      Pas non plus un bug dû à ce code mais il y a toujours un problème dû au code qui s’inclus dans un paragraphe généré par spip

      4e :
      Intertitres de niveaux différents, c’est bien mais du point de vue de l’accessibilité ça reste tous des titres de niveau h3 alors que ça devrait etre h3 puis h4 puis h5 puis h6.

      Voilà, quelques conseils pour améliorer ce code :)

      Répondre à ce message

    • Bonjour

      très bonne contrib, mais quand je place la « balise » #TABLEMATIERES au début de l’article (champ texte) tout le texte de l’article apparait souligné !!

      ceci disparaît quand je place la « balise » à la fin du texte ????

      mystère, qui peut m’aider
      merci

      Jean

      Répondre à ce message

    • 2

      Bonjour,
      Cette contribution m’interresse bien, mais je n’arrive pas à l’installer correctement (j’utilise Windows XP et Spip V 1.8.2 e avec le squelette Epona).
      Dans mes articles, J’obtiens bien les différents niveaux de titres numérotés, mais je n’ai pas la table des matières (j’ai pourtant installé la balise #TABLEMATIERES dans mon squelette, et l’id tablematiere dans ma feuille de style).
      Quelqu’un pourrait-il m’aider ? D’avance merci. JFD

      • Non, avec le code fournit ici, la balise pour générer la table des matières doit être placée quelque part dans le texte de l’article et pas dans le squelette.

        James à produit un code différent qui permet de placer la balise n’importe où dans le squelette. Il y a une version mais je crois qu’elle ne marche qu’avec la version de dévelopement.

      • Ca marche ! Et merci pour la rapidité de votre réponse.
        Il me reste juste un problème (je ne suis pas un champion des css) : tout le texte qui suit la balise #TABLEMATIERES se retrouve en souligné. Si vous pouvez encore m’aider ...
        JFD

      Répondre à ce message

    • Bravo pour cette contrib’ très utile (à condition d’utiliser l’astuce des « trim », voir le message 6)...

      Pour ma part je viens de la mettre en « production » avec une petite modification. D’abord, il me paraissait plus logique que le raccourci pour un titre numéroté soit {{{# plutôt que {{{*, c’est plus compatible avec le système de puces hiérarchisées (-* pour les puces non numérotées et -# pour les puces numérotées). D’autre part, je voulais permettre à mes rédacteurs d’intégrer des titres numérotés (avec des #) et des titres non numérotés ou avec des numérotations personnelles genre a, b, c... (avec des *).

      Je suis ultra-débutant en php (et en programmation en général) ce qui m’a amené à faire les modifs suivantes :

      // on cherche les noms de section commençant par des * et #
        //j'ai rajouté le # dans l'expression régulière [\\*#]+ à la place de \\*+
        $my_debut_intertitre=trim($debut_intertitre); //merci à fleg ! :o)
        $my_fin_intertitre=trim($fin_intertitre);
        $count = preg_match_all("(($my_debut_intertitre([\\*#]+)(.*?)(<(.*?)>)?$my_fin_intertitre))",$texte,$matches);

      et plus bas :

      //on génère le titre et le numéros
                $numeros = ++$cnt[0];
              //on teste si le(s) caractère(s) matché est # pour savoir si l'on affiche les
              //numéros avec le titre ou non (#->numéros affichés)
                if(preg_match("/#+/",$matches[2][$j])) {$titre = $numeros.' '.$titre;}

      et encore plus bas :

                $numeros = $numeros.(++$cnt[$i]);
                //on génère le titre
              //on teste si le(s) caractère(s) matché est # pour savoir si l'on affiche les
              //numéros avec le titre ou non (#->numéros affichés)
      
                if(preg_match("/#+/",$matches[2][$j])) {$titre = $numeros.' '.$titre;}

      La première modif permet de détecter les # comme les * ; les deux modifs suivantes permettent de ne mettre la numérotation que si le raccourci contient #

      Compte tenu de l’intégration très sale de ma modif, il n’est pas conseillé —du tout— d’utiliser des # et des * dans le même article, car des numérotations étranges seraient à prévoir (des fois ça donne des trucs vraiment très drôles ! :o))

      Pour finir je trouvais plus propre que les titres soient de la forme <hx class="spip"> avec x>2 (comme l’écrit Triangle dans le message 5) donc voici ce que j’ai changé :

        //pour que les raccourcis soient remplacés par des headlines (<hx>)
        $css_debut_intertitre = "\n<h%num% class=\"spip\">";
        $css_fin_intertitre = "</h%num%>\n";

      et plus bas :

              //on ajoute 2 à $lastlevel pour avoir des <hx> qui commencent à <h3> et plus
              $mdebut_intertitre = str_replace('%num%',$lastlevel+2,$css_debut_intertitre);
              $mfin_intertitre = str_replace('%num%',$lastlevel+2,$css_fin_intertitre);

      Merci encore pour cette belle contrib’ !

      Répondre à ce message

    • 1

      Salut,
      Très bien cette contrib.
      La seule chose est que dans certains cas complexes (par ex une chapitre qui n’est composé
      que d’une liste énuméré, ou l’appel à des filtres qui changent un peu la mise en page),
      il y a des petits problèmes de titres de chapitres qui ne sont pas reconnus.
      Pour ma part, j’ai résolu ce problème en insérant les trim suivants :
      Je remplace la ligne :

      $count = preg_match_all("(($debut_intertitre(\\*+)(.*?)(<(.*?)>)?$fin_intertitre))",$texte,$matches);

      par

        $my_debut_intertitre=trim($debut_intertitre);
        $my_fin_intertitre=trim($fin_intertitre);
        $count = preg_match_all("(($my_debut_intertitre(\\*+)(.*?)(<(.*?)>)?$my_fin_intertitre))",$texte,$matches);

      Si ça peut en aider certains.

      F.

      • Très bonne idée ces trim, celà s’avère assez vite indispensable !

        Merci !

      Répondre à ce message

    • 2

      Bonjour,

      Je voudrais savoir savoir où il faut mettre le code :

      function apres_propre($texte) {
       $new_texte = table_des_matieres($texte);
       return $new_texte;
      }

      Personnellement, je l’ai mis à la fin su fichier ecrire/inc_texte.php mais j’obtiens alors un message d’erreur me disant qu’il y a un appel à une fonction non définie.

      J’ai donc du oublier quelque chose.

      Merci d’avance

      Pascal

      • Ce qui n’allait pas, c’est que je ne savais pas que le fichier mes_options.php3 devait aussi se trouver dans le répertoire ecrire !

        Pascal

      • il faut mettre la fonction apres_propre dans le fichier ecrire/mes_options.php3 (à créer si nescessaire)

      Répondre à ce message

    • Bonjour,
      personnellement, dans des articles longs, j’aime pouvoir revenir à la table des matières par un petit lien de retour vers celle-ci.

      À cette fin, j’ai donc modifier la fin de votre code de la manière suivante :

      //on place la table des matières dans le texte
        $table_ancre = '<a name="tablematieres"></a>'.$table;
        $texte = str_replace('#TABLEMATIERES',$table_ancre,$texte);
        
        //On remplace la balise de Retour à la table des matières
        $retour = '<div class="retourtable"><a href="#tablematieres">Retour à la table des matières</a></div>';
       $texte = str_replace('#RETOUR',$retour,$texte);
         
        return $texte;
      }

      En utilisant une nouvelle balise #RETOUR dans le texte de l’article, cette dernière est remplacée par un lien vers la table des matières. Le texte est dans une boite div avec l’attribut CSS class=’retourtable’. On peut ainsi le faire s’afficher à droite si on le désire, et le masquer dans le cas d’une impression (si on utilise une feuille de style spécifique).

      Le texte est en français. Je suppose qu’il doit être possible de l’adapter à un squelette multilingue à l’aide des balises de langues, mais je ne sais pas si SPIP procède au remplacement des balises de langues avant ou après avoir appliqué le filtre.

      D’autre part, je n’ai pas encore regardé comment intégrer cette contrib dans la génération automatique de PDF (Génération améliorée de fichiers PDF). Peut-être le plus simple consiste à écrire un second filtre qui au lieu de remplacer par des balises h3 avec des class CSS différentes, remplace par des balises h3, h4, h5... puisque FPDF ne gère que du code HTML et pas le CSS.

      Si vous avez une meilleure idée.

      Cordialement,

      Répondre à ce message

    Ajouter un commentaire

    Qui êtes-vous ?

    Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

    Ajoutez votre commentaire ici

    Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

    Ajouter un document

    Suivre les commentaires : RSS 2.0 | Atom