Une bonne galère c'te problème...
j'ai fini par trouver une solution (unique?) grâce à l'ébauche de la faq
http://www.developpez.net/forums/viewtopic.php?t=90007
Donc merci à grégory !
pour les débutants qui tomberaient ici, je vais essayer d'expliquer la solution en détail,
pour les pros, vos commentaires, conseils et corrections seront gratifiés et gratifiants.
(le code n'est pas optimisé, il est le plus clair possible,
ça fait deux jours que je débute avec ce langage,
et je suis maçon d'origine ;-)
J'en ai fait des tartines bien beurrées, c'est peut-être un peu lourd,
s'il reste des points peu clairs, n'hésitez pas à proposer des corrections.
On y va ?
Rappel de la DTD (simplifiée) :
Code :
- <!ELEMENT site (parametres,page+)>
- <!ELEMENT page (paragraphe+)>
- <!ELEMENT paragraphe (image+)>
- <!ELEMENT image (#PCDATA)>
- <!ATTLIST image l_orig NMTOKEN #REQUIRED h_orig NMTOKEN #REQUIRED>
|
Exemple de fichier xml :
Code :
- <!DOCTYPE librairie SYSTEM "generique.dtd">
- <site>
- <page>
- <paragraphe pos_page="ab2">
- <image l_orig="600" h_orig="398">exemple3.jpg</image>
- <image l_orig="600" h_orig="511">exemple4.jpg</image>
- <image l_orig="600" h_orig="426">exemple5.jpg</image>
- </paragraphe>
- <paragraphe pos_page="ab1">
- <image l_orig="600" h_orig="404">exemple1.jpg</image>
- <image l_orig="600" h_orig="404">exemple1.jpg</image>
- <image l_orig="600" h_orig="404">exemple1.jpg</image>
- </paragraphe>
- <paragraphe pos_page="ab3">
- <info>Info du paragraphe3</info>
- <image l_orig="600" h_orig="180">exemple2.jpg</image>
- <image l_orig="600" h_orig="180">exemple2.jpg</image>
- </paragraphe>
- </page>
- </site>
|
Contraintes :
Code :
- Chaque <p>paragraphe</p> doit contenir toutes les images sur une seule ligne
- en fonction de la largeur du paragraphe qui est imposée ($larg_page)
- La hauteur des images sera identique (pour chaque ligne)
- Le ratio hauteur/largeur des images ne doit pas être modifié
|
Après un bon moment passé sur papier, j'abouti au résultat suivant :
Code :
- l_new = (l_orig * larg_page) / (h_orig * tot_ratio)
- h_new = (l_new * (h_orig / l_orig)
- avec tot_ratio = somme de tous les rapports largeur / hauteur des images
|
La difficulté vient principalement de ce calcul tot_ratio,
qui ne peut être réalisé simplement avec un :
Code :
- "sum(@l_orig div @h_orig)"
|
D'après grégory, la seule solution est la méthode itérative
(puisqu'une xls:variable est en fait une constante :-/)
Plongeons et creusons pas à pas notre trou (ou plutôt résultat) :
Le principe consiste à calculer le ratio de la première image,
puis de le "transmettre" à l'image suivante, qui l'additionnera
à son propre ratio avant de l'envoyer à l'image d'après etc...
jusqu'à ce qu'il n'y ait plus d'image (dans le paragraphe).
Lorsqu'il n'y a plus d'image, il n'y a plus de ratio à calculer,
on affiche alors la valeur qui a été transmise par l'image précédente,
(qui était logiquement la dernière du paragraphe).
Techniquement, on utilisera une fonction avec deux paramètres :
- la somme des ratios envoyée par l'image précédente
- la "référence" de l'image courante, celle qu'il faut traiter
Code :
- <!-- Définition de la fonction -->
- <xsl:template name="somme_ratios">
- <!-- et ses deux paramètres -->
- <xsl:param name="total"/>
- <xsl:param name="position"/>
- <!-- Ici viendra l'implémentation de la fonction -->
- </xsl:template>
|
La fonction doit être capable de lire la largeur et la hauteur de l'image courante :
Rq : on prendra soin d'appeler cette fonction à partir d'un noeud parent de image,
donc à partir de "paragraphe" dans notre cas.
Code :
- <xsl:variable name="largeuri"><xsl:value-of select="image[position()=$position]/@l_orig"/></xsl:variable>
- <xsl:variable name="hauteuri"><xsl:value-of select="image[position()=$position]/@h_orig"/></xsl:variable>
|
Mais que se passe-t-il si la position à laquelle nous nous trouvons ne correspond plus à une image ?
Nous renverrons le résultat !
Il va falloir tester cette condition :
Code :
- <xsl:choose>
- <xsl:when test="$position <= count(image)">
- <!-- en clair : test="$position <= count(image)", ou encore
- notre position actuelle est-elle inférieure ou égale au nombre d'images de ce paragraphe ? -->
- <!-- C'est le cas général, il y a une image à cette position -->
- </xsl:when>
- <xsl:otherwise>
- <!-- Sinon, il n'y a plus d'image, on renvoie le résultat -->
- </xsl:otherwise>
- </xsl:choose>
|
Dans le cas général, il faudra calculer le ratio de l'image courante, l'additionner à la somme des ratios des images précédentes, et transmettre le tout à l'image suivante (qu'elle existe ou pas, nous avons vu que si elle n'existe pas, on prend alors la décision de renvoyer le résultat).
c'est là qu'intervient la récursivité, nous sommes dans une fonction et nous rappelons cette même fonction,
mais avec des paramètres qui auront évolués (le $total a changé, et la position est incrémentée) :
Code :
- <xsl:call-template name="somme_ratios">
- <xsl:with-param name="total"><xsl:value-of select="$total+$largeuri div $hauteuri"/></xsl:with-param>
- <xsl:with-param name="position"><xsl:value-of select="$position+1"/></xsl:with-param>
- </xsl:call-template>
|
Dans le cas final, on se contente d'afficher le total passé en paramètre
(pour les sceptiques : ça marche !)
Code :
- <xsl:value-of select="$total"/>
|
exemple de fonctionnement si nous avons 3 images :
Code :
- tot_ratio(tot=0,pos=1)
- il y a une image (pos=1<=nb_img=3)
- ->tot_ratio(tot=ratio1,pos=2)
- il y a une image (pos=2<=nb_img=3)
- ->tot_ratio(tot=r1+r2,pos=3)
- il y a une image (pos=3<=nb_img=3)
- ->tot_ratio(tot=r1+r2+r3,pos=4)
- il n'y a plus d'image
- -> AFFICHE tot
- <- tot est affiché
- fin fonction(pos=4)
- <- tot est affiché
- fin fonction(pos=3)
- <- tot est affiché
- fin fonction(pos=2)
- <-tot est toujours affiché
- fin de la fonction initiale,
|
Assemblons le tout :
Code :
- <!-- définition de la fonction et de ses deux paramètres -->
- <xsl:template name="somme_ratios">
- <xsl:param name="total"/>
- <xsl:param name="position"/>
- <!-- à dé-commenter pour voir le fonctionnement de plus près
- (mais attention, nous modifiez le fonctionnement en ajoutant des affichages
- et ne pourrez plus exploiter le résultat)
- <xsl:text>Appel fonction (total = </xsl:text>
- <xsl:value-of select="$total"/>
- <xsl:text>; position = </xsl:text>
- <xsl:value-of select="$position"/>
- <xsl:text> )</xsl:text><br />
- -->
- <!--Dans quel cas somme-nous ? -->
- <xsl:choose>
- <!-- 1: oui, il existe une image -->
- <xsl:when test="$position <= count(image)">
- <!-- définition des deux variables largeur et hauteur (lecture d'attributs) -->
- <xsl:variable name="largeur_i">
- <xsl:value-of select="image[position()=$position]/@l_orig"/>
- </xsl:variable>
- <xsl:variable name="hauteur_i">
- <xsl:value-of select="image[position()=$position]/@h_orig"/>
- </xsl:variable>
- <!-- calcul du ratio l/h de cette image -->
- <xsl:variable name="ratio_i">
- <xsl:value-of select="number($largeuri div $hauteuri)"/>
- </xsl:variable>
- <!-- à dé-commenter pour voir le fonctionnement de plus près
- <xsl:text>Image trouvée, valeurs : larg_i = </xsl:text>
- <xsl:value-of select="$largeur_i"/>
- <xsl:text>; haut_i = </xsl:text>
- <xsl:value-of select="$hauteur_i"/>
- <xsl:text>; ratio_i = </xsl:text>
- <xsl:value-of select="$ratio_i"/>
- <br />
- -->
- <!-- puis on passe à l'image suivante, en mettant à jour les paramètres
- <xsl:call-template name="somme_ratios">
- <xsl:with-param name="total">
- <xsl:value-of select="$total+$ratio_i"/>
- </xsl:with-param>
- <xsl:with-param name="position">
- <xsl:value-of select="$position+1"/>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:when>
- <!-- 2:Sinon, cas final, plus d'image, on "affiche" le résultat -->
- <xsl:otherwise>
- <!-- à dé-commenter pour voir le fonctionnement de plus près
- <xsl:text>Image non trouvée, fin de la récursivité</xsl:text>
- <xsl:text>Seule la ligne suivante devrait être affichée</xsl:text>
- <br />
- -->
- <xsl:value-of select="$total"/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:template>
|
La fonction est maintenant écrite, reste à l'initialiser, au bon moment et au bon endroit :
Dans mon cas, mon fichier xsl commence par définir une "variable" :
puis traite la racine du xml, et se termine par notre fonction :
Code :
- <?xml version="1.0" encoding="iso-8859-1"?>
- <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- <xsl:variable name="larg_page">900</xsl:variable>
- <xsl:template match="/">
- <html>
- <head>
- <!-- La partie en-tête -->
- </head>
- <body class="defaut">
- <!-- Le corps -->
- </body>
- </html>
- </xsl:template>
- <xsl:template name="somme_ratios">
- <!-- La fonction décrite ci-dessus -->
- </xsl:template>
- </xsl:stylesheet>
|
C'est bien sûr dans le corps du html que nous ferons notre appel à la fonction.
Tout d'abord un traitement à réaliser indépendamment, pour chaque paragraphe :
Code :
- <xsl:for-each select="site/page/paragraphe">
- <!-- ... -->
- </xsl:for-each>
|
(Rq : vous aurez probablement à traiter chaque page avant
de traiter chaque paragraphe, hors sujet dans notre cas)
Dans chaque paragraphe, nous commençons par calculer ce fameux total_ratio,
on confirme, on vérifie qu'on est bien sur le noeud parent de "image",
on peut lancer notre calcul itératif :
Code :
- <xsl:call-template name="somme_ratios">
- <xsl:with-param name="total">0</xsl:with-param>
- <xsl:with-param name="position">1</xsl:with-param>
- </xsl:call-template>
|
Le résultat s'affiche à l'écran, et si je veux le stocker ?
Aussi incroyable que ça puisse paraître, tout simplement
en encadrant cet appel par une définition de variable :
Code :
- <xsl:variable name="tot_ratio">
- <xsl:call-template name="somme_ratios">
- <xsl:with-param name="total">0</xsl:with-param>
- <xsl:with-param name="position">1</xsl:with-param>
- </xsl:call-template>
- </xsl:variable>
|
Voilà c'est tout !
Je ne me défausse pas pour autant, on a bien envie de les voir bien rangées ces images ?
Nous pouvons les mettre dans un paragraphe :
Code :
- <p class="photo">
- <!-- ... -->
- </p>
|
Nous sommes toujours sur le noeud "parapraphe", et nous allons afficher chaque image :
Code :
- <xsl:for-each select="image">
- <!-- ... -->
- </xsl:for-each>
|
Calculons la largeur et la hauteur modifiée de cette image
(d'après la formule du début et le désormais fameux total_ratio) :
Code :
- <xsl:variable name="l_new">
- <xsl:value-of select="number(@l_orig * $larg_page div @h_orig div $tot_ratio)" />
- </xsl:variable>
- <xsl:variable name="h_new">
- <xsl:value-of select="number($l_new * @h_orig div @l_orig)" />
- </xsl:variable>
|
Et affichons cette image, suivie d'un espace pour les séparer "proprement" :
Code :
- <img alt="Image introuvable...">
- <xsl:attribute name="src"><xsl:value-of select="." /></xsl:attribute>
- <xsl:attribute name="width">
- <xsl:value-of select="$l_new" />
- </xsl:attribute>
- <xsl:attribute name="height">
- <xsl:value-of select="$h_new" />
- </xsl:attribute>
- </img>
- <xsl:text> </xsl:text>
|
Bon ben c'est fini ?
Reste plus qu'à recomposer...
[mettez tout dans le même répertoire
exemple.xml (page à appeler dans le navigateur)
exemple.dtd (appelé par exemple.xml)
exemple.xsl (appelé par exemple.xml, traité par le navigateur récent)
exemple.css (appelé par le navigateur via exemple.xsl)
]
exemple.dtd
Code :
- <?xml version="1.0" encoding="iso-8859-1"?>
- <!ELEMENT site (parametres,page+)>
- <!ELEMENT page (paragraphe+)>
- <!ELEMENT paragraphe (image+)>
- <!ELEMENT image (#PCDATA)>
- <!ATTLIST image l_orig NMTOKEN #REQUIRED h_orig NMTOKEN #REQUIRED>
|
exemple.xml (pensez à mettre les exemplex.jpg dans le même dossier) :
l_orig="400" h_orig="398" sont les dimensions originales de l'image
(un jour je saurais les trouver automatiquement)
un coup d'explorateur de fichier vous informera facilement...
Code :
- <?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
- <!--?xml version="1.0" encoding="iso-8859-1" standalone="no"?-->
- <?xml-stylesheet type="text/xsl" href="exemple.xsl"?>
- <!DOCTYPE librairie SYSTEM "exemple.dtd">
- <site>
- <page>
- <paragraphe>
- <image l_orig="400" h_orig="398">exemple3.jpg</image>
- <image l_orig="700" h_orig="511">exemple4.jpg</image>
- <image l_orig="300" h_orig="426">exemple5.jpg</image>
- </paragraphe>
- <paragraphe>
- <image l_orig="400" h_orig="404">exemple1.jpg</image>
- <image l_orig="200" h_orig="226">exemple1.jpg</image>
- <image l_orig="100" h_orig="324">exemple1.jpg</image>
- </paragraphe>
- <paragraphe>
- <image l_orig="600" h_orig="180">exemple2.jpg</image>
- <image l_orig="600" h_orig="320">exemple2.jpg</image>
- </paragraphe>
- </page>
- </site>
|
exemple.css (une feuille de style classique)
Code :
- .defaut {
- color: #CCCCCC;
- font-size: small;
- text-align: left;
- background-color: #000000;
- font-weight: 700;
- }
- .photo {
- text-align: center;
- }
|
exemple.xsl :
Code :
- <?xml version="1.0" encoding="iso-8859-1"?>
- <!-- Une feuille de style xsl est un document xml -->
- <!-- Début de la feuille de style -->
- <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
- <!-- Variables globales, le mieux est de les lire depuis le XML -->
- <xsl:variable name="larg_page">900</xsl:variable>
- <!-- Début du traitement, par le noeud racine -->
- <xsl:template match="/">
- <!-- Affichage html -->
- <html>
- <!-- La partie en-tête -->
- <head>
- <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1" />
- <meta http-equiv="Content-Language" content="fr" />
- <link href="exemple.css" rel="stylesheet" type="text/css" />
- </head>
- <!-- Le corps de la page -->
- <body class="defaut">
- <!-- Pour chaque noeud "paragraphe" -->
- <xsl:for-each select="site/page/paragraphe">
-
- <!-- Calcul de la somme des ratios des images contenues dans chaque paragraphe -->
- <xsl:variable name="tot_ratio">
- <xsl:call-template name="somme_ratios">
- <xsl:with-param name="total">0</xsl:with-param>
- <xsl:with-param name="position">1</xsl:with-param>
- </xsl:call-template>
- </xsl:variable>
- <!-- Affichage du paragraphe -->
- <p class="photo">
- <!-- Pour chaque image contenue dans le paragraphe -->
- <xsl:for-each select="image">
- <!-- Calculs de la largeur et de la hauteur modifiée -->
- <xsl:variable name="l_new">
- <xsl:value-of select="number(@l_orig * $larg_page div @h_orig div $tot_ratio)" />
- </xsl:variable>
- <xsl:variable name="h_new">
- <xsl:value-of select="number($l_new * @h_orig div @l_orig)" />
- </xsl:variable>
- <!-- La balise image, sur laquelle on ajoute des attributs paramétrables -->
- <img alt="Image introuvable...">
- <xsl:attribute name="src"><xsl:value-of select="." /></xsl:attribute>
- <xsl:attribute name="width"><xsl:value-of select="$l_new" /></xsl:attribute>
- <xsl:attribute name="height"><xsl:value-of select="$h_new" /></xsl:attribute>
- </img>
- <!-- Un espace pour décoller les images les unes des autres -->
- <xsl:text> </xsl:text>
- </xsl:for-each>
- <!-- Fin du traitement de chaque image -->
- </p>
- <!-- Fin du paragraphe -->
- </xsl:for-each>
- <!-- Fin du noeud "paragraphe" -->
- </body>
- </html>
- <!-- Fin de la page html -->
- </xsl:template>
- <!-- Fin du xml, noeud racine "/" -->
- <!-- Fonction qui calcule la somme des rapports largeur/hauteur,
- pour tous les noeuds "image" contenu dans le noeud courant -->
- <xsl:template name="somme_ratios">
- <!-- Paramètres d'entrée, l'appel externe se fait avec total=0 et position = 1 -->
- <xsl:param name="total"/>
- <xsl:param name="position"/>
- <!--Dans quel cas somme-nous ? -->
- <xsl:choose>
- <!-- 1: oui, il existe une image -->
- <xsl:when test="$position <= count(image)">
- <!-- définition des deux variables largeur et hauteur (lecture d'attributs) -->
- <xsl:variable name="largeur_i">
- <xsl:value-of select="image[position()=$position]/@l_orig"/>
- </xsl:variable>
- <xsl:variable name="hauteur_i">
- <xsl:value-of select="image[position()=$position]/@h_orig"/>
- </xsl:variable>
- <!-- calcul du ratio l/h de cette image -->
- <xsl:variable name="ratio_i">
- <xsl:value-of select="number($largeur_i div $hauteur_i)"/>
- </xsl:variable>
- <!-- puis on passe à l'image suivante, en mettant à jour les paramètres -->
- <xsl:call-template name="somme_ratios">
- <xsl:with-param name="total">
- <xsl:value-of select="$total+$ratio_i"/>
- </xsl:with-param>
- <xsl:with-param name="position">
- <xsl:value-of select="$position+1"/>
- </xsl:with-param>
- </xsl:call-template>
- </xsl:when>
- <!-- 2 : Sinon, cas final, plus d'image, on "affiche" le résultat -->
- <xsl:otherwise>
- <xsl:value-of select="$total"/>
- </xsl:otherwise>
- </xsl:choose>
- <!-- Fin des cas -->
- </xsl:template>
- <!-- Fin de la fonction -->
- </xsl:stylesheet>
- <!-- Fin de la feuille de style xsl -->
|
Message édité par La_Buze le 19-06-2005 à 11:11:10