Tutoriel pour générer du SVG avec du XSLT
Introduction
Ce tutoriel est une traduction de cet article sur EduTechWiki (en). Ce tutoriel vous apprend à générer des graphiques SVG à partir de données XML. Nous produirons du pur SVG et du HTML5 intégré dans le SVG.
Objectifs d'apprentissage
- Comprendre le but du XSLT, par exemple être capable de penser au XSLT comme étant un langage de traduction.
- Faire des transformations simples à partir d'un XML à un HTML.
- Être capable d'utiliser des expressions simples XPath (balise et nom d'attribut) dans les sélecteurs de modèle, pour des éléments et pour l'extraction d'attributs.
- Pré requis
- Édition en XML (être capable d'utiliser une DTD simple). Rattraper le retard avec le tutoriel sur l'édition XML Editing XML tutorial
- XML namespaces (certains devront regarder l'article sur les XML namespace)
- HTML et CSS (quelques connaissances)
- Tutoriel XSLT débutant
- Tutoriel XPath
- Tutoriel SVG statique (important)
- Tutoriel SVG avec HTML5
- Comprendre ce qu'est une image SVG
- SVG/SMIL animation tutorial (optionnel)
- Prochaines étapes
- XPath tutorial - basics
- XQuery tutorial - basics (si vous avez de l'intérêt pour les bases de données XML)
- PHP - MySQL - XML tutorial - basics (montre comment afficher un ensemble de résultats XML récupérés à partir de MySQL avec du XSLT)
Mon premier SVG à partir du XML
Générer du SVG avec du XSLT est réellement simple si vous maitrisez quelques bases de la programmation en XSLT comme il est expliqué dans le Tutoriel XSLT débutant. Bien sûr, vous devriez aussi avoir quelques bases en SVG.
Au moment où cet article est écrit, vous pouvez utiliser le SVG de trois façons différentes sur le Web:
- Intégrer du SVG dans du XHTML 1. Cela nécessite que vous utilisiez le XHTML en tant que XML. Si vous ne comprenez pas ce que cela veut dire, passez cette option.
- Générer un pur fichier SVG. Tous les navigateurs modernes peuvent l'afficher.
- Générer un fichier HTML5 contenant du SVG. Tous les navigateurs modernes peuvent l'afficher.
Exemple SVG niveau 0
Nous allons commencer par montrer comment produire quelques graphiques SVG à partir du contenu d'un document XML simple. Nous montrerons ensuite comment procéder pour du 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 traduire ceci en un rectangle. Pour cela, nous devons écrire un template qui va extraire le contenu de la hauteur (height) et de la alrgeur (width), pour ensuite l'utiliser dans la définition des dimensions du rectangle SVG. Les débutants doivent savoir que les noms de ces balises XML importent peu, on aurait aussi pu appeler ces éléments a et b.
Ci-dessous se trouve le code XSLT. Comme vous pouvez le voir, il est assez simple. En ce qui concerne la transformation du XSLT à l'HTML, il y a deux différences:
- Nous devrions déclarer l'espace (namespace) SVG dans l'élément racine xsl:stylesheet en haut. C'est pourquoi nous avons ajouté cette ligne: xmlns="http://www.w3.org/2000/svg".
- Nous devrions configurer ce qui est produit en sortie, sinon le navigateur pourrait ne pas vouloir afficher le code SVG. C'est pourquoi nous avons ajouté la commande <xsl:output ... />
<?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 template qui traite de "thing": <xsl:template match="thing">. Nous n'avons pas besoin d'un template pour l'élément racine ( / ). Dans le code au-dessus, le template XSL pour "thing" va générer la balise SVG au plus haut niveau.
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
.....
</svg>
Ensuite, nous avons simplement à définir un rectangle SVG et définir les valeurs des attributs SVG pour la largeur width et la hauteurheight avec les valeurs que nous avons extrait à partir du XML. En d'autres termes, {width} et {height} vont être remplacés par le texte trouvé entre les balises <width> et <height> dans le document XML.
<rect x="10" y="10" width="{width}" height="{height}" fill="red" stroke="black"/>
Code (peut comprendre de légères variations):
Exemple HTML5 niveau 0
Maintenant produisons le HTML5. Le principe est exactement le même, sauf que la déclaration pour le output est un peu différente et que le template est plus long car nous devons produire 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>
Code (peut comprendre de légères variations):
Visualisation de nombres
L'exemple suivant (cliquez ici pour voir le résultat) montre comment remplacer un nombre par une barre qui apparait dans un élément de la liste.
Dans le XML ci-dessous, nous aimerions montrer les contenus de la balise age de façon 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 simplement "des règles basées" sur ce qui a été codé dans le fichier XSLT. Le seul template qui nous intéresse concerne la balise age. En outre, nous ajoutons un peu de CSS pour modifier l'apparence des balises HTML, par exemple les boites div avec une bordure, et nous laisserons la balise racine SVG flotter sur la 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 des expressions XPath complexes. Remarquez qu'une variable est utilisée dans une expression XPath avec un signe $ juste avant, par exemple $years.
<xsl:variable name="years" select="."/>
La partie SVG ressemble donc au code ci-dessous. Remarquez comment la hauteur du rect (rectangle) est calculée: l'âge multiplié par 1.2 ({$years*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>
Ci-dessous 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 source:
Un exemple plus complexe est montré plus loin sous la section Créer des fiches simples à partir de structures d'informations tabulaires.
Créer un diagramme bâton (bar chart) simple
L'intérêt de l'exemple présenté à la section précédente est relativement limité. Dans les données XML, il y a généralement davantage de nombres. Nous verrons dans cette section ce qu'il est possible de faire avec une simple liste de nombres.
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>
Une telle liste rendrait bien avec un diagramme à bâton.
C'est ce que le code ci-dessous permet de faire. Bien sûr c'est un exemple qui reste simple, un code plus complet devrait également calculer les paramètres de la hauteur et de la largeur en prenant en compte nos données et l'espace de dessin que nous voulons utiliser.
La différence entre l'exemple "zéro" et celui-ci est que nous faisons quelques calculs simples et que nous utilisons aussi les fonctions XSLT count() et position().
- La coordonnée y du rectangle est 100 moins le nombre trouvé dans le XML {100- .}
y="{100- .}
- Nous utilisons la fonction count() pour savoir le nombre d'éléments item que nous avons dans la liste (balise list).
<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 positions dans la liste XML:
<rect x="{10*position()}" y="{100- .}" width="10"
height="{.}" fill="red" stroke="black"/>
Le code source complet se trouve ci-dessous. Nous ne discuterons pas de 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>
Les exemples (avec un rectangle en arrière plan inclus)
Nous pourrions aussi produire une version pour les programmeurs effrayés par la programmation basée sur des règles. Pour cela, 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>
Exemple:
Créer un diagramme bâton simple et flexible
L'exemple précédent présente un défaut : la hauteur et la largeur des barres ne s'ajustent pas automatiquement à:
- La taille du canevas SVG
- Le nombre d'items
- La valeur maximum d'un item
Le résultat devrait ressembler à l'image à droite. Le diagramme bâton devrait combler le canevas fournis par le SVG. Bien sûr nous voulons aussi être capable de traiter n'importe quel ensemble (raisonnable) de nombre positifs, c'est-à-dire que le fichier XML pourrait inclure plus ou moins d'items et aussi des valeurs différentes.
Comme cet exemple inclus quelques éléments un peu plus complexes, et introduit des variables XSLT et des traitements de listes, vous pouvez le passer et y revenir plus tard.
Premièrement, nous allons permettre aux utilisateurs de cette feuille de style de définir la taille du canevas avec des variables 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"/>
Remarque: Les variables XSLT ne sont pas vraiment des variables, par exemple vous ne pouvez pas réaffecter une valeur dans le même contexte une fois qu'elle a été fixée durant le temps d'exécution. Par exemple, l'instruction suivante:
<xsl:variable name="qual" select="engagement"/> va lier le contenu de l'élément '^'engagement à une variable qui peut être rappelée en utilisant le signe $, par exemple $qual dans notre cas. À chaque fois que le XSLT exécute un template, un nouveau $qual lié sera créé. |
Retour à notre projet. Sachant que la taille du canevas est une des informations dont nous avons besoin pour manipuler la largeur, la hauteur et la position des bâtons. Nous devons ensuite déterminer combien d'items nous avons obtenu. L'expression suivante va calculer la largeur d'un bâton comme étant la largeur du canevas SVG en soustrayant un peu de marge, 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 avons aussi besoin d'une sorte de "y-step" qui, multiplié par le nombre dans la balise item, va définir la hauteur du bâton. Cela va dépendre du nombre le plus grand contenu dans l'ensemble des balises item.
Trouver un maximum avec XSLT 1.0 est un casse-tête. Typiquement, pour de tels problèmes, vous pouvez Google pour une réponse ou prendre par exemple une solution provenant d'un site Web comme Stack Overflow.
L'expression suivante va calculer la hauteur du bâton:
<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 maintenant simple. Voici le template 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>
Exemple:
Exemple W3C depuis la spécification XSLT
La spécification XSLT 1.0 inclut un exemple de diagramme bâton que nous avons légérement modifié et adapté au HTML5. Cet exemple montre comment produire du texte en sortie, utilise un seul template avec une construction "for-each" ("pour chaque" élément) à la place de templates qui définissent comment traduire les éléments XML.
Créer des cartes simples à partir d'informations tabulaires
Regardez d'abord le résultat:
Ce fichier permet de visualiser une structure d'information simple pour des projets. Le fichier XML inclut principalement deux listes:
- participants: Chaque participant a un prénom, un niveau d'engagement et une description
- tasks: Chaque task (tâche) inclut un titre, une description, un members (membre) qui renvoie aux participants, un niveau de difficulté et un 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>
Ci-dessous se trouvent les deux fragments XSLT les 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 source:
Travaux en cours
(Certains des exemples ci-dessous ont besoin de davantage de documentation, et d'autres ne fonctionnent pas)
En cours
- Exemples de SVG dans XSLT
- XML, XSLT and SVG links: ajouter un lien à une image SVG et du texte dans XML et XSLT.
Projet de visualisation:
Autres:
- XSLT use with PHP 5. Cet exemple montre comment appliquer une feuille de style (stylesheet) XSLT à un fichier XML, afin de produire un SVG avec PHP5. Pour cela, PHP5 doit pouvoir lire le XML et le XSLT dans un objet DOM.
Cercles
Liens
Tutoriels
- How to turn an XML file into SVG using XSL?, Ensemble de Questions/Réponses, 2011.
Tutoriels obsolètes
Les liens ci-dessous pointent vers des textes qui contiennent des informations utiles car ils tentent des choses plus ambitieuses. Cependant, certaines parties sont obsolètes, en particulier les plaintes concernant les mauvaises implémentations de SVG et de l'utilisation du XSLT côté serveur à la place de son utilisation côté client. Le code XSLT en soit me semble correct, mais je ne suis pas un expert que ce soit en SVG ou en XSLT - Daniel K. Schneider (talk) 19:00, 20 March 2013 (CET)
- Using XSLT and SVG together: a survey of case studies by Max Froumentin, Vincent Hardy, W3C, W3C/Sun Microsystems Inc. (2004). J'ai pris quelques idées de l'introduction pour mon introduction.
- Using XML by J. David Eisenberg July 19, 2002 in AList Apart.
- XSLT pour SVG (en français)
- Hands-On SVG Tutorial Using XML and XSLT by Duane Odom
- Automated Tree Drawing: XSLT and SVG by Jirka Kosek, September 08, 2004. Cet article intéressant est uniquement adapté pour de réels programmeurs, car il inclut du code sur le parsing de chaine de caractères.
- Réaliser un histogramme en SVG (XSLT 1.0) by Emmanuel Lazinier (2001). Un diagramme bâton en français, mais l'explication du code est un peu simpliste ....
- Using XSLT to create SVG content from carto:net. J'ai un peu réglé le code: xml, XSLT.
Bon exemples
- extending SVG with XSLT
- Par exemple: la 3D Pie Chart. Si vous aimez les mathématiques, jetez un œil au code XSLT code, si vous avez des connaissances en mathématiques regardez le simple bar chart et son stylesheet
- XSLorenz Structure fractale de Lorenz Attractor utilisant seulement un document XML stylé par une feuille XSLT liée, by Matt Gibson (2011).