doSimple Introduction à XSL-FO (Suite)
XSL-FO via XSLT
Cet article s’inscrit dans la continuïté de l’Introduction à XSL-FO en prenant un exemple concret de transformation d’un contenu XML existant pour en faire un joli PDF (du moins XSL-FO).
Le But
Étant un afficionados de la salle de concert Bikini Test, je souhaiterais avoir une version papier de l’agenda (du mois) pouvant être imprimée pour faire un peu de pub à gauche à droite. L’impression CSS devrait suffire mais loin de moi l’idée de lacher le troll.
Avant de commencer, veuillez vous munir des outils nécessaires.
Les outils
Pour les (heureux) utilisateurs d’une distribution UN*X, je ne peux que vous conseiller d’utiliser xsltproc conjointement à FOP. Quant aux windowsiens, Cooktop (qui utilise Internet Explorer par derrière) est pas si mal.
Dégrossissage
Avant d’appliquer une quelconque mise en forme, une première feuille de style pour récupèrer l’information utile. Un conseil : ayez sous les yeux le code source de l’agenda.
Simple.xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<!-- Le mode de sortie (ici du texte) -->
<xsl:output method="text" />
<xsl:template match="/">
<!-- Récupération de la valeur de l’élément title (dans le head) -->
<xsl:value-of select="//xhtml:title" />
<!-- Pour chaques blocs : div.prog_entry -->
<xsl:for-each select="//xhtml:div[@class = 'prog_entry']">
<xsl:text>
- </xsl:text>
<!-- affichage du contenu textuel de l'élément : dd.title -->
<xsl:value-of select="xhtml:dl/xhtml:dd[@class = 'title']" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
La transformation : xsltproc simple.xsl http://www.bikinitest.ch/newsite/agenda.php
donnera le résultat suivant (pour le mois de mai 2006)
Bikini Test - Agenda - House Of Fix (UK) - Cult Of Luna (SWE) (Unique Date CH) - The Poets Of Rhythm (D)(Quannum/Ninja Tune) Live - Winterthur Rocks! - Beautés Vulgaires (F) - Masterclass de la BAF'!
XPath
Pour introduire rapidement les chemins XPath (ceux utilisés dans les select et match),
un document est un arbre où chaque nœud est une balise. Typiquement pour un document XHTML.
html
/ \
body head
/ \ \
p div title
/ \
ul p
/ \
li li
Un nœud est identifié par son chemin : le titre /html/head/title ou //title toutes les balises title.
Ensuite les attributs d’un nœud sont accessibles via le @, par exemple a/@href pour obtenir où pointe un
lien hypertexte.
Les conditions
Dans l’exemple ci-dessus, les différents dd sont différenciés selon leur classe. Avec les parenthèses carrées [ ],
une condition peut être appliquée à un nœud : //dd[@class = 'foobar'] va séléctionner tous les dd de la
classe foobar. Ce que l’on ferait avec dd.foobar en CSS.
Récupérer tout le contenu
Ce qui va nous intéresser pour notre document imprimé sont les blocs représentant la soirée ainsi qu'éventuellement le titre du contenu qui indique quel mois est affiché.
Première étape se placer au niveau des entrées du programme, dans le contenu : //div[@id = 'contenu'] et
afficher chaque div[@class = 'prog_entry'] en fonction de ce qu’il contient (dd[@class = 'title'], ...).
...
<xsl:apply-templates select="//xhtml:div[@id = 'contenu']" />
...
<xsl:template match="xhtml:div[@id = 'contenu']">
<xsl:apply-templates select="xhtml:div" />
</xsl:template>
<xsl:template match="xhtml:div[@class = 'prog_entry']">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="xhtml:dd[@class = 'title']">
<xsl:text>titre : </xsl:text>
<xsl:apply-templates />
</xsl:template>
<!-- Ne rien faire des divs non-sélectionnés précédemment -->
<xsl:template match="xhtml:div" />
À partir de là et avec l’article précédant il me semble que les bases sont posées pour obtenir un résultat sympa.
Le résultat final
Avant d’aller directement au but, j’aimerais relever un élément intéressant : les liens. Il est possible, dans un document PDF, d’avoir des liens externes (aussi bien qu’interne d’ailleurs) et les liens des soirées sur les groupes. Mais une fois le document imprimé, les liens ne sont plus utiles, il en faut une version textuelle. Pour ce faire, une note de bas de page.
<xsl:template match="xhtml:a">
<!-- Affichage du lien -->
<fo:basic-link color="#333" external-destination="{@href}">
<xsl:apply-templates />
</fo:basic-link>
<!-- Affichage de la note de bas de page -->
<fo:footnote>
<fo:inline font-size="60%" vertical-align="super">
<!-- Comptage des liens externes précédents -->
<xsl:value-of select="count(preceding::xhtml:a[starts-with(@href,
'http://')]) + 1" />
</fo:inline>
<fo:footnote-body>
<fo:block font-size="8pt" color="#000" font-weight="normal">
<xsl:value-of select="count(preceding::xhtml:a[starts-with(@href,
'http://')]) + 1" />
<xsl:text>) </xsl:text>
<xsl:value-of select="@href" />
</fo:block>
</fo:footnote-body>
</fo:footnote>
</xsl:template>
Télécharger la feuille de transformation : template.xsl et le résultat (pour le mois de mai).