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') < 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>