Tutoriel XSLT pour générer du SVG

De EduTech Wiki
Aller à : navigation, rechercher


1 Introduction

Ce tutoriel va vous apprendre comment générer des graphiques SVG à partir de données XML.

Objectifs d'apprentissage

  • Comprendre le but de XSLT, c'est-à-dire être capable de penser le XSLT comme une langue de traduction.
  • Faire de transformations simples de XML en HTML.
  • Etre capable d'utiliser des expressions XPath (noms de balises et attributs) dans les sélecteurs d'éléments et pour l'extraction d'éléments et d'attributs.
Pré-requis
Prochaines étapes

2 Mon premier SVG à partir d'XML

Générer du SVG avec XSLT est très facile si vous maîtrisez les basiques de la programmation XSLT, comme expliqué dans Tutoriel XSLT débutant. Bien-sûr, vous avez aussi besoin de quelques bases en SVG.

Pour le moment, vous pouvez utiliser du SVG de trois manières différentes sur le web:

  1. Intégrer du SVG dans XHTML 1. Cela exige que vous vous serviez d'XHTML en tant qu'XML. Si vous ne comprenez pas ce que cela signifie, passez cette option.
  2. Générer un pur fichier SVG. Tous les navigateurs modernes peuvent l'afficher.
  3. Générer un fichier HTML5 avec du SVG inline. Generate an HTML5 file with inline SVG. Tous les navigateurs modernes peuvent l'afficher.

2.1 Exemple SVG niveau 0

Nous allons d'abord montrer comment produire du SVG graphique à partir du contenu d'un document XML simple. Ensuite, nous allons montrer comment faire ceci pour HTML5.

Prenez le fichier XML suivant:

<?xml version="1.0" ?>
<?xml-stylesheet href="zero.xsl" type="text/xsl" ?>
<thing>
  <height>50</height>
  <width>100</width>
</thing>

Nous pouvons imaginer que cela va donner un beau rectangle rouge. Nous devons écrire un modèle qui va extraire le contenu de la hauteur (height et de la largeur (width), puis l'utiliser pour définir les dimensions du rectangle SVG. Les débutants devraient remarquer que les noms de ces balises XML n'ont pas d'importance, nous aurions également pu appeler ces éléments a et b ...

Voici le code XSLT. Comme vous pouvez le voir, il est assez simple. En ce qui concerne les transformations XSLT à HTML, il ya deux différences:

  • Nous devons déclarer l'espace de SVG dans l'élément xsl: racine stylesheet en haut
  • Nous devons configurer la sortie produite, le reste de votre navigateur peut ne pas vouloir afficher le code SVG.
<?xml version="1.0"?>

<xsl:stylesheet version="1.0" 
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns="http://www.w3.org/2000/svg"
		>
  <xsl:output
      method="xml"
      indent="yes"
      standalone="no"
      doctype-public="-//W3C//DTD SVG 1.1//EN"
      doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
      media-type="image/svg" />
  
  <xsl:template match="thing">
    <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
      <rect x="10" y="10" width="{width}" 
	    height="{height}" fill="red" stroke="black"/>  
    </svg>
  </xsl:template>
</xsl:stylesheet>

Maintenant examinons le modèle qui traite de thing. Nous n'avons pas besoin d'un modèle pour l'élément racine(/). Dans le code ci-dessus, le modèle de XSL pour générer thing sera la balise SVG du plus haut niveau.

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
   .....
</svg>

Ensuite, il suffit de définir un rectangle SVG et définir les valeurs des attributs SVG pour la largeur et la hauteur avec les valeurs que nous avons extraites du XML. En d'autres termes, {width} and {height} vont être remplacés par le texte trouvés dans les balises <width> et <height> du document XML.

<rect x="10" y="10" width="{width}" height="{height}" fill="red" stroke="black"/>

Testez le code (peut inclure de légères variations):

2.2 Exemple HTML5 niveau 0

Maintenant nous allons produire du HTML5. Le principe est exactement le même, mais la déclaration de sortie est un peu différente et le modèle est plus long, puisque nous produisons plus de code.

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" 
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns="http://www.w3.org/2000/svg"
		>
<xsl:output
     method="xml"
     doctype-system="about:legacy-compat"
     omit-xml-declaration = "yes"
     encoding="UTF-8"
     indent="yes" />
  
  <xsl:template match="thing">
   <html xmlns="http://www.w3.org/1999/xhtml">
     <head>
       <meta charset="utf-8"></meta>
       <title>XHTML5 + SVG example</title>
     </head>
     <body>
       <p>This line is HTML, embedded SVG is below. Read the <a
       href="http://edutechwiki.unige.ch/en/XSLT_to_generate_SVG_tutorial">
       XSLT to generate SVG tutorial</a></p>

       <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
	 <rect x="10" y="10" width="{width}" 
	       height="{height}" fill="red" stroke="black"/>  
       </svg>
     </body>
   </html>
  </xsl:template>
  
</xsl:stylesheet>

Live Code (peut inclure de légères variations):

2.3 Visualiser quelques chiffres

L'exemple suivant (look at it) vous montre comment substituer un nombre par une barre qui apparaît dans les éléments de la liste.

Dans le XML ci-dessous, nous voulons traiter le contenu de l'élément age d'une manière plus intéressante :

<?xml version="1.0"?>
<?xml-stylesheet href="one-html5.xsl" type="text/xsl"?>
<project>
 <title>Simple XML to SVG demo</title>
 <people>
  <person>
    <FirstName>Kaspar</FirstName>
    <age>50</age>
    <description>A small man with a green tie</description>
  </person>
  <person>
    <FirstName>Jonathan</FirstName>
    <age>9</age>
    <description>A young person</description>
  </person>
  <person>
   <FirstName>Julie</FirstName>
   <age>30</age>
   <description>A young woman</description>
  </person>
 </people>
</project>

Le XSLT utilise une programmation XSLT assez simple, "fondée sur des règles". Le seul modèle d'intérêt concerne l'élément de l'âge. De plus, nous ajoutons un peu plus de style CSS pour les éléments HTML, par exemple, des boîtes div avec des bordures et nous allons aussi laisser l'élément SVG tout en haut, flotter à droite.

Tout d'abord, nous stockons la valeur de l'élément en question (c'est-à-dire, les contenus) de l'âge dans une variable appelée years. Ce n'est pas nécessaire ici, mais cela permet de simplifier les expressions XPath complexes. Notez qu'une variable est utilisée dans une expression XPath avec un signe $ devant, par exemple, $years.

 <xsl:variable name="years" select="."/>

La partie du SVG ressemble alors à ceci. Notez que la hauteur rect est calculée : age*1.2

   <svg style="background-color:yellow;float:right" width="20" height="100" 
	xmlns:xlink="http://www.w3.org/1999/xlink" 
	xmlns="http://www.w3.org/2000/svg">
     <rect x="5px" y="5px" height="{$years*1.2}" width="10px" fill="green" />
   </svg>

Voici le code XSLT complet :

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:xlink="http://www.w3.org/1999/xlink" 
   xmlns="http://www.w3.org/1999/xhtml"
   >

<xsl:output
     method="xml"
     doctype-system="about:legacy-compat"
     omit-xml-declaration = "yes"
     encoding="UTF-8"
     indent="yes" />

 <xsl:strip-space elements="tasks participants"/>

 <xsl:template match="/">
   <html xmlns="http://www.w3.org/1999/xhtml">
     <head>
       <meta charset="utf-8"></meta>
       <title>XHTML5 + SVG example</title>
     </head>
     <body>
       <p>XHTML5 contents below are generated from XML with XSLT. 
       Contents of and age elements are display in both text and with SVG as a green bar.
       Look at the source of this page for the XML and at the <a
       href="http://tecfa.unige.ch/guides/svg/ex/html5-xslt/one-html5.xsl">one-html5.xsl</a>
       file. Read the <a href="http://edutechwiki.unige.ch/en/XSLT_to_generate_SVG_tutorial">
       XSLT to generate SVG tutorial</a></p>
       <hr/>
       <xsl:apply-templates/>
     </body>
 </html>
 </xsl:template>

 <xsl:template match="people">
   <h1>People</h1>
   <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="person">
   <div style="border-style:dotted;margin:10px;padding:5px;width:200px;height:100px;">
     <xsl:apply-templates/>
   </div>
 </xsl:template>

 <xsl:template match="FirstName">
   <xsl:apply-templates/>,
 </xsl:template>

 <xsl:template match="age">
   Age: <xsl:apply-templates/>
   <xsl:variable name="years" select="."/>
   <svg style="background-color:yellow;float:right" width="20" height="100" 
	xmlns:xlink="http://www.w3.org/1999/xlink" 
	xmlns="http://www.w3.org/2000/svg">
     <rect x="5px" y="5px" height="{$years*1.2}" width="10px" fill="green" />
   </svg>
 </xsl:template>

 <xsl:template match="description">
   <p><xsl:apply-templates/></p>
 </xsl:template>

 <xsl:template match="title">
   <h1><xsl:apply-templates/></h1>
 </xsl:template>

</xsl:stylesheet>

Fichiers sources :

Vous trouverez un exemple plus complexe plus bas dans la section Création de simples cartes depuis des structures d'information tabulaire

2.4 Création d'un graphique à barres simple

L'exemple nous avons présenté ci-dessus n'était pas vraiment intéressant. Dans les données XML, nous trouvons généralement plus que deux nombres. Voyons donc ce que nous pouvons faire avec une simple liste de chiffres:

Prenez le fichier XML suivant:

<?xml version="1.0" ?>
<?xml-stylesheet href="intro.xsl" type="text/xsl" ?>
<list>
 <item>10</item>
 <item>15</item>
 <item>12</item>
 <item>20</item>
 <item>5</item>
</list>

Cette liste permettrait la création d'un bon graphique à barres:

Le code suivant va le faire. Bien sûr, il reste simple. Par exemple, un bon code serait de calculer également des paramètres hauteur et largeur prenant en compte à la fois les données et l'espace de dessin que nous voulons utiliser.

La différence entre l'exemple "zéro" et celui-ci, c'est que nous faisons un peu de maths très simples et que nous utilisons des fonctions XSLT count() et position().

  • La coordonnée y du rectangle est de 100 moins le nombre trouvé dans le fichier XML
  y="{100- .}
  • Nous utilisons la fonction count( pour déterminer le nombre d'éléments item que nous avons dans la liste.
    <rect x="10" y="105" width="{10 * count(item)}" 
	  height="5" fill="black" stroke="red"/>
  • Notez également que les espaces vides entre les éléments comptent comme des nœuds selon la norme XML. Par conséquent, vous devez supprimer ces nœuds vides avec l'instruction suivante :
  <xsl:strip-space elements="list"/>
  • Chaque rectangle est positionné dans le SVG en fonction de sa position dans la liste XML:
    <rect x="{10*position()}" y="{100- .}" width="10" 
	  height="{.}" fill="red" stroke="black"/>

Le code source complet est ci-dessous. Nous ne discuterons pas la version HTML5 ici, mais vous trouverez ci-dessous est le lien qui vous fournira le code.

<?xml version="1.0"?>
<!-- keep all three namespaces, xlink may not be needed, but the others are -->
<xsl:stylesheet version="1.0" 
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns:xlink="http://www.w3.org/1999/xlink"
		xmlns="http://www.w3.org/2000/svg"
		>
  
  <!-- **** output/input configuration -->
  
  <xsl:output
      method="xml"
      indent="yes"
      standalone="no"
      doctype-public="-//W3C//DTD SVG 1.1//EN"
      doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
      media-type="image/svg" />
  
  <!-- must remove white spaces within the list element, 
       otherwise count will not work -->
  <xsl:strip-space elements="list"/>
  
  <xsl:template match="/">
    <svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" >
      <!-- here we could draw a background rectangle -->
      <xsl:apply-templates/>
    </svg>
  </xsl:template>
  
  <xsl:template match="list">
    <rect x="10" y="105" width="{10 * count(item)}" 
	  height="5" fill="black" stroke="red"/>
    <xsl:apply-templates/>
  </xsl:template>
  
  <xsl:template match="item">
    <rect x="{10*position()}" y="{100- .}" width="10" 
	  height="{.}" fill="red" stroke="black"/>  
  </xsl:template>
  
</xsl:stylesheet>

Live example (comprenant un rectangle de fond)

Nous avons également réalisé une version pour programmeur qui ont peur de la programmation à base de règles. Vous pouvez utiliser le code comme ceci:

<xsl:template match="list">
 <xsl:for-each select="item">
    <rect x="{10*position()}" y="{100- .}" width="10" height="{.}" 
          fill="red" stroke="black"/>            
  </xsl:for-each>
  </xsl:template>

Live example:

2.5 Création d'un simple graphique à barres flexible

Screenshort d'un XML modifié en graphique à barres SVG

L'exemple précédent présente un défaut : la hauteur et la largeur des barres ne s'ajustent pas automatiquement :

  • à la taille des canevas SVG
  • au nombre d'items
  • à la valeur maximale dans un item

Le résultat devrait ressembler à l'image de droite. Le diagramme doit remplir les canevas de dessin SVG fournis. Bien sûr, nous voulons aussi être en mesure de faire face à n'importe quel jeu ( raisonnable) de nombres positifs , c'est à dire le fichier XML peut comprendre plus ou moins d'éléments et aussi des valeurs différentes .

Cet exemple introduit des variables xslt et le traitement de la liste , vous pouvez sauter et y revenir plus tard ....

Tout d'abord , nous allons permettre aux utilisateurs de cette feuille de style de définir la taille des canevas avec des variables de style XSLT.

  <!-- you could change these in any way you like -->
  <xsl:variable name="svg_width" select="400"/>
  <xsl:variable name="svg_height" select="300"/>
  <xsl:variable name="padding" select="5"/>

Note "à côté" : Les variables XSLT ne sont pas vraiment variables, c'est que vous ne pouvez pas réaffecter une valeur dans le même contexte une fois qu'il est défini lors de l'exécution. Par exemple :

<xsl:variable name="qual" select="engagement"/>

liera le contenu de l'élément d'engagement dans une variable qui peut être rappelée en utilisant le signe $ , soit $qual dans notre cas. Chaque fois que le XSLT exécute un modèle, une nouvelle liaison $qual sera créée.

Retour à notre projet. Connaître la taille des canevas est l'une des informations dont nous avons besoin pour gérer la largeur , la hauteur et la position des barres. Nous devons ensuite déterminer combien d'items nous avons obtenu. L' expression suivante va calculer la largeur d'une barre à la largeur des canevas SVG, moins certains types de remplissage, divisé par le nombre d'items :

  <!-- x-width with respect to N elements -->
  <xsl:variable name="x_width"
		select="($svg_width - 2*$padding) div count(//list/item)"/>

Nous devons aussi besoin d'une sorte de "y-step" qui multiplié par le nombre définira la hauteur. Cela dépendra du plus grand nombre dans l'ensemble des items. L' expression suivante calcule la largeur de la barre :

  <xsl:variable name ="y_steps">
    <xsl:for-each select="//list/item">
      <xsl:sort select="." data-type="number" order="descending"/>
      <xsl:if test="position() = 1">
	<xsl:value-of select="($svg_height - 2*$padding) div ."/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

Le reste est simple. Nous avons juste montré le modèle pour item:

  <xsl:template match="item">
    <rect x="{$padding + $x_width * (position() - 1) }"
	  y="{($svg_height - $padding) - $y_steps * .}" 
	  width="{$x_width}" 
	  height="{. * $y_steps}"
	  fill="red" stroke="black"/>  
  </xsl:template>

Live example:

2.6 Exemple W3C de la spécification XSLT

La spécification XSLT 1.0 comprend un exemple de graphique à barres que nous avons légèrement modifié et adapté à HTML5.

3 Création de simples cartes depuis des structures d'information tabulaire

Regardez d'abord le résultat:

Ce fichier permet de visualiser une structure simple de l'information pour des projets. Le fichier XML comprend principalement deux listes :

  • participants: Chaque participant a un prénom, un niveau d'engagement et une description
  • tâches : Chaque tâche comprend un titre, une description, les membres qui renvoient aux participants, la difficulté et le niveau d'achèvement, une liste d'idées et une liste de résultats .

Extrait du fichier XML

<?xml version="1.0"?>
<?xml-stylesheet href="xpath-jungle-links-1.xsl" type="text/xsl"?>
<project>
 <title>The XSLT and SVG project</title>
 <description>This project will explore some simple means to generate SVG from XSLT</description>
 <participants>
  <participant id="p1">
    <FirstName>Daniel</FirstName>
    <engagement>4</engagement>
    <description>Daniel will be the project manager</description>
  </participant>
  ..........
 </participants>
 <tasks>
  <task id="t1">
    <title>Initial task</title>
    <description>Project preparation</description>
    <members><member idref="p1"/><member idref="p2"/></members>
    <difficulty level="3">This task should not be too hard</difficulty>
    <completion level="8">Fairly well understood so far</completion>
    <ideas>
      <item val="low">Buy a XSLT book</item>
      <item val="low">Buy an SVG book</item>
      <item val="high">Do some exploratory tiny examples</item>
    </ideas>
    <results>
      <result><a href="http://edutechwiki.unige.ch/en/XSLT_to_generate_SVG_tutorial">Wiki page</a> started</result>
      <result><a href="http://edutechwiki.unige.ch/en/XPath_tutorial_-_basics">XPath tutorial revised</a></result>
    </results>
  </task>
  ......... 
 </tasks>
</project>

Voici les deux fragments XSLT plus importants , et qui mériteraient de plus amples explications :

 <xsl:template match="participant">
   <div style="float:left;border-style:dotted;margin:10px;padding:5px;width:200px;height:200px;">
     <xsl:apply-templates select="FirstName"/>
     <xsl:variable name="qual" select="engagement"/>
     <svg style="background-color:yellow" width="100" height="100" 
	  xmlns:xlink="http://www.w3.org/1999/xlink" 
	  xmlns="http://www.w3.org/2000/svg">
       <circle id="greencircle" cx="{$qual*5}" cy="{$qual*5}" r="{$qual*5}" fill="green" />
     </svg>
     <xsl:apply-templates select="engagement"/>
     <xsl:apply-templates select="description"/>
   </div>
 </xsl:template>
 <!-- task - programmed as a big block for change -->
 <xsl:template match="task">
   <div style="float:left;border-style:solid;margin:10px;padding:5px;width:300px">
     <!-- title -->
     <p style="font-size:120%;">    
       <xsl:value-of select="position()"/>.
       <xsl:value-of select="title"/>
     </p>
     <p><xsl:value-of select="description"/></p>

     <!-- difficulty -->
     <p>Difficulty: <xsl:value-of select="difficulty"/></p>     
     <svg width="210" height="10" 
	  xmlns:xlink="http://www.w3.org/1999/xlink" 
	  xmlns="http://www.w3.org/2000/svg">
       <rect x="5" y="1" width="{difficulty/@level * 20}" height="5" fill="red" />
       <rect x="4" y="0" width="201" height="6" fill="none" stroke="black" />
     </svg>
     
     <!-- completion -->
     <p>Completion: <xsl:value-of select="completion"/></p>
     <svg width="210" height="10" 
	  xmlns:xlink="http://www.w3.org/1999/xlink" 
	  xmlns="http://www.w3.org/2000/svg">
       <rect x="5" y="1" width="{completion/@level * 20}" height="5" fill="red" />
       <rect x="4" y="0" width="201" height="6" fill="none" stroke="black" />
     </svg>
     <xsl:apply-templates select="ideas"/>
     <xsl:apply-templates select="results"/>
   </div>
 </xsl:template>

Fichiers sources :

4 Work in progress

(certains peuvent avoir besoin de documentation ou de modifications ... d'autres ne fonctionnent pas)

... Ainsi que de vieux trucs que je dois arranger

Actuel

A réparer un jour (project visualization)

Divers

Cercles

5 Liens

Tutoriels

Tutoriels obsolètes Links below point to texts that include useful information since they attempt more ambitious stuff. However, some portions are outdated, in particular complaints about bad SVG implementations and server-side instead of client-side use of XSLT. But XSLT coding itself looks just about fine to me, but then I am not an expert in either SVG or XSLT - Daniel K. Schneider (talk) 19:00, 20 March 2013 (CET)

Bons exemples'