von Patrick Gundlach |

Feature der Woche: Virtuelle Seiten und Optimierung

Kürzlich hatte ich eine Anfrage reinbekommen, es ging um eine Optimierung einer Seite. Und zwar soll ein Bild und ein Text möglichst auf einer Seite dargestellt werden, die Bildgröße und -position soll variabel sein. Da ich aber, wie im Database Publishing üblich, keine Annahmen über die Bildgröße machen kann, muss eine allgemeingültige Lösung her.

Die Idee hier ist ein paar Vorgaben zu machen, wie die Seite aussehen könnte. Die Vorgaben können natürlich beliebig sein. In diesem Fall gebe ich Werte für die Bildgröße und -position vor. Ich weise den speedata Publisher an, Seiten zu erzeugen, und sobald eine Seite meinen Vorstellungen entspricht, gebe ich sie aus, ansonsten verwerfe ich die Seite.

Alles, was innerhalb von <SavePages> ... </SavePages> steht, wird zunächst gespeichert und nicht ausgegeben. Ich kann dann mithilfe der XPath-Funktion sd:count-saved-pages() abfragen, wie viele Seiten erzeugt wurden. Sofern dann 1 ergibt, kann ich abbrechen und brauche nicht weiter die Bildgröße zu reduzieren.

Die Datendatei data.xml (download) ist wie folgt aufgebaut:

<?xml version="1.0" encoding="UTF-8"?>
<Journal>
    <Artikel ueberschrift="Gummibärchen">
        <Text>
            <Absatz>Freilebende Gummibärchen gibt es nicht.
              Man kauft sie in Packungen an der
                Kinokasse. Dieser Kauf ist der Beginn
                einer fast erotischen und sehr ambivalenten
                Beziehung Gummibärchen-Mensch. Zuerst genießt man.
                Dieser Genuß umfaßt alle Sinne.
                Man wühlt in den Gummibärchen, man fühlt sie.
            </Absatz>
        </Text>
    </Artikel>
</Journal>

Es folgt die Datei layout.xml, die auch herunterladbar ist.

Der Kopf ist wieder wie üblich, die Option ignoreeol besagt, dass die Zeilenumbrüche in den Daten ignoriert werden sollen.

<Layout xmlns="urn:speedata.de:2009/publisher/en"
  xmlns:sd="urn:speedata:2009/publisher/functions/en">

  <Options ignoreeol="yes" mainlanguage="German"/>

Anschließend wird die Schriftgröße für die Überschrift eingestellt (die voreingestellte Schriftgröße mit dem Namen text ist 10 Punkt mit 12 Punkt Zeilenabstand), zwei Textformate definiert und allgemeine Seiteneinstellungen gemacht. Wenn man die Rasterhöhe gleich dem normalen Zeilenabstand legt, kann man leicht registerhaltigen Satz erreichen. Der Seitentyp definiert zwei Seitenbereiche. Einfach mal mit sp --grid ein PDF erzeugen, dann sieht man die Seitenbereiche rot eingezeichnet.

  <DefineFontfamily name="Title" fontsize="16" leading="16">
    <Regular fontface="TeXGyreHeros-Bold"/>
  </DefineFontfamily>

  <DefineTextformat name="indent" indentation="14pt"/>
  <DefineTextformat name="Title"  alignment="rightaligned"/>

  <SetGrid width="5mm" height="12pt"/>
  <Pageformat width="210mm" height="297mm"/>
  <Pagetype name="text" test="true()">
    <Margin left="1,5cm" right="0,5cm" top="1cm" bottom="2cm"/>

    <PositioningArea name="Überschrift">
      <PositioningFrame width="27" height="8" row="1" column="1"/>
    </PositioningArea>

    <PositioningArea name="text">
      <PositioningFrame width="13" height="51" row="9" column="1"/>
      <PositioningFrame width="13" height="51" row="9" column="15"/>
    </PositioningArea>
  </Pagetype>

Es folgt die eigentliche Datenverarbeitung. Anfangs werden die Vorgaben zur Bildposition und Bildgröße festgelegt. Die Variable i wählt dann die Vorgabe 1, 2 oder 3 aus.

  <Record element="Journal">

    <SetVariable variable="bildbreite1" select="38"/>
    <SetVariable variable="bildspalte1" select="1"/>

    <SetVariable variable="bildbreite2" select="24"/>
    <SetVariable variable="bildspalte2" select="15"/>

    <SetVariable variable="bildbreite3" select="10"/>
    <SetVariable variable="bildspalte3" select="29"/>
    <SetVariable variable="i" select="0"/>

Nun wird in der Schleife der Text mit Bild erzeugt, zwischengespeichert und geprüft, ob es eine Seite geworden ist. Wenn ja, wird das Abbruchkriterium ($i = 3) gesetzt. Ansonsten geht die Schleife weiter. Das Spielchen könnte man natürlich beliebig weit treiben.

    <Until test="$i = 3">
      <SetVariable variable="i" select="$i + 1"/>

      <!-- SavePages heißt, die Seite erst im Speicher halten und _nicht_ ausgeben -->

      <SavePages name="seitemitbild">
        <!-- Erst das Bild platzieren, dann den Rest drumherum laufen lassen -->
        <PlaceObject column="{sd:variable('bildspalte',$i)}" row="9">
          <Image width="{sd:variable('bildbreite',$i)}" file="_sampleb.pdf"/>
        </PlaceObject>
        <NextRow/>
        <!-- Bildunterschrift -->
        <PlaceObject column="{sd:variable('bildspalte',$i)}">
          <Table stretch="max" padding="3pt">
            <Tr>
              <Td padding-left="10pt" align="left">
                <Paragraph>
                  <I><Value>Bildunterschrift</Value></I>
                </Paragraph>
              </Td>
            </Tr>
            <Tablerule rulewidth="20pt" color="white"/>
          </Table>
        </PlaceObject>

        <!-- Dann alle Artikel ausgeben -->
        <ProcessNode select="Artikel"/>
      </SavePages>

      <!-- Wenn es weniger als 2 Seiten sind, Schleife abbrechen -->
      <Switch>
        <Case test="sd:count-saved-pages('seitemitbild') &lt; 2 ">
          <SetVariable variable="i" select="3"/>
        </Case>
      </Switch>

    </Until>

Mit <InsertPages> wird eine Seitenstrecke ausgegeben.

    <InsertPages name="seitemitbild"/>
  </Record>

Was jetzt noch fehlt ist ist die Ausgabe des Textes. Die wird oben in <ProcessNode select="Artikel"/> angestoßen. Da dies ein Beispiel ist, erzeugen wir einen Absatz in einer Schleife.

  <Record element="Artikel">
    <Output area="Überschrift">
      <Text>
        <Paragraph textformat="Title" fontface="Title">
          <Color name="green">
            <Value select="@ueberschrift"/>
          </Color>
        </Paragraph>
      </Text>
    </Output>

    <Loop select="7" variable="c">
      <Output area="text" allocate="auto">
        <Text>
          <Paragraph fontface="text"
              textformat="{if ($c = 1) then 'text' else 'indent'}">
            <Value select="Text/Absatz"/>
          </Paragraph>
        </Text>
      </Output>
    </Loop>
  </Record>
</Layout>

Zum Erzeugen der PDF-Datei die beiden Dateien (layout.xml und data.xml) in ein Verzeichnis kopieren, im Terminal in das Verzeichnis wechseln und sp aufrufen.

Üblicherweise wird der Text aber Absatz für Absatz wie folgt ausgegeben.

<ForAll select="Text/Absatz">
  <Output area="text" allocate="auto">
    <Text>
      <Paragraph fontface="text">
        <Value select="."/>
      </Paragraph>
    </Text>
  </Output>
</ForAll>

Seitenaufbau mit 7 und 12 Absätzen. Im ersten Fall werden die Vorgaben »2« angewendet, im zweiten Fall die Vorgaben »3«

In der Serie »Feature der Woche« beschreibe ich jeden Montag mehr oder weniger nützliche Eigenschaften des Publishers. Kommentare gerne an mich per E-Mail oder einfach im Kommentarfeld.