XML | XML-Schema | XPath | XSL-T | XSL-FO | XQuery | XProc | SVG |
SVG / 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 die grafische Anzeigen ermöglichen.
Auf dieser Seite:In <polyline> können die Eckpunkte über X- und Y-Koordinaten definiert werden.
<svg xmlns="http://www.w3.org/2000/svg" width="380" height="167">
<rect x="5"
y="5" width="375"
height="162"
style="fill:none; stroke:black"/>
<text x="110"
y="20"
style="font-size:7pt; fill:blue;"
>Zick-zack-Kurs</text>
<polyline
style="fill:none;stroke:green;stroke-width:5;"
points="20 11
133 119
247 50
360 100" />
</svg>
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:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs fn">
<xsl:output method="xml"
version="1.0"
encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:result-document
href="../output/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>
</xsl:stylesheet>
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:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs fn">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:result-document href="../output/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>
</xsl:stylesheet>
Noch einmal 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.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<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="../output/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 > 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"/>
</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>
<xsl:template match="/">
<xsl:call-template name="CreateKreisdiagramm"/>
</xsl:template>
</xsl:stylesheet>
Das vorstehende XSL-Stylesheet für das Kreisdiagramm greift auf diese Farbliste ('../input/Farbliste.xml') zurück:
<?xml version="1.0" encoding="UTF-8"?>
<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>
Und so siehts aus:
Hübsch, nicht?
Ein häufiges Problem ist die automatisierte Datenvisualisierung mit SVG, wenn Minimal-, Maximal- oder logarithmische Werte berechnet werden sollen und der verwendete XSL-Prozessor diesbezügliche Funktionen nicht unterstützt. In diesem Fall kann die Einbindung von Extensions hilfreich sein. Hier finden Sie eine XSL-FO-Logik mit eingebetteter SVG-Grafik für logarithmische Skalierung.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
extension-element-prefixes="math">
<xsl:variable name="vmaxGehalt">
<xsl:value-of
select="//Gehalt[not(
following::Gehalt > .
or
preceding::Gehalt > .)]"/>
</xsl:variable>
<xsl:variable name="vminGehalt">
<xsl:value-of
select="//Gehalt[not(
following::Gehalt < .
or
preceding::Gehalt < .)]"/>
</xsl:variable>
<xsl:variable name="vheight" >360</xsl:variable>
<xsl:variable name="vzoomfaktor">0.05</xsl:variable>
<xsl:variable name="vAbstandVonOben">20</xsl:variable>
<xsl:variable name="vAbstandVonUnten">70</xsl:variable>
<xsl:variable name="vAbstandVonLinks">50</xsl:variable>
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master
master-name="a"
page-height="297mm"
page-width="21cm"
margin="10mm">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="a">
<fo:flow flow-name="xsl-region-body">
<fo:block>Einkommensverteilung</fo:block>
<fo:block>
<fo:instream-foreign-object>
<svg:svg
xmlns:svg="http://www.w3.org/2000/svg"
width="380"
height="{$vheight}"
style="fill:gray;">
<svg:defs>
<svg:linearGradient
id="wgrad" x1="0%" y1="0%" x2="100%" y2="0%">
<svg:stop offset="0%"
style="stop-color:yellow;stop-opacity:1" />
<svg:stop offset="33%"
style="stop-color:green;stop-opacity:1" />
<svg:stop offset="66%"
style="stop-color:blue;stop-opacity:1" />
<svg:stop offset="100%"
style="stop-color:black;stop-opacity:1" />
</svg:linearGradient>
</svg:defs>
<svg:rect x="5" y="5"
width="375"
height="{$vheight - 5}"
style="fill:none; stroke:black"/>
<svg:text x="{$vAbstandVonLinks}"
y="100">Normale Skalierung</svg:text>
<svg:rect x="{$vAbstandVonLinks}"
y="{115}"
width="{7000 * 0.04}"
height="10"
style="fill:url(#wgrad)"/>
<svg:rect x="{(100 * 0.04) + $vAbstandVonLinks}"
y="{115}"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{(100 * 0.04) + $vAbstandVonLinks}"
y="125"
writing-mode="tb">100</svg:text>
<svg:rect x="{(1000 * 0.04) + $vAbstandVonLinks}"
y="{115}"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{(1000 * 0.04) + $vAbstandVonLinks}"
y="125"
writing-mode="tb">1.000</svg:text>
<svg:rect x="{(5000 * 0.04) + $vAbstandVonLinks}"
y="{115}"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{(5000 * 0.04) + $vAbstandVonLinks}"
y="125"
writing-mode="tb">5.000</svg:text>
<svg:text x="{$vAbstandVonLinks}"
y="260">Logarithmische Skalierung</svg:text>
<svg:rect x="{$vAbstandVonLinks}"
y="270"
width="{7000 * 0.04}"
height="10"
style="fill:url(#wgrad); stroke:black"/>
<svg:rect x="{math:log(10) * 29 + $vAbstandVonLinks}"
y="270"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{math:log(10) * 29 + $vAbstandVonLinks}"
y="290"
writing-mode="tb">10</svg:text>
<svg:rect x="{math:log(100) * 29 + $vAbstandVonLinks}"
y="270"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{math:log(100) * 29 + $vAbstandVonLinks}"
y="290"
writing-mode="tb">100</svg:text>
<svg:rect x="{math:log(1000) * 29 + $vAbstandVonLinks}"
y="270"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{math:log(1000) * 29 + $vAbstandVonLinks}"
y="290"
writing-mode="tb">1.000</svg:text>
<svg:rect x="{math:log(10000) * 29 + $vAbstandVonLinks}"
y="270"
width="1"
height="10"
style="fill:white"/>
<svg:text
x="{math:log(10000) * 29 + $vAbstandVonLinks}"
y="290"
writing-mode="tb">10.000</svg:text>
<xsl:for-each select="/Orte/Ort/Mensch">
<xsl:sort
select="Gehalt"
data-type="number"
order="ascending"/>
<svg:rect x="{(Gehalt * 0.04) + $vAbstandVonLinks}"
y="{120}"
width="1"
height="1"
style="fill:red"/>
<svg:rect x="{math:log(Gehalt) * 29 + $vAbstandVonLinks}"
y="{275}"
width="1"
height="1"
style="fill:red"/>
</xsl:for-each>
</svg:svg>
</fo:instream-foreign-object>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
wg / 14. Juni 2020
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
ᐃ SVG
ᐅ svg:a
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de