XML | XML-Schema | XPath | XSL-T | XSL-FO | XQuery | XProc | SVG |
XSL-T / XSLT-Tipps / Konvertierung von XML nach XML / Erzeugen von skalierbaren Vektor-Grafiken (SVG)
![]() |
![]() |
➪ Skalierbare Vektor-Grafiken (SVG) sind pures XML mit einem speziellen Namespace, der die Funktionalitäten definiert, auf der u.a. auch Browser arbeiten, die SVG-Dateien anzeigen können. Spezielle Grafikelemente (Text, Rechteck, Kreis, Linie, ...) können mit Koordinaten und weiteren Eigenschaften ausgestattet werden, die ein Anzeigen als Grafik möglich machen.
Sehr sinnvoll ist der Einsatz von XSLT bei der Generierung von skalierbaren Vektor-Grafiken (SVG). Das ist zwar grundsätzlich auch unter XSLT 1.0 möglich; da in diesem Beispiel jedoch xsl:result-document sowie weitere Funktionen zur Anwendung kommen, die nur in XSLT 2.0 verfügbar sind, habe ich es hier aufgeführt.
Verhältnismäßig einfach ist das Erstellen von Balkendiagrammen. Dabei ist für das Verständnis wichtig, dass sich die Null-Koordinate nicht unten links auf einer Zeichenfläche, sondern oben links befindet.
<xsl:template name="generateSVG_Balkendiagramm">
<xsl:result-document href="Balkendiagramm.svg">
<xsl:variable name="vheight"
select="count(Orte/Ort/Mensch) * 15 + 130"/>
<svg:svg
xmlns:svg="http://www.w3.org/2000/svg"
width="380"
height="{$vheight}"
style="fill:gray;">
<svg:rect x="5" y="5" width="375"
height="{$vheight - 5}"
style="fill:none; stroke:black"/>
<svg:text x="50" y="50">Einkommen</svg:text>
<xsl:for-each select="Orte/Ort/Mensch">
<xsl:sort select="Gehalt"
data-type="number" order="ascending"/>
<svg:text x="20" y="{105 + position() *15}">
<xsl:value-of select="vorname"/>
</svg:text>
<svg:rect
x="100"
y="{100 + position() *15}"
width="{Gehalt * 0.04}"
height="5"
style="fill:blue"/>
</xsl:for-each>
</svg:svg>
</xsl:result-document>
</xsl:template>
Das Ergebnis sieht dann folgendermaßen aus:
Schon etwas komplexer wird es bei der Generierung von Säulendiagrammen, da (wie erwähnt) die Null-Koordinate der Zeichenfläche sich oben links auf der Zeichenfläche befindet. Dadurch muss erst einmal eine Koordinatentransformation durchgeführt werden, um die Basislinie auf eine Ebene zu bekommen. Zu Darstellungszwecken ist es ggf. auch wünschenswert, die Textlegende in senkrechter Schrift zu realisieren.
<xsl:template name="generateSVG_Saeulendiagramm">
<xsl:result-document href="Saeulendiagramm.svg">
<xsl:variable name="zoomfaktor">0.05</xsl:variable>
<xsl:variable name="vAbstandVonOben">20</xsl:variable>
<xsl:variable name="vAbstandVonUnten">70</xsl:variable>
<xsl:variable
name="vmaxGehalt"
select="max(Orte/Ort/Mensch/Gehalt)"/>
<xsl:variable
name="vwidth"
select="count(Orte/Ort/Mensch) * 15 + 50"/>
<xsl:variable
name="vheight"
select="($vmaxGehalt * $zoomfaktor)
+ $vAbstandVonOben + $vAbstandVonUnten"/>
<svg:svg
xmlns:svg="http://www.w3.org/2000/svg"
width="{$vwidth}"
height="{$vheight}"
style="fill:gray;">
<svg:rect x="5" y="5"
width="{$vwidth - 5}"
height="{$vheight - 5}"
style="fill:none; stroke:black"/>
<svg:text x="50" y="50">Einkommen</svg:text>
<xsl:for-each select="Orte/Ort/Mensch">
<xsl:sort
select="Gehalt"
data-type="number"
order="ascending"/>
<svg:text
x="{20 + position() *15}"
y="{$vheight - $vAbstandVonUnten + 10}"
writing-mode="tb">
<xsl:value-of select="vorname"/>
</svg:text>
<svg:rect
x="{20 + position() *15}"
y="{(($vmaxGehalt - Gehalt) * $zoomfaktor) + $vAbstandVonOben}"
width="5"
height="{Gehalt * $zoomfaktor}"
style="fill:blue"/>
</xsl:for-each>
</svg:svg>
</xsl:result-document>
</xsl:template>
Auch dieses Ergebnis wurde generiert:
Noch mal komplexer wird es bei der automatisierten Erstellung von Kreisdiagrammen, weil die Konfiguration der Kreis-Segmente keineswegs trivial ist und zudem auch unterschiedliche Farbgebungen sowie die dazu passende Legende relevant werden. Zum besseren Verständnis macht es Sinn, mit mehreren Variablen zu arbeiten, die z.B. die extern definierten Farbschemata einbinden oder auch mit einer vorsortierten Liste arbeiten. Diese Farbliste könnte so aussehen:
<FARBEN>
<farbe hex="#138808" name="India green"/>
<farbe hex="#177245" name="Dark spring green"/>
<farbe hex="#059033" name="North Texas Green"/>
<farbe hex="#03C03C" name="Dark pastel green"/>
<farbe hex="#CEFF00" name="Volt"/>
<farbe hex="#FDFD96" name="Pastel yellow"/>
<farbe hex="#FFFF66" name="Laser Lemon"/>
<farbe hex="#FFFF33" name="Electric yellow"/>
<farbe hex="#FEDF00" name="Yellow (Pantone)"/>
<farbe hex="#FB9902" name="Orange (RYB)"/>
<farbe hex="#FD0E35" name="Scarlet"/>
<farbe hex="#FF1493" name="deeppink"/>
<farbe hex="#FD6C9E" name="French pink"/>
<farbe hex="#FFC0CB" name="pink"/>
<farbe hex="#FDD5B1" name="Feldspar"/>
<farbe hex="#CC99CC" name="Light grayish magenta"/>
<farbe hex="#CC99FF" name="Pale violet"/>
<farbe hex="#871F78" name="darkpurple"/>
<farbe hex="#6F2DA8" name="Grape"/>
<farbe hex="#6C3082" name="Eminence"/>
<farbe hex="#66023C" name="Imperial purple"/>
<farbe hex="#000080" name="navy"/>
<farbe hex="#FE28A2" name="Persian rose"/>
<farbe hex="#FDBCB4" name="Melon"/>
<farbe hex="#6F00FF" name="Electric indigo"/>
<farbe hex="#6E6EF9" name="Dark imperial blue"/>
<farbe hex="#87CEFA" name="lightskyblue"/>
<farbe hex="#66DDAA" name="Medium aquamarine"/>
<farbe hex="#BCD4E6" name="Beau blue"/>
<farbe hex="#B0E0E6" name="powderblue"/>
<farbe hex="#E6E6FA" name="lavendel"/>
<farbe hex="#CCFF00" name="Electric lime"/>
<farbe hex="#a6d608" name="Vivid lime green"/>
<farbe hex="#987654" name="Pale brown"/>
<farbe hex="#965A3E" name="Coconut"/>
<farbe hex="#778BA5" name="Shadow blue"/>
<farbe hex="#74C365" name="Mantis"/>
<farbe hex="#76FF7A" name="Screaming Green"/>
<farbe hex="#664C28" name="Donkey brown"/>
<farbe hex="#663399" name="Rebecca Purple"/>
<farbe hex="#000000" name="black"/>
</FARBEN>
Deren Einbindung in das XSL-Stylesheet funktioniert so:
<xsl:template name="CreateKreisdiagramm">
<xsl:param name="pwidth">630</xsl:param>
<xsl:variable name="pheight">450</xsl:variable>
<xsl:variable name="vcntGesamt">
<xsl:value-of select="count(/Orte/Ort/Mensch)"/>
</xsl:variable>
<xsl:variable name="farbliste_svg"
select="document('../input/Farbliste.xml')"/>
<xsl:variable name="sortierteListe">
<SORTIERTELISTE>
<xsl:for-each select="/Orte/Ort/Mensch">
<xsl:sort select="Gehalt"
data-type="number"
order="descending"/>
<Mensch>
<vorname>
<xsl:value-of select="vorname"/>
</vorname>
<name>
<xsl:value-of select="name"/>
</name>
<Gehalt>
<xsl:value-of select="Gehalt"/>
</Gehalt>
</Mensch>
</xsl:for-each>
</SORTIERTELISTE>
</xsl:variable>
<xsl:variable name="vGesamtgehalt"
select="sum($sortierteListe/SORTIERTELISTE/Mensch/Gehalt)"/>
<xsl:result-document
href="../documentation/Kreisdiagramm.svg"
encoding="ISO-8859-1"
indent="yes">
<svg width="{$pwidth}"
height="{$pheight}"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
baseProfile="tiny"
xml:lang="de"
fill="none">
<rect x="5"
y="5"
width="{$pwidth - 5}"
height="{$pheight - 5}"
style="fill:none; stroke:black;"/>
<title>Einkommen pro Person</title>
<svg baseProfile="tiny"
viewBox="-35 -35 70 70"
width="{$pheight - 100}"
height="{$pheight - 100}"
y="25"
x="25">
<circle r="32.831"
fill="{$farbliste_svg/FARBEN/farbe[1]/@hex}"
stroke="black"
title="{$sortierteListe/SORTIERTELISTE/Mensch[1]/name}"/>
<xsl:for-each
select="$sortierteListe/SORTIERTELISTE/Mensch">
<xsl:variable name="pos" select="position()"/>
<xsl:if test="$pos &gt; 1">
<xsl:variable name="vbisher">
<xsl:value-of select="sum(preceding-sibling::*/Gehalt)"/>
</xsl:variable>
<xsl:variable name="vAnteil">
<xsl:value-of select="$vbisher * 100 div $vGesamtgehalt"/>
</xsl:variable>
<xsl:variable name="vpendant">
<xsl:value-of select="100 - $vAnteil"/>
</xsl:variable>
<circle r="15.9155" stroke-width="31.831"
stroke-dasharray="0, {$vAnteil}, {$vpendant}, 0"
title="{concat(vorname, ' ', name)}">
<xsl:attribute name="stroke">
<xsl:value-of
select="$farbliste_svg//FARBEN/farbe[$pos]/@hex"/>
</xsl:attribute>
</circle>
</xsl:if>
</xsl:for-each>
<circle r="15.3" fill="black"
title="Innenkreis Rand"/>
<circle r="15" fill="white"
title="Innenkreis"/>
<line x2="32" stroke-linecap="round"
stroke-width="1" stroke="red"
transform="rotate(0)"/>
</svg>
<xsl:for-each select="$sortierteListe/SORTIERTELISTE/Mensch">
<xsl:variable name="pos" select="position()"/>
<rect x="400" y="{($pos * 20)}"
width="10"
height="10"
style="fill:{$farbliste_svg//FARBEN/farbe[$pos]/@hex};
stroke:{$farbliste_svg//FARBEN/farbe[$pos]/@hex};"
title="{name}"/>
<text x="430"
y="{$pos * 20 + 10}"
font-family="Verdana,sans-serif"
font-size="9px"
fill="black">
<xsl:value-of select="vorname"/>
<xsl:text> </xsl:text>
<xsl:value-of select="name"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="format-number(Gehalt, '0')"/>
<xsl:text> Euro (</xsl:text>
<xsl:value-of
select="format-number(Gehalt * 100 div $vGesamtgehalt, '0')"/>
<xsl:text> %)</xsl:text>
</text>
</xsl:for-each>
<text x="50"
y="{$pheight - 20}"
font-family="Verdana,sans-serif" font-size="19px"
font-weight="bold" fill="black">Einkommen pro Person</text>
</svg>
</xsl:result-document>
</xsl:template>
Auch dieses Ergebnis kann bewundert werden:
wg / 18. Dezember 2019
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de