Die Bürosoftware Openoffice lässt sich auf vielfältige Weise mit Skripts und Makros automatisieren. Am einfachsten funktioniert das noch mit dem eingebauten Basic-Dialekt. Dieser Artikel bietet einen Einstieg in die trotzdem anspruchsvolle Programmierung.
Openoffice [1] stellt eine Menge nützlicher Funktionen zur Verfügung. Manchmal reichen aber selbst diese nicht aus. Dann helfen Skripts, mit denen sich jede erdenkliche Anwendung programmieren lässt. Schon in Version 1.1 gab es den Weg, über so genannte Bridges, eigene Anwendungen mit C, C++, Java oder Python zu schreiben. In der bevorstehenden Version 2.0 sollen diese Möglichkeiten schon im Standard-Openoffice eingebaut sein. Dazu kommt das so genannte Common Language Interface (CLI), das eigene Programme in Javascript und C# und auch das Mischen der Sprachen erlauben soll.
Komplexes UNO, simples Basic
Die einfachste Variante der Openoffice-Programmierung bietet zur Zeit die Sprache Basic. Sie ist ohne weiter Klimmzüge in Openoffice verfügbar und auch ohne Informatik-Studium zu beherrschen. Ganz ohne einige fortgeschrittene Konzepte geht es aber nicht, denn die komplexe Architektur von Openoffice hat auch in der Basic-Schnittstelle Spuren hinterlassen. Die Sprache selbst ähnelt dem von Microsoft-Produkten bekannten Visual Basic, ist aber nicht zu 100% kompatibel.
Die sprachunabhängige Openoffice-Programmierschnittstelle heißt UNO (Universal Network Objects). Sie gehorcht den zur Zeit modernen Designprinzipien (und den so genannten Entwurfsmustern [2] ) für Software: Dienste (Services), Komponenten und Schnittstellen (Interfaces). Der Umfang von Entwickler- [3] und API-Dokumentation [4] sprechen für sich.
Das Basic in Openoffice kann nicht den ganzen Umfang von UNO unterstützen, da die Sprache sehr einfach aufgebaut ist. So kennt sie zum Beispiel keine komplexen Datentypen wie Hashes, die Schlüsselworten Werte zuordnen. Das machte ein geplantes Beispiel schwer zu realisieren: Es sollte zählen, wie häufig jedes Worts in einem Text vorkommt. Dazu bietet es sich an, einen solchen Hash (auch assoziatives Array) zu verwenden, mit dem Wort selbst als Schlüssel (Key) und der Anzahl als Wert. Diese einfache Aufgabe erfordert in Openoffice schon einen außordentlichen Programmieraufwand. Man müsste schon selbst eine Hash-Tabelle implementieren, aber das würde den Umfang dieses Artikels kaum sprengen. Stattdessen stellen wir einige Grundlagen des Machbaren anhand eines einfachen HTML-Exporters vor.
Makros, Module und Bibliotheken
Im Gegensatz zum Openoffice-Basic arbeitet die allgemeine Schnittstelle von UNO objektorientiert. Dieser Bruch spiegelt sich in einigen Eigenarten wider. So werden manche Methoden direkt auf Eigenschaften (Properties) abgebildet. Das bedeutet, dass man als Programmierer keine Funktion kreis.radius() aufrufen muss, sondern direkt das Attribut kreis.radius benutzen darf. In der Praxis bedeutet das jedenfalls Verwirrung, denn man findet in Beispielen beide Schreibweisen gemischt.
Die Programmierung mit Basic beginnt im Menü Extras | Makros. Dort finden sich zwei Einträge: Makro aufzeichnen, das eine Art Rekorder für die interaktive Benutzung darstellt, und Makro..., das ein neues Fenster öffnet (Abbildung 1).
Es dient zur Organisation der mitgelieferten und selbstgeschriebenen Makro-Skripts. Die Basic-Programme sind jeweils einem Modul und einer Bibliothek zugeordnet, per Default Module1 respektive Standard. Ein neues Modul enthält bereits die Methode Main, deren Rumpf existiert, die aber sonst noch keinen Code enthält. Klicken Sie auf den Button Bearbeiten, öffnet sich der Editor und sie sehen das leere Gerüst der Funktion (Abbildung 2).

Abbildung 2: Legt man ein neues Makro an, erzeugt Openoffice eine leere Funktion Main, die hier im integrierten Editor zu sehen ist.
Nun steht der ganze Basic-Sprachumfang zur Verfügung. Wählen Sie im Editor Hilfe | Inhalt, erscheint die Hilfe zu Openoffice-Basic, wo Sie unter anderem eine Liste der Funktionen finden (Makros und Programmierung | Befehle | Alphabetische Liste ..). Einen guten Einstieg bietet auch das Tutorial zu Starbasic [5], der Sprache des Staroffice-Produkts von Sun. Es passt auch zu Openoffice.
Ergänzen Sie für den Anfang das Programmgerüst folgendermaßen:
Sub Main
Ausgabe = "Es ist " & Time()
MsgBox Ausgabe, 0
End Sub
Das Schlüsselwort Sub gibt an, dass es sich um eine Funktion handelt, die in diesem Fall den Namen Main trägt. Tatsächlich schert sich Openoffice wenig um den Namen, es startet die Abarbeitung einfach bei der ersten Funktion im Makromodul. Bei Ausgabe handelt es sich um einen String, der sich aus dem festen Bestandteil “Es ist ” und der Ausgabe der Funktion Time() zusammensetzt, die die aktuelle Zeit liefert. Der Operator & verkettet beide Teile zu einem String. Sie können auch, wie beschrieben, die Klammern der Time-Funktion weglassen – es macht keinen Unterschied.
Die Methode MsgBox schließlich zeigt ein Dialog-Fenster. Der erste Parameter enthält den anzuzeigenden Text, der zweite bestimmt den Typ des Dialogs. Die “0” im Beispiel legt fest, dass er nur einen Ok-Button enthalten soll. Eine “1” würde zusätzlich einen Abbruch-Button einbauen. Es existieren weitere Varianten für Ja-/Nein-Buttons und andere Kombinationen. MsgBox gibt auch einen Wert zurück, der anzeigt, welche Buttons der Benutzer gedrückt hat. Das Beispiel macht davon aber keinen Gebrauch.
Startpunkt: Dieses Dokument
Um mit Openoffice-Dokumenten zu arbeiten, genügen die Basic-Grundfunktionen nicht, denn dafür ist der Zugriff auf die beschrieben UNO-Objekte und Interfaces nötig. Den Einstieg in diese Objekthierarchie bietet ThisDocument. Damit erhält der Programmierer eine Referenz auf das Dokument, in dem das Skript abläuft – egal ob es sich um einen Text, eine Tabellenkalkulation oder eine Zeichnung handelt. Gewissermaßen hängen an diesem Objekt die grundlegenden Methoden, von denen aus sich der Programmierer am baumförmigen Aufbau eines Dokuments weiterhangelt.
Davor steht häufig noch die so genannte Deklaration der Variablen. Die ist allerdings optional, deshalb auch nicht weiter problematisch. Das Schlüsselwort Dim übernimmt diese Aufgabe. Bei Listen (Arrays) steht die Zahl der Elemente in normalen Klammern: liste(10). Die Basic-Konvention weicht hier noch in einem zweiten Punkt von anderen Programmiersprachen ab: Der Parameter steht nicht für die Anzahl der Elemente, sondern für den höchsten Index. Liste(10) besitzt also 11 Elemente, von Liste(0) bis liste(10) – wie man sieht, spielen Groß- und Kleinschreibung auch keine Rolle.
Nachdem das Skript die Referenz auf das Dokument erhalten hat, prüft es, ob es sich um ein Textdokument handelt. Dazu dient die Methode supportsService() eben dieser Dokumentreferenz:
oDoc = ThisComponent
If oDoc.supportsService("com.?
sun.star.text.TextDocument") Then
Neben dieser Methode kennt das Dokumentenobjekt noch die vielversprechende Funktion getText(). Sie liefert aber nicht einfach den rohen Text des ganzen Dokuments, sondern eine Referenz auf den so genannten Text-Service. Der kennt wiederum andere Methoden, zum Beispiel um mit einem Cursor durch den Text zu wandern (createTextCursor).
Eine andere Funktion des Text-Service heißt createEnumeration(). Sie gibt eine Aufzählung aller Absätze des Dokuments zurück. Doch damit hat man den Rohtext immer noch nicht erreicht. Vielmehr gehören zu einem Absatz auch noch knapp 150 Absatzeigenschaften, die zum Beispiel den jeweiligen Stil beschreiben.
An die normalen Textelemente eines Absatzes kommt man wieder mit createEnumeration(). Enthält das Dokument eine Tabelle, meldet Openoffice einen Fehler: Denn das entsprechende Element kennt die Methode createEnumeration nicht. Man müsste diesen Fall also prüfen und gesondert behandeln.
Den Rohtext jedes Elements liefert die Methode String(). Die Aufzählungen durchläuft man in Basic jeweils mit einer Schleife, die mit Do startet und mit Loop endet. Die Abbruchbedingung darf dabei hinter jedem der beiden Schlüsselwörter stehen. Befindet sie sich am Schleifenende, durchläuft das Skript die dazwischen stehenden Befehle mindestens einmal.
Die so ausgelesenen Textdaten schreibt das Beispielmakro in eine Datei, deren Name die Variable Filename angibt. Ungewöhnlich ist, dass man zum Öffnen der Datei noch eine Dateinummer braucht, die man über die Methode Freefile() erhält. Damit und mit dem Namen öffnet die Open() die Datei zum Schreiben:
Open Filename For Output ? As #FileNo
Um einzelne Zeilen in die Datei zu schreiben, dient der Befehl Print mit der Dateinummer als erstem Parameter: Print #FileNo String. Ohne File-Nummer öffnet Openoffice mit Print ein Dialogfenster. Sind alle Zeilen abgearbeitet, bleibt nicht mehr, als die Datei zu schließen. Das übernimmt Close mit der Dateinummer als Argument. Listing 1 zeigt das vollständige Makro.
Listing 1
Einfacher Text-Exporter
Sub Main
Dim oDoc As Object
Filename = "/home/oliver/output.txt"
oDoc = ThisComponent
title$ = oDoc.DocumentInfo.Title
If oDoc.supportsService("com.sun.star.text.TextDocument") Then
FileNo = Freefile()
Open Filename For Output As #FileNo
oText = oDoc.getText()
oParagraphs = oText.createEnumeration()
Do While oParagraphs.hasMoreElements()
oPar = oParagraphs.nextElement()
oTexts = oPar.createEnumeration()
Do While oTexts.hasMoreElements()
oText = oTexts.nextElement()
Print #FileNo oText.string
If oText.string = "" Then
Print #FileNo
Endif
Loop
Loop
Endif ' If oDoc.supportsService(..)
Close #FileNo
End Sub
Um das Skript zu starten, klicken Sie auf den zweiten Button von links in der zweiten Reihe (siehe Abbildung 1). Es bearbeitet dann das aktuelle Dokument. Findet Openoffice einen Syntaxfehler, meldet es ihn sofort. Fehler in der Ausführung zeigt es in Dialogboxen an. Leider fallen diese Meldungen meist recht allgemein aus und sind deshalb wenig hilfreich (etwa “Objektvariable nicht belegt”). Ein “Allgemeiner Fehler” tritt zum Beispiel auch auf, wenn Sie der Hilfebrowser bei der Skriptausführung das aktuelle Dokument ist.
Die Buttons mit den geschweiften Klammern ermöglichen es, den Code schrittweise abzuarbeiten. Markieren Sie einen Variablennamen und drücken dann den Knopf mit der Brille, zeigt das untere Feld den Wert – mit den oben erwähnten Einschränkungen. Bessere Debugging-Tipps finden Sie im Kasten “Fortgeschrittene Fehlersuche”.
Fortgeschrittene Fehlersuche
Der eingebaute Debugger ist nur bei grundlegenden Basic-Typen nützlich. Weil die Openoffice-UNO-Typen keine direkte Entsprechung in Basic besitzen, zeigt er für UNO-Objekte nur Fragezeichen. Einige Methoden verschaffen mehr Informationen, erfordern aber etwas Programmieraufwand. Als hilfreich erweisen sich zum Beispiel Dbg_supportedInterfaces() und Dbg_methods(), die man als Methoden eines Objekts aufruft.
Ein fertiges Basic-Makro vereinfacht demgegenüber die Fehlersuche. Es zeigt auf Wunsch ein Fenster mit allen Methoden und Eigenschaften solcher Objekte (Abbildung 3).
Das Makro heißt passenderweise XRay [6], denn es durchdringt praktisch jedes Detail eines solchen Objekts. Haben Sie die Zip-Datei entpackt, laden Sie das Dokument in Openoffice und folgen den Anweisungen. Im Prinzip müssen Sie nur das im Dokument enthaltene XRay-Makro mit dem Dokument verknüpfen, an dem Sie arbeiten (Makromanagement | Verwalten, dann Bibliotheken). Um ein Objekt zu durchleuchten, schreiben Sie in Ihre Skripts eine Zeile wie Xray.Xray oDoc.
Auf derselben Webseite finden Sie übrigens ein weiteres hilfreiches Dokument: eine Sammlung von Skript-Fragmenten [7] mit Erklärungen von Makro-Buchautor Andrew Pytonyak.

Abbildung 3: Das Makro Xray hilft bei der Fehlersuche. Es zeigt auch in Basic die Eigenschaften von UNO-Objekten.
Warten und Hoffen
Openoffice bietet mit der UNO-Schnittstelle einen programmierbaren Baukasten für Büroanwendungen, mit dem ein findiger Entwickler nahezu alles realisieren kann. Leider gestaltet sich Programmierung entsprechend schwierig. Das ganze System ist ebenso komplex wie CORBA [8] oder J2EE [9] und setzt die Kenntnis moderner Konzepte der Software-Entwicklung voraus, wie Komponentenarchitekturen und Entwurfsmuster.
Damit eignet es sich nicht unbedingt für den Hobby-Anwender, stellt aber eine Spielwiese für ambitionierte Amateure und Profis dar. Die kommende Openoffice-Version dürfte die Programmierschnittstellen für Sprachen wie Java oder Python vereinheitlichen. Im aktuellen Zustand bietet Basic keine echte Alternative.
Infos
[1] Openoffice: http://www.openoffice.org
[2] Entwurfsmuster: http://de.wikipedia.org/wiki/Design_Pattern
[3] Developer’s Guide: http://api.openoffice.org/docs/DevelopersGuide/DevelopersGuide.htm
[4] UNO-Referenz: http://api.openoffice.org/docs/common/ref/com/sun/star/module-ix.html
[5] Starbasic-Tutorial von Sun: ftp://docs-pdf.sun.com/817-3924/817-3924.pdf
[6] XRay: http://www.ooomacros.org/dev.php#101416
[7] Macros explained: http://www.ooomacros.org/dev.php#91896
[8] CORBA: http://www.corba.org
[9] J2EE: http://java.sun.com/j2ee





