4.4  XSL-FO mit XSLT1.x

XSL-FO ist eine XML basierte Auszeichnungssprache, die von einem XSL-FO Prozessor, wie dem kommerziellen AntennaHouse Formatter ↗↗ oder der freien Open Source Lösung Apache FOP ↗↗ verarbeitet wird.
Ausgabe ist meistens PDF, aber auch andere Formate, wie das veraltete RTF (Rich Text Format) oder auch spezielle Hardware spezifische Formate, wie PCL ↗↗, werden unterstützt.

4.4.1  XSL Entwickler

XSL-FO ist eine Wissenschaft für sich, wie die sehr umfangreiche Spezifikation ↗↗ belegt. Deshalb nimmt der XSL-Entwickler (Stylesheet-Entwickler) meistens eine Sonderrolle in einer XML Company ein.
VORSICHT!
Bei der Berufswahl gilt es zu beachten, dass ein "Stylesheet-Entwickler" in einer XML Company etwas ganz anderes macht, als ein "Stylesheet Spezialist" in einer Web Company. Ausserdem ist der XML Anwendungsbereich in zwei Sparten aufgeteilt:
Publishing
Datenkonvertierung
Beide Bereiche überlappen, und XML Entwickler werden sowohl im Publishing als auch in der Datenkonvertierung händeringend gesucht. Jedoch kommt ein Publisher seltener mit XML Datenbanken , und deren Abfragesprache XQuery als Programmiersprache in Berührung. Dagegen wird sich der Konvertierer weniger mit kniffeligen Layoutproblemen herumschlagen müssen.
Diese Differenzierung ist wohl vergleichbar mit Backend- und Frontend-Entwicklung.

4.4.2  XSLT1.x

In der Version 1.x von XSLT musste man noch auf viele XPath Features verzichten. Bspw. wurde das Iterieren über eine Knotenmenge rekursiv mittels Template-Calls realisiert. Auch bzgl. der Arithmetik gab es noch nicht so viele Funktionen, wie man leicht anhand des Funktionskatalogs ↗↗ überprüfen kann.
Vermutlich mag es also an den begrenzten Möglichkeiten liegen, warum mein damaliges Stylesheet (vor 10 Jahren) etwas komplizierter aussieht ...

4.4.3  Notentabellen

Folgend ist eine Notentabelle zur Benotung nach dem 15-Punkte-Schema für die Kollegstufe abgebildet.
Notentabelle zur Benotung nach dem 15-Punkte Schema am Gymnasium. Mit ein paar Zeilen Code liessen sich schon in der Version 1.0 der Programmiersprache XSLT in Verbindung mit der Auszeichnungssprache XSL-FO beeindruckende PDF Layouts erzeugen.

Bild: 21  Ausschnitt aus einer PDF XSL-FO Demo
(Link zum PDF mit der vollständigen Notentabelle ↗↗)
Die Note 6 ist in der Kollegstufe am Gymnasium mit "0 Punkte" bezeichnet. Die Note 5 ist aufgeteilt auf die Stufen "1 Punkt", "2 Punkte" und "3 Punkte". Um die Note 5 zu erzielen muss mindestens ein Drittel der Gesamtpunktzahl erreicht werden. Die Note 4 ist unterteilt in "4 Punkte", "5 Punkte" und "6 Punkte". Um die Note 4 zu erreichen müssen mindestens 50% der Gesamtpunktzahl erzielt werden. Die restlichen 50% der Gesamtpunkte werden dann möglichst gleichverteilt auf die Noten "7 Punkte" bis "15 Punkte".
Die Note "15 Punkte" entspricht dabei der "Note 1 mit Stern". Es können halbe Punkte vergeben werden. Gesucht ist eine homogene Verteilung nach obigen Regeln einer vorgegebenen Gesamtpunktzahl auf die 15 Notenstufen. Das Stylesheet generiert über 200 Tabellen mit verschiedene Werten für die Gesamtpunktzahl aufgeteilt auf 5 DIN A4 Seiten.
Die Logik dazu sieht folgendermassen aus:
<xsl:template name="note-runden">
  <xsl:param name="note"/>
  <xsl:variable name="rest" select="$note * 10 mod 10"/>
  <xsl:choose>
    <xsl:when test="$rest &gt; 2.5">
      <xsl:value-of select="floor(number($note))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="floor(number($note))-0.5"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="noten-tabelle">
  <xsl:param name="von-punkte"/>
  <xsl:param name="bis-punkte"/>
  <xsl:if test="$von-punkte &lt; $bis-punkte+1">
    <xsl:variable name="note6" select="$von-punkte div 3"/>
    <xsl:variable name="note4" select="$von-punkte div 2"/>
    <xsl:variable name="note5-schrittweite" select="($note4 +(-$note6)) div 3"/>
    <xsl:variable name="schrittweite" select="$note4 div 12"/>
    <xsl:variable name="note-6">
      <xsl:call-template name="note-runden">
        <xsl:with-param name="note" select="$note6"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="note6-korrigiert">
      <xsl:choose>
        <xsl:when test="3*($note-6+0.5) &lt; $von-punkte">
          <xsl:value-of select="$note6+0.5"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$note6"/>
        </xsl:otherwise>
      </xsl:choose>                          
    </xsl:variable>
    <fo:block border="1pt solid grey" space-after="3pt" 
              keep-together.within-column="always" padding="2pt">
    <fo:block text-align="center" background-color="#999" color="white" 
              padding-bottom="0pt" padding-top="2pt" space-after="2pt">
      <xsl:text>Gesamt: </xsl:text>
      <fo:inline  font-weight="bold">
        <xsl:value-of select="$von-punkte"/>
      </fo:inline>
    </fo:block>
    <fo:block>
      <fo:inline font-weight="bold">
        <xsl:text>0 P: </xsl:text>
      </fo:inline>
      <xsl:text>bis </xsl:text>
      <xsl:call-template name="note-runden">
        <xsl:with-param name="note" select="$note6-korrigiert"/>
      </xsl:call-template>
    </fo:block>
    <xsl:call-template name="note5">
      <xsl:with-param name="i" select="0"/>
      <xsl:with-param name="note6" select="$note6-korrigiert"/>
      <xsl:with-param name="note5-schrittweite" select="$note5-schrittweite"/>
      <xsl:with-param name="gesamt" select="$von-punkte"/>
    </xsl:call-template>
    <xsl:call-template name="ab-note4">
      <xsl:with-param name="i" select="0"/>
      <xsl:with-param name="note4" select="$note4"/>
      <xsl:with-param name="schrittweite" select="$schrittweite"/>
    </xsl:call-template>
    <fo:block/>
    <fo:block/>
    </fo:block>
    <xsl:call-template name="noten-tabelle">
      <xsl:with-param name="von-punkte" select="$von-punkte+1"/>
      <xsl:with-param name="bis-punkte" select="$bis-punkte"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template name="note5">
  <xsl:param name="i"/>
  <xsl:param name="note6"/>
  <xsl:param name="note5-schrittweite"/>
  <xsl:param name="gesamt"/>
  <xsl:if test="$i &lt; 3">
    <xsl:variable name="note-von-gerundet">
      <xsl:call-template name="note-runden">
        <xsl:with-param name="note" select="number($note6) + 
                                    number($i)*number($note5-schrittweite)"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="von" select="$note-von-gerundet+0.5"/>
    <xsl:variable name="bis">
      <xsl:call-template name="note-runden">
        <xsl:with-param name="note" select="number($note6)+
                                   (number($i)+1)*number($note5-schrittweite)"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="bis-korrigiert">
      <xsl:choose>
        <xsl:when test="$i=2 and (2*$bis)=number($gesamt)">
          <xsl:value-of select="$bis+(-0.5)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$bis"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <fo:block>
      <fo:inline font-weight="bold">
        <xsl:value-of select="$i+1"/>
        <xsl:text> P: </xsl:text>
      </fo:inline>
      <xsl:value-of select="$von"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="$bis-korrigiert"/>
    </fo:block>
    <xsl:call-template name="note5">
      <xsl:with-param name="i" select="$i+1"/>
      <xsl:with-param name="note6" select="$note6"/>
      <xsl:with-param name="note5-schrittweite" select="$note5-schrittweite"/>
      <xsl:with-param name="gesamt" select="$gesamt"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<xsl:template name="ab-note4">
  <xsl:param name="i"/>
  <xsl:param name="note4"/>
  <xsl:param name="schrittweite"/>
  <xsl:if test="$i &lt; 12">
    <xsl:variable name="note-von-gerundet">
      <xsl:call-template name="note-runden">
        <xsl:with-param name="note" select="number($note4) + 
                                    number($i)*number($schrittweite)"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="von" select="$note-von-gerundet+0.5"/>
    <xsl:variable name="bis">
      <xsl:call-template name="note-runden">
        <xsl:with-param name="note" select="number($note4)+
                                           (number($i)+1)*number($schrittweite)"/>
      </xsl:call-template>
    </xsl:variable>
    <fo:block>
      <fo:inline font-weight="bold">
        <xsl:value-of select="$i+4"/>
        <xsl:text> P: </xsl:text>
      </fo:inline>
      <xsl:value-of select="$von"/>
      <xsl:text> - </xsl:text>
      <xsl:value-of select="if ($i=11) then ($bis+0.5) else $bis"/>
    </fo:block>
    <xsl:call-template name="ab-note4">
      <xsl:with-param name="i" select="$i+1"/>
      <xsl:with-param name="note4" select="$note4"/>
      <xsl:with-param name="schrittweite" select="$schrittweite"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>
Die drei Templates rufen sich so lange selbst auf, bis eine Abbruchbedingung erreicht ist. In den Templates, in denen die zwei Sonderfälle Note 4 und Note 5 behandelt werden, bestimmt die Abbruchbedingung eine Laufvariable $i , die jeweils mit 3 (1P + 2P) und 12 (3P + 4P + 5P) ihren Schwellenwert hat. Das Template notentabelle dispatched auf die Sonderfälle und kümmert sich selbst um die Noten 1 bis 3.
Ohne jetzt groß meine Logik von damals zu überprüfen und in Frage zu stellen, finde ich doch, dass sich XSLT in den letzten Jahren sehr gemausert hat. Musste man sich früher noch mit diversen Saxon Bugs bzgl. der XPath Auswertung herumschlagen, ist diese nun doch äusserst stabil und es gelingen auch komplizierte Prädikate und verschachtelte Selektoren auf Anhieb.
Eine Iteration mittels Endrekursion ist heute in den meisten Fällen nicht mehr notwendig. Auch Templates, die nur eine Hilfsfunktion realisieren, wie das obige Template note-runden , werden besser mittels xsl:function umgesetzt, was auch innerhalb eines XPath Ausdrucks ausgewertet werden kann.

4.4.4  XSL-FO Seitenvorlage

Um diese Logik nun in eine XSL-FO Transformation einzubauen, wickelt man einfach das typische XSL-FO Gerüst herum:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fo="http://www.w3.org/1999/XSL/Format"
  xmlns:html="http://www.w3.org/1999/xhtml">
       
  <xsl:output method="xml" indent="no"/>
  
  <!-- Logik von oben hier einfuegen -->
       
  <xsl:template name="noten">
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-size="6pt">
      <fo:layout-master-set>
        <fo:simple-page-master master-name="my-page">
          <fo:region-body
            margin="5mm"
            column-gap="2mm"
            column-count="10"/>
          <fo:region-start extent="5mm"/>
          <fo:region-end extent="5mm"/>
        </fo:simple-page-master>
      </fo:layout-master-set>
      <fo:page-sequence master-reference="my-page">
        <fo:flow flow-name="xsl-region-body">
          <fo:block font-size="12pt" color="#999" font-weight="bold" 
                    span="all" text-align="center" line-height="20pt" 
                    letter-spacing="5mm">
                    <xsl:text>Notentabellen</xsl:text>
          </fo:block>
          <xsl:call-template name="noten-tabelle">
            <xsl:with-param name="von-punkte">12</xsl:with-param>
            <xsl:with-param name="bis-punkte">261</xsl:with-param>
          </xsl:call-template>
        </fo:flow>
       </fo:page-sequence>
     </fo:root>
   </xsl:template>
</xsl:stylesheet>
In der Seitenvorlage fo:simple-page-master wird das Layout der Seite festgelegt. Im Seitenfluss fo:page-sequence wird diese Vorlage referenziert.
Dieses Beispiel zeigt schon die Flexibilität des Ansatzes: Man erstellt für eine Publikation eine Reihe von Seitenvorlagen für die unterschiedlichen Layoutbereiche und referenziert bei Bedarf. Dadurch wird Redundanz vermindert. Ausserdem können die Vorlagen in einer anderen Ausgabestrecke ggf. parameterisiert wiederverwendet werden.
Relativ neu ist die Auszeichnung von Layout und Design im PDF Bereich mittels CSS. Der Fachbegriff hierzu lautet "CSS for Paged Media". AntennaHouse Formatter unterstützt diese Technologie und erste Ausgabestrecken werden damit umgesetzt. Wie aber die Entwickler von AntennaHouse bestätigen (Auf der XML Prag 2019) wird XSL-FO bzgl. der Umsetzung komplexer Layouts seine Alleinstellung behalten.
Previous Page Next Page
Version: 93
Jan 25 2021