headerbild blog

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.
von Patrick Gundlach |

Feature der Woche: Mehrseitige PDFs einbinden

Diese Woche gibt es ein sehr kurzes Beispiel: wie binde ich mehrseitige PDF Dateien im speedata Publisher ein?

Eigentlich ist die Sache ganz einfach. Mithilfe der Funktion sd:number-of-pages() kann man ermitteln, wie viele Seiten eine PDF-Datei hat. Und bei dem Befehl Image, um ein Bild einzubinden, kann man die gewünschte Seitenzahl angeben (das natürlich nur Sinn ergibt, wenn man eine PDF-Datei einbettet).

von Patrick Gundlach |

Feature der Woche: Datenverarbeitung

Kein Feature an sich, aber eine Erklärung wert: Wie funktioniert die Datenverarbeitung im speedata Publisher? Ein etwas umfangreicherer Beitrag, es lohnt sich.

von Patrick Gundlach |

Layoutregeln weiterhin in mehreren Sprachen?

Derzeit gibt es die Möglichkeit, die Layoutanweisungen für den speedata Publisher auf Englisch und auf Deutsch zu formulieren. Dazu nutzt man einfach einen anderen Namensraum: urn:speedata.de:2009/publisher/en für Befehle wie <PlaceObject> und urn:speedata.de:2009/publisher/de (die Endung ist anders) für <ObjektAusgeben>.

Ursprünglich war das Layoutregelwerk ausschließlich auf Deutsch verfügbar, auch die Ausgaben auf der Konsole gab es nur auf Deutsch. Ab der Version 1.6 gibt es auch die Möglichkeit, englischsprachige Regelwerke zu schreiben. Das Handbuch ist inzwischen auch zweisprachig verfügbar, wobei die Ausgabe auf Englisch ein paar Rechtschreibfehler mehr enthalten dürfte…

Die beiden Sprachen sind in der Praxis gleichberechtigt. Der Übersetzungsprozess findet automatisch im Publisher statt. Aus dem Handbuch (Datei commands.xml) werden sowohl die beiden Schemadateien für die Regelwerke als auch die Übersetzungsdatei (translations.lua) für den Publisher generiert. Somit muss ich für neue Befehle und neue Attribute (Parameter) nur eine Datei ändern, sofort habe ich ein aktuelles Schema für Englisch und Deutsch und der Publisher erkennt die Übersetzung.

Wenn ich in meine Projekthistorie schaue, entdecke ich, dass ich immer weniger die deutschsprachigen Layoutanweisungen nutze, ob wohl diese ja äquivalent sind und sich automatisch transformieren lassen. In mehreren Projekten arbeite ich mit einem internationalen Team zusammen, damit besteht dort keine Auswahl. Das deutschsprachige Regelwerk nutze ich ab und zu für Demonstrationen, wenn die Ansprechpartner nicht Entwickler sind.

Nun überlege ich, ob ich für die Zukunft die Mehrsprachigkeit weglasse. Das würde mir in der Entwicklung doch einige neue Möglichkeiten in der Optimierung bringen. Es ist ein großer Einschnitt, womöglich betrifft es aber sehr wenige Anwender. Ich würde mich sehr über Feedback freuen, z.B. per E-Mail, Kommentar oder Twitter. Es wird eine Übersetzungshilfe in Form eines XSLT-Skripts geben, das aber nicht alle Fälle erschlagen wird (ca. 5% Nachbearbeitung notwendig). Für mich bedeutet das zwar einen Haufen Arbeit für die nächsten Versionen, aber langfristig wird sich das wohl lohnen. Der Plan ist, erst einmal die Beispiel im Handbuch alle auf Englisch zu übersetzen und die Mehrsprachigkeit nicht mehr zu erwähnen. Bis Version 3.0 könnten die deutschsprachigen Regelwerke weiterhin (aber mit Warnung) funktionieren und anschließend würde ich die Funktionalität entfernen.

Eigentlich widerspricht das meinem Wunsch nach hundertprozentiger Abwärtskompatibilität, aber vielleicht muss man manchmal alte Zöpfe doch abschneiden!?

Doch etwas länger geworden als gedacht. Eine Anekdote muss ich doch noch anbringen: ein Auftraggeber in der Schweiz hat erst das deutschsprachige Regelwerk genutzt. Nachdem die Entwickler den Befehl »Tabellenfuß« aber mangels »ß« auf der Tastatur nicht eingeben konnten, haben sie auf das englischsprachige Layout gewechselt. An so etwas habe ich beim besten Willen nicht gedacht.

von Patrick Gundlach |

Feature der Woche: Seitentypen

Seitentypen (oder auch Masterseiten, Seitenvorlagen) dienen dazu, Ränder für Seiten zu definieren, Platzierungsrahmen zu erstellen und Aktionen auszuführen, wenn eine Seite erzeugt oder in die PDF Datei geschrieben wird. Klassischerweise gibt es eine Seitenvorlage für linke Seiten und für rechte Seiten. Im einfachsten Fall sieht eine Vorlage so aus:

von Patrick Gundlach |

Neues Feature: Tabellenumbruch und »Zukünftige Objekte«

Die neue Version 2.5.5 hat ein kleines, aber nettes Feature. Eigentlich sind es zwei schon bestehende Features, die durch eine kleine Änderung nun zusammenarbeiten:

  1. Man kann Objekte auf einer zukünftigen Seite platzieren.
  2. Tabellen können sich über mehrere Seiten erstrecken und werden automatisch umbrochen.

Nun können diese beiden Funktionalitäten kombiniert werden.

von Patrick Gundlach |

Feature der Woche: Wiederholender Tabellenkopf (1)

Es gibt zwei Arten, in Tabellen Tabellenköpfe zu definieren. Die erste Variante stelle ich diese Woche vor. Sie eignet sich besonders, wenn der Tabellenkopf statisch ist. Die zweite Variante (nächste Woche) eignet sich, wenn gewisse Tabellenzellen als Kopfzeile dienen sollen. Beide Varianten kann man auch kombinieren.

von Patrick Gundlach |

Fazit der PDF Tage 2016

Nun sind sie vorbei, die PDF-Tage 2016 in Berlin (korrekte Bezeichnung: PDF Days Europe 2016). Es war eine professionell organisierte Veranstaltung mit vier parallelen Tracks über zwei Tage, die sowohl »Praxis« als auch »Technik« ziemlich gut abgedeckt hat.

Eine Reihe der Branchengrößen waren vertreten (z.B. Adobe, iText, Callas, Abbyy und viele andere), so dass hier einiges Neues zu erfahren war. Gelegentlich könnte man den Eindruck gewinnen, auf einer Werbevaranstaltung zu sein, aber zum Glück wirklich nur gelegentlich.

Das Programm (ZUGFeRD und auch PDF/VT könnten interessant für mich sein) war inspirierend und ich hoffe, daraus im Nachgang auch etwas in den Publisher einfließen lassen zu können.

Etwas schade fand ich, dass das Theme »OpenSource« recht wenig Stellenwert in der PDF Community hat. Es wurde zwar beklagt, dass es deutliche Tendenzen gibt, weg von PDF hin zu anderen Formaten wie ePub oder HTML zu gehen. Sichtbare Schlüsse werden daraus nicht gezogen. Meines Erachtens ist einer der Erfolgsfaktoren einer Technologie, die in der »breiten Masse« verbleiben soll, eine große Verfügbarkeit OpenSource-Software.

Mit etwas Sorge betrachte ich die Zukunft der PDF-Standards. Nun soll es in naher Zukunft PDF 2.0 geben (ISO 32000-2), zusammen mit PDF/A-4, PDF/UA-2, PDF/E-2, PDF/Raster und den derzeit schon vorhandenen Standards. Ob das hilft, die Zukunft von PDF zu stärken?

Da fällt mir der Klassiker dazu ein:

Competing standards

Quelle, CC BY-NC 2.5.

von Patrick Gundlach |