Open Office 2 sitzt in den Startlöchern und alle Welt wartet gespannt – auf Features wie die Access-ähnliche Datenbankanwendung. Auch unter der Haube hat sich viel getan: Das SDK bietet eine Basisklasse für Java- Beans, um die Office-Suite in eigene Programme einzubinden.
Eine Beschreibung für das so genannte Office-Bean versteckte sich bereits in der Version 1.1 des Open Office Software Development Kit[1]. Mit diesem Java-Bean lassen sich Open-Office-Komponenten wie Calc oder Writer in eigene Java-Anwendungen einbetten. Dann ist es leicht, Programme durch enorm leistungsfähige Textverarbeitungs-, Tabellenkalkulations-, Vektorgrafik- oder Präsentationsfähigkeiten zu erweitern. Eingesetzt wird diese Komponente in angepasster Form zum Beispiel in Neo Office/J[2], einer Art Java-Wrapper um Open Office. Ziel des Projekts ist es, das Büroprogramm möglichst bequem auf Mac OS X zur Verfügung zu stellen.
Das Office-Bean, das in den Beispielen des alten Developer\’s Guide auftritt, ist jedoch für den produktiven Einsatz nicht tauglich. Die schon erwähnte Anpassung an eigene Bedürfnisse ist nämlich keine Kür, sondern Pflicht: Dem Programmierer bleibt nichts anderes übrig, als große Teile des Office-Bean selbst neu zu schreiben. Das ändert sich mit dem Office-Bean von Open Office 2. Es eignet sich nun als Basisklasse für eigene Erweiterungen und ist tauglich für den Produktivbetrieb.
Open Office fernsteuern
Die Fähigkeiten von Open Office erschöpfen sich aber nicht in der Möglichkeit, eingebettet zu werden. Es kann selbst Zusatzfunktionalitäten integrieren, zum Beispiel im Writer einen Button einfügen, der in Java geschriebene Funktionen aufruft. Auch lässt sich Open Office vollständig fernsteuern. So kann ein C++-, Java- oder Python-Programm eine Open-Office-Instanz öffnen, Text einfügen, Formatierungen durchführen, Dokumente öffnen, importieren, verarbeiten, speichern, drucken oder auch exportieren.
Universal Network Objects
Zentrale Anlaufstelle für Open-Office-Programmierprojekte ist das SDK mit dem enthaltenen 1000 Seiten starken Developer\’s Guide. Im Mittelpunkt der Programmierung stehen die Universal Network Objects (UNO), die für Open Office spezifische Objekt-Middleware[3]. Die komplexen Fernsteuerzaubereien überforderten nämlich sogar die existierende Middleware CORBA. Kurzerhand schufen die Entwickler mit UNO einen CORBA-Ersatz, der den eigenen Anforderungen gerecht wird (siehe zum Beispiel Open Office Developer\’s Guide, Kapitel 5.3, Seite 319).
Mit UNO lassen sich zum Beispiel Dokumente speichern, bearbeiten, formatieren, auslesen, auf dem Drucker ausgeben und vieles mehr. UNO lässt sich aus Star-Basic, Java, C++ und Python ansprechen. Es gibt auch die Möglichkeit, etwa die C++-Version mit Delphi zu verwenden. Das Office-Bean ist jedoch ein reines Java-Bean und dementsprechend nur mit Java nutzbar.
Bei UNO geht es nicht um Makros, also in Open-Office-Dokumenten eingebetteten Quelltext in der Programmiersprache Star-Basic. Bei der Fernsteuerung oder Einbettung durch UNO steuert ein anderes Programm Open Office. UNO ist vielmehr lediglich eine Zwischenschicht, mit der unterschiedliche Programmiersprachen die Funktionen von Open Office nutzen.
Doch lassen sich mit UNO Star-Basic-Makros erzeugen, Star-Basic kann andererseits alle UNO-Operationen ausführen, die auch von anderen UNO-fähigen Programmiersprachen ausführbar sind. Daher können Star-Basic-Makros selbst wiederum andere Makros erstellen. In ähnlicher Weise ermöglicht Open Office, dass eine Programmiersprache per UNO ein Makro in ein Dokument einbettet.
Office-Beans der Versionen 1 und 2
Das Office-Bean dient dem Einbetten der Office-Funktionalität in Java-Programme. Es wird zwar im noch aktuellen Developer\’s Guide von Open Office 1.1 beschrieben (Kapitel 16), doch ist diese Version nur umständlich einzusetzen. Das hat nichts damit zu tun, dass das Open Office Application Programming Interface in irgendeiner Form schlecht wäre – im Gegenteil, allen Unkenrufen zum Trotz ist das API gut dokumentiert und sehr zielstrebig.
Laut Michael Hönnig, dem Projektleiter für das Open-Office-API, gibt es drei Gründe, warum Entwickler für Version 1.1 das Office-Bean in wesentlichen Teilen neu schreiben müssen:
- Es ist keine offizielle Basisklasse:
»BasicOfficeBean« existiert nur als SDK-Beispiel. Ihm
fehlen wichtige Methoden und Parameter (zum Beispiel die Optionen
für »loadComponentFromURL«) und die
Fehlerbehandlung ist nur rudimentär implementiert. Da viele
Programmierer die Klasse individuell anpassen, kann Sun keine
allgemeinen Bugfixes dafür liefern. - Es ist mit dem alten Bean nicht möglich, die
Window-Parent-Beziehung wieder aufzuheben, wenn zum Beispiel eine
AWT-Komponente aus ihrem Parent entfernt wurde. Auch das Beenden
des Programms gestaltet sich schwierig, weil dafür eine
bestimmte Reihenfolge vorgeschrieben ist. Eine entsprechende
Änderung im SDK ist allerdings schon in neueren Versionen von
Open Office 1.1.x eingeflossen. - Abstürze und Hänger in Open Office machen das
Java-Bean unbenutzbar, manchmal stirbt sogar das AWT.
Open Office 2 behebt all diese Probleme. Das Office-Bean ist damit zu einer offiziellen Komponente geworden. Java-Entwickler können mit ihm Programme schreiben, die Suns Originalklasse einfach benutzen.
|
Listing 1: Speichern |
|---|
01 public synchronized void saveToStream(java.io.OutputStream o)
02 throws java.io.IOException
03 {
04 removeXSelectionChangeListener();
05 super.save(o);
06 postInitListenersEvent();
07 }
|
|
Listing 2: Speichern |
|---|
01 FileOutputStream o = null;
02 try {
03 o = new FileOutputStream("/tmp/MeinDokument.sxw");
04 } catch (FileNotFoundException ex) {
05 ex.printStackTrace();
06 }
07 try {
08 officeWriter1.saveToStream(o);
09 o.close();
10 } catch (IOException ex1) {
11 ex1.printStackTrace();
12 }
|
|
Listing 3: Einfaches |
|---|
01 com.sun.star.view.XPrintable xPrintable = (com.sun.star.view.XPrintable) com.sun.star.uno.UnoRuntime.queryInterface(com.sun.star.view.XPrintable.class,
02 officeWriter1.getTextDocument());
03
04 try {
05 xPrintable.print(null);
06 } catch (Exception ex1) {
07 ex1.printStackTrace();
08 }
|
Einschränkungen beim Gebrauch des Office-Bean
Aber auch in Open Office 2 gibt es einige Dinge, die zu beachten sind. Da es sich um ein natives Programm handelt, führt die Java Virtual Machine die Swing-Zeichenoperationen in der Regel optisch hinter der Darstellung von Open Office aus. In der Praxis heißt dies, dass ein Swing-Popup nur über einem Office-Bean gezeichnet werden kann, es sei denn, das Popup benutzt native Zeichenfunktionen.
Dies tut es, wenn man es als so genanntes Heavy-Weight zeichnen lässt, was aber nicht für alle Komponenten funktioniert, siehe[4]. Die Alternative besteht darin, alle grafischen Elemente nativ zu zeichnen, also in der Regel mit dem Abstract Window Toolkit (AWT). Profis könnten durchaus auch mit einer Einbettung in das Standard Widget Toolkit (SWT) Erfolg haben, für das jedoch keine Quelltextbeispiele existieren.
Alle Aktionen mit UNO setzen beim Endanwender ein Open Office voraus, das mit speziellen Optionen gestartet wird oder dessen Einstellungsdatei die Fernsteuerung erlaubt, siehe[3].
Probleme beim Packen
Außerdem ist es schwierig, eigene Programme als Jar-Archiv zu verpacken. Denn das erfordert die Kenntnis des Speicherorts von Open Office, der in den »CLASSPATH« des Archivs eingehen muss. Das Office-Bean gehört nämlich zu einer Klasse, die die Open-Office-Installation auf das Zielsystem kopiert. So kann sich der Pfad zur Open-Office-Installation von Maschine zu Maschine unterscheiden.
Einen eigenen Classpath über die Kommandozeile anzugeben ist bei ausführbaren Jar-Archiven nicht möglich; den Classpath durch einen Loader in das Manifest der Jar-Datei schreiben ist aber offiziell nicht vorgesehen. Eine weitere Alternative besteht in einem selbst geschriebenen Classloader. Der zieht jedoch massive programmiertechnische Auswirkungen nach sich, da der Entwickler sämtliche Klassennamen einem Typecast unterziehen muss, auch die im Office-Bean.
Bugs umgehen
Die Dokumentation für das neue Office-Bean liegt zwar vor, sie ist jedoch noch nicht in das SDK von Open Office 2 eingepflegt, das außerdem per CVS ausgecheckt werden müsste. Deshalb soll das Verständnis für UNO-Anwendungen und das Office-Bean durch Beispiele aus dem aktuellen Open Office 1.1 verdeutlicht werden. Bei Kernel 2.6 empfiehlt sich Open Office 1.1.4, da sonst ein Bug im Zusammenhang mit IPv6 die Kommunikation mit Open Office verhindert.
Writer einbetten
Sind Open Office 1.1.4 und das entsprechende SDK installiert und das Programm wie im Developer\’s Guide beschrieben konfiguriert (“Make the office listen”, Kapitel 2, Seite 31), kann\’s losgehen. Der Developer\’s Guide verwendet Java-Beispiele, die man per Kommandozeile kompiliert oder in Net Beans lädt. Selbstverständlich ist das Office-Bean auch JBuilder-kompatibel.
Zu Beginn erstellt der angehende Open-Office-Programmierer ein neues Projekt mit einer AWT-Anwendung und fügt in den Projekteigenschaften als benötigte Bibliotheken alle Archive unterhalb »program/classes« der Open-Office-Installation hinzu – wie erwähnt befinden sich diese Klassen nicht im SDK. Dann kopiert er die Dateien »Office.java«, »OfficeWriter.java«, »BasicOfficeBean.java« und »OfficeCommand.java« aus »examples/DevelopersGuide/OfficeBean/OfficeWriterBean« beziehungsweise »examples/DevelopersGuide/OfficeBean/« in den Pfad des Package, ändert deren Package-Deklaration entsprechend und nimmt sie in das Projekt auf.
Grafische Oberfläche gestalten
Der GUI-Entwickler startet das Designer-Programm für »Frame1«, wählt als Layout-Manager »XYLayout«, sucht dann in der Bean-Auswahl nach »OfficeWriter« und positioniert ihn im Fenster. Wenn er »import com.sun.star.beans.LocalOfficeConnection« hinzufügt, kann er einen AWT-Button platzieren, der »officeWriter1.setOfficeConnection(new LocalOfficeConnection())« und in einem Try-Catch-Block »officeWriter1.load(“private:factory/swriter”)« ausführt. Zusätzlich empfiehlt sich ein »WindowListener«, der beim Schließen der Anwendung »officeWriter1.closeConnection()« aufruft, sofern »officeWriter1« nicht gleich »null« ist.
Dabei ist interessant, dass es sich bei »private:factory/swriter« um einen speziellen Uniform Resource Locator handelt, der ein neues Writer-Dokument erzeugt. Jede andere URL, etwa mit den Protokollen »file://« für das Dateisystem oder »http://« für WebDAV, lädt das entsprechende Dokument.
|
Listing 4: Drucken |
|---|
01 com.sun.star.beans.PropertyValue[] printerDesc = new com.sun.star.beans.PropertyValue[1];
02 printerDesc[0] = new com.sun.star.beans.PropertyValue();
03 printerDesc[0].Name = "Name";
04 printerDesc[0].Value = "<ml1650>";
05
06 try {
07 xPrintable.setPrinter(printerDesc);
08 } catch (com.sun.star.lang.IllegalArgumentException ex2) {
09 ex2.printStackTrace();
10 }
|
Das neue Programm startet Open Office durch den »setOfficeConnection«-Aufruf, sobald der Anwender den Knopf drückt (Abbildung 1). Aber auch dies passiert wie beim richtigen Open Office nur einmal: Hat der User mehrere Fenster oder mehrere Open-Office-Dokumente in einem Fenster geöffnet, fällt auch hier die volle Ladezeit nur einmal an.
Arbeiten lässt sich mit dem eigenen Open-Office-Ableger erst, wenn die Menüleiste sichtbar ist: Gestaltet es sich doch sonst etwas schwierig, zum Beispiel Grafiken einzufügen. Nach dem Öffnen eines Dokuments dient dazu die Anweisung »officeWriter1.setMenuBarVisible(true)«. Dass dies ohne aktives Dokument nicht funktioniert, also nicht zuerst »setMenuBarVisible« und anschließend »load« aufgerufen werden kann, ist eines der Probleme, die das Office-Bean von Open Office 2 löst.
|
Listing 5: Text |
|---|
01 com.sun.star.util.XReplaceDescriptor xReplaceDescriptor = null;
02 com.sun.star.util.XSearchDescriptor xSearchDescriptor = null;
03 com.sun.star.util.XReplaceable xReplaceable = null;
04
05 xReplaceable = (com.sun.star.util.XReplaceable) com.sun.star.uno.UnoRuntime.
06 queryInterface(
07 com.sun.star.util.XReplaceable.class,
08 officeWriter1.getTextDocument());
09
10 xReplaceDescriptor = (com.sun.star.util.XReplaceDescriptor)
11 xReplaceable.createReplaceDescriptor();
12
13 xReplaceDescriptor.setSearchString("Word");
14 xReplaceDescriptor.setReplaceString("Writer");
15 xReplaceable.replaceAll(xReplaceDescriptor);
|
|
Listing 6: Einfügen von |
|---|
01 com.sun.star.beans.PropertyValue[] writerLoaderProps = new com.sun.star.beans.PropertyValue[1];
02 writerLoaderProps[0] = new com.sun.star.beans.PropertyValue();
03 com.sun.star.text.XText aText = officeWriter1.getTextDocument().getText();
04 com.sun.star.text.XTextCursor xtc = aText.createTextCursor();
05 xtc.gotoEnd(false);
06
07 com.sun.star.document.XDocumentInsertable ins = (com.sun.star.document.XDocumentInsertable) $$
08 com.sun.star.uno.UnoRuntime.queryInterface(com.sun.star.document.XDocumentInsertable.class, xtc);
09 try {
10
11 ins.insertDocumentFromURL("file:///tmp/MeinDokument.sxw",
12 writerLoaderProps);
13 } catch (Exception ex1) {
14 ex1.printStackTrace();
15 }
|
Speichern und drucken
Das Speichern muss man sich erst in der Klasse »officeWriter« zugänglich machen. Die Funktion ist allerdings ähnlich einfach gestrickt wie das Laden. Listing 1 zeigt, wie\’s geht: Die Funktion ruft im Wesentlichen lediglich die Methode »save()« der Vaterklasse auf (Zeile 5). Mit dem Code in Listing 2 lässt sich dann ein Dokument speichern. Dazu benutzt es die in Listing 1 geschriebene Methode (Zeile 8).
Für das Drucken kommt ein »XPrintable«-Objekt zum Einsatz, das wieder per »getTextDocument()« auf den Inhalt des Writers zugreift (Listing 3, Zeile 1). Die Ausgabe auf dem Standarddrucker startet dann in Zeile 5 »xPrintable.print()«. Dieses Beispiel steht allerdings nur jenen Anwendern offen, die sich an der saftigen Warnung »WARNING! writing null sequence as empty sequence« der Fehlerausgabe nicht stören.
Sensible Naturen werden zwischen der »xPrintable«-Zuweisung und dem Try-Catch-Block etwas Sinnvolles mit den üblicherweise benötigten »PropertyValues« anfangen, etwa die Auswahl eines Druckers (Listing 4, Zeilen 2 bis 4). Open Office verlangt Druckernamen in spitzen Klammern. Das Programm druckt also auf dem Drucker »ml1650«, eingetragen als »<ml1650>« (Zeile 4).
Ersetzen von Text
Beim Ersetzen von Text ist zunächst mit »officeWriter1.getTextDocument()« eine Referenz auf das Textdokument einzurichten. Dann initialisiert Zeile 2 von Open Office einen Search-Descriptor, Zeile 5 fordert einen Replace-Descriptor (Listing 5) und Zeile 10 ein »XReplaceable«-Objekt an. Die Methode »setSearchString()« legt den Such-String fest, hier die Zeichenkette »Word«. (Zeile 13). Die Methode »setReplaceString()« nimmt die Zeichenkette auf, die den gesuchten Text ersetzt (Zeile 14). Schließlich führt »replaceAll()« die Ersetzung im ganzen Dokument durch (Zeile 15).
Einfügen ganzer Dokumente
Der Inhalt von Abschnitten lässt sich mit UNO relativ leicht in ein bestehendes Dokument einfügen. Manchmal ist es aber erforderlich, gleich ein ganzes Dokument in ein bestehendes Dokument zu laden. Dazu legt man einen »PropertyValue« an, beschafft sich per »getTextDocument()« wieder das »TextDocument« aus dem Writer, holt sich daraus den Text (Objekt »XText«) und instanziiert einen Text-Cursor. Über das »XDocumentInsertable«-Objekt wird danach das Dokument aus der übergebenen URL eingefügt (Listing 6).
Die Vorgehensweise ist etwas schwieriger als bei den anderen Beispielen. Naturgemäß bringt eine ausgereifte Textverarbeitung wie Open Writer etwas Komplexität mit sich, doch gewinnt man bei diesem Vorgehen auch zusätzlichen Gestaltungsspielraum: Der Text-Cursor lässt sich zum Beispiel genau positionieren und nicht nur ans Ende oder an den Anfang eines Dokuments setzen.
Die gezeigten fünf Beispiele Speichern, Laden, Einfügen, Drucken und Ersetzen von Text genügen im Grunde schon, um eine simple Applikation zu schreiben.

Abbildung 2: Die erstellte Demo-Anwendung: »Word« wird beim Klick auf den entsprechenden Button selbstverständlich durch »Writer« ersetzt.
Faktura mit OO
Als Beispiel dient eine kleine Faktura-Anwendung mit dem Namen Gnu Accounting[5]. Sie bettet Open Office ein, um die Vorlagen für Rechnungen und Gutschriften zu gestalten. Artikel und Kunden speichert sie in MySQL, zeigt Rechnungen oder Gutschriften im eingebettetem Open Office an und druckt sie auf Wunsch gleich aus.
In den Vorlagen lassen sich Kopf, Fuß und die Rechnungspositionen mit allen Möglichkeiten von Open Office gestalten: Listen, Tabellen, Schriften, Grafiken und so weiter. Auch der Einsatz echter Datenfelder, wie sie aus Serienbriefen bekannt sind, ist nicht schwierig, nur müsste man eine Auswahl solcher Felder präsentieren.
Das ließe sich zum Beispiel innerhalb des Kontextmenüs von Open Office realisieren, das innerhalb des Office-Bean modifiziert werden kann, siehe[6]. Der Einfachheit halber beschränkt sich das Beispiel aber auf Schlüsselwörter, zum Beispiel »<item:name/>« für den Artikeltext.
Die Anwendung lädt also den Kopf, ersetzt die Schlüsselwörter beispielsweise für die Kundenadresse, öffnet die Vorlage für jeweils eine Rechnungsposition und ersetzt die Schlüsselwörter für die Positionsdaten. Dann berechnet sie die Gesamtsumme, fügt den Footer an und passt darin die entsprechenden Schlüsselwörter für Gesamtsumme und Zahlungsbedingungen an.
Schließlich speichert sie das Ganze als SXW-Datei und druckt es auf Wunsch automatisch aus. Die gestellten Rechnungen landen mit dem Zahlungsziel in einer Tabelle, um später nach ordnungsgemäßem Erhalt der Zahlung aktualisiert zu werden. Eine spezielle Ansicht listet alle fälligen Zahlungen.
Weil Kunden in manchen Fällen nicht nur Geld bezahlen, sondern eventuell auch (zurück)bekommen, verwendet die Anwendung unterschiedliche Vorlagen, um beispielsweise auch Gutschriften auszustellen.
Zusammenfassung
Das hier entwickelte Beispielprogramm Gnu Accounting[5] ist an sich trivial, bietet aber Zusatznutzen in Form einer History, der Kunden- und Artikelverwaltung und der Anzeige fälliger Zahlungen. Ohne Open Office wäre eine solche Anwendung, mit der sich eine Rechnungsvorlage auch mal mit einem Jpeg-Bild versehen lässt, weitaus schwieriger zu realisieren.
Weitere Anwendungen sind denkbar, etwa eine fürs Customer-Relationship-Management, die in Java geschrieben als Knopf in Open Office erscheint und nicht nur die Adresse des Empfängers ausfüllt, sondern auch gleich per Verknüpfung in der unternehmensweiten Datenbank anzeigt, dass dieser Brief zu diesem Zeitpunkt an diesen Empfänger gesendet wurde. (ofr)
|
Infos |
|---|
|
[1] Open-Office-SDK: [http://www.openffice.org/dev_docs/source/sdk/] [2] Neo Office/J: [http://www.planamesa.com/neojava/en] [3] Bernhard Bablok, “Open-Office-Dokumente mit Java verarbeiten”: Linux-Magazin 10/03, S. 96 [4] Java-Komponenten mischen: [http://java.sun.com/products/jfc/tsc/articles/mixing/] [5] Gnu Accounting: [http://gnuaccounting.sf.net] [6] Kontextmenü verändern: [http://documentation.openoffice.org/HOW_TO/various_topics/HowTo_modify_context_menu.pdf] |
|
Der Autor |
|---|
|
Jochen Stärk ist Diplom-Wirtschaftsinformatiker (BA) und als Freiberufler tätig. |







